【发布时间】:2013-07-16 07:22:06
【问题描述】:
我试图理解为什么 git 会生成一个具有唯一 SHA1 的提交,用于两个分支之间的合并而没有冲突。存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?
【问题讨论】:
标签: git git-merge revision-history
我试图理解为什么 git 会生成一个具有唯一 SHA1 的提交,用于两个分支之间的合并而没有冲突。存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?
【问题讨论】:
标签: git git-merge revision-history
简短的回答是,提交记录了您工作目录的特定状态,而合并将创建一个与父级中的任何一个都不匹配的状态,因此需要新的提交,而不是仅仅移动一个分支指针。不过,更详细地说,它有助于准确理解合并两个分支的含义。
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,您将丢失在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 等查看该提交,并查看是否存在冲突,但这不是做出新提交的原因。
【讨论】:
git rebase 不如git merge 适合。这清楚地表明:您将在合并之前丢失您的状态信息。这对我来说也很有意义,为什么快进合并不需要唯一的提交。
merge 通常会更新工作树,所以除非你从脏树开始(merge 往往不喜欢) ,整体效果差不多。
合并提交的重点是在生成的 HEAD 的父级中包含两个不同的提交树。
有三种可能的方式来执行合并:
为您合并的每个内容创建一个带有单独父级的合并提交。
这意味着您合并的分支中的所有原始提交仍将作为单独的并行轨道显示在您的历史记录中。
创建一个只有一个包含所有合并更改的父级的虚假合并提交。
这会导致更简单的历史记录(没有单独的树),但它会丢失合并分支的所有提交。
这是个坏主意。
为以原始分支作为父分支的合并更改创建新提交。
这会导致两组提交散布在线性历史记录中。
这就是git rebase 所做的。
【讨论】:
存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?
你想要git rebase,它会这样做。考虑使用 rebase 进行合并和避免合并提交的一种简单方法是,“假设工作首先发生而不是并行发生。”
在 git 对你来说优雅之前“玩弄” Rebase 可能很危险,但它确实解决了你直觉的问题。通常,当“原始”提交历史对您没有意义时,会使用 rebase。在合并的情况下,当您认为父历史之一不相关并且不应该真正发生时使用它(它“重写历史”)。在一些重大合并的情况下,您真的确实想要两个历史;在其他人中,你没有。我省略了更完整的解释,因为你可以在网上找到很多,有些比其他更好。
【讨论】:
git rebase,但我试图理解为什么如果没有冲突,git merge 可能更可取。作为记录,当我刚接触 git 时,我发现 git merge 比 git rebase 要可怕得多。
rebase 并不总是能够消除历史记录,但它可以。不如说它改写了历史……