Quickly rewrite git repository history (filter-branch replacement)
Quickly rewrite git repository history (filter-branch replacement)
git filter-repo is a versatile tool for rewriting history, which includes capabilities I have not found anywhere else. It roughly falls into the same space of tool as git filter-branch but without the capitulation-inducing poor performance, with far more capabilities, and with a design that scales usability-wise beyond trivial rewriting cases. git filter-repo is now recommended by the git project instead of git filter-branch.
While most users will probably just use filter-repo as a simple command line tool (and likely only use a few of its flags), at its core filter-repo contains a library for creating history rewriting tools. As such, users with specialized needs can leverage it to quickly create entirely new history rewriting tools.
filter-repo requires:
While the git-filter-repo repository has many files, the main logic
is all contained in a single-file python script named
git-filter-repo, which was done to make installation for basic use
on many systems trivial: just place that one file into your $PATH.
See INSTALL.md for things beyond basic usage or special cases. The more involved instructions are only needed if one of the following apply:
For comprehensive documentation:
If you prefer learning from examples:
In either case, you may also find the Frequently Answered Questions useful.
This was covered in more detail in a Git Rev News article on filter-repo, but some highlights for the main competitors:
filter-branch is extremely to unusably slow (multiple orders of magnitude slower than it should be) for non-trivial repositories.
filter-branch is riddled with gotchas that can silently corrupt your rewrite or at least thwart your "cleanup" efforts by giving you something more problematic and messy than what you started with.
filter-branch is very onerous to use for any rewrite which is even slightly non-trivial.
the git project has stated that the above issues with filter-branch cannot be backward compatibly fixed; they recommend that you stop using filter-branch
die-hard fans of filter-branch may be interested in filter-lamely (a.k.a. filter-branch-ish), a reimplementation of filter-branch based on filter-repo which is more performant (though not nearly as fast or safe as filter-repo).
a cheat sheet is available showing how to convert example commands from the manual of filter-branch into filter-repo commands.
great tool for its time, but while it makes some things simple, it is limited to a few kinds of rewrites.
its architecture is not amenable to handling more types of rewrites.
its architecture presents some shortcomings and bugs even for its intended usecase.
fans of bfg may be interested in bfg-ish, a reimplementation of bfg based on filter-repo which includes several new features and bugfixes relative to bfg.
a cheat sheet is available showing how to convert example commands from the manual of BFG Repo Cleaner into filter-repo commands.
Let's say that we want to extract a piece of a repository, with the intent on merging just that piece into some other bigger repo. For extraction, we want to:
Doing this with filter-repo is as simple as the following command:
git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename '':'my-module-'
(the single quotes are unnecessary, but make it clearer to a human that we
are replacing the empty string as a prefix with my-module-)
BFG Repo Cleaner is not capable of this kind of rewrite; in fact, all three types of wanted changes are outside of its capabilities.
filter-branch comes with a pile of caveats (more on that below) even once you figure out the necessary invocation(s):
…
Some might notice that the above filter-branch invocation will be really slow due to using --tree-filter; you could alternatively use the --index-filter option of filter-branch, changing the above commands to:
…
However, for either filter-branch command there are a pile of caveats. First, some may be wondering why I list five commands here for filter-branch. Despite the use of --all and --tag-name-filter, and filter-branch's manpage claiming that a clone is enough to get rid of old objects, the extra steps to delete the other tags and do another gc are still required to clean out the old objects and avoid mixing new and old history before pushing somewhere. Other caveats:
One can kind of hack this together with something like:
…
But this comes with some nasty caveats and limitations:
No open issues yet, or sync has not completed.