git: merge only the completed work

❝The benefits of merging only a completed work, forgetting in-progress development history.❞

Something that pops up occasionally in a conversation on software development, is how one should reintegrate feature branches into the main line of development (typically “master” or “develop"). There are a number of options for reintegrating development work that lives on a separate branch:

  1. Merge the branch using a merge-commit.
  2. Rebase feature development work on top of the main line.
  3. Introduce the work, squashed into a single commit.

In this article, it is assumed that a new feature is developed on a separate branch, then reintegrated when finished. This may not apply to trivial changes, such as bug fixes. This article highlights the benefits of introducing new work as a squashed single commit. Consequently, advantages and disadvantages are not exhaustive. Instead, it intends to consider what the benefits are of forgetting feature development history.

The merge-commit

The most straight-forward and obvious solution for reintegrating a completed feature, is joining two branches with a merge-commit. A merge-commit has 2 parents, literally joining 2 paths together in the commit graph. By its nature, this preserves each branch’s unique history that emerged as part of the development work, both of the main line and in the feature branch.

This approach allows every part of the original history to be found and inspected. Consequently, it also pollutes the main line of development with commits that contain all kinds of “work-in-progress” changes, be it non-compilable code, bad solution attempts, trivial mistakes, and their (uninformative) commit messages.

Rebasing

Another approach to reintegrating feature work into the main branch, is to retroactively pretend that all work on the feature was performed on top of the current main line of development. This would allow committing the individual changes directly on top of the main branch, negating the need for “reintegration” of a deviated branch. Rebasing is the procedure of re-applying each of the commits of the feature branch on top of the (current) main line. Therefore, there is no joining of branches, only a direct progression of the commit graph.

The difficulty with rebasing is that for each commit there may (again) be conflicts that need to be resolved, given that there are recent changes on the main line. Secondly, the individual development commits still exist so there is pollution of the commit history with “in-progress” work. And, thirdly, rebasing becomes more difficult the longer the branch is separated.

Squashed-merges

The next approach, and the main topic of this article, is the mechanism of merging a completed work as a single commit. The previous approaches both have some problems. The most prevalent are the consequences of preserving in-progress development history after development has completed.

Issues with the previous approaches to reintegration:

The key difference between the previous approaches and squash-merging a feature branch, is that development history gets lost in the process. An arbitrary number of previous commits is squashed into a single end result, intermediate changes that were obsoleted are forgotten in the process. There are benefits, both to the feature developer and to the main line, when this information is forgotten.

There are plenty of advantages to squashing development history when reintegrating a feature into the main line of development.