git rebase用什么来确定共同的祖先?

时间:2017-08-03 20:45:16

标签: git merge rebase

我知道我可以使用git merge-base来确定执行git merge时的共同祖先,但看起来这不适用于 git rebase < / p>

以下是rebase之前的设置:
master branchA--Y--(C)
dev branchA-----C--D
(C)是我将A - C变为A - Y的结果,相同的内容,但不同的提交信息

git merge-base master dev会返回A,如果我git merge dev,我会在历史记录中看到(C)和C

git rebase master,结果是: A - Y - (C) - (D)其中(D)在rebase后是D

git rebase是否认为(C)是共同的祖先? (这在代码中很难做到)我猜它仍然使用A,但是当它选择C,D追加到master的末尾时,C最终成为 no-op

1 个答案:

答案 0 :(得分:3)

让我们从这开头:git rebase做什么是复制一些提交。在您的情况下,它似乎复制提交C但不提交D。 (我想你问为什么,但是假设它与合并基础有关,这可能不正确。)

git rebase选择复制的提交集合主要是但不完全,由以下结果决定:

git rev-list <upstream>..HEAD

其中<upstream>是您传递给git rebase或已配置上游的参数。例如,对于git rebase master,此序列中的<upstream>master。您当前的分支是dev,因此HEAD引用dev。因此,要复制的提交集是按以下列出的:

git rev-list master..dev

(虽然增加了其他选项;见下文)。

如果我正确阅读你的图表,输入图表是:

...--A--Y   <-- master
      \
       C--D   <-- dev

这样git rev-list的输出就会提交CD

Rebase然后继续:

  1. git checkout目标分支的提示(此处提交Y);
  2. 在提交git cherry-pickC时,有时字面上,有时比喻性地D;最后
  3. 运行checkout -B dev HEAD(不完全相同,但效果相同:在dev移动到指向最终复制的提交后,返回dev
  4. 我认为你直接询问的是 - 我认为这不是你应该要求的 - 在采摘过程中哪个提交被用作合并基础。这里的答案是,樱桃选择的合并基础是樱桃选择的父提交。因此,对于git cherry-pick C步骤,合并基础(如果需要合并)是提交AC的父级。对于git cherry-pick D步骤,合并基础(如果需要合并)是提交CD的父级。

    现在,我列出了几个&#34;其他选项&#34;上面的项目。这些是指提交选择过程。特别是,git rebase有两种方法可以提交 off 的提交#34;复制&#34;名单。 (它还会按照通常的向后顺序生​​成列表,因此它会在转发顺序中进行挑选。)

    它消除to-copy列表提交的主要方式是使用git patch-idgit patch-id命令计算一个ID号,人们希望它足够独特&#34;,基于通过比较提交到其直接父级而找到的补丁,丢弃行号和其他一些可能的项目影响樱桃挑选的提交。如果提交已被挑选,则结果应为相同的 ID,否则不同

    因此,在列出CD之后,Git继续将其补丁ID与提交Y的补丁ID进行比较。

    有两个命令可以执行此类操作,一个用于用户,另一个用于git rev-list。面向用户的命令是git cherry,但我想在这里,面向计算机的git rev-list实际上更容易解释。

    这里有趣的是Git知道如何将提交CD的所有修补程序ID与提交Y的修补程序ID进行比较,当初始图形是我们的时候如上所示。或者,如果图表是:

    ...--A--E--F--G   <-- master
          \
           C--D   <-- dev
    

    要比较的两组补丁ID将是(C,D)与(E,F,G)的补丁ID。如果我们有:

              H--I
             /    \
    ...--A--G---J--K   <-- master
          \
           \   D
            \ / \
             C   F   <-- dev
              \ /
               E
    

    要比较的集合将是(C,D,E,F)vs(G,H,I,J,K)。我认为,这使得整个概念更加清晰:它的工作方式是git rev-list可以检查两个分支到他们遇到的点,并收集一组提交每个&#34; side&#34;。

    我们git rev-list执行此操作的方式是:

    git rev-list --left-right master...dev
    

    (请注意此处的三个点,devmaster的顺序仅用于确定哪些提交是&#34;左侧&#34; -on master但不是在dev上 - dev上的&#34;右侧&#34;,但master上没有。

    这个三点符号以及这个--left-right的想法是Git如何计算出哪些提交放在哪个集合中,以便计算完整的补丁ID集。如果左侧提交的修补程序ID与右侧提交的修补程序ID相同,那些提交虽然在某种程度上有所不同,但代表相同的更改:它们是具有的已被挑选出来。

    git rebase命令跳过这些预先挑选的樱桃。它只是挑选剩余的提交,无论那些是什么。 git rebase代码运行git rev-list --right-only --cherry-pick --no-merges <upstream>..HEAD以获取要在步骤2中复制的提交列表。然后它运行这三个步骤。如果CD的修补程序ID与提交Y的修补程序ID匹配,则该提交永远不会被复制。在这种情况下,似乎提交D的修补程序ID可能与提交Y匹配。

    我还说有两种方法可以消除提交。这种樱桃检测就是其中之一,我怀疑它是你看到我认为你看到的结果的原因。另一个是Git所谓的--fork-point,这有点复杂。我不打算在这里介绍它。它只省略了早期&#34;提交:也就是说,它在C--D链上找到了一个点 - 如果链条更长 - 这会更有意义 - 它认为它应该丢弃,只有副本提交之后。由于提交C被复制,因此它不能是导致提交D被省略的fork-point代码。