github.com/gernest/nezuko@v0.1.2/internal/modget/get.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package modget implements the module-aware ``go get'' command.
     6  package modget
     7  
     8  import (
     9  	"fmt"
    10  	pathpkg "path"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/gernest/nezuko/internal/base"
    15  	"github.com/gernest/nezuko/internal/cfg"
    16  	"github.com/gernest/nezuko/internal/get"
    17  	"github.com/gernest/nezuko/internal/load"
    18  	"github.com/gernest/nezuko/internal/modfetch"
    19  	"github.com/gernest/nezuko/internal/modload"
    20  	"github.com/gernest/nezuko/internal/module"
    21  	"github.com/gernest/nezuko/internal/mvs"
    22  	"github.com/gernest/nezuko/internal/par"
    23  	"github.com/gernest/nezuko/internal/search"
    24  	"github.com/gernest/nezuko/internal/semver"
    25  	"github.com/gernest/nezuko/internal/str"
    26  )
    27  
    28  var CmdGet = &base.Command{
    29  	// Note: -d -m -u are listed explicitly because they are the most common get flags.
    30  	// Do not send CLs removing them because they're covered by [get flags].
    31  	UsageLine: "z get [-d] [-m] [-u] [-v] [-insecure] [build flags] [packages]",
    32  	Short:     "add dependencies to current module and install them",
    33  	Long: `
    34  Get resolves and adds dependencies to the current development module
    35  and then builds and installs them.
    36  
    37  The first step is to resolve which dependencies to add.
    38  
    39  For each named package or package pattern, get must decide which version of
    40  the corresponding module to use. By default, get chooses the latest tagged
    41  release version, such as v0.4.5 or v1.2.3. If there are no tagged release
    42  versions, get chooses the latest tagged prerelease version, such as
    43  v0.0.1-pre1. If there are no tagged versions at all, get chooses the latest
    44  known commit.
    45  
    46  This default version selection can be overridden by adding an @version
    47  suffix to the package argument, as in 'go get golang.org/x/text@v0.3.0'.
    48  For modules stored in source control repositories, the version suffix can
    49  also be a commit hash, branch identifier, or other syntax known to the
    50  source control system, as in 'go get golang.org/x/text@master'.
    51  The version suffix @latest explicitly requests the default behavior
    52  described above.
    53  
    54  If a module under consideration is already a dependency of the current
    55  development module, then get will update the required version.
    56  Specifying a version earlier than the current required version is valid and
    57  downgrades the dependency. The version suffix @none indicates that the
    58  dependency should be removed entirely, downgrading or removing modules
    59  depending on it as needed.
    60  
    61  Although get defaults to using the latest version of the module containing
    62  a named package, it does not use the latest version of that module's
    63  dependencies. Instead it prefers to use the specific dependency versions
    64  requested by that module. For example, if the latest A requires module
    65  B v1.2.3, while B v1.2.4 and v1.3.1 are also available, then 'go get A'
    66  will use the latest A but then use B v1.2.3, as requested by A. (If there
    67  are competing requirements for a particular module, then 'go get' resolves
    68  those requirements by taking the maximum requested version.)
    69  
    70  The -u flag instructs get to update dependencies to use newer minor or
    71  patch releases when available. Continuing the previous example,
    72  'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3).
    73  
    74  The -u=patch flag (not -u patch) instructs get to update dependencies
    75  to use newer patch releases when available. Continuing the previous example,
    76  'go get -u=patch A' will use the latest A with B v1.2.4 (not B v1.2.3).
    77  
    78  In general, adding a new dependency may require upgrading
    79  existing dependencies to keep a working build, and 'go get' does
    80  this automatically. Similarly, downgrading one dependency may
    81  require downgrading other dependencies, and 'go get' does
    82  this automatically as well.
    83  
    84  The -m flag instructs get to stop here, after resolving, upgrading,
    85  and downgrading modules and updating z.mod. When using -m,
    86  each specified package path must be a module path as well,
    87  not the import path of a package below the module root.
    88  
    89  The -insecure flag permits fetching from repositories and resolving
    90  custom domains using insecure schemes such as HTTP. Use with caution.
    91  
    92  The second step is to download (if needed), build, and install
    93  the named packages.
    94  
    95  If an argument names a module but not a package (because there is no
    96  Go source code in the module's root directory), then the install step
    97  is skipped for that argument, instead of causing a build failure.
    98  For example 'go get golang.org/x/perf' succeeds even though there
    99  is no code corresponding to that import path.
   100  
   101  Note that package patterns are allowed and are expanded after resolving
   102  the module versions. For example, 'go get golang.org/x/perf/cmd/...'
   103  adds the latest golang.org/x/perf and then installs the commands in that
   104  latest version.
   105  
   106  The -d flag instructs get to download the source code needed to build
   107  the named packages, including downloading necessary dependencies,
   108  but not to build and install them.
   109  
   110  With no package arguments, 'go get' applies to the main module,
   111  and to the Go package in the current directory, if any. In particular,
   112  'go get -u' and 'go get -u=patch' update all the dependencies of the
   113  main module. With no package arguments and also without -u,
   114  'go get' is not much more than 'go install', and 'go get -d' not much
   115  more than 'go list'.
   116  
   117  For more about modules, see 'go help modules'.
   118  
   119  For more about specifying packages, see 'go help packages'.
   120  
   121  This text describes the behavior of get using modules to manage source
   122  code and dependencies. If instead the go command is running in GOPATH
   123  mode, the details of get's flags and effects change, as does 'go help get'.
   124  See 'go help modules' and 'go help gopath-get'.
   125  
   126  See also: go build, go install, go clean, z mod.
   127  	`,
   128  }
   129  
   130  // Note that this help text is a stopgap to make the module-aware get help text
   131  // available even in non-module settings. It should be deleted when the old get
   132  // is deleted. It should NOT be considered to set a precedent of having hierarchical
   133  // help names with dashes.
   134  var HelpModuleGet = &base.Command{
   135  	UsageLine: "module-get",
   136  	Short:     "module-aware go get",
   137  	Long: `
   138  The 'go get' command changes behavior depending on whether the
   139  go command is running in module-aware mode or legacy GOPATH mode.
   140  This help text, accessible as 'go help module-get' even in legacy GOPATH mode,
   141  describes 'go get' as it operates in module-aware mode.
   142  
   143  Usage: ` + CmdGet.UsageLine + `
   144  ` + CmdGet.Long,
   145  }
   146  
   147  var (
   148  	getD = CmdGet.Flag.Bool("d", false, "")
   149  	getM = CmdGet.Flag.Bool("m", false, "")
   150  	getU upgradeFlag
   151  	// -insecure is get.Insecure
   152  	// -v is cfg.BuildV
   153  )
   154  
   155  // upgradeFlag is a custom flag.Value for -u.
   156  type upgradeFlag string
   157  
   158  func (*upgradeFlag) IsBoolFlag() bool { return true } // allow -u
   159  
   160  func (v *upgradeFlag) Set(s string) error {
   161  	if s == "false" {
   162  		s = ""
   163  	}
   164  	*v = upgradeFlag(s)
   165  	return nil
   166  }
   167  
   168  func (v *upgradeFlag) String() string { return "" }
   169  
   170  func init() {
   171  	CmdGet.Run = runGet // break init loop
   172  	CmdGet.Flag.BoolVar(&get.Insecure, "insecure", get.Insecure, "")
   173  	CmdGet.Flag.Var(&getU, "u", "")
   174  }
   175  
   176  // A task holds the state for processing a single get argument (path@vers).
   177  type task struct {
   178  	arg             string // original argument
   179  	index           int
   180  	path            string           // package path part of arg
   181  	forceModulePath bool             // path must be interpreted as a module path
   182  	vers            string           // version part of arg
   183  	m               module.Version   // module version indicated by argument
   184  	req             []module.Version // m's requirement list (not upgraded)
   185  }
   186  
   187  func runGet(cmd *base.Command, args []string) {
   188  	// -mod=readonly has no effect on "go get".
   189  	if cfg.BuildMod == "readonly" {
   190  		cfg.BuildMod = ""
   191  	}
   192  
   193  	switch getU {
   194  	case "", "patch", "true":
   195  		// ok
   196  	default:
   197  		base.Fatalf("z get: unknown upgrade flag -u=%s", getU)
   198  	}
   199  
   200  	modload.LoadBuildList()
   201  
   202  	// Do not allow any updating of z.mod until we've applied
   203  	// all the requested changes and checked that the result matches
   204  	// what was requested.
   205  	modload.DisallowWriteGoMod()
   206  
   207  	// Build task and install lists.
   208  	// The command-line arguments are of the form path@version
   209  	// or simply path, with implicit @latest. path@none is "downgrade away".
   210  	// At the end of the loop, we've resolved the list of arguments into
   211  	// a list of tasks (a path@vers that needs further processing)
   212  	// and a list of install targets (for the "go install" at the end).
   213  	var tasks []*task
   214  	var install []string
   215  	for _, arg := range search.CleanPatterns(args) {
   216  		// Argument is module query path@vers, or else path with implicit @latest.
   217  		path := arg
   218  		vers := ""
   219  		if i := strings.Index(arg, "@"); i >= 0 {
   220  			path, vers = arg[:i], arg[i+1:]
   221  		}
   222  		if strings.Contains(vers, "@") || arg != path && vers == "" {
   223  			base.Errorf("go get %s: invalid module version syntax", arg)
   224  			continue
   225  		}
   226  		if vers != "none" {
   227  			install = append(install, path)
   228  		}
   229  
   230  		// Deciding which module to upgrade/downgrade for a particular argument is difficult.
   231  		// Patterns only make it more difficult.
   232  		// We impose restrictions to avoid needing to interlace pattern expansion,
   233  		// like in modload.ImportPaths.
   234  		// Specifically, these patterns are supported:
   235  		//
   236  		//	- Relative paths like ../../foo or ../../foo... are restricted to matching directories
   237  		//	  in the current module and therefore map to the current module.
   238  		//	  It's possible that the pattern matches no packages, but we will still treat it
   239  		//	  as mapping to the current module.
   240  		//	  TODO: In followup, could just expand the full list and remove the discrepancy.
   241  		//	- The pattern "all" has its usual package meaning and maps to the list of modules
   242  		//	  from which the matched packages are drawn. This is potentially a subset of the
   243  		//	  module pattern "all". If module A requires B requires C but A does not import
   244  		//	  the parts of B that import C, the packages matched by "all" are only from A and B,
   245  		//	  so only A and B end up on the tasks list.
   246  		//	  TODO: Even in -m mode?
   247  		//	- The patterns "std" and "cmd" expand to packages in the standard library,
   248  		//	  which aren't upgradable, so we skip over those.
   249  		//	  In -m mode they expand to non-module-paths, so they are disallowed.
   250  		//	- Import path patterns like foo/bar... are matched against the module list,
   251  		//	  assuming any package match would imply a module pattern match.
   252  		//	  TODO: What about -m mode?
   253  		//	- Import paths without patterns are left as is, for resolution by getQuery (eventually modload.Import).
   254  		//
   255  		if search.IsRelativePath(path) {
   256  			// Check that this relative pattern only matches directories in the current module,
   257  			// and then record the current module as the target.
   258  			dir := path
   259  			if i := strings.Index(path, "..."); i >= 0 {
   260  				dir, _ = pathpkg.Split(path[:i])
   261  			}
   262  			abs, err := filepath.Abs(dir)
   263  			if err != nil {
   264  				base.Errorf("go get %s: %v", arg, err)
   265  				continue
   266  			}
   267  			if !str.HasFilePathPrefix(abs, modload.ModRoot()) {
   268  				base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot())
   269  				continue
   270  			}
   271  			// TODO: Check if abs is inside a nested module.
   272  			tasks = append(tasks, &task{arg: arg, path: modload.Target.Path, vers: ""})
   273  			continue
   274  		}
   275  		if path == "all" {
   276  			// TODO: If *getM, should this be the module pattern "all"?
   277  
   278  			// This is the package pattern "all" not the module pattern "all":
   279  			// enumerate all the modules actually needed by builds of the packages
   280  			// in the main module, not incidental modules that happen to be
   281  			// in the package graph (and therefore build list).
   282  			// Note that LoadALL may add new modules to the build list to
   283  			// satisfy new imports, but vers == "latest" implicitly anyway,
   284  			// so we'll assume that's OK.
   285  			seen := make(map[module.Version]bool)
   286  			pkgs := modload.LoadALL()
   287  			for _, pkg := range pkgs {
   288  				m := modload.PackageModule(pkg)
   289  				if m.Path != "" && !seen[m] {
   290  					seen[m] = true
   291  					tasks = append(tasks, &task{arg: arg, path: m.Path, vers: "latest", forceModulePath: true})
   292  				}
   293  			}
   294  			continue
   295  		}
   296  		if search.IsMetaPackage(path) {
   297  			// Already handled "all", so this must be "std" or "cmd",
   298  			// which are entirely in the standard library.
   299  			if path != arg {
   300  				base.Errorf("go get %s: cannot use pattern %q with explicit version", arg, arg)
   301  			}
   302  			if *getM {
   303  				base.Errorf("go get %s: cannot use pattern %q with -m", arg, arg)
   304  				continue
   305  			}
   306  			continue
   307  		}
   308  		if strings.Contains(path, "...") {
   309  			// Apply to modules in build list matched by pattern (golang.org/x/...), if any.
   310  			match := search.MatchPattern(path)
   311  			matched := false
   312  			for _, m := range modload.BuildList() {
   313  				if match(m.Path) || str.HasPathPrefix(path, m.Path) {
   314  					tasks = append(tasks, &task{arg: arg, path: m.Path, vers: vers, forceModulePath: true})
   315  					matched = true
   316  				}
   317  			}
   318  			// If matched, we're done.
   319  			// Otherwise assume pattern is inside a single module
   320  			// (golang.org/x/text/unicode/...) and leave for usual lookup.
   321  			// Unless we're using -m.
   322  			if matched {
   323  				continue
   324  			}
   325  			if *getM {
   326  				base.Errorf("go get %s: pattern matches no modules in build list", arg)
   327  				continue
   328  			}
   329  		}
   330  		tasks = append(tasks, &task{arg: arg, path: path, vers: vers})
   331  	}
   332  	base.ExitIfErrors()
   333  
   334  	// Now we've reduced the upgrade/downgrade work to a list of path@vers pairs (tasks).
   335  	// Resolve each one in parallel.
   336  	reqs := modload.Reqs()
   337  	var lookup par.Work
   338  	for _, t := range tasks {
   339  		lookup.Add(t)
   340  	}
   341  	lookup.Do(10, func(item interface{}) {
   342  		t := item.(*task)
   343  		if t.vers == "none" {
   344  			// Wait for downgrade step.
   345  			t.m = module.Version{Path: t.path, Version: "none"}
   346  			return
   347  		}
   348  		m, err := getQuery(t.path, t.vers, t.forceModulePath)
   349  		if err != nil {
   350  			base.Errorf("go get %v: %v", t.arg, err)
   351  			return
   352  		}
   353  		t.m = m
   354  	})
   355  	base.ExitIfErrors()
   356  
   357  	// Now we know the specific version of each path@vers.
   358  	// The final build list will be the union of three build lists:
   359  	//	1. the original build list
   360  	//	2. the modules named on the command line (other than @none)
   361  	//	3. the upgraded requirements of those modules (if upgrading)
   362  	// Start building those lists.
   363  	// This loop collects (2).
   364  	// Also, because the list of paths might have named multiple packages in a single module
   365  	// (or even the same package multiple times), now that we know the module for each
   366  	// package, this loop deduplicates multiple references to a given module.
   367  	// (If a module is mentioned multiple times, the listed target version must be the same each time.)
   368  	var named []module.Version
   369  	byPath := make(map[string]*task)
   370  	for _, t := range tasks {
   371  		prev, ok := byPath[t.m.Path]
   372  		if prev != nil && prev.m != t.m {
   373  			base.Errorf("go get: conflicting versions for module %s: %s and %s", t.m.Path, prev.m.Version, t.m.Version)
   374  			byPath[t.m.Path] = nil // sentinel to stop errors
   375  			continue
   376  		}
   377  		if ok {
   378  			continue // already added
   379  		}
   380  		byPath[t.m.Path] = t
   381  		if t.m.Version != "none" {
   382  			named = append(named, t.m)
   383  		}
   384  	}
   385  	base.ExitIfErrors()
   386  
   387  	// If the modules named on the command line have any dependencies
   388  	// and we're supposed to upgrade dependencies,
   389  	// chase down the full list of upgraded dependencies.
   390  	// This turns required from a not-yet-upgraded (3) to the final (3).
   391  	// (See list above.)
   392  	var required []module.Version
   393  	if getU != "" {
   394  		upgraded, err := mvs.UpgradeAll(upgradeTarget, &upgrader{
   395  			Reqs:    modload.Reqs(),
   396  			targets: named,
   397  			patch:   getU == "patch",
   398  			tasks:   byPath,
   399  		})
   400  		if err != nil {
   401  			base.Fatalf("go get: %v", err)
   402  		}
   403  		required = upgraded[1:] // slice off upgradeTarget
   404  		base.ExitIfErrors()
   405  	}
   406  
   407  	// Put together the final build list as described above (1) (2) (3).
   408  	// If we're not using -u, then len(required) == 0 and ReloadBuildList
   409  	// chases down the dependencies of all the named module versions
   410  	// in one operation.
   411  	var list []module.Version
   412  	list = append(list, modload.BuildList()...)
   413  	list = append(list, named...)
   414  	list = append(list, required...)
   415  	modload.SetBuildList(list)
   416  	modload.ReloadBuildList() // note: does not update z.mod
   417  	base.ExitIfErrors()
   418  
   419  	// Scan for and apply any needed downgrades.
   420  	var down []module.Version
   421  	for _, m := range modload.BuildList() {
   422  		t := byPath[m.Path]
   423  		if t != nil && semver.Compare(m.Version, t.m.Version) > 0 {
   424  			down = append(down, module.Version{Path: m.Path, Version: t.m.Version})
   425  		}
   426  	}
   427  	if len(down) > 0 {
   428  		list, err := mvs.Downgrade(modload.Target, modload.Reqs(), down...)
   429  		if err != nil {
   430  			base.Fatalf("go get: %v", err)
   431  		}
   432  		modload.SetBuildList(list)
   433  		modload.ReloadBuildList() // note: does not update z.mod
   434  	}
   435  	base.ExitIfErrors()
   436  
   437  	// Scan for any upgrades lost by the downgrades.
   438  	lost := make(map[string]string)
   439  	for _, m := range modload.BuildList() {
   440  		t := byPath[m.Path]
   441  		if t != nil && semver.Compare(m.Version, t.m.Version) != 0 {
   442  			lost[m.Path] = m.Version
   443  		}
   444  	}
   445  	if len(lost) > 0 {
   446  		desc := func(m module.Version) string {
   447  			s := m.Path + "@" + m.Version
   448  			t := byPath[m.Path]
   449  			if t != nil && t.arg != s {
   450  				s += " from " + t.arg
   451  			}
   452  			return s
   453  		}
   454  		downByPath := make(map[string]module.Version)
   455  		for _, d := range down {
   456  			downByPath[d.Path] = d
   457  		}
   458  		var buf strings.Builder
   459  		fmt.Fprintf(&buf, "go get: inconsistent versions:")
   460  		for _, t := range tasks {
   461  			if lost[t.m.Path] == "" {
   462  				continue
   463  			}
   464  			// We lost t because its build list requires a newer version of something in down.
   465  			// Figure out exactly what.
   466  			// Repeatedly constructing the build list is inefficient
   467  			// if there are MANY command-line arguments,
   468  			// but at least all the necessary requirement lists are cached at this point.
   469  			list, err := mvs.BuildList(t.m, reqs)
   470  			if err != nil {
   471  				base.Fatalf("go get: %v", err)
   472  			}
   473  
   474  			fmt.Fprintf(&buf, "\n\t%s", desc(t.m))
   475  			sep := " requires"
   476  			for _, m := range list {
   477  				if down, ok := downByPath[m.Path]; ok && semver.Compare(down.Version, m.Version) < 0 {
   478  					fmt.Fprintf(&buf, "%s %s@%s (not %s)", sep, m.Path, m.Version, desc(down))
   479  					sep = ","
   480  				}
   481  			}
   482  			if sep != "," {
   483  				// We have no idea why this happened.
   484  				// At least report the problem.
   485  				fmt.Fprintf(&buf, " ended up at %v unexpectedly (please report at golang.org/issue/new)", lost[t.m.Path])
   486  			}
   487  		}
   488  		base.Fatalf("%v", buf.String())
   489  	}
   490  
   491  	// Everything succeeded. Update z.mod.
   492  	modload.AllowWriteGoMod()
   493  	modload.WriteGoMod()
   494  	modload.WriteZigBuildFile()
   495  
   496  	// If -m was specified, we're done after the module work. No download, no build.
   497  	if *getM {
   498  		return
   499  	}
   500  
   501  	if len(install) > 0 {
   502  		// All requested versions were explicitly @none.
   503  		// Note that 'go get -u' without any arguments results in len(install) == 1:
   504  		// search.CleanImportPaths returns "." for empty args.
   505  		// TODO(gernest): BuildInit()
   506  		pkgs := load.PackagesAndErrors(install)
   507  		var todo []*load.Package
   508  		for _, p := range pkgs {
   509  			// Ignore "no Go source files" errors for 'go get' operations on modules.
   510  			if p.Error != nil {
   511  				if len(args) == 0 && getU != "" && strings.HasPrefix(p.Error.Err, "no Go files") {
   512  					// Upgrading modules: skip the implicitly-requested package at the
   513  					// current directory, even if it is not tho module root.
   514  					continue
   515  				}
   516  				if strings.Contains(p.Error.Err, "cannot find module providing") && modload.ModuleInfo(p.ImportPath) != nil {
   517  					// Explicitly-requested module, but it doesn't contain a package at the
   518  					// module root.
   519  					continue
   520  				}
   521  				base.Errorf("%s", p.Error)
   522  			}
   523  			todo = append(todo, p)
   524  		}
   525  		base.ExitIfErrors()
   526  
   527  		// If -d was specified, we're done after the download: no build.
   528  		// (The load.PackagesAndErrors is what did the download
   529  		// of the named packages and their dependencies.)
   530  		if len(todo) > 0 && !*getD {
   531  			//TODO(gernest): install packages
   532  		}
   533  	}
   534  }
   535  
   536  // getQuery evaluates the given package path, version pair
   537  // to determine the underlying module version being requested.
   538  // If forceModulePath is set, getQuery must interpret path
   539  // as a module path.
   540  func getQuery(path, vers string, forceModulePath bool) (module.Version, error) {
   541  	if vers == "" {
   542  		vers = "latest"
   543  	}
   544  
   545  	// First choice is always to assume path is a module path.
   546  	// If that works out, we're done.
   547  	info, err := modload.Query(path, vers, modload.Allowed)
   548  	if err == nil {
   549  		return module.Version{Path: path, Version: info.Version}, nil
   550  	}
   551  
   552  	// Even if the query fails, if the path must be a real module, then report the query error.
   553  	if forceModulePath || *getM {
   554  		return module.Version{}, err
   555  	}
   556  
   557  	// Otherwise, try a package path.
   558  	m, _, err := modload.QueryPackage(path, vers, modload.Allowed)
   559  	return m, err
   560  }
   561  
   562  // An upgrader adapts an underlying mvs.Reqs to apply an
   563  // upgrade policy to a list of targets and their dependencies.
   564  // If patch=false, the upgrader implements "get -u".
   565  // If patch=true, the upgrader implements "get -u=patch".
   566  type upgrader struct {
   567  	mvs.Reqs
   568  	targets []module.Version
   569  	patch   bool
   570  	tasks   map[string]*task
   571  }
   572  
   573  // upgradeTarget is a fake "target" requiring all the modules to be upgraded.
   574  var upgradeTarget = module.Version{Path: "upgrade target", Version: ""}
   575  
   576  // Required returns the requirement list for m.
   577  // Other than the upgradeTarget, we defer to u.Reqs.
   578  func (u *upgrader) Required(m module.Version) ([]module.Version, error) {
   579  	if m == upgradeTarget {
   580  		return u.targets, nil
   581  	}
   582  	return u.Reqs.Required(m)
   583  }
   584  
   585  // Upgrade returns the desired upgrade for m.
   586  // If m is a tagged version, then Upgrade returns the latest tagged version.
   587  // If m is a pseudo-version, then Upgrade returns the latest tagged version
   588  // when that version has a time-stamp newer than m.
   589  // Otherwise Upgrade returns m (preserving the pseudo-version).
   590  // This special case prevents accidental downgrades
   591  // when already using a pseudo-version newer than the latest tagged version.
   592  func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
   593  	// Allow pkg@vers on the command line to override the upgrade choice v.
   594  	// If t's version is < v, then we're going to downgrade anyway,
   595  	// and it's cleaner to avoid moving back and forth and picking up
   596  	// extraneous other newer dependencies.
   597  	// If t's version is > v, then we're going to upgrade past v anyway,
   598  	// and again it's cleaner to avoid moving back and forth picking up
   599  	// extraneous other newer dependencies.
   600  	if t := u.tasks[m.Path]; t != nil {
   601  		return t.m, nil
   602  	}
   603  
   604  	// Note that query "latest" is not the same as
   605  	// using repo.Latest.
   606  	// The query only falls back to untagged versions
   607  	// if nothing is tagged. The Latest method
   608  	// only ever returns untagged versions,
   609  	// which is not what we want.
   610  	query := "latest"
   611  	if u.patch {
   612  		// For patch upgrade, query "v1.2".
   613  		query = semver.MajorMinor(m.Version)
   614  	}
   615  	info, err := modload.Query(m.Path, query, modload.Allowed)
   616  	if err != nil {
   617  		// Report error but return m, to let version selection continue.
   618  		// (Reporting the error will fail the command at the next base.ExitIfErrors.)
   619  		// Special case: if the error is "no matching versions" then don't
   620  		// even report the error. Because Query does not consider pseudo-versions,
   621  		// it may happen that we have a pseudo-version but during -u=patch
   622  		// the query v0.0 matches no versions (not even the one we're using).
   623  		if !strings.Contains(err.Error(), "no matching versions") {
   624  			base.Errorf("go get: upgrading %s@%s: %v", m.Path, m.Version, err)
   625  		}
   626  		return m, nil
   627  	}
   628  
   629  	// If we're on a later prerelease, keep using it,
   630  	// even though normally an Upgrade will ignore prereleases.
   631  	if semver.Compare(info.Version, m.Version) < 0 {
   632  		return m, nil
   633  	}
   634  
   635  	// If we're on a pseudo-version chronologically after the latest tagged version, keep using it.
   636  	// This avoids some accidental downgrades.
   637  	if mTime, err := modfetch.PseudoVersionTime(m.Version); err == nil && info.Time.Before(mTime) {
   638  		return m, nil
   639  	}
   640  
   641  	return module.Version{Path: m.Path, Version: info.Version}, nil
   642  }