Skip to content

Fix: .gitignore Not Working (Files Still Being Tracked)

FixDevs ·

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 .gitignore but git status still shows changes inside it.
  • A .env file appears in git diff even after adding .env to .gitignore.
  • A build output folder keeps showing as untracked despite being ignored.
  • .gitignore works for new files but not for files already in the repository.
  • .gitignore rules 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 .gitignore was created — it is now tracked and .gitignore cannot retroactively untrack it.
  • Wrong syntax in the .gitignore pattern — a typo, missing wildcard, or incorrect path separator.
  • .gitignore is in the wrong directory — patterns are relative to the directory containing the .gitignore file.
  • 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_Store are 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 --cached removes 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 .gitignore is complete. After clearing the cache and recommitting, only files not matching .gitignore will be re-added. Run git status before 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
dist

Wildcards:

# * 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 repo

Test 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 .env

git 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 .gitignore

Check 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 file

Fix 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.example

Broken — 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.js

Once 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.js

Or 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_global

Add 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 nothing

git 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 push

Remove 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 --force

Warning: 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/exclude

Check 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.excludesFile

Check 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.ignoreCase

Check 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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles