git filter-repo: Usage, Options & Common CI Errors
git filter-repo is the modern, fast tool for rewriting an entire repository history.
When you need to purge a secret or a huge file from every commit, filter-repo is the supported replacement for filter-branch. It rewrites all SHAs, so it is a one-off, coordinated operation.
What it does
git filter-repo (a separate tool, not bundled with core Git) rewrites the whole commit graph according to path, blob, and content rules, producing new commits with new SHAs and removing the original refs and reflogs.
Common usage
git filter-repo --path secrets.env --invert-paths
git filter-repo --path src/ --path docs/
git filter-repo --replace-text expressions.txt
git filter-repo --strip-blobs-bigger-than 10MOptions
| Flag | What it does |
|---|---|
| --path <p> | Keep only matching paths |
| --invert-paths | Remove the matching paths instead |
| --replace-text <file> | Redact matched text in blobs |
| --strip-blobs-bigger-than <n> | Drop oversized blobs |
| --force | Run even on a non-fresh clone |
Common errors in CI
Aborting: Refusing to destructively overwrite repo history since this does not look like a fresh clone - filter-repo insists on a fresh clone to prevent accidents. Either clone anew or pass --force. After rewriting, every collaborator must re-clone, and you must force-push all refs and tags.
Using this in CI
CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # history, tags, and git describe all need this
- run: |
git rev-parse --is-shallow-repository # expect false
git rev-parse --abbrev-ref HEAD # prints HEAD when detached