我注意到以下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选择了一系列提交,并根据参数给出的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的措辞在这里有点令人困惑。如果你把命令伪装成这样可能会有所帮助:

Git rebase - to=<new_base> <old_base>[<分支>]

如果我们现在在分支上,它可以被省略:

git rebase - access =<new_base> <old_base>

如果new_base和old_base相同,我们可以省略——onto形参:

Git rebase <new_old_base>

这可能听起来很奇怪:如果旧的基数和新的基数相同,您如何重设基数?但是可以这样想,如果你有一个功能分支foo,它已经(可能)基于你的主分支中的一些提交。通过“re- based”,我们只是让它基于的提交更当前。

(In fact, <old_base> is something that we compare branch against. If it's a branch, then git looks for a common ancestor (see also --fork-point); if it's a commit on current branch, the commits after that are used; if it's a commit that has no common ancestor with current branch, all commits from current branch are used. <new_base> can also be a commit. So, for instance, git rebase --onto HEAD~ HEAD will take commits between old base HEAD and current HEAD and place them on top of HEAD~, effectively deleting the last commit.)

这就是你需要了解的全部内容——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和git rebase之间的区别,最好了解这两个命令可能的行为。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的文章—进入概述

简单地说,给定:

      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)上重新赋值提交。注意,该范围是左排他的。它是排他性的,因为它更容易指定第一次提交,例如输入分支,让git找到第一次从分支分流提交,即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指向的单一提交。提交被跳过,因为它已经应用到目标B,所以什么都没有发生。

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

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

git 变基默认值。

git rebase master

扩展为:

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

更换底座后自动结账。

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

git checkout branch
git rebase master

你不会注意到,在重基之后,git将分支移动到最近的重基提交,并执行git checkout分支(参见git reflog历史)。有趣的是,当第二个参数是提交哈希,而不是分支名称rebase仍然有效,但没有分支移动,所以你最终在“分离HEAD”,而不是被检出到移动的分支。

省略主发散提交。

in -onto的主参数取自第一个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”中结束。

避免显式签出。

默认的HEAD或current_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.

这意味着您可以从任何地方重设提交和分支的基。 因此,与自动结帐后,改基。你不必在重基之前或之后分别签出重基分支。

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