Removing and moving files (git rm, git mv)
git rm and git mv delete and rename tracked files while staging that change in one step, keeping the index and disk in sync.
Why it matters
Deleting or renaming with the OS leaves the change unstaged, so it is easy to forget half of a rename in the next commit. These commands do the filesystem action and the staging together, and git rm --cached is the standard way to stop tracking a file (e.g. a leaked config) without deleting it locally.
How it works
Git does not store renames; it stores snapshots and detects renames at diff time by content similarity. git mv old new is just shorthand for rm old + add new.
| Command | On disk | In index |
|---|---|---|
git rm f | Deletes f | Stages deletion |
git rm --cached f | Keeps f | Untracks it (now ??) |
git rm -r dir/ | Deletes dir tree | Stages all deletions |
git mv a b | Renames a→b | Stages rename |
--cached— the key flag for “stop tracking but keep the file”; pair it with a .gitignore entry so it does not get re-added.- Safety guard —
git rmrefuses if the file has staged or working-tree changes you’d lose; override with-f, or use--cachedto be safe. - Rename detection —
git log --follow <file>andgit diff -Mreconstruct history across renames using the similarity index; a rename + heavy edit in one commit may not be detected as a rename.
Example
git rm --cached .env # stop tracking a committed secret, keep it locally
echo ".env" >> .gitignore
git commit -m "chore: stop tracking .env, add to gitignore"
git mv src/util.py src/helpers.py
git status -s # R src/util.py -> src/helpers.py
Pitfalls
- OS
rm/mvinstead ofgit— the deletion/rename shows as unstaged; you must stillgit add(orgit add -A) it, and a forgotten half-rename breaks the build. --cacheddoes not scrub history — the file’s old contents remain in every prior commit; for a real secret you must rewrite history and rotate the credential.- Rename + big edit looks like delete+add — drops
blame/--followcontinuity; split the rename and the edit into two commits to preserve history. git rm -ris staged immediately — recovering meansgit restore --staged+git restorebefore committing; after commit, usegit revertor checkout fromHEAD~1.