Using TortoiseHg with Git

There is a mercurial plugin called hggit that allows you to push and pull to/from a Git remote repository from Hg.

To use you just prefix the source repository url with git+ e.g.

1
2
3
git+https://github.com/company/project.git
git+ssh://git@github.com/company/project.git
git://github.com/company/project.git

Installation

TortoiseHg comes with hggit bundled so you can be enable it by going to File > Settings and then under the global settings tab and extensions option as seen in the screenshot below; tick hggit and then restart TortoiseHg.

There have been a couple of TortoiseHg releases in the past where the bundled version of hggit of didn’t work. So alternatively hggit can be manually installed by first cloning the repo:

1
hg clone https://bitbucket.org/durin42/hg-git

And then under File > Settings > Edit File add the following:

1
2
[extensions]
hggit = /path/hg-git/hggit or C:\path\hg-git\hggit

Branches

You cannot create branches the normal Hg way for a Git repo. Hg branches store the branch information as part of each commit, which Git does not support instead it just stores what the latest commit for the branch is in a separate file similar to tags. This does allow branches to be created/deleted/renamed/moved without rewriting the commit history. It also means you don’t have the problem of needing to resolve multiple heads on a branch like you do in Hg since a bookmark can only point to one commit so a branch will only ever have one head. Hg can support Git style branches through the use of a feature called bookmarks and this is what hggit uses to represent Git branches. To use you move or add a new bookmark to the latest commit that’s represented by the branch. Any commits not part of a branch, i.e. has no bookmark pointing to it or a descendant, will not get pushed or pulled (they will still appear in the outgoing changesets preview though but don’t actually get pushed). Hggit also creates a local tag labelled default/<branch name> to mimic git’s origin/<branch name>; default is the alias name for the remote repository url as specified in the settings files.

In the bookmark dialog the new name field is used for renaming an existing bookmark; to add a new bookmark enter the name under the bookmark field and click add. To activate a bookmark means this is the bookmark that will be automatically brought forward when you make your next commit.

Modifying Public History

After rewriting the public history under View > Show Console execute the command:

hg git-cleanup

Then restart TortoiseHg, and perform a pull. This will restore the original commit you had modified, move the bookmark to the tip of the newly created commit (only needed if using the deleted bookmarks workaround). Then perform a forced push:

hg push -f

Afterwards the original commit can be stripped if hg strip extension is enabled by right clicking the commit and selecting strip under modify history context menu.

Note if you’re doing this to remove a password accidentily commited and pushed, it is strongly recommended to change the password as well. Using the above, commits are not removed in Git they’re just hidden (as mentioned here to remove instead of hide on Github involves creating a new repo) so while people can no longer see the original commit in the repository online or pull it down, they can still see it in the activity log sites like Github and Bitbucket provides and click the link to see the hidden commit.

Note #2, if you import a commit into MQ and then unapply and reapply that commit it will have a different hash id even if you made no changes. This only happens with commits you pulled down, commits made locally will have the same hash id if no changes are made. https://bitbucket.org/durin42/hg-git/issue/143

Windows / Linux Issues

Refreshing working directory file status is slow

Sometimes refreshing the status of the files in working directory in TortoiseHg workbench can take a while, especially if you have a large number of ignored files like a node_modules folder. The fix is to make a copy of .gitignore and name it .hgignore instead. When .hgignore exists .gitignore is no longer processed so you may miss changes when performing a pull, the solution is to symlink .gitignore to .hgignore. Note you may need to change the ignore syntax to work with Hg since there are differences, also you can add .hgignore into itself so you won’t be shown .hgingore as an uncommited file.

Push restores old branches that others have deleted

Note when you perform a pull hggit does not delete branches locally that others have deleted from the remote repository, so the next time you do a push you end up restoring the deleted branches (Hg by default pushes all branches unlike Git which by default only pushes the current branch, this can be changed via File > Settings > Sync > Default Push).

The workaround is to add the following to your global settings file File > Settings > Edit File:

1
2
3
4
5
[hooks]
# linux
pre-pull = powershell.exe -command "& { hg bookmarks | %{$_.trim('*',' ').split(' ')[0]} | %{hg bookmark -d $_} }"
# windows
pre-pull = hg bookmarks | tr -d '*' | tr -s ' ' | cut -d ' ' -f 2 | xargs hg bookmark -d

This deletes all the local bookmarks on pre-pull so that pull will restore only the bookmarks that exist on the remote repository. It does mean you will have to re-do any bookmark changes you’ve made like moving a bookmark to the tip that normally happens automatically when you make a commit before pushing. You may experience an error ocassionally I think with the linux version due to no local bookmarks if so just temporarily comment out the pre-pull setting and after pulling re-enable it.

https://bitbucket.org/durin42/hg-git/issue/107

Can’t clone empty repository

Unlike Hg in Git you cannot clone a repository until you have at least one commit, so this isn’t actually a hggit limitation. You can just create a repository locally using File > New Repository and then set the remote url via View > Synchronize and enter url in the textbox then click the save icon to the left of the textbox.

Linux Only Issues

Performing pull is slow

I have experienced it talking a few minutes to perform a pull even when there are no changes to download. This only happens for me with SSH, the workaround is to use HTTPS instead which performs at normal speed (it’s also mentioned upgrading dulwich may solve this but I’ve not tried it after doing so). See here on how to save authentication credentials for HTTPS.

https://bitbucket.org/durin42/hg-git/issue/69

Get “init() got an unexpected keyword argument ‘opener’” error

If you see this error, it likely means your version of dulwich is out of date. Dulwich is a python based implementation of Git that hggit uses.

1
2
3
4
5
6
7
sudo apt-get remove python-dulwich
cd /tmp
# get url for latest version here https://pypi.python.org/pypi/dulwich
wget https://pypi.python.org/packages/source/d/dulwich/dulwich-0.9.8.tar.gz
tar zxvf dulwich-0.9.8.tar.gz
cd dulwich-0.9.8
sudo python setup.py --pure install

https://bitbucket.org/durin42/hg-git/issue/124

Update: This issue is now resolved so on a version of hggit later than 0.8 you won’t experience this issue but it is still recommended to install the latest version of dulwich anyway.