@@ -21,7 +21,6 @@ import (
21
21
"cmd/go/internal/work"
22
22
"fmt"
23
23
"os"
24
- pathpkg "path"
25
24
"path/filepath"
26
25
"strings"
27
26
)
@@ -49,8 +48,6 @@ suffix to the package argument, as in 'go get golang.org/x/
[email protected] '.
49
48
For modules stored in source control repositories, the version suffix can
50
49
also be a commit hash, branch identifier, or other syntax known to the
51
50
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
51
55
52
If a module under consideration is already a dependency of the current
56
53
development module, then get will update the required version.
@@ -59,6 +56,13 @@ downgrades the dependency. The version suffix @none indicates that the
59
56
dependency should be removed entirely, downgrading or removing modules
60
57
depending on it as needed.
61
58
59
+ The version suffix @latest explicitly requests the latest minor release of the
60
+ given path.
61
+
62
+ The suffix @patch requests the latest patch release: if the path is already in
63
+ the build list, the selected version will have the same minor version.
64
+ If the path is not already in the build list, @patch is equivalent to @latest.
65
+
62
66
Although get defaults to using the latest version of the module containing
63
67
a named package, it does not use the latest version of that module's
64
68
dependencies. Instead it prefers to use the specific dependency versions
@@ -72,9 +76,11 @@ The -u flag instructs get to update dependencies to use newer minor or
72
76
patch releases when available. Continuing the previous example,
73
77
'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3).
74
78
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).
79
+ The -u=patch flag (not -u patch) also instructs get to update dependencies,
80
+ but changes the default to select patch releases.
81
+ Continuing the previous example,
82
+ 'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3),
83
+ while 'go get -u=patch A' will use a patch release of A instead.
78
84
79
85
In general, adding a new dependency may require upgrading
80
86
existing dependencies to keep a working build, and 'go get' does
@@ -165,6 +171,9 @@ func (v *upgradeFlag) Set(s string) error {
165
171
if s == "false" {
166
172
s = ""
167
173
}
174
+ if s == "true" {
175
+ s = "latest"
176
+ }
168
177
* v = upgradeFlag (s )
169
178
return nil
170
179
}
@@ -180,12 +189,12 @@ func init() {
180
189
181
190
// A task holds the state for processing a single get argument (path@vers).
182
191
type task struct {
183
- arg string // original argument
184
- index int
192
+ arg string // original argument
185
193
path string // package path part of arg
186
194
forceModulePath bool // path must be interpreted as a module path
187
195
vers string // version part of arg
188
196
m module.Version // module version indicated by argument
197
+ prevM module.Version // module version from initial build list
189
198
req []module.Version // m's requirement list (not upgraded)
190
199
}
191
200
@@ -196,7 +205,7 @@ func runGet(cmd *base.Command, args []string) {
196
205
}
197
206
198
207
switch getU {
199
- case "" , "patch " , "true " :
208
+ case "" , "latest " , "patch " :
200
209
// ok
201
210
default :
202
211
base .Fatalf ("go get: unknown upgrade flag -u=%s" , getU )
@@ -230,6 +239,7 @@ func runGet(cmd *base.Command, args []string) {
230
239
// and a list of install targets (for the "go install" at the end).
231
240
var tasks []* task
232
241
var install []string
242
+ var needModule []* task
233
243
for _ , arg := range search .CleanPatterns (args ) {
234
244
// Argument is module query path@vers, or else path with implicit @latest.
235
245
path := arg
@@ -245,6 +255,12 @@ func runGet(cmd *base.Command, args []string) {
245
255
install = append (install , path )
246
256
}
247
257
258
+ // If the user runs 'go get -u=patch some/module', update some/module to a
259
+ // patch release, not a minor version.
260
+ if vers == "" && getU != "" {
261
+ vers = string (getU )
262
+ }
263
+
248
264
// Deciding which module to upgrade/downgrade for a particular argument is difficult.
249
265
// Patterns only make it more difficult.
250
266
// We impose restrictions to avoid needing to interlace pattern expansion,
@@ -271,25 +287,43 @@ func runGet(cmd *base.Command, args []string) {
271
287
// - Import paths without patterns are left as is, for resolution by getQuery (eventually modload.Import).
272
288
//
273
289
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
290
+ t := & task {arg : arg , path : modload .Target .Path , vers : "" , prevM : modload .Target , forceModulePath : true }
291
+
292
+ // If the path is relative, always upgrade the entire main module.
293
+ // (TODO(golang.org/issue/26902): maybe we should upgrade the modules
294
+ // containing the dependencies of the requested packages instead.)
295
+ //
296
+ // If the path is explicit, at least check that it is a package in the main module.
297
+ if len (args ) > 0 {
298
+ if * getM {
299
+ base .Errorf ("go get %s: -m requires a module path, but a relative path must be a package in the main module" , arg )
300
+ continue
301
+ }
302
+
303
+ pkgPath := modload .DirImportPath (filepath .FromSlash (path ))
304
+ if pkgs := modload .TargetPackages (pkgPath ); len (pkgs ) == 0 {
305
+ if strings .Contains (path , "..." ) {
306
+ fmt .Fprintf (os .Stderr , "go get %s: warning: pattern patched no packages" , arg )
307
+ } else {
308
+ abs , err := filepath .Abs (path )
309
+ if err != nil {
310
+ abs = path
311
+ }
312
+ base .Errorf ("go get %s: path %s is not in module rooted at %s" , arg , abs , modload .ModRoot ())
313
+ }
314
+ continue
315
+ }
284
316
}
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
317
+
318
+ switch vers {
319
+ case "" , "latest" , "patch" :
320
+ tasks = append (tasks , t )
321
+ default :
322
+ base .Errorf ("go get %s: can't request explicit version of path in main module" , arg )
288
323
}
289
- // TODO: Check if abs is inside a nested module.
290
- tasks = append (tasks , & task {arg : arg , path : modload .Target .Path , vers : "" })
291
324
continue
292
325
}
326
+
293
327
if path == "all" {
294
328
// TODO: If *getM, should this be the module pattern "all"?
295
329
@@ -306,30 +340,19 @@ func runGet(cmd *base.Command, args []string) {
306
340
m := modload .PackageModule (pkg )
307
341
if m .Path != "" && ! seen [m ] {
308
342
seen [m ] = true
309
- tasks = append (tasks , & task {arg : arg , path : m .Path , vers : "latest" , forceModulePath : true })
343
+ tasks = append (tasks , & task {arg : arg , path : m .Path , vers : vers , prevM : m , forceModulePath : true })
310
344
}
311
345
}
312
346
continue
313
347
}
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
- }
348
+
326
349
if strings .Contains (path , "..." ) {
327
350
// Apply to modules in build list matched by pattern (golang.org/x/...), if any.
328
351
match := search .MatchPattern (path )
329
352
matched := false
330
353
for _ , m := range modload .BuildList () {
331
354
if match (m .Path ) || str .HasPathPrefix (path , m .Path ) {
332
- tasks = append (tasks , & task {arg : arg , path : m .Path , vers : vers , forceModulePath : true })
355
+ tasks = append (tasks , & task {arg : arg , path : m .Path , vers : vers , prevM : m , forceModulePath : true })
333
356
matched = true
334
357
}
335
358
}
@@ -345,10 +368,66 @@ func runGet(cmd *base.Command, args []string) {
345
368
continue
346
369
}
347
370
}
348
- tasks = append (tasks , & task {arg : arg , path : path , vers : vers })
371
+ t := & task {arg : arg , path : path , vers : vers }
372
+ if vers == "patch" {
373
+ if * getM {
374
+ for _ , m := range modload .BuildList () {
375
+ if m .Path == path {
376
+ t .prevM = m
377
+ break
378
+ }
379
+ }
380
+ tasks = append (tasks , t )
381
+ } else {
382
+ // We need to know the module containing t so that we can restrict the patch to its minor version.
383
+ needModule = append (needModule , t )
384
+ }
385
+ } else {
386
+ // The requested version of path doesn't depend on the existing version,
387
+ // so don't bother resolving it.
388
+ tasks = append (tasks , t )
389
+ }
349
390
}
350
391
base .ExitIfErrors ()
351
392
393
+ if len (needModule ) > 0 {
394
+ paths := make ([]string , len (needModule ))
395
+ for i , t := range needModule {
396
+ paths [i ] = t .path
397
+ }
398
+ matches := modload .ImportPaths (paths )
399
+ if len (matches ) != len (paths ) {
400
+ base .Fatalf ("go get: internal error: ImportPaths resolved %d paths to %d matches" , len (paths ), len (matches ))
401
+ }
402
+
403
+ for i , match := range matches {
404
+ t := needModule [i ]
405
+ if len (match .Pkgs ) == 0 {
406
+ // Let modload.Query resolve the path during task processing.
407
+ tasks = append (tasks , t )
408
+ continue
409
+ }
410
+
411
+ allStd := true
412
+ for _ , pkg := range match .Pkgs {
413
+ m := modload .PackageModule (pkg )
414
+ if m .Path == "" {
415
+ // pkg is in the standard library.
416
+ } else {
417
+ allStd = false
418
+ tasks = append (tasks , & task {arg : t .arg , path : pkg , vers : t .vers , prevM : m })
419
+ }
420
+ }
421
+ if allStd {
422
+ if * getM {
423
+ base .Errorf ("go get %s: cannot use pattern %q with -m" , t .arg , t .arg )
424
+ } else if t .path != t .arg {
425
+ base .Errorf ("go get %s: cannot use pattern %q with explicit version" , t .arg , t .arg )
426
+ }
427
+ }
428
+ }
429
+ }
430
+
352
431
// Now we've reduced the upgrade/downgrade work to a list of path@vers pairs (tasks).
353
432
// Resolve each one in parallel.
354
433
reqs := modload .Reqs ()
@@ -363,7 +442,7 @@ func runGet(cmd *base.Command, args []string) {
363
442
t .m = module.Version {Path : t .path , Version : "none" }
364
443
return
365
444
}
366
- m , err := getQuery (t .path , t .vers , t .forceModulePath )
445
+ m , err := getQuery (t .path , t .vers , t .prevM , t . forceModulePath )
367
446
if err != nil {
368
447
base .Errorf ("go get %v: %v" , t .arg , err )
369
448
return
@@ -412,7 +491,6 @@ func runGet(cmd *base.Command, args []string) {
412
491
upgraded , err := mvs .UpgradeAll (upgradeTarget , & upgrader {
413
492
Reqs : modload .Reqs (),
414
493
targets : named ,
415
- patch : getU == "patch" ,
416
494
tasks : byPath ,
417
495
})
418
496
if err != nil {
@@ -554,9 +632,16 @@ func runGet(cmd *base.Command, args []string) {
554
632
// to determine the underlying module version being requested.
555
633
// If forceModulePath is set, getQuery must interpret path
556
634
// as a module path.
557
- func getQuery (path , vers string , forceModulePath bool ) (module.Version , error ) {
558
- if vers == "" {
635
+ func getQuery (path , vers string , prevM module.Version , forceModulePath bool ) (module.Version , error ) {
636
+ switch vers {
637
+ case "" :
559
638
vers = "latest"
639
+ case "patch" :
640
+ if prevM .Version == "" {
641
+ vers = "latest"
642
+ } else {
643
+ vers = semver .MajorMinor (prevM .Version )
644
+ }
560
645
}
561
646
562
647
// First choice is always to assume path is a module path.
@@ -625,7 +710,7 @@ func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
625
710
// only ever returns untagged versions,
626
711
// which is not what we want.
627
712
query := "latest"
628
- if u . patch {
713
+ if getU == " patch" {
629
714
// For patch upgrade, query "v1.2".
630
715
query = semver .MajorMinor (m .Version )
631
716
}
0 commit comments