分支#1包含错误修复C1。分支#2首先 cherry-pick 编辑C1,然后分支#2所有者意识到在C1中完成的工作实际上是错误,所以他提交了正确的修复C2。
在C2中,他基本上删除了C1中的更改,替换为正确的更改。当分支#1所有者想要"提货"修复,合并赢了。因为合并后结果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?虽然没有一个答案提到我的情况。所以问题是,如果我知道有人樱桃选择我的提交,我最好改变他的分支而不是合并?
还有其他方法可以避免(或修复)我在这里提到的问题吗?
------更新-------
我知道樱桃挑选是发生这种情况的原因。但我已经多次面对这种情况,我现在只想要一个特定的提交,我还不想在他们的分支中提交其他提交。所以我不知道是否有更好的方法来做到这一点。
答案 0 :(得分:3)
你基本上是正确的。然而,Rebase并不能解决这个问题。
让我们画一个稍微复杂的情况。我们将从类似的提交图开始:
...--o--o
这里的最终o
节点是某个分支的提示提交,一些早期的提交o
也不是很有区别。我们不会为分支标签而烦恼,因为它们只是为了帮助人类,而我们正在研究 Git 所做的事情,而不是人类所做的事情。 : - )
随着一个人提出新提交,你的提交C1
(我只是称之为C
),其中有一个错误:
C
/
...--o--o
与此同时,在这个回购中,或者在它的其他一些副本中,一个不同的人做了一个无关的提交F
:
C
/
...--o--*
\
F
提交C
和F
可能在不同的(命名)分支上,但重要的是它们有一个共同的基础*
(以前只是标记为{{1}但是现在我们需要记住它是共同的基础 - 尽管从图纸中可以看出它很明显。)
在您的特定情况下,第二位用户通过挑选o
来F
。让我们说,在我们的例子中,第二个用户非常独立地C
。现在第二个用户决定 now 是时候挑选F
,所以他们得到了它的副本 - 但是它并不干净,所以他们稍微改变它 - 编辑它 - 以便它适用。现在他们有:
C
再次注意,提交 C
/
...--o--*
\
F--G
主要是,但不完全,G
的副本 - 正如我们所说,即将被视为有缺陷。
你的第一个人因此将C
恢复为实际上将其从分支中移除,然后添加C
(更正的修正):
D
你的第二个人继续添加更多提交:
C--R--D
/
...--o--*
\
F--G
(这次我也加入了分支名称。)
C--R--D <-- branch1
/
...--o--*
\
F--G--H--I <-- branch2
的作用实质上是找到两个分支之间共同的提交,并且这两个分支都是独占的。你的第二个人将会出现并试图在git rebase
之上重新定位F-G-H-I
序列。
common 提交从合并基础D
开始并向后工作; rebase完全忽略了这些。
要复制的提交在合并基础之后开始,以最末端提交结束,因此是*
,F
,G
和H
。< / p>
但是,在复制这些之前,Git会检查&#34;另一方&#34;提交的提交:在以I
结尾的合并基础*
之后提交。这些是D
(错误提交),C
(R
的还原)和C
。它在每个提交上使用D
,并在所有要复制的提交上使用git patch-id
。如果其中一个&#34;的补丁ID被复制&#34;提交匹配以D
&#34;结尾的链中的一个&#34;的补丁ID;提交,Git drop 提交。
当提交G
是C
的完全(非手动编辑)副本时,Git可以放弃G
并只复制F
,{ {1}}和H
。精确的副本使用相同的补丁ID。但是这个I
是手工编辑的,以使其适合,这改变了它的补丁ID。 Rebase因此复制G
,给出:
G
因此,虽然 C--R--D <-- branch1
/ \
...--o--* F'-G'-H'-I' <-- branch2
\
F--G--H--I [abandoned]
肯定会失败,但git merge
有时也会失败(特别是当必须修改樱桃挑选的提交以适应时)。在这种情况下,发生这种情况是因为git rebase
和樱桃挑选的F
之间存在冲突,但有很多方法可以解决这个问题。
还有其他方法可以避免(或修复)我在这里提到的问题吗?
理想情况下,首先不是挑选C
,而是C
工作的人当时只会重新加入branch2
,然后重新加入C
如果需要,可以稍后再次(或直接进入R
),或者在所述rebase之后合并。如果处理D
的第二个人已经将他的branch2
提交重新定位到F
而不是采摘樱桃,那么让我们看一下图表的样子。让我们画出之前的基础:
C
并将 C <-- branch1
/
...--o--*
\
F <-- branch2
向下移动几行,这是完全相同的提交,只是线性绘制:
C
现在让...--o--*---C <-- branch1
\
F <-- branch2
复制到F
F'
上方并移动分支标签:
C
...--o--*---C <-- branch1
\ \
\ F' <-- branch2
\
F [abandoned]
和C
的合并基础现在是F'
本身,而不是提交C
。让我们将剩余的提交放入,取消标记*
提交并删除放弃的提交:
*
如果我们现在使用...--o--o---C--R--D <-- branch1
\
F'-H--I <-- branch2
在提交git merge
上合并提交I
,我们将不会通过D
重新引入错误提交C
,因为现在不是G
。
当然,如果多人正在使用G
- 如果旧的branch2
提交已发布 - 这个rebase-make-a-copy就意味着他们必须所有切换每次我们改变时使用新副本。
还有其他方法可以避免(或修复)我在这里提到的问题吗?
理想情况下,当有人发现错误时,在编写提交F
之前,他们会编写一个测试用例。测试用例显示需要提交C
并且提交C
修复了错误,这就是为什么提交C
的原因。
当C
被发现有问题时,应该改进它的测试用例,或者编写一个额外的测试用例,证明提交C
不太正确。这也是恢复C
进入的原因,以及随后更好的修复R
。 (或许D
本质上是D
的壁球和替换修复 - 尽管R
被复制的事实表明C
应该作为一个单独存在反转。)
如果rebase或merge重新引入了一个轻微的commit R
变体,那么这些测试现在显示问题,例如我们的假设提交C
。这不会避免或解决问题本身,但至少会立即抓住。
答案 1 :(得分:1)
所以问题是,如果我知道某人樱桃选择我的提交我更好地改变他的分支而不是合并?
是的,因为正如我之前所说,樱桃选择重复分支之间的提交。由于rebase跳过重复提交,这是一个很好的出路 有关具体说明,请参阅“Git cherry pick and datamodel integrity”。
还有其他方法可以避免(或修复)我在这里提到的问题吗?
如果你打算最终合并两个分支,那么两者之间应该没有挑选。只合并或改组。
只有在不应该合并分支的情况下,挑选错误修复才是好主意。