git - 将子目录分离(移动)到单独的 Git 存储库中

标签 git git-subtree git-filter-branch

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

如何在保持子目录中文件的历史记录的同时做到这一点?

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

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

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

但我想要这个:
XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

最佳答案

更新 :这个过程非常普遍,所以 git 团队使用一个新工具,git subtree 使它变得更加简单。 .看这里:Detach (move) subdirectory into separate Git repository

您想克隆您的存储库,然后使用 git filter-branch标记除您希望在新存储库中进行垃圾收集的子目录之外的所有内容。

  • 要克隆本地存储库:
    git clone /XYZ /ABC
    

    (注意:存储库将使用硬链接(hard link)克隆,但这不是问题,因为硬链接(hard link)文件本身不会被修改 - 将创建新文件。)
  • 现在,让我们保留我们想要重写的有趣分支,然后删除原点以避免推送到那里并确保原点不会引用旧提交:
    cd /ABC
    for i in branch1 br2 br3; do git branch -t $i origin/$i; done
    git remote rm origin
    

    或者对于所有远程分支:
    cd /ABC
    for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
    
  • 现在您可能还想删除与子项目无关的标签;你也可以稍后再做,但你可能需要再次修剪你的 repo 。我没有这样做并得到了 WARNING: Ref 'refs/tags/v0.1' is unchanged对于所有标签(因为它们都与子项目无关);此外,删除此类标签后,将回收更多空间。显然 git filter-branch应该能够重写其他标签,但我无法验证这一点。如果要删除所有标签,请使用 git tag -l | xargs git tag -d .
  • 然后使用 filter-branch 和 reset 排除其他文件,以便可以修剪它们。让我们也添加 --tag-name-filter cat --prune-empty删除空提交并重写标签(请注意,这将不得不剥离它们的签名):
    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
    

    或者,只重写 HEAD 分支并忽略标签和其他分支:
    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
    
  • 然后删除备份的reflogs,这样空间才能真正被回收(虽然现在操作是破坏性的)
    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --prune=now
    

    现在您拥有 ABC 子目录的本地 git 存储库,并保留了其所有历史记录。

  • 注意:对于大多数用途,git filter-branch确实应该添加参数 -- --all .是的,这真的是--space-- all .这需要是命令的最后一个参数。正如 Matli 发现的那样,这将保留项目分支和标签包含在新的存储库中。

    编辑:整合了来自以下评论的各种建议,以确保例如存储库实际上缩小了(以前并非总是如此)。

    关于git - 将子目录分离(移动)到单独的 Git 存储库中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/696043/

    相关文章:

    git - 如何在保留子目录的同时拆分 git 存储库?

    git - 如何比较git中两个非顺序提交的代码?

    git子树添加: change prefix preserving local commits

    git - git 子树在特定上下文中的反向分割

    Git子树拆分两个目录

    git filter-branch 导致断开连接的历史记录 : how to get rid of the old commits?

    git - 快速切换版本

    javascript - 仅当 js 文件发生更改时才运行 eslint 集成

    git - 如何使用 mingw 和 sbt 在 Windows 上获得 Specs2 颜色支持

    git - 在 git -rm'ed 并推送之后,从 git 存储库中删除提交的内容