Most “gitignore is broken” reports have one cause: ignore rules do not apply to files Git already tracks. Untrack the file without deleting your local copy, commit that change, and the rule starts working.
🎙️ Published & recorded: ·
Gitignore decides which untracked files stay invisible. It does not retroactively remove a path from the repository. Add the rule first, remove only the index copy with the cached flag, then commit. The local file remains on disk. Before doing this to a folder, check the path carefully; the recursive command changes tracking for everything below it.
# .env was committed before the ignore rule existed:
git rm --cached .env
git commit -m "Stop tracking .env"
# Whole folder version; local files remain:
git rm -r --cached node_modules/
git commit -m "Stop tracking node_modules"
# Show the matching ignore file, line, and pattern:
git check-ignore -v .env
git status --ignored
fatal: pathspec '.env' did not match any filesFirst confirm the spelling and current directory. Run git ls-files -- .env. No output means there is no tracked index entry to remove, so skip the cached removal. Then run git check-ignore -v .env. If that also prints nothing, fix the ignore rule or the location of the gitignore file. If the file itself does not exist, create it only when your application needs it.
A bare filename or wildcard can match at more than one depth. A leading slash anchors the rule to the repository root. A trailing slash means directory only. Double star crosses directory levels, and an exclamation mark re-includes a path. Read rules from top to bottom because a later matching rule can change the earlier decision.
*.log # every .log file, anywhere
build/ # directories named build
/config.json # config.json only at repository root
docs/*.pdf # PDFs directly inside docs
docs/**/*.pdf # PDFs at any depth below docs
!keep.log # exception to an earlier *.log rule
# Keep one file inside an otherwise ignored directory:
!logs/
logs/*
!logs/keep.txt
If logs/ is ignored, Git stops walking into that directory, so a lone !logs/keep.txt cannot rescue the file. Re-include the directory, ignore its contents, then re-include the one file, in that order. Use git check-ignore -v logs/keep.txt after each edit instead of guessing which rule won.
Start with generated files, dependency directories, local environments, and operating-system litter. Do not blindly ignore every editor directory; teams sometimes share useful workspace settings. For a real stack, compare your short list with the maintained templates in GitHub’s gitignore repository, then keep only rules you understand.
# Python
__pycache__/
*.pyc
.venv/
venv/
.env
dist/
*.egg-info/
# Node
node_modules/
dist/
build/
.env
.env.*
npm-debug.log*
# Common local litter
.DS_Store
Thumbs.db
*.swp
.idea/
.vscode/
If the tracked file contained a key, untracking is not incident response. The old value remains in earlier commits, forks, caches, and somebody else’s clone. Revoke or rotate the credential at the provider first. Then remove it from current tracking, update the application to use the replacement securely, and clean history with git filter-repo only if the repository policy requires it. Rotation makes the leaked value useless; rewriting history does not.