首页 > 解决方案 > Git 完全删除一个未合并的根节点

问题描述

我们有两个 repo,Repo1 和 Repo2。看起来“某人”将 Repo2 推送到了 Repo1/origin,现在 Repo1 包含 Repo1 和 Repo2。有两个单独的根节点,没有任何东西被合并(幸运的是)。Repo1 也被来自 Repo2 的一大堆标签/分支污染。

一个特定的祖先(Repo2 的根节点又名“初始提交”)如何完全取消任何提交(及其标签/分支)?或者也许是一种不同/更简单的方式来拼接这两个回购?

请注意,这两个存储库都包含多年的工作(因此手动查看每个提交是不切实际的),但是如果需要,每个使用 Repo1 的人都可以重新克隆。

标签: git

解决方案


从您的文本描述中,我认为您的意思是您有一个存储库,其中包含两个独立的(不相交的,技术上的)提交子图。例如,这里是这样一个存储库的图表:

     C--D
    /    \
A--B      G--H   <-- branch1
    \    /
     E--F   <-- branch2

I--J--K--L   <-- master
       \
        M--N   <-- develop

这个特定的图有四个“入口点”,即提交FH以 为根的子图中A和 提交LN以 为根的子图I

虽然这样的存储库从根本上没有任何问题或破坏,但将其拆分为两个独立的存储库相对容易。只需从两个克隆开始,它们看起来都像这样。(您可以git clone --mirror用来制作保留所有 refs 的镜像克隆。请务必擦除它们origin的 s,以免它们都指向原始组合存储库。)

在一个这样的克隆中,删除任何外部标签(分支名称、标签名称和其他引用(如果存在任何其他引用))到两个子图中之一中的任何提交:

     C--D
    /    \
A--B      G--H   <-- branch1
    \    /
     E--F   <-- branch2

I--J--K--L   [abandoned]
       \
        M--N   [abandoned]

确保包含指向故意放弃子图的任何标签或其他名称。普通的图形查看命令,如git log,不会显示未引用的子图:它似乎已被删除,尽管它仍然存在于物理上。最终,未引用的子图会消失,或者您可以使用git gc. 从此存储库制作的克隆不会有未引用的子图。

在两个克隆中的另一个中,删除对另一个子图的所有引用:

     C--D
    /    \
A--B      G--H   [abandoned]
    \    /
     E--F   [abandoned]

I--J--K--L   <-- master
       \
        M--N   <-- develop

和以前一样,未引用的子图最终会消失。

请注意,原始的两个独立子图存储库的任何克隆可以用于这些拆分克隆git push中的任何一个。就此而言,任何第三个完全独立的存储库可用于推送到这两个克隆中的任何一个。任何为独立子图中的提交添加名称的推送都将导致整个独立子图进入推送的接收者。 正如您在评论中推测的那样,我怀疑这种情况最初就是这样出现的。

您可以添加一个 pre-receive 钩子来拒绝添加新根提交的新名称,尽管我知道没有这种形式的方便的 pre-receive 钩子。这很容易做到,但速度很慢:运行git rev-list --all --max-parents=0 --count以计算现有根,然后git rev-list --all --max-parents=0 --count <hash>再计算根,如果您接受了来自git push. 如果计数增加,新的推送会添加一个新的根。

请注意,可以添加属于不相交子图的新根。例如,考虑“之前和建议之后”的图表:

before:
A--B--C  <-- master

after:

A--B--C--F--G  <-- master
        /
    D--E

这种预接收钩子会拒绝这样的推送。这可能是您想要的,但可能不是;小心你的程序。:-)


推荐阅读