Commit a7fc7109 authored by Bryan C. Mills's avatar Bryan C. Mills

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 <bcmills@google.com>
Reviewed-by: default avatarJay Conrod <jayconrod@google.com>
parent d6b2b35e
...@@ -558,8 +558,6 @@ ...@@ -558,8 +558,6 @@
// For modules stored in source control repositories, the version suffix can // For modules stored in source control repositories, the version suffix can
// also be a commit hash, branch identifier, or other syntax known to the // 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'. // 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 // If a module under consideration is already a dependency of the current
// development module, then get will update the required version. // development module, then get will update the required version.
...@@ -568,6 +566,13 @@ ...@@ -568,6 +566,13 @@
// dependency should be removed entirely, downgrading or removing modules // dependency should be removed entirely, downgrading or removing modules
// depending on it as needed. // 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 // 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 // a named package, it does not use the latest version of that module's
// dependencies. Instead it prefers to use the specific dependency versions // dependencies. Instead it prefers to use the specific dependency versions
...@@ -581,9 +586,11 @@ ...@@ -581,9 +586,11 @@
// patch releases when available. Continuing the previous example, // 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). // '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 // The -u=patch flag (not -u patch) also instructs get to update dependencies,
// to use newer patch releases when available. Continuing the previous example, // but changes the default to select patch releases.
// 'go get -u=patch A' will use the latest A with B v1.2.4 (not B v1.2.3). // 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 // In general, adding a new dependency may require upgrading
// existing dependencies to keep a working build, and 'go get' does // existing dependencies to keep a working build, and 'go get' does
......
...@@ -21,7 +21,6 @@ import ( ...@@ -21,7 +21,6 @@ import (
"cmd/go/internal/work" "cmd/go/internal/work"
"fmt" "fmt"
"os" "os"
pathpkg "path"
"path/filepath" "path/filepath"
"strings" "strings"
) )
...@@ -49,8 +48,6 @@ suffix to the package argument, as in 'go get golang.org/x/text@v0.3.0'. ...@@ -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 For modules stored in source control repositories, the version suffix can
also be a commit hash, branch identifier, or other syntax known to the 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'. 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 If a module under consideration is already a dependency of the current
development module, then get will update the required version. development module, then get will update the required version.
...@@ -59,6 +56,13 @@ downgrades the dependency. The version suffix @none indicates that the ...@@ -59,6 +56,13 @@ downgrades the dependency. The version suffix @none indicates that the
dependency should be removed entirely, downgrading or removing modules dependency should be removed entirely, downgrading or removing modules
depending on it as needed. 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 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 a named package, it does not use the latest version of that module's
dependencies. Instead it prefers to use the specific dependency versions 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 ...@@ -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, 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). '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 The -u=patch flag (not -u patch) also instructs get to update dependencies,
to use newer patch releases when available. Continuing the previous example, but changes the default to select patch releases.
'go get -u=patch A' will use the latest A with B v1.2.4 (not B v1.2.3). 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 In general, adding a new dependency may require upgrading
existing dependencies to keep a working build, and 'go get' does existing dependencies to keep a working build, and 'go get' does
...@@ -165,6 +171,9 @@ func (v *upgradeFlag) Set(s string) error { ...@@ -165,6 +171,9 @@ func (v *upgradeFlag) Set(s string) error {
if s == "false" { if s == "false" {
s = "" s = ""
} }
if s == "true" {
s = "latest"
}
*v = upgradeFlag(s) *v = upgradeFlag(s)
return nil return nil
} }
...@@ -180,12 +189,12 @@ func init() { ...@@ -180,12 +189,12 @@ func init() {
// A task holds the state for processing a single get argument (path@vers). // A task holds the state for processing a single get argument (path@vers).
type task struct { type task struct {
arg string // original argument arg string // original argument
index int
path string // package path part of arg path string // package path part of arg
forceModulePath bool // path must be interpreted as a module path forceModulePath bool // path must be interpreted as a module path
vers string // version part of arg vers string // version part of arg
m module.Version // module version indicated by argument 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) req []module.Version // m's requirement list (not upgraded)
} }
...@@ -196,7 +205,7 @@ func runGet(cmd *base.Command, args []string) { ...@@ -196,7 +205,7 @@ func runGet(cmd *base.Command, args []string) {
} }
switch getU { switch getU {
case "", "patch", "true": case "", "latest", "patch":
// ok // ok
default: default:
base.Fatalf("go get: unknown upgrade flag -u=%s", getU) base.Fatalf("go get: unknown upgrade flag -u=%s", getU)
...@@ -230,6 +239,7 @@ func runGet(cmd *base.Command, args []string) { ...@@ -230,6 +239,7 @@ func runGet(cmd *base.Command, args []string) {
// and a list of install targets (for the "go install" at the end). // and a list of install targets (for the "go install" at the end).
var tasks []*task var tasks []*task
var install []string var install []string
var needModule []*task
for _, arg := range search.CleanPatterns(args) { for _, arg := range search.CleanPatterns(args) {
// Argument is module query path@vers, or else path with implicit @latest. // Argument is module query path@vers, or else path with implicit @latest.
path := arg path := arg
...@@ -245,6 +255,12 @@ func runGet(cmd *base.Command, args []string) { ...@@ -245,6 +255,12 @@ func runGet(cmd *base.Command, args []string) {
install = append(install, path) 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. // Deciding which module to upgrade/downgrade for a particular argument is difficult.
// Patterns only make it more difficult. // Patterns only make it more difficult.
// We impose restrictions to avoid needing to interlace pattern expansion, // We impose restrictions to avoid needing to interlace pattern expansion,
...@@ -271,25 +287,43 @@ func runGet(cmd *base.Command, args []string) { ...@@ -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). // - Import paths without patterns are left as is, for resolution by getQuery (eventually modload.Import).
// //
if search.IsRelativePath(path) { if search.IsRelativePath(path) {
// Check that this relative pattern only matches directories in the current module, t := &task{arg: arg, path: modload.Target.Path, vers: "", prevM: modload.Target, forceModulePath: true}
// and then record the current module as the target.
dir := path // If the path is relative, always upgrade the entire main module.
if i := strings.Index(path, "..."); i >= 0 { // (TODO(golang.org/issue/26902): maybe we should upgrade the modules
dir, _ = pathpkg.Split(path[:i]) // containing the dependencies of the requested packages instead.)
} //
abs, err := filepath.Abs(dir) // If the path is explicit, at least check that it is a package in the main module.
if err != nil { if len(args) > 0 {
base.Errorf("go get %s: %v", arg, err) if *getM {
continue 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()) switch vers {
continue 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 continue
} }
if path == "all" { if path == "all" {
// TODO: If *getM, should this be the module pattern "all"? // TODO: If *getM, should this be the module pattern "all"?
...@@ -306,30 +340,19 @@ func runGet(cmd *base.Command, args []string) { ...@@ -306,30 +340,19 @@ func runGet(cmd *base.Command, args []string) {
m := modload.PackageModule(pkg) m := modload.PackageModule(pkg)
if m.Path != "" && !seen[m] { if m.Path != "" && !seen[m] {
seen[m] = true 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 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, "...") { if strings.Contains(path, "...") {
// Apply to modules in build list matched by pattern (golang.org/x/...), if any. // Apply to modules in build list matched by pattern (golang.org/x/...), if any.
match := search.MatchPattern(path) match := search.MatchPattern(path)
matched := false matched := false
for _, m := range modload.BuildList() { for _, m := range modload.BuildList() {
if match(m.Path) || str.HasPathPrefix(path, m.Path) { 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 matched = true
} }
} }
...@@ -345,10 +368,66 @@ func runGet(cmd *base.Command, args []string) { ...@@ -345,10 +368,66 @@ func runGet(cmd *base.Command, args []string) {
continue 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() 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). // Now we've reduced the upgrade/downgrade work to a list of path@vers pairs (tasks).
// Resolve each one in parallel. // Resolve each one in parallel.
reqs := modload.Reqs() reqs := modload.Reqs()
...@@ -363,7 +442,7 @@ func runGet(cmd *base.Command, args []string) { ...@@ -363,7 +442,7 @@ func runGet(cmd *base.Command, args []string) {
t.m = module.Version{Path: t.path, Version: "none"} t.m = module.Version{Path: t.path, Version: "none"}
return return
} }
m, err := getQuery(t.path, t.vers, t.forceModulePath) m, err := getQuery(t.path, t.vers, t.prevM, t.forceModulePath)
if err != nil { if err != nil {
base.Errorf("go get %v: %v", t.arg, err) base.Errorf("go get %v: %v", t.arg, err)
return return
...@@ -412,7 +491,6 @@ func runGet(cmd *base.Command, args []string) { ...@@ -412,7 +491,6 @@ func runGet(cmd *base.Command, args []string) {
upgraded, err := mvs.UpgradeAll(upgradeTarget, &upgrader{ upgraded, err := mvs.UpgradeAll(upgradeTarget, &upgrader{
Reqs: modload.Reqs(), Reqs: modload.Reqs(),
targets: named, targets: named,
patch: getU == "patch",
tasks: byPath, tasks: byPath,
}) })
if err != nil { if err != nil {
...@@ -554,9 +632,16 @@ func runGet(cmd *base.Command, args []string) { ...@@ -554,9 +632,16 @@ func runGet(cmd *base.Command, args []string) {
// to determine the underlying module version being requested. // to determine the underlying module version being requested.
// If forceModulePath is set, getQuery must interpret path // If forceModulePath is set, getQuery must interpret path
// as a module path. // as a module path.
func getQuery(path, vers string, forceModulePath bool) (module.Version, error) { func getQuery(path, vers string, prevM module.Version, forceModulePath bool) (module.Version, error) {
if vers == "" { switch vers {
case "":
vers = "latest" 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. // 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) { ...@@ -625,7 +710,7 @@ func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
// only ever returns untagged versions, // only ever returns untagged versions,
// which is not what we want. // which is not what we want.
query := "latest" query := "latest"
if u.patch { if getU == "patch" {
// For patch upgrade, query "v1.2". // For patch upgrade, query "v1.2".
query = semver.MajorMinor(m.Version) query = semver.MajorMinor(m.Version)
} }
......
...@@ -339,7 +339,7 @@ func loadAll(testAll bool) []string { ...@@ -339,7 +339,7 @@ func loadAll(testAll bool) []string {
if !testAll { if !testAll {
loaded.testRoots = true loaded.testRoots = true
} }
all := TargetPackages() all := TargetPackages("...")
loaded.load(func() []string { return all }) loaded.load(func() []string { return all })
WriteGoMod() WriteGoMod()
...@@ -357,10 +357,11 @@ func loadAll(testAll bool) []string { ...@@ -357,10 +357,11 @@ func loadAll(testAll bool) []string {
// Only "ignore" and malformed build tag requirements are considered false. // Only "ignore" and malformed build tag requirements are considered false.
var anyTags = map[string]bool{"*": true} var anyTags = map[string]bool{"*": true}
// TargetPackages returns the list of packages in the target (top-level) module, // TargetPackages returns the list of packages in the target (top-level) module
// under all build tag settings. // matching pattern, which may be relative to the working directory, under all
func TargetPackages() []string { // build tag settings.
return matchPackages("...", anyTags, false, []module.Version{Target}) func TargetPackages(pattern string) []string {
return matchPackages(pattern, anyTags, false, []module.Version{Target})
} }
// BuildList returns the module build list, // BuildList returns the module build list,
......
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
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
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"
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"
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"
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
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
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
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
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"
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"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment