To remove a file from Git tracking without deleting it locally, use git rm --cached <file> and commit the staged removal. If future changes to that file should not appear in Git status, add the file to .gitignore before committing the removal. This is the key difference: .gitignore prevents new untracked files from being added, but it does not remove files that are already tracked by Git.
Most searches for git stop tracking file, git remove tracked file, remove file from Git tracking, git remove file from index, or remove file from Git status are asking for the same fix: remove the path from the Git index while keeping the working tree copy. The sections below separate that from deleting a file, amending a commit, and hiding local-only edits.
This article uses tested output from a temporary Git repository. The examples were run with Git 2.48.1 on June 9, 2026.
Git Remove File from Tracking Cheat Sheet
| Task | Command pattern | What happens |
|---|---|---|
| Stop tracking one file but keep it locally | git rm --cached <file> |
Removes the file from the Git index only |
| Stop tracking one folder but keep it locally | git rm -r --cached <folder>/ |
Removes tracked files under the folder from the index |
| Preview folder removal | git rm -r --cached --dry-run <folder>/ |
Prints files that would be removed from tracking |
| Delete a tracked file from Git and disk | git rm <file> |
Stages deletion and removes the local file |
| Undo a staged Git removal before commit | git restore --staged <file> then git restore <file> |
Restores the file to the working tree |
| Remove all tracked files now covered by ignore rules | git rm -r --cached . then git add . |
Keeps valid tracked files and stages ignored files for untracking |
| Avoid script failure when a path is not tracked | git rm --cached --ignore-unmatch <file> |
Exits successfully even when the path is absent from the index |
| Hide local edits in your clone only | git update-index --skip-worktree <file> |
Local hint, not a repository-wide ignore rule |
Use the sections below for tested output and the safer order of operations.
Tested Lab Setup
The following lab repository was created in /tmp, so the commands could be verified without touching a real project.
git --version
git init -b main untrack-demo
cd untrack-demo
git config user.name "GoLinuxCloud Test"
git config user.email "test@golinuxcloud.local"
mkdir -p logs build src
printf 'password=local-only\n' > config.yml
printf 'debug log\n' > logs/debug.log
printf 'error log\n' > logs/error.log
printf 'binary output\n' > build/app.bin
printf 'print("hello")\n' > src/app.py
git add .
git commit -m "track sample files"
git ls-filesTested output:
git version 2.48.1
Initialized empty Git repository in /tmp/git-untrack-clean.CL31wj/untrack-demo/.git/
[main (root-commit) af71b4d] track sample files
5 files changed, 5 insertions(+)
create mode 100644 build/app.bin
create mode 100644 config.yml
create mode 100644 logs/debug.log
create mode 100644 logs/error.log
create mode 100644 src/app.py
build/app.bin
config.yml
logs/debug.log
logs/error.log
src/app.pyRemove a File from Git Tracking Without Deleting It
In this example, config.yml is already tracked. We want Git to stop tracking it, but we want the local file to remain on disk.
printf 'config.yml\n' > .gitignore
git add .gitignore
git rm --cached config.yml
git status --short
git commit -m "stop tracking config file"
ls config.yml
printf 'password=changed-locally\n' > config.yml
git status --shortTested output:
rm 'config.yml'
A .gitignore
D config.yml
[main 23f9783] stop tracking config file
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 .gitignore
delete mode 100644 config.yml
config.ymlThe final git status --short command produced no output. That is expected: after config.yml is removed from the Git index and matched by .gitignore, local edits no longer appear in normal status output.
If you only add a tracked file to .gitignore, Git continues tracking it. The official gitignore documentation is clear that ignore rules are for intentionally untracked files. For a broader set of ignore patterns, see these Git ignore examples.
Remove a Folder from Git Tracking
For a folder or directory, use -r so Git removes tracked files below that path from the index. A dry run is useful before untracking a folder with many files.
git rm -r --cached --dry-run logs/Tested output:
rm 'logs/debug.log'
rm 'logs/error.log'Now add the folder to .gitignore and remove it from tracking.
printf 'logs/\n' >> .gitignore
git add .gitignore
git rm -r --cached logs/
git status --short
ls logs
git commit -m "stop tracking logs directory"
git ls-filesTested output:
rm 'logs/debug.log'
rm 'logs/error.log'
M .gitignore
D logs/debug.log
D logs/error.log
debug.log
error.log
[main d932410] stop tracking logs directory
3 files changed, 1 insertion(+), 2 deletions(-)
delete mode 100644 logs/debug.log
delete mode 100644 logs/error.log
.gitignore
build/app.bin
src/app.pyThe files still exist locally, but Git no longer tracks them after the commit. This also covers searches such as remove folder from Git tracking, remove directory from Git tracking, and Git remove files from tracking.
Difference Between Git rm and Git rm Cached
Use git rm --cached when you want to remove the file from the Git index only. Use git rm when you want Git to delete the file from the working directory and stage that deletion.
git rm build/app.bin
git status --short
test -e build/app.bin; echo "build_app_exists=$?"Tested output:
rm 'build/app.bin'
D build/app.bin
build_app_exists=1In shell exit codes, 1 means the file does not exist. So git rm build/app.bin deleted the local file and staged the deletion.
To undo this before committing, use git restore to restore the staged removal and then restore the working tree file.
git restore --staged build/app.bin
git restore build/app.bin
test -e build/app.bin; echo "build_app_exists_after_restore=$?"Tested output:
build_app_exists_after_restore=0Here 0 means the file exists again. For more delete-focused examples, see remove a file or directory in Git.
Remove All Tracked Files That Are Now Ignored
If many tracked files should now be ignored, update .gitignore first, then refresh the index.
printf 'build/\n' >> .gitignore
git add .gitignore
git commit -m "ignore build directory"
git rm -r --cached .
git add .
git status --short
git commit -m "remove ignored files from tracking"
git ls-filesTested output:
[main 5627b29] ignore build directory
1 file changed, 1 insertion(+)
rm '.gitignore'
rm 'build/app.bin'
rm 'src/app.py'
D build/app.bin
[main a8a8df0] remove ignored files from tracking
1 file changed, 1 deletion(-)
delete mode 100644 build/app.bin
.gitignore
src/app.pyThe rm '.gitignore' and rm 'src/app.py' lines can look alarming, but git add . stages the files that should remain tracked. The final status only staged build/app.bin for removal because build/ was ignored.
Run this from the repository root and review git status --short before committing. If you want a safer preview, run git rm -r --cached --dry-run . first.
Remove a File from the Last Commit But Keep It Locally
If you accidentally committed a file and have not shared that commit, remove the file from the index and amend the commit. This is useful for searches such as Git remove file from commit, Git remove files from commit, and Git rm file from commit.
git init -b main remove-from-commit-demo
cd remove-from-commit-demo
git config user.name "GoLinuxCloud Test"
git config user.email "test@golinuxcloud.local"
printf 'public setting\n' > app.conf
printf 'token=abc123\n' > secret.env
git add app.conf secret.env
git commit -m "add app config"
printf '# keep local secrets out of Git\nsecret.env\n' > .gitignore
git add .gitignore
git rm --cached secret.env
git commit --amend --no-edit
git ls-tree --name-only HEAD
ls secret.env
git status --shortTested output:
Initialized empty Git repository in /tmp/git-remove-commit-lab.J38ROY/remove-from-commit-demo/.git/
[main (root-commit) 84dc690] add app config
2 files changed, 2 insertions(+)
create mode 100644 app.conf
create mode 100644 secret.env
rm 'secret.env'
[main dccb4e5] add app config
Date: Tue Jun 9 09:00:49 2026 +0530
2 files changed, 3 insertions(+)
create mode 100644 .gitignore
create mode 100644 app.conf
.gitignore
app.conf
secret.envThe final git status --short command produced no output because secret.env exists locally and is ignored. The amended commit tracks .gitignore and app.conf, not secret.env.
Do not amend a commit that other people already pulled unless your team agrees to rewrite history. For already pushed commits, a normal follow-up commit is usually safer. If you need to remove a file from older history, read about removing commits in Git and use a history-rewrite tool only after coordinating with your team.
Use Ignore Unmatch in Scripts
Automation should not fail just because a path is already untracked or absent from the Git index. Use --ignore-unmatch for that case.
git rm --cached --ignore-unmatch missing.txt
echo "ignore_unmatch_exit=$?"Tested output:
ignore_unmatch_exit=0Exit code 0 means the command succeeded even though missing.txt was not tracked.
Skip Worktree and Assume Unchanged Are Local Hints
Sometimes you do not want to remove a file from the repository; you only want Git in your clone to stop reporting your local edits. For that local-only case, Git has skip-worktree and assume-unchanged bits.
skip-worktree is usually the more relevant option for local config files that remain tracked in the repository:
git update-index --skip-worktree src/app.py
printf '# local edit\n' >> src/app.py
git status --short
git ls-files -v src/app.py
git update-index --no-skip-worktree src/app.py
git status --shortTested output:
S src/app.py
M src/app.pyThe first git status --short produced no output while the skip-worktree bit was set. git ls-files -v showed S, then status showed the modification again after clearing the bit.
assume-unchanged is mainly a performance hint, but many developers use it to hide local edits temporarily:
git restore src/app.py
git update-index --assume-unchanged src/app.py
printf '# another local edit\n' >> src/app.py
git status --short
git ls-files -v src/app.py
git update-index --no-assume-unchanged src/app.py
git status --shortTested output:
h src/app.py
M src/app.pyAgain, the first status command produced no output while the bit was set. These commands do not update .gitignore, do not affect teammates, and do not remove the file from tracking. Use them only when the repository should continue tracking the file.
What Happens After You Push This Change?
git rm --cached keeps the file on the machine where you run the command. The commit, however, records the file as removed from the repository. After that commit is pushed, other developers who pull it may see Git remove the tracked file from their working tree.
That behavior is correct from Git's point of view: the file no longer exists in the latest repository snapshot. For shared config files, a safer pattern is often to commit a template such as config.example.yml, ignore the real local file, and document how each developer creates their own copy.
Troubleshooting
| Symptom | Likely reason | Fix |
|---|---|---|
| File still appears in status after adding it to ignore rules | The file is already tracked | Remove it from the index with git rm --cached <file> |
| Folder still appears as deleted after cached removal | That deletion is the staged index change Git must commit | Commit the change after confirming files still exist locally |
| Local file disappeared | You used git rm instead of the cached form |
Restore before commit with git restore --staged <file> and git restore <file> |
| A script fails when the path is not tracked | Git returns an error for unmatched paths by default | Add --ignore-unmatch |
| You only want to hide local edits in one clone | This is not an ignore-file problem | Use git update-index --skip-worktree <file> temporarily |
FAQ
How do I remove a file from Git tracking without deleting it?
Add the file to .gitignore if it should remain ignored, then run git rm --cached <file> and commit the staged deletion. The local file stays on disk.
How do I remove a folder from Git tracking without deleting it?
Add the folder to .gitignore, run git rm -r --cached <folder>/, verify the files still exist locally, and commit the change.
Why is a file still tracked after adding it to .gitignore?
Because .gitignore does not apply retroactively to tracked files. Remove the file from the Git index first, then the ignore rule can keep future local changes quiet.
Does git rm --cached delete the file locally?
No. The cached form removes the file from the Git index only. Plain git rm <file> deletes the local file and stages the deletion.
How do I remove a file from Git status?
If the file is tracked, untrack it with git rm --cached <file> and add a matching ignore rule. If it is untracked, adding the right .gitignore rule is enough.
Can I remove all tracked ignored files at once?
Yes. Run git rm -r --cached ., then git add ., review git status --short, and commit. This keeps files that should remain tracked and stages ignored files as removed from tracking.
Is git rm --cached safe after pushing?
It is safe technically, but it changes the repository snapshot. Other developers may lose the tracked copy when they pull the commit, so coordinate before doing this for shared files.
Should I use skip-worktree instead of git rm --cached?
Use git rm --cached when everyone should stop tracking the file. Use skip-worktree only when your local clone should temporarily hide changes to a file that remains tracked.

