// 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. Neither @latest nor @patch will cause 'go get' to downgrade a module
-// in the build list if it is required at a newer pre-release version that is
-// newer than the latest released version.
+// module named by the given path. The suffix @upgrade is like @latest but
+// will not downgrade a module if it is already required at a revision or
+// pre-release version newer than the latest released version. The suffix
+// @patch requests the latest patch release: the latest released version
+// with the same major and minor version numbers as the currently required
+// version. Like @upgrade, @patch will not downgrade a module already required
+// at a newer version. If the path is not already required, @upgrade and @patch
+// are 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
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. Neither @latest nor @patch will cause 'go get' to downgrade a module
-in the build list if it is required at a newer pre-release version that is
-newer than the latest released version.
+module named by the given path. The suffix @upgrade is like @latest but
+will not downgrade a module if it is already required at a revision or
+pre-release version newer than the latest released version. The suffix
+@patch requests the latest patch release: the latest released version
+with the same major and minor version numbers as the currently required
+version. Like @upgrade, @patch will not downgrade a module already required
+at a newer version. If the path is not already required, @upgrade and @patch
+are 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
s = ""
}
if s == "true" {
- s = "latest"
+ s = "upgrade"
}
*v = upgradeFlag(s)
return nil
// if there is no "@"). path specifies the modules or packages to get.
path string
- // vers is the part of the argument after "@" (or "" if there is no "@").
- // vers specifies the module version to get.
+ // vers is the part of the argument after "@" or an implied
+ // "upgrade" or "patch" if there is no "@". vers specifies the
+ // module version to get.
vers string
}
}
switch getU {
- case "", "latest", "patch":
+ case "", "upgrade", "patch":
// ok
default:
base.Fatalf("go get: unknown upgrade flag -u=%s", getU)
// Parse command-line arguments and report errors. The command-line
// arguments are of the form path@version or simply path, with implicit
- // @latest. path@none is "downgrade away".
+ // @upgrade. path@none is "downgrade away".
var gets []getArg
var queries []*query
for _, arg := range search.CleanPatterns(args) {
- // Argument is module query path@vers, or else path with implicit @latest.
+ // Argument is path or path@vers.
path := arg
vers := ""
if i := strings.Index(arg, "@"); i >= 0 {
continue
}
- // 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)
+ // If no version suffix is specified, assume @upgrade.
+ // If -u=patch was specified, assume @patch instead.
+ if vers == "" {
+ if getU != "" {
+ vers = string(getU)
+ } else {
+ vers = "upgrade"
+ }
}
gets = append(gets, getArg{raw: arg, path: path, vers: vers})
// The argument is a package path.
if pkgs := modload.TargetPackages(path); len(pkgs) != 0 {
// The path is in the main module. Nothing to query.
- if vers != "" && vers != "latest" && vers != "patch" {
+ if vers != "upgrade" && vers != "patch" {
base.Errorf("go get %s: can't request explicit version of path in main module", arg)
}
continue
continue
}
- // If we're querying "latest" or "patch", we need to know the current
- // version of the module. For "latest", we want to avoid accidentally
+ // If we're querying "upgrade" or "patch", we need to know the current
+ // version of the module. For "upgrade", we want to avoid accidentally
// downgrading from a newer prerelease. For "patch", we need to query
// the correct minor version.
// Here, we check if "path" is the name of a module in the build list
base.Fatalf("go get: internal error: prevM may be set if and only if forceModulePath is set")
}
- if vers == "" || vers == "patch" && prevM.Version == "" {
- vers = "latest"
- }
-
if forceModulePath || !strings.Contains(path, "...") {
if path == modload.Target.Path {
if vers != "latest" {
// which may return a pseudoversion for the latest commit.
// Query "latest" returns the newest tagged version or the newest
// prerelease version if there are no non-prereleases, or repo.Latest
- // if there aren't any tagged versions. Since we're providing the previous
- // version, Query will confirm the latest version is actually newer
- // and will return the current version if not.
+ // if there aren't any tagged versions.
+ // If we're querying "upgrade" or "patch", Query will compare the current
+ // version against the chosen version and will return the current version
+ // if it is newer.
info, err := modload.Query(m.Path, string(getU), m.Version, modload.Allowed)
if err != nil {
// Report error but return m, to let version selection continue.
return
}
- if info, err := Query(m.Path, "latest", m.Version, Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
+ if info, err := Query(m.Path, "upgrade", m.Version, Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
m.Update = &modinfo.ModulePublic{
Path: m.Path,
Version: info.Version,
// tagged version, with non-prereleases preferred over prereleases.
// If there are no tagged versions in the repo, latest returns the most
// recent commit.
+// - the literal string "upgrade", equivalent to "latest" except that if
+// current is a newer version, current will be returned (see below).
// - the literal string "patch", denoting the latest available tagged version
-// with the same major and minor number as current. If current is "",
-// "patch" is equivalent to "latest".
+// with the same major and minor number as current (see below).
// - 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.
// with non-prereleases preferred over prereleases.
// - a repository commit identifier or tag, denoting that commit.
//
-// current is optional, denoting the current version of the module.
-// If query is "latest" or "patch", current will be returned if it is a newer
-// semantic version or if it is a chronologically later pseudoversion. This
-// prevents accidental downgrades from newer prerelease or development
-// versions.
+// current denotes the current version of the module; it may be "" if the
+// current version is unknown or should not be considered. If query is
+// "upgrade" or "patch", current will be returned if it is a newer
+// semantic version or a chronologically later pseudo-version than the
+// version that would otherwise be chosen. This prevents accidental downgrades
+// from newer pre-release or development versions.
//
// If the allowed function is non-nil, Query excludes any versions for which
// allowed returns false.
ok = allowed
mayUseLatest = true
+ case query == "upgrade":
+ ok = allowed
+ mayUseLatest = true
+
case query == "patch":
if current == "" {
ok = allowed
return nil, err
}
- // For "latest" and "patch", make sure we don't accidentally downgrade
+ // For "upgrade" and "patch", make sure we don't accidentally downgrade
// from a newer prerelease or from a chronologically newer pseudoversion.
- if current != "" && (query == "latest" || query == "patch") {
+ if current != "" && (query == "upgrade" || query == "patch") {
currentTime, err := modfetch.PseudoVersionTime(current)
if semver.Compare(rev.Version, current) < 0 || (err == nil && rev.Time.Before(currentTime)) {
return repo.Stat(current)
# @patch and @latest within the main module refer to the current version.
# The main module won't be upgraded, but missing dependencies will be added.
cp go.mod.orig go.mod
-go get -d rsc.io/x@latest
+go get -d rsc.io/x
+grep 'rsc.io/quote v1.5.2' go.mod
+go get -d rsc.io/x@upgrade
grep 'rsc.io/quote v1.5.2' go.mod
cp go.mod.orig go.mod
go get -d rsc.io/x@patch
grep 'rsc.io/quote v1.5.2' go.mod
cp go.mod.orig go.mod
+# The main module cannot be updated to @latest, which is a specific version.
+! go get -d rsc.io/x@latest
+stderr '^go get rsc.io/x@latest: can.t request explicit version of path in main module$'
+
# The main module cannot be updated to a specific version.
! go get rsc.io/x@v0.1.0
stderr '^go get rsc.io/x@v0.1.0: can.t request explicit version of path in main module$'
cp go.mod.orig go.mod
! go get -d rsc.io/quote/x...
-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...'
+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...'
! grep 'require rsc.io/quote' go.mod
! go get -d rsc.io/quote/x/...
-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/...'
+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/...'
! grep 'require rsc.io/quote' go.mod
# If a pattern matches no packages within a module, the module should not
# reasonable message instead of a panic.
! go get -d vcs-test.golang.org/svn/nonexistent.svn
! stderr panic
-stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "latest"'
+stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"'
-- go.mod --
module golang/go/issues/28943/main
# The v0.1.1 pseudo-version is semantically higher than the latest tag.
# The v0.0.0 pseudo-version is chronologically newer.
-# 'get -u' should not downgrade to the (lower) tagged version.
+# Start at v0.1.1-0.20190429073117-b5426c86b553
go get -d example.com/pseudoupgrade@b5426c8
+go list -m -u all
+stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
+
+# 'get -u' should not downgrade to the (lower) tagged version.
go get -d -u
go list -m -u all
stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
-# 'get example.com/pseudoupgrade@latest' should not downgrade to
-# the (lower) tagged version.
-go get -d example.com/pseudoupgrade@latest
+# 'get example.com/pseudoupgrade@upgrade' should not downgrade.
+go get -d example.com/pseudoupgrade@upgrade
go list -m all
stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
+# 'get example.com/pseudoupgrade' should not downgrade.
+# This is equivalent to 'get example.com/pseudoupgrade@upgrade'.
+go get -d example.com/pseudoupgrade
+go list -m all
+stdout '^example.com/pseudoupgrade v0.1.1-0.20190429073117-b5426c86b553$'
+
+# 'get example.com/pseudoupgrade@latest' should downgrade.
+# @latest should not consider the current version.
+go get -d example.com/pseudoupgrade@latest
+go list -m all
+stdout '^example.com/pseudoupgrade v0.1.0$'
+
# We should observe the same behavior with the newer pseudo-version.
go get -d example.com/pseudoupgrade@v0.0.0-20190430073000-30950c05d534
go list -m -u all
stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$'
-# 'get example.com/pseudoupgrade@latest' should not downgrade to the
-# chronologically older tagged version.
-go get -d example.com/pseudoupgrade@latest
+# 'get example.com/pseudoupgrade@upgrade should not downgrade.
+go get -d example.com/pseudoupgrade@upgrade
go list -m -u all
stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$'
+# 'get example.com/pseudoupgrade' should not downgrade.
+go get -d example.com/pseudoupgrade
+go list -m -u all
+stdout '^example.com/pseudoupgrade v0.0.0-20190430073000-30950c05d534$'
+
+# 'get example.com/pseudoupgrade@latest' should downgrade.
+go get -d example.com/pseudoupgrade@latest
+go list -m -u all
+stdout '^example.com/pseudoupgrade v0.1.0$'
+
-- go.mod --
module x