git rebase 和git merge 作用基本是相同的,都可以用来合并两个branch。这里不对这两个命令做原理上的介绍,只是从实践的角度对这两个命令进行解释。如果不明白其中的原理可以自行查找。
测试基本环境:本地创建一个git仓库,包括两个分支master和dev。两个分支保持同步都只有一次提交:
然后分别在这两个分支上做两次提交,但是提交的时间顺序是穿插的。
branch master
按照时间先后 Master commit 1 -> Dev commit 1 -> Master commit 2 -> Dev commit 2:
ID | commit | date |
---|---|---|
86894d7 | Dev commit 2 | 18-10-19 14:06:11 |
ddf7f38 | Master commit 2 | 18-10-19 14:05:31 |
912bf54 | Dev commit 1 | 18-10-19 14:04:32 |
75944e4 | Master commit 1 | 18-10-19 14:04:01 |
表1:按时间排序
在master分支执行merge命令
$ git merge dev
看一下合并后的log信息:
对比一下图4和表1可以发现:
这三点也正是merge的特点,总的来说就是merge合并时创建一个新的commit进行标记并且保存了提交的原始记录。
rebase使用起来比merge麻烦一些,在合并时要考虑到开发分支和基础分支。
以本文为例,我是以master为基础分支,以dev为开发分支(分支叫什么名字无所谓),所以我最终想要的结果是:基于master分支的提交把dev分支的提交添加上。
在操作前先用git reset --hard命令把仓库的状态回退到执行merge之前,这样能保持commit ID与之前一样,方便对比。
执行命令:
$ git checkout dev
$ git rebase master
对比图5和图4和表1发现:
这里介绍一下rebase的过程,git rebase master命令执行的时候会把dev分支(当前分支)里的每个提交取消掉,并且把它们临时保存为patch(这些补丁放到".git/rebase"目录中),然后把dev分支更新到到与master分支一致,最后把保存的这些patch再贴回来。
所以就造成了当前的现象:合并后commit信息并没有按照两个分支的提交顺序进行排序,而且会产生两个新的commit。
用回退版本的方式再验证一下merge和rebase的区别
如图5所示,在branch dev上有五次提交,如果想要回退到Dev commit 1(commit ID:d6bfde7) 或者 Master commit 2(commit ID:75944e4) 会有怎么样的效果呢?
//回退到Dev commit 1
$ git reset --hard 912bf54
//回退到Master commit 1
$ git reset --hard 75944e4
看上去是符合预期的,也是理所当然的,用reset --hare强制回退到某一个commit ID,必然能成功。
但是merge会是同样的表现吗?
可以用reset --hard的方式把仓库强制更新到图4的状态。
在branch master上有六次提交,其中包括一次merge commit。强制撤销掉merge commit和回退到Dev commit 2(commit ID:86894d7) 。
//把merge commit撤销掉。
$ git reset --hard HEAD^
//回退到Dev commit 2。
$ git reset --hard 86894d7
现在可以很明显的看出问题:
当然这也是可以理解的,因为本来merge的信息就是由新创建出来的merge commit记录的,那么现在的两个操作就相当于把这个信息撤销了,理所当然的当前分支只能保留信息只能是你想要回退的分支的信息了。
这两个命令没有优劣之分,完全看自己的需求和使用习惯。
从上面的分析来看:这两个命令的功能是一样的都是用来合并代码,但是对于使用场景来说:
注意:为了能有更好的演示效果,测试的过程中都没有考虑提交冲突的情况,如有疑问自行解决。