Blog·Tanky WooABOUTRSS

ff and no-ff in git merge

19 Apr 2014

关于Git Merge时的--ff--no-ff 参数,ff代表的意思是fast forward,即直接快速前进。

man git-merge的解释:

--ff
   When the merge resolves as a fast-forward, only update the branch pointer, without creating a merge commit. This is the default behavior.

--no-ff
   Create a merge commit even when the merge resolves as a fast-forward. This is the default behaviour when merging an annotated (and possibly signed)
   tag.

fast forward 和 no fast forward 的合并图:

git branch merge

(图片来至 A successful Git branching model)

fast forward 快速前进的合并方式适用于分支B从分支A从checkout出来后,分支A没有commit,这样分支B被合并到分支A时,可以快速前进,这个也是git默认的合并方式。

如果分支B被checkout出来后,分支A也有修改,那么就没法快速前进合并,会额外建立一个merge commit,对分支A和分支B做一个合并操作。

使用 no fast forward 的好处就是时分支的结构性没有做改动,保持分支的结构,把分支B的多次开发最后做一个merge commit 合并到分支A上。

网上有一些探讨的帖子和文章,建议把--no-ff作为默认的方式,比如上面提到的 A successful Git branching model

另外,StackOverflow上也有几篇很详细的讲解:

首先checkout一个分支,叫ff, 并切换过去:

TankyWoo@Mac::test-git/ (master) » git co -b ff master
Switched to a new branch 'ff'

新建一个文件c,做一个commit,再随便修改下,并又做一个commit。

接着切换回master分支,做 fast forward 合并:

TankyWoo@Mac::test-git/ (ff) » git co master
Switched to branch 'master'
TankyWoo@Mac::test-git/ (master) » git merge ff
Updating 2f1e828..8bffa7b
Fast-forward
 c | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 c

查看log:

* 8bffa7b - (HEAD, ff) update c (10 seconds ago) <Tanky Woo>
* 525481f - add c (18 seconds ago) <Tanky Woo>
* 2f1e828 - (origin/test, origin/master, origin/HEAD, master) update test-git-submodule (2 days ago) <Tanky Woo>

如果做 no fast forward 合并:

TankyWoo@Mac::test-git/ (master) » git merge -no-ff ff

查看log:

*   a368dcc - (HEAD, master) Merge branch 'ff' (4 seconds ago) <Tanky Woo>
|\
| * 8bffa7b - (ff) update c (3 minutes ago) <Tanky Woo>
| * 525481f - add c (3 minutes ago) <Tanky Woo>
|/
* 2f1e828 - (origin/test, origin/master, origin/HEAD) update test-git-submodule (2 days ago) <Tanky Woo>

再尝试做一个正常情况下不可 fast forward 的,先在ff分支里做两次commit,然后checkout回master分支,也做一个commit。

ff分支的log:

* 693ad65 - (HEAD, ff) update c (12 seconds ago) <Tanky Woo>
* a9ce393 - add c (24 seconds ago) <Tanky Woo>
* 2f1e828 - (origin/test, origin/master, origin/HEAD, master) update test-git-submodule (2 days ago) <Tanky Woo>

master分支的log:

* 503393f - (HEAD, master) add d (1 second ago) <Tanky Woo>
* 2f1e828 - (origin/test, origin/master, origin/HEAD) update test-git-submodule (2 days ago) <Tanky Woo>

可以看到commit id为2f1e828 的提交是master和ff的公共原点,也就是ff是在这个commit上从master分支checkout出去的。

如果直接做-ff-only,可以看到报错提示:

TankyWoo@Mac::test-git/ (master) » git merge --ff-only ff
fatal: Not possible to fast-forward, aborting.

这个时候,要么做一次--no-ff提交合并,要么就rebase,如:

TankyWoo@Mac::test-git/ (master) » git rebase master

做完rebase后的log:

* 3b2a4d9 - (HEAD, master) add d (77 seconds ago) <Tanky Woo>
* 693ad65 - (ff) update c (15 minutes ago) <Tanky Woo>
* a9ce393 - add c (15 minutes ago) <Tanky Woo>
* 2f1e828 - (origin/test, origin/master, origin/HEAD) update test-git-submodule (2 days ago) <Tanky Woo>