git rm and git mv: deleting and renaming under git's watch

Updated July 31, 2026

git rm deletes a file and puts that deletion straight into the index; git mv renames one and does the same. Both commands do in one step what would otherwise take two.

$ git rm draft.txt
rm 'draft.txt'
$ git status --short
D  draft.txt

The D in the first column means the deletion is already staged and will go into the next commit. The file is gone from disk. A rename looks like this:

$ git mv old.txt new.txt
$ git status --short
R  old.txt -> new.txt

You can also delete or rename a file with the ordinary tools of your system: the rm command, the mv command, a drag in the file manager. Git notices, but the change stays outside the index:

$ mv new.txt renamed.txt
$ git status --short
 D new.txt
?? renamed.txt

A space in the first column means "nothing staged", and ?? marks an untracked file. A single git add with -A sorts it out, and git pairs the two halves into a rename by itself:

$ git add -A
$ git status --short
R  new.txt -> renamed.txt

So git rm and git mv save you a step; they are not a separate mechanism inside git.

Git does not store renames

There is no "this file was renamed" record in a commit. A commit holds a whole snapshot, and the R old.txt -> new.txt in the output is a guess git makes on the fly: it compares the contents of the file that vanished with the one that appeared, and if they are similar enough, it shows them as a pair. That has a consequence: rename a file and rewrite half of its lines in the same commit, and git will report one file deleted and another added. Nothing in the history breaks, it just reads worse. Keep the rename and the content edit in separate commits.

git rm --cached: stop watching, keep on disk

This is what people are usually looking for. The --cached flag takes the file out of the index without touching it on disk:

$ git rm --cached config.env
rm 'config.env'
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    config.env

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	config.env

The same file is listed as deleted (that is how git sees it now) and as untracked (it is right there on disk). Next you add its name to .gitignore and commit both changes together. That is the move when a secret or a dependency directory made it into the repository by accident: a directory also needs -r, so git rm -r --cached node_modules.

Common mistakes

git rm --cached does not take a secret out of the history. Past commits hold past snapshots in full, and the commit where the key still sat is untouched: anyone with a copy of the repository pulls it out with git show <hash>:config.env. A key that has been in the history counts as leaked, and the right response is to reissue it, not to tidy up the file.

git rm on a file with unsaved edits refuses and tells you the way out:

$ git rm report.txt
error: the following file has local modifications:
    report.txt
(use --cached to keep the file, or -f to force removal)

A whole directory goes only with -r. Without the flag git answers fatal: not removing 'docs' recursively without -r.

Where this is in the book

The index and .gitignore are covered here: Chapter 4. Inspect, undo, ignore.

To practise it by hand, take the interactive lesson Undoing changes.