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
......
This diff is collapsed.
...@@ -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