What to Ignore and What to Commit
Guidelines for ignoring build output, dependencies, caches and secrets while committing lockfiles, example configs and team editor settings, plus nested .gitignore files, keeping empty directories and merging templates.
A good .gitignore is judged by its criteria, not its length. There is one criterion: can someone who freshly clones the repository reproduce the same result from the source and configuration alone? Ignore what can be recreated, and commit what cannot be recreated or what pins down the result.
What to ignore
| Type | Examples | Reason |
|---|---|---|
| Build output | dist/, build/, target/, *.o, *.class | Rebuilt from source |
| Installed dependencies | node_modules/, vendor/ (PHP), .venv/ | Reinstalled from the manifest and lockfile |
| Caches and logs | .cache/, .pytest_cache/, *.log, coverage/ | Change on every run |
| Local environment and secrets | .env, *.pem, local.properties, *.tfstate | Differ per person or environment, or must not leak |
| Personal tool state | .DS_Store, *.swp, .idea/workspace.xml | Unrelated to the project (global gitignore recommended) |
What to commit
- Lockfiles:
package-lock.json,yarn.lock,pnpm-lock.yaml,Cargo.lock,poetry.lock,uv.lock,Gemfile.lock,composer.lock,go.sum,.terraform.lock.hcl. They make everyone install the same versions. Some templates, such as Angular, ignore lockfiles, so check the result. - Example configs:
.env.example,config.example.yml. They show which values are needed without containing real secrets. - Build tool wrappers:
gradlewandgradle/wrapper/,mvnw. Make sure the wrapper jar is not ignored because of the Java template's*.jar. - Editor settings agreed on by the team:
.editorconfig,.vscode/settings.json,.vscode/extensions.json,.idea/codeStyles/. Sharing formatting and lint settings makes reviews easier.
Putting .gitignore in subdirectories
.gitignore is not limited to the root. A .gitignore in a subdirectory works relative to that directory and takes precedence over the files above it. In a monorepo where each package needs different rules, keeping a separate file in each package folder is easier to read.
repo/
├── .gitignore # shared: .DS_Store, .env, coverage/
├── apps/web/.gitignore # .next/, out/
└── services/api/.gitignore # target/
Keeping empty directories
git tracks only files and does not store empty directories. When the folder must exist but its contents should be ignored, as with a log folder, put a marker file inside the folder and restore it with a negation rule.
/log/*
!/log/.keep
Names such as .keep or .gitkeep are only conventions, not an official git feature. Any name works. Alternatively, placing a .gitignore with the following contents in that folder ignores everything except itself.
*
!.gitignore
When merging templates
- The usual combination is one or two languages/frameworks + OS + editor. If you keep OS and editor rules in your global file, you can omit them.
- Check the order. Put templates that contain negation rules (Gradle, VisualStudioCode) after templates with broad rules (Java, Kotlin).
- Remove duplicates in a way that preserves meaning. Repeated identical rules behave the same but are harder to read. This site's generator removes duplicates only when there is no negation rule in between.
- Keep section comments. Months later, you need to know where each rule came from to remove it safely.
- Test. Put real paths extracted with
git ls-filesinto the pattern test to make sure unintended files (for example, frontend code caught by the Python template'slib/) are not ignored.
Keep rules short and specific
Overly broad rules such as *.json swallow configuration files too. Narrow paths as much as possible (/dist/), and add a trailing slash to directories to distinguish them from files with the same name. A one-line comment above a rule explaining why lets the next person decide whether it is safe to remove.
References
- gitignore official documentation
- github/gitignore repository README
- npm Docs: package-lock.json
- Verified as of: 2026-09-23