合并第一次和第二次提交 parents

Merge commit first and second parents

在涵盖相对提交引用的 Udacity 课程中,它说:

^ indicates the parent commit, ~ indicates the first parent commit

The main difference between the ^ and the ~ is when a commit is created from a merge. A merge commit has two parents. With a merge commit, the ^ reference is used to indicate the first parent of the commit while ^2 indicates the second parent. The first parent is the branch you were on when you ran git merge while the second parent is the branch that was merged in.

基于 运行 git log --oneline --graph 在命令行上的以下输出:

使用 SHA f69811c 的提交相对于(最顶层,带有头指针)提交 9ec05ca 是 HEAD~4^2。合并提交 1a56a81 的第二个 parent 是 f69811c,课程解释了这个 "HEAD~4 references the fourth parent commit of the current one and then the ^2 tells us that it’s the second parent of the merge commit (the one that got merged in)."

根据黄色引用的课文,e014d91似乎是1a56a81的第一个parent,因为它是与parent具有相同分支的parent合并提交。

根据这些信息,e6c65a61a56a81 的关系是什么?

我不认为它也是第二个parent(如果可以有多个第一个和第二个parent...),尽管它也是合并到的分支master,因为那个答案在课堂测验中被拒绝了。

此外,如果我说 e014d911a56a81 的第一个 parent 是对的,那么 20975213772ab11a56a81(看起来它们也是第一个 parents,再次,如果合并提交实际上可以有多个 parents)?

e6c65a6 是 1a56a81 的第 2 个父项 (f69811c) 的第 1 个父项。

3772ab1 是 1a56a81(e014d91) 的第一个父代(2097521) 的第一个父代。

一个提交可以有零个父项。这是根提交。

一个commit可以有2个parents,这是将一个分支以"true merge"的方式合并到另一个分支的结果。

一个commit可以有2个以上的parents,这是将2个或多个branch同时合并到一个branch的结果,"octopus merge".

要找出提交 X 的父提交,您可以使用:

git log -1 X --pretty=%P

git log -1 X --pretty=raw

git cat-file -p X

等等

我认为查看图表的一些替代演示文稿可能会有所帮助。

考虑这个非常简单的 diamond-shaped 图,后来的提交画得更高,更早的提交画得更低:

  D
 / \
B   C
 \ /
  A

这里,D 可能是合并提交的 extremely-shortened 哈希 ID,BC 是它的两个输入(两个 parent s,在 Git 的术语中)。 ABC 的(一个,单个)parent。因此,BC 兄弟姐妹, 或者如果 Git 直接使用该概念:它们具有相同的 parent,所以他们一定是兄妹(或两兄弟或两姐妹之类的)。但是 Git 通常不会以这种方式谈论提交——它通常只对直接的 parent/child 关系感兴趣。

我们可以——而且 git log --graph 确实如此——也可以将其绘制为:

*    d...... fourth message
|\
| *  c...... third message
* |  b...... another message
|/
*  a...... some commit message

像人类children一样,Git的"children"可以有多个parent。然而,最典型的情况是只有一个parent,在这种情况下parent是第一个,最后一个,也是唯一的parent。您 可以 给它编号——例如,C^1A——但没有真正的需要。没有 C^2 并且要求它只会让你出错。

在 Whosebug 帖子中,我喜欢在 左侧 右侧 绘制早期提交的图表,就像这样:

  B
 / \
A   D   <-- master (HEAD)
 \ /
  C   <-- develop

这为我提供了插入分支名称的空间,并将单词 HEAD 附加到其中一个,就像 Git 通常那样。但是,这使得很难分辨哪个 parent 是第一个,哪个是第二个。注意上面第一个竖图也有同样的问题

任何 至少 两个 parent 的提交称为 合并提交 。这使用单词 merge 作为形容词,修饰 commit。我们还会经常看到它只是 a merge,使用单词 merge 作为名词。正如 ElpieKay 指出的那样,合并提交可以有两个以上的 parent,但是这些 octopus merges(Git 称它们)不会做任何你能做的事情'只进行成对合并,所以它们大多只是为了炫耀。 :-) 当您确实合并了三个或更多 parent 时,您可以对所有这些进行编号。 Git 本身唯一的特殊区别是 first parent,不过,对各种 Git 命令使用 --first-parent 标志比如git log.

令人困惑的是,git merge 命令不必进行合并提交。它有两个部分,我喜欢将其称为 合并动词 ——合并工作的行为——然后进行提交。它所做的提交是一个合并提交除非你告诉它不要。而且,好像这还不够混乱,几个额外的 Git 命令将 merge 作为动词 部分执行,而不产生 merge commit.因此,请务必牢记 动词 形式 to merge 与名词或形容词形式之间的区别。

还有几点值得注意:

  • 所有提交——所有 Git objects,真的——是 read-only。一旦提交,就永远无法更改,因为它的哈希 ID(可以说是它的真实名称)是由 运行 所有基础数​​据通过哈希函数计算得出的。如果您以某种方式更改提交中的哪怕一点,您将获得一个新的和不同的哈希,从而获得一个新的和不同的提交。

  • 由于child时存在parent或parent,所以child可以记录parent .

  • 但由于在制作parent时其child或children尚不存在,因此parent无法记录其child仁.

  • 正是这些向后的 parent <- child 链接构成了提交图。1

这意味着 Git 的内部链接总是 向后 。 Git必须最后、child-most开始,提交并向后工作。这就是为什么branch names like master 总是指向分支的tip commit。由于 Git 向后工作,一次提交一次,合并 - 有两个或更多 parents - 存在一个问题:Git 只能从 child 返回到 个 parent。 Git 通常解决这个问题的方法是将 parent 中的 所有 放入一个队列,然后处理队列中的第一个提交。

--first-parent 标志告诉 Git 仅将 第一个 parent 放入队列,忽略第二个 parent (如果这是章鱼合并,则还有任何其他 parents)。这允许 Git 遍历提交图,而无需一次处理多个提交。


1在数学上,任何图 G 都由一个集合定义关于顶点 V 和边 E,我们写为 G = (V, E)。图可以是有向,而Git是:顶点到顶点的链接只有一种方式。这样的边称为 arcs。在我们的例子中,我更喜欢将顶点称为 nodes; 这些是实际的提交本身,每个节点都包含一个包含其所有传出弧的列表,即 [=198 的哈希 ID =] 提交。

Git的提交图不仅是有向而且是无环,这意味着如果我们从任何一个开始提交并浏览图表,我们永远不会 return 进行相同的提交。换句话说,没有 parent 可以是它自己的 child。对于 Git 所做的各种图转换,这是一个有用且重要的 属性,因此我们有时将 Git 提交图称为 DAG ,它是 Directed Acyclic Graph 的缩写。提交图或提交 DAG 代表您制作的所有快照。

请注意,每个源快照仅附加到一个提交。图操作不必关心相应快照中的内容:它们只查看图本身!