Merging a branch is a pretty common operation when using Git. In some circumstances, Git by default will try to merge a branch in fast-forward mode. How is this different from a merge without fast-forwarding?
Let's assume that I created a topic branch named
speedup from the current
master. After working on this branch for a while (three commits, those white circles), I finally decided that I was done and then I pushed it to my own remote. Meanwhile, nothing else happened in the master branch; it remained in the same state right before I branched off. The situation is depicted in the following diagram.
Once the project maintainer was notified that my branch was ready to be integrated, she might use the usual steps of
git fetch followed by
git merge and thus, my work lands in the source tree. Because the master has not been changed since the commit (gray circle), which serves as the base for the said topic branch, Git will perform the merge using fast-forward. The whole series of the commits will be linear. The history will look like the diagram below (left side).
Another variant of the merge is to use the
-no-ff (no fast-forward) option. In this case, the history looks slightly different (right side); there is an additional commit (dotted circle) emphasizing the merge. This commit even has the right message informing us about the merged branch.
The default behavior of Git is to use fast-forwarding whenever possible. This can be changed; no fast-forward mode can be easily set as the default merge using the proper configuration.
Perhaps a typical example of a non-fast-forward merge is via the use of the green merge button on GitHub, as part of its pull request workflow. When someone creates a pull request, there is a choice to merge the change (whenever GitHub thinks it is possible to do so) by just pressing this button on the project page.
Unfortunately, at least as of now, GitHub’s web interface will perform the merge as if you specified
-no-ff. In other words, even if there is a possibility of fast-forwarding, GitHub will not do so. One possible explanation is so that the pull request could be identified. For example, the few recent commits of a project (I picked ESLint as an example, nothing particular about the project) can look like this:
Looking at the graph, it is clear that those few patches can be merged using fast-forward mode. Alas, the GitHub style of merging turned the commit history from a linear progression to something that resembles a railroad diagram.
In short, non-fast-forward merging keeps the notion of explicit branches. It may complicate the commit history with its non-linear outcome at the price of preserving the source of the branches (pull requests, when using GitHub). On the other hand, fast-forward merging keeps the changesets in a linear history, making it easier to use other tools (log, blame, bisect, etc.). The source of each branch will not be obvious, although this is not a big deal if the project mandates the strict cross-reference between the commit message and its issue tracker.
Which one is your merging preference, with or without fast-forwarding?