Skip to main content

Migrating Hg Repos with hg-fast-export and Windows Subsystem for Linux

Introduction

I prefer Mercurial (hg) to git. I don’t really have any reason for this preference - they both do the same thing, and the user experience for 90% of the use cases is the same. It probably comes from the conditions of the DVCS landscape when I started using these systems. Some of this may have been perception only, but it looked like this:

  • GitHub didn’t have free private repos
  • BitBucket did have free private repos
  • BitBucket was very hg-friendly
  • Joel Spolsky had an amazing tutorial that served as both a how-to for hg as well as a general intro to DVCS
  • hg was much more Windows-friendly than git
  • Since hg was written in python, I felt like extending it would be easier than doing so for git if I ever needed to (admittedly, this is a pretty ridiculous reason)
  • hg felt like a more unified, “coherent” system than the very linux-y feeling git and its extensions (also pretty ridiculous)
  • Where they differed, I liked the verbs hg used better than git’s counterparts

Nowadays, most of these are moot:

  • Git has private repos
  • Git for Windows is a thing, and I also love SourceTree
  • Pretty much everybody else in the world uses git

Anyway, I still generally prefer using hg and hosting with BitBucket, so I have many (private) hg repos out there. Unfortunately, BitBucket is sunsetting support for Mercurial, so I have a lot of migrating to do.

Migration Tools

There are basically two ways to migrate a Mercurial repo to git:

  • Use the hg-git Mercurial plugin to push an existing hg repo to a git repo
  • Execute hg-fast-export against a local repo to export to create a git version of the repo in another local directory

I’ve used hg-git before; in general it works pretty well. But I’d need to install and configure it, or install TortoiseHg, and that didn’t sound appealing. On the other hand, hg-fast-export is a bash script, which requires a bash. The readme says On windows the bash that comes with "Git for Windows" is known to work well. Well, I don’t use Git for Windows, so that didn’t sound very appealing either.

I thought, “maybe I should boot into Ubuntu and do all of this.” Wait… why not give Windows Subsystem for Linux a try?

Using WSL and hg-fast-export

Once I installed Windows Subsystem for Linux (note you have to run a PowerShell command to enable installation) with Ubuntu 18.04, I performed the following steps to prepare my system:

  • Update all packages (sudo apt update)
  • Upgrade all packages (sudo apt upgrade)
  • Install Python 2.7 (sudo apt install python)
  • Install pip (sudo apt install python-pip)
  • Install Mercurial (sudo pip install mercurial)
  • Clone the hg-fast-export repo (sudo git clone https://github.com/frej/fast-export.git)

It’s important to note that I performed the last step after changing to the directory I wanted to clone to. WSL mounts your Windows drives and gives you access via /mnt, so your entire C: drive is accessible under /mnt/c, for example. My repos were in C:\repos and I wanted to put the fast-export tool alongside them, so I did this:

cd /mnt/c/repos
mkdir hg-fast-export
cd hg-fast-export
sudo git clone https://github.com/frej/fast-export.git

With the tools I needed all set up, I converted a project in an hg repo named my-project like this:

mkdir /mnt/c/repos/my-project-git
cd /mnt/c/repos/my-project-git
sudo git init
sudo ./../hg-fast-export/fast-export/hg-fast-export.sh -r ./../my-project

The result was some output like this:

master: Exporting full revision 1/75 with 35/0/0 added/changed/removed files
master: Exporting simple delta revision 2/75 with 0/4/2 added/changed/removed files
   ...
Skip .hgtags
master: Exporting simple delta revision 5/75 with 1/0/0 added/changed/removed files
develop: Exporting simple delta revision 6/75 with 0/0/0 added/changed/removed files
   ...
feature/authentication: Exporting simple delta revision 75/75 with 26/3/8 added/changed/removed files
Exporting tag [initial-setup] at [hg r2] [git :3]
Exporting tag [appharbor-build-and-deployment-working] at [hg r19] [git :20]
Issued 77 commands
/usr/lib/git-core/git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:          689 (       162 duplicates                  )
      blobs  :          331 (       105 duplicates        227 deltas of        331 attempts)
      trees  :          283 (        57 duplicates        191 deltas of        282 attempts)
      commits:           75 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:          10 (         8 loads     )
      marks:           1024 (        75 unique    )
      atoms:            202
Memory total:          2344 KiB
       pools:          2110 KiB
     objects:           234 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 35184372088832
pack_report: pack_used_ctr            =         94
pack_report: pack_mmap_calls          =         45
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =     732185 /     732185
---------------------------------------------------------------------

I followed this up with git checkout HEAD. I examined the resulting git repo in SourceTree, and I have to say it looks like a good copy. I didn’t use any technical measure to verify the conversion. I could see that the default branch from the hg repo was renamed master in the git repo in accordance with convention. hg-fast-export allows you to customize the export process with plugins as well. More details are available on the github readme.

Overall, I found that the Windows Subsystem for Linux was an excellent way to use linux terminal tools to apply changes on a Windows system!

Comments

Popular posts from this blog

Enabling Globalization Invariant Mode for .NET Core App on Raspberry Pi Running LibreElec

I had an app I wanted to run on my Raspberry Pi 3 running LibreElec . In LibreElec you can install the dotnet core 2.2 runtime as an addon, and in Visual Studio you can compile for ARM processors with ‘Target Runtime’ set to ‘linux-arm’ in the publish profile. So, I published to a folder from VS using that profile, and I copied the output over to my RPi which had the dotnet runtime installed. I did a simple dotnet Whatever.dll to run the app (actually in this case, it was /storage/.kodi/addons/tools.dotnet-runtime/bin/dotnet Whatever.dll because of the way the addon is installed) and was met with this error: FailFast: Couldn't find a valid ICU package installed on the system. Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support. at System.Environment.FailFast(System.String) at System.Globalization.GlobalizationMode.GetGlobalizationInvariantMode() at System.Globalization.GlobalizationMode..cctor() at Syste...

Stubbing Static Methods with PostSharp

TypeMock uses the Profiler API to allow mocking, stubbing, etc. of classes used by code under test. It has the ability to handle sealed classes, static classes, non-virtual methods, and other troublesome-yet-oft-encountered scenarios in the world of unit testing. Other frameworks rely on proxies to intercept method calls, limiting them to be able to only fake virtual, abstract, and interface members. They also rely on dependecy injection to place the proxies as the concrete implementation of calls to the abstracted interface members. Anyone working with a legacy codebase is bound to run into static method calls (especially in the data access layer), dependencies on concrete types with non-virtual methods, and sealed class dependencies (HttpContext anyone?). The only way to unit test this without refactoring is with TypeMock. I've never used TypeMock, and I'm sure it's a great product, but it's not free. I decided to spike some code to see if I could solve the prob...