如何在git中解释合并提交?

How to interpret a merge commit in git?

这是提交历史记录的示例,其中添加了一些文件(abc):

1
2
3
4
* 51c3dd7 (HEAD -> master) add c
| * ded49bb (my_branch) add b
|/
* f3cea38 add a

假设我使用git merge --no-commit --no-ff my_branchmy_branch开始到当前分支(HEAD)的合并,例如在这里。

现在,当处于MERGING状态时,我先取消所有内容的分级,然后再按git commit完成合并。

因此,即使HEAD实际上没有变化,我仍然会得到合并提交:

1
2
3
4
5
6
*   5990dce (HEAD -> master) Merge branch 'my_branch'
|\\
| * ded49bb (my_branch) add b
* | 51c3dd7 add c
|/
* f3cea38 a

我觉得这很混乱。合并不应该自动中止吗?

当然这是一个简单的例子,但是在"选择性"合并之后可能会出现类似的混乱,例如,如前所述。在这里。

应该将合并提交解释为:"此分支中可能添加或未添加某些东西?"


I find this quite confusing.

许多人经常谈论Git。 :-)

在我进一步介绍之前,请先记下此术语。此处的单词merge既可以是形容词(一个合并提交),也可以是动词:合并另一个提交。当合并提交缩短为仅合并时,形容词形式可以成为名词。区分合并动词和合并提交的形容词(或名词)非常重要。合并提交只是具有两个或多个父提交的任何提交。通常,我们使用git merge命令启动"动词合并"过程,此过程以制作这些"动词合并"中的一项结束。

(git merge命令可以被告知做一个动词的合并而不做名词的合并。Git中还有更多的命令也可以做一个动词的合并进行名词合并。不过,我们无需为此担心。)

Shouldn't the merge automatically abort?

否:Git假设您的意思是您所说的。

您说过:开始合并过程,但即使看起来可行,也不要结束。让我大惊小怪的是索引,然后通过运行git commit .1自己完成合并。要理解这一点,您需要了解索引的角色,也称为暂存区,有时也称为缓存。 >

简而言之,索引就是构建下一个提交的地方。无论下一次提交是否将成为合并提交,都是如此。对于普通的非合并提交,索引仅保存每个文件的副本:它从当前提交的每个文件的副本开始,然后使用git add覆盖其中的一些副本,以将工作树版本复制到索引中,替换旧的当前提交版本。如果您git add全新文件,则所执行的操作是相同的,只是该文件是新文件而不是替换文件。如果git rm一个文件(带有或不带有--cached),则要从索引中删除该文件,以便该文件不会在下一次提交中。

在动词合并过程中,索引至少在合并冲突的情况下扩展了很多作用。在这里,索引结束时将保存(最多)每个文件的三个副本。这些用"较高级"数字编号。阶段插槽零用于普通文件;插槽1-3用于合并冲突状态文件。尽管这些较高的阶段号中有任何条目,但是不能使用索引进行提交。 Git强制您首先解决冲突。

现在git add具有一个新功能:告诉Git它应该剔除该文件的三个更高阶段的版本,并将正常的阶段零条目写入索引。您在这里没有执行任何操作,但是值得一提。

请注意,git rm只是删除文件,而不考虑阶段插槽号。同时,git resetgit add一样,获得了一个新功能:git reset像往常一样,将当前提交中的文件复制到Stage-Slot零,但是像git add一样,如果出现以下情况,它也会删除更高的Stage-Slot条目:他们存在。如果当前提交中不存在该文件,则git reset从索引中删除该文件。

(实际上,git addgit reset总是会做额外的工作。当没有更高的阶段编号时,它什么都不做。如果文件foo.txt仅存在于阶段0作为:0:foo.txt,则删除:1:foo.txt-同一文件的stage-slot-1条目-无效。)

无论如何,在此合并过程结束时,索引具有将要进入新合并提交的所有文件,就像它具有将要进入常规非合并提交的所有文件一样。最后的git commit步骤会注意到git merge剩下的信息-具体来说,用于使新提交成为合并提交的额外父级(在.git/MERGE_HEAD中)。因此,git commit会像使用任何提交一样使用索引内容进行新的提交,但要设置至少一个额外的父对象,如该MERGE_HEAD文件中记录的那样。重要的是索引处于可提交状态。无论其内容是什么,这些文件都将成为提交中的文件。您在git merge --no-commitgit commit之间运行的每个(Git)命令的真正最终目的都是对索引中的文件进行一些修改。

1在相对较新的Git版本中,您可以运行git merge --continue来让git merge为您运行git commit。如果打算停止合并,请使用git merge --abort,这是非常古老的git reset --merge的同义词。 (每个人都应该有足够新的Git来使用git merge --abort;一旦git merge --continue很常见,我建议在这里也使用它。)