# Rebase 代替合并
虽然合并(merge)操作可以用来简单和方便地整合改动,但是它却不是唯一的方法。“Rebase” 就是另一种替代手段。
##### 注释
虽然 rebase 相对于我们已知的整合操作来说有着比较显著的优点,但是这也是在很大程度上取决于个人的喜好。一些团队喜欢使用 rebase,而另一些可能倾向于使用合并。
Rebase 相对于合并来说是比较复杂的。我建议你可以跳过这一章,除非你和你的团队确定会用到 rebase 操作。当你积累了 Git 的一些基本使用流程的实践经验后,你也可以在以后的时间再回来学习本章的内容。
## 深入了解合并操作
在你进入 rebase 这个主题前,我们有必要来再次探讨一下更多关于合并操作的细节。当 Git 执行一个合并时,它实际上会查找三个提交:
* (1)共同的原始提交
如果你在项目中查看两个分支的历史,它们总是会出自于一次共同的提交,那么在当时的时间点上,这两个分支还是拥有相同的内容。之后它们就开始有了差别。
* (2) + (3) 两个分支的最终点
合并操作的目的就是把两个分支的最新状态结合起来。因此他们各自的最新版本是有特殊含义的。
结合这三个提交后得到的结果就是我们整合的目标。
## 快进或合并提交
一种最简单的情况是,在其中的一个分支上没有任何一个新的改动提交发生。那么在它之前的最后一次提交就仍然还是那个共同的原始提交。
![starting-situation-fast-forward](https://box.kancloud.cn/2016-05-04_572967e282802.gif)
在这种情况下,执行整合操作就非常简单了。 Git 仅仅需要添加所有那些在另外一个分支上的新提交就可以了。在 Git 中,这种最简单的整合操作我们称之为 “快进(fast-forward)”合并。之后两个分支就拥有了完全相同的历史。
![end-situation-fast-forward](https://box.kancloud.cn/2016-05-04_572967e29d5c7.gif)
但是在大多数情况下,两个分支都会有自己不同的发展轨迹。
![starting-situation-merge-commit](https://box.kancloud.cn/2016-05-04_572967e2b3038.gif)
为了完成整合,Git 会需要创建一个新的提交来含括它们之间的差异,这就是整合提交(merge commit)。
![end-situation-merge-commit](https://box.kancloud.cn/2016-05-04_572967e2cc1f0.gif)
## 手工提交与合并提交
通常情况下,提交都是由手工精心创建的。这样也就能更好地保证一次提交只涉及一个关联改动,并且能更好地注释这个提交。
一个合并提交就不同了,它不是由开发人员手动创建的,而是由 Git 自动生成的。它也不涉及一个关联改动,其目的只是连接两个分支,就像节点一样。如果之后想要了解某个合并操作,你只需要查看这两个分支的历史记录和它们相应的提交树(version tree)。
## Rebase 整合
有些人并不喜欢使用这种自动合并提交。相反,他们希望项目拥有一个单一的历史发展轨迹。比如一条直线。在历史纪录上没有迹象表明在某些时间它被分成过多个分支。
![end-situation-rebase](https://box.kancloud.cn/2016-05-04_572967e2decae.gif)
现在就让我们一步一步地了解一下 rebase 操作吧!仍然来使用前面的例子:我们想合并分支 B 到 分支 A 中,但是这次使用 rebase 操作。
![starting-situation-rebase](https://box.kancloud.cn/2016-05-04_572967e300fab.gif)
使用下面这个非常的简单的命令:
```
$ git rebase branch-B
```
首先,Git 会 “撤销” 所有在分支 A 上的那些在与分支 B 的共同提交之后发生的提交。当然,Git 不会真的放弃这些提交,其实你可以把这些撤销的提交想像成 “_被暂时地存储_” 到另外的一个地方去了。
![rebase-step-1](https://box.kancloud.cn/2016-05-04_572967e314f0f.gif)
接下来它会整合那些在分支 B(这个我们想要整合的分支)上的还未整合的提交到分支 A 中。在这个时间点,这两个分支看起来会是一模一样的。
![rebase-step-2](https://box.kancloud.cn/2016-05-04_572967e328be2.gif)
最后,那些在分支 A 的新的提交(也就是第一步中自动撤销掉的那些提交)会被重新应用到这个分支上,但是在不同的位置上,在那些从分支 B 被整合过来的提交之后,它们就被 _re-based_ 了。
整个项目开发轨迹看起来就像发生在一条直线上。相对于一个合并提交,rebase 包括了所有的组合变化,最原始的提交结构会被保留下来。
![rebase-step-3](https://box.kancloud.cn/2016-05-04_572967e339c54.gif)
## Rebase 存在的陷阱
当然,使用 rebase 操作不会是永远一帆风顺的。很有可能会搬起石头砸自己的脚,因此你不能忽视一个重要的事实:**rebase 会改写历史记录**。
你有可能已经注意到了,在被 rebase 操作之后的版本中,提交 “C3*” 存在一个新添加的星号。这是因为,尽管这个提交的内容和 “C3” 完全一样,但是它实际上是一个不同的提交。这样做的原因是,它现在有一个新的源提交 C4(在最初创建 C3 时的源提交是 C1)。
一个提交仅仅包括很少的属性,比如作者,日期,变动和谁是它的父提交。如果改变其中任何一个信息,就必须创建一个全新的提交。当然,新的提交也会拥有一个新的 hash ID 。
如果还仅仅只是操作那些尚未发布的提交,重写历史记录本身也没有什么很大的问题。但是如果你重写了已经发布到公共服务器上的提交历史,这样做就非常危险了。其他的开发人员可能这时已经在最原始的提交 C3 上开始工作,并使它成为了一些新提交中不可或缺的部分,而现在你却把 C3 的改动设置到了另一个时间点(就是那个新的 C3*)。除此之外,通过rebase 操作,这个原始的 C3 还被删除掉了,这将是非常可怕的……
因此你应该只使用 rebase 来清理你的本地工作,千万不要尝试着对那些已经被发布的提交进行这个操作。
- Learn Version Control with Git 中文版
- 前言
- Part 1 - 基础知识
- 什么是版本控制?
- 为什么要使用版本控制系统?
- 准备工作
- 版本控制的基本工作流程
- 从一个未被纳入版本控制的项目开始
- 从一个已被纳入版本控制的项目开始
- 工作在你的项目上
- Part 2 - 分支与合并
- 分支可以改变你的生命
- 在分支上工作
- 暂时保存更改
- 切换一个本地分支
- 合并改动
- 分支的工作流程
- Part 3 - 远程仓库
- 关于远程仓库
- 连接一个远程仓库
- 查看远程数据
- 整合远程的改动
- 发布一个本地分支
- 删除分支
- Part 4 - 高级应用
- 撤销操作
- 用 diff 来检查改动
- 处理合并冲突
- Rebase 代替合并
- 子模块
- git-flow 的工作流程
- 使用 SSH 公钥验证
- Part 5 - 工具与服务
- 桌面应用程序
- 比较和整合工具
- 代码托管服务
- 更多学习资源
- 附录
- 版本控制的最佳实践
- 命令 101
- 从 Subversion 过渡到 Git
- 为什么选择 Git