What this is
Git never edits a file. Every version of every file you have ever committed is stored whole, as an object named by the hash of its own contents. Committing a change does not replace the old object — it adds a new one and points the newest commit at it.
That design is what makes git trustworthy: history cannot be quietly altered. It is also what makes a leaked secret permanent. Deleting the line writes a new object; the old one keeps its name, keeps its contents, and stays reachable from the commit that introduced it.
Work through the four panels below and you will commit a credential, fail twice to remove it, watch a scanner find it in seconds, and end up at the only response that actually helps.
What this does not prove
-
A clean scan is not a safe repository. This scanner reads one branch's
parent chain. A real repository also has other branches, tags, stashes, dangling
objects from rebases, submodules, and notes — and a secret can hide in any of them.
git rev-list --objects --allis the honest starting point. - Entropy does not detect secrets. It detects strings that look uniformly distributed. Object ids and UUIDs score at the top of the range and are harmless; a short PIN or a real English passphrase scores low and is not. Every threshold on this page is a trade-off someone chose, not a fact.
- Not finding it here does not mean nobody found it there. Public-repo crawlers act within minutes. By the time you are reading the diff, the useful question is not "can I remove it" but "what did it have access to, and has it been used".
- Rewriting history is not erasure. Forks, existing clones, CI caches, and platform-side dangling objects all keep their copy. On GitHub, an unreferenced commit remains fetchable by SHA.
- SHA-1 is used here because git uses it. It is broken for collision resistance, which matters for git's integrity story but is irrelevant to this demo — nothing here depends on it being collision-resistant.
Glossary — the terms this page assumes
- Blob
-
A file's contents, stored as one object. Its name is the hash of
blob <length>\0<contents>, so identical contents always land on the same name — which is why an unchanged file is never stored twice. - Tree
- A directory listing: names, modes, and the object id of each entry.
- Commit
- A pointer to one tree, plus its parent commits, author, timestamp, and message. The timestamp is inside the hashed bytes, which is why rewriting history changes every downstream id.
- HEAD
- The commit you currently have checked out. One node in the graph, not the graph.
- Reachable
- An object is reachable if you can walk to it from a ref by following commits, trees, and parents. Reachable objects are never garbage-collected.
- Shannon entropy
-
The average information per symbol, in bits:
H = −Σ p·log₂p. A measure of how evenly a string uses its alphabet — not of how hard it is to guess. - Rotation
- Revoking a credential at the system that issued it and putting a new one in service. The only step that changes what a leaked value can do.
Verify the hashes yourself
Nothing here needs to be taken on trust. Reproduce any object id on this page with the real thing:
git init --object-format=sha1 demo && cd demo
printf 'hello world\n' | git hash-object --stdin
# 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
# ...and for the newer format
git init --object-format=sha256 demo256 && cd demo256
printf '' | git hash-object --stdin
# 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813
The demo's own commit chain is pinned the same way: tools/gen-git-kat.ts
drives the git binary over the exact scenario in this page and records the ids, and the
test suite fails if the browser code disagrees with any of them.