多年来,我们庞大的回购协议之一已经有机地增长到包含两个项目。这些项目现在相去甚远,以至于我们决定将它们放在单独的存储库中。
拆分它们是没有问题的(复制存储库,删除存储库1中的项目A并删除存储库2中的项目B),也没有将项目移至存储库根目录而不是repo / projectX(git filter-branch --subdirectory-filter
)。
但是,我们有8000多个提交,并且其中绝大多数只涉及项目之一,而不是两者。理想情况下,我们希望从repo2中清除对项目A的提交,反之亦然。
是否存在执行此类操作的脚本或工具?从逻辑上讲,这似乎很简单:
for each commit {
if all files startwith '/projectA' delete commit
}
答案 0 :(得分:0)
不可能更改有关任何提交的任何内容。
同时,每个提交都记录其前身( parent )提交的哈希ID(直接:原始哈希ID)。
将这两个事实放在一起时,您会发现不可能从链的中间删除提交。这是一个简单的玩具示例,整个存储库中只有三个提交:
A <--B <--C <--master
在这里,名称master
包含第三次提交C
的ID。这是最后的提交:这是Git开始工作的地方。 Git从提交C
中读取 second 提交B
的哈希ID。 Git从提交B
中读取 first 提交A
的哈希ID。提交A
具有 no 父项,因此操作停止,并且我们刚刚看到了历史记录。
您现在决定要删除落实B
。提交C
不能更改,但是有 您可以执行以下操作:您可以提取提交C
,使进行一些更改,然后重新提交结果进行新的提交,我们可以将其称为D
,但我们可以将其称为C'
:
A--B--C
\
C'
我们想在进行C'
之前进行的更改是消除两件事:
提交B
对源树的效果:无论B
中发生了什么变化,我们都以某种方式退回。
新提交C'
的 parent 应该是A
,而不是B
。
完成此过程后,我们将拥有一个新的历史记录,其中从未出现提交B
。但是,名称master
现在必须指向新的不同提交C'
:
B--C [abandoned]
/
A--C' <-- master
如果我们有一个更大的存储库,其中包含更多提交,并且想跳过其中两个提交,则过程将非常相似:
B--C--D--E--F--G--H [abandoned]
/
A--C'-F'-G'-H' <-- master
请注意,被放弃的提交在存储库中保留了一段时间,但最终通过提供的通过Git的“垃圾收集”过程(git gc
)被丢弃了。 em>,例如,通过将提交H
与提交H'
合并,可以确保您永远不会重新连接到旧提交。
任何仍然具有master
指向H
的克隆都必须被视为具有放射性,以免您不慎将H
合并到H'
中并把所有这些提交带回。 / p>
Git附带了一个工具,使您可以执行上述操作。但是,此工具就像瑞士军队的电锯一样,没有防护措施可防止割伤手,脚,头等。它并不是特别容易使用,尤其是对于您在此处设置的任务。
它的作用是简单地枚举存储库中的可到达的提交(请参阅Think Like (a) Git,并阅读Git对图论的使用)。然后运行一个循环:
--commit-filter
)允许您跳过提交结果并一路保持“原始哈希ID⟶新哈希ID”的映射,以映射父哈希ID。您将想了解在跳过这样的提交时“重映射到祖先”是如何工作的。
重新复制存储库中的每个提交都很慢。在某些情况下,例如使用--tree-filter
从字面上提取每个提交时,它的运行速度非常慢。结果,filter-branch有许多过滤器选项可以尝试加快处理速度。请记住,这些选项本质上只是优化技巧:作为打算使用filter-branch作为工具的人,您应该首先明确定义问题并提出正确的解决方案(即,如何修改源树快照和提交)。 ),然后查看是否可以使用优化路径(例如--index-filter
)进行快照编辑。
如果您的存储库中有标签,请记住,除非提供--tag-name-filter
,否则filter-branch不会重写标签以指向新的提交哈希。
最后,请记住,如果您选择走这条路-即使您使用自己编写的工具而不是使用git filter-branch
-您正在做的是对某些子集进行复制原始提交。新的存储库不再与旧版本的 any 现有克隆兼容!