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

Ala*_*ACK 1 git

正如标题所说,git rebase master和之间有什么区别git rebase --onto master

我运行了这两个命令,希望看到完全相同的结果,但之后得到了两个截然不同的提交历史记录。

这有什么大不了的?它们有何不同?

mat*_*att 14

【不接受这个答案。这只是对 torek 答案的一种评论。]

\n

在我看来,看待这个问题的方法是理解 的完整形式git rebase是 withonto和 三个参数:

\n
git rebase --onto x y z\n
Run Code Online (Sandbox Code Playgroud)\n

要阅读该内容,请在脑海中聚集y在一起z,然后交换--onto x到结尾(因为这是直接宾语和介词短语的自然英语顺序),以便整个内容解析如下(伪代码):

\n
rebase (y z] onto x\n
Run Code Online (Sandbox Code Playgroud)\n

在该伪代码中,该表达式的(y z]意思是“从 开始y并一直持续到z”。Git 通过从 向后计算而不是y从 向前计算“从之后开始”的含义,但效果通常是相同的。zy

\n

因此意味着:“抓取紧随其后并一直持续到 的git rebase --onto x y z所有提交,并将它们附加到。”yzx

\n
\n

很好。这就是的完整形式git rebase。当您省略任何参数时,Git 会为您填写它们。它的做法令人惊讶。这就是您所看到的结果的原因。

\n

让我们举一个真实的例子。这是我们的起始位置:

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

仔细观察图表。我们正在dev. 我们有一个遥控器origin,并且我们领先于我们的远程跟踪分支origin/dev。从at 中dev分离出来,然后它就消失了maina

\n
x y z \n
Run Code Online (Sandbox Code Playgroud)\n

与此同时,main

\n
a b c\n
Run Code Online (Sandbox Code Playgroud)\n

现在让我们尝试一下

\n
git rebase main\n
Run Code Online (Sandbox Code Playgroud)\n

我们在dev,所以这意味着

\n
git rebase main main dev\n
Run Code Online (Sandbox Code Playgroud)\n

这意味着“从之后开始抓取提交main并继续到dev\xe2\x80\x94 即x y z\xe2\x80\x94 并将它们附加到main.” 我们开始...这是我们得到的:

\n
* f6b903e (HEAD -> dev) z\n* 4adb109 y\n* e9cc7fd x\n* 8032a5d (origin/main, main) c\n* 2caa1e9 b\n* 06c7439 a\n
Run Code Online (Sandbox Code Playgroud)\n

是的,正如我所说。我认为,这是大多数人在使用单参数时所期望的git rebase

\n

好吧,现在重新开始。这次我们要说的是

\n
git rebase --onto main\n
Run Code Online (Sandbox Code Playgroud)\n

正如托雷克的回答告诉你的那样,这意味着

\n
git rebase --onto main origin/dev dev\n
Run Code Online (Sandbox Code Playgroud)\n

所以这意味着“抓取从\xe2\x80\x94 到origin/dev\ xe2\x80\x94 的所有内容并将其附加到。这非常令人惊讶!我们从未说过任何关于 的事情,但这就是当我们变基时 Git 将剪断我们的分支的地方. 开始吧...这是我们得到的结果:devzmainorigin/dev

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

这可能就是发生在你(OP)身上的事情。很容易理解为什么您会感到惊讶!

\n

因此,我认为主要的结论是,如果您省略这三个参数中的任何一个,您可能会对 Git 为它们选择的内容感到惊讶。因此,同样在我看来,你应该遗漏其中任何一个!你只是不知道如果你这样做会发生什么。

\n
\n

最后说明:好吧,我撒了一点谎。还记得第一个结果吗?

\n
* f6b903e (HEAD -> dev) z\n* 4adb109 y\n* e9cc7fd x\n* 8032a5d (origin/main, main) c\n* 2caa1e9 b\n* 06c7439 a\n
Run Code Online (Sandbox Code Playgroud)\n

origin/dev我从那个图表中遗漏了。事实上,我们现在拥有的是:

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

注意和提交的重复。这就是它的作用:它复制提交。如果我们打算推送,这是一个棘手的情况,因为我们将要求遥控器忘记当前指向的和,并且它不会对此感到高兴。xygit rebasedevoriginyxorigin/dev

\n


tor*_*rek 5

编辑:我忘记了最后一个\xe2\x80\x94或第一个\xe2\x80\x94点,我将首先在此处插入。的用法大大git rebase简化了:

\n
git rebase [ --onto <newbase> ] [ <upstream> ]\n
Run Code Online (Sandbox Code Playgroud)\n

方括号[...]表示每个参数都是可选的。尖括号<...>意味着您可以在此处填写一些内容。该选项使用一个标志;当且仅当它前面有关键字 (用双连字符拼写)时(由您,用户)给出the 。类似地,当且仅当您给出参数时,该参数才是给出的。所以:--onto newbasenewbase--ontoupstream

\n
git rebase master\n
Run Code Online (Sandbox Code Playgroud)\n

给出一个参数,一个upstream, of master; 给出mastergit rebase --onto master一个参数 a 。如果你不给出论据,那就自己找一个论据。如果你不给出论据,那就自己找一个论据。如果你给出了一个,但没有给出另一个,仍然会自己找到另一个。newbaseupstreamgit rebasenewbasegit rebasegit rebase

\n

作为一个简单的答案,然后:git rebase master选择master作为目标和上游,但git rebase --onto master选择master作为目标,使用默认的上游,无论当前分支是什么。您可以使用以下命令查看当前分支的默认值:

\n
git rev-parse --abbrev-ref @{upstream}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

各种论点的含义是什么

\n

要履行其职责\xe2\x80\x94,也就是说,复制一些提交,然后移动一个分支名称\xe2\x80\x94,git rebase需要知道三件事:

\n
    \n
  • 我应该复制哪些提交?
  • \n
  • 我应该把这些副本放在哪里?
  • \n
  • 复印后我到底应该移动哪个分支名称?
  • \n
\n

其中最后一个默认为当前分支1 所以你只需运行git checkoutorgit switch首先,选择正确的分支。2

\n

Git 作者巧妙地将这三件事中的其余两项塞进了一个参数中git rebase文档将其称为upstream参数。

\n

然而,有时您确实需要将这两件事分开。该--onto标志允许您将它们分开:

\n
git rebase --onto <newbase> <upstream>\n
Run Code Online (Sandbox Code Playgroud)\n

将提交复制提供的newbase,而不是将它们复制提供的upstream

\n

令人好奇的不是这个非常newbase简单的问题,而是如何使用这个upstream参数。它的使用方式很复杂,但为了将其简化为要点,Git 运行:

\n
git rev-list upstream..HEAD\n
Run Code Online (Sandbox Code Playgroud)\n

(在执行初始git checkout或后git switch,如果您提供branch参数)。因此upstream,不是指定要复制什么,而是指定复制什么

\n

rebase 命令复制一些提交集。 这是给定的,因为目标git rebase是以某种方式或形式采取一些不够好的现有提交并将它们转变为改进的提交\xe2\x80\x94,但实际上不可能更改任何提交,一旦它是被制造出来的。由于现有提交无法更改,因此最好的git rebase办法是将它们复制到新的和改进的副本,然后开始使用副本代替原始副本。

\n

“使用副本而不是原件”这一步骤使得需要移动一个分支名称。Git使用分支名称查找提交。分支名称一直在变化,通常以一种简单、一次一步、易于遵循的方式变化。但因为名字可以移动,git reset其他 Git 命令也可以移动它们,甚至可能是暴力地移动它们,所以一次会进行许多次提交,将它们从中国的家乡绑架到澳大利亚内陆地区或其他地方。在 的情况下git rebase,变基代码首先将选定的、旧的和糟糕的提交复制到它们的新家,进行更改\xe2\x80\x94我们希望\xe2\x80\x94改进它们,或者至少使它们适合它们的新家。然后它移动分支名称,以便我们找到副本而不是原始\xe2\x80\x94,然后完成变基。

\n

upstream参数指定不复制的内容,有时\xe2\x80\x94实际上非常频繁!\xe2\x80\x94这个相同的说明符可以用作要复制的提交应该去的地方。但是,当它不能以这种方式使用时,--onto参数可以让您指定复制的位置不复制的内容,作为两个单独的事物。

\n

git | git 将旧提交移到另一个分支的过去显示了一种情况,可以方便地将不复制的内容复制到的位置指定为两个单独的事物。

\n
\n

1事实上,只能git rebase移动当前分支(从最新的 Git 版本开始,2.32 左右,但可能还需要很长一段时间)\xe2\x80\x94so 如果您提供分支名称,请使用开始。参见脚注 2。git rebase git checkoutgit switch

\n

2您可以向命令行命令提供分支名称。如果这样做,该命令当前实际上会运行或已内置必要的签出/切换操作。变基完成后,您将位于所选的分支上,即使在变基开始之前您不在该分支上。也就是说,在:

\n
git checkout main          # puts us on `main`\ngit rebase origin/foo foo\n
Run Code Online (Sandbox Code Playgroud)\n

我们不妨运行:

\n
git checkout foo\ngit rebase origin/foo\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
git rebase origin/foo foo\n
Run Code Online (Sandbox Code Playgroud)\n

或者:

\n
git rebase --onto target origin/foo foo\n
Run Code Online (Sandbox Code Playgroud)\n

以便为我们git rebase做这件事git checkout

\n

  • 真的吗?所以你是说“upstream”作为 rebase 的参数名称和“upstream”作为被跟踪的远程跟踪分支是相同的?因为我不这么认为。这是 /sf/ask/191756351/ 和 /sf/ask/4498409681/ 中讨论的歧义变基 (2认同)