Skip to content

Commit e267cdc

Browse files
committed
better closest to head
1 parent 79cac1e commit e267cdc

File tree

7 files changed

+150
-79
lines changed

7 files changed

+150
-79
lines changed

pkg/gitsemver.go

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import (
88
)
99

1010
type GitSemVer struct {
11-
Git Gitter // Git
12-
Env Environment // environment
13-
DebugOut io.Writer // if nit nil, write debug output here
11+
Git Gitter // Git
12+
Env Environment // environment
13+
DebugOut io.Writer // if nit nil, write debug output here
14+
cleanstatus bool // true if there are no uncommitted changes in current tree
15+
tags []GitTag // tags
1416
}
1517

1618
// New returns a GitSemVer ready to examine
@@ -72,42 +74,65 @@ func (vs *GitSemVer) Debug(f string, args ...any) {
7274
}
7375
}
7476

77+
func (vs *GitSemVer) getTreeHash(repo, tag string) (gt GitTag) {
78+
for i := range vs.tags {
79+
if vs.tags[i].Tag == tag {
80+
return vs.tags[i]
81+
}
82+
}
83+
if commit, tree := vs.Git.GetHashes(repo, tag); commit != "" && tree != "" {
84+
gt.Tag = tag
85+
gt.Commit = commit
86+
gt.Tree = tree
87+
vs.tags = append(vs.tags, gt)
88+
}
89+
return
90+
}
91+
92+
func (vs *GitSemVer) examineTags(repo string) {
93+
vs.cleanstatus = vs.Git.CleanStatus(repo)
94+
headHashes := vs.getTreeHash(repo, "HEAD")
95+
vs.Debug("treehash %s: HEAD (clean: %v)\n", headHashes.Tree, vs.cleanstatus)
96+
for _, testtag := range vs.Git.GetTags(repo) {
97+
tagtreehashes := vs.getTreeHash(repo, testtag)
98+
if tagtreehashes.Tree != "" {
99+
vs.Debug("treehash %s: %q\n", tagtreehashes.Tree, testtag)
100+
if vs.cleanstatus && tagtreehashes.Tree == headHashes.Tree {
101+
return
102+
}
103+
}
104+
}
105+
}
106+
75107
// GetTag returns the semver git version tag matching the current tree, or
76108
// the closest semver tag if none match exactly. It also returns a bool
77109
// that is true if the tree hashes match and there are no uncommitted changes.
78110
func (vs *GitSemVer) GetTag(repo string) (string, bool) {
111+
vs.examineTags(repo)
79112
if tag := strings.TrimSpace(vs.Env.Getenv("CI_COMMIT_TAG")); tag != "" {
80113
return tag, true
81114
}
82-
treehashes := map[string]string{}
83-
cleanstatus := vs.Git.CleanStatus(repo)
84-
currtreehash := vs.Git.GetCurrentTreeHash(repo)
85-
if currtreehash != "" {
86-
vs.Debug("treehash %s: HEAD (clean: %v)\n", currtreehash, cleanstatus)
87-
for _, testtag := range vs.Git.GetTags(repo) {
88-
tagtreehash := vs.Git.GetTreeHash(repo, testtag)
89-
if _, ok := treehashes[tagtreehash]; !ok {
90-
treehashes[tagtreehash] = testtag
91-
}
92-
vs.Debug("treehash %s: %q\n", tagtreehash, testtag)
93-
if tagtreehash == currtreehash {
94-
return testtag, cleanstatus
95-
}
115+
head := vs.getTreeHash(repo, "HEAD")
116+
for _, gt := range vs.tags {
117+
if gt.Tag != "HEAD" && gt.Tree == head.Tree {
118+
return gt.Tag, vs.cleanstatus
96119
}
97120
}
98121
if tag := vs.Git.GetClosestTag(repo, "HEAD"); tag != "" {
99-
tagtreehash := vs.Git.GetTreeHash(repo, tag)
100-
if lasttag, ok := treehashes[tagtreehash]; ok {
101-
tag = lasttag
122+
found := vs.getTreeHash(repo, tag)
123+
for _, gt := range vs.tags {
124+
if gt.Tag != "HEAD" && gt.Tree == found.Tree {
125+
found = gt
126+
break
127+
}
102128
}
103-
vs.Debug("treehash %s: %q is closest to HEAD\n", tagtreehash, tag)
104-
return tag, cleanstatus && (tagtreehash == currtreehash)
129+
vs.Debug("treehash %s: %q is closest to HEAD\n", found.Tree, found.Tag)
130+
return found.Tag, vs.cleanstatus && (found.Tree == head.Tree)
105131
}
106132
return "v0.0.0", false
107133
}
108134

109135
func (vs *GitSemVer) getBranchGitHub(repo string) (branchName string) {
110-
//
111136
if branchName = strings.TrimSpace(vs.Env.Getenv("GITHUB_REF_NAME")); branchName != "" {
112137
if strings.TrimSpace(vs.Env.Getenv("GITHUB_REF_TYPE")) == "tag" {
113138
for _, branchName = range vs.Git.GetBranchesFromTag(repo, branchName) {
@@ -166,6 +191,7 @@ func (vs *GitSemVer) GetVersion(repo string) (vi VersionInfo, err error) {
166191
vi.Build = vs.GetBuild(repo)
167192
vi.Branch = vs.GetBranch(repo)
168193
vi.IsRelease = vs.IsReleaseBranch(vi.Branch)
194+
vi.Tags = vs.tags
169195
}
170196
}
171197
return

pkg/gitsemver_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,24 +88,27 @@ func Test_VersionStringer_IsReleaseBranch(t *testing.T) {
8888
func Test_VersionStringer_GetTag(t *testing.T) {
8989
env := MockEnvironment{}
9090
git := &MockGitter{}
91-
vs := gitsemver.GitSemVer{Git: git, Env: env}
9291

9392
var tag string
9493
var sametree bool
9594

95+
vs := gitsemver.GitSemVer{Git: git, Env: env}
9696
tag, sametree = vs.GetTag("/")
9797
isEqual(t, "v0.0.0", tag)
9898
isEqual(t, false, sametree)
9999

100+
vs = gitsemver.GitSemVer{Git: git, Env: env}
100101
tag, sametree = vs.GetTag(".")
101102
isEqual(t, "v6.0.0", tag)
102103
isEqual(t, false, sametree)
103104

105+
vs = gitsemver.GitSemVer{Git: git, Env: env}
104106
git.treehash = "tree-4"
105107
tag, sametree = vs.GetTag(".")
106108
isEqual(t, "v4.0.0", tag)
107109
isEqual(t, true, sametree)
108110

111+
vs = gitsemver.GitSemVer{Git: git, Env: env}
109112
git.treehash = ""
110113
env["CI_COMMIT_TAG"] = "v3"
111114
tag, sametree = vs.GetTag(".")
@@ -181,27 +184,30 @@ func Test_VersionStringer_GetBuild(t *testing.T) {
181184
func Test_VersionStringer_GetVersion(t *testing.T) {
182185
env := MockEnvironment{}
183186
git := &MockGitter{}
184-
vs := gitsemver.GitSemVer{Git: git, Env: env}
185187

188+
vs := gitsemver.GitSemVer{Git: git, Env: env}
186189
vi, err := vs.GetVersion("/") // invalid repo
187190
if err == nil {
188191
t.Error("no error")
189192
}
190193
isEqual(t, "", vi.Version())
191194

195+
vs = gitsemver.GitSemVer{Git: git, Env: env}
192196
vi, err = vs.GetVersion(".")
193197
if err != nil {
194198
t.Error(err)
195199
}
196200
isEqual(t, "v6.0.0-main.build", vi.Version())
197201

202+
vs = gitsemver.GitSemVer{Git: git, Env: env}
198203
git.treehash = "tree-6"
199204
vi, err = vs.GetVersion(".")
200205
if err != nil {
201206
t.Error(err)
202207
}
203208
isEqual(t, "v6.0.0", vi.Version())
204209

210+
vs = gitsemver.GitSemVer{Git: git, Env: env}
205211
git.treehash = ""
206212
env["CI_COMMIT_REF_NAME"] = "HEAD"
207213
vi, err = vs.GetVersion(".")
@@ -210,6 +216,7 @@ func Test_VersionStringer_GetVersion(t *testing.T) {
210216
}
211217
isEqual(t, "v6.0.0-head.build", vi.Version())
212218

219+
vs = gitsemver.GitSemVer{Git: git, Env: env}
213220
delete(env, "CI_COMMIT_REF_NAME")
214221
env["GITHUB_RUN_NUMBER"] = "789"
215222
vi, err = vs.GetVersion(".")
@@ -218,6 +225,7 @@ func Test_VersionStringer_GetVersion(t *testing.T) {
218225
}
219226
isEqual(t, "v6.0.0-main.789", vi.Version())
220227

228+
vs = gitsemver.GitSemVer{Git: git, Env: env}
221229
env["CI_COMMIT_REF_NAME"] = "*Branch--.--ONE*-*"
222230
env["GITHUB_RUN_NUMBER"] = "789"
223231
vi, err = vs.GetVersion(".")
@@ -226,6 +234,7 @@ func Test_VersionStringer_GetVersion(t *testing.T) {
226234
}
227235
isEqual(t, "v6.0.0-branch-one.789", vi.Version())
228236

237+
vs = gitsemver.GitSemVer{Git: git, Env: env}
229238
env["CI_COMMIT_REF_NAME"] = "main"
230239
vi, err = vs.GetVersion(".")
231240
if err != nil {

pkg/gitter.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ type Gitter interface {
2020
GetTags(repo string) (tags []string)
2121
// GetCurrentTreeHash returns the current tree hash.
2222
GetCurrentTreeHash(repo string) string
23-
// GetTreeHash returns the tree hash for the given tag or commit.
24-
GetTreeHash(repo, tag string) string
23+
// GetHashes returns the commit and tree hashes for the given tag.
24+
GetHashes(repo, tag string) (commit string, tree string)
2525
// GetClosestTag returns the closest semver tag for the given commit hash.
2626
GetClosestTag(repo, commit string) (tag string)
2727
// GetBranch returns the current branch in the repository or an empty string.
@@ -127,17 +127,27 @@ func (dg DefaultGitter) GetCurrentTreeHash(repo string) string {
127127
return ""
128128
}
129129

130-
// GetTagTreeHash returns the tree hash for the given tag or commit hash.
131-
func (dg DefaultGitter) GetTreeHash(repo, tag string) string {
132-
if b, _ := exec.Command(string(dg), "-C", repo, "rev-parse", tag+"^{tree}").Output(); len(b) > 0 /* #nosec G204 */ {
133-
return strings.TrimSpace(string(b))
130+
// GetHashes returns the commit and tree hashes for the given tag.
131+
func (dg DefaultGitter) GetHashes(repo, tag string) (commit, tree string) {
132+
if b, _ := exec.Command(string(dg), "-C", repo, "rev-parse", tag, tag+"^{tree}").Output(); len(b) > 0 /* #nosec G204 */ {
133+
hashes := strings.Split(strings.TrimSpace(string(b)), "\n")
134+
if len(hashes) == 2 {
135+
return hashes[0], hashes[1]
136+
}
134137
}
135-
return ""
138+
return
136139
}
137140

138141
// GetClosestTag returns the closest semver tag for the given commit hash.
139142
func (dg DefaultGitter) GetClosestTag(repo, commit string) (tag string) {
140143
_ = exec.Command(string(dg), "-C", repo, "fetch", "--unshallow", "--tags").Run() //#nosec G204
144+
if commit == "HEAD" {
145+
if b, _ := exec.Command(string(dg), "-C", repo, "rev-list", "--tags", "--max-count=1").Output(); len(b) > 0 /* #nosec G204 */ {
146+
if tag = dg.GetClosestTag(repo, strings.TrimSpace(string(b))); tag != "" {
147+
return
148+
}
149+
}
150+
}
141151
if b, _ := exec.Command(string(dg), "-C", repo, "describe", "--tags", "--match=v[0-9]*", "--match=[0-9]*", "--abbrev=0", commit).Output(); len(b) > 0 /* #nosec G204 */ {
142152
return strings.TrimSpace(string(b))
143153
}

pkg/gitter_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ func Test_DefaultGitter_GetTreeHash(t *testing.T) {
138138
if err != nil {
139139
t.Error(err)
140140
}
141-
if x := dg.GetTreeHash("/", "v1.0.0"); x != "" {
142-
t.Error(x)
141+
if x, y := dg.GetHashes("/", "v1.0.0"); x != "" || y != "" {
142+
t.Error(x, y)
143143
}
144-
if x := dg.GetTreeHash(".", "v0.0.2"); x != "57562d5fc36ef21a9785fb6afd128e87ab302fae" {
145-
t.Error(x)
144+
if x, y := dg.GetHashes(".", "v0.0.2"); x != "f9a1633a72ca04515d517a830a2e2835a98767f6" || y != "57562d5fc36ef21a9785fb6afd128e87ab302fae" {
145+
t.Error(x, y)
146146
}
147147
}
148148

@@ -158,6 +158,10 @@ func Test_DefaultGitter_GetClosestTag(t *testing.T) {
158158
if tag != "v0.0.2" {
159159
t.Error(tag)
160160
}
161+
tag = dg.GetClosestTag(".", "HEAD")
162+
if tag == "" {
163+
t.Error("no closest tag for HEAD")
164+
}
161165
}
162166

163167
func Test_DefaultGitter_GetBranchFromTag(t *testing.T) {

pkg/mockgitter_test.go

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,14 @@ func (me MockEnvironment) LookupEnv(key string) (val string, ok bool) {
1818
return
1919
}
2020

21-
type mockCommit struct {
22-
commithash string
23-
treehash string
24-
tag string
25-
}
26-
27-
var mockHistory = []mockCommit{
28-
{"HEAD", "tree-HEAD", ""},
29-
{"commit-6", "tree-6", "v6.0.0"},
30-
{"commit-5", "tree-5", ""},
31-
{"commit-4", "tree-4", "v4.0.0"},
32-
{"commit-3", "tree-3", ""},
33-
{"commit-2", "tree-2", "v2.0.0"},
34-
{"commit-1", "tree-1", ""},
21+
var mockHistory = []gitsemver.GitTag{
22+
{"HEAD", "commit-7", "tree-7"},
23+
{"v6.0.0", "commit-6", "tree-6"},
24+
{"", "commit-5", "tree-5"},
25+
{"v4.0.0", "commit-4", "tree-4"},
26+
{"", "commit-3", "tree-3"},
27+
{"v2.0.0", "commit-2", "tree-2"},
28+
{"", "commit-1", "tree-1"},
3529
}
3630

3731
type MockGitter struct {
@@ -48,20 +42,11 @@ func (mg *MockGitter) CheckGitRepo(dir string) (repo string, err error) {
4842
return dir, os.ErrNotExist
4943
}
5044

51-
func (mg *MockGitter) GetCommits(repo string) (commits []string) {
52-
if repo == "." {
53-
for _, h := range mockHistory {
54-
commits = append(commits, h.commithash)
55-
}
56-
}
57-
return
58-
}
59-
6045
func (mg *MockGitter) GetTags(repo string) (tags []string) {
6146
if repo == "." {
6247
for _, h := range mockHistory {
63-
if h.tag != "" {
64-
tags = append(tags, h.tag)
48+
if h.Tag != "" && h.Tag != "HEAD" {
49+
tags = append(tags, h.Tag)
6550
}
6651
}
6752
}
@@ -78,24 +63,34 @@ func (mg *MockGitter) GetCurrentTreeHash(repo string) string {
7863
return ""
7964
}
8065

81-
func (mg *MockGitter) GetTreeHash(repo, tag string) string {
66+
func (mg *MockGitter) GetHashes(repo, tag string) (commit, tree string) {
8267
if repo == "." {
8368
for _, h := range mockHistory {
84-
if h.commithash == tag || h.tag == tag {
85-
return h.treehash
69+
if h.Tag == tag {
70+
tree := h.Tree
71+
if tag == "HEAD" && mg.treehash != "" {
72+
tree = mg.treehash
73+
}
74+
return h.Commit, tree
8675
}
8776
}
8877
}
89-
return ""
78+
return "", ""
9079
}
9180

92-
func (mg *MockGitter) GetClosestTag(repo, commit string) (tag string) {
81+
func (mg *MockGitter) GetClosestTag(repo, from string) (tag string) {
9382
if repo == "." {
83+
if from == "HEAD" {
84+
from = mg.treehash
85+
if from == "" {
86+
from = mockHistory[0].Tree
87+
}
88+
}
9489
for i := range mockHistory {
95-
if mockHistory[i].commithash == commit {
90+
if mockHistory[i].Tree == from {
9691
for i < len(mockHistory) {
97-
if mockHistory[i].tag != "" {
98-
return mockHistory[i].tag
92+
if mockHistory[i].Tag != "" && mockHistory[i].Tag != "HEAD" {
93+
return mockHistory[i].Tag
9994
}
10095
i++
10196
}

0 commit comments

Comments
 (0)