“git rebase master”和“git rebase --onto master”之间有什么区别?

a2mppw5e  于 12个月前  发布在  Git
关注(0)|答案(2)|浏览(175)

git rebase mastergit rebase --onto master有什么区别?
我运行了两个命令,希望看到完全相同的结果,但后来得到了两个截然不同的提交历史。
这有什么大不了的?它们之间有什么不同?

pod7payv

pod7payv1#

【不要接受这个答案,这只是对托雷克的回答的一种评论。】
在我看来,看待这个问题的方法是理解git rebase的完整形式是onto和三个参数:

git rebase --onto x y z

字符串
要读到这一点,请在脑海中将yz组合在一起,并将--onto x交换到最后(因为这是直接宾语和介词短语的自然英语顺序),因此整个过程将解析如下内容(伪代码):

rebase (y z] onto x


在这个伪代码中,表达式(y z]表示“从y之后开始,一直到z”。Git通过从z向后计算“从y之后开始“的含义,而不是从y向前计算,但效果大致相同。
所以git rebase --onto x y z的意思是:“抓取所有从y之后开始一直到z的提交,并将它们附加到x。”
很好。这是git rebase的 * 完整 * 形式。当你省略任何参数时,Git会为你填充它们。它这样做的方式令人惊讶。这就是你看到的结果的原因。
让我们举一个真实的例子。这是我们的起始位置:

* f8696e6 (HEAD -> dev) z
* 103333e (origin/dev) y
* 559ad1f x
| * 8032a5d (origin/main, main) c
| * 2caa1e9 b
|/  
* 06c7439 a


请仔细观察图表。我们在dev上。我们有一个远程origin,并且我们比远程跟踪分支origin/dev领先一个分支。deva处从main分离,之后它继续

x y z


同时,main

a b c


现在试试

git rebase main


我们在dev上,所以这意味着

git rebase main main dev


这意味着“抓取从main之后开始并继续到dev的提交-即xyz-并将它们附加到main。”

* f6b903e (HEAD -> dev) z
* 4adb109 y
* e9cc7fd x
* 8032a5d (origin/main, main) c
* 2caa1e9 b
* 06c7439 a


是的,就像我说的。我想,这是大多数人在使用单参数git rebase时所期望的。
好,现在重新开始。这次我们说

git rebase --onto main


正如torek的回答告诉你的那样

git rebase --onto main origin/dev dev


所以这意味着“抓取从origin/devdev的所有内容--也就是z--并将其附加到main上。这非常令人惊讶!我们从来没有提到origin/dev,但这就是Git在我们变基时将要剪切我们的分支的地方。我们开始了...这就是我们得到的:

* 0dccc25 (HEAD -> dev) z
* 8032a5d (origin/main, main) c
* 2caa1e9 b
| * 103333e (origin/dev) y
| * 559ad1f x
|/  
* 06c7439 a


这可能就是发生在你身上的事情(OP)。很容易看出为什么你会觉得惊讶!
所以在我看来,主要的收获是,如果你遗漏了这三个参数中的任何一个,你可能会对Git为它们选择的东西感到惊讶。因此,在我看来,你不应该遗漏它们中的任何一个!你只是不知道如果你这样做会发生什么。
最后一点:好吧,我撒了一点谎。还记得第一个结果吗?

* f6b903e (HEAD -> dev) z
* 4adb109 y
* e9cc7fd x
* 8032a5d (origin/main, main) c
* 2caa1e9 b
* 06c7439 a

我在图中省略了origin/dev。实际上,这就是我们现在所拥有的:

* f6b903e (HEAD -> dev) z
* 4adb109 y
* e9cc7fd x
* 8032a5d (origin/main, main) c
* 2caa1e9 b
| * 103333e (origin/dev) y
| * 559ad1f x
|/  
* 06c7439 a

注意xy提交的 * 重复 *。这就是git rebase所做的:如果我们打算推送dev,这是一个棘手的情况,因为我们将要求远程origin忘记origin/dev当前指向的yx,它不会高兴的

ktca8awb

ktca8awb2#

编辑:我忘记了最后一点或第一点,我将在这里首先插入。git rebase的 * 用法 * 非常简化:

git rebase [ --onto <newbase> ] [ <upstream> ]

字符串
方括号[...]表示每个参数都是 * 可选的 *。尖括号<...>表示 * 您在这里填写某些内容 --onto *newbase*选项使用一个标志;newbase * 是给定的(由您,用户)当且仅当它前面有关键字--onto,用双连字符拼写。类似地,当且仅当您给出 * upstream * 参数时,它才被给予。因此:

