Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

get all remote branches #601

Closed
ankurmittal opened this issue Sep 19, 2017 · 9 comments · Fixed by #609
Closed

get all remote branches #601

ankurmittal opened this issue Sep 19, 2017 · 9 comments · Fixed by #609

Comments

@ankurmittal
Copy link

support feature to get all remote branches.

@erizocosmico
Copy link
Contributor

You can do it using Fetch or FetchContext:

remote, err := repo.Remote("origin")
if err != nil {
        // something with err
}

opts := &git.FetchOptions{
	RefSpecs: []config.RefSpec{"refs/*:refs/*", "HEAD:refs/heads/HEAD"},
}

if err := remote.Fetch(opts); err != nil {
        // something with err
}

@ankurmittal
Copy link
Author

by get i meant retrieving the name and hash

@orirawlings
Copy link
Contributor

Hey @ankurmittal,

I assume you are looking for something equivalent to git ls-remote? If so, there isn't any porcelain support for that at the moment, but it would be cool to see this functionality in the (*Remote) type.

The way we achieve this is by just starting a normal git upload-pack session on the remote repository, wait for it to advertise its references, and then close the session before requesting any references we want to fetch.

Some example code (with two functions copy-pasted directly from remote.go) demonstrates roughly how this can be done:

package main

import (
	"fmt"
	"os"

	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/transport"
	"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
	"gopkg.in/src-d/go-git.v4/storage/memory"
	"gopkg.in/src-d/go-git.v4/utils/ioutil"

	. "gopkg.in/src-d/go-git.v4/_examples"
)

// List remote references
func main() {
	CheckArgs("<path>", "<remote>")
	path := os.Args[1]
	remoteName := os.Args[2]

	r, err := git.PlainOpen(path)
	CheckIfError(err)

	remote, err := r.Remote(remoteName)
	CheckIfError(err)

	Info("git ls-remote %s", remoteName)

	// You can pass in a custom auth for the transport if necessary
	ar, err := lsRemote(remote, nil)
	CheckIfError(err)

	refs, err := ar.IterReferences()
	CheckIfError(err)

	refs.ForEach(func(ref *plumbing.Reference) error {
		fmt.Println(ref)
		return nil
	})
	CheckIfError(err)
}

// lsRemote returns the references contained in the remote
func lsRemote(remote *git.Remote, auth transport.AuthMethod) (memory.ReferenceStorage, error) {
	url := remote.Config().URLs[0]
	s, err := newUploadPackSession(url, auth)
	if err != nil {
		return nil, err
	}
	defer ioutil.CheckClose(s, &err)

	ar, err := s.AdvertisedReferences()
	if err != nil {
		return nil, err
	}

	return ar.AllReferences()
}

func newUploadPackSession(url string, auth transport.AuthMethod) (transport.UploadPackSession, error) {
	c, ep, err := newClient(url)
	if err != nil {
		return nil, err
	}

	return c.NewUploadPackSession(ep, auth)
}

func newClient(url string) (transport.Transport, transport.Endpoint, error) {
	ep, err := transport.NewEndpoint(url)
	if err != nil {
		return nil, nil, err
	}

	c, err := client.NewClient(ep)
	if err != nil {
		return nil, nil, err
	}

	return c, ep, err
}

Pull requests to add this porcelain functionality are definitely welcome!

@ankurmittal
Copy link
Author

no i was looking for git branch -r, I just want local remote branches. I suppose i can get all references and check if they are remote but it seems like a overkill, not sure how git does it though

@ankurmittal
Copy link
Author

As we have r.Branches function which returns all the branches can we have a branchtype so that is can return all local, remote or all the branches

@orirawlings
Copy link
Contributor

Ah, I understand now @ankurmittal.

I think you have the right idea. Here is a simple example program that mimics git branch -r:

package main

import (
	"fmt"
	"os"

	"gopkg.in/src-d/go-git.v4"
	. "gopkg.in/src-d/go-git.v4/_examples"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/storer"
)

// Open an existing repository in a specific folder.
func main() {
	CheckArgs("<path>")
	path := os.Args[1]

	r, err := git.PlainOpen(path)
	CheckIfError(err)

	// Length of the HEAD history
	Info("git branch -r")

	bs, err := remoteBranches(r.Storer)
	CheckIfError(err)

	err = bs.ForEach(func(b *plumbing.Reference) error {
		fmt.Println(b)
		return nil
	})
	CheckIfError(err)
}

func remoteBranches(s storer.ReferenceStorer) (storer.ReferenceIter, error) {
	refs, err := s.IterReferences()
	if err != nil {
		return nil, err
	}

	return storer.NewReferenceFilteredIter(func(ref *plumbing.Reference) bool {
		return ref.Name().IsRemote()
	}, refs), nil
}

@ankurmittal
Copy link
Author

yeah that works thanks

but the hash that i get for origin/HEAD is "0000000000000000000000000000000000000000", is this a bug? Should i open one?

@orirawlings
Copy link
Contributor

orirawlings commented Sep 20, 2017

origin/HEAD is likely a symbolic reference to another ref. So you'd need to resolve the reference before you can access the hash. Check out (*Repository).Reference to help with this.

@ankurmittal
Copy link
Author

gotcha, thanks

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants