什么是 git rebase?
git rebase 是将更改从一个分支合并到另一个分支的两个 git 过程之一 (另一个是git merge)。
虽然 merge 总是可以简单的地合并分支,但是 rebase 可以更强大(也更危险) ,因为它允许我们重写历史。
为什么需要使用 git rebase 呢?
rebase 是将一系列提交移动或组合(即压缩)到一个新 base 的提交过程。
想象一下,你现在正在处理一个 feature 分支,过了一段时间,你意识到 main (有时候也叫“master”)分支,已经被其他人的更新了。这意味着你的分支现在已经偏离了主分支。在某些时候,你可能希望在 feature 分支中包含 main 分支上的更改。常见做法是简单地从 main 中进行一个 git pull,这样将会在 feature 分支添加一个 commit 记录。
这种方法的问题可能是:
合并提交可能非常模糊,在 git 树中提供的信息非常少,这使得调试变得更加困难
多次通过pull/merge更新一个分支可以得到类似下面这样的 git 历史记录
rebase 的目标是保持一个干净、清晰的项目历史记录。因此,我们可以告诉分支将其提交移动到最新更改的顶部,而不是 pull main 分支更改并添加合并提交。
通过将 feature 分支的 rebase 到 main 分支的顶部,我们可以在获取最新的 main 更新时保持所有更改和提交不变。为了实现这一点,我们可以在 feature 分支中执行以下命令:
$ git rebase main
这将启动 rebase 过程。如果没有发现任何冲突,你应该会看到一条成功消息,并且你的 feature 分支现在与 main 分支保持同步,这样我们离类似于以下内容的项目历史记录又近了一步:
交互式 rebase
交互式 rebase 带来的力量,可以让 rebase 到一个全新的阶段!它允许我们以不同的方式交互式地更改多个提交。
您可以通过执行 rebase 命令,后跟-i 参数和我们想要修改的提交来启动交互式 rebase:
$ git rebase -i <commit hash | HEAD position>
通过查看 CLI 中显示的命令列表,我们可以知道这个工具有多么强大!我们可以编辑提交, squash 它们,delete 它们,edit 它们,等等。我们甚至可以永久地更改提交顺序!
squash
squash 允许我们指定要组合的提交,以帮助维护干净的历史记录。
像这样的东西看起来很熟悉吗?
如果你喜欢在工作时提交很多东西,你可能不喜欢考虑对每一条提交消息都进行详细的描述。在这些情况下,squash 可能非常有用,因为启动交互式 rebase,然后您可以告诉 git 哪些提交将被“合并”为一个(或多个),然后编辑其提交消息。
请记住,我们必须始终将一个提交 squash 到另一个提交中。
因此,假设我们有 6 个提交,我们认为其中只有两个是必需的,我们可以压缩其他 4 个,如下所示:
$ git rebase -i HEAD~
在这种情况下,我们说我们要合并提交 2 - 5,并将它们包含在第一个(77497f5)中,并保持最后一个提交不变。
接下来,git 将每个提交重新定位到他们的新 base(77497f5),如果没有冲突,你将能够更改他们的最终提交消息,瞧!总共只有 2 次提交!
我们可能发生的错误
冲突
与 git 合并时发生的情况类似,用另一个分支更新分支可能会导致代码冲突。
rebase 冲突的一大区别在于,我们处理的不是一个合并提交,而是(可能)移动多个提交。
这意味着解决冲突的 rebase 是一个迭代过程,通过每个提交被 rebase。
在一个有两次提交的 feature 分支中,如果在 rebase 提交 A 的基础时发现了冲突,我们就修复它,告诉 git 继续进程到下一次提交,修复提交 B 上的新冲突。
rebase 后 push 本地分支
rebase 本地 feature 分支的 base 意味着我们正在重写历史,所以这实际上意味着我们没有选择提交给新基础的特性分支。尽管看上去像是一切都是相同的 git 在目标分支上创建了新的提交。
在我们的交互式 rebase 的任何时候,如果事情变得太混乱,我们不再知道如何修复它们,我们可以中止整个进程在终端和交互式 rebase :
$ git rebase --abort
结论
正如你所看到的,rebase 可以用于许多不同的目的,特别是使用交互式 rebase 功能。
如果在理解 git rebase 的遇到一些问题不要担心,这个理解起来本来就不是很容易,大家可以静下心慢慢去研究。