正如标题所说,git rebase master和之间有什么区别git rebase --onto master?
我运行了这两个命令,希望看到完全相同的结果,但之后得到了两个截然不同的提交历史记录。
这有什么大不了的?它们有何不同?
mat*_*att 14
【不接受这个答案。这只是对 torek 答案的一种评论。]
\n在我看来,看待这个问题的方法是理解 的完整形式git rebase是 withonto和 三个参数:
git rebase --onto x y z\nRun Code Online (Sandbox Code Playgroud)\n要阅读该内容,请在脑海中聚集y在一起z,然后交换--onto x到结尾(因为这是直接宾语和介词短语的自然英语顺序),以便整个内容解析如下(伪代码):
rebase (y z] onto x\nRun Code Online (Sandbox Code Playgroud)\n在该伪代码中,该表达式的(y z]意思是“从 开始y并一直持续到z”。Git 通过从 向后计算而不是y从 向前计算“从之后开始”的含义,但效果通常是相同的。zy
因此意味着:“抓取紧随其后并一直持续到 的git rebase --onto x y z所有提交,并将它们附加到。”yzx
很好。这就是的完整形式git rebase。当您省略任何参数时,Git 会为您填写它们。它的做法令人惊讶。这就是您所看到的结果的原因。
让我们举一个真实的例子。这是我们的起始位置:
\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\nRun Code Online (Sandbox Code Playgroud)\n仔细观察图表。我们正在dev. 我们有一个遥控器origin,并且我们领先于我们的远程跟踪分支origin/dev。从at 中dev分离出来,然后它就消失了maina
x y z \nRun Code Online (Sandbox Code Playgroud)\n与此同时,main去
a b c\nRun Code Online (Sandbox Code Playgroud)\n现在让我们尝试一下
\ngit rebase main\nRun Code Online (Sandbox Code Playgroud)\n我们在dev,所以这意味着
git rebase main main dev\nRun Code Online (Sandbox Code Playgroud)\n这意味着“从之后开始抓取提交main并继续到dev\xe2\x80\x94 即x y z\xe2\x80\x94 并将它们附加到main.” 我们开始...这是我们得到的:
* f6b903e (HEAD -> dev) z\n* 4adb109 y\n* e9cc7fd x\n* 8032a5d (origin/main, main) c\n* 2caa1e9 b\n* 06c7439 a\nRun Code Online (Sandbox Code Playgroud)\n是的,正如我所说。我认为,这是大多数人在使用单参数时所期望的git rebase。
好吧,现在重新开始。这次我们要说的是
\ngit rebase --onto main\nRun Code Online (Sandbox Code Playgroud)\n正如托雷克的回答告诉你的那样,这意味着
\ngit rebase --onto main origin/dev dev\nRun Code Online (Sandbox Code Playgroud)\n所以这意味着“抓取从\xe2\x80\x94 到origin/dev\ xe2\x80\x94 的所有内容并将其附加到。这非常令人惊讶!我们从未说过任何关于 的事情,但这就是当我们变基时 Git 将剪断我们的分支的地方. 开始吧...这是我们得到的结果:devzmainorigin/dev
* 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\nRun Code Online (Sandbox Code Playgroud)\n这可能就是发生在你(OP)身上的事情。很容易理解为什么您会感到惊讶!
\n因此,我认为主要的结论是,如果您省略这三个参数中的任何一个,您可能会对 Git 为它们选择的内容感到惊讶。因此,同样在我看来,你不应该遗漏其中任何一个!你只是不知道如果你这样做会发生什么。
\n最后说明:好吧,我撒了一点谎。还记得第一个结果吗?
\n* f6b903e (HEAD -> dev) z\n* 4adb109 y\n* e9cc7fd x\n* 8032a5d (origin/main, main) c\n* 2caa1e9 b\n* 06c7439 a\nRun Code Online (Sandbox Code Playgroud)\norigin/dev我从那个图表中遗漏了。事实上,我们现在拥有的是:
* 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\nRun Code Online (Sandbox Code Playgroud)\n注意和提交的重复。这就是它的作用:它复制提交。如果我们打算推送,这是一个棘手的情况,因为我们将要求遥控器忘记当前指向的和,并且它不会对此感到高兴。xygit rebasedevoriginyxorigin/dev
编辑:我忘记了最后一个\xe2\x80\x94或第一个\xe2\x80\x94点,我将首先在此处插入。的用法大大git rebase简化了:
git rebase [ --onto <newbase> ] [ <upstream> ]\nRun Code Online (Sandbox Code Playgroud)\n方括号[...]表示每个参数都是可选的。尖括号<...>意味着您可以在此处填写一些内容。该选项使用一个标志;当且仅当它前面有关键字 (用双连字符拼写)时(由您,用户)给出the 。类似地,当且仅当您给出参数时,该参数才是给出的。所以:--onto newbasenewbase--ontoupstream
git rebase master\nRun Code Online (Sandbox Code Playgroud)\n给出一个参数,一个upstream, of master; 给出master的git rebase --onto master一个参数 a 。如果你不给出论据,那就自己找一个论据。如果你不给出论据,那就自己找一个论据。如果你给出了一个,但没有给出另一个,仍然会自己找到另一个。newbaseupstreamgit rebasenewbasegit rebasegit rebase
作为一个简单的答案,然后:git rebase master选择master作为目标和上游,但git rebase --onto master选择master作为目标,使用默认的上游,无论当前分支是什么。您可以使用以下命令查看当前分支的默认值:
git rev-parse --abbrev-ref @{upstream}\nRun 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。
要履行其职责\xe2\x80\x94,也就是说,复制一些提交,然后移动一个分支名称\xe2\x80\x94,git rebase需要知道三件事:
其中最后一个默认为当前分支。1 所以你只需运行git checkoutorgit switch首先,选择正确的分支。2
Git 作者巧妙地将这三件事中的其余两项塞进了一个参数中git rebase,文档将其称为upstream参数。
然而,有时您确实需要将这两件事分开。该--onto标志允许您将它们分开:
git rebase --onto <newbase> <upstream>\nRun Code Online (Sandbox Code Playgroud)\n将提交复制到提供的newbase,而不是将它们复制到提供的upstream。
令人好奇的不是这个非常newbase简单的问题,而是如何使用这个upstream参数。它的使用方式很复杂,但为了将其简化为要点,Git 运行:
git rev-list upstream..HEAD\nRun Code Online (Sandbox Code Playgroud)\n(在执行初始git checkout或后git switch,如果您提供branch参数)。因此upstream,不是指定要复制什么,而是指定不复制什么。
rebase 命令将复制一些提交集。 这是给定的,因为目标git rebase是以某种方式或形式采取一些不够好的现有提交,并将它们转变为改进的提交\xe2\x80\x94,但实际上不可能更改任何提交,一旦它是被制造出来的。由于现有提交无法更改,因此最好的git rebase办法是将它们复制到新的和改进的副本,然后开始使用副本代替原始副本。
“使用副本而不是原件”这一步骤使得需要移动一个分支名称。Git使用分支名称查找提交。分支名称一直在变化,通常以一种简单、一次一步、易于遵循的方式变化。但因为名字可以移动,git reset其他 Git 命令也可以移动它们,甚至可能是暴力地移动它们,所以一次会进行许多次提交,将它们从中国的家乡绑架到澳大利亚内陆地区或其他地方。在 的情况下git rebase,变基代码首先将选定的、旧的和糟糕的提交复制到它们的新家,进行更改\xe2\x80\x94我们希望\xe2\x80\x94改进它们,或者至少使它们适合它们的新家。然后它移动分支名称,以便我们找到副本而不是原始\xe2\x80\x94,然后完成变基。
该upstream参数指定不复制的内容,有时\xe2\x80\x94实际上非常频繁!\xe2\x80\x94这个相同的说明符可以用作要复制的提交应该去的地方。但是,当它不能以这种方式使用时,--onto参数可以让您指定复制的位置和不复制的内容,作为两个单独的事物。
git | git 将旧提交移到另一个分支的过去显示了一种情况,可以方便地将不复制的内容和复制到的位置指定为两个单独的事物。
\n1事实上,只能git rebase移动当前分支(从最新的 Git 版本开始,2.32 左右,但可能还需要很长一段时间)\xe2\x80\x94so 如果您提供分支名称,请使用或开始。参见脚注 2。git rebase git checkoutgit switch
2您可以向命令行命令提供分支名称。如果这样做,该命令当前实际上会运行或已内置必要的签出/切换操作。变基完成后,您将位于所选的分支上,即使在变基开始之前您不在该分支上。也就是说,在:
\ngit checkout main # puts us on `main`\ngit rebase origin/foo foo\nRun Code Online (Sandbox Code Playgroud)\n我们不妨运行:
\ngit checkout foo\ngit rebase origin/foo\nRun Code Online (Sandbox Code Playgroud)\n无论如何,因为我们最终的结果是foo,而不是main。但这确实意味着如果我们必须运行git checkoutfoo,我们可以运行:
git rebase origin/foo foo\nRun Code Online (Sandbox Code Playgroud)\n或者:
\ngit rebase --onto target origin/foo foo\nRun Code Online (Sandbox Code Playgroud)\n以便为我们git rebase做这件事git checkout。