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.
Git · Command
git rm removes tracked files from the index, or from both the index and the working tree. Committing afterwards records the deletion.
Last updated
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.
git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>…]| Name | Type | Description |
|---|---|---|
<pathspec>… | pathspec | Files to remove. A directory name removes everything under it, but only when -r is given. |
-f, --force | flag | Skips the up-to-date safety check. |
-n, --dry-run | flag | Lists what would be removed without removing anything. |
-r | flag | Allows recursive removal when you give a leading directory name. |
--cached | flag | Unstages and removes paths from the index only. Working tree files are left alone. |
--ignore-unmatch | flag | Exits with status 0 even if no files matched. |
--sparse | flag | Allows updating index entries outside the sparse-checkout cone. |
-q, --quiet | flag | Turns off the one-line rm message printed for each removed file. |
--pathspec-from-file=<file> | string | Reads pathspecs from a file instead of the arguments. Use - to read from standard input. |
--pathspec-file-nul | flag | Works only with --pathspec-from-file. Pathspecs are separated by NUL, and every other character is taken literally. |
-- | separator | Separates options from file names, so a file name can't be mistaken for an option. |
# 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.
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.
Delete a retired module directory and stage the deletion in one step with git rm -r, ready to commit.
Drop a vendored submodule from the superproject. git rm removes its work tree and its .gitmodules entry, and stages both.
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.
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.
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/.
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 addgit mvgit commitgit restoregit resetgit submodulegit sparse-checkoutgit ls-files