Skip to content

Use .gitignore for directory exclusion during traversal#83

Merged
lifeizhou-ap merged 4 commits into
block:mainfrom
deanbaker:use-gitignore-to-exclude-paths
Mar 25, 2026
Merged

Use .gitignore for directory exclusion during traversal#83
lifeizhou-ap merged 4 commits into
block:mainfrom
deanbaker:use-gitignore-to-exclude-paths

Conversation

@deanbaker

Copy link
Copy Markdown
Contributor

Summary

  • Replace hardcoded EXCLUDED_DIRECTORIES list with .gitignore-based filtering via the ignore crate when a .gitignore file exists
  • Fall back to the hardcoded list when no .gitignore is found
  • Hidden directories and ai-rules are always excluded regardless of strategy

Test plan

  • All 339 existing tests pass
  • 5 new tests covering: gitignore exclusion, hidden dir exclusion, ai-rules exclusion, non-ignored dirs traversed when not in .gitignore, and hardcoded fallback
  • cargo clippy clean
  • Manual test: cargo run -- status in a repo with .gitignore confirms target/ still excluded
  • Manual test: project without .gitignore confirms hardcoded fallback works

Replace hardcoded EXCLUDED_DIRECTORIES list with .gitignore-based
filtering when a .gitignore file exists. Falls back to the hardcoded
list when no .gitignore is found. Hidden directories and ai-rules
are always excluded regardless of strategy.
@deanbaker
deanbaker marked this pull request as ready for review March 15, 2026 23:10
@lifeizhou-ap

Copy link
Copy Markdown
Collaborator

Hi @deanbaker,

Thank you so much for your contribution. Using .gitignore instead of a hardcoded list is a great idea!

I noticed two issues with the current implementation:

  • Negation patterns don't work as expected — if .gitignore has build/ and !build/important/, the traversal skips build/ entirely, so build/important/ is never reached. Users who wrote that negation rule would expect the tool to still process that directory.

  • Nested .gitignore files are ignored — only the root-level .gitignore is loaded. In monorepos, subdirectories often have their own .gitignore (e.g., frontend/.gitignore with dist/), which wouldn't be respected. Since the tool supports nested_depth for monorepo use cases, this could lead to traversing large build output directories in subprojects.

What do you think? Would love to hear your thoughts on how to handle these. Thank you!

Replace the binary should_traverse() with a three-state TraversalDecision
enum (Enter, SkipCallbackButRecurse, Skip) so that ignored directories
with negated children are still recursed into.

- Use a Vec<Gitignore> chain instead of a single matcher so each nested
  .gitignore is built with its own directory as root (works around the
  ignore crate's limitation with cross-directory negation patterns)
- Track parent_ignored state during traversal so children of ignored
  directories inherit the ignore unless explicitly whitelisted
- Read each .gitignore file once via build_gitignore_for_dir(), producing
  both the compiled matcher and negation patterns in a single pass
- Only clone the filter chain when a child .gitignore actually exists
- Add 6 new tests covering negation, nested .gitignore, combined
  negation+nesting, full skip, depth boundaries, and decision assertions
@deanbaker

Copy link
Copy Markdown
Contributor Author

Hi @deanbaker,

Thank you so much for your contribution. Using .gitignore instead of a hardcoded list is a great idea!

I noticed two issues with the current implementation:

  • Negation patterns don't work as expected — if .gitignore has build/ and !build/important/, the traversal skips build/ entirely, so build/important/ is never reached. Users who wrote that negation rule would expect the tool to still process that directory.
  • Nested .gitignore files are ignored — only the root-level .gitignore is loaded. In monorepos, subdirectories often have their own .gitignore (e.g., frontend/.gitignore with dist/), which wouldn't be respected. Since the tool supports nested_depth for monorepo use cases, this could lead to traversing large build output directories in subprojects.

What do you think? Would love to hear your thoughts on how to handle these. Thank you!

Thanks for the feedback @lifeizhou-ap, I have made another commit that has addressed it

@lifeizhou-ap lifeizhou-ap left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @deanbaker for your contribution. The PR looks good! I've approved the PR. I've added a comment about the code separation. WDYT? If it makes sense, would you please extract these functions to the a separate file/mod before merging to main? Thank you!

Comment thread src/utils/file_utils.rs Outdated
Moves TraversalDecision, DirectoryFilter, build_gitignore_for_dir,
has_negated_children, should_traverse_directory, and EXCLUDED_DIRECTORIES
into src/utils/dir_filter.rs per PR review feedback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@lifeizhou-ap
lifeizhou-ap merged commit 1f2f63d into block:main Mar 25, 2026
7 checks passed
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.

2 participants