git——Rebase 和 Merge
rebasing
和 merge
都是为了将一个分支的修改合并到另一个分支上,但是他们的实现方式不同。
比如说,假如我们有像下面这样的一些提交内容,merge
操作的结果像是合并了一个分支上的所有提交。而 rebase
操作将从 master 分支的最后一次提交开始添加 feature 分支中的所有更改。
- 当你
rebase
master 分支到 feature 分支,其实是将整个 feature 分支的起点移到了 master 分支的终点 Merging
获取到 feature 分支的内容并将它合并到 master 分支,最后只有 master 分支被改变了,feature 分支保持历史修改不变。Merging
会添加一个新的commit
到历史记录里
一个 commit 的流程看起来像是这样
从这里开始:
问题:在 second_branch 分支上需要 commit3 修改的内容(或者我的老大想让我的工作目录始终与 master 分支保持一致)
这里有两种可能的实现方法:
- 使用
merge
(或者偷懒的话可以使用pull
) - 使用
rebase
提醒:我们在
second_branch
分支上进行工作之前我们执行了git checkout second_branch
命令
git merge master
的结果
fast-forward
(一篇关于fast-ward
的好文档)合并的过程,因此 Git 创建了我们称之为合并提交的方法,以将 master 分支的所有修改应用于 second_branch。
这使 git 折线图产生了一个不好看的边缘线。
但是,它让所有提交都与合并之前保持一致,因为它们不会更改分支的历史记录。
注意:如果我们从 Git 提交树上删除了(使用 rebase -i
或者 reset
)合并的提交记录(在我们的例子中是编号:63c6403),那么来自这个被合并分支的所有提交也将从分支中撤离。
这使的代码回滚变得非常容易,当您合并了一个分支的大量提交但它们不能满足需求且必须删除它们时:只需删除 merge commit
即可。
git rebase master
的结果
second_branch
分支的前一个 HEAD 指针(指向当前选定分支的最后一个提交的指针)指向的是 “Commit 2”,并且通过运行 git rebase
,我希望它的 HEAD 指针与 master 分支相同,因此,分支首先是一模一样的,然后合并来自 second_branch 上面的提交。
这创造了一个非常好看且线性的树。但是,树会发生变化,因为用于 Detached commit 这次提交( second_branch 分支)的基本哈希值由最开始是 3b36f32,在
rebasing
之后会改变为 a018520,这意味着如果有人正在使用 second_branch,他将很难检索到整个新的 second_branch,因为它的树已经完全改变了。
我承认这有点复杂,但实际上它正在将这个分支的旧的 “起点提交” 更改为 “新的” 起点。
如果您需要更好的表示方法,可以在 google 图片上查看 git rebase 的一些图形。
更直观的图形表示
什么时候使用 rebase
,什么时候使用 merge
?
如果要同步修改的 feature 分支是和别人共同开发的分支,则不建议使用 rebase
操作,因为 rebase
会使仓库变得前后不一致。对于个人单独开发而言,rebase
操作会更有意义。
如果您想看到与合并操作发生之前完全相同的历史记录,那就用 merge
吧, merge
会保存历史记录,但 rebase
会重写它。
rebase
更好的简化了复杂的历史记录,您可以通过交互式 rebase
更改提交历史。您可以删除不需要的提交(git rebase --onto )、将两个或多个提交压缩到一个提交中或编辑提交消息。
rebase
将一次显示一个提交冲突,而 merge
将一次显示所有冲突。一次显示一个提交冲突会使冲突处理的更容易,更好,但是不要忘了当有很多冲突发生时 revert
rebase 操作比 revert
merge 操作要困难得多。
参考与扩展
git — Basic Rebase
git-difference-between-merge-and-rebase
git-for-each-ref
Rebase vs Merge
Interactive Rebase