From a7fc71092dfb6c6d5fef09b8c85b7d9e78908717 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Fri, 15 Mar 2019 14:58:40 -0400 Subject: [PATCH] cmd/go/internal/modget: support the suffix '@patch' in 'go get' As of this change, an explicit '@patch' suffix is to '-u=patch' as '@latest' is to '-u'. RELNOTE='go get' in module mode now supports the version suffix '@patch'. Fixes #26812 Change-Id: Ib5eee40de640440f7470d37a574b311ef8a67f67 Reviewed-on: https://go-review.googlesource.com/c/go/+/167747 Run-TryBot: Bryan C. Mills Reviewed-by: Jay Conrod --- src/cmd/go/alldocs.go | 17 +- src/cmd/go/internal/modget/get.go | 173 +++++++++++++----- src/cmd/go/internal/modload/load.go | 11 +- ...ch.example.com_depofdirectpatch_v1.0.0.txt | 11 ++ ...ch.example.com_depofdirectpatch_v1.0.1.txt | 11 ++ .../mod/patch.example.com_direct_v1.0.0.txt | 21 +++ .../mod/patch.example.com_direct_v1.0.1.txt | 27 +++ .../mod/patch.example.com_direct_v1.1.0.txt | 21 +++ .../mod/patch.example.com_indirect_v1.0.0.txt | 11 ++ .../mod/patch.example.com_indirect_v1.0.1.txt | 11 ++ .../mod/patch.example.com_indirect_v1.1.0.txt | 11 ++ .../go/testdata/script/mod_upgrade_patch.txt | 29 --- .../testdata/script/mod_upgrade_patch_mod.txt | 85 +++++++++ .../testdata/script/mod_upgrade_patch_pkg.txt | 88 +++++++++ 14 files changed, 444 insertions(+), 83 deletions(-) create mode 100644 src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.0.txt create mode 100644 src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.1.txt create mode 100644 src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.0.txt create mode 100644 src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.1.txt create mode 100644 src/cmd/go/testdata/mod/patch.example.com_direct_v1.1.0.txt create mode 100644 src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.0.txt create mode 100644 src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.1.txt create mode 100644 src/cmd/go/testdata/mod/patch.example.com_indirect_v1.1.0.txt delete mode 100644 src/cmd/go/testdata/script/mod_upgrade_patch.txt create mode 100644 src/cmd/go/testdata/script/mod_upgrade_patch_mod.txt create mode 100644 src/cmd/go/testdata/script/mod_upgrade_patch_pkg.txt diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index f42635f6a8..f02df514b7 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -558,8 +558,6 @@ // 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. @@ -568,6 +566,13 @@ // dependency should be removed entirely, downgrading or removing modules // depending on it as needed. // +// The version suffix @latest explicitly requests the latest minor release of the +// given path. +// +// The suffix @patch requests the latest patch release: if the path is already in +// the build list, the selected version will have the same minor version. +// If the path is not already in the build list, @patch is equivalent to @latest. +// // 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 @@ -581,9 +586,11 @@ // 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). +// The -u=patch flag (not -u patch) also instructs get to update dependencies, +// but changes the default to select patch releases. +// Continuing the previous example, +// 'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3), +// while 'go get -u=patch A' will use a patch release of A instead. // // In general, adding a new dependency may require upgrading // existing dependencies to keep a working build, and 'go get' does diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 17a0ed45e2..40bbd50746 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -21,7 +21,6 @@ import ( "cmd/go/internal/work" "fmt" "os" - pathpkg "path" "path/filepath" "strings" ) @@ -49,8 +48,6 @@ 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. @@ -59,6 +56,13 @@ downgrades the dependency. The version suffix @none indicates that the dependency should be removed entirely, downgrading or removing modules depending on it as needed. +The version suffix @latest explicitly requests the latest minor release of the +given path. + +The suffix @patch requests the latest patch release: if the path is already in +the build list, the selected version will have the same minor version. +If the path is not already in the build list, @patch is equivalent to @latest. + 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 @@ -72,9 +76,11 @@ 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). +The -u=patch flag (not -u patch) also instructs get to update dependencies, +but changes the default to select patch releases. +Continuing the previous example, +'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3), +while 'go get -u=patch A' will use a patch release of A instead. In general, adding a new dependency may require upgrading existing dependencies to keep a working build, and 'go get' does @@ -165,6 +171,9 @@ func (v *upgradeFlag) Set(s string) error { if s == "false" { s = "" } + if s == "true" { + s = "latest" + } *v = upgradeFlag(s) return nil } @@ -180,12 +189,12 @@ func init() { // A task holds the state for processing a single get argument (path@vers). type task struct { - arg string // original argument - index int + 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 + prevM module.Version // module version from initial build list req []module.Version // m's requirement list (not upgraded) } @@ -196,7 +205,7 @@ func runGet(cmd *base.Command, args []string) { } switch getU { - case "", "patch", "true": + case "", "latest", "patch": // ok default: base.Fatalf("go get: unknown upgrade flag -u=%s", getU) @@ -230,6 +239,7 @@ func runGet(cmd *base.Command, args []string) { // and a list of install targets (for the "go install" at the end). var tasks []*task var install []string + var needModule []*task for _, arg := range search.CleanPatterns(args) { // Argument is module query path@vers, or else path with implicit @latest. path := arg @@ -245,6 +255,12 @@ func runGet(cmd *base.Command, args []string) { install = append(install, path) } + // If the user runs 'go get -u=patch some/module', update some/module to a + // patch release, not a minor version. + if vers == "" && getU != "" { + vers = string(getU) + } + // 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, @@ -271,25 +287,43 @@ func runGet(cmd *base.Command, args []string) { // - 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 + t := &task{arg: arg, path: modload.Target.Path, vers: "", prevM: modload.Target, forceModulePath: true} + + // If the path is relative, always upgrade the entire main module. + // (TODO(golang.org/issue/26902): maybe we should upgrade the modules + // containing the dependencies of the requested packages instead.) + // + // If the path is explicit, at least check that it is a package in the main module. + if len(args) > 0 { + if *getM { + base.Errorf("go get %s: -m requires a module path, but a relative path must be a package in the main module", arg) + continue + } + + pkgPath := modload.DirImportPath(filepath.FromSlash(path)) + if pkgs := modload.TargetPackages(pkgPath); len(pkgs) == 0 { + if strings.Contains(path, "...") { + fmt.Fprintf(os.Stderr, "go get %s: warning: pattern patched no packages", arg) + } else { + abs, err := filepath.Abs(path) + if err != nil { + abs = path + } + base.Errorf("go get %s: path %s is not in module rooted at %s", arg, abs, modload.ModRoot()) + } + continue + } } - if !str.HasFilePathPrefix(abs, modload.ModRoot()) { - base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot()) - continue + + switch vers { + case "", "latest", "patch": + tasks = append(tasks, t) + default: + base.Errorf("go get %s: can't request explicit version of path in main module", arg) } - // TODO: Check if abs is inside a nested module. - tasks = append(tasks, &task{arg: arg, path: modload.Target.Path, vers: ""}) continue } + if path == "all" { // TODO: If *getM, should this be the module pattern "all"? @@ -306,30 +340,19 @@ func runGet(cmd *base.Command, args []string) { 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}) + tasks = append(tasks, &task{arg: arg, path: m.Path, vers: vers, prevM: m, 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}) + tasks = append(tasks, &task{arg: arg, path: m.Path, vers: vers, prevM: m, forceModulePath: true}) matched = true } } @@ -345,10 +368,66 @@ func runGet(cmd *base.Command, args []string) { continue } } - tasks = append(tasks, &task{arg: arg, path: path, vers: vers}) + t := &task{arg: arg, path: path, vers: vers} + if vers == "patch" { + if *getM { + for _, m := range modload.BuildList() { + if m.Path == path { + t.prevM = m + break + } + } + tasks = append(tasks, t) + } else { + // We need to know the module containing t so that we can restrict the patch to its minor version. + needModule = append(needModule, t) + } + } else { + // The requested version of path doesn't depend on the existing version, + // so don't bother resolving it. + tasks = append(tasks, t) + } } base.ExitIfErrors() + if len(needModule) > 0 { + paths := make([]string, len(needModule)) + for i, t := range needModule { + paths[i] = t.path + } + matches := modload.ImportPaths(paths) + if len(matches) != len(paths) { + base.Fatalf("go get: internal error: ImportPaths resolved %d paths to %d matches", len(paths), len(matches)) + } + + for i, match := range matches { + t := needModule[i] + if len(match.Pkgs) == 0 { + // Let modload.Query resolve the path during task processing. + tasks = append(tasks, t) + continue + } + + allStd := true + for _, pkg := range match.Pkgs { + m := modload.PackageModule(pkg) + if m.Path == "" { + // pkg is in the standard library. + } else { + allStd = false + tasks = append(tasks, &task{arg: t.arg, path: pkg, vers: t.vers, prevM: m}) + } + } + if allStd { + if *getM { + base.Errorf("go get %s: cannot use pattern %q with -m", t.arg, t.arg) + } else if t.path != t.arg { + base.Errorf("go get %s: cannot use pattern %q with explicit version", t.arg, t.arg) + } + } + } + } + // Now we've reduced the upgrade/downgrade work to a list of path@vers pairs (tasks). // Resolve each one in parallel. reqs := modload.Reqs() @@ -363,7 +442,7 @@ func runGet(cmd *base.Command, args []string) { t.m = module.Version{Path: t.path, Version: "none"} return } - m, err := getQuery(t.path, t.vers, t.forceModulePath) + m, err := getQuery(t.path, t.vers, t.prevM, t.forceModulePath) if err != nil { base.Errorf("go get %v: %v", t.arg, err) return @@ -412,7 +491,6 @@ func runGet(cmd *base.Command, args []string) { upgraded, err := mvs.UpgradeAll(upgradeTarget, &upgrader{ Reqs: modload.Reqs(), targets: named, - patch: getU == "patch", tasks: byPath, }) if err != nil { @@ -554,9 +632,16 @@ func runGet(cmd *base.Command, args []string) { // 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 vers == "" { +func getQuery(path, vers string, prevM module.Version, forceModulePath bool) (module.Version, error) { + switch vers { + case "": vers = "latest" + case "patch": + if prevM.Version == "" { + vers = "latest" + } else { + vers = semver.MajorMinor(prevM.Version) + } } // First choice is always to assume path is a module path. @@ -625,7 +710,7 @@ func (u *upgrader) Upgrade(m module.Version) (module.Version, error) { // only ever returns untagged versions, // which is not what we want. query := "latest" - if u.patch { + if getU == "patch" { // For patch upgrade, query "v1.2". query = semver.MajorMinor(m.Version) } diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index 57c2dd25a6..d55e0c5403 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -339,7 +339,7 @@ func loadAll(testAll bool) []string { if !testAll { loaded.testRoots = true } - all := TargetPackages() + all := TargetPackages("...") loaded.load(func() []string { return all }) WriteGoMod() @@ -357,10 +357,11 @@ func loadAll(testAll bool) []string { // 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, false, []module.Version{Target}) +// TargetPackages returns the list of packages in the target (top-level) module +// matching pattern, which may be relative to the working directory, under all +// build tag settings. +func TargetPackages(pattern string) []string { + return matchPackages(pattern, anyTags, false, []module.Version{Target}) } // BuildList returns the module build list, diff --git a/src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.0.txt b/src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.0.txt new file mode 100644 index 0000000000..40616c668a --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.0.txt @@ -0,0 +1,11 @@ +patch.example.com/depofdirectpatch v1.0.0 +written by hand + +-- .mod -- +module patch.example.com/depofdirectpatch +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module patch.example.com/depofdirectpatch +-- depofdirectpatch.go -- +package depofdirectpatch diff --git a/src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.1.txt b/src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.1.txt new file mode 100644 index 0000000000..e075028656 --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_depofdirectpatch_v1.0.1.txt @@ -0,0 +1,11 @@ +patch.example.com/depofdirectpatch v1.0.1 +written by hand + +-- .mod -- +module patch.example.com/depofdirectpatch +-- .info -- +{"Version":"v1.0.1"} +-- go.mod -- +module patch.example.com/depofdirectpatch +-- depofdirectpatch.go -- +package depofdirectpatch diff --git a/src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.0.txt b/src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.0.txt new file mode 100644 index 0000000000..1e775fb89b --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.0.txt @@ -0,0 +1,21 @@ +patch.example.com/direct v1.0.0 +written by hand + +-- .mod -- +module patch.example.com/direct + +require ( + patch.example.com/indirect v1.0.0 +) +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module patch.example.com/direct + +require ( + patch.example.com/indirect v1.0.0 +) +-- direct.go -- +package direct + +import _ "patch.example.com/indirect" diff --git a/src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.1.txt b/src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.1.txt new file mode 100644 index 0000000000..64912b7b43 --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_direct_v1.0.1.txt @@ -0,0 +1,27 @@ +patch.example.com/direct v1.0.1 +written by hand + +-- .mod -- +module patch.example.com/direct + +require ( + patch.example.com/indirect v1.0.0 + patch.example.com/depofdirectpatch v1.0.0 +) +-- .info -- +{"Version":"v1.0.1"} +-- go.mod -- +module patch.example.com/direct + +require ( + patch.example.com/indirect v1.0.0 + patch.example.com/depofdirectpatch v1.0.0 +) +-- direct.go -- +package direct + +import _ "patch.example.com/indirect" +-- usedepofdirectpatch/unused.go -- +package usedepofdirectpatch + +import _ "patch.example.com/depofdirectpatch" diff --git a/src/cmd/go/testdata/mod/patch.example.com_direct_v1.1.0.txt b/src/cmd/go/testdata/mod/patch.example.com_direct_v1.1.0.txt new file mode 100644 index 0000000000..406e3b9f62 --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_direct_v1.1.0.txt @@ -0,0 +1,21 @@ +patch.example.com/direct v1.1.0 +written by hand + +-- .mod -- +module patch.example.com/direct + +require ( + patch.example.com/indirect v1.0.0 +) +-- .info -- +{"Version":"v1.1.0"} +-- go.mod -- +module patch.example.com/direct + +require ( + patch.example.com/indirect v1.0.0 +) +-- direct.go -- +package direct + +import _ "patch.example.com/indirect" diff --git a/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.0.txt b/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.0.txt new file mode 100644 index 0000000000..ea7f5e2d8d --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.0.txt @@ -0,0 +1,11 @@ +patch.example.com/indirect v1.0.0 +written by hand + +-- .mod -- +module patch.example.com/indirect +-- .info -- +{"Version":"v1.0.0"} +-- go.mod -- +module patch.example.com/indirect +-- direct.go -- +package indirect diff --git a/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.1.txt b/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.1.txt new file mode 100644 index 0000000000..8c6cf8e7bf --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.0.1.txt @@ -0,0 +1,11 @@ +patch.example.com/indirect v1.0.1 +written by hand + +-- .mod -- +module patch.example.com/indirect +-- .info -- +{"Version":"v1.0.1"} +-- go.mod -- +module patch.example.com/indirect +-- direct.go -- +package indirect diff --git a/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.1.0.txt b/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.1.0.txt new file mode 100644 index 0000000000..f7229d417a --- /dev/null +++ b/src/cmd/go/testdata/mod/patch.example.com_indirect_v1.1.0.txt @@ -0,0 +1,11 @@ +patch.example.com/indirect v1.1.0 +written by hand + +-- .mod -- +module patch.example.com/indirect +-- .info -- +{"Version":"v1.1.0"} +-- go.mod -- +module patch.example.com/indirect +-- direct.go -- +package indirect diff --git a/src/cmd/go/testdata/script/mod_upgrade_patch.txt b/src/cmd/go/testdata/script/mod_upgrade_patch.txt deleted file mode 100644 index 3c27cdbf7b..0000000000 --- a/src/cmd/go/testdata/script/mod_upgrade_patch.txt +++ /dev/null @@ -1,29 +0,0 @@ -env GO111MODULE=on - -go list -m all -stdout '^rsc.io/quote v1.4.0' -stdout '^rsc.io/sampler v1.0.0' - -# get -u=patch rsc.io/quote should take latest quote & patch update its deps -go get -m -u=patch rsc.io/quote -go list -m all -stdout '^rsc.io/quote v1.5.2' -stdout '^rsc.io/sampler v1.3.1' -stdout '^golang.org/x/text v0.0.0-' - -# get -u=patch quote@v1.2.0 should take that version of quote & patch update its deps -go get -m -u=patch rsc.io/quote@v1.2.0 -go list -m all -stdout '^rsc.io/quote v1.2.0' -stdout '^rsc.io/sampler v1.3.1' -stdout '^golang.org/x/text v0.0.0-' - -# get -u=patch with no args applies to all deps -go get -m -u=patch -go list -m all -stdout '^rsc.io/quote v1.2.1' - --- go.mod -- -module x -require rsc.io/quote v1.4.0 - diff --git a/src/cmd/go/testdata/script/mod_upgrade_patch_mod.txt b/src/cmd/go/testdata/script/mod_upgrade_patch_mod.txt new file mode 100644 index 0000000000..0853c37d3f --- /dev/null +++ b/src/cmd/go/testdata/script/mod_upgrade_patch_mod.txt @@ -0,0 +1,85 @@ +env GO111MODULE=on + +# Initially, we are at v1.0.0 for all dependencies. +cp go.mod go.mod.orig +go list -m all +stdout '^patch.example.com/direct v1.0.0' +stdout '^patch.example.com/indirect v1.0.0' +! stdout '^patch.example.com/depofdirectpatch' + +# get -m -u=patch, with no arguments, should patch-update all dependencies, +# pulling in transitive dependencies and also patching those. +# +# TODO(golang.org/issue/26902): We should not update transitive dependencies +# that don't affect the transitive import graph of the main module in any way. +cp go.mod.orig go.mod +go get -m -u=patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.1' +stdout '^patch.example.com/depofdirectpatch v1.0.1' # TODO: leave at v1.0.0 + +# 'get -m all@patch' should be equivalent to 'get -u=patch -m all' +cp go.mod.orig go.mod +go get -m all@patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.1' +stdout '^patch.example.com/depofdirectpatch v1.0.0' + +# Requesting the direct dependency with -u=patch but without an explicit version +# should patch-update it and its dependencies. +cp go.mod.orig go.mod +go get -m -u=patch patch.example.com/direct +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.1' +stdout '^patch.example.com/depofdirectpatch v1.0.1' # TODO: leave at v1.0.0 + +# Requesting only the indirect dependency should not update the direct one. +cp go.mod.orig go.mod +go get -m -u=patch patch.example.com/indirect +go list -m all +stdout '^patch.example.com/direct v1.0.0' +stdout '^patch.example.com/indirect v1.0.1' +! stdout '^patch.example.com/depofdirectpatch' + +# @patch should apply only to the specific module. +# but the result must reflect its upgraded requirements. +cp go.mod.orig go.mod +go get -m patch.example.com/direct@patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.0' +stdout '^patch.example.com/depofdirectpatch v1.0.0' + +# An explicit @patch should override a general -u. +cp go.mod.orig go.mod +go get -m -u patch.example.com/direct@patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.1.0' +stdout '^patch.example.com/depofdirectpatch v1.0.1' + +# An explicit @latest should override a general -u=patch. +cp go.mod.orig go.mod +go get -m -u=patch patch.example.com/direct@latest +go list -m all +stdout '^patch.example.com/direct v1.1.0' +stdout '^patch.example.com/indirect v1.0.1' +! stdout '^patch.example.com/depofdirectpatch' + +# Standard-library modules cannot be upgraded explicitly. +cp go.mod.orig go.mod +! go get -m std@patch +stderr 'explicit requirement on standard-library module std not allowed' + + +-- go.mod -- +module x + +require patch.example.com/direct v1.0.0 + +-- main.go -- +package x +import _ "patch.example.com/direct" diff --git a/src/cmd/go/testdata/script/mod_upgrade_patch_pkg.txt b/src/cmd/go/testdata/script/mod_upgrade_patch_pkg.txt new file mode 100644 index 0000000000..8aedaefd90 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_upgrade_patch_pkg.txt @@ -0,0 +1,88 @@ +env GO111MODULE=on + +# Initially, we are at v1.0.0 for all dependencies. +cp go.mod go.mod.orig +go list -m all +stdout '^patch.example.com/direct v1.0.0' +stdout '^patch.example.com/indirect v1.0.0' +! stdout '^patch.example.com/depofdirectpatch' + +# get -u=patch, with no arguments, should patch-update all dependencies, +# pulling in transitive dependencies and also patching those. +# +# TODO(golang.org/issue/26902): We should not update dependencies +# that don't affect the transitive import graph of the main module in any way. +cp go.mod.orig go.mod +go get -u=patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.1' +stdout '^patch.example.com/depofdirectpatch v1.0.1' # TODO: leave at v1.0.0 + +# 'get all@patch' should be equivalent to 'get -u=patch all' +cp go.mod.orig go.mod +go get all@patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.1' +stdout '^patch.example.com/depofdirectpatch v1.0.0' + +# Requesting the direct dependency with -u=patch but without an explicit version +# should patch-update it and its dependencies. +cp go.mod.orig go.mod +go get -u=patch patch.example.com/direct +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.1' +stdout '^patch.example.com/depofdirectpatch v1.0.1' # TODO: leave at v1.0.0 + +# Requesting only the indirect dependency should not update the direct one. +cp go.mod.orig go.mod +go get -u=patch patch.example.com/indirect +go list -m all +stdout '^patch.example.com/direct v1.0.0' +stdout '^patch.example.com/indirect v1.0.1' +! stdout '^patch.example.com/depofdirectpatch' + +# @patch should apply only to the specific module, +# but the result must reflect its upgraded requirements. +cp go.mod.orig go.mod +go get patch.example.com/direct@patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.0.0' +stdout '^patch.example.com/depofdirectpatch v1.0.0' + +# An explicit @patch should override a general -u. +cp go.mod.orig go.mod +go get -u patch.example.com/direct@patch +go list -m all +stdout '^patch.example.com/direct v1.0.1' +stdout '^patch.example.com/indirect v1.1.0' +stdout '^patch.example.com/depofdirectpatch v1.0.1' + +# An explicit @latest should override a general -u=patch. +cp go.mod.orig go.mod +go get -u=patch patch.example.com/direct@latest +go list -m all +stdout '^patch.example.com/direct v1.1.0' +stdout '^patch.example.com/indirect v1.0.1' +! stdout '^patch.example.com/depofdirectpatch' + +# Standard-library packages cannot be upgraded explicitly. +cp go.mod.orig go.mod +! go get cmd/vet@patch +stderr 'cannot use pattern .* with explicit version' + +# However, standard-library packages without explicit versions are fine. +go get -u=patch -d cmd/get + + +-- go.mod -- +module x + +require patch.example.com/direct v1.0.0 + +-- main.go -- +package x +import _ "patch.example.com/direct" -- 2.50.0