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

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions