代码之家  ›  专栏  ›  技术社区  ›  Rory

Git完全删除未合并的根节点

git
  •  0
  • Rory  · 技术社区  · 6 年前

    我们有两个回购协议,回购1和回购2。看起来“有人”把Repo2推到了Repo1/origin,现在Repo1同时包含了Repo1和Repo2。有两个独立的根节点,没有合并任何内容(幸运的是)。Repo1也被来自Repo2的一大堆标记/分支所污染。

    如何用一个特定的祖先(Repo2的根节点,也就是“Initial commit”)来完全攻击任何提交(及其标记/分支)?或者是一种不同的/更简单的方式来拼接这两个回购协议?

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

    0 回复  |  直到 6 年前
        1
  •  1
  •   torek    6 年前

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

         C--D
        /    \
    A--B      G--H   <-- branch1
        \    /
         E--F   <-- branch2
    
    I--J--K--L   <-- master
           \
            M--N   <-- develop
    

    这个特殊的图有四个“入口点”,即提交 F H 在根于的子图中 A 并承诺 L N 在根于的子图中 I .

    虽然这样一个存储库没有本质上的错误,也没有被破坏,但是将它拆分成两个独立的存储库相对容易。简单地从两个克隆开始,它们都是这样的。(您可以使用 git clone --mirror 以生成保留所有引用的镜像克隆。一定要删除他们的 origin 这样它们就不会同时指向原始的组合存储库。)

    在一个这样的克隆中,如果存在任何其他引用,则删除任何外部标记分支名称、标签名和其他引用。 两个子图中:

         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 任何一个 这些分裂的克隆体。因此,任何第三个完全独立的存储库都可以 用于推送到这两个克隆体中的任何一个。为独立子图中的提交添加名称的任何推送都将导致整个独立子图进入推送的接收者。 As you surmise in a comment ,我怀疑这就是这种情况最初的起因。

    您可以添加一个pre-receive钩子,该钩子拒绝添加新根提交的新名称,尽管据我所知,此表单没有方便的pre-receive钩子。做起来容易,但速度慢:快跑 git rev-list --all --max-parents=0 --count 计算现有根和第二根 git rev-list --all --max-parents=0 --count <hash> 若接受来自 git推送 . 如果计数增加,新的推送将添加一个新的根。

    注意,可以添加一个新的根 不相交子图的一部分。例如,考虑“before and proposed after”图:

    before:
    A--B--C  <-- master
    
    after:
    
    A--B--C--F--G  <-- master
            /
        D--E
    

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

        2
  •  0
  •   Rory    6 年前

    把这个贴在TLDR人群上,都归功于torek的出色回答。

    我运行了这个程序(在WindowsMingW64 GitCLI上,引用/转义在其他终端上可能有所不同),查看了它,然后运行了它输出的所有命令。

    git for-each-ref --contains <root_commit_hash> --format="%(refname:short)" refs/tags | xargs -I % echo git tag -d % \&\& git push --delete origin %
    

    加一些 git gc 和/或 git prune 好办法。