Fast forward 概述
通常,合并分支时,如果可能,Git会用Fast forward
模式。
举例来说,开发一直在 master 分支进行,但忽然接到了一个新的需求,于是新建了一个 dev 的分支,并在其上进行一系列提交,完成时,回到 master 分支,此时,master 分支在创建 dev 分支之后并未产生任何新的 commit。此时的合并就叫 Fast forward
。
Fast forward
方式就是当条件允许的时候,Git 直接把 HEAD 指针指向合并分支的头,完成合并。属于“快进方式”,因为在这个过程中没有创建commit,在这种模式下如果删除分支,则会丢失分支信息。
如果要强制禁用Fast forward
模式,Git 就会在 merge 时生成一个新的 commit,这样,从分支历史上就可以看出分支信息。--no-ff指的是强行关闭fast-forward方式
强制禁用 Fast forward 模式的具体方法
首先,仍然创建并切换dev
分支:
$ git checkout -b dev
Switched to a new branch 'dev'
修改readme.txt文件,并提交一个新的commit:
$ git add readme.txt
$ git commit -m "add merge"
[dev 6224937] add merge
1 file changed, 1 insertion(+)
现在,我们切换回master
:
$ git checkout master
Switched to branch 'master'
准备合并dev
分支,请注意--no-ff
参数,表示禁用Fast forward
:
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去。
合并后,我们用git log
看看分支历史:
$ git log --graph --pretty=oneline --abbrev-commit
* 7825a50 merge with no-ff
|\
| * 6224937 add merge
|/
* 59bc1cb conflict fixed
...
可以看到,不使用Fast forward
模式,merge后就像这样:
关于默认的fast-forward的,上面引文没有详述。基本就是看不出有从其他分支合并过来的信息,只能看到每个commit实际的信息。如果其他分支丢失了,分支合并的信息就丢失了。
自己试了一下 默认的有fast-forward的情况。如下:
$ git checkout -b testff
Switched to a new branch 'testff'
$ vi readme
$ git add readme
$ git commit readme
test ff
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i or -o; assuming --only paths...
# On branch testff
# Changes to be committed:
# new file: readme
#...
".git/COMMIT_EDITMSG" [converted] 8L, 282C written
[testff 5a036ee] test ff
1 file changed, 1 insertion(+)
create mode 100644 readme
$ git checkout -b newtestff
Switched to a new branch 'newtestff'
$ git merge testff
Already up-to-date.
$ git diff testff newtestff
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git pull
$ git checkout -b newff
Switched to a new branch 'newff'
$ git checkout newff
Already on 'newff'
$ git merge testff
Updating 30a974d..5a036ee
Fast-forward
readme | 1 +
1 file changed, 1 insertion(+)
create mode 100644 readme
$ git log --graph --pretty=oneline --abbrev-commit
* 5a036ee test ff
* 30a974d Merge branch 'xxx' into master
|\
...
重点看两点:
-
第一处在test-ff版本 checkout -b的,结果发现git 两个branch完全没有差别。
git checkout -b dev
命令相当于
创建分支:git branch dev
切换分支:git checkout dev
而这个创建是基于当前HEAD的。
所以新创建的分支和test-ff没有区别。
-
采用默认的merge之后,使用
git log --graph --pretty=oneline --abbrev-commit
能够看到:* 5a036ee test ff
完全没有原来 test-ff 分支的信息。
另,
git log
命令加上后面的参数很好用,--graph --pretty=oneline --abbrev-commit
比原始的输出好看多了。
Fast Forward 和 no fast foward 合并模式对比
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
小结
Git 合并分支时,加上--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward
合并就看不出来曾经做过合并。