git-filter-branch
Rewrite repository history
TLDR
Remove a file from all commits
Update author email
Delete a folder from history
SYNOPSIS
git filter-branch [--setup <setup>] [--env-filter <command>] [--tree-filter <command>] [--index-filter <command>] [--parent-filter <formula>] [--msg-filter <command>] [--commit-filter <command>] [--tag-name-filter <command>] [--prune-empty [--tag-name-filter]] [-- --all | --branches | --tags | <ref-list>]
PARAMETERS
--setup <command>
Command run once at start, before filters.
--env-filter <command>
Shell command per commit; env vars like GIT_COMMIT set.
--tree-filter <command>
Command run in worktree per commit; slow, checks out each time.
--index-filter <command>
Command on index per commit; faster than tree-filter.
--parent-filter <formula>
Perl expression to modify parents list.
--msg-filter <command>
Filter commit message via shell command.
--commit-filter <command>
Shell command replacing commit creation; return new SHA or empty.
--tag-name-filter <command>
Filter tag names; omit to strip tags.
--prune-empty
Remove commits with no changes; repeat for chains.
--force
Overwrite existing refs/original backups.
-d <dir>
Use specified dir for temp index/worktree.
-- --all
Rewrite all refs (branches, tags, notes).
--branches
Rewrite local branches only.
--tags
Rewrite annotated tags only.
DESCRIPTION
git-filter-branch is a powerful but dangerous Git command used to rewrite the commit history of branches or tags by applying user-defined filters to each commit. It allows modification of commit metadata, trees, messages, parents, or even skipping commits entirely.
Common use cases include removing unwanted files (e.g., large binaries or sensitive data) from history, renaming paths, splitting repositories, or normalizing commit authorship. Filters run sequentially: env-filter sets environment variables, tree-filter rewrites worktree, index-filter manipulates index, etc.
The command creates backups in refs/original/ but permanently alters history, making it incompatible with shared repositories unless force-pushed (which is risky). It's computationally expensive for large histories due to full checkouts per commit.
Deprecated since Git 2.24 (Q4 2019) in favor of the faster, safer git filter-repo. Use with extreme caution; test on clones first.
CAVEATS
Extremely dangerous: rewrites shared history, slow on large repos, no safety checks. Deprecated—prefer git filter-repo. Always clone and test first. Backups in refs/original/ are deleted by git update-ref -d.
MIGRATION TIP
Use git filter-repo --path path/to/remove instead for file removal; 10-100x faster.
BACKUP SAFETY
Pre-command: git branch backup-branch. Post: git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin to clean backups.
HISTORY
Introduced in Git 1.5.3 (February 2007) for history rewriting. Widely used for data sanitization until deprecated in Git 2.24 (November 2019) due to performance/safety issues. Superseded by third-party git filter-repo (2019+), now recommended.
SEE ALSO
git filter-repo(1), git rebase(1), git reset(1), git reflog(1)


