all repos — elgit @ d0c522021ad5e7b85dbfbadfc546676c80d4b99d

fork of legit: web frontend for git, written in go

git: show lightweight annotated tags (#42)

The "TagObjects" function only returns annotated tags (the ones created
with "-a"). Git interpretes lightweight and annotated tags[^1]
differently, so we need to query all reference and check which one have
a tag object attached.

[^1]: https://git-scm.com/book/en/v2/Git-Basics-Tagging
Gabriel mail@gabrielgio.me
Fri, 04 Oct 2024 17:52:56 +0200
commit

d0c522021ad5e7b85dbfbadfc546676c80d4b99d

parent

4447fcda1f06f4d516dfd40e3d306da193aaeeae

1 files changed, 75 insertions(+), 23 deletions(-)

jump to
M git/git.gogit/git.go
@@ -19,7 +19,18 @@ r *git.Repository 	h plumbing.Hash
 }
 
-type TagList []*object.Tag
+type TagList struct {
+	refs []*TagReference
+	r    *git.Repository
+}
+
+// TagReference is used to list both tag and non-annotated tags.
+// Non-annotated tags should only contains a reference.
+// Annotated tags should contain its reference and its tag information.
+type TagReference struct {
+	ref *plumbing.Reference
+	tag *object.Tag
+}
 
 // infoWrapper wraps the property of a TreeEntry so it can export fs.FileInfo
 // to tar WriteHeader
@@ -31,17 +42,42 @@ modTime time.Time 	isDir   bool
 }
 
-func (self TagList) Len() int {
-	return len(self)
+func (self *TagList) Len() int {
+	return len(self.refs)
 }
 
-func (self TagList) Swap(i, j int) {
-	self[i], self[j] = self[j], self[i]
+func (self *TagList) Swap(i, j int) {
+	self.refs[i], self.refs[j] = self.refs[j], self.refs[i]
 }
 
 // sorting tags in reverse chronological order
-func (self TagList) Less(i, j int) bool {
-	return self[i].Tagger.When.After(self[j].Tagger.When)
+func (self *TagList) Less(i, j int) bool {
+	var dateI time.Time
+	var dateJ time.Time
+
+	if self.refs[i].tag != nil {
+		dateI = self.refs[i].tag.Tagger.When
+	} else {
+		c, err := self.r.CommitObject(self.refs[i].ref.Hash())
+		if err != nil {
+			dateI = time.Now()
+		} else {
+			dateI = c.Committer.When
+		}
+	}
+
+	if self.refs[j].tag != nil {
+		dateJ = self.refs[j].tag.Tagger.When
+	} else {
+		c, err := self.r.CommitObject(self.refs[j].ref.Hash())
+		if err != nil {
+			dateJ = time.Now()
+		} else {
+			dateJ = c.Committer.When
+		}
+	}
+
+	return dateI.After(dateJ)
 }
 
 func Open(path string, ref string) (*GitRepo, error) {
@@ -116,31 +152,36 @@ return "Not displaying binary file", nil 	}
 }
 
-func (g *GitRepo) Tags() ([]*object.Tag, error) {
-	ti, err := g.r.TagObjects()
+func (g *GitRepo) Tags() ([]*TagReference, error) {
+	iter, err := g.r.Tags()
 	if err != nil {
 		return nil, fmt.Errorf("tag objects: %w", err)
 	}
 
-	tags := []*object.Tag{}
+	tags := make([]*TagReference, 0)
 
-	_ = ti.ForEach(func(t *object.Tag) error {
-		for i, existing := range tags {
-			if existing.Name == t.Name {
-				if t.Tagger.When.After(existing.Tagger.When) {
-					tags[i] = t
-				}
-				return nil
-			}
+	if err := iter.ForEach(func(ref *plumbing.Reference) error {
+		obj, err := g.r.TagObject(ref.Hash())
+		switch err {
+		case nil:
+			tags = append(tags, &TagReference{
+				ref: ref,
+				tag: obj,
+			})
+		case plumbing.ErrObjectNotFound:
+			tags = append(tags, &TagReference{
+				ref: ref,
+			})
+		default:
+			return err
 		}
-		tags = append(tags, t)
 		return nil
-	})
+	}); err != nil {
+		return nil, err
+	}
 
-	var tagList TagList
-	tagList = tags
+	tagList := &TagList{r: g.r, refs: tags}
 	sort.Sort(tagList)
-
 	return tags, nil
 }
 
@@ -290,3 +331,14 @@ func (i *infoWrapper) Sys() any {
 	return nil
 }
+
+func (t *TagReference) Name() string {
+	return t.ref.Name().Short()
+}
+
+func (t *TagReference) Message() string {
+	if t.tag != nil {
+		return t.tag.Message
+	}
+	return ""
+}