Git Filter-Repo是用于重写历史记录的多功能工具,其中包括我在其他任何地方都没有找到的功能。它大致与Git Filter-Branch的工具空间大致相同,但没有引起投降的性能较差,具有更多功能,并且设计可在可用性方面扩展,超出了微不足道的重写案例。现在,Git Project推荐Git Filter-Repo而不是Git Filter-Branch。
虽然大多数用户可能只会将过滤器repo用作简单的命令行工具(并且可能只使用其一些标志),但在其核心过滤器中,repo包含一个用于创建历史记录重写工具的库。因此,具有专业需求的用户可以利用它来快速创建全新的历史记录重写工具。
过滤器repo需要:
git-filter-repo
是一个单文件python脚本,它是为了在许多系统上进行基本使用的安装而进行的:只需将文件放入您的$路径中即可。
有关基本用法或特殊情况以外的内容,请参见install.md。仅当适用以下一个应用时,需要越多的指示:
用于综合文档:
如果您喜欢从示例中学习:
有关过滤器repo的Git Rev新闻文章中,这是更详细介绍的,但是对于主要竞争对手来说,一些亮点是:
对于非琐事存储库,滤波器分支极为缓慢(比应该慢的多个数量级)。
Filter-Cranch充满了陷阱,可以使您的重写默默破坏或至少阻止您的“清理”努力,这比您最初的问题更有问题和混乱。
过滤器分支非常繁重,用于任何重写甚至略有无事的重写。
GIT项目指出,上述过滤器分支的问题不能向后兼容。他们建议您停止使用过滤器分支
过滤器分支的顽固迷可能对滤波器(又名滤波器 - 分支机构)感兴趣,这是基于过滤器repo的过滤器分支的重新实现,该滤波器更具性能(尽管不如过滤器 - 不如过滤器 - repo)。
有一个备忘单,显示如何将示例命令从过滤器 - 布兰奇手册中转换为filter-repo命令。
当时的好工具很棒,但是尽管它使一些事情变得简单,但它仅限于一些重写。
它的架构不适合处理更多类型的重写。
它的架构甚至是针对其预期的用户酶的一些缺点和错误。
BFG的粉丝可能对BFG-ish感兴趣,BFG-ish是基于Filter-Repo的BFG的重新实现,其中包括相对于BFG的几个新功能和错误。
有一个备忘单显示了如何将示例命令从BFG回购器的手册转换为filter-repo命令。
假设我们要提取一个存储库,目的是将该件合并到其他一些更大的存储库中。为了提取,我们想:
使用filter-repo进行此操作与以下命令一样简单:
git filter-repo --path src/ --to-subdirectory-filter my-module --tag-rename ' ' : ' my-module- '
(单引号是不必要的,但要使人更清楚地说,我们正在用my-module-
取代空字符串作为前缀 - )
BFG回购清洁器无法进行这种改写;实际上,所有三种想要的更改都超出了其功能。
过滤器分支带有一堆警告(以下更多内容),即使您弄清楚了必要的调用:
git filter-branch
--tree-filter ' mkdir -p my-module &&
git ls-files
| grep -v ^src/
| xargs git rm -f -q &&
ls -d *
| grep -v my-module
| xargs -I files mv files my-module/ '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
有些人可能会注意到,由于使用-tree-filter,上述过滤器分支调用确实会很慢。您也可以使用过滤器分支的 - 索引滤波器选项,将上述命令更改为:
git filter-branch
--index-filter ' git ls-files
| grep -v ^src/
| xargs git rm -q --cached;
git ls-files -s
| sed "s%$(printf \t)%&my-module/%"
| git update-index --index-info;
git ls-files
| grep -v ^my-module/
| xargs git rm -q --cached '
--tag-name-filter ' echo "my-module-$(cat)" '
--prune-empty -- --all
git clone file:// $( pwd ) newcopy
cd newcopy
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git gc --prune=now
但是,对于任何一个过滤器 - 分支命令,都有一堆警告。首先,有些人可能想知道为什么我在此处列出五个命令进行过滤器分支。尽管使用了 - all和 - 标记名称过滤器,以及过滤器 - 布兰奇(Filter-Branch)声称克隆足以摆脱旧对象,但删除其他标签并执行另一个GC的额外步骤仍需要清理旧物体,并避免将新的和旧历史混合在某个地方。其他警告:
一个人可以将其与类似的东西一起入侵:
git fast-export --no-data --reencode=yes --mark-tags --fake-missing-tagger
--signed-tags=strip --tag-of-filtered-object=rewrite --all
| grep -vP ' ^M [0-9]+ [0-9a-f]+ (?!src/) '
| grep -vP ' ^D (?!src/) '
| perl -pe ' s%^(M [0-9]+ [0-9a-f]+ )(.*)$%1my-module/2% '
| perl -pe ' s%^(D )(.*)$%1my-module/2% '
| perl -pe s%refs/tags/%refs/tags/my-module-%
| git -c core.ignorecase=false fast-import --date-format=raw-permissive
--force --quiet
git for-each-ref --format= " delete %(refname) " refs/tags/
| grep -v refs/tags/my-module-
| git update-ref --stdin
git reset --hard
git reflog expire --expire=now --all
git gc --prune=now
但这带来了一些令人讨厌的警告和局限性:
现有的存储库过滤工具都没有做我想要的。他们都满足我的需求。没有工具提供下面我想要的前八个特征中的任何一个,也没有工具提供最后四个特征中的两个以上:
[起始报告]为用户提供了对其存储库的分析,以帮助他们开始修改或重命名的内容,而不是期望他们猜测或找到其他工具来弄清楚它。 (触发,例如,第一次使用特殊标志(例如 - 分析)运行。)
[保持与删除]不仅为用户提供一种轻松删除所选路径的方式,还为用户提供标志以仅保留某些路径。当然,用户可以通过指定除了要保留的路径以外的所有路径来解决此问题,但是需要指定任何版本中任何版本中存在的所有路径有时可能会很痛苦。用于过滤器分支,使用诸如git ls-files | grep -v ... | xargs -r git rm
可能是一个合理的解决方法,但可能会变得笨拙,对用户来说并不那么简单。再加上这些命令通常是特定于操作系统的(您可以在我提供的摘要中发现GNUism吗?)。
[重命名]重命名路径应该很容易。例如,除了允许一个人将某个子目录视为存储库的根源外,还为用户提供了使存储库的根源的选项,只是成为一个子目录。通常,更允许文件和目录易于重命名。提供理智检查,如果重命名导致多个文件在同一路径上存在。 (并添加特殊处理,以便如果提交仅复制oldname-> newname而无需修改,则过滤OldName-> NewName不会触发理智检查并在该提交上死亡。)
[更智能的安全]编写原始参考文献副本,以在存储库中的特殊名称空间中提供不友好的恢复机制。许多人会很难使用它来恢复。我见过的几乎每个人都做了一个存储库过滤操作,因此使用了一个新鲜的克隆,因为在错误的情况下擦除克隆是一种非常容易的恢复机制。如果我们不在新鲜的克隆中,则强烈鼓励通过检测和保释来鼓励工作流程,除非用户覆盖(用户)。
[自动收缩]过滤后自动卸下旧的cruft并为用户重新包装存储库(除非覆盖);这简化了用户的内容,有助于避免将新旧历史和新历史混合在一起,并避免在缩小Manpage中记录的多个仓库的多步骤过程中实际上在某些情况下不起作用的问题。 (我在看着你,过滤分支。)
[清洁分离]避免使用户混淆(并防止将旧的存储库和重写的存储库混合在一起,以防止意外重新刷新旧物品。 (使用 - 标签名称滤波器选项时,这尤其是滤波器分支的问题,有时也是一个问题时,当仅滤波分支的子集时也是一个问题。)
[多功能性]为用户提供了扩展工具甚至编写新工具的能力,以利用现有功能,并以(a)避免需要单独的过程(会破坏性能)的方式提供这种可扩展性,(b)避免使用户指定与OS相关的外壳命令(这将阻止用户彼此共享命令),(c)利用丰富的数据结构(因为Hashes,Dict,dict,列表和数组在Shell中很难)和( d)提供合理的字符串操纵功能(外壳中缺乏)。
[旧提交引用]为用户提供了一种使用新存储库的旧提交ID(尤其是通过REFS/ replot/ references从旧哈希映射到新哈希的映射)。
[提交消息一致性]如果提交消息请参阅ID的其他提交(例如“ This this revers Commit 01234567890ABCDEF”,“在COMMIT 0013DEADBEEF9A ...”中,应将这些提交消息重写以引用新提交ID。
[变成空的修剪]应修剪由于过滤而变为空的提交。如果将提交的父母修剪,则第一个非封闭的祖先需要成为新父母。如果不存在非统治的祖先,并且该提交不是合并,那么它将成为一个新的根源提交。如果不存在非统治的祖先,并且该提交是合并的,那么合并将少一个父母(因此,如果它没有自己的文件更改,则可能会成为一个非合并犯罪的犯罪。 。这里要注意的一件特殊的事情是,我们修剪命令会变为空,而不是开始空虚。有些项目故意出于版本控制或发布原因创建空委托,不应将其删除。 (作为特殊情况,提交开始空虚,但将父母修剪的父母也将被认为是“空的”。)
[变成降级的修剪]变为空的委员会修剪可能会导致拓扑变化,并且有很多特殊情况。通常,由于需要保留图形拓扑,因此不会删除合并提交,但是父母和其他祖先的修剪最终可能导致一个或多个父母的损失。上面已经指出了一个简单的案例:如果合并提交失去了足够多的父母成为非合并提交,并且没有文件更改,那么它也可以修剪。合并提交也可以具有变性的拓扑:最终可能会成为父母兼职的Merge_base(如果所有介入原始仓库的所有干预都均已修剪),或者可能最终与一位父母在一起,这是其另一个父母的祖先父母。在这种情况下,如果合并没有自己的文件更改,则也可以修剪合并提交。但是,就像我们在空的修剪时一样,我们不修剪开始合并开始堕落的提交(这表明它可能是故意的,例如与 - no-ff合并),但只有合并的承诺会变成堕落,没有任何文件更改的文件更改他们自己的。
[速度]过滤应合理的快速
请参阅贡献指南。
预计过滤器repo社区的参与者将遵守与GIT项目相同的标准,因此GIT行为守则适用。
在Filter-Repo及其前任方面的工作还为核心GIT中的快速进出和快速IMPORT(以及其他命令)进行了许多改进,基于Filter-Repo需要做的工作: