Remove Tracked Files Safely with git rm
git rm removes a tracked file from your working tree and the index together, or just from the index while the file stays on disk. Delete a tracked file with plain rm instead and Git still thinks it exists, which is exactly the mismatch this command avoids. Allow about ten minutes for a single file, a little longer for a directory or a recovery check. These are ordinary user commands; you normally do not need sudo, and using it can leave the repository owned by root.
The route
Jump straight to the step you need, or tick off Done means at the end.
1. Check the repository and the target
Run this from the repository that contains the file. The installed command on this machine is Git 2.43.0, from the Ubuntu git-man package version 1:2.43.0-1ubuntu7.3, and this guide follows that local manual page, including its index and sparse-checkout behaviour.
$ git --version
git version 2.43.0
$ git status --short
$ git ls-files -- path/to/old-config.ini
path/to/old-config.ini
The second command should show the path if Git tracks it. An empty result means git rm is probably the wrong tool here, because it only removes paths Git already knows about. Read the status before acting, so an existing staged change or an unrelated dirty file does not surprise you.
Checkpoint
You have confirmed the repository, the exact tracked path and the change you intend to make.
2. Preview the removal
Use --dry-run before a removal that matters. It reports the paths that would be removed without changing the index or working tree.
$ git rm --dry-run -- path/to/old-config.ini
rm 'path/to/old-config.ini'
The -- separates options from pathspecs. Keep it when a filename could begin with a hyphen, or just as a clear boundary in scripts. The output is a plan, not proof the command will still succeed later: another process or an earlier command can change the repository between the preview and the real operation.
3. Remove one file from disk and the index
Once the preview looks right, run the same command without --dry-run:
$ git rm -- path/to/old-config.ini
rm 'path/to/old-config.ini'
$ git status --short
D path/to/old-config.ini
This stages the deletion and removes the working-tree file. It is not a commit: the deletion stays local until you commit it, so check the staged diff before sharing or merging the change:
$ git diff --cached --stat
path/to/old-config.ini | 12 ------------
1 file changed, 12 deletions(-)
$ git diff --cached -- path/to/old-config.ini
Warning
Do not treat a successful git rm as a backup. A file removed from the working tree is not recoverable from an uncommitted working copy. If the content existed in a previous commit, Git can usually restore it, but verify that history actually contains the version you need before committing anything irreversible.
4. Keep the file locally but stop tracking it
For a local configuration file that should stay on this machine, use --cached:
$ git rm --cached -- config/local.ini
rm 'config/local.ini'
$ test -f config/local.ini && echo 'file remains on disk'
file remains on disk
$ git status --short
D config/local.ini
?? config/local.ini
The staged deletion tells the next commit to remove the file from the repository. The untracked entry shows the working copy still exists. Add a suitable pattern to .gitignore before committing if this is a generated or machine-specific file. Do not add real passwords, tokens or private keys to an ignore file as a substitute for rotating a secret that was already committed.
--cached can also remove a modified path from the index when the staged content matches either the branch tip or the working file. For other changes, Git refuses by default so you do not silently discard staged work.
5. Remove a directory or a selected group
A leading directory name needs -r. Preview first, especially if the directory holds more than the files you meant to delete:
$ git rm -r --dry-run -- docs/generated
rm 'docs/generated/index.html'
rm 'docs/generated/version.txt'
$ git rm -r -- docs/generated
Pathspec matching is done by Git when the pattern is quoted, and it can match files below nested directories. For example, this quoted pattern selects tracked text files below Documentation:
$ git rm --dry-run -- 'Documentation/*.txt'
Quoting is a useful distraction trap here. An unquoted * may be expanded by the shell first, changing what Git receives. A pattern such as 'd*' can also match a directory named d2, whereas 'd/*' describes paths below d. Use --pathspec-from-file for a long list, and add --pathspec-file-nul when the list is NUL-separated or contains newlines and quotes that must be treated literally.
6. Handle modified files deliberately
By default, git rm refuses a file whose working-tree or staged content has changed from the branch tip. That refusal is there to protect edits:
$ git rm -- path/to/edited-file
error: the following file has local modifications:
path/to/edited-file
Decide first whether those edits matter. Save them in a commit or a patch if they do. If deletion is definitely intended, -f or --force overrides the up-to-date check and removes the file, and previews the same way:
$ git rm --dry-run --force -- path/to/edited-file
rm 'path/to/edited-file'
$ git rm --force -- path/to/edited-file
Warning
Force is destructive to the uncommitted working copy. It is not an elevated-privilege operation, and it does not make the discarded content recoverable by itself.
7. Undo before committing
If you remove a file and change your mind before committing, restore both the index and working tree from the current HEAD:
$ git restore --source=HEAD --staged --worktree -- path/to/old-config.ini
$ git status --short
$ test -f path/to/old-config.ini && echo restored
restored
For a --cached removal where you want to keep the local file and only undo the staged deletion, restore the index without touching the working tree:
$ git restore --source=HEAD --staged -- config/local.ini
$ git status --short
M config/local.ini
Check the output carefully. If the path was already modified before the removal, restoring from HEAD may replace that working copy. A committed deletion needs a different recovery step, such as restoring the path from the commit that still contains it, then reviewing and committing that restoration.
8. Deal with missing files and submodules
If a tracked file was already deleted with ordinary rm, git rm is not required: git add -u or git commit -a can record such removals as part of their broader workflows. If you want to remove only disappeared paths from the index while leaving other dirty work alone, inspect the deleted-path list first rather than applying a broad command.
A submodule has extra history and metadata. The manual distinguishes removing a submodule with git rm from removing only its local checkout with git submodule deinit. Use the latter when the repository should remain configured and only the checkout should disappear. Never delete a submodule's internal .git directory casually, because an older layout can hold the submodule's history there.
Done means
- The target path was confirmed with
git ls-filesand previewed with--dry-run. - You chose between deleting the working copy and using
--cachedto keep it. - Directory removals used -r, and pathspecs were quoted where Git should expand them.
- Modified files were saved or reviewed before any use of
--force. - git diff --cached shows only the intended deletion, uncommitted until recovery is no longer needed.