Repository navigation
Fix ancestor exclusion checks after gitignore negations - #165
Conversation
Check strict ancestors even when the last directory match is a negation or the matching pattern has no directory marker. Return the excluding ancestor's pattern index consistently across Simple, RE2, and Hyperscan. Cover nested directory exceptions, terminal globstars, batch filtering, re-inclusion controls, and bounded ancestor matching in regression tests.
|
Follow-up validation for f70a1d2, including a measured performance cost:
Performance tradeoff: with the Go.AllowList template and 246 synthetic paths, Each timing used seven samples of twenty batches, repeated in reverse The supplemental scripts, complete oracle corpus, raw timing samples and five |
|
Thanks for finding and fixing this! |
With rules ['a/', '!a/b/'], GitIgnoreSpec currently reports a/b/file.txt as
not ignored even though a/ remains excluded. The same problem affects the
deep-file case explicitly deferred by #164: ['d/**/', '!d/child/'] incorrectly
re-includes d/child/grand/deep.txt while grand/ remains ignored.
This follows the ancestor-exclusion requirement discussed in #129, rather
than repeating the already merged #132 or #164 changes:
#129
#164
The ancestor walk currently runs only when dir_include is true. A later
directory negation makes that false; some matching patterns, such as a/**,
do not set a directory marker at all. Check ancestors whenever there is a
match, and return the first excluded ancestor's pattern index. Apply the
same change to Simple, RE2, and Hyperscan. Keep the non-recursive ancestor
walk and skip it for unmatched paths.
The patch adds regression tests using the existing backend/configuration
matrix, updates the previously documented incorrect expectation, and adds
a changelog entry. It introduces no runtime dependency or public signature
change. Checking additional ancestors can increase matching work for paths
that previously bypassed the rule; no speedup is claimed. A 40-level test
guards against recursive rechecking (at most 41 matcher calls).
Validation on Windows, CPython 3.10.11, Git 2.52.0.windows.1,
google-re2 1.1.20251105 and hyperscan 0.9.1:
differences in ignored status or the decisive index of ignored paths.
On the same corpus, the original code has 1,885 ignored-status differences
and 4,681 ignored-index differences (overlapping counts).
ran all 281 tests against that installed package: successful, 16 skipped.
Commands from the repository root, using the isolated environment's Python:
python -m unittest discover -t . -s tests/
python -m unittest tests.test_09_gitignore_ancestor_negation -v
python -m build --no-isolation --outdir ../packages
The Git-oracle counts above are supplemental local validation; the standalone
oracle and corpus are retained in the local delivery bundle, not added to this PR.
The committed regression tests can be run using the commands above.
Minimal API reproduction (False before this patch, True afterward):
Limitations: the 16 skips are symlink-permission-dependent tests on Windows.
Other OS/Python combinations and upstream CI have not run. mypy 2.4.0
reports the same two pre-existing diagnostics in pathspec/_typing.py on
both the base and patched trees (lines 40 and 51); type checking is not
reported as passing. The finite oracle corpus is not exhaustive.
Base: ac29e19
AI assistance disclosure: Codex performed the research, implementation,
test authoring, local execution and draft preparation. The user's request
was to find a real, non-duplicate open-source need, implement and verify a
useful contribution, and obtain approval before any external publication.
The user authorized publication of this contribution. It has not received
independent human code review.