Skip to content

perf: optimize tracked-file exclusion predicate [CLI-1411] - #698

Closed
basti-snyk wants to merge 2 commits into
mainfrom
fix/CLI-1411_optimize-tracked-file-predicate
Closed

perf: optimize tracked-file exclusion predicate [CLI-1411]#698
basti-snyk wants to merge 2 commits into
mainfrom
fix/CLI-1411_optimize-tracked-file-predicate

Conversation

@basti-snyk

@basti-snyk basti-snyk commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • Replace eager O(entries × patterns) index scan with lazy map lookup + early-exit
  • Parse .git/index binary directly instead of go-git's full Entry deserialization — reads only file names, skips hashes/timestamps/modes
  • Eliminates ~20MB go-git overhead on large repos (100k+ files)

Benchmark (median, flag=on vs flag=off overhead)

Repo Metric Before optimization After optimization
DefinitelyTyped (63k files) Wall +60% +4%
DefinitelyTyped (63k files) Memory +73% ≤10% (expected)
cli (84k files) Wall +16% -3%
cli (84k files) Memory +59% +4%
juice-shop (129k files) Wall -13% 0%
juice-shop (129k files) Memory +5% -1%

Commits

  1. Lazy map + early exit — build map[string]struct{} from index names, early-exit when allMatcher says not excluded, compile 2 matchers instead of 3
  2. Direct .git/index parsing — replace go-git PlainOpenWithOptions + Storer.Index() with a lightweight binary parser that reads only file names; handles v2/v3/v4, gitlink files (submodules/worktrees)

Test plan

  • Existing TestFileFilter_TrackedFiles* tests pass (real git repos via git.PlainInit)
  • New unit tests for readTrackedFileNames (v2, v2 long names, v4 prefix compression)
  • New unit tests for findDotGit (found, not found)
  • Benchmark on DefinitelyTyped to verify memory reduction
  • make lint && make test green

PR Type

Enhancement


Description

  • Replaced dependency on go-git for index parsing with custom logic.

  • Optimized tracked file exclusion predicate for performance.

  • Introduced utilities for finding the .git directory and parsing index files.

  • Added comprehensive tests for new parsing and discovery utilities.


Diagram Walkthrough

flowchart LR
  A[FileFilter Context] --> B{Get Tracked Files};
  B -- Old way --> C[go-git Index Loader];
  B -- New way --> D[findDotGit + Custom Index Parser];
  C --> E[All Index Entries Loaded];
  D --> F[Only Tracked File Names Parsed];
  E --> G[Filter Logic (go-git-dependent)];
  F --> H[Filter Logic (custom, performant)];
Loading

File Walkthrough

Relevant files
Enhancement, dependencies, performance
file_filter.go
Custom Git index parsing and optimized exclusion predicate.

pkg/utils/file_filter.go

  • Removed the go-git dependency for index operations.
  • Implemented custom binary parsing of .git/index files to read only
    file names.
  • Added findDotGit function to locate the .git directory and its parent
    worktree root.
  • Optimized the trackedFileExclusionPredicate to use the custom-parsed
    tracked file names directly, improving performance.
+210/-39
Tests
file_filter_test.go
Tests for Git index parsing and .git discovery.                   

pkg/utils/file_filter_test.go

  • Added helper functions (gitIndexV2Entry, gitIndexV4Entry,
    buildGitIndex) for constructing Git index files for testing.
  • Introduced new unit tests for readTrackedFileNames, covering index
    versions (v2, v4) and prefix compression.
  • Added unit tests for the new findDotGit function, including scenarios
    where the directory is not found.
+101/-0 

Replace eager O(entries × patterns) index scan with lazy map lookup.

- Build map[string]struct{} of all tracked files (O(n) inserts, no
  regex) instead of running gitignoreMatcher.MatchesPath per entry
- Early-exit when allMatcher says not excluded — skip map lookup
- Compile 2 matchers instead of 3 (removed buggy otherMatcher)
- Replace per-file filepath.Rel with strings.TrimPrefix

Benchmark (median, flag=on vs flag=off overhead):
  DefinitelyTyped (63k): wall +60%->+4%, mem +73%->+55%
  cli (84k):             wall +16%->-3%, mem +59%->+4%
  juice-shop (129k):     wall -13%-> 0%, mem  +5%->-1%
trackedFileExclusionPredicate only needs tracked file names, but
go-git's repo.Storer.Index() deserializes every index Entry (hash,
timestamps, mode, ~160 bytes each), which adds significant memory
overhead on repositories with many tracked files.

Replace the go-git repo/index loading with a direct binary parser
(findDotGit + readTrackedFileNames) that walks up to the .git
directory and reads only file names out of the index (v2-v4,
including v4 name-prefix compression), skipping every other field.

matchingRepositoryRoot is unchanged; it now takes the worktree root
resolved by findDotGit instead of go-git's worktree.Filesystem.Root().
@snyk-io

snyk-io Bot commented Aug 12, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@snyk-io

snyk-io Bot commented Aug 12, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues
Secrets 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@basti-snyk

Copy link
Copy Markdown
Contributor Author

/describe

@snyk-pr-review-bot

Copy link
Copy Markdown

PR Description updated to latest commit (acd46f1)

@basti-snyk basti-snyk closed this Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant