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