Secrets in Git Repositories: Find, Redact, and Rotate Them
Git never forgets, and that is the whole problem. Deleting a password from a file and committing the change removes it from the current tree while leaving it fully readable in every prior commit, and anyone who can clone the repository can walk that history in seconds.1 The scale of the failure is public record: GitGuardian's State of Secrets Sprawl 2026 report counted roughly 29 million secrets pushed to public GitHub during 2025 alone.2 Private repositories fare little better, because contractors, CI systems, and future acquisitions all widen the audience over time.2
The cheapest fix is the one applied before the commit exists. Pasting a manifest into the Cloud Config Sanitizer takes seconds and returns a placeholder version that is safe to commit, along with a .env template for the real values. Once a credential reaches a remote, the problem stops being a code-review question and becomes an incident-response one, with a materially higher cost to resolve.
Why deleting a committed secret does nothing
Every commit is a snapshot, not a diff. When you commit a manifest containing a database password, git stores that file's content as a blob addressed by its hash, and the commit that follows your cleanup simply points to a new blob while the old one remains reachable from every earlier commit, tag, and branch that referenced it.1 Commands like git log -p, git show, and any code-search tool indexing history will surface the value indefinitely. Furthermore, platform caches, forks, and clones on developer laptops each hold independent copies your cleanup cannot touch.3
Consequently, the moment a secret is pushed, the meaningful question stops being how to delete it and becomes how quickly you can rotate it. Automated scanners operated by GitHub and third parties index public pushes within minutes, so the window for a quiet fix closes faster than most teams expect.4
A snapshot, not a diff
Every commit is a snapshot, not a diff, which is the mechanic that keeps a deleted secret alive in history. When you commit a manifest containing a database password, git stores that file's content as a blob addressed by its hash, and the commit that follows your cleanup simply points to a new blob while the old one remains reachable from every earlier commit and branch.
Commands like git log and git show, plus any code-search tool indexing history, will surface the value indefinitely once it has been pushed. Platform caches, forks, and clones on developer laptops each hold independent copies that a cleanup commit cannot touch, so the moment a secret reaches a remote the real question becomes how quickly you can rotate it.
The correct incident order: rotate, rewrite, prevent
Rotation always comes first. Issue a replacement credential, deploy it, and revoke the exposed one; nothing about cleaning the repository reduces the risk while the old value still works.3 Rewriting history is the second step, and tools like git filter-repo5 or BFG Repo-Cleaner6 can strip a blob from every commit, after which a force push replaces the remote history. Yet rewriting is disruptive, since every collaborator must re-clone or rebase, and it does nothing for forks and existing clones, which is exactly why rotation cannot wait for it.
Prevention closes the loop: sanitize manifests before staging them, so the committed version carries __REDACTED__ placeholders and the real values live in an ignored .env file or a secrets manager from day one. Skipping straight to a history rewrite while the old credential is still active is a common and costly mistake, since it spends effort on cleanup before the actual exposure has ended.
Why rotation cannot wait
Rotation always comes first, because nothing about cleaning the repository reduces the risk while the old value still works for an attacker. Issue a replacement credential, deploy it, and revoke the exposed one before spending any effort on history, since a live credential is the actual exposure that needs to end as soon as possible under incident response.
Rewriting history is the second step, and tools like git filter-repo or BFG Repo-Cleaner can strip a blob from every commit before a force push replaces the remote history. Yet rewriting is disruptive, since every collaborator must re-clone or rebase, and it does nothing for forks and existing clones, which is exactly why rotation cannot wait for it to finish before you move on.
Pre-commit sanitization as a working habit
Committed-code scanners see the leak after it exists; the sanitizer works on whatever you paste, including uncommitted drafts.7 Before staging a Kubernetes manifest, a compose file, or a Terraform values file, find hardcoded credentials in a config before you commit by pasting it into the sanitizer, then read the findings table: critical rows for provider-format tokens and connection strings, high rows for random-looking values, medium rows for values under names like password or api_key. Downloading the sanitized .yaml gives you the commit-safe version with placeholders, and the .env template enumerates every redacted key for local use.
Building on this, the habit compounds: once the repository's manifests reference placeholders and environment variables consistently, a raw credential in a diff becomes an anomaly reviewers notice immediately instead of background noise. Teams that adopt the habit early tend to find fewer legacy leaks later, since the discipline of checking new files also surfaces old ones during ordinary edits.
The habit compounds over time
Committed-code scanners see the leak after it exists, while the sanitizer works on whatever you paste, including uncommitted drafts that never reach a commit. Before staging a manifest, a compose file, or a values file, drop it into the input area and read the findings table for critical, high, and medium rows that point to credential-shaped values you would not want in history.
Downloading the sanitized file gives you the commit-safe version with placeholders, and the .env template enumerates every redacted key for local use without the real values attached. The habit compounds, because once the repository's manifests reference placeholders and environment variables consistently, a raw credential in a diff becomes an anomaly reviewers notice immediately instead of background noise.
When to use this
Use this workflow whenever a config file is about to enter version control, before opening a pull request that touches deployment manifests, and during incident response after discovering a committed credential. It fits solo projects and large teams equally, because the sanitizer runs in the browser with nothing to install, and the placeholder convention it produces reads clearly in any code review. Repository migrations and platform consolidations are a good trigger too, since old manifests often get a fresh look for the first time in years during exactly that kind of move.
Examples
A ConfigMap with a hardcoded database URL is about to be committed
DATABASE_URL: postgres://app:[email protected]:5432/orders
DATABASE_URL: __REDACTED_DB_CONNECTION_STRING_1__
The connection string rule fires at critical severity, and DATABASE_URL appears in the downloaded .env template for local values.
An AWS key pair is found in a Deployment spec already pushed to a shared repo
Rotate the key in IAM first, then rewrite history with git filter-repo, then commit the sanitized manifest. Rotation is the only step that ends the exposure; the rewrite limits future discovery.
A reviewer wants to check a manifest from a PR without pulling secrets onto their machine
Paste the manifest into the sanitizer and share the placeholder version in the review thread. The findings list doubles as the review comment: each row names the key path that needs to move to a Secret or environment variable.
- 1.
GitHub, "Commits are snapshots, not diffs," github.blog, December 2020. https://github.blog/open-source/git/commits-are-snapshots-not-diffs/
- 2.
GitGuardian, "The State of Secrets Sprawl 2026," gitguardian.com, March 2026. https://www.gitguardian.com/state-of-secrets-sprawl-report-2026
- 3.
GitHub, "Removing sensitive data from a repository," docs.github.com, accessed July 2026. https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository
- 4.
GitDoctor, "GitHub Secrets Scanning: How to Find Exposed API Keys in Your Repo," gitdoctor.io, accessed July 2026. https://gitdoctor.io/blog/github-secrets-scanning/
- 5.
newren, "git-filter-repo," github.com, accessed July 2026. https://github.com/newren/git-filter-repo
- 6.
R. Tyley, "BFG Repo-Cleaner," rtyley.github.io, accessed July 2026. https://rtyley.github.io/bfg-repo-cleaner/
- 7.
Truffle Security, "Do Pre-Commit Hooks Prevent Secrets Leakage?," trufflesecurity.com, August 2023. https://trufflesecurity.com/blog/do-pre-commit-hooks-prevent-secrets-leakage