Fix: .gitignore Not Working (Files Still Being Tracked)
Quick Answer
How to fix .gitignore not working — files still showing in git status after being added to .gitignore, caused by already-tracked files, wrong syntax, nested gitignore rules, and cache issues.
The Error
You add a file or pattern to .gitignore but Git still tracks the file. It shows up in git status as modified, or git add . still stages it. There is no error — .gitignore just silently has no effect.
Common symptoms:
node_modules/is listed in.gitignorebutgit statusstill shows changes inside it.- A
.envfile appears ingit diffeven after adding.envto.gitignore. - A build output folder keeps showing as untracked despite being ignored.
.gitignoreworks for new files but not for files already in the repository..gitignorerules work locally but not after cloning the repository on another machine.
Why This Happens
.gitignore only prevents untracked files from being added to Git. If a file is already tracked (previously committed), .gitignore has no effect on it — Git continues tracking it regardless.
Other causes:
- The file was committed before
.gitignorewas created — it is now tracked and.gitignorecannot retroactively untrack it. - Wrong syntax in the
.gitignorepattern — a typo, missing wildcard, or incorrect path separator. .gitignoreis in the wrong directory — patterns are relative to the directory containing the.gitignorefile.- A negation pattern (
!) re-includes a file that was excluded by a parent pattern. - Global gitignore (
~/.gitignore_global) is not set up, so OS-generated files like.DS_Storeare not ignored. - The pattern matches a directory but not its contents, or vice versa.
Fix 1: Remove Already-Tracked Files from the Index
If Git is already tracking a file, you must explicitly remove it from the index (the staging area / cache) to stop tracking it:
# Remove a specific file from tracking (keeps the file on disk)
git rm --cached path/to/file.env
# Remove a directory from tracking recursively
git rm --cached -r node_modules/
# Remove all files that now match .gitignore
git rm --cached -r .After removing from the cache, commit the removal:
git add .gitignore
git commit -m "Remove tracked files that should be ignored"From this point forward, Git no longer tracks those files, and .gitignore prevents them from being re-added.
Warning:
git rm --cachedremoves the file from Git’s tracking but keeps it on your disk. It does NOT delete the file. However, when your teammates pull this commit, the file will be deleted from their working directory since it is being removed from the repository. Make sure teammates know to keep a local copy if needed.
Pro Tip: Before running
git rm --cached -r ., make sure your.gitignoreis complete. After clearing the cache and recommitting, only files not matching.gitignorewill be re-added. Rungit statusbefore committing to verify exactly which files will be tracked going forward.
Fix 2: Fix .gitignore Pattern Syntax
A malformed pattern silently fails — no error, just no effect. Common syntax mistakes:
Paths must be relative to the .gitignore file location:
# Wrong — absolute path never matches
/home/user/project/build/
# Correct — relative path
build/Leading slash anchors the pattern to the repo root:
# Matches /logs/ at the root only
/logs/
# Matches logs/ anywhere in the tree
logs/Trailing slash matches directories only:
# Matches the dist directory but NOT a file named dist
dist/
# Matches both files and directories named dist
distWildcards:
# * matches anything except /
*.log # All .log files in any directory
logs/*.log # Only .log files directly in logs/
# ** matches across directories
logs/**/*.log # All .log files anywhere under logs/
**/*.env # All .env files anywhere in the repoTest a pattern before committing:
# Check if a specific file would be ignored
git check-ignore -v path/to/file.env
# Check all files that would be ignored
git check-ignore -v *
# Check why a file is NOT being ignored
git check-ignore -v --no-index .envgit check-ignore -v prints the pattern and the .gitignore file that caused the match — invaluable for debugging.
Fix 3: Verify .gitignore File Location
.gitignore rules apply to the directory it is in and all subdirectories. A .gitignore in a subdirectory only affects that subdirectory:
project/
├── .gitignore ← Rules apply to everything in project/
├── src/
│ ├── .gitignore ← Rules apply only to src/ and below
│ └── components/
└── dist/Common mistake — .gitignore not at the repo root:
# Check if .gitignore is at the root
ls -la .gitignore
# If it's missing, create it
touch .gitignoreCheck which .gitignore is affecting a file:
git check-ignore -v path/to/file
# Output: .gitignore:5:*.log path/to/file.log
# Shows: file path, line number, pattern, matched fileFix 4: Fix Negation Pattern Issues
Negation patterns (!) re-include files excluded by earlier patterns. Order matters:
# Ignore all .env files
*.env
# But keep .env.example — this works
!.env.exampleBroken — trying to negate inside an ignored directory:
# Ignore node_modules
node_modules/
# Try to keep a specific file — THIS DOES NOT WORK
!node_modules/important-file.jsOnce a directory is ignored, Git does not look inside it — negation patterns for files inside an ignored directory have no effect. The fix is to not ignore the directory wholesale:
# Instead of ignoring the whole directory, ignore specific patterns
node_modules/*
!node_modules/important-file.jsOr restructure so the file you want to keep is outside the ignored directory.
Fix 5: Set Up a Global .gitignore
OS-generated and editor-specific files (.DS_Store, Thumbs.db, .idea/, .vscode/) should be ignored globally, not per-project, so every repo on your machine ignores them automatically:
# Create a global gitignore file
touch ~/.gitignore_global
# Configure Git to use it
git config --global core.excludesFile ~/.gitignore_globalAdd OS and editor files to ~/.gitignore_global:
# macOS
.DS_Store
.AppleDouble
.LSOverride
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
# VS Code
.vscode/
*.code-workspace
# JetBrains IDEs
.idea/
*.iml
# Vim
*.swp
*.swo
*~This keeps project .gitignore files clean and focused on project-specific ignores.
Fix 6: Fix .gitignore After It Was Committed With Wrong Contents
If you accidentally committed a .gitignore that was too permissive (and files got committed that should not have been), fix it by updating .gitignore and clearing the cache:
# Step 1: Update .gitignore with the correct rules
echo "node_modules/" >> .gitignore
echo ".env" >> .gitignore
echo "dist/" >> .gitignore
# Step 2: Clear the entire Git index cache
git rm --cached -r .
# Step 3: Re-add everything (only non-ignored files will be added)
git add .
# Step 4: Commit
git commit -m "Fix: update .gitignore and remove incorrectly tracked files"Verify before committing:
git status
# Should NOT show node_modules/, .env, dist/ etc.
git ls-files | grep "node_modules"
# Should return nothinggit ls-files lists every file Git is currently tracking. Use it to audit what is in the index.
Fix 7: Handle .env and Secret Files Already Pushed
If you accidentally committed .env or secrets to the remote repository, removing them from .gitignore and the index is not enough — they exist in the Git history and can still be accessed.
Remove from the current state:
git rm --cached .env
echo ".env" >> .gitignore
git commit -m "Remove .env from tracking"
git pushRemove from history entirely (use with caution):
# Using git filter-repo (recommended over filter-branch)
pip install git-filter-repo
git filter-repo --path .env --invert-paths
# Force push (required after rewriting history)
git push --forceWarning: Rewriting history disrupts all collaborators — they must re-clone or rebase. After removing secrets from history, rotate all exposed credentials immediately. The secrets were public (or accessible to anyone with repo access) from the moment they were pushed.
For credentials accidentally pushed to GitHub, GitHub’s secret scanning may have already detected and notified you. Treat all exposed secrets as compromised regardless.
Still Not Working?
Check for a .git/info/exclude file. This is a per-repository ignore file that is not committed. It works the same as .gitignore but is local only. Check if conflicting rules exist there:
cat .git/info/excludeCheck git config core.excludesFile. If this is set to a different global ignore file, your ~/.gitignore_global may not be the active global ignore:
git config --global core.excludesFileCheck for core.ignoreCase settings. On case-insensitive filesystems (macOS, Windows), Git may or may not respect case in .gitignore patterns depending on the core.ignoreCase setting. If *.Log does not ignore file.log, check this setting:
git config core.ignoreCaseCheck that the file is not staged. If a file is already staged (in the index), .gitignore does not affect it. Run git restore --staged file.env to unstage, then .gitignore applies on the next git add.
For accidentally committed and pushed sensitive data, also see Fix: git push rejected (non-fast-forward) if you encounter push issues after rewriting history.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Undo git reset --hard and Recover Lost Commits
How to undo git reset --hard and recover lost commits using git reflog — step-by-step recovery for accidentally reset branches, lost work, and dropped stashes.
Fix: git fatal: A branch named 'x' already exists
How to fix 'git fatal: A branch named already exists' when creating or renaming branches — including local conflicts, remote tracking branches, and worktree issues.
Fix: Git Keeps Asking for Username and Password
How to fix Git repeatedly prompting for credentials — credential helper not configured, HTTPS vs SSH, expired tokens, macOS keychain issues, and setting up a Personal Access Token.
Fix: Git submodule update failed / fatal: not a git repository
Resolve Git submodule update and init failures including 'fatal: not a git repository', path conflicts, URL mismatches, shallow clone issues, and CI/CD checkout problems.