yagh: Yet Another (set of) Git to Hg bridge(s)
I want to use Git to interact with upstream Mercurial repositories. Surely I thought someone else must have already hashed this out. So I looked into what tools were available.
As I describe at Stack Overflow, I found three approaches to this that looked lightweight and initially appealing. Two of the approaches rely on the hg-git extension for Mercurial; the other approach relies on
hg-fast-export from the fast-export project. Here they are (not in order of when they first appeared):
hg-gitand is inspired by a 2010 blog post by Travis Cline. This method uses the toplevel directory as a working directory for both Mercurial and Git at the same time. It creates a Mercurial bookmark that it keeps in synch with the tip of the
default(unnamed) branch in the Mercurial repository; and it updates a local Git branch from that bookmark.
git-remote-hg also uses
hg-git, and additionally makes use of the
git-remote-helpersprotocol. This method uses the toplevel directory only as a working directory for Git; it keeps its Mercurial repository bare. It also maintains a second bare Git repository to make synching between Git and Mercurial safer and more idiomatically Gitlike.
For pulling, this tool ignores Mercurial bookmarks and instead imports every named Mercurial branch into a Git branch, and the
default(unnamed) Mercurial branch into
Some commentary discusses this tool as being hg->git only, but it claims to have merged in git->hg push support on 7 Dec 2011. As I discuss on the Backends page, though, the way this tool tries to implement push support doesn't seem to be workable.
git-hg! The first is a Mercurial extension that's a backend to some of these tools, the second is a frontend script that doesn't make any use of the first.
Update: Recently, I learned of another project called git-remote-hg. Unlike the version listed above, this one doesn't rely on hg-git, but instead directly accesses the Mercurial Python API. At the moment, using it also requires a patched version of git. I haven't tried this yet.
I wasn't sure which of these tools would work best, so I tried out all three. None of them are yet packaged or documented in a friendly way, but they're not very complicated either. The underlying machinery they make use of (the
hg-git extension, the
git-remote-helpers protocol, and the
hg-fast-export script) are what do the heavy lifting.
I saw some ways to usefully tweak the different frontends---in some cases, these tweaks were necessary to get them to run on the FreeBSD machine I'm currently using---and I also thought it'd help evaluate them to make their behind-the-scenes layout more like each other's. This github repo holds the results. I also included a Makefile that will let you install any of the three. I encourage you to try them out and decide for yourself what works best.
Details on installing and using (the yagh versions of) these tools are below.
You might also like to read the accompanying Backends page, which gives the different backend choices a work-out and figures out what works best. The Frontends page then discuss the design choices behind why these frontends work as they do here.
I think these different Git/Hg bridges can work under Windows, too, but I don't know much about that, and will continue on the assumption you're trying to install to some Unix-like system.
You'll need to have Git and Mercurial already installed. I'll leave that to you.
If you plan to use either
git-remote-hg, you'll need to have the
hg-git Mercurial extension installed. Perhaps your distribution already packages this. If not, you might install it by typing
easy_install hg-git. Or see the hg-git homepage for more information.
Concerning the installation of
hg-git, you may see references to including some of the following in your
[extensions] hggit = bookmarks =
bookmarks line isn't necessary anymore; that's been built into the Mercurial core since version 1.8, released 1 March 2011. The
hggit line can be included in your
~/.hgrc if you like; but the versions of these tools that are distributed with yagh will also specify that in the individual repositories, so you really don't need to bother with it.
To use the version of
git-remote-hg that I'm supplying here, the upstream Mercurial server you're interacting with needs to support bookmarks. So it either needs to be version
>= 1.8, or it needs to have the older
bookmarks extension enabled.
Finally, type one of the following:
gmake && sudo gmake install-git-hg-again gmake && sudo gmake install-git-remote-hg gmake && sudo gmake install-git-hg
(If you don't like using
sudo, I expect you'll know what to do instead.) I say
gmake to make it clear that you need to use GNU Make. On some systems, that's only installed under the name
This will install yagh's version of the chosen system. Only one such can be enabled at a time. If you want to evaluate a different system, just request its installation, and the previous one will automatically be disabled.
If you want to uninstall all this stuff, type:
sudo gmake uninstall
How do the versions distributed here differ from their upstream originals?
See the git logs. I will inform the upstream authors of the changes that seemed useful, and will try to keep track of other changes they make to the originals. I'll be glad to hear about cases where any of these tools break.
How does one use these tools?
That depends on which tool you're using. I've tried to make the versions packaged here behave as close to each other as possible, but they're still not exactly the same.
Note that these instructions apply to these tools as configured in yagh, which differ in several ways from how the original authors distribute them.
To clone from an upstream Mercurial repository:
git clone hg::url [localdir]
Bookmarks and branches in the Mercurial repository will be visible in Git as remote tracking branches of the form:
hg/default hg/branch1 hg/bookmark1
To keep up to date:
git fetch [hg] git pull [--rebase] [hg] git push
These are just the plain
git fetch and so on commands. You can omit the
hg if you're in a branch that tracks part of the upstream Mercurial repository. Your
master branch is initially set up to do so.
To push "new branches" into the Mercurial repository, do this:
git push hg git_branchname:hg_branchname git branch --set-upstream git_branchname hg/hg_branchname
If you want the branch to be named the same on both ends, you can just use
branchname instead of
branchname:branchname. (But you still need to call
git branch --set-upstream ...) Note that what these commands will do is push a new Mercurial bookmark upstream; when you push and pull, that bookmark will be kept in synch with the head of your Git branch.
All of your Git commits will belong to the
default branch in Mercurial, even if they're on a Git branch that you based on a named Mercurial branch. This means that you may be pushing multiple unmerged heads upstream, one for each of the Git branches you push changes on. Hopefully the maintainers and other users of the upstream repository will be OK with that.
Our frontend will try to keep the
hg/default branch (which your
master branch pulls from) in synch with the Mercurial commits that no other Git branch/Mercurial bookmark is tracking.
If you want to delete a Mercurial branch, you can try this:
git push hg :hg_branchname_to_delete
Note though that this will only delete the "branches" that are implemented as Mercurial bookmarks. Those that correspond to named branches in the Mercurial repository will be automatically re-created in your
hg Git remote the next time you pull. If you want to ignore them, just don't base any local Git branches on them.
This tool permits the use of multiple Mercurial remotes for a given Git repository. Just specify the ones after the first by:
git remote add [-t branch] [--tags] remotename hg::url
Then you can
git fetch remotename and so on as usual.
Global tags from Mercurial upstream will be pulled into Git annotated tags, but won't be pushed back in the other direction. Additionally, no attempt is made to coordinate
To clone from an upstream Mercurial repository:
git hg clone [--force] url [localdir]
--force will be needed if the repository has multiple heads on a branch.
Branches from the Mercurial repository will be visible in Git as remote tracking branches of the form:
To checkout a new local branch following one of the remote branches, use:
git hg checkout [--force] branch1
This will create a new branch named
branch1 in your working Git repository, that tracks the remote
hg/branch1 branch which is synchronized with upstream Mercurial. If there already exists a local branch
branch1, the command will fail. After
branch1 has been created, it can be checked out again later using the ordinary
git checkout. When checked out, it can be brought up-to-date with the Mercurial upstream using:
git hg pull [--force] [--rebase]
or you can use:
git hg fetch [--force]
git merge or
git rebase by hand. The same commands are used to bring your
master branch up-to-date.
This tool doesn't provide good push support.
To clone from an upstream Mercurial repository:
git hg clone url [localdir]
This will create a local Git branch
default, that your
master branch will track and be based on. You can also commit directly to
To keep up to date afterwards:
git hg fetch git hg pull [--rebase] git hg push
Mercurial for Git Users
Here are some useful comparisons/translation manuals between Git and Mercurial, in some cases targetted at users who already know Git:
- Mercurial for Git users
- Git and Mercurial - Compare and Contrast
- What is the difference between Mercurial and Git
- Mercurial and Git: a technical comparison
- Git hg rosetta stone
- Homebrew Coding: Mercurial
- Francisoud's Blog: Git vs Mercurial (hg)
- Git vs Mercurial