cmd/go: merge module support from x/vgo repo

This CL corresponds to CL 123361, the final manual CL in that repo,
making this the final manual sync.

All future commits will happen in this repo (the main Go repo),
and we'll update x/vgo automatically with a fixed patch+script.

Change-Id: I572243309c1809727604fd704705a23c30e85d1a
Reviewed-on: https://go-review.googlesource.com/123576
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index c9353e9..7b7f1cc 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -8,6 +8,7 @@
 	"bytes"
 	"debug/elf"
 	"debug/macho"
+	"flag"
 	"fmt"
 	"go/format"
 	"internal/race"
@@ -112,6 +113,12 @@
 	}
 	os.Unsetenv("GOROOT_FINAL")
 
+	flag.Parse()
+	if *proxyAddr != "" {
+		StartProxy()
+		select {}
+	}
+
 	if canRun {
 		args := []string{"build", "-tags", "testgo", "-o", "testgo" + exeSuffix}
 		if race.Enabled {
@@ -417,7 +424,8 @@
 func (tg *testgoData) run(args ...string) {
 	tg.t.Helper()
 	if status := tg.doRun(args); status != nil {
-		tg.t.Logf("go %v failed unexpectedly: %v", args, status)
+		wd, _ := os.Getwd()
+		tg.t.Logf("go %v failed unexpectedly in %s: %v", args, wd, status)
 		tg.t.FailNow()
 	}
 }
@@ -760,24 +768,51 @@
 	}
 }
 
+// If -testwork is specified, the test prints the name of the temp directory
+// and does not remove it when done, so that a programmer can
+// poke at the test file tree afterward.
+var testWork = flag.Bool("testwork", false, "")
+
 // cleanup cleans up a test that runs testgo.
 func (tg *testgoData) cleanup() {
 	tg.t.Helper()
 	if tg.wd != "" {
+		wd, _ := os.Getwd()
+		tg.t.Logf("ended in %s", wd)
+
 		if err := os.Chdir(tg.wd); err != nil {
 			// We are unlikely to be able to continue.
 			fmt.Fprintln(os.Stderr, "could not restore working directory, crashing:", err)
 			os.Exit(2)
 		}
 	}
+	if *testWork {
+		tg.t.Logf("TESTWORK=%s\n", tg.path("."))
+		return
+	}
 	for _, path := range tg.temps {
-		tg.check(os.RemoveAll(path))
+		tg.check(removeAll(path))
 	}
 	if tg.tempdir != "" {
-		tg.check(os.RemoveAll(tg.tempdir))
+		tg.check(removeAll(tg.tempdir))
 	}
 }
 
+func removeAll(dir string) error {
+	// module cache has 0444 directories;
+	// make them writable in order to remove content.
+	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return nil // ignore errors walking in file system
+		}
+		if info.IsDir() {
+			os.Chmod(path, 0777)
+		}
+		return nil
+	})
+	return os.RemoveAll(dir)
+}
+
 // failSSH puts an ssh executable in the PATH that always fails.
 // This is to stub out uses of ssh by go get.
 func (tg *testgoData) failSSH() {
@@ -1745,8 +1780,8 @@
 	defer tg.cleanup()
 	tg.tempDir("gopath")
 	tg.setenv("GOPATH", tg.path("gopath"))
-	tg.run("get", "golang.org/x/tour/content")
-	tg.run("get", "-t", "golang.org/x/tour/content")
+	tg.run("get", "golang.org/x/tour/content...")
+	tg.run("get", "-t", "golang.org/x/tour/content...")
 }
 
 func TestInstalls(t *testing.T) {
@@ -3165,11 +3200,25 @@
 	checkbar("cmd")
 }
 
-func TestGoBuildInTestOnlyDirectoryFailsWithAGoodError(t *testing.T) {
+func TestGoBuildTestOnly(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
-	tg.runFail("build", "./testdata/testonly")
-	tg.grepStderr("no non-test Go files in", "go build ./testdata/testonly produced unexpected error")
+	tg.makeTempdir()
+	tg.setenv("GOPATH", tg.path("."))
+	tg.tempFile("src/testonly/t_test.go", `package testonly`)
+	tg.tempFile("src/testonly2/t.go", `package testonly2`)
+	tg.cd(tg.path("src"))
+
+	// Named explicitly, test-only packages should be reported as unbuildable/uninstallable,
+	// even if there is a wildcard also matching.
+	tg.runFail("build", "testonly", "testonly...")
+	tg.grepStderr("no non-test Go files in", "go build ./xtestonly produced unexpected error")
+	tg.runFail("install", "./testonly")
+	tg.grepStderr("no non-test Go files in", "go install ./testonly produced unexpected error")
+
+	// Named through a wildcards, the test-only packages should be silently ignored.
+	tg.run("build", "testonly...")
+	tg.run("install", "./testonly...")
 }
 
 func TestGoTestDetectsTestOnlyImportCycles(t *testing.T) {
@@ -6115,12 +6164,12 @@
 	tg.tempFile("src/@x/x.go", "package x\n")
 	tg.setenv("GOPATH", tg.path("."))
 	tg.runFail("build", "@x")
-	tg.grepStderr("invalid input directory name \"@x\"", "did not reject @x directory")
+	tg.grepStderr("invalid input directory name \"@x\"|cannot use path@version syntax", "did not reject @x directory")
 
 	tg.tempFile("src/@x/y/y.go", "package y\n")
 	tg.setenv("GOPATH", tg.path("."))
 	tg.runFail("build", "@x/y")
-	tg.grepStderr("invalid import path \"@x/y\"", "did not reject @x/y import path")
+	tg.grepStderr("invalid import path \"@x/y\"|cannot use path@version syntax", "did not reject @x/y import path")
 
 	tg.tempFile("src/-x/x.go", "package x\n")
 	tg.setenv("GOPATH", tg.path("."))
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index b7906bb..4e69da5 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -21,6 +21,7 @@
 	BuildA                 bool   // -a flag
 	BuildBuildmode         string // -buildmode flag
 	BuildContext           = build.Default
+	BuildGetmode           string             // -getmode flag
 	BuildI                 bool               // -i flag
 	BuildLinkshared        bool               // -linkshared flag
 	BuildMSan              bool               // -msan flag
@@ -67,6 +68,11 @@
 	Goos      = BuildContext.GOOS
 	ExeSuffix string
 	Gopath    = filepath.SplitList(BuildContext.GOPATH)
+
+	// ModulesEnabled specifies whether the go command is running
+	// in module-aware mode (as opposed to GOPATH mode).
+	// It is equal to modload.Enabled, but not all packages can import modload.
+	ModulesEnabled bool
 )
 
 func init() {
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index 1c5640f..44fd18a 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -9,6 +9,7 @@
 	"encoding/json"
 	"fmt"
 	"os"
+	"path/filepath"
 	"runtime"
 	"strings"
 
@@ -16,7 +17,7 @@
 	"cmd/go/internal/cache"
 	"cmd/go/internal/cfg"
 	"cmd/go/internal/load"
-	"cmd/go/internal/vgo"
+	"cmd/go/internal/modload"
 	"cmd/go/internal/work"
 )
 
@@ -112,6 +113,18 @@
 
 // ExtraEnvVars returns environment variables that should not leak into child processes.
 func ExtraEnvVars() []cfg.EnvVar {
+	gomod := ""
+	if modload.Init(); modload.ModRoot != "" {
+		gomod = filepath.Join(modload.ModRoot, "go.mod")
+	}
+	return []cfg.EnvVar{
+		{Name: "GOMOD", Value: gomod},
+	}
+}
+
+// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
+// but are costly to evaluate.
+func ExtraEnvVarsCostly() []cfg.EnvVar {
 	var b work.Builder
 	b.Init()
 	cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
@@ -121,6 +134,7 @@
 		return nil
 	}
 	cmd := b.GccCmd(".", "")
+
 	return []cfg.EnvVar{
 		// Note: Update the switch in runEnv below when adding to this list.
 		{Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")},
@@ -130,19 +144,19 @@
 		{Name: "CGO_LDFLAGS", Value: strings.Join(ldflags, " ")},
 		{Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
 		{Name: "GOGCCFLAGS", Value: strings.Join(cmd[3:], " ")},
-		{Name: "VGOMODROOT", Value: vgo.ModRoot},
 	}
 }
 
 func runEnv(cmd *base.Command, args []string) {
 	env := cfg.CmdEnv
+	env = append(env, ExtraEnvVars()...)
 
-	// Do we need to call ExtraEnvVars, which is a bit expensive?
+	// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
 	// Only if we're listing all environment variables ("go env")
 	// or the variables being requested are in the extra list.
-	needExtra := true
+	needCostly := true
 	if len(args) > 0 {
-		needExtra = false
+		needCostly = false
 		for _, arg := range args {
 			switch arg {
 			case "CGO_CFLAGS",
@@ -152,12 +166,12 @@
 				"CGO_LDFLAGS",
 				"PKG_CONFIG",
 				"GOGCCFLAGS":
-				needExtra = true
+				needCostly = true
 			}
 		}
 	}
-	if needExtra {
-		env = append(env, ExtraEnvVars()...)
+	if needCostly {
+		env = append(env, ExtraEnvVarsCostly()...)
 	}
 
 	if len(args) > 0 {
diff --git a/src/cmd/go/internal/fix/fix.go b/src/cmd/go/internal/fix/fix.go
index 56f8832..1f51d77 100644
--- a/src/cmd/go/internal/fix/fix.go
+++ b/src/cmd/go/internal/fix/fix.go
@@ -9,8 +9,8 @@
 	"cmd/go/internal/base"
 	"cmd/go/internal/cfg"
 	"cmd/go/internal/load"
+	"cmd/go/internal/modload"
 	"cmd/go/internal/str"
-	"cmd/go/internal/vgo"
 	"fmt"
 	"os"
 )
@@ -34,9 +34,9 @@
 func runFix(cmd *base.Command, args []string) {
 	printed := false
 	for _, pkg := range load.Packages(args) {
-		if vgo.Enabled() && !pkg.Module.Top {
+		if modload.Enabled() && !pkg.Module.Main {
 			if !printed {
-				fmt.Fprintf(os.Stderr, "vgo: not fixing packages in dependency modules\n")
+				fmt.Fprintf(os.Stderr, "go: not fixing packages in dependency modules\n")
 				printed = true
 			}
 			continue
diff --git a/src/cmd/go/internal/fmtcmd/fmt.go b/src/cmd/go/internal/fmtcmd/fmt.go
index c3a90d0..ae158a7 100644
--- a/src/cmd/go/internal/fmtcmd/fmt.go
+++ b/src/cmd/go/internal/fmtcmd/fmt.go
@@ -16,8 +16,8 @@
 	"cmd/go/internal/base"
 	"cmd/go/internal/cfg"
 	"cmd/go/internal/load"
+	"cmd/go/internal/modload"
 	"cmd/go/internal/str"
-	"cmd/go/internal/vgo"
 )
 
 func init() {
@@ -60,9 +60,9 @@
 		}()
 	}
 	for _, pkg := range load.PackagesAndErrors(args) {
-		if vgo.Enabled() && !pkg.Module.Top {
+		if modload.Enabled() && !pkg.Module.Main {
 			if !printed {
-				fmt.Fprintf(os.Stderr, "vgo: not formatting packages in dependency modules\n")
+				fmt.Fprintf(os.Stderr, "go: not formatting packages in dependency modules\n")
 				printed = true
 			}
 			continue
diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go
index 365427d..2afd84f 100644
--- a/src/cmd/go/internal/generate/generate.go
+++ b/src/cmd/go/internal/generate/generate.go
@@ -21,7 +21,7 @@
 	"cmd/go/internal/base"
 	"cmd/go/internal/cfg"
 	"cmd/go/internal/load"
-	"cmd/go/internal/vgo"
+	"cmd/go/internal/modload"
 	"cmd/go/internal/work"
 )
 
@@ -161,9 +161,9 @@
 	// Even if the arguments are .go files, this loop suffices.
 	printed := false
 	for _, pkg := range load.Packages(args) {
-		if vgo.Enabled() && !pkg.Module.Top {
+		if modload.Enabled() && !pkg.Module.Main {
 			if !printed {
-				fmt.Fprintf(os.Stderr, "vgo: not generating in packages in dependency modules\n")
+				fmt.Fprintf(os.Stderr, "go: not generating in packages in dependency modules\n")
 				printed = true
 			}
 			continue
diff --git a/src/cmd/go/internal/get/discovery.go b/src/cmd/go/internal/get/discovery.go
index 97aa1d7..6ba5c09 100644
--- a/src/cmd/go/internal/get/discovery.go
+++ b/src/cmd/go/internal/get/discovery.go
@@ -28,7 +28,7 @@
 
 // parseMetaGoImports returns meta imports from the HTML in r.
 // Parsing ends at the end of the <head> section or the beginning of the <body>.
-func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
+func parseMetaGoImports(r io.Reader, mod ModuleMode) (imports []metaImport, err error) {
 	d := xml.NewDecoder(r)
 	d.CharsetReader = charsetReader
 	d.Strict = false
@@ -39,13 +39,13 @@
 			if err == io.EOF || len(imports) > 0 {
 				err = nil
 			}
-			return
+			break
 		}
 		if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
-			return
+			break
 		}
 		if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
-			return
+			break
 		}
 		e, ok := t.(xml.StartElement)
 		if !ok || !strings.EqualFold(e.Name.Local, "meta") {
@@ -55,13 +55,6 @@
 			continue
 		}
 		if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
-			// Ignore VCS type "mod", which is new Go modules.
-			// This code is for old go get and must ignore the new mod lines.
-			// Otherwise matchGoImport will complain about two
-			// different metaImport lines for the same Prefix.
-			if f[1] == "mod" {
-				continue
-			}
 			imports = append(imports, metaImport{
 				Prefix:   f[0],
 				VCS:      f[1],
@@ -69,6 +62,27 @@
 			})
 		}
 	}
+
+	// Extract mod entries if we are paying attention to them.
+	var list []metaImport
+	var have map[string]bool
+	if mod == PreferMod {
+		have = make(map[string]bool)
+		for _, m := range imports {
+			if m.VCS == "mod" {
+				have[m.Prefix] = true
+				list = append(list, m)
+			}
+		}
+	}
+
+	// Append non-mod entries, ignoring those superseded by a mod entry.
+	for _, m := range imports {
+		if m.VCS != "mod" && !have[m.Prefix] {
+			list = append(list, m)
+		}
+	}
+	return list, nil
 }
 
 // attrValue returns the attribute value for the case-insensitive key
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index 6eabc4e..36aa171 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -18,13 +18,12 @@
 	"cmd/go/internal/load"
 	"cmd/go/internal/search"
 	"cmd/go/internal/str"
-	"cmd/go/internal/vgo"
 	"cmd/go/internal/web"
 	"cmd/go/internal/work"
 )
 
 var CmdGet = &base.Command{
-	UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [-v] [build flags] [packages]",
+	UsageLine: "get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages]",
 	Short:     "download and install packages and dependencies",
 	Long: `
 Get downloads the packages named by the import paths, along with their
@@ -75,25 +74,49 @@
 For more about how 'go get' finds source code to
 download, see 'go help importpath'.
 
+This text describes the behavior of get when using GOPATH
+to manage source code and dependencies.
+If instead the go command is running in module-aware mode,
+the details of get's flags and effects change, as does 'go help get'.
+See 'go help modules' and 'go help module-get'.
+
 See also: go build, go install, go clean.
 	`,
 }
 
-var getD = CmdGet.Flag.Bool("d", false, "")
-var getF = CmdGet.Flag.Bool("f", false, "")
-var getT = CmdGet.Flag.Bool("t", false, "")
-var getU = CmdGet.Flag.Bool("u", false, "")
-var getFix = CmdGet.Flag.Bool("fix", false, "")
-var getInsecure = CmdGet.Flag.Bool("insecure", false, "")
+var HelpGopathGet = &base.Command{
+	UsageLine: "gopath-get",
+	Short:     "legacy GOPATH go get",
+	Long: `
+The 'go get' command changes behavior depending on whether the
+go command is running in module-aware mode or legacy GOPATH mode.
+This help text, accessible as 'go help gopath-get' even in module-aware mode,
+describes 'go get' as it operates in legacy GOPATH mode.
+
+Usage: ` + CmdGet.UsageLine + `
+` + CmdGet.Long,
+}
+
+var (
+	getD   = CmdGet.Flag.Bool("d", false, "")
+	getF   = CmdGet.Flag.Bool("f", false, "")
+	getT   = CmdGet.Flag.Bool("t", false, "")
+	getU   = CmdGet.Flag.Bool("u", false, "")
+	getFix = CmdGet.Flag.Bool("fix", false, "")
+
+	Insecure bool
+)
 
 func init() {
 	work.AddBuildFlags(CmdGet)
 	CmdGet.Run = runGet // break init loop
+	CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "")
 }
 
 func runGet(cmd *base.Command, args []string) {
-	if vgo.Enabled() {
-		base.Fatalf("go get: vgo not implemented")
+	if cfg.ModulesEnabled {
+		// Should not happen: main.go should install the separate module-enabled get code.
+		base.Fatalf("go get: modules not implemented")
 	}
 
 	work.BuildInit()
@@ -167,7 +190,7 @@
 		return
 	}
 
-	work.InstallPackages(args, true)
+	work.InstallPackages(args)
 }
 
 // downloadPaths prepares the list of paths to pass to download.
@@ -176,6 +199,12 @@
 // in the hope that we can figure out the repository from the
 // initial ...-free prefix.
 func downloadPaths(args []string) []string {
+	for _, arg := range args {
+		if strings.Contains(arg, "@") {
+			base.Fatalf("go: cannot use path@version syntax in GOPATH mode")
+		}
+	}
+
 	args = load.ImportPathsForGoGet(args)
 	var out []string
 	for _, a := range args {
@@ -379,7 +408,7 @@
 	)
 
 	security := web.Secure
-	if *getInsecure {
+	if Insecure {
 		security = web.Insecure
 	}
 
@@ -402,16 +431,16 @@
 			}
 			repo = remote
 			if !*getF && err == nil {
-				if rr, err := repoRootForImportPath(p.ImportPath, security); err == nil {
-					repo := rr.repo
+				if rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security); err == nil {
+					repo := rr.Repo
 					if rr.vcs.resolveRepo != nil {
 						resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
 						if err == nil {
 							repo = resolved
 						}
 					}
-					if remote != repo && rr.isCustom {
-						return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote)
+					if remote != repo && rr.IsCustom {
+						return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote)
 					}
 				}
 			}
@@ -419,13 +448,13 @@
 	} else {
 		// Analyze the import path to determine the version control system,
 		// repository, and the import path for the root of the repository.
-		rr, err := repoRootForImportPath(p.ImportPath, security)
+		rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security)
 		if err != nil {
 			return err
 		}
-		vcs, repo, rootPath = rr.vcs, rr.repo, rr.root
+		vcs, repo, rootPath = rr.vcs, rr.Repo, rr.Root
 	}
-	if !blindRepo && !vcs.isSecure(repo) && !*getInsecure {
+	if !blindRepo && !vcs.isSecure(repo) && !Insecure {
 		return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
 	}
 
diff --git a/src/cmd/go/internal/get/pkg_test.go b/src/cmd/go/internal/get/pkg_test.go
index 1179d86..2f61365 100644
--- a/src/cmd/go/internal/get/pkg_test.go
+++ b/src/cmd/go/internal/get/pkg_test.go
@@ -33,15 +33,18 @@
 
 var parseMetaGoImportsTests = []struct {
 	in  string
+	mod ModuleMode
 	out []metaImport
 }{
 	{
 		`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
+		IgnoreMod,
 		[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
 	},
 	{
 		`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
 		<meta name="go-import" content="baz/quux git http://github.com/rsc/baz/quux">`,
+		IgnoreMod,
 		[]metaImport{
 			{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
 			{"baz/quux", "git", "http://github.com/rsc/baz/quux"},
@@ -50,6 +53,7 @@
 	{
 		`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
 		<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`,
+		IgnoreMod,
 		[]metaImport{
 			{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
 		},
@@ -57,35 +61,48 @@
 	{
 		`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
 		<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
+		IgnoreMod,
 		[]metaImport{
 			{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
 		},
 	},
 	{
+		`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
+		<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
+		PreferMod,
+		[]metaImport{
+			{"foo/bar", "mod", "http://github.com/rsc/baz/quux"},
+		},
+	},
+	{
 		`<head>
 		<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
 		</head>`,
+		IgnoreMod,
 		[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
 	},
 	{
 		`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
 		<body>`,
+		IgnoreMod,
 		[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
 	},
 	{
 		`<!doctype html><meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
+		IgnoreMod,
 		[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
 	},
 	{
 		// XML doesn't like <div style=position:relative>.
 		`<!doctype html><title>Page Not Found</title><meta name=go-import content="chitin.io/chitin git https://github.com/chitin-io/chitin"><div style=position:relative>DRAFT</div>`,
+		IgnoreMod,
 		[]metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}},
 	},
 }
 
 func TestParseMetaGoImports(t *testing.T) {
 	for i, tt := range parseMetaGoImportsTests {
-		out, err := parseMetaGoImports(strings.NewReader(tt.in))
+		out, err := parseMetaGoImports(strings.NewReader(tt.in), tt.mod)
 		if err != nil {
 			t.Errorf("test#%d: %v", i, err)
 			continue
diff --git a/src/cmd/go/internal/get/vcs.go b/src/cmd/go/internal/get/vcs.go
index 45fc69a..82392d3 100644
--- a/src/cmd/go/internal/get/vcs.go
+++ b/src/cmd/go/internal/get/vcs.go
@@ -624,27 +624,29 @@
 	return nil
 }
 
-// repoRoot represents a version control system, a repo, and a root of
-// where to put it on disk.
-type repoRoot struct {
-	vcs *vcsCmd
+// RepoRoot describes the repository root for a tree of source code.
+type RepoRoot struct {
+	Repo     string // repository URL, including scheme
+	Root     string // import path corresponding to root of repo
+	IsCustom bool   // defined by served <meta> tags (as opposed to hard-coded pattern)
+	VCS      string // vcs type ("mod", "git", ...)
 
-	// repo is the repository URL, including scheme
-	repo string
-
-	// root is the import path corresponding to the root of the
-	// repository
-	root string
-
-	// isCustom is true for custom import paths (those defined by HTML meta tags)
-	isCustom bool
+	vcs *vcsCmd // internal: vcs command access
 }
 
 var httpPrefixRE = regexp.MustCompile(`^https?:`)
 
-// repoRootForImportPath analyzes importPath to determine the
+// ModuleMode specifies whether to prefer modules when looking up code sources.
+type ModuleMode int
+
+const (
+	IgnoreMod ModuleMode = iota
+	PreferMod
+)
+
+// RepoRootForImportPath analyzes importPath to determine the
 // version control system, and code repository to use.
-func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoRoot, error) {
+func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) {
 	rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths)
 	if err == errUnknownSite {
 		// If there are wildcards, look up the thing before the wildcard,
@@ -654,7 +656,7 @@
 		if i := strings.Index(lookup, "/.../"); i >= 0 {
 			lookup = lookup[:i]
 		}
-		rr, err = repoRootForImportDynamic(lookup, security)
+		rr, err = repoRootForImportDynamic(lookup, mod, security)
 		if err != nil {
 			err = fmt.Errorf("unrecognized import path %q (%v)", importPath, err)
 		}
@@ -667,7 +669,7 @@
 		}
 	}
 
-	if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.root, "...") {
+	if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") {
 		// Do not allow wildcards in the repo root.
 		rr = nil
 		err = fmt.Errorf("cannot expand ... in %q", importPath)
@@ -680,7 +682,7 @@
 // repoRootFromVCSPaths attempts to map importPath to a repoRoot
 // using the mappings defined in vcsPaths.
 // If scheme is non-empty, that scheme is forced.
-func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*repoRoot, error) {
+func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*RepoRoot, error) {
 	// A common error is to use https://packagepath because that's what
 	// hg and git require. Diagnose this helpfully.
 	if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil {
@@ -733,28 +735,32 @@
 					if security == web.Secure && !vcs.isSecureScheme(scheme) {
 						continue
 					}
-					if vcs.ping(scheme, match["repo"]) == nil {
+					if vcs.pingCmd != "" && vcs.ping(scheme, match["repo"]) == nil {
 						match["repo"] = scheme + "://" + match["repo"]
-						break
+						goto Found
 					}
 				}
+				// No scheme found. Fall back to the first one.
+				match["repo"] = vcs.scheme[0] + "://" + match["repo"]
+			Found:
 			}
 		}
-		rr := &repoRoot{
+		rr := &RepoRoot{
+			Repo: match["repo"],
+			Root: match["root"],
+			VCS:  vcs.cmd,
 			vcs:  vcs,
-			repo: match["repo"],
-			root: match["root"],
 		}
 		return rr, nil
 	}
 	return nil, errUnknownSite
 }
 
-// repoRootForImportDynamic finds a *repoRoot for a custom domain that's not
+// repoRootForImportDynamic finds a *RepoRoot for a custom domain that's not
 // statically known by repoRootForImportPathStatic.
 //
 // This handles custom import paths like "name.tld/pkg/foo" or just "name.tld".
-func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*repoRoot, error) {
+func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) {
 	slash := strings.Index(importPath, "/")
 	if slash < 0 {
 		slash = len(importPath)
@@ -772,7 +778,7 @@
 		return nil, fmt.Errorf(msg, err)
 	}
 	defer body.Close()
-	imports, err := parseMetaGoImports(body)
+	imports, err := parseMetaGoImports(body, mod)
 	if err != nil {
 		return nil, fmt.Errorf("parsing %s: %v", importPath, err)
 	}
@@ -799,7 +805,7 @@
 		}
 		urlStr0 := urlStr
 		var imports []metaImport
-		urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, security)
+		urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, mod, security)
 		if err != nil {
 			return nil, err
 		}
@@ -812,15 +818,18 @@
 	if err := validateRepoRoot(mmi.RepoRoot); err != nil {
 		return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, mmi.RepoRoot, err)
 	}
-	rr := &repoRoot{
-		vcs:      vcsByCmd(mmi.VCS),
-		repo:     mmi.RepoRoot,
-		root:     mmi.Prefix,
-		isCustom: true,
-	}
-	if rr.vcs == nil {
+	vcs := vcsByCmd(mmi.VCS)
+	if vcs == nil && mmi.VCS != "mod" {
 		return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, mmi.VCS)
 	}
+
+	rr := &RepoRoot{
+		Repo:     mmi.RepoRoot,
+		Root:     mmi.Prefix,
+		IsCustom: true,
+		VCS:      mmi.VCS,
+		vcs:      vcs,
+	}
 	return rr, nil
 }
 
@@ -851,7 +860,7 @@
 // It is an error if no imports are found.
 // urlStr will still be valid if err != nil.
 // The returned urlStr will be of the form "https://golang.org/x/tools?go-get=1"
-func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlStr string, imports []metaImport, err error) {
+func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.SecurityMode) (urlStr string, imports []metaImport, err error) {
 	setCache := func(res fetchResult) (fetchResult, error) {
 		fetchCacheMu.Lock()
 		defer fetchCacheMu.Unlock()
@@ -871,7 +880,7 @@
 		if err != nil {
 			return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("fetch %s: %v", urlStr, err)})
 		}
-		imports, err := parseMetaGoImports(body)
+		imports, err := parseMetaGoImports(body, mod)
 		if err != nil {
 			return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("parsing %s: %v", urlStr, err)})
 		}
diff --git a/src/cmd/go/internal/get/vcs_test.go b/src/cmd/go/internal/get/vcs_test.go
index 1ce9b73..142701a 100644
--- a/src/cmd/go/internal/get/vcs_test.go
+++ b/src/cmd/go/internal/get/vcs_test.go
@@ -16,43 +16,43 @@
 	"cmd/go/internal/web"
 )
 
-// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
+// Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath.
 // TODO(cmang): Add tests for SVN and BZR.
 func TestRepoRootForImportPath(t *testing.T) {
 	testenv.MustHaveExternalNetwork(t)
 
 	tests := []struct {
 		path string
-		want *repoRoot
+		want *RepoRoot
 	}{
 		{
 			"github.com/golang/groupcache",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://github.com/golang/groupcache",
+				Repo: "https://github.com/golang/groupcache",
 			},
 		},
 		// Unicode letters in directories (issue 18660).
 		{
 			"github.com/user/unicode/испытание",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://github.com/user/unicode",
+				Repo: "https://github.com/user/unicode",
 			},
 		},
 		// IBM DevOps Services tests
 		{
 			"hub.jazz.net/git/user1/pkgname",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://hub.jazz.net/git/user1/pkgname",
+				Repo: "https://hub.jazz.net/git/user1/pkgname",
 			},
 		},
 		{
 			"hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://hub.jazz.net/git/user1/pkgname",
+				Repo: "https://hub.jazz.net/git/user1/pkgname",
 			},
 		},
 		{
@@ -91,9 +91,9 @@
 		},
 		{
 			"hub.jazz.net/git/user/pkg.name",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://hub.jazz.net/git/user/pkg.name",
+				Repo: "https://hub.jazz.net/git/user/pkg.name",
 			},
 		},
 		// User names cannot have uppercase letters
@@ -104,9 +104,9 @@
 		// OpenStack tests
 		{
 			"git.openstack.org/openstack/swift",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://git.openstack.org/openstack/swift",
+				Repo: "https://git.openstack.org/openstack/swift",
 			},
 		},
 		// Trailing .git is less preferred but included for
@@ -114,16 +114,16 @@
 		// be compilable on both old and new go
 		{
 			"git.openstack.org/openstack/swift.git",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://git.openstack.org/openstack/swift.git",
+				Repo: "https://git.openstack.org/openstack/swift.git",
 			},
 		},
 		{
 			"git.openstack.org/openstack/swift/go/hummingbird",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://git.openstack.org/openstack/swift",
+				Repo: "https://git.openstack.org/openstack/swift",
 			},
 		},
 		{
@@ -150,23 +150,23 @@
 		},
 		{
 			"git.apache.org/package-name.git",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://git.apache.org/package-name.git",
+				Repo: "https://git.apache.org/package-name.git",
 			},
 		},
 		{
 			"git.apache.org/package-name_2.x.git/path/to/lib",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsGit,
-				repo: "https://git.apache.org/package-name_2.x.git",
+				Repo: "https://git.apache.org/package-name_2.x.git",
 			},
 		},
 		{
 			"chiselapp.com/user/kyle/repository/fossilgg",
-			&repoRoot{
+			&RepoRoot{
 				vcs:  vcsFossil,
-				repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
+				Repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
 			},
 		},
 		{
@@ -181,21 +181,21 @@
 	}
 
 	for _, test := range tests {
-		got, err := repoRootForImportPath(test.path, web.Secure)
+		got, err := RepoRootForImportPath(test.path, IgnoreMod, web.Secure)
 		want := test.want
 
 		if want == nil {
 			if err == nil {
-				t.Errorf("repoRootForImportPath(%q): Error expected but not received", test.path)
+				t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path)
 			}
 			continue
 		}
 		if err != nil {
-			t.Errorf("repoRootForImportPath(%q): %v", test.path, err)
+			t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
 			continue
 		}
-		if got.vcs.name != want.vcs.name || got.repo != want.repo {
-			t.Errorf("repoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.repo, want.vcs, want.repo)
+		if got.vcs.name != want.vcs.name || got.Repo != want.Repo {
+			t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.Repo, want.vcs, want.Repo)
 		}
 	}
 }
@@ -227,18 +227,18 @@
 			f.Close()
 		}
 
-		want := repoRoot{
+		want := RepoRoot{
 			vcs:  vcs,
-			root: path.Join("example.com", vcs.name),
+			Root: path.Join("example.com", vcs.name),
 		}
-		var got repoRoot
-		got.vcs, got.root, err = vcsFromDir(dir, tempDir)
+		var got RepoRoot
+		got.vcs, got.Root, err = vcsFromDir(dir, tempDir)
 		if err != nil {
 			t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
 			continue
 		}
-		if got.vcs.name != want.vcs.name || got.root != want.root {
-			t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.root, want.vcs, want.root)
+		if got.vcs.name != want.vcs.name || got.Root != want.Root {
+			t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.Root, want.vcs, want.Root)
 		}
 	}
 }
diff --git a/src/cmd/go/internal/help/help.go b/src/cmd/go/internal/help/help.go
index c79bf8b..14cfb6d 100644
--- a/src/cmd/go/internal/help/help.go
+++ b/src/cmd/go/internal/help/help.go
@@ -45,7 +45,15 @@
 		buf := new(bytes.Buffer)
 		PrintUsage(buf)
 		usage := &base.Command{Long: buf.String()}
-		tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*base.Command{usage}, base.Commands...))
+		cmds := []*base.Command{usage}
+		for _, cmd := range base.Commands {
+			if cmd.UsageLine == "gopath-get" {
+				// Avoid duplication of the "get" documentation.
+				continue
+			}
+			cmds = append(cmds, cmd)
+		}
+		tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, cmds)
 		fmt.Println("package main")
 		return
 	}
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index ce19796..a5cfffd 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -30,7 +30,7 @@
 
 var HelpPackages = &base.Command{
 	UsageLine: "packages",
-	Short:     "package lists",
+	Short:     "package lists and patterns",
 	Long: `
 Many commands apply to a set of packages:
 
@@ -54,9 +54,11 @@
 
 - "main" denotes the top-level package in a stand-alone executable.
 
-- "all" expands to all package directories found in all the GOPATH
+- "all" expands to all packages found in all the GOPATH
 trees. For example, 'go list all' lists all the packages on the local
-system.
+system. When using modules, "all" expands to all packages in
+the main module and their dependencies, including dependencies
+needed by tests of any of those.
 
 - "std" is like all but expands to just the packages in the standard
 Go library.
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index 50bb53e..9a5edd4 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -19,14 +19,20 @@
 	"cmd/go/internal/cache"
 	"cmd/go/internal/cfg"
 	"cmd/go/internal/load"
+	"cmd/go/internal/modload"
 	"cmd/go/internal/work"
 )
 
 var CmdList = &base.Command{
-	UsageLine: "list [-cgo] [-deps] [-e] [-export] [-f format] [-json] [-test] [build flags] [packages]",
-	Short:     "list packages",
+	// Note: -f -json -m are listed explicitly because they are the most common list flags.
+	// Do not send CLs removing them because they're covered by [list flags].
+	UsageLine: "list [-f format] [-json] [-m] [list flags] [build flags] [packages]",
+	Short:     "list packages or modules",
 	Long: `
-List lists the packages named by the import paths, one per line.
+List lists the named packages, one per line.
+The most commonly-used flags are -f and -json, which control the form
+of the output printed for each package. Other list flags, documented below,
+control more specific details.
 
 The default output shows the package import path:
 
@@ -36,27 +42,28 @@
     golang.org/x/net/html
 
 The -f flag specifies an alternate format for the list, using the
-syntax of package template. The default output is equivalent to -f
-'{{.ImportPath}}'. The struct being passed to the template is:
+syntax of package template. The default output is equivalent
+to -f '{{.ImportPath}}'. The struct being passed to the template is:
 
     type Package struct {
-        Dir           string // directory containing package sources
-        ImportPath    string // import path of package in dir
-        ImportComment string // path in import comment on package statement
-        Name          string // package name
-        Doc           string // package documentation string
-        Target        string // install path
-        Shlib         string // the shared library that contains this package (only set when -linkshared)
-        Goroot        bool   // is this package in the Go root?
-        Standard      bool   // is this package part of the standard Go library?
-        Stale         bool   // would 'go install' do anything for this package?
-        StaleReason   string // explanation for Stale==true
-        Root          string // Go root or Go path dir containing this package
-        ConflictDir   string // this directory shadows Dir in $GOPATH
-        BinaryOnly    bool   // binary-only package: cannot be recompiled from sources
-        ForTest       string // package is only for use in named test
-        DepOnly       bool   // package is only a dependency, not explicitly listed
-        Export        string // file containing export data (when using -export)
+        Dir           string  // directory containing package sources
+        ImportPath    string  // import path of package in dir
+        ImportComment string  // path in import comment on package statement
+        Name          string  // package name
+        Doc           string  // package documentation string
+        Target        string  // install path
+        Shlib         string  // the shared library that contains this package (only set when -linkshared)
+        Goroot        bool    // is this package in the Go root?
+        Standard      bool    // is this package part of the standard Go library?
+        Stale         bool    // would 'go install' do anything for this package?
+        StaleReason   string  // explanation for Stale==true
+        Root          string  // Go root or Go path dir containing this package
+        ConflictDir   string  // this directory shadows Dir in $GOPATH
+        BinaryOnly    bool    // binary-only package: cannot be recompiled from sources
+        ForTest       string  // package is only for use in named test
+        DepOnly       bool    // package is only a dependency, not explicitly listed
+        Export        string  // file containing export data (when using -export)
+        Module        *Module // info about package's containing module, if any (can be nil)
 
         // Source files
         GoFiles        []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@@ -109,22 +116,25 @@
         Err           string   // the error itself
     }
 
+The module information is a Module struct, defined in the discussion
+of list -m below.
+
 The template function "join" calls strings.Join.
 
 The template function "context" returns the build context, defined as:
 
-	type Context struct {
-		GOARCH        string   // target architecture
-		GOOS          string   // target operating system
-		GOROOT        string   // Go root
-		GOPATH        string   // Go path
-		CgoEnabled    bool     // whether cgo can be used
-		UseAllFiles   bool     // use files regardless of +build lines, file names
-		Compiler      string   // compiler to assume when computing target paths
-		BuildTags     []string // build constraints to match in +build lines
-		ReleaseTags   []string // releases the current release is compatible with
-		InstallSuffix string   // suffix to use in the name of the install dir
-	}
+    type Context struct {
+        GOARCH        string   // target architecture
+        GOOS          string   // target operating system
+        GOROOT        string   // Go root
+        GOPATH        string   // Go path
+        CgoEnabled    bool     // whether cgo can be used
+        UseAllFiles   bool     // use files regardless of +build lines, file names
+        Compiler      string   // compiler to assume when computing target paths
+        BuildTags     []string // build constraints to match in +build lines
+        ReleaseTags   []string // releases the current release is compatible with
+        InstallSuffix string   // suffix to use in the name of the install dir
+    }
 
 For more information about the meaning of these fields see the documentation
 for the go/build package's Context type.
@@ -178,9 +188,90 @@
 referring to cached copies of generated Go source files.
 Although they are Go source files, the paths may not end in ".go".
 
+The -m flag causes list to list modules instead of packages.
+
+When listing modules, the -f flag still specifies a format template
+applied to a Go struct, but now a Module struct:
+
+    type Module struct {
+        Path     string       // module path
+        Version  string       // module version
+        Versions []string     // available module versions (with -versions)
+        Replace  *Module      // replaced by this module
+        Time     *time.Time   // time version was created
+        Update   *Module      // available update, if any (with -u)
+        Main     bool         // is this the main module?
+        Indirect bool         // is this module only an indirect dependency of main module?
+        Dir      string       // directory holding files for this module, if any
+        Error    *ModuleError // error loading module
+    }
+
+    type ModuleError struct {
+        Err string // the error itself
+    }
+
+The default output is to print the module path and then
+information about the version and replacement if any.
+For example, 'go list -m all' might print:
+
+    my/main/module
+    golang.org/x/text v0.3.0 => /tmp/text
+    rsc.io/pdf v0.1.1
+
+The Module struct has a String method that formats this
+line of output, so that the default format is equivalent
+to -f '{{.String}}'.
+
+Note that when a module has been replaced, its Replace field
+describes the replacement module, and its Dir field is set to
+the replacement's source code, if present. (That is, if Replace
+is non-nil, then Dir is set to Replace.Dir, with no access to
+the replaced source code.)
+
+The -u flag adds information about available upgrades.
+When the latest version of a given module is newer than
+the current one, list -u sets the Module's Update field
+to information about the newer module.
+The Module's String method indicates an available upgrade by
+formatting the newer version in brackets after the current version.
+For example, 'go list -m -u all' might print:
+
+    my/main/module
+    golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
+    rsc.io/pdf v0.1.1 [v0.1.2]
+
+(For tools, 'go list -m -u -json all' may be more convenient to parse.)
+
+The -versions flag causes list to set the Module's Versions field
+to a list of all known versions of that module, ordered according
+to semantic versioning, earliest to latest. The flag also changes
+the default output format to display the module path followed by the
+space-separated version list.
+
+The arguments to list -m are interpreted as a list of modules, not packages.
+The main module is the module containing the current directory.
+The active modules are the main module and its dependencies.
+With no arguments, list -m shows the main module.
+With arguments, list -m shows the modules specified by the arguments.
+Any of the active modules can be specified by its module path.
+The special pattern "all" specifies all the active modules, first the main
+module and then dependencies sorted by module path.
+A pattern containing "..." specifies the active modules whose
+module paths match the pattern.
+A query of the form path@version specifies the result of that query,
+which is not limited to active modules.
+See 'go help module' for more about module queries.
+
+The template function "module" takes a single string argument
+that must be a module path or query and returns the specified
+module as a Module struct. If an error occurs, the result will
+be a Module struct with a non-nil Error field.
+
 For more about build flags, see 'go help build'.
 
 For more about specifying packages, see 'go help packages'.
+
+For more about modules, see 'go help modules'.
 	`,
 }
 
@@ -189,13 +280,19 @@
 	work.AddBuildFlags(CmdList)
 }
 
-var listCgo = CmdList.Flag.Bool("cgo", false, "")
-var listDeps = CmdList.Flag.Bool("deps", false, "")
-var listE = CmdList.Flag.Bool("e", false, "")
-var listExport = CmdList.Flag.Bool("export", false, "")
-var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "")
-var listJson = CmdList.Flag.Bool("json", false, "")
-var listTest = CmdList.Flag.Bool("test", false, "")
+var (
+	listCgo      = CmdList.Flag.Bool("cgo", false, "")
+	listDeps     = CmdList.Flag.Bool("deps", false, "")
+	listE        = CmdList.Flag.Bool("e", false, "")
+	listExport   = CmdList.Flag.Bool("export", false, "")
+	listFmt      = CmdList.Flag.String("f", "", "")
+	listJson     = CmdList.Flag.Bool("json", false, "")
+	listM        = CmdList.Flag.Bool("m", false, "")
+	listU        = CmdList.Flag.Bool("u", false, "")
+	listTest     = CmdList.Flag.Bool("test", false, "")
+	listVersions = CmdList.Flag.Bool("versions", false, "")
+)
+
 var nl = []byte{'\n'}
 
 func runList(cmd *base.Command, args []string) {
@@ -203,10 +300,21 @@
 	out := newTrackingWriter(os.Stdout)
 	defer out.w.Flush()
 
-	var do func(*load.PackagePublic)
+	if *listFmt == "" {
+		if *listM {
+			*listFmt = "{{.String}}"
+			if *listVersions {
+				*listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}`
+			}
+		} else {
+			*listFmt = "{{.ImportPath}}"
+		}
+	}
+
+	var do func(interface{})
 	if *listJson {
-		do = func(p *load.PackagePublic) {
-			b, err := json.MarshalIndent(p, "", "\t")
+		do = func(x interface{}) {
+			b, err := json.MarshalIndent(x, "", "\t")
 			if err != nil {
 				out.Flush()
 				base.Fatalf("%s", err)
@@ -225,13 +333,14 @@
 		fm := template.FuncMap{
 			"join":    strings.Join,
 			"context": context,
+			"module":  modload.ModuleInfo,
 		}
 		tmpl, err := template.New("main").Funcs(fm).Parse(*listFmt)
 		if err != nil {
 			base.Fatalf("%s", err)
 		}
-		do = func(p *load.PackagePublic) {
-			if err := tmpl.Execute(out, p); err != nil {
+		do = func(x interface{}) {
+			if err := tmpl.Execute(out, x); err != nil {
 				out.Flush()
 				base.Fatalf("%s", err)
 			}
@@ -241,6 +350,50 @@
 		}
 	}
 
+	if *listM {
+		// Module mode.
+		if *listCgo {
+			base.Fatalf("go list -cgo cannot be used with -m")
+		}
+		if *listDeps {
+			// TODO(rsc): Could make this mean something with -m.
+			base.Fatalf("go list -deps cannot be used with -m")
+		}
+		if *listExport {
+			base.Fatalf("go list -export cannot be used with -m")
+		}
+		if *listTest {
+			base.Fatalf("go list -test cannot be used with -m")
+		}
+
+		if modload.Init(); !modload.Enabled() {
+			base.Fatalf("go list -m: not using modules")
+		}
+		modload.LoadBuildList()
+
+		mods := modload.ListModules(args, *listU, *listVersions)
+		if !*listE {
+			for _, m := range mods {
+				if m.Error != nil {
+					base.Errorf("go list -m %s: %v", m.Path, m.Error.Err)
+				}
+			}
+			base.ExitIfErrors()
+		}
+		for _, m := range mods {
+			do(m)
+		}
+		return
+	}
+
+	// Package mode (not -m).
+	if *listU {
+		base.Fatalf("go list -u can only be used with -m")
+	}
+	if *listVersions {
+		base.Fatalf("go list -versions can only be used with -m")
+	}
+
 	var pkgs []*load.Package
 	if *listE {
 		pkgs = load.PackagesAndErrors(args)
diff --git a/src/cmd/go/internal/load/flag.go b/src/cmd/go/internal/load/flag.go
index 7ad4208..d9177b0 100644
--- a/src/cmd/go/internal/load/flag.go
+++ b/src/cmd/go/internal/load/flag.go
@@ -6,6 +6,7 @@
 
 import (
 	"cmd/go/internal/base"
+	"cmd/go/internal/search"
 	"cmd/go/internal/str"
 	"fmt"
 	"strings"
@@ -92,7 +93,10 @@
 	return flags
 }
 
-var cmdlineMatchers []func(*Package) bool
+var (
+	cmdlineMatchers        []func(*Package) bool
+	cmdlineMatcherLiterals []func(*Package) bool
+)
 
 // SetCmdlinePatterns records the set of patterns given on the command line,
 // for use by the PerPackageFlags.
@@ -105,9 +109,15 @@
 		args = []string{"."}
 	}
 	cmdlineMatchers = nil // allow reset for testing
+	cmdlineMatcherLiterals = nil
 	for _, arg := range args {
 		cmdlineMatchers = append(cmdlineMatchers, MatchPackage(arg, cwd))
 	}
+	for _, arg := range args {
+		if !strings.Contains(arg, "...") && !search.IsMetaPackage(arg) {
+			cmdlineMatcherLiterals = append(cmdlineMatcherLiterals, MatchPackage(arg, cwd))
+		}
+	}
 }
 
 // isCmdlinePkg reports whether p is a package listed on the command line.
@@ -119,3 +129,15 @@
 	}
 	return false
 }
+
+// isCmdlinePkgLiteral reports whether p is a package listed as
+// a literal package argument on the command line
+// (as opposed to being the result of expanding a wildcard).
+func isCmdlinePkgLiteral(p *Package) bool {
+	for _, m := range cmdlineMatcherLiterals {
+		if m(p) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index d369fde..198fef3 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -25,7 +25,17 @@
 	"cmd/go/internal/modinfo"
 	"cmd/go/internal/search"
 	"cmd/go/internal/str"
-	"cmd/go/internal/vgo"
+)
+
+var (
+	// module hooks; nil if module use is disabled
+	ModBinDir            func() string                                                   // return effective bin directory
+	ModLookup            func(parentPath, path string) (dir, realPath string, err error) // lookup effective meaning of import
+	ModPackageModuleInfo func(path string) *modinfo.ModulePublic                         // return module info for Package struct
+	ModImportPaths       func(args []string) []string                                    // expand import paths
+	ModPackageBuildInfo  func(main string, deps []string) string                         // return module info to embed in binary
+	ModInfoProg          func(info string) []byte                                        // wrap module info in .go code for binary
+	ModImportFromFiles   func([]string)                                                  // update go.mod to add modules for imports in these files
 )
 
 var IgnoreImports bool // control whether we ignore imports in packages
@@ -40,21 +50,22 @@
 	// Note: These fields are part of the go command's public API.
 	// See list.go. It is okay to add fields, but not to change or
 	// remove existing ones. Keep in sync with list.go
-	Dir           string `json:",omitempty"` // directory containing package sources
-	ImportPath    string `json:",omitempty"` // import path of package in dir
-	ImportComment string `json:",omitempty"` // path in import comment on package statement
-	Name          string `json:",omitempty"` // package name
-	Doc           string `json:",omitempty"` // package documentation string
-	Target        string `json:",omitempty"` // installed target for this package (may be executable)
-	Shlib         string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared)
-	Goroot        bool   `json:",omitempty"` // is this package found in the Go root?
-	Standard      bool   `json:",omitempty"` // is this package part of the standard Go library?
-	Root          string `json:",omitempty"` // Go root or Go path dir containing this package
-	ConflictDir   string `json:",omitempty"` // Dir is hidden by this other directory
-	BinaryOnly    bool   `json:",omitempty"` // package cannot be recompiled
-	ForTest       string `json:",omitempty"` // package is only for use in named test
-	DepOnly       bool   `json:",omitempty"` // package is only as a dependency, not explicitly listed
-	Export        string `json:",omitempty"` // file containing export data (set by go list -export)
+	Dir           string                `json:",omitempty"` // directory containing package sources
+	ImportPath    string                `json:",omitempty"` // import path of package in dir
+	ImportComment string                `json:",omitempty"` // path in import comment on package statement
+	Name          string                `json:",omitempty"` // package name
+	Doc           string                `json:",omitempty"` // package documentation string
+	Target        string                `json:",omitempty"` // installed target for this package (may be executable)
+	Shlib         string                `json:",omitempty"` // the shared library that contains this package (only set when -linkshared)
+	Goroot        bool                  `json:",omitempty"` // is this package found in the Go root?
+	Standard      bool                  `json:",omitempty"` // is this package part of the standard Go library?
+	Root          string                `json:",omitempty"` // Go root or Go path dir containing this package
+	ConflictDir   string                `json:",omitempty"` // Dir is hidden by this other directory
+	BinaryOnly    bool                  `json:",omitempty"` // package cannot be recompiled
+	ForTest       string                `json:",omitempty"` // package is only for use in named test
+	DepOnly       bool                  `json:",omitempty"` // package is only as a dependency, not explicitly listed
+	Export        string                `json:",omitempty"` // file containing export data (set by go list -export)
+	Module        *modinfo.ModulePublic `json:",omitempty"` // info about package's module, if any
 
 	// Stale and StaleReason remain here *only* for the list command.
 	// They are only initialized in preparation for list execution.
@@ -103,8 +114,6 @@
 	TestImports  []string `json:",omitempty"` // imports from TestGoFiles
 	XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
 	XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
-
-	Module *modinfo.ModulePublic `json:",omitempty"` // info about package module
 }
 
 // AllFiles returns the names of all the files considered for the package.
@@ -141,21 +150,22 @@
 
 type PackageInternal struct {
 	// Unexported fields are not part of the public API.
-	Build        *build.Package
-	Imports      []*Package           // this package's direct imports
-	RawImports   []string             // this package's original imports as they appear in the text of the program
-	ForceLibrary bool                 // this package is a library (even if named "main")
-	CmdlineFiles bool                 // package built from files listed on command line
-	CmdlinePkg   bool                 // package listed on command line
-	Local        bool                 // imported via local path (./ or ../)
-	LocalPrefix  string               // interpret ./ and ../ imports relative to this prefix
-	ExeName      string               // desired name for temporary executable
-	CoverMode    string               // preprocess Go source files with the coverage tool in this mode
-	CoverVars    map[string]*CoverVar // variables created by coverage analysis
-	OmitDebug    bool                 // tell linker not to write debug information
-	GobinSubdir  bool                 // install target would be subdir of GOBIN
-	BuildInfo    string               // add this info to package main
-	TestmainGo   *[]byte              // content for _testmain.go
+	Build             *build.Package
+	Imports           []*Package           // this package's direct imports
+	RawImports        []string             // this package's original imports as they appear in the text of the program
+	ForceLibrary      bool                 // this package is a library (even if named "main")
+	CmdlineFiles      bool                 // package built from files listed on command line
+	CmdlinePkg        bool                 // package listed on command line
+	CmdlinePkgLiteral bool                 // package listed as literal on command line (not via wildcard)
+	Local             bool                 // imported via local path (./ or ../)
+	LocalPrefix       string               // interpret ./ and ../ imports relative to this prefix
+	ExeName           string               // desired name for temporary executable
+	CoverMode         string               // preprocess Go source files with the coverage tool in this mode
+	CoverVars         map[string]*CoverVar // variables created by coverage analysis
+	OmitDebug         bool                 // tell linker not to write debug information
+	GobinSubdir       bool                 // install target would be subdir of GOBIN
+	BuildInfo         string               // add this info to package main
+	TestmainGo        *[]byte              // content for _testmain.go
 
 	Asmflags   []string // -asmflags for this package
 	Gcflags    []string // -gcflags for this package
@@ -416,6 +426,41 @@
 	stk.Push(path)
 	defer stk.Pop()
 
+	if strings.HasPrefix(path, "mod/") {
+		// Paths beginning with "mod/" might accidentally
+		// look in the module cache directory tree in $GOPATH/src/mod/.
+		// This prefix is owned by the Go core for possible use in the
+		// standard library (since it does not begin with a domain name),
+		// so it's OK to disallow entirely.
+		return &Package{
+			PackagePublic: PackagePublic{
+				ImportPath: path,
+				Error: &PackageError{
+					ImportStack: stk.Copy(),
+					Err:         fmt.Sprintf("disallowed import path %q", path),
+				},
+			},
+		}
+	}
+
+	if strings.Contains(path, "@") {
+		var text string
+		if cfg.ModulesEnabled {
+			text = "can only use path@version syntax with 'go get'"
+		} else {
+			text = "cannot use path@version syntax in GOPATH mode"
+		}
+		return &Package{
+			PackagePublic: PackagePublic{
+				ImportPath: path,
+				Error: &PackageError{
+					ImportStack: stk.Copy(),
+					Err:         text,
+				},
+			},
+		}
+	}
+
 	// Determine canonical identifier for this package.
 	// For a local import the identifier is the pseudo-import path
 	// we create from the full directory to the package.
@@ -424,18 +469,18 @@
 	importPath := path
 	origPath := path
 	isLocal := build.IsLocalImport(path)
-	var vgoDir string
-	var vgoErr error
+	var modDir string
+	var modErr error
 	if isLocal {
 		importPath = dirToImportPath(filepath.Join(srcDir, path))
-	} else if vgo.Enabled() {
+	} else if cfg.ModulesEnabled {
 		parentPath := ""
 		if parent != nil {
 			parentPath = parent.ImportPath
 		}
 		var p string
-		vgoDir, p, vgoErr = vgo.Lookup(parentPath, path)
-		if vgoErr == nil {
+		modDir, p, modErr = ModLookup(parentPath, path)
+		if modErr == nil {
 			importPath = p
 		}
 	} else if mode&ResolveImport != 0 {
@@ -464,11 +509,14 @@
 		// in order to return partial information.
 		var bp *build.Package
 		var err error
-		if vgoDir != "" {
-			bp, err = cfg.BuildContext.ImportDir(vgoDir, 0)
-		} else if vgoErr != nil {
+		if modDir != "" {
+			bp, err = cfg.BuildContext.ImportDir(modDir, 0)
+		} else if modErr != nil {
 			bp = new(build.Package)
-			err = fmt.Errorf("unknown import path %q: %v", importPath, vgoErr)
+			err = fmt.Errorf("unknown import path %q: %v", importPath, modErr)
+		} else if cfg.ModulesEnabled && path != "unsafe" {
+			bp = new(build.Package)
+			err = fmt.Errorf("unknown import path %q: internal error: module loader did not resolve import", importPath)
 		} else {
 			buildMode := build.ImportComment
 			if mode&ResolveImport == 0 || path != origPath {
@@ -480,10 +528,10 @@
 		bp.ImportPath = importPath
 		if cfg.GOBIN != "" {
 			bp.BinDir = cfg.GOBIN
-		} else if vgo.Enabled() {
-			bp.BinDir = vgo.BinDir()
+		} else if cfg.ModulesEnabled {
+			bp.BinDir = ModBinDir()
 		}
-		if vgoDir == "" && err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
+		if modDir == "" && err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path &&
 			!strings.Contains(path, "/vendor/") && !strings.HasPrefix(path, "vendor/") {
 			err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
 		}
@@ -492,7 +540,7 @@
 			p = setErrorPos(p, importPos)
 		}
 
-		if vgoDir == "" && origPath != cleanImport(origPath) {
+		if modDir == "" && origPath != cleanImport(origPath) {
 			p.Error = &PackageError{
 				ImportStack: stk.Copy(),
 				Err:         fmt.Sprintf("non-canonical import path: %q should be %q", origPath, pathpkg.Clean(origPath)),
@@ -568,14 +616,14 @@
 // There are two different resolutions applied.
 // First, there is Go 1.5 vendoring (golang.org/s/go15vendor).
 // If vendor expansion doesn't trigger, then the path is also subject to
-// Go 1.11 vgo legacy conversion (golang.org/issue/25069).
+// Go 1.11 module legacy conversion (golang.org/issue/25069).
 func ResolveImportPath(parent *Package, path string) (found string) {
-	if vgo.Enabled() {
+	if cfg.ModulesEnabled {
 		parentPath := ""
 		if parent != nil {
 			parentPath = parent.ImportPath
 		}
-		if _, p, e := vgo.Lookup(parentPath, path); e == nil {
+		if _, p, e := ModLookup(parentPath, path); e == nil {
 			return p
 		}
 		return path
@@ -1108,8 +1156,9 @@
 	// of fmt before it attempts to load as a command-line argument.
 	// Because loads are cached, the later load will be a no-op,
 	// so it is important that the first load can fill in CmdlinePkg correctly.
-	// Hence the call to an explicit matching check here.
+	// Hence the call to a separate matching check here.
 	p.Internal.CmdlinePkg = isCmdlinePkg(p)
+	p.Internal.CmdlinePkgLiteral = isCmdlinePkgLiteral(p)
 
 	p.Internal.Asmflags = BuildAsmflags.For(p)
 	p.Internal.Gcflags = BuildGcflags.For(p)
@@ -1157,8 +1206,8 @@
 			// Install cross-compiled binaries to subdirectories of bin.
 			elem = full
 		}
-		if p.Internal.Build.BinDir == "" && vgo.Enabled() {
-			p.Internal.Build.BinDir = vgo.BinDir()
+		if p.Internal.Build.BinDir == "" && cfg.ModulesEnabled {
+			p.Internal.Build.BinDir = ModBinDir()
 		}
 		if p.Internal.Build.BinDir != "" {
 			// Install to GOBIN or bin of GOPATH entry.
@@ -1411,10 +1460,10 @@
 		return
 	}
 
-	if vgo.Enabled() {
-		p.Module = vgo.PackageModuleInfo(p.ImportPath)
+	if cfg.ModulesEnabled {
+		p.Module = ModPackageModuleInfo(p.ImportPath)
 		if p.Name == "main" {
-			p.Internal.BuildInfo = vgo.PackageBuildInfo(p.ImportPath, p.Deps)
+			p.Internal.BuildInfo = ModPackageBuildInfo(p.ImportPath, p.Deps)
 		}
 	}
 }
@@ -1695,7 +1744,10 @@
 	if cmdlineMatchers == nil {
 		SetCmdlinePatterns(search.CleanImportPaths(args))
 	}
-	return vgo.ImportPaths(args)
+	if cfg.ModulesEnabled {
+		return ModImportPaths(args)
+	}
+	return search.ImportPaths(args)
 }
 
 func ImportPathsForGoGet(args []string) []string {
@@ -1790,7 +1842,9 @@
 	}
 	ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil }
 
-	vgo.AddImports(gofiles)
+	if cfg.ModulesEnabled {
+		ModImportFromFiles(gofiles)
+	}
 
 	var err error
 	if dir == "" {
@@ -1820,8 +1874,8 @@
 		}
 		if cfg.GOBIN != "" {
 			pkg.Target = filepath.Join(cfg.GOBIN, exe)
-		} else if vgo.Enabled() {
-			pkg.Target = filepath.Join(vgo.BinDir(), exe)
+		} else if cfg.ModulesEnabled {
+			pkg.Target = filepath.Join(ModBinDir(), exe)
 		}
 	}
 
diff --git a/src/cmd/go/internal/load/search.go b/src/cmd/go/internal/load/search.go
index 4943398..d379c7b 100644
--- a/src/cmd/go/internal/load/search.go
+++ b/src/cmd/go/internal/load/search.go
@@ -15,7 +15,7 @@
 // MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd.
 func MatchPackage(pattern, cwd string) func(*Package) bool {
 	switch {
-	case strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == "..":
+	case search.IsRelativePath(pattern):
 		// Split pattern into leading pattern-free directory path
 		// (including all . and .. elements) and the final pattern.
 		var dir string
diff --git a/src/cmd/go/internal/modcmd/mod.go b/src/cmd/go/internal/modcmd/mod.go
new file mode 100644
index 0000000..d8ae1d9
--- /dev/null
+++ b/src/cmd/go/internal/modcmd/mod.go
@@ -0,0 +1,532 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package modcmd implements the ``go mod'' command.
+package modcmd
+
+import (
+	"bufio"
+	"encoding/json"
+	"fmt"
+	"os"
+	"sort"
+	"strings"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/modfile"
+	"cmd/go/internal/modload"
+	"cmd/go/internal/module"
+	"cmd/go/internal/par"
+)
+
+var CmdMod = &base.Command{
+	UsageLine: "mod [-v] [maintenance flags]",
+	Short:     "module maintenance",
+	Long: `
+Mod performs module maintenance operations as specified by the
+following flags, which may be combined.
+
+The -v flag enables additional output about operations performed.
+
+The first group of operations provide low-level editing operations
+for manipulating go.mod from the command line or in scripts or
+other tools. They read only go.mod itself; they do not look up any
+information about the modules involved.
+
+The -init flag initializes and writes a new go.mod to the current directory,
+in effect creating a new module rooted at the current directory.
+The file go.mod must not already exist.
+If possible, mod will guess the module path from import comments
+(see 'go help importpath') or from version control configuration.
+To override this guess, use the -module flag.
+(Without -init, mod applies to the current module.)
+
+The -module flag changes (or, with -init, sets) the module's path
+(the go.mod file's module line).
+
+The -require=path@version and -droprequire=path flags
+add and drop a requirement on the given module path and version.
+Note that -require overrides any existing requirements on path.
+These flags are mainly for tools that understand the module graph.
+Users should prefer 'go get path@version' or 'go get path@none',
+which make other go.mod adjustments as needed to satisfy
+constraints imposed by other modules.
+
+The -exclude=path@version and -dropexclude=path@version flags
+add and drop an exclusion for the given module path and version.
+Note that -exclude=path@version is a no-op if that exclusion already exists.
+
+The -replace=old@v=new@w and -dropreplace=old@v flags
+add and drop a replacement of the given module path and version pair.
+If the @v in old@v is omitted, the replacement applies to all versions
+with the old module path. If the @v in new@v is omitted, the
+new path should be a directory on the local system, not a module path.
+Note that -replace overrides any existing replacements for old@v.
+
+These editing flags (-require, -droprequire, -exclude, -dropexclude,
+-replace, and -dropreplace) may be repeated.
+
+The -fmt flag reformats the go.mod file without making other changes.
+This reformatting is also implied by any other modifications that use or
+rewrite the go.mod file. The only time this flag is needed is if no other
+flags are specified, as in 'go mod -fmt'.
+
+The -graph flag prints the module requirement graph (with replacements applied)
+in text form. Each line in the output has two space-separated fields: a module
+and one of its requirements. Each module is identified as a string of the form
+path@version, except for the main module, which has no @version suffix.
+
+The -json flag prints the go.mod file in JSON format corresponding to these
+Go types:
+
+	type Module struct {
+		Path string
+		Version string
+	}
+
+	type GoMod struct {
+		Module Module
+		Require []Require
+		Exclude []Module
+		Replace []Replace
+	}
+	
+	type Require struct {
+		Path string
+		Version string
+		Indirect bool
+	}
+	
+	type Replace string {
+		Old Module
+		New Module
+	}
+
+Note that this only describes the go.mod file itself, not other modules
+referred to indirectly. For the full set of modules available to a build,
+use 'go list -m -json all'.
+
+The next group of operations provide higher-level editing and maintenance
+of a module, beyond the go.mod file.
+
+The -packages flag prints a list of packages in the module.
+It only identifies directories containing Go source code;
+it does not check that those directories contain code that builds.
+
+The -fix flag updates go.mod to use canonical version identifiers and
+to be semantically consistent. For example, consider this go.mod file:
+
+	module M
+
+	require (
+		A v1
+		B v1.0.0
+		C v1.0.0
+		D v1.2.3
+		E dev
+	)
+
+	exclude D v1.2.3
+
+First, -fix rewrites non-canonical version identifiers to semver form, so
+A's v1 becomes v1.0.0 and E's dev becomes the pseudo-version for the latest
+commit on the dev branch, perhaps v0.0.0-20180523231146-b3f5c0f6e5f1.
+
+Next, -fix updates requirements to respect exclusions, so the requirement
+on the excluded D v1.2.3 is updated to use the next available version of D,
+perhaps D v1.2.4 or D v1.3.0.
+
+Finally, -fix removes redundant or misleading requirements.
+For example, if A v1.0.0 itself requires B v1.2.0 and C v1.0.0,
+then go.mod's requirement of B v1.0.0 is misleading (superseded
+by B's need for v1.2.0), and its requirement of C v1.0.0 is redundant
+(implied by B's need for the same version), so both will be removed.
+
+Although -fix runs the fix-up operation in isolation, the fix-up also
+runs automatically any time a go command uses the module graph,
+to update go.mod to reflect reality. For example, the -sync, -vendor,
+and -verify flags all effectively imply -fix. And because the module
+graph defines the meaning of import statements, any commands
+that load packages—'go build', 'go test', 'go list', and so on—also
+effectively imply 'go mod -fix'.
+
+The -sync flag synchronizes go.mod with the source code in the module.
+It adds any missing modules necessary to build the current module's
+packages and dependencies, and it removes unused modules that
+don't provide any relevant packages.
+
+The -vendor flag resets the module's vendor directory to include all
+packages needed to build and test all the module's packages.
+It does not include any test code for the vendored packages.
+
+The -verify flag checks that the dependencies of the current module,
+which are stored in a local downloaded source cache, have not been
+modified since being downloaded. If all the modules are unmodified,
+-verify prints "all modules verified." Otherwise it reports which
+modules have been changed and causes 'go mod' to exit with a
+non-zero status.
+	`,
+}
+
+var (
+	modV = CmdMod.Flag.Bool("v", false, "")
+
+	modFmt      = CmdMod.Flag.Bool("fmt", false, "")
+	modFix      = CmdMod.Flag.Bool("fix", false, "")
+	modGraph    = CmdMod.Flag.Bool("graph", false, "")
+	modJSON     = CmdMod.Flag.Bool("json", false, "")
+	modPackages = CmdMod.Flag.Bool("packages", false, "")
+	modSync     = CmdMod.Flag.Bool("sync", false, "")
+	modVendor   = CmdMod.Flag.Bool("vendor", false, "")
+	modVerify   = CmdMod.Flag.Bool("verify", false, "")
+
+	modEdits []func(*modfile.File) // edits specified in flags
+)
+
+type flagFunc func(string)
+
+func (f flagFunc) String() string     { return "" }
+func (f flagFunc) Set(s string) error { f(s); return nil }
+
+func init() {
+	CmdMod.Run = runMod // break init cycle
+
+	CmdMod.Flag.BoolVar(&modload.CmdModInit, "init", modload.CmdModInit, "")
+	CmdMod.Flag.StringVar(&modload.CmdModModule, "module", modload.CmdModModule, "")
+
+	CmdMod.Flag.Var(flagFunc(flagRequire), "require", "")
+	CmdMod.Flag.Var(flagFunc(flagDropRequire), "droprequire", "")
+	CmdMod.Flag.Var(flagFunc(flagExclude), "exclude", "")
+	CmdMod.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
+	CmdMod.Flag.Var(flagFunc(flagReplace), "replace", "")
+	CmdMod.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
+
+	base.AddBuildFlagsNX(&CmdMod.Flag)
+}
+
+func runMod(cmd *base.Command, args []string) {
+	if modload.Init(); !modload.Enabled() {
+		base.Fatalf("go mod: cannot use outside module")
+	}
+	if len(args) != 0 {
+		base.Fatalf("go mod: mod takes no arguments")
+	}
+
+	anyFlags :=
+		modload.CmdModInit ||
+			modload.CmdModModule != "" ||
+			*modVendor ||
+			*modVerify ||
+			*modJSON ||
+			*modFmt ||
+			*modFix ||
+			*modGraph ||
+			*modPackages ||
+			*modSync ||
+			len(modEdits) > 0
+
+	if !anyFlags {
+		base.Fatalf("go mod: no flags specified (see 'go help mod').")
+	}
+
+	if modload.CmdModModule != "" {
+		if err := module.CheckPath(modload.CmdModModule); err != nil {
+			base.Fatalf("go mod: invalid -module: %v", err)
+		}
+	}
+
+	if modload.CmdModInit {
+		if _, err := os.Stat("go.mod"); err == nil {
+			base.Fatalf("go mod -init: go.mod already exists")
+		}
+	}
+	modload.InitMod()
+
+	// Syntactic edits.
+
+	modFile := modload.ModFile()
+	if modload.CmdModModule != "" {
+		modFile.AddModuleStmt(modload.CmdModModule)
+	}
+
+	if len(modEdits) > 0 {
+		for _, edit := range modEdits {
+			edit(modFile)
+		}
+	}
+	modFile.SortBlocks()
+	modload.WriteGoMod() // write back syntactic changes
+
+	// Semantic edits.
+
+	needBuildList := *modFix || *modGraph
+
+	if *modSync || *modVendor || needBuildList {
+		var pkgs []string
+		if *modSync || *modVendor {
+			pkgs = modload.LoadALL()
+		} else {
+			modload.LoadBuildList()
+		}
+		if *modSync {
+			// LoadALL already added missing modules.
+			// Remove unused modules.
+			used := map[module.Version]bool{modload.Target: true}
+			for _, pkg := range pkgs {
+				used[modload.PackageModule(pkg)] = true
+			}
+
+			inGoMod := make(map[string]bool)
+			for _, r := range modload.ModFile().Require {
+				inGoMod[r.Mod.Path] = true
+			}
+
+			var keep []module.Version
+			for _, m := range modload.BuildList() {
+				if used[m] {
+					keep = append(keep, m)
+				} else if *modV && inGoMod[m.Path] {
+					fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
+				}
+			}
+			modload.SetBuildList(keep)
+		}
+		modload.WriteGoMod()
+		if *modVendor {
+			runVendor()
+		}
+	}
+
+	// Read-only queries, processed only after updating go.mod.
+
+	if *modJSON {
+		modPrintJSON()
+	}
+
+	if *modGraph {
+		modPrintGraph()
+	}
+
+	if *modPackages {
+		for _, pkg := range modload.TargetPackages() {
+			fmt.Printf("%s\n", pkg)
+		}
+	}
+
+	if *modVerify {
+		runVerify()
+	}
+}
+
+// parsePathVersion parses -flag=arg expecting arg to be path@version.
+func parsePathVersion(flag, arg string) (path, version string) {
+	i := strings.Index(arg, "@")
+	if i < 0 {
+		base.Fatalf("go mod: -%s=%s: need path@version", flag, arg)
+	}
+	path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
+	if err := module.CheckPath(path); err != nil {
+		base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
+	}
+
+	// We don't call modfile.CheckPathVersion, because that insists
+	// on versions being in semver form, but here we want to allow
+	// versions like "master" or "1234abcdef", which the go command will resolve
+	// the next time it runs (or during -fix).
+	// Even so, we need to make sure the version is a valid token.
+	if modfile.MustQuote(version) {
+		base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version)
+	}
+
+	return path, version
+}
+
+// parsePath parses -flag=arg expecting arg to be path (not path@version).
+func parsePath(flag, arg string) (path string) {
+	if strings.Contains(arg, "@") {
+		base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg)
+	}
+	path = arg
+	if err := module.CheckPath(path); err != nil {
+		base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
+	}
+	return path
+}
+
+// flagRequire implements the -require flag.
+func flagRequire(arg string) {
+	path, version := parsePathVersion("require", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.AddRequire(path, version); err != nil {
+			base.Fatalf("go mod: -require=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagDropRequire implements the -droprequire flag.
+func flagDropRequire(arg string) {
+	path := parsePath("droprequire", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.DropRequire(path); err != nil {
+			base.Fatalf("go mod: -droprequire=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagExclude implements the -exclude flag.
+func flagExclude(arg string) {
+	path, version := parsePathVersion("exclude", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.AddExclude(path, version); err != nil {
+			base.Fatalf("go mod: -exclude=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagDropExclude implements the -dropexclude flag.
+func flagDropExclude(arg string) {
+	path, version := parsePathVersion("dropexclude", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.DropExclude(path, version); err != nil {
+			base.Fatalf("go mod: -dropexclude=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagReplace implements the -replace flag.
+func flagReplace(arg string) {
+	var i int
+	if i = strings.Index(arg, "="); i < 0 {
+		base.Fatalf("go mod: -replace=%s: need old@v=new[@v] (missing =)", arg)
+	}
+	old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+2:])
+	if strings.HasPrefix(new, ">") {
+		base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg)
+	}
+	var oldPath, oldVersion string
+	if i = strings.Index(old, "@"); i < 0 {
+		oldPath = old
+	} else {
+		oldPath, oldVersion = strings.TrimSpace(old[:i]), strings.TrimSpace(old[i+1:])
+	}
+	if err := module.CheckPath(oldPath); err != nil {
+		base.Fatalf("go mod: -replace=%s: invalid old path: %v", arg, err)
+	}
+	if oldPath != old && modfile.MustQuote(oldVersion) {
+		base.Fatalf("go mod: -replace=%s: invalid old version %q", arg, oldVersion)
+	}
+	var newPath, newVersion string
+	if i = strings.Index(new, "@"); i >= 0 {
+		newPath, newVersion = strings.TrimSpace(new[:i]), strings.TrimSpace(new[i+1:])
+		if err := module.CheckPath(newPath); err != nil {
+			base.Fatalf("go mod: -replace=%s: invalid new path: %v", arg, err)
+		}
+		if modfile.MustQuote(newVersion) {
+			base.Fatalf("go mod: -replace=%s: invalid new version %q", arg, newVersion)
+		}
+	} else {
+		if !modfile.IsDirectoryPath(new) {
+			base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg)
+		}
+		newPath = new
+	}
+
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
+			base.Fatalf("go mod: -replace=%s: %v", arg, err)
+		}
+	})
+}
+
+// flagDropReplace implements the -dropreplace flag.
+func flagDropReplace(arg string) {
+	path, version := parsePathVersion("dropreplace", arg)
+	modEdits = append(modEdits, func(f *modfile.File) {
+		if err := f.DropReplace(path, version); err != nil {
+			base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
+		}
+	})
+}
+
+// fileJSON is the -json output data structure.
+type fileJSON struct {
+	Module  module.Version
+	Require []requireJSON
+	Exclude []module.Version
+	Replace []replaceJSON
+}
+
+type requireJSON struct {
+	Path     string
+	Version  string `json:",omitempty"`
+	Indirect bool   `json:",omitempty"`
+}
+
+type replaceJSON struct {
+	Old module.Version
+	New module.Version
+}
+
+// modPrintJSON prints the -json output.
+func modPrintJSON() {
+	modFile := modload.ModFile()
+
+	var f fileJSON
+	f.Module = modFile.Module.Mod
+	for _, r := range modFile.Require {
+		f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect})
+	}
+	for _, x := range modFile.Exclude {
+		f.Exclude = append(f.Exclude, x.Mod)
+	}
+	for _, r := range modFile.Replace {
+		f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
+	}
+	data, err := json.MarshalIndent(&f, "", "\t")
+	if err != nil {
+		base.Fatalf("go mod -json: internal error: %v", err)
+	}
+	data = append(data, '\n')
+	os.Stdout.Write(data)
+}
+
+// modPrintGraph prints the -graph output.
+func modPrintGraph() {
+	reqs := modload.Reqs()
+
+	format := func(m module.Version) string {
+		if m.Version == "" {
+			return m.Path
+		}
+		return m.Path + "@" + m.Version
+	}
+
+	// Note: using par.Work only to manage work queue.
+	// No parallelism here, so no locking.
+	var out []string
+	var deps int // index in out where deps start
+	var work par.Work
+	work.Add(modload.Target)
+	work.Do(1, func(item interface{}) {
+		m := item.(module.Version)
+		list, _ := reqs.Required(m)
+		for _, r := range list {
+			work.Add(r)
+			out = append(out, format(m)+" "+format(r)+"\n")
+		}
+		if m == modload.Target {
+			deps = len(out)
+		}
+	})
+
+	sort.Slice(out[deps:], func(i, j int) bool {
+		return out[deps+i][0] < out[deps+j][0]
+	})
+
+	w := bufio.NewWriter(os.Stdout)
+	for _, line := range out {
+		w.WriteString(line)
+	}
+	w.Flush()
+}
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
new file mode 100644
index 0000000..078c44f
--- /dev/null
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -0,0 +1,178 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modcmd
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/modload"
+	"cmd/go/internal/module"
+)
+
+func runVendor() {
+	pkgs := modload.LoadVendor()
+
+	vdir := filepath.Join(modload.ModRoot, "vendor")
+	if err := os.RemoveAll(vdir); err != nil {
+		base.Fatalf("go vendor: %v", err)
+	}
+
+	modpkgs := make(map[module.Version][]string)
+	for _, pkg := range pkgs {
+		m := modload.PackageModule(pkg)
+		if m == modload.Target {
+			continue
+		}
+		modpkgs[m] = append(modpkgs[m], pkg)
+	}
+
+	var buf bytes.Buffer
+	for _, m := range modload.BuildList()[1:] {
+		if pkgs := modpkgs[m]; len(pkgs) > 0 {
+			repl := ""
+			if r := modload.Replacement(m); r.Path != "" {
+				repl = " => " + r.Path
+				if r.Version != "" {
+					repl += " " + r.Version
+				}
+			}
+			fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl)
+			if *modV {
+				fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl)
+			}
+			for _, pkg := range pkgs {
+				fmt.Fprintf(&buf, "%s\n", pkg)
+				if *modV {
+					fmt.Fprintf(os.Stderr, "%s\n", pkg)
+				}
+				vendorPkg(vdir, pkg)
+			}
+		}
+	}
+	if buf.Len() == 0 {
+		fmt.Fprintf(os.Stderr, "go: no dependencies to vendor\n")
+		return
+	}
+	if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
+		base.Fatalf("go vendor: %v", err)
+	}
+}
+
+func vendorPkg(vdir, pkg string) {
+	realPath := modload.ImportMap(pkg)
+	if realPath != pkg && modload.ImportMap(realPath) != "" {
+		fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg)
+	}
+
+	dst := filepath.Join(vdir, pkg)
+	src := modload.PackageDir(realPath)
+	if src == "" {
+		fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
+	}
+	copyDir(dst, src, matchNonTest)
+	if m := modload.PackageModule(realPath); m.Path != "" {
+		copyMetadata(m.Path, realPath, dst, src)
+	}
+}
+
+type metakey struct {
+	modPath string
+	dst     string
+}
+
+var copiedMetadata = make(map[metakey]bool)
+
+// copyMetadata copies metadata files from parents of src to parents of dst,
+// stopping after processing the src parent for modPath.
+func copyMetadata(modPath, pkg, dst, src string) {
+	for parent := 0; ; parent++ {
+		if copiedMetadata[metakey{modPath, dst}] {
+			break
+		}
+		copiedMetadata[metakey{modPath, dst}] = true
+		if parent > 0 {
+			copyDir(dst, src, matchMetadata)
+		}
+		if modPath == pkg {
+			break
+		}
+		pkg = filepath.Dir(pkg)
+		dst = filepath.Dir(dst)
+		src = filepath.Dir(src)
+	}
+}
+
+// metaPrefixes is the list of metadata file prefixes.
+// Vendoring copies metadata files from parents of copied directories.
+// Note that this list could be arbitrarily extended, and it is longer
+// in other tools (such as godep or dep). By using this limited set of
+// prefixes and also insisting on capitalized file names, we are trying
+// to nudge people toward more agreement on the naming
+// and also trying to avoid false positives.
+var metaPrefixes = []string{
+	"AUTHORS",
+	"CONTRIBUTORS",
+	"COPYLEFT",
+	"COPYING",
+	"COPYRIGHT",
+	"LEGAL",
+	"LICENSE",
+	"NOTICE",
+	"PATENTS",
+}
+
+// matchMetadata reports whether info is a metadata file.
+func matchMetadata(info os.FileInfo) bool {
+	name := info.Name()
+	for _, p := range metaPrefixes {
+		if strings.HasPrefix(name, p) {
+			return true
+		}
+	}
+	return false
+}
+
+// matchNonTest reports whether info is any non-test file (including non-Go files).
+func matchNonTest(info os.FileInfo) bool {
+	return !strings.HasSuffix(info.Name(), "_test.go")
+}
+
+// copyDir copies all regular files satisfying match(info) from src to dst.
+func copyDir(dst, src string, match func(os.FileInfo) bool) {
+	files, err := ioutil.ReadDir(src)
+	if err != nil {
+		base.Fatalf("go vendor: %v", err)
+	}
+	if err := os.MkdirAll(dst, 0777); err != nil {
+		base.Fatalf("go vendor: %v", err)
+	}
+	for _, file := range files {
+		if file.IsDir() || !file.Mode().IsRegular() || !match(file) {
+			continue
+		}
+		r, err := os.Open(filepath.Join(src, file.Name()))
+		if err != nil {
+			base.Fatalf("go vendor: %v", err)
+		}
+		w, err := os.Create(filepath.Join(dst, file.Name()))
+		if err != nil {
+			base.Fatalf("go vendor: %v", err)
+		}
+		if _, err := io.Copy(w, r); err != nil {
+			base.Fatalf("go vendor: %v", err)
+		}
+		r.Close()
+		if err := w.Close(); err != nil {
+			base.Fatalf("go vendor: %v", err)
+		}
+	}
+}
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
new file mode 100644
index 0000000..27cd9ed
--- /dev/null
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -0,0 +1,75 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modcmd
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/dirhash"
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modload"
+	"cmd/go/internal/module"
+)
+
+func runVerify() {
+	ok := true
+	for _, mod := range modload.LoadBuildList()[1:] {
+		ok = verifyMod(mod) && ok
+	}
+	if ok {
+		fmt.Printf("all modules verified\n")
+	}
+}
+
+func verifyMod(mod module.Version) bool {
+	ok := true
+	zip := filepath.Join(modfetch.SrcMod, "cache/download", mod.Path, "/@v/", mod.Version+".zip")
+	_, zipErr := os.Stat(zip)
+	dir := filepath.Join(modfetch.SrcMod, mod.Path+"@"+mod.Version)
+	_, dirErr := os.Stat(dir)
+	data, err := ioutil.ReadFile(zip + "hash")
+	if err != nil {
+		if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
+			// Nothing downloaded yet. Nothing to verify.
+			return true
+		}
+		base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)
+		return false
+	}
+	h := string(bytes.TrimSpace(data))
+
+	if zipErr != nil && os.IsNotExist(zipErr) {
+		// ok
+	} else {
+		hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
+		if err != nil {
+			base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
+			return false
+		} else if hZ != h {
+			base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)
+			ok = false
+		}
+	}
+	if dirErr != nil && os.IsNotExist(dirErr) {
+		// ok
+	} else {
+		hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
+		if err != nil {
+
+			base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
+			return false
+		}
+		if hD != h {
+			base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)
+			ok = false
+		}
+	}
+	return ok
+}
diff --git a/src/cmd/go/internal/modconv/convert.go b/src/cmd/go/internal/modconv/convert.go
new file mode 100644
index 0000000..c2a7813
--- /dev/null
+++ b/src/cmd/go/internal/modconv/convert.go
@@ -0,0 +1,87 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modconv
+
+import (
+	"fmt"
+	"os"
+	"sort"
+	"strings"
+	"sync"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modfile"
+	"cmd/go/internal/module"
+	"cmd/go/internal/par"
+	"cmd/go/internal/semver"
+)
+
+// ConvertLegacyConfig converts legacy config to modfile.
+// The file argument is slash-delimited.
+func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error {
+	i := strings.LastIndex(file, "/")
+	j := -2
+	if i >= 0 {
+		j = strings.LastIndex(file[:i], "/")
+	}
+	convert := Converters[file[i+1:]]
+	if convert == nil && j != -2 {
+		convert = Converters[file[j+1:]]
+	}
+	if convert == nil {
+		return fmt.Errorf("unknown legacy config file %s", file)
+	}
+	mf, err := convert(file, data)
+	if err != nil {
+		return fmt.Errorf("parsing %s: %v", file, err)
+	}
+
+	// Convert requirements block, which may use raw SHA1 hashes as versions,
+	// to valid semver requirement list, respecting major versions.
+	var work par.Work
+	for _, r := range mf.Require {
+		m := r.Mod
+		if m.Path == "" {
+			continue
+		}
+		work.Add(r.Mod)
+	}
+
+	var (
+		mu   sync.Mutex
+		need = make(map[string]string)
+	)
+	work.Do(10, func(item interface{}) {
+		r := item.(module.Version)
+		repo, info, err := modfetch.ImportRepoRev(r.Path, r.Version)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "go: converting %s: stat %s@%s: %v\n", base.ShortPath(file), r.Path, r.Version, err)
+			return
+		}
+		mu.Lock()
+		path := repo.ModulePath()
+		need[path] = semver.Max(need[path], info.Version)
+		mu.Unlock()
+	})
+
+	var paths []string
+	for path := range need {
+		paths = append(paths, path)
+	}
+	sort.Strings(paths)
+	for _, path := range paths {
+		f.AddNewRequire(path, need[path], false)
+	}
+
+	for _, r := range mf.Replace {
+		err := f.AddReplace(r.Old.Path, r.Old.Version, r.New.Path, r.New.Version)
+		if err != nil {
+			return fmt.Errorf("add replace: %v", err)
+		}
+	}
+	f.Cleanup()
+	return nil
+}
diff --git a/src/cmd/go/internal/modfetch/convert_test.go b/src/cmd/go/internal/modconv/convert_test.go
similarity index 75%
rename from src/cmd/go/internal/modfetch/convert_test.go
rename to src/cmd/go/internal/modconv/convert_test.go
index 03ae156..811bbb1 100644
--- a/src/cmd/go/internal/modfetch/convert_test.go
+++ b/src/cmd/go/internal/modconv/convert_test.go
@@ -2,19 +2,49 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package modfetch
+package modconv
 
 import (
 	"bytes"
+	"fmt"
 	"internal/testenv"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
 	"strings"
 	"testing"
 
 	"cmd/go/internal/cfg"
-	"cmd/go/internal/modconv"
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modfetch/codehost"
 	"cmd/go/internal/modfile"
+	"cmd/go/internal/module"
 )
 
+func TestMain(m *testing.M) {
+	os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+	if _, err := exec.LookPath("git"); err != nil {
+		fmt.Fprintln(os.Stderr, "skipping because git binary not found")
+		fmt.Println("PASS")
+		return 0
+	}
+
+	dir, err := ioutil.TempDir("", "modconv-test-")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+	modfetch.SrcMod = filepath.Join(dir, "src/mod")
+	codehost.WorkRoot = filepath.Join(dir, "codework")
+
+	return m.Run()
+}
+
 func TestConvertLegacyConfig(t *testing.T) {
 	testenv.MustHaveExternalNetwork(t)
 
@@ -65,7 +95,7 @@
 			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/Sirupsen/logrus v0.7.3
 				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
@@ -117,23 +147,32 @@
 			if err != nil {
 				t.Fatal(err)
 			}
-			repo, err := Lookup(tt.path)
+
+			dir, err := modfetch.Download(module.Version{Path: tt.path, Version: tt.vers})
 			if err != nil {
 				t.Fatal(err)
 			}
 
-			out, err := repo.GoMod(tt.vers)
-			if err != nil {
-				t.Fatal(err)
+			for name := range Converters {
+				file := filepath.Join(dir, name)
+				data, err := ioutil.ReadFile(file)
+				if err == nil {
+					f := new(modfile.File)
+					f.AddModuleStmt(tt.path)
+					if err := ConvertLegacyConfig(f, filepath.ToSlash(file), data); err != nil {
+						t.Fatal(err)
+					}
+					out, err := f.Format()
+					if err != nil {
+						t.Fatalf("format after conversion: %v", err)
+					}
+					if !bytes.Equal(out, want) {
+						t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
+					}
+					return
+				}
 			}
-			prefix := modconv.Prefix + "\n"
-			if !bytes.HasPrefix(out, []byte(prefix)) {
-				t.Fatalf("go.mod missing prefix %q:\n%s", prefix, out)
-			}
-			out = out[len(prefix):]
-			if !bytes.Equal(out, want) {
-				t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
-			}
+			t.Fatalf("no converter found for %s@%s", tt.path, tt.vers)
 		})
 	}
 }
diff --git a/src/cmd/go/internal/modconv/dep.go b/src/cmd/go/internal/modconv/dep.go
index 28dd28a..690c206 100644
--- a/src/cmd/go/internal/modconv/dep.go
+++ b/src/cmd/go/internal/modconv/dep.go
@@ -9,11 +9,13 @@
 	"strconv"
 	"strings"
 
+	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
 	"cmd/go/internal/semver"
 )
 
-func ParseGopkgLock(file string, data []byte) ([]module.Version, error) {
+func ParseGopkgLock(file string, data []byte) (*modfile.File, error) {
+	mf := new(modfile.File)
 	var list []module.Version
 	var r *module.Version
 	for lineno, line := range strings.Split(string(data), "\n") {
@@ -66,6 +68,7 @@
 		if r.Path == "" || r.Version == "" {
 			return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path)
 		}
+		mf.Require = append(mf.Require, &modfile.Require{Mod: r})
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/glide.go b/src/cmd/go/internal/modconv/glide.go
index abe88c4..3bc675f 100644
--- a/src/cmd/go/internal/modconv/glide.go
+++ b/src/cmd/go/internal/modconv/glide.go
@@ -5,12 +5,14 @@
 package modconv
 
 import (
-	"cmd/go/internal/module"
 	"strings"
+
+	"cmd/go/internal/modfile"
+	"cmd/go/internal/module"
 )
 
-func ParseGlideLock(file string, data []byte) ([]module.Version, error) {
-	var list []module.Version
+func ParseGlideLock(file string, data []byte) (*modfile.File, error) {
+	mf := new(modfile.File)
 	imports := false
 	name := ""
 	for lineno, line := range strings.Split(string(data), "\n") {
@@ -32,9 +34,9 @@
 		if strings.HasPrefix(line, "  version:") {
 			version := strings.TrimSpace(line[len("  version:"):])
 			if name != "" && version != "" {
-				list = append(list, module.Version{Path: name, Version: version})
+				mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: name, Version: version}})
 			}
 		}
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/glock.go b/src/cmd/go/internal/modconv/glock.go
index 57eb66e..1b786a9 100644
--- a/src/cmd/go/internal/modconv/glock.go
+++ b/src/cmd/go/internal/modconv/glock.go
@@ -7,17 +7,18 @@
 import (
 	"strings"
 
+	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
 )
 
-func ParseGLOCKFILE(file string, data []byte) ([]module.Version, error) {
-	var list []module.Version
+func ParseGLOCKFILE(file string, data []byte) (*modfile.File, error) {
+	mf := new(modfile.File)
 	for lineno, line := range strings.Split(string(data), "\n") {
 		lineno++
 		f := strings.Fields(line)
 		if len(f) >= 2 && f[0] != "cmd" {
-			list = append(list, module.Version{Path: f[0], Version: f[1]})
+			mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: f[0], Version: f[1]}})
 		}
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/godeps.go b/src/cmd/go/internal/modconv/godeps.go
index 904fd70..6398dbe 100644
--- a/src/cmd/go/internal/modconv/godeps.go
+++ b/src/cmd/go/internal/modconv/godeps.go
@@ -7,10 +7,11 @@
 import (
 	"encoding/json"
 
+	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
 )
 
-func ParseGodepsJSON(file string, data []byte) ([]module.Version, error) {
+func ParseGodepsJSON(file string, data []byte) (*modfile.File, error) {
 	var cfg struct {
 		ImportPath string
 		Deps       []struct {
@@ -21,9 +22,9 @@
 	if err := json.Unmarshal(data, &cfg); err != nil {
 		return nil, err
 	}
-	var list []module.Version
+	mf := new(modfile.File)
 	for _, d := range cfg.Deps {
-		list = append(list, module.Version{Path: d.ImportPath, Version: d.Rev})
+		mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: d.ImportPath, Version: d.Rev}})
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/modconv.go b/src/cmd/go/internal/modconv/modconv.go
index b689b52..a586733 100644
--- a/src/cmd/go/internal/modconv/modconv.go
+++ b/src/cmd/go/internal/modconv/modconv.go
@@ -4,9 +4,9 @@
 
 package modconv
 
-import "cmd/go/internal/module"
+import "cmd/go/internal/modfile"
 
-var Converters = map[string]func(string, []byte) ([]module.Version, error){
+var Converters = map[string]func(string, []byte) (*modfile.File, error){
 	"GLOCKFILE":          ParseGLOCKFILE,
 	"Godeps/Godeps.json": ParseGodepsJSON,
 	"Gopkg.lock":         ParseGopkgLock,
@@ -17,9 +17,3 @@
 	"vendor/manifest":    ParseVendorManifest,
 	"vendor/vendor.json": ParseVendorJSON,
 }
-
-// Prefix is a line we write at the top of auto-converted go.mod files
-// for dependencies before caching them.
-// In case of bugs in the converter, if we bump this version number,
-// then all the cached copies will be ignored.
-const Prefix = "//vgo 0.0.4\n"
diff --git a/src/cmd/go/internal/modconv/modconv_test.go b/src/cmd/go/internal/modconv/modconv_test.go
index 04a1db3..353161b 100644
--- a/src/cmd/go/internal/modconv/modconv_test.go
+++ b/src/cmd/go/internal/modconv/modconv_test.go
@@ -7,7 +7,6 @@
 import (
 	"bytes"
 	"fmt"
-	"internal/testenv"
 	"io/ioutil"
 	"path/filepath"
 	"testing"
@@ -26,8 +25,6 @@
 }
 
 func Test(t *testing.T) {
-	testenv.MustHaveExternalNetwork(t)
-
 	tests, _ := filepath.Glob("testdata/*")
 	if len(tests) == 0 {
 		t.Fatalf("no tests found")
@@ -58,8 +55,8 @@
 				t.Error(err)
 			}
 			var buf bytes.Buffer
-			for _, r := range out {
-				fmt.Fprintf(&buf, "%s %s\n", r.Path, r.Version)
+			for _, r := range out.Require {
+				fmt.Fprintf(&buf, "%s %s\n", r.Mod.Path, r.Mod.Version)
 			}
 			if !bytes.Equal(buf.Bytes(), want) {
 				t.Errorf("have:\n%s\nwant:\n%s", buf.Bytes(), want)
diff --git a/src/cmd/go/internal/modconv/tsv.go b/src/cmd/go/internal/modconv/tsv.go
index fd33649..feba181 100644
--- a/src/cmd/go/internal/modconv/tsv.go
+++ b/src/cmd/go/internal/modconv/tsv.go
@@ -7,17 +7,18 @@
 import (
 	"strings"
 
+	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
 )
 
-func ParseDependenciesTSV(file string, data []byte) ([]module.Version, error) {
-	var list []module.Version
+func ParseDependenciesTSV(file string, data []byte) (*modfile.File, error) {
+	mf := new(modfile.File)
 	for lineno, line := range strings.Split(string(data), "\n") {
 		lineno++
 		f := strings.Split(line, "\t")
 		if len(f) >= 3 {
-			list = append(list, module.Version{Path: f[0], Version: f[2]})
+			mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: f[0], Version: f[2]}})
 		}
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/vconf.go b/src/cmd/go/internal/modconv/vconf.go
index 5d3cd3c..a9a8e62 100644
--- a/src/cmd/go/internal/modconv/vconf.go
+++ b/src/cmd/go/internal/modconv/vconf.go
@@ -7,11 +7,12 @@
 import (
 	"strings"
 
+	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
 )
 
-func ParseVendorConf(file string, data []byte) ([]module.Version, error) {
-	var list []module.Version
+func ParseVendorConf(file string, data []byte) (*modfile.File, error) {
+	mf := new(modfile.File)
 	for lineno, line := range strings.Split(string(data), "\n") {
 		lineno++
 		if i := strings.Index(line, "#"); i >= 0 {
@@ -19,8 +20,8 @@
 		}
 		f := strings.Fields(line)
 		if len(f) >= 2 {
-			list = append(list, module.Version{Path: f[0], Version: f[1]})
+			mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: f[0], Version: f[1]}})
 		}
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/vjson.go b/src/cmd/go/internal/modconv/vjson.go
index 38b0a68..eec86b7 100644
--- a/src/cmd/go/internal/modconv/vjson.go
+++ b/src/cmd/go/internal/modconv/vjson.go
@@ -7,10 +7,11 @@
 import (
 	"encoding/json"
 
+	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
 )
 
-func ParseVendorJSON(file string, data []byte) ([]module.Version, error) {
+func ParseVendorJSON(file string, data []byte) (*modfile.File, error) {
 	var cfg struct {
 		Package []struct {
 			Path     string
@@ -20,9 +21,9 @@
 	if err := json.Unmarshal(data, &cfg); err != nil {
 		return nil, err
 	}
-	var list []module.Version
+	mf := new(modfile.File)
 	for _, d := range cfg.Package {
-		list = append(list, module.Version{Path: d.Path, Version: d.Revision})
+		mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: d.Path, Version: d.Revision}})
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/vmanifest.go b/src/cmd/go/internal/modconv/vmanifest.go
index f2cf0f5..c0ef2a9 100644
--- a/src/cmd/go/internal/modconv/vmanifest.go
+++ b/src/cmd/go/internal/modconv/vmanifest.go
@@ -7,10 +7,11 @@
 import (
 	"encoding/json"
 
+	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
 )
 
-func ParseVendorManifest(file string, data []byte) ([]module.Version, error) {
+func ParseVendorManifest(file string, data []byte) (*modfile.File, error) {
 	var cfg struct {
 		Dependencies []struct {
 			ImportPath string
@@ -20,9 +21,9 @@
 	if err := json.Unmarshal(data, &cfg); err != nil {
 		return nil, err
 	}
-	var list []module.Version
+	mf := new(modfile.File)
 	for _, d := range cfg.Dependencies {
-		list = append(list, module.Version{Path: d.ImportPath, Version: d.Revision})
+		mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: d.ImportPath, Version: d.Revision}})
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modconv/vyml.go b/src/cmd/go/internal/modconv/vyml.go
index e2ea9e3..0f017a3 100644
--- a/src/cmd/go/internal/modconv/vyml.go
+++ b/src/cmd/go/internal/modconv/vyml.go
@@ -5,12 +5,14 @@
 package modconv
 
 import (
-	"cmd/go/internal/module"
 	"strings"
+
+	"cmd/go/internal/modfile"
+	"cmd/go/internal/module"
 )
 
-func ParseVendorYML(file string, data []byte) ([]module.Version, error) {
-	var list []module.Version
+func ParseVendorYML(file string, data []byte) (*modfile.File, error) {
+	mf := new(modfile.File)
 	vendors := false
 	path := ""
 	for lineno, line := range strings.Split(string(data), "\n") {
@@ -32,9 +34,9 @@
 		if strings.HasPrefix(line, "  rev:") {
 			rev := strings.TrimSpace(line[len("  rev:"):])
 			if path != "" && rev != "" {
-				list = append(list, module.Version{Path: path, Version: rev})
+				mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: path, Version: rev}})
 			}
 		}
 	}
-	return list, nil
+	return mf, nil
 }
diff --git a/src/cmd/go/internal/modfetch/bitbucket/fetch.go b/src/cmd/go/internal/modfetch/bitbucket/fetch.go
deleted file mode 100644
index c077a3e..0000000
--- a/src/cmd/go/internal/modfetch/bitbucket/fetch.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package bitbucket
-
-import (
-	"fmt"
-	"strings"
-
-	"cmd/go/internal/modfetch/codehost"
-	"cmd/go/internal/modfetch/gitrepo"
-)
-
-func Lookup(path string) (codehost.Repo, error) {
-	f := strings.Split(path, "/")
-	if len(f) < 3 || f[0] != "bitbucket.org" {
-		return nil, fmt.Errorf("bitbucket repo must be bitbucket.org/org/project")
-	}
-	path = f[0] + "/" + f[1] + "/" + f[2]
-	return gitrepo.Repo("https://"+path, path)
-}
diff --git a/src/cmd/go/internal/modfetch/cache.go b/src/cmd/go/internal/modfetch/cache.go
new file mode 100644
index 0000000..587b2a6
--- /dev/null
+++ b/src/cmd/go/internal/modfetch/cache.go
@@ -0,0 +1,409 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modfetch
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/modfetch/codehost"
+	"cmd/go/internal/par"
+	"cmd/go/internal/semver"
+)
+
+var QuietLookup bool // do not print about lookups
+
+var SrcMod string // $GOPATH/src/mod; set by package modload
+
+// A cachingRepo is a cache around an underlying Repo,
+// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not Zip).
+// It is also safe for simultaneous use by multiple goroutines
+// (so that it can be returned from Lookup multiple times).
+// It serializes calls to the underlying Repo.
+type cachingRepo struct {
+	path  string
+	cache par.Cache // cache for all operations
+	r     Repo
+}
+
+func newCachingRepo(r Repo) *cachingRepo {
+	return &cachingRepo{
+		r:    r,
+		path: r.ModulePath(),
+	}
+}
+
+func (r *cachingRepo) ModulePath() string {
+	return r.path
+}
+
+func (r *cachingRepo) Versions(prefix string) ([]string, error) {
+	type cached struct {
+		list []string
+		err  error
+	}
+	c := r.cache.Do("versions:"+prefix, func() interface{} {
+		list, err := r.r.Versions(prefix)
+		return cached{list, err}
+	}).(cached)
+
+	if c.err != nil {
+		return nil, c.err
+	}
+	return append([]string(nil), c.list...), nil
+}
+
+type cachedInfo struct {
+	info *RevInfo
+	err  error
+}
+
+func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
+	c := r.cache.Do("stat:"+rev, func() interface{} {
+		file, info, err := readDiskStat(r.path, rev)
+		if err == nil {
+			return cachedInfo{info, nil}
+		}
+
+		if !QuietLookup {
+			fmt.Fprintf(os.Stderr, "go: finding %s %s\n", r.path, rev)
+		}
+		info, err = r.r.Stat(rev)
+		if err == nil {
+			if err := writeDiskStat(file, info); err != nil {
+				fmt.Fprintf(os.Stderr, "go: writing stat cache: %v\n", err)
+			}
+			// If we resolved, say, 1234abcde to v0.0.0-20180604122334-1234abcdef78,
+			// then save the information under the proper version, for future use.
+			if info.Version != rev {
+				r.cache.Do("stat:"+info.Version, func() interface{} {
+					return cachedInfo{info, err}
+				})
+			}
+		}
+		return cachedInfo{info, err}
+	}).(cachedInfo)
+
+	if c.err != nil {
+		return nil, c.err
+	}
+	info := *c.info
+	return &info, nil
+}
+
+func (r *cachingRepo) Latest() (*RevInfo, error) {
+	c := r.cache.Do("latest:", func() interface{} {
+		if !QuietLookup {
+			fmt.Fprintf(os.Stderr, "go: finding %s latest\n", r.path)
+		}
+		info, err := r.r.Latest()
+
+		// Save info for likely future Stat call.
+		if err == nil {
+			r.cache.Do("stat:"+info.Version, func() interface{} {
+				return cachedInfo{info, err}
+			})
+			if file, _, err := readDiskStat(r.path, info.Version); err != nil {
+				writeDiskStat(file, info)
+			}
+		}
+
+		return cachedInfo{info, err}
+	}).(cachedInfo)
+
+	if c.err != nil {
+		return nil, c.err
+	}
+	info := *c.info
+	return &info, nil
+}
+
+func (r *cachingRepo) GoMod(rev string) ([]byte, error) {
+	type cached struct {
+		text []byte
+		err  error
+	}
+	c := r.cache.Do("gomod:"+rev, func() interface{} {
+		file, text, err := readDiskGoMod(r.path, rev)
+		if err == nil {
+			// Note: readDiskGoMod already called checkGoMod.
+			return cached{text, nil}
+		}
+
+		// Convert rev to canonical version
+		// so that we use the right identifier in the go.sum check.
+		info, err := r.Stat(rev)
+		if err != nil {
+			return cached{nil, err}
+		}
+		rev = info.Version
+
+		text, err = r.r.GoMod(rev)
+		if err == nil {
+			checkGoMod(r.path, rev, text)
+			if err := writeDiskGoMod(file, text); err != nil {
+				fmt.Fprintf(os.Stderr, "go: writing go.mod cache: %v\n", err)
+			}
+		}
+		return cached{text, err}
+	}).(cached)
+
+	if c.err != nil {
+		return nil, c.err
+	}
+	return append([]byte(nil), c.text...), nil
+}
+
+func (r *cachingRepo) Zip(version, tmpdir string) (string, error) {
+	return r.r.Zip(version, tmpdir)
+}
+
+// Stat is like Lookup(path).Stat(rev) but avoids the
+// repository path resolution in Lookup if the result is
+// already cached on local disk.
+func Stat(path, rev string) (*RevInfo, error) {
+	_, info, err := readDiskStat(path, rev)
+	if err == nil {
+		return info, nil
+	}
+	repo, err := Lookup(path)
+	if err != nil {
+		return nil, err
+	}
+	return repo.Stat(rev)
+}
+
+// GoMod is like Lookup(path).GoMod(rev) but avoids the
+// repository path resolution in Lookup if the result is
+// already cached on local disk.
+func GoMod(path, rev string) ([]byte, error) {
+	// Convert commit hash to pseudo-version
+	// to increase cache hit rate.
+	if !semver.IsValid(rev) {
+		info, err := Stat(path, rev)
+		if err != nil {
+			return nil, err
+		}
+		rev = info.Version
+	}
+	_, data, err := readDiskGoMod(path, rev)
+	if err == nil {
+		return data, nil
+	}
+	repo, err := Lookup(path)
+	if err != nil {
+		return nil, err
+	}
+	return repo.GoMod(rev)
+}
+
+var errNotCached = fmt.Errorf("not in cache")
+
+// readDiskStat reads a cached stat result from disk,
+// returning the name of the cache file and the result.
+// If the read fails, the caller can use
+// writeDiskStat(file, info) to write a new cache entry.
+func readDiskStat(path, rev string) (file string, info *RevInfo, err error) {
+	file, data, err := readDiskCache(path, rev, "info")
+	if err != nil {
+		if file, info, err := readDiskStatByHash(path, rev); err == nil {
+			return file, info, nil
+		}
+		return file, nil, err
+	}
+	info = new(RevInfo)
+	if err := json.Unmarshal(data, info); err != nil {
+		return file, nil, errNotCached
+	}
+	return file, info, nil
+}
+
+// readDiskStatByHash is a fallback for readDiskStat for the case
+// where rev is a commit hash instead of a proper semantic version.
+// In that case, we look for a cached pseudo-version that matches
+// the commit hash. If we find one, we use it.
+// This matters most for converting legacy package management
+// configs, when we are often looking up commits by full hash.
+// Without this check we'd be doing network I/O to the remote repo
+// just to find out about a commit we already know about
+// (and have cached under its pseudo-version).
+func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error) {
+	if SrcMod == "" {
+		// Do not download to current directory.
+		return "", nil, errNotCached
+	}
+
+	if !codehost.AllHex(rev) || len(rev) < 12 {
+		return "", nil, errNotCached
+	}
+	rev = rev[:12]
+	dir, err := os.Open(filepath.Join(SrcMod, "cache/download", path, "@v"))
+	if err != nil {
+		return "", nil, errNotCached
+	}
+	names, err := dir.Readdirnames(-1)
+	dir.Close()
+	if err != nil {
+		return "", nil, errNotCached
+	}
+	suffix := "-" + rev + ".info"
+	for _, name := range names {
+		if strings.HasSuffix(name, suffix) && IsPseudoVersion(strings.TrimSuffix(name, ".info")) {
+			return readDiskStat(path, strings.TrimSuffix(name, ".info"))
+		}
+	}
+	return "", nil, errNotCached
+}
+
+// oldVgoPrefix is the prefix in the old auto-generated cached go.mod files.
+// We stopped trying to auto-generate the go.mod files. Now we use a trivial
+// go.mod with only a module line, and we've dropped the version prefix
+// entirely. If we see a version prefix, that means we're looking at an old copy
+// and should ignore it.
+var oldVgoPrefix = []byte("//vgo 0.0.")
+
+// readDiskGoMod reads a cached stat result from disk,
+// returning the name of the cache file and the result.
+// If the read fails, the caller can use
+// writeDiskGoMod(file, data) to write a new cache entry.
+func readDiskGoMod(path, rev string) (file string, data []byte, err error) {
+	file, data, err = readDiskCache(path, rev, "mod")
+
+	// If the file has an old auto-conversion prefix, pretend it's not there.
+	if bytes.HasPrefix(data, oldVgoPrefix) {
+		err = errNotCached
+		data = nil
+	}
+
+	if err == nil {
+		checkGoMod(path, rev, data)
+	}
+
+	return file, data, err
+}
+
+// readDiskCache is the generic "read from a cache file" implementation.
+// It takes the revision and an identifying suffix for the kind of data being cached.
+// It returns the name of the cache file and the content of the file.
+// If the read fails, the caller can use
+// writeDiskCache(file, data) to write a new cache entry.
+func readDiskCache(path, rev, suffix string) (file string, data []byte, err error) {
+	if !semver.IsValid(rev) || SrcMod == "" {
+		return "", nil, errNotCached
+	}
+	file = filepath.Join(SrcMod, "cache/download", path, "@v", rev+"."+suffix)
+	data, err = ioutil.ReadFile(file)
+	if err != nil {
+		return file, nil, errNotCached
+	}
+	return file, data, nil
+}
+
+// writeDiskStat writes a stat result cache entry.
+// The file name must have been returned by a previous call to readDiskStat.
+func writeDiskStat(file string, info *RevInfo) error {
+	if file == "" {
+		return nil
+	}
+	js, err := json.Marshal(info)
+	if err != nil {
+		return err
+	}
+	return writeDiskCache(file, js)
+}
+
+// writeDiskGoMod writes a go.mod cache entry.
+// The file name must have been returned by a previous call to readDiskGoMod.
+func writeDiskGoMod(file string, text []byte) error {
+	return writeDiskCache(file, text)
+}
+
+// writeDiskCache is the generic "write to a cache file" implementation.
+// The file must have been returned by a previous call to readDiskCache.
+func writeDiskCache(file string, data []byte) error {
+	if file == "" {
+		return nil
+	}
+	// Make sure directory for file exists.
+	if err := os.MkdirAll(filepath.Dir(file), 0777); err != nil {
+		return err
+	}
+	// Write data to temp file next to target file.
+	f, err := ioutil.TempFile(filepath.Dir(file), filepath.Base(file)+".tmp-")
+	if err != nil {
+		return err
+	}
+	defer os.Remove(f.Name())
+	defer f.Close()
+	if _, err := f.Write(data); err != nil {
+		return err
+	}
+	if err := f.Close(); err != nil {
+		return err
+	}
+	// Rename temp file onto cache file,
+	// so that the cache file is always a complete file.
+	if err := os.Rename(f.Name(), file); err != nil {
+		return err
+	}
+
+	if strings.HasSuffix(file, ".mod") {
+		rewriteVersionList(filepath.Dir(file))
+	}
+	return nil
+}
+
+// rewriteVersionList rewrites the version list in dir
+// after a new *.mod file has been written.
+func rewriteVersionList(dir string) {
+	if filepath.Base(dir) != "@v" {
+		base.Fatalf("go: internal error: misuse of rewriteVersionList")
+	}
+
+	// TODO(rsc): We should do some kind of directory locking here,
+	// to avoid lost updates.
+
+	infos, err := ioutil.ReadDir(dir)
+	if err != nil {
+		return
+	}
+	var list []string
+	for _, info := range infos {
+		// We look for *.mod files on the theory that if we can't supply
+		// the .mod file then there's no point in listing that version,
+		// since it's unusable. (We can have *.info without *.mod.)
+		// We don't require *.zip files on the theory that for code only
+		// involved in module graph construction, many *.zip files
+		// will never be requested.
+		name := info.Name()
+		if strings.HasSuffix(name, ".mod") {
+			v := strings.TrimSuffix(name, ".mod")
+			if semver.IsValid(v) && semver.Canonical(v) == v {
+				list = append(list, v)
+			}
+		}
+	}
+	SortVersions(list)
+
+	var buf bytes.Buffer
+	for _, v := range list {
+		buf.WriteString(v)
+		buf.WriteString("\n")
+	}
+	listFile := filepath.Join(dir, "list")
+	old, _ := ioutil.ReadFile(listFile)
+	if bytes.Equal(buf.Bytes(), old) {
+		return
+	}
+	// TODO: Use rename to install file,
+	// so that readers never see an incomplete file.
+	ioutil.WriteFile(listFile, buf.Bytes(), 0666)
+}
diff --git a/src/cmd/go/internal/modfetch/cache_test.go b/src/cmd/go/internal/modfetch/cache_test.go
new file mode 100644
index 0000000..241c800
--- /dev/null
+++ b/src/cmd/go/internal/modfetch/cache_test.go
@@ -0,0 +1,25 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modfetch
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+func TestWriteDiskCache(t *testing.T) {
+	tmpdir, err := ioutil.TempDir("", "go-writeCache-test-")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmpdir)
+
+	err = writeDiskCache(filepath.Join(tmpdir, "file"), []byte("data"))
+	if err != nil {
+		t.Fatal(err)
+	}
+}
diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go
index 0e3bb7d..f47b8ad 100644
--- a/src/cmd/go/internal/modfetch/codehost/codehost.go
+++ b/src/cmd/go/internal/modfetch/codehost/codehost.go
@@ -16,6 +16,7 @@
 	"os/exec"
 	"path/filepath"
 	"strings"
+	"sync"
 	"time"
 
 	"cmd/go/internal/cfg"
@@ -32,10 +33,8 @@
 // A Repo represents a code hosting source.
 // Typical implementations include local version control repositories,
 // remote version control servers, and code hosting sites.
+// A Repo must be safe for simultaneous use by multiple goroutines.
 type Repo interface {
-	// Root returns the import path of the root directory of the repository.
-	Root() string
-
 	// List lists all tags with the given prefix.
 	Tags(prefix string) (tags []string, err error)
 
@@ -50,6 +49,9 @@
 
 	// ReadFile reads the given file in the file tree corresponding to revision rev.
 	// It should refuse to read more than maxSize bytes.
+	//
+	// If the requested file does not exist it should return an error for which
+	// os.IsNotExist(err) returns true.
 	ReadFile(rev, file string, maxSize int64) (data []byte, err error)
 
 	// ReadZip downloads a zip file for the subdir subdirectory
@@ -66,8 +68,9 @@
 type RevInfo struct {
 	Name    string    // complete ID in underlying repository
 	Short   string    // shortened ID, for use in pseudo-version
-	Version string    // TODO what is this?
+	Version string    // version used in lookup
 	Time    time.Time // commit time
+	Tags    []string  // known tags for commit
 }
 
 // AllHex reports whether the revision rev is entirely lower-case hexadecimal digits.
@@ -92,7 +95,7 @@
 }
 
 // WorkRoot is the root of the cached work directory.
-// It is set by cmd/go/internal/vgo.InitMod.
+// It is set by cmd/go/internal/modload.InitMod.
 var WorkRoot string
 
 // WorkDir returns the name of the cached work directory to use for the
@@ -113,21 +116,20 @@
 	key := typ + ":" + name
 	dir := filepath.Join(WorkRoot, fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
 	data, err := ioutil.ReadFile(dir + ".info")
-	if err == nil {
+	info, err2 := os.Stat(dir)
+	if err == nil && err2 == nil && info.IsDir() {
+		// Info file and directory both already exist: reuse.
 		have := strings.TrimSuffix(string(data), "\n")
 		if have != key {
 			return "", fmt.Errorf("%s exists with wrong content (have %q want %q)", dir+".info", have, key)
 		}
-		_, err := os.Stat(dir)
-		if err != nil {
-			return "", fmt.Errorf("%s exists but %s does not", dir+".info", dir)
-		}
 		if cfg.BuildX {
 			fmt.Fprintf(os.Stderr, "# %s for %s %s\n", dir, typ, name)
 		}
 		return dir, nil
 	}
 
+	// Info file or directory missing. Start from scratch.
 	if cfg.BuildX {
 		fmt.Fprintf(os.Stderr, "mkdir -p %s # %s %s\n", dir, typ, name)
 	}
@@ -157,19 +159,36 @@
 	return text
 }
 
+var dirLock sync.Map
+
 // Run runs the command line in the given directory
 // (an empty dir means the current directory).
 // It returns the standard output and, for a non-zero exit,
 // a *RunError indicating the command, exit status, and standard error.
 // Standard error is unavailable for commands that exit successfully.
 func Run(dir string, cmdline ...interface{}) ([]byte, error) {
+	if dir != "" {
+		muIface, ok := dirLock.Load(dir)
+		if !ok {
+			muIface, _ = dirLock.LoadOrStore(dir, new(sync.Mutex))
+		}
+		mu := muIface.(*sync.Mutex)
+		mu.Lock()
+		defer mu.Unlock()
+	}
+
 	cmd := str.StringList(cmdline...)
 	if cfg.BuildX {
-		var cd string
+		var text string
 		if dir != "" {
-			cd = "cd " + dir + "; "
+			text = "cd " + dir + "; "
 		}
-		fmt.Fprintf(os.Stderr, "%s%s\n", cd, strings.Join(cmd, " "))
+		text += strings.Join(cmd, " ")
+		fmt.Fprintf(os.Stderr, "%s\n", text)
+		start := time.Now()
+		defer func() {
+			fmt.Fprintf(os.Stderr, "%.3fs # %s\n", time.Since(start).Seconds(), text)
+		}()
 	}
 	// TODO: Impose limits on command output size.
 	// TODO: Set environment to get English error messages.
diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go
new file mode 100644
index 0000000..afa0467
--- /dev/null
+++ b/src/cmd/go/internal/modfetch/codehost/git.go
@@ -0,0 +1,466 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package codehost
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"cmd/go/internal/par"
+)
+
+// GitRepo returns the code repository at the given Git remote reference.
+func GitRepo(remote string) (Repo, error) {
+	return newGitRepoCached(remote, false)
+}
+
+// LocalGitRepo is like Repo but accepts both Git remote references
+// and paths to repositories on the local file system.
+func LocalGitRepo(remote string) (Repo, error) {
+	return newGitRepoCached(remote, true)
+}
+
+const gitWorkDirType = "git2"
+
+var gitRepoCache par.Cache
+
+func newGitRepoCached(remote string, localOK bool) (Repo, error) {
+	type key struct {
+		remote  string
+		localOK bool
+	}
+	type cached struct {
+		repo Repo
+		err  error
+	}
+
+	c := gitRepoCache.Do(key{remote, localOK}, func() interface{} {
+		repo, err := newGitRepo(remote, localOK)
+		return cached{repo, err}
+	}).(cached)
+
+	return c.repo, c.err
+}
+
+func newGitRepo(remote string, localOK bool) (Repo, error) {
+	r := &gitRepo{remote: remote}
+	if strings.Contains(remote, "://") {
+		// This is a remote path.
+		dir, err := WorkDir(gitWorkDirType, r.remote)
+		if err != nil {
+			return nil, err
+		}
+		r.dir = dir
+		if _, err := os.Stat(filepath.Join(dir, "objects")); err != nil {
+			if _, err := Run(dir, "git", "init", "--bare"); err != nil {
+				os.RemoveAll(dir)
+				return nil, err
+			}
+			// We could just say git fetch https://whatever later,
+			// but this lets us say git fetch origin instead, which
+			// is a little nicer. More importantly, using a named remote
+			// avoids a problem with Git LFS. See golang.org/issue/25605.
+			if _, err := Run(dir, "git", "remote", "add", "origin", r.remote); err != nil {
+				os.RemoveAll(dir)
+				return nil, err
+			}
+			r.remote = "origin"
+		}
+	} else {
+		// Local path.
+		// Disallow colon (not in ://) because sometimes
+		// that's rcp-style host:path syntax and sometimes it's not (c:\work).
+		// The go command has always insisted on URL syntax for ssh.
+		if strings.Contains(remote, ":") {
+			return nil, fmt.Errorf("git remote cannot use host:path syntax")
+		}
+		if !localOK {
+			return nil, fmt.Errorf("git remote must not be local directory")
+		}
+		r.local = true
+		info, err := os.Stat(remote)
+		if err != nil {
+			return nil, err
+		}
+		if !info.IsDir() {
+			return nil, fmt.Errorf("%s exists but is not a directory", remote)
+		}
+		r.dir = remote
+	}
+	return r, nil
+}
+
+type gitRepo struct {
+	remote string
+	local  bool
+	dir    string
+
+	mu         sync.Mutex // protects fetchLevel, some git repo state
+	fetchLevel int
+
+	statCache par.Cache
+
+	refsOnce sync.Once
+	refs     map[string]string
+	refsErr  error
+
+	localTagsOnce sync.Once
+	localTags     map[string]bool
+}
+
+const (
+	// How much have we fetched into the git repo (in this process)?
+	fetchNone = iota // nothing yet
+	fetchSome        // shallow fetches of individual hashes
+	fetchAll         // "fetch -t origin": get all remote branches and tags
+)
+
+// loadLocalTags loads tag references from the local git cache
+// into the map r.localTags.
+// Should only be called as r.localTagsOnce.Do(r.loadLocalTags).
+func (r *gitRepo) loadLocalTags() {
+	// The git protocol sends all known refs and ls-remote filters them on the client side,
+	// so we might as well record both heads and tags in one shot.
+	// Most of the time we only care about tags but sometimes we care about heads too.
+	out, err := Run(r.dir, "git", "tag", "-l")
+	if err != nil {
+		return
+	}
+
+	r.localTags = make(map[string]bool)
+	for _, line := range strings.Split(string(out), "\n") {
+		if line != "" {
+			r.localTags[line] = true
+		}
+	}
+}
+
+// loadRefs loads heads and tags references from the remote into the map r.refs.
+// Should only be called as r.refsOnce.Do(r.loadRefs).
+func (r *gitRepo) loadRefs() {
+	// The git protocol sends all known refs and ls-remote filters them on the client side,
+	// so we might as well record both heads and tags in one shot.
+	// Most of the time we only care about tags but sometimes we care about heads too.
+	out, err := Run(r.dir, "git", "ls-remote", "-q", r.remote)
+	if err != nil {
+		r.refsErr = err
+		return
+	}
+
+	r.refs = make(map[string]string)
+	for _, line := range strings.Split(string(out), "\n") {
+		f := strings.Fields(line)
+		if len(f) != 2 {
+			continue
+		}
+		if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") {
+			r.refs[f[1]] = f[0]
+		}
+	}
+	for ref, hash := range r.refs {
+		if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag
+			r.refs[strings.TrimSuffix(ref, "^{}")] = hash
+			delete(r.refs, ref)
+		}
+	}
+}
+
+func (r *gitRepo) Tags(prefix string) ([]string, error) {
+	r.refsOnce.Do(r.loadRefs)
+	if r.refsErr != nil {
+		return nil, r.refsErr
+	}
+
+	tags := []string{}
+	for ref := range r.refs {
+		if !strings.HasPrefix(ref, "refs/tags/") {
+			continue
+		}
+		tag := ref[len("refs/tags/"):]
+		if !strings.HasPrefix(tag, prefix) {
+			continue
+		}
+		tags = append(tags, tag)
+	}
+	sort.Strings(tags)
+	return tags, nil
+}
+
+func (r *gitRepo) Latest() (*RevInfo, error) {
+	r.refsOnce.Do(r.loadRefs)
+	if r.refsErr != nil {
+		return nil, r.refsErr
+	}
+	if r.refs["HEAD"] == "" {
+		return nil, fmt.Errorf("no commits")
+	}
+	return r.Stat(r.refs["HEAD"])
+}
+
+// findRef finds some ref name for the given hash,
+// for use when the server requires giving a ref instead of a hash.
+// There may be multiple ref names for a given hash,
+// in which case this returns some name - it doesn't matter which.
+func (r *gitRepo) findRef(hash string) (ref string, ok bool) {
+	r.refsOnce.Do(r.loadRefs)
+	for ref, h := range r.refs {
+		if h == hash {
+			return ref, true
+		}
+	}
+	return "", false
+}
+
+func unshallow(gitDir string) []string {
+	if _, err := os.Stat(filepath.Join(gitDir, "shallow")); err == nil {
+		return []string{"--unshallow"}
+	}
+	return []string{}
+}
+
+// minHashDigits is the minimum number of digits to require
+// before accepting a hex digit sequence as potentially identifying
+// a specific commit in a git repo. (Of course, users can always
+// specify more digits, and many will paste in all 40 digits,
+// but many of git's commands default to printing short hashes
+// as 7 digits.)
+const minHashDigits = 7
+
+// stat stats the given rev in the local repository,
+// or else it fetches more info from the remote repository and tries again.
+func (r *gitRepo) stat(rev string) (*RevInfo, error) {
+	if r.local {
+		return r.statLocal(rev, rev)
+	}
+
+	// Fast path: maybe rev is a hash we already have locally.
+	if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) {
+		if info, err := r.statLocal(rev, rev); err == nil {
+			return info, nil
+		}
+	}
+
+	// Maybe rev is a tag we already have locally.
+	// (Note that we're excluding branches, which can be stale.)
+	r.localTagsOnce.Do(r.loadLocalTags)
+	if r.localTags[rev] {
+		return r.statLocal(rev, "refs/tags/"+rev)
+	}
+
+	// Maybe rev is the name of a tag or branch on the remote server.
+	// Or maybe it's the prefix of a hash of a named ref.
+	// Try to resolve to both a ref (git name) and full (40-hex-digit) commit hash.
+	r.refsOnce.Do(r.loadRefs)
+	var ref, hash string
+	if r.refs["refs/tags/"+rev] != "" {
+		ref = "refs/tags/" + rev
+		hash = r.refs[ref]
+		// Keep rev as is: tags are assumed not to change meaning.
+	} else if r.refs["refs/heads/"+rev] != "" {
+		ref = "refs/heads/" + rev
+		hash = r.refs[ref]
+		rev = hash // Replace rev, because meaning of refs/heads/foo can change.
+	} else if rev == "HEAD" && r.refs["HEAD"] != "" {
+		ref = "HEAD"
+		hash = r.refs[ref]
+		rev = hash // Replace rev, because meaning of HEAD can change.
+	} else if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) {
+		// At the least, we have a hash prefix we can look up after the fetch below.
+		// Maybe we can map it to a full hash using the known refs.
+		prefix := rev
+		// Check whether rev is prefix of known ref hash.
+		for k, h := range r.refs {
+			if strings.HasPrefix(h, prefix) {
+				if hash != "" && hash != h {
+					// Hash is an ambiguous hash prefix.
+					// More information will not change that.
+					return nil, fmt.Errorf("ambiguous revision %s", rev)
+				}
+				if ref == "" || ref > k { // Break ties deterministically when multiple refs point at same hash.
+					ref = k
+				}
+				rev = h
+				hash = h
+			}
+		}
+		if hash == "" && len(rev) == 40 { // Didn't find a ref, but rev is a full hash.
+			hash = rev
+		}
+	} else {
+		return nil, fmt.Errorf("unknown revision %s", rev)
+	}
+
+	// Protect r.fetchLevel and the "fetch more and more" sequence.
+	// TODO(rsc): Add LockDir and use it for protecting that
+	// sequence, so that multiple processes don't collide in their
+	// git commands.
+	r.mu.Lock()
+	defer r.mu.Unlock()
+
+	// If we know a specific commit we need, fetch it.
+	if r.fetchLevel <= fetchSome && hash != "" && !r.local {
+		r.fetchLevel = fetchSome
+		var refspec string
+		if ref != "" && ref != "head" {
+			// If we do know the ref name, save the mapping locally
+			// so that (if it is a tag) it can show up in localTags
+			// on a future call. Also, some servers refuse to allow
+			// full hashes in ref specs, so prefer a ref name if known.
+			refspec = ref + ":" + ref
+		} else {
+			// Fetch the hash but give it a local name (refs/dummy),
+			// because that triggers the fetch behavior of creating any
+			// other known remote tags for the hash. We never use
+			// refs/dummy (it's not refs/tags/dummy) and it will be
+			// overwritten in the next command, and that's fine.
+			ref = hash
+			refspec = hash + ":refs/dummy"
+		}
+		_, err := Run(r.dir, "git", "fetch", "-f", "--depth=1", r.remote, refspec)
+		if err == nil {
+			return r.statLocal(rev, ref)
+		}
+		if !strings.Contains(err.Error(), "unadvertised object") && !strings.Contains(err.Error(), "no such remote ref") && !strings.Contains(err.Error(), "does not support shallow") {
+			return nil, err
+		}
+	}
+
+	// Last resort.
+	// Fetch all heads and tags and hope the hash we want is in the history.
+	if r.fetchLevel < fetchAll {
+		r.fetchLevel = fetchAll
+
+		// To work around a protocol version 2 bug that breaks --unshallow,
+		// add -c protocol.version=0.
+		// TODO(rsc): The bug is believed to be server-side, meaning only
+		// on Google's Git servers. Once the servers are fixed, drop the
+		// protocol.version=0. See Google-internal bug b/110495752.
+		var protoFlag []string
+		unshallowFlag := unshallow(r.dir)
+		if len(unshallowFlag) > 0 {
+			protoFlag = []string{"-c", "protocol.version=0"}
+		}
+		if _, err := Run(r.dir, "git", protoFlag, "fetch", unshallowFlag, "-f", r.remote, "refs/heads/*:refs/heads/*", "refs/tags/*:refs/tags/*"); err != nil {
+			return nil, err
+		}
+	}
+
+	return r.statLocal(rev, rev)
+}
+
+// statLocal returns a RevInfo describing rev in the local git repository.
+// It uses version as info.Version.
+func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
+	out, err := Run(r.dir, "git", "log", "-n1", "--format=format:%H %ct %D", rev)
+	if err != nil {
+		return nil, fmt.Errorf("unknown revision %s", rev)
+	}
+	f := strings.Fields(string(out))
+	if len(f) < 2 {
+		return nil, fmt.Errorf("unexpected response from git log: %q", out)
+	}
+	hash := f[0]
+	if strings.HasPrefix(hash, version) {
+		version = hash // extend to full hash
+	}
+	t, err := strconv.ParseInt(f[1], 10, 64)
+	if err != nil {
+		return nil, fmt.Errorf("invalid time from git log: %q", out)
+	}
+
+	info := &RevInfo{
+		Name:    hash,
+		Short:   ShortenSHA1(hash),
+		Time:    time.Unix(t, 0).UTC(),
+		Version: hash,
+	}
+
+	// Add tags. Output looks like:
+	//	ede458df7cd0fdca520df19a33158086a8a68e81 1523994202 HEAD -> master, tag: v1.2.4-annotated, tag: v1.2.3, origin/master, origin/HEAD
+	for i := 2; i < len(f); i++ {
+		if f[i] == "tag:" {
+			i++
+			if i < len(f) {
+				info.Tags = append(info.Tags, strings.TrimSuffix(f[i], ","))
+			}
+		}
+	}
+	sort.Strings(info.Tags)
+
+	// Used hash as info.Version above.
+	// Use caller's suggested version if it appears in the tag list
+	// (filters out branch names, HEAD).
+	for _, tag := range info.Tags {
+		if version == tag {
+			info.Version = version
+		}
+	}
+
+	return info, nil
+}
+
+func (r *gitRepo) Stat(rev string) (*RevInfo, error) {
+	if rev == "latest" {
+		return r.Latest()
+	}
+	type cached struct {
+		info *RevInfo
+		err  error
+	}
+	c := r.statCache.Do(rev, func() interface{} {
+		info, err := r.stat(rev)
+		return cached{info, err}
+	}).(cached)
+	return c.info, c.err
+}
+
+func (r *gitRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
+	// TODO: Could use git cat-file --batch.
+	info, err := r.Stat(rev) // download rev into local git repo
+	if err != nil {
+		return nil, err
+	}
+	out, err := Run(r.dir, "git", "cat-file", "blob", info.Name+":"+file)
+	if err != nil {
+		return nil, os.ErrNotExist
+	}
+	return out, nil
+}
+
+func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+	// TODO: Use maxSize or drop it.
+	args := []string{}
+	if subdir != "" {
+		args = append(args, "--", subdir)
+	}
+	info, err := r.Stat(rev) // download rev into local git repo
+	if err != nil {
+		return nil, "", err
+	}
+
+	// Incredibly, git produces different archives depending on whether
+	// it is running on a Windows system or not, in an attempt to normalize
+	// text file line endings. Setting -c core.autocrlf=input means only
+	// translate files on the way into the repo, not on the way out (archive).
+	// The -c core.eol=lf should be unnecessary but set it anyway.
+	archive, err := Run(r.dir, "git", "-c", "core.autocrlf=input", "-c", "core.eol=lf", "archive", "--format=zip", "--prefix=prefix/", info.Name, args)
+	if err != nil {
+		if bytes.Contains(err.(*RunError).Stderr, []byte("did not match any files")) {
+			return nil, "", os.ErrNotExist
+		}
+		return nil, "", err
+	}
+
+	return ioutil.NopCloser(bytes.NewReader(archive)), "", nil
+}
diff --git a/src/cmd/go/internal/modfetch/gitrepo/fetch_test.go b/src/cmd/go/internal/modfetch/codehost/git_test.go
similarity index 69%
rename from src/cmd/go/internal/modfetch/gitrepo/fetch_test.go
rename to src/cmd/go/internal/modfetch/codehost/git_test.go
index cc86cd9..aa1328d 100644
--- a/src/cmd/go/internal/modfetch/gitrepo/fetch_test.go
+++ b/src/cmd/go/internal/modfetch/codehost/git_test.go
@@ -2,36 +2,41 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package gitrepo
+package codehost
 
 import (
 	"archive/zip"
 	"bytes"
-	"flag"
 	"fmt"
 	"internal/testenv"
 	"io/ioutil"
 	"log"
 	"os"
 	"os/exec"
+	"path"
 	"path/filepath"
 	"reflect"
-	"runtime"
 	"strings"
 	"testing"
 	"time"
-
-	"cmd/go/internal/modfetch/codehost"
 )
 
 func TestMain(m *testing.M) {
-	// needed for initializing the test environment variables as testing.Short
-	// and HasExternalNetwork
-	flag.Parse()
 	os.Exit(testMain(m))
 }
 
-const gitrepo1 = "https://vcs-test.golang.org/git/gitrepo1"
+const (
+	gitrepo1 = "https://vcs-test.golang.org/git/gitrepo1"
+	hgrepo1  = "https://vcs-test.golang.org/hg/hgrepo1"
+)
+
+var altRepos = []string{
+	"localGitRepo",
+	hgrepo1,
+}
+
+// TODO: Convert gitrepo1 to svn, bzr, fossil and add tests.
+// For now, at least the hgrepo1 tests check the general vcs.go logic.
 
 // localGitRepo is like gitrepo1 but allows archive access.
 var localGitRepo string
@@ -43,18 +48,12 @@
 		return 0
 	}
 
-	if runtime.GOOS == "plan9" {
-		fmt.Fprintln(os.Stderr, "skipping on plan9")
-		fmt.Println("PASS")
-		return 0
-	}
-
 	dir, err := ioutil.TempDir("", "gitrepo-test-")
 	if err != nil {
 		log.Fatal(err)
 	}
 	defer os.RemoveAll(dir)
-	codehost.WorkRoot = dir
+	WorkRoot = dir
 
 	if testenv.HasExternalNetwork() && testenv.HasExec() {
 		// Clone gitrepo1 into a local directory.
@@ -62,10 +61,10 @@
 		// then git starts up all the usual protocol machinery,
 		// which will let us test remote git archive invocations.
 		localGitRepo = filepath.Join(dir, "gitrepo2")
-		if _, err := codehost.Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
+		if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
 			log.Fatal(err)
 		}
-		if _, err := codehost.Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
+		if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
 			log.Fatal(err)
 		}
 	}
@@ -73,13 +72,17 @@
 	return m.Run()
 }
 
-func testRepo(remote string) (codehost.Repo, error) {
+func testRepo(remote string) (Repo, error) {
 	if remote == "localGitRepo" {
-		remote = "file://" + filepath.ToSlash(localGitRepo)
+		return LocalGitRepo(filepath.ToSlash(localGitRepo))
 	}
-	// Re ?root: nothing should care about the second argument,
-	// so use a string that will be distinctive if it does show up.
-	return LocalRepo(remote, "?root")
+	kind := "git"
+	for _, k := range []string{"hg"} {
+		if strings.Contains(remote, "/"+k+"/") {
+			kind = k
+		}
+	}
+	return NewRepo(kind, remote)
 }
 
 var tagsTests = []struct {
@@ -112,25 +115,36 @@
 				t.Errorf("Tags: incorrect tags\nhave %v\nwant %v", tags, tt.tags)
 			}
 		}
-		t.Run(tt.repo+"/"+tt.prefix, f)
+		t.Run(path.Base(tt.repo)+"/"+tt.prefix, f)
 		if tt.repo == gitrepo1 {
-			tt.repo = "localGitRepo"
-			t.Run(tt.repo+"/"+tt.prefix, f)
+			for _, tt.repo = range altRepos {
+				t.Run(path.Base(tt.repo)+"/"+tt.prefix, f)
+			}
 		}
 	}
 }
 
 var latestTests = []struct {
 	repo string
-	info *codehost.RevInfo
+	info *RevInfo
 }{
 	{
 		gitrepo1,
-		&codehost.RevInfo{
+		&RevInfo{
 			Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Short:   "ede458df7cd0",
 			Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+			Tags:    []string{"v1.2.3", "v1.2.4-annotated"},
+		},
+	},
+	{
+		hgrepo1,
+		&RevInfo{
+			Name:    "18518c07eb8ed5c80221e997e518cccaa8c0c287",
+			Short:   "18518c07eb8e",
+			Version: "18518c07eb8ed5c80221e997e518cccaa8c0c287",
+			Time:    time.Date(2018, 6, 27, 16, 16, 30, 0, time.UTC),
 		},
 	},
 }
@@ -149,14 +163,14 @@
 			if err != nil {
 				t.Fatal(err)
 			}
-			if *info != *tt.info {
+			if !reflect.DeepEqual(info, tt.info) {
 				t.Errorf("Latest: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
 			}
 		}
-		t.Run(tt.repo, f)
+		t.Run(path.Base(tt.repo), f)
 		if tt.repo == gitrepo1 {
 			tt.repo = "localGitRepo"
-			t.Run(tt.repo, f)
+			t.Run(path.Base(tt.repo), f)
 		}
 	}
 }
@@ -170,7 +184,7 @@
 }{
 	{
 		repo: gitrepo1,
-		rev:  "HEAD",
+		rev:  "latest",
 		file: "README",
 		data: "",
 	},
@@ -184,7 +198,7 @@
 		repo: gitrepo1,
 		rev:  "v2.3.4",
 		file: "another.txt",
-		err:  "file not found",
+		err:  os.ErrNotExist.Error(),
 	},
 }
 
@@ -218,10 +232,11 @@
 				t.Errorf("ReadFile: incorrect data\nhave %q\nwant %q", data, tt.data)
 			}
 		}
-		t.Run(tt.repo+"/"+tt.rev+"/"+tt.file, f)
+		t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f)
 		if tt.repo == gitrepo1 {
-			tt.repo = "localGitRepo"
-			t.Run(tt.repo+"/"+tt.rev+"/"+tt.file, f)
+			for _, tt.repo = range altRepos {
+				t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, f)
+			}
 		}
 	}
 }
@@ -245,6 +260,17 @@
 		},
 	},
 	{
+		repo:   hgrepo1,
+		rev:    "v2.3.4",
+		subdir: "",
+		files: map[string]uint64{
+			"prefix/.hg_archival.txt": ^uint64(0),
+			"prefix/README":           0,
+			"prefix/v2":               3,
+		},
+	},
+
+	{
 		repo:   gitrepo1,
 		rev:    "v2",
 		subdir: "",
@@ -257,6 +283,19 @@
 		},
 	},
 	{
+		repo:   hgrepo1,
+		rev:    "v2",
+		subdir: "",
+		files: map[string]uint64{
+			"prefix/.hg_archival.txt": ^uint64(0),
+			"prefix/README":           0,
+			"prefix/v2":               3,
+			"prefix/another.txt":      8,
+			"prefix/foo.txt":          13,
+		},
+	},
+
+	{
 		repo:   gitrepo1,
 		rev:    "v3",
 		subdir: "",
@@ -270,6 +309,18 @@
 		},
 	},
 	{
+		repo:   hgrepo1,
+		rev:    "v3",
+		subdir: "",
+		files: map[string]uint64{
+			"prefix/.hg_archival.txt":    ^uint64(0),
+			"prefix/.hgtags":             405,
+			"prefix/v3/sub/dir/file.txt": 16,
+			"prefix/README":              0,
+		},
+	},
+
+	{
 		repo:   gitrepo1,
 		rev:    "v3",
 		subdir: "v3/sub/dir",
@@ -282,6 +333,15 @@
 		},
 	},
 	{
+		repo:   hgrepo1,
+		rev:    "v3",
+		subdir: "v3/sub/dir",
+		files: map[string]uint64{
+			"prefix/v3/sub/dir/file.txt": 16,
+		},
+	},
+
+	{
 		repo:   gitrepo1,
 		rev:    "v3",
 		subdir: "v3/sub",
@@ -294,12 +354,28 @@
 		},
 	},
 	{
+		repo:   hgrepo1,
+		rev:    "v3",
+		subdir: "v3/sub",
+		files: map[string]uint64{
+			"prefix/v3/sub/dir/file.txt": 16,
+		},
+	},
+
+	{
 		repo:   gitrepo1,
 		rev:    "aaaaaaaaab",
 		subdir: "",
-		err:    "cannot find hash",
+		err:    "unknown revision",
 	},
 	{
+		repo:   hgrepo1,
+		rev:    "aaaaaaaaab",
+		subdir: "",
+		err:    "unknown revision",
+	},
+
+	{
 		repo:   "https://github.com/rsc/vgotest1",
 		rev:    "submod/v1.0.4",
 		subdir: "submod",
@@ -364,7 +440,7 @@
 					continue
 				}
 				have[f.Name] = true
-				if f.UncompressedSize64 != size {
+				if size != ^uint64(0) && f.UncompressedSize64 != size {
 					t.Errorf("ReadZip: file %s has unexpected size %d != %d", f.Name, f.UncompressedSize64, size)
 				}
 			}
@@ -374,84 +450,98 @@
 				}
 			}
 		}
-		t.Run(tt.repo+"/"+tt.rev+"/"+tt.subdir, f)
+		t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f)
 		if tt.repo == gitrepo1 {
 			tt.repo = "localGitRepo"
-			t.Run(tt.repo+"/"+tt.rev+"/"+tt.subdir, f)
+			t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.subdir, f)
 		}
 	}
 }
 
+var hgmap = map[string]string{
+	"HEAD": "41964ddce1180313bdc01d0a39a2813344d6261d", // not tip due to bad hgrepo1 conversion
+	"9d02800338b8a55be062c838d1f02e0c5780b9eb": "8f49ee7a6ddcdec6f0112d9dca48d4a2e4c3c09e",
+	"76a00fb249b7f93091bc2c89a789dab1fc1bc26f": "88fde824ec8b41a76baa16b7e84212cee9f3edd0",
+	"ede458df7cd0fdca520df19a33158086a8a68e81": "41964ddce1180313bdc01d0a39a2813344d6261d",
+	"97f6aa59c81c623494825b43d39e445566e429a4": "c0cbbfb24c7c3c50c35c7b88e7db777da4ff625d",
+}
+
 var statTests = []struct {
 	repo string
 	rev  string
 	err  string
-	info *codehost.RevInfo
+	info *RevInfo
 }{
 	{
 		repo: gitrepo1,
 		rev:  "HEAD",
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Short:   "ede458df7cd0",
 			Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+			Tags:    []string{"v1.2.3", "v1.2.4-annotated"},
 		},
 	},
 	{
 		repo: gitrepo1,
 		rev:  "v2", // branch
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "9d02800338b8a55be062c838d1f02e0c5780b9eb",
 			Short:   "9d02800338b8",
 			Version: "9d02800338b8a55be062c838d1f02e0c5780b9eb",
 			Time:    time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC),
+			Tags:    []string{"v2.0.2"},
 		},
 	},
 	{
 		repo: gitrepo1,
 		rev:  "v2.3.4", // badly-named branch (semver should be a tag)
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
 			Short:   "76a00fb249b7",
 			Version: "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
 			Time:    time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+			Tags:    []string{"v2.0.1", "v2.3"},
 		},
 	},
 	{
 		repo: gitrepo1,
 		rev:  "v2.3", // badly-named tag (we only respect full semver v2.3.0)
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "76a00fb249b7f93091bc2c89a789dab1fc1bc26f",
 			Short:   "76a00fb249b7",
 			Version: "v2.3",
 			Time:    time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+			Tags:    []string{"v2.0.1", "v2.3"},
 		},
 	},
 	{
 		repo: gitrepo1,
 		rev:  "v1.2.3", // tag
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Short:   "ede458df7cd0",
 			Version: "v1.2.3",
 			Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+			Tags:    []string{"v1.2.3", "v1.2.4-annotated"},
 		},
 	},
 	{
 		repo: gitrepo1,
 		rev:  "ede458df", // hash prefix in refs
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Short:   "ede458df7cd0",
 			Version: "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+			Tags:    []string{"v1.2.3", "v1.2.4-annotated"},
 		},
 	},
 	{
 		repo: gitrepo1,
 		rev:  "97f6aa59", // hash prefix not in refs
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "97f6aa59c81c623494825b43d39e445566e429a4",
 			Short:   "97f6aa59c81c",
 			Version: "97f6aa59c81c623494825b43d39e445566e429a4",
@@ -461,17 +551,18 @@
 	{
 		repo: gitrepo1,
 		rev:  "v1.2.4-annotated", // annotated tag uses unwrapped commit hash
-		info: &codehost.RevInfo{
+		info: &RevInfo{
 			Name:    "ede458df7cd0fdca520df19a33158086a8a68e81",
 			Short:   "ede458df7cd0",
 			Version: "v1.2.4-annotated",
 			Time:    time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+			Tags:    []string{"v1.2.3", "v1.2.4-annotated"},
 		},
 	},
 	{
 		repo: gitrepo1,
 		rev:  "aaaaaaaaab",
-		err:  "cannot find hash",
+		err:  "unknown revision",
 	},
 }
 
@@ -498,14 +589,43 @@
 				}
 				return
 			}
-			if *info != *tt.info {
+			if !reflect.DeepEqual(info, tt.info) {
 				t.Errorf("Stat: incorrect info\nhave %+v\nwant %+v", *info, *tt.info)
 			}
 		}
-		t.Run(filepath.Base(tt.repo)+"/"+tt.rev, f)
+		t.Run(path.Base(tt.repo)+"/"+tt.rev, f)
 		if tt.repo == gitrepo1 {
-			tt.repo = "localGitRepo"
-			t.Run(filepath.Base(tt.repo)+"/"+tt.rev, f)
+			for _, tt.repo = range altRepos {
+				old := tt
+				var m map[string]string
+				if tt.repo == hgrepo1 {
+					m = hgmap
+				}
+				if tt.info != nil {
+					info := *tt.info
+					tt.info = &info
+					tt.info.Name = remap(tt.info.Name, m)
+					tt.info.Version = remap(tt.info.Version, m)
+					tt.info.Short = remap(tt.info.Short, m)
+				}
+				tt.rev = remap(tt.rev, m)
+				t.Run(path.Base(tt.repo)+"/"+tt.rev, f)
+				tt = old
+			}
 		}
 	}
 }
+
+func remap(name string, m map[string]string) string {
+	if m[name] != "" {
+		return m[name]
+	}
+	if AllHex(name) {
+		for k, v := range m {
+			if strings.HasPrefix(k, name) {
+				return v[:len(name)]
+			}
+		}
+	}
+	return name
+}
diff --git a/src/cmd/go/internal/modfetch/codehost/shell.go b/src/cmd/go/internal/modfetch/codehost/shell.go
new file mode 100644
index 0000000..7b813c3
--- /dev/null
+++ b/src/cmd/go/internal/modfetch/codehost/shell.go
@@ -0,0 +1,140 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Interactive debugging shell for codehost.Repo implementations.
+
+package main
+
+import (
+	"archive/zip"
+	"bufio"
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"strings"
+	"time"
+
+	"cmd/go/internal/modfetch/codehost"
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: go run shell.go vcs remote\n")
+	os.Exit(2)
+}
+
+func main() {
+	codehost.WorkRoot = "/tmp/vcswork"
+	log.SetFlags(0)
+	log.SetPrefix("shell: ")
+	flag.Usage = usage
+	flag.Parse()
+	if flag.NArg() != 2 {
+		usage()
+	}
+
+	repo, err := codehost.NewRepo(flag.Arg(0), flag.Arg(1))
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	b := bufio.NewReader(os.Stdin)
+	for {
+		fmt.Fprintf(os.Stderr, ">>> ")
+		line, err := b.ReadString('\n')
+		if err != nil {
+			log.Fatal(err)
+		}
+		f := strings.Fields(line)
+		if len(f) == 0 {
+			continue
+		}
+		switch f[0] {
+		default:
+			fmt.Fprintf(os.Stderr, "?unknown command\n")
+			continue
+		case "tags":
+			prefix := ""
+			if len(f) == 2 {
+				prefix = f[1]
+			}
+			if len(f) > 2 {
+				fmt.Fprintf(os.Stderr, "?usage: tags [prefix]\n")
+				continue
+			}
+			tags, err := repo.Tags(prefix)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "?%s\n", err)
+				continue
+			}
+			for _, tag := range tags {
+				fmt.Printf("%s\n", tag)
+			}
+
+		case "stat":
+			if len(f) != 2 {
+				fmt.Fprintf(os.Stderr, "?usage: stat rev\n")
+				continue
+			}
+			info, err := repo.Stat(f[1])
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "?%s\n", err)
+				continue
+			}
+			fmt.Printf("name=%s short=%s version=%s time=%s\n", info.Name, info.Short, info.Version, info.Time.UTC().Format(time.RFC3339))
+
+		case "read":
+			if len(f) != 3 {
+				fmt.Fprintf(os.Stderr, "?usage: read rev file\n")
+				continue
+			}
+			data, err := repo.ReadFile(f[1], f[2], 10<<20)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "?%s\n", err)
+				continue
+			}
+			os.Stdout.Write(data)
+
+		case "zip":
+			if len(f) != 4 {
+				fmt.Fprintf(os.Stderr, "?usage: zip rev subdir output\n")
+				continue
+			}
+			subdir := f[2]
+			if subdir == "-" {
+				subdir = ""
+			}
+			rc, _, err := repo.ReadZip(f[1], subdir, 10<<20)
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "?%s\n", err)
+				continue
+			}
+			data, err := ioutil.ReadAll(rc)
+			rc.Close()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "?%s\n", err)
+				continue
+			}
+
+			if f[3] != "-" {
+				if err := ioutil.WriteFile(f[3], data, 0666); err != nil {
+					fmt.Fprintf(os.Stderr, "?%s\n", err)
+					continue
+				}
+			}
+			z, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "?%s\n", err)
+				continue
+			}
+			for _, f := range z.File {
+				fmt.Printf("%s %d\n", f.Name, f.UncompressedSize64)
+			}
+		}
+	}
+}
diff --git a/src/cmd/go/internal/modfetch/codehost/vcs.go b/src/cmd/go/internal/modfetch/codehost/vcs.go
new file mode 100644
index 0000000..12e45cb
--- /dev/null
+++ b/src/cmd/go/internal/modfetch/codehost/vcs.go
@@ -0,0 +1,506 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package codehost
+
+import (
+	"encoding/xml"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"cmd/go/internal/par"
+	"cmd/go/internal/str"
+)
+
+func NewRepo(vcs, remote string) (Repo, error) {
+	type key struct {
+		vcs    string
+		remote string
+	}
+	type cached struct {
+		repo Repo
+		err  error
+	}
+	c := vcsRepoCache.Do(key{vcs, remote}, func() interface{} {
+		repo, err := newVCSRepo(vcs, remote)
+		return cached{repo, err}
+	}).(cached)
+
+	return c.repo, c.err
+}
+
+var vcsRepoCache par.Cache
+
+type vcsRepo struct {
+	remote string
+	cmd    *vcsCmd
+	dir    string
+
+	tagsOnce sync.Once
+	tags     map[string]bool
+
+	branchesOnce sync.Once
+	branches     map[string]bool
+
+	fetchOnce sync.Once
+	fetchErr  error
+}
+
+func newVCSRepo(vcs, remote string) (Repo, error) {
+	if vcs == "git" {
+		return newGitRepo(remote, false)
+	}
+	cmd := vcsCmds[vcs]
+	if cmd == nil {
+		return nil, fmt.Errorf("unknown vcs: %s %s", vcs, remote)
+	}
+	if !strings.Contains(remote, "://") {
+		return nil, fmt.Errorf("invalid vcs remote: %s %s", vcs, remote)
+	}
+	r := &vcsRepo{remote: remote, cmd: cmd}
+	if cmd.init == nil {
+		return r, nil
+	}
+	dir, err := WorkDir(vcsWorkDirType+vcs, r.remote)
+	if err != nil {
+		return nil, err
+	}
+	r.dir = dir
+	if _, err := os.Stat(filepath.Join(dir, "."+vcs)); err != nil {
+		if _, err := Run(dir, cmd.init(r.remote)); err != nil {
+			os.RemoveAll(dir)
+			return nil, err
+		}
+	}
+	return r, nil
+}
+
+const vcsWorkDirType = "vcs1."
+
+type vcsCmd struct {
+	vcs           string                                            // vcs name "hg"
+	init          func(remote string) []string                      // cmd to init repo to track remote
+	tags          func(remote string) []string                      // cmd to list local tags
+	tagRE         *regexp.Regexp                                    // regexp to extract tag names from output of tags cmd
+	branches      func(remote string) []string                      // cmd to list local branches
+	branchRE      *regexp.Regexp                                    // regexp to extract branch names from output of tags cmd
+	badLocalRevRE *regexp.Regexp                                    // regexp of names that must not be served out of local cache without doing fetch first
+	statLocal     func(rev, remote string) []string                 // cmd to stat local rev
+	parseStat     func(rev, out string) (*RevInfo, error)           // cmd to parse output of statLocal
+	fetch         []string                                          // cmd to fetch everything from remote
+	latest        string                                            // name of latest commit on remote (tip, HEAD, etc)
+	readFile      func(rev, file, remote string) []string           // cmd to read rev's file
+	readZip       func(rev, subdir, remote, target string) []string // cmd to read rev's subdir as zip file
+}
+
+var re = regexp.MustCompile
+
+var vcsCmds = map[string]*vcsCmd{
+	"hg": {
+		vcs: "hg",
+		init: func(remote string) []string {
+			return []string{"hg", "clone", "-U", remote, "."}
+		},
+		tags: func(remote string) []string {
+			return []string{"hg", "tags", "-q"}
+		},
+		tagRE: re(`(?m)^[^\n]+$`),
+		branches: func(remote string) []string {
+			return []string{"hg", "branches", "-c", "-q"}
+		},
+		branchRE:      re(`(?m)^[^\n]+$`),
+		badLocalRevRE: re(`(?m)^(tip)$`),
+		statLocal: func(rev, remote string) []string {
+			return []string{"hg", "log", "-l1", "-r", rev, "--template", "{node} {date|hgdate} {tags}"}
+		},
+		parseStat: hgParseStat,
+		fetch:     []string{"hg", "pull", "-f"},
+		latest:    "tip",
+		readFile: func(rev, file, remote string) []string {
+			return []string{"hg", "cat", "-r", rev, file}
+		},
+		readZip: func(rev, subdir, remote, target string) []string {
+			pattern := []string{}
+			if subdir != "" {
+				pattern = []string{"-I", subdir + "/**"}
+			}
+			return str.StringList("hg", "archive", "-t", "zip", "--no-decode", "-r", rev, "--prefix=prefix/", pattern, target)
+		},
+	},
+
+	"svn": {
+		vcs:  "svn",
+		init: nil, // no local checkout
+		tags: func(remote string) []string {
+			return []string{"svn", "list", strings.TrimSuffix(remote, "/trunk") + "/tags"}
+		},
+		tagRE: re(`(?m)^(.*?)/?$`),
+		statLocal: func(rev, remote string) []string {
+			suffix := "@" + rev
+			if rev == "latest" {
+				suffix = ""
+			}
+			return []string{"svn", "log", "-l1", "--xml", remote + suffix}
+		},
+		parseStat: svnParseStat,
+		latest:    "latest",
+		readFile: func(rev, file, remote string) []string {
+			return []string{"svn", "cat", remote + "/" + file + "@" + rev}
+		},
+		// TODO: zip
+	},
+
+	"bzr": {
+		vcs: "bzr",
+		init: func(remote string) []string {
+			return []string{"bzr", "branch", "--use-existing-dir", remote, "."}
+		},
+		fetch: []string{
+			"bzr", "pull", "--overwrite-tags",
+		},
+		tags: func(remote string) []string {
+			return []string{"bzr", "tags"}
+		},
+		tagRE:         re(`(?m)^\S+`),
+		badLocalRevRE: re(`^revno:-`),
+		statLocal: func(rev, remote string) []string {
+			return []string{"bzr", "log", "-l1", "--long", "--show-ids", "-r", rev}
+		},
+		parseStat: bzrParseStat,
+		latest:    "revno:-1",
+		readFile: func(rev, file, remote string) []string {
+			return []string{"bzr", "cat", "-r", rev, file}
+		},
+		readZip: func(rev, subdir, remote, target string) []string {
+			extra := []string{}
+			if subdir != "" {
+				extra = []string{"./" + subdir}
+			}
+			return str.StringList("bzr", "export", "--format=zip", "-r", rev, "--root=prefix/", target, extra)
+		},
+	},
+
+	"fossil": {
+		vcs: "fossil",
+		init: func(remote string) []string {
+			return []string{"fossil", "clone", remote, ".fossil"}
+		},
+		fetch: []string{"fossil", "pull", "-R", ".fossil"},
+		tags: func(remote string) []string {
+			return []string{"fossil", "tag", "-R", ".fossil", "list"}
+		},
+		tagRE: re(`XXXTODO`),
+		statLocal: func(rev, remote string) []string {
+			return []string{"fossil", "info", "-R", ".fossil", rev}
+		},
+		parseStat: fossilParseStat,
+		latest:    "trunk",
+		readFile: func(rev, file, remote string) []string {
+			return []string{"fossil", "cat", "-R", ".fossil", "-r", rev, file}
+		},
+		readZip: func(rev, subdir, remote, target string) []string {
+			extra := []string{}
+			if subdir != "" && !strings.ContainsAny(subdir, "*?[],") {
+				extra = []string{"--include", subdir}
+			}
+			// Note that vcsRepo.ReadZip below rewrites this command
+			// to run in a different directory, to work around a fossil bug.
+			return str.StringList("fossil", "zip", "-R", ".fossil", "--name", "prefix", extra, rev, target)
+		},
+	},
+}
+
+func (r *vcsRepo) loadTags() {
+	out, err := Run(r.dir, r.cmd.tags(r.remote))
+	if err != nil {
+		return
+	}
+
+	// Run tag-listing command and extract tags.
+	r.tags = make(map[string]bool)
+	for _, tag := range r.cmd.tagRE.FindAllString(string(out), -1) {
+		if r.cmd.badLocalRevRE != nil && r.cmd.badLocalRevRE.MatchString(tag) {
+			continue
+		}
+		r.tags[tag] = true
+	}
+}
+
+func (r *vcsRepo) loadBranches() {
+	if r.cmd.branches == nil {
+		return
+	}
+
+	out, err := Run(r.dir, r.cmd.branches(r.remote))
+	if err != nil {
+		return
+	}
+
+	r.branches = make(map[string]bool)
+	for _, branch := range r.cmd.branchRE.FindAllString(string(out), -1) {
+		if r.cmd.badLocalRevRE != nil && r.cmd.badLocalRevRE.MatchString(branch) {
+			continue
+		}
+		r.branches[branch] = true
+	}
+}
+
+func (r *vcsRepo) Tags(prefix string) ([]string, error) {
+	r.tagsOnce.Do(r.loadTags)
+
+	tags := []string{}
+	for tag := range r.tags {
+		if strings.HasPrefix(tag, prefix) {
+			tags = append(tags, tag)
+		}
+	}
+	sort.Strings(tags)
+	return tags, nil
+}
+
+func (r *vcsRepo) Stat(rev string) (*RevInfo, error) {
+	if rev == "latest" {
+		rev = r.cmd.latest
+	}
+	r.branchesOnce.Do(r.loadBranches)
+	revOK := (r.cmd.badLocalRevRE == nil || !r.cmd.badLocalRevRE.MatchString(rev)) && !r.branches[rev]
+	if revOK {
+		if info, err := r.statLocal(rev); err == nil {
+			return info, nil
+		}
+	}
+
+	r.fetchOnce.Do(r.fetch)
+	if r.fetchErr != nil {
+		return nil, r.fetchErr
+	}
+	info, err := r.statLocal(rev)
+	if err != nil {
+		return nil, err
+	}
+	if !revOK {
+		info.Version = info.Name
+	}
+	return info, nil
+}
+
+func (r *vcsRepo) fetch() {
+	_, r.fetchErr = Run(r.dir, r.cmd.fetch)
+}
+
+func (r *vcsRepo) statLocal(rev string) (*RevInfo, error) {
+	out, err := Run(r.dir, r.cmd.statLocal(rev, r.remote))
+	if err != nil {
+		return nil, fmt.Errorf("unknown revision %s", rev)
+	}
+	return r.cmd.parseStat(rev, string(out))
+}
+
+func (r *vcsRepo) Latest() (*RevInfo, error) {
+	return r.Stat("latest")
+}
+
+func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
+	if rev == "latest" {
+		rev = r.cmd.latest
+	}
+	_, err := r.Stat(rev) // download rev into local repo
+	if err != nil {
+		return nil, err
+	}
+	out, err := Run(r.dir, r.cmd.readFile(rev, file, r.remote))
+	if err != nil {
+		return nil, os.ErrNotExist
+	}
+	return out, nil
+}
+
+func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
+	if rev == "latest" {
+		rev = r.cmd.latest
+	}
+	f, err := ioutil.TempFile("", "go-readzip-*.zip")
+	if err != nil {
+		return nil, "", err
+	}
+	if r.cmd.vcs == "fossil" {
+		// If you run
+		//	fossil zip -R .fossil --name prefix trunk /tmp/x.zip
+		// fossil fails with "unable to create directory /tmp" [sic].
+		// Change the command to run in /tmp instead,
+		// replacing the -R argument with an absolute path.
+		args := r.cmd.readZip(rev, subdir, r.remote, filepath.Base(f.Name()))
+		for i := range args {
+			if args[i] == ".fossil" {
+				args[i] = filepath.Join(r.dir, ".fossil")
+			}
+		}
+		_, err = Run(filepath.Dir(f.Name()), args)
+	} else {
+		_, err = Run(r.dir, r.cmd.readZip(rev, subdir, r.remote, f.Name()))
+	}
+	if err != nil {
+		f.Close()
+		os.Remove(f.Name())
+		return nil, "", err
+	}
+	return &deleteCloser{f}, "", nil
+}
+
+// deleteCloser is a file that gets deleted on Close.
+type deleteCloser struct {
+	*os.File
+}
+
+func (d *deleteCloser) Close() error {
+	defer os.Remove(d.File.Name())
+	return d.File.Close()
+}
+
+func hgParseStat(rev, out string) (*RevInfo, error) {
+	f := strings.Fields(string(out))
+	if len(f) < 3 {
+		return nil, fmt.Errorf("unexpected response from hg log: %q", out)
+	}
+	hash := f[0]
+	version := rev
+	if strings.HasPrefix(hash, version) {
+		version = hash // extend to full hash
+	}
+	t, err := strconv.ParseInt(f[1], 10, 64)
+	if err != nil {
+		return nil, fmt.Errorf("invalid time from hg log: %q", out)
+	}
+
+	var tags []string
+	for _, tag := range f[3:] {
+		if tag != "tip" {
+			tags = append(tags, tag)
+		}
+	}
+	sort.Strings(tags)
+
+	info := &RevInfo{
+		Name:    hash,
+		Short:   ShortenSHA1(hash),
+		Time:    time.Unix(t, 0).UTC(),
+		Version: version,
+		Tags:    tags,
+	}
+	return info, nil
+}
+
+func svnParseStat(rev, out string) (*RevInfo, error) {
+	var log struct {
+		Logentry struct {
+			Revision int64  `xml:"revision,attr"`
+			Date     string `xml:"date"`
+		} `xml:"logentry"`
+	}
+	if err := xml.Unmarshal([]byte(out), &log); err != nil {
+		return nil, fmt.Errorf("unexpected response from svn log --xml: %v\n%s", err, out)
+	}
+
+	t, err := time.Parse(time.RFC3339, log.Logentry.Date)
+	if err != nil {
+		return nil, fmt.Errorf("unexpected response from svn log --xml: %v\n%s", err, out)
+	}
+
+	info := &RevInfo{
+		Name:    fmt.Sprintf("%d", log.Logentry.Revision),
+		Short:   fmt.Sprintf("%012d", log.Logentry.Revision),
+		Time:    t.UTC(),
+		Version: rev,
+	}
+	return info, nil
+}
+
+func bzrParseStat(rev, out string) (*RevInfo, error) {
+	var revno int64
+	var tm time.Time
+	for _, line := range strings.Split(out, "\n") {
+		if line == "" || line[0] == ' ' || line[0] == '\t' {
+			// End of header, start of commit message.
+			break
+		}
+		if line[0] == '-' {
+			continue
+		}
+		i := strings.Index(line, ":")
+		if i < 0 {
+			// End of header, start of commit message.
+			break
+		}
+		key, val := line[:i], strings.TrimSpace(line[i+1:])
+		switch key {
+		case "revno":
+			if j := strings.Index(val, " "); j >= 0 {
+				val = val[:j]
+			}
+			i, err := strconv.ParseInt(val, 10, 64)
+			if err != nil {
+				return nil, fmt.Errorf("unexpected revno from bzr log: %q", line)
+			}
+			revno = i
+		case "timestamp":
+			j := strings.Index(val, " ")
+			if j < 0 {
+				return nil, fmt.Errorf("unexpected timestamp from bzr log: %q", line)
+			}
+			t, err := time.Parse("2006-01-02 15:04:05 -0700", val[j+1:])
+			if err != nil {
+				return nil, fmt.Errorf("unexpected timestamp from bzr log: %q", line)
+			}
+			tm = t.UTC()
+		}
+	}
+	if revno == 0 || tm.IsZero() {
+		return nil, fmt.Errorf("unexpected response from bzr log: %q", out)
+	}
+
+	info := &RevInfo{
+		Name:    fmt.Sprintf("%d", revno),
+		Short:   fmt.Sprintf("%012d", revno),
+		Time:    tm,
+		Version: rev,
+	}
+	return info, nil
+}
+
+func fossilParseStat(rev, out string) (*RevInfo, error) {
+	for _, line := range strings.Split(out, "\n") {
+		if strings.HasPrefix(line, "uuid:") {
+			f := strings.Fields(line)
+			if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" {
+				return nil, fmt.Errorf("unexpected response from fossil info: %q", line)
+			}
+			t, err := time.Parse("2006-01-02 15:04:05", f[2]+" "+f[3])
+			if err != nil {
+				return nil, fmt.Errorf("unexpected response from fossil info: %q", line)
+			}
+			hash := f[1]
+			version := rev
+			if strings.HasPrefix(hash, version) {
+				version = hash // extend to full hash
+			}
+			info := &RevInfo{
+				Name:    hash,
+				Short:   ShortenSHA1(hash),
+				Time:    t,
+				Version: version,
+			}
+			return info, nil
+		}
+	}
+	return nil, fmt.Errorf("unexpected response from fossil info: %q", out)
+}
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
index bb6e8ac..33be117 100644
--- a/src/cmd/go/internal/modfetch/coderepo.go
+++ b/src/cmd/go/internal/modfetch/coderepo.go
@@ -6,20 +6,16 @@
 
 import (
 	"archive/zip"
-	"bytes"
 	"errors"
 	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
 	"path"
-	"path/filepath"
 	"regexp"
-	"strconv"
 	"strings"
 	"time"
 
-	"cmd/go/internal/modconv"
 	"cmd/go/internal/modfetch/codehost"
 	"cmd/go/internal/modfile"
 	"cmd/go/internal/module"
@@ -28,7 +24,8 @@
 
 // A codeRepo implements modfetch.Repo using an underlying codehost.Repo.
 type codeRepo struct {
-	modPath  string
+	modPath string
+
 	code     codehost.Repo
 	codeRoot string
 	codeDir  string
@@ -39,10 +36,9 @@
 	pseudoMajor string
 }
 
-func newCodeRepo(code codehost.Repo, path string) (Repo, error) {
-	codeRoot := code.Root()
-	if !hasPathPrefix(path, codeRoot) {
-		return nil, fmt.Errorf("mismatched repo: found %s for %s", codeRoot, path)
+func newCodeRepo(code codehost.Repo, root, path string) (Repo, error) {
+	if !hasPathPrefix(path, root) {
+		return nil, fmt.Errorf("mismatched repo: found %s for %s", root, path)
 	}
 	pathPrefix, pathMajor, ok := module.SplitPathVersion(path)
 	if !ok {
@@ -62,7 +58,7 @@
 	//
 	// Compute codeDir = bar, the subdirectory within the repo
 	// corresponding to the module root.
-	codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, codeRoot), "/")
+	codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, root), "/")
 	if strings.HasPrefix(path, "gopkg.in/") {
 		// But gopkg.in is a special legacy case, in which pathPrefix does not start with codeRoot.
 		// For example we might have:
@@ -78,7 +74,7 @@
 	r := &codeRepo{
 		modPath:     path,
 		code:        code,
-		codeRoot:    codeRoot,
+		codeRoot:    root,
 		codeDir:     codeDir,
 		pathPrefix:  pathPrefix,
 		pathMajor:   pathMajor,
@@ -110,7 +106,7 @@
 		if r.codeDir != "" {
 			v = v[len(r.codeDir)+1:]
 		}
-		if !semver.IsValid(v) || v != semver.Canonical(v) || isPseudoVersion(v) || !module.MatchPathMajor(v, r.pathMajor) {
+		if !semver.IsValid(v) || v != semver.Canonical(v) || IsPseudoVersion(v) || !module.MatchPathMajor(v, r.pathMajor) {
 			continue
 		}
 		list = append(list, v)
@@ -131,7 +127,7 @@
 	if err != nil {
 		return nil, err
 	}
-	return r.convert(info)
+	return r.convert(info, rev)
 }
 
 func (r *codeRepo) Latest() (*RevInfo, error) {
@@ -139,39 +135,70 @@
 	if err != nil {
 		return nil, err
 	}
-	return r.convert(info)
+	return r.convert(info, "")
 }
 
-func (r *codeRepo) convert(info *codehost.RevInfo) (*RevInfo, error) {
-	versionOK := func(v string) bool {
-		return semver.IsValid(v) && v == semver.Canonical(v) && !isPseudoVersion(v) && module.MatchPathMajor(v, r.pathMajor)
+func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) {
+	info2 := &RevInfo{
+		Name:  info.Name,
+		Short: info.Short,
+		Time:  info.Time,
 	}
-	v := info.Version
-	if r.codeDir == "" {
-		if !versionOK(v) {
-			v = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
-		}
+
+	// Determine version.
+	if semver.IsValid(statVers) && statVers == semver.Canonical(statVers) && module.MatchPathMajor(statVers, r.pathMajor) {
+		// The original call was repo.Stat(statVers), and requestedVersion is OK, so use it.
+		info2.Version = statVers
 	} else {
-		p := r.codeDir + "/"
-		if strings.HasPrefix(v, p) && versionOK(v[len(p):]) {
+		// Otherwise derive a version from a code repo tag.
+		// Tag must have a prefix matching codeDir.
+		p := ""
+		if r.codeDir != "" {
+			p = r.codeDir + "/"
+		}
+
+		tagOK := func(v string) bool {
+			if !strings.HasPrefix(v, p) {
+				return false
+			}
 			v = v[len(p):]
+			return semver.IsValid(v) && v == semver.Canonical(v) && module.MatchPathMajor(v, r.pathMajor) && !IsPseudoVersion(v)
+		}
+
+		// If info.Version is OK, use it.
+		if tagOK(info.Version) {
+			info2.Version = info.Version[len(p):]
 		} else {
-			v = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
+			// Otherwise look through all known tags for latest in semver ordering.
+			for _, tag := range info.Tags {
+				if tagOK(tag) && semver.Compare(info2.Version, tag[len(p):]) < 0 {
+					info2.Version = tag[len(p):]
+				}
+			}
+			// Otherwise make a pseudo-version.
+			if info2.Version == "" {
+				info2.Version = PseudoVersion(r.pseudoMajor, info.Time, info.Short)
+			}
 		}
 	}
 
-	info2 := &RevInfo{
-		Name:    info.Name,
-		Short:   info.Short,
-		Time:    info.Time,
-		Version: v,
+	// Do not allow a successful stat of a pseudo-version for a subdirectory
+	// unless the subdirectory actually does have a go.mod.
+	if IsPseudoVersion(info2.Version) && r.codeDir != "" {
+		_, _, _, err := r.findDir(info2.Version)
+		if err != nil {
+			// TODO: It would be nice to return an error like "not a module".
+			// Right now we return "missing go.mod", which is a little confusing.
+			return nil, err
+		}
 	}
+
 	return info2, nil
 }
 
 func (r *codeRepo) revToRev(rev string) string {
 	if semver.IsValid(rev) {
-		if isPseudoVersion(rev) {
+		if IsPseudoVersion(rev) {
 			i := strings.Index(rev, "-")
 			j := strings.Index(rev[i+1:], "-")
 			return rev[i+1+j+1:]
@@ -196,82 +223,100 @@
 	if err != nil {
 		return "", "", nil, err
 	}
-	if r.pathMajor == "" || strings.HasPrefix(r.pathMajor, ".") {
-		if r.codeDir == "" {
-			return rev, "", nil, nil
-		}
-		file1 := path.Join(r.codeDir, "go.mod")
-		gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod)
-		if err1 != nil {
-			return "", "", nil, fmt.Errorf("missing go.mod")
-		}
-		return rev, r.codeDir, gomod1, nil
-	}
 
-	// Suppose pathMajor is "/v2".
-	// Either go.mod should claim v2 and v2/go.mod should not exist,
-	// or v2/go.mod should exist and claim v2. Not both.
-	// Note that we don't check the full path, just the major suffix,
-	// because of replacement modules. This might be a fork of
-	// the real module, found at a different path, usable only in
-	// a replace directive.
+	// Load info about go.mod but delay consideration
+	// (except I/O error) until we rule out v2/go.mod.
 	file1 := path.Join(r.codeDir, "go.mod")
-	file2 := path.Join(r.codeDir, r.pathMajor[1:], "go.mod")
 	gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod)
-	gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
-	found1 := err1 == nil && isMajor(gomod1, r.pathMajor)
-	found2 := err2 == nil && isMajor(gomod2, r.pathMajor)
+	if err1 != nil && !os.IsNotExist(err1) {
+		return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file1, rev, err1)
+	}
+	mpath1 := modfile.ModulePath(gomod1)
+	found1 := err1 == nil && isMajor(mpath1, r.pathMajor)
 
-	if err2 == nil && !found2 {
-		return "", "", nil, fmt.Errorf("%s has non-...%s module path", file2, r.pathMajor)
+	var file2 string
+	if r.pathMajor != "" && !strings.HasPrefix(r.pathMajor, ".") {
+		// Suppose pathMajor is "/v2".
+		// Either go.mod should claim v2 and v2/go.mod should not exist,
+		// or v2/go.mod should exist and claim v2. Not both.
+		// Note that we don't check the full path, just the major suffix,
+		// because of replacement modules. This might be a fork of
+		// the real module, found at a different path, usable only in
+		// a replace directive.
+		dir2 := path.Join(r.codeDir, r.pathMajor[1:])
+		file2 = path.Join(dir2, "go.mod")
+		gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
+		if err2 != nil && !os.IsNotExist(err2) {
+			return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file2, rev, err2)
+		}
+		mpath2 := modfile.ModulePath(gomod2)
+		found2 := err2 == nil && isMajor(mpath2, r.pathMajor)
+
+		if found1 && found2 {
+			return "", "", nil, fmt.Errorf("%s/%s and ...%s/go.mod both have ...%s module paths at revision %s", r.pathPrefix, file1, r.pathMajor, r.pathMajor, rev)
+		}
+		if found2 {
+			return rev, dir2, gomod2, nil
+		}
+		if err2 == nil {
+			if mpath2 == "" {
+				return "", "", nil, fmt.Errorf("%s/%s is missing module path at revision %s", r.pathPrefix, file2, rev)
+			}
+			return "", "", nil, fmt.Errorf("%s/%s has non-...%s module path %q at revision %s", r.pathPrefix, file2, r.pathMajor, mpath2, rev)
+		}
 	}
-	if found1 && found2 {
-		return "", "", nil, fmt.Errorf("both %s and %s claim ...%s module", file1, file2, r.pathMajor)
-	}
-	if found2 {
-		return rev, filepath.Join(r.codeDir, r.pathMajor), gomod2, nil
-	}
+
+	// Not v2/go.mod, so it's either go.mod or nothing. Which is it?
 	if found1 {
+		// Explicit go.mod with matching module path OK.
 		return rev, r.codeDir, gomod1, nil
 	}
-	return "", "", nil, fmt.Errorf("missing or invalid go.mod")
-}
-
-func isMajor(gomod []byte, pathMajor string) bool {
-	return strings.HasSuffix(modPath(gomod), pathMajor)
-}
-
-var moduleStr = []byte("module")
-
-func modPath(mod []byte) string {
-	for len(mod) > 0 {
-		line := mod
-		mod = nil
-		if i := bytes.IndexByte(line, '\n'); i >= 0 {
-			line, mod = line[:i], line[i+1:]
+	if err1 == nil {
+		// Explicit go.mod with non-matching module path disallowed.
+		suffix := ""
+		if file2 != "" {
+			suffix = fmt.Sprintf(" (and ...%s/go.mod does not exist)", r.pathMajor)
 		}
-		line = bytes.TrimSpace(line)
-		if !bytes.HasPrefix(line, moduleStr) {
-			continue
+		if mpath1 == "" {
+			return "", "", nil, fmt.Errorf("%s is missing module path%s at revision %s", file1, suffix, rev)
 		}
-		line = line[len(moduleStr):]
-		n := len(line)
-		line = bytes.TrimSpace(line)
-		if len(line) == n || len(line) == 0 {
-			continue
+		if r.pathMajor != "" { // ".v1", ".v2" for gopkg.in
+			return "", "", nil, fmt.Errorf("%s has non-...%s module path %q%s at revision %s", file1, r.pathMajor, mpath1, suffix, rev)
 		}
-
-		if line[0] == '"' || line[0] == '`' {
-			p, err := strconv.Unquote(string(line))
-			if err != nil {
-				return "" // malformed quoted string or multiline module path
-			}
-			return p
-		}
-
-		return string(line)
+		return "", "", nil, fmt.Errorf("%s has post-%s module path %q%s at revision %s", file1, semver.Major(version), mpath1, suffix, rev)
 	}
-	return "" // missing module path
+
+	if r.codeDir == "" && (r.pathMajor == "" || strings.HasPrefix(r.pathMajor, ".")) {
+		// Implicit go.mod at root of repo OK for v0/v1 and for gopkg.in.
+		return rev, "", nil, nil
+	}
+
+	// Implicit go.mod below root of repo or at v2+ disallowed.
+	// Be clear about possibility of using either location for v2+.
+	if file2 != "" {
+		return "", "", nil, fmt.Errorf("missing %s/go.mod and ...%s/go.mod at revision %s", r.pathPrefix, r.pathMajor, rev)
+	}
+	return "", "", nil, fmt.Errorf("missing %s/go.mod at revision %s", r.pathPrefix, rev)
+}
+
+func isMajor(mpath, pathMajor string) bool {
+	if mpath == "" {
+		return false
+	}
+	if pathMajor == "" {
+		// mpath must NOT have version suffix.
+		i := len(mpath)
+		for i > 0 && '0' <= mpath[i-1] && mpath[i-1] <= '9' {
+			i--
+		}
+		if i < len(mpath) && i >= 2 && mpath[i-1] == 'v' && mpath[i-2] == '/' {
+			// Found valid suffix.
+			return false
+		}
+		return true
+	}
+	// Otherwise pathMajor is ".v1", ".v2" (gopkg.in), or "/v2", "/v3" etc.
+	return strings.HasSuffix(mpath, pathMajor)
 }
 
 func (r *codeRepo) GoMod(version string) (data []byte, err error) {
@@ -284,7 +329,7 @@
 	}
 	data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod)
 	if err != nil {
-		if e := strings.ToLower(err.Error()); strings.Contains(e, "not found") || strings.Contains(e, "404") { // TODO
+		if os.IsNotExist(err) {
 			return r.legacyGoMod(rev, dir), nil
 		}
 		return nil, err
@@ -292,41 +337,15 @@
 	return data, nil
 }
 
-var altConfigs = []string{
-	"Gopkg.lock",
-
-	"GLOCKFILE",
-	"Godeps/Godeps.json",
-	"dependencies.tsv",
-	"glide.lock",
-	"vendor.conf",
-	"vendor.yml",
-	"vendor/manifest",
-	"vendor/vendor.json",
-}
-
 func (r *codeRepo) legacyGoMod(rev, dir string) []byte {
-	mf := new(modfile.File)
-	mf.AddModuleStmt(r.modPath)
-	for _, file := range altConfigs {
-		data, err := r.code.ReadFile(rev, path.Join(dir, file), codehost.MaxGoMod)
-		if err != nil {
-			continue
-		}
-		convert := modconv.Converters[file]
-		if convert == nil {
-			continue
-		}
-		if err := ConvertLegacyConfig(mf, file, data); err != nil {
-			continue
-		}
-		break
-	}
-	data, err := mf.Format()
-	if err != nil {
-		return []byte(fmt.Sprintf("%s\nmodule %q\n", modconv.Prefix, r.modPath))
-	}
-	return append([]byte(modconv.Prefix+"\n"), data...)
+	// We used to try to build a go.mod reflecting pre-existing
+	// package management metadata files, but the conversion
+	// was inherently imperfect (because those files don't have
+	// exactly the same semantics as go.mod) and, when done
+	// for dependencies in the middle of a build, impossible to
+	// correct. So we stopped.
+	// Return a fake go.mod that simply declares the module path.
+	return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(r.modPath)))
 }
 
 func (r *codeRepo) modPrefix(rev string) string {
@@ -348,7 +367,7 @@
 	subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
 
 	// Spool to local file.
-	f, err := ioutil.TempFile(tmpdir, "vgo-codehost-")
+	f, err := ioutil.TempFile(tmpdir, "go-codehost-")
 	if err != nil {
 		dl.Close()
 		return "", err
@@ -375,7 +394,7 @@
 	if err != nil {
 		return "", err
 	}
-	f2, err := ioutil.TempFile(tmpdir, "vgo-")
+	f2, err := ioutil.TempFile(tmpdir, "go-codezip-")
 	if err != nil {
 		return "", err
 	}
@@ -441,6 +460,11 @@
 		if !strings.HasPrefix(name, subdir) {
 			continue
 		}
+		if name == ".hg_archival.txt" {
+			// Inserted by hg archive.
+			// Not correct to drop from other version control systems, but too bad.
+			continue
+		}
 		name = strings.TrimPrefix(name, subdir)
 		if isVendoredPackage(name) {
 			continue
@@ -476,7 +500,8 @@
 	}
 
 	if !haveLICENSE && subdir != "" {
-		if data, err := r.code.ReadFile(rev, "LICENSE", codehost.MaxLICENSE); err == nil {
+		data, err := r.code.ReadFile(rev, "LICENSE", codehost.MaxLICENSE)
+		if err == nil {
 			w, err := zw.Create(r.modPrefix(version) + "/LICENSE")
 			if err != nil {
 				return "", err
@@ -571,6 +596,23 @@
 
 var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
 
-func isPseudoVersion(v string) bool {
+// IsPseudoVersion reports whether v is a pseudo-version.
+func IsPseudoVersion(v string) bool {
 	return pseudoVersionRE.MatchString(v)
 }
+
+// PseudoVersionTime returns the time stamp of the pseudo-version v.
+// It returns an error if v is not a pseudo-version or if the time stamp
+// embedded in the pseudo-version is not a valid time.
+func PseudoVersionTime(v string) (time.Time, error) {
+	if !IsPseudoVersion(v) {
+		return time.Time{}, fmt.Errorf("not a pseudo-version")
+	}
+	i := strings.Index(v, "-") + 1
+	j := i + strings.Index(v[i:], "-")
+	t, err := time.Parse("20060102150405", v[i:j])
+	if err != nil {
+		return time.Time{}, fmt.Errorf("malformed pseudo-version %q", v)
+	}
+	return t, nil
+}
diff --git a/src/cmd/go/internal/modfetch/coderepo_test.go b/src/cmd/go/internal/modfetch/coderepo_test.go
index e3b106e..b1790e6 100644
--- a/src/cmd/go/internal/modfetch/coderepo_test.go
+++ b/src/cmd/go/internal/modfetch/coderepo_test.go
@@ -19,10 +19,6 @@
 	"cmd/go/internal/modfetch/codehost"
 )
 
-func init() {
-	isTest = true
-}
-
 func TestMain(m *testing.M) {
 	os.Exit(testMain(m))
 }
@@ -38,6 +34,15 @@
 	return m.Run()
 }
 
+const (
+	vgotest1git = "github.com/rsc/vgotest1"
+	vgotest1hg  = "vcs-test.golang.org/hg/vgotest1.hg"
+)
+
+var altVgotests = []string{
+	vgotest1hg,
+}
+
 var codeRepoTests = []struct {
 	path     string
 	lookerr  string
@@ -86,12 +91,12 @@
 		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
 		short:   "80d85c5d4d17",
 		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
-		ziperr:  "missing go.mod",
+		ziperr:  "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
 	},
 	{
 		path:    "github.com/rsc/vgotest1",
-		rev:     "80d85",
-		version: "v0.0.0-20180219231006-80d85c5d4d17",
+		rev:     "80d85c5",
+		version: "v1.0.0",
 		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
 		short:   "80d85c5d4d17",
 		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
@@ -104,7 +109,7 @@
 	{
 		path:    "github.com/rsc/vgotest1",
 		rev:     "mytag",
-		version: "v0.0.0-20180219231006-80d85c5d4d17",
+		version: "v1.0.0",
 		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
 		short:   "80d85c5d4d17",
 		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
@@ -116,32 +121,32 @@
 	},
 	{
 		path:     "github.com/rsc/vgotest1/v2",
-		rev:      "80d85",
-		version:  "v2.0.0-20180219231006-80d85c5d4d17",
+		rev:      "80d85c5",
+		version:  "v2.0.0",
 		name:     "80d85c5d4d17598a0e9055e7c175a32b415d6128",
 		short:    "80d85c5d4d17",
 		time:     time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
-		gomoderr: "missing go.mod",
-		ziperr:   "missing go.mod",
+		gomoderr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
+		ziperr:   "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
 	},
 	{
 		path:    "github.com/rsc/vgotest1/v54321",
-		rev:     "80d85",
+		rev:     "80d85c5",
 		version: "v54321.0.0-20180219231006-80d85c5d4d17",
 		name:    "80d85c5d4d17598a0e9055e7c175a32b415d6128",
 		short:   "80d85c5d4d17",
 		time:    time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
-		ziperr:  "missing go.mod",
+		ziperr:  "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17",
 	},
 	{
 		path: "github.com/rsc/vgotest1/submod",
 		rev:  "v1.0.0",
-		err:  "unknown revision \"submod/v1.0.0\"",
+		err:  "unknown revision submod/v1.0.0",
 	},
 	{
 		path: "github.com/rsc/vgotest1/submod",
 		rev:  "v1.0.3",
-		err:  "unknown revision \"submod/v1.0.3\"",
+		err:  "unknown revision submod/v1.0.3",
 	},
 	{
 		path:    "github.com/rsc/vgotest1/submod",
@@ -188,7 +193,7 @@
 		name:     "f18795870fb14388a21ef3ebc1d75911c8694f31",
 		short:    "f18795870fb1",
 		time:     time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC),
-		gomoderr: "v2/go.mod has non-.../v2 module path",
+		gomoderr: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3",
 	},
 	{
 		path:     "github.com/rsc/vgotest1/v2",
@@ -197,7 +202,7 @@
 		name:     "1f863feb76bc7029b78b21c5375644838962f88d",
 		short:    "1f863feb76bc",
 		time:     time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC),
-		gomoderr: "both go.mod and v2/go.mod claim .../v2 module",
+		gomoderr: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4",
 	},
 	{
 		path:    "github.com/rsc/vgotest1/v2",
@@ -209,33 +214,6 @@
 		gomod:   "module \"github.com/rsc/vgotest1/v2\" // v2/go.mod\n",
 	},
 	{
-		path:    "go.googlesource.com/scratch",
-		rev:     "0f302529858",
-		version: "v0.0.0-20180220024720-0f3025298580",
-		name:    "0f30252985809011f026b5a2d5cf456e021623da",
-		short:   "0f3025298580",
-		time:    time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
-		gomod:   "//vgo 0.0.4\n\nmodule go.googlesource.com/scratch\n",
-	},
-	{
-		path:    "go.googlesource.com/scratch/rsc",
-		rev:     "0f302529858",
-		version: "v0.0.0-20180220024720-0f3025298580",
-		name:    "0f30252985809011f026b5a2d5cf456e021623da",
-		short:   "0f3025298580",
-		time:    time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
-		gomod:   "",
-	},
-	{
-		path:     "go.googlesource.com/scratch/cbro",
-		rev:      "0f302529858",
-		version:  "v0.0.0-20180220024720-0f3025298580",
-		name:     "0f30252985809011f026b5a2d5cf456e021623da",
-		short:    "0f3025298580",
-		time:     time.Date(2018, 2, 20, 2, 47, 20, 0, time.UTC),
-		gomoderr: "missing go.mod",
-	},
-	{
 		// redirect to github
 		path:    "rsc.io/quote",
 		rev:     "v1.0.0",
@@ -274,27 +252,26 @@
 	},
 	{
 		// package in subdirectory - custom domain
-		path:    "golang.org/x/net/context",
-		lookerr: "module root is \"golang.org/x/net\"",
+		// In general we can't reject these definitively in Lookup,
+		// but gopkg.in is special.
+		path:    "gopkg.in/yaml.v2/abc",
+		lookerr: "invalid module path \"gopkg.in/yaml.v2/abc\"",
 	},
 	{
 		// package in subdirectory - github
-		path:     "github.com/rsc/quote/buggy",
-		rev:      "c4d4236f",
-		version:  "v0.0.0-20180214154420-c4d4236f9242",
-		name:     "c4d4236f92427c64bfbcf1cc3f8142ab18f30b22",
-		short:    "c4d4236f9242",
-		time:     time.Date(2018, 2, 14, 15, 44, 20, 0, time.UTC),
-		gomoderr: "missing go.mod",
+		// Because it's a package, Stat should fail entirely.
+		path: "github.com/rsc/quote/buggy",
+		rev:  "c4d4236f",
+		err:  "missing github.com/rsc/quote/buggy/go.mod at revision c4d4236f9242",
 	},
 	{
 		path:    "gopkg.in/yaml.v2",
 		rev:     "d670f940",
-		version: "v2.0.0-20180109114331-d670f9405373",
+		version: "v2.0.0",
 		name:    "d670f9405373e636a5a2765eea47fac0c9bc91a4",
 		short:   "d670f9405373",
 		time:    time.Date(2018, 1, 9, 11, 43, 31, 0, time.UTC),
-		gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/yaml.v2\n",
+		gomod:   "module gopkg.in/yaml.v2\n",
 	},
 	{
 		path:    "gopkg.in/check.v1",
@@ -303,12 +280,12 @@
 		name:    "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
 		short:   "20d25e280405",
 		time:    time.Date(2016, 12, 8, 18, 13, 25, 0, time.UTC),
-		gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/check.v1\n",
+		gomod:   "module gopkg.in/check.v1\n",
 	},
 	{
 		path:    "gopkg.in/yaml.v2",
 		rev:     "v2",
-		version: "v2.0.0-20180328195020-5420a8b6744d",
+		version: "v2.2.1",
 		name:    "5420a8b6744d3b0345ab293f6fcba19c978f1183",
 		short:   "5420a8b6744d",
 		time:    time.Date(2018, 3, 28, 19, 50, 20, 0, time.UTC),
@@ -317,11 +294,11 @@
 	{
 		path:    "vcs-test.golang.org/go/mod/gitrepo1",
 		rev:     "master",
-		version: "v0.0.0-20180417194322-ede458df7cd0",
+		version: "v1.2.4-annotated",
 		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",
+		gomod:   "module vcs-test.golang.org/go/mod/gitrepo1\n",
 	},
 	{
 		path:    "gopkg.in/natefinch/lumberjack.v2",
@@ -330,7 +307,7 @@
 		name:    "a96e63847dc3c67d17befa69c303767e2f84e54f",
 		short:   "a96e63847dc3",
 		time:    time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
-		gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/natefinch/lumberjack.v2\n",
+		gomod:   "module gopkg.in/natefinch/lumberjack.v2\n",
 	},
 	{
 		path: "gopkg.in/natefinch/lumberjack.v2",
@@ -345,7 +322,7 @@
 		name:    "a96e63847dc3c67d17befa69c303767e2f84e54f",
 		short:   "a96e63847dc3",
 		time:    time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
-		gomod:   "//vgo 0.0.4\n\nmodule gopkg.in/natefinch/lumberjack.v2\n",
+		gomod:   "module gopkg.in/natefinch/lumberjack.v2\n",
 	},
 }
 
@@ -358,15 +335,15 @@
 	}
 	defer os.RemoveAll(tmpdir)
 	for _, tt := range codeRepoTests {
-		t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, func(t *testing.T) {
+		f := func(t *testing.T) {
 			repo, err := Lookup(tt.path)
-			if err != nil {
-				if tt.lookerr != "" {
-					if err.Error() == tt.lookerr {
-						return
-					}
-					t.Errorf("Lookup(%q): %v, want error %q", tt.path, err, tt.lookerr)
+			if tt.lookerr != "" {
+				if err != nil && err.Error() == tt.lookerr {
+					return
 				}
+				t.Errorf("Lookup(%q): %v, want error %q", tt.path, err, tt.lookerr)
+			}
+			if err != nil {
 				t.Fatalf("Lookup(%q): %v", tt.path, err)
 			}
 			if tt.mpath == "" {
@@ -446,59 +423,64 @@
 					t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
 				}
 			}
-		})
-	}
-}
-
-var importTests = []struct {
-	path  string
-	mpath string
-	err   string
-}{
-	{
-		path:  "golang.org/x/net/context",
-		mpath: "golang.org/x/net",
-	},
-	{
-		path:  "github.com/rsc/quote/buggy",
-		mpath: "github.com/rsc/quote",
-	},
-	{
-		path:  "golang.org/x/net",
-		mpath: "golang.org/x/net",
-	},
-	{
-		path:  "github.com/rsc/quote",
-		mpath: "github.com/rsc/quote",
-	},
-	{
-		path: "golang.org/x/foo/bar",
-		err:  "unknown module golang.org/x/foo/bar: no go-import tags",
-	},
-}
-
-func TestImport(t *testing.T) {
-	testenv.MustHaveExternalNetwork(t)
-
-	for _, tt := range importTests {
-		t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
-			repo, info, err := Import(tt.path, nil)
-			if err != nil {
-				if tt.err != "" {
-					if err.Error() == tt.err {
-						return
-					}
-					t.Errorf("Import(%q): %v, want error %q", tt.path, err, tt.err)
+		}
+		t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, f)
+		if strings.HasPrefix(tt.path, vgotest1git) {
+			for _, alt := range altVgotests {
+				// Note: Communicating with f through tt; should be cleaned up.
+				old := tt
+				tt.path = alt + strings.TrimPrefix(tt.path, vgotest1git)
+				if strings.HasPrefix(tt.mpath, vgotest1git) {
+					tt.mpath = alt + strings.TrimPrefix(tt.mpath, vgotest1git)
 				}
-				t.Fatalf("Lookup(%q): %v", tt.path, err)
+				var m map[string]string
+				if alt == vgotest1hg {
+					m = hgmap
+				}
+				tt.version = remap(tt.version, m)
+				tt.name = remap(tt.name, m)
+				tt.short = remap(tt.short, m)
+				tt.rev = remap(tt.rev, m)
+				tt.gomoderr = remap(tt.gomoderr, m)
+				tt.ziperr = remap(tt.ziperr, m)
+				t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, f)
+				tt = old
 			}
-			if mpath := repo.ModulePath(); mpath != tt.mpath {
-				t.Errorf("repo.ModulePath() = %q (%v), want %q", mpath, info.Version, tt.mpath)
-			}
-		})
+		}
 	}
 }
 
+var hgmap = map[string]string{
+	"github.com/rsc/vgotest1/":                 "vcs-test.golang.org/hg/vgotest1.hg/",
+	"f18795870fb14388a21ef3ebc1d75911c8694f31": "a9ad6d1d14eb544f459f446210c7eb3b009807c6",
+	"ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9": "f1fc0f22021b638d073d31c752847e7bf385def7",
+	"b769f2de407a4db81af9c5de0a06016d60d2ea09": "92c7eb888b4fac17f1c6bd2e1060a1b881a3b832",
+	"8afe2b2efed96e0880ecd2a69b98a53b8c2738b6": "4e58084d459ae7e79c8c2264d0e8e9a92eb5cd44",
+	"2f615117ce481c8efef46e0cc0b4b4dccfac8fea": "879ea98f7743c8eff54f59a918f3a24123d1cf46",
+	"80d85c5d4d17598a0e9055e7c175a32b415d6128": "e125018e286a4b09061079a81e7b537070b7ff71",
+	"1f863feb76bc7029b78b21c5375644838962f88d": "bf63880162304a9337477f3858f5b7e255c75459",
+}
+
+func remap(name string, m map[string]string) string {
+	if m[name] != "" {
+		return m[name]
+	}
+	if codehost.AllHex(name) {
+		for k, v := range m {
+			if strings.HasPrefix(k, name) {
+				return v[:len(name)]
+			}
+		}
+	}
+	for k, v := range m {
+		name = strings.Replace(name, k, v, -1)
+		if codehost.AllHex(k) {
+			name = strings.Replace(name, k[:12], v[:12], -1)
+		}
+	}
+	return name
+}
+
 var codeRepoVersionsTests = []struct {
 	path     string
 	prefix   string
@@ -528,7 +510,7 @@
 	},
 	{
 		path:     "gopkg.in/natefinch/lumberjack.v2",
-		versions: []string{},
+		versions: nil,
 	},
 }
 
@@ -571,6 +553,10 @@
 		version: "v0.0.0-20180219223237-a08abb797a67",
 	},
 	{
+		path: "github.com/rsc/vgotest1/subdir",
+		err:  "missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67",
+	},
+	{
 		path:    "swtch.com/testmod",
 		version: "v1.1.1",
 	},
@@ -601,6 +587,9 @@
 				}
 				t.Fatalf("Latest(): %v", err)
 			}
+			if tt.err != "" {
+				t.Fatalf("Latest() = %v, want error %q", info.Version, tt.err)
+			}
 			if info.Version != tt.version {
 				t.Fatalf("Latest() = %v, want %v", info.Version, tt.version)
 			}
@@ -610,12 +599,10 @@
 
 // fixedTagsRepo is a fake codehost.Repo that returns a fixed list of tags
 type fixedTagsRepo struct {
-	root string
 	tags []string
 }
 
 func (ch *fixedTagsRepo) Tags(string) ([]string, error)                  { return ch.tags, nil }
-func (ch *fixedTagsRepo) Root() string                                   { return ch.root }
 func (ch *fixedTagsRepo) Latest() (*codehost.RevInfo, error)             { panic("not impl") }
 func (ch *fixedTagsRepo) ReadFile(string, string, int64) ([]byte, error) { panic("not impl") }
 func (ch *fixedTagsRepo) ReadZip(string, string, int64) (io.ReadCloser, string, error) {
@@ -626,7 +613,6 @@
 func TestNonCanonicalSemver(t *testing.T) {
 	root := "golang.org/x/issue24476"
 	ch := &fixedTagsRepo{
-		root: root,
 		tags: []string{
 			"", "huh?", "1.0.1",
 			// what about "version 1 dot dogcow"?
@@ -637,7 +623,7 @@
 		},
 	}
 
-	cr, err := newCodeRepo(ch, root)
+	cr, err := newCodeRepo(ch, root, root)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -650,39 +636,3 @@
 		t.Fatal("unexpected versions returned:", v)
 	}
 }
-
-var modPathTests = []struct {
-	input    []byte
-	expected string
-}{
-	{input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
-	{input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
-	{input: []byte("module  \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
-	{input: []byte("module  github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
-	{input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"},
-	{input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"},
-	{input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"},
-	{input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"},
-	{input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"},
-	{input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"},
-	{input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""},
-	{input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"},
-	{input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"},
-	{input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"},
-	{input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"},
-	{input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"},
-	{input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""},
-	{input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""},
-	{input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""},
-}
-
-func TestModPath(t *testing.T) {
-	for _, test := range modPathTests {
-		t.Run(string(test.input), func(t *testing.T) {
-			result := modPath(test.input)
-			if result != test.expected {
-				t.Fatalf("modPath(%s): %s, want %s", string(test.input), result, test.expected)
-			}
-		})
-	}
-}
diff --git a/src/cmd/go/internal/modfetch/convert.go b/src/cmd/go/internal/modfetch/convert.go
deleted file mode 100644
index 5c482be..0000000
--- a/src/cmd/go/internal/modfetch/convert.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package modfetch
-
-import (
-	"fmt"
-	"os"
-	"sort"
-	"strings"
-
-	"cmd/go/internal/modconv"
-	"cmd/go/internal/modfile"
-	"cmd/go/internal/semver"
-)
-
-// ConvertLegacyConfig converts legacy config to modfile.
-// The file argument is slash-delimited.
-func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error {
-	i := strings.LastIndex(file, "/")
-	j := -2
-	if i >= 0 {
-		j = strings.LastIndex(file[:i], "/")
-	}
-	convert := modconv.Converters[file[i+1:]]
-	if convert == nil && j != -2 {
-		convert = modconv.Converters[file[j+1:]]
-	}
-	if convert == nil {
-		return fmt.Errorf("unknown legacy config file %s", file)
-	}
-	require, err := convert(file, data)
-	if err != nil {
-		return fmt.Errorf("parsing %s: %v", file, err)
-	}
-
-	// Convert requirements block, which may use raw SHA1 hashes as versions,
-	// to valid semver requirement list, respecting major versions.
-	need := make(map[string]string)
-	for _, r := range require {
-		if r.Path == "" {
-			continue
-		}
-
-		// TODO: Something better here.
-		if strings.HasPrefix(r.Path, "github.com/") || strings.HasPrefix(r.Path, "golang.org/x/") {
-			f := strings.Split(r.Path, "/")
-			if len(f) > 3 {
-				r.Path = strings.Join(f[:3], "/")
-			}
-		}
-
-		repo, err := Lookup(r.Path)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "vgo: lookup %s: %v\n", r.Path, err)
-			continue
-		}
-		info, err := repo.Stat(r.Version)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "vgo: stat %s@%s: %v\n", r.Path, r.Version, err)
-			continue
-		}
-		path := repo.ModulePath()
-		need[path] = semver.Max(need[path], info.Version)
-	}
-
-	var paths []string
-	for path := range need {
-		paths = append(paths, path)
-	}
-	sort.Strings(paths)
-	for _, path := range paths {
-		f.AddRequire(path, need[path])
-	}
-
-	return nil
-}
diff --git a/src/cmd/go/internal/modfetch/domain.go b/src/cmd/go/internal/modfetch/domain.go
deleted file mode 100644
index 2494f80..0000000
--- a/src/cmd/go/internal/modfetch/domain.go
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Support for custom domains.
-
-package modfetch
-
-import (
-	"encoding/xml"
-	"fmt"
-	"io"
-	"net/url"
-	"os"
-	"strings"
-
-	"cmd/go/internal/modfetch/codehost"
-	"cmd/go/internal/modfetch/gitrepo"
-)
-
-// metaImport represents the parsed <meta name="go-import"
-// content="prefix vcs reporoot" /> tags from HTML files.
-type metaImport struct {
-	Prefix, VCS, RepoRoot string
-}
-
-func lookupCustomDomain(path string) (Repo, error) {
-	dom := path
-	if i := strings.Index(dom, "/"); i >= 0 {
-		dom = dom[:i]
-	}
-	if !strings.Contains(dom, ".") {
-		return nil, fmt.Errorf("unknown module %s: not a domain name", path)
-	}
-	var body io.ReadCloser
-	err := webGetGoGet("https://"+path+"?go-get=1", &body)
-	if body != nil {
-		defer body.Close()
-	}
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "FindRepo: %v\n", err)
-		return nil, err
-	}
-	// Note: accepting a non-200 OK here, so people can serve a
-	// meta import in their http 404 page.
-	imports, err := parseMetaGoImports(body)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "findRepo: %v\n", err)
-		return nil, err
-	}
-	if len(imports) == 0 {
-		return nil, fmt.Errorf("unknown module %s: no go-import tags", path)
-	}
-
-	// First look for new module definition.
-	for _, imp := range imports {
-		if path == imp.Prefix || strings.HasPrefix(path, imp.Prefix+"/") {
-			if imp.VCS == "mod" {
-				u, err := url.Parse(imp.RepoRoot)
-				if err != nil {
-					return nil, fmt.Errorf("invalid module URL %q", imp.RepoRoot)
-				} else if u.Scheme != "https" {
-					// TODO: Allow -insecure flag as a build flag?
-					return nil, fmt.Errorf("invalid module URL %q: must be HTTPS", imp.RepoRoot)
-				}
-				return newProxyRepo(imp.RepoRoot, imp.Prefix), nil
-			}
-		}
-	}
-
-	// 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)
-			}
-			if imp.VCS == "git" {
-				code, err := gitrepo.Repo(imp.RepoRoot, imp.Prefix)
-				if err != nil {
-					return nil, err
-				}
-				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)
-}
-
-type ModuleSubdirError struct {
-	ModulePath string
-}
-
-func (e *ModuleSubdirError) Error() string {
-	return fmt.Sprintf("module root is %q", e.ModulePath)
-}
-
-type customPrefix struct {
-	codehost.Repo
-	root string
-}
-
-func (c *customPrefix) Root() string {
-	return c.root
-}
-
-// parseMetaGoImports returns meta imports from the HTML in r.
-// Parsing ends at the end of the <head> section or the beginning of the <body>.
-func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
-	d := xml.NewDecoder(r)
-	d.CharsetReader = charsetReader
-	d.Strict = false
-	var t xml.Token
-	for {
-		t, err = d.RawToken()
-		if err != nil {
-			if err == io.EOF || len(imports) > 0 {
-				err = nil
-			}
-			return
-		}
-		if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
-			return
-		}
-		if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
-			return
-		}
-		e, ok := t.(xml.StartElement)
-		if !ok || !strings.EqualFold(e.Name.Local, "meta") {
-			continue
-		}
-		if attrValue(e.Attr, "name") != "go-import" {
-			continue
-		}
-		if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
-			imports = append(imports, metaImport{
-				Prefix:   f[0],
-				VCS:      f[1],
-				RepoRoot: f[2],
-			})
-		}
-	}
-}
-
-// attrValue returns the attribute value for the case-insensitive key
-// `name', or the empty string if nothing is found.
-func attrValue(attrs []xml.Attr, name string) string {
-	for _, a := range attrs {
-		if strings.EqualFold(a.Name.Local, name) {
-			return a.Value
-		}
-	}
-	return ""
-}
-
-// charsetReader returns a reader for the given charset. Currently
-// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful
-// error which is printed by go get, so the user can find why the package
-// wasn't downloaded if the encoding is not supported. Note that, in
-// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters
-// greater than 0x7f are not rejected).
-func charsetReader(charset string, input io.Reader) (io.Reader, error) {
-	switch strings.ToLower(charset) {
-	case "ascii":
-		return input, nil
-	default:
-		return nil, fmt.Errorf("can't decode XML document using charset %q", charset)
-	}
-}
diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go
new file mode 100644
index 0000000..87797f9
--- /dev/null
+++ b/src/cmd/go/internal/modfetch/fetch.go
@@ -0,0 +1,302 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modfetch
+
+import (
+	"archive/zip"
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+	"sync"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/dirhash"
+	"cmd/go/internal/module"
+	"cmd/go/internal/par"
+)
+
+var downloadCache par.Cache
+
+// Download downloads the specific module version to the
+// local download cache and returns the name of the directory
+// corresponding to the root of the module's file tree.
+func Download(mod module.Version) (dir string, err error) {
+	if SrcMod == "" {
+		// Do not download to current directory.
+		return "", fmt.Errorf("missing modfetch.SrcMod")
+	}
+
+	// The par.Cache here avoids duplicate work but also
+	// avoids conflicts from simultaneous calls by multiple goroutines
+	// for the same version.
+	type cached struct {
+		dir string
+		err error
+	}
+	c := downloadCache.Do(mod, func() interface{} {
+		modpath := mod.Path + "@" + mod.Version
+		dir = filepath.Join(SrcMod, modpath)
+		if files, _ := ioutil.ReadDir(dir); len(files) == 0 {
+			zipfile := filepath.Join(SrcMod, "cache/download", mod.Path, "@v", mod.Version+".zip")
+			if _, err := os.Stat(zipfile); err == nil {
+				// Use it.
+				// This should only happen if the mod/cache directory is preinitialized
+				// or if src/mod/path was removed but not src/mod/cache/download.
+				fmt.Fprintf(os.Stderr, "go: extracting %s %s\n", mod.Path, mod.Version)
+			} else {
+				if err := os.MkdirAll(filepath.Join(SrcMod, "cache/download", mod.Path, "@v"), 0777); err != nil {
+					return cached{"", err}
+				}
+				fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version)
+				if err := downloadZip(mod, zipfile); err != nil {
+					return cached{"", err}
+				}
+			}
+			if err := Unzip(dir, zipfile, modpath, 0); err != nil {
+				fmt.Fprintf(os.Stderr, "-> %s\n", err)
+				return cached{"", err}
+			}
+		}
+		checkSum(mod)
+		return cached{dir, nil}
+	}).(cached)
+	return c.dir, c.err
+}
+
+func downloadZip(mod module.Version, target string) error {
+	repo, err := Lookup(mod.Path)
+	if err != nil {
+		return err
+	}
+	tmpfile, err := repo.Zip(mod.Version, os.TempDir())
+	if err != nil {
+		return err
+	}
+	defer os.Remove(tmpfile)
+
+	// Double-check zip file looks OK.
+	z, err := zip.OpenReader(tmpfile)
+	if err != nil {
+		z.Close()
+		return err
+	}
+	prefix := mod.Path + "@" + mod.Version
+	for _, f := range z.File {
+		if !strings.HasPrefix(f.Name, prefix) {
+			z.Close()
+			return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
+		}
+	}
+	z.Close()
+
+	hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash)
+	if err != nil {
+		return err
+	}
+	checkOneSum(mod, hash) // check before installing the zip file
+	r, err := os.Open(tmpfile)
+	if err != nil {
+		return err
+	}
+	defer r.Close()
+	w, err := os.Create(target)
+	if err != nil {
+		return err
+	}
+	if _, err := io.Copy(w, r); err != nil {
+		w.Close()
+		return fmt.Errorf("copying: %v", err)
+	}
+	if err := w.Close(); err != nil {
+		return err
+	}
+	return ioutil.WriteFile(target+"hash", []byte(hash), 0666)
+}
+
+var GoSumFile string // path to go.sum; set by package modload
+
+var goSum struct {
+	mu        sync.Mutex
+	m         map[module.Version][]string // content of go.sum file (+ go.modverify if present)
+	enabled   bool                        // whether to use go.sum at all
+	modverify string                      // path to go.modverify, to be deleted
+}
+
+// initGoSum initializes the go.sum data.
+// It reports whether use of go.sum is now enabled.
+// The goSum lock must be held.
+func initGoSum() bool {
+	if GoSumFile == "" {
+		return false
+	}
+	if goSum.m != nil {
+		return true
+	}
+
+	goSum.m = make(map[module.Version][]string)
+	data, err := ioutil.ReadFile(GoSumFile)
+	if err != nil && !os.IsNotExist(err) {
+		base.Fatalf("go: %v", err)
+	}
+	goSum.enabled = true
+	readGoSum(GoSumFile, data)
+
+	// Add old go.modverify file.
+	// We'll delete go.modverify in WriteGoSum.
+	alt := strings.TrimSuffix(GoSumFile, ".sum") + ".modverify"
+	if data, err := ioutil.ReadFile(alt); err == nil {
+		readGoSum(alt, data)
+		goSum.modverify = alt
+	}
+	return true
+}
+
+// emptyGoModHash is the hash of a 1-file tree containing a 0-length go.mod.
+// A bug caused us to write these into go.sum files for non-modules.
+// We detect and remove them.
+const emptyGoModHash = "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY="
+
+// readGoSum parses data, which is the content of file,
+// and adds it to goSum.m. The goSum lock must be held.
+func readGoSum(file string, data []byte) {
+	lineno := 0
+	for len(data) > 0 {
+		var line []byte
+		lineno++
+		i := bytes.IndexByte(data, '\n')
+		if i < 0 {
+			line, data = data, nil
+		} else {
+			line, data = data[:i], data[i+1:]
+		}
+		f := strings.Fields(string(line))
+		if len(f) == 0 {
+			// blank line; skip it
+			continue
+		}
+		if len(f) != 3 {
+			base.Fatalf("go: malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
+		}
+		if f[2] == emptyGoModHash {
+			// Old bug; drop it.
+			continue
+		}
+		mod := module.Version{Path: f[0], Version: f[1]}
+		goSum.m[mod] = append(goSum.m[mod], f[2])
+	}
+}
+
+// checkSum checks the given module's checksum.
+func checkSum(mod module.Version) {
+	if SrcMod == "" {
+		// Do not use current directory.
+		return
+	}
+
+	// Do the file I/O before acquiring the go.sum lock.
+	data, err := ioutil.ReadFile(filepath.Join(SrcMod, "cache/download", mod.Path, "@v", mod.Version+".ziphash"))
+	if err != nil {
+		if os.IsNotExist(err) {
+			// This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes.
+			return
+		}
+		base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err)
+	}
+	h := strings.TrimSpace(string(data))
+	if !strings.HasPrefix(h, "h1:") {
+		base.Fatalf("go: verifying %s@%s: unexpected ziphash: %q", mod.Path, mod.Version, h)
+	}
+
+	checkOneSum(mod, h)
+}
+
+// checkGoMod checks the given module's go.mod checksum;
+// data is the go.mod content.
+func checkGoMod(path, version string, data []byte) {
+	h, err := dirhash.Hash1([]string{"go.mod"}, func(string) (io.ReadCloser, error) {
+		return ioutil.NopCloser(bytes.NewReader(data)), nil
+	})
+	if err != nil {
+		base.Fatalf("go: verifying %s %s go.mod: %v", path, version, err)
+	}
+
+	checkOneSum(module.Version{Path: path, Version: version + "/go.mod"}, h)
+}
+
+// checkOneSum checks that the recorded hash for mod is h.
+func checkOneSum(mod module.Version, h string) {
+	goSum.mu.Lock()
+	defer goSum.mu.Unlock()
+	if !initGoSum() {
+		return
+	}
+
+	for _, vh := range goSum.m[mod] {
+		if h == vh {
+			return
+		}
+		if strings.HasPrefix(vh, "h1:") {
+			base.Fatalf("go: verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum:     %v", mod.Path, mod.Version, h, vh)
+		}
+	}
+	if len(goSum.m[mod]) > 0 {
+		fmt.Fprintf(os.Stderr, "warning: verifying %s@%s: unknown hashes in go.sum: %v; adding %v", mod.Path, mod.Version, strings.Join(goSum.m[mod], ", "), h)
+	}
+	goSum.m[mod] = append(goSum.m[mod], h)
+}
+
+// Sum returns the checksum for the downloaded copy of the given module,
+// if present in the download cache.
+func Sum(mod module.Version) string {
+	if SrcMod == "" {
+		// Do not use current directory.
+		return ""
+	}
+
+	data, err := ioutil.ReadFile(filepath.Join(SrcMod, "cache/download", mod.Path, "@v", mod.Version+".ziphash"))
+	if err != nil {
+		return ""
+	}
+	return strings.TrimSpace(string(data))
+}
+
+// WriteGoSum writes the go.sum file if it needs to be updated.
+func WriteGoSum() {
+	goSum.mu.Lock()
+	defer goSum.mu.Unlock()
+	if !initGoSum() {
+		return
+	}
+
+	var mods []module.Version
+	for m := range goSum.m {
+		mods = append(mods, m)
+	}
+	module.Sort(mods)
+	var buf bytes.Buffer
+	for _, m := range mods {
+		list := goSum.m[m]
+		sort.Strings(list)
+		for _, h := range list {
+			fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
+		}
+	}
+
+	data, _ := ioutil.ReadFile(GoSumFile)
+	if !bytes.Equal(data, buf.Bytes()) {
+		if err := ioutil.WriteFile(GoSumFile, buf.Bytes(), 0666); err != nil {
+			base.Fatalf("go: writing go.sum: %v", err)
+		}
+	}
+
+	if goSum.modverify != "" {
+		os.Remove(goSum.modverify)
+	}
+}
diff --git a/src/cmd/go/internal/modfetch/github/fetch.go b/src/cmd/go/internal/modfetch/github/fetch.go
deleted file mode 100644
index a2a90f1..0000000
--- a/src/cmd/go/internal/modfetch/github/fetch.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package github
-
-import (
-	"fmt"
-	"strings"
-
-	"cmd/go/internal/modfetch/codehost"
-	"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")
-	}
-	path = f[0] + "/" + f[1] + "/" + f[2]
-	return gitrepo.Repo("https://"+path, path)
-}
diff --git a/src/cmd/go/internal/modfetch/gitrepo/fetch.go b/src/cmd/go/internal/modfetch/gitrepo/fetch.go
deleted file mode 100644
index 0d6eb4b..0000000
--- a/src/cmd/go/internal/modfetch/gitrepo/fetch.go
+++ /dev/null
@@ -1,445 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package gitrepo provides a Git-based implementation of codehost.Repo.
-package gitrepo
-
-import (
-	"archive/zip"
-	"bytes"
-	"cmd/go/internal/modfetch/codehost"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"sort"
-	"strconv"
-	"strings"
-	"sync"
-	"time"
-)
-
-// Repo returns the code repository at the given Git remote reference.
-// The returned repo reports the given root as its module root.
-func Repo(remote, root string) (codehost.Repo, error) {
-	return newRepo(remote, root, false)
-}
-
-// LocalRepo is like Repo but accepts both Git remote references
-// and paths to repositories on the local file system.
-// The returned repo reports the given root as its module root.
-func LocalRepo(remote, root string) (codehost.Repo, error) {
-	return newRepo(remote, root, true)
-}
-
-const workDirType = "git2"
-
-func newRepo(remote, root string, localOK bool) (codehost.Repo, error) {
-	r := &repo{remote: remote, root: root, canArchive: true}
-	if strings.Contains(remote, "://") {
-		// This is a remote path.
-		dir, err := codehost.WorkDir(workDirType, r.remote)
-		if err != nil {
-			return nil, err
-		}
-		r.dir = dir
-		if _, err := os.Stat(filepath.Join(dir, "objects")); err != nil {
-			if _, err := codehost.Run(dir, "git", "init", "--bare"); err != nil {
-				os.RemoveAll(dir)
-				return nil, err
-			}
-			// We could just say git fetch https://whatever later,
-			// but this lets us say git fetch origin instead, which
-			// is a little nicer. More importantly, using a named remote
-			// avoids a problem with Git LFS. See golang.org/issue/25605.
-			if _, err := codehost.Run(dir, "git", "remote", "add", "origin", r.remote); err != nil {
-				os.RemoveAll(dir)
-				return nil, err
-			}
-			r.remote = "origin"
-		}
-	} else {
-		// Local path.
-		// Disallow colon (not in ://) because sometimes
-		// that's rcp-style host:path syntax and sometimes it's not (c:\work).
-		// The go command has always insisted on URL syntax for ssh.
-		if strings.Contains(remote, ":") {
-			return nil, fmt.Errorf("git remote cannot use host:path syntax")
-		}
-		if !localOK {
-			return nil, fmt.Errorf("git remote must not be local directory")
-		}
-		r.local = true
-		info, err := os.Stat(remote)
-		if err != nil {
-			return nil, err
-		}
-		if !info.IsDir() {
-			return nil, fmt.Errorf("%s exists but is not a directory", remote)
-		}
-		r.dir = remote
-	}
-	return r, nil
-}
-
-type repo struct {
-	remote     string
-	local      bool
-	root       string
-	dir        string
-	canArchive bool
-
-	refsOnce sync.Once
-	refs     map[string]string
-	refsErr  error
-}
-
-func (r *repo) Root() string {
-	return r.root
-}
-
-// loadRefs loads heads and tags references from the remote into the map r.refs.
-// Should only be called as r.refsOnce.Do(r.loadRefs).
-func (r *repo) loadRefs() {
-	// The git protocol sends all known refs and ls-remote filters them on the client side,
-	// so we might as well record both heads and tags in one shot.
-	// Most of the time we only care about tags but sometimes we care about heads too.
-	out, err := codehost.Run(r.dir, "git", "ls-remote", "-q", r.remote)
-	if err != nil {
-		r.refsErr = err
-		return
-	}
-
-	r.refs = make(map[string]string)
-	for _, line := range strings.Split(string(out), "\n") {
-		f := strings.Fields(line)
-		if len(f) != 2 {
-			continue
-		}
-		if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") {
-			r.refs[f[1]] = f[0]
-		}
-	}
-	for ref, hash := range r.refs {
-		if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag
-			r.refs[strings.TrimSuffix(ref, "^{}")] = hash
-			delete(r.refs, ref)
-		}
-	}
-}
-
-func (r *repo) Tags(prefix string) ([]string, error) {
-	r.refsOnce.Do(r.loadRefs)
-	if r.refsErr != nil {
-		return nil, r.refsErr
-	}
-
-	tags := []string{}
-	for ref := range r.refs {
-		if !strings.HasPrefix(ref, "refs/tags/") {
-			continue
-		}
-		tag := ref[len("refs/tags/"):]
-		if !strings.HasPrefix(tag, prefix) {
-			continue
-		}
-		tags = append(tags, tag)
-	}
-	sort.Strings(tags)
-	return tags, nil
-}
-
-func (r *repo) Latest() (*codehost.RevInfo, error) {
-	r.refsOnce.Do(r.loadRefs)
-	if r.refsErr != nil {
-		return nil, r.refsErr
-	}
-	if r.refs["HEAD"] == "" {
-		return nil, fmt.Errorf("no commits")
-	}
-	return r.Stat(r.refs["HEAD"])
-}
-
-// findRef finds some ref name for the given hash,
-// for use when the server requires giving a ref instead of a hash.
-// There may be multiple ref names for a given hash,
-// in which case this returns some name - it doesn't matter which.
-func (r *repo) findRef(hash string) (ref string, ok bool) {
-	r.refsOnce.Do(r.loadRefs)
-	for ref, h := range r.refs {
-		if h == hash {
-			return ref, true
-		}
-	}
-	return "", false
-}
-
-func unshallow(gitDir string) []string {
-	if _, err := os.Stat(filepath.Join(gitDir, "shallow")); err == nil {
-		return []string{"--unshallow"}
-	}
-	return []string{}
-}
-
-// statOrArchive tries to stat the given rev in the local repository,
-// or else it tries to obtain an archive at the rev with the given arguments,
-// or else it falls back to aggressive fetching and then a local stat.
-// The archive step is an optimization for servers that support it
-// (most do not, but maybe that will change), to let us minimize
-// the amount of code downloaded.
-func (r *repo) statOrArchive(rev string, archiveArgs ...string) (info *codehost.RevInfo, archive []byte, err error) {
-	// Do we have this rev?
-	r.refsOnce.Do(r.loadRefs)
-	var hash string
-	if k := "refs/tags/" + rev; r.refs[k] != "" {
-		hash = r.refs[k]
-	} else if k := "refs/heads/" + rev; r.refs[k] != "" {
-		hash = r.refs[k]
-		rev = hash
-	} else if rev == "HEAD" && r.refs["HEAD"] != "" {
-		hash = r.refs["HEAD"]
-		rev = hash
-	} else if len(rev) >= 5 && len(rev) <= 40 && codehost.AllHex(rev) {
-		hash = rev
-	} else {
-		return nil, nil, fmt.Errorf("unknown revision %q", rev)
-	}
-
-	out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash)
-	if err == nil {
-		hash = strings.TrimSpace(string(out))
-		goto Found
-	}
-
-	// We don't have the rev. Can we fetch it?
-	if r.local {
-		return nil, nil, fmt.Errorf("unknown revision %q", rev)
-	}
-
-	if r.canArchive {
-		// git archive with --remote requires a ref, not a hash.
-		// Proceed only if we know a ref for this hash.
-		if ref, ok := r.findRef(hash); ok {
-			out, err := codehost.Run(r.dir, "git", "archive", "--format=zip", "--remote="+r.remote, "--prefix=prefix/", ref, archiveArgs)
-			if err == nil {
-				return &codehost.RevInfo{Version: rev}, out, nil
-			}
-			if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("did not match any files")) {
-				return nil, nil, fmt.Errorf("file not found")
-			}
-			if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("Operation not supported by protocol")) {
-				r.canArchive = false
-			}
-		}
-	}
-
-	// Maybe it's a prefix of a ref we know.
-	// Iterating through all the refs is faster than doing unnecessary fetches.
-	// This is not strictly correct, in that the short ref might be ambiguous
-	// in the git repo as a whole, but not ambiguous in the list of named refs,
-	// so that we will resolve it where the git server would not.
-	// But this check avoids great expense, and preferring a known ref does
-	// not seem like such a bad failure mode.
-	if len(hash) >= 5 && len(hash) < 40 {
-		var full string
-		for _, h := range r.refs {
-			if strings.HasPrefix(h, hash) {
-				if full != "" {
-					// Prefix is ambiguous even in the ref list!
-					full = ""
-					break
-				}
-				full = h
-			}
-		}
-		if full != "" {
-			hash = full
-		}
-	}
-
-	// Fetch it.
-	if len(hash) == 40 {
-		name := hash
-		if ref, ok := r.findRef(hash); ok {
-			name = ref
-		}
-		if _, err = codehost.Run(r.dir, "git", "fetch", "--depth=1", r.remote, name); err == nil {
-			goto Found
-		}
-		if !strings.Contains(err.Error(), "unadvertised object") && !strings.Contains(err.Error(), "no such remote ref") && !strings.Contains(err.Error(), "does not support shallow") {
-			return nil, nil, err
-		}
-	}
-
-	// It's a prefix, and we don't have a way to make the server resolve the prefix for us,
-	// or it's a full hash but also an unadvertised object.
-	// Download progressively more of the repo to look for it.
-
-	// Fetch the main branch (non-shallow).
-	if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), r.remote); err != nil {
-		return nil, nil, err
-	}
-	if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
-		hash = strings.TrimSpace(string(out))
-		goto Found
-	}
-
-	// Fetch all tags (non-shallow).
-	if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", "--tags", r.remote); err != nil {
-		return nil, nil, err
-	}
-	if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
-		hash = strings.TrimSpace(string(out))
-		goto Found
-	}
-
-	// Fetch all branches (non-shallow).
-	if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", r.remote, "refs/heads/*:refs/heads/*"); err != nil {
-		return nil, nil, err
-	}
-	if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
-		hash = strings.TrimSpace(string(out))
-		goto Found
-	}
-
-	// Fetch all refs (non-shallow).
-	if _, err := codehost.Run(r.dir, "git", "fetch", unshallow(r.dir), "-f", r.remote, "refs/*:refs/*"); err != nil {
-		return nil, nil, err
-	}
-	if out, err := codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%H", hash); err == nil {
-		hash = strings.TrimSpace(string(out))
-		goto Found
-	}
-	return nil, nil, fmt.Errorf("cannot find hash %s", hash)
-Found:
-
-	if strings.HasPrefix(hash, rev) {
-		rev = hash
-	}
-
-	out, err = codehost.Run(r.dir, "git", "log", "-n1", "--format=format:%ct", hash)
-	if err != nil {
-		return nil, nil, err
-	}
-	t, err := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64)
-	if err != nil {
-		return nil, nil, fmt.Errorf("invalid time from git log: %q", out)
-	}
-
-	info = &codehost.RevInfo{
-		Name:    hash,
-		Short:   codehost.ShortenSHA1(hash),
-		Time:    time.Unix(t, 0).UTC(),
-		Version: rev,
-	}
-	return info, nil, nil
-}
-
-func (r *repo) Stat(rev string) (*codehost.RevInfo, error) {
-	// If the server will give us a git archive, we can pull the
-	// commit ID and the commit time out of the archive.
-	// We want an archive as small as possible (for speed),
-	// but we have to specify a pattern that matches at least one file name.
-	// The pattern here matches README, .gitignore, .gitattributes,
-	// and go.mod (and some other incidental file names);
-	// hopefully most repos will have at least one of these.
-	info, archive, err := r.statOrArchive(rev, "[Rg.][Ego][A.i][Dmt][Miao][Edgt]*")
-	if err != nil {
-		return nil, err
-	}
-	if archive != nil {
-		return zip2info(archive, info.Version)
-	}
-	return info, nil
-}
-
-func (r *repo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
-	info, archive, err := r.statOrArchive(rev, file)
-	if err != nil {
-		return nil, err
-	}
-	if archive != nil {
-		return zip2file(archive, file, maxSize)
-	}
-	out, err := codehost.Run(r.dir, "git", "cat-file", "blob", info.Name+":"+file)
-	if err != nil {
-		return nil, fmt.Errorf("file not found")
-	}
-	return out, nil
-}
-
-func (r *repo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
-	// TODO: Use maxSize or drop it.
-	args := []string{}
-	if subdir != "" {
-		args = append(args, "--", subdir)
-	}
-	info, archive, err := r.statOrArchive(rev, args...)
-	if err != nil {
-		return nil, "", err
-	}
-	if archive == nil {
-		archive, err = codehost.Run(r.dir, "git", "archive", "--format=zip", "--prefix=prefix/", info.Name, args)
-		if err != nil {
-			if bytes.Contains(err.(*codehost.RunError).Stderr, []byte("did not match any files")) {
-				return nil, "", fmt.Errorf("file not found")
-			}
-			return nil, "", err
-		}
-	}
-
-	return ioutil.NopCloser(bytes.NewReader(archive)), "", nil
-}
-
-func zip2info(archive []byte, rev string) (*codehost.RevInfo, error) {
-	r, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
-	if err != nil {
-		return nil, err
-	}
-	if r.Comment == "" {
-		return nil, fmt.Errorf("missing commit ID in git zip comment")
-	}
-	hash := r.Comment
-	if len(hash) != 40 || !codehost.AllHex(hash) {
-		return nil, fmt.Errorf("invalid commit ID in git zip comment")
-	}
-	if len(r.File) == 0 {
-		return nil, fmt.Errorf("git zip has no files")
-	}
-	info := &codehost.RevInfo{
-		Name:    hash,
-		Short:   codehost.ShortenSHA1(hash),
-		Time:    r.File[0].Modified.UTC(),
-		Version: rev,
-	}
-	return info, nil
-}
-
-func zip2file(archive []byte, file string, maxSize int64) ([]byte, error) {
-	r, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
-	if err != nil {
-		return nil, err
-	}
-	for _, f := range r.File {
-		if f.Name != "prefix/"+file {
-			continue
-		}
-		rc, err := f.Open()
-		if err != nil {
-			return nil, err
-		}
-		defer rc.Close()
-		l := &io.LimitedReader{R: rc, N: maxSize + 1}
-		data, err := ioutil.ReadAll(l)
-		if err != nil {
-			return nil, err
-		}
-		if l.N <= 0 {
-			return nil, fmt.Errorf("file %s too large", file)
-		}
-		return data, nil
-	}
-	return nil, fmt.Errorf("incomplete git zip archive: cannot find %s", file)
-}
diff --git a/src/cmd/go/internal/modfetch/googlesource/fetch.go b/src/cmd/go/internal/modfetch/googlesource/fetch.go
deleted file mode 100644
index 8317ac3..0000000
--- a/src/cmd/go/internal/modfetch/googlesource/fetch.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package googlesource
-
-import (
-	"fmt"
-	"strings"
-
-	"cmd/go/internal/modfetch/codehost"
-	"cmd/go/internal/modfetch/gitrepo"
-)
-
-func Lookup(path string) (codehost.Repo, error) {
-	i := strings.Index(path, "/")
-	if i+1 == len(path) || !strings.HasSuffix(path[:i+1], ".googlesource.com/") {
-		return nil, fmt.Errorf("not *.googlesource.com/*")
-	}
-	j := strings.Index(path[i+1:], "/")
-	if j >= 0 {
-		path = path[:i+1+j]
-	}
-	return gitrepo.Repo("https://"+path, path)
-}
diff --git a/src/cmd/go/internal/modfetch/gopkgin.go b/src/cmd/go/internal/modfetch/gopkgin.go
deleted file mode 100644
index d49b60b..0000000
--- a/src/cmd/go/internal/modfetch/gopkgin.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// 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 (
-	"cmd/go/internal/modfetch/codehost"
-	"cmd/go/internal/modfetch/gitrepo"
-	"cmd/go/internal/modfile"
-	"fmt"
-)
-
-func gopkginLookup(path string) (codehost.Repo, error) {
-	root, _, _, _, ok := modfile.ParseGopkgIn(path)
-	if !ok {
-		return nil, fmt.Errorf("invalid gopkg.in/ path: %q", path)
-	}
-	return gitrepo.Repo("https://"+root, root)
-}
diff --git a/src/cmd/go/internal/modfetch/proxy.go b/src/cmd/go/internal/modfetch/proxy.go
index 419323b..02c2e63 100644
--- a/src/cmd/go/internal/modfetch/proxy.go
+++ b/src/cmd/go/internal/modfetch/proxy.go
@@ -55,6 +55,7 @@
 			list = append(list, f[0])
 		}
 	}
+	SortVersions(list)
 	return list, nil
 }
 
@@ -134,7 +135,7 @@
 	defer body.Close()
 
 	// Spool to local file.
-	f, err := ioutil.TempFile(tmpdir, "vgo-proxy-download-")
+	f, err := ioutil.TempFile(tmpdir, "go-proxy-download-")
 	if err != nil {
 		return "", err
 	}
diff --git a/src/cmd/go/internal/modfetch/query.go b/src/cmd/go/internal/modfetch/query.go
deleted file mode 100644
index 5e9d86c..0000000
--- a/src/cmd/go/internal/modfetch/query.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package modfetch
-
-import (
-	"cmd/go/internal/module"
-	"cmd/go/internal/semver"
-	"fmt"
-	"strings"
-)
-
-// Query looks up a revision of a given module given a version query string.
-// The module must be a complete module path.
-// The version must take one of the following forms:
-//
-//	- the literal string "latest", denoting the latest available tagged version
-//	- v1.2.3, a semantic version string
-//	- v1 or v1.2, an abbreviated semantic version string completed by adding zeroes (v1.0.0 or v1.2.0);
-//	- >v1.2.3, denoting the earliest available version after v1.2.3
-//	- <v1.2.3, denoting the latest available version before v1.2.3
-//	- an RFC 3339 time stamp, denoting the latest available version at that time
-//	- a Unix time expressed as seconds since 1970, denoting the latest available version at that time
-//	- a repository commit identifier, denoting that version
-//
-// The time stamps can be followed by an optional @branch suffix to limit the
-// result to revisions on a particular branch name.
-//
-func Query(path, vers string, allowed func(module.Version) bool) (*RevInfo, error) {
-	repo, err := Lookup(path)
-	if err != nil {
-		return nil, err
-	}
-
-	if strings.HasPrefix(vers, "v") && semver.IsValid(vers) {
-		// TODO: This turns query for "v2" into Stat "v2.0.0",
-		// but probably it should allow checking for a branch named "v2".
-		vers = semver.Canonical(vers)
-		if allowed != nil && !allowed(module.Version{Path: path, Version: vers}) {
-			return nil, fmt.Errorf("%s@%s excluded", path, vers)
-		}
-		return repo.Stat(vers)
-	}
-	if strings.HasPrefix(vers, ">") || strings.HasPrefix(vers, "<") || vers == "latest" {
-		var op string
-		if vers != "latest" {
-			if !semver.IsValid(vers[1:]) {
-				return nil, fmt.Errorf("invalid semantic version in range %s", vers)
-			}
-			op, vers = vers[:1], vers[1:]
-		}
-		versions, err := repo.Versions("")
-		if err != nil {
-			return nil, err
-		}
-		if len(versions) == 0 && vers == "latest" {
-			return repo.Latest()
-		}
-		if vers == "latest" {
-			for i := len(versions) - 1; i >= 0; i-- {
-				if allowed == nil || allowed(module.Version{Path: path, Version: versions[i]}) {
-					return repo.Stat(versions[i])
-				}
-			}
-		} else if op == "<" {
-			for i := len(versions) - 1; i >= 0; i-- {
-				if semver.Compare(versions[i], vers) < 0 && (allowed == nil || allowed(module.Version{Path: path, Version: versions[i]})) {
-					return repo.Stat(versions[i])
-				}
-			}
-		} else {
-			for i := 0; i < len(versions); i++ {
-				if semver.Compare(versions[i], vers) > 0 && (allowed == nil || allowed(module.Version{Path: path, Version: versions[i]})) {
-					return repo.Stat(versions[i])
-				}
-			}
-		}
-		return nil, fmt.Errorf("no matching versions for %s%s", op, vers)
-	}
-	// TODO: Time queries, maybe.
-
-	return repo.Stat(vers)
-}
diff --git a/src/cmd/go/internal/modfetch/repo.go b/src/cmd/go/internal/modfetch/repo.go
index 6e21a41..917d0ff 100644
--- a/src/cmd/go/internal/modfetch/repo.go
+++ b/src/cmd/go/internal/modfetch/repo.go
@@ -5,21 +5,23 @@
 package modfetch
 
 import (
-	"errors"
-	pathpkg "path"
+	"fmt"
+	"os"
 	"sort"
-	"strings"
 	"time"
 
-	"cmd/go/internal/modfetch/bitbucket"
+	"cmd/go/internal/cfg"
+	"cmd/go/internal/get"
 	"cmd/go/internal/modfetch/codehost"
-	"cmd/go/internal/modfetch/github"
-	"cmd/go/internal/modfetch/googlesource"
-	"cmd/go/internal/module"
+	"cmd/go/internal/par"
 	"cmd/go/internal/semver"
+	web "cmd/go/internal/web"
 )
 
+const traceRepo = false // trace all repo actions, for debugging
+
 // A Repo represents a repository storing all versions of a single module.
+// It must be safe for simultaneous use by multiple goroutines.
 type Repo interface {
 	// ModulePath returns the module path.
 	ModulePath() string
@@ -58,72 +60,223 @@
 	Time    time.Time // commit time
 }
 
+// Re: module paths, import paths, repository roots, and lookups
+//
+// A module is a collection of Go packages stored in a file tree
+// with a go.mod file at the root of the tree.
+// The go.mod defines the module path, which is the import path
+// corresponding to the root of the file tree.
+// The import path of a directory within that file tree is the module path
+// joined with the name of the subdirectory relative to the root.
+//
+// For example, the module with path rsc.io/qr corresponds to the
+// file tree in the repository https://github.com/rsc/qr.
+// That file tree has a go.mod that says "module rsc.io/qr".
+// The package in the root directory has import path "rsc.io/qr".
+// The package in the gf256 subdirectory has import path "rsc.io/qr/gf256".
+// In this example, "rsc.io/qr" is both a module path and an import path.
+// But "rsc.io/qr/gf256" is only an import path, not a module path:
+// it names an importable package, but not a module.
+//
+// As a special case to incorporate code written before modules were
+// introduced, if a path p resolves using the pre-module "go get" lookup
+// to the root of a source code repository without a go.mod file,
+// that repository is treated as if it had a go.mod in its root directory
+// declaring module path p. (The go.mod is further considered to
+// contain requirements corresponding to any legacy version
+// tracking format such as Gopkg.lock, vendor/vendor.conf, and so on.)
+//
+// The presentation so far ignores the fact that a source code repository
+// has many different versions of a file tree, and those versions may
+// differ in whether a particular go.mod exists and what it contains.
+// In fact there is a well-defined mapping only from a module path, version
+// pair - often written path@version - to a particular file tree.
+// For example rsc.io/qr@v0.1.0 depends on the "implicit go.mod at root of
+// repository" rule, while rsc.io/qr@v0.2.0 has an explicit go.mod.
+// Because the "go get" import paths rsc.io/qr and github.com/rsc/qr
+// both redirect to the Git repository https://github.com/rsc/qr,
+// github.com/rsc/qr@v0.1.0 is the same file tree as rsc.io/qr@v0.1.0
+// but a different module (a different name). In contrast, since v0.2.0
+// of that repository has an explicit go.mod that declares path rsc.io/qr,
+// github.com/rsc/qr@v0.2.0 is an invalid module path, version pair.
+// Before modules, import comments would have had the same effect.
+//
+// The set of import paths associated with a given module path is
+// clearly not fixed: at the least, new directories with new import paths
+// can always be added. But another potential operation is to split a
+// subtree out of a module into its own module. If done carefully,
+// this operation can be done while preserving compatibility for clients.
+// For example, suppose that we want to split rsc.io/qr/gf256 into its
+// own module, so that there would be two modules rsc.io/qr and rsc.io/qr/gf256.
+// Then we can simultaneously issue rsc.io/qr v0.3.0 (dropping the gf256 subdirectory)
+// and rsc.io/qr/gf256 v0.1.0, including in their respective go.mod
+// cyclic requirements pointing at each other: rsc.io/qr v0.3.0 requires
+// rsc.io/qr/gf256 v0.1.0 and vice versa. Then a build can be
+// using an older rsc.io/qr module that includes the gf256 package, but if
+// it adds a requirement on either the newer rsc.io/qr or the newer
+// rsc.io/qr/gf256 module, it will automatically add the requirement
+// on the complementary half, ensuring both that rsc.io/qr/gf256 is
+// available for importing by the build and also that it is only defined
+// by a single module. The gf256 package could move back into the
+// original by another simultaneous release of rsc.io/qr v0.4.0 including
+// the gf256 subdirectory and an rsc.io/qr/gf256 v0.2.0 with no code
+// in its root directory, along with a new requirement cycle.
+// The ability to shift module boundaries in this way is expected to be
+// important in large-scale program refactorings, similar to the ones
+// described in https://talks.golang.org/2016/refactor.article.
+//
+// The possibility of shifting module boundaries reemphasizes
+// that you must know both the module path and its version
+// to determine the set of packages provided directly by that module.
+//
+// On top of all this, it is possible for a single code repository
+// to contain multiple modules, either in branches or subdirectories,
+// as a limited kind of monorepo. For example rsc.io/qr/v2,
+// the v2.x.x continuation of rsc.io/qr, is expected to be found
+// in v2-tagged commits in https://github.com/rsc/qr, either
+// in the root or in a v2 subdirectory, disambiguated by go.mod.
+// Again the precise file tree corresponding to a module
+// depends on which version we are considering.
+//
+// It is also possible for the underlying repository to change over time,
+// without changing the module path. If I copy the github repo over
+// to https://bitbucket.org/rsc/qr and update https://rsc.io/qr?go-get=1,
+// then clients of all versions should start fetching from bitbucket
+// instead of github. That is, in contrast to the exact file tree,
+// the location of the source code repository associated with a module path
+// does not depend on the module version. (This is by design, as the whole
+// point of these redirects is to allow package authors to establish a stable
+// name that can be updated as code moves from one service to another.)
+//
+// All of this is important background for the lookup APIs defined in this
+// file.
+//
+// The Lookup function takes a module path and returns a Repo representing
+// that module path. Lookup can do only a little with the path alone.
+// It can check that the path is well-formed (see semver.CheckPath)
+// and it can check that the path can be resolved to a target repository.
+// To avoid version control access except when absolutely necessary,
+// Lookup does not attempt to connect to the repository itself.
+//
+// The Import function takes an import path found in source code and
+// determines which module to add to the requirement list to satisfy
+// that import. It checks successive truncations of the import path
+// to determine possible modules and stops when it finds a module
+// in which the latest version satisfies the import path.
+//
+// The ImportRepoRev function is a variant of Import which is limited
+// to code in a source code repository at a particular revision identifier
+// (usually a commit hash or source code repository tag, not necessarily
+// a module version).
+// ImportRepoRev is used when converting legacy dependency requirements
+// from older systems into go.mod files. Those older systems worked
+// at either package or repository granularity, and most of the time they
+// recorded commit hashes, not tagged versions.
+
+var lookupCache par.Cache
+
 // Lookup returns the module with the given module path.
+// A successful return does not guarantee that the module
+// has any defined versions.
 func Lookup(path string) (Repo, error) {
+	if traceRepo {
+		defer logCall("Lookup(%q)", path)()
+	}
+
+	type cached struct {
+		r   Repo
+		err error
+	}
+	c := lookupCache.Do(path, func() interface{} {
+		r, err := lookup(path)
+		if err == nil {
+			if traceRepo {
+				r = newLoggingRepo(r)
+			}
+			r = newCachingRepo(r)
+		}
+		return cached{r, err}
+	}).(cached)
+
+	return c.r, c.err
+}
+
+// lookup returns the module with the given module path.
+func lookup(path string) (r Repo, err error) {
+	if cfg.BuildGetmode != "" {
+		return nil, fmt.Errorf("module lookup disabled by -getmode=%s", cfg.BuildGetmode)
+	}
 	if proxyURL != "" {
 		return lookupProxy(path)
 	}
-	if code, err := lookupCodeHost(path, false); err != errNotHosted {
-		if err != nil {
-			return nil, err
-		}
-		return newCodeRepo(code, path)
+
+	rr, err := get.RepoRootForImportPath(path, get.PreferMod, web.Secure)
+	if err != nil {
+		// We don't know where to find code for a module with this path.
+		return nil, err
 	}
-	return lookupCustomDomain(path)
+
+	if rr.VCS == "mod" {
+		// Fetch module from proxy with base URL rr.Repo.
+		return newProxyRepo(rr.Repo, path), nil
+	}
+
+	code, err := lookupCodeRepo(rr)
+	if err != nil {
+		return nil, err
+	}
+	return newCodeRepo(code, rr.Root, path)
 }
 
-func Import(path string, allowed func(module.Version) bool) (Repo, *RevInfo, error) {
-	try := func(path string) (Repo, *RevInfo, error) {
-		r, err := Lookup(path)
-		if err != nil {
-			return nil, nil, err
-		}
-		info, err := Query(path, "latest", allowed)
-		if err != nil {
-			return nil, nil, err
-		}
-		_, err = r.GoMod(info.Version)
-		if err != nil {
-			return nil, nil, err
-		}
-		return r, info, nil
+func lookupCodeRepo(rr *get.RepoRoot) (codehost.Repo, error) {
+	code, err := codehost.NewRepo(rr.VCS, rr.Repo)
+	if err != nil {
+		return nil, fmt.Errorf("lookup %s: %v", rr.Root, err)
 	}
-
-	var firstErr error
-	for {
-		r, info, err := try(path)
-		if err == nil {
-			return r, info, nil
-		}
-		if firstErr == nil {
-			firstErr = err
-		}
-		p := pathpkg.Dir(path)
-		if p == "." {
-			break
-		}
-		path = p
-	}
-	return nil, nil, firstErr
+	return code, nil
 }
 
-var errNotHosted = errors.New("not hosted")
-
-var isTest bool
-
-func lookupCodeHost(path string, customDomain bool) (codehost.Repo, error) {
-	switch {
-	case strings.HasPrefix(path, "github.com/"):
-		return github.Lookup(path)
-	case strings.HasPrefix(path, "bitbucket.org/"):
-		return bitbucket.Lookup(path)
-	case customDomain && strings.HasSuffix(path[:strings.Index(path, "/")+1], ".googlesource.com/") ||
-		isTest && strings.HasPrefix(path, "go.googlesource.com/scratch"):
-		return googlesource.Lookup(path)
-	case strings.HasPrefix(path, "gopkg.in/"):
-		return gopkginLookup(path)
+// ImportRepoRev returns the module and version to use to access
+// the given import path loaded from the source code repository that
+// the original "go get" would have used, at the specific repository revision
+// (typically a commit hash, but possibly also a source control tag).
+func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) {
+	if cfg.BuildGetmode != "" {
+		return nil, nil, fmt.Errorf("repo version lookup disabled by -getmode=%s", cfg.BuildGetmode)
 	}
-	return nil, errNotHosted
+
+	// Note: Because we are converting a code reference from a legacy
+	// version control system, we ignore meta tags about modules
+	// and use only direct source control entries (get.IgnoreMod).
+	rr, err := get.RepoRootForImportPath(path, get.IgnoreMod, web.Secure)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	code, err := lookupCodeRepo(rr)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	revInfo, err := code.Stat(rev)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: Look in repo to find path, check for go.mod files.
+	// For now we're just assuming rr.Root is the module path,
+	// which is true in the absence of go.mod files.
+
+	repo, err := newCodeRepo(code, rr.Root, rr.Root)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	info, err := repo.(*codeRepo).convert(revInfo, "")
+	if err != nil {
+		return nil, nil, err
+	}
+	return repo, info, nil
 }
 
 func SortVersions(list []string) {
@@ -135,3 +288,59 @@
 		return list[i] < list[j]
 	})
 }
+
+// A loggingRepo is a wrapper around an underlying Repo
+// that prints a log message at the start and end of each call.
+// It can be inserted when debugging.
+type loggingRepo struct {
+	r Repo
+}
+
+func newLoggingRepo(r Repo) *loggingRepo {
+	return &loggingRepo{r}
+}
+
+// logCall prints a log message using format and args and then
+// also returns a function that will print the same message again,
+// along with the elapsed time.
+// Typical usage is:
+//
+//	defer logCall("hello %s", arg)()
+//
+// Note the final ().
+func logCall(format string, args ...interface{}) func() {
+	start := time.Now()
+	fmt.Fprintf(os.Stderr, "+++ %s\n", fmt.Sprintf(format, args...))
+	return func() {
+		fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), fmt.Sprintf(format, args...))
+	}
+}
+
+func (l *loggingRepo) ModulePath() string {
+	return l.r.ModulePath()
+}
+
+func (l *loggingRepo) Versions(prefix string) (tags []string, err error) {
+	defer logCall("Repo[%s]: Versions(%q)", l.r.ModulePath(), prefix)()
+	return l.r.Versions(prefix)
+}
+
+func (l *loggingRepo) Stat(rev string) (*RevInfo, error) {
+	defer logCall("Repo[%s]: Stat(%q)", l.r.ModulePath(), rev)()
+	return l.r.Stat(rev)
+}
+
+func (l *loggingRepo) Latest() (*RevInfo, error) {
+	defer logCall("Repo[%s]: Latest()", l.r.ModulePath())()
+	return l.r.Latest()
+}
+
+func (l *loggingRepo) GoMod(version string) ([]byte, error) {
+	defer logCall("Repo[%s]: GoMod(%q)", l.r.ModulePath(), version)()
+	return l.r.GoMod(version)
+}
+
+func (l *loggingRepo) Zip(version, tmpdir string) (string, error) {
+	defer logCall("Repo[%s]: Zip(%q, %q)", l.r.ModulePath(), version, tmpdir)()
+	return l.r.Zip(version, tmpdir)
+}
diff --git a/src/cmd/go/internal/modfetch/unzip.go b/src/cmd/go/internal/modfetch/unzip.go
index 9d9e298..3c69803 100644
--- a/src/cmd/go/internal/modfetch/unzip.go
+++ b/src/cmd/go/internal/modfetch/unzip.go
@@ -11,9 +11,11 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"cmd/go/internal/modfetch/codehost"
+	"cmd/go/internal/str"
 )
 
 func Unzip(dir, zipfile, prefix string, maxSize int64) error {
@@ -49,12 +51,15 @@
 	// Check total size.
 	var size int64
 	for _, zf := range z.File {
-		if !strings.HasPrefix(zf.Name, prefix) {
+		if !str.HasPathPrefix(zf.Name, prefix) {
 			return fmt.Errorf("unzip %v: unexpected file name %s", zipfile, zf.Name)
 		}
-		if strings.HasSuffix(zf.Name, "/") {
+		if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") {
 			continue
 		}
+		if filepath.Clean(zf.Name) != zf.Name || strings.HasPrefix(zf.Name[len(prefix)+1:], "/") {
+			return fmt.Errorf("unzip %v: invalid file name %s", zipfile, zf.Name)
+		}
 		s := int64(zf.UncompressedSize64)
 		if s < 0 || maxSize-size < s {
 			return fmt.Errorf("unzip %v: content too large", zipfile)
@@ -63,11 +68,18 @@
 	}
 
 	// Unzip, enforcing sizes checked earlier.
+	dirs := map[string]bool{dir: true}
 	for _, zf := range z.File {
-		if strings.HasSuffix(zf.Name, "/") {
+		if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") {
 			continue
 		}
-		dst := filepath.Join(dir, zf.Name[len(prefix):])
+		name := zf.Name[len(prefix):]
+		dst := filepath.Join(dir, name)
+		parent := filepath.Dir(dst)
+		for parent != dir {
+			dirs[parent] = true
+			parent = filepath.Dir(parent)
+		}
 		if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
 			return err
 		}
@@ -77,7 +89,6 @@
 		}
 		r, err := zf.Open()
 		if err != nil {
-			r.Close()
 			w.Close()
 			return fmt.Errorf("unzip %v: %v", zipfile, err)
 		}
@@ -96,5 +107,17 @@
 		}
 	}
 
+	// Mark directories unwritable, best effort.
+	var dirlist []string
+	for dir := range dirs {
+		dirlist = append(dirlist, dir)
+	}
+	sort.Strings(dirlist)
+
+	// Run over list backward to chmod children before parents.
+	for i := len(dirlist) - 1; i >= 0; i-- {
+		os.Chmod(dir, 0555)
+	}
+
 	return nil
 }
diff --git a/src/cmd/go/internal/modfile/read.go b/src/cmd/go/internal/modfile/read.go
index a0c88d6..1d81ff1 100644
--- a/src/cmd/go/internal/modfile/read.go
+++ b/src/cmd/go/internal/modfile/read.go
@@ -11,6 +11,7 @@
 	"bytes"
 	"fmt"
 	"os"
+	"strconv"
 	"strings"
 	"unicode"
 	"unicode/utf8"
@@ -88,6 +89,131 @@
 	return start, end
 }
 
+func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line {
+	if hint == nil {
+		// If no hint given, add to the last statement of the given type.
+	Loop:
+		for i := len(x.Stmt) - 1; i >= 0; i-- {
+			stmt := x.Stmt[i]
+			switch stmt := stmt.(type) {
+			case *Line:
+				if stmt.Token != nil && stmt.Token[0] == tokens[0] {
+					hint = stmt
+					break Loop
+				}
+			case *LineBlock:
+				if stmt.Token[0] == tokens[0] {
+					hint = stmt
+					break Loop
+				}
+			}
+		}
+	}
+
+	if hint != nil {
+		for i, stmt := range x.Stmt {
+			switch stmt := stmt.(type) {
+			case *Line:
+				if stmt == hint {
+					// Convert line to line block.
+					stmt.InBlock = true
+					block := &LineBlock{Token: stmt.Token[:1], Line: []*Line{stmt}}
+					stmt.Token = stmt.Token[1:]
+					x.Stmt[i] = block
+					new := &Line{Token: tokens[1:], InBlock: true}
+					block.Line = append(block.Line, new)
+					return new
+				}
+			case *LineBlock:
+				if stmt == hint {
+					new := &Line{Token: tokens[1:], InBlock: true}
+					stmt.Line = append(stmt.Line, new)
+					return new
+				}
+				for j, line := range stmt.Line {
+					if line == hint {
+						// Add new line after hint.
+						stmt.Line = append(stmt.Line, nil)
+						copy(stmt.Line[j+2:], stmt.Line[j+1:])
+						new := &Line{Token: tokens[1:], InBlock: true}
+						stmt.Line[j+1] = new
+						return new
+					}
+				}
+			}
+		}
+	}
+
+	new := &Line{Token: tokens}
+	x.Stmt = append(x.Stmt, new)
+	return new
+}
+
+func (x *FileSyntax) updateLine(line *Line, tokens ...string) {
+	if line.InBlock {
+		tokens = tokens[1:]
+	}
+	line.Token = tokens
+}
+
+func (x *FileSyntax) removeLine(line *Line) {
+	line.Token = nil
+}
+
+// Cleanup cleans up the file syntax x after any edit operations.
+// To avoid quadratic behavior, removeLine marks the line as dead
+// by setting line.Token = nil but does not remove it from the slice
+// in which it appears. After edits have all been indicated,
+// calling Cleanup cleans out the dead lines.
+func (x *FileSyntax) Cleanup() {
+	w := 0
+	for _, stmt := range x.Stmt {
+		switch stmt := stmt.(type) {
+		case *Line:
+			if stmt.Token == nil {
+				continue
+			}
+		case *LineBlock:
+			ww := 0
+			for _, line := range stmt.Line {
+				if line.Token != nil {
+					stmt.Line[ww] = line
+					ww++
+				}
+			}
+			if ww == 0 {
+				continue
+			}
+			if ww == 1 {
+				// Collapse block into single line.
+				line := &Line{
+					Comments: Comments{
+						Before: commentsAdd(stmt.Before, stmt.Line[0].Before),
+						Suffix: commentsAdd(stmt.Line[0].Suffix, stmt.Suffix),
+						After:  commentsAdd(stmt.Line[0].After, stmt.After),
+					},
+					Token: stringsAdd(stmt.Token, stmt.Line[0].Token),
+				}
+				x.Stmt[w] = line
+				w++
+				continue
+			}
+			stmt.Line = stmt.Line[:ww]
+		}
+		x.Stmt[w] = stmt
+		w++
+	}
+	x.Stmt = x.Stmt[:w]
+}
+
+func commentsAdd(x, y []Comment) []Comment {
+	return append(x[:len(x):len(x)], y...)
+}
+
+func stringsAdd(x, y []string) []string {
+	return append(x[:len(x):len(x)], y...)
+}
+
 // A CommentBlock represents a top-level block of comments separate
 // from any rule.
 type CommentBlock struct {
@@ -102,9 +228,10 @@
 // A Line is a single line of tokens.
 type Line struct {
 	Comments
-	Start Position
-	Token []string
-	End   Position
+	Start   Position
+	Token   []string
+	InBlock bool
+	End     Position
 }
 
 func (x *Line) Span() (start, end Position) {
@@ -679,9 +806,10 @@
 		switch tok {
 		case '\n', _EOF, _EOL:
 			return &Line{
-				Start: start,
-				Token: token,
-				End:   end,
+				Start:   start,
+				Token:   token,
+				End:     end,
+				InBlock: true,
 			}
 		default:
 			token = append(token, sym.text)
@@ -697,3 +825,45 @@
 	_STRING
 	_COMMENT
 )
+
+var (
+	slashSlash = []byte("//")
+	moduleStr  = []byte("module")
+)
+
+// ModulePath returns the module path from the gomod file text.
+// If it cannot find a module path, it returns an empty string.
+// It is tolerant of unrelated problems in the go.mod file.
+func ModulePath(mod []byte) string {
+	for len(mod) > 0 {
+		line := mod
+		mod = nil
+		if i := bytes.IndexByte(line, '\n'); i >= 0 {
+			line, mod = line[:i], line[i+1:]
+		}
+		if i := bytes.Index(line, slashSlash); i >= 0 {
+			line = line[:i]
+		}
+		line = bytes.TrimSpace(line)
+		if !bytes.HasPrefix(line, moduleStr) {
+			continue
+		}
+		line = line[len(moduleStr):]
+		n := len(line)
+		line = bytes.TrimSpace(line)
+		if len(line) == n || len(line) == 0 {
+			continue
+		}
+
+		if line[0] == '"' || line[0] == '`' {
+			p, err := strconv.Unquote(string(line))
+			if err != nil {
+				return "" // malformed quoted string or multiline module path
+			}
+			return p
+		}
+
+		return string(line)
+	}
+	return "" // missing module path
+}
diff --git a/src/cmd/go/internal/modfile/read_test.go b/src/cmd/go/internal/modfile/read_test.go
index 2c617b8..254e549 100644
--- a/src/cmd/go/internal/modfile/read_test.go
+++ b/src/cmd/go/internal/modfile/read_test.go
@@ -304,3 +304,47 @@
 	}
 	t.Error(string(data))
 }
+
+var modulePathTests = []struct {
+	input    []byte
+	expected string
+}{
+	{input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
+	{input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
+	{input: []byte("module  \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"},
+	{input: []byte("module  github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"},
+	{input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"},
+	{input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"},
+	{input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"},
+	{input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"},
+	{input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"},
+	{input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"},
+	{input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""},
+	{input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"},
+	{input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"},
+	{input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"},
+	{input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"},
+	{input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"},
+	{input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""},
+	{input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""},
+	{input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""},
+	{input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""},
+	{input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""},
+	{input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""},
+	{input: []byte("module  \nmodule a/b/c "), expected: "a/b/c"},
+	{input: []byte("module \"   \""), expected: "   "},
+	{input: []byte("module   "), expected: ""},
+	{input: []byte("module \"  a/b/c  \""), expected: "  a/b/c  "},
+	{input: []byte("module \"github.com/rsc/vgotest1\" // with a comment"), expected: "github.com/rsc/vgotest1"},
+}
+
+func TestModulePath(t *testing.T) {
+	for _, test := range modulePathTests {
+		t.Run(string(test.input), func(t *testing.T) {
+			result := ModulePath(test.input)
+			if result != test.expected {
+				t.Fatalf("ModulePath(%q): %s, want %s", string(test.input), result, test.expected)
+			}
+		})
+	}
+}
diff --git a/src/cmd/go/internal/modfile/rule.go b/src/cmd/go/internal/modfile/rule.go
index 5a784a3..80a3bbc 100644
--- a/src/cmd/go/internal/modfile/rule.go
+++ b/src/cmd/go/internal/modfile/rule.go
@@ -18,6 +18,7 @@
 	"cmd/go/internal/semver"
 )
 
+// A File is the parsed, interpreted form of a go.mod file.
 type File struct {
 	Module  *Module
 	Require []*Require
@@ -27,38 +28,46 @@
 	Syntax *FileSyntax
 }
 
+// A Module is the module statement.
 type Module struct {
-	Mod   module.Version
-	Major string
-}
-
-type Require struct {
 	Mod    module.Version
 	Syntax *Line
 }
 
+// A Require is a single require statement.
+type Require struct {
+	Mod      module.Version
+	Indirect bool // has "// indirect" comment
+	Syntax   *Line
+}
+
+// An Exclude is a single exclude statement.
 type Exclude struct {
 	Mod    module.Version
 	Syntax *Line
 }
 
+// A Replace is a single replace statement.
 type Replace struct {
-	Old module.Version
-	New module.Version
-
+	Old    module.Version
+	New    module.Version
 	Syntax *Line
 }
 
-func (f *File) AddModuleStmt(path string) {
-	f.Module = &Module{
-		Mod: module.Version{Path: path},
-	}
+func (f *File) AddModuleStmt(path string) error {
 	if f.Syntax == nil {
 		f.Syntax = new(FileSyntax)
 	}
-	f.Syntax.Stmt = append(f.Syntax.Stmt, &Line{
-		Token: []string{"module", AutoQuote(path)},
-	})
+	if f.Module == nil {
+		f.Module = &Module{
+			Mod:    module.Version{Path: path},
+			Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)),
+		}
+	} else {
+		f.Module.Mod.Path = path
+		f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path))
+	}
+	return nil
 }
 
 func (f *File) AddComment(text string) {
@@ -122,11 +131,6 @@
 	// replace and exclude either. They don't matter, and it will work better for
 	// forward compatibility if we can depend on modules that have local changes.
 
-	// TODO: For the target module (not dependencies), maybe we should
-	// relax the semver requirement and rewrite the file with updated info
-	// after resolving any versions. That would let people type commit hashes
-	// or tags or branch names, and then vgo would fix them.
-
 	switch verb {
 	default:
 		fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb)
@@ -135,7 +139,7 @@
 			fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line)
 			return
 		}
-		f.Module = new(Module)
+		f.Module = &Module{Syntax: line}
 		if len(args) != 1 {
 
 			fmt.Fprintf(errs, "%s:%d: usage: module module/path [version]\n", f.Syntax.Name, line.Start.Line)
@@ -174,8 +178,9 @@
 		}
 		if verb == "require" {
 			f.Require = append(f.Require, &Require{
-				Mod:    module.Version{Path: s, Version: v},
-				Syntax: line,
+				Mod:      module.Version{Path: s, Version: v},
+				Syntax:   line,
+				Indirect: isIndirect(line),
 			})
 		} else {
 			f.Exclude = append(f.Exclude, &Exclude{
@@ -184,8 +189,12 @@
 			})
 		}
 	case "replace":
-		if len(args) < 4 || len(args) > 5 || args[2] != "=>" {
-			fmt.Fprintf(errs, "%s:%d: usage: %s module/path v1.2.3 => other/module v1.4\n\t or %s module/path v1.2.3 => ../local/directory", f.Syntax.Name, line.Start.Line, verb, verb)
+		arrow := 2
+		if len(args) >= 2 && args[1] == "=>" {
+			arrow = 1
+		}
+		if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" {
+			fmt.Fprintf(errs, "%s:%d: usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory", f.Syntax.Name, line.Start.Line, verb, verb)
 			return
 		}
 		s, err := parseString(&args[0])
@@ -193,29 +202,32 @@
 			fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
 			return
 		}
-		old := args[1]
-		v, err := parseVersion(s, &args[1], fix)
-		if err != nil {
-			fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
-			return
-		}
 		v1, err := moduleMajorVersion(s)
 		if err != nil {
 			fmt.Fprintf(errs, "%s:%d: %v\n", f.Syntax.Name, line.Start.Line, err)
 			return
 		}
-		if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
-			fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
-			return
+		var v string
+		if arrow == 2 {
+			old := args[1]
+			v, err = parseVersion(s, &args[1], fix)
+			if err != nil {
+				fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
+				return
+			}
+			if v2 := semver.Major(v); v1 != v2 && (v1 != "v1" || v2 != "v0") {
+				fmt.Fprintf(errs, "%s:%d: invalid module: %s should be %s, not %s (%s)\n", f.Syntax.Name, line.Start.Line, s, v1, v2, v)
+				return
+			}
 		}
-		ns, err := parseString(&args[3])
+		ns, err := parseString(&args[arrow+1])
 		if err != nil {
 			fmt.Fprintf(errs, "%s:%d: invalid quoted string: %v\n", f.Syntax.Name, line.Start.Line, err)
 			return
 		}
 		nv := ""
-		if len(args) == 4 {
-			if !isDirectoryPath(ns) {
+		if len(args) == arrow+2 {
+			if !IsDirectoryPath(ns) {
 				fmt.Fprintf(errs, "%s:%d: replacement module without version must be directory path (rooted or starting with ./ or ../)", f.Syntax.Name, line.Start.Line)
 				return
 			}
@@ -224,19 +236,18 @@
 				return
 			}
 		}
-		if len(args) == 5 {
-			old := args[4]
-			nv, err = parseVersion(ns, &args[4], fix)
+		if len(args) == arrow+3 {
+			old := args[arrow+1]
+			nv, err = parseVersion(ns, &args[arrow+2], fix)
 			if err != nil {
 				fmt.Fprintf(errs, "%s:%d: invalid module version %v: %v\n", f.Syntax.Name, line.Start.Line, old, err)
 				return
 			}
-			if isDirectoryPath(ns) {
+			if IsDirectoryPath(ns) {
 				fmt.Fprintf(errs, "%s:%d: replacement module directory path %q cannot have version", f.Syntax.Name, line.Start.Line, ns)
 				return
 			}
 		}
-		// TODO: More sanity checks about directories vs module paths.
 		f.Replace = append(f.Replace, &Replace{
 			Old:    module.Version{Path: s, Version: v},
 			New:    module.Version{Path: ns, Version: nv},
@@ -245,7 +256,58 @@
 	}
 }
 
-func isDirectoryPath(ns string) bool {
+// isIndirect reports whether line has a "// indirect" comment,
+// meaning it is in go.mod only for its effect on indirect dependencies,
+// so that it can be dropped entirely once the effective version of the
+// indirect dependency reaches the given minimum version.
+func isIndirect(line *Line) bool {
+	if len(line.Suffix) == 0 {
+		return false
+	}
+	f := strings.Fields(line.Suffix[0].Token)
+	return (len(f) == 2 && f[1] == "indirect" || len(f) > 2 && f[1] == "indirect;") && f[0] == "//"
+}
+
+// setIndirect sets line to have (or not have) a "// indirect" comment.
+func setIndirect(line *Line, indirect bool) {
+	if isIndirect(line) == indirect {
+		return
+	}
+	if indirect {
+		// Adding comment.
+		if len(line.Suffix) == 0 {
+			// New comment.
+			line.Suffix = []Comment{{Token: "// indirect", Suffix: true}}
+			return
+		}
+		// Insert at beginning of existing comment.
+		com := &line.Suffix[0]
+		space := " "
+		if len(com.Token) > 2 && com.Token[2] == ' ' || com.Token[2] == '\t' {
+			space = ""
+		}
+		com.Token = "// indirect;" + space + com.Token[2:]
+		return
+	}
+
+	// Removing comment.
+	f := strings.Fields(line.Suffix[0].Token)
+	if len(f) == 2 {
+		// Remove whole comment.
+		line.Suffix = nil
+		return
+	}
+
+	// Remove comment prefix.
+	com := &line.Suffix[0]
+	i := strings.Index(com.Token, "indirect;")
+	com.Token = "//" + com.Token[i+len("indirect;"):]
+}
+
+// IsDirectoryPath reports whether the given path should be interpreted
+// as a directory path. Just like on the go command line, relative paths
+// and rooted paths are directory paths; the rest are module paths.
+func IsDirectoryPath(ns string) bool {
 	// Because go.mod files can move from one system to another,
 	// we check all known path syntaxes, both Unix and Windows.
 	return strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, "/") ||
@@ -253,19 +315,21 @@
 		len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':'
 }
 
-func mustQuote(t string) bool {
-	for _, r := range t {
+// MustQuote reports whether s must be quoted in order to appear as
+// a single token in a go.mod line.
+func MustQuote(s string) bool {
+	for _, r := range s {
 		if !unicode.IsPrint(r) || r == ' ' || r == '"' || r == '\'' || r == '`' {
 			return true
 		}
 	}
-	return t == "" || strings.Contains(t, "//") || strings.Contains(t, "/*")
+	return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*")
 }
 
 // AutoQuote returns s or, if quoting is required for s to appear in a go.mod,
 // the quotation of s.
 func AutoQuote(s string) string {
-	if mustQuote(s) {
+	if MustQuote(s) {
 		return strconv.Quote(s)
 	}
 	return s
@@ -339,50 +403,80 @@
 	return Format(f.Syntax), nil
 }
 
-func (x *File) AddRequire(path, vers string) {
-	var syntax *Line
+// Cleanup cleans up the file f after any edit operations.
+// To avoid quadratic behavior, modifications like DropRequire
+// clear the entry but do not remove it from the slice.
+// Cleanup cleans out all the cleared entries.
+func (f *File) Cleanup() {
+	w := 0
+	for _, r := range f.Require {
+		if r.Mod.Path != "" {
+			f.Require[w] = r
+			w++
+		}
+	}
+	f.Require = f.Require[:w]
 
-	for i, stmt := range x.Syntax.Stmt {
-		switch stmt := stmt.(type) {
-		case *LineBlock:
-			if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
-				syntax = &Line{Token: []string{AutoQuote(path), vers}}
-				stmt.Line = append(stmt.Line, syntax)
-				goto End
-			}
-		case *Line:
-			if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
-				stmt.Token = stmt.Token[1:]
-				syntax = &Line{Token: []string{AutoQuote(path), vers}}
-				x.Syntax.Stmt[i] = &LineBlock{
-					Comments: stmt.Comments,
-					Token:    []string{"require"},
-					Line: []*Line{
-						stmt,
-						syntax,
-					},
-				}
-				goto End
+	w = 0
+	for _, x := range f.Exclude {
+		if x.Mod.Path != "" {
+			f.Exclude[w] = x
+			w++
+		}
+	}
+	f.Exclude = f.Exclude[:w]
+
+	w = 0
+	for _, r := range f.Replace {
+		if r.Old.Path != "" {
+			f.Replace[w] = r
+			w++
+		}
+	}
+	f.Replace = f.Replace[:w]
+
+	f.Syntax.Cleanup()
+}
+
+func (f *File) AddRequire(path, vers string) error {
+	need := true
+	for _, r := range f.Require {
+		if r.Mod.Path == path {
+			if need {
+				r.Mod.Version = vers
+				f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers)
+				need = false
+			} else {
+				f.Syntax.removeLine(r.Syntax)
+				*r = Require{}
 			}
 		}
 	}
 
-	syntax = &Line{Token: []string{"require", AutoQuote(path), vers}}
-	x.Syntax.Stmt = append(x.Syntax.Stmt, syntax)
-
-End:
-	x.Require = append(x.Require, &Require{module.Version{Path: path, Version: vers}, syntax})
+	if need {
+		f.AddNewRequire(path, vers, false)
+	}
+	return nil
 }
 
-func (f *File) SetRequire(req []module.Version) {
+func (f *File) AddNewRequire(path, vers string, indirect bool) {
+	line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers)
+	setIndirect(line, indirect)
+	f.Require = append(f.Require, &Require{module.Version{Path: path, Version: vers}, indirect, line})
+}
+
+func (f *File) SetRequire(req []*Require) {
 	need := make(map[string]string)
-	for _, m := range req {
-		need[m.Path] = m.Version
+	indirect := make(map[string]bool)
+	for _, r := range req {
+		need[r.Mod.Path] = r.Mod.Version
+		indirect[r.Mod.Path] = r.Indirect
 	}
 
 	for _, r := range f.Require {
 		if v, ok := need[r.Mod.Path]; ok {
 			r.Mod.Version = v
+			r.Indirect = indirect[r.Mod.Path]
 		}
 	}
 
@@ -393,9 +487,10 @@
 			if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
 				var newLines []*Line
 				for _, line := range stmt.Line {
-					if p, err := strconv.Unquote(line.Token[0]); err == nil && need[p] != "" {
+					if p, err := parseString(&line.Token[0]); err == nil && need[p] != "" {
 						line.Token[1] = need[p]
 						delete(need, p)
+						setIndirect(line, indirect[p])
 						newLines = append(newLines, line)
 					}
 				}
@@ -407,9 +502,10 @@
 
 		case *Line:
 			if len(stmt.Token) > 0 && stmt.Token[0] == "require" {
-				if p, err := strconv.Unquote(stmt.Token[1]); err == nil && need[p] != "" {
+				if p, err := parseString(&stmt.Token[1]); err == nil && need[p] != "" {
 					stmt.Token[2] = need[p]
 					delete(need, p)
+					setIndirect(stmt, indirect[p])
 				} else {
 					continue // drop stmt
 				}
@@ -420,11 +516,93 @@
 	f.Syntax.Stmt = newStmts
 
 	for path, vers := range need {
-		f.AddRequire(path, vers)
+		f.AddNewRequire(path, vers, indirect[path])
 	}
 	f.SortBlocks()
 }
 
+func (f *File) DropRequire(path string) error {
+	for _, r := range f.Require {
+		if r.Mod.Path == path {
+			f.Syntax.removeLine(r.Syntax)
+			*r = Require{}
+		}
+	}
+	return nil
+}
+
+func (f *File) AddExclude(path, vers string) error {
+	var hint *Line
+	for _, x := range f.Exclude {
+		if x.Mod.Path == path && x.Mod.Version == vers {
+			return nil
+		}
+		if x.Mod.Path == path {
+			hint = x.Syntax
+		}
+	}
+
+	f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)})
+	return nil
+}
+
+func (f *File) DropExclude(path, vers string) error {
+	for _, x := range f.Exclude {
+		if x.Mod.Path == path && x.Mod.Version == vers {
+			f.Syntax.removeLine(x.Syntax)
+			*x = Exclude{}
+		}
+	}
+	return nil
+}
+
+func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
+	need := true
+	old := module.Version{Path: oldPath, Version: oldVers}
+	new := module.Version{Path: newPath, Version: newVers}
+	tokens := []string{"replace", AutoQuote(oldPath)}
+	if oldVers != "" {
+		tokens = append(tokens, oldVers)
+	}
+	tokens = append(tokens, "=>", AutoQuote(newPath))
+	if newVers != "" {
+		tokens = append(tokens, newVers)
+	}
+
+	var hint *Line
+	for _, r := range f.Replace {
+		if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) {
+			if need {
+				// Found replacement for old; update to use new.
+				r.New = new
+				f.Syntax.updateLine(r.Syntax, tokens...)
+				need = false
+				continue
+			}
+			// Already added; delete other replacements for same.
+			f.Syntax.removeLine(r.Syntax)
+			*r = Replace{}
+		}
+		if r.Old.Path == oldPath {
+			hint = r.Syntax
+		}
+	}
+	if need {
+		f.Replace = append(f.Replace, &Replace{Old: old, New: new, Syntax: f.Syntax.addLine(hint, tokens...)})
+	}
+	return nil
+}
+
+func (f *File) DropReplace(oldPath, oldVers string) error {
+	for _, r := range f.Replace {
+		if r.Old.Path == oldPath && r.Old.Version == oldVers {
+			f.Syntax.removeLine(r.Syntax)
+			*r = Replace{}
+		}
+	}
+	return nil
+}
+
 func (f *File) SortBlocks() {
 	f.removeDups() // otherwise sorting is unsafe
 
diff --git a/src/cmd/go/internal/modfile/rule_test.go b/src/cmd/go/internal/modfile/rule_test.go
new file mode 100644
index 0000000..b88ad62
--- /dev/null
+++ b/src/cmd/go/internal/modfile/rule_test.go
@@ -0,0 +1,90 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modfile
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+)
+
+var addRequireTests = []struct {
+	in   string
+	path string
+	vers string
+	out  string
+}{
+	{
+		`
+		module m
+		require x.y/z v1.2.3
+		`,
+		"x.y/z", "v1.5.6",
+		`
+		module m
+		require x.y/z v1.5.6
+		`,
+	},
+	{
+		`
+		module m
+		require x.y/z v1.2.3
+		`,
+		"x.y/w", "v1.5.6",
+		`
+		module m
+		require (
+			x.y/z v1.2.3
+			x.y/w v1.5.6
+		)
+		`,
+	},
+	{
+		`
+		module m
+		require x.y/z v1.2.3
+		require x.y/q/v2 v2.3.4
+		`,
+		"x.y/w", "v1.5.6",
+		`
+		module m
+		require x.y/z v1.2.3
+		require (
+			x.y/q/v2 v2.3.4
+			x.y/w v1.5.6
+		)
+		`,
+	},
+}
+
+func TestAddRequire(t *testing.T) {
+	for i, tt := range addRequireTests {
+		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
+			f, err := Parse("in", []byte(tt.in), nil)
+			if err != nil {
+				t.Fatal(err)
+			}
+			g, err := Parse("out", []byte(tt.out), nil)
+			if err != nil {
+				t.Fatal(err)
+			}
+			golden, err := g.Format()
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if err := f.AddRequire(tt.path, tt.vers); err != nil {
+				t.Fatal(err)
+			}
+			out, err := f.Format()
+			if err != nil {
+				t.Fatal(err)
+			}
+			if !bytes.Equal(out, golden) {
+				t.Errorf("have:\n%s\nwant:\n%s", out, golden)
+			}
+		})
+	}
+}
diff --git a/src/cmd/go/internal/modfile/testdata/replace2.golden b/src/cmd/go/internal/modfile/testdata/replace2.golden
index 1d18a3b..e1d9c72 100644
--- a/src/cmd/go/internal/modfile/testdata/replace2.golden
+++ b/src/cmd/go/internal/modfile/testdata/replace2.golden
@@ -5,4 +5,6 @@
 	xyz v1.3.4 => my/xyz v1.3.4-me
 	xyz v1.4.5 => "/tmp/my dir"
 	xyz v1.5.6 => my/xyz v1.5.6
+
+	xyz => my/other/xyz v1.5.4
 )
diff --git a/src/cmd/go/internal/modfile/testdata/replace2.in b/src/cmd/go/internal/modfile/testdata/replace2.in
index 78c4669..7864698 100644
--- a/src/cmd/go/internal/modfile/testdata/replace2.in
+++ b/src/cmd/go/internal/modfile/testdata/replace2.in
@@ -5,4 +5,6 @@
 	"xyz" v1.3.4 => "my/xyz" "v1.3.4-me"
 	xyz "v1.4.5" => "/tmp/my dir"
 	xyz v1.5.6 => my/xyz v1.5.6
+
+	xyz => my/other/xyz v1.5.4
 )
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
new file mode 100644
index 0000000..4f97dbb
--- /dev/null
+++ b/src/cmd/go/internal/modget/get.go
@@ -0,0 +1,606 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package modget implements the module-aware ``go get'' command.
+package modget
+
+import (
+	"cmd/go/internal/base"
+	"cmd/go/internal/cfg"
+	"cmd/go/internal/get"
+	"cmd/go/internal/load"
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modload"
+	"cmd/go/internal/module"
+	"cmd/go/internal/mvs"
+	"cmd/go/internal/par"
+	"cmd/go/internal/search"
+	"cmd/go/internal/semver"
+	"cmd/go/internal/str"
+	"cmd/go/internal/work"
+	"fmt"
+	"os"
+	pathpkg "path"
+	"path/filepath"
+	"strings"
+)
+
+var CmdGet = &base.Command{
+	// Note: -d -m -u are listed explicitly because they are the most common get flags.
+	// Do not send CLs removing them because they're covered by [get flags].
+	UsageLine: "get [-d] [-m] [-u] [-v] [-insecure] [build flags] [packages]",
+	Short:     "add dependencies to current module and install them",
+	Long: `
+Get resolves and adds dependencies to the current development module
+and then builds and installs them.
+
+The first step is to resolve which dependencies to add. 
+
+For each named package or package pattern, get must decide which version of
+the corresponding module to use. By default, get chooses the latest tagged
+release version, such as v0.4.5 or v1.2.3. If there are no tagged release
+versions, get chooses the latest tagged prerelease version, such as
+v0.0.1-pre1. If there are no tagged versions at all, get chooses the latest
+known commit.
+
+This default version selection can be overridden by adding an @version
+suffix to the package argument, as in 'go get golang.org/x/text@v0.3.0'.
+For modules stored in source control repositories, the version suffix can
+also be a commit hash, branch identifier, or other syntax known to the
+source control system, as in 'go get golang.org/x/text@master'.
+The version suffix @latest explicitly requests the default behavior
+described above.
+
+If a module under consideration is already a dependency of the current
+development module, then get will update the required version.
+Specifying a version earlier than the current required version is valid and
+downgrades the dependency. The version suffix @none indicates that the
+dependency should be removed entirely.
+
+Although get defaults to using the latest version of the module containing
+a named package, it does not use the latest version of that module's
+dependencies. Instead it prefers to use the specific dependency versions
+requested by that module. For example, if the latest A requires module
+B v1.2.3, while B v1.2.4 and v1.3.1 are also available, then 'go get A'
+will use the latest A but then use B v1.2.3, as requested by A. (If there
+are competing requirements for a particular module, then 'go get' resolves
+those requirements by taking the maximum requested version.)
+
+The -u flag instructs get to update dependencies to use newer minor or
+patch releases when available. Continuing the previous example,
+'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3).
+
+The -u=patch flag (not -u patch) instructs get to update dependencies
+to use newer patch releases when available. Continuing the previous example,
+'go get -u=patch A' will use the latest A with B v1.2.4 (not B v1.2.3).
+
+In general, adding a new dependency may require upgrading
+existing dependencies to keep a working build, and 'go get' does
+this automatically. Similarly, downgrading one dependency may
+require downgrading other dependenceis, and 'go get' does
+this automatically as well.
+
+The -m flag instructs get to stop here, after resolving, upgrading,
+and downgrading modules and updating go.mod. When using -m,
+each specified package path must be a module path as well,
+not the import path of a package below the module root.
+
+The -insecure flag permits fetching from repositories and resolving
+custom domains using insecure schemes such as HTTP. Use with caution.
+
+The second step is to download (if needed), build, and install
+the named packages.
+
+If an argument names a module but not a package (because there is no
+Go source code in the module's root directory), then the install step
+is skipped for that argument, instead of causing a build failure.
+For example 'go get golang.org/x/perf' succeeds even though there
+is no code corresponding to that import path.
+
+Note that package patterns are allowed and are expanded after resolving
+the module versions. For example, 'go get golang.org/x/perf/cmd/...'
+adds the latest golang.org/x/perf and then installs the commands in that
+latest version.
+
+The -d flag instructs get to download the source code needed to build
+the named packages, including downloading necessary dependencies,
+but not to build and install them.
+
+With no package arguments, 'go get' applies to the main module,
+and to the Go package in the current directory, if any. In particular,
+'go get -u' and 'go get -u=patch' update all the dependencies of the
+main module. With no package arguments and also without -u,
+'go get' is not much more than 'go install', and 'go get -d' not much
+more than 'go list'.
+
+For more about modules, see 'go help modules'.
+
+For more about specifying packages, see 'go help packages'.
+
+This text describes the behavior of get using modules to manage source
+code and dependencies. If instead the go command is running in GOPATH
+mode, the details of get's flags and effects change, as does 'go help get'.
+See 'go help modules' and 'go help gopath-get'.
+
+See also: go build, go install, go clean, go mod.
+	`,
+}
+
+// Note that this help text is a stopgap to make the module-aware get help text
+// available even in non-module settings. It should be deleted when the old get
+// is deleted. It should NOT be considered to set a precedent of having hierarchical
+// help names with dashes.
+var HelpModuleGet = &base.Command{
+	UsageLine: "module-get",
+	Short:     "module-aware go get",
+	Long: `
+The 'go get' command changes behavior depending on whether the
+go command is running in module-aware mode or legacy GOPATH mode.
+This help text, accessible as 'go help module-get' even in legacy GOPATH mode,
+describes 'go get' as it operates in module-aware mode.
+
+Usage: ` + CmdGet.UsageLine + `
+` + CmdGet.Long,
+}
+
+var (
+	getD   = CmdGet.Flag.Bool("d", false, "")
+	getF   = CmdGet.Flag.Bool("f", false, "")
+	getFix = CmdGet.Flag.Bool("fix", false, "")
+	getM   = CmdGet.Flag.Bool("m", false, "")
+	getT   = CmdGet.Flag.Bool("t", false, "")
+	getU   upgradeFlag
+	// -insecure is get.Insecure
+	// -v is cfg.BuildV
+)
+
+// upgradeFlag is a custom flag.Value for -u.
+type upgradeFlag string
+
+func (*upgradeFlag) IsBoolFlag() bool { return true } // allow -u
+
+func (v *upgradeFlag) Set(s string) error {
+	if s == "false" {
+		s = ""
+	}
+	*v = upgradeFlag(s)
+	return nil
+}
+
+func (v *upgradeFlag) String() string { return "" }
+
+func init() {
+	work.AddBuildFlags(CmdGet)
+	CmdGet.Run = runGet // break init loop
+	CmdGet.Flag.BoolVar(&get.Insecure, "insecure", get.Insecure, "")
+	CmdGet.Flag.Var(&getU, "u", "")
+}
+
+type Pkg struct {
+	Arg  string
+	Path string
+	Vers string
+}
+
+func runGet(cmd *base.Command, args []string) {
+	switch getU {
+	case "", "patch", "true":
+		// ok
+	default:
+		base.Fatalf("go get: unknown upgrade flag -u=%s", getU)
+	}
+	if *getF {
+		fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n")
+	}
+	if *getFix {
+		fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n")
+	}
+	if *getT {
+		fmt.Fprintf(os.Stderr, "go get: -t flag is a no-op when using modules\n")
+	}
+
+	if cfg.BuildGetmode == "vendor" {
+		base.Fatalf("go get: disabled by -getmode=vendor")
+	}
+
+	modload.LoadBuildList()
+
+	// A task holds the state for processing a single get argument (path@vers).
+	type task struct {
+		arg             string           // original argument
+		path            string           // package path part of arg
+		forceModulePath bool             // path must be interpreted as a module path
+		vers            string           // version part of arg
+		m               module.Version   // module version indicated by argument
+		req             []module.Version // m's requirement list (not upgraded)
+	}
+
+	// Build task and install lists.
+	// The command-line arguments are of the form path@version
+	// or simply path, with implicit @latest. path@none is "downgrade away".
+	// At the end of the loop, we've resolved the list of arguments into
+	// a list of tasks (a path@vers that needs further processing)
+	// and a list of install targets (for the "go install" at the end).
+	var tasks []*task
+	var install []string
+	for _, arg := range search.CleanImportPaths(args) {
+		// Argument is module query path@vers, or else path with implicit @latest.
+		path := arg
+		vers := ""
+		if i := strings.Index(arg, "@"); i >= 0 {
+			path, vers = arg[:i], arg[i+1:]
+		}
+		if strings.Contains(vers, "@") || arg != path && vers == "" {
+			base.Errorf("go get %s: invalid module version syntax", arg)
+			continue
+		}
+		if vers != "none" {
+			install = append(install, path)
+		}
+
+		// Deciding which module to upgrade/downgrade for a particular argument is difficult.
+		// Patterns only make it more difficult.
+		// We impose restrictions to avoid needing to interlace pattern expansion,
+		// like in in modload.ImportPaths.
+		// Specifically, these patterns are supported:
+		//
+		//	- Relative paths like ../../foo or ../../foo... are restricted to matching directories
+		//	  in the current module and therefore map to the current module.
+		//	  It's possible that the pattern matches no packages, but we will still treat it
+		//	  as mapping to the current module.
+		//	  TODO: In followup, could just expand the full list and remove the discrepancy.
+		//	- The pattern "all" has its usual package meaning and maps to the list of modules
+		//	  from which the matched packages are drawn. This is potentially a subset of the
+		//	  module pattern "all". If module A requires B requires C but A does not import
+		//	  the parts of B that import C, the packages matched by "all" are only from A and B,
+		//	  so only A and B end up on the tasks list.
+		//	  TODO: Even in -m mode?
+		//	- The patterns "std" and "cmd" expand to packages in the standard library,
+		//	  which aren't upgradable, so we skip over those.
+		//	  In -m mode they expand to non-module-paths, so they are disallowed.
+		//	- Import path patterns like foo/bar... are matched against the module list,
+		//	  assuming any package match would imply a module pattern match.
+		//	  TODO: What about -m mode?
+		//	- Import paths without patterns are left as is, for resolution by getQuery (eventually modload.Import).
+		//
+		if search.IsRelativePath(path) {
+			// Check that this relative pattern only matches directories in the current module,
+			// and then record the current module as the target.
+			dir := path
+			if i := strings.Index(path, "..."); i >= 0 {
+				dir, _ = pathpkg.Split(path[:i])
+			}
+			abs, err := filepath.Abs(dir)
+			if err != nil {
+				base.Errorf("go get %s: %v", arg, err)
+				continue
+			}
+			if !str.HasFilePathPrefix(abs, modload.ModRoot) {
+				base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot)
+				continue
+			}
+			// TODO: Check if abs is inside a nested module.
+			tasks = append(tasks, &task{arg: arg, path: modload.Target.Path, vers: ""})
+			continue
+		}
+		if path == "all" {
+			if path != arg {
+				base.Errorf("go get %s: cannot use pattern %q with explicit version", arg, arg)
+			}
+
+			// TODO: If *getM, should this be the module pattern "all"?
+
+			// This is the package pattern "all" not the module pattern "all":
+			// enumerate all the modules actually needed by builds of the packages
+			// in the main module, not incidental modules that happen to be
+			// in the package graph (and therefore build list).
+			// Note that LoadALL may add new modules to the build list to
+			// satisfy new imports, but vers == "latest" implicitly anyway,
+			// so we'll assume that's OK.
+			seen := make(map[module.Version]bool)
+			pkgs := modload.LoadALL()
+			for _, pkg := range pkgs {
+				m := modload.PackageModule(pkg)
+				if m.Path != "" && !seen[m] {
+					seen[m] = true
+					tasks = append(tasks, &task{arg: arg, path: m.Path, vers: "latest", forceModulePath: true})
+				}
+			}
+			continue
+		}
+		if search.IsMetaPackage(path) {
+			// Already handled "all", so this must be "std" or "cmd",
+			// which are entirely in the standard library.
+			if path != arg {
+				base.Errorf("go get %s: cannot use pattern %q with explicit version", arg, arg)
+			}
+			if *getM {
+				base.Errorf("go get %s: cannot use pattern %q with -m", arg, arg)
+				continue
+			}
+			continue
+		}
+		if strings.Contains(path, "...") {
+			// Apply to modules in build list matched by pattern (golang.org/x/...), if any.
+			match := search.MatchPattern(path)
+			matched := false
+			for _, m := range modload.BuildList() {
+				if match(m.Path) || str.HasPathPrefix(path, m.Path) {
+					tasks = append(tasks, &task{arg: arg, path: m.Path, vers: vers, forceModulePath: true})
+					matched = true
+				}
+			}
+			// If matched, we're done.
+			// Otherwise assume pattern is inside a single module
+			// (golang.org/x/text/unicode/...) and leave for usual lookup.
+			// Unless we're using -m.
+			if matched {
+				continue
+			}
+			if *getM {
+				base.Errorf("go get %s: pattern matches no modules in build list", arg)
+				continue
+			}
+		}
+		tasks = append(tasks, &task{arg: arg, path: path, vers: vers})
+	}
+	base.ExitIfErrors()
+
+	// Now we've reduced the upgrade/downgrade work to a list of path@vers pairs (tasks).
+	// Resolve each one and load direct requirements in parallel.
+	reqs := modload.Reqs()
+	var lookup par.Work
+	for _, t := range tasks {
+		lookup.Add(t)
+	}
+	lookup.Do(10, func(item interface{}) {
+		t := item.(*task)
+		m, err := getQuery(t.path, t.vers, t.forceModulePath)
+		if err != nil {
+			base.Errorf("go get %v: %v", t.arg, err)
+			return
+		}
+		t.m = m
+		if t.vers == "none" {
+			// Wait for downgrade step.
+			return
+		}
+		// If there is no -u, then we don't need to upgrade the
+		// collected requirements separately from the overall
+		// recalculation of the build list (modload.ReloadBuildList below),
+		// so don't bother doing it now. Doing it now wouldn't be
+		// any slower (because it would prime the cache for later)
+		// but the larger operation below can report more errors in a single run.
+		if getU != "" {
+			list, err := reqs.Required(m)
+			if err != nil {
+				base.Errorf("go get %v: %v", t.arg, err)
+				return
+			}
+			t.req = list
+		}
+	})
+	base.ExitIfErrors()
+
+	// Now we know the specific version of each path@vers along with its requirements.
+	// The final build list will be the union of three build lists:
+	//	1. the original build list
+	//	2. the modules named on the command line
+	//	3. the upgraded requirements of those modules (if upgrading)
+	// Start building those lists.
+	// This loop collects (2) and the not-yet-upgraded (3).
+	// Also, because the list of paths might have named multiple packages in a single module
+	// (or even the same package multiple times), now that we know the module for each
+	// package, this loop deduplicates multiple references to a given module.
+	// (If a module is mentioned multiple times, the listed target version must be the same each time.)
+	var named []module.Version
+	var required []module.Version
+	byPath := make(map[string]*task)
+	for _, t := range tasks {
+		prev, ok := byPath[t.m.Path]
+		if prev != nil && prev.m != t.m {
+			base.Errorf("go get: conflicting versions for module %s: %s and %s", t.m.Path, prev.m.Version, t.m.Version)
+			byPath[t.m.Path] = nil // sentinel to stop errors
+			continue
+		}
+		if ok {
+			continue // already added
+		}
+		byPath[t.m.Path] = t
+		named = append(named, t.m)
+		required = append(required, t.req...)
+	}
+	base.ExitIfErrors()
+
+	// If the modules named on the command line have any dependencies
+	// and we're supposed to upgrade dependencies,
+	// chase down the full list of upgraded dependencies.
+	// This turns required from a not-yet-upgraded (3) to the final (3).
+	// (See list above.)
+	if len(required) > 0 {
+		upgraded, err := mvs.UpgradeAll(upgradeTarget, &upgrader{
+			Reqs:    modload.Reqs(),
+			targets: required,
+			patch:   getU == "patch",
+		})
+		if err != nil {
+			base.Fatalf("go get: %v", err)
+		}
+		required = upgraded[1:] // slice off upgradeTarget
+	}
+
+	// Put together the final build list as described above (1) (2) (3).
+	// If we're not using -u, then len(required) == 0 and ReloadBuildList
+	// chases down the dependencies of all the named module versions
+	// in one operation.
+	var list []module.Version
+	list = append(list, modload.BuildList()...)
+	list = append(list, named...)
+	list = append(list, required...)
+	modload.SetBuildList(list)
+	modload.ReloadBuildList() // note: does not update go.mod
+
+	// Apply any needed downgrades.
+	var down []module.Version
+	for _, m := range modload.BuildList() {
+		t := byPath[m.Path]
+		if t != nil && semver.Compare(m.Version, t.m.Version) > 0 {
+			down = append(down, module.Version{Path: m.Path, Version: t.m.Version})
+		}
+	}
+	if len(down) > 0 {
+		list, err := mvs.Downgrade(modload.Target, modload.Reqs(), down...)
+		if err != nil {
+			base.Fatalf("go get: %v", err)
+		}
+		modload.SetBuildList(list)
+		modload.ReloadBuildList() // note: does not update go.mod
+	}
+
+	// Everything succeeded. Update go.mod.
+	modload.WriteGoMod()
+
+	// If -m was specified, we're done after the module work. No download, no build.
+	if *getM {
+		return
+	}
+
+	if len(install) > 0 {
+		work.BuildInit()
+		var pkgs []string
+		for _, p := range load.PackagesAndErrors(install) {
+			if p.Error == nil || !strings.HasPrefix(p.Error.Err, "no Go files") {
+				pkgs = append(pkgs, p.ImportPath)
+			}
+		}
+		// If -d was specified, we're done after the download: no build.
+		// (The load.PackagesAndErrors is what did the download
+		// of the named packages and their dependencies.)
+		if len(pkgs) > 0 && !*getD {
+			work.InstallPackages(pkgs)
+		}
+	}
+}
+
+// getQuery evaluates the given package path, version pair
+// to determine the underlying module version being requested.
+// If forceModulePath is set, getQuery must interpret path
+// as a module path.
+func getQuery(path, vers string, forceModulePath bool) (module.Version, error) {
+	if path == modload.Target.Path {
+		if vers != "" {
+			return module.Version{}, fmt.Errorf("cannot update main module to explicit version")
+		}
+		return modload.Target, nil
+	}
+
+	if vers == "" {
+		vers = "latest"
+	}
+
+	// First choice is always to assume path is a module path.
+	// If that works out, we're done.
+	info, err := modload.Query(path, vers, modload.Allowed)
+	if err == nil {
+		return module.Version{Path: path, Version: info.Version}, nil
+	}
+
+	// Even if the query fails, if the path is (or must be) a real module, then report the query error.
+	if forceModulePath || *getM || isModulePath(path) {
+		return module.Version{}, err
+	}
+
+	// Otherwise, interpret the package path as an import
+	// and determine what module that import would address
+	// if found in the current source code.
+	// Then apply the version to that module.
+	m, _, err := modload.Import(path)
+	if err != nil {
+		return module.Version{}, err
+	}
+	if m.Path == "" {
+		return module.Version{}, fmt.Errorf("package %q is not in a module", path)
+	}
+	info, err = modload.Query(m.Path, vers, modload.Allowed)
+	if err != nil {
+		return module.Version{}, err
+	}
+	return module.Version{Path: m.Path, Version: info.Version}, nil
+}
+
+// isModulePath reports whether path names an actual module,
+// defined as one with an accessible latest version.
+func isModulePath(path string) bool {
+	_, err := modload.Query(path, "latest", modload.Allowed)
+	return err == nil
+}
+
+// An upgrader adapts an underlying mvs.Reqs to apply an
+// upgrade policy to a list of targets and their dependencies.
+// If patch=false, the upgrader implements "get -u".
+// If patch=true, the upgrader implements "get -u=patch".
+type upgrader struct {
+	mvs.Reqs
+	targets []module.Version
+	patch   bool
+}
+
+// upgradeTarget is a fake "target" requiring all the modules to be upgraded.
+var upgradeTarget = module.Version{Path: "upgrade target", Version: ""}
+
+// Required returns the requirement list for m.
+// Other than the upgradeTarget, we defer to u.Reqs.
+func (u *upgrader) Required(m module.Version) ([]module.Version, error) {
+	if m == upgradeTarget {
+		return u.targets, nil
+	}
+	return u.Reqs.Required(m)
+}
+
+// Upgrade returns the desired upgrade for m.
+// If m is a tagged version, then Upgrade returns the latest tagged version.
+// If m is a pseudo-version, then Upgrade returns the latest tagged version
+// when that version has a time-stamp newer than m.
+// Otherwise Upgrade returns m (preserving the pseudo-version).
+// This special case prevents accidental downgrades
+// when already using a pseudo-version newer than the latest tagged version.
+func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
+	// Note that query "latest" is not the same as
+	// using repo.Latest.
+	// The query only falls back to untagged versions
+	// if nothing is tagged. The Latest method
+	// only ever returns untagged versions,
+	// which is not what we want.
+	query := "latest"
+	if u.patch {
+		// For patch upgrade, query "v1.2".
+		query = semver.MajorMinor(m.Version)
+	}
+	info, err := modload.Query(m.Path, query, modload.Allowed)
+	if err != nil {
+		// Report error but return m, to let version selection continue.
+		// (Reporting the error will fail the command at the next base.ExitIfErrors.)
+		// Special case: if the error is "no matching versions" then don't
+		// even report the error. Because Query does not consider pseudo-versions,
+		// it may happen that we have a pseudo-version but during -u=patch
+		// the query v0.0 matches no versions (not even the one we're using).
+		if !strings.Contains(err.Error(), "no matching versions") {
+			base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err)
+		}
+		return m, nil
+	}
+
+	// If we're on a later prerelease, keep using it,
+	// even though normally an Upgrade will ignore prereleases.
+	if semver.Compare(info.Version, m.Version) < 0 {
+		return m, nil
+	}
+
+	// If we're on a pseudo-version chronologically after the latest tagged version, keep using it.
+	// This avoids some accidental downgrades.
+	if mTime, err := modfetch.PseudoVersionTime(m.Version); err == nil && info.Time.Before(mTime) {
+		return m, nil
+	}
+	return module.Version{Path: m.Path, Version: info.Version}, nil
+}
diff --git a/src/cmd/go/internal/modinfo/info.go b/src/cmd/go/internal/modinfo/info.go
index 6dff2d2..3920546 100644
--- a/src/cmd/go/internal/modinfo/info.go
+++ b/src/cmd/go/internal/modinfo/info.go
@@ -4,8 +4,44 @@
 
 package modinfo
 
+import "time"
+
+// Note that these structs are publicly visible (part of go list's API)
+// and the fields are documented in the help text in ../list/list.go
+
 type ModulePublic struct {
-	Top     bool
-	Path    string
-	Version string
+	Path     string        `json:",omitempty"` // module path
+	Version  string        `json:",omitempty"` // module version
+	Versions []string      `json:",omitempty"` // available module versions
+	Replace  *ModulePublic `json:",omitempty"` // replaced by this module
+	Time     *time.Time    `json:",omitempty"` // time version was created
+	Update   *ModulePublic `json:",omitempty"` // available update (with -u)
+	Main     bool          `json:",omitempty"` // is this the main module?
+	Indirect bool          `json:",omitempty"` // module is only indirectly needed by main module
+	Dir      string        `json:",omitempty"` // directory holding local copy of files, if any
+	Error    *ModuleError  `json:",omitempty"` // error loading module
+}
+
+type ModuleError struct {
+	Err string // error text
+}
+
+func (m *ModulePublic) String() string {
+	s := m.Path
+	if m.Version != "" {
+		s += " " + m.Version
+		if m.Update != nil {
+			s += " [" + m.Update.Version + "]"
+		}
+	}
+	if m.Replace != nil {
+		s += " => " + m.Replace.Path
+		if m.Replace.Version != "" {
+			s += " " + m.Replace.Version
+			if m.Replace.Update != nil {
+				s += " [" + m.Replace.Update.Version + "]"
+			}
+		}
+	}
+	return s
 }
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go
new file mode 100644
index 0000000..13424a2
--- /dev/null
+++ b/src/cmd/go/internal/modload/build.go
@@ -0,0 +1,220 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"bytes"
+	"cmd/go/internal/base"
+	"cmd/go/internal/cfg"
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modinfo"
+	"cmd/go/internal/module"
+	"cmd/go/internal/search"
+	"cmd/go/internal/semver"
+	"encoding/hex"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var (
+	infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
+	infoEnd, _   = hex.DecodeString("f932433186182072008242104116d8f2")
+)
+
+func isStandardImportPath(path string) bool {
+	if search.IsStandardImportPath(path) {
+		if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src", path)); err == nil {
+			return true
+		}
+		if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src/vendor", path)); err == nil {
+			return true
+		}
+	}
+	return false
+}
+
+func PackageModuleInfo(pkgpath string) *modinfo.ModulePublic {
+	if isStandardImportPath(pkgpath) || !Enabled() {
+		return nil
+	}
+	return moduleInfo(findModule(pkgpath, pkgpath), true)
+}
+
+func ModuleInfo(path string) *modinfo.ModulePublic {
+	if !Enabled() {
+		return nil
+	}
+
+	if i := strings.Index(path, "@"); i >= 0 {
+		return moduleInfo(module.Version{Path: path[:i], Version: path[i+1:]}, false)
+	}
+
+	for _, m := range BuildList() {
+		if m.Path == path {
+			return moduleInfo(m, true)
+		}
+	}
+
+	return &modinfo.ModulePublic{
+		Path: path,
+		Error: &modinfo.ModuleError{
+			Err: "module not in current build",
+		},
+	}
+}
+
+// addUpdate fills in m.Update if an updated version is available.
+func addUpdate(m *modinfo.ModulePublic) {
+	if m.Version != "" {
+		if info, err := Query(m.Path, "latest", Allowed); err == nil && info.Version != m.Version {
+			m.Update = &modinfo.ModulePublic{
+				Path:    m.Path,
+				Version: info.Version,
+				Time:    &info.Time,
+			}
+		}
+	}
+}
+
+// addVersions fills in m.Versions with the list of known versions.
+func addVersions(m *modinfo.ModulePublic) {
+	m.Versions, _ = versions(m.Path)
+}
+
+func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
+	if m == Target {
+		return &modinfo.ModulePublic{
+			Path:    m.Path,
+			Version: m.Version,
+			Main:    true,
+			Dir:     ModRoot,
+		}
+	}
+
+	info := &modinfo.ModulePublic{
+		Path:     m.Path,
+		Version:  m.Version,
+		Indirect: fromBuildList && loaded != nil && !loaded.direct[m.Path],
+	}
+
+	if cfg.BuildGetmode == "vendor" {
+		info.Dir = filepath.Join(ModRoot, "vendor", m.Path)
+		return info
+	}
+
+	// complete fills in the extra fields in m.
+	complete := func(m *modinfo.ModulePublic) {
+		if m.Version != "" {
+			if q, err := Query(m.Path, m.Version, nil); err != nil {
+				m.Error = &modinfo.ModuleError{Err: err.Error()}
+			} else {
+				m.Version = q.Version
+				m.Time = &q.Time
+			}
+
+			if semver.IsValid(m.Version) {
+				dir := filepath.Join(modfetch.SrcMod, m.Path+"@"+m.Version)
+				if stat, err := os.Stat(dir); err == nil && stat.IsDir() {
+					m.Dir = dir
+				}
+			}
+		}
+		if cfg.BuildGetmode == "vendor" {
+			m.Dir = filepath.Join(ModRoot, "vendor", m.Path)
+		}
+	}
+
+	complete(info)
+
+	if r := Replacement(m); r.Path != "" {
+		info.Replace = &modinfo.ModulePublic{
+			Path:    r.Path,
+			Version: r.Version,
+		}
+		if r.Version == "" {
+			if filepath.IsAbs(r.Path) {
+				info.Replace.Dir = r.Path
+			} else {
+				info.Replace.Dir = filepath.Join(ModRoot, r.Path)
+			}
+		}
+		complete(info.Replace)
+		info.Dir = info.Replace.Dir
+		info.Error = nil // ignore error loading original module version (it has been replaced)
+	}
+
+	return info
+}
+
+func PackageBuildInfo(path string, deps []string) string {
+	if isStandardImportPath(path) || !Enabled() {
+		return ""
+	}
+	target := findModule(path, path)
+	mdeps := make(map[module.Version]bool)
+	for _, dep := range deps {
+		if !isStandardImportPath(dep) {
+			mdeps[findModule(path, dep)] = true
+		}
+	}
+	var mods []module.Version
+	delete(mdeps, target)
+	for mod := range mdeps {
+		mods = append(mods, mod)
+	}
+	module.Sort(mods)
+
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "path\t%s\n", path)
+	tv := target.Version
+	if tv == "" {
+		tv = "(devel)"
+	}
+	fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, modfetch.Sum(target))
+	for _, mod := range mods {
+		mv := mod.Version
+		if mv == "" {
+			mv = "(devel)"
+		}
+		r := Replacement(mod)
+		h := ""
+		if r.Path == "" {
+			h = "\t" + modfetch.Sum(mod)
+		}
+		fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mod.Version, h)
+		if r.Path != "" {
+			fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
+		}
+	}
+	return buf.String()
+}
+
+func findModule(target, path string) module.Version {
+	// TODO: This should use loaded.
+	if path == "." {
+		return buildList[0]
+	}
+	for _, mod := range buildList {
+		if maybeInModule(path, mod.Path) {
+			return mod
+		}
+	}
+	base.Fatalf("build %v: cannot find module for path %v", target, path)
+	panic("unreachable")
+}
+
+func ModInfoProg(info string) []byte {
+	return []byte(fmt.Sprintf(`
+		package main
+		import _ "unsafe"
+		//go:linkname __debug_modinfo__ runtime/debug.modinfo
+		var __debug_modinfo__ string
+		func init() {
+			__debug_modinfo__ = %q
+		}
+	`, string(infoStart)+info+string(infoEnd)))
+}
diff --git a/src/cmd/go/internal/modload/help.go b/src/cmd/go/internal/modload/help.go
new file mode 100644
index 0000000..b457433
--- /dev/null
+++ b/src/cmd/go/internal/modload/help.go
@@ -0,0 +1,324 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import "cmd/go/internal/base"
+
+// TODO(rsc): The links out to research.swtch.com here should all be
+// replaced eventually with links to proper documentation.
+
+var HelpModules = &base.Command{
+	UsageLine: "modules",
+	Short:     "modules, module versions, and more",
+	Long: `
+A module is a collection of related Go packages.
+Modules are the unit of source code interchange and versioning.
+The go command has direct support for working with modules,
+including recording and resolving dependencies on other modules.
+Modules replace the old GOPATH-based approach to specifying
+which source files are used in a given build.
+
+Experimental module support
+
+Go 1.11 includes experimental support for Go modules,
+including a new module-aware 'go get' command.
+We intend to keep revising this support, while preserving compatibility,
+until it can be declared official (no longer experimental),
+and then at a later point we may remove support for work
+in GOPATH and the old 'go get' command.
+
+The quickest way to take advantage of the new Go 1.11 module support
+is to check out your repository into a directory outside GOPATH/src,
+create a go.mod file (described in the next section) there, and run
+go commands from within that file tree.
+
+For more fine-grained control, the module support in Go 1.11 respects
+a temporary environment variable, GO111MODULE, which can be set to one
+of three string values: off, on, or auto (the default).
+If GO111MODULE=off, then the go command never uses the
+new module support. Instead it looks in vendor directories and GOPATH
+to find dependencies; we now refer to this as "GOPATH mode."
+If GO111MODULE=on, then the go command requires the use of modules,
+never consulting GOPATH. We refer to this as the command being
+module-aware or running in "module-aware mode".
+If GO111MODULE=auto or is unset, then the go command enables or
+disables module support based on the current directory.
+Module support is enabled only when the current directory is outside
+GOPATH/src and itself contains a go.mod file or is below a directory
+containing a go.mod file.
+
+Defining a module
+
+A module is defined by a tree of Go source files with a go.mod file
+in the tree's root directory. The directory containing the go.mod file
+is called the module root. Typically the module root will also correspond
+to a source code repository root (but in general it need not).
+The module is the set of all Go packages in the module root and its
+subdirectories, but excluding subtrees with their own go.mod files.
+
+The "module path" is the import path prefix corresponding to the module root.
+The go.mod file defines the module path and lists the specific versions
+of other modules that should be used when resolving imports during a build,
+by giving their module paths and versions.
+
+For example, this go.mod declares that the directory containing it is the root
+of the module with path example.com/m, and it also declares that the module
+depends on specific versions of golang.org/x/text and gopkg.in/yaml.v2:
+
+	module example.com/m
+	
+	require (
+		golang.org/x/text v0.3.0
+		gopkg.in/yaml.v2 v2.1.0
+	)
+
+The go.mod file can also specify replacements and excluded versions
+that only apply when building the module directly; they are ignored
+when the module is incorporated into a larger build.
+For more about the go.mod file, see https://research.swtch.com/vgo-module.
+
+To start a new module, simply create a go.mod file in the root of the
+module's directory tree, containing only a module statement.
+The 'go mod' command can be used to do this:
+
+	go mod -init -module example.com/m
+
+In a project already using an existing dependency management tool like
+godep, glide, or dep, 'go mod -init' will also add require statements
+matching the existing configuration.
+
+Once the go.mod file exists, no additional steps are required:
+go commands like 'go build', 'go test', or even 'go list' will automatically
+add new dependencies as needed to satisfy imports.
+
+The main module and the build list
+
+The "main module" is the module containing the directory where the go command
+is run. The go command finds the module root by looking for a go.mod in the
+current directory, or else the current directory's parent directory,
+or else the parent's parent directory, and so on.
+
+The main module's go.mod file defines the precise set of packages available
+for use by the go command, through require, replace, and exclude statements.
+Dependency modules, found by following require statements, also contribute
+to the definition of that set of packages, but only through their go.mod
+files' require statements: any replace and exclude statements in dependency
+modules are ignored. The replace and exclude statements therefore allow the
+main module complete control over its own build, without also being subject
+to complete control by dependencies.
+
+The set of modules providing packages to builds is called the "build list".
+The build list initially contains only the main module. Then the go command
+adds to the list the exact module versions required by modules already
+on the list, recursively, until there is nothing left to add to the list.
+If multiple versions of a particular module are added to the list,
+then at the end only the latest version (according to semantic version
+ordering) is kept for use in the build.
+
+The 'go list' command provides information about the main module
+and the build list. For example:
+
+	go list -m              # print path of main module
+	go list -m -f={{.Dir}}  # print root directory of main module
+	go list -m all          # print build list
+
+Maintaining module requirements
+
+The go.mod file is meant to be readable and editable by both
+programmers and tools. The go command itself automatically updates the go.mod file
+to maintain a standard formatting and the accuracy of require statements.
+
+Any go command that finds an unfamiliar import will look up the module
+containing that import and add the latest version of that module
+to go.mod automatically. In most cases, therefore, it suffices to
+add an import to source code and run 'go build', 'go test', or even 'go list':
+as part of analyzing the package, the go command will discover
+and resolve the import and update the go.mod file.
+
+Any go command can determine that a module requirement is
+missing and must be added, even when considering only a single
+package from the module. On the other hand, determining that a module requirement
+is no longer necessary and can be deleted requires a full view of
+all packages in the module, across all possible build configurations
+(architectures, operating systems, build tags, and so on).
+The 'go mod -sync' command builds that view and then
+adds any missing module requirements and removes unnecessary ones.
+
+As part of maintaining the require statements in go.mod, the go command
+tracks which ones provide packages imported directly by the current module
+and which ones provide packages only used indirectly by other module
+dependencies. Requirements needed only for indirect uses are marked with a
+"// indirect" comment in the go.mod file. Indirect requirements are
+automatically removed from the go.mod file once they are implied by other
+direct requirements. Indirect requirements only arise when using modules
+that fail to state some of their own dependencies or when explicitly
+upgrading a module's dependencies ahead of its own stated requirements.
+
+Because of this automatic maintenance, the information in go.mod is an
+up-to-date, readable description of the build.
+
+The 'go get' command updates go.mod to change the module versions used in a
+build. An upgrade of one module may imply upgrading others, and similarly a
+downgrade of one module may imply downgrading others. The 'go get' command
+makes these implied changes as well. If go.mod is edited directly, commands
+like 'go build' or 'go list' will assume that an upgrade is intended and
+automatically make any implied upgrades and update go.mod to reflect them.
+
+The 'go mod' command provides other functionality for use in maintaining
+and understanding modules and go.mod files. See 'go help mod'.
+
+Pseudo-versions
+
+The go.mod file and the go command more generally use semantic versions as
+the standard form for describing module versions, so that versions can be
+compared to determine which should be considered earlier or later than another.
+A module version like v1.2.3 is introduced by tagging a revision in the
+underlying source repository. Untagged revisions can be referred to
+using a "pseudo-version" of the form v0.0.0-yyyymmddhhmmss-abcdefabcdef,
+where the time is the commit time in UTC and the final suffix is the prefix
+of the commit hash. The time portion ensures that two pseudo-versions can
+be compared to determine which happened later, the commit hash identifes
+the underlying commit, and the v0.0.0- prefix identifies the pseudo-version
+as a pre-release before version v0.0.0, so that the go command prefers any
+tagged release over any pseudo-version.
+
+Pseudo-versions never need to be typed by hand: the go command will accept
+the plain commit hash and translate it into a pseudo-version (or a tagged
+version if available) automatically. This conversion is an example of a
+module query.
+
+Module queries
+
+The go command accepts a "module query" in place of a module version
+both on the command line and in the main module's go.mod file.
+(After evaluating a query found in the main module's go.mod file,
+the go command updates the file to replace the query with its result.)
+
+A fully-specified semantic version, such as "v1.2.3",
+evaluates to that specific version.
+
+A semantic version prefix, such as "v1" or "v1.2",
+evaluates to the latest available tagged version with that prefix.
+
+A semantic version comparison, such as "<v1.2.3" or ">=v1.5.6",
+evaluates to the available tagged version nearest to the comparison target
+(the latest version for < and <=, the earliest version for > and >=).
+
+The string "latest" matches the latest available tagged version,
+or else the underlying source repository's latest untagged revision.
+
+A revision identifier for the underlying source repository,
+such as a commit hash prefix, revision tag, or branch name,
+selects that specific code revision. If the revision is
+also tagged with a semantic version, the query evaluates to
+that semantic version. Otherwise the query evaluates to a
+pseudo-version for the commit.
+
+All queries prefer release versions to pre-release versions.
+For example, "<v1.2.3" will prefer to return "v1.2.2"
+instead of "v1.2.3-pre1", even though "v1.2.3-pre1" is nearer
+to the comparison target.
+
+Module versions disallowed by exclude statements in the
+main module's go.mod are considered unavailable and cannot
+be returned by queries.
+
+For example, these commands are all valid:
+
+	go get github.com/gorilla/mux@latest    # same (@latest is default for 'go get')
+	go get github.com/gorilla/mux@v1.6.2    # records v1.6.2
+	go get github.com/gorilla/mux@e3702bed2 # records v1.6.2
+	go get github.com/gorilla/mux@c856192   # records v0.0.0-20180517173623-c85619274f5d
+	go get github.com/gorilla/mux@master    # records current meaning of master
+
+
+Module compatibility and semantic versioning
+
+The go command requires that modules use semantic versions and expects that
+the versions accurately describe compatibility: it assumes that v1.5.4 is a
+backwards-compatible replacement for v1.5.3, v1.4.0, and even v1.0.0.
+More generally the go command expects that packages follow the
+"import compatibility rule", which says:
+
+"If an old package and a new package have the same import path, 
+the new package must be backwards compatible with the old package."
+
+Because the go command assumes the import compatibility rule,
+a module definition can only set the minimum required version of one 
+of its dependencies: it cannot set a maximum or exclude selected versions.
+Still, the import compatibility rule is not a guarantee: it may be that
+v1.5.4 is buggy and not a backwards-compatible replacement for v1.5.3.
+Because of this, the go command never updates from an older version
+to a newer version of a module unasked.
+
+In semantic versioning, changing the major version number indicates a lack
+of backwards compatibility with earlier versions. To preserve import
+compatibility, the go command requires that modules with major version v2
+or later use a module path with that major version as the final element.
+For example, version v2.0.0 of example.com/m must instead use module path
+example.com/m/v2, and packages in that module would use that path as
+their import path prefix, as in example.com/m/v2/sub/pkg. Including the
+major version number in the module path and import paths in this way is
+called "semantic import versioning". Pseudo-versions for modules with major
+version v2 and later begin with that major version instead of v0, as in
+v2.0.0-20180326061214-4fc5987536ef.
+
+The go command treats modules with different module paths as unrelated:
+it makes no connection between example.com/m and example.com/m/v2.
+Modules with different major versions can be used together in a build
+and are kept separate by the fact that their packages use different
+import paths.
+
+In semantic versioning, major version v0 is for initial development,
+indicating no expectations of stability or backwards compatibility.
+Major version v0 does not appear in the module path, because those
+versions are preparation for v1.0.0, and v1 does not appear in the
+module path either.
+
+As a special case, for historical reasons, module paths beginning with
+gopkg.in/ continue to use the conventions established on that system:
+the major version is always present, and it is preceded by a dot 
+instead of a slash: gopkg.in/yaml.v1 and gopkg.in/yaml.v2, not
+gopkg.in/yaml and gopkg.in/yaml/v2.
+
+See https://research.swtch.com/vgo-import and https://semver.org/
+for more information.
+
+Module verification
+
+The go command maintains, in the main module's root directory alongside
+go.mod, a file named go.sum containing the expected cryptographic checksums
+of the content of specific module versions. Each time a dependency is
+used, its checksum is added to go.sum if missing or else required to match
+the existing entry in go.sum.
+
+The go command maintains a cache of downloaded packages and computes
+and records the cryptographic checksum of each package at download time.
+In normal operation, the go command checks these pre-computed checksums
+against the main module's go.sum file, instead of recomputing them on
+each command invocation. The 'go mod -verify' command checks that
+the cached copies of module downloads still match both their recorded
+checksums and the entries in go.sum.
+
+Modules and vendoring
+
+When using modules, the go command completely ignores vendor directories.
+
+By default, the go command satisfies dependencies by downloading modules
+from their sources and using those downloaded copies (after verification,
+as described in the previous section). To allow interoperation with older
+versions of Go, or to ensure that all files used for a build are stored
+together in a single file tree, 'go mod -vendor' creates a directory named
+vendor in the root directory of the main module and stores there all the
+packages from dependency modules that are needed to support builds and
+tests of packages in the main module.
+
+To build using the main module's top-level vendor directory to satisfy
+dependencies (disabling use of the usual network sources and local
+caches), use 'go build -getmode=vendor'. Note that only the main module's
+top-level vendor directory is used; vendor directories in other locations
+are still ignored.
+	`,
+}
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
new file mode 100644
index 0000000..e9dff9f
--- /dev/null
+++ b/src/cmd/go/internal/modload/import.go
@@ -0,0 +1,267 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"go/build"
+	"os"
+	pathpkg "path"
+	"path/filepath"
+	"strings"
+
+	"cmd/go/internal/cfg"
+	"cmd/go/internal/module"
+	"cmd/go/internal/par"
+	"cmd/go/internal/search"
+)
+
+type ImportMissingError struct {
+	ImportPath string
+	Module     module.Version
+}
+
+func (e *ImportMissingError) Error() string {
+	if e.Module.Path == "" {
+		return "cannot find module providing package " + e.ImportPath
+	}
+	return "missing module for import: " + e.Module.Path + "@" + e.Module.Version + " provides " + e.ImportPath
+}
+
+// Import finds the module and directory in the build list
+// containing the package with the given import path.
+// The answer must be unique: Import returns an error
+// if multiple modules attempt to provide the same package.
+// Import can return a module with an empty m.Path, for packages in the standard library.
+// Import can return an empty directory string, for fake packages like "C" and "unsafe".
+//
+// If the package cannot be found in the current build list,
+// Import returns an ImportMissingError as the error.
+// If Import can identify a module that could be added to supply the package,
+// the ImportMissingErr records that module.
+func Import(path string) (m module.Version, dir string, err error) {
+	if strings.Contains(path, "@") {
+		return module.Version{}, "", fmt.Errorf("import path should not have @version")
+	}
+	if build.IsLocalImport(path) {
+		return module.Version{}, "", fmt.Errorf("relative import not supported")
+	}
+	if path == "C" || path == "unsafe" {
+		// There's no directory for import "C" or import "unsafe".
+		return module.Version{}, "", nil
+	}
+
+	// Is the package in the standard library?
+	if search.IsStandardImportPath(path) {
+		if strings.HasPrefix(path, "golang_org/") {
+			return module.Version{}, filepath.Join(cfg.GOROOT, "src/vendor", path), nil
+		}
+		dir := filepath.Join(cfg.GOROOT, "src", path)
+		if _, err := os.Stat(dir); err == nil {
+			return module.Version{}, dir, nil
+		}
+	}
+
+	// -getmode=vendor is special.
+	// Everything must be in the main module or the main module's vendor directory.
+	if cfg.BuildGetmode == "vendor" {
+		mainDir, mainOK := dirInModule(path, Target.Path, ModRoot, true)
+		vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot, "vendor"), false)
+		if mainOK && vendorOK {
+			return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir)
+		}
+		// Prefer to return main directory if there is one,
+		// Note that we're not checking that the package exists.
+		// We'll leave that for load.
+		if !vendorOK && mainDir != "" {
+			return Target, mainDir, nil
+		}
+		readVendorList()
+		return vendorMap[path], vendorDir, nil
+	}
+
+	// Check each module on the build list.
+	var dirs []string
+	var mods []module.Version
+	for _, m := range buildList {
+		if !maybeInModule(path, m.Path) {
+			// Avoid possibly downloading irrelevant modules.
+			continue
+		}
+		root, isLocal, err := fetch(m)
+		if err != nil {
+			// Report fetch error.
+			// Note that we don't know for sure this module is necessary,
+			// but it certainly _could_ provide the package, and even if we
+			// continue the loop and find the package in some other module,
+			// we need to look at this module to make sure the import is
+			// not ambiguous.
+			return module.Version{}, "", err
+		}
+		dir, ok := dirInModule(path, m.Path, root, isLocal)
+		if ok {
+			mods = append(mods, m)
+			dirs = append(dirs, dir)
+		}
+	}
+	if len(mods) == 1 {
+		return mods[0], dirs[0], nil
+	}
+	if len(mods) > 0 {
+		var buf bytes.Buffer
+		fmt.Fprintf(&buf, "ambiguous import: found %s in multiple modules:", path)
+		for i, m := range mods {
+			fmt.Fprintf(&buf, "\n\t%s", m.Path)
+			if m.Version != "" {
+				fmt.Fprintf(&buf, " %s", m.Version)
+			}
+			fmt.Fprintf(&buf, " (%s)", dirs[i])
+		}
+		return module.Version{}, "", errors.New(buf.String())
+	}
+
+	// Special case: if the path matches a module path,
+	// and we haven't found code in any module on the build list
+	// (since we haven't returned yet),
+	// force the use of the current module instead of
+	// looking for an alternate one.
+	// This helps "go get golang.org/x/net" even though
+	// there is no code in x/net.
+	for _, m := range buildList {
+		if m.Path == path {
+			root, isLocal, err := fetch(m)
+			if err != nil {
+				return module.Version{}, "", err
+			}
+			dir, _ := dirInModule(path, m.Path, root, isLocal)
+			return m, dir, nil
+		}
+	}
+
+	// Not on build list.
+
+	// Look up module containing the package, for addition to the build list.
+	// Goal is to determine the module, download it to dir, and return m, dir, ErrMissing.
+	if cfg.BuildGetmode == "local" {
+		return module.Version{}, "", fmt.Errorf("import lookup disabled by -getmode=local")
+	}
+
+	for p := path; p != "."; p = pathpkg.Dir(p) {
+		// We can't upgrade the main module.
+		// Note that this loop does consider upgrading other modules on the build list.
+		// If that's too aggressive we can skip all paths already on the build list,
+		// not just Target.Path, but for now let's try being aggressive.
+		if p == Target.Path {
+			// Can't move to a new version of main module.
+			continue
+		}
+
+		info, err := Query(p, "latest", Allowed)
+		if err != nil {
+			continue
+		}
+		m := module.Version{Path: p, Version: info.Version}
+		root, isLocal, err := fetch(m)
+		if err != nil {
+			continue
+		}
+		_, ok := dirInModule(path, m.Path, root, isLocal)
+		if ok {
+			return module.Version{}, "", &ImportMissingError{ImportPath: path, Module: m}
+		}
+
+		// Special case matching the one above:
+		// if m.Path matches path, assume adding it to the build list
+		// will either add the right code or the right code doesn't exist.
+		if m.Path == path {
+			return module.Version{}, "", &ImportMissingError{ImportPath: path, Module: m}
+		}
+	}
+
+	// Did not resolve import to any module.
+	// TODO(rsc): It would be nice to return a specific error encountered
+	// during the loop above if possible, but it's not clear how to pick
+	// out the right one.
+	return module.Version{}, "", &ImportMissingError{ImportPath: path}
+}
+
+// maybeInModule reports whether, syntactically,
+// a package with the given import path could be supplied
+// by a module with the given module path (mpath).
+func maybeInModule(path, mpath string) bool {
+	return mpath == path ||
+		len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath
+}
+
+var haveGoModCache, haveGoFilesCache par.Cache
+
+// dirInModule locates the directory that would hold the package named by the given path,
+// if it were in the module with module path mpath and root mdir.
+// If path is syntactically not within mpath,
+// or if mdir is a local file tree (isLocal == true) and the directory
+// that would hold path is in a sub-module (covered by a go.mod below mdir),
+// dirInModule returns "", false.
+//
+// Otherwise, dirInModule returns the name of the directory where
+// Go source files would be expected, along with a boolean indicating
+// whether there are in fact Go source files in that directory.
+func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFiles bool) {
+	// Determine where to expect the package.
+	if path == mpath {
+		dir = mdir
+	} else if mpath == "" { // vendor directory
+		dir = filepath.Join(mdir, path)
+	} else if len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath {
+		dir = filepath.Join(mdir, path[len(mpath)+1:])
+	} else {
+		return "", false
+	}
+
+	// Check that there aren't other modules in the way.
+	// This check is unnecessary inside the module cache
+	// and important to skip in the vendor directory,
+	// where all the module trees have been overlaid.
+	// So we only check local module trees
+	// (the main module, and any directory trees pointed at by replace directives).
+	if isLocal {
+		for d := dir; d != mdir && len(d) > len(mdir); d = filepath.Dir(d) {
+			haveGoMod := haveGoModCache.Do(d, func() interface{} {
+				_, err := os.Stat(filepath.Join(d, "go.mod"))
+				return err == nil
+			}).(bool)
+
+			if haveGoMod {
+				return "", false
+			}
+		}
+	}
+
+	// Now committed to returning dir (not "").
+
+	// Are there Go source files in the directory?
+	// We don't care about build tags, not even "+build ignore".
+	// We're just looking for a plausible directory.
+	haveGoFiles = haveGoFilesCache.Do(dir, func() interface{} {
+		f, err := os.Open(dir)
+		if err != nil {
+			return false
+		}
+		defer f.Close()
+		names, _ := f.Readdirnames(-1)
+		for _, name := range names {
+			if strings.HasSuffix(name, ".go") {
+				info, err := os.Stat(filepath.Join(dir, name))
+				if err == nil && info.Mode().IsRegular() {
+					return true
+				}
+			}
+		}
+		return false
+	}).(bool)
+
+	return dir, haveGoFiles
+}
diff --git a/src/cmd/go/internal/modload/import_test.go b/src/cmd/go/internal/modload/import_test.go
new file mode 100644
index 0000000..8e01dc5
--- /dev/null
+++ b/src/cmd/go/internal/modload/import_test.go
@@ -0,0 +1,59 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"internal/testenv"
+	"regexp"
+	"strings"
+	"testing"
+)
+
+var importTests = []struct {
+	path string
+	err  string
+}{
+	{
+		path: "golang.org/x/net/context",
+		err:  "missing module for import: golang.org/x/net@.* provides golang.org/x/net/context",
+	},
+	{
+		path: "golang.org/x/net",
+		err:  "missing module for import: golang.org/x/net@.* provides golang.org/x/net",
+	},
+	{
+		path: "golang.org/x/text",
+		err:  "missing module for import: golang.org/x/text@.* provides golang.org/x/text",
+	},
+	{
+		path: "github.com/rsc/quote/buggy",
+		err:  "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote/buggy",
+	},
+	{
+		path: "github.com/rsc/quote",
+		err:  "missing module for import: github.com/rsc/quote@v1.5.2 provides github.com/rsc/quote",
+	},
+	{
+		path: "golang.org/x/foo/bar",
+		err:  "cannot find module providing package golang.org/x/foo/bar",
+	},
+}
+
+func TestImport(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
+	for _, tt := range importTests {
+		t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
+			// Note that there is no build list, so Import should always fail.
+			m, dir, err := Import(tt.path)
+			if err == nil {
+				t.Fatalf("Import(%q) = %v, %v, nil; expected error", tt.path, m, dir)
+			}
+			if !regexp.MustCompile(tt.err).MatchString(err.Error()) {
+				t.Fatalf("Import(%q): error %q, want error matching %#q", tt.path, err, tt.err)
+			}
+		})
+	}
+}
diff --git a/src/cmd/go/internal/vgo/init.go b/src/cmd/go/internal/modload/init.go
similarity index 62%
rename from src/cmd/go/internal/vgo/init.go
rename to src/cmd/go/internal/modload/init.go
index b307b6b..a360a2c 100644
--- a/src/cmd/go/internal/vgo/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package vgo
+package modload
 
 import (
 	"bytes"
 	"cmd/go/internal/base"
+	"cmd/go/internal/cache"
 	"cmd/go/internal/cfg"
+	"cmd/go/internal/load"
 	"cmd/go/internal/modconv"
 	"cmd/go/internal/modfetch"
 	"cmd/go/internal/modfetch/codehost"
@@ -16,8 +18,8 @@
 	"cmd/go/internal/mvs"
 	"cmd/go/internal/search"
 	"cmd/go/internal/semver"
+	"cmd/go/internal/str"
 	"encoding/json"
-	"flag"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -29,10 +31,10 @@
 )
 
 var (
-	cwd         string
-	enabled     = MustBeVgo
-	MustBeVgo   = mustBeVgo()
-	initialized bool
+	cwd            string
+	enabled        = MustUseModules
+	MustUseModules = mustUseModules()
+	initialized    bool
 
 	ModRoot  string
 	modFile  *modfile.File
@@ -40,24 +42,34 @@
 	Target   module.Version
 
 	gopath string
-	srcV   string
+
+	CmdModInit   bool   // go mod -init flag
+	CmdModModule string // go mod -module flag
 )
 
+// ModFile returns the parsed go.mod file.
+//
+// Note that after calling ImportPaths or LoadBuildList,
+// the require statements in the modfile.File are no longer
+// the source of truth and will be ignored: edits made directly
+// will be lost at the next call to WriteGoMod.
+// To make permanent changes to the require statements
+// in go.mod, edit it before calling ImportPaths or LoadBuildList.
+func ModFile() *modfile.File {
+	return modFile
+}
+
 func BinDir() string {
 	if !Enabled() {
-		panic("vgo.Bin")
+		panic("modload.BinDir")
 	}
 	return filepath.Join(gopath, "bin")
 }
 
-func init() {
-	flag.BoolVar(&MustBeVgo, "vgo", MustBeVgo, "require use of modules")
-}
-
-// mustBeVgo reports whether we are invoked as vgo
+// mustUseModules reports whether we are invoked as vgo
 // (as opposed to go).
 // If so, we only support builds with go.mod files.
-func mustBeVgo() bool {
+func mustUseModules() bool {
 	name := os.Args[0]
 	name = name[strings.LastIndex(name, "/")+1:]
 	name = name[strings.LastIndex(name, `\`)+1:]
@@ -70,10 +82,23 @@
 	}
 	initialized = true
 
-	// If this is testgo - the test binary during cmd/go tests - then
-	// do not let it look for a go.mod. Only use vgo support if the
-	// global -vgo flag has been passed on the command line.
-	if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && !MustBeVgo {
+	env := os.Getenv("GO111MODULE")
+	switch env {
+	default:
+		base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
+	case "", "auto":
+		// leave MustUseModules alone
+	case "on":
+		MustUseModules = true
+	case "off":
+		if !MustUseModules {
+			return
+		}
+	}
+
+	// If this is testgo - the test binary during cmd/go tests -
+	// then do not let it look for a go.mod unless GO111MODULE has an explicit setting.
+	if base := filepath.Base(os.Args[0]); (base == "testgo" || base == "testgo.exe") && env == "" {
 		return
 	}
 
@@ -110,27 +135,62 @@
 		base.Fatalf("go: %v", err)
 	}
 
-	root, _ := FindModuleRoot(cwd, "", MustBeVgo)
-	if root == "" {
-		// If invoked as vgo, insist on a mod file.
-		if MustBeVgo {
-			base.Fatalf("cannot determine module root; please create a go.mod file there")
+	if CmdModInit {
+		// Running 'go mod -init': go.mod will be created in current directory.
+		ModRoot = cwd
+	} else {
+		inGOPATH := false
+		for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
+			if gopath == "" {
+				continue
+			}
+			if str.HasFilePathPrefix(cwd, filepath.Join(gopath, "src")) {
+				inGOPATH = true
+				break
+			}
 		}
-		return
+		if inGOPATH {
+			if !MustUseModules {
+				// No automatic enabling in GOPATH.
+				return
+			}
+		}
+		root, _ := FindModuleRoot(cwd, "", MustUseModules)
+		if root == "" {
+			// If invoked as vgo, insist on a mod file.
+			if MustUseModules {
+				base.Fatalf("go: cannot find main module root; see 'go help modules'")
+			}
+			return
+		}
+
+		ModRoot = root
 	}
+
+	if c := cache.Default(); c == nil {
+		// With modules, there are no install locations for packages
+		// other than the build cache.
+		base.Fatalf("go: cannot use modules with build cache disabled")
+	}
+
+	cfg.ModulesEnabled = true
 	enabled = true
-	ModRoot = root
-	search.SetModRoot(root)
+	load.ModBinDir = BinDir
+	load.ModLookup = Lookup
+	load.ModPackageModuleInfo = PackageModuleInfo
+	load.ModImportPaths = ImportPaths
+	load.ModPackageBuildInfo = PackageBuildInfo
+	load.ModInfoProg = ModInfoProg
+	load.ModImportFromFiles = ImportFromFiles
+
+	search.SetModRoot(ModRoot)
 }
 
 func Enabled() bool {
-	return false // COMPLETELY OFF FOR NOW
-	/*
-		if !initialized {
-			panic("vgo: Enabled called before Init")
-		}
-		return enabled
-	*/
+	if !initialized {
+		panic("go: Enabled called before Init")
+	}
+	return enabled
 }
 
 func InitMod() {
@@ -146,20 +206,43 @@
 	if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
 		base.Fatalf("$GOPATH/go.mod exists but should not")
 	}
-	srcV = filepath.Join(list[0], "src/v")
-	codehost.WorkRoot = filepath.Join(srcV, "cache/vcswork")
+
+	srcV := filepath.Join(list[0], "src/v")
+	srcMod := filepath.Join(list[0], "src/mod")
+	infoV, errV := os.Stat(srcV)
+	_, errMod := os.Stat(srcMod)
+	if errV == nil && infoV.IsDir() && errMod != nil && os.IsNotExist(errMod) {
+		os.Rename(srcV, srcMod)
+	}
+
+	modfetch.SrcMod = srcMod
+	modfetch.GoSumFile = filepath.Join(ModRoot, "go.sum")
+	codehost.WorkRoot = filepath.Join(srcMod, "cache/vcs")
+
+	if CmdModInit {
+		// Running go mod -init: do legacy module conversion
+		// (go.mod does not exist yet, and it's not our job to write it).
+		legacyModInit()
+		modFileToBuildList()
+		return
+	}
 
 	gomod := filepath.Join(ModRoot, "go.mod")
 	data, err := ioutil.ReadFile(gomod)
 	if err != nil {
-		legacyModInit()
-		return
+		if os.IsNotExist(err) {
+			legacyModInit()
+			modFileToBuildList()
+			WriteGoMod()
+			return
+		}
+		base.Fatalf("go: %v", err)
 	}
 
 	f, err := modfile.Parse(gomod, data, fixVersion)
 	if err != nil {
 		// Errors returned by modfile.Parse begin with file:line.
-		base.Fatalf("vgo: errors parsing go.mod:\n%s\n", err)
+		base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
 	}
 	modFile = f
 
@@ -167,7 +250,7 @@
 		// Empty mod file. Must add module path.
 		path, err := FindModulePath(ModRoot)
 		if err != nil {
-			base.Fatalf("vgo: %v", err)
+			base.Fatalf("go: %v", err)
 		}
 		f.AddModuleStmt(path)
 	}
@@ -182,11 +265,22 @@
 	for _, x := range f.Exclude {
 		excluded[x.Mod] = true
 	}
-	Target = f.Module.Mod
-	writeGoMod()
+	modFileToBuildList()
+	WriteGoMod()
 }
 
-func allowed(m module.Version) bool {
+// modFileToBuildList initializes buildList from the modFile.
+func modFileToBuildList() {
+	Target = modFile.Module.Mod
+	list := []module.Version{Target}
+	for _, r := range modFile.Require {
+		list = append(list, r.Mod)
+	}
+	buildList = list
+}
+
+// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
+func Allowed(m module.Version) bool {
 	return !excluded[m]
 }
 
@@ -194,14 +288,13 @@
 	if modFile == nil {
 		path, err := FindModulePath(ModRoot)
 		if err != nil {
-			base.Fatalf("vgo: %v", err)
+			base.Fatalf("go: %v", err)
 		}
-		fmt.Fprintf(os.Stderr, "vgo: creating new go.mod: module %s\n", path)
+		fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", path)
 		modFile = new(modfile.File)
 		modFile.AddModuleStmt(path)
 	}
 
-	Target = modFile.Module.Mod
 	for _, name := range altConfigs {
 		cfg := filepath.Join(ModRoot, name)
 		data, err := ioutil.ReadFile(cfg)
@@ -210,14 +303,14 @@
 			if convert == nil {
 				return
 			}
-			fmt.Fprintf(os.Stderr, "vgo: copying requirements from %s\n", cfg)
+			fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(cfg))
 			cfg = filepath.ToSlash(cfg)
-			if err := modfetch.ConvertLegacyConfig(modFile, cfg, data); err != nil {
-				base.Fatalf("vgo: %v", err)
+			if err := modconv.ConvertLegacyConfig(modFile, cfg, data); err != nil {
+				base.Fatalf("go: %v", err)
 			}
 			if len(modFile.Syntax.Stmt) == 1 {
-				// Add comment to prevent vgo from re-converting every time it runs.
-				modFile.AddComment("// vgo: no requirements found in " + name)
+				// Add comment to avoid re-converting every time it runs.
+				modFile.AddComment("// go: no requirements found in " + name)
 			}
 			return
 		}
@@ -285,11 +378,9 @@
 
 // Exported only for testing.
 func FindModulePath(dir string) (string, error) {
-	for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
-		src := filepath.Join(gpdir, "src") + string(filepath.Separator)
-		if strings.HasPrefix(dir, src) {
-			return filepath.ToSlash(dir[len(src):]), nil
-		}
+	if CmdModModule != "" {
+		// Running go mod -init -module=x/y/z; return x/y/z.
+		return CmdModModule, nil
 	}
 
 	// Cast about for import comments,
@@ -317,10 +408,10 @@
 
 	// Look for Godeps.json declaring import path.
 	data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
-	var cfg struct{ ImportPath string }
-	json.Unmarshal(data, &cfg)
-	if cfg.ImportPath != "" {
-		return cfg.ImportPath, nil
+	var cfg1 struct{ ImportPath string }
+	json.Unmarshal(data, &cfg1)
+	if cfg1.ImportPath != "" {
+		return cfg1.ImportPath, nil
 	}
 
 	// Look for vendor.json declaring import path.
@@ -331,6 +422,14 @@
 		return cfg2.RootPath, nil
 	}
 
+	// Look for path in GOPATH.
+	for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
+		src := filepath.Join(gpdir, "src") + string(filepath.Separator)
+		if strings.HasPrefix(dir, src) {
+			return filepath.ToSlash(dir[len(src):]), nil
+		}
+	}
+
 	// Look for .git/config with github origin as last resort.
 	data, _ = ioutil.ReadFile(filepath.Join(dir, ".git/config"))
 	if m := gitOriginRE.FindSubmatch(data); m != nil {
@@ -361,28 +460,43 @@
 	return path
 }
 
-func writeGoMod() {
-	writeModHash()
+// WriteGoMod writes the current build list back to go.mod.
+func WriteGoMod() {
+	modfetch.WriteGoSum()
 
-	if buildList != nil {
-		min, err := mvs.Req(Target, buildList, newReqs())
-		if err != nil {
-			base.Fatalf("vgo: %v", err)
+	if loaded != nil {
+		var direct []string
+		for _, m := range buildList[1:] {
+			if loaded.direct[m.Path] {
+				direct = append(direct, m.Path)
+			}
 		}
-		modFile.SetRequire(min)
+		min, err := mvs.Req(Target, buildList, direct, Reqs())
+		if err != nil {
+			base.Fatalf("go: %v", err)
+		}
+		var list []*modfile.Require
+		for _, m := range min {
+			list = append(list, &modfile.Require{
+				Mod:      m,
+				Indirect: !loaded.direct[m.Path],
+			})
+		}
+		modFile.SetRequire(list)
 	}
 
 	file := filepath.Join(ModRoot, "go.mod")
 	old, _ := ioutil.ReadFile(file)
+	modFile.Cleanup() // clean file after edits
 	new, err := modFile.Format()
 	if err != nil {
-		base.Fatalf("vgo: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 	if bytes.Equal(old, new) {
 		return
 	}
 	if err := ioutil.WriteFile(file, new, 0666); err != nil {
-		base.Fatalf("vgo: %v", err)
+		base.Fatalf("go: %v", err)
 	}
 }
 
@@ -403,7 +517,7 @@
 		return vers, nil
 	}
 
-	info, err := modfetch.Query(path, vers, nil)
+	info, err := Query(path, vers, nil)
 	if err != nil {
 		return "", err
 	}
diff --git a/src/cmd/go/internal/modload/list.go b/src/cmd/go/internal/modload/list.go
new file mode 100644
index 0000000..69a832d
--- /dev/null
+++ b/src/cmd/go/internal/modload/list.go
@@ -0,0 +1,109 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/modinfo"
+	"cmd/go/internal/module"
+	"cmd/go/internal/par"
+	"cmd/go/internal/search"
+)
+
+func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic {
+	mods := listModules(args)
+	if listU || listVersions {
+		var work par.Work
+		for _, m := range mods {
+			work.Add(m)
+			if m.Replace != nil {
+				work.Add(m.Replace)
+			}
+		}
+		work.Do(10, func(item interface{}) {
+			m := item.(*modinfo.ModulePublic)
+			if listU {
+				addUpdate(m)
+			}
+			if listVersions {
+				addVersions(m)
+			}
+		})
+	}
+	return mods
+}
+
+func listModules(args []string) []*modinfo.ModulePublic {
+	LoadBuildList()
+	if len(args) == 0 {
+		return []*modinfo.ModulePublic{moduleInfo(buildList[0], true)}
+	}
+
+	var mods []*modinfo.ModulePublic
+	matchedBuildList := make([]bool, len(buildList))
+	for _, arg := range args {
+		if strings.Contains(arg, `\`) {
+			base.Fatalf("go: module paths never use backslash")
+		}
+		if search.IsRelativePath(arg) {
+			base.Fatalf("go: cannot use relative path %s to specify module", arg)
+		}
+		if i := strings.Index(arg, "@"); i >= 0 {
+			info, err := Query(arg[:i], arg[i+1:], nil)
+			if err != nil {
+				mods = append(mods, &modinfo.ModulePublic{
+					Path:    arg[:i],
+					Version: arg[i+1:],
+					Error: &modinfo.ModuleError{
+						Err: err.Error(),
+					},
+				})
+				continue
+			}
+			mods = append(mods, moduleInfo(module.Version{Path: arg[:i], Version: info.Version}, false))
+			continue
+		}
+
+		// Module path or pattern.
+		var match func(string) bool
+		var literal bool
+		if arg == "all" {
+			match = func(string) bool { return true }
+		} else if strings.Contains(arg, "...") {
+			match = search.MatchPattern(arg)
+		} else {
+			match = func(p string) bool { return arg == p }
+			literal = true
+		}
+		matched := false
+		for i, m := range buildList {
+			if match(m.Path) {
+				matched = true
+				if !matchedBuildList[i] {
+					matchedBuildList[i] = true
+					mods = append(mods, moduleInfo(m, true))
+				}
+			}
+		}
+		if !matched {
+			if literal {
+				mods = append(mods, &modinfo.ModulePublic{
+					Path: arg,
+					Error: &modinfo.ModuleError{
+						Err: fmt.Sprintf("module %q is not a known dependency", arg),
+					},
+				})
+			} else {
+				fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
+			}
+		}
+	}
+
+	return mods
+}
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
new file mode 100644
index 0000000..5333c65
--- /dev/null
+++ b/src/cmd/go/internal/modload/load.go
@@ -0,0 +1,891 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"go/build"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+	"sync"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/cfg"
+	"cmd/go/internal/imports"
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modfile"
+	"cmd/go/internal/module"
+	"cmd/go/internal/mvs"
+	"cmd/go/internal/par"
+	"cmd/go/internal/search"
+	"cmd/go/internal/semver"
+)
+
+// buildList is the list of modules to use for building packages.
+// It is initialized by calling ImportPaths, ImportFromFiles,
+// LoadALL, or LoadBuildList, each of which uses loaded.load.
+//
+// Ideally, exactly ONE of those functions would be called,
+// and exactly once. Most of the time, that's true.
+// During "go get" it may not be. TODO(rsc): Figure out if
+// that restriction can be established, or else document why not.
+//
+var buildList []module.Version
+
+// loaded is the most recently-used package loader.
+// It holds details about individual packages.
+//
+// Note that loaded.buildList is only valid during a load operation;
+// afterward, it is copied back into the global buildList,
+// which should be used instead.
+var loaded *loader
+
+// ImportPaths returns the set of packages matching the args (patterns),
+// adding modules to the build list as needed to satisfy new imports.
+func ImportPaths(args []string) []string {
+	if Init(); !Enabled() {
+		return search.ImportPaths(args)
+	}
+	InitMod()
+
+	cleaned := search.CleanImportPaths(args)
+	loaded = newLoader()
+	var paths []string
+	loaded.load(func() []string {
+		var roots []string
+		paths = nil
+		for _, pkg := range cleaned {
+			switch {
+			case build.IsLocalImport(pkg):
+				list := []string{pkg}
+				if strings.Contains(pkg, "...") {
+					// TODO: Where is the go.mod cutoff?
+					list = warnPattern(pkg, search.AllPackagesInFS(pkg))
+				}
+				for _, pkg := range list {
+					dir := filepath.Join(cwd, pkg)
+					if dir == ModRoot {
+						pkg = Target.Path
+					} else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) {
+						suffix := filepath.ToSlash(dir[len(ModRoot):])
+						if strings.HasPrefix(suffix, "/vendor/") {
+							// TODO getmode vendor check
+							pkg = strings.TrimPrefix(suffix, "/vendor/")
+						} else {
+							pkg = Target.Path + suffix
+						}
+					} else {
+						base.Errorf("go: package %s outside module root", pkg)
+						continue
+					}
+					roots = append(roots, pkg)
+					paths = append(paths, pkg)
+				}
+
+			case pkg == "all":
+				if loaded.testRoots {
+					loaded.testAll = true
+				}
+				// TODO: Don't print warnings multiple times.
+				roots = append(roots, warnPattern("all", matchPackages("...", loaded.tags, []module.Version{Target}))...)
+				paths = append(paths, "all") // will expand after load completes
+
+			case search.IsMetaPackage(pkg): // std, cmd
+				list := search.AllPackages(pkg)
+				roots = append(roots, list...)
+				paths = append(paths, list...)
+
+			case strings.Contains(pkg, "..."):
+				// TODO: Don't we need to reevaluate this one last time once the build list stops changing?
+				list := warnPattern(pkg, matchPackages(pkg, loaded.tags, buildList))
+				roots = append(roots, list...)
+				paths = append(paths, list...)
+
+			default:
+				roots = append(roots, pkg)
+				paths = append(paths, pkg)
+			}
+		}
+		return roots
+	})
+	WriteGoMod()
+
+	// Process paths to produce final paths list.
+	// Remove duplicates and expand "all".
+	have := make(map[string]bool)
+	var final []string
+	for _, path := range paths {
+		if have[path] {
+			continue
+		}
+		have[path] = true
+		if path == "all" {
+			for _, pkg := range loaded.pkgs {
+				if !have[pkg.path] {
+					have[pkg.path] = true
+					final = append(final, pkg.path)
+				}
+			}
+			continue
+		}
+		final = append(final, path)
+	}
+	return final
+}
+
+// warnPattern returns list, the result of matching pattern,
+// but if list is empty then first it prints a warning about
+// the pattern not matching any packages.
+func warnPattern(pattern string, list []string) []string {
+	if len(list) == 0 {
+		fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+	}
+	return list
+}
+
+// ImportFromFiles adds modules to the build list as needed
+// to satisfy the imports in the named Go source files.
+func ImportFromFiles(gofiles []string) {
+	if Init(); !Enabled() {
+		return
+	}
+	InitMod()
+
+	imports, testImports, err := imports.ScanFiles(gofiles, imports.Tags())
+	if err != nil {
+		base.Fatalf("go: %v", err)
+	}
+
+	loaded = newLoader()
+	loaded.load(func() []string {
+		var roots []string
+		roots = append(roots, imports...)
+		roots = append(roots, testImports...)
+		return roots
+	})
+	WriteGoMod()
+}
+
+// LoadBuildList loads and returns the build list from go.mod.
+// The loading of the build list happens automatically in ImportPaths:
+// LoadBuildList need only be called if ImportPaths is not
+// (typically in commands that care about the module but
+// no particular package).
+func LoadBuildList() []module.Version {
+	if Init(); !Enabled() {
+		base.Fatalf("go: LoadBuildList called but modules not enabled")
+	}
+	InitMod()
+	ReloadBuildList()
+	WriteGoMod()
+	return buildList
+}
+
+func ReloadBuildList() []module.Version {
+	loaded = newLoader()
+	loaded.load(func() []string { return nil })
+	return buildList
+}
+
+// LoadALL returns the set of all packages in the current module
+// and their dependencies in any other modules, without filtering
+// due to build tags, except "+build ignore".
+// It adds modules to the build list as needed to satisfy new imports.
+// This set is useful for deciding whether a particular import is needed
+// anywhere in a module.
+func LoadALL() []string {
+	return loadAll(true)
+}
+
+// LoadVendor is like LoadALL but only follows test dependencies
+// for tests in the main module. Tests in dependency modules are
+// ignored completely.
+// This set is useful for identifying the which packages to include in a vendor directory.
+func LoadVendor() []string {
+	return loadAll(false)
+}
+
+func loadAll(testAll bool) []string {
+	if Init(); !Enabled() {
+		panic("go: misuse of LoadALL/LoadVendor")
+	}
+	InitMod()
+
+	loaded = newLoader()
+	loaded.isALL = true
+	loaded.tags = anyTags
+	loaded.testAll = testAll
+	if !testAll {
+		loaded.testRoots = true
+	}
+	all := TargetPackages()
+	loaded.load(func() []string { return all })
+	WriteGoMod()
+
+	var paths []string
+	for _, pkg := range loaded.pkgs {
+		paths = append(paths, pkg.path)
+	}
+	return paths
+}
+
+// anyTags is a special tags map that satisfies nearly all build tag expressions.
+// Only "ignore" and malformed build tag requirements are considered false.
+var anyTags = map[string]bool{"*": true}
+
+// TargetPackages returns the list of packages in the target (top-level) module,
+// under all build tag settings.
+func TargetPackages() []string {
+	return matchPackages("...", anyTags, []module.Version{Target})
+}
+
+// BuildList returns the module build list,
+// typically constructed by a previous call to
+// LoadBuildList or ImportPaths.
+// The caller must not modify the returned list.
+func BuildList() []module.Version {
+	return buildList
+}
+
+// SetBuildList sets the module build list.
+// The caller is responsible for ensuring that the list is valid.
+// SetBuildList does not retain a reference to the original list.
+func SetBuildList(list []module.Version) {
+	buildList = append([]module.Version{}, list...)
+}
+
+// ImportMap returns the actual package import path
+// for an import path found in source code.
+// If the given import path does not appear in the source code
+// for the packages that have been loaded, ImportMap returns the empty string.
+func ImportMap(path string) string {
+	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
+	if !ok {
+		return ""
+	}
+	return pkg.path
+}
+
+// PackageDir returns the directory containing the source code
+// for the package named by the import path.
+func PackageDir(path string) string {
+	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
+	if !ok {
+		return ""
+	}
+	return pkg.dir
+}
+
+// PackageModule returns the module providing the package named by the import path.
+func PackageModule(path string) module.Version {
+	pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
+	if !ok {
+		return module.Version{}
+	}
+	return pkg.mod
+}
+
+// ModuleUsedDirectly reports whether the main module directly imports
+// some package in the module with the given path.
+func ModuleUsedDirectly(path string) bool {
+	return loaded.direct[path]
+}
+
+// Lookup XXX TODO.
+func Lookup(parentPath, path string) (dir, realPath string, err error) {
+	realPath = ImportMap(path)
+	if realPath == "" {
+		if isStandardImportPath(path) {
+			dir := filepath.Join(cfg.GOROOT, "src", path)
+			if _, err := os.Stat(dir); err == nil {
+				return dir, path, nil
+			}
+		}
+		return "", "", fmt.Errorf("no such package in module")
+	}
+	return PackageDir(realPath), realPath, nil
+}
+
+// A loader manages the process of loading information about
+// the required packages for a particular build,
+// checking that the packages are available in the module set,
+// and updating the module set if needed.
+// Loading is an iterative process: try to load all the needed packages,
+// but if imports are missing, try to resolve those imports, and repeat.
+//
+// Although most of the loading state is maintained in the loader struct,
+// one key piece - the build list - is a global, so that it can be modified
+// separate from the loading operation, such as during "go get"
+// upgrades/downgrades or in "go mod" operations.
+// TODO(rsc): It might be nice to make the loader take and return
+// a buildList rather than hard-coding use of the global.
+type loader struct {
+	tags      map[string]bool // tags for scanDir
+	testRoots bool            // include tests for roots
+	isALL     bool            // created with LoadALL
+	testAll   bool            // include tests for all packages
+
+	// reset on each iteration
+	roots    []*loadPkg
+	pkgs     []*loadPkg
+	work     *par.Work  // current work queue
+	pkgCache *par.Cache // map from string to *loadPkg
+
+	// computed at end of iterations
+	direct map[string]bool // imported directly by main module
+}
+
+func newLoader() *loader {
+	ld := new(loader)
+	ld.tags = imports.Tags()
+
+	switch cfg.CmdName {
+	case "test", "vet":
+		ld.testRoots = true
+	}
+	return ld
+}
+
+func (ld *loader) reset() {
+	ld.roots = nil
+	ld.pkgs = nil
+	ld.work = new(par.Work)
+	ld.pkgCache = new(par.Cache)
+}
+
+// A loadPkg records information about a single loaded package.
+type loadPkg struct {
+	path        string         // import path
+	mod         module.Version // module providing package
+	dir         string         // directory containing source code
+	imports     []*loadPkg     // packages imported by this one
+	err         error          // error loading package
+	stack       *loadPkg       // package importing this one in minimal import stack for this pkg
+	test        *loadPkg       // package with test imports, if we need test
+	testOf      *loadPkg
+	testImports []string // test-only imports, saved for use by pkg.test.
+}
+
+var errMissing = errors.New("cannot find package")
+
+// load attempts to load the build graph needed to process a set of root packages.
+// The set of root packages is defined by the addRoots function,
+// which must call add(path) with the import path of each root package.
+func (ld *loader) load(roots func() []string) {
+	var err error
+	buildList, err = mvs.BuildList(Target, Reqs())
+	if err != nil {
+		base.Fatalf("go: %v", err)
+	}
+
+	added := make(map[string]bool)
+	for {
+		ld.reset()
+		if roots != nil {
+			// Note: the returned roots can change on each iteration,
+			// since the expansion of package patterns depends on the
+			// build list we're using.
+			for _, path := range roots() {
+				ld.work.Add(ld.pkg(path, true))
+			}
+		}
+		ld.work.Do(10, ld.doPkg)
+		ld.buildStacks()
+		numAdded := 0
+		haveMod := make(map[module.Version]bool)
+		for _, m := range buildList {
+			haveMod[m] = true
+		}
+		for _, pkg := range ld.pkgs {
+			if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" {
+				if added[pkg.path] {
+					base.Fatalf("go: %s: looping trying to add package", pkg.stackText())
+				}
+				added[pkg.path] = true
+				numAdded++
+				if !haveMod[err.Module] {
+					haveMod[err.Module] = true
+					buildList = append(buildList, err.Module)
+				}
+				continue
+			}
+			if pkg.err != nil {
+				base.Errorf("go: %s: %s", pkg.stackText(), pkg.err)
+			}
+		}
+		base.ExitIfErrors()
+		if numAdded == 0 {
+			break
+		}
+
+		// Recompute buildList with all our additions.
+		buildList, err = mvs.BuildList(Target, Reqs())
+		if err != nil {
+			base.Fatalf("go: %v", err)
+		}
+	}
+	base.ExitIfErrors()
+
+	// Compute directly referenced dependency modules.
+	ld.direct = make(map[string]bool)
+	for _, pkg := range ld.pkgs {
+		if pkg.mod == Target {
+			for _, dep := range pkg.imports {
+				if dep.mod.Path != "" {
+					ld.direct[dep.mod.Path] = true
+				}
+			}
+		}
+	}
+
+	// Mix in direct markings (really, lack of indirect markings)
+	// from go.mod, unless we scanned the whole module
+	// and can therefore be sure we know better than go.mod.
+	if !ld.isALL && modFile != nil {
+		for _, r := range modFile.Require {
+			if !r.Indirect {
+				ld.direct[r.Mod.Path] = true
+			}
+		}
+	}
+
+	// Check for visibility violations.
+	// TODO!
+}
+
+// pkg returns the *loadPkg for path, creating and queuing it if needed.
+// If the package should be tested, its test is created but not queued
+// (the test is queued after processing pkg).
+// If isRoot is true, the pkg is being queued as one of the roots of the work graph.
+func (ld *loader) pkg(path string, isRoot bool) *loadPkg {
+	return ld.pkgCache.Do(path, func() interface{} {
+		pkg := &loadPkg{
+			path: path,
+		}
+		if ld.testRoots && isRoot || ld.testAll {
+			test := &loadPkg{
+				path:   path,
+				testOf: pkg,
+			}
+			pkg.test = test
+		}
+		if isRoot {
+			ld.roots = append(ld.roots, pkg)
+		}
+		ld.work.Add(pkg)
+		return pkg
+	}).(*loadPkg)
+}
+
+// doPkg processes a package on the work queue.
+func (ld *loader) doPkg(item interface{}) {
+	// TODO: what about replacements?
+	pkg := item.(*loadPkg)
+	var imports []string
+	if pkg.testOf != nil {
+		pkg.dir = pkg.testOf.dir
+		pkg.mod = pkg.testOf.mod
+		imports = pkg.testOf.testImports
+	} else {
+		pkg.mod, pkg.dir, pkg.err = ld.doImport(pkg.path)
+		if pkg.dir == "" {
+			return
+		}
+		var testImports []string
+		var err error
+		imports, testImports, err = scanDir(pkg.dir, ld.tags)
+		if err != nil {
+			if strings.HasPrefix(err.Error(), "no Go ") {
+				// Don't print about directories with no Go source files.
+				// Let the eventual real package load do that.
+				return
+			}
+			pkg.err = err
+			return
+		}
+		if pkg.test != nil {
+			pkg.testImports = testImports
+		}
+	}
+
+	for _, path := range imports {
+		pkg.imports = append(pkg.imports, ld.pkg(path, false))
+	}
+
+	// Now that pkg.dir, pkg.mod, pkg.testImports are set, we can queue pkg.test.
+	// TODO: All that's left is creating new imports. Why not just do it now?
+	if pkg.test != nil {
+		ld.work.Add(pkg.test)
+	}
+}
+
+// doImport finds the directory holding source code for the given import path.
+// It returns the module containing the package (if any),
+// the directory containing the package (if any),
+// and any error encountered.
+// Not all packages have modules: the ones in the standard library do not.
+// Not all packages have directories: "unsafe" and "C" do not.
+func (ld *loader) doImport(path string) (mod module.Version, dir string, err error) {
+	if strings.Contains(path, "@") {
+		// Leave for error during load.
+		return module.Version{}, "", nil
+	}
+	if build.IsLocalImport(path) {
+		// Leave for error during load.
+		// (Module mode does not allow local imports.)
+		return module.Version{}, "", nil
+	}
+
+	return Import(path)
+}
+
+// scanDir is like imports.ScanDir but elides known magic imports from the list,
+// so that we do not go looking for packages that don't really exist.
+//
+// The standard magic import is "C", for cgo.
+//
+// The only other known magic imports are appengine and appengine/*.
+// These are so old that they predate "go get" and did not use URL-like paths.
+// Most code today now uses google.golang.org/appengine instead,
+// but not all code has been so updated. When we mostly ignore build tags
+// during "go vendor", we look into "// +build appengine" files and
+// may see these legacy imports. We drop them so that the module
+// search does not look for modules to try to satisfy them.
+func scanDir(dir string, tags map[string]bool) (imports_, testImports []string, err error) {
+	imports_, testImports, err = imports.ScanDir(dir, tags)
+
+	filter := func(x []string) []string {
+		w := 0
+		for _, pkg := range x {
+			if pkg != "C" && pkg != "appengine" && !strings.HasPrefix(pkg, "appengine/") &&
+				pkg != "appengine_internal" && !strings.HasPrefix(pkg, "appengine_internal/") {
+				x[w] = pkg
+				w++
+			}
+		}
+		return x[:w]
+	}
+
+	return filter(imports_), filter(testImports), err
+}
+
+// buildStacks computes minimal import stacks for each package,
+// for use in error messages. When it completes, packages that
+// are part of the original root set have pkg.stack == nil,
+// and other packages have pkg.stack pointing at the next
+// package up the import stack in their minimal chain.
+// As a side effect, buildStacks also constructs ld.pkgs,
+// the list of all packages loaded.
+func (ld *loader) buildStacks() {
+	if len(ld.pkgs) > 0 {
+		panic("buildStacks")
+	}
+	for _, pkg := range ld.roots {
+		pkg.stack = pkg // sentinel to avoid processing in next loop
+		ld.pkgs = append(ld.pkgs, pkg)
+	}
+	for i := 0; i < len(ld.pkgs); i++ { // not range: appending to ld.pkgs in loop
+		pkg := ld.pkgs[i]
+		for _, next := range pkg.imports {
+			if next.stack == nil {
+				next.stack = pkg
+				ld.pkgs = append(ld.pkgs, next)
+			}
+		}
+		if next := pkg.test; next != nil && next.stack == nil {
+			next.stack = pkg
+			ld.pkgs = append(ld.pkgs, next)
+		}
+	}
+	for _, pkg := range ld.roots {
+		pkg.stack = nil
+	}
+}
+
+// stackText builds the import stack text to use when
+// reporting an error in pkg. It has the general form
+//
+//	import root ->
+//		import other ->
+//		import other2 ->
+//		import pkg
+//
+func (pkg *loadPkg) stackText() string {
+	var stack []*loadPkg
+	for p := pkg.stack; p != nil; p = p.stack {
+		stack = append(stack, p)
+	}
+
+	var buf bytes.Buffer
+	for i := len(stack) - 1; i >= 0; i-- {
+		p := stack[i]
+		if p.testOf != nil {
+			fmt.Fprintf(&buf, "test ->\n\t")
+		} else {
+			fmt.Fprintf(&buf, "import %q ->\n\t", p.path)
+		}
+	}
+	fmt.Fprintf(&buf, "import %q", pkg.path)
+	return buf.String()
+}
+
+// Replacement returns the replacement for mod, if any, from go.mod.
+// If there is no replacement for mod, Replacement returns
+// a module.Version with Path == "".
+func Replacement(mod module.Version) module.Version {
+	if modFile == nil {
+		// Happens during testing.
+		return module.Version{}
+	}
+	var found *modfile.Replace
+	for _, r := range modFile.Replace {
+		if r.Old.Path == mod.Path && (r.Old.Version == "" || r.Old.Version == mod.Version) {
+			found = r // keep going
+		}
+	}
+	if found == nil {
+		return module.Version{}
+	}
+	return found.New
+}
+
+// mvsReqs implements mvs.Reqs for module semantic versions,
+// with any exclusions or replacements applied internally.
+type mvsReqs struct {
+	buildList []module.Version
+	cache     par.Cache
+}
+
+// Reqs returns the current module requirement graph.
+// Future calls to SetBuildList do not affect the operation
+// of the returned Reqs.
+func Reqs() mvs.Reqs {
+	r := &mvsReqs{
+		buildList: buildList,
+	}
+	return r
+}
+
+func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
+	type cached struct {
+		list []module.Version
+		err  error
+	}
+
+	c := r.cache.Do(mod, func() interface{} {
+		list, err := r.required(mod)
+		if err != nil {
+			return cached{nil, err}
+		}
+		for i, mv := range list {
+			for excluded[mv] {
+				mv1, err := r.next(mv)
+				if err != nil {
+					return cached{nil, err}
+				}
+				if mv1.Version == "none" {
+					return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)}
+				}
+				mv = mv1
+			}
+			list[i] = mv
+		}
+
+		return cached{list, nil}
+	}).(cached)
+
+	return c.list, c.err
+}
+
+var vendorOnce sync.Once
+
+var (
+	vendorList []module.Version
+	vendorMap  map[string]module.Version
+)
+
+// readVendorList reads the list of vendored modules from vendor/modules.txt.
+func readVendorList() {
+	vendorOnce.Do(func() {
+		vendorList = nil
+		vendorMap = make(map[string]module.Version)
+		data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "vendor/modules.txt"))
+		var m module.Version
+		for _, line := range strings.Split(string(data), "\n") {
+			if strings.HasPrefix(line, "# ") {
+				f := strings.Fields(line)
+				m = module.Version{}
+				if len(f) == 3 && semver.IsValid(f[2]) {
+					m = module.Version{Path: f[1], Version: f[2]}
+					vendorList = append(vendorList, m)
+				}
+			} else if m.Path != "" {
+				f := strings.Fields(line)
+				if len(f) == 1 {
+					vendorMap[f[0]] = m
+				}
+			}
+		}
+	})
+}
+
+func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
+	if mod == Target {
+		var list []module.Version
+		list = append(list, r.buildList[1:]...)
+		return list, nil
+	}
+
+	if cfg.BuildGetmode == "vendor" {
+		// For every module other than the target,
+		// return the full list of modules from modules.txt.
+		readVendorList()
+		return vendorList, nil
+	}
+
+	origPath := mod.Path
+	if repl := Replacement(mod); repl.Path != "" {
+		if repl.Version == "" {
+			// TODO: need to slip the new version into the tags list etc.
+			dir := repl.Path
+			if !filepath.IsAbs(dir) {
+				dir = filepath.Join(ModRoot, dir)
+			}
+			gomod := filepath.Join(dir, "go.mod")
+			data, err := ioutil.ReadFile(gomod)
+			if err != nil {
+				base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err)
+				return nil, ErrRequire
+			}
+			f, err := modfile.Parse(gomod, data, nil)
+			if err != nil {
+				base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err)
+				return nil, ErrRequire
+			}
+			var list []module.Version
+			for _, r := range f.Require {
+				list = append(list, r.Mod)
+			}
+			return list, nil
+		}
+		mod = repl
+	}
+
+	if mod.Version == "none" {
+		return nil, nil
+	}
+
+	if !semver.IsValid(mod.Version) {
+		// Disallow the broader queries supported by fetch.Lookup.
+		base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version)
+	}
+
+	data, err := modfetch.GoMod(mod.Path, mod.Version)
+	if err != nil {
+		base.Errorf("go: %s@%s: %v\n", mod.Path, mod.Version, err)
+		return nil, ErrRequire
+	}
+	f, err := modfile.Parse("go.mod", data, nil)
+	if err != nil {
+		base.Errorf("go: %s@%s: parsing go.mod: %v", mod.Path, mod.Version, err)
+		return nil, ErrRequire
+	}
+
+	if f.Module == nil {
+		base.Errorf("go: %s@%s: parsing go.mod: missing module line", mod.Path, mod.Version)
+		return nil, ErrRequire
+	}
+	if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
+		base.Errorf("go: %s@%s: parsing go.mod: unexpected module path %q", mod.Path, mod.Version, mpath)
+		return nil, ErrRequire
+	}
+
+	var list []module.Version
+	for _, req := range f.Require {
+		list = append(list, req.Mod)
+	}
+	return list, nil
+}
+
+// ErrRequire is the sentinel error returned when Require encounters problems.
+// It prints the problems directly to standard error, so that multiple errors
+// can be displayed easily.
+var ErrRequire = errors.New("error loading module requirements")
+
+func (*mvsReqs) Max(v1, v2 string) string {
+	if v1 != "" && semver.Compare(v1, v2) == -1 {
+		return v2
+	}
+	return v1
+}
+
+// Upgrade is a no-op, here to implement mvs.Reqs.
+// The upgrade logic for go get -u is in ../modget/get.go.
+func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
+	return m, nil
+}
+
+func versions(path string) ([]string, error) {
+	// Note: modfetch.Lookup and repo.Versions are cached,
+	// so there's no need for us to add extra caching here.
+	repo, err := modfetch.Lookup(path)
+	if err != nil {
+		return nil, err
+	}
+	return repo.Versions("")
+}
+
+// Previous returns the tagged version of m.Path immediately prior to
+// m.Version, or version "none" if no prior version is tagged.
+func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
+	list, err := versions(m.Path)
+	if err != nil {
+		return module.Version{}, err
+	}
+	i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
+	if i > 0 {
+		return module.Version{Path: m.Path, Version: list[i-1]}, nil
+	}
+	return module.Version{Path: m.Path, Version: "none"}, nil
+}
+
+// next returns the next version of m.Path after m.Version.
+// It is only used by the exclusion processing in the Required method,
+// not called directly by MVS.
+func (*mvsReqs) next(m module.Version) (module.Version, error) {
+	list, err := versions(m.Path)
+	if err != nil {
+		return module.Version{}, err
+	}
+	i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
+	if i < len(list) {
+		return module.Version{Path: m.Path, Version: list[i]}, nil
+	}
+	return module.Version{Path: m.Path, Version: "none"}, nil
+}
+
+func fetch(mod module.Version) (dir string, isLocal bool, err error) {
+	if mod == Target {
+		return ModRoot, true, nil
+	}
+	if r := Replacement(mod); r.Path != "" {
+		if r.Version == "" {
+			dir = r.Path
+			if !filepath.IsAbs(dir) {
+				dir = filepath.Join(ModRoot, dir)
+			}
+			return dir, true, nil
+		}
+		mod = r
+	}
+
+	dir, err = modfetch.Download(mod)
+	return dir, false, err
+}
diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go
new file mode 100644
index 0000000..2973f81
--- /dev/null
+++ b/src/cmd/go/internal/modload/query.go
@@ -0,0 +1,187 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/module"
+	"cmd/go/internal/semver"
+	"fmt"
+	"strings"
+)
+
+// Query looks up a revision of a given module given a version query string.
+// The module must be a complete module path.
+// The version must take one of the following forms:
+//
+//	- the literal string "latest", denoting the latest available, allowed tagged version,
+//	  with non-prereleases preferred over prereleases.
+//	  If there are no tagged versions in the repo, latest returns the most recent commit.
+//	- v1, denoting the latest available tagged version v1.x.x.
+//	- v1.2, denoting the latest available tagged version v1.2.x.
+//	- v1.2.3, a semantic version string denoting that tagged version.
+//	- <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3,
+//	   denoting the version closest to the target and satisfying the given operator,
+//	   with non-prereleases preferred over prereleases.
+//	- a repository commit identifier, denoting that commit.
+//
+// If the allowed function is non-nil, Query excludes any versions for which allowed returns false.
+//
+func Query(path, query string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) {
+	if allowed == nil {
+		allowed = func(module.Version) bool { return true }
+	}
+
+	// Parse query to detect parse errors (and possibly handle query)
+	// before any network I/O.
+	badVersion := func(v string) (*modfetch.RevInfo, error) {
+		return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
+	}
+	var ok func(module.Version) bool
+	var preferOlder bool
+	switch {
+	case query == "latest":
+		ok = allowed
+
+	case strings.HasPrefix(query, "<="):
+		v := query[len("<="):]
+		if !semver.IsValid(v) {
+			return badVersion(v)
+		}
+		if isSemverPrefix(v) {
+			// Refuse to say whether <=v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3).
+			return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
+		}
+		ok = func(m module.Version) bool {
+			return semver.Compare(m.Version, v) <= 0 && allowed(m)
+		}
+
+	case strings.HasPrefix(query, "<"):
+		v := query[len("<"):]
+		if !semver.IsValid(v) {
+			return badVersion(v)
+		}
+		ok = func(m module.Version) bool {
+			return semver.Compare(m.Version, v) < 0 && allowed(m)
+		}
+
+	case strings.HasPrefix(query, ">="):
+		v := query[len(">="):]
+		if !semver.IsValid(v) {
+			return badVersion(v)
+		}
+		ok = func(m module.Version) bool {
+			return semver.Compare(m.Version, v) >= 0 && allowed(m)
+		}
+		preferOlder = true
+
+	case strings.HasPrefix(query, ">"):
+		v := query[len(">"):]
+		if !semver.IsValid(v) {
+			return badVersion(v)
+		}
+		if isSemverPrefix(v) {
+			// Refuse to say whether >v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3).
+			return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
+		}
+		ok = func(m module.Version) bool {
+			return semver.Compare(m.Version, v) > 0 && allowed(m)
+		}
+		preferOlder = true
+
+	case semver.IsValid(query) && isSemverPrefix(query):
+		ok = func(m module.Version) bool {
+			return matchSemverPrefix(query, m.Version) && allowed(m)
+		}
+
+	case semver.IsValid(query):
+		vers := semver.Canonical(query)
+		if !allowed(module.Version{Path: path, Version: vers}) {
+			return nil, fmt.Errorf("%s@%s excluded", path, vers)
+		}
+		return modfetch.Stat(path, vers)
+
+	default:
+		// Direct lookup of semantic version or commit identifier.
+		info, err := modfetch.Stat(path, query)
+		if err != nil {
+			return nil, err
+		}
+		if !allowed(module.Version{Path: path, Version: info.Version}) {
+			return nil, fmt.Errorf("%s@%s excluded", path, info.Version)
+		}
+		return info, nil
+	}
+
+	// Load versions and execute query.
+	repo, err := modfetch.Lookup(path)
+	if err != nil {
+		return nil, err
+	}
+	versions, err := repo.Versions("")
+	if err != nil {
+		return nil, err
+	}
+
+	if preferOlder {
+		for _, v := range versions {
+			if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) {
+				return repo.Stat(v)
+			}
+		}
+		for _, v := range versions {
+			if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) {
+				return repo.Stat(v)
+			}
+		}
+	} else {
+		for i := len(versions) - 1; i >= 0; i-- {
+			v := versions[i]
+			if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) {
+				return repo.Stat(v)
+			}
+		}
+		for i := len(versions) - 1; i >= 0; i-- {
+			v := versions[i]
+			if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) {
+				return repo.Stat(v)
+			}
+		}
+	}
+
+	if query == "latest" {
+		// Special case for "latest": if no tags match, use latest commit in repo,
+		// provided it is not excluded.
+		if info, err := repo.Latest(); err == nil && allowed(module.Version{Path: path, Version: info.Version}) {
+			return info, nil
+		}
+	}
+
+	return nil, fmt.Errorf("no matching versions for query %q", query)
+}
+
+// isSemverPrefix reports whether v is a semantic version prefix: v1 or  v1.2 (not v1.2.3).
+// The caller is assumed to have checked that semver.IsValid(v) is true.
+func isSemverPrefix(v string) bool {
+	dots := 0
+	for i := 0; i < len(v); i++ {
+		switch v[i] {
+		case '-', '+':
+			return false
+		case '.':
+			dots++
+			if dots >= 2 {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// matchSemverPrefix reports whether the shortened semantic version p
+// matches the full-width (non-shortened) semantic version v.
+func matchSemverPrefix(p, v string) bool {
+	return len(v) > len(p) && v[len(p)] == '.' && v[:len(p)] == p
+}
diff --git a/src/cmd/go/internal/modload/query_test.go b/src/cmd/go/internal/modload/query_test.go
new file mode 100644
index 0000000..eb194b5
--- /dev/null
+++ b/src/cmd/go/internal/modload/query_test.go
@@ -0,0 +1,151 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"internal/testenv"
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modfetch/codehost"
+	"cmd/go/internal/module"
+)
+
+func TestMain(m *testing.M) {
+	os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+	dir, err := ioutil.TempDir("", "modload-test-")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+	modfetch.SrcMod = filepath.Join(dir, "src/mod")
+	codehost.WorkRoot = filepath.Join(dir, "codework")
+	return m.Run()
+}
+
+var (
+	queryRepo   = "vcs-test.golang.org/git/querytest.git"
+	queryRepoV2 = queryRepo + "/v2"
+	queryRepoV3 = queryRepo + "/v3"
+
+	// Empty version list (no semver tags), not actually empty.
+	emptyRepo = "vcs-test.golang.org/git/emptytest.git"
+)
+
+var queryTests = []struct {
+	path  string
+	query string
+	allow string
+	vers  string
+	err   string
+}{
+	/*
+		git init
+		echo module vcs-test.golang.org/git/querytest.git >go.mod
+		git add go.mod
+		git commit -m v1 go.mod
+		git tag start
+		for i in v0.0.0-pre1 v0.0.0 v0.0.1 v0.0.2 v0.0.3 v0.1.0 v0.1.1 v0.1.2 v0.3.0 v1.0.0 v1.1.0 v1.9.0 v1.9.9 v1.9.10-pre1; do
+			echo before $i >status
+			git add status
+			git commit -m "before $i" status
+			echo at $i >status
+			git commit -m "at $i" status
+			git tag $i
+		done
+		git tag favorite v0.0.3
+
+		git branch v2 start
+		git checkout v2
+		echo module vcs-test.golang.org/git/querytest.git/v2 >go.mod
+		git commit -m v2 go.mod
+		for i in v2.0.0 v2.1.0 v2.2.0 v2.5.5; do
+			echo before $i >status
+			git add status
+			git commit -m "before $i" status
+			echo at $i >status
+			git commit -m "at $i" status
+			git tag $i
+		done
+		echo after v2.5.5 >status
+		git commit -m 'after v2.5.5' status
+		git checkout master
+		zip -r ../querytest.zip
+		gsutil cp ../querytest.zip gs://vcs-test/git/querytest.zip
+		curl 'https://vcs-test.golang.org/git/querytest?go-get=1'
+	*/
+	{path: queryRepo, query: "<v0.0.0", vers: "v0.0.0-pre1"},
+	{path: queryRepo, query: "<v0.0.0-pre1", err: `no matching versions for query "<v0.0.0-pre1"`},
+	{path: queryRepo, query: "<=v0.0.0", vers: "v0.0.0"},
+	{path: queryRepo, query: ">v0.0.0", vers: "v0.0.1"},
+	{path: queryRepo, query: ">=v0.0.0", vers: "v0.0.0"},
+	{path: queryRepo, query: "v0.0.1", vers: "v0.0.1"},
+	{path: queryRepo, query: "v0.0.1+foo", vers: "v0.0.1"},
+	{path: queryRepo, query: "v0.0.99", err: `unknown revision v0.0.99`},
+	{path: queryRepo, query: "v0", vers: "v0.3.0"},
+	{path: queryRepo, query: "v0.1", vers: "v0.1.2"},
+	{path: queryRepo, query: "v0.2", err: `no matching versions for query "v0.2"`},
+	{path: queryRepo, query: "v0.0", vers: "v0.0.3"},
+	{path: queryRepo, query: "latest", vers: "v1.9.9"},
+	{path: queryRepo, query: "latest", allow: "NOMATCH", err: `no matching versions for query "latest"`},
+	{path: queryRepo, query: ">v1.9.9", vers: "v1.9.10-pre1"},
+	{path: queryRepo, query: ">v1.10.0", err: `no matching versions for query ">v1.10.0"`},
+	{path: queryRepo, query: ">=v1.10.0", err: `no matching versions for query ">=v1.10.0"`},
+	{path: queryRepo, query: "6cf84eb", vers: "v0.0.0-20180704023347-6cf84ebaea54"},
+	{path: queryRepo, query: "start", vers: "v0.0.0-20180704023101-5e9e31667ddf"},
+	{path: queryRepo, query: "7a1b6bf", vers: "v0.1.0"},
+
+	{path: queryRepoV2, query: "<v0.0.0", err: `no matching versions for query "<v0.0.0"`},
+	{path: queryRepoV2, query: "<=v0.0.0", err: `no matching versions for query "<=v0.0.0"`},
+	{path: queryRepoV2, query: ">v0.0.0", vers: "v2.0.0"},
+	{path: queryRepoV2, query: ">=v0.0.0", vers: "v2.0.0"},
+	{path: queryRepoV2, query: "v0.0.1+foo", vers: "v2.0.0-20180704023347-179bc86b1be3"},
+	{path: queryRepoV2, query: "latest", vers: "v2.5.5"},
+
+	{path: queryRepoV3, query: "latest", vers: "v3.0.0-20180704024501-e0cf3de987e6"},
+
+	{path: emptyRepo, query: "latest", vers: "v0.0.0-20180704023549-7bb914627242"},
+	{path: emptyRepo, query: ">v0.0.0", err: `no matching versions for query ">v0.0.0"`},
+	{path: emptyRepo, query: "<v10.0.0", err: `no matching versions for query "<v10.0.0"`},
+}
+
+func TestQuery(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+
+	for _, tt := range queryTests {
+		allow := tt.allow
+		if allow == "" {
+			allow = "*"
+		}
+		allowed := func(m module.Version) bool {
+			ok, _ := path.Match(allow, m.Version)
+			return ok
+		}
+		t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.query+"/"+allow, func(t *testing.T) {
+			info, err := Query(tt.path, tt.query, allowed)
+			if tt.err != "" {
+				if err != nil && err.Error() == tt.err {
+					return
+				}
+				t.Fatalf("Query(%q, %q, %v): %v, want error %q", tt.path, tt.query, allow, err, tt.err)
+			}
+			if err != nil {
+				t.Fatalf("Query(%q, %q, %v): %v", tt.path, tt.query, allow, err)
+			}
+			if info.Version != tt.vers {
+				t.Errorf("Query(%q, %q, %v) = %v, want %v", tt.path, tt.query, allow, info.Version, tt.vers)
+			}
+		})
+	}
+}
diff --git a/src/cmd/go/internal/modload/search.go b/src/cmd/go/internal/modload/search.go
new file mode 100644
index 0000000..9ce65f0
--- /dev/null
+++ b/src/cmd/go/internal/modload/search.go
@@ -0,0 +1,106 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"cmd/go/internal/base"
+	"cmd/go/internal/cfg"
+	"cmd/go/internal/imports"
+	"cmd/go/internal/module"
+	"cmd/go/internal/search"
+)
+
+// matchPackages returns a list of packages in the list of modules
+// matching the pattern. Package loading assumes the given set of tags.
+func matchPackages(pattern string, tags map[string]bool, modules []module.Version) []string {
+	match := func(string) bool { return true }
+	treeCanMatch := func(string) bool { return true }
+	if !search.IsMetaPackage(pattern) {
+		match = search.MatchPattern(pattern)
+		treeCanMatch = search.TreeCanMatchPattern(pattern)
+	}
+
+	have := map[string]bool{
+		"builtin": true, // ignore pseudo-package that exists only for documentation
+	}
+	if !cfg.BuildContext.CgoEnabled {
+		have["runtime/cgo"] = true // ignore during walk
+	}
+	var pkgs []string
+
+	for _, mod := range modules {
+		if !treeCanMatch(mod.Path) {
+			continue
+		}
+		var root string
+		if mod.Version == "" {
+			root = ModRoot
+		} else {
+			var err error
+			root, _, err = fetch(mod)
+			if err != nil {
+				base.Errorf("go: %v", err)
+				continue
+			}
+		}
+		root = filepath.Clean(root)
+
+		filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+			if err != nil {
+				return nil
+			}
+
+			want := true
+			// Avoid .foo, _foo, and testdata directory trees.
+			_, elem := filepath.Split(path)
+			if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+				want = false
+			}
+
+			name := mod.Path + filepath.ToSlash(path[len(root):])
+			if !treeCanMatch(name) {
+				want = false
+			}
+
+			if !fi.IsDir() {
+				if fi.Mode()&os.ModeSymlink != 0 && want {
+					if target, err := os.Stat(path); err == nil && target.IsDir() {
+						fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+					}
+				}
+				return nil
+			}
+
+			if !want {
+				return filepath.SkipDir
+			}
+			if path != root {
+				if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil {
+					return filepath.SkipDir
+				}
+			}
+
+			if !have[name] {
+				have[name] = true
+				if match(name) {
+					if _, _, err := scanDir(path, tags); err != imports.ErrNoGo {
+						pkgs = append(pkgs, name)
+					}
+				}
+			}
+
+			if elem == "vendor" {
+				return filepath.SkipDir
+			}
+			return nil
+		})
+	}
+	return pkgs
+}
diff --git a/src/cmd/go/internal/module/module.go b/src/cmd/go/internal/module/module.go
index 1e8f74c..7b32b24 100644
--- a/src/cmd/go/internal/module/module.go
+++ b/src/cmd/go/internal/module/module.go
@@ -8,6 +8,7 @@
 
 import (
 	"fmt"
+	"sort"
 	"strings"
 	"unicode"
 	"unicode/utf8"
@@ -17,8 +18,15 @@
 
 // A Version is defined by a module path and version pair.
 type Version struct {
-	Path    string
-	Version string
+	Path string
+
+	// Version is usually a semantic version in canonical form.
+	// There are two exceptions to this general rule.
+	// First, the top-level target of a build has no specific version
+	// and uses Version = "".
+	// Second, during MVS calculations the version "none" is used
+	// to represent the decision to take no version of a given module.
+	Version string `json:",omitempty"`
 }
 
 // Check checks that a given module path, version pair is valid.
@@ -193,3 +201,30 @@
 	}
 	return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:]
 }
+
+// Sort sorts the list by Path, breaking ties by comparing Versions.
+func Sort(list []Version) {
+	sort.Slice(list, func(i, j int) bool {
+		mi := list[i]
+		mj := list[j]
+		if mi.Path != mj.Path {
+			return mi.Path < mj.Path
+		}
+		// To help go.sum formatting, allow version/file.
+		// Compare semver prefix by semver rules,
+		// file by string order.
+		vi := mi.Version
+		vj := mj.Version
+		var fi, fj string
+		if k := strings.Index(vi, "/"); k >= 0 {
+			vi, fi = vi[:k], vi[k:]
+		}
+		if k := strings.Index(vj, "/"); k >= 0 {
+			vj, fj = vj[:k], vj[k:]
+		}
+		if vi != vj {
+			return semver.Compare(vi, vj) < 0
+		}
+		return fi < fj
+	})
+}
diff --git a/src/cmd/go/internal/mvs/mvs.go b/src/cmd/go/internal/mvs/mvs.go
index 47670ff..8ec9162 100644
--- a/src/cmd/go/internal/mvs/mvs.go
+++ b/src/cmd/go/internal/mvs/mvs.go
@@ -9,14 +9,53 @@
 import (
 	"fmt"
 	"sort"
+	"sync"
 
+	"cmd/go/internal/base"
 	"cmd/go/internal/module"
+	"cmd/go/internal/par"
 )
 
+// A Reqs is the requirement graph on which Minimal Version Selection (MVS) operates.
+//
+// The version strings are opaque except for the special version "none"
+// (see the documentation for module.Version). In particular, MVS does not
+// assume that the version strings are semantic versions; instead, the Max method
+// gives access to the comparison operation.
+//
+// It must be safe to call methods on a Reqs from multiple goroutines simultaneously.
+// Because a Reqs may read the underlying graph from the network on demand,
+// the MVS algorithms parallelize the traversal to overlap network delays.
 type Reqs interface {
+	// Required returns the module versions explicitly required by m itself.
+	// The caller must not modify the returned list.
 	Required(m module.Version) ([]module.Version, error)
+
+	// Max returns the maximum of v1 and v2 (it returns either v1 or v2).
+	//
+	// For all versions v, Max(v, "none") must be v,
+	// and for the tanget passed as the first argument to MVS functions,
+	// Max(target, v) must be target.
+	//
+	// Note that v1 < v2 can be written Max(v1, v2) != v1
+	// and similarly v1 <= v2 can be written Max(v1, v2) == v2.
 	Max(v1, v2 string) string
-	Latest(path string) (module.Version, error)
+
+	// Upgrade returns the upgraded version of m,
+	// for use during an UpgradeAll operation.
+	// If m should be kept as is, Upgrade returns m.
+	// If m is not yet used in the build, then m.Version will be "none".
+	// More typically, m.Version will be the version required
+	// by some other module in the build.
+	//
+	// If no module version is available for the given path,
+	// Upgrade returns a non-nil error.
+	// TODO(rsc): Upgrade must be able to return errors,
+	// but should "no latest version" just return m instead?
+	Upgrade(m module.Version) (module.Version, error)
+
+	// Previous returns the version of m.Path immediately prior to m.Version,
+	// or "none" if no such version is known.
 	Previous(m module.Version) (module.Version, error)
 }
 
@@ -30,61 +69,81 @@
 
 // BuildList returns the build list for the target module.
 func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) {
-	return buildList(target, reqs, nil, nil)
+	return buildList(target, reqs, nil)
 }
 
-func buildList(target module.Version, reqs Reqs, uses map[module.Version][]module.Version, vers map[string]string) ([]module.Version, error) {
+func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) module.Version) ([]module.Version, error) {
+	// Explore work graph in parallel in case reqs.Required
+	// does high-latency network operations.
+	var work par.Work
+	work.Add(target)
 	var (
-		min  = map[string]string{target.Path: target.Version}
-		todo = []module.Version{target}
-		seen = map[module.Version]bool{target: true}
+		mu       sync.Mutex
+		min      = map[string]string{target.Path: target.Version}
+		firstErr error
 	)
-	for len(todo) > 0 {
-		m := todo[len(todo)-1]
-		todo = todo[:len(todo)-1]
-		required, _ := reqs.Required(m)
-		for _, r := range required {
-			if uses != nil {
-				uses[r] = append(uses[r], m)
-			}
-			if !seen[r] {
-				if v, ok := min[r.Path]; !ok {
-					min[r.Path] = r.Version
-				} else if max := reqs.Max(v, r.Version); max != v {
-					min[r.Path] = max
-				}
-				todo = append(todo, r)
-				seen[r] = true
-			}
+	work.Do(10, func(item interface{}) {
+		m := item.(module.Version)
+		required, err := reqs.Required(m)
+
+		mu.Lock()
+		if err != nil && firstErr == nil {
+			firstErr = err
 		}
+		if firstErr != nil {
+			mu.Unlock()
+			return
+		}
+		if v, ok := min[m.Path]; !ok || reqs.Max(v, m.Version) != v {
+			min[m.Path] = m.Version
+		}
+		mu.Unlock()
+
+		for _, r := range required {
+			if r.Path == "" {
+				base.Errorf("Required(%v) returned zero module in list", m)
+				continue
+			}
+			work.Add(r)
+		}
+
+		if upgrade != nil {
+			u := upgrade(m)
+			if u.Path == "" {
+				base.Errorf("Upgrade(%v) returned zero module", m)
+				return
+			}
+			work.Add(u)
+		}
+	})
+
+	if firstErr != nil {
+		return nil, firstErr
+	}
+	if v := min[target.Path]; v != target.Version {
+		panic(fmt.Sprintf("mistake: chose version %q instead of target %+v", v, target)) // TODO: Don't panic.
 	}
 
-	if min[target.Path] != target.Version {
-		panic("unbuildable") // TODO
-	}
-
-	if vers == nil {
-		vers = make(map[string]string)
-	}
 	list := []module.Version{target}
+	listed := map[string]bool{target.Path: true}
 	for i := 0; i < len(list); i++ {
 		m := list[i]
 		required, err := reqs.Required(m)
 		if err != nil {
-			// TODO: Check error is decent.
 			return nil, err
 		}
 		for _, r := range required {
 			v := min[r.Path]
-			if reqs.Max(v, r.Version) != v {
-				panic("mistake") // TODO
+			if r.Path != target.Path && reqs.Max(v, r.Version) != v {
+				panic(fmt.Sprintf("mistake: version %q does not satisfy requirement %+v", v, r)) // TODO: Don't panic.
 			}
-			if _, ok := vers[r.Path]; !ok {
-				vers[r.Path] = v
+			if !listed[r.Path] {
 				list = append(list, module.Version{Path: r.Path, Version: v})
+				listed[r.Path] = true
 			}
 		}
 	}
+
 	tail := list[1:]
 	sort.Slice(tail, func(i, j int) bool {
 		return tail[i].Path < tail[j].Path
@@ -93,8 +152,13 @@
 }
 
 // Req returns the minimal requirement list for the target module
-// that result in the given build list.
-func Req(target module.Version, list []module.Version, reqs Reqs) ([]module.Version, error) {
+// that results in the given build list, with the constraint that all
+// module paths listed in base must appear in the returned list.
+func Req(target module.Version, list []module.Version, base []string, reqs Reqs) ([]module.Version, error) {
+	// Note: Not running in parallel because we assume
+	// that list came from a previous operation that paged
+	// in all the requirements, so there's no I/O to overlap now.
+
 	// Compute postorder, cache requirements.
 	var postorder []module.Version
 	reqCache := map[module.Version][]module.Version{}
@@ -138,13 +202,20 @@
 	}
 	max := map[string]string{}
 	for _, m := range list {
-		if max[m.Path] == "" {
-			max[m.Path] = m.Version
+		if v, ok := max[m.Path]; ok {
+			max[m.Path] = reqs.Max(m.Version, v)
 		} else {
-			max[m.Path] = reqs.Max(m.Version, max[m.Path])
+			max[m.Path] = m.Version
 		}
 	}
+	// First walk the base modules that must be listed.
 	var min []module.Version
+	for _, path := range base {
+		m := module.Version{Path: path, Version: max[path]}
+		min = append(min, m)
+		walk(m)
+	}
+	// Now the reverse postorder to bring in anything else.
 	for i := len(postorder) - 1; i >= 0; i-- {
 		m := postorder[i]
 		if max[m.Path] != m.Version {
@@ -165,33 +236,18 @@
 // UpgradeAll returns a build list for the target module
 // in which every module is upgraded to its latest version.
 func UpgradeAll(target module.Version, reqs Reqs) ([]module.Version, error) {
-	have := map[string]bool{target.Path: true}
-	list := []module.Version{target}
-	for i := 0; i < len(list); i++ {
-		m := list[i]
-		required, err := reqs.Required(m)
+	return buildList(target, reqs, func(m module.Version) module.Version {
+		if m.Path == target.Path {
+			return target
+		}
+
+		latest, err := reqs.Upgrade(m)
 		if err != nil {
 			panic(err) // TODO
 		}
-		for _, r := range required {
-			latest, err := reqs.Latest(r.Path)
-			if err != nil {
-				panic(err) // TODO
-			}
-			if reqs.Max(latest.Version, r.Version) != latest.Version {
-				panic("mistake") // TODO
-			}
-			if !have[r.Path] {
-				have[r.Path] = true
-				list = append(list, module.Version{Path: r.Path, Version: latest.Version})
-			}
-		}
-	}
-	tail := list[1:]
-	sort.Slice(tail, func(i, j int) bool {
-		return tail[i].Path < tail[j].Path
+		m.Version = latest.Version
+		return m
 	})
-	return list, nil
 }
 
 // Upgrade returns a build list for the target module
@@ -211,6 +267,10 @@
 
 // Downgrade returns a build list for the target module
 // in which the given additional modules are downgraded.
+//
+// The versions to be downgraded may be unreachable from reqs.Latest and
+// reqs.Previous, but the methods of reqs must otherwise handle such versions
+// correctly.
 func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([]module.Version, error) {
 	list, err := reqs.Required(target)
 	if err != nil {
diff --git a/src/cmd/go/internal/mvs/mvs_test.go b/src/cmd/go/internal/mvs/mvs_test.go
index 0fd55e4..2a27dfb 100644
--- a/src/cmd/go/internal/mvs/mvs_test.go
+++ b/src/cmd/go/internal/mvs/mvs_test.go
@@ -156,6 +156,47 @@
 E1: D2
 build A: A B C D2 E2
 
+# Upgrade from B1 to B2 should drop the transitive dep on D.
+name: drop
+A: B1 C1
+B1: D1
+B2:
+C2:
+D2:
+build A: A B1 C1 D1
+upgrade* A: A B2 C2
+
+name: simplify
+A: B1 C1
+B1: C2
+C1: D1
+C2:
+build A: A B1 C2
+
+name: up1
+A: B1 C1
+B1:
+B2:
+B3:
+B4:
+B5.hidden:
+C2:
+C3:
+build A: A B1 C1
+upgrade* A: A B4 C3
+
+name: up2
+A: B5.hidden C1
+B1:
+B2:
+B3:
+B4:
+B5.hidden:
+C2:
+C3:
+build A: A B5.hidden C1
+upgrade* A: A B5.hidden C3
+
 name: down1
 A: B2
 B1: C1
@@ -199,6 +240,48 @@
 B2.hidden: 
 C2: 
 downgrade A B2.hidden: A B2.hidden C2
+
+# Cycles involving the target.
+
+# The target must be the newest version of itself.
+name: cycle1
+A: B1
+B1: A1
+B2: A2
+B3: A3
+build A: A B1
+upgrade A B2: A B2
+upgrade* A: A B3
+
+# Requirements of older versions of the target
+# must not be carried over.
+name: cycle2
+A: B1
+A1: C1
+A2: D1
+B1: A1
+B2: A2
+C1: A2
+C2:
+D2:
+build A: A B1
+upgrade* A: A B2
+
+# Requirement minimization.
+
+name: req1
+A: B1 C1 D1 E1 F1
+B1: C1 E1 F1
+req A: B1 D1
+req A C: B1 C1 D1
+
+name: req2
+A: G1 H1
+G1: H1
+H1: G1
+req A: G1
+req A G: G1
+req A H: H1
 `
 
 func Test(t *testing.T) {
@@ -281,19 +364,19 @@
 			continue
 		case "upgradereq":
 			if len(kf) < 2 {
-				t.Fatalf("upgrade takes at least one arguments: %q", line)
+				t.Fatalf("upgrade takes at least one argument: %q", line)
 			}
 			fns = append(fns, func(t *testing.T) {
 				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
 				if err == nil {
-					list, err = Req(m(kf[1]), list, reqs)
+					list, err = Req(m(kf[1]), list, nil, reqs)
 				}
 				checkList(t, key, list, err, val)
 			})
 			continue
 		case "upgrade":
 			if len(kf) < 2 {
-				t.Fatalf("upgrade takes at least one arguments: %q", line)
+				t.Fatalf("upgrade takes at least one argument: %q", line)
 			}
 			fns = append(fns, func(t *testing.T) {
 				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
@@ -302,13 +385,26 @@
 			continue
 		case "downgrade":
 			if len(kf) < 2 {
-				t.Fatalf("downgrade takes at least one arguments: %q", line)
+				t.Fatalf("downgrade takes at least one argument: %q", line)
 			}
 			fns = append(fns, func(t *testing.T) {
 				list, err := Downgrade(m(kf[1]), reqs, ms(kf[1:])...)
 				checkList(t, key, list, err, val)
 			})
 			continue
+		case "req":
+			if len(kf) < 2 {
+				t.Fatalf("req takes at least one argument: %q", line)
+			}
+			fns = append(fns, func(t *testing.T) {
+				list, err := BuildList(m(kf[1]), reqs)
+				if err != nil {
+					t.Fatal(err)
+				}
+				list, err = Req(m(kf[1]), list, kf[2:], reqs)
+				checkList(t, key, list, err, val)
+			})
+			continue
 		}
 		if len(kf) == 1 && 'A' <= key[0] && key[0] <= 'Z' {
 			var rs []module.Version
@@ -330,10 +426,10 @@
 type reqsMap map[module.Version][]module.Version
 
 func (r reqsMap) Max(v1, v2 string) string {
-	if v1 == "none" {
+	if v1 == "none" || v2 == "" {
 		return v2
 	}
-	if v2 == "none" {
+	if v2 == "none" || v1 == "" {
 		return v1
 	}
 	if v1 < v2 {
@@ -342,17 +438,17 @@
 	return v1
 }
 
-func (r reqsMap) Latest(path string) (module.Version, error) {
-	var m module.Version
+func (r reqsMap) Upgrade(m module.Version) (module.Version, error) {
+	var u module.Version
 	for k := range r {
-		if k.Path == path && m.Version < k.Version {
-			m = k
+		if k.Path == m.Path && u.Version < k.Version && !strings.HasSuffix(k.Version, ".hidden") {
+			u = k
 		}
 	}
-	if m.Path == "" {
-		return module.Version{}, &MissingModuleError{module.Version{Path: path, Version: ""}}
+	if u.Path == "" {
+		return module.Version{}, &MissingModuleError{module.Version{Path: m.Path, Version: ""}}
 	}
-	return m, nil
+	return u, nil
 }
 
 func (r reqsMap) Previous(m module.Version) (module.Version, error) {
diff --git a/src/cmd/go/internal/par/work.go b/src/cmd/go/internal/par/work.go
new file mode 100644
index 0000000..6543f1a
--- /dev/null
+++ b/src/cmd/go/internal/par/work.go
@@ -0,0 +1,150 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package par implements parallel execution helpers.
+package par
+
+import (
+	"math/rand"
+	"sync"
+	"sync/atomic"
+)
+
+// Work manages a set of work items to be executed in parallel, at most once each.
+// The items in the set must all be valid map keys.
+type Work struct {
+	f       func(interface{}) // function to run for each item
+	running int               // total number of runners
+
+	mu      sync.Mutex
+	added   map[interface{}]bool // items added to set
+	todo    []interface{}        // items yet to be run
+	wait    sync.Cond            // wait when todo is empty
+	waiting int                  // number of runners waiting for todo
+}
+
+func (w *Work) init() {
+	if w.added == nil {
+		w.added = make(map[interface{}]bool)
+	}
+}
+
+// Add adds item to the work set, if it hasn't already been added.
+func (w *Work) Add(item interface{}) {
+	w.mu.Lock()
+	w.init()
+	if !w.added[item] {
+		w.added[item] = true
+		w.todo = append(w.todo, item)
+		if w.waiting > 0 {
+			w.wait.Signal()
+		}
+	}
+	w.mu.Unlock()
+}
+
+// Do runs f in parallel on items from the work set,
+// with at most n invocations of f running at a time.
+// It returns when everything added to the work set has been processed.
+// At least one item should have been added to the work set
+// before calling Do (or else Do returns immediately),
+// but it is allowed for f(item) to add new items to the set.
+// Do should only be used once on a given Work.
+func (w *Work) Do(n int, f func(item interface{})) {
+	if n < 1 {
+		panic("par.Work.Do: n < 1")
+	}
+	n = 1
+	if w.running >= 1 {
+		panic("par.Work.Do: already called Do")
+	}
+
+	w.running = n
+	w.f = f
+	w.wait.L = &w.mu
+
+	for i := 0; i < n-1; i++ {
+		go w.runner()
+	}
+	w.runner()
+}
+
+// runner executes work in w until both nothing is left to do
+// and all the runners are waiting for work.
+// (Then all the runners return.)
+func (w *Work) runner() {
+	for {
+		// Wait for something to do.
+		w.mu.Lock()
+		for len(w.todo) == 0 {
+			w.waiting++
+			if w.waiting == w.running {
+				// All done.
+				w.wait.Broadcast()
+				w.mu.Unlock()
+				return
+			}
+			w.wait.Wait()
+			w.waiting--
+		}
+
+		// Pick something to do at random,
+		// to eliminate pathological contention
+		// in case items added at about the same time
+		// are most likely to contend.
+		i := rand.Intn(len(w.todo))
+		item := w.todo[i]
+		w.todo[i] = w.todo[len(w.todo)-1]
+		w.todo = w.todo[:len(w.todo)-1]
+		w.mu.Unlock()
+
+		w.f(item)
+	}
+}
+
+// Cache runs an action once per key and caches the result.
+type Cache struct {
+	m sync.Map
+}
+
+type cacheEntry struct {
+	done   uint32
+	mu     sync.Mutex
+	result interface{}
+}
+
+// Do calls the function f if and only if Do is being called for the first time with this key.
+// No call to Do with a given key returns until the one call to f returns.
+// Do returns the value returned by the one call to f.
+func (c *Cache) Do(key interface{}, f func() interface{}) interface{} {
+	entryIface, ok := c.m.Load(key)
+	if !ok {
+		entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry))
+	}
+	e := entryIface.(*cacheEntry)
+	if atomic.LoadUint32(&e.done) == 0 {
+		e.mu.Lock()
+		if atomic.LoadUint32(&e.done) == 0 {
+			e.result = f()
+			atomic.StoreUint32(&e.done, 1)
+		}
+		e.mu.Unlock()
+	}
+	return e.result
+}
+
+// Get returns the cached result associated with key.
+// It returns nil if there is no such result.
+// If the result for key is being computed, Get does not wait for the computation to finish.
+func (c *Cache) Get(key interface{}) interface{} {
+	entryIface, ok := c.m.Load(key)
+	if !ok {
+		return nil
+	}
+	e := entryIface.(*cacheEntry)
+	if atomic.LoadUint32(&e.done) == 0 {
+		return nil
+	}
+	return e.result
+}
diff --git a/src/cmd/go/internal/par/work_test.go b/src/cmd/go/internal/par/work_test.go
new file mode 100644
index 0000000..71c0395
--- /dev/null
+++ b/src/cmd/go/internal/par/work_test.go
@@ -0,0 +1,53 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package par
+
+import (
+	"sync/atomic"
+	"testing"
+)
+
+func TestWork(t *testing.T) {
+	var w Work
+
+	const N = 10000
+	n := int32(0)
+	w.Add(N)
+	w.Do(100, func(x interface{}) {
+		atomic.AddInt32(&n, 1)
+		i := x.(int)
+		if i >= 2 {
+			w.Add(i - 1)
+			w.Add(i - 2)
+		}
+		w.Add(i >> 1)
+		w.Add((i >> 1) ^ 1)
+	})
+	if n != N+1 {
+		t.Fatalf("ran %d items, expected %d", n, N+1)
+	}
+}
+
+func TestCache(t *testing.T) {
+	var cache Cache
+
+	n := 1
+	v := cache.Do(1, func() interface{} { n++; return n })
+	if v != 2 {
+		t.Fatalf("cache.Do(1) did not run f")
+	}
+	v = cache.Do(1, func() interface{} { n++; return n })
+	if v != 2 {
+		t.Fatalf("cache.Do(1) ran f again!")
+	}
+	v = cache.Do(2, func() interface{} { n++; return n })
+	if v != 3 {
+		t.Fatalf("cache.Do(2) did not run f")
+	}
+	v = cache.Do(1, func() interface{} { n++; return n })
+	if v != 2 {
+		t.Fatalf("cache.Do(1) did not returned saved value from original cache.Do(1)")
+	}
+}
diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go
index 42a6be0..0c34009 100644
--- a/src/cmd/go/internal/search/search.go
+++ b/src/cmd/go/internal/search/search.go
@@ -173,6 +173,7 @@
 		if err != nil || !fi.IsDir() {
 			return nil
 		}
+		top := false
 		if path == dir {
 			// filepath.Walk starts at dir and recurses. For the recursive case,
 			// the path is the result of filepath.Join, which calls filepath.Clean.
@@ -182,6 +183,7 @@
 			// "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
 			// package, because prepending the prefix "./" to the unclean path would
 			// result in "././io", and match("././io") returns false.
+			top = true
 			path = filepath.Clean(path)
 		}
 
@@ -192,6 +194,13 @@
 			return filepath.SkipDir
 		}
 
+		if !top && cfg.ModulesEnabled {
+			// Ignore other modules found in subdirectories.
+			if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil {
+				return filepath.SkipDir
+			}
+		}
+
 		name := prefix + filepath.ToSlash(path)
 		if !match(name) {
 			return nil
@@ -352,7 +361,7 @@
 
 // ImportPathsNoDotExpansion returns the import paths to use for the given
 // command line, but it does no ... expansion.
-// TODO(vgo): Delete once old go get is gone.
+// TODO(rsc): Delete once old go get is gone.
 func ImportPathsNoDotExpansion(args []string) []string {
 	args = CleanImportPaths(args)
 	var out []string
@@ -421,3 +430,10 @@
 	elem := path[:i]
 	return !strings.Contains(elem, ".")
 }
+
+// IsRelativePath reports whether pattern should be interpreted as a directory
+// path relative to the current directory, as opposed to a pattern matching
+// import paths.
+func IsRelativePath(pattern string) bool {
+	return strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == ".."
+}
diff --git a/src/cmd/go/internal/semver/semver.go b/src/cmd/go/internal/semver/semver.go
index ecc5300..4af7118 100644
--- a/src/cmd/go/internal/semver/semver.go
+++ b/src/cmd/go/internal/semver/semver.go
@@ -69,6 +69,43 @@
 	return v[:1+len(pv.major)]
 }
 
+// MajorMinor returns the major.minor version prefix of the semantic version v.
+// For example, MajorMinor("v2.1.0") == "v2.1".
+// If v is an invalid semantic version string, MajorMinor returns the empty string.
+func MajorMinor(v string) string {
+	pv, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	i := 1 + len(pv.major)
+	if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
+		return v[:j]
+	}
+	return v[:i] + "." + pv.minor
+}
+
+// Prerelease returns the prerelease suffix of the semantic version v.
+// For example, Prerelease("v2.1.0-pre+meta") == "-pre".
+// If v is an invalid semantic version string, Prerelease returns the empty string.
+func Prerelease(v string) string {
+	pv, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	return pv.prerelease
+}
+
+// Build returns the build suffix of the semantic version v.
+// For example, Build("v2.1.0+meta") == "+meta".
+// If v is an invalid semantic version string, Build returns the empty string.
+func Build(v string) string {
+	pv, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	return pv.build
+}
+
 // Compare returns an integer comparing two versions according to
 // according to semantic version precedence.
 // The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
diff --git a/src/cmd/go/internal/semver/semver_test.go b/src/cmd/go/internal/semver/semver_test.go
index 7a697f6..96b64a5 100644
--- a/src/cmd/go/internal/semver/semver_test.go
+++ b/src/cmd/go/internal/semver/semver_test.go
@@ -14,6 +14,7 @@
 	out string
 }{
 	{"bad", ""},
+	{"v1-alpha.beta.gamma", ""},
 	{"v1-pre", ""},
 	{"v1+meta", ""},
 	{"v1-pre+meta", ""},
@@ -42,6 +43,7 @@
 	{"v1.2.3-zzz", "v1.2.3-zzz"},
 	{"v1.2.3", "v1.2.3"},
 	{"v1.2.3+meta", "v1.2.3"},
+	{"v1.2.3+meta-pre", "v1.2.3"},
 }
 
 func TestIsValid(t *testing.T) {
@@ -75,6 +77,63 @@
 	}
 }
 
+func TestMajorMinor(t *testing.T) {
+	for _, tt := range tests {
+		out := MajorMinor(tt.in)
+		var want string
+		if tt.out != "" {
+			want = tt.in
+			if i := strings.Index(want, "+"); i >= 0 {
+				want = want[:i]
+			}
+			if i := strings.Index(want, "-"); i >= 0 {
+				want = want[:i]
+			}
+			switch strings.Count(want, ".") {
+			case 0:
+				want += ".0"
+			case 1:
+				// ok
+			case 2:
+				want = want[:strings.LastIndex(want, ".")]
+			}
+		}
+		if out != want {
+			t.Errorf("MajorMinor(%q) = %q, want %q", tt.in, out, want)
+		}
+	}
+}
+
+func TestPrerelease(t *testing.T) {
+	for _, tt := range tests {
+		pre := Prerelease(tt.in)
+		var want string
+		if tt.out != "" {
+			if i := strings.Index(tt.out, "-"); i >= 0 {
+				want = tt.out[i:]
+			}
+		}
+		if pre != want {
+			t.Errorf("Prerelease(%q) = %q, want %q", tt.in, pre, want)
+		}
+	}
+}
+
+func TestBuild(t *testing.T) {
+	for _, tt := range tests {
+		build := Build(tt.in)
+		var want string
+		if tt.out != "" {
+			if i := strings.Index(tt.in, "+"); i >= 0 {
+				want = tt.in[i:]
+			}
+		}
+		if build != want {
+			t.Errorf("Build(%q) = %q, want %q", tt.in, build, want)
+		}
+	}
+}
+
 func TestCompare(t *testing.T) {
 	for i, ti := range tests {
 		for j, tj := range tests {
diff --git a/src/cmd/go/internal/str/path.go b/src/cmd/go/internal/str/path.go
index 84ca9d5..65cd639 100644
--- a/src/cmd/go/internal/str/path.go
+++ b/src/cmd/go/internal/str/path.go
@@ -9,8 +9,25 @@
 	"strings"
 )
 
-// HasFilePathPrefix reports whether the filesystem path s begins with the
-// elements in prefix.
+// HasPath reports whether the slash-separated path s
+// begins with the elements in prefix.
+func HasPathPrefix(s, prefix string) bool {
+	if len(s) == len(prefix) {
+		return s == prefix
+	}
+	if prefix == "" {
+		return true
+	}
+	if len(s) > len(prefix) {
+		if prefix != "" && prefix[len(prefix)-1] == '/' || s[len(prefix)] == '/' {
+			return s[:len(prefix)] == prefix
+		}
+	}
+	return false
+}
+
+// HasFilePathPrefix reports whether the filesystem path s
+// begins with the elements in prefix.
 func HasFilePathPrefix(s, prefix string) bool {
 	sv := strings.ToUpper(filepath.VolumeName(s))
 	pv := strings.ToUpper(filepath.VolumeName(prefix))
@@ -23,8 +40,10 @@
 		return false
 	case len(s) == len(prefix):
 		return s == prefix
+	case prefix == "":
+		return true
 	case len(s) > len(prefix):
-		if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
+		if prefix[len(prefix)-1] == filepath.Separator {
 			return strings.HasPrefix(s, prefix)
 		}
 		return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
diff --git a/src/cmd/go/internal/txtar/archive.go b/src/cmd/go/internal/txtar/archive.go
new file mode 100644
index 0000000..c384f33
--- /dev/null
+++ b/src/cmd/go/internal/txtar/archive.go
@@ -0,0 +1,140 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package txtar implements a trivial text-based file archive format.
+//
+// The goals for the format are:
+//
+//	- be trivial enough to create and edit by hand.
+//	- be able to store trees of text files describing go command test cases.
+//	- diff nicely in git history and code reviews.
+//
+// Non-goals include being a completely general archive format,
+// storing binary data, storing file modes, storing special files like
+// symbolic links, and so on.
+//
+// Txtar format
+//
+// A txtar archive is zero or more comment lines and then a sequence of file entries.
+// Each file entry begins with a file marker line of the form "-- FILENAME --"
+// and is followed by zero or more file content lines making up the file data.
+// The comment or file content ends at the next file marker line.
+// The file marker line must begin with the three-byte sequence "-- "
+// and end with the three-byte sequence " --", but the enclosed
+// file name can be surrounding by additional white space,
+// all of which is stripped.
+//
+// If the txtar file is missing a trailing newline on the final line,
+// parsers should consider a final newline to be present anyway.
+//
+// There are no possible syntax errors in a txtar archive.
+package txtar
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"strings"
+)
+
+// An Archive is a collection of files.
+type Archive struct {
+	Comment []byte
+	Files   []File
+}
+
+// A File is a single file in an archive.
+type File struct {
+	Name string // name of file ("foo/bar.txt")
+	Data []byte // text content of file
+}
+
+// Format returns the serialized form of an Archive.
+// It is assumed that the Archive data structure is well-formed:
+// a.Comment and all a.File[i].Data contain no file marker lines,
+// and all a.File[i].Name is non-empty.
+func Format(a *Archive) []byte {
+	var buf bytes.Buffer
+	buf.Write(fixNL(a.Comment))
+	for _, f := range a.Files {
+		fmt.Fprintf(&buf, "-- %s --\n", f.Name)
+		buf.Write(fixNL(f.Data))
+	}
+	return buf.Bytes()
+}
+
+// ParseFile parses the named file as an archive.
+func ParseFile(file string) (*Archive, error) {
+	data, err := ioutil.ReadFile(file)
+	if err != nil {
+		return nil, err
+	}
+	return Parse(data), nil
+}
+
+// Parse parses the serialized form of an Archive.
+// The returned Archive holds slices of data.
+func Parse(data []byte) *Archive {
+	a := new(Archive)
+	var name string
+	a.Comment, name, data = findFileMarker(data)
+	for name != "" {
+		f := File{name, nil}
+		f.Data, name, data = findFileMarker(data)
+		a.Files = append(a.Files, f)
+	}
+	return a
+}
+
+var (
+	newlineMarker = []byte("\n-- ")
+	marker        = []byte("-- ")
+	markerEnd     = []byte(" --")
+)
+
+// findFileMarker finds the next file marker in data,
+// extracts the file name, and returns the data before the marker,
+// the file name, and the data after the marker.
+// If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil.
+func findFileMarker(data []byte) (before []byte, name string, after []byte) {
+	var i int
+	for {
+		if name, after = isMarker(data[i:]); name != "" {
+			return data[:i], name, after
+		}
+		j := bytes.Index(data[i:], newlineMarker)
+		if j < 0 {
+			return fixNL(data), "", nil
+		}
+		i += j + 1 // positioned at start of new possible marker
+	}
+}
+
+// isMarker checks whether data begins with a file marker line.
+// If so, it returns the name from the line and the data after the line.
+// Otherwise it returns name == "" with an unspecified after.
+func isMarker(data []byte) (name string, after []byte) {
+	if !bytes.HasPrefix(data, marker) {
+		return "", nil
+	}
+	if i := bytes.IndexByte(data, '\n'); i >= 0 {
+		data, after = data[:i], data[i+1:]
+	}
+	if !bytes.HasSuffix(data, markerEnd) {
+		return "", nil
+	}
+	return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after
+}
+
+// If data is empty or ends in \n, fixNL returns data.
+// Otherwise fixNL returns a new slice consisting of data with a final \n added.
+func fixNL(data []byte) []byte {
+	if len(data) == 0 || data[len(data)-1] == '\n' {
+		return data
+	}
+	d := make([]byte, len(data)+1)
+	copy(d, data)
+	d[len(data)] = '\n'
+	return d
+}
diff --git a/src/cmd/go/internal/txtar/archive_test.go b/src/cmd/go/internal/txtar/archive_test.go
new file mode 100644
index 0000000..3f734f6
--- /dev/null
+++ b/src/cmd/go/internal/txtar/archive_test.go
@@ -0,0 +1,67 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package txtar
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+	"testing"
+)
+
+var tests = []struct {
+	name   string
+	text   string
+	parsed *Archive
+}{
+	{
+		name: "basic",
+		text: `comment1
+comment2
+-- file1 --
+File 1 text.
+-- foo ---
+More file 1 text.
+-- file 2 --
+File 2 text.
+-- empty --
+-- noNL --
+hello world`,
+		parsed: &Archive{
+			Comment: []byte("comment1\ncomment2\n"),
+			Files: []File{
+				{"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")},
+				{"file 2", []byte("File 2 text.\n")},
+				{"empty", []byte{}},
+				{"noNL", []byte("hello world\n")},
+			},
+		},
+	},
+}
+
+func Test(t *testing.T) {
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			a := Parse([]byte(tt.text))
+			if !reflect.DeepEqual(a, tt.parsed) {
+				t.Fatalf("Parse: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed))
+			}
+			text := Format(a)
+			a = Parse(text)
+			if !reflect.DeepEqual(a, tt.parsed) {
+				t.Fatalf("Parse after Format: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed))
+			}
+		})
+	}
+}
+
+func shortArchive(a *Archive) string {
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "comment: %q\n", a.Comment)
+	for _, f := range a.Files {
+		fmt.Fprintf(&buf, "file %q: %q\n", f.Name, f.Data)
+	}
+	return buf.String()
+}
diff --git a/src/cmd/go/internal/vgo/build.go b/src/cmd/go/internal/vgo/build.go
deleted file mode 100644
index ba10309..0000000
--- a/src/cmd/go/internal/vgo/build.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"bytes"
-	"cmd/go/internal/base"
-	"cmd/go/internal/cfg"
-	"cmd/go/internal/modinfo"
-	"cmd/go/internal/module"
-	"cmd/go/internal/search"
-	"encoding/hex"
-	"fmt"
-	"os"
-	"path/filepath"
-)
-
-var (
-	infoStart, _ = hex.DecodeString("3077af0c9274080241e1c107e6d618e6")
-	infoEnd, _   = hex.DecodeString("f932433186182072008242104116d8f2")
-)
-
-func isStandardImportPath(path string) bool {
-	if search.IsStandardImportPath(path) {
-		if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src", path)); err == nil {
-			return true
-		}
-		if _, err := os.Stat(filepath.Join(cfg.GOROOT, "src/vendor", path)); err == nil {
-			return true
-		}
-	}
-	return false
-}
-
-func PackageModuleInfo(path string) *modinfo.ModulePublic {
-	var info modinfo.ModulePublic
-	if isStandardImportPath(path) || !Enabled() {
-		return nil
-	}
-	target := findModule(path, path)
-	info.Top = target.Path == buildList[0].Path
-	info.Path = target.Path
-	info.Version = target.Version
-	return &info
-}
-
-func PackageBuildInfo(path string, deps []string) string {
-	if isStandardImportPath(path) || !Enabled() {
-		return ""
-	}
-	target := findModule(path, path)
-	mdeps := make(map[module.Version]bool)
-	for _, dep := range deps {
-		if !isStandardImportPath(dep) {
-			mdeps[findModule(path, dep)] = true
-		}
-	}
-	var mods []module.Version
-	delete(mdeps, target)
-	for mod := range mdeps {
-		mods = append(mods, mod)
-	}
-	sortModules(mods)
-
-	var buf bytes.Buffer
-	fmt.Fprintf(&buf, "path\t%s\n", path)
-	tv := target.Version
-	if tv == "" {
-		tv = "(devel)"
-	}
-	fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, findModHash(target))
-	for _, mod := range mods {
-		mv := mod.Version
-		if mv == "" {
-			mv = "(devel)"
-		}
-		r := replaced(mod)
-		h := ""
-		if r == nil {
-			h = "\t" + findModHash(mod)
-		}
-		fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mod.Version, h)
-		if r := replaced(mod); r != nil {
-			fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.New.Path, r.New.Version, findModHash(r.New))
-		}
-	}
-	return buf.String()
-}
-
-func findModule(target, path string) module.Version {
-	if path == "." {
-		return buildList[0]
-	}
-	for _, mod := range buildList {
-		if importPathInModule(path, mod.Path) {
-			return mod
-		}
-	}
-	base.Fatalf("build %v: cannot find module for path %v", target, path)
-	panic("unreachable")
-}
-
-func ModInfoProg(info string) []byte {
-	return []byte(fmt.Sprintf(`
-		package main
-		import _ "unsafe"
-		//go:linkname __debug_modinfo__ runtime/debug.modinfo
-		var __debug_modinfo__ string
-		func init() {
-			__debug_modinfo__ = %q
-		}
-	`, string(infoStart)+info+string(infoEnd)))
-}
diff --git a/src/cmd/go/internal/vgo/fetch.go b/src/cmd/go/internal/vgo/fetch.go
deleted file mode 100644
index c28353c..0000000
--- a/src/cmd/go/internal/vgo/fetch.go
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"archive/zip"
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-
-	"cmd/go/internal/base"
-	"cmd/go/internal/dirhash"
-	"cmd/go/internal/modfetch"
-	"cmd/go/internal/module"
-	"cmd/go/internal/semver"
-)
-
-// fetch returns the directory in the local download cache
-// holding the root of mod's source tree.
-// It downloads the module if needed.
-func fetch(mod module.Version) (dir string, err error) {
-	if r := replaced(mod); r != nil {
-		if r.New.Version == "" {
-			dir = r.New.Path
-			if !filepath.IsAbs(dir) {
-				dir = filepath.Join(ModRoot, dir)
-			}
-			return dir, nil
-		}
-		mod = r.New
-	}
-
-	modpath := mod.Path + "@" + mod.Version
-	dir = filepath.Join(srcV, modpath)
-	if files, _ := ioutil.ReadDir(dir); len(files) == 0 {
-		zipfile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".zip")
-		if _, err := os.Stat(zipfile); err == nil {
-			// Use it.
-			// This should only happen if the v/cache directory is preinitialized
-			// or if src/v/modpath was removed but not src/v/cache.
-			fmt.Fprintf(os.Stderr, "vgo: extracting %s %s\n", mod.Path, mod.Version)
-		} else {
-			if err := os.MkdirAll(filepath.Join(srcV, "cache", mod.Path, "@v"), 0777); err != nil {
-				return "", err
-			}
-			fmt.Fprintf(os.Stderr, "vgo: downloading %s %s\n", mod.Path, mod.Version)
-			if err := downloadZip(mod, zipfile); err != nil {
-				return "", err
-			}
-		}
-		if err := modfetch.Unzip(dir, zipfile, modpath, 0); err != nil {
-			fmt.Fprintf(os.Stderr, "-> %s\n", err)
-			return "", err
-		}
-	}
-	checkModHash(mod)
-	return dir, nil
-}
-
-func downloadZip(mod module.Version, target string) error {
-	repo, err := modfetch.Lookup(mod.Path)
-	if err != nil {
-		return err
-	}
-	tmpfile, err := repo.Zip(mod.Version, os.TempDir())
-	if err != nil {
-		return err
-	}
-	defer os.Remove(tmpfile)
-
-	// Double-check zip file looks OK.
-	z, err := zip.OpenReader(tmpfile)
-	if err != nil {
-		z.Close()
-		return err
-	}
-	prefix := mod.Path + "@" + mod.Version
-	for _, f := range z.File {
-		if !strings.HasPrefix(f.Name, prefix) {
-			z.Close()
-			return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
-		}
-	}
-	z.Close()
-
-	hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash)
-	if err != nil {
-		return err
-	}
-	r, err := os.Open(tmpfile)
-	if err != nil {
-		return err
-	}
-	defer r.Close()
-	w, err := os.Create(target)
-	if err != nil {
-		return err
-	}
-	if _, err := io.Copy(w, r); err != nil {
-		w.Close()
-		return fmt.Errorf("copying: %v", err)
-	}
-	if err := w.Close(); err != nil {
-		return err
-	}
-	return ioutil.WriteFile(target+"hash", []byte(hash), 0666)
-}
-
-var useModHash = false
-var modHash map[module.Version][]string
-
-func initModHash() {
-	if modHash != nil {
-		return
-	}
-	modHash = make(map[module.Version][]string)
-	file := filepath.Join(ModRoot, "go.modverify")
-	data, err := ioutil.ReadFile(file)
-	if err != nil && os.IsNotExist(err) {
-		return
-	}
-	if err != nil {
-		base.Fatalf("vgo: %v", err)
-	}
-	useModHash = true
-	lineno := 0
-	for len(data) > 0 {
-		var line []byte
-		lineno++
-		i := bytes.IndexByte(data, '\n')
-		if i < 0 {
-			line, data = data, nil
-		} else {
-			line, data = data[:i], data[i+1:]
-		}
-		f := strings.Fields(string(line))
-		if len(f) == 0 {
-			// blank line; skip it
-			continue
-		}
-		if len(f) != 3 {
-			base.Fatalf("vgo: malformed go.modverify:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
-		}
-		mod := module.Version{Path: f[0], Version: f[1]}
-		modHash[mod] = append(modHash[mod], f[2])
-	}
-}
-
-func checkModHash(mod module.Version) {
-	initModHash()
-	if !useModHash {
-		return
-	}
-
-	data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash"))
-	if err != nil {
-		base.Fatalf("vgo: verifying %s %s: %v", mod.Path, mod.Version, err)
-	}
-	h := strings.TrimSpace(string(data))
-	if !strings.HasPrefix(h, "h1:") {
-		base.Fatalf("vgo: verifying %s %s: unexpected ziphash: %q", mod.Path, mod.Version, h)
-	}
-
-	for _, vh := range modHash[mod] {
-		if h == vh {
-			return
-		}
-		if strings.HasPrefix(vh, "h1:") {
-			base.Fatalf("vgo: verifying %s %s: module hash mismatch\n\tdownloaded:   %v\n\tgo.modverify: %v", mod.Path, mod.Version, h, vh)
-		}
-	}
-	if len(modHash[mod]) > 0 {
-		fmt.Fprintf(os.Stderr, "warning: verifying %s %s: unknown hashes in go.modverify: %v; adding %v", mod.Path, mod.Version, strings.Join(modHash[mod], ", "), h)
-	}
-	modHash[mod] = append(modHash[mod], h)
-}
-
-func findModHash(mod module.Version) string {
-	data, err := ioutil.ReadFile(filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".ziphash"))
-	if err != nil {
-		return ""
-	}
-	return strings.TrimSpace(string(data))
-}
-
-func writeModHash() {
-	if !useModHash {
-		return
-	}
-
-	var mods []module.Version
-	for m := range modHash {
-		mods = append(mods, m)
-	}
-	sortModules(mods)
-	var buf bytes.Buffer
-	for _, m := range mods {
-		list := modHash[m]
-		sort.Strings(list)
-		for _, h := range list {
-			fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
-		}
-	}
-
-	file := filepath.Join(ModRoot, "go.modverify")
-	data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "go.modverify"))
-	if bytes.Equal(data, buf.Bytes()) {
-		return
-	}
-
-	if err := ioutil.WriteFile(file, buf.Bytes(), 0666); err != nil {
-		base.Fatalf("vgo: writing go.modverify: %v", err)
-	}
-}
-
-func sortModules(mods []module.Version) {
-	sort.Slice(mods, func(i, j int) bool {
-		mi := mods[i]
-		mj := mods[j]
-		if mi.Path != mj.Path {
-			return mi.Path < mj.Path
-		}
-		return semver.Compare(mi.Version, mj.Version) < 0
-	})
-}
diff --git a/src/cmd/go/internal/vgo/get.go b/src/cmd/go/internal/vgo/get.go
deleted file mode 100644
index 9fd8497..0000000
--- a/src/cmd/go/internal/vgo/get.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"strings"
-
-	"cmd/go/internal/base"
-	"cmd/go/internal/modfetch"
-	"cmd/go/internal/module"
-	"cmd/go/internal/mvs"
-	"cmd/go/internal/semver"
-)
-
-var CmdGet = &base.Command{
-	UsageLine: "get [build flags] [packages]",
-	Short:     "download and install versioned modules and dependencies",
-	Long: `
-Get downloads the latest versions of modules containing the named packages,
-along with the versions of the dependencies required by those modules
-(not necessarily the latest ones).
-
-It then installs the named packages, like 'go install'.
-
-The -u flag causes get to download the latest version of dependencies as well.
-
-Each package being updated can be suffixed with @version to specify
-the desired version. Specifying a version older than the one currently
-in use causes a downgrade, which may in turn downgrade other
-modules using that one, to keep everything consistent.
-
-TODO: Make this documentation better once the semantic dust settles.
-	`,
-}
-
-var getU = CmdGet.Flag.Bool("u", false, "")
-
-func init() {
-	CmdGet.Run = runGet // break init loop
-}
-
-func runGet(cmd *base.Command, args []string) {
-	if *getU && len(args) > 0 {
-		base.Fatalf("vgo get: -u not supported with argument list")
-	}
-	if !*getU && len(args) == 0 {
-		base.Fatalf("vgo get: need arguments or -u")
-	}
-
-	if *getU {
-		isGetU = true
-		ImportPaths([]string{"."})
-		return
-	}
-
-	Init()
-	InitMod()
-	var upgrade []module.Version
-	var downgrade []module.Version
-	var newPkgs []string
-	for _, pkg := range args {
-		var path, vers string
-		/* OLD CODE
-		if n := strings.Count(pkg, "(") + strings.Count(pkg, ")"); n > 0 {
-			i := strings.Index(pkg, "(")
-			j := strings.Index(pkg, ")")
-			if n != 2 || i < 0 || j <= i+1 || j != len(pkg)-1 && pkg[j+1] != '/' {
-				base.Errorf("vgo get: invalid module version syntax: %s", pkg)
-				continue
-			}
-			path, vers = pkg[:i], pkg[i+1:j]
-			pkg = pkg[:i] + pkg[j+1:]
-		*/
-		if i := strings.Index(pkg, "@"); i >= 0 {
-			path, pkg, vers = pkg[:i], pkg[:i], pkg[i+1:]
-			if strings.Contains(vers, "@") {
-				base.Errorf("vgo get: invalid module version syntax: %s", pkg)
-				continue
-			}
-		} else {
-			path = pkg
-			vers = "latest"
-		}
-		if vers == "none" {
-			downgrade = append(downgrade, module.Version{Path: path, Version: ""})
-		} else {
-			info, err := modfetch.Query(path, vers, allowed)
-			if err != nil {
-				base.Errorf("vgo get %v: %v", pkg, err)
-				continue
-			}
-			upgrade = append(upgrade, module.Version{Path: path, Version: info.Version})
-			newPkgs = append(newPkgs, pkg)
-		}
-	}
-	args = newPkgs
-
-	// Upgrade.
-	var err error
-	buildList, err = mvs.Upgrade(Target, newReqs(), upgrade...)
-	if err != nil {
-		base.Fatalf("vgo get: %v", err)
-	}
-
-	importPaths([]string{"."})
-
-	// Downgrade anything that went too far.
-	version := make(map[string]string)
-	for _, mod := range buildList {
-		version[mod.Path] = mod.Version
-	}
-	for _, mod := range upgrade {
-		if semver.Compare(mod.Version, version[mod.Path]) < 0 {
-			downgrade = append(downgrade, mod)
-		}
-	}
-
-	if len(downgrade) > 0 {
-		buildList, err = mvs.Downgrade(Target, newReqs(buildList[1:]...), downgrade...)
-		if err != nil {
-			base.Fatalf("vgo get: %v", err)
-		}
-
-		// TODO: Check that everything we need to import is still available.
-		/*
-			local := v.matchPackages("all", v.Reqs[:1])
-			for _, path := range local {
-				dir, err := v.importDir(path)
-				if err != nil {
-					return err // TODO
-				}
-				imports, testImports, err := imports.ScanDir(dir, v.Tags)
-				for _, path := range imports {
-					xxx
-				}
-				for _, path := range testImports {
-					xxx
-				}
-			}
-		*/
-	}
-	writeGoMod()
-
-	if len(args) > 0 {
-		InstallHook(args)
-	}
-}
-
-// Call into "go install". Set by internal/work, which imports us.
-var InstallHook func([]string)
diff --git a/src/cmd/go/internal/vgo/list.go b/src/cmd/go/internal/vgo/list.go
deleted file mode 100644
index c6656c2..0000000
--- a/src/cmd/go/internal/vgo/list.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"bufio"
-	"fmt"
-	"io"
-	"os"
-	"regexp"
-	"unicode/utf8"
-
-	"cmd/go/internal/base"
-	"cmd/go/internal/modfetch"
-	"cmd/go/internal/module"
-)
-
-func ListT(pkgs []string) {
-	if Init(); !Enabled() {
-		base.Fatalf("go list: cannot use -t outside module")
-	}
-	InitMod()
-
-	if len(pkgs) == 0 {
-		base.Fatalf("vgo list -t: need list of modules")
-	}
-
-	for _, pkg := range pkgs {
-		repo, err := modfetch.Lookup(pkg)
-		if err != nil {
-			base.Errorf("vgo list -t: %v", err)
-			continue
-		}
-		path := repo.ModulePath()
-		fmt.Printf("%s\n", path)
-		tags, err := repo.Versions("")
-		if err != nil {
-			base.Errorf("vgo list -t: %v", err)
-			continue
-		}
-		for _, t := range tags {
-			if excluded[module.Version{Path: path, Version: t}] {
-				t += " # excluded"
-			}
-			fmt.Printf("\t%s\n", t)
-		}
-	}
-}
-
-func ListM() {
-	if Init(); !Enabled() {
-		base.Fatalf("go list: cannot use -m outside module")
-	}
-	InitMod()
-	iterate(func(*loader) {})
-	printListM(os.Stdout)
-}
-
-func printListM(w io.Writer) {
-	var rows [][]string
-	rows = append(rows, []string{"MODULE", "VERSION"})
-	for _, mod := range buildList {
-		v := mod.Version
-		if v == "" {
-			v = "-"
-		}
-		rows = append(rows, []string{mod.Path, v})
-		if r := replaced(mod); r != nil {
-			rows = append(rows, []string{" => " + r.New.Path, r.New.Version})
-		}
-	}
-	printTable(w, rows)
-}
-
-func ListMU() {
-	if Init(); !Enabled() {
-		base.Fatalf("go list: cannot use -m outside module")
-	}
-	InitMod()
-
-	quietLookup = true // do not chatter in v.Lookup
-	iterate(func(*loader) {})
-
-	var rows [][]string
-	rows = append(rows, []string{"MODULE", "VERSION", "LATEST"})
-	for _, mod := range buildList {
-		var latest string
-		v := mod.Version
-		if v == "" {
-			v = "-"
-			latest = "-"
-		} else {
-			info, err := modfetch.Query(mod.Path, "latest", allowed)
-			if err != nil {
-				latest = "ERR: " + err.Error()
-			} else {
-				latest = info.Version
-				if !isPseudoVersion(latest) && !info.Time.IsZero() {
-					latest += info.Time.Local().Format(" (2006-01-02 15:04)")
-				}
-			}
-			if !isPseudoVersion(mod.Version) {
-				if info, err := modfetch.Query(mod.Path, mod.Version, nil); err == nil && !info.Time.IsZero() {
-					v += info.Time.Local().Format(" (2006-01-02 15:04)")
-				}
-			}
-		}
-		if latest == v {
-			latest = "-"
-		}
-		rows = append(rows, []string{mod.Path, v, latest})
-	}
-	printTable(os.Stdout, rows)
-}
-
-var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
-
-func isPseudoVersion(v string) bool {
-	return pseudoVersionRE.MatchString(v)
-}
-
-func printTable(w io.Writer, rows [][]string) {
-	var max []int
-	for _, row := range rows {
-		for i, c := range row {
-			n := utf8.RuneCountInString(c)
-			if i >= len(max) {
-				max = append(max, n)
-			} else if max[i] < n {
-				max[i] = n
-			}
-		}
-	}
-
-	b := bufio.NewWriter(w)
-	for _, row := range rows {
-		for len(row) > 0 && row[len(row)-1] == "" {
-			row = row[:len(row)-1]
-		}
-		for i, c := range row {
-			b.WriteString(c)
-			if i+1 < len(row) {
-				for j := utf8.RuneCountInString(c); j < max[i]+2; j++ {
-					b.WriteRune(' ')
-				}
-			}
-		}
-		b.WriteRune('\n')
-	}
-	b.Flush()
-}
diff --git a/src/cmd/go/internal/vgo/load.go b/src/cmd/go/internal/vgo/load.go
deleted file mode 100644
index 3ebba06..0000000
--- a/src/cmd/go/internal/vgo/load.go
+++ /dev/null
@@ -1,575 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"go/build"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-
-	"cmd/go/internal/base"
-	"cmd/go/internal/cfg"
-	"cmd/go/internal/imports"
-	"cmd/go/internal/modconv"
-	"cmd/go/internal/modfetch"
-	"cmd/go/internal/modfile"
-	"cmd/go/internal/module"
-	"cmd/go/internal/mvs"
-	"cmd/go/internal/search"
-	"cmd/go/internal/semver"
-)
-
-type importLevel int
-
-const (
-	levelNone          importLevel = 0
-	levelBuild         importLevel = 1
-	levelTest          importLevel = 2
-	levelTestRecursive importLevel = 3
-)
-
-var (
-	buildList []module.Version
-	tags      map[string]bool
-	importmap map[string]string
-	pkgdir    map[string]string
-	pkgmod    map[string]module.Version
-	isGetU    bool
-)
-
-func AddImports(gofiles []string) {
-	if Init(); !Enabled() {
-		return
-	}
-	InitMod()
-
-	imports, testImports, err := imports.ScanFiles(gofiles, tags)
-	if err != nil {
-		base.Fatalf("vgo: %v", err)
-	}
-
-	iterate(func(ld *loader) {
-		ld.importList(imports, levelBuild)
-		ld.importList(testImports, levelBuild)
-	})
-	writeGoMod()
-}
-
-func ImportPaths(args []string) []string {
-	if Init(); !Enabled() {
-		return search.ImportPaths(args)
-	}
-	InitMod()
-
-	paths := importPaths(args)
-	writeGoMod()
-	return paths
-}
-
-func importPaths(args []string) []string {
-	level := levelBuild
-	switch cfg.CmdName {
-	case "test", "vet":
-		level = levelTest
-	}
-	cleaned := search.CleanImportPaths(args)
-	iterate(func(ld *loader) {
-		args = expandImportPaths(cleaned)
-		for i, pkg := range args {
-			if pkg == "." || pkg == ".." || strings.HasPrefix(pkg, "./") || strings.HasPrefix(pkg, "../") {
-				dir := filepath.Join(cwd, pkg)
-				if dir == ModRoot {
-					pkg = Target.Path
-				} else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) {
-					pkg = Target.Path + filepath.ToSlash(dir[len(ModRoot):])
-				} else {
-					base.Errorf("vgo: package %s outside module root", pkg)
-					continue
-				}
-				args[i] = pkg
-			}
-			ld.importPkg(pkg, level)
-		}
-	})
-	return args
-}
-
-func Lookup(parentPath, path string) (dir, realPath string, err error) {
-	realPath = importmap[path]
-	if realPath == "" {
-		if isStandardImportPath(path) {
-			dir := filepath.Join(cfg.GOROOT, "src", path)
-			if _, err := os.Stat(dir); err == nil {
-				return dir, path, nil
-			}
-		}
-		return "", "", fmt.Errorf("no such package in module")
-	}
-	return pkgdir[realPath], realPath, nil
-}
-
-func iterate(doImports func(*loader)) {
-	var err error
-	mvsOp := mvs.BuildList
-	if isGetU {
-		mvsOp = mvs.UpgradeAll
-	}
-	buildList, err = mvsOp(Target, newReqs())
-	if err != nil {
-		base.Fatalf("vgo: %v", err)
-	}
-
-	var ld *loader
-	for {
-		ld = newLoader()
-		doImports(ld)
-		if len(ld.missing) == 0 {
-			break
-		}
-		for _, m := range ld.missing {
-			findMissing(m)
-		}
-		base.ExitIfErrors()
-		buildList, err = mvsOp(Target, newReqs())
-		if err != nil {
-			base.Fatalf("vgo: %v", err)
-		}
-	}
-	base.ExitIfErrors()
-
-	importmap = ld.importmap
-	pkgdir = ld.pkgdir
-	pkgmod = ld.pkgmod
-}
-
-type loader struct {
-	imported  map[string]importLevel
-	importmap map[string]string
-	pkgdir    map[string]string
-	pkgmod    map[string]module.Version
-	tags      map[string]bool
-	missing   []missing
-	imports   []string
-	stack     []string
-}
-
-type missing struct {
-	path  string
-	stack string
-}
-
-func newLoader() *loader {
-	ld := &loader{
-		imported:  make(map[string]importLevel),
-		importmap: make(map[string]string),
-		pkgdir:    make(map[string]string),
-		pkgmod:    make(map[string]module.Version),
-		tags:      imports.Tags(),
-	}
-	ld.imported["C"] = 100
-	return ld
-}
-
-func (ld *loader) stackText() string {
-	var buf bytes.Buffer
-	for _, p := range ld.stack[:len(ld.stack)-1] {
-		fmt.Fprintf(&buf, "import %q ->\n\t", p)
-	}
-	fmt.Fprintf(&buf, "import %q", ld.stack[len(ld.stack)-1])
-	return buf.String()
-}
-
-func (ld *loader) importList(pkgs []string, level importLevel) {
-	for _, pkg := range pkgs {
-		ld.importPkg(pkg, level)
-	}
-}
-
-func (ld *loader) importPkg(path string, level importLevel) {
-	if ld.imported[path] >= level {
-		return
-	}
-
-	ld.stack = append(ld.stack, path)
-	defer func() {
-		ld.stack = ld.stack[:len(ld.stack)-1]
-	}()
-
-	// Any rewritings go here.
-	realPath := path
-
-	ld.imported[path] = level
-	ld.importmap[path] = realPath
-	if realPath != path && ld.imported[realPath] >= level {
-		// Already handled.
-		return
-	}
-
-	dir := ld.importDir(realPath)
-	if dir == "" {
-		return
-	}
-
-	ld.pkgdir[realPath] = dir
-
-	imports, testImports, err := imports.ScanDir(dir, ld.tags)
-	if err != nil {
-		base.Errorf("vgo: %s [%s]: %v", ld.stackText(), dir, err)
-		return
-	}
-	nextLevel := level
-	if level == levelTest {
-		nextLevel = levelBuild
-	}
-	for _, pkg := range imports {
-		ld.importPkg(pkg, nextLevel)
-	}
-	if level >= levelTest {
-		for _, pkg := range testImports {
-			ld.importPkg(pkg, nextLevel)
-		}
-	}
-}
-
-func (ld *loader) importDir(path string) string {
-	if importPathInModule(path, Target.Path) {
-		dir := ModRoot
-		if len(path) > len(Target.Path) {
-			dir = filepath.Join(dir, path[len(Target.Path)+1:])
-		}
-		ld.pkgmod[path] = Target
-		return dir
-	}
-
-	i := strings.Index(path, "/")
-	if i < 0 || !strings.Contains(path[:i], ".") {
-		if strings.HasPrefix(path, "golang_org/") {
-			return filepath.Join(cfg.GOROOT, "src/vendor", path)
-		}
-		dir := filepath.Join(cfg.GOROOT, "src", path)
-		if _, err := os.Stat(dir); err == nil {
-			return dir
-		}
-	}
-
-	var mod1 module.Version
-	var dir1 string
-	for _, mod := range buildList {
-		if !importPathInModule(path, mod.Path) {
-			continue
-		}
-		dir, err := fetch(mod)
-		if err != nil {
-			base.Errorf("vgo: %s: %v", ld.stackText(), err)
-			return ""
-		}
-		if len(path) > len(mod.Path) {
-			dir = filepath.Join(dir, path[len(mod.Path)+1:])
-		}
-		if dir1 != "" {
-			base.Errorf("vgo: %s: found in both %v %v and %v %v", ld.stackText(),
-				mod1.Path, mod1.Version, mod.Path, mod.Version)
-			return ""
-		}
-		dir1 = dir
-		mod1 = mod
-	}
-	if dir1 != "" {
-		ld.pkgmod[path] = mod1
-		return dir1
-	}
-	ld.missing = append(ld.missing, missing{path, ld.stackText()})
-	return ""
-}
-
-func replaced(mod module.Version) *modfile.Replace {
-	var found *modfile.Replace
-	for _, r := range modFile.Replace {
-		if r.Old == mod {
-			found = r // keep going
-		}
-	}
-	return found
-}
-
-func importPathInModule(path, mpath string) bool {
-	return mpath == path ||
-		len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath
-}
-
-var found = make(map[string]bool)
-
-func findMissing(m missing) {
-	for _, mod := range buildList {
-		if importPathInModule(m.path, mod.Path) {
-			// Leave for ordinary build to complain about the missing import.
-			return
-		}
-	}
-	if build.IsLocalImport(m.path) {
-		base.Errorf("vgo: relative import is not supported: %s", m.path)
-		return
-	}
-	fmt.Fprintf(os.Stderr, "vgo: resolving import %q\n", m.path)
-	repo, info, err := modfetch.Import(m.path, allowed)
-	if err != nil {
-		base.Errorf("vgo: %s: %v", m.stack, err)
-		return
-	}
-	root := repo.ModulePath()
-	fmt.Fprintf(os.Stderr, "vgo: finding %s (latest)\n", root)
-	if found[root] {
-		base.Fatalf("internal error: findmissing loop on %s", root)
-	}
-	found[root] = true
-	fmt.Fprintf(os.Stderr, "vgo: adding %s %s\n", root, info.Version)
-	buildList = append(buildList, module.Version{Path: root, Version: info.Version})
-	modFile.AddRequire(root, info.Version)
-}
-
-type mvsReqs struct {
-	extra []module.Version
-}
-
-func newReqs(extra ...module.Version) *mvsReqs {
-	r := &mvsReqs{
-		extra: extra,
-	}
-	return r
-}
-
-func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
-	list, err := r.required(mod)
-	if err != nil {
-		return nil, err
-	}
-	if *getU {
-		for i := range list {
-			list[i].Version = "none"
-		}
-		return list, nil
-	}
-	for i, mv := range list {
-		for excluded[mv] {
-			mv1, err := r.Next(mv)
-			if err != nil {
-				return nil, err
-			}
-			if mv1.Version == "" {
-				return nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)
-			}
-			mv = mv1
-		}
-		list[i] = mv
-	}
-	return list, nil
-}
-
-var vgoVersion = []byte(modconv.Prefix)
-
-func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
-	if mod == Target {
-		var list []module.Version
-		if buildList != nil {
-			list = append(list, buildList[1:]...)
-			return list, nil
-		}
-		for _, r := range modFile.Require {
-			list = append(list, r.Mod)
-		}
-		list = append(list, r.extra...)
-		return list, nil
-	}
-
-	origPath := mod.Path
-	if repl := replaced(mod); repl != nil {
-		if repl.New.Version == "" {
-			// TODO: need to slip the new version into the tags list etc.
-			dir := repl.New.Path
-			if !filepath.IsAbs(dir) {
-				dir = filepath.Join(ModRoot, dir)
-			}
-			gomod := filepath.Join(dir, "go.mod")
-			data, err := ioutil.ReadFile(gomod)
-			if err != nil {
-				return nil, err
-			}
-			f, err := modfile.Parse(gomod, data, nil)
-			if err != nil {
-				return nil, err
-			}
-			var list []module.Version
-			for _, r := range f.Require {
-				list = append(list, r.Mod)
-			}
-			return list, nil
-		}
-		mod = repl.New
-	}
-
-	if mod.Version == "none" {
-		return nil, nil
-	}
-
-	if !semver.IsValid(mod.Version) {
-		// Disallow the broader queries supported by fetch.Lookup.
-		panic(fmt.Errorf("invalid semantic version %q for %s", mod.Version, mod.Path))
-		// TODO return nil, fmt.Errorf("invalid semantic version %q", mod.Version)
-	}
-
-	gomod := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".mod")
-	infofile := filepath.Join(srcV, "cache", mod.Path, "@v", mod.Version+".info")
-	var f *modfile.File
-	if data, err := ioutil.ReadFile(gomod); err == nil {
-		// If go.mod has a //vgo comment at the start,
-		// it was auto-converted from a legacy lock file.
-		// The auto-conversion details may have bugs and
-		// may be fixed in newer versions of vgo.
-		// We ignore cached go.mod files if they do not match
-		// our own vgoVersion.
-		if !bytes.HasPrefix(data, vgoVersion[:len("//vgo")]) || bytes.HasPrefix(data, vgoVersion) {
-			f, err := modfile.Parse(gomod, data, nil)
-			if err != nil {
-				return nil, err
-			}
-			var list []module.Version
-			for _, r := range f.Require {
-				list = append(list, r.Mod)
-			}
-			return list, nil
-		}
-		f, err = modfile.Parse("go.mod", data, nil)
-		if err != nil {
-			return nil, fmt.Errorf("parsing downloaded go.mod: %v", err)
-		}
-	} else {
-		if !quietLookup {
-			fmt.Fprintf(os.Stderr, "vgo: finding %s %s\n", mod.Path, mod.Version)
-		}
-		repo, err := modfetch.Lookup(mod.Path)
-		if err != nil {
-			base.Errorf("vgo: %s: %v\n", mod.Path, err)
-			return nil, err
-		}
-		info, err := repo.Stat(mod.Version)
-		if err != nil {
-			base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err)
-			return nil, err
-		}
-		data, err := repo.GoMod(info.Version)
-		if err != nil {
-			base.Errorf("vgo: %s %s: %v\n", mod.Path, mod.Version, err)
-			return nil, err
-		}
-
-		f, err = modfile.Parse("go.mod", data, nil)
-		if err != nil {
-			return nil, fmt.Errorf("parsing downloaded go.mod: %v", err)
-		}
-
-		dir := filepath.Dir(gomod)
-		if err := os.MkdirAll(dir, 0777); err != nil {
-			return nil, fmt.Errorf("caching go.mod: %v", err)
-		}
-		js, err := json.Marshal(info)
-		if err != nil {
-			return nil, fmt.Errorf("internal error: json failure: %v", err)
-		}
-		if err := ioutil.WriteFile(infofile, js, 0666); err != nil {
-			return nil, fmt.Errorf("caching info: %v", err)
-		}
-		if err := ioutil.WriteFile(gomod, data, 0666); err != nil {
-			return nil, fmt.Errorf("caching go.mod: %v", err)
-		}
-	}
-	if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
-		return nil, fmt.Errorf("downloaded %q and got module %q", mod.Path, mpath)
-	}
-
-	var list []module.Version
-	for _, req := range f.Require {
-		list = append(list, req.Mod)
-	}
-	if false {
-		fmt.Fprintf(os.Stderr, "REQLIST %v:\n", mod)
-		for _, req := range list {
-			fmt.Fprintf(os.Stderr, "\t%v\n", req)
-		}
-	}
-	return list, nil
-}
-
-var quietLookup bool
-
-func (*mvsReqs) Max(v1, v2 string) string {
-	if semver.Compare(v1, v2) == -1 {
-		return v2
-	}
-	return v1
-}
-
-func (*mvsReqs) Latest(path string) (module.Version, error) {
-	// Note that query "latest" is not the same as
-	// using repo.Latest.
-	// The query only falls back to untagged versions
-	// 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)
-	info, err := modfetch.Query(path, "latest", allowed)
-	if err != nil {
-		return module.Version{}, err
-	}
-	return module.Version{Path: path, Version: info.Version}, nil
-}
-
-var versionCache = make(map[string][]string)
-
-func versions(path string) ([]string, error) {
-	list, ok := versionCache[path]
-	if !ok {
-		var err error
-		repo, err := modfetch.Lookup(path)
-		if err != nil {
-			return nil, err
-		}
-		list, err = repo.Versions("")
-		if err != nil {
-			return nil, err
-		}
-		versionCache[path] = list
-	}
-	return list, nil
-}
-
-func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
-	list, err := versions(m.Path)
-	if err != nil {
-		return module.Version{}, err
-	}
-	i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
-	if i > 0 {
-		return module.Version{Path: m.Path, Version: list[i-1]}, nil
-	}
-	return module.Version{Path: m.Path, Version: "none"}, nil
-}
-
-func (*mvsReqs) Next(m module.Version) (module.Version, error) {
-	list, err := versions(m.Path)
-	if err != nil {
-		return module.Version{}, err
-	}
-	i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
-	if i < len(list) {
-		return module.Version{Path: m.Path, Version: list[i]}, nil
-	}
-	return module.Version{Path: m.Path, Version: "none"}, nil
-}
diff --git a/src/cmd/go/internal/vgo/search.go b/src/cmd/go/internal/vgo/search.go
deleted file mode 100644
index c3f7ab1..0000000
--- a/src/cmd/go/internal/vgo/search.go
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"fmt"
-	"go/build"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-
-	"cmd/go/internal/base"
-	"cmd/go/internal/cfg"
-	"cmd/go/internal/imports"
-	"cmd/go/internal/module"
-	"cmd/go/internal/search"
-)
-
-func expandImportPaths(args []string) []string {
-	var out []string
-	for _, a := range args {
-		// TODO(rsc): Move a == "ALL" test into search.IsMetaPackage
-		// once we officially lock in all the module work (tentatively, Go 1.12).
-		if search.IsMetaPackage(a) || a == "ALL" {
-			switch a {
-			default:
-				fmt.Fprintf(os.Stderr, "vgo: warning: %q matches no packages when using modules\n", a)
-			case "all", "ALL":
-				out = append(out, AllPackages(a)...)
-			}
-			continue
-		}
-		if strings.Contains(a, "...") {
-			if build.IsLocalImport(a) {
-				out = append(out, search.AllPackagesInFS(a)...)
-			} else {
-				out = append(out, AllPackages(a)...)
-			}
-			continue
-		}
-		out = append(out, a)
-	}
-	return out
-}
-
-// AllPackages returns all the packages that can be found
-// under the $GOPATH directories and $GOROOT matching pattern.
-// The pattern is either "all" (all packages), "std" (standard packages),
-// "cmd" (standard commands), or a path including "...".
-func AllPackages(pattern string) []string {
-	pkgs := MatchPackages(pattern)
-	if len(pkgs) == 0 {
-		fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
-	}
-	return pkgs
-}
-
-// MatchPackages returns a list of package paths matching pattern
-// (see go help packages for pattern syntax).
-func MatchPackages(pattern string) []string {
-	if pattern == "std" || pattern == "cmd" {
-		return nil
-	}
-	if pattern == "all" {
-		return MatchAll()
-	}
-	if pattern == "ALL" {
-		return MatchALL()
-	}
-
-	return matchPackages(pattern, buildList)
-}
-
-func matchPackages(pattern string, buildList []module.Version) []string {
-	match := func(string) bool { return true }
-	treeCanMatch := func(string) bool { return true }
-	if !search.IsMetaPackage(pattern) && pattern != "ALL" {
-		match = search.MatchPattern(pattern)
-		treeCanMatch = search.TreeCanMatchPattern(pattern)
-	}
-
-	have := map[string]bool{
-		"builtin": true, // ignore pseudo-package that exists only for documentation
-	}
-	if !cfg.BuildContext.CgoEnabled {
-		have["runtime/cgo"] = true // ignore during walk
-	}
-	var pkgs []string
-
-	for _, mod := range buildList {
-		if !treeCanMatch(mod.Path) {
-			continue
-		}
-		var root string
-		if mod.Version == "" {
-			root = ModRoot
-		} else {
-			var err error
-			root, err = fetch(mod)
-			if err != nil {
-				base.Errorf("vgo: %v", err)
-				continue
-			}
-		}
-		root = filepath.Clean(root)
-
-		filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
-			if err != nil {
-				return nil
-			}
-
-			want := true
-			// Avoid .foo, _foo, and testdata directory trees.
-			_, elem := filepath.Split(path)
-			if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
-				want = false
-			}
-
-			name := mod.Path + filepath.ToSlash(path[len(root):])
-			if !treeCanMatch(name) {
-				want = false
-			}
-
-			if !fi.IsDir() {
-				if fi.Mode()&os.ModeSymlink != 0 && want {
-					if target, err := os.Stat(path); err == nil && target.IsDir() {
-						fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
-					}
-				}
-				return nil
-			}
-
-			if !want {
-				return filepath.SkipDir
-			}
-			if path != root {
-				if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil {
-					return filepath.SkipDir
-				}
-			}
-
-			if !have[name] {
-				have[name] = true
-				if match(name) {
-					if _, _, err := imports.ScanDir(path, imports.Tags()); err != imports.ErrNoGo {
-						pkgs = append(pkgs, name)
-					}
-				}
-			}
-
-			if elem == "vendor" {
-				return filepath.SkipDir
-			}
-			return nil
-		})
-	}
-	return pkgs
-}
-
-// MatchAll returns a list of the packages matching the pattern "all".
-// We redefine "all" to mean start with the packages in the current module
-// and then follow imports into other modules to add packages imported
-// (directly or indirectly) as part of builds in this module.
-// It does not include packages in other modules that are not needed
-// by builds of this module.
-func MatchAll() []string {
-	return matchAll(imports.Tags())
-}
-
-// MatchALL returns a list of the packages matching the pattern "ALL".
-// The pattern "ALL" is like "all" but looks at all source files,
-// even ones that would be ignored by current build tag settings.
-// That's useful for identifying which packages to include in a vendor directory.
-func MatchALL() []string {
-	return matchAll(map[string]bool{"*": true})
-}
-
-// matchAll is the common implementation of MatchAll and MatchALL,
-// which differ only in the set of tags to apply to select files.
-func matchAll(tags map[string]bool) []string {
-	local := matchPackages("all", buildList[:1])
-	ld := newLoader()
-	ld.tags = tags
-	ld.importList(local, levelTestRecursive)
-	var all []string
-	for _, pkg := range ld.importmap {
-		if !isStandardImportPath(pkg) {
-			all = append(all, pkg)
-		}
-	}
-	sort.Strings(all)
-	return all
-}
diff --git a/src/cmd/go/internal/vgo/vendor.go b/src/cmd/go/internal/vgo/vendor.go
deleted file mode 100644
index acba4af..0000000
--- a/src/cmd/go/internal/vgo/vendor.go
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"cmd/go/internal/base"
-	"cmd/go/internal/module"
-)
-
-var CmdVendor = &base.Command{
-	UsageLine: "vendor [-v]",
-	Short:     "vendor dependencies of current module",
-	Long: `
-Vendor resets the module's vendor directory to include all
-packages needed to build and test all packages in the module
-and their dependencies.
-
-The -v flag causes vendor to print to standard error the
-module paths of the modules processed and the import paths
-of the packages copied.
-	`,
-}
-
-var vendorV = CmdVendor.Flag.Bool("v", false, "")
-
-func init() {
-	CmdVendor.Run = runVendor // break init cycle
-}
-
-func runVendor(cmd *base.Command, args []string) {
-	if Init(); !Enabled() {
-		base.Fatalf("vgo vendor: cannot use -m outside module")
-	}
-	if len(args) != 0 {
-		base.Fatalf("vgo vendor: vendor takes no arguments")
-	}
-	InitMod()
-	pkgs := ImportPaths([]string{"ALL"})
-
-	vdir := filepath.Join(ModRoot, "vendor")
-	if err := os.RemoveAll(vdir); err != nil {
-		base.Fatalf("vgo vendor: %v", err)
-	}
-
-	modpkgs := make(map[module.Version][]string)
-	for _, pkg := range pkgs {
-		m := pkgmod[pkg]
-		if m == Target {
-			continue
-		}
-		modpkgs[m] = append(modpkgs[m], pkg)
-	}
-
-	var buf bytes.Buffer
-	for _, m := range buildList[1:] {
-		if pkgs := modpkgs[m]; len(pkgs) > 0 {
-			repl := ""
-			if r := replaced(m); r != nil {
-				repl = " => " + r.New.Path
-				if r.New.Version != "" {
-					repl += " " + r.New.Version
-				}
-			}
-			fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl)
-			if *vendorV {
-				fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl)
-			}
-			for _, pkg := range pkgs {
-				fmt.Fprintf(&buf, "%s\n", pkg)
-				if *vendorV {
-					fmt.Fprintf(os.Stderr, "%s\n", pkg)
-				}
-				vendorPkg(vdir, pkg)
-			}
-		}
-	}
-	if err := ioutil.WriteFile(filepath.Join(vdir, "vgo.list"), buf.Bytes(), 0666); err != nil {
-		base.Fatalf("vgo vendor: %v", err)
-	}
-}
-
-func vendorPkg(vdir, pkg string) {
-	realPath := importmap[pkg]
-	if realPath != pkg && importmap[realPath] != "" {
-		fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg)
-	}
-
-	dst := filepath.Join(vdir, pkg)
-	src := pkgdir[realPath]
-	if src == "" {
-		fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
-	}
-	copyDir(dst, src, false)
-}
-
-func copyDir(dst, src string, recursive bool) {
-	files, err := ioutil.ReadDir(src)
-	if err != nil {
-		base.Fatalf("vgo vendor: %v", err)
-	}
-	if err := os.MkdirAll(dst, 0777); err != nil {
-		base.Fatalf("vgo vendor: %v", err)
-	}
-	for _, file := range files {
-		if file.IsDir() {
-			if recursive || file.Name() == "testdata" {
-				copyDir(filepath.Join(dst, file.Name()), filepath.Join(src, file.Name()), true)
-			}
-			continue
-		}
-		if !file.Mode().IsRegular() {
-			continue
-		}
-		r, err := os.Open(filepath.Join(src, file.Name()))
-		if err != nil {
-			base.Fatalf("vgo vendor: %v", err)
-		}
-		w, err := os.Create(filepath.Join(dst, file.Name()))
-		if err != nil {
-			base.Fatalf("vgo vendor: %v", err)
-		}
-		if _, err := io.Copy(w, r); err != nil {
-			base.Fatalf("vgo vendor: %v", err)
-		}
-		r.Close()
-		if err := w.Close(); err != nil {
-			base.Fatalf("vgo vendor: %v", err)
-		}
-	}
-}
-
-// hasPathPrefix reports whether the path s begins with the
-// elements in prefix.
-func hasPathPrefix(s, prefix string) bool {
-	switch {
-	default:
-		return false
-	case len(s) == len(prefix):
-		return s == prefix
-	case len(s) > len(prefix):
-		if prefix != "" && prefix[len(prefix)-1] == '/' {
-			return strings.HasPrefix(s, prefix)
-		}
-		return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
-	}
-}
diff --git a/src/cmd/go/internal/vgo/verify.go b/src/cmd/go/internal/vgo/verify.go
deleted file mode 100644
index d21c9d7..0000000
--- a/src/cmd/go/internal/vgo/verify.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vgo
-
-import (
-	"bytes"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-
-	"cmd/go/internal/base"
-	"cmd/go/internal/dirhash"
-	"cmd/go/internal/module"
-)
-
-var CmdVerify = &base.Command{
-	UsageLine: "verify",
-	Run:       runVerify,
-	Short:     "verify downloaded modules against expected hashes",
-	Long: `
-Verify checks that the dependencies of the current module,
-which are stored in a local downloaded source cache,
-have not been modified since being downloaded.
-
-If all the modules are unmodified, verify prints
-
-	all modules verified
-
-and exits successfully (status 0). Otherwise, verify reports
-which modules have been changed and exits with a non-zero status.
-	`,
-}
-
-func runVerify(cmd *base.Command, args []string) {
-	if Init(); !Enabled() {
-		base.Fatalf("vgo verify: cannot use outside module")
-	}
-	if len(args) != 0 {
-		// TODO: take arguments
-		base.Fatalf("vgo verify: verify takes no arguments")
-	}
-
-	// Make go.mod consistent but don't load any packages.
-	InitMod()
-	iterate(func(*loader) {})
-	writeGoMod()
-
-	ok := true
-	for _, mod := range buildList[1:] {
-		ok = verifyMod(mod) && ok
-	}
-	if ok {
-		fmt.Printf("all modules verified\n")
-	}
-}
-
-func verifyMod(mod module.Version) bool {
-	ok := true
-	zip := filepath.Join(srcV, "cache", mod.Path, "/@v/", mod.Version+".zip")
-	_, zipErr := os.Stat(zip)
-	dir := filepath.Join(srcV, mod.Path+"@"+mod.Version)
-	_, dirErr := os.Stat(dir)
-	data, err := ioutil.ReadFile(zip + "hash")
-	if err != nil {
-		if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
-			// Nothing downloaded yet. Nothing to verify.
-			return true
-		}
-		base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)
-		return false
-	}
-	h := string(bytes.TrimSpace(data))
-
-	if zipErr != nil && os.IsNotExist(zipErr) {
-		// ok
-	} else {
-		hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
-		if err != nil {
-			base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
-			return false
-		} else if hZ != h {
-			base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)
-			ok = false
-		}
-	}
-	if dirErr != nil && os.IsNotExist(dirErr) {
-		// ok
-	} else {
-		hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
-		if err != nil {
-
-			base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
-			return false
-		}
-		if hD != h {
-			base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)
-			ok = false
-		}
-	}
-	return ok
-}
diff --git a/src/cmd/go/internal/web2/web.go b/src/cmd/go/internal/web2/web.go
index d11ee6b..f390037 100644
--- a/src/cmd/go/internal/web2/web.go
+++ b/src/cmd/go/internal/web2/web.go
@@ -280,7 +280,7 @@
 	return err
 }
 
-var githubMessage = `vgo: 403 response from api.github.com
+var githubMessage = `go: 403 response from api.github.com
 
 GitHub applies fairly small rate limits to unauthenticated users, and
 you appear to be hitting them. To authenticate, please visit
diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go
index 8edf55f..8ce68be 100644
--- a/src/cmd/go/internal/work/action.go
+++ b/src/cmd/go/internal/work/action.go
@@ -327,8 +327,8 @@
 // depMode is the action (build or install) to use when building dependencies.
 // To turn package main into an executable, call b.Link instead.
 func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action {
-	if mode != ModeBuild && p.Internal.Local && p.Target == "" {
-		// Imported via local path. No permanent target.
+	if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" {
+		// Imported via local path or using modules. No permanent target.
 		mode = ModeBuild
 	}
 	if mode != ModeBuild && p.Name == "main" {
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 35ff250..5ba2d91 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -86,6 +86,8 @@
 		arguments to pass on each gccgo compiler/linker invocation.
 	-gcflags '[pattern=]arg list'
 		arguments to pass on each go tool compile invocation.
+	-getmode mode
+		module download mode to use. See 'go help modules' for more.
 	-installsuffix suffix
 		a suffix to use in the name of the package installation directory,
 		in order to keep output separate from default builds.
@@ -218,6 +220,7 @@
 	cmd.Flag.StringVar(&cfg.BuildBuildmode, "buildmode", "default", "")
 	cmd.Flag.Var(&load.BuildGcflags, "gcflags", "")
 	cmd.Flag.Var(&load.BuildGccgoflags, "gccgoflags", "")
+	cmd.Flag.StringVar(&cfg.BuildGetmode, "getmode", "", "")
 	cmd.Flag.StringVar(&cfg.BuildContext.InstallSuffix, "installsuffix", "", "")
 	cmd.Flag.Var(&load.BuildLdflags, "ldflags", "")
 	cmd.Flag.BoolVar(&cfg.BuildLinkshared, "linkshared", false, "")
@@ -305,7 +308,7 @@
 		depMode = ModeInstall
 	}
 
-	pkgs = pkgsFilter(load.Packages(args))
+	pkgs = omitTestOnly(pkgsFilter(load.Packages(args)))
 
 	// Special case -o /dev/null by not writing at all.
 	if cfg.BuildO == os.DevNull {
@@ -410,19 +413,44 @@
 
 func runInstall(cmd *base.Command, args []string) {
 	BuildInit()
-	InstallPackages(args, false)
+	InstallPackages(args)
 }
 
-func InstallPackages(args []string, forGet bool) {
+// omitTestOnly returns pkgs with test-only packages removed.
+func omitTestOnly(pkgs []*load.Package) []*load.Package {
+	var list []*load.Package
+	for _, p := range pkgs {
+		if len(p.GoFiles)+len(p.CgoFiles) == 0 && !p.Internal.CmdlinePkgLiteral {
+			// Package has no source files,
+			// perhaps due to build tags or perhaps due to only having *_test.go files.
+			// Also, it is only being processed as the result of a wildcard match
+			// like ./..., not because it was listed as a literal path on the command line.
+			// Ignore it.
+			continue
+		}
+		list = append(list, p)
+	}
+	return list
+}
+
+func InstallPackages(args []string) {
 	if cfg.GOBIN != "" && !filepath.IsAbs(cfg.GOBIN) {
 		base.Fatalf("cannot install, GOBIN must be an absolute path")
 	}
 
-	pkgs := pkgsFilter(load.PackagesForBuild(args))
-
+	pkgs := omitTestOnly(pkgsFilter(load.PackagesForBuild(args)))
 	for _, p := range pkgs {
-		if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") {
+		if p.Target == "" {
 			switch {
+			case p.Standard && p.ImportPath == "unsafe":
+				// unsafe is a built-in package, has no target
+			case p.Name != "main" && p.Internal.Local && p.ConflictDir == "":
+				// Non-executables outside GOPATH need not have a target:
+				// we can use the cache to hold the built package archive for use in future builds.
+				// The ones inside GOPATH should have a target (in GOPATH/pkg)
+				// or else something is wrong and worth reporting (like a ConflictDir).
+			case p.Name != "main" && p.Module != nil:
+				// Non-executables have no target (except the cache) when building with modules.
 			case p.Internal.GobinSubdir:
 				base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName)
 			case p.Internal.CmdlineFiles:
@@ -446,11 +474,6 @@
 	a := &Action{Mode: "go install"}
 	var tools []*Action
 	for _, p := range pkgs {
-		// During 'go get', don't attempt (and fail) to install packages with only tests.
-		// TODO(rsc): It's not clear why 'go get' should be different from 'go install' here. See #20760.
-		if forGet && len(p.GoFiles)+len(p.CgoFiles) == 0 && len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 {
-			continue
-		}
 		// If p is a tool, delay the installation until the end of the build.
 		// This avoids installing assemblers/compilers that are being executed
 		// by other steps in the build.
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index b51cd47..514d9c3 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -30,15 +30,8 @@
 	"cmd/go/internal/cfg"
 	"cmd/go/internal/load"
 	"cmd/go/internal/str"
-	"cmd/go/internal/vgo"
 )
 
-func init() {
-	vgo.InstallHook = func(args []string) {
-		CmdInstall.Run(CmdInstall, args)
-	}
-}
-
 // actionList returns the list of actions in the dag rooted at root
 // as visited in a depth-first post-order traversal.
 func actionList(root *Action) []*Action {
@@ -609,8 +602,8 @@
 		fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, a1.built)
 	}
 
-	if p.Internal.BuildInfo != "" {
-		if err := b.writeFile(objdir+"_gomod_.go", vgo.ModInfoProg(p.Internal.BuildInfo)); err != nil {
+	if p.Internal.BuildInfo != "" && cfg.ModulesEnabled {
+		if err := b.writeFile(objdir+"_gomod_.go", load.ModInfoProg(p.Internal.BuildInfo)); err != nil {
 			return err
 		}
 		gofiles = append(gofiles, objdir+"_gomod_.go")
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index 608f564..5308139 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -9,6 +9,7 @@
 import (
 	"cmd/go/internal/base"
 	"cmd/go/internal/cfg"
+	"cmd/go/internal/load"
 	"flag"
 	"fmt"
 	"os"
@@ -224,4 +225,16 @@
 			cfg.BuildContext.InstallSuffix += codegenArg[1:]
 		}
 	}
+
+	switch cfg.BuildGetmode {
+	case "":
+		// ok
+	case "local", "vendor":
+		// ok but check for modules
+		if load.ModLookup == nil {
+			base.Fatalf("build flag -getmode=%s only valid when using modules", cfg.BuildGetmode)
+		}
+	default:
+		base.Fatalf("-getmode=%s not supported (can be '', 'local', or 'vendor')", cfg.BuildGetmode)
+	}
 }
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 6388b59..5deb726 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -27,6 +27,9 @@
 	"cmd/go/internal/get"
 	"cmd/go/internal/help"
 	"cmd/go/internal/list"
+	"cmd/go/internal/modcmd"
+	"cmd/go/internal/modget"
+	"cmd/go/internal/modload"
 	"cmd/go/internal/run"
 	"cmd/go/internal/test"
 	"cmd/go/internal/tool"
@@ -48,6 +51,7 @@
 		get.CmdGet,
 		work.CmdInstall,
 		list.CmdList,
+		modcmd.CmdMod,
 		run.CmdRun,
 		test.CmdTest,
 		tool.CmdTool,
@@ -60,7 +64,10 @@
 		help.HelpEnvironment,
 		help.HelpFileType,
 		help.HelpGopath,
+		get.HelpGopathGet,
 		help.HelpImportPath,
+		modload.HelpModules,
+		modget.HelpModuleGet,
 		help.HelpPackages,
 		test.HelpTestflag,
 		test.HelpTestfunc,
@@ -78,6 +85,11 @@
 		base.Usage()
 	}
 
+	if modload.MustUseModules {
+		// If running with modules force-enabled, change get now to change help message.
+		*get.CmdGet = *modget.CmdGet
+	}
+
 	cfg.CmdName = args[0] // for error messages
 	if args[0] == "help" {
 		help.Help(args[1:])
@@ -115,6 +127,30 @@
 		os.Exit(2)
 	}
 
+	switch args[0] {
+	case "mod":
+		// Skip modload.Init (which may insist on go.mod existing)
+		// so that go mod -init has a chance to write the file.
+	case "version":
+		// Skip modload.Init so that users can report bugs against
+		// go mod -init.
+	case "vendor":
+		fmt.Fprintf(os.Stderr, "go vendor is now go mod -vendor\n")
+		os.Exit(2)
+	case "verify":
+		fmt.Fprintf(os.Stderr, "go verify is now go mod -verify\n")
+		os.Exit(2)
+	default:
+		// Run modload.Init so that each subcommand doesn't have to worry about it.
+		// Also install the module get command instead of the GOPATH go get command
+		// if modules are enabled.
+		modload.Init()
+		if modload.Enabled() {
+			// Might not have done this above, so do it now.
+			*get.CmdGet = *modget.CmdGet
+		}
+	}
+
 	// Set environment (GOOS, GOARCH, etc) explicitly.
 	// In theory all the commands we invoke should have
 	// the same default computation of these as we do,
diff --git a/src/cmd/go/mod_test.go b/src/cmd/go/mod_test.go
new file mode 100644
index 0000000..ebd9c9b
--- /dev/null
+++ b/src/cmd/go/mod_test.go
@@ -0,0 +1,1319 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main_test
+
+import (
+	"bytes"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"sort"
+	"strings"
+	"testing"
+
+	"cmd/go/internal/cfg"
+	"cmd/go/internal/modconv"
+	"cmd/go/internal/modload"
+	"cmd/go/internal/txtar"
+)
+
+var cmdGoDir, _ = os.Getwd()
+
+// testGoModules returns a testgoData set up for running
+// tests of Go modules. It:
+//
+// - sets $GO111MODULE=on
+// - sets $GOPROXY to the URL of a module proxy serving from ./testdata/mod
+// - creates a new temp directory with subdirectories home, gopath, and work
+// - sets $GOPATH to the new temp gopath directory
+// - sets $HOME to the new temp home directory
+// - writes work/go.mod containing "module m"
+// - chdirs to the the new temp work directory
+//
+// The caller must defer tg.cleanup().
+//
+func testGoModules(t *testing.T) *testgoData {
+	tg := testgo(t)
+	tg.setenv("GO111MODULE", "on")
+	StartProxy()
+	tg.setenv("GOPROXY", proxyURL)
+	tg.makeTempdir()
+	tg.setenv(homeEnvName(), tg.path("home")) // for build cache
+	tg.setenv("GOPATH", tg.path("gopath"))    // for download cache
+	tg.tempFile("work/go.mod", "module m")
+	tg.cd(tg.path("work"))
+
+	return tg
+}
+
+// extract clears the temp work directory and then
+// extracts the txtar archive named by file into that directory.
+// The file name is interpreted relative to the cmd/go directory,
+// so it usually begins with "testdata/".
+func (tg *testgoData) extract(file string) {
+	a, err := txtar.ParseFile(filepath.Join(cmdGoDir, file))
+	if err != nil {
+		tg.t.Fatal(err)
+	}
+	tg.cd(tg.path("."))
+	tg.must(removeAll(tg.path("work")))
+	tg.must(os.MkdirAll(tg.path("work"), 0777))
+	tg.cd(tg.path("work"))
+	for _, f := range a.Files {
+		tg.tempFile(filepath.Join("work", f.Name), string(f.Data))
+	}
+}
+
+func TestModGO111MODULE(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.tempFile("gp/src/x/y/z/go.mod", "module x/y/z")
+	tg.tempFile("gp/src/x/y/z/w/w.txt", "")
+	tg.tempFile("gp/foo/go.mod", "module example.com/mod")
+	tg.tempFile("gp/foo/bar/baz/quux.txt", "")
+	tg.tempFile("gp/bar/x.txt", "")
+	tg.setenv("GOPATH", tg.path("gp"))
+
+	// In GOPATH/src with go.mod.
+	tg.cd(tg.path("gp/src/x/y/z"))
+	tg.setenv("GO111MODULE", "auto")
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ""`, "expected module mode disabled")
+
+	tg.cd(tg.path("gp/src/x/y/z/w"))
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ""`, "expected module mode disabled")
+
+	tg.setenv("GO111MODULE", "off")
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ""`, "expected module mode disabled")
+
+	tg.setenv("GO111MODULE", "on")
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ".*z[/\\]go.mod"`, "expected module mode enabled")
+
+	// In GOPATH/src without go.mod.
+	tg.cd(tg.path("gp/src/x/y"))
+	tg.setenv("GO111MODULE", "auto")
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ""`, "expected module mode disabled")
+
+	tg.setenv("GO111MODULE", "off")
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ""`, "expected module mode disabled")
+
+	tg.setenv("GO111MODULE", "on")
+	tg.runFail("env", "-json")
+	tg.grepStderr(`cannot find main module root`, "expected module mode failure")
+
+	// Outside GOPATH/src with go.mod.
+	tg.cd(tg.path("gp/foo"))
+	tg.setenv("GO111MODULE", "auto")
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ".*foo[/\\]go.mod"`, "expected module mode enabled")
+
+	tg.cd(tg.path("gp/foo/bar/baz"))
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ".*foo[/\\]go.mod"`, "expected module mode enabled")
+
+	tg.setenv("GO111MODULE", "off")
+	tg.run("env", "-json")
+	tg.grepStdout(`"GOMOD": ""`, "expected module mode disabled")
+}
+
+func TestModVersionsInGOPATHMode(t *testing.T) {
+	tg := testgo(t)
+	tg.setenv("GO111MODULE", "off") // GOPATH mode
+	defer tg.cleanup()
+	tg.makeTempdir()
+
+	tg.runFail("get", "rsc.io/quote@v1.5.1")
+	tg.grepStderr(`go: cannot use path@version syntax in GOPATH mode`, "expected path@version error")
+
+	tg.runFail("build", "rsc.io/quote@v1.5.1")
+	tg.grepStderr(`can't load package:.* cannot use path@version syntax in GOPATH mode`, "expected path@version error")
+
+	tg.setenv("GO111MODULE", "on") // GOPATH mode
+	tg.tempFile("x/go.mod", "module x")
+	tg.cd(tg.path("x"))
+	tg.runFail("build", "rsc.io/quote@v1.5.1")
+	tg.grepStderr(`can't load package:.* can only use path@version syntax with 'go get'`, "expected path@version error")
+}
+
+func TestModFindModuleRoot(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x/Godeps"), 0777))
+	tg.must(os.MkdirAll(tg.path("x/vendor"), 0777))
+	tg.must(os.MkdirAll(tg.path("x/y/z"), 0777))
+	tg.must(os.MkdirAll(tg.path("x/.git"), 0777))
+	var files []string
+	for file := range modconv.Converters {
+		files = append(files, file)
+	}
+	files = append(files, "go.mod")
+	files = append(files, ".git/config")
+	sort.Strings(files)
+
+	for file := range modconv.Converters {
+		tg.must(ioutil.WriteFile(tg.path("x/"+file), []byte{}, 0666))
+		root, file1 := modload.FindModuleRoot(tg.path("x/y/z"), tg.path("."), true)
+		if root != tg.path("x") || file1 != file {
+			t.Errorf("%s: findModuleRoot = %q, %q, want %q, %q", file, root, file1, tg.path("x"), file)
+		}
+		tg.must(os.Remove(tg.path("x/" + file)))
+	}
+}
+
+func TestModFindModulePath(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte("package x // import \"x\"\n"), 0666))
+	path, err := modload.FindModulePath(tg.path("x"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if path != "x" {
+		t.Fatalf("FindModulePath = %q, want %q", path, "x")
+	}
+
+	// Windows line-ending.
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte("package x // import \"x\"\r\n"), 0666))
+	path, err = modload.FindModulePath(tg.path("x"))
+	if path != "x" || err != nil {
+		t.Fatalf("FindModulePath = %q, %v, want %q, nil", path, err, "x")
+	}
+
+	// Explicit setting in Godeps.json takes priority over implicit setting from GOPATH location.
+	tg.tempFile("gp/src/example.com/x/y/z/z.go", "package z")
+	gopath := cfg.BuildContext.GOPATH
+	defer func() {
+		cfg.BuildContext.GOPATH = gopath
+	}()
+	cfg.BuildContext.GOPATH = tg.path("gp")
+	path, err = modload.FindModulePath(tg.path("gp/src/example.com/x/y/z"))
+	if path != "example.com/x/y/z" || err != nil {
+		t.Fatalf("FindModulePath = %q, %v, want %q, nil", path, err, "example.com/x/y/z")
+	}
+
+	tg.tempFile("gp/src/example.com/x/y/z/Godeps/Godeps.json", `
+		{"ImportPath": "unexpected.com/z"}
+	`)
+	path, err = modload.FindModulePath(tg.path("gp/src/example.com/x/y/z"))
+	if path != "unexpected.com/z" || err != nil {
+		t.Fatalf("FindModulePath = %q, %v, want %q, nil", path, err, "unexpected.com/z")
+	}
+}
+
+func TestModImportModFails(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv("GO111MODULE", "off")
+	tg.tempFile("gopath/src/mod/foo/foo.go", "package foo")
+	tg.runFail("list", "mod/foo")
+	tg.grepStderr(`disallowed import path`, "expected disallowed because of module cache")
+}
+
+func TestModEdit(t *testing.T) {
+	// Test that local replacements work
+	// and that they can use a dummy name
+	// that isn't resolvable and need not even
+	// include a dot. See golang.org/issue/24100.
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.cd(tg.path("."))
+	tg.must(os.MkdirAll(tg.path("w"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x.go"), []byte("package x\n"), 0666))
+	tg.must(ioutil.WriteFile(tg.path("w/w.go"), []byte("package w\n"), 0666))
+
+	mustHaveGoMod := func(text string) {
+		t.Helper()
+		data, err := ioutil.ReadFile(tg.path("go.mod"))
+		tg.must(err)
+		if string(data) != text {
+			t.Fatalf("go.mod mismatch:\nhave:<<<\n%s>>>\nwant:<<<\n%s\n", string(data), text)
+		}
+	}
+
+	tg.runFail("mod", "-init")
+	tg.grepStderr(`cannot determine module path`, "")
+	_, err := os.Stat(tg.path("go.mod"))
+	if err == nil {
+		t.Fatalf("failed go mod -init created go.mod")
+	}
+
+	tg.run("mod", "-init", "-module", "x.x/y/z")
+	tg.grepStderr("creating new go.mod: module x.x/y/z", "")
+	mustHaveGoMod(`module x.x/y/z
+`)
+
+	tg.runFail("mod", "-init")
+	mustHaveGoMod(`module x.x/y/z
+`)
+
+	tg.run("mod",
+		"-droprequire=x.1",
+		"-require=x.1@v1.0.0",
+		"-require=x.2@v1.1.0",
+		"-droprequire=x.2",
+		"-exclude=x.1 @ v1.2.0",
+		"-exclude=x.1@v1.2.1",
+		"-replace=x.1@v1.3.0=>y.1@v1.4.0",
+		"-replace=x.1@v1.4.0 => ../z",
+	)
+	mustHaveGoMod(`module x.x/y/z
+
+require x.1 v1.0.0
+
+exclude (
+	x.1 v1.2.0
+	x.1 v1.2.1
+)
+
+replace (
+	x.1 v1.3.0 => y.1 v1.4.0
+	x.1 v1.4.0 => ../z
+)
+`)
+
+	tg.run("mod",
+		"-droprequire=x.1",
+		"-dropexclude=x.1@v1.2.1",
+		"-dropreplace=x.1@v1.3.0",
+		"-require=x.3@v1.99.0",
+	)
+	mustHaveGoMod(`module x.x/y/z
+
+exclude x.1 v1.2.0
+
+replace x.1 v1.4.0 => ../z
+
+require x.3 v1.99.0
+`)
+
+	tg.run("mod", "-json")
+	want := `{
+	"Module": {
+		"Path": "x.x/y/z"
+	},
+	"Require": [
+		{
+			"Path": "x.3",
+			"Version": "v1.99.0"
+		}
+	],
+	"Exclude": [
+		{
+			"Path": "x.1",
+			"Version": "v1.2.0"
+		}
+	],
+	"Replace": [
+		{
+			"Old": {
+				"Path": "x.1",
+				"Version": "v1.4.0"
+			},
+			"New": {
+				"Path": "../z"
+			}
+		}
+	]
+}
+`
+	if have := tg.getStdout(); have != want {
+		t.Fatalf("go mod -json mismatch:\nhave:<<<\n%s>>>\nwant:<<<\n%s\n", have, want)
+	}
+
+	tg.run("mod",
+		"-replace=x.1@v1.3.0=>y.1/v2@v2.3.5",
+		"-replace=x.1@v1.4.0=>y.1/v2@v2.3.5",
+	)
+	mustHaveGoMod(`module x.x/y/z
+
+exclude x.1 v1.2.0
+
+replace (
+	x.1 v1.3.0 => y.1/v2 v2.3.5
+	x.1 v1.4.0 => y.1/v2 v2.3.5
+)
+
+require x.3 v1.99.0
+`)
+	tg.run("mod",
+		"-replace=x.1=>y.1/v2@v2.3.6",
+	)
+	mustHaveGoMod(`module x.x/y/z
+
+exclude x.1 v1.2.0
+
+replace x.1 => y.1/v2 v2.3.6
+
+require x.3 v1.99.0
+`)
+
+	tg.run("mod", "-packages")
+	want = `x.x/y/z
+x.x/y/z/w
+`
+	if have := tg.getStdout(); have != want {
+		t.Fatalf("go mod -packages mismatch:\nhave:<<<\n%s>>>\nwant:<<<\n%s\n", have, want)
+	}
+
+	data, err := ioutil.ReadFile(tg.path("go.mod"))
+	tg.must(err)
+	data = bytes.Replace(data, []byte("\n"), []byte("\r\n"), -1)
+	data = append(data, "    \n"...)
+	tg.must(ioutil.WriteFile(tg.path("go.mod"), data, 0666))
+
+	tg.run("mod", "-fmt")
+	mustHaveGoMod(`module x.x/y/z
+
+exclude x.1 v1.2.0
+
+replace x.1 => y.1/v2 v2.3.6
+
+require x.3 v1.99.0
+`)
+}
+
+func TestModLocalModule(t *testing.T) {
+	// Test that local replacements work
+	// and that they can use a dummy name
+	// that isn't resolvable and need not even
+	// include a dot. See golang.org/issue/24100.
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x/y"), 0777))
+	tg.must(os.MkdirAll(tg.path("x/z"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/y/go.mod"), []byte(`
+		module x/y
+		require zz v1.0.0
+		replace zz v1.0.0 => ../z
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/y/y.go"), []byte(`package y; import _ "zz"`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/z/go.mod"), []byte(`
+		module x/z
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/z/z.go"), []byte(`package z`), 0666))
+	tg.cd(tg.path("x/y"))
+	tg.run("build")
+}
+
+func TestModTags(t *testing.T) {
+	// Test that build tags are used. See golang.org/issue/24053.
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`// +build tag1
+
+		package y
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/y.go"), []byte(`// +build tag2
+
+		package y
+	`), 0666))
+	tg.cd(tg.path("x"))
+
+	tg.runFail("list", "-f={{.GoFiles}}")
+	tg.grepStderr("build constraints exclude all Go files", "no Go source files without tags")
+
+	tg.run("list", "-f={{.GoFiles}}", "-tags=tag1")
+	tg.grepStdout(`\[x.go\]`, "Go source files for tag1")
+
+	tg.run("list", "-f={{.GoFiles}}", "-tags", "tag2")
+	tg.grepStdout(`\[y.go\]`, "Go source files for tag2")
+
+	tg.run("list", "-f={{.GoFiles}}", "-tags", "tag1 tag2")
+	tg.grepStdout(`\[x.go y.go\]`, "Go source files for tag1 and tag2")
+}
+
+func TestModFSPatterns(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x/vendor/v"), 0777))
+	tg.must(os.MkdirAll(tg.path("x/y/z/w"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module m
+	`), 0666))
+
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/vendor/v/v.go"), []byte(`package v; import "golang.org/x/crypto"`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/vendor/v.go"), []byte(`package main`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/y/y.go"), []byte(`package y`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/y/z/go.mod"), []byte(`syntax error`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/y/z/z.go"), []byte(`package z`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/y/z/w/w.go"), []byte(`package w`), 0666))
+
+	tg.cd(tg.path("x"))
+	for _, pattern := range []string{"all", "m/...", "./..."} {
+		tg.run("list", pattern)
+		tg.grepStdout(`^m$`, "expected m")
+		tg.grepStdout(`^m/vendor$`, "must see package named vendor")
+		tg.grepStdoutNot(`vendor/`, "must not see vendored packages")
+		tg.grepStdout(`^m/y$`, "expected m/y")
+		tg.grepStdoutNot(`^m/y/z`, "should ignore submodule m/y/z...")
+	}
+}
+
+func TestModGetVersions(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv(homeEnvName(), tg.path("home"))
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.cd(tg.path("x"))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x`), 0666))
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require rsc.io/quote v1.1.0
+	`), 0666))
+	tg.run("get", "rsc.io/quote@v2.0.0")
+	tg.run("list", "-m", "all")
+	tg.grepStdout("rsc.io/quote.*v0.0.0-20180709153244-fd906ed3b100", "did downgrade to v0.0.0-*")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require rsc.io/quote v1.2.0
+	`), 0666))
+	tg.run("get", "rsc.io/quote@v1.1.0")
+	tg.run("list", "-m", "-u", "all")
+	tg.grepStdout(`rsc.io/quote v1.1.0`, "did downgrade to v1.1.0")
+	tg.grepStdout(`rsc.io/quote v1.1.0 \[v1`, "did show upgrade to v1.2.0 or later")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require rsc.io/quote v1.1.0
+	`), 0666))
+	tg.run("get", "rsc.io/quote@v1.2.0")
+	tg.run("list", "-m", "all")
+	tg.grepStdout("rsc.io/quote.*v1.2.0", "did upgrade to v1.2.0")
+
+	// @14c0d48ead0c should resolve,
+	// and also there should be no build error about not having Go files in the root.
+	tg.run("get", "golang.org/x/text@14c0d48ead0c")
+
+	// @14c0d48ead0c should resolve.
+	// Now there should be no build at all.
+	tg.run("get", "-m", "golang.org/x/text@14c0d48ead0c")
+
+	// language@7f39a6fea4fe9364 should not resolve with -m,
+	// because .../language is not a module path.
+	tg.runFail("get", "-m", "golang.org/x/text/language@14c0d48ead0c")
+
+	// language@7f39a6fea4fe9364 should resolve without -m.
+	// Because of -d, there should be no build at all.
+	tg.run("get", "-d", "-x", "golang.org/x/text/language@14c0d48ead0c")
+	tg.grepStderrNot(`compile|cp .*language\.a$`, "should not see compile steps")
+
+	// Dropping -d, we should see a build now.
+	tg.run("get", "-x", "golang.org/x/text/language@14c0d48ead0c")
+	tg.grepStderr(`compile|cp .*language\.a$`, "should see compile steps")
+
+	// Even with -d, we should see an error for unknown packages.
+	tg.runFail("get", "-x", "golang.org/x/text/foo@14c0d48ead0c")
+}
+
+func TestModGetUpgrade(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv(homeEnvName(), tg.path("home"))
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.cd(tg.path("x"))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x; import _ "rsc.io/quote"`), 0666))
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require rsc.io/quote v1.5.1
+	`), 0666))
+
+	tg.run("get", "-x", "-u")
+	tg.run("list", "-m", "-f={{.Path}} {{.Version}}{{if .Indirect}} // indirect{{end}}", "all")
+	tg.grepStdout(`quote v1.5.2$`, "should have upgraded only to v1.5.2")
+	tg.grepStdout(`x/text [v0-9a-f.\-]+ // indirect`, "should list golang.org/x/text as indirect")
+
+	var gomod string
+	readGoMod := func() {
+		data, err := ioutil.ReadFile(tg.path("x/go.mod"))
+		if err != nil {
+			t.Fatal(err)
+		}
+		gomod = string(data)
+	}
+	readGoMod()
+	if !strings.Contains(gomod, "rsc.io/quote v1.5.2\n") {
+		t.Fatalf("expected rsc.io/quote direct requirement:\n%s", gomod)
+	}
+	if !regexp.MustCompile(`(?m)golang.org/x/text.* // indirect`).MatchString(gomod) {
+		t.Fatalf("expected golang.org/x/text indirect requirement:\n%s", gomod)
+	}
+
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x; import _ "golang.org/x/text"`), 0666))
+	tg.run("list") // rescans directory
+	readGoMod()
+	if !strings.Contains(gomod, "rsc.io/quote v1.5.2\n") {
+		t.Fatalf("expected rsc.io/quote direct requirement:\n%s", gomod)
+	}
+	if !regexp.MustCompile(`(?m)golang.org/x/text[^/]+\n`).MatchString(gomod) {
+		t.Fatalf("expected golang.org/x/text DIRECT requirement:\n%s", gomod)
+	}
+
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x; import _ "rsc.io/quote"`), 0666))
+	tg.run("mod", "-sync") // rescans everything, can put // indirect marks back
+	readGoMod()
+	if !strings.Contains(gomod, "rsc.io/quote v1.5.2\n") {
+		t.Fatalf("expected rsc.io/quote direct requirement:\n%s", gomod)
+	}
+	if !regexp.MustCompile(`(?m)golang.org/x/text.* // indirect\n`).MatchString(gomod) {
+		t.Fatalf("expected golang.org/x/text indirect requirement:\n%s", gomod)
+	}
+
+	tg.run("get", "rsc.io/quote@v0.0.0-20180214005840-23179ee8a569") // should record as (time-corrected) pseudo-version
+	readGoMod()
+	if !strings.Contains(gomod, "rsc.io/quote v0.0.0-20180214005840-23179ee8a569\n") {
+		t.Fatalf("expected rsc.io/quote v0.0.0-20180214005840-23179ee8a569 (not v1.5.1)\n%s", gomod)
+	}
+
+	tg.run("get", "rsc.io/quote@23179ee") // should record as v1.5.1
+	readGoMod()
+	if !strings.Contains(gomod, "rsc.io/quote v1.5.1\n") {
+		t.Fatalf("expected rsc.io/quote v1.5.1 (not 23179ee)\n%s", gomod)
+	}
+
+	tg.run("mod", "-require", "rsc.io/quote@23179ee") // should record as 23179ee
+	readGoMod()
+	if !strings.Contains(gomod, "rsc.io/quote 23179ee\n") {
+		t.Fatalf("expected rsc.io/quote 23179ee\n%s", gomod)
+	}
+
+	tg.run("mod", "-fix") // fixup in any future go command should find v1.5.1 again
+	readGoMod()
+	if !strings.Contains(gomod, "rsc.io/quote v1.5.1\n") {
+		t.Fatalf("expected rsc.io/quote v1.5.1\n%s", gomod)
+	}
+
+	tg.run("get", "-m", "rsc.io/quote@dd9747d")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`quote v0.0.0-20180628003336-dd9747d19b04$`, "should have moved to pseudo-commit")
+
+	tg.run("get", "-m", "-u")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`quote v0.0.0-20180628003336-dd9747d19b04$`, "should have stayed on pseudo-commit")
+
+	tg.run("get", "-m", "rsc.io/quote@e7a685a342")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`quote v0.0.0-20180214005133-e7a685a342c0$`, "should have moved to new pseudo-commit")
+
+	tg.run("get", "-m", "-u")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`quote v1.5.2$`, "should have moved off pseudo-commit")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+	`), 0666))
+	tg.run("list")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`quote v1.5.2$`, "should have added quote v1.5.2")
+	tg.grepStdoutNot(`v1.5.3-pre1`, "should not mention v1.5.3-pre1")
+
+	tg.run("list", "-m", "-versions", "rsc.io/quote")
+	want := "rsc.io/quote v1.0.0 v1.1.0 v1.2.0 v1.2.1 v1.3.0 v1.4.0 v1.5.0 v1.5.1 v1.5.2 v1.5.3-pre1\n"
+	if tg.getStdout() != want {
+		t.Errorf("go list versions:\nhave:\n%s\nwant:\n%s", tg.getStdout(), want)
+	}
+
+	tg.run("list", "-m", "rsc.io/quote@>v1.5.2")
+	tg.grepStdout(`v1.5.3-pre1`, "expected to find v1.5.3-pre1")
+	tg.run("list", "-m", "rsc.io/quote@<v1.5.4")
+	tg.grepStdout(`v1.5.2$`, "expected to find v1.5.2 (NOT v1.5.3-pre1)")
+
+	tg.runFail("list", "-m", "rsc.io/quote@>v1.5.3")
+	tg.grepStderr(`go list -m rsc.io/quote: no matching versions for query ">v1.5.3"`, "expected no matching version")
+
+	tg.run("list", "-m", "-e", "-f={{.Error.Err}}", "rsc.io/quote@>v1.5.3")
+	tg.grepStdout(`no matching versions for query ">v1.5.3"`, "expected no matching version")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require rsc.io/quote v1.4.0
+	`), 0666))
+
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`rsc.io/sampler v1.0.0`, "expected sampler v1.0.0")
+
+	tg.run("get", "-m", "-u=patch", "rsc.io/quote")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`rsc.io/quote v1.5.2`, "expected quote v1.5.2")                // rsc.io/quote gets implicit @latest (not -u=patch)
+	tg.grepStdout(`rsc.io/sampler v1.3.1`, "expected sampler v1.3.1")            // even though v1.5.2 requires v1.3.0
+	tg.grepStdout(`golang.org/x/text v0.0.0-`, "expected x/text pseudo-version") // can't jump from v0.0.0- to v0.3.0
+
+	tg.run("get", "-m", "-u=patch", "rsc.io/quote@v1.2.0")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`rsc.io/quote v1.2.0`, "expected quote v1.2.0")           // not v1.2.1: -u=patch applies to deps of args, not args
+	tg.grepStdout(`rsc.io/sampler v1.3.1`, "expected sampler line to stay") // even though v1.2.0 does not require sampler?
+
+	tg.run("get", "-m", "-u=patch")
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`rsc.io/quote v1.2.1`, "expected quote v1.2.1") // -u=patch with no args applies to deps of main module
+	tg.grepStdout(`rsc.io/sampler v1.3.1`, "expected sampler line to stay")
+	tg.grepStdout(`golang.org/x/text v0.0.0-`, "expected x/text pseudo-version") // even though x/text v0.3.0 is tagged
+
+	tg.run("get", "-m", "rsc.io/quote@v1.5.1")
+	tg.run("mod", "-vendor")
+	tg.setenv("GOPATH", tg.path("empty"))
+	tg.setenv("GOPROXY", "file:///nonexist")
+
+	tg.run("list", "-getmode=vendor", "all")
+	tg.run("list", "-getmode=vendor", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", "all")
+	tg.grepStdout(`rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote`, "expected vendored rsc.io/quote")
+	tg.grepStdout(`golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text`, "expected vendored golang.org/x/text")
+
+	tg.runFail("list", "-getmode=vendor", "-m", "rsc.io/quote@latest")
+	tg.grepStderr(`module lookup disabled by -getmode=vendor`, "expected disabled")
+	tg.runFail("get", "-getmode=vendor", "-u")
+	tg.grepStderr(`go get: disabled by -getmode=vendor`, "expected disabled")
+}
+
+func TestModBadDomain(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.tempFile("work/x.go", `
+		package x
+
+		import _ "appengine"
+		import _ "nonexistent.rsc.io" // domain does not exist
+	`)
+
+	tg.runFail("get", "appengine")
+	tg.grepStderr(`cannot find module providing package appengine`, "expected module error ")
+	tg.runFail("get", "x/y.z")
+	tg.grepStderr(`cannot find module providing package x/y.z`, "expected module error")
+
+	tg.runFail("build")
+	tg.grepStderrNot("unknown module appengine: not a domain name", "expected nothing about appengine")
+	tg.grepStderr("cannot find module providing package nonexistent.rsc.io", "expected error for nonexistent.rsc.io")
+}
+
+func TestModSync(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	write := func(name, text string) {
+		name = tg.path(name)
+		dir := filepath.Dir(name)
+		tg.must(os.MkdirAll(dir, 0777))
+		tg.must(ioutil.WriteFile(name, []byte(text), 0666))
+	}
+
+	write("m/go.mod", `
+module m
+
+require (
+	x.1 v1.0.0
+	y.1 v1.0.0
+	w.1 v1.2.0
+)
+
+replace x.1 v1.0.0 => ../x
+replace y.1 v1.0.0 => ../y
+replace z.1 v1.1.0 => ../z
+replace z.1 v1.2.0 => ../z
+replace w.1 => ../w
+`)
+	write("m/m.go", `
+package m
+
+import _ "x.1"
+import _ "z.1/sub"
+`)
+
+	write("w/go.mod", `
+module w
+`)
+	write("w/w.go", `
+package w
+`)
+
+	write("x/go.mod", `
+module x
+require w.1 v1.1.0
+require z.1 v1.1.0
+`)
+	write("x/x.go", `
+package x
+
+import _ "w.1"
+`)
+
+	write("y/go.mod", `
+module y
+require z.1 v1.2.0
+`)
+
+	write("z/go.mod", `
+module z
+`)
+	write("z/sub/sub.go", `
+package sub
+`)
+
+	tg.cd(tg.path("m"))
+	tg.run("mod", "-sync", "-v")
+	tg.grepStderr(`^unused y.1`, "need y.1 unused")
+	tg.grepStderrNot(`^unused [^y]`, "only y.1 should be unused")
+
+	tg.run("list", "-m", "all")
+	tg.grepStdoutNot(`^y.1`, "y should be gone")
+	tg.grepStdout(`^w.1\s+v1.2.0`, "need w.1 to stay at v1.2.0")
+	tg.grepStdout(`^z.1\s+v1.2.0`, "need z.1 to stay at v1.2.0 even though y is gone")
+}
+
+func TestModVendor(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.extract("testdata/vendormod.txt")
+
+	tg.run("list", "-m", "all")
+	tg.grepStdout(`^x`, "expected to see module x")
+	tg.grepStdout(`=> ./x`, "expected to see replacement for module x")
+	tg.grepStdout(`^w`, "expected to see module w")
+
+	if !testing.Short() {
+		tg.run("build")
+		tg.runFail("build", "-getmode=vendor")
+	}
+
+	tg.run("list", "-f={{.Dir}}", "x")
+	tg.grepStdout(`work[/\\]x$`, "expected x in work/x")
+
+	mustHaveVendor := func(name string) {
+		t.Helper()
+		tg.mustExist(filepath.Join(tg.path("work/vendor"), name))
+	}
+	mustNotHaveVendor := func(name string) {
+		t.Helper()
+		tg.mustNotExist(filepath.Join(tg.path("work/vendor"), name))
+	}
+
+	tg.run("mod", "-vendor", "-v")
+	tg.grepStderr(`^# x v1.0.0 => ./x`, "expected to see module x with replacement")
+	tg.grepStderr(`^x`, "expected to see package x")
+	tg.grepStderr(`^# y v1.0.0 => ./y`, "expected to see module y with replacement")
+	tg.grepStderr(`^y`, "expected to see package y")
+	tg.grepStderr(`^# z v1.0.0 => ./z`, "expected to see module z with replacement")
+	tg.grepStderr(`^z`, "expected to see package z")
+	tg.grepStderrNot(`w`, "expected NOT to see unused module w")
+
+	tg.run("list", "-f={{.Dir}}", "x")
+	tg.grepStdout(`work[/\\]x$`, "expected x in work/x")
+
+	tg.run("list", "-f={{.Dir}}", "-m", "x")
+	tg.grepStdout(`work[/\\]x$`, "expected x in work/x")
+
+	tg.run("list", "-getmode=vendor", "-f={{.Dir}}", "x")
+	tg.grepStdout(`work[/\\]vendor[/\\]x$`, "expected x in work/vendor/x in -get=vendor mode")
+
+	tg.run("list", "-getmode=vendor", "-f={{.Dir}}", "-m", "x")
+	tg.grepStdout(`work[/\\]vendor[/\\]x$`, "expected x in work/vendor/x in -get=vendor mode")
+
+	tg.run("list", "-f={{.Dir}}", "w")
+	tg.grepStdout(`work[/\\]w$`, "expected w in work/w")
+	tg.runFail("list", "-getmode=vendor", "-f={{.Dir}}", "w")
+	tg.grepStderr(`work[/\\]vendor[/\\]w`, "want error about work/vendor/w not existing")
+
+	tg.run("list", "-getmode=local", "-f={{.Dir}}", "w")
+	tg.grepStdout(`work[/\\]w`, "expected w in work/w")
+
+	tg.runFail("list", "-getmode=local", "-f={{.Dir}}", "newpkg")
+	tg.grepStderr(`disabled by -getmode=local`, "expected -getmode=local to avoid network")
+
+	mustNotHaveVendor("x/testdata")
+	mustNotHaveVendor("a/foo/bar/b/main_test.go")
+
+	mustHaveVendor("a/foo/AUTHORS.txt")
+	mustHaveVendor("a/foo/CONTRIBUTORS")
+	mustHaveVendor("a/foo/LICENSE")
+	mustHaveVendor("a/foo/PATENTS")
+	mustHaveVendor("a/foo/COPYING")
+	mustHaveVendor("a/foo/COPYLEFT")
+	mustHaveVendor("x/NOTICE!")
+	mustHaveVendor("mysite/myname/mypkg/LICENSE.txt")
+
+	mustNotHaveVendor("a/foo/licensed-to-kill")
+	mustNotHaveVendor("w")
+	mustNotHaveVendor("w/LICENSE") // w wasn't copied at all
+	mustNotHaveVendor("x/x2")
+	mustNotHaveVendor("x/x2/LICENSE") // x/x2 wasn't copied at all
+
+	if !testing.Short() {
+		tg.run("build")
+		tg.run("build", "-getmode=vendor")
+		tg.run("test", "-getmode=vendor", ".", "./subdir")
+		tg.run("test", "-getmode=vendor", "./...")
+	}
+}
+
+func TestModList(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv(homeEnvName(), tg.path("."))
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`
+		package x
+		import _ "rsc.io/quote"
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require rsc.io/quote v1.5.1
+		replace rsc.io/sampler v1.3.0 => rsc.io/sampler v1.3.1
+	`), 0666))
+	tg.cd(tg.path("x"))
+
+	tg.run("list", "-m", "-f={{.Main}}: {{.Dir}}")
+	tg.grepStdout(`^true: `, "expected main module to have Main=true")
+	tg.grepStdout(regexp.QuoteMeta(tg.path("x")), "expected Dir of main module to be present")
+
+	tg.run("list", "-m", "-f={{.Main}}: {{.Dir}}", "rsc.io/quote")
+	tg.grepStdout(`^false: `, "expected non-main module to have Main=false")
+	tg.grepStdoutNot(`quote@`, "should not have local copy of code")
+
+	tg.run("list", "-f={{.Dir}}", "rsc.io/quote") // downloads code to load package
+	tg.grepStdout(`mod[\\/]rsc.io[\\/]quote@v1.5.1`, "expected cached copy of code")
+	dir := strings.TrimSpace(tg.getStdout())
+	info, err := os.Stat(dir)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if info.Mode()&0222 != 0 {
+		t.Fatalf("%s should be unwritable", dir)
+	}
+
+	tg.run("list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}{{with .Replace}} => {{.Version}} {{.Dir}}{{end}}", "all")
+	tg.grepStdout(`mod[\\/]rsc.io[\\/]quote@v1.5.1`, "expected cached copy of code")
+	tg.grepStdout(`v1.3.0 .*mod[\\/]rsc.io[\\/]sampler@v1.3.1 => v1.3.1 .*@v1.3.1`, "expected v1.3.1 replacement")
+
+	// check that list std works; also check that rsc.io/quote/buggy is a listable package
+	tg.run("list", "std", "rsc.io/quote/buggy")
+	tg.grepStdout("^math/big", "expected standard library")
+
+	tg.run("list", "-m", "-e", "-f={{.Path}} {{.Error.Err}}", "nonexist", "rsc.io/quote/buggy")
+	tg.grepStdout(`^nonexist module "nonexist" is not a known dependency`, "expected error via template")
+	tg.grepStdout(`^rsc.io/quote/buggy module "rsc.io/quote/buggy" is not a known dependency`, "expected error via template")
+
+	tg.runFail("list", "-m", "nonexist", "rsc.io/quote/buggy")
+	tg.grepStderr(`go list -m nonexist: module "nonexist" is not a known dependency`, "expected error on stderr")
+	tg.grepStderr(`go list -m rsc.io/quote/buggy: module "rsc.io/quote/buggy" is not a known dependency`, "expected error on stderr")
+
+	// Check that module loader does not interfere with list -e (golang.org/issue/24149).
+	tg.run("list", "-e", "-f={{.ImportPath}} {{.Error.Err}}", "database")
+	tg.grepStdout(`^database no Go files in `, "expected error via template")
+	tg.runFail("list", "database")
+	tg.grepStderr(`package database: no Go files`, "expected error on stderr")
+
+}
+
+func TestModInitLegacy(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv(homeEnvName(), tg.path("."))
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`
+		package x
+	`), 0666))
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/Gopkg.lock"), []byte(`
+[[projects]]
+  name = "rsc.io/sampler"
+  version = "v1.0.0"
+	`), 0666))
+
+	tg.cd(tg.path("x"))
+	tg.run("build", "-v")
+	tg.grepStderr("copying requirements from .*Gopkg.lock", "did not copy Gopkg.lock")
+	tg.run("list", "-m", "all")
+	tg.grepStderrNot("copying requirements from .*Gopkg.lock", "should not copy Gopkg.lock again")
+	tg.grepStdout("rsc.io/sampler.*v1.0.0", "did not copy Gopkg.lock")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/Gopkg.lock"), []byte(`
+	`), 0666))
+
+	tg.run("list")
+	tg.grepStderr("copying requirements from .*Gopkg.lock", "did not copy Gopkg.lock")
+	tg.run("list")
+	tg.grepStderrNot("copying requirements from .*Gopkg.lock", "should not copy Gopkg.lock again")
+}
+
+func TestModQueryExcluded(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x; import _ "github.com/gorilla/mux"`), 0666))
+	gomod := []byte(`
+		module x
+
+		exclude rsc.io/quote v1.5.0
+	`)
+
+	tg.setenv(homeEnvName(), tg.path("home"))
+	tg.cd(tg.path("x"))
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), gomod, 0666))
+	tg.runFail("get", "rsc.io/quote@v1.5.0")
+	tg.grepStderr("rsc.io/quote@v1.5.0 excluded", "print version excluded")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), gomod, 0666))
+	tg.run("get", "rsc.io/quote@v1.5.1")
+	tg.grepStderr("rsc.io/quote v1.5.1", "find version 1.5.1")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), gomod, 0666))
+	tg.run("get", "rsc.io/quote@>=v1.5")
+	tg.run("list", "-m", "...quote")
+	tg.grepStdout("rsc.io/quote v1.5.[1-9]", "expected version 1.5.1 or later")
+}
+
+func TestModRequireExcluded(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x; import _ "rsc.io/quote"`), 0666))
+
+	tg.setenv(homeEnvName(), tg.path("home"))
+	tg.cd(tg.path("x"))
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		exclude rsc.io/sampler latest
+		require rsc.io/sampler latest
+	`), 0666))
+	tg.runFail("build")
+	tg.grepStderr("no newer version available", "only available version excluded")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		exclude rsc.io/quote v1.5.1
+		require rsc.io/quote v1.5.1
+	`), 0666))
+	tg.run("build")
+	tg.grepStderr("rsc.io/quote v1.5.2", "find version 1.5.2")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		exclude rsc.io/quote v1.5.2
+		require rsc.io/quote v1.5.1
+	`), 0666))
+	tg.run("build")
+	tg.grepStderr("rsc.io/quote v1.5.1", "find version 1.5.1")
+}
+
+func TestModInitLegacy2(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv(homeEnvName(), tg.path("."))
+
+	// Testing that on Windows the path x/Gopkg.lock turning into x\Gopkg.lock does not confuse converter.
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/Gopkg.lock"), []byte(`
+	  [[projects]]
+		name = "rsc.io/quote"
+		packages = ["."]
+		revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
+		version = "v1.4.0"`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/main.go"), []byte("package x // import \"x\"\n import _ \"github.com/pkg/errors\""), 0666))
+	tg.cd(tg.path("x"))
+	tg.run("list", "-m", "all")
+
+	// If the conversion just ignored the Gopkg.lock entirely
+	// it would choose a newer version (like v1.5.2 or maybe
+	// something even newer). Check for the older version to
+	// make sure Gopkg.lock was properly used.
+	tg.grepStdout("v1.4.0", "expected rsc.io/quote at v1.4.0")
+}
+
+func TestModVerify(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	gopath := tg.path("gp")
+	tg.setenv("GOPATH", gopath)
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require rsc.io/quote v1.1.0
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`package x; import _ "rsc.io/quote"`), 0666))
+
+	// With correct go.sum,verify succeeds but avoids download.
+	tg.must(ioutil.WriteFile(tg.path("x/go.sum"), []byte(`rsc.io/quote v1.1.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
+`), 0666))
+	tg.cd(tg.path("x"))
+	tg.run("mod", "-verify")
+	tg.mustNotExist(filepath.Join(gopath, "src/mod/cache/download/rsc.io/quote/@v/v1.1.0.zip"))
+	tg.mustNotExist(filepath.Join(gopath, "src/mod/github.com/pkg"))
+
+	// With incorrect sum, sync (which must download) fails.
+	// Even if the incorrect sum is in the old legacy go.modverify file.
+	tg.must(ioutil.WriteFile(tg.path("x/go.sum"), []byte(`
+`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/go.modverify"), []byte(`rsc.io/quote v1.1.0 h1:a3YaZoizPtXyv6ZsJ74oo2L4/bwOSTKMY7MAyo4O/1c=
+`), 0666))
+	tg.runFail("mod", "-sync") // downloads pkg/errors
+	tg.grepStderr("checksum mismatch", "must detect mismatch")
+	tg.mustNotExist(filepath.Join(gopath, "src/mod/cache/download/rsc.io/quote/@v/v1.1.0.zip"))
+	tg.mustNotExist(filepath.Join(gopath, "src/mod/github.com/pkg"))
+
+	// With corrected sum, sync works.
+	tg.must(ioutil.WriteFile(tg.path("x/go.modverify"), []byte(`rsc.io/quote v1.1.0 h1:a3YaZoizPtXyv6ZsJ74oo2L4/bwOSTKMY7MAyo4O/0c=
+`), 0666))
+	tg.run("mod", "-sync")
+	tg.mustExist(filepath.Join(gopath, "src/mod/cache/download/rsc.io/quote/@v/v1.1.0.zip"))
+	tg.mustExist(filepath.Join(gopath, "src/mod/rsc.io"))
+	tg.mustNotExist(tg.path("x/go.modverify")) // moved into go.sum
+
+	// Sync should have added sum for go.mod.
+	data, err := ioutil.ReadFile(tg.path("x/go.sum"))
+	if !strings.Contains(string(data), "\nrsc.io/quote v1.1.0/go.mod ") {
+		t.Fatalf("cannot find go.mod hash in go.sum: %v\n%s", err, data)
+	}
+
+	// Verify should work too.
+	tg.run("mod", "-verify")
+
+	// Even the most basic attempt to load the module graph should detect incorrect go.mod files.
+	tg.run("mod", "-graph") // loads module graph, is OK
+	tg.must(ioutil.WriteFile(tg.path("x/go.sum"), []byte(`rsc.io/quote v1.1.0 a3YaZoizPtXyv6ZsJ74oo2L4/bwOSTKMY7MAyo4O/1c=
+rsc.io/quote v1.1.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl1=
+`), 0666))
+	tg.runFail("mod", "-graph") // loads module graph, fails (even though sum is in old go.modverify file)
+	tg.grepStderr("go.mod: checksum mismatch", "must detect mismatch")
+
+	// go.sum should be created and updated automatically.
+	tg.must(os.Remove(tg.path("x/go.sum")))
+	tg.run("mod", "-graph")
+	tg.mustExist(tg.path("x/go.sum"))
+	data, err = ioutil.ReadFile(tg.path("x/go.sum"))
+	if !strings.Contains(string(data), " v1.1.0/go.mod ") {
+		t.Fatalf("cannot find go.mod hash in go.sum: %v\n%s", err, data)
+	}
+	if strings.Contains(string(data), " v1.1.0 ") {
+		t.Fatalf("unexpected module tree hash in go.sum: %v\n%s", err, data)
+	}
+	tg.run("mod", "-sync")
+	data, err = ioutil.ReadFile(tg.path("x/go.sum"))
+	if !strings.Contains(string(data), " v1.1.0/go.mod ") {
+		t.Fatalf("cannot find go.mod hash in go.sum: %v\n%s", err, data)
+	}
+	if !strings.Contains(string(data), " v1.1.0 ") {
+		t.Fatalf("cannot find module tree hash in go.sum: %v\n%s", err, data)
+	}
+
+	tg.must(os.Remove(filepath.Join(gopath, "src/mod/cache/download/rsc.io/quote/@v/v1.1.0.ziphash")))
+	tg.run("mod", "-sync") // ignores missing ziphash file for ordinary go.sum validation
+
+	tg.runFail("mod", "-verify") // explicit verify fails with missing ziphash
+
+	tg.run("mod", "-droprequire", "rsc.io/quote")
+	tg.run("list", "rsc.io/quote/buggy")
+	data, err = ioutil.ReadFile(tg.path("x/go.sum"))
+	if strings.Contains(string(data), "buggy") {
+		t.Fatalf("did not expect buggy in go.sum:\n%s", data)
+	}
+	if !strings.Contains(string(data), "rsc.io/quote v1.5.2/go.mod") {
+		t.Fatalf("did expect rsc.io/quote go.mod in go.sum:\n%s", data)
+	}
+
+	tg.run("mod", "-droprequire", "rsc.io/quote")
+	tg.runFail("list", "rsc.io/quote/buggy/foo")
+	data, err = ioutil.ReadFile(tg.path("x/go.sum"))
+	if strings.Contains(string(data), "buggy") {
+		t.Fatalf("did not expect buggy in go.sum:\n%s", data)
+	}
+	if !strings.Contains(string(data), "rsc.io/quote v1.5.2/go.mod") {
+		t.Fatalf("did expect rsc.io/quote go.mod in go.sum:\n%s", data)
+	}
+
+	tg.run("mod", "-droprequire", "rsc.io/quote")
+	tg.runFail("list", "rsc.io/quote/morebuggy")
+	if strings.Contains(string(data), "morebuggy") {
+		t.Fatalf("did not expect morebuggy in go.sum:\n%s", data)
+	}
+	if !strings.Contains(string(data), "rsc.io/quote v1.5.2/go.mod") {
+		t.Fatalf("did expect rsc.io/quote go.mod in go.sum:\n%s", data)
+	}
+}
+
+func TestModFileProxy(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv("GOPATH", tg.path("gp1"))
+
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/main.go"), []byte(`package x; import _ "rsc.io/quote"`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`module x
+		require rsc.io/quote v1.5.1`), 0666))
+	tg.cd(tg.path("x"))
+	tg.run("list", "all")
+	tg.run("list", "-getmode=local", "all")
+	tg.mustExist(tg.path("gp1/src/mod/cache/download/rsc.io/quote/@v/list"))
+
+	// @v/list should contain version list.
+	data, err := ioutil.ReadFile(tg.path("gp1/src/mod/cache/download/rsc.io/quote/@v/list"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !strings.Contains(string(data), "v1.5.1\n") {
+		t.Fatalf("cannot find v1.5.1 in @v/list:\n%s", data)
+	}
+
+	tg.setenv("GOPROXY", "file:///nonexist")
+	tg.run("list", "-getmode=local", "all")
+
+	tg.setenv("GOPATH", tg.path("gp2"))
+	tg.runFail("list", "-getmode=local", "all")
+	tg.runFail("list", "all") // because GOPROXY is bogus
+
+	tg.setenv("GOPROXY", "file://"+filepath.ToSlash(tg.path("gp1/src/mod/cache/download")))
+	tg.runFail("list", "-getmode=local", "all")
+	tg.run("list", "all")
+	tg.mustExist(tg.path("gp2/src/mod/cache/download/rsc.io/quote/@v/list"))
+}
+
+func TestModVendorNoDeps(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/main.go"), []byte(`package x`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`module x`), 0666))
+	tg.cd(tg.path("x"))
+	tg.run("mod", "-vendor")
+	tg.grepStderr("go: no dependencies to vendor", "print vendor info")
+}
+
+func TestModVersionNoModule(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.cd(tg.path("."))
+	tg.run("version")
+}
+
+func TestModImportDomainRoot(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv("GOPATH", tg.path("."))
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/main.go"), []byte(`
+		package x
+		import _ "example.com"`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte("module x"), 0666))
+	tg.cd(tg.path("x"))
+	tg.run("build")
+}
+
+func TestModSyncPrintJson(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	tg.setenv("GOPATH", tg.path("."))
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/main.go"), []byte(`
+		package x
+		import "rsc.io/quote"
+		func main() {
+			_ = mux.NewRouter()
+		}`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte("module x"), 0666))
+	tg.cd(tg.path("x"))
+	tg.run("mod", "-sync", "-json")
+	count := tg.grepCountBoth(`"Path": "rsc.io/quote",`)
+	if count != 1 {
+		t.Fatal("produces duplicate imports")
+	}
+	// test quoted module path
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+		require (
+			"rsc.io/sampler" v1.3.0
+			"rsc.io/quote" v1.5.2
+	)`), 0666))
+	tg.run("mod", "-sync", "-json")
+	count = tg.grepCountBoth(`"Path": "rsc.io/quote",`)
+	if count != 1 {
+		t.Fatal("produces duplicate imports")
+	}
+}
+
+func TestModMultiVersion(t *testing.T) {
+	tg := testGoModules(t)
+	defer tg.cleanup()
+
+	checkModules := func(dirs ...string) {
+		t.Helper()
+		tg.run("list", "-deps", "-f", "{{.ImportPath}}: {{.Dir}}")
+		for _, line := range strings.Split(tg.getStdout(), "\n") {
+			line = strings.Replace(line, `\`, `/`, -1) // windows!
+			if strings.HasPrefix(line, "rsc.io/quote: ") {
+				if strings.Contains(line, "/src/mod/") {
+					t.Fatalf("rsc.io/quote should not be from module cache: %v", line)
+				}
+			} else if strings.Contains(line, "rsc.io/quote") {
+				if !strings.Contains(line, "/src/mod/") {
+					t.Fatalf("rsc.io/quote/* should be from module cache: %v", line)
+				}
+			}
+		}
+	}
+
+	tg.extract("testdata/mod/rsc.io_quote_v1.5.2.txt")
+	checkModules()
+
+	// These are git checkouts from rsc.io/quote not downlaoded modules.
+	// As such they contain extra files like spurious pieces of other modules.
+	tg.extract("testdata/rsc.io_quote_0d003b9.txt") // wraps v2
+	checkModules()
+
+	tg.extract("testdata/rsc.io_quote_b44a0b1.txt") // adds go.mod
+	checkModules()
+
+	tg.extract("testdata/rsc.io_quote_fe488b8.txt") // adds broken v3 subdirectory
+	tg.run("list", "./...")                         // should ignore v3 because v3/go.mod exists
+	checkModules()
+
+	tg.extract("testdata/rsc.io_quote_a91498b.txt") // wraps v3
+	checkModules()                                  // looks up v3 from internet, not v3 subdirectory
+
+	tg.extract("testdata/rsc.io_quote_5d9f230.txt") // adds go.mod
+	checkModules()                                  // knows which v3 to use (still needs download from internet, cached from last step)
+}
diff --git a/src/cmd/go/proxy_test.go b/src/cmd/go/proxy_test.go
new file mode 100644
index 0000000..28b2ac1
--- /dev/null
+++ b/src/cmd/go/proxy_test.go
@@ -0,0 +1,234 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main_test
+
+import (
+	"archive/zip"
+	"bytes"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+
+	"cmd/go/internal/modfetch"
+	"cmd/go/internal/modfetch/codehost"
+	"cmd/go/internal/module"
+	"cmd/go/internal/par"
+	"cmd/go/internal/semver"
+	"cmd/go/internal/txtar"
+)
+
+var (
+	proxyAddr = flag.String("proxy", "", "run proxy on this network address instead of running any tests")
+	proxyURL  string
+)
+
+var proxyOnce sync.Once
+
+// StartProxy starts the Go module proxy running on *proxyAddr (like "localhost:1234")
+// and sets proxyURL to the GOPROXY setting to use to access the proxy.
+// Subsequent calls are no-ops.
+//
+// The proxy serves from testdata/mod. See testdata/mod/README.
+func StartProxy() {
+	proxyOnce.Do(func() {
+		fmt.Fprintf(os.Stderr, "go test proxy starting\n")
+		readModList()
+		addr := *proxyAddr
+		if addr == "" {
+			addr = "localhost:0"
+		}
+		l, err := net.Listen("tcp", addr)
+		if err != nil {
+			log.Fatal(err)
+		}
+		*proxyAddr = l.Addr().String()
+		proxyURL = "http://" + *proxyAddr + "/mod"
+		fmt.Fprintf(os.Stderr, "go test proxy running at GOPROXY=%s\n", proxyURL)
+		go func() {
+			log.Fatalf("go proxy: http.Serve: %v", http.Serve(l, http.HandlerFunc(proxyHandler)))
+		}()
+	})
+}
+
+var modList []module.Version
+
+func readModList() {
+	infos, err := ioutil.ReadDir("testdata/mod")
+	if err != nil {
+		log.Fatal(err)
+	}
+	for _, info := range infos {
+		name := info.Name()
+		if !strings.HasSuffix(name, ".txt") {
+			continue
+		}
+		name = strings.TrimSuffix(name, ".txt")
+		i := strings.LastIndex(name, "_v")
+		if i < 0 {
+			continue
+		}
+		path := strings.Replace(name[:i], "_", "/", -1)
+		vers := name[i+1:]
+		modList = append(modList, module.Version{Path: path, Version: vers})
+	}
+}
+
+var zipCache par.Cache
+
+// proxyHandler serves the Go module proxy protocol.
+// See the proxy section of https://research.swtch.com/vgo-module.
+func proxyHandler(w http.ResponseWriter, r *http.Request) {
+	if !strings.HasPrefix(r.URL.Path, "/mod/") {
+		http.NotFound(w, r)
+		return
+	}
+	path := strings.TrimPrefix(r.URL.Path, "/mod/")
+	i := strings.Index(path, "/@v/")
+	if i < 0 {
+		http.NotFound(w, r)
+		return
+	}
+	path, file := path[:i], path[i+len("/@v/"):]
+	if file == "list" {
+		n := 0
+		for _, m := range modList {
+			if m.Path == path && !modfetch.IsPseudoVersion(m.Version) {
+				if err := module.Check(m.Path, m.Version); err == nil {
+					fmt.Fprintf(w, "%s\n", m.Version)
+					n++
+				}
+			}
+		}
+		if n == 0 {
+			http.NotFound(w, r)
+		}
+		return
+	}
+
+	i = strings.LastIndex(file, ".")
+	if i < 0 {
+		http.NotFound(w, r)
+		return
+	}
+	vers, ext := file[:i], file[i+1:]
+
+	if codehost.AllHex(vers) {
+		var best string
+		// Convert commit hash (only) to known version.
+		// Use latest version in semver priority, to match similar logic
+		// in the repo-based module server (see modfetch.(*codeRepo).convert).
+		for _, m := range modList {
+			if m.Path == path && semver.Compare(best, m.Version) < 0 {
+				var hash string
+				if modfetch.IsPseudoVersion(m.Version) {
+					hash = m.Version[strings.LastIndex(m.Version, "-")+1:]
+				} else {
+					hash = findHash(m)
+				}
+				if strings.HasPrefix(hash, vers) || strings.HasPrefix(vers, hash) {
+					best = m.Version
+				}
+			}
+		}
+		if best != "" {
+			vers = best
+		}
+	}
+
+	a := readArchive(path, vers)
+	if a == nil {
+		http.Error(w, "cannot load archive", 500)
+		return
+	}
+
+	switch ext {
+	case "info", "mod":
+		want := "." + ext
+		for _, f := range a.Files {
+			if f.Name == want {
+				w.Write(f.Data)
+				return
+			}
+		}
+
+	case "zip":
+		type cached struct {
+			zip []byte
+			err error
+		}
+		c := zipCache.Do(a, func() interface{} {
+			var buf bytes.Buffer
+			z := zip.NewWriter(&buf)
+			for _, f := range a.Files {
+				if strings.HasPrefix(f.Name, ".") {
+					continue
+				}
+				zf, err := z.Create(path + "@" + vers + "/" + f.Name)
+				if err != nil {
+					return cached{nil, err}
+				}
+				if _, err := zf.Write(f.Data); err != nil {
+					return cached{nil, err}
+				}
+			}
+			if err := z.Close(); err != nil {
+				return cached{nil, err}
+			}
+			return cached{buf.Bytes(), nil}
+		}).(cached)
+
+		if c.err != nil {
+			http.Error(w, c.err.Error(), 500)
+			return
+		}
+		w.Write(c.zip)
+		return
+
+	}
+	http.NotFound(w, r)
+}
+
+func findHash(m module.Version) string {
+	a := readArchive(m.Path, m.Version)
+	if a == nil {
+		return ""
+	}
+	var data []byte
+	for _, f := range a.Files {
+		if f.Name == ".info" {
+			data = f.Data
+			break
+		}
+	}
+	var info struct{ Short string }
+	json.Unmarshal(data, &info)
+	return info.Short
+}
+
+var archiveCache par.Cache
+
+func readArchive(path, vers string) *txtar.Archive {
+	prefix := strings.Replace(path, "/", "_", -1)
+	name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+vers+".txt")
+	a := archiveCache.Do(name, func() interface{} {
+		a, err := txtar.ParseFile(name)
+		if err != nil {
+			if !os.IsNotExist(err) {
+				fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
+			}
+			a = nil
+		}
+		return a
+	}).(*txtar.Archive)
+	return a
+}
diff --git a/src/cmd/go/testdata/addmod.go b/src/cmd/go/testdata/addmod.go
new file mode 100644
index 0000000..16dca0e
--- /dev/null
+++ b/src/cmd/go/testdata/addmod.go
@@ -0,0 +1,154 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Addmod adds a module as a txtar archive to the testdata/mod directory.
+//
+// Usage:
+//
+//	go run addmod.go path@version...
+//
+// It should only be used for very small modules - we do not want to check
+// very large files into testdata/mod.
+//
+// It is acceptable to edit the archive afterward to remove or shorten files.
+// See mod/README for more information.
+//
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+
+	"../internal/txtar"
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: go run addmod.go path@version...\n")
+	os.Exit(2)
+}
+
+var tmpdir string
+
+func fatalf(format string, args ...interface{}) {
+	os.RemoveAll(tmpdir)
+	log.Fatalf(format, args...)
+}
+
+const goCmd = "vgo"
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	if flag.NArg() == 0 {
+		usage()
+	}
+
+	log.SetPrefix("addmod: ")
+	log.SetFlags(0)
+
+	var err error
+	tmpdir, err = ioutil.TempDir("", "addmod-")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	run := func(command string, args ...string) string {
+		cmd := exec.Command(command, args...)
+		cmd.Dir = tmpdir
+		var stderr bytes.Buffer
+		cmd.Stderr = &stderr
+		out, err := cmd.Output()
+		if err != nil {
+			fatalf("%s %s: %v\n%s", command, strings.Join(args, " "), err, stderr.Bytes())
+		}
+		return string(out)
+	}
+
+	gopath := strings.TrimSpace(run("go", "env", "GOPATH"))
+	if gopath == "" {
+		fatalf("cannot find GOPATH")
+	}
+
+	exitCode := 0
+	for _, arg := range flag.Args() {
+		if err := ioutil.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module m\n"), 0666); err != nil {
+			fatalf("%v", err)
+		}
+		run(goCmd, "get", "-d", arg)
+		path := arg
+		if i := strings.Index(path, "@"); i >= 0 {
+			path = path[:i]
+		}
+		out := run(goCmd, "list", "-m", "-f={{.Path}} {{.Version}} {{.Dir}}", path)
+		f := strings.Fields(out)
+		if len(f) != 3 {
+			log.Printf("go list -m %s: unexpected output %q", arg, out)
+			exitCode = 1
+			continue
+		}
+		path, vers, dir := f[0], f[1], f[2]
+		mod, err := ioutil.ReadFile(filepath.Join(gopath, "src/mod/cache/download", path, "@v", vers+".mod"))
+		if err != nil {
+			log.Printf("%s: %v", arg, err)
+			exitCode = 1
+			continue
+		}
+		info, err := ioutil.ReadFile(filepath.Join(gopath, "src/mod/cache/download", path, "@v", vers+".info"))
+		if err != nil {
+			log.Printf("%s: %v", arg, err)
+			exitCode = 1
+			continue
+		}
+
+		a := new(txtar.Archive)
+		title := arg
+		if !strings.Contains(arg, "@") {
+			title += "@" + vers
+		}
+		a.Comment = []byte(fmt.Sprintf("module %s\n\n", title))
+		a.Files = []txtar.File{
+			{Name: ".mod", Data: mod},
+			{Name: ".info", Data: info},
+		}
+		dir = filepath.Clean(dir)
+		err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+			if !info.Mode().IsRegular() {
+				return nil
+			}
+			name := info.Name()
+			if name == "go.mod" || strings.HasSuffix(name, ".go") {
+				data, err := ioutil.ReadFile(path)
+				if err != nil {
+					return err
+				}
+				a.Files = append(a.Files, txtar.File{Name: strings.TrimPrefix(path, dir+string(filepath.Separator)), Data: data})
+			}
+			return nil
+		})
+		if err != nil {
+			log.Printf("%s: %v", arg, err)
+			exitCode = 1
+			continue
+		}
+
+		data := txtar.Format(a)
+		target := filepath.Join("mod", strings.Replace(path, "/", "_", -1)+"_"+vers+".txt")
+		if err := ioutil.WriteFile(target, data, 0666); err != nil {
+			log.Printf("%s: %v", arg, err)
+			exitCode = 1
+			continue
+		}
+	}
+	os.RemoveAll(tmpdir)
+	os.Exit(exitCode)
+}
diff --git a/src/cmd/go/testdata/mod/README b/src/cmd/go/testdata/mod/README
new file mode 100644
index 0000000..43ddf77
--- /dev/null
+++ b/src/cmd/go/testdata/mod/README
@@ -0,0 +1,36 @@
+This directory holds Go modules served by a Go module proxy
+that runs on localhost during tests, both to make tests avoid
+requiring specific network servers and also to make them 
+significantly faster.
+
+A small go get'able test module can be added here by running
+
+	cd cmd/go/testdata
+	go run addmod.go path@vers
+
+where path and vers are the module path and version to add here.
+
+For interactive experimentation using this set of modules, run:
+
+	cd cmd/go
+	go test -proxy=localhost:1234 &
+	export GOPROXY=http://localhost:1234/mod
+
+and then run go commands as usual.
+
+Modules saved to this directory should be small: a few kilobytes at most.
+It is acceptable to edit the archives created by addmod.go to remove
+or shorten files. It is also acceptable to write module archives by hand: 
+they need not be backed by some public git repo.
+
+Each module archive is named path_vers.txt, where slashes in path
+have been replaced with underscores. The archive must contain
+two files ".info" and ".mod", to be served as the info and mod files
+in the proxy protocol (see https://research.swtch.com/vgo-module).
+The remaining files are served as the content of the module zip file.
+The path@vers prefix required of files in the zip file is added
+automatically by the proxy: the files in the archive have names without
+the prefix, like plain "go.mod", "x.go", and so on.
+
+See ../addmod.go and ../savedir.go for tools to generate txtar files,
+although again it is also fine to write them by hand.
diff --git a/src/cmd/go/testdata/mod/example.com_v1.0.0.txt b/src/cmd/go/testdata/mod/example.com_v1.0.0.txt
new file mode 100644
index 0000000..263287d
--- /dev/null
+++ b/src/cmd/go/testdata/mod/example.com_v1.0.0.txt
@@ -0,0 +1,9 @@
+Written by hand.
+Test case for module at root of domain.
+
+-- .mod --
+module example.com
+-- .info --
+{"Version": "v1.0.0"}
+-- x.go --
+package x
diff --git a/src/cmd/go/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt b/src/cmd/go/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt
new file mode 100644
index 0000000..e03b3ce
--- /dev/null
+++ b/src/cmd/go/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt
@@ -0,0 +1,45 @@
+written by hand - just enough to compile rsc.io/sampler, rsc.io/quote
+
+-- .mod --
+module golang.org/x/text
+-- .info --
+{"Version":"v0.0.0-20170915032832-14c0d48ead0c","Name":"v0.0.0-20170915032832-14c0d48ead0c","Short":"14c0d48ead0c","Time":"2017-09-15T03:28:32Z"}
+-- go.mod --
+module golang.org/x/text
+-- language/lang.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is a tiny version of golang.org/x/text.
+
+package language
+
+import "strings"
+
+type Tag string
+
+func Make(s string) Tag { return Tag(s) }
+
+func (t Tag) String() string { return string(t) }
+
+func NewMatcher(tags []Tag) Matcher { return &matcher{tags} }
+
+type Matcher interface {
+	Match(...Tag) (Tag, int, int)
+}
+
+type matcher struct {
+	tags []Tag
+}
+
+func (m *matcher) Match(prefs ...Tag) (Tag, int, int) {
+	for _, pref := range prefs {
+		for _, tag := range m.tags {
+			if tag == pref || strings.HasPrefix(string(pref), string(tag+"-")) || strings.HasPrefix(string(tag), string(pref+"-")) {
+				return tag, 0, 0
+			}
+		}
+	}
+	return m.tags[0], 0, 0
+}
diff --git a/src/cmd/go/testdata/mod/golang.org_x_text_v0.3.0.txt b/src/cmd/go/testdata/mod/golang.org_x_text_v0.3.0.txt
new file mode 100644
index 0000000..6932642
--- /dev/null
+++ b/src/cmd/go/testdata/mod/golang.org_x_text_v0.3.0.txt
@@ -0,0 +1,45 @@
+written by hand - just enough to compile rsc.io/sampler, rsc.io/quote
+
+-- .mod --
+module golang.org/x/text
+-- .info --
+{"Version":"v0.3.0","Name":"","Short":"","Time":"2017-09-16T03:28:32Z"}
+-- go.mod --
+module golang.org/x/text
+-- language/lang.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is a tiny version of golang.org/x/text.
+
+package language
+
+import "strings"
+
+type Tag string
+
+func Make(s string) Tag { return Tag(s) }
+
+func (t Tag) String() string { return string(t) }
+
+func NewMatcher(tags []Tag) Matcher { return &matcher{tags} }
+
+type Matcher interface {
+	Match(...Tag) (Tag, int, int)
+}
+
+type matcher struct {
+	tags []Tag
+}
+
+func (m *matcher) Match(prefs ...Tag) (Tag, int, int) {
+	for _, pref := range prefs {
+		for _, tag := range m.tags {
+			if tag == pref || strings.HasPrefix(string(pref), string(tag+"-")) || strings.HasPrefix(string(tag), string(pref+"-")) {
+				return tag, 0, 0
+			}
+		}
+	}
+	return m.tags[0], 0, 0
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005133-e7a685a342c0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005133-e7a685a342c0.txt
new file mode 100644
index 0000000..8ae173e
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005133-e7a685a342c0.txt
@@ -0,0 +1,60 @@
+rsc.io/quote@e7a685a342
+
+-- .mod --
+module "rsc.io/quote"
+-- .info --
+{"Version":"v0.0.0-20180214005133-e7a685a342c0","Name":"e7a685a342c001acc3eb7f5eafa82980480042c7","Short":"e7a685a342c0","Time":"2018-02-14T00:51:33Z"}
+-- go.mod --
+module "rsc.io/quote"
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+// Hello returns a greeting.
+func Hello() string {
+	return "Hello, world."
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory. Share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005840-23179ee8a569.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005840-23179ee8a569.txt
new file mode 100644
index 0000000..bc626ba
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180214005840-23179ee8a569.txt
@@ -0,0 +1,86 @@
+rsc.io/quote@v0.0.0-20180214005840-23179ee8a569
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v0.0.0-20180214005840-23179ee8a569","Name":"23179ee8a569bb05d896ae05c6503ec69a19f99f","Short":"23179ee8a569","Time":"2018-02-14T00:58:40Z"}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func Hello() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180628003336-dd9747d19b04.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180628003336-dd9747d19b04.txt
new file mode 100644
index 0000000..bbc8097
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180628003336-dd9747d19b04.txt
@@ -0,0 +1,100 @@
+rsc.io/quote@dd9747d
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v0.0.0-20180628003336-dd9747d19b04","Name":"dd9747d19b041365fbddf0399ddba6bff5eb1b3e","Short":"dd9747d19b04","Time":"2018-06-28T00:33:36Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+AN EVEN WORSE CHANGE!
+
+// Hello returns a greeting.
+func Hello() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt
new file mode 100644
index 0000000..e461ed4
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt
@@ -0,0 +1,86 @@
+rsc.io/quote@v2.0.0
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v0.0.0-20180709153244-fd906ed3b100","Name":"fd906ed3b100e47181ffa9ec36d82294525c9109","Short":"fd906ed3b100","Time":"2018-07-09T15:32:44Z"}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV2() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func GlassV2() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func GoV2() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func OptV2() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709160352-0d003b9c4bfa.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709160352-0d003b9c4bfa.txt
new file mode 100644
index 0000000..c1d511f
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709160352-0d003b9c4bfa.txt
@@ -0,0 +1,98 @@
+rsc.io/quote@v0.0.0-20180709160352-0d003b9c4bfa
+
+-- .mod --
+module rsc.io/quote
+
+require rsc.io/sampler v1.3.0
+-- .info --
+{"Version":"v0.0.0-20180709160352-0d003b9c4bfa","Name":"0d003b9c4bfac881641be8eb1598b782a467a97f","Short":"0d003b9c4bfa","Time":"2018-07-09T16:03:52Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require rsc.io/sampler v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v2"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV2()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV2()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV2()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV2()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162749-b44a0b17b2d1.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162749-b44a0b17b2d1.txt
new file mode 100644
index 0000000..f7f794d
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162749-b44a0b17b2d1.txt
@@ -0,0 +1,104 @@
+rsc.io/quote@v0.0.0-20180709162749-b44a0b17b2d1
+
+-- .mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v2 v2.0.1
+	rsc.io/sampler v1.3.0
+)
+-- .info --
+{"Version":"v0.0.0-20180709162749-b44a0b17b2d1","Name":"b44a0b17b2d1fe4c98a8d0e7a68c9bf9e762799a","Short":"b44a0b17b2d1","Time":"2018-07-09T16:27:49Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v2 v2.0.1
+	rsc.io/sampler v1.3.0
+)
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v2"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV2()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV2()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV2()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV2()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162816-fe488b867524.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162816-fe488b867524.txt
new file mode 100644
index 0000000..2d5d8b4
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162816-fe488b867524.txt
@@ -0,0 +1,104 @@
+rsc.io/quote@v0.0.0-20180709162816-fe488b867524
+
+-- .mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v2 v2.0.1
+	rsc.io/sampler v1.3.0
+)
+-- .info --
+{"Version":"v0.0.0-20180709162816-fe488b867524","Name":"fe488b867524806e861c3f4f43ae6946a42ca3f1","Short":"fe488b867524","Time":"2018-07-09T16:28:16Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v2 v2.0.1
+	rsc.io/sampler v1.3.0
+)
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v2"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV2()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV2()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV2()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV2()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162918-a91498bed0a7.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162918-a91498bed0a7.txt
new file mode 100644
index 0000000..853a8c2
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180709162918-a91498bed0a7.txt
@@ -0,0 +1,98 @@
+rsc.io/quote@v0.0.0-20180709162918-a91498bed0a7
+
+-- .mod --
+module rsc.io/quote
+
+require rsc.io/sampler v1.3.0
+-- .info --
+{"Version":"v0.0.0-20180709162918-a91498bed0a7","Name":"a91498bed0a73d4bb9c1fb2597925f7883bc40a7","Short":"a91498bed0a7","Time":"2018-07-09T16:29:18Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require rsc.io/sampler v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v3"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV3()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV3()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV3()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV3()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180710144737-5d9f230bcfba.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180710144737-5d9f230bcfba.txt
new file mode 100644
index 0000000..2ebeac3
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v0.0.0-20180710144737-5d9f230bcfba.txt
@@ -0,0 +1,104 @@
+rsc.io/quote@v0.0.0-20180710144737-5d9f230bcfba
+
+-- .mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v3 v3.0.0
+	rsc.io/sampler v1.3.0
+)
+-- .info --
+{"Version":"v0.0.0-20180710144737-5d9f230bcfba","Name":"5d9f230bcfbae514bb6c2215694c2ce7273fc604","Short":"5d9f230bcfba","Time":"2018-07-10T14:47:37Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v3 v3.0.0
+	rsc.io/sampler v1.3.0
+)
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v3"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV3()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV3()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV3()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV3()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.0.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.0.0.txt
new file mode 100644
index 0000000..9a07937
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.0.0.txt
@@ -0,0 +1,35 @@
+rsc.io/quote@v1.0.0
+
+-- .mod --
+module "rsc.io/quote"
+-- .info --
+{"Version":"v1.0.0","Name":"f488df80bcdbd3e5bafdc24ad7d1e79e83edd7e6","Short":"f488df80bcdb","Time":"2018-02-14T00:45:20Z"}
+-- go.mod --
+module "rsc.io/quote"
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+// Hello returns a greeting.
+func Hello() string {
+	return "Hello, world."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.1.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.1.0.txt
new file mode 100644
index 0000000..0c41605
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.1.0.txt
@@ -0,0 +1,48 @@
+rsc.io/quote@v1.1.0
+
+-- .mod --
+module "rsc.io/quote"
+-- .info --
+{"Version":"v1.1.0","Name":"cfd7145f43f92a8d56b4a3dd603795a3291381a9","Short":"cfd7145f43f9","Time":"2018-02-14T00:46:44Z"}
+-- go.mod --
+module "rsc.io/quote"
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+// Hello returns a greeting.
+func Hello() string {
+	return "Hello, world."
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.2.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.2.0.txt
new file mode 100644
index 0000000..e714f0b
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.2.0.txt
@@ -0,0 +1,61 @@
+rsc.io/quote@v1.2.0
+
+-- .mod --
+module "rsc.io/quote"
+-- .info --
+{"Version":"v1.2.0","Name":"d8a3de91045c932a1c71e545308fe97571d6d65c","Short":"d8a3de91045c","Time":"2018-02-14T00:47:51Z"}
+-- go.mod --
+module "rsc.io/quote"
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+// Hello returns a greeting.
+func Hello() string {
+	return "Hello, world."
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+// Go returns a Go proverb.
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory. Share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.2.1.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.2.1.txt
new file mode 100644
index 0000000..89d5191
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.2.1.txt
@@ -0,0 +1,60 @@
+rsc.io/quote@v1.2.1
+
+-- .mod --
+module "rsc.io/quote"
+-- .info --
+{"Version":"v1.2.1","Name":"5c1f03b64ab7aa958798a569a31924655dc41e76","Short":"5c1f03b64ab7","Time":"2018-02-14T00:54:20Z"}
+-- go.mod --
+module "rsc.io/quote"
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+// Hello returns a greeting.
+func Hello() string {
+	return "Hello, world."
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.3.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.3.0.txt
new file mode 100644
index 0000000..d62766c
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.3.0.txt
@@ -0,0 +1,73 @@
+rsc.io/quote@v1.3.0
+
+-- .mod --
+module "rsc.io/quote"
+-- .info --
+{"Version":"v1.3.0","Name":"84de74b35823c1e49634f2262f1a58cfc951ebae","Short":"84de74b35823","Time":"2018-02-14T00:54:53Z"}
+-- go.mod --
+module "rsc.io/quote"
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+// Hello returns a greeting.
+func Hello() string {
+	return "Hello, world."
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.4.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.4.0.txt
new file mode 100644
index 0000000..698ff8d
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.4.0.txt
@@ -0,0 +1,79 @@
+rsc.io/quote@v1.4.0
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.0.0
+-- .info --
+{"Version":"v1.4.0","Name":"19e8b977bd2f437798c2cc2dcfe8a1c0f169481b","Short":"19e8b977bd2f","Time":"2018-02-14T00:56:05Z"}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.0.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func Hello() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.0.txt
new file mode 100644
index 0000000..e7fcdbcc
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.0.txt
@@ -0,0 +1,79 @@
+rsc.io/quote@v1.5.0
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v1.5.0","Name":"3ba1e30dc83bd52c990132b9dfb1688a9d22de13","Short":"3ba1e30dc83b","Time":"2018-02-14T00:58:15Z"}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func Hello() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import "testing"
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.1.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.1.txt
new file mode 100644
index 0000000..eed051b
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.1.txt
@@ -0,0 +1,86 @@
+rsc.io/quote@23179ee8a569
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v1.5.1","Name":"23179ee8a569bb05d896ae05c6503ec69a19f99f","Short":"23179ee8a569","Time":"2018-02-14T00:58:40Z"}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func Hello() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.2.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.2.txt
new file mode 100644
index 0000000..8671f6f
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.2.txt
@@ -0,0 +1,98 @@
+rsc.io/quote@v1.5.2
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v1.5.2","Name":"c4d4236f92427c64bfbcf1cc3f8142ab18f30b22","Short":"c4d4236f9242","Time":"2018-02-14T15:44:20Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func Hello() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.3-pre1.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.3-pre1.txt
new file mode 100644
index 0000000..212ef13
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v1.5.3-pre1.txt
@@ -0,0 +1,100 @@
+rsc.io/quote@v1.5.3-pre1
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v1.5.3-pre1","Name":"2473dfd877c95382420e47686aa9076bf58c79e0","Short":"2473dfd877c9","Time":"2018-06-28T00:32:53Z"}
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// A CHANGE!
+
+// Hello returns a greeting.
+func Hello() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v2.0.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v2.0.0.txt
new file mode 100644
index 0000000..2f687f5
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v2.0.0.txt
@@ -0,0 +1,86 @@
+rsc.io/quote@v2.0.0 && cp mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt mod/rsc.io_quote_v2.0.0.txt
+
+-- .mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- .info --
+{"Version":"v0.0.0-20180709153244-fd906ed3b100","Name":"fd906ed3b100e47181ffa9ec36d82294525c9109","Short":"fd906ed3b100","Time":"2018-07-09T15:32:44Z"}
+-- go.mod --
+module "rsc.io/quote"
+
+require "rsc.io/sampler" v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV2() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func GlassV2() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func GoV2() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func OptV2() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v2_v2.0.1.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v2_v2.0.1.txt
new file mode 100644
index 0000000..d51128c
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v2_v2.0.1.txt
@@ -0,0 +1,86 @@
+rsc.io/quote/v2@v2.0.1
+
+-- .mod --
+module rsc.io/quote/v2
+
+require rsc.io/sampler v1.3.0
+-- .info --
+{"Version":"v2.0.1","Name":"754f68430672776c84704e2d10209a6ec700cd64","Short":"754f68430672","Time":"2018-07-09T16:25:34Z"}
+-- go.mod --
+module rsc.io/quote/v2
+
+require rsc.io/sampler v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV2() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func GlassV2() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func GoV2() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func OptV2() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_quote_v3_v3.0.0.txt b/src/cmd/go/testdata/mod/rsc.io_quote_v3_v3.0.0.txt
new file mode 100644
index 0000000..0afe1f0
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_quote_v3_v3.0.0.txt
@@ -0,0 +1,45 @@
+rsc.io/quote/v3@v3.0.0
+
+-- .mod --
+module rsc.io/quote/v3
+
+require rsc.io/sampler v1.3.0
+
+-- .info --
+{"Version":"v3.0.0","Name":"d88915d7e77ed0fd35d0a022a2f244e2202fd8c8","Short":"d88915d7e77e","Time":"2018-07-09T15:34:46Z"}
+-- go.mod --
+module rsc.io/quote/v3
+
+require rsc.io/sampler v1.3.0
+
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV3() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func GlassV3() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func GoV3() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func OptV3() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_sampler_v1.0.0.txt b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.0.0.txt
new file mode 100644
index 0000000..c4b6a71
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.0.0.txt
@@ -0,0 +1,20 @@
+rsc.io/sampler@v1.0.0
+
+-- .mod --
+module "rsc.io/sampler"
+-- .info --
+{"Version":"v1.0.0","Name":"60bef405c52117ad21d2adb10872b95cf17f8fca","Short":"60bef405c521","Time":"2018-02-13T18:05:54Z"}
+-- go.mod --
+module "rsc.io/sampler"
+-- sampler.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sampler shows simple texts.
+package sampler // import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func Hello() string {
+	return "Hello, world."
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_sampler_v1.2.0.txt b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.2.0.txt
new file mode 100644
index 0000000..98c35fa
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.2.0.txt
@@ -0,0 +1,138 @@
+rsc.io/sampler@v1.2.0
+
+-- .mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- .info --
+{"Version":"v1.2.0","Name":"25f24110b153246056eccc14a3a4cd81afaff586","Short":"25f24110b153","Time":"2018-02-13T18:13:45Z"}
+-- go.mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- hello.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Translations by Google Translate.
+
+package sampler
+
+var hello = newText(`
+
+English: en: Hello, world.
+French: fr: Bonjour le monde.
+Spanish: es: Hola Mundo.
+
+`)
+-- hello_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sampler
+
+import (
+	"testing"
+
+	"golang.org/x/text/language"
+)
+
+var helloTests = []struct {
+	prefs []language.Tag
+	text  string
+}{
+	{
+		[]language.Tag{language.Make("en-US"), language.Make("fr")},
+		"Hello, world.",
+	},
+	{
+		[]language.Tag{language.Make("fr"), language.Make("en-US")},
+		"Bonjour la monde.",
+	},
+}
+
+func TestHello(t *testing.T) {
+	for _, tt := range helloTests {
+		text := Hello(tt.prefs...)
+		if text != tt.text {
+			t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text)
+		}
+	}
+}
+-- sampler.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sampler shows simple texts.
+package sampler // import "rsc.io/sampler"
+
+import (
+	"os"
+	"strings"
+
+	"golang.org/x/text/language"
+)
+
+// DefaultUserPrefs returns the default user language preferences.
+// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment
+// variables, in that order.
+func DefaultUserPrefs() []language.Tag {
+	var prefs []language.Tag
+	for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
+		if env := os.Getenv(k); env != "" {
+			prefs = append(prefs, language.Make(env))
+		}
+	}
+	return prefs
+}
+
+// Hello returns a localized greeting.
+// If no prefs are given, Hello uses DefaultUserPrefs.
+func Hello(prefs ...language.Tag) string {
+	if len(prefs) == 0 {
+		prefs = DefaultUserPrefs()
+	}
+	return hello.find(prefs)
+}
+
+// A text is a localized text.
+type text struct {
+	byTag   map[string]string
+	matcher language.Matcher
+}
+
+// newText creates a new localized text, given a list of translations.
+func newText(s string) *text {
+	t := &text{
+		byTag: make(map[string]string),
+	}
+	var tags []language.Tag
+	for _, line := range strings.Split(s, "\n") {
+		line = strings.TrimSpace(line)
+		if line == "" {
+			continue
+		}
+		f := strings.Split(line, ": ")
+		if len(f) != 3 {
+			continue
+		}
+		tag := language.Make(f[1])
+		tags = append(tags, tag)
+		t.byTag[tag.String()] = f[2]
+	}
+	t.matcher = language.NewMatcher(tags)
+	return t
+}
+
+// find finds the text to use for the given language tag preferences.
+func (t *text) find(prefs []language.Tag) string {
+	tag, _, _ := t.matcher.Match(prefs...)
+	s := t.byTag[tag.String()]
+	if strings.HasPrefix(s, "RTL ") {
+		s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E"
+	}
+	return s
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_sampler_v1.2.1.txt b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.2.1.txt
new file mode 100644
index 0000000..00b71bf
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.2.1.txt
@@ -0,0 +1,134 @@
+generated by ./addmod.bash rsc.io/sampler@v1.2.1
+
+-- .mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- .info --
+{"Version":"v1.2.1","Name":"cac3af4f8a0ab40054fa6f8d423108a63a1255bb","Short":"cac3af4f8a0a","Time":"2018-02-13T18:16:22Z"}EOF
+-- hello.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Translations by Google Translate.
+
+package sampler
+
+var hello = newText(`
+
+English: en: Hello, world.
+French: fr: Bonjour le monde.
+Spanish: es: Hola Mundo.
+
+`)
+-- hello_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sampler
+
+import (
+	"testing"
+
+	"golang.org/x/text/language"
+)
+
+var helloTests = []struct {
+	prefs []language.Tag
+	text  string
+}{
+	{
+		[]language.Tag{language.Make("en-US"), language.Make("fr")},
+		"Hello, world.",
+	},
+	{
+		[]language.Tag{language.Make("fr"), language.Make("en-US")},
+		"Bonjour le monde.",
+	},
+}
+
+func TestHello(t *testing.T) {
+	for _, tt := range helloTests {
+		text := Hello(tt.prefs...)
+		if text != tt.text {
+			t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text)
+		}
+	}
+}
+-- sampler.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sampler shows simple texts.
+package sampler // import "rsc.io/sampler"
+
+import (
+	"os"
+	"strings"
+
+	"golang.org/x/text/language"
+)
+
+// DefaultUserPrefs returns the default user language preferences.
+// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment
+// variables, in that order.
+func DefaultUserPrefs() []language.Tag {
+	var prefs []language.Tag
+	for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
+		if env := os.Getenv(k); env != "" {
+			prefs = append(prefs, language.Make(env))
+		}
+	}
+	return prefs
+}
+
+// Hello returns a localized greeting.
+// If no prefs are given, Hello uses DefaultUserPrefs.
+func Hello(prefs ...language.Tag) string {
+	if len(prefs) == 0 {
+		prefs = DefaultUserPrefs()
+	}
+	return hello.find(prefs)
+}
+
+// A text is a localized text.
+type text struct {
+	byTag   map[string]string
+	matcher language.Matcher
+}
+
+// newText creates a new localized text, given a list of translations.
+func newText(s string) *text {
+	t := &text{
+		byTag: make(map[string]string),
+	}
+	var tags []language.Tag
+	for _, line := range strings.Split(s, "\n") {
+		line = strings.TrimSpace(line)
+		if line == "" {
+			continue
+		}
+		f := strings.Split(line, ": ")
+		if len(f) != 3 {
+			continue
+		}
+		tag := language.Make(f[1])
+		tags = append(tags, tag)
+		t.byTag[tag.String()] = f[2]
+	}
+	t.matcher = language.NewMatcher(tags)
+	return t
+}
+
+// find finds the text to use for the given language tag preferences.
+func (t *text) find(prefs []language.Tag) string {
+	tag, _, _ := t.matcher.Match(prefs...)
+	s := t.byTag[tag.String()]
+	if strings.HasPrefix(s, "RTL ") {
+		s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E"
+	}
+	return s
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_sampler_v1.3.0.txt b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.3.0.txt
new file mode 100644
index 0000000..000f212
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.3.0.txt
@@ -0,0 +1,201 @@
+rsc.io/sampler@v1.3.0
+
+-- .mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- .info --
+{"Version":"v1.3.0","Name":"0cc034b51e57ed7832d4c67d526f75a900996e5c","Short":"0cc034b51e57","Time":"2018-02-13T19:05:03Z"}
+-- glass.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Translations from Frank da Cruz, Ethan Mollick, and many others.
+// See http://kermitproject.org/utf8.html.
+// http://www.oocities.org/nodotus/hbglass.html
+// https://en.wikipedia.org/wiki/I_Can_Eat_Glass
+
+package sampler
+
+var glass = newText(`
+
+English: en: I can eat glass and it doesn't hurt me.
+French: fr: Je peux manger du verre, ça ne me fait pas mal.
+Spanish: es: Puedo comer vidrio, no me hace daño.
+
+`)
+-- glass_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sampler
+
+import (
+	"testing"
+
+	"golang.org/x/text/language"
+)
+
+var glassTests = []struct {
+	prefs []language.Tag
+	text  string
+}{
+	{
+		[]language.Tag{language.Make("en-US"), language.Make("fr")},
+		"I can eat glass and it doesn't hurt me.",
+	},
+	{
+		[]language.Tag{language.Make("fr"), language.Make("en-US")},
+		"Je peux manger du verre, ça ne me fait pas mal.",
+	},
+}
+
+func TestGlass(t *testing.T) {
+	for _, tt := range glassTests {
+		text := Glass(tt.prefs...)
+		if text != tt.text {
+			t.Errorf("Glass(%v) = %q, want %q", tt.prefs, text, tt.text)
+		}
+	}
+}
+-- go.mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- hello.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Translations by Google Translate.
+
+package sampler
+
+var hello = newText(`
+
+English: en: Hello, world.
+French: fr: Bonjour le monde.
+Spanish: es: Hola Mundo.
+
+`)
+-- hello_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sampler
+
+import (
+	"testing"
+
+	"golang.org/x/text/language"
+)
+
+var helloTests = []struct {
+	prefs []language.Tag
+	text  string
+}{
+	{
+		[]language.Tag{language.Make("en-US"), language.Make("fr")},
+		"Hello, world.",
+	},
+	{
+		[]language.Tag{language.Make("fr"), language.Make("en-US")},
+		"Bonjour le monde.",
+	},
+}
+
+func TestHello(t *testing.T) {
+	for _, tt := range helloTests {
+		text := Hello(tt.prefs...)
+		if text != tt.text {
+			t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text)
+		}
+	}
+}
+-- sampler.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sampler shows simple texts.
+package sampler // import "rsc.io/sampler"
+
+import (
+	"os"
+	"strings"
+
+	"golang.org/x/text/language"
+)
+
+// DefaultUserPrefs returns the default user language preferences.
+// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment
+// variables, in that order.
+func DefaultUserPrefs() []language.Tag {
+	var prefs []language.Tag
+	for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
+		if env := os.Getenv(k); env != "" {
+			prefs = append(prefs, language.Make(env))
+		}
+	}
+	return prefs
+}
+
+// Hello returns a localized greeting.
+// If no prefs are given, Hello uses DefaultUserPrefs.
+func Hello(prefs ...language.Tag) string {
+	if len(prefs) == 0 {
+		prefs = DefaultUserPrefs()
+	}
+	return hello.find(prefs)
+}
+
+// Glass returns a localized silly phrase.
+// If no prefs are given, Glass uses DefaultUserPrefs.
+func Glass(prefs ...language.Tag) string {
+	if len(prefs) == 0 {
+		prefs = DefaultUserPrefs()
+	}
+	return glass.find(prefs)
+}
+
+// A text is a localized text.
+type text struct {
+	byTag   map[string]string
+	matcher language.Matcher
+}
+
+// newText creates a new localized text, given a list of translations.
+func newText(s string) *text {
+	t := &text{
+		byTag: make(map[string]string),
+	}
+	var tags []language.Tag
+	for _, line := range strings.Split(s, "\n") {
+		line = strings.TrimSpace(line)
+		if line == "" {
+			continue
+		}
+		f := strings.Split(line, ": ")
+		if len(f) != 3 {
+			continue
+		}
+		tag := language.Make(f[1])
+		tags = append(tags, tag)
+		t.byTag[tag.String()] = f[2]
+	}
+	t.matcher = language.NewMatcher(tags)
+	return t
+}
+
+// find finds the text to use for the given language tag preferences.
+func (t *text) find(prefs []language.Tag) string {
+	tag, _, _ := t.matcher.Match(prefs...)
+	s := t.byTag[tag.String()]
+	if strings.HasPrefix(s, "RTL ") {
+		s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E"
+	}
+	return s
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_sampler_v1.3.1.txt b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.3.1.txt
new file mode 100644
index 0000000..a293f10
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.3.1.txt
@@ -0,0 +1,201 @@
+rsc.io/sampler@v1.3.1
+
+-- .mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- .info --
+{"Version":"v1.3.1","Name":"f545d0289d06e2add4556ea6a15fc4938014bf87","Short":"f545d0289d06","Time":"2018-02-14T16:34:12Z"}
+-- glass.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Translations from Frank da Cruz, Ethan Mollick, and many others.
+// See http://kermitproject.org/utf8.html.
+// http://www.oocities.org/nodotus/hbglass.html
+// https://en.wikipedia.org/wiki/I_Can_Eat_Glass
+
+package sampler
+
+var glass = newText(`
+
+English: en: I can eat glass and it doesn't hurt me.
+French: fr: Je peux manger du verre, ça ne me fait pas mal.
+Spanish: es: Puedo comer vidrio, no me hace daño.
+
+`)
+-- glass_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sampler
+
+import (
+	"testing"
+
+	"golang.org/x/text/language"
+)
+
+var glassTests = []struct {
+	prefs []language.Tag
+	text  string
+}{
+	{
+		[]language.Tag{language.Make("en-US"), language.Make("fr")},
+		"I can eat glass and it doesn't hurt me.",
+	},
+	{
+		[]language.Tag{language.Make("fr"), language.Make("en-US")},
+		"Je peux manger du verre, ça ne me fait pas mal.",
+	},
+}
+
+func TestGlass(t *testing.T) {
+	for _, tt := range glassTests {
+		text := Glass(tt.prefs...)
+		if text != tt.text {
+			t.Errorf("Glass(%v) = %q, want %q", tt.prefs, text, tt.text)
+		}
+	}
+}
+-- go.mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- hello.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Translations by Google Translate.
+
+package sampler
+
+var hello = newText(`
+
+English: en: Hello, world.
+French: fr: Bonjour le monde.
+Spanish: es: Hola Mundo.
+
+`)
+-- hello_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sampler
+
+import (
+	"testing"
+
+	"golang.org/x/text/language"
+)
+
+var helloTests = []struct {
+	prefs []language.Tag
+	text  string
+}{
+	{
+		[]language.Tag{language.Make("en-US"), language.Make("fr")},
+		"Hello, world.",
+	},
+	{
+		[]language.Tag{language.Make("fr"), language.Make("en-US")},
+		"Bonjour le monde.",
+	},
+}
+
+func TestHello(t *testing.T) {
+	for _, tt := range helloTests {
+		text := Hello(tt.prefs...)
+		if text != tt.text {
+			t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text)
+		}
+	}
+}
+-- sampler.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sampler shows simple texts in a variety of languages.
+package sampler // import "rsc.io/sampler"
+
+import (
+	"os"
+	"strings"
+
+	"golang.org/x/text/language"
+)
+
+// DefaultUserPrefs returns the default user language preferences.
+// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment
+// variables, in that order.
+func DefaultUserPrefs() []language.Tag {
+	var prefs []language.Tag
+	for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
+		if env := os.Getenv(k); env != "" {
+			prefs = append(prefs, language.Make(env))
+		}
+	}
+	return prefs
+}
+
+// Hello returns a localized greeting.
+// If no prefs are given, Hello uses DefaultUserPrefs.
+func Hello(prefs ...language.Tag) string {
+	if len(prefs) == 0 {
+		prefs = DefaultUserPrefs()
+	}
+	return hello.find(prefs)
+}
+
+// Glass returns a localized silly phrase.
+// If no prefs are given, Glass uses DefaultUserPrefs.
+func Glass(prefs ...language.Tag) string {
+	if len(prefs) == 0 {
+		prefs = DefaultUserPrefs()
+	}
+	return glass.find(prefs)
+}
+
+// A text is a localized text.
+type text struct {
+	byTag   map[string]string
+	matcher language.Matcher
+}
+
+// newText creates a new localized text, given a list of translations.
+func newText(s string) *text {
+	t := &text{
+		byTag: make(map[string]string),
+	}
+	var tags []language.Tag
+	for _, line := range strings.Split(s, "\n") {
+		line = strings.TrimSpace(line)
+		if line == "" {
+			continue
+		}
+		f := strings.Split(line, ": ")
+		if len(f) != 3 {
+			continue
+		}
+		tag := language.Make(f[1])
+		tags = append(tags, tag)
+		t.byTag[tag.String()] = f[2]
+	}
+	t.matcher = language.NewMatcher(tags)
+	return t
+}
+
+// find finds the text to use for the given language tag preferences.
+func (t *text) find(prefs []language.Tag) string {
+	tag, _, _ := t.matcher.Match(prefs...)
+	s := t.byTag[tag.String()]
+	if strings.HasPrefix(s, "RTL ") {
+		s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E"
+	}
+	return s
+}
diff --git a/src/cmd/go/testdata/mod/rsc.io_sampler_v1.99.99.txt b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.99.99.txt
new file mode 100644
index 0000000..5036d20
--- /dev/null
+++ b/src/cmd/go/testdata/mod/rsc.io_sampler_v1.99.99.txt
@@ -0,0 +1,140 @@
+rsc.io/sampler@v1.99.99
+
+-- .mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- .info --
+{"Version":"v1.99.99","Name":"732a3c400797d8835f2af34a9561f155bef85435","Short":"732a3c400797","Time":"2018-02-13T22:20:19Z"}
+-- go.mod --
+module "rsc.io/sampler"
+
+require "golang.org/x/text" v0.0.0-20170915032832-14c0d48ead0c
+-- hello.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Translations by Google Translate.
+
+package sampler
+
+var hello = newText(`
+
+English: en: 99 bottles of beer on the wall, 99 bottles of beer, ...
+
+`)
+-- hello_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sampler
+
+import (
+	"testing"
+
+	"golang.org/x/text/language"
+)
+
+var helloTests = []struct {
+	prefs []language.Tag
+	text  string
+}{
+	{
+		[]language.Tag{language.Make("en-US"), language.Make("fr")},
+		"Hello, world.",
+	},
+	{
+		[]language.Tag{language.Make("fr"), language.Make("en-US")},
+		"Bonjour le monde.",
+	},
+}
+
+func TestHello(t *testing.T) {
+	for _, tt := range helloTests {
+		text := Hello(tt.prefs...)
+		if text != tt.text {
+			t.Errorf("Hello(%v) = %q, want %q", tt.prefs, text, tt.text)
+		}
+	}
+}
+-- sampler.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package sampler shows simple texts.
+package sampler // import "rsc.io/sampler"
+
+import (
+	"os"
+	"strings"
+
+	"golang.org/x/text/language"
+)
+
+// DefaultUserPrefs returns the default user language preferences.
+// It consults the $LC_ALL, $LC_MESSAGES, and $LANG environment
+// variables, in that order.
+func DefaultUserPrefs() []language.Tag {
+	var prefs []language.Tag
+	for _, k := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
+		if env := os.Getenv(k); env != "" {
+			prefs = append(prefs, language.Make(env))
+		}
+	}
+	return prefs
+}
+
+// Hello returns a localized greeting.
+// If no prefs are given, Hello uses DefaultUserPrefs.
+func Hello(prefs ...language.Tag) string {
+	if len(prefs) == 0 {
+		prefs = DefaultUserPrefs()
+	}
+	return hello.find(prefs)
+}
+
+func Glass() string {
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// A text is a localized text.
+type text struct {
+	byTag   map[string]string
+	matcher language.Matcher
+}
+
+// newText creates a new localized text, given a list of translations.
+func newText(s string) *text {
+	t := &text{
+		byTag: make(map[string]string),
+	}
+	var tags []language.Tag
+	for _, line := range strings.Split(s, "\n") {
+		line = strings.TrimSpace(line)
+		if line == "" {
+			continue
+		}
+		f := strings.Split(line, ": ")
+		if len(f) != 3 {
+			continue
+		}
+		tag := language.Make(f[1])
+		tags = append(tags, tag)
+		t.byTag[tag.String()] = f[2]
+	}
+	t.matcher = language.NewMatcher(tags)
+	return t
+}
+
+// find finds the text to use for the given language tag preferences.
+func (t *text) find(prefs []language.Tag) string {
+	tag, _, _ := t.matcher.Match(prefs...)
+	s := t.byTag[tag.String()]
+	if strings.HasPrefix(s, "RTL ") {
+		s = "\u200F" + strings.TrimPrefix(s, "RTL ") + "\u200E"
+	}
+	return s
+}
diff --git a/src/cmd/go/testdata/rsc.io_quote_0d003b9.txt b/src/cmd/go/testdata/rsc.io_quote_0d003b9.txt
new file mode 100644
index 0000000..9b2817f
--- /dev/null
+++ b/src/cmd/go/testdata/rsc.io_quote_0d003b9.txt
@@ -0,0 +1,166 @@
+generated by: go run savedir.go .
+
+-- LICENSE --
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-- README.md --
+This package collects pithy sayings.
+
+It's part of a demonstration of
+[package versioning in Go](https://research.swtch.com/vgo1).
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require rsc.io/sampler v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v2"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV2()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV2()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV2()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV2()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
+-- v3/go.mod --
+module rsc.io/quote/v3
+
+require rsc.io/sampler v1.3.0
+
+-- v3/go.sum --
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
+rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+-- v3/quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV3() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func GlassV3() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func GoV3() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func OptV3() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
diff --git a/src/cmd/go/testdata/rsc.io_quote_5d9f230.txt b/src/cmd/go/testdata/rsc.io_quote_5d9f230.txt
new file mode 100644
index 0000000..8f8bdc4
--- /dev/null
+++ b/src/cmd/go/testdata/rsc.io_quote_5d9f230.txt
@@ -0,0 +1,151 @@
+generated by: go run savedir.go .
+
+-- LICENSE --
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-- README.md --
+This package collects pithy sayings.
+
+It's part of a demonstration of
+[package versioning in Go](https://research.swtch.com/vgo1).
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v3 v3.0.0
+	rsc.io/sampler v1.3.0
+)
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v3"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV3()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV3()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV3()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV3()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
+-- v3/go.mod --
+module rsc.io/quote/v3
+
+require rsc.io/sampler v1.3.0
+
+-- v3/go.sum --
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
+rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+-- v3/quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV3() string {
+	return sampler.Hello()
diff --git a/src/cmd/go/testdata/rsc.io_quote_a91498b.txt b/src/cmd/go/testdata/rsc.io_quote_a91498b.txt
new file mode 100644
index 0000000..8567243
--- /dev/null
+++ b/src/cmd/go/testdata/rsc.io_quote_a91498b.txt
@@ -0,0 +1,148 @@
+generated by: go run savedir.go .
+
+-- LICENSE --
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-- README.md --
+This package collects pithy sayings.
+
+It's part of a demonstration of
+[package versioning in Go](https://research.swtch.com/vgo1).
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require rsc.io/sampler v1.3.0
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v3"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV3()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV3()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV3()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV3()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
+-- v3/go.mod --
+module rsc.io/quote/v3
+
+require rsc.io/sampler v1.3.0
+
+-- v3/go.sum --
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
+rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+-- v3/quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV3() string {
+	return sampler.Hello()
diff --git a/src/cmd/go/testdata/rsc.io_quote_b44a0b1.txt b/src/cmd/go/testdata/rsc.io_quote_b44a0b1.txt
new file mode 100644
index 0000000..37f298f
--- /dev/null
+++ b/src/cmd/go/testdata/rsc.io_quote_b44a0b1.txt
@@ -0,0 +1,169 @@
+generated by: go run savedir.go .
+
+-- LICENSE --
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-- README.md --
+This package collects pithy sayings.
+
+It's part of a demonstration of
+[package versioning in Go](https://research.swtch.com/vgo1).
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v2 v2.0.1
+	rsc.io/sampler v1.3.0
+)
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v2"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV2()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV2()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV2()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV2()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
+-- v3/go.mod --
+module rsc.io/quote/v3
+
+require rsc.io/sampler v1.3.0
+
+-- v3/go.sum --
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
+rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+-- v3/quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV3() string {
+	return sampler.Hello()
+}
+
+// Glass returns a useful phrase for world travelers.
+func GlassV3() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return "I can eat glass and it doesn't hurt me."
+}
+
+// Go returns a Go proverb.
+func GoV3() string {
+	return "Don't communicate by sharing memory, share memory by communicating."
+}
+
+// Opt returns an optimization truth.
+func OptV3() string {
+	// Wisdom from ken.
+	return "If a program is too slow, it must have a loop."
+}
diff --git a/src/cmd/go/testdata/rsc.io_quote_fe488b8.txt b/src/cmd/go/testdata/rsc.io_quote_fe488b8.txt
new file mode 100644
index 0000000..fb80e51
--- /dev/null
+++ b/src/cmd/go/testdata/rsc.io_quote_fe488b8.txt
@@ -0,0 +1,151 @@
+generated by: go run savedir.go .
+
+-- LICENSE --
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-- README.md --
+This package collects pithy sayings.
+
+It's part of a demonstration of
+[package versioning in Go](https://research.swtch.com/vgo1).
+-- buggy/buggy_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buggy
+
+import "testing"
+
+func Test(t *testing.T) {
+	t.Fatal("buggy!")
+}
+-- go.mod --
+module rsc.io/quote
+
+require (
+	rsc.io/quote/v2 v2.0.1
+	rsc.io/sampler v1.3.0
+)
+-- quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/quote/v2"
+
+// Hello returns a greeting.
+func Hello() string {
+	return quote.HelloV2()
+}
+
+// Glass returns a useful phrase for world travelers.
+func Glass() string {
+	// See http://www.oocities.org/nodotus/hbglass.html.
+	return quote.GlassV2()
+}
+
+// Go returns a Go proverb.
+func Go() string {
+	return quote.GoV2()
+}
+
+// Opt returns an optimization truth.
+func Opt() string {
+	// Wisdom from ken.
+	return quote.OptV2()
+}
+-- quote_test.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package quote
+
+import (
+	"os"
+	"testing"
+)
+
+func init() {
+	os.Setenv("LC_ALL", "en")
+}
+
+func TestHello(t *testing.T) {
+	hello := "Hello, world."
+	if out := Hello(); out != hello {
+		t.Errorf("Hello() = %q, want %q", out, hello)
+	}
+}
+
+func TestGlass(t *testing.T) {
+	glass := "I can eat glass and it doesn't hurt me."
+	if out := Glass(); out != glass {
+		t.Errorf("Glass() = %q, want %q", out, glass)
+	}
+}
+
+func TestGo(t *testing.T) {
+	go1 := "Don't communicate by sharing memory, share memory by communicating."
+	if out := Go(); out != go1 {
+		t.Errorf("Go() = %q, want %q", out, go1)
+	}
+}
+
+func TestOpt(t *testing.T) {
+	opt := "If a program is too slow, it must have a loop."
+	if out := Opt(); out != opt {
+		t.Errorf("Opt() = %q, want %q", out, opt)
+	}
+}
+-- v3/go.mod --
+module rsc.io/quote/v3
+
+require rsc.io/sampler v1.3.0
+
+-- v3/go.sum --
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+rsc.io/sampler v1.99.99 h1:7i08f/p5TBU5joCPW3GjWG1ZFCmr28ybGqlXtelhEK8=
+rsc.io/sampler v1.99.99/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+-- v3/quote.go --
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package quote collects pithy sayings.
+package quote // import "rsc.io/quote"
+
+import "rsc.io/sampler"
+
+// Hello returns a greeting.
+func HelloV3() string {
+	return sampler.Hello()
diff --git a/src/cmd/go/testdata/savedir.go b/src/cmd/go/testdata/savedir.go
new file mode 100644
index 0000000..48a6318
--- /dev/null
+++ b/src/cmd/go/testdata/savedir.go
@@ -0,0 +1,79 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Savedir archives a directory tree as a txtar archive printed to standard output.
+//
+// Usage:
+//
+//	go run savedir.go /path/to/dir >saved.txt
+//
+// Typically the tree is later extracted during a test with tg.extract("testdata/saved.txt").
+//
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"unicode/utf8"
+
+	"../internal/txtar"
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "usage: go run savedir.go dir >saved.txt\n")
+	os.Exit(2)
+}
+
+const goCmd = "vgo"
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	if flag.NArg() != 1 {
+		usage()
+	}
+
+	log.SetPrefix("savedir: ")
+	log.SetFlags(0)
+
+	dir := flag.Arg(0)
+
+	a := new(txtar.Archive)
+	dir = filepath.Clean(dir)
+	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+		if path == dir {
+			return nil
+		}
+		name := info.Name()
+		if strings.HasPrefix(name, ".") {
+			if info.IsDir() {
+				return filepath.SkipDir
+			}
+			return nil
+		}
+		if !info.Mode().IsRegular() {
+			return nil
+		}
+		data, err := ioutil.ReadFile(path)
+		if err != nil {
+			log.Fatal(err)
+		}
+		if !utf8.Valid(data) {
+			log.Printf("%s: ignoring invalid UTF-8 data", path)
+			return nil
+		}
+		a.Files = append(a.Files, txtar.File{Name: strings.TrimPrefix(path, dir+string(filepath.Separator)), Data: data})
+		return nil
+	})
+
+	data := txtar.Format(a)
+	os.Stdout.Write(data)
+}
diff --git a/src/cmd/go/testdata/testcover/pkg1/a.go b/src/cmd/go/testdata/testcover/pkg1/a.go
new file mode 100644
index 0000000..e291611
--- /dev/null
+++ b/src/cmd/go/testdata/testcover/pkg1/a.go
@@ -0,0 +1,7 @@
+package pkg1
+
+import "fmt"
+
+func F() {
+	fmt.Println("pkg1")
+}
diff --git a/src/cmd/go/testdata/testcover/pkg2/a.go b/src/cmd/go/testdata/testcover/pkg2/a.go
new file mode 100644
index 0000000..7bd9bd4
--- /dev/null
+++ b/src/cmd/go/testdata/testcover/pkg2/a.go
@@ -0,0 +1,7 @@
+package pkg2
+
+import "fmt"
+
+func F() {
+	fmt.Println("pkg2")
+}
diff --git a/src/cmd/go/testdata/testcover/pkg2/a_test.go b/src/cmd/go/testdata/testcover/pkg2/a_test.go
new file mode 100644
index 0000000..4f791ad
--- /dev/null
+++ b/src/cmd/go/testdata/testcover/pkg2/a_test.go
@@ -0,0 +1 @@
+package pkg2
diff --git a/src/cmd/go/testdata/testcover/pkg3/a.go b/src/cmd/go/testdata/testcover/pkg3/a.go
new file mode 100644
index 0000000..bf86ed8
--- /dev/null
+++ b/src/cmd/go/testdata/testcover/pkg3/a.go
@@ -0,0 +1,7 @@
+package pkg3
+
+import "fmt"
+
+func F() {
+	fmt.Println("pkg3")
+}
diff --git a/src/cmd/go/testdata/testcover/pkg3/a_test.go b/src/cmd/go/testdata/testcover/pkg3/a_test.go
new file mode 100644
index 0000000..39c2c5a
--- /dev/null
+++ b/src/cmd/go/testdata/testcover/pkg3/a_test.go
@@ -0,0 +1,7 @@
+package pkg3
+
+import "testing"
+
+func TestF(t *testing.T) {
+	F()
+}
diff --git a/src/cmd/go/testdata/testonly2/t.go b/src/cmd/go/testdata/testonly2/t.go
new file mode 100644
index 0000000..82267d3
--- /dev/null
+++ b/src/cmd/go/testdata/testonly2/t.go
@@ -0,0 +1,6 @@
+// This package is not a test-only package,
+// but it still matches the pattern ./testdata/testonly... when in cmd/go.
+
+package main
+
+func main() {}
diff --git a/src/cmd/go/testdata/vendormod.txt b/src/cmd/go/testdata/vendormod.txt
new file mode 100644
index 0000000..1bdaf2a
--- /dev/null
+++ b/src/cmd/go/testdata/vendormod.txt
@@ -0,0 +1,160 @@
+generated by: go run savedir.go vendormod
+
+-- a/foo/AUTHORS.txt --
+-- a/foo/CONTRIBUTORS --
+-- a/foo/LICENSE --
+-- a/foo/PATENTS --
+-- a/foo/COPYING --
+-- a/foo/COPYLEFT --
+-- a/foo/licensed-to-kill --
+-- w/LICENSE --
+-- x/NOTICE! --
+-- x/x2/LICENSE --
+-- mypkg/LICENSE.txt --
+-- a/foo/bar/b/main.go --
+package b
+-- a/foo/bar/b/main_test.go --
+package b
+
+import (
+	"os"
+	"testing"
+)
+
+func TestDir(t *testing.T) {
+	if _, err := os.Stat("../testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+}
+-- a/foo/bar/c/main.go --
+package c
+-- a/foo/bar/c/main_test.go --
+package c
+
+import (
+	"os"
+	"testing"
+)
+
+func TestDir(t *testing.T) {
+	if _, err := os.Stat("../../../testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+	if _, err := os.Stat("./testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+}
+-- a/foo/bar/c/testdata/1 --
+-- a/foo/bar/testdata/1 --
+-- a/go.mod --
+module a
+-- a/main.go --
+package a
+-- a/main_test.go --
+package a
+
+import (
+	"os"
+	"testing"
+)
+
+func TestDir(t *testing.T) {
+	if _, err := os.Stat("./testdata/1"); err != nil {
+		t.Fatalf("testdata: %v", err)
+	}
+}
+-- a/testdata/1 --
+-- appengine.go --
+// +build appengine
+
+package m
+
+import _ "appengine"
+import _ "appengine/datastore"
+-- go.mod --
+module m
+
+require (
+	a v1.0.0
+	mysite/myname/mypkg v1.0.0
+	w v1.0.0 // indirect
+	x v1.0.0
+	y v1.0.0
+	z v1.0.0
+)
+
+replace (
+	a v1.0.0 => ./a
+	mysite/myname/mypkg v1.0.0 => ./mypkg
+	w v1.0.0 => ./w
+	x v1.0.0 => ./x
+	y v1.0.0 => ./y
+	z v1.0.0 => ./z
+)
+-- mypkg/go.mod --
+module me
+-- mypkg/mydir/d.go --
+package mydir
+-- subdir/v1_test.go --
+package m
+
+import _ "mysite/myname/mypkg/mydir"
+-- testdata1.go --
+package m
+
+import _ "a"
+-- testdata2.go --
+package m
+
+import _ "a/foo/bar/b"
+import _ "a/foo/bar/c"
+-- v1.go --
+package m
+
+import _ "x"
+-- v2.go --
+// +build abc
+
+package mMmMmMm
+
+import _ "y"
+-- v3.go --
+// +build !abc
+
+package m
+
+import _ "z"
+-- v4.go --
+// +build notmytag
+
+package m
+
+import _ "x/x1"
+-- w/go.mod --
+module w
+-- w/w.go --
+package w
+-- x/go.mod --
+module x
+-- x/testdata/x.txt --
+placeholder - want directory with no go files
+-- x/x.go --
+package x
+-- x/x1/x1.go --
+// +build notmytag
+
+package x1
+-- x/x2/dummy.txt --
+dummy
+-- x/x_test.go --
+package x
+
+import _ "w"
+-- y/go.mod --
+module y
+-- y/y.go --
+package y
+-- z/go.mod --
+module z
+-- z/z.go --
+package z
diff --git a/src/cmd/go/testdata/vendormod/go.mod b/src/cmd/go/testdata/vendormod/go.mod
deleted file mode 100644
index 6f71634..0000000
--- a/src/cmd/go/testdata/vendormod/go.mod
+++ /dev/null
@@ -1,16 +0,0 @@
-module m
-
-replace x v1.0.0 => ./x
-
-replace y v1.0.0 => ./y
-
-replace z v1.0.0 => ./z
-
-replace w v1.0.0 => ./w
-
-require (
-	w v1.0.0
-	x v1.0.0
-	y v1.0.0
-	z v1.0.0
-)
diff --git a/src/cmd/go/testdata/vendormod/v1.go b/src/cmd/go/testdata/vendormod/v1.go
deleted file mode 100644
index 6ca04a5..0000000
--- a/src/cmd/go/testdata/vendormod/v1.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package m
-
-import _ "x"
diff --git a/src/cmd/go/testdata/vendormod/v2.go b/src/cmd/go/testdata/vendormod/v2.go
deleted file mode 100644
index 8b089e4..0000000
--- a/src/cmd/go/testdata/vendormod/v2.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// +build abc
-
-package mMmMmMm
-
-import _ "y"
diff --git a/src/cmd/go/testdata/vendormod/v3.go b/src/cmd/go/testdata/vendormod/v3.go
deleted file mode 100644
index 318b5f0..0000000
--- a/src/cmd/go/testdata/vendormod/v3.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// +build !abc
-
-package m
-
-import _ "z"
diff --git a/src/cmd/go/testdata/vendormod/w/go.mod b/src/cmd/go/testdata/vendormod/w/go.mod
deleted file mode 100644
index ce2a6c1..0000000
--- a/src/cmd/go/testdata/vendormod/w/go.mod
+++ /dev/null
@@ -1 +0,0 @@
-module w
diff --git a/src/cmd/go/testdata/vendormod/w/w.go b/src/cmd/go/testdata/vendormod/w/w.go
deleted file mode 100644
index a796c0b..0000000
--- a/src/cmd/go/testdata/vendormod/w/w.go
+++ /dev/null
@@ -1 +0,0 @@
-package w
diff --git a/src/cmd/go/testdata/vendormod/x/go.mod b/src/cmd/go/testdata/vendormod/x/go.mod
deleted file mode 100644
index c191435..0000000
--- a/src/cmd/go/testdata/vendormod/x/go.mod
+++ /dev/null
@@ -1 +0,0 @@
-module x
diff --git a/src/cmd/go/testdata/vendormod/x/x.go b/src/cmd/go/testdata/vendormod/x/x.go
deleted file mode 100644
index 823aafd..0000000
--- a/src/cmd/go/testdata/vendormod/x/x.go
+++ /dev/null
@@ -1 +0,0 @@
-package x
diff --git a/src/cmd/go/testdata/vendormod/y/go.mod b/src/cmd/go/testdata/vendormod/y/go.mod
deleted file mode 100644
index ac82a48..0000000
--- a/src/cmd/go/testdata/vendormod/y/go.mod
+++ /dev/null
@@ -1 +0,0 @@
-module y
diff --git a/src/cmd/go/testdata/vendormod/y/y.go b/src/cmd/go/testdata/vendormod/y/y.go
deleted file mode 100644
index 789ca71..0000000
--- a/src/cmd/go/testdata/vendormod/y/y.go
+++ /dev/null
@@ -1 +0,0 @@
-package y
diff --git a/src/cmd/go/testdata/vendormod/z/go.mod b/src/cmd/go/testdata/vendormod/z/go.mod
deleted file mode 100644
index efc58fe..0000000
--- a/src/cmd/go/testdata/vendormod/z/go.mod
+++ /dev/null
@@ -1 +0,0 @@
-module z
diff --git a/src/cmd/go/testdata/vendormod/z/z.go b/src/cmd/go/testdata/vendormod/z/z.go
deleted file mode 100644
index 46458cb..0000000
--- a/src/cmd/go/testdata/vendormod/z/z.go
+++ /dev/null
@@ -1 +0,0 @@
-package z
diff --git a/src/cmd/go/vendor_test.go b/src/cmd/go/vendor_test.go
index 0e7a633..22aa643 100644
--- a/src/cmd/go/vendor_test.go
+++ b/src/cmd/go/vendor_test.go
@@ -332,7 +332,7 @@
 
 // Module legacy support does path rewriting very similar to vendoring.
 
-func TestModLegacy(t *testing.T) {
+func TestLegacyMod(t *testing.T) {
 	tg := testgo(t)
 	defer tg.cleanup()
 	tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/modlegacy"))
@@ -347,7 +347,7 @@
 	tg.run("build", "old/p1", "new/p1")
 }
 
-func TestModLegacyGet(t *testing.T) {
+func TestLegacyModGet(t *testing.T) {
 	testenv.MustHaveExternalNetwork(t)
 
 	tg := testgo(t)