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.