Category Archives: Mercurial

Move a directory (or a few) to a new repository, with history, in Mercurial

This popped up on my schedule once again today, so I thought I better save it here – JIC.

The goal is simple: project grew big, some parts got a job and moved to LA are big enough to be developed separately. However it’d be better to move them along with their history. Luckily, hg can get you there, with a little to no sweat. Here’s the deal.

First, you need the convert extension enabled.  http://mercurial.selenic.com/wiki/ConvertExtension (that’s where extra details are) says it should be

[extensions]
hgext.convert=

in your ~/.hgrc (or wherever level you keep your Mercurial configuration at), but mine has

[extensions]
convert=

So… go figure yourself.

Then, you need to get a list of what you would like relocated and save it to some file, each line starting with “include” (you can also do “exclude” if you need some inner parts excluded). Like this (I’m using some real name from project so I could share it with colleagues. Names are pretty vague though, so no confidentiality breach involved):

airhydra:uslicer halien$ cat /tmp/targets
include API/REST
include Scheduler
include UI/lib

if you need to rename some directories in new repository, you can do that with adding “rename olddir newdir” (that should go after “include” list) – for instance,

airhydra:uslicer halien$ cat /tmp/targets
include API/REST
include Scheduler
include UI/lib
rename Scheduler Backend/Scheduler

Now, run that convert command, pointing it to a file with targets/actions, source and target (temporary) directory. In my case, source is current directory. Remember: considering on repository size, it could go on for some time and crap in your console like a bloody deranged elephant. Example (crap wiped off):

airhydra:uslicer halien$ hg convert --filemap /tmp/targets ./ /tmp/TempRepo
initializing destination /tmp/TempRepo repository
scanning source...
sorting...
converting...
15855 add data validation
15854 update data validation
15853 add API placeholder
15852 add test data
15851 Initial API skeleton
(...loads of crap...)

Now go to the repository you need those changes at. Here starts the artificial part – I initialized a new repo called NewWorld, but you should create and clone (or, well, just init, if that’s the case) yours in advance. In that repo, pull from the temporary repository you’ve dumped changes into:

airhydra:NewWorld halien$ hg pull -f /tmp/TempRepo
pulling from /tmp/TempRepo
requesting all changes
adding changesets
adding manifests
adding file changes
added 5809 changesets with 7074 changes to 364 files (+166 heads)
(run 'hg heads' to see heads)

Big one, eh? That’s just a fraction. Deserved a move long ago.

If your repository is new, just update to the default branch (or whatever you fancy). If you’re pulling changes into existing one, do a merge. And if the existing repository had some branches named similarly to those available in pulled changeset, you need to resolve conflicts. Which is ugly, so I wish you don’t have to. Or it might be one of the rare cases to justify push -f (given you don’t care much of those branches).

Aaaand… that should be it:

airhydra:NewWorld halien$ ls
API Backend UI
airhydra:NewWorld halien$ hg branches|wc -l
918
airhydra:NewWorld halien$ hg hi|grep changeset:|wc -l
5809

It probably could be done in some more, ehm, (cruelly) sophisticated way through pipes – but I don’t want to bother. This one’s good enough for the case.