cmd/go/internal/modfetch: update for new gitrepo backend

Drop API-based access to Bitbucket, Gerrit, GitHub,
now that we can (again) talk directly to their Git repos.

Also drop general LatestAt query in favor of plain Latest
(at current time on default branch), which is all that we
needed and all that makes sense when talking directly
to Git servers.

For golang/go#24915 (still need hg, svn, bzr, fossil).
Fixes golang/go#23955 (no GitHub API calls, so no rate limits).
Fixes golang/go#24085 (no GitHub API calls, so no 403 responses).
Fixes golang/go#24076 (GitHub enterprise's go-get=1 pages are now OK).
Fixes golang/go#24102 (no more canonical GitHub import path checks).

Change-Id: I99fadb09a565398da3ec88f17691217aca7bf571
Reviewed-on: https://go-review.googlesource.com/107657
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/modfetch/bitbucket/fetch.go b/vendor/cmd/go/internal/modfetch/bitbucket/fetch.go
index 9ae409b..c077a3e 100644
--- a/vendor/cmd/go/internal/modfetch/bitbucket/fetch.go
+++ b/vendor/cmd/go/internal/modfetch/bitbucket/fetch.go
@@ -6,14 +6,10 @@
 
 import (
 	"fmt"
-	"io"
-	"net/http"
-	"net/url"
 	"strings"
-	"time"
 
 	"cmd/go/internal/modfetch/codehost"
-	web "cmd/go/internal/web2"
+	"cmd/go/internal/modfetch/gitrepo"
 )
 
 func Lookup(path string) (codehost.Repo, error) {
@@ -21,158 +17,6 @@
 	if len(f) < 3 || f[0] != "bitbucket.org" {
 		return nil, fmt.Errorf("bitbucket repo must be bitbucket.org/org/project")
 	}
-
-	// Check for moved, renamed, or incorrect case in repository.
-	// Otherwise the API calls all appear to work.
-	// We need to do better here, but it's unclear exactly what.
-	var data struct {
-		FullName string `json:"full_name"`
-	}
-	err := web.Get("https://api.bitbucket.org/2.0/repositories/"+url.PathEscape(f[1])+"/"+url.PathEscape(f[2]), web.DecodeJSON(&data))
-	if err != nil {
-		return nil, err
-	}
-	myFullName := f[1] + "/" + f[2]
-	if myFullName != data.FullName {
-		why := "moved"
-		if strings.EqualFold(myFullName, data.FullName) {
-			why = "wrong case"
-		}
-		return nil, fmt.Errorf("module path of repo is bitbucket.org/%s, not %s (%s)", data.FullName, path, why)
-	}
-
-	return newRepo(f[1], f[2]), nil
-}
-
-func newRepo(owner, repository string) codehost.Repo {
-	return &repo{owner: owner, repo: repository}
-}
-
-type repo struct {
-	owner string
-	repo  string
-}
-
-func (r *repo) Root() string {
-	return "bitbucket.org/" + r.owner + "/" + r.repo
-}
-
-func (r *repo) Tags(prefix string) ([]string, error) {
-	var tags []string
-	u := "https://api.bitbucket.org/2.0/repositories/" + url.PathEscape(r.owner) + "/" + url.PathEscape(r.repo) + "/refs/tags"
-	var data struct {
-		Values []struct {
-			Name string `json:"name"`
-		} `json:"values"`
-	}
-	var hdr http.Header
-	err := web.Get(u, web.Header(&hdr), web.DecodeJSON(&data))
-	if err != nil {
-		return nil, err
-	}
-	for _, t := range data.Values {
-		if strings.HasPrefix(t.Name, prefix) {
-			tags = append(tags, t.Name)
-		}
-	}
-	return tags, nil
-}
-
-func (r *repo) LatestAt(t time.Time, branch string) (*codehost.RevInfo, error) {
-	u := "https://api.bitbucket.org/2.0/repositories/" + url.PathEscape(r.owner) + "/" + url.PathEscape(r.repo) + "/commits/" + url.QueryEscape(branch) + "?pagelen=10"
-	for u != "" {
-		var commits struct {
-			Values []struct {
-				Hash string `json:"hash"`
-				Date string `json:"date"`
-			} `json:"values"`
-			Next string `json:"next"`
-		}
-		err := web.Get(u, web.DecodeJSON(&commits))
-		if err != nil {
-			return nil, err
-		}
-		if len(commits.Values) == 0 {
-			return nil, fmt.Errorf("no commits")
-		}
-		for _, commit := range commits.Values {
-			d, err := time.Parse(time.RFC3339, commit.Date)
-			if err != nil {
-				return nil, err
-			}
-			if d.Before(t) {
-				info := &codehost.RevInfo{
-					Name:  commit.Hash,
-					Short: codehost.ShortenSHA1(commit.Hash),
-					Time:  d,
-				}
-				return info, nil
-			}
-		}
-		u = commits.Next
-	}
-	return nil, fmt.Errorf("no commits")
-}
-
-func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
-	var tag string
-	if !codehost.AllHex(rev) {
-		tag = rev
-	}
-	var commit struct {
-		Hash string `json:"hash"`
-		Type string `json:"type"`
-		Date string `json:"date"`
-	}
-	err := web.Get(
-		"https://api.bitbucket.org/2.0/repositories/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/commit/"+rev,
-		web.DecodeJSON(&commit),
-	)
-	if err != nil {
-		return nil, err
-	}
-	rev = commit.Hash
-	if rev == "" {
-		return nil, fmt.Errorf("no commits")
-	}
-	d, err := time.Parse(time.RFC3339, commit.Date)
-	if err != nil {
-		return nil, err
-	}
-	info := &codehost.RevInfo{
-		Name:    rev,
-		Short:   codehost.ShortenSHA1(rev),
-		Version: tag,
-		Time:    d,
-	}
-	return info, nil
-}
-
-func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
-	// TODO: Use maxSize.
-	// TODO: I could not find an API endpoint for getting information about an
-	// individual file, and I do not know if the raw file download endpoint is
-	// a stable API.
-	var body []byte
-	err := web.Get(
-		"https://bitbucket.org/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/raw/"+url.PathEscape(rev)+"/"+url.PathEscape(file),
-		web.ReadAllBody(&body),
-	)
-	return body, err
-}
-
-func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
-	// TODO: Make web.Get copy to file for us, with limit.
-	var body io.ReadCloser
-	err = web.Get(
-		"https://bitbucket.org/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/get/"+url.PathEscape(rev)+".zip",
-		web.Body(&body),
-	)
-	if err != nil {
-		if body != nil {
-			body.Close()
-		}
-		return nil, "", err
-	}
-	return body, "", nil
+	path = f[0] + "/" + f[1] + "/" + f[2]
+	return gitrepo.Repo("https://"+path, path)
 }
