我有一个Git存储库,其中包含许多子目录。现在我发现其中一个子目录与另一个子目录无关,应该分离到一个单独的存储库中。

如何在保留子目录中文件的历史记录的同时执行此操作?

我想我可以制作一个克隆并删除每个克隆中不需要的部分,但我想这会在检查旧版本等时提供完整的树。这可能是可以接受的,但我更希望能够假装这两个存储库没有共享的历史。

为了清楚起见,我有以下结构:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我想改为:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

当前回答

这里是对CoolAJ86的“简单方法”的一个小修改™回答,以便将多个子文件夹(假设sub1和sub2)拆分为一个新的git存储库。

简单的方法™ (多个子文件夹)

准备旧回购推送<大回购>gitfilter分支--树过滤器“mkdir<文件夹名称>;mv<sub1><sub2><文件夹名称>/”HEADgit子树拆分-P<文件夹名称>-b<新分支名称>邻苯二胺注意:<文件夹名称>不能包含前导或尾随字符。例如,名为subject的文件夹必须作为子项目传递,而不是/子项目/windows用户注意:当文件夹深度>1时,<文件夹名称>必须具有*nix样式的文件夹分隔符(/)。例如,名为path1\path2\subject的文件夹必须作为path1/path2/subject传递。此外,不要使用mvcommand,而是移动。最后一点:与基本答案的最大区别是脚本“gitfilter分支…”的第二行创建新回购mkdir<新回购>推送<新回购>初始化git pull</path/to/big repo><新分支的名称>将新回购链接到Github或任何地方git远程添加原点<git@github.com:我的用户/new repo.git>git推送原点-u主清理(如果需要)popd#退出<新回购>推送<大回购>gitrm-rf<文件夹名称>注意:这会将所有历史引用保留在存储库中。如果您确实担心提交了密码或需要减小.git文件夹的文件大小,请参阅原始答案中的附录。

其他回答

使用此筛选器命令删除子目录,同时保留标记和分支:

git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --prune-empty \
--tag-name-filter cat -- --all

在垃圾收集之前,您可能需要像“gitreflog-expire--expire=now--all”这样的内容来实际清理文件。gitfilter分支只删除历史记录中的引用,但不删除保存数据的reflog条目。当然,先测试一下。

尽管我的初始条件有所不同,但我的磁盘使用量在这样做时大幅下降。也许--子目录过滤器否定了这种需要,但我对此表示怀疑。

如上所述,我必须使用相反的解决方案(删除所有提交而不触及我的dir/subdr/targetdir),这似乎可以很好地去除大约95%的提交(根据需要)。然而,还有两个小问题。

首先,过滤器分支完成了一项出色的工作,删除了引入或修改代码的提交,但显然,合并提交在Gitiverse的站点之下。

截图:合并疯狂!

这是一个我可能可以忍受的美容问题(他说……慢慢后退,眼睛转向)。

第二,剩下的几个提交几乎都是重复的!我似乎获得了第二个多余的时间线,它几乎涵盖了整个项目的历史。有趣的是(你可以从下面的图片中看到),我的三个本地分支并不都在同一个时间线上(这就是为什么它存在,而不仅仅是垃圾收集)。

尖叫:双双,Git过滤器分支样式

我唯一能想到的是,其中一个被删除的提交可能是过滤器分支实际删除的单个合并提交,并且创建了并行时间线,因为每个现在未合并的链都有自己的提交副本。(耸耸肩,我的TARDiS在哪里?)我很确定我能解决这个问题,尽管我真的很想知道它是怎么发生的。

对于疯狂的mergefest-O-RAMA,我很可能会把它单独放在一边,因为它在我的承诺历史中根深蒂固,每当我走近时,它都会威胁我——它似乎并没有真正引起任何非外观问题,因为在Tower.app中它非常漂亮。

发现这篇精彩的文章原创参考很容易理解。记录在这里,以防无法访问。

1.准备当前存储库

$ cd path/to/repository
$ git subtree split -P my-folder -b my-folder
Created branch 'my-folder'
aecbdc3c8fe2932529658f5ed40d95c135352eff

文件夹的名称必须是相对路径,从存储库的根目录开始。

2.创建新存储库

$ cd my-folder
$ git init
Initialized empty Git repository in /Users/adamwest/Projects/learngit/shop/my-folder/.git/
$ git add .
$ git commit -m "initial commit"
[master (root-commit) 192c10b] initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file

这里我们只需要cd到新文件夹,初始化新存储库,并提交任何内容。

3.添加新的远程存储库并推送

$ git remote add origin git@github.com:robertlyall/my-folder.git
$ git push origin -u master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 199 bytes | 199.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:robertlyall/my-folder.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

我们在这里添加远离GitHub的新存储库,然后推送我们的第一次提交。

4.从主存储库中删除文件夹并推送

$ cd ../
$ git rm -rf my-folder
rm 'my-folder/file'
$ git commit -m "Remove old folder"
[master 56aedbe] remove old folder
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 my-folder/file
$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 217 bytes | 217.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:robertlyall/shop.git
   74dd8b3..56aedbe  master -> master

最后,我们cd回到rooot目录,从主存储库中删除文件夹,然后提交并推送更改。现在,我们的主存储库中有一个文件夹,但它链接到一个完全独立的存储库,可以在多个项目中重用。

这里是对CoolAJ86的“简单方法”的一个小修改™回答,以便将多个子文件夹(假设sub1和sub2)拆分为一个新的git存储库。

简单的方法™ (多个子文件夹)

准备旧回购推送<大回购>gitfilter分支--树过滤器“mkdir<文件夹名称>;mv<sub1><sub2><文件夹名称>/”HEADgit子树拆分-P<文件夹名称>-b<新分支名称>邻苯二胺注意:<文件夹名称>不能包含前导或尾随字符。例如,名为subject的文件夹必须作为子项目传递,而不是/子项目/windows用户注意:当文件夹深度>1时,<文件夹名称>必须具有*nix样式的文件夹分隔符(/)。例如,名为path1\path2\subject的文件夹必须作为path1/path2/subject传递。此外,不要使用mvcommand,而是移动。最后一点:与基本答案的最大区别是脚本“gitfilter分支…”的第二行创建新回购mkdir<新回购>推送<新回购>初始化git pull</path/to/big repo><新分支的名称>将新回购链接到Github或任何地方git远程添加原点<git@github.com:我的用户/new repo.git>git推送原点-u主清理(如果需要)popd#退出<新回购>推送<大回购>gitrm-rf<文件夹名称>注意:这会将所有历史引用保留在存储库中。如果您确实担心提交了密码或需要减小.git文件夹的文件大小,请参阅原始答案中的附录。