{"thread":{"id":"31884","subject":"[PATCH v3 0/6] New remote-hg helper","startedAt":"2012-10-21T17:48:58Z","lastAt":"2012-10-24T15:47:40Z","messageCount":9,"participants":["Felipe Contreras","Sverre Rabbelier"],"isPatch":true,"patchVersion":3,"patchTotal":6},"messages":[{"id":"201634","messageId":"1350841744-21564-1-git-send-email-felipe.contreras@gmail.com","threadId":"31884","inReplyTo":null,"subject":"[PATCH v3 0/6] New remote-hg helper","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-21T17:48:58Z","receivedAt":"2012-10-21T17:48:58Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"I've looked at many hg<->git tools and none satisfy me. Too complicated, or too\nslow, or to difficult to setup, etc.\n\nThe only one I've liked so far is hg-fast-export[1], which is indeed fast,\nrelatively simple, and relatively easy to use. But it's not properly maintained\nany more.\n\nSo, I decided to write my own from scratch, using hg-fast-export as\ninspiration, and voila.\n\nThis one doesn't have any dependencies, just put it into your $PATH, and you\ncan clone and fetch hg repositories. More importantly to me; the code is\nsimple, and easy to maintain.\n\nOne important remote-hg alternative is the one written by Sverre Rabbelier that\nis now maintained and distributed in msysgit, however, in my opinion the code\nis bloated, and there isn't even a standalone branch to take a look at the\npatches, and give them a try.\n\nThis version has some features that Sverre's version doesn't:\n\n * Support for tags\n * Support to specify branches to pull\n\nSverre's version has some features this one doesn't:\n\n * Support for octopus merges\n\n[1] http://repo.or.cz/w/fast-export.git\n\nChanges since v2:\n\n * Added support for pushing\n * Tests copied from original remote-hg\n * Custom default -> master renames removed\n * Code reorganized\n\nChanges since v1:\n\n * Improved documentation\n * Use more common 'python' binary\n * Warn, don't barf when a branch has multiple heads\n * Fixed marks to fetch after cloned\n * Support for cloning/pulling remote repositories\n * Use a more appropriate internal directory (e.g. .git/hg/origin)\n * Fixes for python3\n\nFelipe Contreras (6):\n  Add new remote-hg transport helper\n  remote-hg: add support for pushing\n  remote-hg: add support for remote pushing\n  remote-hg: add support for trivial features\n  tests: add remote-hg tests\n  tests: fix remote-hg warnings for modern git\n\n contrib/remote-hg/git-remote-hg | 489 ++++++++++++++++++++++++++++++++++++++++\n t/t5801-remote-hg.sh            | 143 ++++++++++++\n 2 files changed, 632 insertions(+)\n create mode 100755 contrib/remote-hg/git-remote-hg\n create mode 100755 t/t5801-remote-hg.sh\n\n-- \n1.8.0.rc2.7.g0961fdf.dirty\n"},{"id":"201636","messageId":"1350841744-21564-2-git-send-email-felipe.contreras@gmail.com","threadId":"31884","inReplyTo":"1350841744-21564-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 1/6] Add new remote-hg transport helper","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-21T17:48:59Z","receivedAt":"2012-10-21T17:48:59Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-hg/git-remote-hg | 313 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 313 insertions(+)\n create mode 100755 contrib/remote-hg/git-remote-hg\n\ndiff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg\nnew file mode 100755\nindex 0000000..f0ce4a4\n--- /dev/null\n+++ b/contrib/remote-hg/git-remote-hg\n@@ -0,0 +1,313 @@\n+#!/usr/bin/python\n+\n+# Inspired by Rocco Rutte's hg-fast-export\n+\n+# Just copy to your ~/bin, or anywhere in your $PATH.\n+# Then you can clone with:\n+# git clone hg::/path/to/mercurial/repo/\n+\n+from mercurial import hg, ui\n+\n+import re\n+import sys\n+import os\n+import json\n+\n+first = True\n+\n+AUTHOR_RE = re.compile('^((.+?) )?(<.+?>)$')\n+\n+def die(msg, *args):\n+    sys.stderr.write('ERROR: %s\\n' % (msg % args))\n+    sys.exit(1)\n+\n+def warn(msg, *args):\n+    sys.stderr.write('WARNING: %s\\n' % (msg % args))\n+\n+def gitmode(flags):\n+    return 'l' in flags and '120000' or 'x' in flags and '100755' or '100644'\n+\n+class Marks:\n+\n+    def __init__(self, path):\n+        self.path = path\n+        self.tips = {}\n+        self.marks = {}\n+        self.last_mark = 0\n+\n+        self.load()\n+\n+    def load(self):\n+        if not os.path.exists(self.path):\n+            return\n+\n+        tmp = json.load(open(self.path))\n+\n+        self.tips = tmp['tips']\n+        self.marks = tmp['marks']\n+        self.last_mark = tmp['last-mark']\n+\n+    def dict(self):\n+        return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }\n+\n+    def store(self):\n+        json.dump(self.dict(), open(self.path, 'w'))\n+\n+    def __str__(self):\n+        return str(self.dict())\n+\n+    def from_rev(self, rev):\n+        return self.marks[str(rev)]\n+\n+    def next_mark(self, rev):\n+        self.last_mark += 1\n+        self.marks[str(rev)] = self.last_mark\n+        return self.last_mark\n+\n+    def is_marked(self, rev):\n+        return self.marks.has_key(str(rev))\n+\n+    def get_tip(self, branch):\n+        return self.tips.get(branch, 0)\n+\n+    def set_tip(self, branch, tip):\n+        self.tips[branch] = tip\n+\n+class Parser:\n+\n+    def __init__(self, repo):\n+        self.repo = repo\n+        self.line = self.get_line()\n+\n+    def get_line(self):\n+        return sys.stdin.readline().strip()\n+\n+    def __getitem__(self, i):\n+        return self.line.split()[i]\n+\n+    def check(self, word):\n+        return self.line.startswith(word)\n+\n+    def each_block(self, separator):\n+        while self.line != separator:\n+            yield self.line\n+            self.line = self.get_line()\n+\n+    def __iter__(self):\n+        return self.each_block('')\n+\n+def export_file(fc):\n+    if fc.path() == '.hgtags':\n+        return\n+    d = fc.data()\n+    print \"M %s inline %s\" % (gitmode(fc.flags()), fc.path())\n+    print \"data %d\" % len(d)\n+    print d\n+\n+def get_filechanges(repo, ctx, parents):\n+    l = [repo.status(p, ctx)[:3] for p in parents]\n+    changed, added, removed = [sum(e, []) for e in zip(*l)]\n+    return added + changed, removed\n+\n+def fixup_user(user):\n+    user = user.replace('\"', '')\n+    m = AUTHOR_RE.match(user)\n+    if m:\n+        name = m.group(2)\n+        mail = m.group(3)\n+    else:\n+        name = user\n+        mail = None\n+\n+    if not name:\n+        name = 'Unknown'\n+    if not mail:\n+        mail = '<unknown>'\n+\n+    return '%s %s' % (name, mail)\n+\n+def get_repo(url, alias):\n+    global dirname\n+\n+    myui = ui.ui()\n+    myui.setconfig('ui', 'interactive', 'off')\n+\n+    if hg.islocal(url):\n+        repo = hg.repository(myui, url)\n+    else:\n+        local_path = os.path.join(dirname, 'clone')\n+        if not os.path.exists(local_path):\n+            srcpeer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True)\n+            repo = dstpeer.local()\n+        else:\n+            repo = hg.repository(myui, local_path)\n+            peer = hg.peer(myui, {}, url)\n+            repo.pull(peer, heads=None, force=True)\n+\n+    return repo\n+\n+def rev_to_mark(rev):\n+    global marks\n+    return marks.from_rev(rev)\n+\n+def export_tag(repo, tag):\n+    global prefix\n+    print \"reset %s/tags/%s\" % (prefix, tag)\n+    print \"from :%s\" % (rev_to_mark(repo[tag].rev()))\n+    print\n+\n+def export_branch(repo, branch):\n+    global prefix, marks, branches\n+\n+    heads = branches[branch]\n+\n+    # verify there's only one head\n+    if (len(heads) > 1):\n+        warn(\"Branch '%s' has more than one head, consider merging\" % branch)\n+        tip = repo.branchtip(branch)\n+        head = repo[tip]\n+    else:\n+        head = repo[heads[0]]\n+\n+    tip = marks.get_tip(branch)\n+    # mercurial takes too much time checking this\n+    if tip and tip == head.rev():\n+        # nothing to do\n+        return\n+    revs = repo.revs('%u:%u' % (tip, head))\n+    count = 0\n+\n+    revs = [rev for rev in revs if not marks.is_marked(rev)]\n+\n+    for rev in revs:\n+\n+        c = repo[rev]\n+        (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())\n+        rev_branch = extra['branch']\n+\n+        tz = '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)\n+\n+        print \"commit %s/branches/%s\" % (prefix, rev_branch)\n+        print \"mark :%d\" % (marks.next_mark(rev))\n+        print \"committer %s %d %s\" % (fixup_user(user), time, tz)\n+        print \"data %d\" % (len(desc) + 1)\n+        print desc\n+        print\n+\n+        parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]\n+\n+        if len(parents) == 0:\n+            modified = c.manifest().keys()\n+            removed = []\n+        else:\n+            added = []\n+            changed = []\n+            print \"from :%s\" % (rev_to_mark(parents[0]))\n+            if len(parents) > 1:\n+                print \"merge :%s\" % (rev_to_mark(parents[1]))\n+            modified, removed = get_filechanges(repo, c, parents)\n+\n+        for f in removed:\n+            print \"D %s\" % (f)\n+        for f in modified:\n+            export_file(c.filectx(f))\n+        print\n+\n+        count += 1\n+        if (count % 100 == 0):\n+            print \"progress revision %d '%s' (%d/%d)\" % (rev, branch, count, len(revs))\n+            print \"#############################################################\"\n+\n+    # make sure the ref is updated\n+    print \"reset %s/branches/%s\" % (prefix, branch)\n+    print \"from :%u\" % rev_to_mark(rev)\n+    print\n+\n+    marks.set_tip(branch, rev)\n+\n+def do_capabilities(parser):\n+    global prefix, dirname\n+\n+    print \"import\"\n+    print \"refspec refs/heads/*:%s/branches/*\" % prefix\n+    print \"refspec refs/tags/*:%s/tags/*\" % prefix\n+    print\n+\n+def do_list(parser):\n+    global branches\n+\n+    repo = parser.repo\n+    head = repo.dirstate.branch()\n+    for branch in repo.branchmap():\n+        heads = repo.branchheads(branch)\n+        if len(heads):\n+            branches[branch] = heads\n+\n+    for branch in branches:\n+        print \"? refs/heads/%s\" % branch\n+    for tag, node in repo.tagslist():\n+        if tag == 'tip':\n+            continue\n+        print \"? refs/tags/%s\" % tag\n+    print \"@refs/heads/%s HEAD\" % head\n+    print\n+\n+def do_import(parser):\n+    global first\n+\n+    repo = parser.repo\n+    ref = parser[1]\n+\n+    if first:\n+        path = os.path.join(dirname, 'marks-git')\n+\n+        if os.path.exists(path):\n+            print \"feature import-marks=%s\" % path\n+        print \"feature export-marks=%s\" % path\n+        sys.stdout.flush()\n+        first = False\n+\n+    if (ref == 'HEAD'):\n+        return\n+\n+    if ref.startswith('refs/heads/'):\n+        branch = ref[len('refs/heads/'):]\n+        export_branch(repo, branch)\n+    elif ref.startswith('refs/tags/'):\n+        tag = ref[len('refs/tags/'):]\n+        export_tag(repo, tag)\n+\n+def main(args):\n+    global prefix, dirname, marks, branches\n+\n+    alias = args[1]\n+    url = args[2]\n+\n+    gitdir = os.environ['GIT_DIR']\n+    dirname = os.path.join(gitdir, 'hg', alias)\n+    branches = {}\n+\n+    repo = get_repo(url, alias)\n+    prefix = 'refs/hg/%s' % alias\n+\n+    if not os.path.exists(dirname):\n+        os.makedirs(dirname)\n+\n+    marks_path = os.path.join(dirname, 'marks-hg')\n+    marks = Marks(marks_path)\n+\n+    parser = Parser(repo)\n+    for line in parser:\n+        if parser.check('capabilities'):\n+            do_capabilities(parser)\n+        elif parser.check('list'):\n+            do_list(parser)\n+        elif parser.check('import'):\n+            do_import(parser)\n+        elif parser.check('export'):\n+            do_export(parser)\n+        sys.stdout.flush()\n+\n+    marks.store()\n+\n+sys.exit(main(sys.argv))\n-- \n1.8.0.rc2.7.g0961fdf.dirty\n"},{"id":"201635","messageId":"1350841744-21564-3-git-send-email-felipe.contreras@gmail.com","threadId":"31884","inReplyTo":"1350841744-21564-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 2/6] remote-hg: add support for pushing","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-21T17:49:00Z","receivedAt":"2012-10-21T17:49:00Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Some parsing of fast-export parsing might be missing, but I couldn't find any.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-hg/git-remote-hg | 156 +++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 154 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg\nindex f0ce4a4..fc4510c 100755\n--- a/contrib/remote-hg/git-remote-hg\n+++ b/contrib/remote-hg/git-remote-hg\n@@ -6,7 +6,7 @@\n # Then you can clone with:\n # git clone hg::/path/to/mercurial/repo/\n \n-from mercurial import hg, ui\n+from mercurial import hg, ui, context\n \n import re\n import sys\n@@ -16,6 +16,7 @@ import json\n first = True\n \n AUTHOR_RE = re.compile('^((.+?) )?(<.+?>)$')\n+RAW_AUTHOR_RE = re.compile('^(\\w+) (.+) <(.+)> (\\d+) ([+-]\\d+)')\n \n def die(msg, *args):\n     sys.stderr.write('ERROR: %s\\n' % (msg % args))\n@@ -27,12 +28,17 @@ def warn(msg, *args):\n def gitmode(flags):\n     return 'l' in flags and '120000' or 'x' in flags and '100755' or '100644'\n \n+def hgmode(mode):\n+    m = { '0100755': 'x', '0120000': 'l' }\n+    return m.get(mode, '')\n+\n class Marks:\n \n     def __init__(self, path):\n         self.path = path\n         self.tips = {}\n         self.marks = {}\n+        self.rev_hgmarks = {}\n         self.last_mark = 0\n \n         self.load()\n@@ -47,6 +53,9 @@ class Marks:\n         self.marks = tmp['marks']\n         self.last_mark = tmp['last-mark']\n \n+        for rev, mark in self.marks.iteritems():\n+            self.rev_hgmarks[mark] = int(rev)\n+\n     def dict(self):\n         return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }\n \n@@ -59,11 +68,19 @@ class Marks:\n     def from_rev(self, rev):\n         return self.marks[str(rev)]\n \n+    def to_rev(self, mark):\n+        return self.rev_hgmarks[mark]\n+\n     def next_mark(self, rev):\n         self.last_mark += 1\n         self.marks[str(rev)] = self.last_mark\n         return self.last_mark\n \n+    def new_mark(self, rev, mark):\n+        self.marks[str(rev)] = mark\n+        self.rev_hgmarks[mark] = rev\n+        self.last_mark = mark\n+\n     def is_marked(self, rev):\n         return self.marks.has_key(str(rev))\n \n@@ -96,6 +113,28 @@ class Parser:\n     def __iter__(self):\n         return self.each_block('')\n \n+    def next(self):\n+        self.line = self.get_line()\n+        if self.line == 'done':\n+            self.line = None\n+\n+    def get_mark(self):\n+        i = self.line.index(':') + 1\n+        return int(self.line[i:])\n+\n+    def get_data(self):\n+        if not self.check('data'):\n+            return None\n+        i = self.line.index(' ') + 1\n+        size = int(self.line[i:])\n+        return sys.stdin.read(size)\n+\n+    def get_author(self):\n+        m = RAW_AUTHOR_RE.match(self.line)\n+        if not m:\n+            return None\n+        return list(m.groups())[1:]\n+\n def export_file(fc):\n     if fc.path() == '.hgtags':\n         return\n@@ -150,6 +189,10 @@ def rev_to_mark(rev):\n     global marks\n     return marks.from_rev(rev)\n \n+def mark_to_rev(mark):\n+    global marks\n+    return marks.to_rev(mark)\n+\n def export_tag(repo, tag):\n     global prefix\n     print \"reset %s/tags/%s\" % (prefix, tag)\n@@ -229,8 +272,16 @@ def do_capabilities(parser):\n     global prefix, dirname\n \n     print \"import\"\n+    print \"export\"\n     print \"refspec refs/heads/*:%s/branches/*\" % prefix\n     print \"refspec refs/tags/*:%s/tags/*\" % prefix\n+\n+    path = os.path.join(dirname, 'marks-git')\n+\n+    if os.path.exists(path):\n+        print \"*import-marks %s\" % path\n+    print \"*export-marks %s\" % path\n+\n     print\n \n def do_list(parser):\n@@ -277,8 +328,108 @@ def do_import(parser):\n         tag = ref[len('refs/tags/'):]\n         export_tag(repo, tag)\n \n+def parse_blob(parser):\n+    global blob_marks\n+\n+    parser.next()\n+    if parser.check('mark'):\n+        mark = parser.get_mark()\n+        parser.next()\n+    data = parser.get_data()\n+    blob_marks[mark] = data\n+    return\n+\n+def parse_commit(parser):\n+    global marks, blob_marks\n+\n+    from_mark = merge_mark = None\n+\n+    a = parser.line.split(' ')\n+    ref = a[1]\n+    if ref.startswith('refs/heads/'):\n+        branch = ref[len('refs/heads/'):]\n+    parser.next()\n+\n+    if parser.check('mark'):\n+        commit_mark = parser.get_mark()\n+        parser.next()\n+    if parser.check('author'):\n+        author = parser.get_author()\n+        parser.next()\n+    committer = parser.get_author()\n+    parser.next()\n+    data = parser.get_data()\n+    parser.next()\n+    if parser.check('from'):\n+        from_mark = parser.get_mark()\n+        parser.next()\n+    if parser.check('merge'):\n+        merge_mark = parser.get_mark()\n+        parser.next()\n+        if parser.check('merge'):\n+            die('octopus merges are not supported yet')\n+\n+    files = {}\n+\n+    for line in parser:\n+        if parser.check('M'):\n+            t, mode, mark_ref, path = line.split(' ')\n+            mark = int(mark_ref[1:])\n+            f = { 'mode' : hgmode(mode), 'data' : blob_marks[mark] }\n+        elif parser.check('D'):\n+            t, path = line.split(' ')\n+            f = { 'deleted' : True }\n+        else:\n+            die('Unknown file command: %s' % line)\n+        files[path] = f\n+\n+    def getfilectx(repo, memctx, f):\n+        of = files[f]\n+        if 'deleted' in of:\n+            raise IOError\n+        return context.memfilectx(f, of['data'], False, False, None)\n+\n+    repo = parser.repo\n+\n+    committer_name, committer_email, date, tz = committer\n+    date = int(date)\n+    tz = int(tz)\n+    tz = ((tz / 100) * 3600) + ((tz % 100) * 60)\n+    extra = {}\n+    extra['branch'] = branch\n+\n+    if from_mark:\n+        p1 = repo.changelog.node(mark_to_rev(from_mark))\n+    else:\n+        p1 = '\\0' * 20\n+\n+    if merge_mark:\n+        p2 = repo.changelog.node(mark_to_rev(merge_mark))\n+    else:\n+        p2 = '\\0' * 20\n+\n+    ctx = context.memctx(repo, (p1, p2), data,\n+            files.keys(), getfilectx,\n+            '%s <%s>' % (committer_name, committer_email), (date, -tz), extra)\n+\n+    node = repo.commitctx(ctx)\n+    rev = repo[node].rev()\n+\n+    marks.new_mark(rev, commit_mark)\n+\n+    print \"ok %s\" % ref\n+\n+def do_export(parser):\n+    for line in parser.each_block('done'):\n+        if parser.check('blob'):\n+            parse_blob(parser)\n+        elif parser.check('commit'):\n+            parse_commit(parser)\n+    print\n+\n def main(args):\n-    global prefix, dirname, marks, branches\n+    global prefix, dirname, branches\n+    global marks, blob_marks\n \n     alias = args[1]\n     url = args[2]\n@@ -286,6 +437,7 @@ def main(args):\n     gitdir = os.environ['GIT_DIR']\n     dirname = os.path.join(gitdir, 'hg', alias)\n     branches = {}\n+    blob_marks = {}\n \n     repo = get_repo(url, alias)\n     prefix = 'refs/hg/%s' % alias\n-- \n1.8.0.rc2.7.g0961fdf.dirty\n"},{"id":"201637","messageId":"1350841744-21564-4-git-send-email-felipe.contreras@gmail.com","threadId":"31884","inReplyTo":"1350841744-21564-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 3/6] remote-hg: add support for remote pushing","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-21T17:49:01Z","receivedAt":"2012-10-21T17:49:01Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"I'm not happy with this, but works.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-hg/git-remote-hg | 10 +++++++++-\n 1 file changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg\nindex fc4510c..34a00cd 100755\n--- a/contrib/remote-hg/git-remote-hg\n+++ b/contrib/remote-hg/git-remote-hg\n@@ -167,6 +167,7 @@ def fixup_user(user):\n \n def get_repo(url, alias):\n     global dirname\n+    global peer\n \n     myui = ui.ui()\n     myui.setconfig('ui', 'interactive', 'off')\n@@ -176,7 +177,7 @@ def get_repo(url, alias):\n     else:\n         local_path = os.path.join(dirname, 'clone')\n         if not os.path.exists(local_path):\n-            srcpeer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True)\n+            peer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True)\n             repo = dstpeer.local()\n         else:\n             repo = hg.repository(myui, local_path)\n@@ -420,6 +421,8 @@ def parse_commit(parser):\n     print \"ok %s\" % ref\n \n def do_export(parser):\n+    global peer\n+\n     for line in parser.each_block('done'):\n         if parser.check('blob'):\n             parse_blob(parser)\n@@ -427,12 +430,17 @@ def do_export(parser):\n             parse_commit(parser)\n     print\n \n+    if peer:\n+        parser.repo.push(peer, force=False)\n+\n def main(args):\n     global prefix, dirname, branches\n     global marks, blob_marks\n+    global peer\n \n     alias = args[1]\n     url = args[2]\n+    peer = None\n \n     gitdir = os.environ['GIT_DIR']\n     dirname = os.path.join(gitdir, 'hg', alias)\n-- \n1.8.0.rc2.7.g0961fdf.dirty\n"},{"id":"201638","messageId":"1350841744-21564-5-git-send-email-felipe.contreras@gmail.com","threadId":"31884","inReplyTo":"1350841744-21564-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 4/6] remote-hg: add support for trivial features","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-21T17:49:02Z","receivedAt":"2012-10-21T17:49:02Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"So that the other remote-hg tests don't barf.\n\nI'm not particularily happy with this code, but does the trick.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-hg/git-remote-hg | 20 ++++++++++++++++++--\n 1 file changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/remote-hg/git-remote-hg b/contrib/remote-hg/git-remote-hg\nindex 34a00cd..772bb69 100755\n--- a/contrib/remote-hg/git-remote-hg\n+++ b/contrib/remote-hg/git-remote-hg\n@@ -12,6 +12,7 @@ import re\n import sys\n import os\n import json\n+import shutil\n \n first = True\n \n@@ -172,7 +173,13 @@ def get_repo(url, alias):\n     myui = ui.ui()\n     myui.setconfig('ui', 'interactive', 'off')\n \n-    if hg.islocal(url):\n+    if url.startswith(\"remote://\"):\n+        remote = True\n+        url = \"file://%s\" % url[9:]\n+    else:\n+        remote = False\n+\n+    if hg.islocal(url) and not remote:\n         repo = hg.repository(myui, url)\n     else:\n         local_path = os.path.join(dirname, 'clone')\n@@ -442,6 +449,12 @@ def main(args):\n     url = args[2]\n     peer = None\n \n+    if not alias.isalnum():\n+        is_tmp = True\n+        alias = \"tmp\"\n+    else:\n+        is_tmp = False\n+\n     gitdir = os.environ['GIT_DIR']\n     dirname = os.path.join(gitdir, 'hg', alias)\n     branches = {}\n@@ -468,6 +481,9 @@ def main(args):\n             do_export(parser)\n         sys.stdout.flush()\n \n-    marks.store()\n+    if not is_tmp:\n+        marks.store()\n+    else:\n+        shutil.rmtree(dirname)\n \n sys.exit(main(sys.argv))\n-- \n1.8.0.rc2.7.g0961fdf.dirty\n"},{"id":"201639","messageId":"1350841744-21564-6-git-send-email-felipe.contreras@gmail.com","threadId":"31884","inReplyTo":"1350841744-21564-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 5/6] tests: add remote-hg tests","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-21T17:49:03Z","receivedAt":"2012-10-21T17:49:03Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":">From the original remote-hg.\n\nYou need git-remote-hg already in your path to run them.\n\nI'm not proposing to include this patch like this, but should make it easier to\ntest.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/t5801-remote-hg.sh | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 143 insertions(+)\n create mode 100755 t/t5801-remote-hg.sh\n\ndiff --git a/t/t5801-remote-hg.sh b/t/t5801-remote-hg.sh\nnew file mode 100755\nindex 0000000..2e68372\n--- /dev/null\n+++ b/t/t5801-remote-hg.sh\n@@ -0,0 +1,143 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Sverre Rabbelier\n+#\n+\n+test_description='Test remote-helper import and export commands'\n+\n+. ./test-lib.sh\n+\n+if test_have_prereq PYTHON && \"$PYTHON_PATH\" -c '\n+import sys\n+if sys.hexversion < 0x02040000:\n+    sys.exit(1)\n+'\n+then\n+\t:\n+else\n+\tskip_all='skipping git remote-hg tests: requires Python 2.4 or newer'\n+\ttest_done\n+fi\n+\n+if ! type hg >/dev/null 2>&1\n+then\n+\tskip_all='skipping git remote-hg tests: requires hg'\n+\ttest_done\n+fi\n+\n+# Call cmp with the arguments -x \".hg\" -x \".git\" <left> <right>\n+\n+vcs_cmp () {\n+\t$DIFF -u -x \".hg\" -x \".git\" $1 $2\n+}\n+\n+ROOT=$PWD\n+\n+test_expect_success 'setup repository' '\n+\tprintf \"[ui]\\nusername = A U Thor <author@example.com>\" > \\\n+\t\t${HOME}/.hgrc &&\n+\tmkdir server &&\n+\thg init server/.hg &&\n+\thg clone \"$ROOT/server\" public &&\n+\t(cd public &&\n+\t echo content >file &&\n+\t hg add file &&\n+\t hg commit -m one &&\n+\t hg push)\n+'\n+\n+test_expect_success 'cloning from local repo' '\n+\tgit clone \"hg::file://${ROOT}/server\" localclone &&\n+\tvcs_cmp public localclone\n+'\n+\n+test_expect_success 'cloning from remote repo' '\n+\tgit clone \"hg::remote://${ROOT}/server\" clone &&\n+\tvcs_cmp public clone\n+'\n+\n+test_expect_success 'create new commit on remote' '\n+\t(cd public &&\n+\t echo content >>file &&\n+\t hg commit -A -m two &&\n+\t hg push)\n+'\n+\n+test_expect_success 'pulling from local repo' '\n+\t(cd localclone && git pull) &&\n+\tvcs_cmp public localclone\n+'\n+\n+test_expect_success 'pulling from remote remote' '\n+\t(cd clone && git pull) &&\n+\tvcs_cmp public clone\n+'\n+\n+test_expect_success 'pushing to local empty repo' '\n+\thg init localempty &&\n+\t(cd localclone &&\n+\tgit push --all \"hg::file://${ROOT}/localempty\") &&\n+\t(cd localempty &&\n+\thg up tip) &&\n+\tvcs_cmp localclone localempty\n+'\n+\n+test_expect_success 'pushing to remote empty repo' '\n+\thg init empty &&\n+\t(cd localclone &&\n+\tgit push --all \"hg::remote://${ROOT}/empty\") &&\n+\t(cd empty &&\n+\thg up tip) &&\n+\tvcs_cmp localclone empty\n+'\n+\n+test_expect_success 'pushing to local repo' '\n+\t(cd localclone &&\n+\techo content >>file &&\n+\tgit commit -a -m three &&\n+\tgit push) &&\n+\t(cd server &&\n+\thg up tip) &&\n+\tvcs_cmp localclone server\n+'\n+\n+test_expect_success 'synch with changes from localclone' '\n+\t(cd clone &&\n+\t git pull)\n+'\n+\n+test_expect_success 'pushing remote local repo' '\n+\t(cd clone &&\n+\techo content >>file &&\n+\tgit commit -a -m four &&\n+\tgit push) &&\n+\t(cd server &&\n+\thg up tip) &&\n+\tvcs_cmp clone server\n+'\n+\n+test_expect_success 'creating new branch' '\n+\t(cd public &&\n+\thg branch different-branch &&\n+\techo different >> file &&\n+\thg commit -m five &&\n+\thg push -f)\n+'\n+\n+test_expect_success 'pull in new branch to local repository' '\n+\t(cd localclone &&\n+\tgit fetch origin default &&\n+\ttest_must_fail git rev-parse -q --verify refs/remotes/origin/different-branch &&\n+\tgit fetch &&\n+\tgit rev-parse --no-revs --verify refs/remotes/origin/different-branch)\n+'\n+\n+test_expect_success 'pull in new branch to remote repository' '\n+\t(cd clone &&\n+\tgit fetch origin default &&\n+\ttest_must_fail git rev-parse -q --verify refs/remotes/origin/different-branch &&\n+\tgit fetch &&\n+\tgit rev-parse --no-revs --verify refs/remotes/origin/different-branch)\n+'\n+\n+test_done\n-- \n1.8.0.rc2.7.g0961fdf.dirty\n"},{"id":"201640","messageId":"1350841744-21564-7-git-send-email-felipe.contreras@gmail.com","threadId":"31884","inReplyTo":"1350841744-21564-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 6/6] tests: fix remote-hg warnings for modern git","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-21T17:49:04Z","receivedAt":"2012-10-21T17:49:04Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/t5801-remote-hg.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t5801-remote-hg.sh b/t/t5801-remote-hg.sh\nindex 2e68372..ba4d7a2 100755\n--- a/t/t5801-remote-hg.sh\n+++ b/t/t5801-remote-hg.sh\n@@ -95,7 +95,7 @@ test_expect_success 'pushing to local repo' '\n \t(cd localclone &&\n \techo content >>file &&\n \tgit commit -a -m three &&\n-\tgit push) &&\n+\tgit push --all) &&\n \t(cd server &&\n \thg up tip) &&\n \tvcs_cmp localclone server\n@@ -110,7 +110,7 @@ test_expect_success 'pushing remote local repo' '\n \t(cd clone &&\n \techo content >>file &&\n \tgit commit -a -m four &&\n-\tgit push) &&\n+\tgit push --all) &&\n \t(cd server &&\n \thg up tip) &&\n \tvcs_cmp clone server\n-- \n1.8.0.rc2.7.g0961fdf.dirty\n"},{"id":"201655","messageId":"CAGdFq_hhjvysViU+rceOcX7L48BkxUbDzKiyT7LakFqz1ikT8A@mail.gmail.com","threadId":"31884","inReplyTo":"1350841744-21564-6-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v3 5/6] tests: add remote-hg tests","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2012-10-21T21:02:02Z","receivedAt":"2012-10-21T21:02:02Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"On Sun, Oct 21, 2012 at 10:49 AM, Felipe Contreras\n<felipe.contreras@gmail.com> wrote:\n> From the original remote-hg.\n>\n> You need git-remote-hg already in your path to run them.\n>\n> I'm not proposing to include this patch like this, but should make it easier to\n> test.\n\nYou should also have a look at the tests that were marked as \"expected\nto fail\", since they point out a bug with fast-export. I'd sent a\nseries to fix that, but didn't follow-up to get it merged:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/184874\n\nIt'd be great if you want to pick this up (like you did with the tests).\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"201811","messageId":"CAMP44s0OLE8wqoGOJ+cw1uuYjdema6UuXJU5-wAuWdJuxi2Mtg@mail.gmail.com","threadId":"31884","inReplyTo":"CAGdFq_hhjvysViU+rceOcX7L48BkxUbDzKiyT7LakFqz1ikT8A@mail.gmail.com","subject":"Re: [PATCH v3 5/6] tests: add remote-hg tests","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-10-24T15:47:40Z","receivedAt":"2012-10-24T15:47:40Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sun, Oct 21, 2012 at 11:02 PM, Sverre Rabbelier <srabbelier@gmail.com> wrote:\n> On Sun, Oct 21, 2012 at 10:49 AM, Felipe Contreras\n> <felipe.contreras@gmail.com> wrote:\n>> From the original remote-hg.\n>>\n>> You need git-remote-hg already in your path to run them.\n>>\n>> I'm not proposing to include this patch like this, but should make it easier to\n>> test.\n>\n> You should also have a look at the tests that were marked as \"expected\n> to fail\", since they point out a bug with fast-export.\n\nWhat tests? All the tests I see in msysgit are expected to succeed:\nhttps://github.com/msysgit/git/blob/devel/t/t5801-remote-hg.sh\n\n> I'd sent a\n> series to fix that, but didn't follow-up to get it merged:\n>\n> http://thread.gmane.org/gmane.comp.version-control.git/184874\n\nI have read that thread multiple times now, and I still don't see the\nproblem. Everything works fine in my remote-hg. I still don't\nunderstand what changes are required in upstream that your remote-hg\nneeds, and the fact that there is no up-to-date remote-hg branch\ndoesn't help.\n\nCheers.\n\n-- \nFelipe Contreras\n"}]}