feat: respect nested .gitignore files + --exclude flag#18
Merged
Conversation
Design for honoring per-directory .gitignore files (built on pathspec, pure-filesystem) plus a repeatable --exclude flag. Full-lane change bundle 2026-06-26.01-nested-gitignore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three-task TDD plan: discovery.py walk, main() wiring + --exclude flag, architecture promotion + decision record. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a one-line comment in `_is_ignored` making explicit that `rel` is always within the anchor's subtree because specs are pushed/popped around their directory's walk in `_walk`. Add the root-anchored qualifier to the architecture prose describing the baseline spec. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop redundant list() copy in iter_text_files; from_lines takes any iterable. - Express the anchor-relative slice as removeprefix(anchor + "/") for clarity and safe degradation if the prefix invariant is ever broken. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
eof-fixerpreviously read only the root.gitignoreand applied it tree-wide, so files ignored by a.gitignorein a subdirectory were still rewritten. This makes the walk honor the nested gitignore convention — per-directory.gitignorefiles with correct anchoring, precedence, negation, and ignored-directory pruning — and adds a repeatable--excludeflag.Scope is nested
.gitignoreonly, pure-filesystem: no.git/info/exclude, no globalcore.excludesFile, no shelling out to git. The tool still works on any directory, repo or not. (Built on the existingpathspecdependency — no new deps; rationale in the decision record below.)How it works
New module
eof_fixer/discovery.pyexposesiter_text_files(root, extra_excludes): a top-down DFS carrying a stack of per-directoryGitIgnoreSpecs. Each entry is classified by evaluating specs deepest→shallowest, first definitivecheck_file().includewins — exactly git's "closest file wins, negation re-includes." Ignored directories are pruned (git-correct: a file under an excluded parent can't be re-included),.gitis hard-skipped by name, and symlinks are skipped.main()shrinks to consume the iterator; the EOF core (_is_binary/_detect_trailing/_fix_file) is untouched.CLI
--exclude DIR(repeatable) augments the always-skipped.gitand the default.cache/.uv-cache:eof-fixer .→ unchanged behavior (backward compatible)eof-fixer . --exclude node_modules --exclude dist→ also skips thosePlanning artifacts
This was built via the repo's planning convention (Full lane):
planning/changes/2026-06-26.01-nested-gitignore/planning/decisions/2026-06-26-build-on-pathspec-for-nested-gitignore.mdarchitecture/file-discovery.md,architecture/cli.mdTests
8 new unit tests in
tests/test_discovery.py(nested subtree, deeper-overrides-shallower, negation, directory pruning,.gitskip, symlink skip, extra-excludes) plus a--excludeintegration test. Full suite 24 passed, 100% coverage;just lint-ciclean. Nested precedence/negation/pruning cross-checked against realgit check-ignore.🤖 Generated with Claude Code