Skip to content

What to Put in a .gitignore File

What belongs in the file

Four categories cover almost everything. Dependencies that a package manager can reinstall — node_modules, vendor, a Python virtualenv — because they are large, machine-specific and fully reproducible from a lock file. Build output such as dist, build or target, since it is derived from the source and regenerating it is the point of having a build step.

Then editor and operating system noise: .DS_Store, Thumbs.db, .idea, and most of .vscode. These carry one person's local setup into everyone else's diffs. Finally, and most importantly, anything holding a secret — .env files, private keys, certificates and service account JSON.

Lock files are the common point of confusion, and they are the opposite case: package-lock.json, yarn.lock and Cargo.lock should be committed for applications, because they are what makes an install reproducible across machines.

Why your rule seems to be ignored

The single most common complaint about .gitignore is that a file keeps showing up despite an obviously correct rule. The reason is that .gitignore only applies to untracked files. Once Git is tracking a path, it keeps tracking it regardless of any pattern you add later.

The fix is to remove it from the index while leaving your working copy alone: `git rm --cached path/to/file`, then commit that removal. For a directory, add -r. From that commit onward the ignore rule takes effect.

When a rule genuinely is not matching, `git check-ignore -v path/to/file` tells you exactly which line in which file is responsible — including rules from a global ignore file or a .gitignore in a parent directory, which are easy to forget about.

The pattern syntax in brief

A pattern without a slash matches at any depth, so `build` ignores a build directory anywhere in the tree. A leading slash anchors it to the directory containing the .gitignore, so `/build` matches only the one at the root. A trailing slash restricts the match to directories, so `build/` will not accidentally ignore a file named build.

An exclamation mark negates an earlier rule, which is how the common VS Code pattern works — ignore everything in .vscode, then re-include the couple of files a team wants to share. Negation has one hard limit: you cannot re-include a file if one of its parent directories is excluded, because Git never looks inside an ignored directory. When a negation rule appears to do nothing, an over-broad directory rule above it is nearly always why.

If a secret has already been committed

Removing the file and adding an ignore rule stops it appearing in future commits, but every earlier commit still contains it, and anyone who has cloned the repository has the whole history. A secret that reached a shared branch should be treated as compromised.

Rotate the credential first — that is the step that actually protects you. Purging it from history with a tool such as git-filter-repo is worth doing afterwards, but it rewrites every affected commit, requires a force push, and cannot reach copies that already exist on other machines or in the host's caches.

Frequently asked questions

Should .gitignore itself be committed?
Yes. It is shared configuration that everyone working on the repository needs. Personal preferences that only apply to you belong in a global ignore file, set with `git config --global core.excludesfile`, so you are not asking the whole team to carry your editor's settings.
Can I have more than one .gitignore in a repository?
Yes. A .gitignore in a subdirectory applies to that directory and everything below it, and its rules combine with those higher up. This is useful in a monorepo where each package has its own build output, though a single root file is easier to reason about for most projects.
What is the difference between .gitignore and .git/info/exclude?
.gitignore is committed and shared with everyone. .git/info/exclude does the same job but lives inside your local .git directory and is never committed, which makes it the right place for a temporary file you alone need ignored.

Try the Gitignore Generator

Pick the languages, frameworks, editors and operating systems in your project and get a combined .gitignore file.

Open tool

More guides