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

Git:删除两个父提交中的一个

  •  0
  • Bananach  · 技术社区  · 8 年前

    我有以下提交历史记录:

      A--B--C branch1
     /     /
    I      D
     \
      \
       J branch2
    

    我想简单地删除commit D,它没有连接到其他地方。

    我试过了 git rebase --onto B C ,但由于某种原因,这也会删除C并生成

      A--B branch1
     /     
    I      
     \
      \
       J branch2
    
    1 回复  |  直到 8 年前
        1
  •  2
  •   Community Mohan Dere    6 年前

    TL;DR公司

    将C的哈希ID保存在某处。除了使用原始剪切和粘贴的哈希ID之外,最简单的方法是创建临时分支或标记名,例如:

    git branch save branch1
    

    然后强制输入名称 branch1 指向提交 B 。假设你还在 分支1 确保你也没有什么可承诺的;使用 git status 要检查这两项,请执行以下操作:

    git reset --hard <hash-of-B>
    

    或类似。如果 分支1 仍需提交点 C 您可以使用 HEAD~1 或 HEAD^1 例如,识别提交B。

    最后,使用 git cherry-pick 制作 复制 的提交 C ,但此副本中只有一个父级。要这样做,你必须说出父母中的哪一个 吉特樱桃采摘 应将其视为 C 的(单个)父级进行复制。要选择的父级为#1;无论如何,这几乎总是正确的;在这种情况下确实如此 C 是通过合并生成的 D :

    git cherry-pick -m 1 save
    

    然后可以删除该名称 save 在任何时候(只要记住提交的哈希ID就可以了 C )。

    请注意,您 能够 择优挑选 D 直接地但是,使用 git cherry-pick -m 1 <thing-to-locate-commit-C> 这意味着,如果在进行合并时必须解决任何冲突,则现在不必重新解决它们。

    长的

    根据注释,实际的图形结构如下:

           A--B--C   <-- branch1 (HEAD)
          /     /
    ...--I     D
          \   /
           \ /
            J   <-- branch2
    

    但这对答案没有影响。

    我试过了 git rebase --onto B C ,但由于某些原因,这也会删除C。。。

    从技术上来说 删去 犯罪 C 完全只是为了名字 分支1 指向提交 B (仅限)。也就是说,我们可以像这样重新绘制图表:

           A---B   <-- branch1 [after rebase]
          /     \
    ...--I       C   <-- branch1 [before rebase]
          \     /
           \   D
            \ /
             J   <-- branch2
    

    原因是:

    git rebase --onto B C
    

    告诉Git:

    1. 列出可从中访问的所有提交 HEAD (使用名称 头部 内置,属于 git rebase 无法从 C (根据你的论点 C ),减去一些我们稍后可以描述的承诺。按“反向拓扑”顺序列出它们,即旧的必需提交优先于新的提交。

      在这种情况下,列表为空:没有从开始的提交 头部 -附于 分支1 具有 分支1 指向提交 C 无法从提交访问的 C 也

    2. 设置指向的临时分支 B (根据你的论点 B )。

    3. 对于步骤1中列出的所有提交,复制这些提交,建立这个新的临时分支。

    4. 完成后,更改名称 分支1 指向复制的最终提交。

      由于没有复制提交,临时分支仍然指向 B ,因此名称 分支1 移动到指向 B 。

    您可以尝试通过命名其他名称来解决此问题 C “不可复制的内容”。问题是你只能 一 提交“不复制的内容”的ID。您可以指定 B ,但Git将尝试复制 D 和 J (按其他顺序: D 首先,然后 J )。您可以指定 D ,但Git将尝试复制 B ,则, A 和 I 哦!

    剩下的另一个问题是,我们在上面提到,在步骤1中,Git省略了某些提交。具体而言,它省略了 全部的 合并提交它不会尝试复制 C 总之,它还省略了已经在上游的提交(通过一种机制,我在这里不尝试描述;它不适用于您的情况,所以这并不重要)。这意味着即使你可以说“复制” C 但不要复制 B 或 D “,Git会扔 C 不在列表中,并且 还 根本不复制任何内容。

    这就是为什么我们需要一种替代机制:使用 吉特樱桃采摘 。我们可以摘樱桃 D 它本身这意味着重新进行任何冲突解决。或者,我们可以使用 git cherry-pick -m 1 <specifier-for-C> 樱桃采摘 效应 合并的 D 做任何冲突解决,这就是TL中的方法;以上DR部分。