我注意到以下git命令的两个块有不同的行为,我不明白为什么。

我有一个A和一个B分支,它们在一次提交中分离

---COMMIT--- (A)
\
 --- (B)

我想在最近的A上重新建立B分支(并在B分支上提交)

---COMMIT--- (A)
         \
          --- (B)

如果我这样做没有问题:

checkout B
rebase A

但如果我这样做了:

checkout B
rebase --onto B A

根本不管用,什么都没发生。我不明白为什么这两种行为不同。

PhpStorm GIT客户端使用第二种语法,所以看起来完全被破坏了,这就是为什么我问这个语法问题。


当前回答

还有另外一个git rebase -onto很难理解的情况:当你rebase到一个对称差分选择器(三个点'…')导致的提交上时

Git 2.24(2019年Q4)在管理这种情况方面做得更好:

See commit 414d924, commit 4effc5b, commit c0efb4c, commit 2b318aa (27 Aug 2019), and commit 793ac7e, commit 359eceb (25 Aug 2019) by Denton Liu (Denton-L). Helped-by: Eric Sunshine (sunshineco), Junio C Hamano (gitster), Ævar Arnfjörð Bjarmason (avar), and Johannes Schindelin (dscho). See commit 6330209, commit c9efc21 (27 Aug 2019), and commit 4336d36 (25 Aug 2019) by Ævar Arnfjörð Bjarmason (avar). Helped-by: Eric Sunshine (sunshineco), Junio C Hamano (gitster), Ævar Arnfjörð Bjarmason (avar), and Johannes Schindelin (dscho). (Merged by Junio C Hamano -- gitster -- in commit 640f9cd, 30 Sep 2019)

Rebase:快进——更多情况下为上 之前,当我们有下面的图表时, A - B - C(主) \ D(侧) 运行'git rebase - to master…master side'将导致D总是被重基,无论如何。

在这一点上,阅读“双点之间的区别是什么?”'和三点"…"在Git diff提交范围?"

:“主人…“指的是大师……HEAD,也就是B: HEAD是侧HEAD(目前签出):您正在重新基于B。 你在改变什么基础?任何不在master中的提交,并且从side branch可以到达:只有一个提交符合描述:D…已经在B上面了!

同样,在Git 2.24之前,这样的rebase -onto会导致D总是被重基,无论发生什么。

然而,期望的行为是,rebase应该注意到这是快速前进的,并取而代之。

这类似于OP的B - A的重基,而OP什么也没做。

Add detection to can_fast_forward so that this case can be detected and a fast-forward will be performed. First of all, rewrite the function to use gotos which simplifies the logic. Next, since the options.upstream && !oidcmp(&options.upstream->object.oid, &options.onto->object.oid) conditions were removed in cmd_rebase, we reintroduce a substitute in can_fast_forward. In particular, checking the merge bases of upstream and head fixes a failing case in t3416. The abbreviated graph for t3416 is as follows:

        F---G topic
       /
  A---B---C---D---E master

失败的命令是

git rebase --onto master...topic F topic

在此之前,Git会看到有一个merge base (C, result of master…topic),并且merge和onto是相同的,因此它会错误地返回1 我们可以快进。这将导致重基图为“ABCFG” 当时我们在期待《ABCG》。

一个rebase—to C F topic意味着在F之后的任何提交,可以通过topic HEAD到达:这只是G,而不是F本身。 在这种情况下,快进会将F包含在重基分支中,这是错误的。

使用附加的逻辑,我们检测上游和头部的合并基 是F,因为onto不是F,这意味着我们没有重基 从master..topic提交。 因为我们排除了一些提交,所以不能执行快进,所以我们正确地返回0。 将'-f'添加到由于此更改而失败的测试用例中,因为 他们并没有料到会有这么快的进展,以至于被迫调降基数。

其他回答

这就是你需要了解的全部内容——onto:

git rebase --onto <newparent> <oldparent>

您在提交时切换了父节点,但您没有提供提交的sha,只提供它当前(旧)父节点的sha。

简单地说,git rebase——onto选择了一系列提交,并根据参数给出的commit对它们进行了重基。

阅读git rebase的手册,搜索“onto”。下面的例子非常好:

example of --onto option is to rebase part of a branch. If we have the following situation:

                                   H---I---J topicB
                                  /
                         E---F---G  topicA
                        /
           A---B---C---D  master

   then the command

       git rebase --onto master topicA topicB

   would result in:

                        H'--I'--J'  topicB
                       /
                       | E---F---G  topicA
                       |/
           A---B---C---D  master

在这种情况下,您告诉git将提交从topicA重置为topicB,位于master之上。

还有另外一个git rebase -onto很难理解的情况:当你rebase到一个对称差分选择器(三个点'…')导致的提交上时

Git 2.24(2019年Q4)在管理这种情况方面做得更好:

See commit 414d924, commit 4effc5b, commit c0efb4c, commit 2b318aa (27 Aug 2019), and commit 793ac7e, commit 359eceb (25 Aug 2019) by Denton Liu (Denton-L). Helped-by: Eric Sunshine (sunshineco), Junio C Hamano (gitster), Ævar Arnfjörð Bjarmason (avar), and Johannes Schindelin (dscho). See commit 6330209, commit c9efc21 (27 Aug 2019), and commit 4336d36 (25 Aug 2019) by Ævar Arnfjörð Bjarmason (avar). Helped-by: Eric Sunshine (sunshineco), Junio C Hamano (gitster), Ævar Arnfjörð Bjarmason (avar), and Johannes Schindelin (dscho). (Merged by Junio C Hamano -- gitster -- in commit 640f9cd, 30 Sep 2019)

