Stage 2 · Tools
Git Internals & Advanced
git filter-repo
Rewrite Git history safely with the modern replacement for filter-branch.
Why filter-repo
Git history rewrite operations are powerful but dangerous. The older git filter-branch command is slow, error-prone, and officially discouraged. git filter-repo is the modern replacement that is faster, safer, and more flexible. It is a standalone Python script that rewrites repository history by applying user-defined filters to commits, tags, and branches.
Common use cases include removing accidentally committed secrets, restructuring repository layout, splitting monorepos, and purging large files from history. Every rewrite operation rewrites commit hashes, so it should only be done on branches that have not been shared with collaborators.
filter-repo rewrites all affected commits. Anyone who has cloned the repository will need to re-clone or reset their branches. Never rewrite history on shared branches without coordinating with your team.
Installation and setup
git filter-repo is not bundled with Git. It must be installed separately. On macOS with Homebrew, install it with brew install git-filter-repo. On Ubuntu or Debian, use apt install git-filter-repo. For other systems, install via pip install git-filter-repo or download the single-file script directly.
# macOS with Homebrew
brew install git-filter-repo
# Ubuntu / Debian
sudo apt install git-filter-repo
# Via pip
pip install git-filter-repoInstall filter-repo using your system package manager or Python pip.
After installation, verify it is available by checking the version. The tool works on bare and non-bare repositories. It requires Git 2.22 or later for full compatibility.
Remove a file from all history
One of the most common tasks is removing a file that should never have been committed, such as API keys, credentials, or large binary assets. filter-repo makes this straightforward with the --path filter combined with --invert-paths to exclude the specified path from all commits.
git filter-repo --path secrets.env --invert-pathsThis command rewrites every commit to remove secrets.env from the entire repository history.
The --path flag specifies the file or directory to target. The --invert-paths flag tells filter-repo to keep everything except the specified path. You can specify multiple paths by repeating the --path flag. Glob patterns are supported for matching multiple files.
git filter-repo --path config/secrets.yml --path node_modules/ --path '*.env' --invert-pathsRemoves multiple paths and glob patterns from all history in a single operation.
filter-repo refuses to run on repositories that have a remote configured unless you pass --force. This is a safety check. Always clone a fresh copy before rewriting history.
Rename files across history
When you need to rename a file across every commit in history, filter-repo handles the rename consistently. This is useful when a file was placed in the wrong directory or named incorrectly from the start. The --path-rename flag performs a find-and-replace on file paths.
git filter-repo --path-rename old-name.ts:new-name.tsRenames old-name.ts to new-name.ts in every commit throughout the repository history.
The rename operation uses colon syntax to separate old and new paths. You can rename directories by specifying the directory prefix. For example, --path-rename src/legacy/:src/legacy-v2/ renames all files under src/legacy/ to src/legacy-v2/.
git filter-repo --path-rename src/legacy/:src/legacy-v2/Moves all files from src/legacy/ to src/legacy-v2/ throughout the entire commit history.
Split subdirectory into new repo
Monorepos often need to be split into separate repositories. filter-repo can extract a subdirectory with its full commit history into a new standalone repository. This preserves blame history and commit metadata for the extracted directory.
# Create a fresh clone of only the subdirectory
git clone --no-local https://github.com/org/monorepo.git split-repo
cd split-repo
# Extract only the services/api directory
git filter-repo --subdirectory-filter services/apiExtracts the services/api directory into a new repository root with its full commit history.
The --subdirectory-filter flag restricts history to only commits that touched the specified directory and adjusts the repository root to that directory. After running this command, the extracted directory becomes the root of the new repository.
If you want to keep the full path structure (for example, services/api stays at services/api instead of becoming the root), use --path instead of --subdirectory-filter and omit the --invert-paths flag.
Safety and best practices
Always follow these practices when rewriting history with filter-repo. First, create a backup clone before running any rewrite operation. Second, only rewrite history on branches that have not been pushed to a shared remote. Third, force-push the rewritten branches and coordinate with your team to re-clone.
- Clone a fresh copy before rewriting history
- Use --force only when necessary and on non-cloned repositories
- Verify results with git log and git diff before pushing
- Force-push rewritten branches and notify collaborators
- Run git gc --aggressive after rewriting to reclaim space
- Test your filters on a small subset first using --dry-run when available
# Check that the file is gone from all commits
git log --all --diff-filter=D -- secrets.env
# Verify no references to the file remain
git log --all --source --remotes -- '*secrets*'
# Reclaim disk space
git reflog expire --expire=now --all
git gc --prune=now --aggressiveAfter filtering, verify the file is removed and garbage-collect to reclaim disk space.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.