Merging disjoint histories with mercurial

At work I was tasked to develop three different lines of work. Each one seemed to be auto-contained and with an specific and compact obective. Of course, for each one of these projects, I set up a VCS repo. The chosen one here at work is Mercurial.

But then, the fourth task proved to be the integration of the three previous tasks in one demo that would show them off and then evolve into an application. At fisrt I just relied on setting up a fourth repo, adding to it only the new files, and symlinking the files I needed from the other repos. It was hacky, but it allowed me to continue the development quickly, and as long as I kept making commits in the four different repos, all was peachy.

Today, after the demo was presented, it occured to me that this setup wasn't easy to share with the rest of the team. So far I was working alone, but now that this project will get more and more developers, sharing was crucial.

So I asked in #mercurial and the short answer was:

11:13 < hstuart> StucKman, you can pull --force and merge the unrelated heads together, but it'll be a two-way merge and there'll potentially be a lot of conflict resolution if they have diverged in the same files

As the four repos had disjoint file set, this seemed enough for me. So, the first step was to set up a fifth empty clean repo:

$ hg init

No surprises there. Now I pulled from the four lines of history from other directories:

$ hg pull --force ../soap/
pulling from ../soap/
requesting all changes
adding changesets
adding manifests
adding file changes
added 7 changesets with 9 changes to 3 files
(run 'hg update' to get a working copy)

$ hg pull --force ../stomp/
pulling from ../stomp/
searching for changes
warning: repository is unrelated
adding changesets
adding manifests
adding file changes
added 43 changesets with 49 changes to 8 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)

$ hg pull --force ../phidgets/
pulling from ../phidgets/
searching for changes
warning: repository is unrelated
adding changesets
adding manifests
adding file changes
added 29 changesets with 40 changes to 7 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)

$ hg pull --force ../smartimmo/
pulling from ../smartimmo/
searching for changes
warning: repository is unrelated
adding changesets
adding manifests
adding file changes
added 26 changesets with 58 changes to 20 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)

So far so good. I checked with hgview what I had: four lines of history with no common point. Do you see that each pull made a new head? Here, look:

$ hg heads
changeset:   104:a923c43e33fa
tag:         tip
user:        Marcos Dione <mdione@sophia.inrira.fr>
date:        Fri Dec 17 11:04:24 2010 +0100
summary:     * soft-coded image filepaths (patch by Manuel).

changeset:   78:1a3faf6e6e8a
branch:      single-ifkit
user:        Marcos Dione <mdione@sophia.inrira.fr>
date:        Thu Dec 16 16:12:40 2010 +0100
summary:     * wrong message for up-> nobody.

changeset:   74:53c2942a8548
user:        Marcos Dione <mdione@grulic.org.ar>
date:        Wed Dec 01 14:58:53 2010 +0100
summary:     * s/state-update/light-state-update/ in event name, so it doesn´t clash with the same event in the temp controller.

changeset:   49:9480820604fe
user:        Marcos Dione <mdione@sophia.inrira.fr>
date:        Thu Dec 16 16:13:04 2010 +0100
summary:     + si-heater-switch.

changeset:   6:be775f2b6af5
user:        Marcos Dione <mdione@sophia.inrira.fr>
date:        Fri Jul 09 11:43:58 2010 +0200
summary:     * cleanup.

Notice how the tip is set to the tip of the last repo I pulled from. If you're very 'detaillist' (I'm not sure that word exists in English; it does in Spanish), take that in account. Me, I didn't care that much. So the next step was to merge them:

$ hg merge --force 6
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don´t forget to commit)
$ hg ci -m "|\ soap history."

The first one went fine, but the second one presented a small problem:

$ hg merge --force 49
merging smart-immo.scm
merging smart-immo.scm failed!
7 files updated, 0 files merged, 0 files removed, 1 files unresolved
use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon

So I went back, before the merge, moved the file, and continued:

$ hg update -C 105
1 files updated, 0 files merged, 7 files removed, 0 files unresolved
$ hg mv smart-immo.scm smart-immo-soap.scm
$ hg ci -m "* rename to avoid file clashes."
$ hg merge --force 49
8 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don´t forget to commit)
$ hg ci -m "|\ obix/stomp history."
$ hg merge --force 74
6 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don´t forget to commit)
$ hg ci -m "|\ smartimmo demo history."

I still had something left: one of the hsitories had a branch which wasn't default but was tip, so I had to merge that too. I don't like how this is done in Mercurial, but I know the moves:

$ hg update -C single-ifkit
2 files updated, 0 files merged, 31 files removed, 0 files unresolved
$ hg ci --close -m "_ closing before merging."
$ hg update -C default
33 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg merge single-ifkit
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don´t forget to commit)
$ hg ci -m "|\ single-ifkit."
$ hg heads
changeset:   110:bfc9eb117a76
tag:         tip
parent:      108:d53b4b9a3ffc
parent:      109:b41570e653c9
user:        Marcos Dione <mdione@sophia.inrira.fr>
date:        Fri Dec 17 11:34:26 2010 +0100
summary:     |\ single-ifkit.

Tadaaa! Yes, I would like a hg merge --close <head> command that did all this dance for me, but as I said, as I know the hoops I have to jump through, and as long they're not in flames, I can live with it. The only thing I miss from other VCS is Bazaar's --show-diff commit option, which lets me review the diff while editing the commit message.

One thing that I figured out out of this: histories in Mercurial are acyclic directed graphs, yes, but they don't need to have only one root. Nice.