Rebase:快进——更多情况下为上 之前,当我们有下面的图表时, A - B - C(主) \ D(侧) 运行'git rebase - to master…master side'将导致D总是被重基,无论如何。

在这一点上,阅读“双点之间的区别是什么?”'和三点"…"在Git diff提交范围?"

:“主人…“指的是大师……HEAD,也就是B: HEAD是侧HEAD(目前签出):您正在重新基于B。 你在改变什么基础?任何不在master中的提交,并且从side branch可以到达:只有一个提交符合描述:D…已经在B上面了!

同样,在Git 2.24之前,这样的rebase -onto会导致D总是被重基,无论发生什么。

然而,期望的行为是,rebase应该注意到这是快速前进的,并取而代之。

这类似于OP的B - A的重基,而OP什么也没做。

Add detection to can_fast_forward so that this case can be detected and a fast-forward will be performed. First of all, rewrite the function to use gotos which simplifies the logic. Next, since the options.upstream && !oidcmp(&options.upstream->object.oid, &options.onto->object.oid) conditions were removed in cmd_rebase, we reintroduce a substitute in can_fast_forward. In particular, checking the merge bases of upstream and head fixes a failing case in t3416. The abbreviated graph for t3416 is as follows:

        F---G topic
       /
  A---B---C---D---E master

失败的命令是

git rebase --onto master...topic F topic

在此之前,Git会看到有一个merge base (C, result of master…topic),并且merge和onto是相同的,因此它会错误地返回1 我们可以快进。这将导致重基图为“ABCFG” 当时我们在期待《ABCG》。

一个rebase—to C F topic意味着在F之后的任何提交,可以通过topic HEAD到达:这只是G,而不是F本身。 在这种情况下,快进会将F包含在重基分支中,这是错误的。

使用附加的逻辑,我们检测上游和头部的合并基 是F,因为onto不是F,这意味着我们没有重基 从master..topic提交。 因为我们排除了一些提交,所以不能执行快进,所以我们正确地返回0。 将'-f'添加到由于此更改而失败的测试用例中,因为 他们并没有料到会有这么快的进展,以至于被迫调降基数。

你还需要另外两个分支。通过这个命令,你可以将来自branchB的基于branchA的提交应用到另一个分支,例如master。在下例中,branchB基于branchA,您希望在master上应用branchB的更改,而不应用branchA的更改。

o---o (master)
     \
      o---o---o---o (branchA)
                   \
                    o---o (branchB)

使用命令:

checkout branchB
rebase --onto master branchA 

您将得到以下提交层次结构。

      o'---o' (branchB)
     /
o---o (master)
     \
      o---o---o---o (branchA)

博士tl;

在你的例子中,使用git rebase—onto在A的基础上重基B的正确语法是:

git checkout B
git rebase --onto A B^

或者从B的父类(引用B^或B~1)的提交开始,在A的基础上重构B。

如果你对git rebase <branch>和git rebase——onto <branch>之间的区别感兴趣,请继续阅读。

快速:git改基

git rebase <branch>将在<branch>可到达但HEAD不能到达的最新提交之上,将你当前已签出的、由HEAD引用的分支重新赋值。 这是最常见的调整基础的情况,可以说是一个需要较少的预先计划。

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                         \
              D---E (HEAD)                              D---E (HEAD)

在这个例子中,F和G是可以从分支到达但不能从HEAD到达的提交。假设git rebase分支将接受D,这是分支点之后的第一次提交,并在分支可到达但不能从HEAD到达的最新提交(即G)之上重基它(即更改其父)。

The Precise: git rebase -onto带有2个参数

Git rebase——onto允许你从一个特定的提交开始。它允许您准确地控制要重基的内容和位置。这适用于需要精确的场景。

例如,让我们想象一下,我们需要精确地在F的基础上从e开始重新建立HEAD。我们只对将F引入我们的工作分支感兴趣,而与此同时,我们不想保留D,因为它包含一些不兼容的更改。

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                     \
              D---E---H---I (HEAD)                  E---H---I (HEAD)

在这种情况下,我们会说git rebase—onto F d。这意味着:

将父级为D的HEAD可到达的提交重设为F的顶部。

换句话说,将E的父元素从D改为f。git rebase——onto的语法是git rebase——onto <newparent> <oldparent>。

当你想要快速地从当前分支中删除一些提交,而不需要做一个交互式的rebase时,这个方法就会派上用场:

          Before                       After
    A---B---C---E---F (HEAD)        A---B---F (HEAD)

在这个例子中,为了从序列中移除C和E,你会说git rebase—to B E,或者rebase HEAD在B的顶部,其中旧的父元素是E。

外科医生:git rebase -on有3个参数

Git rebase -onto可以在精度方面更进一步。事实上,它允许您在另一个提交之上重新设置一个任意范围的提交。

这里有一个例子:

          Before                                     After
    A---B---C---F---G (branch)                A---B---C---F---G (branch)
             \                                             \
              D---E---H---I (HEAD)                          E---H (HEAD)

在这种情况下,我们想要在F之上重新确定精确的范围E—H,忽略HEAD当前指向的位置。我们可以这样做,git rebase—onto F D H,意思是:

将父对象为D的提交的范围重设为F之上的H。

git rebase——onto的语法是一系列的提交,然后变成git rebase——onto <newparent> <oldparent> <直到>。这里的技巧是记住<until>引用的提交包含在范围中,并将在重基完成后成为新的HEAD。