3.1 KiB
name, description
| name | description |
|---|---|
| gitignore-trailing-slash-symlink-contamination | Trailing-slash gitignore rules don't match symlinks, so worktree dependency symlinks get tracked and pushed as 1-line blobs; same-name rules silently match worktree dirs, and .deploy-stale pollutes. Three fixes with tests. |
Gitignore semantics trap with three real-world consequences (serverAppConfigs, 2026-10-19):
The rule
.gitignore patterns ending in / match directories only. Worktree-relative: **/node_modules/ is **/node_modules/ → a symlink named node_modules does NOT match, so it is "untracked" no more — it gets added, committed, and pushed as a 1-line blob containing its target path.
Consequence 1: worktree symlink contamination
git add -Athen commit then push in a worktree whose root holdsnode_modules -> ...(the worktree convention symlink) commits it.- The repo then holds
package/frontend/node_modulesas a text file containing the absolute path on the author's box. - The receiving worktree gets that 1-line file instead of the symlink → "module not found" and "why is this a 30-byte file?".
- Detection:
git ls-tree -r origin/main | awk '$1=="120000"'(mode 120000 = symlink) orgit ls-files -s | grep 120000. - Fix: ignore rules WITHOUT trailing slash (
**/node_modules,**/.venv) so symlinks match too.
Consequence 2: same-name worktree rules silently matching worktree dirs
A root rule like wt-* (meant to ignore local worktree checkouts) matches every path whose last segment starts with wt- — so legitimate content under such a directory goes untracked. git status --ignored is the only visible trace; detection: git check-ignore -v <path> prints the owning rule + line number. (Repo-specific: dank/command-center's .gitignore carries a "per-worktree memory buckets" block ignoring agent/memories/sharpshooter/wt-*/ and agent/memories/summaries/wt-*/ — intentional there.)
Consequence 3: .deploy-stale
deploy.sh renames a stale tree to <path>.deploy-stale before recreating it. Without an ignore rule those directories land in git status as untracked and get swept into git add -A commits; they accumulate on every server.
Fixes with tests
**/node_modules
**/.venv
.deploy-stale/
Black-box test against the real rules, not a fixture: git check-ignore -v <symlink-target> must name the rule. git check-ignore takes hypothetical paths — nothing needs to exist on disk:
git check-ignore -v natives/x.wasm dotfiles/x/.config/noctalia/Noctalia.json
Two ignore surfaces must be kept in sync (deploy exclusions vs ignore rules): if a deployer excludes */Noctalia* but the ignore list has only the lowercase class, generated files become tracked-but-never-deployed — test both halves with git check-ignore.
Recovery (if contamination already pushed)
git rm --cached <path>on the symlink blob, commit, push.- Fix the ignore rule first (else the next
git add -Are-adds it). - Untracked ≠ deleted from history: the blob stays in every old commit. Only a rewrite removes it; that is the operator's call, not a cleanup run alone.