首页 > 解决方案 > 合并提交第一和第二父母

问题描述

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

^ 表示父提交,~ 表示第一个父提交

^ 和 ~ 之间的主要区别在于从合并创建提交的时间。合并提交有两个父级。对于合并提交,^ 引用用于指示提交的第一个父级,而 ^2 指示第二个父级。第一个父级是您运行 git merge 时所在的分支,而第二个父级是合并的分支。

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

图形

使用 SHA 的提交f69811c是 HEAD~4^2 相对于(最上面,带有头指针)提交9ec05ca。合并提交1a56a81的第二个父级是f69811c,本课解释了这个“HEAD~4 引用当前提交的第四个父级提交,然后 ^2 告诉我们它是合并提交的第二个父级(合并的那个) )。”

根据以黄色引用的课文,它似乎e014d911a56a81的第一个父级,因为它是与合并提交具有相同分支的父级。

根据这些信息,e6c65a6的关系1a56a81是什么?

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

另外,如果我是对的那e014d911a56a81' 的第一个父母,那么20975213772ab1to的关系是什么1a56a81(如果合并提交实际上可以有多个父母,他们似乎也是第一个父母)?

标签: gitmergegit-log

解决方案


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

考虑这个非常简单的菱形图,后面的提交绘制得更高,而早期的提交绘制得更低:

  D
 / \
B   C
 \ /
  A

在这里,D可能是合并提交的极其缩短的哈希 ID,其中BC是它的两个输入(在 Git 的术语中是两个父级)。 是和A的(一个,单)父母。结果,并且是兄弟姐妹,或者如果 Git 直接使用该概念,则为兄弟姐妹:两者具有相同的父级,因此它们必须是兄弟姐妹(或两个兄弟或两个姐妹或其他)。但 Git 通常不会以这种方式谈论提交——它通常只对直接的父/子关系感兴趣。BCBC

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

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

像人类孩子一样,Git 的“孩子”可以有多个父母。然而,最典型的情况是只有一个父母,在这种情况下,父母是第一个、最后一个也是唯一的父母。你可以给它编号——例如,<code>C^1 是A——但没有真正的需要。没有C^2,要求它只会给你一个错误。

在 StackOverflow 的帖子中,我喜欢在左侧绘制早期提交的图表,在右侧绘制后期提交的图表,如下所示:

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

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

任何具有至少两个父级的提交称为合并提交。这使用单词merge作为形容词,修饰commit。我们也经常看到它只是一个 merge,使用单词merge作为名词。正如 ElpieKay 所指出的,一个合并提交可以有两个以上的父级,但是这些章鱼合并(正如 Git 所说的那样)不会做任何你不能做的事情,所以它们主要是为了炫耀。:-) 当您确实与三个或更多父母合并时,您可以为所有父母编号。不过,Git 本身唯一的特殊区别是对于第一个父级,使用--first-parent标记各种 Git 命令,例如git log.

令人困惑的是,该git merge命令不必进行合并提交。它有两个部分,我喜欢将它们称为动词——合并工作的行为——然后进行提交。它所做的提交是合并提交,除非你告诉它不要。而且,好像这还不够令人困惑,几个额外的 Git 命令将合并作为动词部分进行,而不产生合并提交。所以重要的是要记住动词形式、合并和名词或形容词形式之间的区别。

值得注意的还有一些项目:

  • 所有的提交——所有的 Git 对象,真的——都是只读的。一旦提交,就永远无法更改,因为它的哈希 ID——它的真实名称——是通过哈希函数运行其所有底层数据来计算的。如果您以某种方式更改提交中的任何一点,您将获得一个新的和不同的哈希,因此是一个新的和不同的提交。

  • 由于制作孩子时父母存在,孩子可以记录父母。

  • 但是由于在创建父级时它的一个或多个子级还不存在,所以父级不能记录它的子级。

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

这意味着 Git 的内部链接总是向后的。Git必须最后一个、child-most、commit 和向后工作开始。这就是为什么分支名称总是master指向分支的尖端提交。当 Git 向后工作时,一次提交一次,合并(有两个或多个父级)提出了一个问题:Git 只能从子级移回父级之一。Git 对这个问题的通常解决方案是将所有父级放入队列中,然后处理队列中的第一个提交。

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


1Mathematically, any graph G is defined by a collection of vertices V and edges E, which we write as G = (V, E). A graph can be directed, and Git's is: the links from vertex to vertex go only one way. Such edges are called arcs. In our case I prefer to call the vertices nodes; these are the actual commits themselves, and each node contains a list of all its outgoing arcs, i.e., the hash IDs of the parent commits.

Git 的提交图不仅是有的,而且是无环的,这意味着如果我们从任何一个提交开始,并遍历该图,我们将永远不会返回到同一个提交。换句话说,没有父母可以是自己的孩子。对于 Git 所做的各种图形转换,这是一个有用且重要的属性,因此我们有时将 Git 提交图称为DAG,它是Directed Acyclic Graph的缩写。提交图或提交 DAG 表示您曾经制作的所有快照。

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


推荐阅读