Fix a .gitignore that isn't ignoring files
Why is .gitignore not working for files that are already tracked?
Short answer
.gitignore only applies to untracked files. Once a file has been committed, Git keeps tracking it no matter what the ignore rules say. Remove it from the index with git rm --cached <file> — that stops tracking while leaving the file on your disk.
git rm --cached path/to/file
git commit -m "Stop tracking file"
Untracks the file without deleting it from your computer
Does this match your situation?
- You added a file to .gitignore but it still appears in git status.
- node_modules, bin/, obj/, Library/ or .env keep showing up as modified.
- Ignore rules work for teammates but not for you (or vice versa).
Step-by-step fix
Check why Git isn't ignoring it
check-ignore names the exact rule and file that matched — or tells you nothing matched, which means the file is tracked and ignore rules simply don't apply.
step 1git check-ignore -v path/to/fileNo output means no rule matched. Output means the rule matched but the file is already tracked.
Stop tracking the file, keep it on disk
--cached removes the file from Git's index only. Your local copy is untouched, but Git stops following it and the ignore rule takes effect from now on.
step 2git rm --cached .env # a whole directory: git rm -r --cached node_modules/ git commit -m "Stop tracking files covered by .gitignore"Without --cached, git rm deletes the file from your disk too.
Reset the whole index when several files are involved
If the ignore file has drifted a long way from reality, it's cleaner to clear the index and re-add everything — Git then honours the current rules for every path at once.
step 3git rm -r --cached . git add . git commit -m "Apply .gitignore to the whole repository"Check your ignore-rule syntax
Most 'broken' ignore files are pattern mistakes. Directories need care, negation only works if the parent directory isn't excluded, and a leading slash anchors to the repo root.
step 4node_modules/ # a directory, at any depth /build # only at the repository root *.log # every .log file !important.log # except this one Library/ # Unity: never commit this **/bin/ # bin at any depthRemember the file stays in history
Untracking stops future commits from including the file, but every existing commit still contains it. If it held a secret, that secret is still in history and must be purged and rotated.
step 5git log --all --oneline -- .env
Why this works
Git divides files into tracked and untracked, and .gitignore is only consulted for untracked ones. That design is deliberate: once you've explicitly told Git to manage a file, a stray ignore pattern shouldn't silently stop recording your changes to it. So the fix isn't to adjust the pattern but to move the file back to untracked status, which is exactly what git rm --cached does — it deletes the index entry while leaving the working file alone.
How to stop it happening again
- Create .gitignore before your first commit — gitignore.io generates one for your stack.
- For Unity, ignore Library/, Temp/, Obj/, Build/, Logs/ and UserSettings/ from the very start.
- Use a global ignore file for editor and OS junk: git config --global core.excludesFile ~/.gitignore_global.
Commands used in this guide
git statusShow what's changed, what's staged, and what Git is ignoring.
git restoreThrow away working-directory changes or unstage files (the modern, clearer replacement for checkout).
git filter-repoRewrite an entire repository's history - the supported way to purge a leaked secret or a huge file.
git addStage changes so they go into your next commit.
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
Why is .gitignore not working?
Almost always because the file is already tracked. .gitignore only affects untracked files, so a committed file keeps being tracked regardless of ignore rules. Run git rm --cached <file> to untrack it, then commit.
How do I untrack a file without deleting it?
git rm --cached <file>. The --cached flag removes it from Git's index while leaving your local copy in place. Omit --cached and Git deletes the file from your disk as well.
How do I apply .gitignore to files already committed?
git rm -r --cached . followed by git add . and a commit. This clears the index and rebuilds it while honouring the current ignore rules, so everything newly covered gets untracked in one commit.
Does untracking remove the file from history?
No. Every previous commit still contains it. If the file held credentials, you also need to rewrite history with git filter-repo — and rotate the credentials, since they should be considered compromised.
Related rescue guides
How do I undo git add without losing my changes?
git restore --staged <file> removes a file from the staging area and leaves your edits completely untouched. The older equivalent is git reset HEAD <file>. Neither one touches the contents of your file, so this is a zero-risk operation.
Read the fix →Always fixableI committed a secret — how do I remove it from Git history?
Rotate the secret first — that's the only step that genuinely protects you. Then purge it from history with git filter-repo (or the BFG Repo-Cleaner) and force-push. Deleting the file in a new commit does nothing, because the old commit still contains it and is still fetchable.
Read the fix →Always fixableI committed a large file and my push is rejected — how do I remove it?
If it's still the last commit, git reset --soft HEAD~1 and re-commit without it. If it's further back, purge it from history with git filter-repo --strip-blobs-bigger-than. Going forward, route large binaries through Git LFS.
Read the fix →Rarely recoverable by GitHow do I throw away all my local changes in Git?
git restore . discards edits to tracked files and git clean -fd deletes untracked ones. Both are irreversible for anything that was never staged or committed, so run git stash first if there's any doubt — it takes a second and makes the whole operation undoable.
Read the fix →