变基是修复错误选择的唯一方法吗?

Does a rebase the only way to fix a wrong cherry-pick?

分支 #1 包含错误修复 C1。分支 #2 首先 cherry-picked C1,然后分支 #2 所有者意识到在 C1 中完成的工作实际上是 错误的,所以他承诺正确修复 C2。

在C2中他基本上去掉了C1中的改动,取而代之的是正确的改动。当分支 #1 所有者想要 "pick-up" 修复时,合并 将不起作用 。因为合并后结果 C3 将包含 C1 和 C2 中引入的正确修复,即 C1 将通过合并保留。

Because the branch #2 now does NOT contain C1 codes at all so merge won't work.

base -- C1'-- C2'(remove C1' change) -- C2''(add the correct fix) <--branch #2


     /---- C1---------------C3 (merge won't do b/c C1 will be kept)  <-- branch #1
base                       /
     \---- C1' ---- C2----/  <--branch #2

Rebase 可以,因为它会丢弃 C1,"a patch already accepted upstream with a different commit message or timestamp will be skipped".

      /---- C1------ xxxxx  ----C3 (Rebase will discard C1) <-- branch #1
base                       /
      \---- C1' ---- C2---/  <--branch #2

我的问题实际上与此有关,When do you use git rebase instead of git merge? 虽然 none 的答案提到了我的情况。所以问题是,如果我知道有人挑选我的提交,我最好重新设置他的分支而不是合并?

有没有其他方法可以避免(或修复)我在这里提到的问题?

------更新------

我知道这是发生这种情况的原因。但是我已经多次遇到这种情况,我现在只想要一个特定的提交,我还不想在他们的分支中进行其他提交。所以不知道有没有更好的办法。

So the question is if I know someone cherry-pick my commit I better rebase his branch instead of merge ?

是的,因为正如我之前所说,cherry-pick 会复制分支之间的提交。由于变基跳过重复提交,这是一个很好的出路。
具体说明参见“Git cherry pick and datamodel integrity”。

Is there other way to avoid (or fix) the problem I mentioned here ?

如果您打算最终合并两个分支,则不应在两者之间进行挑选。仅合并或变基。

仅当不应合并分支时,择优修复错误才是好主意。

你基本上是正确的。然而,Rebase 并不是解决问题的万能药。

让我们画一个稍微复杂一点的情况。我们将从类似的提交图开始:

...--o--o

这里最后的o节点是某个分支的tip commit,和一些更早的commit o也不是很区分。我们不会理会分支标签,因为它们只是为了帮助人类,我们正在研究 Git 做了什么,而不是人类做了什么。 :-)

有人做了一个新的提交,你的提交 C1(不过我就叫它 C)有一个错误:

          C
         /
...--o--o

与此同时,在这个 repo 或它的其他克隆中,出现了一个不同的人进行了不相关的提交 F:

          C
         /
...--o--*
         \
          F

提交 CF 可能在不同的(命名的)分支上,但重要的是它们有一个共同的基础 *(这曾经被标记为 o 但现在我们需要记住它是公共基础——尽管从图中可以看出它很明显)。

在您的特定情况下,第二个用户通过挑选 C 赚取了 F。假设在我们的例子中,第二个用户完全独立地创建了 F。现在第二个用户决定 现在 是挑选 C 的时候了,所以他们得到了它的副本——但它并没有完全适用,所以他们稍微改变了它- 手动编辑它 - 以便它适用。现在他们有:

          C
         /
...--o--*
         \
          F--G

再次注意,提交 G 大部分 ,但 不完全是 ,[=24= 的副本]——正如我们所指出的,这将被视为有缺陷。

因此,您的第一个人类将 C 还原为实际上将其从他的分支中删除,然后添加 D(更正后的修复):

          C--R--D
         /
...--o--*
         \
          F--G

您的第二个人继续添加更多提交:

          C--R--D       <-- branch1
         /
...--o--*
         \
          F--G--H--I    <-- branch2

(这次我也输入了分支名称)。

什么时候变基有效,什么时候失败

本质上,git rebase 所做的是找到两个分支之间共有的提交,以及两个分支中每个分支所独有的提交。你的第二个人会出现并尝试将 F-G-H-I 序列重新定位到 D.

之上

common 提交从合并基础 * 开始并向后工作; rebase 可以完全忽略这些。

要复制的提交在合并基础之后开始并以最尖端的提交结束,因此是 FGHI .

但是,在复制这些之前,Git 检查 "other side" 独有的提交:在以 D 结尾的合并基础 * 之后提交。它们是 C(错误提交)、[=​​49=](C 的还原)和 D。它在每个提交上使用 git patch-id ,也在所有要复制的提交上使用。如果 "to be copied" 提交之一的补丁 ID 与 "already in the chain ending with D" 提交之一的补丁 ID 匹配,Git drops 该提交。

这就是当提交 GC 的精确(非手工编辑)副本时,Git 可以删除 G 并仅复制 FHI。完全相同的副本最终具有相同的补丁 ID。但是这个 G 是手工编辑的以使其适合,这改变了它的补丁 ID。 Rebase 因此复制 G,给出:

          C--R--D               <-- branch1
         /       \
...--o--*         F'-G'-H'-I'   <-- branch2
         \
          F--G--H--I            [abandoned]

因此,虽然 git merge 肯定会失败,但 git rebase 有时也会失败(特别是当必须修改精心挑选的提交以适应时)。在这种情况下,这是由于 F 和精心挑选的 C 之间的冲突而发生的,但是有很多方法可以 运行 进入这个。

Is there other way to avoid (or fix) the problem I mentioned here?

理想情况下,无论谁在 branch2 上工作,与其一开始就挑选 C,不如在那个时候变基到 C,然后再变基到 R 稍后如果需要(或直接进入 D),或在所述 rebase 之后合并。让我们看看如果第二个人在 branch2 上工作,将他的 F 提交重新定位到 C 而不是 cherry-picking,那么图表会是什么样子。让我们画出 before-rebase:

          C    <-- branch1
         /
...--o--*
         \
          F    <-- branch2

并向下移动 C 几行,这与提交完全相同,只是更线性地绘制:

...--o--*---C   <-- branch1
         \
          F     <-- branch2

现在让我们将 F 复制到 C 顶部的 F' 并移动分支标签:

...--o--*---C      <-- branch1
         \   \
          \   F'   <-- branch2
           \
            F      [abandoned]

CF' 的合并基础现在是 C 本身,而不是提交 *。让我们放入剩余的提交,取消标记 * 提交并删除放弃的提交:

...--o--o---C--R--D     <-- branch1
             \
              F'-H--I   <-- branch2

如果我们现在使用 git merge 将提交 I 合并到提交 D 之上,我们将不会通过 G 重新引入错误的提交 C , 因为现在 没有 G.

当然,如果多人使用 branch2——如果发布了旧的 F 提交——这个 rebase-makes-a-copy 意味着他们必须 all 每次变基时切换到使用新副本。

测试

Is there other way to avoid (or fix) the problem I mentioned here?

理想情况下,当有人发现错误时,在提交 C 之前,他们会编写一个测试用例。测试用例显示提交 C 是必需的,提交 C 修复了错误,这就是为什么首先提交提交 C 的原因。

C被发现错误时,应该改进它的测试用例,或者写一个额外的测试用例,证明commit C不太正确。这也是为什么 revert R 进入,随后更好的修复 D。 (也许 D 本质上是 R 的压缩和替换修复——尽管 C 被复制的事实表明 R 应该作为一个独立的存在回归。)

这些测试现在将显示问题 如果变基或合并重新引入提交的细微变化 C,例如我们假设的提交 G.这不会避免或解决问题本身,但至少会 立即发现