You committed a key, caught it, and removed it in the next commit. The file looks clean now. But git is a time machine, and the old value is still sitting in history, readable by anyone with clone access who runs one command. The working tree is not the repository. The repository remembers everything.
Every commit you've ever made is kept as an immutable snapshot. Deleting a line in a new commit adds a new snapshot on top. It does not touch the old one. So a secret that appeared in commit 200 and got "removed" in commit 201 still lives in commit 200, forever, until you rewrite history and force every clone to update.
The danger isn't that the secret is hard to find. It's that it's trivial to find, and almost nobody looks before they make the repo public.
Anyone who can clone the repo runs git log -p --all and reads every line ever added or removed, across every branch. They don't need an exploit or special access. A teammate, a contractor, a new hire, or anyone you later grant access to, all see the secret that you thought was gone.
The most common version of this: you build privately, drop a key in early, "remove" it, then flip the repo to public for launch. Bots scrape new public repositories within minutes, and they read history, not just the current files. The key you deleted three months ago gets harvested the moment the repo goes live.
This is the trap that gets nearly everyone. Editing the file out in a later commit feels like a fix. It changes nothing about the credential itself. The value is still valid, still in history, and still works for whoever finds it.
"I deleted it in a later commit" is not remediation. If a secret was ever committed, assume it is compromised, rotate it, and then purge it from history. The old value lives in every clone, fork, and CI cache until you do both.
A git stash with a secret in it never shows up in normal review. It's not on any branch, it's not in the log you usually read, and it survives until someone runs git stash clear. Most people forget stashes exist. The secret sits there quietly.
It almost always plays out like this:
# commit 200, three months ago, building privately + DATABASE_URL="postgres://admin:[email protected]:5432/app" # commit 201, next morning, "cleaned up" + DATABASE_URL=process.env.DATABASE_URL // looks fixed ✓ # launch day: repo flipped to public # attacker runs one command on the fresh clone: $ git log -p --all | grep -i "postgres://" # …and commit 200 hands over the live database password.
IOnclad has a dedicated git-history scanner. It runs 100% offline against the .git directory on your machine, so nothing is uploaded anywhere:
git stash entry, the place nobody reviews.git filter-branch / BFG commands to purge it.It finds known secret patterns and high-entropy values across history. It's a strong safety net, not a guarantee, so still rotate anything it flags.
git filter-repo or the BFG Repo Cleaner, then force-push so the old commits are gone for new clones.git stash list and git stash drop so old work-in-progress secrets don't linger..env plus key files in .gitignore so the secret never enters history in the first place.Scan your code and full git history in under a minute. Free verdict, no signup, nothing leaves your machine.