diff --git a/walk/git.go b/walk/git.go index 75473d6b..f5e60a90 100644 --- a/walk/git.go +++ b/walk/git.go @@ -2,13 +2,12 @@ package walk import ( "context" - "errors" "fmt" "io/fs" + "os" "path/filepath" "github.com/charmbracelet/log" - "github.com/go-git/go-git/v5/plumbing/format/index" "github.com/go-git/go-git/v5" ) @@ -39,9 +38,57 @@ func (g *gitWalker) Walk(ctx context.Context, fn WalkFunc) error { return fmt.Errorf("failed to open git index: %w", err) } + // cache in-memory whether a path is present in the git index + var cache map[string]bool + for path := range g.paths { - err = filepath.Walk(path, func(path string, info fs.FileInfo, err error) error { + if path == g.root { + // we can just iterate the index entries + for _, entry := range idx.Entries { + select { + case <-ctx.Done(): + return ctx.Err() + default: + path := filepath.Join(g.root, entry.Name) + + // stat the file + info, err := os.Lstat(path) + + file := File{ + Path: path, + RelPath: relPathFn(path), + Info: info, + } + + if err = fn(&file, err); err != nil { + return err + } + } + } + continue + } + + // otherwise we ensure the git index entries are cached and then check if they are in the git index + if cache == nil { + cache = make(map[string]bool) + for _, entry := range idx.Entries { + cache[entry.Name] = true + } + } + + relPath, err := filepath.Rel(g.root, path) + if err != nil { + return fmt.Errorf("failed to find relative path for %v: %w", path, err) + } + + _, ok := cache[relPath] + if !(path == g.root || ok) { + log.Debugf("path %v not found in git index, skipping", path) + continue + } + + return filepath.Walk(path, func(path string, info fs.FileInfo, err error) error { if info.IsDir() { return nil } @@ -51,9 +98,8 @@ func (g *gitWalker) Walk(ctx context.Context, fn WalkFunc) error { return err } - if _, err = idx.Entry(relPath); errors.Is(err, index.ErrEntryNotFound) { - // we skip this path as it's not staged - log.Debugf("Path not found in git index, skipping: %v, %v", relPath, path) + if _, ok := cache[relPath]; !ok { + log.Debugf("path %v not found in git index, skipping", path) return nil } @@ -65,10 +111,6 @@ func (g *gitWalker) Walk(ctx context.Context, fn WalkFunc) error { return fn(&file, err) }) - if err != nil { - return err - } - } return nil