Signed-off-by: Felipe Contreras felipe.contre...@gmail.com
---
I've looked at many hg-git tools and none satisfy me. Too complicated, or too
slow, or to difficult to setup, etc.
The only one I've liked so far is hg-fast-export[1], which is indeed fast,
relatively simple, and relatively easy to use. But it's not properly maintained
any more.
So, I decided to write my own from scratch, using hg-fast-export as
inspiration, and voila.
This one doesn't have any dependencies, just put it into your $PATH, and you
can clone and fetch hg repositories. More importantly to me; the code is
simple, and easy to maintain.
One important remote-hg alternative is the one written by Sverre Rabbelier that
is now maintained and distributed in msysgit, however, in my opinion the code
is bloated, and there isn't even a standalone branch to take a look at the
patches, and give them a try.
This version has some features that Sverre's version doesn't:
* Support for tags
* Support to specify branchesto pull
Sverre's version has some features this one doesn't:
* Support for pushing
* Tests
[1] http://repo.or.cz/w/fast-export.git
Changes since v1:
* Improved documentation
* Use more common 'python' binary
* Warn, don't barf when a branch has multiple heads
* Fixed marks to fetch after cloned
* Support for cloning/pulling remote repositories
* Use a more appropriate internal directory (e.g. .git/hg/origin)
* Fixes for python3
contrib/remote-hg/git-remote-hg | 254
1 file changed, 254 insertions(+)
create mode 100755 contrib/remote-hg/git-remote-hg
diff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg
new file mode 100755
index 000..cc53091
--- /dev/null
+++ b/contrib/remote-hg/git-remote-hg
@@ -0,0 +1,254 @@
+#!/usr/bin/python
+
+# Inspired by Rocco Rutte's hg-fast-export
+
+# Just copy to your ~/bin, or anywhere in your $PATH.
+# Then you can clone with:
+# git clone hg::/path/to/mercurial/repo/
+
+from mercurial import hg, ui
+
+import re
+import sys
+import os
+import json
+
+first = True
+
+def die(msg, *args):
+sys.stderr.write('ERROR: %s\n' % (msg % args))
+sys.exit(1)
+
+def warn(msg, *args):
+sys.stderr.write('WARNING: %s\n' % (msg % args))
+
+def gitmode(flags):
+return 'l' in flags and '12' or 'x' in flags and '100755' or '100644'
+
+def export_file(fc):
+if fc.path() == '.hgtags':
+return
+d = fc.data()
+print M %s inline %s % (gitmode(fc.flags()), fc.path())
+print data %d % len(d)
+print d
+
+def get_filechanges(repo, ctx, parents):
+l = [repo.status(p, ctx)[:3] for p in parents]
+changed, added, removed = [sum(e, []) for e in zip(*l)]
+return added + changed, removed
+
+author_re = re.compile('^((.+?) )?(.+?)$')
+
+def fixup_user(user):
+user = user.replace('', '')
+m = author_re.match(user)
+if m:
+name = m.group(1)
+mail = m.group(3)
+else:
+name = user
+mail = None
+
+if not name:
+name = 'Unknown'
+if not mail:
+mail = 'unknown'
+
+return '%s %s' % (name, mail)
+
+def get_repo(path, alias):
+global dirname
+
+myui = ui.ui()
+myui.setconfig('ui', 'interactive', 'off')
+
+if hg.islocal(path):
+repo = hg.repository(myui, path)
+else:
+local_path = os.path.join(dirname, 'clone')
+if not os.path.exists(local_path):
+srcpeer, dstpeer = hg.clone(myui, {}, path, local_path,
update=False, pull=True)
+repo = dstpeer.local()
+else:
+repo = hg.repository(myui, local_path)
+peer = hg.peer(myui, {}, path)
+repo.pull(peer, heads=None, force=True)
+
+return repo
+
+def hg_branch(b):
+if b == 'master':
+return 'default'
+return b
+
+def git_branch(b):
+if b == 'default':
+return 'master'
+return b
+
+def export_tag(repo, tag):
+global prefix
+print reset %s/tags/%s % (prefix, tag)
+print from :%s % (repo[tag].rev() + 1)
+print
+
+def export_branch(repo, branch):
+global prefix, marks, cache, branches
+
+heads = branches[hg_branch(branch)]
+
+# verify there's only one head
+if (len(heads) 1):
+warn(Branch '%s' has more than one head, consider merging %
hg_branch(branch))
+
+head = repo[heads[0]]
+tip = marks.get(branch, 0)
+# mercurial takes too much time checking this
+if tip == head.rev():
+# nothing to do
+return
+revs = repo.revs('%u:%u' % (tip, head))
+count = 0
+
+revs = [rev for rev in revs if not cache.get(rev, False)]
+
+for rev in revs:
+
+c = repo[rev]
+(manifest, user, (time, tz), files, desc, extra) =
repo.changelog.read(c.node())
+rev_branch = git_branch(extra['branch'])
+
+tz = '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)
+
+print commit %s/branches/%s % (prefix, rev_branch)
+print mark :%d % (rev + 1)
+