git rebase master


给出master的一个参数an * upstream *; git rebase --onto master给出 master 的一个参数 * newbase。如果你不给出 * upstream * 参数,git rebase会自己找到一个。如果你不给出 * newbase * 参数,git rebase会自己找到一个。如果你只给出一个参数,而不给出另一个参数,git rebase * 仍然 * 自己找到另一个。
作为一个一行式的回答,那么:**git rebase master选择master作为 * 目标和上游 ,但是git rebase --onto master选择master作为目标,并带有 default 上游
,不管它是当前分支的默认值。您可以看到当前分支的默认值:

git rev-parse --abbrev-ref @{upstream}


如果当前分支是dev,其上游是origin/dev,那么git rebase master意味着git rebase --onto master master,但git rebase --onto master意味着git rebase --onto master origin/dev

各种参数的含义

要执行它的任务--也就是说,复制一些提交,然后移动一个分支名称--git rebase需要知道三件事:

  • 我应该复制哪些提交?
  • 我应该把这些复印件放在哪里?
  • 复制后,我最后应该移动哪个分支名称?

最后一个默认为 * 当前分支 *。1因此,您只需首先运行git checkoutgit switch,以选择正确的分支。2
Git的作者巧妙地将这三件事中剩下的两件塞进了git rebase的一个参数中,the documentation称之为 * upstream * 参数。
然而,有时候,你真的需要这两件事是 * 分开 *. --onto标志允许你分开他们:

git rebase --onto <newbase> <upstream>


将提交 * 复制到 * 提供的 * newbase *,而不是将它们 * 复制到 * 提供的 * upstream *。
奇怪的不是 * newbase *,这很简单,而是 * upstream * 参数是如何使用的 *。它的使用方式很复杂,但为了简化它,Git运行:

git rev-list upstream..HEAD


(在执行初始git checkoutgit switch之后,如果您提供 * branch * 参数)。因此,* upstream * 指定的不是 * 要复制的内容 *,而是 * 不**要复制的内容 *。

**rebase命令 * 将 * 复制一些提交集。**这是一个给定的,因为git rebase的目标是以某种方式或形式将一些不太好的现有提交转化为 * 改进的 * 提交-但是一旦提交完成,就不可能 * 更改 * 任何提交。由于现有提交 * 不能 * 更改,git rebase所能做的最好的事情就是将它们 * 复制 * 到新的和改进的 * 副本 * 中,然后开始使用副本来代替原始文件。

“使用副本而不是原始文件”这一步是需要移动一个分支名称的原因。Git使用分支名称来查找提交。分支名称一直在移动,通常是以一种简单的、一步一步的、容易遵循的方式。但是因为名称可以移动,git reset和其他Git命令可以移动它们,甚至可能是暴力地移动,一次可以移动许多提交,把他们从中国的家乡绑架到澳大利亚内陆或其他地方。😀在git rebase的情况下,rebase代码首先将选定的旧的和糟糕的提交复制到他们的新家,进行修改,我们可以改进它们,或者至少让它们适应它们的新家。然后它移动分支名称,这样我们就可以找到 * 副本 * 而不是原始的-然后变基完成。

  • upstream * 参数指定了 * 不复制 * 什么,有时候--实际上,非常频繁!--这个 * 相同的 * 说明符 * 可以被用作 * 要复制的提交应该去的地方。但是当它 * 不能 * 以这种方式使用时,--onto参数让你指定 * 复制到哪里 * 和 * 不复制什么 *,作为两个独立的东西。

git | move old commit to the past of another branch展示了一种情况,在这种情况下,可以方便地将 * 不复制什么 * 和 * 复制到哪里 * 作为两个单独的东西。
1实际上,git rebase * 只能 * 移动当前的分支(从最新的Git版本2.32开始,但可能还需要很长一段时间)--所以如果你提供一个分支名称,git rebase * 通过 * 使用 * git checkoutgit switch * 开始。参见脚注2。
2您可以为命令行命令提供一个分支名称。如果您这样做,则该命令当前实际运行或已在其中内置了必要的 checkout /切换操作。当变基完成时,即使您不在变基开始之前,您也在所选的分支上。也就是说,在:

git checkout main          # puts us on `main`
git rebase origin/foo foo


我们还不如跑:

git checkout foo
git rebase origin/foo


无论如何,因为我们最终使用的是foo,而不是main。但这确实意味着如果我们必须运行git checkout foo,我们可以运行:

git rebase origin/foo foo


或者:

git rebase --onto target origin/foo foo


git rebase帮我们做git checkout

相关问题