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

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

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

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

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我想改为:

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

当前回答

更简单的方法

安装git拆分。我基于jkeating的解决方案将其创建为git扩展。将目录拆分为本地分支#更改到回购的目录cd/path/to/repo#检查分支机构数字结帐XYZ#将多个目录拆分为新的分支XYZ数字拆分-b XYZ XY1 XY2在某处创建空回购。我们假设在GitHub上创建了一个名为xyz的空repo,其路径为:git@github.com:simpliwp/xyz.git推送至新回购。#为空回购添加一个新的远程源,以便我们可以在GitHub上推送到空回购git远程添加origin_xyzgit@github.com:simpliwp/xyz.git#将分支推送到空回购的主分支git push origin_xyz xyz:master将新创建的远程存储库克隆到新的本地目录#将当前目录更改为旧的存储库cd/path/to/where/you/want/the/new/local/repo#克隆您刚刚推送到的远程存储库git克隆git@github.com:simpliwp/xyz.git

其他回答

我确实遇到了这个问题,但所有基于gitfilter分支的标准解决方案都非常缓慢。如果你有一个小的存储库,那么这可能不是问题,这是我的问题。我基于libgit2编写了另一个git过滤程序,作为第一步,它为主存储库的每个过滤创建分支,然后作为下一步将这些分支推送到清理存储库。在我的存储库中(500Mb100000次提交),标准的gitfilter分支方法花费了几天时间。我的程序需要几分钟来完成相同的过滤。

它有一个神话般的名字git_filter,住在这里:

https://github.com/slobobaby/git_filter

在GitHub上。

我希望它对某人有用。

我发现,为了从新存储库中正确删除旧的历史记录,在过滤器分支步骤之后,您必须做更多的工作。

执行克隆和筛选:gitclone--没有硬链接foo bar;cd条gitfilter分支--子目录filter subdir/you/wente删除对旧历史的所有引用。“origin”是跟踪您的克隆,“original”是过滤器分支保存旧内容的位置:git远程rm源git update ref-d refs/original/refs/heads/mastergit reflog expire--expire=现在--全部即使是现在,您的历史记录也可能被保存在fsck不会触及的文件包中。将其撕成碎片,创建新的打包文件并删除未使用的对象:git重新打包-ad

过滤器分支手册中对此进行了解释。

更简单的方法

安装git拆分。我基于jkeating的解决方案将其创建为git扩展。将目录拆分为本地分支#更改到回购的目录cd/path/to/repo#检查分支机构数字结帐XYZ#将多个目录拆分为新的分支XYZ数字拆分-b XYZ XY1 XY2在某处创建空回购。我们假设在GitHub上创建了一个名为xyz的空repo,其路径为:git@github.com:simpliwp/xyz.git推送至新回购。#为空回购添加一个新的远程源,以便我们可以在GitHub上推送到空回购git远程添加origin_xyzgit@github.com:simpliwp/xyz.git#将分支推送到空回购的主分支git push origin_xyz xyz:master将新创建的远程存储库克隆到新的本地目录#将当前目录更改为旧的存储库cd/path/to/where/you/want/the/new/local/repo#克隆您刚刚推送到的远程存储库git克隆git@github.com:simpliwp/xyz.git

查看git_split项目https://github.com/vangorra/git_split

在自己的位置将git目录转换为自己的存储库。没有子树有趣的业务。该脚本将获取git存储库中的现有目录,并将该目录转换为独立的存储库。在此过程中,它将复制您提供的目录的整个更改历史记录。

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.

这里是对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文件夹的文件大小,请参阅原始答案中的附录。