.gitignore Guide: What to Ignore and How Patterns Work
Practice4 minInnovialabs editorial team
In short
.gitignore tells Git which untracked files to keep out of the repository, such as dependencies, builds, local settings and secrets; it does not affect files already committed — those must be removed with git rm --cached.
What .gitignore does
.gitignore is a text file with a list of patterns. Files that match are hidden from git status and skipped by git add ..
The key thing to remember: .gitignore only works for untracked files. If a file has already been committed, adding it to .gitignore changes nothing — Git keeps tracking it.
What people usually ignore:
- dependencies:
node_modules/,.venv/,Pods/; - build output:
dist/,build/, caches; - local settings: IDE files,
.DS_Store,Thumbs.db; - secrets:
.env, keys, certificates, keystores.
What you do not ignore: source code, lock files (package-lock.json, poetry.lock) and sample configs such as .env.example.
Pattern syntax
| Pattern | What it matches |
|---|---|
debug.log | A file or folder with that name at any level |
*.log | All files ending in .log |
build/ | Only directories named build (trailing slash) |
/config.local.json | Only at the root, next to this .gitignore |
docs/*.pdf | PDFs directly in docs, not in subfolders |
**/temp | temp at any depth |
logs/** | Everything inside logs |
a/**/b | a/b, a/x/b, a/x/y/b |
file?.txt | Exactly one character |
[abc].txt | One character from the set |
The rules people mix up most often:
*does not cross/. Use**to reach nested folders.- A pattern with a slash at the start or in the middle is anchored to the folder containing the .gitignore. Without a slash it matches at any level.
- Lines starting with
#are comments. Blank lines are ignored.
Negation and nested files
An exclamation mark ! brings back a file that an earlier pattern excluded:
*.env
!example.env
The trap: you cannot re-include a file if its whole parent directory is excluded. Git simply does not look inside. This does not work:
logs/
!logs/.gitkeep
This does — exclude the contents, not the folder itself:
logs/*
!logs/.gitkeep
Nested .gitignore files. Any subfolder can have its own .gitignore. Its patterns are relative to that folder, and when rules conflict, the deeper file wins.
.git/info/exclude uses the same rules but is never committed. It suits personal exclusions in a single repository.
Global gitignore
Junk from your OS and editor is better ignored once for all projects than in every repository:
git config --global core.excludesFile ~/.gitignore_global
Good candidates: .DS_Store, Thumbs.db, .idea/, .vscode/, *.swp. The project .gitignore should hold what concerns the project itself and everyone working on it.
Ready templates
Node.js / frontend:
node_modules/
dist/
build/
.next/
coverage/
npm-debug.log*
.env
.env.*
!.env.example
Python:
__pycache__/
*.py[cod]
.venv/
venv/
*.egg-info/
dist/
build/
.pytest_cache/
.mypy_cache/
.env
Mobile (Android, iOS, Flutter):
# Android
.gradle/
build/
local.properties
*.jks
*.keystore
# iOS
xcuserdata/
DerivedData/
*.xcuserstate
# Flutter
.dart_tool/
Whether to commit Pods/ on iOS is a team decision; both approaches are common. GitHub maintains a large collection of templates in the github/gitignore repository.
Untracking files that are already committed
If a file is already tracked, add it to .gitignore and remove it from the index without deleting it from disk:
git rm --cached config.local.json
git rm -r --cached build/
git commit -m "Stop tracking local files"
To see which rule applies to a file: git check-ignore -v path/to/file.
This is not enough for secrets. git rm --cached only removes the file from future commits; it stays in history, and anyone with access to the repository can find it. The right order:
- Revoke and rotate the key, password or token right away. Treat it as compromised.
- Untrack the file and add it to .gitignore.
- If needed, rewrite history (
git filter-repoor BFG Repo-Cleaner), force push and ask the team to re-clone.
Rewriting history without rotating the secret does not help: copies of the repository may already be out there.
The full syntax is described in the official Git documentation.