Skip to content

Rebase vs Merge for submitting features

ermshiperete edited this page Sep 13, 2012 · 8 revisions

Overview

Gerrit can be configured to submit features (or individual changes) in one of two ways:

  • Rebase
  • Merge

The only difference is in the way the history will look afterwards. Any conflicts (with other people's changes that have already been submitted) will still need to be resolved, and the process of resolving those will be almost the same. Any branch that can be merged can be rebased.

Example

An example of what history will look like if merging is used can be seen in the test repo:

Merge loops in history

The history begins (at the bottom) with a linear series of commits such as would be produced by rebasing, or by changes/features that are created and submitted before anyone else has made changes. Then the history starts to contain loops. The changes on the gray branch were probably submitted directly onto develop (although it's impossible to tell) and the changes on the red and yellow branches were started before the gray changes and submitted afterwards, and most likely were features. You can see the merge commits that were used to submit them. Additional loops occur further up.

Merits

The advantage of rebasing over merging is that the history is always linear, except for significant merges such as from develop onto master, or from a hotfix onto master. Features correspond to contiguous groups of commits, but are not specifically marked in any way.

The advantage of merging over rebasing is that features are always clearly indicated by loops, although it's not always clear which side of the loop is the feature and which are just commits on develop that took place while the feature was being developed. However, there is always a merge commit (a commit that just records the merge) and the message on it should contain the name of the branch or feature that was merged.

Which approach is used is partly a matter of taste, and partly situational. If a feature is large (more than about five commits) it is often helpful to have it delineated by a loop. If it's just one or two commits, a loop is overkill.

Choice

Unfortunately, Gerrit's behavior when the Submit button is pressed has to be configured one way or the other. However, it is possible to customize it on a case-by-case basis.

  • If Gerrit is configured to use merges, but the developer rebases their change/feature and resubmits it as an updated patch set (or uses the Rebase button in Gerrit), and the Submit button is used before any other changes are submitted, the merge becomes a "fast-forward" and produces linear history with no merge commit.
  • If Gerrit is configured to use rebasing, but the developer merges their change/feature manually onto develop and resubmits it to Gerrit, it will produce a loop (I think — we need to test this).

So, since customization is possible (with a small amount of effort) we need to consider only what the default, convenience behaviour should be when pressing Submit. Since it's likely that developers will press this button without too much thought, and since this will typically produce a large number of small, redundant loops, Gerrit should be configured to use rebasing. When a feature genuinely should be delineated with a merge, this can still be achieved, but since this is less commonly needed, it should not be the default.

Caveats

If a change was already reviewed before being rebased, the change may need to be reviewed and verified again. This is certainly the case if there were conflicts and the developer had to rebase it on the local machine. However, when using the Rebase button in Gerrit the Code-Reviewed and Verified status aren't cleared. (They get re-applied by a script. Note that you might have to reload the page in order to see the reapplied status after using using the Rebase button)

JohnT: I tend to agree with your recommendation, but it seems the process for keeping the loop if you want it is MUCH more complex than the one for avoiding an unwanted loop. Definitely needs testing and documentation.

Eberhard: I think most often when we want a loop (i.e. a separate merge commit) is when we're done with developing a new feature consisting of several commits. But in that case it's feasible to have a separate feature branch on Gerrit that receives the changes, and when we're finished implementing the feature we can manually merge the feature branch into develop and delete the feature branch. (The branch will still appear in the history, but it won't exist as a named branch on Gerrit anymore)

Clone this wiki locally