
A client kept their notes vault in git. Years of markdown. Meeting notes, runbooks, half-finished ideas — the kind of stuff you don’t think about until it’s gone.
They wanted to stop tracking one big folder. Reasonable. It didn’t belong in version control.
So they ran the obvious command:
git rm -r attachments/
Git chewed on it for a second and printed a wall of rm 'attachments/...' lines. Looked fine. Then git status showed something that made my stomach drop.
It wasn’t just the attachments/ folder.
deleted: notes/2024/standup.md
deleted: notes/2024/oncall.md
deleted: runbooks/dns.md
... 30,206 more ...
The working tree was empty. ls came back nearly bare. 30,209 markdown files were gone from disk. Not staged-for-deletion-someday. Gone. Off the filesystem.
The investigation
The panic version of this story ends with a restore from last night’s backup and a lost afternoon. The calm version starts with one question: where does git actually delete from?
People think git rm means “stop tracking this file.” It doesn’t. git rm removes a file from two places at once: the index (staging area) and the working tree — your actual disk.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Working Tree │ │ Index │ │ HEAD/Repo │
│ (your disk) │ │ (staging) │ │ (last commit)│
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
git rm ──► ✗ DELETES ──►│ ✗ stages delete │ ✓ STILL HERE
│ │ │
git rm --cached ─────► │ ✗ untracks only │ ✓ STILL HERE
▼ ▼ ▼
file gone marked rm recoverable
That last column is the whole ballgame. git rm touches the working tree and the index, but it does not touch your last commit until you actually commit the deletion.
Nobody had committed yet.
The files were sitting safe in HEAD the entire time, even as the disk looked like a crime scene.
The aha
If HEAD still has every file, recovery is just “copy them back out of the last commit.” No backup tape, no remote clone, no heroics.
The fix
First, confirm the damage scope. Trust nothing, count everything:
git status --porcelain | grep -c '^D'
# 30209
30,209 staged deletions. Matches the missing files exactly. Good — nothing weird, just one bad command.
Now restore the working tree and reset the index straight from HEAD:
git restore --staged --worktree .
# or, on older git:
git checkout HEAD -- .
Verify the files actually came back on disk before believing it:
find . -name '*.md' | wc -l
# 30209
All present. git status clean. If HEAD had been corrupt, the same files were still in the remote — git fetch && git restore --source=origin/main --worktree . would have done it from there.
Then we did the thing they meant to do — untrack without deleting:
git rm -r --cached attachments/
echo 'attachments/' >> .gitignore
git commit -m "Stop tracking attachments/ (keep on disk)"
--cached removes the folder from the index only. The disk is never touched. That one flag is the entire difference between a config tidy-up and a disaster.
Why it happened
git rm carries a default that doesn’t match most people’s mental model. The intent was “git, please forget this exists.” Git heard “delete this from the index and the disk,” because that’s what it does. The -r made it recursive, and the glob pulled in far more than expected.
There was no malice and no bad command syntax. It did exactly what it’s documented to do. The gap was understanding that the working tree is a target, not a bystander.
Takeaways
git rmdeletes from your disk. Usegit rm --cachedto untrack a file while keeping it on the filesystem — that’s almost always what you actually want.- Count before, count after.
find . -name '*.md' | wc -lon both sides of any bulk git op. A number that drops by 30,209 is not a surprise you want at commit time. - Test on ONE file first. Run the destructive command against a single path, check
git statusandls, then scale up. One file lost is a shrug; thirty thousand is a bad day. - Don’t run destructive git in a live data directory without a backup. A notes vault is data, not just a repo. Treat it like one.
- Know your three trees. Working tree, index, HEAD. Recovery is trivial when you know which one still has your files — and panic when you don’t.