.

Tags:

Merging a branch is a pretty common operation when using Git. In some circumstances, Git by default will try to merge a branch in a fast-forward mode. How is this different with a merge without fast-forwarding?

gitbranch Let us 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 am 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 got notified that my branch is ready to be integrated, she might use the usual steps of git fetch followed by git merge and thus, my work is landed in the source tree. Because 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).

merging

Another variant of the merge is to use -no-ff option (it stands for no fast-forward). 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, the no fast-forward mode can be easily set as the default merge using the right proper configuration.

Perhaps the typical encounter of 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 it) just pressing this button on the project page.

automerge

Unfortunately, at least as of now, GitHub’s web interface will perform the merge as if you would specify -no-ff. In other words, even if there is a possibility of fast-forwarding, GitHub will not do that. 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:

nonlinear

Looking at the graph, it is clear that those few patches can be merged using fast-forward mode. Alas, GitHub style of merge turned the commit history from a linear progression to something that resembling a railroad diagram.

In short, non fast-forward merge 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 merge keeps the changesets in a linear history, making it easier to use other tools (log, blame, bisect). 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?

  • Moch Lutfi

    I always use fast-forwarding because I don’t know the different between use ff or not. But now I know the different. :)
    Thank’s for the great article.

  • http://www.limulus.net/ Eric McCarthy

    I don’t have enough experience with using git with other people to know if my opinion here has much value, but to me it makes sense for GitHub to not fast-forward. It seems like a good idea to have the ability to identify the merge and the acceptance of the pull request. I’d argue that the problem isn’t that GitHub creates a noisy commit history, but that it doesn’t provide a way to hide the noise from its visualization of the history.

    It seems a lot of people are intent on doing all sorts of cartwheels to keep their commit histories tidy. This has made me somewhat apprehensive about git. However now I’m beginning to suspect that these are all workarounds for not having better history visualization tools.

    • http://ariya.ofilabs.com/ Ariya Hidayat

      Technically, merge commits can be hidden already using –no-merges option when using git log. However, if one would hide it anyway, then why not using fast-forwarding at the first place?

      For other points, I kind of addressed it already in the post. Linear history is not only about visualization (bisect, for example, is easier to follow along when there are less complicated branching). Identifying the merge/pull request is not a big deal in many projects (as used by WebKit, Chromium, etc see the link to my previous blog post on cross reference) where one wouldn’t start working on a patch right away.

  • alFReD_NSH

    In our internal project, we only merge pull requests fast forward only. First reason, is that a linear history is way easier to read. The other reason is that the person who merges it doesn’t have to deal with merge conflicts.

    And there’s also need to reference to the pull request through, since the commit has links to issue tracker and pull request contains nothing more than code review comments.

    • http://ariya.ofilabs.com/ Ariya Hidayat

      For a potential merge conflicts, fortunately GitHub will notice it and won’t allow merging via the green button.

  • maksimrv

    Use **no fast-forward** for merging feature branches to `rc` branch.

    It allow easy remove feature from `rc` and it’s easier to see which commits belong to a feature branch.

  • Vladimir Varankin

    I believe that `–no-ff`-merge “popularity” is came from “git-flow” origins from “A successful Git branching model” article (http://nvie.com/posts/a-successful-git-branching-model/) back into 2010.

    P.S. What tool did you use to draw this commits-graph from ESLint? It looks so neat

    • http://ariya.ofilabs.com/ Ariya Hidayat

      Interesting theory on that git-flow theory! Now I remember that that when that article became popular for the first time, not many people are aware of ff vs no-ff yet.

      For that graph, I use BitBucket. Google Code can also produce a nice graph. As much as everyone loves GitHub, unfortunately it can’t even visualize branches yet (see http://ariya.ofilabs.com/2012/09/git-viewer-github-vs-google-code.html).

  • medikoo

    I constantly merge pull request on Github, and every time I do that all commits from merged branch land in master repository. It doesn’t look as cumulated merge that’s result of `–no-ff`, am I missing something?

    • http://ariya.ofilabs.com/ Ariya Hidayat

      Did you merge it using the green button or manual command-line? Also, you should visualize your repo branches using the right tool (GitHub can’t display branch relation, see http://ariya.ofilabs.com/2012/09/git-viewer-github-vs-google-code.html).

      • medikoo

        I use merge button, and I think it works as expected. If in my fork/branch (which is at state of origin/master) I do commits A and B, and then submit pull request, and merge it with green button. In master I land with three new commits: A, B and “Merge pull request from fork/branch”

        • http://ariya.ofilabs.com/ Ariya Hidayat

          That’s exactly the result -no-ff, no surprise there. With -ff however, you don’t have that merge commit at all (since it’s all fast-forwarded).

          • medikoo

            Indeed, sorry for confusion. I thought that `–no-ff` is supposed to save whole pull request with one “Merge pull request ..” commit.
            I finally got the idea from this article: http://nvie.com/posts/a-successful-git-branching-model/ and it looks `–no-ff` has it’s valid points, as it allows to keep full picture. It’s hard to strongly decide that one is better than the other.

          • http://ariya.ofilabs.com/ Ariya Hidayat

            My last paragraph kind of explains why the decision to pick one merging strategy can be easily defined by the project requirements. For example, in projects with a strong usage of task/issue tracker, there is no definite need for the explicit notion of topic branch and hence it’s unnecessary to use –no-ff.

  • http://www.facebook.com/hrishikesh.kumar.mishra Hrishikesh Mishra

    Good explanation.
    But if a person comes from SVN, he/she will be easily confused with fast forward merge in being.
    But with merging I have a question.
    Suppose I create a branch from master named “my_branch”. I worked on branch “my_branch” and made 5-10 commit. Before merge to “my_branch” back to “master”, I first merge master to branch “my_branch” to bring main line change in “my_branch” and test my merged branch “my_branch”. Now if everything fine, I merge “my_branch” back to master. Now I want to check all the changed with I made in branch “my_branch” in master branch, not the merge code. How can I achieve this?

  • lynxluna

    I’d go with fast forwarding, bisecting is easy with fast forwarded commits. I always avoid commit bubble. You’re right, managing strict cross references between issue vs commits is needed.