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/359424/

    相关文章:

    重命名文件夹后的 Git 过滤

    git - 使用 Git 更改项目的第一次提交?

    git - 从 Git 仓库和远程 GitHub 上完全删除文件

    git - 有没有办法像临时存储 git login 但不存储密码?

    linux - 是否有 gedit 的 ftp 插件可以让我在本地工作?

    推送到远程时 git push 卡住了

    git - 如何强制子树推送覆盖远程更改?

    Git 子树 merge 策略 - 设置本身如何跨克隆进行?

    git - ssh-keygen 没有找到 ssh_askpass

    git - 删除无效的 git-subtree-split 哈希