Skip to content
T
Tools.Town
Free Online Tools for Everyone
Developer Tools

How .gitignore Works: Patterns, Precedence & Best Practices

A practical guide to .gitignore — how pattern matching works, common gotchas like already-tracked files, where to place files, and which templates to combine.

23 June 2026 4 min read By Tools.Town Team Fact Checked

Key Takeaways

  • Because
  • Yes
  • Almost always yes

What .gitignore is for

A .gitignore file tells Git which files to leave untracked. Without one, git status quickly fills with noise — dependency folders, compiled output, editor settings, OS metadata — and it becomes dangerously easy to commit a secret or a 200 MB build artefact by accident. A good .gitignore, added at the very first commit, keeps your repository lean and your history clean.

The .gitignore Generator assembles one for you by combining vetted templates, but knowing how the patterns work lets you customise with confidence.

How pattern matching works

Each non-comment line in .gitignore is a pattern. Git compares every untracked path against these patterns and ignores the matches. The syntax is small but expressive:

folder

node_modules/

wildcard

*.log

root-only

/dist

negation

!keep.log

  • A trailing slash (build/) matches a directory and everything inside it.
  • An asterisk (*.log) matches any sequence of characters except a slash, so it covers error.log and debug.log but not logs/error.log.
  • A leading slash (/dist) anchors the pattern to the directory containing the .gitignore, so only the top-level dist is ignored, not src/dist.
  • A leading ! (!important.log) negates a previous match, re-including a file that an earlier pattern excluded.
  • Lines starting with # are comments and are ignored by Git.

Precedence and ordering

Order matters when negation is involved. Git evaluates patterns top to bottom, and the last matching pattern wins. To ignore every log file except one, you must list the broad rule first and the exception after:

*.log
!keep.log

Reverse those two lines and keep.log gets ignored along with the rest. There’s one important limitation: you cannot re-include a file if its parent directory is ignored. If you’ve ignored logs/, a later !logs/keep.log won’t work — you’d need to ignore the contents (logs/*) rather than the folder itself.

The most common gotcha: already-tracked files

The single most frequent confusion is adding a file to .gitignore and finding Git still tracks it. The rule is simple but easy to forget: .gitignore only affects untracked files. Once a file is in the index, ignoring it has no effect until you remove it from tracking:

git rm --cached secrets.env
git commit -m "Stop tracking secrets.env"

After that commit, the ignore rule applies and the file stays out of future commits while remaining on your disk. This matters most for secrets — if you’ve already committed an API key, removing it from tracking is not enough, because it lives in your history. Rotate the key and consider scrubbing the history.

Where to put the file

Place your primary .gitignore in the repository root, beside the .git directory. Its rules apply to the whole project. You can add extra .gitignore files in subdirectories for localised rules, and deeper files take precedence over shallower ones. For ignores that are personal to your machine and shouldn’t be shared — your editor’s scratch files, say — use a global gitignore configured with git config --global core.excludesfile, or the untracked .git/info/exclude file.

Which templates to combine

A real project usually needs several templates layered together. A typical full-stack web app might combine:

  • A language base — Node, Python, Go, or Rust — to ignore dependency folders and build output.
  • A framework layer — React or Next.js — for framework-specific caches like .next/.
  • Operating systems — macOS and Windows — so .DS_Store and Thumbs.db never reach the repo, regardless of who clones it.
  • Editors — JetBrains or VS Code — to exclude IDE settings while optionally keeping shared config.

The .gitignore Generator lets you tick all of these at once and removes the duplicate patterns that overlapping templates inevitably share, so node_modules/ appears exactly once even if three templates list it.

Keeping secrets out

Secrets deserve special attention. Environment files (.env, .env.local) almost always belong in .gitignore, and committing them is one of the most common ways credentials leak. If you work with .env files regularly, the Env File Parser and its guide explain how to handle them safely. The golden rule: ignore the real .env, commit a .env.example with blank values instead.

Ignoring versus tracking empty folders

Git tracks files, not directories, which produces a small but common surprise: an empty folder you want to keep — logs/ or tmp/, say — simply won’t appear in the repository at all. The conventional fix is to add a placeholder file such as .gitkeep inside it and then ignore the folder’s contents while keeping the placeholder. A pattern like logs/* paired with !logs/.gitkeep ignores everything generated in the folder but preserves its existence in the repo. It’s a neat illustration of how negation and directory rules interact in practice.

Global ignores for personal noise

Not every ignore rule belongs in the shared project file. Your editor’s swap files, your operating system’s metadata, and tooling specific to your own machine are personal — forcing them into the project .gitignore clutters it for everyone else. Git supports a global ignore file for exactly this. Point Git at one with git config --global core.excludesfile ~/.gitignore_global, list your personal patterns there, and they apply across every repository you work on without touching any project’s committed rules. This keeps the project .gitignore focused on what the project needs, while your individual quirks stay on your machine. It’s a small habit that scales well across teams, because nobody has to argue about whether .idea/ belongs in a shared file.

Best-practice checklist

  • Add .gitignore before your first commit, not after files have been tracked.
  • Combine a language template with your OS and editor templates so the repo is clean for every contributor.
  • Always ignore environment and secret files; commit an example file in their place.
  • Commit the .gitignore itself so the whole team shares the rules.
  • Use a global gitignore for personal, machine-specific noise.

Wrapping up

.gitignore is a small file with outsized impact on repository hygiene. Its pattern syntax is compact, precedence is last-match-wins, and the one rule everyone forgets is that it only governs untracked files. Start every project by generating a combined, de-duplicated file with the .gitignore Generator, commit it first, and your history stays clean from day one.

Advertisement

Try .gitignore Generator — Free

Apply what you just learned with our free tool. No sign-up required.

Try .gitignore Generator

Frequently Asked Questions

Why is my file still tracked even though it's in .gitignore?
Because .gitignore only affects untracked files. If Git is already tracking a file, adding it to .gitignore does nothing until you untrack it with 'git rm --cached <file>' and commit that change. After that, the ignore rule takes over.
Can I have more than one .gitignore file?
Yes. A .gitignore in any directory applies to that directory and its subfolders, and rules in deeper files can override those higher up. A root .gitignore plus the occasional folder-specific one is a common, clean setup.
Should I commit my .gitignore file?
Almost always yes. Committing .gitignore means everyone on the team shares the same ignore rules. For personal, machine-specific ignores you don't want to share, use a global gitignore or .git/info/exclude instead.

Was this guide helpful?

Your feedback helps us improve our content.

Get the best Developer Tools tips & guides in your inbox

Join 25,000+ users who get our weekly developer tools insights.