变基是修复错误选择的唯一方法吗?
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
提交 C
和 F
可能在不同的(命名的)分支上,但重要的是它们有一个共同的基础 *
(这曾经被标记为 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 可以完全忽略这些。
要复制的提交在合并基础之后开始并以最尖端的提交结束,因此是 F
、G
、H
和 I
.
但是,在复制这些之前,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 该提交。
这就是当提交 G
是 C
的精确(非手工编辑)副本时,Git 可以删除 G
并仅复制 F
、H
和 I
。完全相同的副本最终具有相同的补丁 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]
C
和 F'
的合并基础现在是 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
.这不会避免或解决问题本身,但至少会 立即发现。
分支 #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
提交 C
和 F
可能在不同的(命名的)分支上,但重要的是它们有一个共同的基础 *
(这曾经被标记为 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 可以完全忽略这些。
要复制的提交在合并基础之后开始并以最尖端的提交结束,因此是 F
、G
、H
和 I
.
但是,在复制这些之前,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 该提交。
这就是当提交 G
是 C
的精确(非手工编辑)副本时,Git 可以删除 G
并仅复制 F
、H
和 I
。完全相同的副本最终具有相同的补丁 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]
C
和 F'
的合并基础现在是 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
.这不会避免或解决问题本身,但至少会 立即发现。