.gitignore is a plain text file, usually in the repository root.
Each line is a pattern (glob-style). Files and directories matching
a pattern are ignored by Git: they do not appear in git status and
do not get picked up by git add ..
Basic syntax rules
# Comments start with #
*.log # all .log files in any directory
node_modules/ # the whole directory, anywhere in the tree
/build # the build directory only at the repo root
!important.log # negation: do NOT ignore this file
src/**/*.pyc # recursive pattern with **
What belongs in .gitignore
- Build artifacts:
dist/,build/,target/,*.o,*.pyc. - Dependencies:
node_modules/,.venv/,vendor/(unless you vendor explicitly). - IDE files:
.vscode/,.idea/. - System files:
.DS_Store,Thumbs.db. - Secrets:
.env,*.pem,*.key,secrets.yaml,credentials/. - Logs and caches:
*.log,.cache/,__pycache__/.
The last category matters most. A secret that lands in a commit is considered exposed, even if you remove it later. Git history shows the old content. The fix is rotating the key plus rewriting history with git-filter-repo.
What .gitignore does NOT do
- It does not untrack an already-tracked file. If you added
secrets.yamlto.gitignoreafter committing it, Git keeps tracking it. You needgit rm --cached secrets.yamlseparately. - It does not "protect" the file from being read. The file still exists on disk; anyone can open it.
- It does not override a forced add.
git add -f secrets.yamlstages the file regardless of ignore rules.
Where .gitignore can live
There are several levels:
- Per-repo:
.gitignorein the root, committed to the repo. This is the main one. - Per-directory:
.gitignoreinside a subdirectory, affecting only its contents. - Global (per-user):
~/.config/git/ignoreor the path incore.excludesFile. For personal preferences: your IDE, your OS. Not committed. - Local-only:
.git/info/exclude. Ignore rules for your clone only, not shared.
# Set a global gitignore
git config --global core.excludesFile ~/.gitignore_global
Put OS and IDE files in ~/.gitignore_global: .DS_Store,
Thumbs.db, .vscode/, .idea/. That keeps the per-project
.gitignore focused on project-specific patterns.
Standard templates
Do not write from scratch. The github.com/github/gitignore repo
has templates for every language: Python.gitignore,
Node.gitignore, Java.gitignore. Copy one into your project and
adjust as needed.
If you already committed something you should not have
# Stop tracking the file without deleting it from disk
git rm --cached path/to/secret
# Add it to .gitignore
echo "path/to/secret" >> .gitignore
# Commit
git add .gitignore
git commit -m "stop tracking secret"
This keeps the file out of future commits. But it is still in the history: anyone can recover it from old commits. If it is a real secret, you need git-filter-repo and key rotation.
Pitfalls
.gitignoreinside.gitignore. You see this sometimes. It is usually a mistake..gitignoreshould be committed, otherwise the team has inconsistent ignore rules.- Local ignore vs the team.
.git/info/excludeis "mine only." If you use it, note it somewhere explicit. Otherwise you will forget in a year that something is hidden. - Case sensitivity depends on the filesystem. On macOS and
Windows,
*.PNGand*.pngusually match the same files. On Linux they do not. For cross-platform projects, list both variants.