Windows: worktree creation in an LFS repo creates a literal dev\null\ directory
Summary
On Windows, creating a Claude Code worktree in a Git LFS repository creates a literal dev\null\ directory at the repository root, containing four Git LFS hook files. It appears to come from a git lfs install invoked with core.hooksPath=/dev/null — an idiom that discards hooks on POSIX but materializes a real directory on Windows.
The directory shows up as untracked in git status and is easy to commit by accident with git add ..
Environment
- OS: Windows 11 Home 10.0.26200
- Claude Code: desktop app 1.0.1307 (CLI binary at
~\.local\bin\claude.exe) - Git: Git for Windows, with
git-lfsatC:\Program Files\Git\cmd\git-lfs.exe - Repository uses Git LFS (
.gitattributescontains*.dll filter=lfs diff=lfs merge=lfs -text)
Steps to reproduce
- On Windows, use a repository that has Git LFS configured (any
filter=lfsentry in.gitattributes). - Create a worktree from Claude Code (
EnterWorktree, or the worktree UI). - Run
git statusin the new worktree.
Expected
Clean worktree; no stray untracked files.
Actual
$ git status --short
?? dev/null/
$ ls dev/null/
post-checkout 360 bytes
post-commit 356 bytes
post-merge 354 bytes
pre-push 350 bytes
Each file is a standard Git LFS hook, e.g. pre-push:
#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { printf >&2 "\n%s\n\n" "This repository is configured for Git LFS but 'git-lfs' was not found on your path. ..."; exit 2; }
git lfs pre-push "$@"
Root cause
The four files are what git lfs install writes. Passing /dev/null as the hooks path is a common way to initialize LFS filters without touching hooks. On Windows there is no /dev/null, so Git resolves it as a path relative to the repository root and creates dev\null\.
Reproduced directly, outside Claude Code:
mkdir lfstest && cd lfstest
git init -q
git -c core.hooksPath=/dev/null lfs install
# -> Updated Git hooks.
# -> Git LFS initialized.
find dev
# dev
# dev/null
# dev/null/post-checkout (360 bytes)
# dev/null/post-commit (356 bytes)
# dev/null/post-merge (354 bytes)
# dev/null/pre-push (350 bytes)
Byte sizes match the files produced during worktree creation exactly.
Evidence that this is worktree creation
- Present in 15 of 18 Claude Code worktrees on this machine; never in the main checkout.
dev\null\is created 2–3 seconds after each worktree directory, consistently across ~3 weeks of worktrees.- A plain
git worktree add --detach <path> HEADdoes not reproduce it, so it is notgit worktree addor the LFSpost-checkouthook on its own. - Each worktree's
config.worktreecontains the hooks configuration Claude Code writes:
``ini``
[core]
longpaths = true
hooksPath = C:\\path\\to\\main\\.git\\hooks
Impact
Cosmetic but persistent:
- Permanently dirty
git statusin every worktree of an LFS repo on Windows. - Risk of committing four stray shell scripts via
git add ./ "commit everything" flows. - Mildly confusing —
/dev/nullis not an obvious thing to find checked into a repository.
The hooks themselves are inert. The real LFS hooks in .git/hooks are installed correctly and unaffected, so LFS keeps working.
Suggested fix
Use the platform-appropriate null device when suppressing hook installation — NUL on Windows, /dev/null elsewhere — or skip the hook-suppression flag entirely on Windows and clean up afterwards. Anything that avoids handing Git a POSIX-only absolute path it will resolve relative to the repo root.
Workaround
Delete the directory and ignore it:
/dev/null/
It reappears on each new worktree, but stays out of git status.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