我无法理解git rebase --onto的行为

Xma*_*oux 136 git git-rebase

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

我有一个A和B分支,一个提交分歧

---COMMIT--- (A)
\
 --- (B)
Run Code Online (Sandbox Code Playgroud)

我想在最新的A上重新绑定B分支(并且在B分支上有commit1)

---COMMIT--- (A)
         \
          --- (B)
Run Code Online (Sandbox Code Playgroud)

如果我这样做没问题:

checkout B
rebase A
Run Code Online (Sandbox Code Playgroud)

但如果我这样做:

checkout B
rebase --onto B A
Run Code Online (Sandbox Code Playgroud)

它根本不起作用,没有任何反应.我不明白为什么这两种行为是不同的.

Phpstorm git客户端使用第二种语法,所以在我看来完全破坏了,这就是我要求这个语法问题的原因.

Enr*_*lio 344

TL;博士

正确的语法变基B之上A使用git rebase --onto你的情况是:

git checkout B
git rebase --onto A B^
Run Code Online (Sandbox Code Playgroud)

或者作为使用or 引用的父项的提交开始的基础上进行rebaseBAB.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)
Run Code Online (Sandbox Code Playgroud)

在这个例子中,F并且G是提交是从可达branch但不从HEAD.说git rebase branch将采取D,那是分支点后的第一个承诺,和重订它(即改变其母公司)上的最新承诺从最高可达branch但不从HEAD,即G.

精确:git rebase - 具有2个参数

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

例如,让我们想象一下,我们需要HEADF开始时精确地进行折扣E.我们只对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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们会说git rebase --onto F D.这意味着:

重新引用HEAD其父级位于其D上的可提交的提交F.

换句话说,E from 的父更改DF.git rebase --onto那么语法就是git rebase --onto <newparent> <oldparent>.

另一种方便的方法就是当你想要从当前分支中快速删除一些提交而不必进行交互式rebase时:

          Before                       After
    A---B---C---E---F (HEAD)        A---B---F (HEAD)
Run Code Online (Sandbox Code Playgroud)

在这个例子中,为了从序列中删除C和删除,或者在旧父母所在的位置上进行变基.Egit rebase --onto B EHEADBE

外科医生:git rebase - 具有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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们想要E---H在顶部重新确定精确范围F,忽略HEAD当前指向的位置.我们可以这样说git rebase --onto F D H,这意味着:

衍合的提交,其父母为范围D高达H之上F.

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

  • `git rebase --onto <newparent> <oldparent>`是我见过的行为的最佳解释! (32认同)
  • 谢谢!我有点挣扎着`--onto`选项,但这让它变得清晰!我甚至没有得到它以前我不能理解它:D感谢优秀的"教程":-) (4认同)
  • 很好的答案。对于一般情况来说只是一个小补充:如果范围的两个部分位于不同的分支上,则“&lt;oldparent&gt;”名称会崩溃。一般来说,它是:“包含可从 `&lt;until&gt;` 访问的每个提交,但排除可从 `&lt;oldparent&gt;` 访问的每个提交。” (3认同)
  • 这是一个非常明确的答案!谢谢! (3认同)
  • 虽然这个答案非常好,但我觉得它并没有涵盖所有可能的情况.最后一种语法形式也可用于表达更微妙的rebase类型.在Pro Git(第二版)中脱离示例,D不一定必须是H的祖先.相反,D和H也可以与共同的祖先一起提交 - 在这种情况下,Git将找出他们共同的祖先并从那个祖先重播到H到F. (3认同)
  • 每次我回到这个答案(我真的做了很多),我希望我能投不止一票。您可以在 StackOverflow 上找到的最佳答案之一。 (3认同)
  • 总的来说,这是一个很好的解释,尽管提到 3 个参数示例(`git rebase --onto DFH`)中的后状态图可能会有所帮助,但只有当您从分离的 HEAD 状态开始时,它才会看起来像这样。如果在初始状态下有一个分支指向 I,那么您的 E 和 H 提交各有两个。您的图表确实看起来像是在每种情况下都向知道要查找什么的人引用了一个独立的 HEAD,但对于不知道要查找什么的人来说并不明显,并且它产生了很大的差异(在第三个示例中) (2认同)
  • @Pastafarian确实, &lt;oldparent&gt; 不需要是 H 的祖先。更像是从 &lt;oldparent&gt; 可到达的任何内容都不会进入变基。我将 ```git rebase &lt;newparent&gt; &lt;oldparent&gt; &lt;until&gt;``` 读为“移动到 &lt;newparent&gt; 任何可从 &lt;until&gt; 访问但无法从 &lt;oldparent&gt; 访问的内容”。 (2认同)

sEv*_*ver 44

这是您需要了解的所有内容--onto:

git rebase --onto <newparent> <oldparent>
Run Code Online (Sandbox Code Playgroud)

您在提交时切换父级,但是您没有提供提交的sha,只提供它的当前(旧)父级的sha.

  • 简短易行。实际上,意识到我必须提供我想作为基准的那个的“父”提交而不是那个提交,这花费了我最长的时间。 (2认同)
  • 注意:需要在分支中,或者添加分支名称作为第三个参数 git rebase --onto &lt;newparent&gt; &lt;oldparent&gt; &lt;feature-branch&gt; (2认同)

小智 12

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

git rebase master
Run Code Online (Sandbox Code Playgroud)

结果是:

Before                              After
A---B---C---F---G (master)          A---B---C---F---G (master)
         \                                           \
          D---E (HEAD next-feature)                   D'---E' (HEAD next-feature)
Run Code Online (Sandbox Code Playgroud)

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

git rebase --onto F D
Run Code Online (Sandbox Code Playgroud)

结果是:

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)
Run Code Online (Sandbox Code Playgroud)

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

  • 所以,我们可以将 `git rebase --onto FD` 理解为**将 D 的父级的子级设置为 F**,不是吗? (2认同)

Wlo*_*oHu 10

简短地说,鉴于:

      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
Run Code Online (Sandbox Code Playgroud)

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

git rebase D H --onto F
Run Code Online (Sandbox Code Playgroud)

手段范围变基提交(d,H] F上的通知顶部的范围是左手排斥。这是独特的,因为它更容易指定1日通过键入如承诺branchgit找到1分歧从提交branchD导致H

OP案

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

git checkout B
git rebase --onto B A
Run Code Online (Sandbox Code Playgroud)

可以更改为单个命令:

git rebase --onto B A B
Run Code Online (Sandbox Code Playgroud)

这里看起来像错误的地方是它的位置B,意思是“移动一些提交,从而导致分支B在顶部B”。问题是什么是“某些提交”。如果添加-i标志,您将看到它是指向的单次提交HEAD。提交被跳过,因为它已经被应用到--onto目标B,因此什么也没有发生。

在这样重复分支名称的情况下,该命令都是无意义的。这是因为提交范围将是该分支中已经存在的某些提交,并且在重新设置基准期间将全部跳过这些提交。

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

git rebase 默认值。

git rebase master
Run Code Online (Sandbox Code Playgroud)

扩展为:

git rebase --onto master master HEAD
git rebase --onto master master current_branch
Run Code Online (Sandbox Code Playgroud)

变基后自动结帐。

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

git checkout branch
git rebase master
Run Code Online (Sandbox Code Playgroud)

您不会注意到在gitbranch基准迁移到最近的基于基准的提交之后,确实会这样做git checkout branch(请参阅git reflog历史记录)。当第二个参数是commit hash而不是分支名称rebase时,仍然很有趣,但是没有分支可移动,因此您最终以“ detached HEAD”结尾,而不是检出到移动的分支。

忽略主要分歧提交。

master--onto从第一个拍摄git rebase参数。

                   git rebase master
                              /    \
         git rebase --onto master master
Run Code Online (Sandbox Code Playgroud)

因此实用上可以是任何其他提交或分支。这样,您可以通过获取最新提交并保留主要的不同提交来限制重新提交的数量。

git rebase --onto master HEAD~
git rebase --onto master HEAD~ HEAD  # Expanded.
Run Code Online (Sandbox Code Playgroud)

HEAD重新master建立指向的单个提交,并最终以“分离的HEAD”结尾。

避免显式检出。

默认值HEADcurrent_branch参数是从上下文中从您所处的位置获取的。这就是为什么大多数人结帐到他们想作为基准的分支的原因。但是,当显式给出第二个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.
Run Code Online (Sandbox Code Playgroud)

这意味着您可以在任何地方重新建立提交和分支。因此,与重新设置基准点后的自动结帐一起使用在重新设置基准之前或之后,您不必分别检出已重新定义的分支。

(master) $ git rebase master branch
(branch) $ # Rebased. Notice checkout.
Run Code Online (Sandbox Code Playgroud)

  • 这。是。最好的。解释!我从来没有意识到“git rebase &lt;upstream&gt;”只是“git rebase &lt;upstream&gt; --onto &lt;upstream&gt;”的缩写。这篇文章还教会了我:我们总是选择一系列提交——来自“git rebase”文档:“在另一个基本提示之上重新应用提交” (2认同)

Mic*_*ger 9

因为onto您需要两个额外的分支。使用该命令,您可以将branchB基于该分支的提交应用branchA到另一个分支,例如masterbranchB在下面的示例中,您希望应用onbranchA的更改而不应用 的更改。branchBmasterbranchA

o---o (master)
     \
      o---o---o---o (branchA)
                   \
                    o---o (branchB)
Run Code Online (Sandbox Code Playgroud)

通过使用以下命令:

checkout branchB
rebase --onto master branchA 
Run Code Online (Sandbox Code Playgroud)

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

      o'---o' (branchB)
     /
o---o (master)
     \
      o---o---o---o (branchA)
Run Code Online (Sandbox Code Playgroud)

  • 这不应该是“checkoutbranchB:rebase--ontomasterbranchA”吗? (8认同)
  • 为什么这被投票了?这并不像它所说的那样。 (5认同)

Gau*_*ier 8

简单地说,git rebase --onto选择一系列提交并在作为参数给出的提交中重新定义它们.

阅读手册页git rebase,搜索"上".这些例子非常好:

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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你告诉git将提交从最重要的topicA转移到.topicBmaster


squ*_*rel 6

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);如果它是当前分支上的提交,则使用之后的提交;如果它是一个没有与当前分支的共同祖先,使用当前分支的所有提交。<new_base>也可以是提交。因此,例如,git rebase --onto HEAD~ HEAD将在旧基HEAD和当前之间进行提交HEAD并将它们放在 之上HEAD~,从而有效地删除最后一次提交。)