Dev Tools

Why Deleting a Secret From Your Code Doesn't Delete It From Git (2026)

A follow-up commit that removes a leaked credential feels like it fixes the problem. It doesn't — the secret still lives in history. Here's what actually needs to happen.

📅 Jul 31, 2026·⏱️ 5 min read·✍️ Cikal Studio Labs
📜

The fix that isn't actually a fix

A common sequence: a developer accidentally commits a database password or API key directly in a config file, notices the mistake, and pushes a follow-up commit removing it. From the current state of the repository, the secret is gone — but git is a history of every change, not just the current snapshot, and the original commit containing the real credential still exists, fully intact and fully retrievable.

Why "it's not in the file anymore" is the wrong test

Anyone with access to the repository can run git log -p or git show <commit-hash> to view the full diff of any past commit, including the one that introduced the secret before it was removed. If the repository is public, or ever becomes public, or is cloned by anyone before the removal, the secret remains accessible indefinitely regardless of how many commits have happened since.

What actually needs to happen once a secret is committed

  • Rotate the credential immediately. This is non-negotiable and should happen regardless of whether the repository is public or private, since the safest assumption is that the secret has already been exposed to anyone with any level of past or current repository access.
  • Purge it from history if the repo is or was ever public. Tools like git-filter-repo or the BFG Repo-Cleaner can rewrite git history to remove a specific string from every commit that ever contained it — a more involved process than a normal commit, since it changes commit hashes throughout the history and requires coordination if others have cloned the repo.

Why scanning has to look at both added and removed lines

A scan that only checks the current state of files entirely misses secrets that were introduced and later removed — exactly the scenario where a developer might mistakenly believe the issue is already resolved. Scanning diff or log output specifically, and checking both added and removed lines, catches this pattern that a simple file scan cannot.

The connection-string pattern deserves specific attention

Database connection strings with an embedded password (postgres://admin:password@host/db) are a particularly common and easy-to-miss leak pattern, since the credential is embedded inline within a URL rather than as an obviously separate password field — making it easy to overlook during a quick visual review of a config change.

Building the habit before it's needed

Scanning a diff before committing — rather than after — is the more effective long-term habit, but checking existing history for past exposures remains valuable for any repository that's accumulated commits over time without this check having been run consistently from the start.

Frequently Asked Questions

If I remove a leaked API key in a new commit, is it actually gone?

No. The original commit that introduced the secret still exists in git history and remains fully accessible via git log -p or git show, regardless of how many commits have happened since. Removing it from the current file only changes the latest snapshot, not the repository's full history.

What should I actually do if I find a secret committed in the past?

Rotate the credential immediately — treat it as compromised regardless of whether the repository is public or private. If the repository is or was ever public, also purge the secret from git history using a tool like git-filter-repo or the BFG Repo-Cleaner, which rewrites history to remove the string entirely.

Why does a database connection string with a password need special attention?

The credential is embedded inline within a URL format (postgres://user:password@host/db) rather than appearing as an obviously separate password field, which makes it easier to overlook during a quick visual review of a config change compared to a clearly labeled 'password' or 'secret' field.

Does purging git history remove a secret from everywhere it might exist?

No — purging your own repository's history with a tool like git-filter-repo only affects that repository going forward. If anyone else has already cloned the repository before the purge, their local copy still contains the original history unless they also update it, and the credential should still be rotated regardless.

Is there a tool that scans git diffs or logs for secrets, including ones already removed?

Yes — the Git Secret History Scanner parses pasted diff or log -p output line by line, distinguishing added from removed content, and specifically flags secrets that were later 'removed' in a follow-up commit but remain fully present in git history.