3 files changed,
73 insertions(+),
38 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-03-05 20:37:08 +0200
Authored at:
2026-03-05 14:21:14 +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 18 } 19 19 return path, err 20 20 } 21 + 22 +// topLevelEntry returns the top-level entry name under base for a given path. 23 +// e.g. base="lua", path="lua/plugins/foo.lua" -> "plugins" 24 +// e.g. base="", path="README.md" -> "README.md" 25 +// returns "" if path is not under base. 26 +func topLevelEntry(fullPath, base string) string { 27 + if base != "" && base != "." { 28 + if !strings.HasPrefix(fullPath, base+"/") { 29 + return "" 30 + } 31 + fullPath = fullPath[len(base)+1:] 32 + } 33 + if before, _, ok := strings.Cut(fullPath, "/"); ok { 34 + return before 35 + } 36 + return fullPath 37 +}
M
internal/git/repo.go
路路路 192 192 return newCommit(c), nil 193 193 } 194 194 195 -func (g *Repo) lastCommitForFile(filepath string) (*Commit, error) { 196 - iter, err := g.r.Log(&git.LogOptions{ 197 - From: g.h, 198 - Order: git.LogOrderCommitterTime, 199 - }) 195 +// lastCommitForFilesInTree ... 196 +// TODO: at the moment it doesn't work well with merges, ideally i would "shell" out it, 197 +// `git log --pretty:format:%H,%ad,%s --date=iso --name-only -- g.path` 198 +func (g *Repo) lastCommitForFilesInTree(tree *object.Tree, dirPath string) (map[string]*Commit, error) { 199 + log, err := g.r.Log(&git.LogOptions{From: g.h}) 200 200 if err != nil { 201 - return nil, fmt.Errorf("failed to log: %w", err) 201 + return nil, err 202 202 } 203 - defer iter.Close() 204 203 205 - var prevHash plumbing.Hash 206 - var result *object.Commit 207 - err = iter.ForEach(func(com *object.Commit) error { 208 - tree, terr := com.Tree() 209 - if terr != nil { 210 - return terr 204 + result := make(map[string]*Commit) 205 + err = log.ForEach(func(c *object.Commit) error { 206 + if c.NumParents() == 0 { 207 + for _, entry := range tree.Entries { 208 + if _, seen := result[entry.Name]; !seen { 209 + result[entry.Name] = newCommit(c) 210 + } 211 + } 212 + return storer.ErrStop 211 213 } 212 214 213 - var hash plumbing.Hash 214 - entry, eerr := tree.FindEntry(filepath) 215 - if eerr == nil { 216 - hash = entry.Hash 217 - } else { 218 - file, ferr := tree.File(filepath) 219 - if ferr != nil { 220 - return storer.ErrStop 221 - } 222 - hash = file.Hash 215 + // skip merge commits 216 + if c.NumParents() > 1 { 217 + return nil 223 218 } 224 219 225 - if hash != prevHash { 226 - result = com 227 - prevHash = hash 220 + parent, perr := c.Parent(0) 221 + if perr != nil { 222 + return perr 223 + } 224 + 225 + patch, perr := parent.Patch(c) 226 + if perr != nil { 227 + return perr 228 + } 229 + 230 + for _, fp := range patch.FilePatches() { 231 + from, to := fp.Files() 232 + 233 + var affectedPath string 234 + if to != nil { 235 + affectedPath = to.Path() 236 + } else if from != nil { 237 + affectedPath = from.Path() 238 + } 239 + 240 + name := topLevelEntry(affectedPath, dirPath) 241 + if name == "" { 242 + continue 243 + } 244 + 245 + if _, seen := result[name]; !seen { 246 + result[name] = newCommit(c) 247 + } 228 248 } 229 249 return nil 230 250 }) 231 - 232 - if !errors.Is(err, storer.ErrStop) && err != nil { 233 - return nil, fmt.Errorf("failed to walk commits: %w", err) 234 - } 235 - if result == nil { 236 - return nil, fmt.Errorf("no commits found for path: %s", filepath) 251 + if err != nil && !errors.Is(err, storer.ErrStop) { 252 + return nil, err 237 253 } 238 - return newCommit(result), nil 254 + return result, nil 239 255 } 240 256 241 257 type Branch struct {
M
internal/git/tree.go
路路路 5 5 "fmt" 6 6 "io" 7 7 "mime" 8 - "path" 9 8 "path/filepath" 10 9 "strings" 11 10 路路路 22 21 23 22 func (g *Repo) makeNiceTree(t *object.Tree, parent string) []NiceTree { 24 23 var nts []NiceTree 24 + 25 + cms, err := g.lastCommitForFilesInTree(t, parent) 26 + if err != nil { 27 + return nts 28 + } 29 + 25 30 for _, e := range t.Entries { 26 31 mode, _ := e.Mode.ToOSFileMode() 27 32 sz, _ := t.Size(e.Name) 28 - 29 - // TODO: this should be cached, its pretty expensive 30 - lc, _ := g.lastCommitForFile(path.Join(parent, e.Name)) 31 33 nts = append(nts, NiceTree{ 32 34 Name: e.Name, 33 35 Mode: mode.String(), 34 36 IsFile: e.Mode.IsFile(), 35 - Commit: lc, 37 + Commit: cms[e.Name], 36 38 Size: sz, 37 39 }) 38 40 }