diff --git a/vendor/cmd/go/internal/modfetch/codehost/codehost.go b/vendor/cmd/go/internal/modfetch/codehost/codehost.go
index c1d12c8..69c2f5f 100644
--- a/vendor/cmd/go/internal/modfetch/codehost/codehost.go
+++ b/vendor/cmd/go/internal/modfetch/codehost/codehost.go
@@ -29,11 +29,9 @@
 	// commit hash, branch, tag, and so on.
 	Stat(rev string) (*RevInfo, error)
 
-	// LatestAt returns the latest revision at the given time.
-	// If branch is non-empty, it restricts the query to revisions
-	// on the named branch. The meaning of "branch" depends
-	// on the underlying implementation.
-	LatestAt(t time.Time, branch string) (*RevInfo, error)
+	// Latest returns the latest revision on the default branch,
+	// whatever that means in the underlying implementation.
+	Latest() (*RevInfo, error)
 
 	// ReadFile reads the given file in the file tree corresponding to revision rev.
 	// It should refuse to read more than maxSize bytes.
diff --git a/vendor/cmd/go/internal/modfetch/coderepo.go b/vendor/cmd/go/internal/modfetch/coderepo.go
index 22e640b..6ff7a89 100644
--- a/vendor/cmd/go/internal/modfetch/coderepo.go
+++ b/vendor/cmd/go/internal/modfetch/coderepo.go
@@ -113,8 +113,8 @@
 	return r.convert(info)
 }
 
-func (r *codeRepo) LatestAt(t time.Time, branch string) (*RevInfo, error) {
-	info, err := r.code.LatestAt(t, branch)
+func (r *codeRepo) Latest() (*RevInfo, error) {
+	info, err := r.code.Latest()
 	if err != nil {
 		return nil, err
 	}
diff --git a/vendor/cmd/go/internal/modfetch/coderepo_test.go b/vendor/cmd/go/internal/modfetch/coderepo_test.go
index 3673481..f7d25d7 100644
--- a/vendor/cmd/go/internal/modfetch/coderepo_test.go
+++ b/vendor/cmd/go/internal/modfetch/coderepo_test.go
@@ -10,6 +10,7 @@
 	"cmd/go/internal/webtest"
 	"io"
 	"io/ioutil"
+	"log"
 	"os"
 	"reflect"
 	"strings"
@@ -21,6 +22,21 @@
 	isTest = true
 }
 
+func TestMain(m *testing.M) {
+	os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+	dir, err := ioutil.TempDir("", "gitrepo-test-")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	codehost.WorkRoot = dir
+	return m.Run()
+}
+
 var codeRepoTests = []struct {
 	path     string
 	lookerr  string
@@ -119,12 +135,12 @@
 	{
 		path: "github.com/rsc/vgotest1/submod",
 		rev:  "v1.0.0",
-		err:  "404 Not Found", // TODO
+		err:  "unknown revision \"submod/v1.0.0\"",
 	},
 	{
 		path: "github.com/rsc/vgotest1/submod",
 		rev:  "v1.0.3",
-		err:  "404 Not Found", // TODO
+		err:  "unknown revision \"submod/v1.0.3\"",
 	},
 	{
 		path:    "github.com/rsc/vgotest1/submod",
@@ -229,16 +245,6 @@
 		gomod:   "module \"rsc.io/quote\"\n",
 	},
 	{
-		// redirect to bitbucket
-		path:    "example.net/vgotest",
-		rev:     "v1.0.0",
-		version: "v1.0.0",
-		name:    "9152736a7559a4b73ca45d36fe457cb617c3b207",
-		short:   "9152736a7559",
-		time:    time.Date(2017, 8, 22, 3, 10, 13, 0, time.UTC),
-		gomod:   "module \"example.net/vgotest\"\n",
-	},
-	{
 		// redirect to static hosting proxy
 		path:    "swtch.com/testmod",
 		rev:     "v1.0.0",
@@ -308,13 +314,13 @@
 		gomod:   "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n",
 	},
 	{
-		path:    "github.com/gobuffalo/buffalo",
-		rev:     "development",
-		version: "v0.0.0-20180406185414-59b4005674b6",
-		name:    "59b4005674b633728e2bfc3bb09cc204f7c2d6f5",
-		short:   "59b4005674b6",
-		time:    time.Date(2018, 4, 6, 18, 54, 14, 0, time.UTC),
-		gomod:   "//vgo 0.0.4\n\nmodule github.com/gobuffalo/buffalo\n",
+		path:    "vcs-test.golang.org/go/mod/gitrepo1",
+		rev:     "master",
+		version: "v0.0.0-20180417194322-ede458df7cd0",
+		name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
+		short:   "ede458df7cd0",
+		time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+		gomod:   "//vgo 0.0.4\n\nmodule vcs-test.golang.org/go/mod/gitrepo1\n",
 	},
 }
 
@@ -528,63 +534,26 @@
 	}
 }
 
-var latestAtTests = []struct {
+var latestTests = []struct {
 	path    string
-	time    time.Time
-	branch  string
 	version string
 	err     string
 }{
 	{
-		path: "github.com/rsc/vgotest1",
-		time: time.Date(2018, 1, 20, 0, 0, 0, 0, time.UTC),
+		path: "github.com/rsc/empty",
 		err:  "no commits",
 	},
 	{
 		path:    "github.com/rsc/vgotest1",
-		time:    time.Date(2018, 2, 20, 0, 0, 0, 0, time.UTC),
 		version: "v0.0.0-20180219223237-a08abb797a67",
 	},
 	{
-		path:    "github.com/rsc/vgotest1",
-		time:    time.Date(2018, 2, 20, 0, 0, 0, 0, time.UTC),
-		branch:  "mybranch",
-		version: "v0.0.0-20180219231006-80d85c5d4d17",
-	},
-	{
 		path:    "swtch.com/testmod",
-		time:    time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),
-		version: "v1.0.0",
-	},
-	{
-		path:    "swtch.com/testmod",
-		time:    time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
 		version: "v1.1.1",
 	},
-	{
-		path:   "swtch.com/testmod",
-		time:   time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC),
-		branch: "branch",
-		err:    "latest on branch not supported",
-	},
-	{
-		path:    "gopkg.in/check.v1",
-		time:    time.Date(2018, 2, 20, 15, 53, 33, 0, time.UTC),
-		version: "v0.0.0-20161208181325-20d25e280405",
-	},
-	{
-		path:    "gopkg.in/yaml.v2",
-		time:    time.Date(2018, 2, 20, 15, 53, 33, 0, time.UTC),
-		version: "v0.0.0-20180109114331-d670f9405373",
-	},
-	{
-		path:    "gopkg.in/russross/blackfriday.v2",
-		time:    time.Date(2018, 2, 20, 15, 53, 33, 0, time.UTC),
-		version: "v0.0.0-20180212083338-119f356b88f8",
-	},
 }
 
-func TestLatestAt(t *testing.T) {
+func TestLatest(t *testing.T) {
 	webtest.LoadOnce("testdata/webtest.txt")
 	webtest.Hook()
 	defer webtest.Unhook()
@@ -594,28 +563,25 @@
 		t.Fatal(err)
 	}
 	defer os.RemoveAll(tmpdir)
-	for _, tt := range latestAtTests {
-		name := strings.Replace(tt.path, "/", "_", -1) + "/" + tt.time.Format("2006-01-02_15:04:05")
-		if tt.branch != "" {
-			name += "/" + tt.branch
-		}
+	for _, tt := range latestTests {
+		name := strings.Replace(tt.path, "/", "_", -1)
 		t.Run(name, func(t *testing.T) {
 			repo, err := Lookup(tt.path)
 			if err != nil {
 				t.Fatalf("Lookup(%q): %v", tt.path, err)
 			}
-			info, err := repo.LatestAt(tt.time, tt.branch)
+			info, err := repo.Latest()
 			if err != nil {
 				if tt.err != "" {
 					if err.Error() == tt.err {
 						return
 					}
-					t.Fatalf("LatestAt(%v, %q): %v, want %q", tt.time, tt.branch, err, tt.err)
+					t.Fatalf("Latest(): %v, want %q", err, tt.err)
 				}
-				t.Fatalf("LatestAt(%v, %q): %v", tt.time, tt.branch, err)
+				t.Fatalf("Latest(): %v", err)
 			}
 			if info.Version != tt.version {
-				t.Fatalf("LatestAt(%v, %q) = %v, want %v", tt.time, tt.branch, info.Version, tt.version)
+				t.Fatalf("Latest() = %v, want %v", info.Version, tt.version)
 			}
 		})
 	}
diff --git a/vendor/cmd/go/internal/modfetch/convert_test.go b/vendor/cmd/go/internal/modfetch/convert_test.go
index ad9f690..d9e91f6 100644
--- a/vendor/cmd/go/internal/modfetch/convert_test.go
+++ b/vendor/cmd/go/internal/modfetch/convert_test.go
@@ -57,9 +57,13 @@
 			
 			require (
     				github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
+				github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
+				github.com/Sirupsen/logrus v0.0.0-20150409230825-55eb11d21d2a
 				github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
 				github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
 				github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
+				github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3
+				github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf
 				github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
 				github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
 				github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
diff --git a/vendor/cmd/go/internal/modfetch/domain.go b/vendor/cmd/go/internal/modfetch/domain.go
index 83d035e..633a4da 100644
--- a/vendor/cmd/go/internal/modfetch/domain.go
+++ b/vendor/cmd/go/internal/modfetch/domain.go
@@ -15,6 +15,7 @@
 	"strings"
 
 	"cmd/go/internal/modfetch/codehost"
+	"cmd/go/internal/modfetch/gitrepo"
 	web "cmd/go/internal/web2"
 )
 
@@ -65,26 +66,31 @@
 		}
 	}
 
