Git merge allows us to perform fast-forward and no fast-forward branch merging. Any ideas when to use fast-forward merge and when to use no fast-forward merge?
-
15Would http://stackoverflow.com/questions/2850369/why-uses-git-fast-forward-merging-per-default/2850413#2850413 help? – VonC Jul 15 '11 at 04:19
-
5I think another really good perspective can be found here http://endoflineblog.com/gitflow-considered-harmful – Dmitry Minkovsky Oct 24 '16 at 16:11
-
1Same question? http://stackoverflow.com/questions/9069061/ – Yousha Aleayoub Mar 07 '17 at 21:55
6 Answers
The --no-ff
option is useful when you want to have a clear notion of your feature branch. So even if in the meantime no commits were made, FF is possible - you still want sometimes to have each commit in the mainline correspond to one feature. So you treat a feature branch with a bunch of commits as a single unit, and merge them as a single unit. It is clear from your history when you do feature branch merging with --no-ff
.
If you do not care about such thing - you could probably get away with FF whenever it is possible. Thus you will have more svn-like feeling of workflow.
For example, the author of this article thinks that --no-ff
option should be default and his reasoning is close to that I outlined above:
Consider the situation where a series of minor commits on the "feature" branch collectively make up one new feature: If you just do "git merge feature_branch" without --no-ff
, "it is impossible to see from the Git history which of the commit objects together have implemented a feature—you would have to manually read all the log messages. Reverting a whole feature (i.e. a group of commits), is a true headache [if --no-ff
is not used], whereas it is easily done if the --no-ff
flag was used [because it's just one commit]."

- 1,285
- 1
- 19
- 28

- 14,287
- 6
- 48
- 66
-
8And fast-forwards are great for when you've got a collection of closely-related branches that now and then you want to just move together. Not all merges are real history events. – Cascabel Jul 15 '11 at 00:44
-
3Why maintaining several branches then? If they're close-related - why not to do everything on single branch? – Ivan Danilov Jul 15 '11 at 01:00
-
13Closely-related doesn't mean identical. Besides, workflow isn't always neat. You don't always make the commits you think you're going to, you don't always branch from the best place. Maybe you start a couple features from one place, start working on one of them, realize it's generic, and fast-forward the other one to it before diverging. – Cascabel Jul 15 '11 at 01:02
-
Another common thing is for integration to have taken place on a branch besides master. All the merges you care about have already taken place; you're just making sure it's okay without touching master. Then you merge it into master - and that can be a fast-forward. – Cascabel Jul 15 '11 at 01:05
-
2As for first reply, my understanding is that OP wants to know best practices to follow. Things happen and not everything is ideal but this seems more like some forced compromise. – Ivan Danilov Jul 15 '11 at 01:12
-
And about the second I have to agree. There's many possible usages for `--ff` and `--no-ff` the question is only what kind of history you want to achieve. Technically git is advanced enough to satisfy almost any demand :) – Ivan Danilov Jul 15 '11 at 01:15
-
In a team with several developers working on various branches I would consider keeping the history of individual commits critical. svn is all but abandoned these days, but there are some companies who insist on using it because "we've always used it" – jcpennypincher Oct 21 '14 at 20:37
-
8It's worth noting that the benefits of `--no-ff` on your commit history may not be immediately evident when using basic tools like `git log`, which will continue to show all commits from all branches that have been merged into your current branch. That said, the benefits become clearer when using e.g. `git log --first-parent` on an integration branch such as `develop` or `master`. If you religiously use `--no-ff` then that will _exclusively_ display merge requests, while `git log` will still provide a (more) comprehensive history. That's why Vincent recommends it for use with **GitFlow**. – Jeremy Caney Dec 28 '19 at 01:43
I can give an example commonly seen in project.
Here, option --no-ff
(i.e. true merge) creates a new commit with multiple parents, and provides a better history tracking. Otherwise, --ff
(i.e. fast-forward merge) is by default.
$ git checkout master
$ git checkout -b newFeature
$ ...
$ git commit -m 'work from day 1'
$ ...
$ git commit -m 'work from day 2'
$ ...
$ git commit -m 'finish the feature'
$ git checkout master
$ git merge --no-ff newFeature -m 'add new feature'
$ git log
// something like below
commit 'add new feature' // => commit created at merge with proper message
commit 'finish the feature'
commit 'work from day 2'
commit 'work from day 1'
$ gitk // => see details with graph
$ git checkout -b anotherFeature // => create a new branch (*)
$ ...
$ git commit -m 'work from day 3'
$ ...
$ git commit -m 'work from day 4'
$ ...
$ git commit -m 'finish another feature'
$ git checkout master
$ git merge anotherFeature // --ff is by default, message will be ignored
$ git log
// something like below
commit 'work from day 4'
commit 'work from day 3'
commit 'add new feature'
commit 'finish the feature'
commit ...
$ gitk // => see details with graph
(*) Note that here if the newFeature
branch is re-used, instead of creating a new branch, git will have to do a --no-ff
merge anyway. This means fast forward merge is not always eligible.

- 3,847
- 30
- 32
When we work on development environment and merge our code to staging/production branch then Git no fast forward can be a better option. Usually when we work in development branch for a single feature we tend to have multiple commits. Tracking changes with multiple commits can be inconvenient later on. If we merge with staging/production branch using Git no fast forward then it will have only 1 commit. Now anytime we want to revert the feature, just revert that commit. Life is easy.

- 1,245
- 10
- 6
-
As long as they know how to get the history. Otherwise they'll be seeing all those commits. – beibichunai Jun 17 '21 at 01:51
-
The commit to revert will be a merge commit, so they'd have to think if the parent number used for reverting will always be right. – beibichunai Jun 17 '21 at 02:19
Git visualization
Here are the git log visualizations with the differences. These are what the trees looked like creating three branches from dev called 1 2 3, then merging with and without fast-forward. I'll put the setup code at the bottom. You can paste a paragraph of commands into your terminal to quickly setup and reset different git scenarios, which was very helpful in learning git.
Notice that with fast-forward, git doesn't even indicate a merge.
--no-ff --ff (default)
* Merge 3
| * 3
* | Merge 2 * Merge 3
| | * 2 | * 3
| |/ * | Merge 2
* / Merge 1 | | * 2
|/ | |/
| * 1 * / 1
|/ |/
* main * main
It's worth comparing this approach with rebasing.
Setup/teardown
You can run this repeatedly and it'll delete and reinitialize the repo. It's for windows, so I think you'll just have to change the filepath and the rd
remove directory commands if you're on *nix.
To see the behavior with fast-forward, remove --no-ff
from the merge commands at the end. Remove the --pretty
piece if you want to see the commit IDs.
cd \user\docs\code\learning\github\sandbox
rd /s /q 0.git 1 2 3
git init --bare 0.git
git clone 0.git 1
cd 1
git config user.name "user"
git config user.email "user@email.com"
git commit --allow-empty -m main
git switch -c 1 main
git commit --allow-empty -m 1
git switch -c 2 main
git commit --allow-empty -m 2
git switch -c 3 main
git commit --allow-empty -m 3
git switch main
git merge --no-ff 1
git merge --no-ff 2
git merge --no-ff 3
git log --graph --oneline --first-parent --all --pretty=%s

- 447
- 4
- 10
Besides the posted reasons to use --no-ff
, keeping a correct parent order in the target branch is another one.
Using --no-ff
, the parent order in the last commit in the resulting branch will be independent of the parent order in the last commit of the branch you are merging, its first parent will be in the target branch, not in the branch you merged. If you perform a ff, the last commit of the branch you are merging would be incorporated to the target branch as the last one, so the first parent of the last commit in target branch will be pointing to a commit out of the mainline. This is probably not what you want.
You could for instance use --first-parent
to filter a branch history to see only the major changes to a branch, or want to do other operations where the first parent is used by default, like revert
. If your branch does not keep a correct parent referencing, the results would be different than expected.

- 130
- 1
- 10
It is possible also that one may want to have personalized feature branches where code is just placed at the end of day. That permits to track development in finer detail.
I would not want to pollute master development with non-working code, thus doing --no-ff may just be what one is looking for.
As a side note, it may not be necessary to commit working code on a personalized branch, since history can be rewritten git rebase -i
and forced on the server as long as nobody else is working on that same branch.

- 3,055
- 15
- 28