Mercurial subrepositories to Git submodules

  |   Source

I didn't start with Git. Mercurial has become my first DVCS. Because of that, Mercurial actually ate my first source code. I'm a huge Git fan now, and I switched all my single-repo repositories to Git using this awesome tool. Now, long few years later, the tool has finally gotten the Mercurial subrepositories to Git submodules support, so now I'm able to migrate to Git completely.

So what I did is the following steps:

Convert all Mercurial subrepositories first

For each subrepository convert the subrepository like this:

git init SOME_GIT_SUBMODULE_NAME_N
cd SOME_GIT_SUBMODULE_NAME_N
hg-fast-export.sh -r SOME_HG_SUBREPO_PATH_N
git add remote origin SOME_REMOTE_URL_N
git push --mirror origin
cd ..

Create the subrepositories to Git repositories mapping file

The mapping file is generally a key/value file where keys and values and enquoted with " and joined with =:

"dir1"="../SOME_GIT_SUBMODULE_NAME_1"
"dir2"="../SOME_GIT_SUBMODULE_NAME_2"
"dir3"="../SOME_GIT_SUBMODULE_NAME_3"

Note a few things here:

  • The paths (the values) must be relative paths by the current design of the convertor.
  • If the paths were moved during the lifetime of the super-repo, the previous subdirectories must also be specified as keys.
  • If there's a repository that cannot be mapped anymore (say, a repository does not longer exist), consider use of an empty repository (not sure if it's a good idea though).

Convert the super-repository using the mapping file

git init SOME_GIT_SUPER_REPO_NAME
cd SOME_GIT_SUPER_REPO_NAME
hg-fast-export.sh -r SOME_HG_SUPER_REPO_PATH --subrepo-map=MAPPING_FILE_PATH
cd ..

Convert the relative submodule paths to remote repository URLs

filter-branch is a great tool that helped me a lot. Now it can help one more time. I resolved my own case using sed:

cd SOME_GIT_SUPER_REPO_NAME
git filter-branch -f --tree-filter "sed -i -e 's/url = SOME_LOCAL_PATH_TO_REPLACE/url = SOME_REPOSITORY_URL_TO_REPLACE_WITH/g' .gitmodules || true" -- --branches --tags
cd ..

which processes every commit in the repository moving branches and tags along. The --tree-filter may take some time to convert, but I didn't use the --index-filter option for simplicity.

Comments powered by Disqus