-	// Fall back to redirections to known hosting services.
+	// Fall back to redirections to known version control systems.
 	for _, imp := range imports {
 		if path == imp.Prefix {
 			if !strings.HasPrefix(imp.RepoRoot, "https://") {
 				// TODO: Allow -insecure flag as a build flag?
 				return nil, fmt.Errorf("invalid server URL %q: must be HTTPS", imp.RepoRoot)
 			}
-			codePath := strings.TrimPrefix(imp.RepoRoot, "https://")
-			if code, err := lookupCodeHost(codePath, true); err != errNotHosted {
+			if imp.VCS == "git" {
+				code, err := gitrepo.Repo(imp.RepoRoot, imp.Prefix)
 				if err != nil {
 					return nil, err
 				}
-				return newCodeRepo(&customPrefix{code, imp.Prefix}, imp.Prefix)
+				return newCodeRepo(code, path)
 			}
 			return nil, fmt.Errorf("unknown VCS, Repo: %s, %s", imp.VCS, imp.RepoRoot)
 		}
+	}
+
+	// Check for redirect to repo root.
+	for _, imp := range imports {
 		if strings.HasPrefix(path, imp.Prefix+"/") {
 			return nil, &ModuleSubdirError{imp.Prefix}
 		}
 	}
+
 	return nil, fmt.Errorf("unknown module %s: no matching go-import tags", path)
 }
 
diff --git a/vendor/cmd/go/internal/modfetch/github/fetch.go b/vendor/cmd/go/internal/modfetch/github/fetch.go
index 6d2b830..a2a90f1 100644
--- a/vendor/cmd/go/internal/modfetch/github/fetch.go
+++ b/vendor/cmd/go/internal/modfetch/github/fetch.go
@@ -6,326 +6,19 @@
 
 import (
 	"fmt"
-	"io"
-	"net/http"
-	"net/url"
 	"strings"
-	"time"
 
 	"cmd/go/internal/modfetch/codehost"
-	web "cmd/go/internal/web2"
+	"cmd/go/internal/modfetch/gitrepo"
 )
 
+// Lookup returns the code repository enclosing the given module path,
+// which must begin with github.com/.
 func Lookup(path string) (codehost.Repo, error) {
 	f := strings.Split(path, "/")
 	if len(f) < 3 || f[0] != "github.com" {
 		return nil, fmt.Errorf("github repo must be github.com/org/project")
 	}
-
-	// Check for moved, renamed, or incorrect case in repository.
-	// Otherwise the API calls all appear to work.
-	// We need to do better here, but it's unclear exactly what.
-	var data struct {
-		FullName string `json:"full_name"`
-	}
-	err := web.Get("https://api.github.com/repos/"+url.PathEscape(f[1])+"/"+url.PathEscape(f[2]), web.DecodeJSON(&data))
-	if err != nil {
-		return nil, err
-	}
-	myFullName := f[1] + "/" + f[2]
-	if myFullName != data.FullName {
-		why := "moved"
-		if strings.EqualFold(myFullName, data.FullName) {
-			why = "wrong case"
-		}
-		return nil, fmt.Errorf("module path of repo is github.com/%s, not %s (%s)", data.FullName, path, why)
-	}
-
-	return newRepo(f[1], f[2]), nil
-}
-
-func newRepo(owner, repository string) codehost.Repo {
-	return &repo{owner, repository}
-}
-
-type repo struct {
-	owner string
-	repo  string
-}
-
-func (r *repo) Root() string {
-	return "github.com/" + r.owner + "/" + r.repo
-}
-
-func (r *repo) Tags(prefix string) ([]string, error) {
-	var tags []string
-	u := "https://api.github.com/repos/" + url.PathEscape(r.owner) + "/" + url.PathEscape(r.repo) + "/tags"
-	for u != "" {
-		var data []struct {
-			Name string
-		}
-		var hdr http.Header
-		err := web.Get(u, web.Header(&hdr), web.DecodeJSON(&data))
-		if err != nil {
-			return nil, err
-		}
-		for _, t := range data {
-			if strings.HasPrefix(t.Name, prefix) {
-				tags = append(tags, t.Name)
-			}
-		}
-		last := u
-		u = ""
-		for _, link := range parseLinks(hdr.Get("Link")) {
-			if link.Rel == "next" && link.URL != last {
-				u = link.URL
-			}
-		}
-	}
-	return tags, nil
-}
-
-func (r *repo) LatestAt(t time.Time, branch string) (*codehost.RevInfo, error) {
-	var commits []struct {
-		SHA    string
-		Commit struct {
-			Committer struct {
-				Date string
-			}
-		}
-	}
-	err := web.Get(
-		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/commits?sha="+url.QueryEscape(branch)+"&until="+url.QueryEscape(t.UTC().Format("2006-01-02T15:04:05Z"))+"&per_page=2",
-		web.DecodeJSON(&commits),
-	)
-	if err != nil {
-		return nil, err
-	}
-	if len(commits) == 0 {
-		return nil, fmt.Errorf("no commits")
-	}
-	d, err := time.Parse("2006-01-02T15:04:05Z", commits[0].Commit.Committer.Date)
-	if err != nil {
-		return nil, err
-	}
-
-	info := &codehost.RevInfo{
-		Name:  commits[0].SHA,
-		Short: codehost.ShortenSHA1(commits[0].SHA),
-		Time:  d,
-	}
-	return info, nil
-}
-
-var refKinds = []string{"tags", "heads"}
-
-func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
-	var tag string
-	if !codehost.AllHex(rev) {
-		// Resolve tag to rev
-		tag = rev
-		var ref struct {
-			Ref    string
-			Object struct {
-				Type string
-				SHA  string
-				URL  string
-			}
-		}
-
-		var firstErr error
-		for _, kind := range refKinds {
-			err := web.Get(
-				"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/git/refs/"+kind+"/"+tag,
-				web.DecodeJSON(&ref),
-			)
-			if err != nil {
-				if firstErr == nil {
-					firstErr = err
-				}
-				continue
-			}
-			switch ref.Object.Type {
-			default:
-				return nil, fmt.Errorf("invalid ref %q: not a commit or tag (%q)", tag, ref.Object.Type)
-
-			case "commit":
-				rev = ref.Object.SHA
-
-			case "tag":
-				var info struct {
-					Object struct {
-						SHA  string
-						Type string
-					}
-				}
-				err = web.Get(
-					ref.Object.URL,
-					web.DecodeJSON(&info),
-				)
-				if err != nil {
-					return nil, err
-				}
-				if info.Object.Type != "commit" {
-					return nil, fmt.Errorf("invalid annotated tag %q: not a commit (%q)", tag, info.Object.Type)
-				}
-				rev = info.Object.SHA
-			}
-			if rev == "" {
-				return nil, fmt.Errorf("invalid ref %q: missing SHA in GitHub response", tag)
-			}
-			break
-		}
-		if rev == "" {
-			return nil, fmt.Errorf("unknown ref %q (%v)", tag, firstErr)
-		}
-	}
-
-	var commits []struct {
-		SHA    string
-		Commit struct {
-			Committer struct {
-				Date string
-			}
-		}
-	}
-	err := web.Get(
-		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/commits?sha="+url.QueryEscape(rev)+"&per_page=2",
-		web.DecodeJSON(&commits),
-	)
-	if err != nil {
-		return nil, err
-	}
-	if len(commits) == 0 {
-		return nil, fmt.Errorf("no commits")
-	}
-	if !strings.HasPrefix(commits[0].SHA, rev) {
-		return nil, fmt.Errorf("wrong rev returned by server")
-	}
-	d, err := time.Parse("2006-01-02T15:04:05Z", commits[0].Commit.Committer.Date)
-	if err != nil {
-		return nil, err
-	}
-	info := &codehost.RevInfo{
-		Name:    commits[0].SHA,
-		Short:   codehost.ShortenSHA1(commits[0].SHA),
-		Version: tag,
-		Time:    d,
-	}
-	return info, nil
-}
-
-func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
-	var meta struct {
-		Type        string
-		Size        int64
-		Name        string
-		DownloadURL string `json:"download_url"`
-	}
-	err := web.Get(
-		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/contents/"+url.PathEscape(file)+"?ref="+url.QueryEscape(rev),
-		web.DecodeJSON(&meta),
-	)
-	if err != nil {
-		return nil, err
-	}
-	if meta.DownloadURL == "" {
-		return nil, fmt.Errorf("no download URL")
-	}
-
-	// TODO: Use maxSize.
-	var body []byte
-	err = web.Get(meta.DownloadURL, web.ReadAllBody(&body))
-	if err != nil {
-		return nil, err
-	}
-	return body, nil
-}
-
-func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
-	// TODO: Make web.Get copy to file for us, with limit.
-	var body io.ReadCloser
-	err = web.Get(
-		"https://api.github.com/repos/"+url.PathEscape(r.owner)+"/"+url.PathEscape(r.repo)+"/zipball/"+url.PathEscape(rev),
-		web.Body(&body),
-	)
-	if err != nil {
-		if body != nil {
-			body.Close()
-		}
-		return nil, "", err
-	}
-	return body, "", nil
-}
-
-type link struct {
-	URL string
-	Rel string
-}
-
-func parseLinks(text string) []link {
-	var links []link
-	for text != "" {
-		text = strings.TrimSpace(text)
-		if !strings.HasPrefix(text, "<") {
-			break
-		}
-		i := strings.Index(text, ">")
-		if i < 0 {
-			break
-		}
-		u := text[1:i]
-		text = strings.TrimSpace(text[i+1:])
-	Attrs:
-		for strings.HasPrefix(text, ";") {
-			text = text[1:]
-			j := 0
-			for j < len(text) && text[j] != '=' {
-				if text[j] == ',' || text[j] == ';' || text[j] == '"' {
-					break Attrs
-				}
-				j++
-			}
-			if j >= len(text) {
-				break Attrs
-			}
-			key := strings.TrimSpace(text[:j])
-			text = strings.TrimSpace(text[j+1:])
-			var val string
-			if len(text) > 0 && text[0] == '"' {
-				k := 1
-				for k < len(text) && text[k] != '"' {
-					if text[k] == '\\' && k+1 < len(text) {
-						k++
-					}
-					k++
-				}
-				if k >= len(text) {
-					break Attrs
-				}
-				val, text = text[1:k], text[k+1:]
-			} else {
-				k := 0
-				for k < len(text) && text[k] != ';' && text[k] != ',' {
-					if text[k] == '"' {
-						break Attrs
-					}
-					k++
-				}
-				if k >= len(text) {
-					break Attrs
-				}
-				val, text = text[:k], text[k:]
-			}
-			if key == "rel" {
-				links = append(links, link{URL: u, Rel: strings.TrimSpace(val)})
-			}
-		}
-		text = strings.TrimSpace(text)
-		if !strings.HasPrefix(text, ",") {
-			break
-		}
-		text = text[1:]
-	}
-	return links
+	path = f[0] + "/" + f[1] + "/" + f[2]
+	return gitrepo.Repo("https://"+path, path)
 }
diff --git a/vendor/cmd/go/internal/modfetch/googlesource/fetch.go b/vendor/cmd/go/internal/modfetch/googlesource/fetch.go
index a51763b..8317ac3 100644
--- a/vendor/cmd/go/internal/modfetch/googlesource/fetch.go
+++ b/vendor/cmd/go/internal/modfetch/googlesource/fetch.go
@@ -5,23 +5,11 @@
 package googlesource
 
 import (
-	"archive/tar"
-	"archive/zip"
-	"bufio"
-	"compress/gzip"
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
-	"io"
-	"io/ioutil"
-	"net/url"
-	"os"
-	"strconv"
 	"strings"
-	"time"
 
 	"cmd/go/internal/modfetch/codehost"
-	web "cmd/go/internal/web2"
+	"cmd/go/internal/modfetch/gitrepo"
 )
 
 func Lookup(path string) (codehost.Repo, error) {
@@ -33,251 +21,5 @@
 	if j >= 0 {
 		path = path[:i+1+j]
 	}
-	r := &repo{
-		root: path,
-		base: "https://" + path,
-	}
-	return r, nil
-}
-
-type repo struct {
-	base string
-	root string
-}
-
-func (r *repo) Root() string {
-	return r.root
-}
-
-func (r *repo) Tags(prefix string) ([]string, error) {
-	var data []byte
-	err := web.Get(r.base+"/+refs/tags/?format=TEXT", web.ReadAllBody(&data))
-	if err != nil {
-		return nil, err
-	}
-	prefix = "refs/tags/" + prefix
-	var tags []string
-	for _, line := range strings.Split(string(data), "\n") {
-		f := strings.Fields(line)
-		if len(f) == 2 && len(f[0]) == 40 && strings.HasPrefix(f[1], prefix) {
-			tags = append(tags, strings.TrimPrefix(f[1], "refs/tags/"))
-		}
-	}
-	return tags, nil
-}
-
-func (r *repo) LatestAt(limit time.Time, branch string) (*codehost.RevInfo, error) {
-	u := r.base + "/+log/" + url.PathEscape(branch) + "?format=JSON&n=2"
-	var n int
-	for u != "" {
-		var body io.ReadCloser
-		err := web.Get(u, web.Body(&body))
-		if err != nil {
-			return nil, err
-		}
-		b := make([]byte, 1)
-		for {
-			_, err := body.Read(b)
-			if err != nil {
-				body.Close()
-				return nil, err
-			}
-			if b[0] == '\n' {
-				break
-			}
-		}
-		var data struct {
-			Log []struct {
-				Commit    string
-				Committer struct {
-					Time string
-				}
-			}
-			Next string
-		}
-		err = json.NewDecoder(body).Decode(&data)
-		body.Close()
-		if err != nil {
-			return nil, err
-		}
-		for i := range data.Log {
-			t, err := time.Parse("Mon Jan 02 15:04:05 2006 -0700", data.Log[i].Committer.Time)
-			if err != nil {
-				return nil, err
-			}
-			if !t.After(limit) {
-				info := &codehost.RevInfo{
-					Time:  t.UTC(),
-					Name:  data.Log[i].Commit,
-					Short: codehost.ShortenSHA1(data.Log[i].Commit),
-				}
-				return info, nil
-			}
-		}
-		u = ""
-		if data.Next != "" {
-			if n == 0 {
-				n = 10
-			} else if n < 1000 {
-				n *= 2
-			}
-			u = r.base + "/+log/" + url.PathEscape(data.Next) + "?format=JSON&n=" + fmt.Sprint(n)
-		}
-	}
-	return nil, fmt.Errorf("no commits")
-}
-
-func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
-	if !codehost.AllHex(rev) || len(rev) != 40 {
-		return r.LatestAt(time.Date(9999, 1, 1, 0, 0, 0, 0, time.UTC), rev)
-	}
-
-	var body io.ReadCloser
-	u := r.base + "/+show/" + url.PathEscape(rev) + "?format=TEXT"
-	if err := web.Get(u, web.Body(&body)); err != nil {
-		return nil, err
-	}
-	defer body.Close()
-	b := bufio.NewReader(base64.NewDecoder(base64.StdEncoding, body))
-	for {
-		line, err := b.ReadSlice('\n')
-		if err != nil {
-			return nil, err
-		}
-		s := string(line)
-		if s == "\n" {
-			return nil, fmt.Errorf("malformed commit: no committer")
-		}
-		if strings.HasPrefix(s, "committer ") {
-			f := strings.Fields(s)
-			if len(f) >= 3 {
-				v, err := strconv.ParseUint(f[len(f)-2], 10, 64)
-				if err == nil {
-					info := &codehost.RevInfo{
-						Time:  time.Unix(int64(v), 0).UTC(),
-						Name:  rev,
-						Short: codehost.ShortenSHA1(rev),
-					}
-					return info, nil
-				}
-			}
-		}
-	}
-}
-
-func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
-	u := r.base + "/+show/" + url.PathEscape(rev) + "/" + file + "?format=TEXT"
-	var body io.ReadCloser
-	if err := web.Get(u, web.Body(&body)); err != nil {
-		return nil, err
-	}
-	defer body.Close()
-	lr := &io.LimitedReader{R: base64.NewDecoder(base64.StdEncoding, body), N: maxSize + 1}
-	data, err := ioutil.ReadAll(lr)
-	if lr.N <= 0 {
-		return data, fmt.Errorf("too long")
-	}
-	return data, err
-}
-
-type closeRemover struct {
-	*os.File
-}
-
-func (c *closeRemover) Close() error {
-	c.File.Close()
-	os.Remove(c.File.Name())
-	return nil
-}
-
-func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zipstream io.ReadCloser, actualSubdir string, err error) {
-	// Start download of tgz for subdir.
-	if subdir != "" {
-		subdir = "/" + strings.TrimSuffix(subdir, "/")
-	}
-	u := r.base + "/+archive/" + url.PathEscape(rev) + subdir + ".tar.gz"
-	var body io.ReadCloser
-	if err := web.Get(u, web.Body(&body)); err != nil {
-		return nil, "", err
-	}
-	defer body.Close()
-	gz, err := gzip.NewReader(body)
-	if err != nil {
-		return nil, "", err
-	}
-	defer gz.Close()
-	tr := tar.NewReader(gz)
-
-	// Start temporary zip file.
-	f, err := ioutil.TempFile("", "vgo-")
-	if err != nil {
-		return nil, "", err
-	}
-	defer func() {
-		f.Close()
-		if err != nil {
-			os.Remove(f.Name())
-		}
-	}()
-	z := zip.NewWriter(f)
-
-	// Copy files from tgz to zip file.
-	prefix := "googlesource/"
-	haveLICENSE := false
-	for {
-		hdr, err := tr.Next()
-		if err != nil {
-			if err == io.EOF {
-				break
-			}
-			return nil, "", fmt.Errorf("reading tgz from gitiles: %v", err)
-		}
-		maxSize -= 512
-		switch hdr.Typeflag {
-		case tar.TypeDir:
-			// ok
-		case tar.TypeReg:
-			if maxSize < hdr.Size {
-				return nil, "", fmt.Errorf("module source tree too big")
-			}
-			maxSize -= hdr.Size
-			if hdr.Name == "LICENSE" {
-				haveLICENSE = true
-			}
-			fw, err := z.Create(prefix + hdr.Name)
-			if err != nil {
-				return nil, "", err
-			}
-			if _, err := io.Copy(fw, tr); err != nil {
-				return nil, "", err
-			}
-		}
-	}
-
-	// Add LICENSE from parent directory if needed.
-	if !haveLICENSE && subdir != "" {
-		if data, err := r.ReadFile(rev, "LICENSE", codehost.MaxLICENSE); err == nil {
-			fw, err := z.Create(prefix + "LICENSE")
-			if err != nil {
-				return nil, "", err
-			}
-			if _, err := fw.Write(data); err != nil {
-				return nil, "", err
-			}
-		}
-	}
-
-	// Finish.
-	if err := z.Close(); err != nil {
-		return nil, "", err
-	}
-	if err := f.Close(); err != nil {
-		return nil, "", err
-	}
-
-	fr, err := os.Open(f.Name())
-	if err != nil {
-		return nil, "", err
-	}
-	return &closeRemover{fr}, subdir, nil
+	return gitrepo.Repo("https://"+path, path)
 }
