Ninety percent of "gitignore is broken" is one fact: ignore rules only apply to files Git isn't already tracking. The fix is two commands. Everything else is syntax.
# you added .env to .gitignore, but git still tracks it —
# because it was committed BEFORE the rule existed.
git rm --cached .env # untrack it (keeps your local file!)
git commit -m "Stop tracking .env"
# whole folder version:
git rm -r --cached node_modules/
git commit -m "Stop tracking node_modules"
git check-ignore -v .env
# .gitignore:2:.env .env ← which file, which line, which rule
# no output = nothing ignores it (or it's already tracked)
git status --ignored # see everything being ignored
*.log # every .log file, anywhere
build/ # the folder "build" (trailing / = folders only)
/config.json # ONLY at repo root (leading / = anchor)
docs/*.pdf # pdfs directly in docs/ (not subfolders)
docs/**/*.pdf # pdfs anywhere under docs/
!keep.log # exception: track this despite *.log above
!important.txt can't rescue a file inside an ignored folder. If logs/ is ignored, Git never looks inside it, so !logs/keep.txt does nothing. Rescue path: un-ignore the folder itself, re-ignore its contents, then except the file — !logs/ + logs/* + !logs/keep.txt.
# Python
__pycache__/
*.pyc
.venv/
venv/
.env
dist/
*.egg-info/
# Node
node_modules/
dist/
build/
.env
.env.*
npm-debug.log*
# universal add-ons (any project)
.DS_Store # macOS litter
Thumbs.db # Windows litter
*.swp # vim litter
.idea/ .vscode/ # editor configs (team preference varies)
Full templates for any stack: github.com/github/gitignore — official, maintained, copy freely.
git rm --cached untracks it going forward but the secret stays in history, and public-repo scrapers find keys in minutes. Order of operations: 1) revoke/rotate the key right now at the provider, 2) then clean history (git filter-repo) if it matters. Rotating is the fix; history surgery is hygiene. Full walkthrough on the main Git page, section 08.