Skip to content

Commit a3a0cc2

Browse files
author
Jay Conrod
committed
cmd/go: restore @latest behavior and support @upgrade in 'go get'
'go get path@latest' may now downgrade a module required at a pre-release or pseudo-version newer than the latest released version. This restores the 1.12 behavior and the ability to easily roll back from a temporary development version. 'go get path@upgrade' is like @latest but will not downgrade. If no version suffix is specified ('go get path'), @upgrade is implied. Fixes #32846 Change-Id: Ibec0628292ab1c484716a5add0950d7a7ee45f47 Reviewed-on: https://go-review.googlesource.com/c/go/+/184440 Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
1 parent b412fde commit a3a0cc2

File tree

8 files changed

+95
-53
lines changed

8 files changed

+95
-53
lines changed

src/cmd/go/alldocs.go

Lines changed: 8 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/internal/modget/get.go

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ dependency should be removed entirely, downgrading or removing modules
5959
depending on it as needed.
6060
6161
The version suffix @latest explicitly requests the latest minor release of the
62-
given path. The suffix @patch requests the latest patch release: if the path
63-
is already in the build list, the selected version will have the same minor
64-
version. If the path is not already in the build list, @patch is equivalent
65-
to @latest. Neither @latest nor @patch will cause 'go get' to downgrade a module
66-
in the build list if it is required at a newer pre-release version that is
67-
newer than the latest released version.
62+
module named by the given path. The suffix @upgrade is like @latest but
63+
will not downgrade a module if it is already required at a revision or
64+
pre-release version newer than the latest released version. The suffix
65+
@patch requests the latest patch release: the latest released version
66+
with the same major and minor version numbers as the currently required
67+
version. Like @upgrade, @patch will not downgrade a module already required
68+
at a newer version. If the path is not already required, @upgrade and @patch
69+
are equivalent to @latest.
6870
6971
Although get defaults to using the latest version of the module containing
7072
a named package, it does not use the latest version of that module's
@@ -178,7 +180,7 @@ func (v *upgradeFlag) Set(s string) error {
178180
s = ""
179181
}
180182
if s == "true" {
181-
s = "latest"
183+
s = "upgrade"
182184
}
183185
*v = upgradeFlag(s)
184186
return nil
@@ -202,8 +204,9 @@ type getArg struct {
202204
// if there is no "@"). path specifies the modules or packages to get.
203205
path string
204206

205-
// vers is the part of the argument after "@" (or "" if there is no "@").
206-
// vers specifies the module version to get.
207+
// vers is the part of the argument after "@" or an implied
208+
// "upgrade" or "patch" if there is no "@". vers specifies the
209+
// module version to get.
207210
vers string
208211
}
209212

@@ -249,7 +252,7 @@ func runGet(cmd *base.Command, args []string) {
249252
}
250253