diff --git a/vendor/cmd/go/internal/modfetch/gopkgin.go b/vendor/cmd/go/internal/modfetch/gopkgin.go
index f3a5987..a3b8b6f 100644
--- a/vendor/cmd/go/internal/modfetch/gopkgin.go
+++ b/vendor/cmd/go/internal/modfetch/gopkgin.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// TODO: Figure out what gopkg.in should do.
+
 package modfetch
 
 import (
@@ -11,7 +13,6 @@
 	"fmt"
 	"io"
 	"strings"
-	"time"
 )
 
 func ParseGopkgIn(path string) (root, repo, major, subdir string, ok bool) {
@@ -99,11 +100,11 @@
 	return r.convert(r.gh.Stat(ghRev))
 }
 
-func (r *gopkgin) LatestAt(t time.Time, branch string) (*codehost.RevInfo, error) {
+func (r *gopkgin) Latest() (*codehost.RevInfo, error) {
 	if r.major == "v0" {
-		return r.convert(r.gh.LatestAt(t, "master"))
+		return r.convert(r.gh.Stat("master"))
 	}
-	return r.convert(r.gh.LatestAt(t, r.major))
+	return r.convert(r.gh.Stat(r.major))
 }
 
 func (r *gopkgin) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
diff --git a/vendor/cmd/go/internal/modfetch/proxy.go b/vendor/cmd/go/internal/modfetch/proxy.go
index 0dc5939..089883d 100644
--- a/vendor/cmd/go/internal/modfetch/proxy.go
+++ b/vendor/cmd/go/internal/modfetch/proxy.go
@@ -59,7 +59,7 @@
 	return list, nil
 }
 
