A secret committed to git doesn't go away when you delete it in a later commit — it just moves one commit further back in a history that anyone with repository access can still read.
Why "I deleted it" isn't enough
Git is a content-addressed history, not a mutable document. Deleting a line in commit B doesn't erase it from commit A. git log -p or a simple git show <commit> on an earlier revision will surface it just fine. This is exactly why the fix for an exposed secret is always revoke, then clean history — never clean history alone.
Scanning full history, not just the current tree
Most secret scanners default to scanning the current working tree. To catch secrets buried in history, you need a tool that walks every commit — tools like gitleaks or trufflehog support a --history or full-repo scan mode specifically for this.
gitleaks detect --source . --log-opts="--all"
Run this as a one-time audit, not just as a pre-commit hook (which only ever sees new commits).
Removing a secret from history
Once a secret is confirmed and revoked, you can remove it from history using git filter-repo (the modern, recommended replacement for git filter-branch and BFG for most cases):
git filter-repo --path path/to/file --invert-paths
This rewrites every commit after the secret's introduction, changing all subsequent commit hashes.
After rewriting history
- Force-push the rewritten branches.
- Have every collaborator re-clone (not pull) the repository.
- Invalidate any CI caches or forks that might still hold the old history.
- Confirm the secret is genuinely gone with a fresh full-history scan.
The real fix is prevention
History cleanup is expensive and disruptive. The cheaper fix is stopping the secret from being committed in the first place — pre-commit hooks with high-confidence secret patterns, and CI-level scanning as a backstop for anything that slips through.