Git, History, and Hidden Mistakes: Why Deleting a Commit Isn't Enough

Git is one of the most widely used version control systems in the world. It's powerful, fast, and highly flexible, making it the backbone of software development for millions of engineers and organizations. But with great power comes great… potential to accidentally commit a secret.
In this post, we’ll dive into how Git handles commit history, why deleting a file or commit isn’t enough to make it disappear, and how commits can linger long after you thought they were gone, especially on GitHub.
Git has powerful history tracking which makes it easy to revert or audit changes. However, Git’s deep memory - can be both a strength and a risk.
New research by Sharon Brizinov demonstrates that accidental exposure of sensitive information to Git is difficult and sometimes impossible to fully undo.
The Git Commit Tree: A Living History
Git stores your project history as a directed graph of commits, often called the commit tree. Each commit references its parent(s), forming a chain of snapshots.
Your branches (main, dev, etc.) are just pointers to the latest commit in a line of changes. When you create a new commit, it gets added to this graph, and the branch pointer moves forward.
This structure is powerful, but it also means Git remembers everything, even if you later delete files.
git rm: Not Really Gone
Let’s say you accidentally commit a file called .env with an API key:
git add .env
git commit -m "oops"
git push origin main
Realizing the mistake, you delete it:
git rm .env
git commit -m "remove sensitive file"
git push
Problem solved? Not quite.
Git has removed the file from the latest commit, but it still exists in previous commits. Anyone can check out an earlier point in the history and retrieve the file.
Rewriting History: Resetting HEAD and Force Push
To actually remove the commit with the sensitive file, you might use:
git reset --hard HEAD~1
This moves the HEAD (your current branch pointer) back one commit, effectively deleting the bad commit from your local history.
But when you try to push:
git push
Git will block it, saying your local history is behind the remote.
To override this, you need:
git push --force
This rewrites the remote branch history, which can be dangerous, especially in collaborative environments. Anyone who cloned the repo before your force push will still have the bad commit locally, and if they push without syncing properly, they might accidentally reintroduce the old history.
The Undead: Dangling Commits
Even after a force push, Git doesn’t immediately delete the removed commit. Instead, it becomes a dangling commit:
- Still exists in the Git object database
- Not referenced by any branch or tag
- Invisible in the standard history
In local Git, these dangling commits will be cleaned up by Git’s garbage collector (git gc) - usually after 30 days.
But on GitHub, things are a bit trickier.
GitHub and Dangling Commits
GitHub retains unreachable commits for several weeks to avoid data loss and allow recovery.
But here’s the kicker: if someone forked your repo before you force-pushed the bad commit, GitHub won’t delete it. That commit still exists in the fork’s history - fully accessible and indexed.
So even if it looks like the commit is gone in your repo, it might still be live somewhere else.
Can Someone Still Access It?
Yes - if they know the commit hash.
Since the commit is no longer in any branch history, it won't show up when running:
git log
However, someone can still find it by:
- Watching the repo activity – GitHub shows a log of events (like pushes) in the network graph or the Events API.
- Noting the commit hash from a prior clone, fork, or GitHub URL (e.g., https://github.com/user/repo/commit/<hash>).
- Using the hash to retrieve the commit, as long as GitHub hasn't garbage collected it yet.
What is GH Archive, and How It Helps Find Dangling Commits
GH Archive is a public project that records and stores GitHub’s event data, including pushes, forks, stars, and other actions, in near real time. It collects this data from the GitHub Events API, and organizes it into hourly JSON files available for download or querying via BigQuery. If you’ve accidentally pushed a sensitive commit and then force-pushed to remove it, the original push event is still logged in GH Archive, including the commit hash. This means that even after you’ve deleted a commit from your repo’s history, anyone with access to GH Archive can look up the push event, extract the hash, and retrieve the commit, if GitHub hasn’t garbage collected it yet. In other words, GH Archive acts like a public audit log of everything that’s ever been pushed to GitHub. If you're dealing with a data leak, you must assume that commit metadata is permanently exposed - even if the contents are eventually deleted.
What You Should Do Instead
If you accidentally push sensitive data:
- Revoke/rotate the secret immediately (treat it as compromised).
- Use tools like:
- git filter-repo (modern, recommended)
BFG Repo-Cleaner
to rewrite history and scrub the secret entirely from the Git object store.
Force-push the cleaned history:git push --force --all
git push --force --tags
- Notify team members and ask them to re-clone the repo.
- Monitor forks or GitHub Actions logs for signs the commit may still exist elsewhere.
Always assume your commits have been exposed, and take immediate action to rotate any compromised secrets.
How can Oasis help you with the process
The recent GitHub vulnerability underscores a harsh reality: even deleted commits can remain accessible to malicious actors. In today’s threat landscape, the boundary between exposed and leaked has effectively vanished.
At Oasis, we help you operate under a new assumption: once a secret is exposed, it should be treated as already compromised.
Our secret scanning engine continuously monitors public repositories, breach data, and commit histories to detect credentials, tokens, and keys that may have been inadvertently exposed. What sets Oasis apart is: (1) our deep correlation capability: we tie exposed secrets back to the exact non-human identity (NHI) (like an Azure service account or an AWS IAM user) that the secret belongs to, and (2) our NHI ownership capability which allows us to determine who within your organization is responsible for each NHI.
This enables faster, more targeted response because you don’t just know what’s exposed, you know exactly who needs to take action.
(Curious how we determine ownership at scale? Check out our blog on NHI ownership)
Meanwhile, our threat intelligence system actively surfaces secrets that have already been leaked and circulated on the darknet, confirming when exposures have become known to malicious actors. This reinforces the urgency to act immediately: if a secret is exposed, assume it’s already in the wrong hands.
With Oasis, you can take action immediately. Our platform enables a safe, automated rotation workflow, guided by identity correlation and consumers’ context, so you can replace compromised secrets without breaking production.
We also support proactive rotation policies, helping customers schedule regular credential updates to reduce long-term exposure risk and eliminate stale secrets before they become attack vectors.
By combining exposure detection, leak verification, identity mapping, and automated rotation, we give you the tools to close the loop on credential compromise before attackers exploit it.

Conclusion
Git is amazing at preserving history, and that’s both a feature and a risk.
Removing a commit from history takes more than just git rm or git reset. You have to understand how Git stores data, what "dangling" means, and how GitHub handles garbage collection. And most importantly: once sensitive data is pushed, always assume it was seen.
We do newsletters, too
Discover tips, technical guides and best practices in our biweekly newsletter.