-
Notifications
You must be signed in to change notification settings - Fork 883
Description
Bug Description
Commit 600fb13 ("Don't delete local untracked files when resetting worktree") introduced an O(N²) performance regression that affects every Clone operation.
During Clone, go-git internally performs a MergeReset (worktree.go:138). In v5.16.4, resetWorktree was called with a nil file list:
// v5.16.4
w.resetWorktree(t, opts.Files) // opts.Files is nil during Clone
In v5.16.5, resetIndex now collects all touched files and passes them to resetWorktree:
// v5.16.5
removedFiles, _ = w.resetIndex(t, dirs, opts.Files)
w.resetWorktree(t, removedFiles)
Inside resetWorktree, the inFiles function is called for every worktree change when the file list is non-nil:
func inFiles(files []string, v string) bool {
v = filepath.Clean(v)
for _, s := range files {
if filepath.Clean(s) == v {
return true
}
}
return false
}
During a fresh Clone, both removedFiles and changes (from diffStagingWithWorktree) contain every file in the repository. This results in N×N calls to filepath.Clean, where every check returns true, the filtering has no practical effect during Clone, but the cost is paid in full.
For a repository with ~50,000 files, this produces ~2.5 billion filepath.Clean calls.
This affects every user of git.Clone / git.PlainClone - the MergeReset is hardcoded in the checkout path (worktree.go:138), not opt-in.
go-git Version
v5.16.5
Steps to Reproduce
No response
Additional Information
No response