git-merge 和 git-rebase 如何处理从另一个主题分支中分叉出来的主题分支?
How git-merge and git-rebase behave with a topic branch forked off of another topic branch?
让我们假设存储库如下所示:
master
|
A--B--C--K--L--M
\
E-F-G
|\
| H-I-J
topicA |
topicB
git merge topicB
在 master
时会做什么?它会仅应用提交 H
、I
和 J
吗?或 topicA
?
初始分叉点的所有提交
git rebase master topicA
会做什么?会不会导致这样:
master
|
A--B--C--K--L--M
\
E'-F'-G'
|\
| H'-I'-J'
topicA |
topicB
或类似的东西?
master
|
A--B--C--K--L--M
\ \
\ E'-F'-G'
\ |
\ topicA
\
E-F-G-H-I-J
|
topicB
除了将分支名称解析为提交 ID 之外,merge 和 rebase 都不关心任何分支标签指向的位置。他们只看提交图。
合并
我认为 git merge
的工作方式更容易理解。除了为默认合并提交消息保存分支名称外,git merge
仅使用 git rev-parse
查找提交 ID。
对于您的第一个问题,假设 git merge topicB
在分支 master
上,git 找到三个提交 ID:
HEAD
解析为提交 M
(分支 master
上的最尖端提交;这也是您在 运行 [=18 时看到的提交 ID =]).
topicB
解析为提交 J
(分支 topicB
上的最尖端提交;这也是您在 运行 [=22 时看到的提交 ID =]).
- 这两个提交的合并基础,(仅)由提交图确定。在这种情况下,即提交
C
.
找到合并基础后,git 然后 运行 两个 git diff
:合并基础和 HEAD
之间的一个提交(看看 "your branch" 做了),合并基础和 topicB
之间的一个提交(看看 "their branch" 做了什么)。它结合了两组更改,如果没有冲突(或者如果它可以通过 git rerere
解决它们),则创建一个新提交,其两个父项是 - 按此顺序 - 当前 HEAD
提交,以及 topicB
提交。使新提交像往常一样更新当前分支,因此 HEAD
现在指向新提交(或更准确地说,HEAD
仍然指向 master
,并且 master
指向到新的提交)。
本题:
Would it apply commits H, I, and J only? or all the commits from the initial fork point of topicA?
暗示了对 git 合并算法的根本误解:git 没有查看 任何 中间提交。它只查看此处标识的三个提交:在本例中为 M
、J
和 C
。 Git 可以 "cheat" 像这样,因为就提交的源代码树内容而言,每个提交都是完全独立的。
变基
Rebase 更复杂(你在上面的第二个猜测中是正确的,但让我们 运行 无论如何)。
它仍然使用相同的 "resolve branch names to IDs via git rev-parse
" 技巧,但它使用了更复杂的 git rev-list
变体。 git rev-list
所做的是在提交图上进行计算集操作(一旦真正进入图本身,再次忽略任何标签)。
给定 git rebase master topicA
,我们(嗯,我 无论如何 :-))首先必须检查 the rebase
documentation,我们发现 master
被视为 参数, topicA
被视为 参数,并且它所做的一切最后一个参数是 运行 git checkout <em><branch></em>
首先,然后继续,就好像你没有提供它一样。所以在心理上我们可以假设你开始做 git checkout topicA
,然后 运行 git rebase master
。 参数是 master
并且 HEAD
指的是分支 topicA
所以它标识提交 G
.
在过去,在 git rebase
拥有 --fork-point
之前,更容易确定接下来会发生什么。今天,根据文档,您必须确定 --fork-point
是否已启用。因为您 did 给出了一个实际的 参数,所以默认情况下它是禁用的:只有当您在命令中给出 --fork-point
时它才会启用线;你没有;因此我们不需要深入研究 --fork-point
的奥秘(哇,耶!:-))。所以,这里发生的是 rebase 本质上是 运行s git rev-list master..HEAD
来获取要复制的提交列表。这列出了提交 E
、F
和 G
,因为这些是 HEAD
可访问但 master
.[=72= 无法访问的集合中的提交]
rebase 进程然后在 --onto
参数给出的提交(如果有)或 < 给出的提交处分离 HEAD
上游> 参数。那是 master
所以这是提交 M
。从这里开始,rebase 本质上只是挑选它之前找到的每个提交,这会为您提供提交 E'
、F'
和 G'
。如果所有这些都正常工作,git 的 rebase 的最后一步是将最初的当前分支(topicA
,再次)指向最后一次提交,G'
。其他标签没有变化,所以你得到你画的最终图表。
让我们假设存储库如下所示:
master
|
A--B--C--K--L--M
\
E-F-G
|\
| H-I-J
topicA |
topicB
git merge topicB
在 master
时会做什么?它会仅应用提交 H
、I
和 J
吗?或 topicA
?
git rebase master topicA
会做什么?会不会导致这样:
master
|
A--B--C--K--L--M
\
E'-F'-G'
|\
| H'-I'-J'
topicA |
topicB
或类似的东西?
master
|
A--B--C--K--L--M
\ \
\ E'-F'-G'
\ |
\ topicA
\
E-F-G-H-I-J
|
topicB
除了将分支名称解析为提交 ID 之外,merge 和 rebase 都不关心任何分支标签指向的位置。他们只看提交图。
合并
我认为 git merge
的工作方式更容易理解。除了为默认合并提交消息保存分支名称外,git merge
仅使用 git rev-parse
查找提交 ID。
对于您的第一个问题,假设 git merge topicB
在分支 master
上,git 找到三个提交 ID:
HEAD
解析为提交M
(分支master
上的最尖端提交;这也是您在 运行 [=18 时看到的提交 ID =]).topicB
解析为提交J
(分支topicB
上的最尖端提交;这也是您在 运行 [=22 时看到的提交 ID =]).- 这两个提交的合并基础,(仅)由提交图确定。在这种情况下,即提交
C
.
找到合并基础后,git 然后 运行 两个 git diff
:合并基础和 HEAD
之间的一个提交(看看 "your branch" 做了),合并基础和 topicB
之间的一个提交(看看 "their branch" 做了什么)。它结合了两组更改,如果没有冲突(或者如果它可以通过 git rerere
解决它们),则创建一个新提交,其两个父项是 - 按此顺序 - 当前 HEAD
提交,以及 topicB
提交。使新提交像往常一样更新当前分支,因此 HEAD
现在指向新提交(或更准确地说,HEAD
仍然指向 master
,并且 master
指向到新的提交)。
本题:
Would it apply commits H, I, and J only? or all the commits from the initial fork point of topicA?
暗示了对 git 合并算法的根本误解:git 没有查看 任何 中间提交。它只查看此处标识的三个提交:在本例中为 M
、J
和 C
。 Git 可以 "cheat" 像这样,因为就提交的源代码树内容而言,每个提交都是完全独立的。
变基
Rebase 更复杂(你在上面的第二个猜测中是正确的,但让我们 运行 无论如何)。
它仍然使用相同的 "resolve branch names to IDs via git rev-parse
" 技巧,但它使用了更复杂的 git rev-list
变体。 git rev-list
所做的是在提交图上进行计算集操作(一旦真正进入图本身,再次忽略任何标签)。
给定 git rebase master topicA
,我们(嗯,我 无论如何 :-))首先必须检查 the rebase
documentation,我们发现 master
被视为 topicA
被视为 git checkout <em><branch></em>
首先,然后继续,就好像你没有提供它一样。所以在心理上我们可以假设你开始做 git checkout topicA
,然后 运行 git rebase master
。 master
并且 HEAD
指的是分支 topicA
所以它标识提交 G
.
在过去,在 git rebase
拥有 --fork-point
之前,更容易确定接下来会发生什么。今天,根据文档,您必须确定 --fork-point
是否已启用。因为您 did 给出了一个实际的 --fork-point
时它才会启用线;你没有;因此我们不需要深入研究 --fork-point
的奥秘(哇,耶!:-))。所以,这里发生的是 rebase 本质上是 运行s git rev-list master..HEAD
来获取要复制的提交列表。这列出了提交 E
、F
和 G
,因为这些是 HEAD
可访问但 master
.[=72= 无法访问的集合中的提交]
rebase 进程然后在 --onto
参数给出的提交(如果有)或 < 给出的提交处分离 HEAD
上游> 参数。那是 master
所以这是提交 M
。从这里开始,rebase 本质上只是挑选它之前找到的每个提交,这会为您提供提交 E'
、F'
和 G'
。如果所有这些都正常工作,git 的 rebase 的最后一步是将最初的当前分支(topicA
,再次)指向最后一次提交,G'
。其他标签没有变化,所以你得到你画的最终图表。