Skip to content

resetWorktree: O(N²) filepath.Clean calls during Clone cause severe CPU regression in v5.16.5 #1845

@EladGabay

Description

@EladGabay

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions