繁体   English   中英

如何使用 on 命令 git rebase 一个分支?

[英]How to git rebase a branch with the onto command?

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

我有一个A和一个B分支,它们在一次commit中发散

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

我想变基B在最新的分支A (和有承诺的B支)

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

如果我这样做没问题:

checkout B
rebase A

但如果我这样做:

checkout B
rebase --onto B A

它根本不起作用,什么也没有发生。 我不明白为什么这两种行为不同。

PhpStorm GIT 客户端使用第二种语法,所以似乎完全被破坏了,这就是我问这个语法问题的原因。

tl;博士

在您的情况下,使用git rebase --ontoA之上重新设置B的正确语法是:

git checkout B
git rebase --onto A B^

变基B上的顶部A从起始提交即父B与引用B^B~1

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

快速:git rebase

git rebase <branch>将重新设置您当前已检出的分支,由HEAD引用,在<branch>访问不能HEAD访问最新提交之上。
这是最常见的变基情况,可以说是需要较少预先计划的情况。

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

在这个例子中, FG是可以从branch访问但不能从HEAD访问的提交。 git rebase branch将采用D ,这是分支点之后的第一次提交,并将(即更改其父级)重新设置在从branch而不是HEAD可到达的最新提交之上,即G

精确: git rebase --onto 带 2 个参数

git rebase --onto允许您从特定的提交开始变基。 它使您可以精确控制正在重新定位的内容和位置。 这适用于需要精确的场景。

例如,假设我们需要从E开始在F之上精确地重新设置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 FD 这意味着:

将可从父级为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)

在这个例子中,为了从序列中删除CE ,你可以说git rebase --onto BE ,或者在B顶部 rebase HEAD ,其中旧的父级是E

外科医生: git rebase --onto 带 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)

在这种情况下,我们希望将E---H的确切范围重新设置在F之上,忽略HEAD当前指向的位置。 我们可以通过说git rebase --onto FDH来做到这git rebase --onto FDH ,这意味着:

F之上将父级为D的提交范围变基到H

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

这是您理解--onto需要知道的全部--onto

git rebase --onto <newparent> <oldparent>

您在提交时切换父项,但没有提供提交的 sha,仅提供其当前(旧)父项的 sha。

简而言之,给定:

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

git rebase --onto F D H

这与(因为--onto接受一个参数)相同:

git rebase D H --onto F

表示在 F 之上的范围 (D, H] 内重新提交提交。注意该范围是左手独占的。它是独占的,因为通过键入 eg branch来指定第一个提交更容易让git找到第一个branch提交,即D导致H

操作案例

    o---o (A)
     \
      o (B)(HEAD)

git checkout B
git rebase --onto B A

可以改为单个命令:

git rebase --onto B A B

什么样子的错误这里是放置B的意思是“将一些提交从而导致分支B之上B ”。 问题是“一些提交”是什么。 如果添加-i标志,您将看到它是HEAD指向的单个提交。 提交被跳过,因为它已经应用于--onto目标B ,所以没有任何反应。

在分支名称像这样重复的任何情况下,该命令都是无意义的。 这是因为提交的范围将是一些已经在该分支中的提交,并且在变基期间所有这些提交都将被跳过。

git rebase <upstream> <branch> --onto <newbase>进一步解释和适用用法。

git rebase默认值。

git rebase master

扩展为:

git rebase --onto master master HEAD
git rebase --onto master master current_branch

变基后自动结帐。

当以标准方式使用时,例如:

git checkout branch
git rebase master

您不会注意到在 rebase gitbranch移动到最近的 rebase 提交并执行git checkout branch (请参阅git reflog历史记录)。 当第二个参数是提交哈希而不是分支名称变基仍然有效但没有分支要移动时,有趣的是您最终处于“分离的 HEAD”而不是被检出到移动的分支。

省略主要的分歧提交。

--ontomaster取自第一个git rebase参数。

                   git rebase master
                              /    \
         git rebase --onto master master

如此实用,它可以是任何其他提交或分支。 通过这种方式,您可以通过获取最新提交并保留主要分歧提交来限制 rebase 提交的数量。

git rebase --onto master HEAD~
git rebase --onto master HEAD~ HEAD  # Expanded.

将把HEAD指向的单个提交变基为master并最终在“分离的 HEAD”中。

避免显式结帐。

默认的HEADcurrent_branch参数是从您所在的位置上下文中获取的。这就是为什么大多数人结帐到他们想要变基的分支的原因。 但是当明确给出第二个 rebase 参数时,您不必在 rebase 之前签出以隐式方式传递它。

(branch) $ git rebase master
(branch) $ git rebase master branch  # Expanded.
(branch) $ git rebase master $(git rev-parse --abbrev-ref HEAD)  # Kind of what git does.

这意味着您可以从任何地方重新设置提交和分支。 因此,与变基后的自动结帐一起使用 您不必在 rebase 之前或之后单独检出 rebase 分支。

(master) $ git rebase master branch
(branch) $ # Rebased. Notice checkout.

为了更好地理解git rebasegit rebase --onto之间的区别,最好了解这两个命令的可能行为。 git rebase允许我们将提交移动到所选分支的顶部。 像这儿:

git rebase master

结果是:

Before                              After
A---B---C---F---G (master)          A---B---C---F---G (master)
         \                                           \
          D---E (HEAD next-feature)                   D'---E' (HEAD next-feature)

git rebase --onto更精确。 它允许我们选择我们想要开始和结束的特定提交。 像这儿:

git rebase --onto F D

结果是:

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

要获得更多详细信息,我建议您查看我自己关于git rebase --onto 概述的文章

简而言之, git rebase --onto选择一系列提交,并根据作为参数给出的提交将它们git rebase --onto

阅读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 在master之上将提交从topicAtopicB

Git 的措辞在这里有点混乱。 如果您假装命令如下所示,这可能会有所帮助:

git rebase --onto=<new_base> <old_base> [<branch>]

如果我们现在在branch ,它可以省略:

git rebase --onto=<new_base> <old_base>

如果new_base相同old_base ,我们可以省略--onto参数:

git rebase <new_old_base>

这听起来可能很奇怪:如果旧基地与新基地相同,你如何重新定位? 但是这样想,如果你有一个功能分支foo ,它已经(可能)基于你的main分支中的一些提交。 通过“re-base”,我们只是基于更新的提交提交。

(事实上​​, <old_base>是我们比较branch东西。如果它是一个分支,那么 git 会寻找一个共同的祖先(另见--fork-point );如果它是当前分支上的提交,那么之后的提交是used; 如果它是一个与当前分支没有共同祖先的提交,则使用当前分支的所有提交。 <new_base>也可以是一个提交。因此,例如, git rebase --onto HEAD~ HEAD将在旧的之间进行提交基本HEAD和当前HEAD并将它们放在HEAD~之上,有效地删除了最后一次提交。)

因为onto你需要两个额外的分支。 与该命令你可以申请从提交branchB是基于branchA到另一个分支如master 在下面的示例branchB基于branchA和要应用的变化branchBmaster而不应用的变化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)

还有另一种情况, git rebase --onto很难掌握:当您重新定位到由对称差异选择器(三个点 ' ... ')产生的提交时

Git 2.24(2019 年第四季度)在管理该案例方面做得更好:

请参阅Denton Liu ( Denton-L ) 的commit 414d924commit 4effc5bcommit c0efb4ccommit 2b318aa (2019 年 8 月 27 日)和commit 793ac7ecommit 359eceb (2019 年 8 月 25 日
帮助者: Eric Sunshine ( sunshineco )Junio C gitster ( gitster )Ævar Arnfjörð Bjarmason ( avar )Johannes Schindelin ( dscho )
请参阅Ævar Arnfjörð Bjarmason ( avar ) 的提交 6330209提交 c9efc21 (2019 年 8 月 27 日)和提交 4336d36 (2019 年 8 月 25 日
帮助者: Eric Sunshine ( sunshineco )Junio C gitster ( gitster )Ævar Arnfjörð Bjarmason ( avar )Johannes Schindelin ( dscho )
(由Junio C gitster合并gitster 提交 640f9cd ,2019 年 9 月 30 日)

rebase :在更多情况下快进--onto

之前,当我们有下图时,

 A---B---C (master) \\ D (side)

运行 ' git rebase --onto master... master side ' 将导致D始终重新定位,无论如何。

在这一点上,请阅读“什么是双点‘之间的差别.. ’和三联点” ... “在git的差异承诺范围?

https://sphinx.mythic-beasts.com/~mark/git-diff-help.png

此处:“ master... ”指的是master...HEAD ,即B :HEAD 是侧面 HEAD(当前已检出):您正在重新定位到B
你在rebase什么? 任何不在master 中的提交,并且可以从side分支访问:只有一个提交符合该描述: D ... 已经在B之上!

同样,在 Git 2.24 之前,这样的rebase --onto将导致D总是被 rebase,无论如何。

但是,所需的行为是rebase 应该注意到这是可快速转发的,并改为这样做。

这类似于 OP 的rebase --onto BA ,它什么也没做。

将检测添加到can_fast_forward以便可以检测到这种情况并执行快进。
首先,重写函数以使用简化逻辑的goto。
接下来,由于

options.upstream && !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)

cmd_rebase中删除了cmd_rebase ,我们在cmd_rebase重新引入了一个替代can_fast_forward
特别是,检查upstreamhead的合并基础修复了t3416的失败情况。

t3416的简图如下:

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

失败的命令是

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

之前,Git 会看到有一个合并基础( Cmaster...topic结果),并且合并和 on 是相同的,因此它会错误地返回 1,表明我们可以快进。 这将导致重订基期图是“ ABCFG ”当我们正期待“ ABCG ”。

一个rebase --onto CF topic意味着F之后的任何提交,可通过topic HEAD 访问:即仅G ,而不是F本身。
在这种情况下,快进会在重新定位的分支中包含F ,这是错误的。

通过附加逻辑,我们检测到 upstream 和 head 的合并基数是F 由于 on 不是F ,这意味着我们没有从master..topic设置完整的提交集。
由于我们排除了一些提交,因此无法执行快进,因此我们正确返回 0。

将“ -f ”添加到由于此更改而失败的测试用例中,因为他们不期望快进,因此强制变基。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM