Git Rebase手册 – Rebase权威指南

发布时间 2023-07-04 10:46:15作者: 晓风晓浪

开发人员工具箱中最强大的工具之一是git rebase. 然而,它却因复杂且容易被误解而臭名昭著。

事实是,如果您了解它的实际用途,就会git rebase发现它是一个非常优雅且简单的工具,可以在 Git 中实现许多不同的事情。

在之前的文章中,您了解了Git 差异是什么、合并是什么以及Git 如何解决合并冲突。在这篇文章中,您将了解 Git rebase 是什么,为什么它与 merge 不同,以及如何自信地进行 rebase ??

(更|多优质内|容:java567 点 c0m)

 

开始之前的注意事项

  1. 我还制作了一个涵盖这篇文章内容的视频。如果您想在阅读的同时观看,可以在这里找到。

  2. 如果您想使用我使用的存储库并亲自尝试命令,您可以在此处获取存储库。

  3. 我正在写一本关于 Git 的书!您有兴趣阅读初始版本并提供反馈吗?

好的,你准备好了吗?

简短回顾 - 什么是 Git Merge??

在幕后,git rebase和git merge是非常非常不同的东西。那么为什么人们总是比较他们呢?

原因在于它们的用途。使用 Git 时,我们通常在不同的分支中工作并对这些分支引入更改。

在之前的教程中,我举了一个例子,约翰和保罗(披头士乐队的成员)正在共同创作一首新歌。他们从main分支开始,然后各自发散,修改歌词并提交自己的更改。

然后,两人想要集成他们的更改,这是使用 Git 时经常发生的事情。

一段不同的历史——paul_branch并且john_branch背离了main(来源:Brief)

有两种主要方法可以集成 Git 中不同分支中引入的更改,或者换句话说,不同的提交和提交历史记录。这些是合并和变基。

在之前的教程中,我们已经git merge非常了解了。我们看到,在执行合并时,我们创建一个合并提交- 该提交的内容是两个分支的组合,并且它还有两个父级,每个分支一个。

因此,假设您位于分支上john_branch(假设上图中描述的历史记录),然后运行git merge paul_branch. 您将进入这种状态 – 在 上john_branch,有两个父母的新提交。第一个是执行合并之前指向的john_branch分支上的提交,在本例中为“Commit 6”。HEAD第二个是paul_branch“Commit 9”指向的提交。

运行结果git merge paul_branch:有两个父级的新合并提交(来源:Brief)

再看看历史图表:您创建了一个分歧的历史。您实际上可以看到它在哪里分支以及在哪里再次合并。

因此,在使用时git merge,您不会重写历史记录 - 而是向现有历史记录添加提交。具体来说,是创建分歧历史记录的提交。

与 有何git rebase不同git merge??

使用时git rebase,会发生不同的情况。?

让我们从大局开始:如果您在 上paul_branch,并使用git rebase john_branch,Git 将转到 John 分支和 Paul 分支的共同祖先。然后,它采用 Paul 分支上的提交中引入的补丁,并将这些更改应用到 John 的分支。

因此,在这里,您通常rebase会获取在一个分支(Paul 的分支)上提交的更改,然后在另一个分支上重播它们john_branch。

运行结果:上面git rebase john_branch的提交被“重放” (来源:Brief)paul_branch``john_branch

等等,这是什么意思??

我们现在将一点一点地了解这一点,以确保您完全了解幕后发生的事情?

cherry-pick作为 Rebase 的基础

将变基视为执行是有用的git cherry-pick- 一个命令接受一次提交,通过计算父级提交和提交本身之间的差异来计算此提交引入的补丁,然后cherry-pick“重放”此差异。

让我们手动执行此操作。

如果我们通过执行以下命令来查看“Commit 5”引入的差异git diff main <SHA_OF_COMMIT_5>:

运行git diff观察“Commit 5”引入的补丁(来源:Brief)

(如果您想使用我使用的存储库并亲自尝试命令,您可以在此处获取存储库)。

您可以看到,在此提交中,约翰开始创作一首名为“Lucy in the Sky with Diamonds”的歌曲:

git diff“Commit 5”引入的补丁的输出(来源: Brief)

提醒一下,您还可以使用以下命令git show获得相同的输出:

 git show <SHA_OF_COMMIT_5>

现在,如果您cherry-pick进行此提交,您将在活动分支上专门引入此更改。切换到main第一个:

git checkout main(或git switch main)

并创建另一个分支,只是为了清楚起见:

git checkout -b my_branch(或git switch -c my_branch)

创建my_branch分支main(来源:Brief)

而cherry-pick这个提交:

 git cherry-pick <SHA_OF_COMMIT_5>

用于cherry-pick将“Commit 5”中引入的更改应用到main(来源:Brief)

考虑日志( 的输出git lol):

的输出git lol(来源:Brief)

(git lol是我添加到 Git 中的别名,以便以图形方式直观地查看历史记录。您可以在此处找到它)。

看来您复制粘贴了“Commit 5”。请记住,即使它具有相同的提交消息,并引入相同的更改,甚至在本例中指向与原始“Commit 5”相同的树对象 - 它仍然是一个不同的提交对象,因为它是使用不同的时间戳。

查看更改,使用git show HEAD:

的输出git show HEAD(来源:Brief)

它们与“Commit 5”相同。

当然,如果您查看该文件(例如,通过使用nano lucy_in_the_sky_with_diamonds.md),它将处于与原始“Commit 5”之后相同的状态。

凉爽的!?

好的,您现在可以删除新分支,这样它就不会每次都出现在您的历史记录中:

 git checkout main
 git branch -D my_branch

Beyond cherry-pick– 如何使用git rebase

您可以将git rebase其视为一种依次执行多个cherry-pick操作的方法,即“重播”多个提交。这不是您可以做的唯一事情rebase,但它是我们解释的一个很好的起点。

是时候一起玩了git rebase!????

之前,你paul_branch并入john_branch. 如果您基于重新建立 paul_branch基础, 会发生什么john_branch?你会得到一段非常不同的历史。

从本质上讲,我们似乎采用了 上的提交中引入的更改paul_branch,并在 上重播了它们john_branch。结果将是一个线性历史。

为了理解这个过程,我将提供高层次的视图,然后深入研究每个步骤。将一个分支变基到另一分支之上的过程如下:

  1. 寻找共同祖先。

  2. 确定要“重播”的提交。

  3. 对于每次提交X,计算diff(parent(X), X)并将其存储为patch(X).

  4. 迁往HEAD新基地。

  5. 将生成的补丁按顺序应用到目标分支上。每次,创建一个具有新状态的新提交对象。

使用与现有变更集相同的变更集进行新提交的过程也称为“重放”这些提交,这是我们已经使用过的术语。

是时候实践 Rebase 了??

从Paul的分支开始:

 git checkout paul_branch

这是历史:

执行前提交历史记录git rebase(来源:Brief)

现在,到了令人兴奋的部分:

 git rebase john_branch

并观察历史:

rebase后的历史(来源:Brief)

(是我在视频中gg介绍的外部工具的别名)。

因此,随着git merge你被添加到历史中,随着git rebase你重写历史。您创建新的提交对象。此外,结果是线性历史图,而不是发散图。

rebase后的历史(来源:Brief)

本质上,我们“复制”了“Commit 4”之后引入的提交paul_branch,并将它们“粘贴”到john_branch.

该命令称为“rebase”,因为它更改了运行它的分支的基本提交。也就是说,在您的情况下,在运行之前git rebase, 的基础paul_branch是“Commit 4” - 因为这是分支“诞生”的地方(来自main)。通过rebase,您要求 Git 给它另一个基础 - 也就是说,假装它是从“Commit 6”诞生的。

为此,Git 采用了以前的“Commit 7”,并将此提交中引入的更改“重播”到“Commit 6”上,然后创建了一个新的提交对象。这个对象与原来的“Commit 7”有三个方面的不同:

  1. 它有不同的时间戳。

  2. 它有一个不同的父提交 - “Commit 6”而不是“Commit 4”。

  3. 它指向的树对象不同 - 因为更改是引入到“Commit 6”指向的树,而不是“Commit 4”指向的树。

请注意此处的最后一次提交“Commit 9'”。它所代表的快照(即它指向的树)与通过合并两个分支得到的树完全相同。Git 存储库中文件的状态与您使用git merge. 只是历史不同,当然还有提交对象。

现在,您可以简单地使用:

 git checkout main
 git merge paul_branch

嗯...如果您运行最后一条命令会发生什么?? 检查后再次考虑提交历史记录main:

变基和检出后的历史main(来源:Brief)

main合并和意味着什么paul_branch?

事实上,Git 可以简单地执行快进合并,因为历史记录是完全线性的(如果您需要有关快进合并的提醒,请查看这篇文章)。结果,main现在paul_branch指向相同的提交:

快进合并的结果(来源:Brief)

Git 中的高级变基??

现在您已经了解了 rebase 的基础知识,是时候考虑更高级的情况了,在这些情况下,命令的附加开关和参数rebase将派上用场。

在前面的示例中,当您仅表示rebase(没有附加开关)时,Git 会重播从共同祖先到当前分支尖端的所有提交。

但 rebase 是一种超级力量,它是一个全能的命令,能够……嗯,重写历史。如果您想修改历史记录以使其成为您自己的历史记录,它会派上用场。

通过再次指向“Commit 4”来撤消上次合并main:

 git reset -–hard <ORIGINAL_COMMIT 4>

“撤消”最后一次合并操作(来源:Brief)

并使用以下命令撤消变基:

 git checkout paul_branch
 git reset -–hard <ORIGINAL_COMMIT 9>

“撤消”变基操作(来源:Brief)

请注意,您获得的历史记录与以前的历史记录完全相同:

可视化“撤消”变基操作后的历史记录(来源:Brief)

再次需要明确的是,当无法从当前 .commit 9 访问时,“Commit 9”并不会消失HEAD。相反,它仍然存储在对象数据库中。当您git reset现在更改HEAD为指向此提交时,您能够检索它及其父提交,因为它们也存储在数据库中。很酷吧??

好的,快速查看 Paul 引入的更改:

 git show HEAD

git show HEAD显示“Commit 9”引入的补丁(来源:Brief)

在提交图中继续向后移动:

 git show HEAD~

git show HEAD~(同git show HEAD~1)显示“Commit 8”引入的补丁(来源:Brief)

并进一步承诺:

 git show HEAD~2

git show HEAD~2显示“Commit 7”引入的补丁(来源:Brief)

所以,这些改变很好,但也许保罗不想要这样的历史。相反,他希望看起来好像他将“Commit 7”和“Commit 8”中的更改作为单个提交引入。

为此,您可以使用交互式变基。为此,我们将-i(或--interactive) 开关添加到rebase命令中:

 git rebase -i <SHA_OF_COMMIT_4>

或者,由于main指向“Commit 4”,我们可以简单地运行:

 git rebase -i main

通过运行此命令,您可以告诉 Git 使用新的基础“Commit 4”。因此,您要求 Git 返回“Commit 4”之后引入的所有提交,并且可以从 current 访问这些提交HEAD,并重播这些提交。

对于重播的每个提交,Git 都会询问我们想用它做什么:

git rebase -i main提示您选择每次提交要执行的操作(来源:Brief)

在这种情况下,将提交视为补丁是很有用的。也就是说,“Commit 7”如““Commit 7”在其父级之上引入的补丁”一样。

一种选择是使用pick. 这是默认行为,它告诉 Git 重放此提交中引入的更改。在这种情况下,如果您保持原样 - 以及pick所有提交 - 您将获得相同的历史记录,并且 Git 甚至不会创建新的提交对象。

另一种选择是squash。压缩的提交会将其内容“折叠”到其前面的提交的内容中。因此,在我们的例子中,Paul 希望将“Commit 8”压缩为“Commit 7”:

将“Commit 8”压缩为“Commit 7”(来源:Brief)

如您所见,git rebase -i提供了其他选项,但我们不会在本文中讨论所有选项。如果您允许运行变基,系统将提示您为新创建的提交选择一条提交消息(即引入“Commit 7”和“Commit 8”更改的消息):

提供提交消息:(Commits 7+8来源:Brief)

看看历史:

交互式rebase之后的历史(来源:Brief)

正是我们想要的!我们有paul_branch“Commit 9”(当然,它是与原始“Commit 9”不同的对象)。这里指向“Commits 7+8”,这是一个单一的提交,同时引入了原始“Commit 7”和原始“Commit 8”的更改。该提交的父级是“Commit 4”,main指向哪里。你有john_branch。

交互式变基后的历史 - 可视化(来源:Brief)

哦,哇,这不是很酷吗??

git rebase让您可以无限制地控制任何分支的形状。您可以使用它来重新排序提交,或删除不正确的更改,或回顾修改更改。或者,您也许可以将分支的基础移动到另一个提交(您希望的任何提交)。

如何使用--onto开关git rebase

让我们再考虑一个例子。再次进入main:

 git checkout main

并删除指向的指针paul_branch,john_branch这样您就不会再在提交图中看到它们:

 git branch -D paul_branch
 git branch -D john_branch

现在分支main到一个新分支:

 git checkout -b new_branch

创造new_branch不同于main(来源:Brief)

new_branch与此不同的干净历史main(来源:Brief)

现在,在此处添加一些更改并提交它们:

 nano code.py

new_branch添加该功能code.py(来源:Brief)

 git add code.py
 git commit -m "Commit 10"

回到main:

 git checkout main

并引入另一个变化:

在文件开头添加了文档字符串(来源:Brief)

是时候准备并提交这些更改了:

 git add code.py
 git commit -m "Commit 11"

还有另一个变化:

添加@Author到文档字符串(来源:Brief)

也提交此更改:

 git add code.py
 git commit -m "Commit 12"

哦等等,现在我意识到我希望您将“Commit 11”中引入的更改作为new_branch. 啊。你能做什么??

回顾一下历史:

引入“Commit 12”后的历史(来源:Brief)

我想要的是,我不希望“Commit 10”仅驻留在分支上main,而是希望它同时位于main分支和new_branch. 从视觉上看,我想将它移到图表中:

从视觉上看,我希望你“推动”“Commit 10”(来源:Brief)

你能看到我要去哪里吗??

嗯,正如我们所理解的,rebase 允许我们基本上重放在“Commit 10”中引入的更改new_branch,就好像它们最初是在“Commit 11”而不是“Commit 4”上进行的。

为此,您可以使用 的其他参数git rebase。您会告诉 Git,您想要获取main和的共同祖先new_branch(即“Commit 4”)之间引入的所有历史记录,并将该历史记录的新基础设为“Commit 11”。为此,请使用:

 git rebase -–onto <SHA_OF_COMMIT_11> main new_branch

rebase前后的历史,“Commit 10”已被“推送”(来源:Brief)

看看我们美丽的历史!?

rebase前后的历史,“Commit 10”已被“推送”(来源:Brief)

让我们考虑另一个案例。

假设我开始在一个分支上工作,并且错误地从 开始工作feature_branch_1,而不是从 开始工作main。

因此,要模拟这一点,请创建feature_branch_1:

 git checkout main
 git checkout -b feature_branch_1

并擦除,new_branch这样您就不会再在图表中看到它:

 git branch -D new_branch

创建一个简单的 Python 文件,名为1.py:

一个新文件,1.py,包含print('Hello world!')(来源:Brief)

暂存并提交此文件:

 git add 1.py
 git commit -m "Commit 13"

现在(错误地)从以下分支出来feature_branch_1:

 git checkout -b feature_branch_2

并创建另一个文件2.py:

创建2.py(来源:Brief)

也暂存并提交该文件:

 git add 2.py
 git commit -m "Commit 14"

并引入更多代码2.py:

修改2.py(来源:Brief)

也暂存并提交这些更改:

 git add 2.py
 git commit -m "Commit 15"

到目前为止你应该有这样的历史:

引入“Commit 15”后的历史(来源:Brief)

返回feature_branch_1并编辑1.py:

 git checkout feature_branch_1

修改1.py(来源:Brief)

现在暂存并提交:

 git add 1.py
 git commit -m "Commit 16"

您的历史记录应该如下所示:

引入“Commit 16”后的历史(来源:Brief)

说现在你意识到你犯了一个错误。你实际上想feature_branch_2从树枝中诞生main,而不是从……中诞生feature_branch_1。

你怎样才能做到这一点??

--onto尝试根据历史图以及您对命令标志的了解来思考它rebase。

好吧,您想要将 上的第一个提交feature_branch_2(即“Commit 14”)的父级“替换”到main分支顶部(在本例中为“Commit 12”),而不是在分支顶部feature_branch_1(在本例中为“”)提交 13 英寸。同样,您将创建一个新的基础,这次 是为了第一次提交feature_branch_2.

您想要移动“Commit 14”和“Commit 15”(来源:Brief)

你会怎么做?

首先,切换到feature_branch_2:

 git checkout feature_branch_2

现在您可以使用:

 git rebase -–onto main <SHA_OF_COMMIT_13>

因此,您feature_branch_2基于main而不是feature_branch_1:

执行rebase后的提交历史(来源:Brief)

该命令的语法是:

 git rebase --onto <new_parent> <old_parent>

如何在单个分支上变基

git rebase您还可以在查看单个分支的历史记录时使用。

让我们看看你是否可以在这里帮助我。

假设我工作feature_branch_2并专门编辑了该文件code.py。我首先将所有字符串更改为用双引号而不是单引号括起来:

更改'为"in code.py(来源:Brief)

然后,我上演并承诺:

 git add code.py
 git commit -m "Commit 17"

然后我决定在文件的开头添加一个新函数:

添加功能another_feature(来源:Brief)

我再次上演并承诺:

 git add code.py
 git commit -m "Commit 18"

现在我意识到我实际上忘记将单引号更改为双引号main(正如您可能已经注意到的那样),所以我也这样做了:

改成(来源'main':简报)"main"

当然,我策划并承诺了这一改变:

 git add code.py
 git commit -m "Commit 19"

现在,回顾一下历史:

引入“Commit 19”后的提交历史(来源:Brief)

 

这不太好,不是吗?我的意思是,我有两个彼此相关的提交,“Commit 17”和“Commit 19”(将's 变成"s),但它们被不相关的“Commit 18”(我在其中添加了一个新函数)分开。我们可以做什么?? 你能帮我吗?

直觉上,我想在这里编辑历史记录:

这些是我要编辑的提交(来源:Brief)

那么,你会怎么做?

你是对的!??

我可以在“Commit 15”之上将历史记录从“Commit 17”重新设置为“Commit 19”。要做到这一点:

 git rebase --interactive --onto <SHA_OF_COMMIT_15> <SHA_OF_COMMIT_15>

请注意,我指定“Commit 15”作为提交范围的开头,不包括此提交。而且我不需要明确指定HEAD为最后一个参数。

rebase --onto在单个分支上使用(来源: Brief)

按照您的建议并运行rebase命令后(谢谢!?)我得到以下屏幕:

交互式变基(来源:Brief)

那么我该怎么办呢?我想将“Commit 19”放在“Commit 18”之前,因此它位于“Commit 17”之后。我可以更进一步,将它们压在一起,如下所示:

交互式变基 - 更改提交和压缩的顺序(来源:Brief)

现在,当我收到提交消息提示时,我可以提供消息“Commit 17+19”:

提供提交消息(来源:Brief)

现在,看看我们美丽的历史:

由此产生的历史(来源:Brief)

再次感谢!??

更多变基用例+更多实践

到目前为止,我希望您对 rebase 的语法感到满意。真正理解它的最好方法是考虑各种情况并自己找出解决方法。

对于即将到来的用例,我强烈建议您在介绍完每个用例后停止阅读,然后尝试自己解决它。

如何排除提交

假设您在另一个存储库上有此历史记录:

另一个提交历史(来源:Brief)

 

在使用它之前,将标签存储到“Commit F”,以便稍后可以返回:

 git tag original_commit_f

现在,您实际上不希望包含“Commit C”和“Commit D”中的更改。您可以像以前一样使用交互式变基并删除它们的更改。或者,可以再次使用git rebase -–onto。您将如何使用--onto来“删除”这两个提交?

您可以HEAD在“Commit B”之上进行变基,其中旧的父级实际上是“Commit D”,现在它应该是“Commit B”。再回顾一下历史:

再次回顾历史(来源:Brief)

变基使“Commit B”成为“Commit E”的基础,意味着“移动”“Commit E”和“Commit F”,并给它们另一个基础—— “ Commit B”。你能自己想出这个命令吗?

 git rebase --onto <SHA_OF_COMMIT_B> <SHA_OF_COMMIT_D> HEAD

请注意,使用上面的语法不会移动main到指向新的提交,因此结果是“分离” HEAD。如果您使用gg或其他显示可从分支访问的历史记录的工具,它可能会让您感到困惑:

变基并--onto产生分离结果HEAD(来源:Brief)

但如果您只是使用git log(或我的别名git lol),您将看到所需的历史记录:

由此产生的历史(来源:Brief)

我不了解你,但这些事情让我真的很高兴。??

顺便说一句,您可以省略HEAD上一个命令,因为这是第三个参数的默认值。所以只需使用:

 git rebase --onto <SHA_OF_COMMIT_B> <SHA_OF_COMMIT_D>

会有同样的效果。最后一个参数实际上告诉 Git 当前的 rebase 提交序列的结尾在哪里。git rebase --onto所以带有三个参数的语法是:

 git rebase --onto <new_parent> <old_parent> <until>

如何跨分支移动提交

假设我们得到了与之前相同的历史记录:

 git checkout original_commit_f

现在我只想要“提交 E”位于基于“提交 B”的分支上。也就是说,我想要一个新分支,从“Commit B”分支,只有“Commit E”。

当前的历史,考虑“Commit E”(来源:Brief)

那么,这对于 rebase 来说意味着什么呢?考虑上面的图片。我应该重新设定哪些提交(或哪些提交),以及哪个提交将成为新的基础?

我知道我可以在这里依靠你?

我想要的是仅采用“Commit E”,并且仅此提交,并将其基础更改为“Commit B”。换句话说,将“提交 E”中引入的更改重播到“提交 B”上。

你能将该逻辑应用到 的语法中吗git rebase?

这是(为了简洁,这次我写<COMMIT_B>的是<SHA_OF_COMMIT_B>):

 git rebase –-onto <COMMIT_B> <COMMIT_D> <COMMIT_E>

现在历史看起来是这样的:

rebase后的历史(来源:Brief)

惊人的!

关于冲突的注意事项

请注意,执行变基时,您可能会像合并时一样遇到冲突。您可能会遇到冲突,因为在变基时,您试图在不同的基础上应用补丁,也许补丁不适用。

例如,再次考虑以前的存储库,具体来说,考虑“Commit 12”中引入的更改,由 指向main:

 git show main

“Commit 12”中引入的补丁(来源:Brief)

我已经在上一篇文章git diff中详细介绍了 的格式,但作为一个快速提醒,此提交指示 Git 在两行上下文之后添加一行:

 ```
 This is a sample file

在这三行上下文之前:

 ```
 def new_feature():
  print('new feature')

假设您正在尝试将“Commit 12”重新设置为另一个提交。如果由于某种原因,这些上下文行并不像您要变基到的提交的补丁中那样存在,那么您将遇到冲突。要了解有关冲突以及如何解决冲突的更多信息,请参阅本指南。

缩小大局

比较变基和合并(来源:Brief)

git merge在本指南的开头,我首先提到和 之间的相似之处git rebase:两者都用于整合不同历史中引入的变化。

但是,正如您现在所知,它们的运作方式非常不同。合并会产生发散的历史记录,而变基则会产生线性历史记录。这两种情况都可能发生冲突。上表中还有一列需要密切关注。

现在您知道什么是“Git rebase”,以及如何使用交互式 rebase,或者rebase --onto,正如我希望您同意的那样,git rebase它是一个超级强大的工具。然而,与合并相比,它有一个巨大的缺点。

Git rebase 改变了历史。

这意味着您不应该对存储库本地副本之外存在的提交进行变基,而其他人可能已经基于这些提交进行了提交。

换句话说,如果唯一有问题的提交是您在本地创建的提交 - 继续,使用 rebase,尽情发挥。

但是,如果提交已被推送,这可能会导致一个巨大的问题 - 因为其他人可能会依赖这些提交,而您稍后会覆盖这些提交,然后您和他们将拥有不同版本的存储库。

正如我们所看到的,这与merge不修改历史不同。

例如,考虑我们重新设置基础并导致此历史记录的最后一个案例:

rebase后的历史(来源:Brief)

现在,假设我已经将此分支推送到远程。在我推送分支后,另一位开发人员将其拉出并从“Commit C”分支出来。另一位开发人员不知道与此同时,我正在本地重新调整我的分支,并稍后再次推送它。

这会导致不一致:其他开发人员所使用的提交在我的存储库副本上不再可用。

我不会在本指南中详细说明这到底是什么原因,因为我的主要信息是您绝对应该避免这种情况。如果您对实际发生的情况感兴趣,我将在下面留下一个有用资源的链接。现在,让我们总结一下我们所涵盖的内容。

回顾

在本教程中,您了解了git rebase,一个重写 Git 历史记录的超级强大工具。您考虑了一些git rebase有用的用例,以及如何使用一个、两个或三个参数(带或不带开关)来使用它--onto。

我希望我能够让您相信这git rebase很强大,而且一旦您掌握了要点,它就非常简单。它是一个“复制粘贴”提交(或更准确地说,补丁)的工具。它是一个值得拥有的有用工具。

(更|多优质内|容:java567 点 c0m)