How to rebase a branch when the parent is rebased with another

Git has certainly made our life super simple in terms of managing code and developing features when working in a team. Rebasing is one of the most commonly used git feature while working in separate branches; however sometimes life may become terrible with it, because rebase heavily depends on commit history.
One of those miserable situation arises when you have cut a branch from an active feature branch and that feature branch is rebased with master or any other branch. You know that your new commits are supposed to be on top of the featured branch you’ve created your sub-feature branch from; but the history is changed now.
Before moving to the solutions, let us know how rebasing actually works.

What happens in rebase?

Lot many people have been introduced to merge before rebase. So I will try to explain rebase comparing with merge.
When you merge two branches, what git tries to do is to get all your commits and file changes from the source branch to the target one. While doing that some changes in files are easily mergeable, but few are now; which leads to conflicts. Then you probably fix those conflicts somehow and continue the merge. Due to merge conflicts resolution a new merge commit will be introduced in the target branch. This way the history of commits will stay intact, but a burden of introducing a new commit every time you merge.
In the other hand, rebase first goes to the point where the target branch was split from the source branch. Then brings all the commits made after the split in source branch to your target branch and then tries to apply your commits from the target branch one by one. While doing that, few commits may face conflicts; however resolution of those will not generate a new commit but modify the existing one. But this changes the commit history, due to which you need to force-push it.

Before Rebase

After Rebase

Problem statement

Below is the diagram of the problem I’m talking about. Where there are two branches apart from the master. One is feature, which was cut from the master from the commit B. Then someone created his sub-feature branch from commit F of branch `feature.

Before rebase of feature

Now the branch feature has been rebased with master and has been forced pushed to origin. So this can be considered as feature(new) now and to refer the old one, I’ve named it feature(old) now.

After rebase of feature

Now you as the owner of the branch sub-feature, want to make it updated with it’s parent branch feature, which in the origin has been converted to feature(new) with a new commit history. So rebasing sub-feature with feature(new) will give you huge and unnecessary conflicts which have no relation with your development. So how can you make your sub-feature updated again?

Solution 1 – cherry-pick

One of the easiest solution to this problem is, cutting a new branch from feature(new) and cherrypicking all the new commits of sub-feature (i.e. X, Y, Z) to that branch.

Con

The con with this is, for each conflict it will create a new merge commit. So if from the commits X, Y and Z, git is unable to absorb the changes of Y and Z each, then the new log history will look like below.

Another con is, if the same situation arise again and you follow the same process, you will have to resolve conflicts for the merge commits as well. So make sure you do this process only when feature is about to be merged to master.

Solution 2 – interactive rebase

Interactive rebase is a way to modify the commit series; where you can remove of stash and reorder commits the way you want. So another solution is, you first do an interactive rebase of sub-feature with itself to remove the commits of feature or feature(old) from it; which is your step 1.

Step 1

After step 1, the situation of the branches will be something like below.

Step 2

Now you can perform the next step which is a simple rebase with feature branch; just the way you do rebasing. It will result like the following.

Con

Doing rebase two times is a pain. Sometimes your sub-feature branch has dependencies on files which was first created in feature branch. That’s when the interactive rebase will give you a huge pain.

Solution 3 – using –onto

This is probably a better way to handle the situation comparing with the first two solution.
One of the most underestimated flag in git is --onto. But this can be very useful in situation like this.
Using --onto you can rebase a part of a branch using the base as some other branch. In our case, the commands will be like below.

Life seems sorted, but the problem is the guy who rebased feature branch with master has force-pushed feature to remote. In the diagrams I’ve shown you the branches as feature(old) and feature(new) for understanding. But in practical it’s just a single branch. So how can you perform the above commands when there is no feature(old).

Don’t worry, git reflog is the rescuer for you. If you don’t know about reflog; you can basically check when the heads of the branches has been moved. So you can simply run git reflog feature to find out when the feature branch was rebased and create a new branch called feature-old from it. If someone else has done that and you have not taken the pull for that branch feature yet, you can simply create a new branch feature-old from your local feature and then pull in the branch feature which will be considered as feature(new).

Final view

Honestly, situation like this should not arise. This shouldn’t be the proper development flow. But yes, edge cases happen and we fall into bad situation. Hope the solutions mentioned above will help you to reduce the misery.

About This Author

Hello! I am Paul Shan, a JavaScript Expert, Full Stack and DevOps Engineer cum Consultant based out of Bengaluru, India.

  • viky gosain

    Superb, saved my day
    i did this command git rebase –onto