Skip to content

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

Description

@willemkokke

Describe the Bug

Running pyrefly check inside a linked git worktree placed under a hidden directory of the main checkout — the layout several agent tools create (.claude/worktrees/<name>/, and ~/.codex/worktrees/… as in #2402) — skips every include with No Python files matched. Two independent mechanisms cause it:

1. The ignore-file search does not honour repository boundaries.

A linked worktree's .git is a gitdir: … pointer file, so the upward search for .gitignore/.ignore/.git/info/exclude walks straight past the working-tree root and finds the enclosing checkout's .git/info/exclude, applying its patterns rooted at the enclosing checkout. A pattern like **/.claude/worktrees/ in the main checkout's exclude file (how these tools keep worktrees out of git status) then matches every file inside the worktree:

ignore files [<worktree>/.gitignore, /path/to/main-checkout/.git/info/exclude]
No Python files matched patterns …

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 (git -C <worktree> check-ignore confirms the files are not ignored).

2. HiddenDirFilter::RelativeTo only carries import_root (follow-up to #2402).

For a src-layout project import_root is <project>/src, so an include outside it — tests/check.py — matches no root and falls back to checking the absolute path's components, where a checkout under .claude/… has every chain hidden; the include is skipped even though the project's own files contain no hidden component. Additionally, the root list is only populated when use_ignore_files is true, so --use-ignore-files=false silently degrades the filter to All — disabling one exclusion mechanism makes another stricter.

Reproduction

git init main && cd main && python -c 'open("pyproject.toml","w")'
printf '**/wt/\n' >> .git/info/exclude
git worktree add .claude/worktrees/wt
cd .claude/worktrees/wt
printf '[tool.pyrefly]\nproject-includes=["src"]\n' > pyproject.toml
mkdir src && touch src/m.py
pyrefly check   # every include skipped

Expected behavior

The worktree checks like any other checkout: outer ignore rules stop at the working-tree boundary, the shared info/exclude applies relative to the worktree root, and hidden ancestors above the project's include roots don't hide the project's own files.

I have a fix for both halves (boundary-aware ignore-file search that resolves gitdir/commondir for linked worktrees, and RelativeTo built from every include root plus the import root, decoupled from use_ignore_files) — PR incoming, happy to adjust the approach.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions