3 files changed,
73 insertions(+),
38 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-03-05 20:37:08 +0200
Change ID:
mvolpyvmqqslksxwntnxnwrnqxyxqpmn
Parent:
1c4878a
jump to
| M | internal/git/paths.go |
| M | internal/git/repo.go |
| M | internal/git/tree.go |
M
internal/git/paths.go
@@ -18,3 +18,20 @@ return "", fmt.Errorf("failed to secure join paths: %w", err)
} return path, err } + +// topLevelEntry returns the top-level entry name under base for a given path. +// e.g. base="lua", path="lua/plugins/foo.lua" -> "plugins" +// e.g. base="", path="README.md" -> "README.md" +// returns "" if path is not under base. +func topLevelEntry(fullPath, base string) string { + if base != "" && base != "." { + if !strings.HasPrefix(fullPath, base+"/") { + return "" + } + fullPath = fullPath[len(base)+1:] + } + if before, _, ok := strings.Cut(fullPath, "/"); ok { + return before + } + return fullPath +}
M
internal/git/repo.go
@@ -192,50 +192,66 @@
return newCommit(c), nil } -func (g *Repo) lastCommitForFile(filepath string) (*Commit, error) { - iter, err := g.r.Log(&git.LogOptions{ - From: g.h, - Order: git.LogOrderCommitterTime, - }) +// lastCommitForFilesInTree ... +// TODO: at the moment it doesn't work well with merges, ideally i would "shell" out it, +// `git log --pretty:format:%H,%ad,%s --date=iso --name-only -- g.path` +func (g *Repo) lastCommitForFilesInTree(tree *object.Tree, dirPath string) (map[string]*Commit, error) { + log, err := g.r.Log(&git.LogOptions{From: g.h}) if err != nil { - return nil, fmt.Errorf("failed to log: %w", err) + return nil, err } - defer iter.Close() - var prevHash plumbing.Hash - var result *object.Commit - err = iter.ForEach(func(com *object.Commit) error { - tree, terr := com.Tree() - if terr != nil { - return terr + result := make(map[string]*Commit) + err = log.ForEach(func(c *object.Commit) error { + if c.NumParents() == 0 { + for _, entry := range tree.Entries { + if _, seen := result[entry.Name]; !seen { + result[entry.Name] = newCommit(c) + } + } + return storer.ErrStop } - var hash plumbing.Hash - entry, eerr := tree.FindEntry(filepath) - if eerr == nil { - hash = entry.Hash - } else { - file, ferr := tree.File(filepath) - if ferr != nil { - return storer.ErrStop - } - hash = file.Hash + // skip merge commits + if c.NumParents() > 1 { + return nil } - if hash != prevHash { - result = com - prevHash = hash + parent, perr := c.Parent(0) + if perr != nil { + return perr + } + + patch, perr := parent.Patch(c) + if perr != nil { + return perr + } + + for _, fp := range patch.FilePatches() { + from, to := fp.Files() + + var affectedPath string + if to != nil { + affectedPath = to.Path() + } else if from != nil { + affectedPath = from.Path() + } + + name := topLevelEntry(affectedPath, dirPath) + if name == "" { + continue + } + + if _, seen := result[name]; !seen { + result[name] = newCommit(c) + } } return nil }) - - if !errors.Is(err, storer.ErrStop) && err != nil { - return nil, fmt.Errorf("failed to walk commits: %w", err) - } - if result == nil { - return nil, fmt.Errorf("no commits found for path: %s", filepath) + if err != nil && !errors.Is(err, storer.ErrStop) { + return nil, err } - return newCommit(result), nil + return result, nil } type Branch struct {
M
internal/git/tree.go
@@ -5,7 +5,6 @@ "errors"
"fmt" "io" "mime" - "path" "path/filepath" "strings"@@ -22,17 +21,20 @@ }
func (g *Repo) makeNiceTree(t *object.Tree, parent string) []NiceTree { var nts []NiceTree + + cms, err := g.lastCommitForFilesInTree(t, parent) + if err != nil { + return nts + } + for _, e := range t.Entries { mode, _ := e.Mode.ToOSFileMode() sz, _ := t.Size(e.Name) - - // TODO: this should be cached, its pretty expensive - lc, _ := g.lastCommitForFile(path.Join(parent, e.Name)) nts = append(nts, NiceTree{ Name: e.Name, Mode: mode.String(), IsFile: e.Mode.IsFile(), - Commit: lc, + Commit: cms[e.Name], Size: sz, }) }