git merge的用法
git一般有以下三种 merge 方式:
- fast-forward
- no fast forward
- squash
git merge 默认使用的 fast-forward 的合并方式。下面图片是三种合并方式的区别:
fast-forward
fast-forward方式就是当条件允许的时候,git直接把HEAD指针指向合并分支的头,完成合并。属于“快进方式”,不过这种情况如果删除分支,则会丢失分支信息,因为在这个过程中没有创建commit。
--no-ff
指的是强行关闭fast-forward方式,会创建一个新的commit。
squash
用来把一些不必要commit进行压缩,比如说,你的feature在开发的时候写的commit很乱,那么我们合并的时候不希望把这些历史commit带过来,于是使用--squash进行合并,此时文件已经同合并后一样了,但不移动HEAD,不提交。需要进行一次额外的commit来“总结”一下,然后完成最终的合并。
实战详解
在master上拉一条dev分支,不做任何修改,dev分支的git log如下,与master一样,只有一次commit信息。
$ git log
commit da487ec6f73886a52f4dc3ebc6c7c77b63fda4b7 (HEAD -> dev, master)
Author: crazyboy
Date: Tue Oct 6 22:32:57 2020 +0800
初始化
在dev分支执行两次commit,此时dev分支的git log如下:
$ git log
commit e695f8684772e79b5146f55f2203f9e3b4750823 (HEAD -> dev)
Author: crazyboy
Date: Tue Oct 6 22:36:38 2020 +0800
commit 2
commit 9bdfa619268a00898b472f40ef519d9cf0212acd
Author: crazyboy
Date: Tue Oct 6 22:36:05 2020 +0800
commit 1
commit da487ec6f73886a52f4dc3ebc6c7c77b63fda4b7 (master)
Author: crazyboy
Date: Tue Oct 6 22:32:57 2020 +0800
初始化
切换回master,执行git merge dev,可以看到提示了Fast-forward
$ git merge dev
Updating da487ec..e695f86
Fast-forward
Hello.java | 2 ++
1 file changed, 2 insertions(+)
此时,master分支git log如下,可以看到,master的HEAD指针与dev的HEAD指针,指向的是同一个commit,说明git merge --ff仅仅是移动了master的HEAD指针,并且可以看到dev的提交历史。
$ git log
commit e695f8684772e79b5146f55f2203f9e3b4750823 (HEAD -> master, dev)
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:36:38 2020 +0800
commit 2
commit 9bdfa619268a00898b472f40ef519d9cf0212acd
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:36:05 2020 +0800
commit 1
commit da487ec6f73886a52f4dc3ebc6c7c77b63fda4b7
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:32:57 2020 +0800
初始化
在master执行git reset HEAD^^ --hard,回退到master最初的版本。
注: 需要回退两次,才能回到最初的版本。
$ git log
commit da487ec6f73886a52f4dc3ebc6c7c77b63fda4b7 (HEAD -> master)
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:32:57 2020 +0800
初始化
在master分支执行git merge --no-ff dev,会要求输入commit备注,git log如下,可以看到,除了dev的两次commit,还有一次merge的commit信息。
$ git log
commit b5444f26db3fdcd44041828f850ee25897c69555 (HEAD -> master)
Merge: da487ec e695f86
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 23:05:47 2020 +0800
Merge branch 'dev'
commit e695f8684772e79b5146f55f2203f9e3b4750823 (dev)
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:36:38 2020 +0800
commit 2
commit 9bdfa619268a00898b472f40ef519d9cf0212acd
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:36:05 2020 +0800
commit 1
commit da487ec6f73886a52f4dc3ebc6c7c77b63fda4b7
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:32:57 2020 +0800
初始化
执行git reset HEAD^ --hard,git log如下
$ git log
commit da487ec6f73886a52f4dc3ebc6c7c77b63fda4b7 (HEAD -> master)
Author: crazyboy <15813514053@163.com>
Date: Tue Oct 6 22:32:57 2020 +0800
初始化
执行
$ git merge --squash dev
$ git commit -m "message here"
看不到dev的两次commit信息,也没有merge的commit信息,master的HEAD指针并没有变化,但是确实把dev的内容合并过来。
--squash
选项的含义是:本地文件内容与不使用该选项的合并结果相同,但是不提交、不移动HEAD
,因此需要一条额外的commit
命令。其效果相当于将dev分支上的多个commit
合并成一个,放在当前分支上,原来的commit
历史则没有拿过来。
判断是否使用--squash
选项最根本的标准是,待合并分支上的历史是否有意义。
如果在开发分支上提交非常随意,那么一定要使用--squash
选项,版本历史记录的应该是代码的发展,而不是开发者在编码时的活动。
只有在开发分支上每个commit
都有其独自存在的意义,并且能够编译通过的情况下(能够通过测试就更完美了),才应该选择缺省的合并方式来保留commit
历史。