251254
switch getU {
252-
case "", "latest", "patch":
255+
case "", "upgrade", "patch":
253256
// ok
254257
default:
255258
base.Fatalf("go get: unknown upgrade flag -u=%s", getU)
@@ -283,11 +286,11 @@ func runGet(cmd *base.Command, args []string) {
283286

284287
// Parse command-line arguments and report errors. The command-line
285288
// arguments are of the form path@version or simply path, with implicit
286-
// @latest. path@none is "downgrade away".
289+
// @upgrade. path@none is "downgrade away".
287290
var gets []getArg
288291
var queries []*query
289292
for _, arg := range search.CleanPatterns(args) {
290-
// Argument is module query path@vers, or else path with implicit @latest.
293+
// Argument is path or path@vers.
291294
path := arg
292295
vers := ""
293296
if i := strings.Index(arg, "@"); i >= 0 {
@@ -298,10 +301,14 @@ func runGet(cmd *base.Command, args []string) {
298301
continue
299302
}
300303

301-
// If the user runs 'go get -u=patch some/module', update some/module to a
302-
// patch release, not a minor version.
303-
if vers == "" && getU != "" {
304-
vers = string(getU)
304+
// If no version suffix is specified, assume @upgrade.
305+
// If -u=patch was specified, assume @patch instead.
306+
if vers == "" {
307+
if getU != "" {
308+
vers = string(getU)
309+
} else {
310+
vers = "upgrade"
311+
}
305312
}
306313

307314
gets = append(gets, getArg{raw: arg, path: path, vers: vers})
@@ -358,7 +365,7 @@ func runGet(cmd *base.Command, args []string) {
358365
// The argument is a package path.
359366
if pkgs := modload.TargetPackages(path); len(pkgs) != 0 {
360367
// The path is in the main module. Nothing to query.
361-
if vers != "" && vers != "latest" && vers != "patch" {
368+
if vers != "upgrade" && vers != "patch" {
362369
base.Errorf("go get %s: can't request explicit version of path in main module", arg)
363370
}
364371
continue
@@ -376,8 +383,8 @@ func runGet(cmd *base.Command, args []string) {
376383
continue
377384
}
378385

379-
// If we're querying "latest" or "patch", we need to know the current
380-
// version of the module. For "latest", we want to avoid accidentally
386+
// If we're querying "upgrade" or "patch", we need to know the current
387+
// version of the module. For "upgrade", we want to avoid accidentally
381388
// downgrading from a newer prerelease. For "patch", we need to query
382389
// the correct minor version.
383390
// Here, we check if "path" is the name of a module in the build list
@@ -736,10 +743,6 @@ func getQuery(path, vers string, prevM module.Version, forceModulePath bool) (mo
736743
base.Fatalf("go get: internal error: prevM may be set if and only if forceModulePath is set")
737744
}
738745

739-
if vers == "" || vers == "patch" && prevM.Version == "" {
740-
vers = "latest"
741-
}
742-
743746
if forceModulePath || !strings.Contains(path, "...") {
744747
if path == modload.Target.Path {
745748
if vers != "latest" {
@@ -893,9 +896,10 @@ func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
893896
// which may return a pseudoversion for the latest commit.
894897
// Query "latest" returns the newest tagged version or the newest
895898
// prerelease version if there are no non-prereleases, or repo.Latest
896-
// if there aren't any tagged versions. Since we're providing the previous
897-
// version, Query will confirm the latest version is actually newer
898-
// and will return the current version if not.
899+
// if there aren't any tagged versions.
900+
// If we're querying "upgrade" or "patch", Query will compare the current
901+
// version against the chosen version and will return the current version
902+
// if it is newer.
899903
info, err := modload.Query(m.Path, string(getU), m.Version, modload.Allowed)
900904
if err != nil {
901905
// Report error but return m, to let version selection continue.

src/cmd/go/internal/modload/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func addUpdate(m *modinfo.ModulePublic) {
7979
return
8080
}
8181

82-
if info, err := Query(m.Path, "latest", m.Version, Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
82+
if info, err := Query(m.Path, "upgrade", m.Version, Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
8383
m.Update = &modinfo.ModulePublic{
8484
Path: m.Path,
8585
Version: info.Version,

src/cmd/go/internal/modload/query.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ import (
2828
// tagged version, with non-prereleases preferred over prereleases.
2929
// If there are no tagged versions in the repo, latest returns the most
3030
// recent commit.
31+
// - the literal string "upgrade", equivalent to "latest" except that if
32+
// current is a newer version, current will be returned (see below).
3133
// - the literal string "patch", denoting the latest available tagged version
32-
// with the same major and minor number as current. If current is "",
33-
// "patch" is equivalent to "latest".
34+
// with the same major and minor number as current (see below).
3435
// - v1, denoting the latest available tagged version v1.x.x.
3536
// - v1.2, denoting the latest available tagged version v1.2.x.
3637
// - v1.2.3, a semantic version string denoting that tagged version.
@@ -39,11 +40,12 @@ import (
3940
// with non-prereleases preferred over prereleases.
4041
// - a repository commit identifier or tag, denoting that commit.
4142
//
42-
// current is optional, denoting the current version of the module.
43-
// If query is "latest" or "patch", current will be returned if it is a newer
44-
// semantic version or if it is a chronologically later pseudoversion. This
45-
// prevents accidental downgrades from newer prerelease or development
46-
// versions.
43+
// current denotes the current version of the module; it may be "" if the
44+
// current version is unknown or should not be considered. If query is
45+
// "upgrade" or "patch", current will be returned if it is a newer
46+
// semantic version or a chronologically later pseudo-version than the
47+
// version that would otherwise be chosen. This prevents accidental downgrades
48+
// from newer pre-release or development versions.
4749
//
4850
// If the allowed function is non-nil, Query excludes any versions for which
4951
// allowed returns false.
@@ -81,6 +83,10 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
8183
ok = allowed
8284
mayUseLatest = true
8385

86+
case query == "upgrade":
87+
ok = allowed
88+
mayUseLatest = true
89+
8490
case query == "patch":
8591
if current == "" {
8692
ok = allowed
@@ -202,9 +208,9 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
202208
return nil, err
203209
}
204210

205-
// For "latest" and "patch", make sure we don't accidentally downgrade
211+
// For "upgrade" and "patch", make sure we don't accidentally downgrade
206212
// from a newer prerelease or from a chronologically newer pseudoversion.
207-
if current != "" && (query == "latest" || query == "patch") {
213+
if current != "" && (query == "upgrade" || query == "patch") {
208214
currentTime, err := modfetch.PseudoVersionTime(current)
209215
if semver.Compare(rev.Version, current) < 0 || (err == nil && rev.Time.Before(currentTime)) {
210216
return repo.Stat(current)

src/cmd/go/testdata/script/mod_get_main.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@ env GO111MODULE=on
44
# @patch and @latest within the main module refer to the current version.
55
# The main module won't be upgraded, but missing dependencies will be added.
66
cp go.mod.orig go.mod
7-
go get -d rsc.io/x@latest
7+
go get -d rsc.io/x
8+
grep 'rsc.io/quote v1.5.2' go.mod
9+
go get -d rsc.io/x@upgrade
810
grep 'rsc.io/quote v1.5.2' go.mod
911
cp go.mod.orig go.mod
1012
go get -d rsc.io/x@patch
1113
grep 'rsc.io/quote v1.5.2' go.mod
1214
cp go.mod.orig go.mod
1315

16+
# The main module cannot be updated to @latest, which is a specific version.
17+
! go get -d rsc.io/x@latest
18+
stderr '^go get rsc.io/x@latest: can.t request explicit version of path in main module$'
19+
1420
# The main module cannot be updated to a specific version.
1521
! go get rsc.io/x@v0.1.0
1622
stderr '^go get rsc.io/x@v0.1.0: can.t request explicit version of path in main module$'

src/cmd/go/testdata/script/mod_get_patterns.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ grep 'require rsc.io/quote' go.mod
1010

1111
cp go.mod.orig go.mod
1212
! go get -d rsc.io/quote/x...
13-
stderr 'go get rsc.io/quote/x...: module rsc.io/quote@latest \(v1.5.2\) found, but does not contain packages matching rsc.io/quote/x...'
13+
stderr 'go get rsc.io/quote/x...: module rsc.io/quote@upgrade \(v1.5.2\) found, but does not contain packages matching rsc.io/quote/x...'
1414
! grep 'require rsc.io/quote' go.mod
1515

1616
! go get -d rsc.io/quote/x/...
17-
stderr 'go get rsc.io/quote/x/...: module rsc.io/quote@latest \(v1.5.2\) found, but does not contain packages matching rsc.io/quote/x/...'
17+
stderr 'go get rsc.io/quote/x/...: module rsc.io/quote@upgrade \(v1.5.2\) found, but does not contain packages matching rsc.io/quote/x/...'
1818
! grep 'require rsc.io/quote' go.mod
1919

2020
# If a pattern matches no packages within a module, the module should not

src/cmd/go/testdata/script/mod_get_svn.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ stderr 'ReadZip not implemented for svn'
1717
# reasonable message instead of a panic.
1818
! go get -d vcs-test.golang.org/svn/nonexistent.svn
1919
! stderr panic
20-
stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "latest"'
20+
stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"'
2121

2222
-- go.mod --
2323
module golang/go/issues/28943/main

src/cmd/go/testdata/script/mod_get_upgrade_pseudo.txt

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,33 @@ env GO111MODULE=on
99
# The v0.1.1 pseudo-version is semantically higher than the latest tag.
1010
# The v0.0.0 pseudo-version is chronologically newer.
1111

12-
# 'get -u' should not downgrade to the (lower) tagged version.
12+
# Start at v0.1.1-0.20190429073117-b5426c86b553
1313
go get -d example.com/pseudoupgrade@b5426c8
14+
go list -m -u all
15+
stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
16+
17+
# 'get -u' should not downgrade to the (lower) tagged version.
1418
go get -d -u
1519
go list -m -u all
1620
stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
1721

18-
# 'get example.com/pseudoupgrade@latest' should not downgrade to
19-
# the (lower) tagged version.
20-
go get -d example.com/pseudoupgrade@latest
22+
# 'get example.com/pseudoupgrade@upgrade' should not downgrade.
23+
go get -d example.com/pseudoupgrade@upgrade
2124
go list -m all
2225
stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
2326

27+
# 'get example.com/pseudoupgrade' should not downgrade.
28+
# This is equivalent to 'get example.com/pseudoupgrade@upgrade'.
29+
go get -d example.com/pseudoupgrade
30+
go list -m all
31+
stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
32+
33+
# 'get example.com/pseudoupgrade@latest' should downgrade.
34+
# @latest should not consider the current version.
35+
go get -d example.com/pseudoupgrade@latest
36+
go list -m all
37+
stdout '^example.com/pseudoupgrade v0.1.0$'
38+
2439
# We should observe the same behavior with the newer pseudo-version.
2540
go get -d example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534
2641

@@ -29,12 +44,21 @@ go get -d -u
2944
go list -m -u all
3045
stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$'
3146

32-
# 'get example.com/pseudoupgrade@latest' should not downgrade to the
33-
# chronologically older tagged version.
34-
go get -d example.com/pseudoupgrade@latest
47+
# 'get example.com/pseudoupgrade@upgrade should not downgrade.
48+
go get -d example.com/pseudoupgrade@upgrade
3549
go list -m -u all
3650
stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$'
3751

52+
# 'get example.com/pseudoupgrade' should not downgrade.
53+
go get -d example.com/pseudoupgrade
54+
go list -m -u all
55+
stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$'
56+
57+
# 'get example.com/pseudoupgrade@latest' should downgrade.
58+
go get -d example.com/pseudoupgrade@latest
59+
go list -m -u all
60+
stdout '^example.com/pseudoupgrade v0.1.0$'
61+
3862
-- go.mod --
3963
module x
4064

0 commit comments

Comments
 (0)