Lesson 18 of 30
Ignoring a file only stops Git from noticing it in the future. Untracking one Git already knows about, and getting a leaked secret out of history, are different problems entirely.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
.gitignore tells Git "don't ask me about these paths when you're deciding what's untracked." That's it — it's a filter on the untracked files list, nothing more. It has no effect on a file Git is already tracking, and it does nothing at all to history that already exists.
Three separate problems get conflated under "gitignore," and they need three separate answers: keeping future junk out, removing a file that's already tracked but shouldn't be, and getting rid of a secret that's already been committed.
.gitignore for a Next.js project# dependencies
node_modules/
# build output
.next/
dist/
# env files — but keep the example
.env*
!.env.example
# logs
*.log
# OS cruft
.DS_Store
# only ignore top-level build/, not every build/ folder
/build
A few patterns worth knowing cold: node_modules/ (trailing slash = directory only, not a file with that name), *.log (glob — matches any depth by default in modern Git, unless anchored), !.env.example (negation — un-ignores something a broader pattern already caught; order matters, the negation has to come after the pattern it's carving an exception out of), and /build (leading slash anchors it to the repo root, so src/build/ isn't affected).
Now say .env got tracked by accident three commits ago, before this .gitignore existed. Adding .env* to .gitignore today does nothing to the version already in the repo — Git keeps tracking a file it's already tracking, ignore rules or not:
git rm --cached .env
git commit -m "chore: stop tracking .env, it's already gitignored"
--cached removes it from Git's index (stops tracking) but leaves the actual file sitting on disk — your local .env still exists, it's just no longer part of the repo going forward.
.gitignore and git rm --cached are not. Every commit that ever contained that API key is still sitting in the repo's history, readable by anyone with a clone, forever, even after you delete the file in a later commit. The only correct response to a leaked credential is: revoke/rotate it at the source (the provider that issued it), then clean history if you need to (git filter-repo, BFG Repo-Cleaner) as a secondary, optional step. Treat the key as burned the moment it's committed, not after someone rewrites history.git rm --cached untracks a file — it doesn't erase it from history. Anyone who runs git log -p -- .env or checks out an old commit can still see the old tracked version. This is the same root cause as the secret problem above, just less urgent when the file wasn't sensitive..gitignore pattern only applies to files Git doesn't already know about. People add a rule, see the file still showing up in git status, and assume the rule is broken — it isn't; the file was already tracked before the rule existed, so the rule is correctly being ignored (pun intended) for it.git rm --cached is really just "remove from the index, leave the working tree alone" — the same index (staging area) concept that shows up every time you git add a file. Understanding the three trees — working directory, index, and HEAD — is what makes --cached make sense instead of feeling like a magic flag.