@@ -10,6 +10,7 @@ import (
1010 "github.com/go-git/go-git/v5/config"
1111 "github.com/go-git/go-git/v5/plumbing"
1212 "github.com/go-git/go-git/v5/plumbing/object"
13+ "github.com/go-git/go-git/v5/plumbing/storer"
1314 "github.com/go-git/go-git/v5/plumbing/transport/http"
1415)
1516
@@ -39,14 +40,23 @@ func NewGitExtendedRepository() (GitExtendedRepository, error) {
3940
4041// LatestTag returns the latest git tag.
4142func (r * gitRepository ) LatestTag (_ context.Context ) (string , error ) {
43+ // First, try to fetch tags from remote to ensure we have the latest
44+ remote , err := r .repo .Remote ("origin" )
45+ if err == nil {
46+ // Fetch tags from remote (ignore error if already up to date)
47+ _ = remote .Fetch (& git.FetchOptions {
48+ RefSpecs : []config.RefSpec {
49+ config .RefSpec ("+refs/tags/*:refs/tags/*" ),
50+ },
51+ Auth : r .getAuth (),
52+ })
53+ }
4254 tagRefs , err := r .repo .Tags ()
4355 if err != nil {
4456 return "" , fmt .Errorf ("failed to get tags: %w" , err )
4557 }
46-
4758 var latestTag string
4859 var latestCommitTime time.Time
49-
5060 if err := tagRefs .ForEach (func (ref * plumbing.Reference ) error {
5161 // Try to get the commit directly first (lightweight tag)
5262 commit , err := r .repo .CommitObject (ref .Hash ())
@@ -61,7 +71,6 @@ func (r *gitRepository) LatestTag(_ context.Context) (string, error) {
6171 return nil // Skip if we can't get the commit
6272 }
6373 }
64-
6574 if commit .Committer .When .After (latestCommitTime ) {
6675 latestCommitTime = commit .Committer .When
6776 latestTag = ref .Name ().Short ()
@@ -70,27 +79,72 @@ func (r *gitRepository) LatestTag(_ context.Context) (string, error) {
7079 }); err != nil {
7180 return "" , fmt .Errorf ("failed to iterate tags: %w" , err )
7281 }
73-
7482 return latestTag , nil
7583}
7684
7785// CommitsSinceTag returns the number of commits since the given tag.
7886func (r * gitRepository ) CommitsSinceTag (_ context.Context , tag string ) (int , error ) {
87+ // First, try to fetch the tag from remote if it doesn't exist locally
7988 tagRef , err := r .repo .Tag (tag )
8089 if err != nil {
81- return 0 , fmt .Errorf ("failed to get tag %s: %w" , tag , err )
90+ // Tag doesn't exist locally, try to fetch it from remote
91+ remote , err := r .repo .Remote ("origin" )
92+ if err != nil {
93+ return 0 , fmt .Errorf ("failed to get remote: %w" , err )
94+ }
95+ // Fetch tags from remote
96+ if err := remote .Fetch (& git.FetchOptions {
97+ RefSpecs : []config.RefSpec {
98+ config .RefSpec ("+refs/tags/*:refs/tags/*" ),
99+ },
100+ Auth : r .getAuth (),
101+ }); err != nil && err != git .NoErrAlreadyUpToDate {
102+ return 0 , fmt .Errorf ("failed to fetch tags from remote: %w" , err )
103+ }
104+ // Try to get the tag again
105+ tagRef , err = r .repo .Tag (tag )
106+ if err != nil {
107+ return 0 , fmt .Errorf ("failed to get tag %s after fetching: %w" , tag , err )
108+ }
82109 }
83-
84- commits , err := r .repo .Log (& git.LogOptions {From : tagRef .Hash ()})
110+ // Resolve the tag to a commit hash (handles lightweight *and* annotated tags)
111+ var tagCommitHash plumbing.Hash
112+ if commit , err := r .repo .CommitObject (tagRef .Hash ()); err == nil {
113+ // Lightweight tag – hash already points to commit
114+ tagCommitHash = commit .Hash
115+ } else if tagObj , err := r .repo .TagObject (tagRef .Hash ()); err == nil {
116+ // Annotated tag – resolve to the tagged commit
117+ if commit , err := r .repo .CommitObject (tagObj .Target ); err == nil {
118+ tagCommitHash = commit .Hash
119+ }
120+ }
121+ if tagCommitHash == (plumbing.Hash {}) {
122+ return 0 , fmt .Errorf ("failed to resolve commit for tag %s" , tag )
123+ }
124+ // Get HEAD commit
125+ head , err := r .repo .Head ()
85126 if err != nil {
86- return 0 , fmt .Errorf ("failed to get commits since tag %s : %w" , tag , err )
127+ return 0 , fmt .Errorf ("failed to get HEAD : %w" , err )
87128 }
88-
129+ headCommit , err := r .repo .CommitObject (head .Hash ())
130+ if err != nil {
131+ return 0 , fmt .Errorf ("failed to get HEAD commit: %w" , err )
132+ }
133+ // Count commits from tag to HEAD (excluding the tag commit itself)
89134 var count int
90- if err := commits .ForEach (func (_ * object.Commit ) error {
135+ commits , err := r .repo .Log (& git.LogOptions {From : headCommit .Hash })
136+ if err != nil {
137+ return 0 , fmt .Errorf ("failed to get commits: %w" , err )
138+ }
139+ err = commits .ForEach (func (c * object.Commit ) error {
140+ // Stop when we reach the tag commit
141+ if c .Hash == tagCommitHash {
142+ return storer .ErrStop
143+ }
91144 count ++
92145 return nil
93- }); err != nil {
146+ })
147+ if err != nil && err != storer .ErrStop {
94148 return 0 , fmt .Errorf ("failed to iterate commits: %w" , err )
95149 }
96150 return count , nil
0 commit comments