Git Fast-Forward Merges

Posted on Thu 05 November 2015 in Git

The Scenario

You've developed and released a tool that people have downloaded and found useful. So useful, in fact, that they have begun contributing to your project.

After weeks of making no commits, you get your first pull request (PR) and see that it looks great. Time to integrate it into your project. What now?

Since there have been no commits on master since the contributor forked your project, the work to merge in the PR is trivial. Or is it?

Whether you realize it or not, the choice you make now can affect your Git history. What is the choice? To fast-foward or not fast-forward merge.

Fast-Forward Merge

Because there have been no commits on master since the PR was submitted, the HEAD pointer on master can be "fast-forwarded" to point to the final commit on the PR. Looking at the log of master then will show a linear history as a series of uninterrupted commits.

Non-Fast-Forward Merge (--no-ff)

In contrast, using the --no-ff flag when merging adds a merge commit when done, preserving a more "true" lineage (referred to as explicit branches). Looking at the log of master will then show that a branch was merged into it. Plus, you get the added bonus of being able to revert cleanly just by reverting the merge commit.

Which is Better?

Some tools, like Github, force --no-ff when committing pull requests, because they prefer to preserve explicit branches. This does makes sense, as the merge commit that's created after merging makes it very obvious from which pull request the commit came from.

However, doing that moves away from a linear history, which makes doing any "archaeology" slightly more difficult (e.g. log, bisect, blame, etc). While fast-forwarding does make it more difficult to find the source of a merged branch, it's not as much of an issue if development teams continue to keep detailed commit messages, including ticket references and PR URLs.

The debate about which one is better usually centers on whether or not to keep as "clean" of a history as possible. I personally prefer fast-forward merging & cleaner history in my local fork, because being able to more easily debug a historical source control problem is more valuable to me than knowing which person conducted a merge. (If needed, git blame would provide all of the details necessary to track down the person who could likely help fix an issue/revert commits.)

However, in an enterprise context, I prefer having --no-ff server-side, as it's advantageous to have more clarity from where/when certain branches - and their correlating PRs - came from.

What do you think?