.gitignore Best Practices: Templates & Common Patterns
A well-crafted .gitignore keeps your repository clean, prevents secrets from leaking, and reduces noise in pull requests. This guide covers what to always ignore, language-specific templates, global gitignore, common mistakes, and how to fix a file that was already committed.
Why .gitignore Matters More Than You Think
Most developers know .gitignore prevents committing node_modules. But a poorly maintained gitignore causes real problems:
- Secret leaks:
.envfiles, API keys, and private keys committed to repositories are one of the most common causes of security incidents. GitHub automatically scans public repos for known secret formats and alerts the service provider. - Bloated repository size: Committing build artifacts, compiled binaries, and dependency folders inflates repository size and slows down clones.
- Noisy diffs: IDE workspace files, OS metadata, and compiled output create irrelevant diff noise in every pull request.
- CI/CD breaks: Platform-specific files (like
.DS_Storeon macOS) can cause unexpected behaviour when checked out on Linux CI runners.
The Universal Baseline: Always Ignore These
These entries belong in every project's .gitignore regardless of language or framework:
# Environment and secrets
.env
.env.local
.env.*.local
.env.development
.env.production
*.pem
*.key
*.p12
secrets.json
# Dependency directories
node_modules/
vendor/
.venv/
venv/
__pycache__/
# Build output
dist/
build/
out/
target/
*.o
*.pyc
*.class
# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*~
.project
.classpath
.settings/
# OS metadata
.DS_Store
.DS_Store?
._*
Thumbs.db
Desktop.ini
$RECYCLE.BIN/
# Logs
*.log
logs/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Test coverage
coverage/
.nyc_output/
.coverage
htmlcov/
Language-Specific Templates
Node.js / JavaScript
# Dependencies
node_modules/
.pnp
.pnp.js
# Build output
dist/
build/
.next/
.nuxt/
.output/
.cache/
# Package manager lockfiles (commit these - do NOT ignore them)
# package-lock.json -- DO NOT IGNORE
# yarn.lock -- DO NOT IGNORE
# pnpm-lock.yaml -- DO NOT IGNORE
# Environment
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Coverage
coverage/
.nyc_output/
# Misc
.DS_Store
*.tsbuildinfo
.eslintcache
A common mistake: some tutorials say to ignore package-lock.json. Do not do this. Lockfiles ensure reproducible installs and must be committed.
Python
# Virtual environments
.venv/
venv/
env/
ENV/
# Byte-compiled files
__pycache__/
*.py[cod]
*$py.class
*.pyo
# Distribution and packaging
dist/
build/
*.egg-info/
.eggs/
*.egg
MANIFEST
# Testing and coverage
.tox/
.pytest_cache/
.coverage
.coverage.*
htmlcov/
*.coveragerc
# Jupyter Notebooks
.ipynb_checkpoints/
# Mypy type checker
.mypy_cache/
.dmypy.json
# Environment
.env
*.env
Java / Maven / Gradle
# Compiled output
*.class
*.jar
*.war
*.ear
*.nar
*.zip
*.tar.gz
*.rar
# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
# Gradle
.gradle/
build/
gradle-app.setting
!gradle-wrapper.jar
!gradle-wrapper.properties
# IDE
.idea/
*.iws
*.iml
*.ipr
.classpath
.project
.settings/
.metadata/
Go
# Binaries
*.exe
*.exe~
*.dll
*.so
*.dylib
/bin/
# Test binary
*.test
# Go workspace
go.work
# Output
/vendor/
/dist/
Generate a .gitignore in Seconds
Our .gitignore Generator supports 500+ templates. Select your language, framework, and IDE, and get a complete gitignore file ready to paste into your project.
Open .gitignore GeneratorGlobal .gitignore for Personal Files
IDE files, OS metadata, and editor backup files are specific to your machine, not your project. They should not be in each project's .gitignore - that would pollute it with personal preferences. Instead, set a global gitignore that applies to all your repositories:
# Create a global gitignore file
touch ~/.gitignore_global
# Tell git to use it
git config --global core.excludesfile ~/.gitignore_global
Contents of ~/.gitignore_global:
# macOS
.DS_Store
.AppleDouble
.LSOverride
._*
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
# Linux
*~
# JetBrains IDEs (PyCharm, IntelliJ, etc.)
.idea/
*.iml
# VS Code
.vscode/
# Vim
*.swp
*.swo
# Emacs
*~
\#*\#
.\#*
# macOS Xcode
*.xcuserstate
This way, team members with different IDEs and operating systems do not need to add each other's personal files to the project gitignore.
How to Remove a File Already Committed to Git
If a file was committed before it was added to .gitignore, simply adding it to .gitignore will not stop git from tracking it. You need to untrack it first:
# Remove a single file from tracking (keeps the file on disk)
git rm --cached path/to/file
# Remove an entire directory from tracking
git rm --cached -r path/to/directory/
# Remove all .env files from tracking
git rm --cached **/.env
# Then commit the removal
git commit -m "Remove tracked files that should be gitignored"
# If a secret was committed, consider the secret compromised.
# Rotate it immediately. Then scrub git history:
git filter-repo --path secrets.json --invert-paths
If a secret (API key, password, private key) was ever committed to a repository - even to a private one - treat it as compromised and rotate it immediately. History rewriting does not help if the repository was ever cloned or forked before the scrub.
.gitignore Pattern Syntax Reference
Understanding the pattern syntax helps you write precise rules:
*.log- Match all files ending in.login any directory/dist- Match only adistdirectory at the repository root (leading slash anchors to root)dist/- Match only directories nameddist, not files**/logs- Matchlogsdirectory anywhere in the tree (double asterisk)!important.log- Negate: do NOT ignoreimportant.logeven if a prior rule woulddoc/**/*.txt- Match all.txtfiles anywhere underdoc/[Dd]ebug- MatchDebugordebug
A common gotcha: negation patterns (!) only work if the parent directory is not ignored. If build/ is ignored, you cannot un-ignore build/important-file with !build/important-file.
Testing Your .gitignore
Before committing, verify your gitignore works as expected:
# Check if a specific file is ignored
git check-ignore -v path/to/file
# List all ignored files in the current directory
git status --ignored
# Show which rule is causing a file to be ignored
git check-ignore -v --no-index path/to/file
Generate Your Project .gitignore
Select your stack from 500+ templates - Node, Python, Java, Go, Rails, Django, React, and more. Get a complete gitignore file in one click, free and browser based.
Open .gitignore Generator →Frequently Asked Questions
Should I commit package-lock.json or yarn.lock?
Yes, always commit lockfiles. They ensure that every team member and CI/CD pipeline installs exactly the same dependency versions, preventing "works on my machine" bugs. Only ignore lockfiles for published libraries (not applications), where lockfiles would force your library's consumers to use specific transitive dependency versions.
Should .gitignore itself be committed?
Yes. The project's .gitignore file should be committed so all team members and CI systems use the same rules. Your personal IDE and OS preferences belong in your global ~/.gitignore_global, not in the project gitignore.
How do I ignore a file in a subdirectory but not at the root?
Use a path relative to the repository root. To ignore logs/ only inside src/ but not at the root level: add src/logs/ to your .gitignore. To ignore it anywhere except the root, you would need to list each path explicitly, or restructure the repository.
My .gitignore isn't working for a file that's already tracked. Why?
Git only respects .gitignore for untracked files. If a file is already in the git index (was ever committed), gitignore rules are ignored for that file. You must run git rm --cached filename to stop tracking it, then commit the change. After that, the gitignore rule will take effect.
Is there a difference between .gitignore in different directories?
Yes. Git supports .gitignore files at any level of the directory tree, not just the repository root. Rules in a subdirectory's .gitignore only apply to that subdirectory and its children. This is useful for monorepos where different packages have different ignore rules. The root .gitignore applies to the entire repository.
Summary
A solid .gitignore strategy has three layers: a project gitignore committed to the repository covering build output, dependency directories, and environment files; a global gitignore for personal IDE and OS files; and clear team guidelines about what secrets management approach to use so .env files never need to be committed in the first place.
Start with a language-specific template, add your project's specific exclusions, and use git check-ignore -v to verify rules work as expected.
Generate a complete .gitignore for your stack → Use our free .gitignore Generator here
Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.