Purge Problematic Files from Git History
Back to Blog

Purge Problematic Files from Git History

Following a curious incident where a file with a problematic path ended up in Git history, even after deleting it in a later commit, it still stuck around in older commits and kept causing unexpected issues.

At that point, just "removing it" isn’t really enough. If it exists anywhere in history, it can come back through merges, rebases, or even someone branching off an older commit. So the only reliable fix is to completely purge it from history.

That’s where git filter-repo comes in.

Installation

On a lot of modern setups, it’s not bundled with Git, so you need to install it separately.

On macOS:

brew install git-filter-repo

On Ubuntu / Debian:

sudo apt install git-filter-repo

Removing a file from history

Say you want to remove something like:

src/some/path/generated.file

The general approach looks like this.

Start with a fresh clone

Things are usually cleaner if you start from a mirror clone:

git clone --mirror git@github.com:org/repo.git

A mirror clone is a full copy of the repository, including all branches, tags, refs, everything. It’s not a working directory clone; it’s essentially a complete Git database snapshot.

That matters because when rewriting history, you don’t want to miss anything hidden in a branch or tag that still contains the unwanted file.

Double-check where the file exists

Before doing anything destructive, it helps to confirm where the file actually appears:

git log --all --name-only | grep -i generated.file

Once you’ve verified it, you can proceed with the actual removal.

Remove it from history

git filter-repo --path "src/some/path/generated.file" --invert-paths

What this does

  • --path selects the file (or path pattern) you want to target
  • --invert-paths flips the meaning, so instead of keeping that file, Git removes it everywhere in history

In practice, it rewrites commits and removes the file from every single one.

After rewriting history

Once filter-repo has finished, you need to force push the rewritten history:

git push --force --all
git push --force --tags

Important consequence

This process rewrites commit history, which means all commit hashes will change.

Anyone else working with the repository will need to re-clone or reset their local branches. Otherwise, they’ll end up with diverging histories.

It’s a bit of a "nuclear option," but when a bad file is deeply embedded in history, it’s often the cleanest and most reliable fix.