Multiple headed Mercurial problems

Date: Mon Apr 10 2017 Mercurial

Supposedly people who imbibe mercury become crazy as, well, a madhatter (so named because old-style hat-making practices involve the use of mercury). So maybe it sounds crazy to talk about multiple heads but in this case the mercury is the Mercurial source code management system. In Mercurial a 'head' is the endpoint of a chain of changes and it becomes downright inconvenient when there are multiple endpoints in the chain of changes in your source repository.

I ran into this twice today and learned a new mercurial command and want to share what I learned.

First I knew of the problem was this:-

[tang]$ hg pull
pulling from /home/reikiman/
searching for changes
adding changesets
adding manifests
adding file changes
added 11 changesets with 107 changes to 212 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)

Hoooboy! I've never completely understood how to get out of this problem. What got me into the problem is that I use the same mercurial repository to drive multiple websites, I had just updated the Drupal code on one of my sites, and had checked in the changes to the master repository, and then gone over to the other site to begin updating it. Only to be met by "+1 heads". Whoopsie. There were outstanding commits in the other website which had not been pushed to the master. Meaning that the updates in the master and the updates in this child repository were in conflict.

First lesson, when you're done with a set of changesets in a repository, check them into the master.

The main thing I knew about resolving multiple heads problems is to use the merge command. But that didn't help much

[tang]$ hg merge
86 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
[tang]$ hg update
abort: outstanding uncommitted merges

I also couldn't push

[tang]$ hg push
pushing to /home/reikiman/
searching for changes
abort: push creates new remote heads!
(did you forget to merge? use push -f to force)

I was in trouble and spent the next 10-20 minutes spinning around with different commands trying to work out what to do. Nothing would merge and to my knowledge there isn't a way to erase changesets once they're in the repository. I could have moved the website code out of the way, checked out a fresh repository, fixed up the web site, and manually in outstanding merged changes. But I came up with a different solution.

Rollback is a way to erase the last transaction, whatever it was. This wasn't much help in this case but it may be useful in other circumstances. In this case I had three changesets which hadn't been pushed.

[tang]$ hg rollback
rolling back last transaction

Revert is a way to remove any current changes which haven't been committed. It can limit the reversion to specific files or directories, and can revert to a specific revision.

Strip is a way to remove changesets (and all following changesets) from a repository. I said above I did not know of a way to erase changesets, but there is, you can strip changesets out of a repository. When you strip changesets it removes the given changeset plus all the following ones.

[tang]$ hg strip 86
abort: local changes found

Hurm, local changes means that some files were modified. It was necessary to first "hg revert --all" to remove the modifications and then to do this to remove the outstanding changesets:-

[tang]$ hg strip 86
114 files updated, 0 files merged, 65 files removed, 0 files unresolved
saving bundle to /home/.nurse/reikiman/


[tang]$ hg pull
pulling from /home/reikiman/
searching for changes
adding changesets
adding manifests
adding file changes
added 11 changesets with 233 changes to 212 files
(run 'hg update' to get a working copy)
[tang]$ hg update
212 files updated, 0 files merged, 0 files removed, 0 files unresolved


Later in the day I had the same problem arise in another repository. I had a new tweak to the problem. In the above case I could live with throwing away the changes in the local repository because I knew I could easily get them back. That's because of the nature of that repository (a Drupal instance) and that Drupal is good about telling me what code I need to update. In the other one I needed to preserve the changes.

I found the hg export command useful. I first exported the changesets I wanted like so

$ hg export 227 >~/Desktop/227.txt
$ hg export 228 >~/Desktop/228.txt

Then I checked out a new repository and applied the changesets there.

$ hg clone http://server/path/to/repository
$ hg import ~/Desktop/227.txt
$ hg import ~/Desktop/228.txt
$ hg push

This allowed my changesets to get to the master repository.