【问题标题】:How git-merge and git-rebase behave with a topic branch forked off of another topic branch?git-merge 和 git-rebase 如何处理从另一个主题分支分支出来的主题分支?
【发布时间】:2016-03-25 18:55:35
【问题描述】:

让我们假设存储库如下所示:

            master
               |
A--B--C--K--L--M
       \
        E-F-G
            |\
            | H-I-J
          topicA  |
                topicB

git merge topicBmaster 中会做什么?它会只应用HIJ 的提交吗?还是来自topicA的初始分叉点的所有提交?

git rebase master topicA 会做什么?会不会导致这个:

            master
               |
A--B--C--K--L--M
                \
                 E'-F'-G'
                       |\
                       | H'-I'-J'
                     topicA    |
                             topicB

或者类似的东西?

            master
               |
A--B--C--K--L--M
       \        \
        \        E'-F'-G'
         \             |
          \          topicA
           \          
            E-F-G-H-I-J
                      |
                    topicB

【问题讨论】:

    标签: git git-merge branching-and-merging git-rebase


    【解决方案1】:

    除了将分支名称解析为提交 ID,merge 和 rebase 都不关心任何分支标签指向的位置。他们只查看提交图。

    合并

    我认为git merge 的工作方式更容易理解。除了为默认的合并提交消息保存分支名称之外,git merge 只是使用git rev-parse 来查找提交 ID。

    对于您的第一个问题,给定 git merge topicB 而在分支 master 上,git 会找到三个提交 ID:

    • HEAD 解析为提交 M(分支上最尖端的提交 master;这也是您在运行 git rev-parse HEAD 时看到的提交 ID)。
    • topicB 解析为提交 J(分支上最尖端的提交 topicB;这也是您在运行 git rev-parse topicB 时看到的提交 ID)。
    • 这两个提交的合并基础,由提交图(仅)确定。在这种情况下,即提交 C

    找到合并基础后,git 运行两个git diffs:一个在合并基础和HEAD 提交之间(查看“你的分支”做了什么),一个在合并基础和@987654338 之间@commits(查看“他们的分支”做了什么)。它结合了两组更改,如果没有冲突(或者如果它可以通过git rerere 解决它们),则进行一个新的提交,其两个父级按此顺序是当前的HEAD 提交和@ 987654341@ 提交。使新提交照常更新当前分支,因此HEAD 现在指向新提交(或更准确地说,HEAD 仍然指向master,而master 指向新提交)。

    这个问题:

    它会只应用提交 H、I 和 J 吗?还是来自 topicA 初始分叉点的所有提交?

    暗示了对 git 合并算法的根本误解:git 不查看 任何 的中间提交。它只查看此处确定的三个提交:MJC 在这种情况下。 Git 可以像这样“作弊”,因为就该提交的源代码树内容而言,每个提交都是完全独立的。

    变基

    变基更复杂(你在上面的第二个猜测中是正确的,但无论如何让我们来看看它)。

    它仍然使用相同的“通过git rev-parse 将分支名称解析为ID”技巧,但它使用了更复杂的git rev-list 变体。 git rev-list 所做的是在提交图上计算集合操作(​​一旦实际进入图本身,再次忽略任何标签)。

    鉴于git rebase master topicA,我们(好吧,无论如何, :-))首先必须检查the rebase documentation,我们发现master 被视为 参数和 topicA 被视为 参数,并且它对最后一个参数所做的一切都是首先运行 git checkout <em>&lt;branch&gt;</em>,然后就好像你没有提供它一样继续.因此,我们可以假设您从执行git checkout topicA 开始,然后运行git rebase master 参数是masterHEAD 指的是分支topicA,因此它标识了提交G

    在过去,在git rebase 拥有--fork-point 之前,更容易确定接下来会发生什么。今天,根据文档,您必须确定是否启用了--fork-point。因为你确实给出了一个实际的参数,所以默认情况下它是禁用的:它只有在你在命令行中给出--fork-point时才会启用;你没有;因此我们不需要深入研究--fork-point 的奥秘(哇,耶!:-))。所以,这里发生的是 rebase 本质上运行git rev-list master..HEAD 来获取要复制的提交列表。这列出了提交 EFG,因为这些是 HEAD 可访问的集合中的提交,但 master 无法访问。

    然后,rebase 进程在 --onto 参数给出的提交(如果没有)或 参数给出的提交上分离 HEAD。那是master,所以这是提交M。从这里开始,rebase 基本上只是挑选它之前找到的每个提交,这给你提交E'F'G'。如果这一切正常,git 的 rebase 的最后一步是将原来的当前分支(topicA,再次)指向最后一次提交,G'。没有其他标签更改,因此您得到了您绘制的最终图表。

    【讨论】:

    • 感谢您的详尽回答。我担心我会得到一个非常肤浅或部分的答案,这个问题会无人回答,但老实说,我无法要求更好的答案。我希望这篇文章也能帮助这里的其他用户,因为 git 文档对我来说还不够清楚。
    • 什么 rebase 操作会导致第一个图(即主题和派生主题分支基于大师的提示,而不是将 topicB 留在原处)?
    • 为了解决第二条评论,没有任何内置的“multi-rebase”。我编写了一个 python 脚本来为自己完成这项工作,但我没有对其进行足够好的测试以将其放在任何地方。
    猜你喜欢
    • 2023-02-07
    • 2021-08-20
    • 2022-11-25
    • 2021-06-28
    • 2017-02-21
    • 2018-06-22
    • 1970-01-01
    • 2014-09-19
    • 1970-01-01
    相关资源
    最近更新 更多