Skip to content

Commit cbe2b14

Browse files
leitzlerJay Conrod
authored and
Jay Conrod
committed
cmd/go/internal/modfetch/codehost: fix pseudoversions for non-semver tags and tags on other branches
Pseudoversion determination depends in part on the results from gitRepo.RecentTag, which currently invokes: git describe --first-parent --always --abbrev=0 --match <prefix>v[0-9]*.[0-9]*.[0-9]* --tags <rev> The comment at #27171 (comment) describes some problems with the current approach. One problem is Docker and other repos can have tags that are not valid semver tags but that still match a glob pattern of v[0-9]*.[0-9]*.[0-9]* which are found by 'git describe' but then rejected by cmd/go, and hence those repos currently can end up with v0.0.0 pseudoversions instead of finding a proper semver tag to use as input to building a pseudoversion (when then causes problems when the v0.0.0 pseudoversion is fed into MVS). An example problematic tag is a date-based tag such as 'v18.06.16', which matches the glob pattern, but is not a valid semver tag (due to the leading 0 in '06'). Issues #31673, #31287, and #27171 also describe problems where the '--first-parent' argument to 'git describe' cause the current approach to miss relevant semver tags that were created on a separate branch and then subsequently merged to master. In #27171, Bryan described the base tag that is supposed to be used for pseudoversions as: "It is intended to be the semantically-latest tag that appears on any commit that is a (transitive) parent of the commit with the given hash, regardless of branches. (The pseudo-version is supposed to sort after every version — tagged or otherwise — that came before it, but before the next tag that a human might plausibly want to apply to the branch.)" This CL solves the glob problem and tags-on-other-branches problem more directly than the current approach: this CL gets the full list of tags that have been merged into the specific revision of interest, and then sorts and filters the results in cmd/go to select the semantically-latest valid semver tag. Fixes #31673 Fixes #31287 Updates #27171 Change-Id: I7c3e6b46b2b21dd60562cf2893b6bd2afaae61d5 Reviewed-on: https://go-review.googlesource.com/c/go/+/174061 Run-TryBot: Jay Conrod <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent e56c73f commit cbe2b14

File tree

3 files changed

+95
-4
lines changed

3 files changed

+95
-4
lines changed

src/cmd/go/internal/modfetch/codehost/git.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919

2020
"cmd/go/internal/lockedfile"
2121
"cmd/go/internal/par"
22+
"cmd/go/internal/semver"
2223
)
2324

2425
// GitRepo returns the code repository at the given Git remote reference.
@@ -652,16 +653,40 @@ func (r *gitRepo) RecentTag(rev, prefix string) (tag string, err error) {
652653
}
653654
rev = info.Name // expand hash prefixes
654655

655-
// describe sets tag and err using 'git describe' and reports whether the
656+
// describe sets tag and err using 'git for-each-ref' and reports whether the
656657
// result is definitive.
657658
describe := func() (definitive bool) {
658659
var out []byte
659-
out, err = Run(r.dir, "git", "describe", "--first-parent", "--always", "--abbrev=0", "--match", prefix+"v[0-9]*.[0-9]*.[0-9]*", "--tags", rev)
660+
out, err = Run(r.dir, "git", "for-each-ref", "--format", "%(refname)", "refs/tags", "--merged", rev)
660661
if err != nil {
661-
return true // Because we use "--always", describe should never fail.
662+
return true
663+
}
664+
665+
// prefixed tags aren't valid semver tags so compare without prefix, but only tags with correct prefix
666+
var highest string
667+
for _, line := range strings.Split(string(out), "\n") {
668+
line = strings.TrimSpace(line)
669+
// git do support lstrip in for-each-ref format, but it was added in v2.13.0. Stripping here
670+
// instead gives support for git v2.7.0.
671+
if !strings.HasPrefix(line, "refs/tags/") {
672+
continue
673+
}
674+
line = line[len("refs/tags/"):]
675+
676+
if !strings.HasPrefix(line, prefix) {
677+
continue
678+
}
679+
680+
semtag := line[len(prefix):]
681+
if semver.IsValid(semtag) {
682+
highest = semver.Max(highest, semtag)
683+
}
684+
}
685+
686+
if highest != "" {
687+
tag = prefix + highest
662688
}
663689

664-
tag = string(bytes.TrimSpace(out))
665690
return tag != "" && !AllHex(tag)
666691
}
667692

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
env GO111MODULE=on
2+
env GOPROXY=
3+
4+
# TODO(jayconrod): get test repo onto vcs-test.golang.org
5+
6+
# Testing that a pseudo-version is based on the semantically-latest
7+
# tag that appears in any commit that is a (transitive) parent of the commit
8+
# supplied to 'go get', regardless of branches
9+
10+
[!net] skip
11+
[!exec:git] skip
12+
13+
# For this test repository:
14+
# tag v0.2.1 is most recent tag on master itself
15+
# tag v0.2.2 is on branch2, which was then merged to master
16+
# commit 5aaa858 is on master at a later point
17+
#
18+
# The pseudo-version hence sorts immediately after v0.2.2 rather
19+
# than v0.2.1, even though the v0.2.2 tag is not on master.
20+
go get -m github.com/leitzler/tagtests@5aaa858
21+
go list -m all
22+
stdout '^github.com/leitzler/tagtests v0.2.3-0.20190424071028-5aaa858a59e2$'
23+
24+
-- go.mod --
25+
module x
26+
27+
go 1.12
28+
-- x.go --
29+
package x
30+
31+
import _ "github.com/leitzler/tagtests"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
env GO111MODULE=on
2+
env GOPROXY=
3+
4+
# TODO(jayconrod): get test repo onto vcs-test.golang.org
5+
6+
# Testing that a pseudo-version is based on the semantically-latest
7+
# prefixed tag in any commit that is a parent of the commit supplied
8+
# to 'go get', when using a repo with go.mod in a sub directory.
9+
10+
[!net] skip
11+
[!exec:git] skip
12+
13+
# For this test repository go.mod resides in sub/ (only):
14+
# master (372cb6e) is not tagged
15+
# tag v0.2.0 is most recent tag before master
16+
# tag sub/v0.0.10 is most recent tag before v0.2.0
17+
#
18+
# The pseudo-version is based on sub/v0.0.10, since v0.2.0 doesn't
19+
# contain the prefix.
20+
go get -m github.com/leitzler/prefixtagtests/sub
21+
go list -m all
22+
stdout '^github.com/leitzler/prefixtagtests/sub v0.0.10$'
23+
24+
go get -u -m github.com/leitzler/prefixtagtests/sub@372cb6e
25+
go list -m all
26+
stdout '^github.com/leitzler/prefixtagtests/sub v0.0.11-0.20190427183112-372cb6ea3fb5$'
27+
28+
-- go.mod --
29+
module x
30+
31+
go 1.12
32+
-- x.go --
33+
package x
34+
35+
import _ "github.com/leitzler/prefixtagtests/sub"

0 commit comments

Comments
 (0)