为什么Git合并没有冲突时仍会生成合并提交?

6

我正在努力理解为什么 Git 会为两个没有冲突的分支合并产生一个具有唯一 SHA1 的提交记录。将“没有冲突”的信息存储起来,是否真的值得在版本历史中增加额外的混乱呢?

3个回答

9
简短的回答是,提交记录了您工作目录的特定状态,合并将创建一个与任何一个父级不匹配的状态,因此需要一个新的提交,而不仅仅是移动分支指针。更详细地说,了解合并两个分支的确切含义会有所帮助。
最常见的两种使用git merge的方法是a)将本地分支与远程分支同步,b)实际上将两个单独的分支合并在一起。
在第一种情况下,您有以下内容:
A--B--C--D--E--F--G
      ^           ^
      |           +-- origin/master
      +-- master

在这种情况下,git merge origin/master只是将master指针移动到新的位置。这就是所谓的“快进合并”,通常不会导致新的提交(尽管如果您有理由可以明确请求一个提交)。
另一方面,如果您有像这样的东西:
A--B--C--D--E--F--G <-- branch1
      \
       J--K--L--M--N <-- branch2

如果你正在分支branch2上,并且想合并到branch1,那么简单地移动你的branch2指针是没有意义的。如果你只是将branch2移动到指向G,那么你将失去在JN之间所做的更改。在这种情况下,git merge必须创建一个新的提交,其中结果工作树看起来像你复制了CGN的合并基础,可以通过运行git merge-base branch1 branch2查看),然后应用了DGJN中的所有更改。即使这不会导致冲突(两个分支修改了不同的文件集或者至少是文件的不同区域),结果的工作目录状态也不与GN匹配,因此需要创建一个新的提交。因此,git将创建一个新的提交,其中包含从两个分支应用更改的工作目录,并将标记该提交具有GN作为父项。
因此,它实际上并没有“存储没有冲突的信息”,而是存储了您项目的新唯一状态。当然,你可以用git loggit diff等查看那个提交,并查看是否有冲突,但这不是创建新提交的原因。

这个解释对我来说最有意义。 (抱歉,@SLaks,我也喜欢你的解释,但这更接近于回答我的推理线。)我试图弄清楚为什么在没有冲突的情况下git rebase不如git merge。这使得它清晰明了:您将失去合并之前状态的信息。这对我来说也是有道理的,这也解释了为什么快进式合并不需要唯一提交。 - pattivacek
1
一个小细节(主要是针对原帖作者):提交是从索引(暂存区)中剪切的,而不是从工作树中剪切的,因此合并提交代表的是索引的确切状态,而不是工作树的状态。 - kostix
1
@kostix 是的,我考虑过涵盖更多的细节,但决定这可能会使答案比必要的复杂,并且实际上会分散回答原始问题的注意力。像今天许多有用的系统一样,它实际上比可以轻松描述的要复杂得多... 而且 merge 通常会更新工作树,因此除非您从脏树开始(merge 倾向于不喜欢),否则总体效果基本相同。 - twalberg

3
合并提交的目的是将两个不同的提交树包含在结果 HEAD 的父级中。
有三种可能的合并方式:
1. 为每个被合并的对象创建一个单独的父级,从而创建一个合并提交。这意味着,原始分支中的所有提交仍将显示为一个单独的平行轨道。 2. 创建一个只有一个父级的虚假合并提交,其中包含所有合并的更改。这将导致更简单的历史记录(没有单独的树),但会丢失合并分支的所有提交记录。这是一个不好的想法。 3. 为合并的更改创建新的提交,这些提交以原始分支作为它们的父级。这将产生一个线性历史,将两组提交交错排列。这就是 `git rebase` 所做的事情。

强调一下,rebase 确实会创建新的提交记录。对于每个被 rebase 的分支提交记录,它都会创建一个新的提交记录,并将其补丁应用到 rebase 操作的目标分支上。 - Krzysztof Jabłoński

1
存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?
您需要使用“git rebase”来实现这一点。使用rebase合并并避免合并提交的一个简单方法是,“假装工作先发生,而不是并行发生”。
在您理解elegant之前,rebase可能会很危险,但它确实解决了您所感知的问题。通常情况下,在“原始”提交历史对您来说没有意义时,会使用rebase。在合并的情况下,当您认为其中一个父历史记录是无关紧要的,并且实际上从未发生过(它“重写历史记录”)时,会使用它。在某些重大合并的情况下,您确实希望拥有两个历史记录;在其他情况下,则不需要。我省略了更详细的解释,因为您可以在网上找到许多解释,有些比其他解释更好。

作为一个新手,你不想使用rebase,因为它会重写历史记录。如果你知道自己在做什么,那么rebase可以解决像问题描述中的那样的问题。 - mnagel
我通常更喜欢使用 git rebase,但我正在尝试理解为什么如果没有冲突,git merge可能更可取。值得一提的是,当我刚开始使用 git 时,我发现 git mergegit rebase 更加可怕。 - pattivacek
@mnagel 添加了更详细的解释 - djechlin
1
@patrickvacek 当你想要两个历史记录时,变基会压缩历史记录。试着在git UI(例如github network)中查看一个复杂的项目,并考虑哪些合并和分支是令人讨厌的杂乱无章,哪些告诉你两个真正的分支进入了一个提交。 - djechlin
@djechlin rebase 不总是压缩历史记录,但它可以。更好的说法是它重写了历史记录... - twalberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接