A .gitignore file tells Git which files not to track: dependencies, build output, logs, editor files, and, most importantly, secrets like .env files. A good .gitignore keeps the repository small, the history clean, and credentials off GitHub.

Pattern syntax

Pattern Matches
*.log Any .log file, in any directory
node_modules/ Directory node_modules anywhere (trailing / = directories only)
/dist dist only at the repository root
build/*.tmp .tmp files directly in build/
**/cache/ cache directories at any depth
docs/**/*.pdf PDFs anywhere under docs/
!keep.log Negation: don’t ignore this file
# Comment

Rules are read top to bottom, and the last matching rule wins. You can’t re-include a file if its parent directory is ignored. Ignore the contents instead:

logs/*
!logs/.gitkeep

A practical .gitignore for a web project

# secrets and environment
.env
.env.*
!.env.example
*.pem
*.key

# dependencies
node_modules/
vendor/
.venv/

# build output
dist/
build/
.next/
coverage/

# logs and runtime
*.log
tmp/
*.pid

# OS and editors
.DS_Store
Thumbs.db
.idea/
.vscode/*
!.vscode/extensions.json

# Docker and local data
docker-compose.override.yml
data/

Commit an .env.example with placeholder values, so others know which variables exist. See secrets management.

GitHub’s github/gitignore repository has templates for most languages and frameworks. gitignore.io (now part of Toptal) generates combined files.

Global ignores (your machine only)

Editor and OS files are personal. Put them in a global ignore file instead of every repository:

git config --global core.excludesfile ~/.gitignore_global
printf '.DS_Store\n.idea/\n*.swp\n' >> ~/.gitignore_global

Per-repository local ignores that shouldn’t be shared go in .git/info/exclude.

Already tracked? Untrack it

.gitignore only affects untracked files. If a file is already committed, ignoring it does nothing until you remove it from the index:

git rm --cached .env
git rm -r --cached node_modules
git commit -m "Stop tracking .env and node_modules"

The files stay on disk, but Git stops tracking them.

If a secret was ever committed, it’s still in history. Rotate the secret (change the password, revoke the key) immediately. Then optionally remove it from history with git filter-repo, or BFG, and force-push. See GitHub Actions secrets.

Debug ignore rules

git check-ignore -v path/to/file       # which rule (file and line) ignores it
git status --ignored                   # list ignored files
git ls-files -ci --exclude-standard    # tracked files that match ignore rules
  • .dockerignore: keeps files out of Docker build contexts (same idea, with slightly different rules). Always exclude .git, node_modules and .env. See Dockerfile tutorial.
  • .gitattributes: line endings, LFS tracking, and diff settings. See Git LFS.

Quick reference

Task Command / pattern
Ignore directory name/
Root only /name
Re-include !file (parent not ignored)
Global ignores core.excludesfile
Untrack a file git rm --cached <file>
Why ignored? git check-ignore -v <file>

Related: Git hooks and Git commands cheat sheet.

  • Git
  • Security
  • Command Line