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):
-
git-hg-again uses
hg-git
and 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 thedefault
(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 thegit-remote-helpers
protocol. 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. -
The git-hg script (formerly maintained here) uses
hg-fast-export
. Like method 2, this also maintains a bare Mercurial repository and an additional bare Git repository.
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 `master`.
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.
Don't confuse hg-git
with 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-hg-again
or 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 ~/.hgrc
:
[extensions]
hggit =
bookmarks =
The 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 make
.
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
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.
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 .gitignore
and .hgignore
.
To clone from an upstream Mercurial repository:
git hg clone [--force] url [localdir]
The --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:
hg/default
hg/branch1
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]
and 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 default
.
To keep up to date afterwards:
git hg fetch
git hg pull [--rebase]
git hg push
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
--
Dubiousjim
dubiousjim@gmail.com
https://github.com/dubiousjim