Command Palette

Search for a command to run...

Hectal
PHASE 7Advanced ~15 min· topic 5 of 5

Topic 7.5

Committed a Secret? Here's Exactly What to Do

In one line

Accidentally committing a real password, API key, or credential is fundamentally different from every other Git mistake in this course — the fix isn't just removing it from history, it's assuming it's already compromised and rotating it immediately.

0/5 · 0%

Think of it like this

Dropping your house key in a public place — even if you find it and pick it back up within minutes, the responsible move is to change the lock anyway, because you can't be certain nobody saw or copied it in that window. A committed secret gets exactly this treatment: assume it's compromised the moment it's pushed, regardless of how quickly you 'fix' it.

Key ideas

  1. 01

    THE FIRST AND MOST IMPORTANT STEP, before touching Git at all: ROTATE THE SECRET — generate a new API key, change the password, revoke the exposed credential at its source. This matters more than anything else in this topic, because if the secret was ever pushed to a remote (even briefly, even to a private repository), it may already have been cloned, cached, or scraped by an automated bot — GitHub in particular is continuously scanned by credential-harvesting bots the moment anything is pushed, public repo or not.

  2. 02

    Removing the secret from a FUTURE commit is not enough — git rm or even Phase 1.3's git revert only stops it from appearing in NEW commits going forward; the secret is still sitting, fully readable, in the OLD commit's history, retrievable by anyone with git log -p or git show on that specific commit, forever, unless the history itself is rewritten.

  3. 03

    Genuinely purging a secret from history requires REWRITING every commit that ever contained it — git filter-repo (the modern, officially recommended tool, replacing the older and slower git filter-branch) or the BFG Repo-Cleaner can strip a specific file or string from EVERY commit in the entire history, producing a rewritten repository with no trace of the secret in any commit, past or present.

  4. 04

    This is a rewrite of published history at the most extreme possible scale (every affected commit gets a new SHA, exactly like Phase 2.3's rebase, just applied retroactively across the whole repository) — it REQUIRES a force-push to the remote, and every single collaborator must re-clone or carefully reset their local copy afterward, since their old history is now fundamentally incompatible with the rewritten version.

  5. 05

    The complete correct sequence, in order: 1) rotate/revoke the secret immediately, treating it as already compromised, 2) remove it from the current code (a normal commit), 3) use git filter-repo (or BFG) to purge it from all of history, 4) force-push the rewritten history, 5) notify every collaborator to re-clone or reset, and 6) consider whether the exposure requires any broader disclosure (a security incident process, if your organization has one) — steps 2 through 5 matter, but step 1 is the one that actually prevents real damage, and it should happen within minutes, not after history has been cleaned up.

Code & diagrams

SecretIncidentResponsediagram

The order matters — rotation comes first, always, before any Git cleanup.

Rendering diagram…
purge-a-secret.shmarkdown

The full sequence — run rotation FIRST, in a completely separate step, before any of this.

# STEP 1 (do this FIRST, separately, before anything below):
# Go to the credential's actual source (database, cloud provider, API
# dashboard) and rotate/revoke it immediately. This is the step that
# actually matters most — everything below is cleanup after the fact.

# STEP 2 — remove it from current code
rm config/secrets.env
echo "config/secrets.env" >> .gitignore
git add config/secrets.env .gitignore
git commit -m "Remove committed secret, add to .gitignore"

# STEP 3 — purge it from ALL history (install git-filter-repo first)
git filter-repo --path config/secrets.env --invert-paths
# this rewrites EVERY commit that ever touched this file, giving each a new SHA

# STEP 4 — force-push the rewritten history
git push origin --force --all
git push origin --force --tags

# STEP 5 — notify every collaborator; their old clones are now incompatible
# They must either re-clone fresh, or carefully reset their local branches
# to match the new, rewritten history — NOT simply pull, which would
# reintroduce the purged commits from their still-old local copy.

Explain it without notes

01

Why is rotating the exposed secret more urgent and more important than removing it from Git history?

02

Why isn't a normal git revert or git rm sufficient to actually fix a committed secret?

Practice

01

On a disposable test repository, commit a fake 'secret' file, then use git filter-repo to purge it from history entirely, and confirm with git log -p on the original commit that the secret's content is genuinely gone.

02

Write out, in your own words, the correct six-step sequence from this topic, in the correct order — and explain in one sentence why step 1 must always come before the others, regardless of how inconvenient that timing is.

Trade-offs

  • ↔

    Purging history with git filter-repo and force-pushing is a genuinely disruptive operation for every collaborator (mandatory re-clone or careful reset, no simple git pull) — but the alternative (leaving a real secret permanently discoverable in history, even after 'removing' and rotating it) is a strictly worse, ongoing risk; the disruption is a one-time, bounded cost, while an un-purged secret in history is a standing liability for as long as the repository exists.

Done when you can

  • I know that rotating a compromised secret comes first, before any Git cleanup at all.

  • I understand why a normal revert or rm doesn't actually remove a secret from history.

  • I know git filter-repo (or BFG) is the tool for genuinely purging a secret from every commit that ever contained it.