-func (p *proxyRepo) latestVersion(limit time.Time) (*RevInfo, error) {
+func (p *proxyRepo) latest() (*RevInfo, error) {
 	var data []byte
 	err := web.Get(p.url+"/@v/list", web.ReadAllBody(&data))
 	if err != nil {
@@ -71,7 +71,7 @@
 		f := strings.Fields(line)
 		if len(f) >= 2 && semver.IsValid(f[0]) {
 			ft, err := time.Parse(time.RFC3339, f[1])
-			if err == nil && !ft.After(limit) && best.Before(ft) {
+			if err == nil && best.Before(ft) {
 				best = ft
 				bestVersion = f[0]
 			}
@@ -102,19 +102,13 @@
 	return info, nil
 }
 
-func (p *proxyRepo) LatestAt(t time.Time, branch string) (*RevInfo, error) {
+func (p *proxyRepo) Latest() (*RevInfo, error) {
 	var data []byte
-	u := p.url + "/@t/" + t.UTC().Format("20060102150405")
-	if branch != "" {
-		u += "/" + pathEscape(branch)
-	}
+	u := p.url + "/@latest"
 	err := web.Get(u, web.ReadAllBody(&data))
 	if err != nil {
 		// TODO return err if not 404
-		if branch != "" {
-			return nil, fmt.Errorf("latest on branch not supported")
-		}
-		return p.latestVersion(t)
+		return p.latest()
 	}
 	info := new(RevInfo)
 	if err := json.Unmarshal(data, info); err != nil {
diff --git a/vendor/cmd/go/internal/modfetch/query.go b/vendor/cmd/go/internal/modfetch/query.go
index 640ee12..3cd7e27 100644
--- a/vendor/cmd/go/internal/modfetch/query.go
+++ b/vendor/cmd/go/internal/modfetch/query.go
@@ -9,7 +9,6 @@
 	"cmd/go/internal/semver"
 	"fmt"
 	"strings"
-	"time"
 )
 
 // Query looks up a revision of a given module given a version query string.
@@ -52,7 +51,7 @@
 			return nil, err
 		}
 		if len(versions) == 0 && vers == "latest" {
-			return repo.LatestAt(time.Now(), "")
+			return repo.Latest()
 		}
 		if vers == "latest" {
 			for i := len(versions) - 1; i >= 0; i-- {
diff --git a/vendor/cmd/go/internal/modfetch/repo.go b/vendor/cmd/go/internal/modfetch/repo.go
index 8e4a3bb..6e21a41 100644
--- a/vendor/cmd/go/internal/modfetch/repo.go
+++ b/vendor/cmd/go/internal/modfetch/repo.go
@@ -35,11 +35,10 @@
 	// commit hash, branch, tag, and so on.
 	Stat(rev string) (*RevInfo, error)
 
-	// LatestAt returns the latest revision at the given time.
-	// If branch is non-empty, it restricts the query to revisions
-	// on the named branch. The meaning of "branch" depends
-	// on the underlying implementation.
-	LatestAt(t time.Time, branch string) (*RevInfo, error)
+	// Latest returns the latest revision on the default branch,
+	// whatever that means in the underlying source code repository.
+	// It is only used when there are no tagged versions.
+	Latest() (*RevInfo, error)
 
 	// GoMod returns the go.mod file for the given version.
 	GoMod(version string) (data []byte, err error)
@@ -115,14 +114,8 @@
 func lookupCodeHost(path string, customDomain bool) (codehost.Repo, error) {
 	switch {
 	case strings.HasPrefix(path, "github.com/"):
-		// Special case GitHub paths ending in ".git" for backwards compatibility
-		// with go get.
-		path = strings.TrimSuffix(path, ".git")
 		return github.Lookup(path)
 	case strings.HasPrefix(path, "bitbucket.org/"):
-		// Special case Bitbucket paths ending in ".git" for backwards compatibility
-		// with go get.
-		path = strings.TrimSuffix(path, ".git")
 		return bitbucket.Lookup(path)
 	case customDomain && strings.HasSuffix(path[:strings.Index(path, "/")+1], ".googlesource.com/") ||
 		isTest && strings.HasPrefix(path, "go.googlesource.com/scratch"):
diff --git a/vendor/cmd/go/internal/vgo/load.go b/vendor/cmd/go/internal/vgo/load.go
index 0dce36a..282a1d3 100644
--- a/vendor/cmd/go/internal/vgo/load.go
+++ b/vendor/cmd/go/internal/vgo/load.go
@@ -502,9 +502,9 @@
 
 func (*mvsReqs) Latest(path string) (module.Version, error) {
 	// Note that query "latest" is not the same as
-	// using repo.LatestAt.
+	// using repo.Latest.
 	// The query only falls back to untagged versions
-	// if nothing is tagged. The LatestAt method
+	// if nothing is tagged. The Latest method
 	// only ever returns untagged versions,
 	// which is not what we want.
 	fmt.Fprintf(os.Stderr, "vgo: finding %s latest\n", path)