Merge branch 'feat/add_log_reference'
Alan Pearce alan@alanpearce.eu
Mon, 31 Mar 2025 10:39:39 +0200
3 files changed, 96 insertions(+), 12 deletions(-)
M git/git.go → git/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.go → templates/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.go → templates/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(