LinuxCommandLibrary

git-filter-branch

Rewrite repository history

TLDR

Remove a file from all commits

$ git filter-branch --tree-filter 'rm [[-f|--force]] [file]' HEAD
copy

Update author email
$ git filter-branch --env-filter 'GIT_AUTHOR_EMAIL=[new_email]' HEAD
copy

Delete a folder from history
$ git filter-branch --tree-filter 'rm [[-rf|--recursive --force]] [folder]' HEAD
copy

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)

Copied to clipboard