[Git] How to Completely Erase Accidentally Committed Files from History (git-filter-repo)
Today, we’re diving into a technical guide on doing a “deep clean" of a Git repository.
When you’re deep in the zone of development, it’s surprisingly easy to accidentally commit something you shouldn’t have and push it to the remote repository.
- Confidential information: secret.txt, API keys, .env files.
- Bloated repository size: huge test videos, zipped archives, node_modules.
If you panic, run git rm to delete the file, and commit again, all you’ve really done is added a new history entry saying “this was deleted." The data for that file still stubbornly remains in your past commit logs (inside the .git directory).
The repository size won’t decrease, and anyone going back through the history will still be able to see the contents.
In this post, I’ll walk through the surgical procedure to make specific files “as if they never existed from the very beginning of history" using git-filter-repo, the modern tool officially recommended by Git.
Goal: Rewriting History and Forcing Synchronization
Our goals for this operation are as follows:
- Complete Eradication: Physically eliminate the specified file (let’s use secret.txt as an example) from all past commits.
- Rewriting History: Recalculate commit hashes (IDs) accompanying the file deletion to reshape the history.
Preparation: The Ultimate Vacuum Cleaner, git-filter-repo
While git filter-branch and BFG Repo-Cleaner used to be the go-to tools, git-filter-repo is now the official recommendation. Written in Python, it runs extremely fast and is designed to handle complex histories safely.
Installation (Requires a Python environment):
pip install git-filter-repo
*If you are on a Mac using Homebrew, you can also install it via brew install git-filter-repo.
[WARNING] Backup Before You Begin
The operation we are about to perform is destructive.
Just in case you accidentally wipe out necessary files, make sure to copy the entire repository directory to create a backup first.
Practical Steps: Eradicating a Specific File
We will proceed with the steps to purge a secret.txt file that was mistakenly mixed into the repository.
*The procedure is completely identical for huge video files or binary data—just substitute the file name.
Confirming Existence
First, let’s check how deeply embedded that file is within Git’s history.
git log --all -- secret.txt commit 476b4bcd36ee3e1b900db9edb98ea82e12d61f90 (HEAD -> master) Author: nando <xxx@gmail.com> Date: Wed Feb 11 18:51:51 2026 +0900 DELETE secret.txt commit c0676094dab6734d147f14273275340655d5f513 Author: nando <xxx@gmail.com> Date: Wed Feb 11 18:51:40 2026 +0900 ADD secret.txt
If logs are displayed, Git remembers the file.
Erasing from History (Execution)
Run the following command. This is where we perform the surgery.
git filter-repo --path secret.txt --invert-paths
Option Breakdown:
- –path secret.txt: Specifies the file you want to delete (if it’s an entire directory, specify the directory name).
- –invert-paths: This is crucial. It instructs Git to **"keep everything except (invert) the specified path"** = **"discard the specified path."**
If an Error Occurs Here
If you encounter the error Aborting: Refusing to destructively overwrite repo history..., that is a safety mechanism.
To prevent accidental operations, git-filter-repo may refuse to run unless you are in a “clean state right after cloning."
If this happens, append the –force option to force execution.
git filter-repo --path secret.txt --invert-paths --force git log commit 60219177b930da26f899b0675a2b8a67fdbc3b3c (HEAD -> master) Author: nando <xxx@gmail.com> Date: Wed Feb 11 18:51:13 2026 +0900 initial commit git log --all -- secret.txt <If nothing outputs, you're good>
After running, the process completes in an instant. Run git log again to check, and if nothing appears, your local repository has been purified.
The Tough Part: Pushing to a Protected Remote
This is where many people stumble.
Your local environment is now clean, but the remote (GitHub, etc.) still holds the “corrupted history." Furthermore, if this file has already been merged into the main branch, things get a bit more complicated.
Normally, the main branch is “Protected" to prevent accidental operations, meaning Force Pushes that rewrite history are configured to be rejected.
If you try to run git push origin main –force as-is, it will be blocked by an error like this:
! [remote rejected] main -> main (protected branch hook declined)
To break through this, you need administrator privileges to temporarily disable the rule.
Lifting Protection Rules
Open your GitHub or GitLab repository settings page (Settings -> Branches) and edit the protection rules for the main branch.
Temporarily disable the rules altogether or check the box for “Allow force pushes."
Executing the Force Push
The exact moment you lift the protection, push without delay.
git push origin main --force
*If the change affects all branches, include the –all flag as well.
Restoring Protection Rules (Most Important)
As soon as the push succeeds, immediately return to the settings page and restore the protection rules.
Leaving this undone leaves a lingering risk that someone might accidentally destroy the history again later.
Thorough Communication with Team Members
Rewriting history comes with major side effects.
Because every single commit ID in the repository has changed, your history will no longer align with the repositories your teammates have on their local machines.
If a team member continues working from their “locally corrupted history" without knowing about your fix and pushes their changes, secret.txt will rise like a zombie, tangling the history beyond easy recovery.
Once you finish the operation, make sure to thoroughly communicate the following:
[Important Notice]
We have rewritten the history to remove unnecessary files (such as secret.txt) from the repository.
Please do not push or pull from the repository you currently have on your local machine.
Delete your existing folder and perform a fresh git clone.
This is the safest and only real solution.
Summary
- Deletion tool: git-filter-repo is the only way to go.
- Error handling: If an error pops up, muscle through it with –force.
- Remote synchronization: Temporarily lift branch protection and run push –force.
- Team coordination: Have everyone re-clone the repository.
They say “water spilled cannot be gathered," but in the world of Git, you *can* gather it back—at the cost of powerful tools, administrative rights, and some friction for your team.
However, that cost is by no means cheap.