Git Rebase and Its Mechanism

Git rebase is a command that may scare some people. They think that rebase is not necessary. Thus they rarely do rebasing and use only merging. However, I think rebasing is a good practise as it helps you confirm that your branch is working on the latest changes done by others.

What is rebase

Given you are working on a master branch, then there is one feature, say a sign-in, needed to be implemented. What we do here according a normal git workflow is to create a branch named like feature/sign-in. We say that master branch (at commit 94c09e9) is a parent branch of feature/sign-in.

After some commits done on feature/sign-in, suppose you decide to update a base css and make another commit to master

As you may see, the recent update(0d03724) is not yet applied to feature/sign-in branch. feature/sign-in branch is built based on the old master branch at commit 94e09e9.

In order to get a new base (a new commit 0d03724) for feature/sign-in, we need to rebase feature/sign-in on top of master. Thus, rebase, in a simple way, means to change the parent commit of your working branch. Or you can say to rebuild your target branch on top of the base branch.

Rebase can be done by:

  1. Check out your feature branch, git checkout feature/sign-in
  2. Issue rebase command by supplying a new base branch as parameter, git rebase master

After doing so, you will see that the parent commit of feature/sign-in is changed from 94c09e9 to 0d03724.

How does rebase work

Roughly, we see what rebase does is just to move the parent commit of feature/sign-in to the latest commit of master. But what exactly goes on behind the scene?

After rebase command is triggered, these following things happen.

  1. The commits in feature/sign-in are temporarily kept.
  2. The head pointer of feature/sign-in branch is (hard) reset to 0d03724.
  3. The commits in (1) are reapplied consequently. The reapplying results in a new commit which has the same content as the original commit, but with different commit id. For instance, in this scenario, fee8b31 is re-applied and results in fbcaae2. Then, 48efde8 is replied, and results in 781c6c6. If there was any conflict between commit in feature/sign-in and master, the rebase is paused and the developer has to resolve the conflict before using git add <target file> and git rebase --continue.

What to do after rebase

Issueing rebase command, as you may see, causes the commit’s structure of feature/sign-in to change. The rebased feature/sign-in contains a whole new commits 0d03724, fbcaae2 and 781c6c6 compared to the old branch which contains fee8b31 and 48efde8 (noted that fbcaae2 and 781c6c6 are the same as these commits but they got new SHAs).

By getting a new commit structure, you are prevented from pushing to the remote feature/sign-in. The only way to get your updated right there is to use a force push with git push -f origin feature/sign-in. So, you have to make sure that your working branch is updated before doing rebase, or you may end up losing some commits.

When rebase is needed

Rebase is a part of the safe procedure to get your working branch merged into the main branch.

Let’s say we are working on a master branch, there are 2 branches in progress: feature/sign-in and feature/base-layout. The latter feature is finished before feature/sign-in and is merged to master branch.

Soon, you also finished working on feature/sign-in and you want to merge this to master. How will you do?

The easiest way

The easiest way is to merge the code right away by checking out master and run git merge feature/sign-in.

If you were lucky enough, your code would get merged. If not, you have to resolve the conflicts. When merging is finished, you got a new merge commit e9e2b8a which may contain the conflict-resolved content. The good things for this approach are it is easy to understand and the conflict resolving is done only once. However, there are many disadvantages:

  1. Conflict resolving is done only once… yet there may be numerous conflict on numerous files to be resolved.
  2. If there were so many commits on master and merging is done very late, resolving conflicts is a nightmare. You have to call some people who did updates on master and have them help you resolve the conflicts which they may forget what they have done already.
  3. After merging is success, there is no gaurantee that the code will work as normal or the code will pass all the tests available. In a worst case, you need to commit some fix-commits on master to resolve the problem. This is not pleasing.

The rebase-first way

Instead of issuing merge command right away, you should rebase feature/sign-in on top of master first. Without waiting feature/sign-in to be finished, you should oftenly rebase it with master multiple times (the best is to rebase everytime there is a new commit on master).

Two commits (fee8b31 and 48efde8 ) are re-applied and result in efab4e8 and f16eb33 respectively. During the re-apply, if there are conflicts, the rebase is paused and you have to resolve them. In worst case, the rebase is paused for 2 times if both commits introduced the conflicts. After you have the rebase done, you can then merge your code with a no fast forward option using git merge --no-ff feature/sign-in. This merge is no-conflict guarantee.

The key is, as stated above, rebase often, everytime there is a new commit on main branch. This approach helps solving the problem we face on the merge-right-away approach.

  1. The conflict may happen multiples time, but we compare it in a small amount of change. Resolving it is a lot easier because we do it in a bite-size. Also, the changes are still fresh. People can help you solve it easily because they can still remember what they just did.
  2. After rebase, you can test if your code worked normally on your branch without getting master dirty. All fix-commits happen on your branch. master will be clean and easier to maintain.

Do not forget: rebase often, everytime there is a new commit on main branch. This is very important, or this approach will not benefit you much.

Conclusion

Rebase lets you move your working branch on top of the recent version of parent branch. This process helps you update your code and make it perfect for merging. The key is, always rebase everytime there is a new commit on a parent branch. Also do not forget that, rebase cause your commit tree structure to change. Make sure that you got your branch synced before rebasing.

git

Comments