【问题标题】:Why does git merge with no conflicts still produce a merge commit?为什么没有冲突的 git 合并仍然会产生合并提交?
【发布时间】:2013-07-16 07:22:06
【问题描述】:

我试图理解为什么 git 会生成一个具有唯一 SHA1 的提交,用于两个分支之间的合并而没有冲突。存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?

【问题讨论】:

    标签: git git-merge revision-history


    【解决方案1】:

    简短的回答是,提交记录了您工作目录的特定状态,而合并将创建一个与父级中的任何一个都不匹配的状态,因此需要新的提交,而不是仅仅移动一个分支指针。不过,更详细地说,它有助于准确理解合并两个分支的含义。

    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 适合。这清楚地表明:您将在合并之前丢失您的状态信息。这对我来说也很有意义,为什么快进合并不需要唯一的提交。
    • +1 表示确定。一个小问题(主要针对 OP):提交是从索引(暂存区域)而不是从工作树中删除的,因此合并提交代表索引的确切状态而不是工作树的状态。跨度>
    • @kostix 是的,我考虑过涵盖更多这些血腥细节,但认为这可能会使答案变得比它需要的更复杂,并且实际上会分散回答原始问题的注意力。像今天许多有用的系统一样,它实际上比容易描述的要复杂得多......而且merge 通常会更新工作树,所以除非你从脏树开始(merge 往往不喜欢) ,整体效果差不多。
    【解决方案2】:

    合并提交的重点是在生成的 HEAD 的父级中包含两个不同的提交树。

    有三种可能的方式来执行合并:

    1. 为您合并的每个内容创建一个带有单独父级的合并提交。
      这意味着您合并的分支中的所有原始提交仍将作为单独的并行轨道显示在您的历史记录中。

    2. 创建一个只有一个包含所有合并更改的父级的虚假合并提交。
      这会导致更简单的历史记录(没有单独的树),但它会丢失合并分支的所有提交。
      这是个坏主意。

    3. 为以原始分支作为父分支的合并更改创建新提交。
      这会导致两组提交散布在线性历史记录中。
      这就是git rebase 所做的。

    【讨论】:

    • 广告 3. 只是为了强调 - rebase 确实确实会创建新的提交。对于 rebase 分支的每个提交,它将创建一个具有相同补丁的新提交,并将其应用于 rebase 操作的目标分支。
    【解决方案3】:

    存储没有冲突的信息真的值得为修订历史增加额外的混乱吗?

    你想要git rebase,它会这样做。考虑使用 rebase 进行合并和避免合并提交的一种简单方法是,“假设工作首先发生而不是并行发生。”

    在 git 对你来说优雅之前“玩弄” Rebase 可能很危险,但它确实解决了你直觉的问题。通常,当“原始”提交历史对您没有意义时,会使用 rebase。在合并的情况下,当您认为父历史之一不相关并且不应该真正发生时使用它(它“重写历史”)。在一些重大合并的情况下,您真的确实想要两个历史;在其他人中,你没有。我省略了更完整的解释,因为你可以在网上找到很多,有些比其他更好。

    【讨论】:

    • 作为菜鸟,您不希望变基,因为它会重写历史。如果您知道自己在做什么,那么 rebase 可以解决问题中描述的问题。
    • 我通常更喜欢git rebase,但我试图理解为什么如果没有冲突,git merge 可能更可取。作为记录,当我刚接触 git 时,我发现 git mergegit rebase 要可怕得多。
    • @mnagel 添加了更完整的解释
    • @patrickvacek 当您想要这两个历史时。变基压缩历史。尝试在 git UI(例如 github 网络)中查看一个复杂的项目,并考虑哪些合并和分支是令人讨厌的混乱,哪些告诉你两个真正的分支进入了提交。
    • @djechlin rebase 并不总是能够消除历史记录,但它可以。不如说它改写了历史……
    猜你喜欢
    • 2013-05-02
    • 2022-12-18
    • 1970-01-01
    • 2016-04-07
    • 2020-07-25
    • 1970-01-01
    • 2021-04-10
    • 2011-12-16
    • 1970-01-01
    相关资源
    最近更新 更多