Skip descending into ignored directories in Distignore_Filter_Iterator - #135
Conversation
The descent-skipping optimization in hasChildren() (#116) never engaged: it probed a synthetic child path which never exists on disk, so the gitignore-checker library always threw InvalidArgumentException and the catch block fell through to descending. Every ignored directory was fully traversed, with every file inside checked against the ignore rules. Now descent into an ignored directory is skipped unless a .distignore negation rule might re-include a path inside it: anchored negation patterns without wildcards are compared by path prefix; unanchored or wildcard patterns conservatively force descent. The top-level-only restriction is removed, so nested ignored directories are skipped too. Debug messages are logged when descent is skipped and when a checker exception is swallowed. Archiving a plugin with a 2 GB node_modules drops from 8m32s to ~10s. Fixes #134 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 6 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough
ChangesDistignore traversal correction
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com>
Edit: I pointed Claude at the issue I wrote and this is what it came up with. It looks good. I tested it against the repo I was working on and everything looks right. I think the existing
should existtests are pretty comprehensive and they are passing.Claude:
Fixes #134
Root cause
The descent-skipping optimization in
Distignore_Filter_Iterator::hasChildren()(#116) never engaged. It probed a synthetic child path ($relative_filepath . '/test') to decide whether an ignored directory's children are also ignored — but that path never exists on disk, andgitignore-checkervalidates existence (RelativePath::setPath()throwsInvalidArgumentExceptionfor nonexistent paths). The catch block then fell through to "descend to be safe", so every ignored directory was fully traversed and every file inside was individually checked against the ignore rules.The existing unit test passed despite this because it only asserted that ignored children aren't yielded —
accept()filters them one by one — it never asserted that descent didn't happen.Fix
hasChildren()now skips descent into ignored directories, with one guard: a.distignorenegation rule (leading!) might re-include a path inside an ignored directory, somight_contain_negated_path()checks for that first.The guard is needed because the checker library reports
/frontenditself as ignored under afrontend/*rule (unlike git), so unconditionally skipping descent would break the supportedfrontend/*+!/frontend/build/pattern (covered by existing unit and Behat tests).!/frontend/build/): compared by path prefix, so only the affected parent directory is descended into.The previous top-level-only (
count( $path_parts ) === 1) restriction is removed, so nested ignored directories (e.g. a deepernode_modules) are skipped too.Per the issue's request for logs when exceptions are caught, debug messages (
--debug=dist-archive) are emitted when descent is skipped and when a checker exception is swallowed.Performance
Archiving bh-wp-bitcoin-gateway (2 GB
node_modules): 8m32s → ~10s, identical 3.03 MB archive.Tests
test_does_not_descend_into_ignored_directoriesuses a recording subclass that captures every path checked against the ignore rules, proving the contents of ignored directories (top-level and nested) are never visited.--debug=dist-archiveand asserts the skip message appears and nonode_modulescontents are mentioned.frontend/*+!/frontend/build/) pass unchanged.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
.distignorehandling for negation rules, preserving files that are explicitly re-included.Tests