niemeyer/gopkg

changeRefs implementation causes an ambiguous default branch.

Closed this issue · 1 comments

The default branch of a git repository is its remote HEAD branch.

In modern versions of Go, go get respects the default branch and uses it.

I have a tool that verifies that a Go package has the default branch checked out (and lets you know if it's not). It's currently failing to work correctly on some gopkg.in repositories. This is because the implementation of changeRefs does something that causes the remote HEAD to be ambiguous.

Consider gopkg.in/sourcemap.v1 import path.

If I go get (or git clone) it and cd into the directory:

$ git remote show origin 
* remote origin
  Fetch URL: https://gopkg.in/sourcemap.v1
  Push  URL: https://gopkg.in/sourcemap.v1
  HEAD branch (remote HEAD is ambiguous, may be one of the following):
    master
    v1
  Remote branches:
    master tracked
    v1     tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

Note:

  HEAD branch (remote HEAD is ambiguous, may be one of the following):
    master
    v1

On the other hand, a regular git repository such as github.com/gorilla/mux typically prints:

$ git remote show origin 
* remote origin
  Fetch URL: https://github.com/gorilla/mux
  Push  URL: https://github.com/gorilla/mux
  HEAD branch: master
  Remote branches:
    custom-context   tracked
    master           tracked
    matcher-refactor tracked
    subexp-fix       tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

Note the single unambiguous value in HEAD branch: master.

This issue was originally reported at shurcooL/Go-Package-Store#72 (/cc @mvdan), you can see it for more details.

You can reproduce it by running this code:

package main

import (
	"fmt"
	"log"

	"github.com/shurcooL/vcsstate"
)

func main() {
	rv, err := vcsstate.NewRemoteVCS(vcs.ByCmd("git"))
	if err != nil {
		log.Fatalln(err)
	}
	branch, revision, err := rv.RemoteBranchAndRevision("https://gopkg.in/sourcemap.v1")
	fmt.Printf("branch = %q\nrevision = %q\nerr = %v\n", branch, revision, err)

	// Output:
	// branch = ""
	// revision = "6e83acea0053641eff084973fee085f0c193c61a"
	// err = HEAD branch not found in ls-remote output
}

On the other hand, a git repository from github results in:

branch, revision, err := rv.RemoteBranchAndRevision("https://github.com/gorilla/mux")
fmt.Printf("branch = %q\nrevision = %q\nerr = %v\n", branch, revision, err)

// Output:
// branch = "master"
// revision = "599cba5e7b6137d46ddf58fb1765f5d928e69604"
// err = <nil>

RemoteBranchAndRevision internally uses git ls-remote --symref remoteURL HEAD refs/heads/*, see its code.

You can also compare the output of:

curl -A 'git/2:2.1.1+github-607-gfba4028' 'https://github.com/gorilla/mux/info/refs?service=git-upload-pack'
curl -A 'git/2:2.1.1+github-607-gfba4028' 'https://gopkg.in/sourcemap.v1/info/refs?service=git-upload-pack'
git ls-remote --symref 'https://github.com/gorilla/mux' HEAD 'refs/heads/*'
git ls-remote --symref 'https://gopkg.in/sourcemap.v1' HEAD 'refs/heads/*'

I believe the root issue is in changeRefs and that needs to be fixed.

I'm using git version 2.10.1 (Apple Git-78) and go version go1.8 darwin/amd64.

Please let me know if you need any more information to reproduce this @niemeyer. Thank you.

Sorry for the late reply here. The logic modifies references with the very specific goal of pleasing git while operating under the "go" tool. The fact this works so well is a feat on itself, so I'm not super keen on doing further work on gopkg.in to chase the general well being of other git tooling. I am sorry that this is the case, though, since your report was clearly well written and reserached, thanks for that.