1.9 KiB
name, description
| name | description |
|---|---|
| gitignore-trailing-slash-symlink-contamination | Trailing-slash gitignore rules don't match symlinks, so worktree dependency symlinks get committed; detect with git ls-tree mode 120000, fix with git rm --cached. |
A .gitignore entry written with a trailing slash matches directories only. A symlink of the same name is not a directory, so it slips past the rule, and git add -A in a git worktree checkout silently tracks it. Cost measured 2026-10-19: a machine-specific symlink (node_modules -> /home/blank/Projects/dankvoice/node_modules) landed in main with a merged PR.
Why worktrees are the trigger: a worktree has no node_modules (or other installed-deps dir) of its own, so the cheap way to make build/test work there is a symlink into the primary checkout. With node_modules/ ignored, nothing complains.
Detect
git ls-tree HEAD -- node_modules # mode 120000 == tracked symlink
git ls-files -s | awk '$1==120000' # every tracked symlink in the index
Fix (branch, never on main directly)
git rm -q --cached node_modules # untrack; leaves the file on disk
plus relax the ignore rule to the slash-free form, with the reason inline so nobody re-adds the slash:
# Trailing slash would only match directories: a node_modules symlink (the
# shape `git worktree` checkouts end up with) slips past it and gets tracked.
node_modules
Then git check-ignore -v node_modules must print the rule, and the suite runs after (the symlink still resolves, it is just ignored).
Prevent
In a fresh worktree, create the deps symlink after confirming the ignore rule has no trailing slash:
git check-ignore -v node_modules || sed -i 's|^node_modules/$|node_modules|' .gitignore
Same class for any ignored-by-convention dir that a worktree might provide as a link: node_modules, vendor, .venv, target, dist when built artifacts are ignored.