Skip to content

Commit e35f1a8

Browse files
authored
Reduce matching allocations and skip unrelated ignore rules (#30)
* Reduce matching allocations and skip unrelated ignore rules * Simplify traversal state and share bracket syntax helpers * Move refactor tests into gitignore_test.go and note WalkTree subprocess cost
1 parent 67b7f6c commit e35f1a8

7 files changed

Lines changed: 577 additions & 132 deletions

File tree

‎README.md‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A Go library for matching paths against gitignore rules. Pattern matching uses a
1111
- Directory-only patterns (trailing `/`) with descendant matching
1212
- Match provenance via `MatchDetail` (which pattern, file, and line number matched)
1313
- Invalid pattern surfacing via `Errors()`
14-
- Literal suffix fast-reject for common patterns like `*.log`
14+
- Fast rejection for literal names and suffix patterns like `*.log`
1515

1616
```go
1717
import "github.com/git-pkgs/gitignore"
@@ -111,7 +111,17 @@ A Matcher is safe for concurrent `Match`/`MatchPath`/`MatchDetail` calls once co
111111

112112
## Match semantics
113113

114-
Paths should use forward slashes and be relative to the repository root. Last-match-wins, same as git.
114+
Paths should use forward slashes and be relative to the repository root. An ignored parent directory excludes all its descendants; otherwise, the last matching rule for the path wins.
115+
116+
## Benchmarks
117+
118+
Run benchmarks on an otherwise idle machine, using the same Go toolchain for both revisions. Record repeated samples and memory allocations:
119+
120+
```sh
121+
go test -run '^$' -bench . -benchmem -count=10 -cpu=1
122+
```
123+
124+
`BenchmarkCompile` and `BenchmarkWalkTree` include filesystem reads and a Git subprocess through `New`; `BenchmarkAddPatterns` measures parsing without filesystem access. Compare samples with `benchstat` and check the timing spread before quoting speed changes.
115125

116126
## License
117127

‎conformance_test.go‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,50 @@ func TestConformanceGitStartupFailure(t *testing.T) {
148148
}
149149
}
150150

151+
func TestConformanceNestedWalk(t *testing.T) {
152+
requireGit(t)
153+
isolateGitEnv(t)
154+
paths := parsePathList("a/cache/x\na/keep.log\na/drop.log\na/sub/keep.log\na/sub/drop.log\nb/drop.log\nb/keep.log\nb/cache/x\nblocked/sub/file\n")
155+
root := buildRepo(t, "*.log\nblocked/\n", paths)
156+
for dir, patterns := range map[string]string{
157+
"a": "!keep.log\ncache/\n", "a/sub": "!drop.log\n",
158+
"b": "!drop.log\n", "blocked/sub": "!file\n",
159+
} {
160+
if err := os.WriteFile(filepath.Join(root, dir, ".gitignore"), []byte(patterns), 0o644); err != nil {
161+
t.Fatal(err)
162+
}
163+
}
164+
want := gitCheckIgnore(t, root, paths)
165+
m := gitignore.NewFromDirectory(root)
166+
for _, p := range paths {
167+
if got := m.Match(p.query()); got != want[p.rel] {
168+
t.Errorf("NewFromDirectory Match(%q) = %v, git = %v", p.rel, got, want[p.rel])
169+
}
170+
}
171+
for _, start := range []string{"", "a", "a/sub", "b", "blocked/sub"} {
172+
visited := make(map[string]bool)
173+
visit := func(path string, _ os.DirEntry) error {
174+
visited[filepath.ToSlash(path)] = true
175+
return nil
176+
}
177+
var err error
178+
if start == "" {
179+
err = gitignore.Walk(root, visit)
180+
} else {
181+
err = gitignore.WalkFrom(root, start, visit)
182+
}
183+
if err != nil {
184+
t.Fatal(err)
185+
}
186+
for _, p := range paths {
187+
inScope := start == "" || strings.HasPrefix(p.rel, start+"/")
188+
if expected := inScope && !want[p.rel]; visited[p.rel] != expected {
189+
t.Errorf("walk from %q: visited %q = %v, want %v", start, p.rel, visited[p.rel], expected)
190+
}
191+
}
192+
}
193+
}
194+
151195
// TestConformanceFuzz generates random pattern sets and paths, then compares
152196
// the library against git check-ignore. It is skipped under -short.
153197
func TestConformanceFuzz(t *testing.T) {

0 commit comments

Comments
 (0)