Rebase in Mercurial

I would like to introduce a quite useful Mercurial extension: RebaseIf. It brings simplicity to the rebase feature by activating it at pull time. If the rebase can’t be done a classic merge is performed.

Basically, rebasing is a good way to keep a clean log history. Consider the following situation:

  • Alice & Bob pull the repo at revision R0
  • Alice adds 2 changesets to her local repository (A1 and A2)
  • Bob adds 2 changesets to his local repository (B1 and B2)
  • Bob pushes to the remote repository

In this quite common situation, when Alice tries to push her changesets, she won’t be able to because there are new ones to be pulled (Bob’s ones).

So Alice issues those commands:

hg pull -u
hg merge
hg commit -m "Merge"
hg push

It results in a log tree like this:

* Tip "Merge"
|    \
* B2  * A2
|     |
* B1  * A1
|    /
* R0

One parallel branch contains Alice’s changesets, the other one contains Bob’s changesets. This is a bad thing because:

  • A merge commit is added which does not contains anything but the melt of B1 + B2 + A1 + A2
  • It confuses reading of the log tree
  • It does not reflect anything but the fact that Alice and Bob worked at the same time on the repository which is not a relevant information in a log history.

Note the fact that Mercurial does not consider that Bob and Alice could have worked on totally different files and thus their changesets would not conflict with each others. By default, Mercurial needs a merge to move on.

This is where rebasing comes along. The idea is, in Alice position, to move aside your changesets, pull the repo and put your changeset back after the pulled ones. In the previous example it would result in a log tree like this:

* Tip A2
* A1
* B2
* B1
* R0

This log tree is clearer because:

  • There is no branch
  • There is no merge commit

It is really important to understand that a rebase can only be done if there is no conflict in changesets. For instance if A1 contained modifications to a file modified by B2, no rebase could be done, a merge would be necessary.

Another important thing is that, rebasing does not “move” changeset per se, it creates new ones. In our case, A1 and A2 would be discarded and new ones would be created.

In Mercurial the only way to perform rebase is by using an extension: Rebase or RebaseIf. Rebase is shipped with Mercurial, RebaseIf has to be installed.

With RebaseIf, Alice would need to issue those commands to push

hg pull --rebasif
hg push

Note that the author of this extension has changed his mind and prefers not to use it anymore. That being said, I personnaly use it and never experienced any problems.

That’s all folks!

Published: March 28 2015

  • category:
blog comments powered by Disqus