Git Rebasing and Merging: What Should You Use, and When?
There's confusion with Git Rebasing and Merging, and when to use what. We've provided examples to clarify where each strategy is appropriate.
Many of us are familiar with Git, so we understand the concepts of developers copying, branching, etc. In this post, I will highlight the differences between Git Rebasing and Merging, and the best strategies for different scenarios. During our recent release process enhancement and branching strategy implementation, we used rebasing. I will also explain the reasoning behind that.
Git Merging Example
To explain the merge, I have executed a few commands from the terminal. Next, I will add those diagrams and explain what’s happening in the merge.
In the above image, I have created a new_branch, added two commits to it, and shown the Git log.
In this diagram, I have shown the Git log graph to clarify how the master and new_branch have deviated.
In the next segment, I have switched back to master and created a commit, then I used a recursive Git merge strategy to merge the master branch into the new_branch.
Now, when we’re investigating the Git log, it shows another new commit has been added to new_branch, which has been generated due to the merge.
Finally, when we see the Git log, it shows up on the the commit graph like this.
From the option above, it’s clear that merging is useful, as it’s a non-destructive operation. The only disadvantage is the creation of an extra Merge commit, which can be annoying for traversing through the commit history.
Disadvantage: If the master is too active, then following this strategy will create too many merge commits and the history becomes unclean for most cases.
Git Rebasing Example
Let’s reconsider the same example regarding rebasing. The strategy is to move the entire tip of the feature branch. Moreover, with this approach, we’re efficiently introducing all of the new commits in the master branch. However, instead of using a merge commit, rebasing rewrites the project history by creating new commits for each commit in the original branch.
Advantage: Rebasing helps keep our branch history cleaner. Furthermore, it avoids the introduction of extra merge commits. Lastly, from the above diagram, we can see that it helps keep the branching history linear.
Interactive rebasing: This is an important feature regarding rebasing, as it gives power to developers to selectively pick the commits that they want in their feature branch. For example, I am working in Feature-X development in my feature branch, and some commits went into the master branch that affects Feature-X. The master/main branch might also contain multiple commits for other features/bug fixes. This is where interactive rebasing comes in: it lets you selectively pick the commits that you want, and works better than cherry-picking when there is a large number of commits.
Branch mirroring and rebasing: This is something I have faced when working with android projects. Google releases a branch, and the chipset company, such as Qualcomm, takes the branch and decides on a different long-term strategy to manage it and eventually release. The strategy of merging/rebasing is essential in finalizing the release strategy. Based on the fixes available in the previous release branch, as well as the complexity of the changes, they decide whether to select rebasing and merging. Any other third-party mobile companies that take the release branch from the chipset companies follow similar strategies. This is a critical process for the companies that deal with other companies’ releases as their base branch for development, and that release products out of them. Ideally, this process should be automated and managed with less manual intervention.
Harness Rebasing and Branching Strategy
We recently introduced a new branching strategy at Harness to keep the master branch clean and streamline our release process. For our Harness-core repo, we have a develop branch that works as a default branch. Developers must generate feature/bugfix branches from develop, and PR checks run on the develop branch. Then, all of the development changes get merged to the develop branch.
This branching concept is meant to catch bugs early in the development stage. That’s why every day, we build the develop branch and deploy it to our Pre-QA environment. Different teams run their automation suites in the Pre-QA env, and, if everything goes fine, then we port the develop branch changes over to the master branch. Rebasing plays an important role here.
There are two reasons behind selecting rebasing over merging:
- Keeping the branching history clean for the master branch.
- Avoiding the introduction of merge commits, which in the long run, become difficult to revert.
In the following diagram, I provide a high level concept about our branching and automation strategy:
Historically speaking, there has been confusion with Git Rebasing and Merging, and when to use what. I hope the above examples clarify in which situations rebasing and merging are appropriate, and that this article helps you decide what strategy to use in different stages of development.