Tim Hatch

Weblog | Photos | Projects | Panoramas | About

Simulating a Rebase in Mercurial 26 Jul, 2008

I ran into this while doing some work with Pygments this morning — I continued working in a local repo without consciously realizing that a merge node had been created from my work yesterday. If I had just pulled that merge node before committing this morning, everything would be pretty. But it wasn’t, and I wanted to avoid merging the merge which would then cause another merge upstream. Forgive the ascii art.

       *   $PUSH1
       |\
       | * 
       |  \
       |   * $PUSH1
MERGE1 *--/ \
       |     \
       |\     * $CUR2, I don't want this.
       | \
       |  * $PUSH2, I want this, so I can...
MERGE2 *-/

Here’s how:

Background

I pushed $PUSH1, which got merged with $TRUNK, creating $MERGE1.

Some local changes (on top of $PUSH1) later, I am ready to push again (I have $CUR2 and want to end up with $PUSH2 on top of $MERGE1).

First, enable the transplant extension.

[extensions]
transplant=

Go ahead and pull so you have multiple heads in your local repo (don’t push to your personal remote, any of ($MERGE1:$CUR2] or you’ll have to go through an extra set of steps first, below).

Rebasing in HG

# we start with two heads, $MERGE and $CUR2, and no uncommitted changes
hg update -C $MERGE
hg transplant --branch $CUR2
# at this point, your $MERGE head is updated to what we'll call $MERGE2
cd ..

# this is the easiest way to remove $CUR2's head
hg clone -r $MERGE2 repo new_repo
mv repo old_repo
mv new_repo repo
cd repo
hg push

Extra steps if you pushed

If you did push something on $CUR2’s line, you’ll need to fix that first.

ssh remote

hg clone -r$MERGE1 remote new_remote
cp remote/.hg/hgrc new_remote/.hg/
# set permissions like they were before
mv remote old_remote
mv new_remote remote