我试图理解为什么 git 会生成一个具有唯一 SHA1 的提交,以便在两个分支之间进行没有冲突的合并。存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?
3 回答
简短的回答是,提交记录了您工作目录的特定状态,而合并将创建一个与任何一个父级都不匹配的状态,因此需要新的提交,而不是仅仅移动分支指针。不过,更详细地说,它有助于准确理解合并两个分支的含义。
最常用的两种方法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
到指向,您将丢失在throughG
中所做的更改。在这种情况下,必须创建一个新的提交,生成的工作树看起来就像您获取了一个副本( and的合并基础,您可以通过运行查看),然后应用through和 in through中的所有更改。即使这不会导致冲突(两个分支修改不同的文件集或至少只是文件的不同区域),生成的工作目录状态也不匹配,或者J
N
git merge
C
G
N
git merge-base branch1 branch2
D
G
J
N
G
N
,因此需要新的提交。因此,git
将创建一个新的提交,其工作目录包含从两个分支应用更改的结果,并将该提交标记为同时具有父级G
和N
父级。
因此,它并不是真正“存储没有冲突的信息”,而是存储项目的一个新的独特状态。git log
当然,您可以使用,等查看该提交git diff
,并查看是否存在冲突,但这不是做出新提交的原因。
合并提交的要点是在生成的 HEAD 的父级中包含两个不同的提交树。
有三种可能的方式来执行合并:
为您合并的每个事物创建一个带有单独父级的合并提交。
这意味着您合并的分支中的所有原始提交仍将作为单独的并行轨道显示在您的历史记录中。创建一个只有一个父级的虚假合并提交,其中包含所有合并的更改。
这会导致更简单的历史记录(没有单独的树),但它会丢失合并分支的所有提交。
这是一个坏主意。为以原始分支作为父分支的合并更改创建新提交。
这导致了一个线性历史,其中散布了两组提交。
这就是这样git rebase
做的。
存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?
你想要git rebase
,它会做到这一点。考虑使用 rebase 进行合并和避免合并提交的一种简单方法是,“假设工作首先发生而不是并行发生”。
在 git 对你来说优雅之前“玩弄” Rebase 可能很危险,但它确实解决了你直觉的问题。通常,当“原始”提交历史对您没有意义时,会使用 rebase。在合并的情况下,当您认为父历史之一不相关且不应该真正发生时使用它(它“重写历史”)。在一些重大合并的情况下,您确实需要两种历史;在其他人中,你没有。我省略了更完整的解释,因为您可以在网上找到很多,其中一些比其他更好。