all repos — elgit @ 703d2e603629535d3e46413aa60f8bd39a9ef720

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

Merge branch 'feat/add_log_reference'
Alan Pearce alan@alanpearce.eu
Mon, 31 Mar 2025 10:39:39 +0200
commit

703d2e603629535d3e46413aa60f8bd39a9ef720

parent

98fa355025227a03b4c3c19fc151a4d73479ab49

3 files changed, 96 insertions(+), 12 deletions(-)

jump to
M git/git.gogit/git.go
@@ -32,6 +32,12 @@ ref *plumbing.Reference 	tag *object.Tag
 }
 
+// CommitReference aggregate all the references for a given commit
+type CommitReference struct {
+	*object.Commit
+	refs []*plumbing.Reference
+}
+
 // infoWrapper wraps the property of a TreeEntry so it can export fs.FileInfo
 // to tar WriteHeader
 type infoWrapper struct {
@@ -104,30 +110,84 @@ } 	return &g, nil
 }
 
-func (g *GitRepo) Commits() ([]*object.Commit, error) {
+func (g *GitRepo) Commits() ([]*CommitReference, error) {
 	ci, err := g.r.Log(&git.LogOptions{From: g.h})
 	if err != nil {
 		return nil, fmt.Errorf("commits from ref: %w", err)
 	}
 
-	commits := []*object.Commit{}
+	commitRefs := []*CommitReference{}
 	err = ci.ForEach(func(c *object.Commit) error {
-		commits = append(commits, c)
+		commitRefs = append(commitRefs, &CommitReference{Commit: c})
 		return nil
 	})
 	if err != nil {
 		return nil, fmt.Errorf("iterating commits: %w", err)
 	}
 
-	return commits, nil
+	// new we fetch for possible tags for each commit
+	iter, err := g.r.References()
+	if err != nil {
+		return nil, err
+	}
+
+	if err := iter.ForEach(func(ref *plumbing.Reference) error {
+		for _, c := range commitRefs {
+			obj, err := g.r.TagObject(ref.Hash())
+			switch err {
+			case nil:
+				if obj.Target == c.Commit.Hash {
+					c.AddReference(ref)
+				}
+			case plumbing.ErrObjectNotFound:
+				if c.Commit.Hash == ref.Hash() {
+					c.AddReference(ref)
+				}
+			default:
+				return err
+			}
+		}
+		return nil
+	}); err != nil {
+		return nil, err
+	}
+
+	return commitRefs, nil
 }
 
-func (g *GitRepo) LastCommit() (*object.Commit, error) {
+func (g *GitRepo) LastCommit() (*CommitReference, error) {
 	c, err := g.r.CommitObject(g.h)
 	if err != nil {
 		return nil, fmt.Errorf("last commit: %w", err)
 	}
-	return c, nil
+
+	iter, err := g.r.Tags()
+	if err != nil {
+		return nil, err
+	}
+
+	commitRef := &CommitReference{Commit: c}
+	if err := iter.ForEach(func(ref *plumbing.Reference) error {
+		obj, err := g.r.TagObject(ref.Hash())
+		switch err {
+		case nil:
+			if obj.Target == commitRef.Commit.Hash {
+				commitRef.AddReference(ref)
+			}
+		case plumbing.ErrObjectNotFound:
+			if commitRef.Commit.Hash == ref.Hash() {
+				commitRef.AddReference(ref)
+			}
+		default:
+			return err
+		}
+
+		return nil
+	}); err != nil {
+		return nil, err
+	}
+
+	return commitRef, nil
 }
 
 func (g *GitRepo) FileContent(path string) (string, error) {
@@ -345,3 +405,15 @@ return t.tag.Message 	}
 	return ""
 }
+
+func (c *CommitReference) HasReference() bool {
+	return len(c.refs) > 0
+}
+
+func (c *CommitReference) References() []*plumbing.Reference {
+	return c.refs
+}
+
+func (c *CommitReference) AddReference(ref *plumbing.Reference) {
+	c.refs = append(c.refs, ref)
+}
M templates/log.gotemplates/log.go
@@ -3,19 +3,20 @@ import (
 	"fmt"
 
-	"github.com/go-git/go-git/v5/plumbing/object"
+	"github.com/go-git/go-git/v5/plumbing"
+	"go.alanpearce.eu/elgit/git"
 	g "go.alanpearce.eu/gomponents"
 	. "go.alanpearce.eu/gomponents/html"
 )
 
 // Log renders the commit log page
-func Log(data PageData, commits []*object.Commit) g.Node {
+func Log(data PageData, commits []*git.CommitReference) g.Node {
 	return Page(data, []g.Node{
 		RepoHeader(data),
 		RenderNav(data),
 		Main(
 			Div(Class("log"),
-				g.Map(commits, func(commit *object.Commit) g.Node {
+				g.Map(commits, func(commit *git.CommitReference) g.Node {
 					return g.Group{
 						Div(
 							Div(
@@ -23,6 +24,17 @@ A( 									Href(fmt.Sprintf("/%s/commit/%s", data.Name, commit.Hash.String())),
 									Class("commit-hash"),
 									g.Text(commit.Hash.String()[:8]),
+								),
+								g.Text(" "),
+								g.If(
+									commit.HasReference(),
+									g.Map(commit.References(), func(ref *plumbing.Reference) g.Node {
+										return A(
+											Href(fmt.Sprintf("/%s/tree/%s", data.Name, ref.Name().Short())),
+											Class("commit-hash"),
+											g.Text(ref.Name().Short()),
+										)
+									}),
 								),
 							),
 							Pre(g.Text(commit.Message)),
M templates/repo.gotemplates/repo.go
@@ -3,19 +3,19 @@ import (
 	"fmt"
 
-	"github.com/go-git/go-git/v5/plumbing/object"
+	"go.alanpearce.eu/elgit/git"
 	g "go.alanpearce.eu/gomponents"
 	. "go.alanpearce.eu/gomponents/html"
 )
 
 // Repo renders the repository summary page
-func Repo(data PageData, commits []*object.Commit, readme string) g.Node {
+func Repo(data PageData, commits []*git.CommitReference, readme string) g.Node {
 	return Page(data, []g.Node{
 		RepoHeader(data),
 		RenderNav(data),
 		Main(
 			Div(Class("log"),
-				g.Map(commits, func(commit *object.Commit) g.Node {
+				g.Map(commits, func(commit *git.CommitReference) g.Node {
 					return g.Group{
 						Div(
 							Div(