Git, CI/CD & GitOpsXXXVI · Git History RewritingTools
git filter-repo — the modern replacement for git filter-branch
What you'll learn
- Install git-filter-repo from a package or a single-file download
- Run git filter-repo --path <path> --invert-paths to remove a file from every commit
- Run git filter-repo --replace-text <file> to substitute a string across every commit
- Distinguish the callback modes (--blob-callback, --name-callback, --mailmap)
- Apply the safety contract: fresh clone, reflog expire, gc, --force-with-lease
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git filter-repo is the modern replacement for the deprecated
git filter-branch. It is faster, has safer defaults, and
refuses to run on a dirty working tree. The tool is the
default for the history-rewrite operations in this course.
Why filter-repo replaced filter-branch
filter-branch rewrote history with a per-commit shell filter
on every commit. The shell-out made it slow on large repos,
the default options lost history unless extra flags were
passed, and the maintenance burden was high. The Git project
deprecated it in 2019 and pointed users at filter-repo.
flowchart LR
A["filter-branch"] -->|"deprecated 2019"| B["filter-repo"]
B --> C["--invert-paths"]
B --> D["--replace-text"]
B --> E["--callbacks"]
B --> F["--mailmap"]
Install
apt install git-filter-repo
# or
curl -O https://raw.githubusercontent.com/newren/git-filter-repo/main/git-filter-repo
chmod +x git-filter-repo && mv git-filter-repo /usr/local/bin/
Verify with git filter-repo --version.
—path and —invert-paths
Remove every commit that touched a path:
git filter-repo --path config/secrets.yaml --invert-paths
The original commits become unreachable and are dropped by
the subsequent gc step.
—replace-text
Substitute a literal in every blob that contained it (commit messages included):
git filter-repo --replace-text expressions.txt
The file is plain text, one literal->replacement pair per
line. Process substitution works for one-shot rewrites:
git filter-repo --replace-text <(echo 'api.example.com/v1==>api.example.com/v2')
The mode is right when the file the credential lives in must remain (a Terraform variable file, a structured config) but the credential itself must change.
Callbacks and —mailmap
For more complex rewrites, the callbacks take a Python expression per object:
--blob-callback- per blob body; return new body orNone--name-callback- per path; return new path orNone--mailmap- per identity in.mailmapformat
Author rename:
git filter-repo --name-callback 'return name.replace(b"Old Name", b"New Name")'
Identity correction (married name, company rebranding):
git filter-repo --mailmap mailmap.txt
The safety contract
The rewrite is destructive. The local repository before the rewrite is the only copy of the original history. The five-step contract:
git clone --no-local /path/to/repo /tmp/clean-repo
cd /tmp/clean-repo
git filter-repo --path .env --invert-paths
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force-with-lease origin main
--no-local so the source repo is not modified. --force-with-lease
rejects the push if the remote has commits the local repo did
not see.
Production discipline
- Fresh clone, never the primary working tree. The clone is cheap; recovery from a bad rewrite on a primary tree is not.
--force-with-leasealways,--forcenever. The flag is the safety catch against clobbering concurrent commits that landed during the rewrite window.- Verify before pushing.
git log --follow <path>for path removal;git log -S<literal>for substitution.
Cross-course references
- Linux for Production Sysadmins - Part XXXIV (ConfigMgmt) covers the package-management analogue.
- Ansible for Production Sysadmins - Part XXXVII (RepoArch) covers the repository layout the callbacks can rewrite.
- Terraform for Production Sysadmins - Parts IX-XII (State) cover the state-file rewrite that follows a history rewrite.
Quiz
Knowledge check · 4 questions
Q1. Which command is the modern replacement for the deprecated git filter-branch?
Q2. git filter-repo cannot safely run on an engineer's primary working tree because it preserves the working tree by default.
Q3. Name the two primary modes of git filter-repo used in infrastructure work, and describe the difference between them.
Q4. An engineer runs git filter-repo on the team's primary working tree and reports the rewrite completed. Diagnose the failure.
Engineer E runs git filter-repo --path .env --invert-paths on the primary working tree at /home/e/repo. The tool prints an error and exits. The team's shared repository on the forge still has the .env file in history. Engineer E has a fresh clone in /tmp/clean-repo from a previous experiment but did not use it.
Passing score: 75%. Answers are checked in this browser.