Skip to content
masterclass Buddy

Git · Command

git rm

git rm removes tracked files from the index, or from both the index and the working tree. Committing afterwards records the deletion.

Last updated

How it works

git rm uses pathspecs to find tracked paths, then removes them from the index (the staging area). By default it also deletes them from disk. Before it does anything, it runs a safety check: each file must match the branch tip and must have no staged changes, which protects work you haven't committed yet. -f skips that check. --cached removes the entry from the index only and leaves the file on disk, so the file becomes untracked after the commit. The deletion is staged like any other change and becomes permanent with git commit.

Syntax

git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>…]

Parameters

NameTypeDescription
<pathspec>…pathspecFiles to remove. A directory name removes everything under it, but only when -r is given.
-f, --forceflagSkips the up-to-date safety check.
-n, --dry-runflagLists what would be removed without removing anything.
-rflagAllows recursive removal when you give a leading directory name.
--cachedflagUnstages and removes paths from the index only. Working tree files are left alone.
--ignore-unmatchflagExits with status 0 even if no files matched.
--sparseflagAllows updating index entries outside the sparse-checkout cone.
-q, --quietflagTurns off the one-line rm message printed for each removed file.
--pathspec-from-file=<file>stringReads pathspecs from a file instead of the arguments. Use - to read from standard input.
--pathspec-file-nulflagWorks only with --pathspec-from-file. Pathspecs are separated by NUL, and every other character is taken literally.
--separatorSeparates options from file names, so a file name can't be mistaken for an option.

Key points

  • Removing a whole directory takes -r. Without it, giving a directory name is not enough.
  • Use --cached to stop tracking a file but keep your local copy. It is the usual fix for a file committed before it was added to .gitignore.
  • Run -n first to preview which index entries a pattern would hit before you delete anything.
  • Untracked files are never touched, even when a pattern matches them on disk.
  • --ignore-unmatch makes the command safe in scripts, where a pattern that matches nothing would otherwise fail.
  • With --pathspec-file-nul, a large or awkward list of paths (for example, names with newlines) can come from a file or stdin without any escaping.
  • When removing a submodule, ignored files inside it don't block the removal. Its .gitmodules entry is also removed and staged.
  • In a sparse checkout, --sparse is needed to change index entries outside the cone.

Common mistakes

  • git rm refuses to remove a file whose content has staged changes or no longer matches the branch tip. You must decide whether to use -f, which discards those changes.
  • Git's own globbing crosses '/' boundaries. A quoted 'd*' therefore also matches a sibling directory like d2, while 'd/*' does not.
  • You can't delete a file from disk while keeping it staged. To do that, use a plain shell rm.
  • git rm has no flag to stage only the files you already deleted with a plain rm. Use git add -u, git commit -a, or a diff --diff-filter=D pipeline instead.

Example: Untrack a committed file and clean up a folder

# Stop tracking a secrets file but keep it locally
echo '.env' >> .gitignore
git rm --cached .env

# Preview, then recursively remove an old directory
git rm -r -n legacy/
git rm -r legacy/

# Let Git (not the shell) expand the glob, matching subdirectories too
git rm 'docs/*.txt'

git commit -m "Untrack .env, drop legacy/ and docs text files"

This shows the three most common uses: --cached for untracking, -n and -r for a safe recursive delete, and a quoted pathspec so Git expands the glob itself.

When to use it

Fixing a tracked file that should be ignored

A build artifact or .env file was committed by mistake. Add it to .gitignore and run git rm --cached so it leaves the repository but stays on your disk.

Deleting obsolete code

Delete a retired module directory and stage the deletion in one step with git rm -r, ready to commit.

Removing a submodule

Drop a vendored submodule from the superproject. git rm removes its work tree and its .gitmodules entry, and stages both.

Scripted cleanup

In CI, feed a generated list of paths through --pathspec-from-file=- with --ignore-unmatch so the cleanup doesn't fail when a path is already gone.

Interview questions

QWhat is the difference between git rm and git rm --cached?

Plain git rm removes the path from the index and also deletes it from the working tree. With --cached, only the index entry is removed and the file on disk stays as it is, so after you commit it shows up as untracked. --cached is the standard way to untrack something you are about to add to .gitignore.

Follow-up: What check does --cached apply? The staged content must match either the branch tip or the file on disk. Otherwise you need -f.

QWhy might git rm refuse to delete a file, and how do you override it?

git rm runs an up-to-date check. The file must match the tip of the branch, and it can't have staged changes. If either condition fails, the command refuses so that it doesn't destroy uncommitted work. Passing -f/--force skips the check.

Follow-up: Why is git rm 'src/*.js' different from git rm src/*.js? In the quoted form, Git expands the pattern and matches across directory boundaries, including subdirectories. In the unquoted form, the shell expands it and matches only files directly in src/.

QYou deleted several tracked files with a plain rm. How do you stage those deletions?

git rm has no option that stages only files already missing from disk. Running git add -u, or committing with git commit -a, records those removals along with any other changes to tracked files. To stage only the deletions while other edits are in progress, pipe git diff --name-only --diff-filter=D -z into xargs -0 git rm --cached.

Follow-up: How do you import a vendor code drop so that additions, modifications and deletions are all recorded? Remove all tracked files, unpack the new tree into the working directory, then run git add -A.

  • git add
  • git mv
  • git commit
  • git restore
  • git reset
  • git submodule
  • git sparse-checkout
  • git ls-files

Source: Git - git-rm Documentation. Checked 2026-11-02.