Skip to content

Honour repository boundaries in the ignore-file search; filter hidden dirs relative to include roots - #4402

Open
willemkokke wants to merge 2 commits into
facebook:mainfrom
willemkokke:fix/ignore-files-repository-boundary
Open

Honour repository boundaries in the ignore-file search; filter hidden dirs relative to include roots#4402
willemkokke wants to merge 2 commits into
facebook:mainfrom
willemkokke:fix/ignore-files-repository-boundary

Conversation

@willemkokke

Copy link
Copy Markdown

Fixes #4401. Two commits, one per mechanism:

1. find_ignore_files honours repository boundaries the way git does (pyrefly_util/globs.rs)

The upward search for .gitignore/.ignore/.git/info/exclude treated the directory tree as one hierarchy: from a linked worktree (whose .git is a gitdir: pointer file, not a directory) it walked past the working-tree root, found the enclosing checkout's .git/info/exclude, and applied its patterns rooted at the enclosing checkout — so a pattern like **/.claude/worktrees/ in the main checkout's exclude file matched every file inside the worktree and all includes were skipped.

The search now collects the nearest .gitignore and .ignore walking upward, stops at the first directory carrying a .git entry (file or directory — the working-tree root), and resolves info/exclude through the gitdir pointer and commondir for linked worktrees, rooted at the working tree being checked — matching git check-ignore's verdicts. The memoized UpwardSearch statics are replaced by a direct ancestor walk; it runs once per GlobFilter construction and is O(depth) file stats.

2. HiddenDirFilter::RelativeTo is built from every include root plus the import root (pyrefly_config/config.rs)

Follow-up to #2402: the allowance carried only import_root, so for a src-layout project (import_root = src/) an include outside it (tests/check.py) fell back to absolute-path component checking and was hidden by the checkout's own ancestors. Also decoupled from use_ignore_files, which previously degraded the filter to All when ignore files were turned off — disabling one exclusion mechanism silently hardened another.

Tests: two new tests in globs.rs (nested-repository boundary; linked worktree with gitdir + commondir resolution, asserting the shared exclude file still applies but relative to the worktree root); the existing test_get_filtered_globs_coverage_scope expectation updated to the new RelativeTo construction. cargo test -p pyrefly_util -p pyrefly_config passes (318 tests).

Verified end to end on the real-world layout that surfaced this (a .claude/worktrees/… checkout of a src-layout project whose main checkout excludes **/.claude/worktrees/): before, every include was skipped; after, the project checks clean with all-default settings, in both --use-ignore-files states.

Willem Kokke added 2 commits August 1, 2026 19:13
The upward search for .gitignore/.ignore/.git/info/exclude treated the
directory tree as one hierarchy: from a linked worktree (whose .git is
a gitdir pointer file, not a directory) it sailed past the working-tree
root, found the enclosing checkout's .git/info/exclude, and applied its
patterns rooted at the enclosing checkout. A pattern like
'.claude/worktrees/' in a main checkout's exclude file (how several
agent tools register their worktrees) then matched every file inside
the worktree, and pyrefly skipped all of the project's includes with
'No Python files matched'.

git's own semantics: a directory carrying a .git entry (file or
directory) is a working-tree root; an outer repository's ignore rules
never apply inside it, and the repository's shared info/exclude applies
to each worktree relative to that worktree's own root.

The search now walks upward collecting the nearest .gitignore and
.ignore, stops at the first directory with a .git entry, and resolves
info/exclude through the gitdir pointer and commondir for linked
worktrees -- rooted at the working tree being checked.
The RelativeTo hidden-directory allowance carried only import_root, so
for a src-layout project (import_root = src/) any include outside it --
tests/check.py -- fell back to absolute-path component checking, where
a checkout under a hidden directory (~/.codex/worktrees/...,
.claude/worktrees/...) has every path hidden and the include was
skipped. The roots are now every include root plus the import root.

Also decouples the allowance from use_ignore_files: the root list was
only populated when ignore files were enabled, so --use-ignore-files=
false degraded the filter to HiddenDirFilter::All and made hidden-
directory filtering stricter -- disabling one exclusion mechanism
silently hardened another.
@meta-cla meta-cla Bot added the cla signed label Aug 1, 2026
@github-actions github-actions Bot added the size/l label Aug 1, 2026
@meta-codesync

meta-codesync Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

This pull request has been imported. If you are a Meta employee, you can view this in D114483806. (Because this pull request was imported automatically, there will not be any future comments.)

@github-actions
github-actions Bot requested a review from kinto0 August 2, 2026 09:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ignore files and hidden-dir filtering exclude every include when the project is a linked git worktree under a hidden directory

2 participants