我有一个Git存储库,其中包含许多子目录。现在我发现其中一个子目录与另一个子目录无关,应该分离到一个单独的存储库中。
如何在保留子目录中文件的历史记录的同时执行此操作?
我想我可以制作一个克隆并删除每个克隆中不需要的部分,但我想这会在检查旧版本等时提供完整的树。这可能是可以接受的,但我更希望能够假装这两个存储库没有共享的历史。
为了清楚起见,我有以下结构:
XYZ/
.git/
XY1/
ABC/
XY2/
但我想改为:
XYZ/
.git/
XY1/
XY2/
ABC/
.git/
ABC/
如上所述,我必须使用相反的解决方案(删除所有提交而不触及我的dir/subdr/targetdir),这似乎可以很好地去除大约95%的提交(根据需要)。然而,还有两个小问题。
首先,过滤器分支完成了一项出色的工作,删除了引入或修改代码的提交,但显然,合并提交在Gitiverse的站点之下。
截图:合并疯狂!
这是一个我可能可以忍受的美容问题(他说……慢慢后退,眼睛转向)。
第二,剩下的几个提交几乎都是重复的!我似乎获得了第二个多余的时间线,它几乎涵盖了整个项目的历史。有趣的是(你可以从下面的图片中看到),我的三个本地分支并不都在同一个时间线上(这就是为什么它存在,而不仅仅是垃圾收集)。
尖叫:双双,Git过滤器分支样式
我唯一能想到的是,其中一个被删除的提交可能是过滤器分支实际删除的单个合并提交,并且创建了并行时间线,因为每个现在未合并的链都有自己的提交副本。(耸耸肩,我的TARDiS在哪里?)我很确定我能解决这个问题,尽管我真的很想知道它是怎么发生的。
对于疯狂的mergefest-O-RAMA,我很可能会把它单独放在一边,因为它在我的承诺历史中根深蒂固,每当我走近时,它都会威胁我——它似乎并没有真正引起任何非外观问题,因为在Tower.app中它非常漂亮。
原始问题希望XYZ/ABC/(*文件)变为ABC/ABC/“*文件”。在为我自己的代码实现了公认的答案后,我注意到它实际上将XYZ/ABC/(*文件)更改为ABC/(*)文件。过滤器分支手册页甚至说,
结果将包含该目录(并且仅包含该目录)作为其项目根目录。"
换句话说,它将顶级文件夹“提升”一个级别。这是一个重要的区别,因为例如,在我的历史中,我重命名了一个顶级文件夹。通过将文件夹“提升”一级,git在我进行重命名的提交时失去了连续性。
我对这个问题的回答是制作存储库的两个副本,然后手动删除每个副本中要保留的文件夹。手册页支持我:
[…]如果一次简单的提交就足以解决您的问题,请避免使用[此命令]
我找到了非常直接的解决方案,这个想法是复制存储库,然后删除不必要的部分。这是它的工作原理:
1) 克隆要拆分的存储库
git clone git@git.thehost.io:testrepo/test.git
2) 移动到git文件夹
cd test/
2) 删除不必要的文件夹并提交
rm -r ABC/
git add .
enter code here
git commit -m 'Remove ABC'
3) 使用BFG从历史记录中删除不必要的文件夹
cd ..
java -jar bfg.jar --delete-folders "{ABC}" test
cd test/
git reflog expire --expire=now --all && git gc --prune=now --aggressive
对于多个文件夹,可以使用逗号java-jar bfg.jar--删除文件夹“{ABC1,ABC2}”metric.git
4) 检查历史记录是否不包含您刚刚删除的文件/文件夹
git log --diff-filter=D --summary | grep delete
5) 现在您有了没有ABC的干净存储库,所以把它推到新的原点
remote add origin git@github.com:username/new_repo
git push -u origin master
就是这样。您可以重复这些步骤来获取另一个存储库,
只需在步骤3中删除XY1、XY2并重命名XYZ->ABC