Everyday Git
Topic 08

Ignoring Files

Core

A .gitignore file tells Git which untracked paths to never offer for staging — build output, dependency directories, secrets, and editor cruft. It keeps the noise out of git status so the files that matter are visible.

The higher-stakes job is keeping things out of history entirely. Without an ignore rule it is far too easy to git add . a .env file full of credentials, and once a secret is committed, ignoring it afterward does nothing — it is already in history for anyone with a clone to read. Getting the ignore rules right before the first commit is cheaper than the cleanup later.

How Matching Works

Patterns are globs. A bare name like build matches that name anywhere in the tree; a leading / anchors it to the directory the .gitignore sits in. * matches within one path segment, ** spans directories, a trailing / restricts the match to directories, and a leading ! negates an earlier rule to re-include something. The anchoring detail matters: node_modules unanchored can match a file of that name buried somewhere unexpected, while /node_modules/ targets exactly the top-level directory you meant.

Where Ignore Rules Live

Rules come from several places. A .gitignore in any directory applies to that directory and below, the root file holds the project-wide rules everyone shares, and the per-machine core.excludesFile holds rules that are about your setup, not the project. The distinction is editorial: project artifacts like dist/ belong in the committed file; your editor's .idea/ and macOS .DS_Store belong in your personal global excludes, not forced onto every collaborator.

Already-Tracked Files

.gitignore only governs untracked files. Adding a rule for a file Git already tracks changes nothing — Git keeps tracking it and keeps committing your edits. To stop tracking it while leaving it on disk, run git rm --cached <path>, which removes it from the index so the next commit drops it, and only then does the ignore rule take effect. Editing .gitignore alone cannot do this.

What to Ignore and What Not To

Ignore generated and reconstructible things: dependency directories like node_modules/, build output like dist/, and any .env holding secrets. Do not ignore lockfiles — package-lock.json, yarn.lock, poetry.lock pin exact dependency versions, and ignoring them makes builds non-reproducible across machines because everyone resolves slightly different versions.

Debugging Ignore Rules

When a file is ignored that should not be, or vice versa, stop guessing which of the layered files is responsible. git check-ignore -v <path> prints the exact file, line number, and pattern that matched. It is the fastest way to settle why git status is or is not showing something.

Common Mistakes
  • Committing a .env or credentials file and adding it to .gitignore afterward — the secret is already in history and must be purged with a history rewrite plus a key rotation, not merely ignored.
  • Expecting .gitignore to untrack a file Git already commits — it does nothing for tracked files; you need git rm --cached.
  • Ignoring package-lock.json or another lockfile, so dependency versions drift and builds stop being reproducible across machines.
  • Writing node_modules without anchoring, which can match a file of that name deep in an unexpected path instead of just the top-level directory.
  • Putting machine-specific cruft like .idea/ or .DS_Store in the committed repo .gitignore instead of a personal global excludes file, polluting it for everyone.
Best Practices
  • Add .gitignore before the first commit, seeded from a stack-specific template at gitignore.io.
  • Run git check-ignore -v <path> to learn exactly which pattern is ignoring — or failing to ignore — a file.
  • Commit lockfiles and ignore only generated dependency directories and build artifacts.
  • Put personal editor and OS files in core.excludesFile (a global ignore) rather than the shared repo file.
  • For a secret that slipped in, rotate the credential immediately and rewrite history with git filter-repo rather than trusting that ignoring it later is enough.
Comparable toolsMercurial .hgignore, supporting regex or glob syntaxSubversion the svn:ignore property set per directoryPerforce a .p4ignore fileFossil ignore-glob settings stored in the repository

Knowledge Check

You committed a .env with API keys, then added it to .gitignore. Is the secret safe now?

  • No — it is already in history and clonable; you must rewrite history and rotate the credential
  • Yes — adding it to .gitignore retroactively strips it out of every one of the past commits
  • Yes, once you make the next commit, which Git uses to scrub the ignored file out of older history
  • It is safe on the remote after a push but still present in your local history

A file Git already tracks keeps getting committed despite a matching .gitignore rule. Why?

  • .gitignore only affects untracked files; to stop tracking it you need git rm --cached
  • The rule sits in the wrong directory's .gitignore, so it never applies to that file's path
  • Tracked files require a leading ! on the pattern before the ignore rule takes effect
  • Git ignores any rule that was added after the file was first created and committed

Why commit package-lock.json but ignore node_modules/?

  • The lockfile pins exact versions for reproducible builds; node_modules/ is large, generated, and rebuildable from the lockfile
  • The lockfile is small enough to commit comfortably, whereas node_modules/ is simply too large for Git to handle
  • Git cannot compute readable diffs for the many binary files buried inside node_modules/, so the whole directory has to be ignored
  • Both of them should be ignored; committing the lockfile to the repository is a common mistake

Where do your editor's .idea/ and .DS_Store files belong?

  • In your personal global core.excludesFile, since they are about your machine, not the project
  • In the committed repo .gitignore, so that every collaborator on the team inherits the same rule
  • Committed directly into the repo, so the whole team shares one identical editor config
  • Nowhere at all — Git already ignores any dotfile automatically by default

You got correct