{"thread":{"id":"32005","subject":"[PATCH v6 03/16] remote-hg: add support for remote pushing","startedAt":"2012-11-04T02:13:22Z","lastAt":"2013-01-08T18:10:15Z","messageCount":22,"participants":["Felipe Contreras","W. Trevor King","Junio C Hamano"],"isPatch":true,"patchVersion":6,"patchTotal":16},"messages":[{"id":"202476","messageId":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":null,"subject":"[PATCH v6 00/16] New remote-hg helper:w","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:22Z","receivedAt":"2012-11-04T02:13:22Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Hi,\n\nOnly a few updates, and has been moved to contrib/remote-helpers\n(git-remote-bzr is on the way).\n\nThis remote-hg has advantages other tools don't have:\n\n * Uses transport-helper (git clone hg::path)\n * The code is small\n * The code is simple\n * No external dependencies (other than mercurial)\n * It's easy to install (put into your path)\n * Has extensive tests (for real)\n * Active development\n * Has compatibility with hg-git\n * The required patches are available\n * No changes necesary to git core\n * Support for bookmarks\n * Support for tags\n\nOne important alternative is the one written by Sverre Rabbelier that is\nnow maintained and distributed in msysgit. A list of issues with that\napproach (not exhaustive):\n\n * Doesn't work on newer versions of mercurial\n * There are multiple versions, each with different issues\n * Don't pass a single of this remote-hg's tests\n\nTo use it add it to your $PATH (e.g. ~/bin).\n\n % git clone hd:///full/path/or/url/to/hg/repo\n\nTo run the tests:\n\n % make -C contrib/remote-helpers test\n\nThe only caveat is that you need 'python' in your $PATH.\n\nChanges since v5:\n\n * Move to contrib/remote-helpers\n * Reorganize tests\n * Fix update of bookmarks\n * Fix for older versions of python\n * Performance improvements\n * Improve default branch/bookmark handling\n * Add fixes test-lib\n * Cleanups\n\nChanges since v4:\n\nFelipe Contreras (16):\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 to push URLs\n  remote-hg: make sure the encoding is correct\n  remote-hg: match hg merge behavior\n  remote-hg: add support for hg-git compat mode\n  remote-hg: add compat for hg-git author fixes\n  remote-hg: fake bookmark when there's none\n  remote-hg: add basic tests\n  test-lib: avoid full path to store test results\n  remote-hg: add bidirectional tests\n  remote-hg: add tests to compare with hg-git\n  remote-hg: add extra author test\n  remote-hg: add option to not track branches\n  remote-hg: the author email can be null\n\n contrib/remote-helpers/Makefile          |  13 +\n contrib/remote-helpers/git-remote-hg     | 785 +++++++++++++++++++++++++++++++\n contrib/remote-helpers/test-hg-bidi.sh   | 243 ++++++++++\n contrib/remote-helpers/test-hg-hg-git.sh | 466 ++++++++++++++++++\n contrib/remote-helpers/test-hg.sh        | 112 +++++\n t/test-lib.sh                            |   3 +-\n 6 files changed, 1621 insertions(+), 1 deletion(-)\n create mode 100644 contrib/remote-helpers/Makefile\n create mode 100755 contrib/remote-helpers/git-remote-hg\n create mode 100755 contrib/remote-helpers/test-hg-bidi.sh\n create mode 100755 contrib/remote-helpers/test-hg-hg-git.sh\n create mode 100755 contrib/remote-helpers/test-hg.sh\n\n-- \n1.8.0\n"},{"id":"202479","messageId":"1351995218-19889-2-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 01/16] Add new remote-hg transport helper","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:23Z","receivedAt":"2012-11-04T02:13:23Z","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-helpers/git-remote-hg | 391 +++++++++++++++++++++++++++++++++++\n 1 file changed, 391 insertions(+)\n create mode 100755 contrib/remote-helpers/git-remote-hg\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nnew file mode 100755\nindex 0000000..e37e278\n--- /dev/null\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -0,0 +1,391 @@\n+#!/usr/bin/env python\n+#\n+# Copyright (c) 2012 Felipe Contreras\n+#\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, bookmarks\n+\n+import re\n+import sys\n+import os\n+import json\n+\n+NAME_RE = re.compile('^([^<>]+)')\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+def gittz(tz):\n+    return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)\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 get_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 next(self):\n+        self.line = self.get_line()\n+        if self.line == 'done':\n+            self.line = None\n+\n+def export_file(fc):\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, parent):\n+    modified = set()\n+    added = set()\n+    removed = set()\n+\n+    cur = ctx.manifest()\n+    prev = repo[parent].manifest().copy()\n+\n+    for fn in cur:\n+        if fn in prev:\n+            if (cur.flags(fn) != prev.flags(fn) or cur[fn] != prev[fn]):\n+                modified.add(fn)\n+            del prev[fn]\n+        else:\n+            added.add(fn)\n+    removed |= set(prev.keys())\n+\n+    return added | modified, removed\n+\n+def fixup_user(user):\n+    user = user.replace('\"', '')\n+    name = mail = None\n+    m = AUTHOR_RE.match(user)\n+    if m:\n+        name = m.group(1)\n+        mail = m.group(2).strip()\n+    else:\n+        m = NAME_RE.match(user)\n+        if m:\n+            name = m.group(1).strip()\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+            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+            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_ref(repo, name, kind, head):\n+    global prefix, marks\n+\n+    ename = '%s/%s' % (kind, name)\n+    tip = marks.get_tip(ename)\n+\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+        author = \"%s %d %s\" % (fixup_user(user), time, gittz(tz))\n+        if 'committer' in extra:\n+            user, time, tz = extra['committer'].rsplit(' ', 2)\n+            committer = \"%s %s %s\" % (user, time, gittz(int(tz)))\n+        else:\n+            committer = author\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+            modified, removed = get_filechanges(repo, c, parents[0])\n+\n+        if len(parents) == 0 and rev:\n+            print 'reset %s/%s' % (prefix, ename)\n+\n+        print \"commit %s/%s\" % (prefix, ename)\n+        print \"mark :%d\" % (marks.get_mark(rev))\n+        print \"author %s\" % (author)\n+        print \"committer %s\" % (committer)\n+        print \"data %d\" % (len(desc))\n+        print desc\n+\n+        if len(parents) > 0:\n+            print \"from :%s\" % (rev_to_mark(parents[0]))\n+            if len(parents) > 1:\n+                print \"merge :%s\" % (rev_to_mark(parents[1]))\n+\n+        for f in modified:\n+            export_file(c.filectx(f))\n+        for f in removed:\n+            print \"D %s\" % (f)\n+        print\n+\n+        count += 1\n+        if (count % 100 == 0):\n+            print \"progress revision %d '%s' (%d/%d)\" % (rev, name, count, len(revs))\n+            print \"#############################################################\"\n+\n+    # make sure the ref is updated\n+    print \"reset %s/%s\" % (prefix, ename)\n+    print \"from :%u\" % rev_to_mark(rev)\n+    print\n+\n+    marks.set_tip(ename, rev)\n+\n+def export_tag(repo, tag):\n+    export_ref(repo, tag, 'tags', repo[tag])\n+\n+def export_bookmark(repo, bmark):\n+    head = bmarks[bmark]\n+    export_ref(repo, bmark, 'bookmarks', head)\n+\n+def export_branch(repo, branch):\n+    tip = get_branch_tip(repo, branch)\n+    head = repo[tip]\n+    export_ref(repo, branch, 'branches', head)\n+\n+def export_head(repo):\n+    global g_head\n+    export_ref(repo, g_head[0], 'bookmarks', g_head[1])\n+\n+def do_capabilities(parser):\n+    global prefix, dirname\n+\n+    print \"import\"\n+    print \"refspec refs/heads/branches/*:%s/branches/*\" % prefix\n+    print \"refspec refs/heads/*:%s/bookmarks/*\" % prefix\n+    print \"refspec refs/tags/*:%s/tags/*\" % prefix\n+    print\n+\n+def get_branch_tip(repo, branch):\n+    global branches\n+\n+    heads = branches.get(branch, None)\n+    if not heads:\n+        return None\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+        # older versions of mercurial don't have this\n+        if hasattr(repo, \"branchtip\"):\n+            return repo.branchtip(branch)\n+\n+    return heads[0]\n+\n+def list_head(repo, cur):\n+    global g_head\n+\n+    head = bookmarks.readcurrent(repo)\n+    if not head:\n+        return\n+    node = repo[head]\n+    print \"@refs/heads/%s HEAD\" % head\n+    g_head = (head, node)\n+\n+def do_list(parser):\n+    global branches, bmarks\n+\n+    repo = parser.repo\n+    for branch in repo.branchmap():\n+        heads = repo.branchheads(branch)\n+        if len(heads):\n+            branches[branch] = heads\n+\n+    for bmark, node in bookmarks.listbookmarks(repo).iteritems():\n+        bmarks[bmark] = repo[node]\n+\n+    cur = repo.dirstate.branch()\n+\n+    list_head(repo, cur)\n+    for branch in branches:\n+        print \"? refs/heads/branches/%s\" % branch\n+    for bmark in bmarks:\n+        print \"? refs/heads/%s\" % bmark\n+\n+    for tag, node in repo.tagslist():\n+        if tag == 'tip':\n+            continue\n+        print \"? refs/tags/%s\" % tag\n+\n+    print\n+\n+def do_import(parser):\n+    repo = parser.repo\n+\n+    path = os.path.join(dirname, 'marks-git')\n+\n+    print \"feature done\"\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+\n+    # lets get all the import lines\n+    while parser.check('import'):\n+        ref = parser[1]\n+\n+        if (ref == 'HEAD'):\n+            export_head(repo)\n+        elif ref.startswith('refs/heads/branches/'):\n+            branch = ref[len('refs/heads/branches/'):]\n+            export_branch(repo, branch)\n+        elif ref.startswith('refs/heads/'):\n+            bmark = ref[len('refs/heads/'):]\n+            export_bookmark(repo, bmark)\n+        elif ref.startswith('refs/tags/'):\n+            tag = ref[len('refs/tags/'):]\n+            export_tag(repo, tag)\n+\n+        parser.next()\n+\n+    print 'done'\n+\n+def main(args):\n+    global prefix, dirname, marks, branches, bmarks\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+    bmarks = {}\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+        else:\n+            die('unhandled command: %s' % line)\n+        sys.stdout.flush()\n+\n+    marks.store()\n+\n+sys.exit(main(sys.argv))\n-- \n1.8.0\n"},{"id":"202475","messageId":"1351995218-19889-3-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 02/16] remote-hg: add support for pushing","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:24Z","receivedAt":"2012-11-04T02:13:24Z","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-helpers/git-remote-hg | 217 ++++++++++++++++++++++++++++++++++-\n 1 file changed, 215 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex e37e278..fcceede 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -9,7 +9,7 @@\n # Then you can clone with:\n # git clone hg::/path/to/mercurial/repo/\n \n-from mercurial import hg, ui, bookmarks\n+from mercurial import hg, ui, bookmarks, context\n \n import re\n import sys\n@@ -18,6 +18,7 @@ import json\n \n NAME_RE = re.compile('^([^<>]+)')\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@@ -32,12 +33,17 @@ def gitmode(flags):\n def gittz(tz):\n     return '%+03d%02d' % (-tz / 3600, -tz % 3600 / 60)\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_marks = {}\n         self.last_mark = 0\n \n         self.load()\n@@ -52,6 +58,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_marks[mark] = int(rev)\n+\n     def dict(self):\n         return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }\n \n@@ -64,11 +73,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_marks[mark]\n+\n     def get_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_marks[mark] = rev\n+        self.last_mark = mark\n+\n     def is_marked(self, rev):\n         return self.marks.has_key(str(rev))\n \n@@ -106,6 +123,35 @@ class Parser:\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+        _, name, email, date, tz = m.groups()\n+\n+        if email != 'unknown':\n+            if name:\n+                user = '%s <%s>' % (name, email)\n+            else:\n+                user = '<%s>' % (email)\n+        else:\n+            user = name\n+\n+        tz = int(tz)\n+        tz = ((tz / 100) * 3600) + ((tz % 100) * 60)\n+        return (user, int(date), -tz)\n+\n def export_file(fc):\n     d = fc.data()\n     print \"M %s inline %s\" % (gitmode(fc.flags()), fc.path())\n@@ -174,6 +220,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_ref(repo, name, kind, head):\n     global prefix, marks\n \n@@ -263,9 +313,17 @@ def do_capabilities(parser):\n     global prefix, dirname\n \n     print \"import\"\n+    print \"export\"\n     print \"refspec refs/heads/branches/*:%s/branches/*\" % prefix\n     print \"refspec refs/heads/*:%s/bookmarks/*\" % 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 get_branch_tip(repo, branch):\n@@ -352,8 +410,161 @@ def do_import(parser):\n \n     print 'done'\n \n+def parse_blob(parser):\n+    global blob_marks\n+\n+    parser.next()\n+    mark = parser.get_mark()\n+    parser.next()\n+    data = parser.get_data()\n+    blob_marks[mark] = data\n+    parser.next()\n+    return\n+\n+def parse_commit(parser):\n+    global marks, blob_marks, bmarks, parsed_refs\n+\n+    from_mark = merge_mark = None\n+\n+    ref = parser[1]\n+    parser.next()\n+\n+    commit_mark = parser.get_mark()\n+    parser.next()\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, m, mark_ref, path = line.split(' ')\n+            mark = int(mark_ref[1:])\n+            f = { 'mode' : hgmode(m), '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+        is_exec = of['mode'] == 'x'\n+        is_link = of['mode'] == 'l'\n+        return context.memfilectx(f, of['data'], is_link, is_exec, None)\n+\n+    repo = parser.repo\n+\n+    user, date, tz = author\n+    extra = {}\n+\n+    if committer != author:\n+        extra['committer'] = \"%s %u %u\" % committer\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+            user, (date, tz), extra)\n+\n+    node = repo.commitctx(ctx)\n+\n+    rev = repo[node].rev()\n+\n+    parsed_refs[ref] = node\n+\n+    marks.new_mark(rev, commit_mark)\n+\n+def parse_reset(parser):\n+    ref = parser[1]\n+    parser.next()\n+    # ugh\n+    if parser.check('commit'):\n+        parse_commit(parser)\n+        return\n+    if not parser.check('from'):\n+        return\n+    from_mark = parser.get_mark()\n+    parser.next()\n+\n+    node = parser.repo.changelog.node(mark_to_rev(from_mark))\n+    parsed_refs[ref] = node\n+\n+def parse_tag(parser):\n+    name = parser[1]\n+    parser.next()\n+    from_mark = parser.get_mark()\n+    parser.next()\n+    tagger = parser.get_author()\n+    parser.next()\n+    data = parser.get_data()\n+    parser.next()\n+\n+    # nothing to do\n+\n+def do_export(parser):\n+    global parsed_refs, bmarks\n+\n+    parser.next()\n+\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+        elif parser.check('reset'):\n+            parse_reset(parser)\n+        elif parser.check('tag'):\n+            parse_tag(parser)\n+        elif parser.check('feature'):\n+            pass\n+        else:\n+            die('unhandled export command: %s' % line)\n+\n+    for ref, node in parsed_refs.iteritems():\n+        if ref.startswith('refs/heads/branches'):\n+            pass\n+        elif ref.startswith('refs/heads/'):\n+            bmark = ref[len('refs/heads/'):]\n+            if bmark in bmarks:\n+                old = bmarks[bmark].hex()\n+            else:\n+                old = ''\n+            if not bookmarks.pushbookmark(parser.repo, bmark, old, node):\n+                continue\n+        elif ref.startswith('refs/tags/'):\n+            tag = ref[len('refs/tags/'):]\n+            parser.repo.tag([tag], node, None, True, None, {})\n+        print \"ok %s\" % ref\n+\n+    print\n+\n def main(args):\n-    global prefix, dirname, marks, branches, bmarks\n+    global prefix, dirname, branches, bmarks\n+    global marks, blob_marks, parsed_refs\n \n     alias = args[1]\n     url = args[2]\n@@ -362,6 +573,8 @@ def main(args):\n     dirname = os.path.join(gitdir, 'hg', alias)\n     branches = {}\n     bmarks = {}\n+    blob_marks = {}\n+    parsed_refs = {}\n \n     repo = get_repo(url, alias)\n     prefix = 'refs/hg/%s' % alias\n-- \n1.8.0\n"},{"id":"202474","messageId":"1351995218-19889-4-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 03/16] remote-hg: add support for remote pushing","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:25Z","receivedAt":"2012-11-04T02:13:25Z","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-helpers/git-remote-hg | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex fcceede..45629e0 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -197,7 +197,7 @@ def fixup_user(user):\n     return '%s <%s>' % (name, mail)\n \n def get_repo(url, alias):\n-    global dirname\n+    global dirname, peer\n \n     myui = ui.ui()\n     myui.setconfig('ui', 'interactive', 'off')\n@@ -526,7 +526,7 @@ def parse_tag(parser):\n     # nothing to do\n \n def do_export(parser):\n-    global parsed_refs, bmarks\n+    global parsed_refs, bmarks, peer\n \n     parser.next()\n \n@@ -562,12 +562,17 @@ def do_export(parser):\n \n     print\n \n+    if peer:\n+        parser.repo.push(peer, force=False)\n+\n def main(args):\n     global prefix, dirname, branches, bmarks\n     global marks, blob_marks, parsed_refs\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\n"},{"id":"202477","messageId":"1351995218-19889-5-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 04/16] remote-hg: add support to push URLs","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:26Z","receivedAt":"2012-11-04T02:13:26Z","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-helpers/git-remote-hg | 14 ++++++++++++--\n 1 file changed, 12 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex 45629e0..a5023c9 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -9,12 +9,13 @@\n # Then you can clone with:\n # git clone hg::/path/to/mercurial/repo/\n \n-from mercurial import hg, ui, bookmarks, context\n+from mercurial import hg, ui, bookmarks, context, util\n \n import re\n import sys\n import os\n import json\n+import shutil\n \n NAME_RE = re.compile('^([^<>]+)')\n AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')\n@@ -574,6 +575,12 @@ def main(args):\n     url = args[2]\n     peer = None\n \n+    if alias[4:] == url:\n+        is_tmp = True\n+        alias = util.sha1(alias).hexdigest()\n+    else:\n+        is_tmp = False\n+\n     gitdir = os.environ['GIT_DIR']\n     dirname = os.path.join(gitdir, 'hg', alias)\n     branches = {}\n@@ -604,6 +611,9 @@ def main(args):\n             die('unhandled command: %s' % line)\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\n"},{"id":"202478","messageId":"1351995218-19889-6-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 05/16] remote-hg: make sure the encoding is correct","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:27Z","receivedAt":"2012-11-04T02:13:27Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Independently of the environment.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-helpers/git-remote-hg | 12 +++++++++++-\n 1 file changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex a5023c9..503a9fc 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -9,7 +9,7 @@\n # Then you can clone with:\n # git clone hg::/path/to/mercurial/repo/\n \n-from mercurial import hg, ui, bookmarks, context, util\n+from mercurial import hg, ui, bookmarks, context, util, encoding\n \n import re\n import sys\n@@ -391,6 +391,9 @@ def do_import(parser):\n     print \"feature export-marks=%s\" % path\n     sys.stdout.flush()\n \n+    tmp = encoding.encoding\n+    encoding.encoding = 'utf-8'\n+\n     # lets get all the import lines\n     while parser.check('import'):\n         ref = parser[1]\n@@ -409,6 +412,8 @@ def do_import(parser):\n \n         parser.next()\n \n+    encoding.encoding = tmp\n+\n     print 'done'\n \n def parse_blob(parser):\n@@ -491,8 +496,13 @@ def parse_commit(parser):\n             files.keys(), getfilectx,\n             user, (date, tz), extra)\n \n+    tmp = encoding.encoding\n+    encoding.encoding = 'utf-8'\n+\n     node = repo.commitctx(ctx)\n \n+    encoding.encoding = tmp\n+\n     rev = repo[node].rev()\n \n     parsed_refs[ref] = node\n-- \n1.8.0\n"},{"id":"202483","messageId":"1351995218-19889-7-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 06/16] remote-hg: match hg merge behavior","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:28Z","receivedAt":"2012-11-04T02:13:28Z","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-helpers/git-remote-hg | 17 +++++++++++++++++\n 1 file changed, 17 insertions(+)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex 503a9fc..247b7cb 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -427,6 +427,14 @@ def parse_blob(parser):\n     parser.next()\n     return\n \n+def get_merge_files(repo, p1, p2, files):\n+    for e in repo[p1].files():\n+        if e not in files:\n+            if e not in repo[p1].manifest():\n+                continue\n+            f = { 'ctx' : repo[p1][e] }\n+            files[e] = f\n+\n def parse_commit(parser):\n     global marks, blob_marks, bmarks, parsed_refs\n \n@@ -470,6 +478,8 @@ def parse_commit(parser):\n         of = files[f]\n         if 'deleted' in of:\n             raise IOError\n+        if 'ctx' in of:\n+            return of['ctx']\n         is_exec = of['mode'] == 'x'\n         is_link = of['mode'] == 'l'\n         return context.memfilectx(f, of['data'], is_link, is_exec, None)\n@@ -492,6 +502,13 @@ def parse_commit(parser):\n     else:\n         p2 = '\\0' * 20\n \n+    #\n+    # If files changed from any of the parents, hg wants to know, but in git if\n+    # nothing changed from the first parent, nothing changed.\n+    #\n+    if merge_mark:\n+        get_merge_files(repo, p1, p2, files)\n+\n     ctx = context.memctx(repo, (p1, p2), data,\n             files.keys(), getfilectx,\n             user, (date, tz), extra)\n-- \n1.8.0\n"},{"id":"202480","messageId":"1351995218-19889-8-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 07/16] remote-hg: add support for hg-git compat mode","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:29Z","receivedAt":"2012-11-04T02:13:29Z","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-helpers/git-remote-hg | 89 +++++++++++++++++++++++++++++++++---\n 1 file changed, 83 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex 247b7cb..d585756 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -16,6 +16,22 @@ import sys\n import os\n import json\n import shutil\n+import subprocess\n+\n+#\n+# If you want to switch to hg-git compatibility mode:\n+# git config --global remote-hg.hg-git-compat true\n+#\n+# git:\n+# Sensible defaults for git.\n+# hg bookmarks are exported as git branches, hg branches are prefixed\n+# with 'branches/'.\n+#\n+# hg:\n+# Emulate hg-git.\n+# Only hg bookmarks are exported as git branches.\n+# Commits are modified to preserve hg information and allow biridectionality.\n+#\n \n NAME_RE = re.compile('^([^<>]+)')\n AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')\n@@ -226,7 +242,7 @@ def mark_to_rev(mark):\n     return marks.to_rev(mark)\n \n def export_ref(repo, name, kind, head):\n-    global prefix, marks\n+    global prefix, marks, mode\n \n     ename = '%s/%s' % (kind, name)\n     tip = marks.get_tip(ename)\n@@ -261,6 +277,33 @@ def export_ref(repo, name, kind, head):\n         else:\n             modified, removed = get_filechanges(repo, c, parents[0])\n \n+        if mode == 'hg':\n+            extra_msg = ''\n+\n+            if rev_branch != 'default':\n+                extra_msg += 'branch : %s\\n' % rev_branch\n+\n+            renames = []\n+            for f in c.files():\n+                if f not in c.manifest():\n+                    continue\n+                rename = c.filectx(f).renamed()\n+                if rename:\n+                    renames.append((rename[0], f))\n+\n+            for e in renames:\n+                extra_msg += \"rename : %s => %s\\n\" % e\n+\n+            for key, value in extra.iteritems():\n+                if key in ('author', 'committer', 'encoding', 'message', 'branch', 'hg-git'):\n+                    continue\n+                else:\n+                    extra_msg += \"extra : %s : %s\\n\" % (key, urllib.quote(value))\n+\n+            desc += '\\n'\n+            if extra_msg:\n+                desc += '\\n--HG--\\n' + extra_msg\n+\n         if len(parents) == 0 and rev:\n             print 'reset %s/%s' % (prefix, ename)\n \n@@ -354,7 +397,7 @@ def list_head(repo, cur):\n     g_head = (head, node)\n \n def do_list(parser):\n-    global branches, bmarks\n+    global branches, bmarks, mode\n \n     repo = parser.repo\n     for branch in repo.branchmap():\n@@ -368,8 +411,11 @@ def do_list(parser):\n     cur = repo.dirstate.branch()\n \n     list_head(repo, cur)\n-    for branch in branches:\n-        print \"? refs/heads/branches/%s\" % branch\n+\n+    if mode != 'hg':\n+        for branch in branches:\n+            print \"? refs/heads/branches/%s\" % branch\n+\n     for bmark in bmarks:\n         print \"? refs/heads/%s\" % bmark\n \n@@ -437,6 +483,7 @@ def get_merge_files(repo, p1, p2, files):\n \n def parse_commit(parser):\n     global marks, blob_marks, bmarks, parsed_refs\n+    global mode\n \n     from_mark = merge_mark = None\n \n@@ -482,7 +529,9 @@ def parse_commit(parser):\n             return of['ctx']\n         is_exec = of['mode'] == 'x'\n         is_link = of['mode'] == 'l'\n-        return context.memfilectx(f, of['data'], is_link, is_exec, None)\n+        rename = of.get('rename', None)\n+        return context.memfilectx(f, of['data'],\n+                is_link, is_exec, rename)\n \n     repo = parser.repo\n \n@@ -509,6 +558,21 @@ def parse_commit(parser):\n     if merge_mark:\n         get_merge_files(repo, p1, p2, files)\n \n+    if mode == 'hg':\n+        i = data.find('\\n--HG--\\n')\n+        if i >= 0:\n+            tmp = data[i + len('\\n--HG--\\n'):].strip()\n+            for k, v in [e.split(' : ') for e in tmp.split('\\n')]:\n+                if k == 'rename':\n+                    old, new = v.split(' => ', 1)\n+                    files[new]['rename'] = old\n+                elif k == 'branch':\n+                    extra[k] = v\n+                elif k == 'extra':\n+                    ek, ev = v.split(' : ', 1)\n+                    extra[ek] = urllib.unquote(ev)\n+            data = data[:i]\n+\n     ctx = context.memctx(repo, (p1, p2), data,\n             files.keys(), getfilectx,\n             user, (date, tz), extra)\n@@ -596,12 +660,25 @@ def do_export(parser):\n def main(args):\n     global prefix, dirname, branches, bmarks\n     global marks, blob_marks, parsed_refs\n-    global peer\n+    global peer, mode\n \n     alias = args[1]\n     url = args[2]\n     peer = None\n \n+    cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat']\n+    hg_git_compat = False\n+    try:\n+        if subprocess.check_output(cmd) == 'true\\n':\n+            hg_git_compat = True\n+    except subprocess.CalledProcessError:\n+        pass\n+\n+    if hg_git_compat:\n+        mode = 'hg'\n+    else:\n+        mode = 'git'\n+\n     if alias[4:] == url:\n         is_tmp = True\n         alias = util.sha1(alias).hexdigest()\n-- \n1.8.0\n"},{"id":"202481","messageId":"1351995218-19889-9-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 08/16] remote-hg: add compat for hg-git author fixes","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:30Z","receivedAt":"2012-11-04T02:13:30Z","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-helpers/git-remote-hg | 59 ++++++++++++++++++++++++++++++++----\n 1 file changed, 53 insertions(+), 6 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex d585756..9db4b7e 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -17,6 +17,7 @@ import os\n import json\n import shutil\n import subprocess\n+import urllib\n \n #\n # If you want to switch to hg-git compatibility mode:\n@@ -35,6 +36,7 @@ import subprocess\n \n NAME_RE = re.compile('^([^<>]+)')\n AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')\n+AUTHOR_HG_RE = re.compile('^(.*?) ?<(.+?)(?:>(.+)?)?$')\n RAW_AUTHOR_RE = re.compile('^(\\w+) (?:(.+)? )?<(.+)> (\\d+) ([+-]\\d+)')\n \n def die(msg, *args):\n@@ -152,12 +154,20 @@ class Parser:\n         return sys.stdin.read(size)\n \n     def get_author(self):\n+        global bad_mail\n+\n+        ex = None\n         m = RAW_AUTHOR_RE.match(self.line)\n         if not m:\n             return None\n         _, name, email, date, tz = m.groups()\n+        if name and 'ext:' in name:\n+            m = re.match('^(.+?) ext:\\((.+)\\)$', name)\n+            if m:\n+                name = m.group(1)\n+                ex = urllib.unquote(m.group(2))\n \n-        if email != 'unknown':\n+        if email != bad_mail:\n             if name:\n                 user = '%s <%s>' % (name, email)\n             else:\n@@ -165,6 +175,9 @@ class Parser:\n         else:\n             user = name\n \n+        if ex:\n+            user += ex\n+\n         tz = int(tz)\n         tz = ((tz / 100) * 3600) + ((tz % 100) * 60)\n         return (user, int(date), -tz)\n@@ -194,9 +207,9 @@ def get_filechanges(repo, ctx, parent):\n \n     return added | modified, removed\n \n-def fixup_user(user):\n-    user = user.replace('\"', '')\n+def fixup_user_git(user):\n     name = mail = None\n+    user = user.replace('\"', '')\n     m = AUTHOR_RE.match(user)\n     if m:\n         name = m.group(1)\n@@ -205,11 +218,41 @@ def fixup_user(user):\n         m = NAME_RE.match(user)\n         if m:\n             name = m.group(1).strip()\n+    return (name, mail)\n+\n+def fixup_user_hg(user):\n+    def sanitize(name):\n+        # stole this from hg-git\n+        return re.sub('[<>\\n]', '?', name.lstrip('< ').rstrip('> '))\n+\n+    m = AUTHOR_HG_RE.match(user)\n+    if m:\n+        name = sanitize(m.group(1))\n+        mail = sanitize(m.group(2))\n+        ex = m.group(3)\n+        if ex:\n+            name += ' ext:(' + urllib.quote(ex) + ')'\n+    else:\n+        name = sanitize(user)\n+        if '@' in user:\n+            mail = name\n+        else:\n+            mail = None\n+\n+    return (name, mail)\n+\n+def fixup_user(user):\n+    global mode, bad_mail\n+\n+    if mode == 'git':\n+        name, mail = fixup_user_git(user)\n+    else:\n+        name, mail = fixup_user_hg(user)\n \n     if not name:\n-        name = 'Unknown'\n+        name = bad_name\n     if not mail:\n-        mail = 'unknown'\n+        mail = bad_mail\n \n     return '%s <%s>' % (name, mail)\n \n@@ -660,7 +703,7 @@ def do_export(parser):\n def main(args):\n     global prefix, dirname, branches, bmarks\n     global marks, blob_marks, parsed_refs\n-    global peer, mode\n+    global peer, mode, bad_mail, bad_name\n \n     alias = args[1]\n     url = args[2]\n@@ -676,8 +719,12 @@ def main(args):\n \n     if hg_git_compat:\n         mode = 'hg'\n+        bad_mail = 'none@none'\n+        bad_name = ''\n     else:\n         mode = 'git'\n+        bad_mail = 'unknown'\n+        bad_name = 'Unknown'\n \n     if alias[4:] == url:\n         is_tmp = True\n-- \n1.8.0\n"},{"id":"202482","messageId":"1351995218-19889-10-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 09/16] remote-hg: fake bookmark when there's none","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:31Z","receivedAt":"2012-11-04T02:13:31Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Or at least no current bookmark.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-helpers/git-remote-hg | 19 ++++++++++++++-----\n 1 file changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex 9db4b7e..dbe309a 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -26,7 +26,7 @@ import urllib\n # git:\n # Sensible defaults for git.\n # hg bookmarks are exported as git branches, hg branches are prefixed\n-# with 'branches/'.\n+# with 'branches/', HEAD is a special case.\n #\n # hg:\n # Emulate hg-git.\n@@ -430,12 +430,21 @@ def get_branch_tip(repo, branch):\n     return heads[0]\n \n def list_head(repo, cur):\n-    global g_head\n+    global g_head, bmarks\n \n     head = bookmarks.readcurrent(repo)\n-    if not head:\n-        return\n-    node = repo[head]\n+    if head:\n+        node = repo[head]\n+    else:\n+        # fake bookmark from current branch\n+        head = cur\n+        node = repo['.']\n+        if not node:\n+            return\n+        if head == 'default':\n+            head = 'master'\n+        bmarks[head] = node\n+\n     print \"@refs/heads/%s HEAD\" % head\n     g_head = (head, node)\n \n-- \n1.8.0\n"},{"id":"202484","messageId":"1351995218-19889-11-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 10/16] remote-hg: add basic tests","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:32Z","receivedAt":"2012-11-04T02:13:32Z","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-helpers/Makefile   |  13 +++++\n contrib/remote-helpers/test-hg.sh | 112 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 125 insertions(+)\n create mode 100644 contrib/remote-helpers/Makefile\n create mode 100755 contrib/remote-helpers/test-hg.sh\n\ndiff --git a/contrib/remote-helpers/Makefile b/contrib/remote-helpers/Makefile\nnew file mode 100644\nindex 0000000..9a76575\n--- /dev/null\n+++ b/contrib/remote-helpers/Makefile\n@@ -0,0 +1,13 @@\n+TESTS := $(wildcard test*.sh)\n+\n+export T := $(addprefix $(CURDIR)/,$(TESTS))\n+export MAKE := $(MAKE) -e\n+export PATH := $(CURDIR):$(PATH)\n+\n+test:\n+\t$(MAKE) -C ../../t $@\n+\n+$(TESTS):\n+\t$(MAKE) -C ../../t $(CURDIR)/$@\n+\n+.PHONY: $(TESTS)\ndiff --git a/contrib/remote-helpers/test-hg.sh b/contrib/remote-helpers/test-hg.sh\nnew file mode 100755\nindex 0000000..40e6e3c\n--- /dev/null\n+++ b/contrib/remote-helpers/test-hg.sh\n@@ -0,0 +1,112 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2012 Felipe Contreras\n+#\n+# Base commands from hg-git tests:\n+# https://bitbucket.org/durin42/hg-git/src\n+#\n+\n+test_description='Test remote-hg'\n+\n+. ./test-lib.sh\n+\n+if ! test_have_prereq PYTHON; then\n+\tskip_all='skipping remote-hg tests; python not available'\n+\ttest_done\n+fi\n+\n+if ! \"$PYTHON_PATH\" -c 'import mercurial'; then\n+\tskip_all='skipping remote-hg tests; mercurial not available'\n+\ttest_done\n+fi\n+\n+check () {\n+\t(cd $1 &&\n+\tgit log --format='%s' -1 &&\n+\tgit symbolic-ref HEAD) > actual &&\n+\t(echo $2 &&\n+\techo \"refs/heads/$3\") > expected &&\n+\ttest_cmp expected actual\n+}\n+\n+test_expect_success 'cloning' '\n+  test_when_finished \"rm -rf gitrepo*\" &&\n+\n+  (\n+  hg init hgrepo &&\n+  cd hgrepo &&\n+  echo zero > content &&\n+  hg add content &&\n+  hg commit -m zero\n+  ) &&\n+\n+  git clone \"hg::$PWD/hgrepo\" gitrepo &&\n+  check gitrepo zero master\n+'\n+\n+test_expect_success 'cloning with branches' '\n+  test_when_finished \"rm -rf gitrepo*\" &&\n+\n+  (\n+  cd hgrepo &&\n+  hg branch next &&\n+  echo next > content &&\n+  hg commit -m next\n+  ) &&\n+\n+  git clone \"hg::$PWD/hgrepo\" gitrepo &&\n+  check gitrepo next next &&\n+\n+  (cd hgrepo && hg checkout default) &&\n+\n+  git clone \"hg::$PWD/hgrepo\" gitrepo2 &&\n+  check gitrepo2 zero master\n+'\n+\n+test_expect_success 'cloning with bookmarks' '\n+  test_when_finished \"rm -rf gitrepo*\" &&\n+\n+  (\n+  cd hgrepo &&\n+  hg bookmark feature-a &&\n+  echo feature-a > content &&\n+  hg commit -m feature-a\n+  ) &&\n+\n+  git clone \"hg::$PWD/hgrepo\" gitrepo &&\n+  check gitrepo feature-a feature-a\n+'\n+\n+test_expect_success 'cloning with detached head' '\n+  test_when_finished \"rm -rf gitrepo*\" &&\n+\n+  (\n+  cd hgrepo &&\n+  hg update -r 0\n+  ) &&\n+\n+  git clone \"hg::$PWD/hgrepo\" gitrepo &&\n+  check gitrepo zero master\n+'\n+\n+test_expect_success 'update bookmark' '\n+  test_when_finished \"rm -rf gitrepo*\" &&\n+\n+  (\n+  cd hgrepo &&\n+  hg bookmark devel\n+  ) &&\n+\n+  (\n+  git clone \"hg::$PWD/hgrepo\" gitrepo &&\n+  cd gitrepo &&\n+  git checkout devel &&\n+  echo devel > content &&\n+  git commit -a -m devel &&\n+  git push\n+  ) &&\n+\n+  hg -R hgrepo bookmarks | grep \"devel\\s\\+3:\"\n+'\n+\n+test_done\n-- \n1.8.0\n"},{"id":"202485","messageId":"1351995218-19889-12-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 11/16] test-lib: avoid full path to store test results","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:33Z","receivedAt":"2012-11-04T02:13:33Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"No reason to use the full path in case this is used externally.\n\nOtherwise we might get errors such as:\n\n./test-lib.sh: line 394: /home/bob/dev/git/t/test-results//home/bob/dev/git/contrib/remote-hg/test-2894.counts: No such file or directory\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/test-lib.sh | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 514282c..5a3d665 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -389,7 +389,8 @@ test_done () {\n \tthen\n \t\ttest_results_dir=\"$TEST_OUTPUT_DIRECTORY/test-results\"\n \t\tmkdir -p \"$test_results_dir\"\n-\t\ttest_results_path=\"$test_results_dir/${0%.sh}-$$.counts\"\n+\t\tbase=${0##*/}\n+\t\ttest_results_path=\"$test_results_dir/${base%.sh}-$$.counts\"\n \n \t\tcat >>\"$test_results_path\" <<-EOF\n \t\ttotal $test_count\n-- \n1.8.0\n"},{"id":"202487","messageId":"1351995218-19889-13-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 12/16] remote-hg: add bidirectional tests","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:34Z","receivedAt":"2012-11-04T02:13:34Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Base commands from hg-git tests:\nhttps://bitbucket.org/durin42/hg-git/src\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-helpers/test-hg-bidi.sh | 243 +++++++++++++++++++++++++++++++++\n 1 file changed, 243 insertions(+)\n create mode 100755 contrib/remote-helpers/test-hg-bidi.sh\n\ndiff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh\nnew file mode 100755\nindex 0000000..a94eb28\n--- /dev/null\n+++ b/contrib/remote-helpers/test-hg-bidi.sh\n@@ -0,0 +1,243 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2012 Felipe Contreras\n+#\n+# Base commands from hg-git tests:\n+# https://bitbucket.org/durin42/hg-git/src\n+#\n+\n+test_description='Test biridectionality of remote-hg'\n+\n+. ./test-lib.sh\n+\n+if ! test_have_prereq PYTHON; then\n+\tskip_all='skipping remote-hg tests; python not available'\n+\ttest_done\n+fi\n+\n+if ! \"$PYTHON_PATH\" -c 'import mercurial'; then\n+\tskip_all='skipping remote-hg tests; mercurial not available'\n+\ttest_done\n+fi\n+\n+# clone to a git repo\n+git_clone () {\n+\thg -R $1 bookmark -f -r tip master &&\n+\tgit clone -q \"hg::$PWD/$1\" $2\n+}\n+\n+# clone to an hg repo\n+hg_clone () {\n+\t(\n+\thg init $2 &&\n+\tcd $1 &&\n+\tgit push -q \"hg::$PWD/../$2\" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'\n+\t) &&\n+\n+\t(cd $2 && hg -q update)\n+}\n+\n+# push an hg repo\n+hg_push () {\n+\t(\n+\tcd $2\n+\told=$(git symbolic-ref --short HEAD)\n+\tgit checkout -q -b tmp &&\n+\tgit fetch -q \"hg::$PWD/../$1\" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&\n+\tgit checkout -q $old &&\n+\tgit branch -q -D tmp 2> /dev/null || true\n+\t)\n+}\n+\n+hg_log () {\n+\thg -R $1 log --graph --debug | grep -v 'tag: *default/'\n+}\n+\n+setup () {\n+\t(\n+\techo \"[ui]\"\n+\techo \"username = A U Thor <author@example.com>\"\n+\techo \"[defaults]\"\n+\techo \"backout = -d \\\"0 0\\\"\"\n+\techo \"commit = -d \\\"0 0\\\"\"\n+\techo \"debugrawcommit = -d \\\"0 0\\\"\"\n+\techo \"tag = -d \\\"0 0\\\"\"\n+\t) >> \"$HOME\"/.hgrc &&\n+\tgit config --global remote-hg.hg-git-compat true\n+\n+\texport HGEDITOR=/usr/bin/true\n+\n+\texport GIT_AUTHOR_DATE=\"2007-01-01 00:00:00 +0230\"\n+\texport GIT_COMMITTER_DATE=\"$GIT_AUTHOR_DATE\"\n+}\n+\n+setup\n+\n+test_expect_success 'encoding' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -m \"add älphà\" &&\n+\n+\texport GIT_AUTHOR_NAME=\"tést èncödîng\" &&\n+\techo beta > beta &&\n+\tgit add beta &&\n+\tgit commit -m \"add beta\" &&\n+\n+\techo gamma > gamma &&\n+\tgit add gamma &&\n+\tgit commit -m \"add gämmâ\" &&\n+\n+\t: TODO git config i18n.commitencoding latin-1 &&\n+\techo delta > delta &&\n+\tgit add delta &&\n+\tgit commit -m \"add déltà\"\n+\t) &&\n+\n+\thg_clone gitrepo hgrepo &&\n+\tgit_clone hgrepo gitrepo2 &&\n+\thg_clone gitrepo2 hgrepo2 &&\n+\n+\tHGENCODING=utf-8 hg_log hgrepo > expected &&\n+\tHGENCODING=utf-8 hg_log hgrepo2 > actual &&\n+\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'file removal' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -m \"add alpha\" &&\n+\techo beta > beta &&\n+\tgit add beta &&\n+\tgit commit -m \"add beta\"\n+\tmkdir foo &&\n+\techo blah > foo/bar &&\n+\tgit add foo &&\n+\tgit commit -m \"add foo\" &&\n+\tgit rm alpha &&\n+\tgit commit -m \"remove alpha\" &&\n+\tgit rm foo/bar &&\n+\tgit commit -m \"remove foo/bar\"\n+\t) &&\n+\n+\thg_clone gitrepo hgrepo &&\n+\tgit_clone hgrepo gitrepo2 &&\n+\thg_clone gitrepo2 hgrepo2 &&\n+\n+\thg_log hgrepo > expected &&\n+\thg_log hgrepo2 > actual &&\n+\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'git tags' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\tgit config receive.denyCurrentBranch ignore &&\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -m \"add alpha\" &&\n+\tgit tag alpha &&\n+\n+\techo beta > beta &&\n+\tgit add beta &&\n+\tgit commit -m \"add beta\" &&\n+\tgit tag -a -m \"added tag beta\" beta\n+\t) &&\n+\n+\thg_clone gitrepo hgrepo &&\n+\tgit_clone hgrepo gitrepo2 &&\n+\thg_clone gitrepo2 hgrepo2 &&\n+\n+\thg_log hgrepo > expected &&\n+\thg_log hgrepo2 > actual &&\n+\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'hg branch' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -q -m \"add alpha\" &&\n+\tgit checkout -q -b not-master\n+\t) &&\n+\n+\t(\n+\thg_clone gitrepo hgrepo &&\n+\n+\tcd hgrepo &&\n+\thg -q co master &&\n+\thg mv alpha beta &&\n+\thg -q commit -m \"rename alpha to beta\" &&\n+\thg branch gamma | grep -v \"permanent and global\" &&\n+\thg -q commit -m \"started branch gamma\"\n+\t) &&\n+\n+\thg_push hgrepo gitrepo &&\n+\thg_clone gitrepo hgrepo2 &&\n+\n+\t: TODO, avoid \"master\" bookmark &&\n+\t(cd hgrepo2 && hg checkout gamma) &&\n+\n+\thg_log hgrepo > expected &&\n+\thg_log hgrepo2 > actual &&\n+\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'hg tags' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -m \"add alpha\" &&\n+\tgit checkout -q -b not-master\n+\t) &&\n+\n+\t(\n+\thg_clone gitrepo hgrepo &&\n+\n+\tcd hgrepo &&\n+\thg co master &&\n+\thg tag alpha\n+\t) &&\n+\n+\thg_push hgrepo gitrepo &&\n+\thg_clone gitrepo hgrepo2 &&\n+\n+\thg_log hgrepo > expected &&\n+\thg_log hgrepo2 > actual &&\n+\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.8.0\n"},{"id":"202486","messageId":"1351995218-19889-14-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 13/16] remote-hg: add tests to compare with hg-git","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:35Z","receivedAt":"2012-11-04T02:13:35Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"The base commands come from the tests of the hg-git project.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-helpers/test-hg-hg-git.sh | 462 +++++++++++++++++++++++++++++++\n 1 file changed, 462 insertions(+)\n create mode 100755 contrib/remote-helpers/test-hg-hg-git.sh\n\ndiff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh\nnew file mode 100755\nindex 0000000..e07bba5\n--- /dev/null\n+++ b/contrib/remote-helpers/test-hg-hg-git.sh\n@@ -0,0 +1,462 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2012 Felipe Contreras\n+#\n+# Base commands from hg-git tests:\n+# https://bitbucket.org/durin42/hg-git/src\n+#\n+\n+test_description='Test remote-hg output compared to hg-git'\n+\n+. ./test-lib.sh\n+\n+if ! test_have_prereq PYTHON; then\n+\tskip_all='skipping remote-hg tests; python not available'\n+\ttest_done\n+fi\n+\n+if ! \"$PYTHON_PATH\" -c 'import mercurial'; then\n+\tskip_all='skipping remote-hg tests; mercurial not available'\n+\ttest_done\n+fi\n+\n+if ! \"$PYTHON_PATH\" -c 'import hggit'; then\n+\tskip_all='skipping remote-hg tests; hg-git not available'\n+\ttest_done\n+fi\n+\n+# clone to a git repo with git\n+git_clone_git () {\n+\thg -R $1 bookmark -f -r tip master &&\n+\tgit clone -q \"hg::$PWD/$1\" $2\n+}\n+\n+# clone to an hg repo with git\n+hg_clone_git () {\n+\t(\n+\thg init $2 &&\n+\tcd $1 &&\n+\tgit push -q \"hg::$PWD/../$2\" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'\n+\t) &&\n+\n+\t(cd $2 && hg -q update)\n+}\n+\n+# clone to a git repo with hg\n+git_clone_hg () {\n+\t(\n+\tgit init -q $2 &&\n+\tcd $1 &&\n+\thg bookmark -f -r tip master &&\n+\thg -q push -r master ../$2 || true\n+\t)\n+}\n+\n+# clone to an hg repo with hg\n+hg_clone_hg () {\n+\thg -q clone $1 $2\n+}\n+\n+# push an hg repo with git\n+hg_push_git () {\n+\t(\n+\tcd $2\n+\told=$(git symbolic-ref --short HEAD)\n+\tgit checkout -q -b tmp &&\n+\tgit fetch -q \"hg::$PWD/../$1\" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&\n+\tgit checkout -q $old &&\n+\tgit branch -q -D tmp 2> /dev/null || true\n+\t)\n+}\n+\n+# push an hg git repo with hg\n+hg_push_hg () {\n+\t(\n+\tcd $1 &&\n+\thg -q push ../$2 || true\n+\t)\n+}\n+\n+hg_log () {\n+\thg -R $1 log --graph --debug | grep -v 'tag: *default/'\n+}\n+\n+git_log () {\n+\tgit --git-dir=$1/.git fast-export --branches\n+}\n+\n+setup () {\n+\t(\n+\techo \"[ui]\"\n+\techo \"username = A U Thor <author@example.com>\"\n+\techo \"[defaults]\"\n+\techo \"backout = -d \\\"0 0\\\"\"\n+\techo \"commit = -d \\\"0 0\\\"\"\n+\techo \"debugrawcommit = -d \\\"0 0\\\"\"\n+\techo \"tag = -d \\\"0 0\\\"\"\n+\techo \"[extensions]\"\n+\techo \"hgext.bookmarks =\"\n+\techo \"hggit =\"\n+\t) >> \"$HOME\"/.hgrc &&\n+\tgit config --global receive.denycurrentbranch warn\n+\tgit config --global remote-hg.hg-git-compat true\n+\n+\texport HGEDITOR=/usr/bin/true\n+\n+\texport GIT_AUTHOR_DATE=\"2007-01-01 00:00:00 +0230\"\n+\texport GIT_COMMITTER_DATE=\"$GIT_AUTHOR_DATE\"\n+}\n+\n+setup\n+\n+test_expect_success 'merge conflict 1' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\thg init hgrepo1 &&\n+\tcd hgrepo1 &&\n+\techo A > afile &&\n+\thg add afile &&\n+\thg ci -m \"origin\" &&\n+\n+\techo B > afile &&\n+\thg ci -m \"A->B\" &&\n+\n+\thg up -r0 &&\n+\techo C > afile &&\n+\thg ci -m \"A->C\" &&\n+\n+\thg merge -r1 || true &&\n+\techo C > afile &&\n+\thg resolve -m afile &&\n+\thg ci -m \"merge to C\"\n+\t) &&\n+\n+\tfor x in hg git; do\n+\t\tgit_clone_$x hgrepo1 gitrepo-$x &&\n+\t\thg_clone_$x gitrepo-$x hgrepo2-$x &&\n+\t\thg_log hgrepo2-$x > hg-log-$x &&\n+\t\tgit_log gitrepo-$x > git-log-$x\n+\tdone &&\n+\n+\ttest_cmp hg-log-hg hg-log-git &&\n+\ttest_cmp git-log-hg git-log-git\n+'\n+\n+test_expect_success 'merge conflict 2' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\thg init hgrepo1 &&\n+\tcd hgrepo1 &&\n+\techo A > afile &&\n+\thg add afile &&\n+\thg ci -m \"origin\" &&\n+\n+\techo B > afile &&\n+\thg ci -m \"A->B\" &&\n+\n+\thg up -r0 &&\n+\techo C > afile &&\n+\thg ci -m \"A->C\" &&\n+\n+\thg merge -r1 || true &&\n+\techo B > afile &&\n+\thg resolve -m afile &&\n+\thg ci -m \"merge to B\"\n+\t) &&\n+\n+\tfor x in hg git; do\n+\t\tgit_clone_$x hgrepo1 gitrepo-$x &&\n+\t\thg_clone_$x gitrepo-$x hgrepo2-$x &&\n+\t\thg_log hgrepo2-$x > hg-log-$x &&\n+\t\tgit_log gitrepo-$x > git-log-$x\n+\tdone &&\n+\n+\ttest_cmp hg-log-hg hg-log-git &&\n+\ttest_cmp git-log-hg git-log-git\n+'\n+\n+test_expect_success 'converged merge' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\thg init hgrepo1 &&\n+\tcd hgrepo1 &&\n+\techo A > afile &&\n+\thg add afile &&\n+\thg ci -m \"origin\" &&\n+\n+\techo B > afile &&\n+\thg ci -m \"A->B\" &&\n+\n+\techo C > afile &&\n+\thg ci -m \"B->C\" &&\n+\n+\thg up -r0 &&\n+\techo C > afile &&\n+\thg ci -m \"A->C\" &&\n+\n+\thg merge -r2 || true &&\n+\thg ci -m \"merge\"\n+\t) &&\n+\n+\tfor x in hg git; do\n+\t\tgit_clone_$x hgrepo1 gitrepo-$x &&\n+\t\thg_clone_$x gitrepo-$x hgrepo2-$x &&\n+\t\thg_log hgrepo2-$x > hg-log-$x &&\n+\t\tgit_log gitrepo-$x > git-log-$x\n+\tdone &&\n+\n+\ttest_cmp hg-log-hg hg-log-git &&\n+\ttest_cmp git-log-hg git-log-git\n+'\n+\n+test_expect_success 'encoding' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -m \"add älphà\" &&\n+\n+\texport GIT_AUTHOR_NAME=\"tést èncödîng\" &&\n+\techo beta > beta &&\n+\tgit add beta &&\n+\tgit commit -m \"add beta\" &&\n+\n+\techo gamma > gamma &&\n+\tgit add gamma &&\n+\tgit commit -m \"add gämmâ\" &&\n+\n+\t: TODO git config i18n.commitencoding latin-1 &&\n+\techo delta > delta &&\n+\tgit add delta &&\n+\tgit commit -m \"add déltà\"\n+\t) &&\n+\n+\tfor x in hg git; do\n+\t\thg_clone_$x gitrepo hgrepo-$x &&\n+\t\tgit_clone_$x hgrepo-$x gitrepo2-$x &&\n+\n+\t\tHGENCODING=utf-8 hg_log hgrepo-$x > hg-log-$x &&\n+\t\tgit_log gitrepo2-$x > git-log-$x\n+\tdone &&\n+\n+\ttest_cmp hg-log-hg hg-log-git &&\n+\ttest_cmp git-log-hg git-log-git\n+'\n+\n+test_expect_success 'file removal' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -m \"add alpha\" &&\n+\techo beta > beta &&\n+\tgit add beta &&\n+\tgit commit -m \"add beta\"\n+\tmkdir foo &&\n+\techo blah > foo/bar &&\n+\tgit add foo &&\n+\tgit commit -m \"add foo\" &&\n+\tgit rm alpha &&\n+\tgit commit -m \"remove alpha\" &&\n+\tgit rm foo/bar &&\n+\tgit commit -m \"remove foo/bar\"\n+\t) &&\n+\n+\tfor x in hg git; do\n+\t\t(\n+\t\thg_clone_$x gitrepo hgrepo-$x &&\n+\t\tcd hgrepo-$x &&\n+\t\thg_log . &&\n+\t\thg manifest -r 3 &&\n+\t\thg manifest\n+\t\t) > output-$x &&\n+\n+\t\tgit_clone_$x hgrepo-$x gitrepo2-$x &&\n+\t\tgit_log gitrepo2-$x > log-$x\n+\tdone &&\n+\n+\ttest_cmp output-hg output-git &&\n+\ttest_cmp log-hg log-git\n+'\n+\n+test_expect_success 'git tags' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\t(\n+\tgit init -q gitrepo &&\n+\tcd gitrepo &&\n+\tgit config receive.denyCurrentBranch ignore &&\n+\techo alpha > alpha &&\n+\tgit add alpha &&\n+\tgit commit -m \"add alpha\" &&\n+\tgit tag alpha &&\n+\n+\techo beta > beta &&\n+\tgit add beta &&\n+\tgit commit -m \"add beta\" &&\n+\tgit tag -a -m \"added tag beta\" beta\n+\t) &&\n+\n+\tfor x in hg git; do\n+\t\thg_clone_$x gitrepo hgrepo-$x &&\n+\t\thg_log hgrepo-$x > log-$x\n+\tdone &&\n+\n+\ttest_cmp log-hg log-git\n+'\n+\n+test_expect_success 'hg author' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\tfor x in hg git; do\n+\t\t(\n+\t\tgit init -q gitrepo-$x &&\n+\t\tcd gitrepo-$x &&\n+\n+\t\techo alpha > alpha &&\n+\t\tgit add alpha &&\n+\t\tgit commit -m \"add alpha\" &&\n+\t\tgit checkout -q -b not-master\n+\t\t) &&\n+\n+\t\t(\n+\t\thg_clone_$x gitrepo-$x hgrepo-$x &&\n+\t\tcd hgrepo-$x &&\n+\n+\t\thg co master &&\n+\t\techo beta > beta &&\n+\t\thg add beta &&\n+\t\thg commit -u \"test\" -m \"add beta\" &&\n+\n+\t\techo gamma >> beta &&\n+\t\thg commit -u \"test <test@example.com> (comment)\" -m \"modify beta\" &&\n+\n+\t\techo gamma > gamma &&\n+\t\thg add gamma &&\n+\t\thg commit -u \"<test@example.com>\" -m \"add gamma\" &&\n+\n+\t\techo delta > delta &&\n+\t\thg add delta &&\n+\t\thg commit -u \"name<test@example.com>\" -m \"add delta\" &&\n+\n+\t\techo epsilon > epsilon &&\n+\t\thg add epsilon &&\n+\t\thg commit -u \"name <test@example.com\" -m \"add epsilon\" &&\n+\n+\t\techo zeta > zeta &&\n+\t\thg add zeta &&\n+\t\thg commit -u \" test \" -m \"add zeta\" &&\n+\n+\t\techo eta > eta &&\n+\t\thg add eta &&\n+\t\thg commit -u \"test < test@example.com >\" -m \"add eta\" &&\n+\n+\t\techo theta > theta &&\n+\t\thg add theta &&\n+\t\thg commit -u \"test >test@example.com>\" -m \"add theta\"\n+\t\t) &&\n+\n+\t\thg_push_$x hgrepo-$x gitrepo-$x &&\n+\t\thg_clone_$x gitrepo-$x hgrepo2-$x &&\n+\n+\t\thg_log hgrepo2-$x > hg-log-$x &&\n+\t\tgit_log gitrepo-$x > git-log-$x\n+\tdone &&\n+\n+\ttest_cmp git-log-hg git-log-git &&\n+\n+\ttest_cmp hg-log-hg hg-log-git &&\n+\ttest_cmp git-log-hg git-log-git\n+'\n+\n+test_expect_success 'hg branch' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\tfor x in hg git; do\n+\t\t(\n+\t\tgit init -q gitrepo-$x &&\n+\t\tcd gitrepo-$x &&\n+\n+\t\techo alpha > alpha &&\n+\t\tgit add alpha &&\n+\t\tgit commit -q -m \"add alpha\" &&\n+\t\tgit checkout -q -b not-master\n+\t\t) &&\n+\n+\t\t(\n+\t\thg_clone_$x gitrepo-$x hgrepo-$x &&\n+\n+\t\tcd hgrepo-$x &&\n+\t\thg -q co master &&\n+\t\thg mv alpha beta &&\n+\t\thg -q commit -m \"rename alpha to beta\" &&\n+\t\thg branch gamma | grep -v \"permanent and global\" &&\n+\t\thg -q commit -m \"started branch gamma\"\n+\t\t) &&\n+\n+\t\thg_push_$x hgrepo-$x gitrepo-$x &&\n+\t\thg_clone_$x gitrepo-$x hgrepo2-$x &&\n+\n+\t\thg_log hgrepo2-$x > hg-log-$x &&\n+\t\tgit_log gitrepo-$x > git-log-$x\n+\tdone &&\n+\n+\ttest_cmp hg-log-hg hg-log-git &&\n+\ttest_cmp git-log-hg git-log-git\n+'\n+\n+test_expect_success 'hg tags' '\n+\tmkdir -p tmp && cd tmp &&\n+\ttest_when_finished \"cd .. && rm -rf tmp\" &&\n+\n+\tfor x in hg git; do\n+\t\t(\n+\t\tgit init -q gitrepo-$x &&\n+\t\tcd gitrepo-$x &&\n+\n+\t\techo alpha > alpha &&\n+\t\tgit add alpha &&\n+\t\tgit commit -m \"add alpha\" &&\n+\t\tgit checkout -q -b not-master\n+\t\t) &&\n+\n+\t\t(\n+\t\thg_clone_$x gitrepo-$x hgrepo-$x &&\n+\n+\t\tcd hgrepo-$x &&\n+\t\thg co master &&\n+\t\thg tag alpha\n+\t\t) &&\n+\n+\t\thg_push_$x hgrepo-$x gitrepo-$x &&\n+\t\thg_clone_$x gitrepo-$x hgrepo2-$x &&\n+\n+\t\t(\n+\t\tgit --git-dir=gitrepo-$x/.git tag -l &&\n+\t\thg_log hgrepo2-$x &&\n+\t\tcat hgrepo2-$x/.hgtags\n+\t\t) > output-$x\n+\tdone &&\n+\n+\ttest_cmp output-hg output-git\n+'\n+\n+test_done\n-- \n1.8.0\n"},{"id":"202488","messageId":"1351995218-19889-15-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 14/16] remote-hg: add extra author test","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:36Z","receivedAt":"2012-11-04T02:13:36Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"For hg.hg.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-helpers/test-hg-hg-git.sh | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/remote-helpers/test-hg-hg-git.sh b/contrib/remote-helpers/test-hg-hg-git.sh\nindex e07bba5..3e76d9f 100755\n--- a/contrib/remote-helpers/test-hg-hg-git.sh\n+++ b/contrib/remote-helpers/test-hg-hg-git.sh\n@@ -370,7 +370,11 @@ test_expect_success 'hg author' '\n \n \t\techo theta > theta &&\n \t\thg add theta &&\n-\t\thg commit -u \"test >test@example.com>\" -m \"add theta\"\n+\t\thg commit -u \"test >test@example.com>\" -m \"add theta\" &&\n+\n+\t\techo iota > iota &&\n+\t\thg add iota &&\n+\t\thg commit -u \"test <test <at> example <dot> com>\" -m \"add iota\"\n \t\t) &&\n \n \t\thg_push_$x hgrepo-$x gitrepo-$x &&\n-- \n1.8.0\n"},{"id":"202489","messageId":"1351995218-19889-16-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 15/16] remote-hg: add option to not track branches","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:37Z","receivedAt":"2012-11-04T02:13:37Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Some people prefer it this way.\n\n % git config --global remote-hg.track-branches false\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-helpers/git-remote-hg | 22 ++++++++++++++--------\n 1 file changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex dbe309a..a9ae844 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -449,14 +449,9 @@ def list_head(repo, cur):\n     g_head = (head, node)\n \n def do_list(parser):\n-    global branches, bmarks, mode\n+    global branches, bmarks, mode, track_branches\n \n     repo = parser.repo\n-    for branch in repo.branchmap():\n-        heads = repo.branchheads(branch)\n-        if len(heads):\n-            branches[branch] = heads\n-\n     for bmark, node in bookmarks.listbookmarks(repo).iteritems():\n         bmarks[bmark] = repo[node]\n \n@@ -464,7 +459,12 @@ def do_list(parser):\n \n     list_head(repo, cur)\n \n-    if mode != 'hg':\n+    if track_branches:\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/branches/%s\" % branch\n \n@@ -713,16 +713,22 @@ def main(args):\n     global prefix, dirname, branches, bmarks\n     global marks, blob_marks, parsed_refs\n     global peer, mode, bad_mail, bad_name\n+    global track_branches\n \n     alias = args[1]\n     url = args[2]\n     peer = None\n \n-    cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat']\n     hg_git_compat = False\n+    track_branches = True\n     try:\n+        cmd = ['git', 'config', '--get', 'remote-hg.hg-git-compat']\n         if subprocess.check_output(cmd) == 'true\\n':\n             hg_git_compat = True\n+            track_branches = False\n+        cmd = ['git', 'config', '--get', 'remote-hg.track-branches']\n+        if subprocess.check_output(cmd) == 'false\\n':\n+            track_branches = False\n     except subprocess.CalledProcessError:\n         pass\n \n-- \n1.8.0\n"},{"id":"202490","messageId":"1351995218-19889-17-git-send-email-felipe.contreras@gmail.com","threadId":"32005","inReplyTo":"1351995218-19889-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v6 16/16] remote-hg: the author email can be null","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2012-11-04T02:13:38Z","receivedAt":"2012-11-04T02:13:38Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Like 'Foo <>'.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n contrib/remote-helpers/git-remote-hg | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex a9ae844..7929eec 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -35,9 +35,9 @@ import urllib\n #\n \n NAME_RE = re.compile('^([^<>]+)')\n-AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]+)>$')\n-AUTHOR_HG_RE = re.compile('^(.*?) ?<(.+?)(?:>(.+)?)?$')\n-RAW_AUTHOR_RE = re.compile('^(\\w+) (?:(.+)? )?<(.+)> (\\d+) ([+-]\\d+)')\n+AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')\n+AUTHOR_HG_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-- \n1.8.0\n"},{"id":"204200","messageId":"20121128202320.GA22522@odin.tremily.us","threadId":"32005","inReplyTo":"1351995218-19889-8-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v6 07/16] remote-hg: add support for hg-git compat mode","fromName":"W. Trevor King","fromEmail":"wking@tremily.us","sentAt":"2012-11-28T20:23:20Z","receivedAt":"2012-11-28T20:23:20Z","isPatch":true,"sender":{"key":"wking@tremily.us","avatar":"https://avatars.githubusercontent.com/u/209920?v=4"},"body":"I'm not sure if this is the most recent patch iteration for this\nfeature, but I just saw this typo in `pu`.\n\nOn Sun, Nov 04, 2012 at 03:13:29AM +0100, Felipe Contreras wrote:\n> +# Commits are modified to preserve hg information and allow biridectionality.\n                                                               ^^^^^^^^\ns/biridectionality/bidirectionality/\n\nCheers,\nTrevor\n\n-- \nThis email may be signed or encrypted with GnuPG (http://www.gnupg.org).\nFor more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy\n"},{"id":"206298","messageId":"20130108154737.GA4662@odin.tremily.us","threadId":"32005","inReplyTo":"20121128202320.GA22522@odin.tremily.us","subject":"[PATCH] remote-hg: Fix biridectionality -> bidirectionality typos","fromName":"W. Trevor King","fromEmail":"wking@tremily.us","sentAt":"2013-01-08T15:47:37Z","receivedAt":"2013-01-08T15:47:37Z","isPatch":true,"sender":{"key":"wking@tremily.us","avatar":"https://avatars.githubusercontent.com/u/209920?v=4"},"body":"Signed-off-by: W. Trevor King <wking@tremily.us>\n---\n\nI was looking for one of my older messages to the Git list, and I\nfound this, which seems to have fallen through the cracks:\n\nOn Wed, Nov 28, 2012 at 03:23:20PM -0500, W. Trevor King wrote:\n> I'm not sure if this is the most recent patch iteration for this\n> feature, but I just saw this typo in `pu`.\n> \n> On Sun, Nov 04, 2012 at 03:13:29AM +0100, Felipe Contreras wrote:\n> > +# Commits are modified to preserve hg information and allow biridectionality.\n>                                                                ^^^^^^^^\n> s/biridectionality/bidirectionality/\n\nThis fixes that instance (which has since landed in master) and\nanother similar typo.\n\n contrib/remote-helpers/git-remote-hg   | 2 +-\n contrib/remote-helpers/test-hg-bidi.sh | 2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\nindex 016cdad..c700600 100755\n--- a/contrib/remote-helpers/git-remote-hg\n+++ b/contrib/remote-helpers/git-remote-hg\n@@ -31,7 +31,7 @@ import urllib\n # hg:\n # Emulate hg-git.\n # Only hg bookmarks are exported as git branches.\n-# Commits are modified to preserve hg information and allow biridectionality.\n+# Commits are modified to preserve hg information and allow bidirectionality.\n #\n \n NAME_RE = re.compile('^([^<>]+)')\ndiff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh\nindex a94eb28..1d61982 100755\n--- a/contrib/remote-helpers/test-hg-bidi.sh\n+++ b/contrib/remote-helpers/test-hg-bidi.sh\n@@ -6,7 +6,7 @@\n # https://bitbucket.org/durin42/hg-git/src\n #\n \n-test_description='Test biridectionality of remote-hg'\n+test_description='Test bidirectionality of remote-hg'\n \n . ./test-lib.sh\n \n-- \n1.8.1.151.g32238ae\n"},{"id":"206308","messageId":"7vboczfunc.fsf@alter.siamese.dyndns.org","threadId":"32005","inReplyTo":"20130108154737.GA4662@odin.tremily.us","subject":"Re: [PATCH] remote-hg: Fix biridectionality -> bidirectionality typos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-08T17:32:07Z","receivedAt":"2013-01-08T17:32:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"W. Trevor King\" <wking@tremily.us> writes:\n\n> Signed-off-by: W. Trevor King <wking@tremily.us>\n> ---\n>\n> I was looking for one of my older messages to the Git list, and I\n> found this, which seems to have fallen through the cracks:\n\nThanks; didn't Documentation/SubmittingPatches ask you not to do PGP\nmultipart but send patches in plain text?\n\n> On Wed, Nov 28, 2012 at 03:23:20PM -0500, W. Trevor King wrote:\n>> I'm not sure if this is the most recent patch iteration for this\n>> feature, but I just saw this typo in `pu`.\n>> \n>> On Sun, Nov 04, 2012 at 03:13:29AM +0100, Felipe Contreras wrote:\n>> > +# Commits are modified to preserve hg information and allow biridectionality.\n>>                                                                ^^^^^^^^\n>> s/biridectionality/bidirectionality/\n>\n> This fixes that instance (which has since landed in master) and\n> another similar typo.\n>\n>  contrib/remote-helpers/git-remote-hg   | 2 +-\n>  contrib/remote-helpers/test-hg-bidi.sh | 2 +-\n>  2 files changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg\n> index 016cdad..c700600 100755\n> --- a/contrib/remote-helpers/git-remote-hg\n> +++ b/contrib/remote-helpers/git-remote-hg\n> @@ -31,7 +31,7 @@ import urllib\n>  # hg:\n>  # Emulate hg-git.\n>  # Only hg bookmarks are exported as git branches.\n> -# Commits are modified to preserve hg information and allow biridectionality.\n> +# Commits are modified to preserve hg information and allow bidirectionality.\n>  #\n>  \n>  NAME_RE = re.compile('^([^<>]+)')\n> diff --git a/contrib/remote-helpers/test-hg-bidi.sh b/contrib/remote-helpers/test-hg-bidi.sh\n> index a94eb28..1d61982 100755\n> --- a/contrib/remote-helpers/test-hg-bidi.sh\n> +++ b/contrib/remote-helpers/test-hg-bidi.sh\n> @@ -6,7 +6,7 @@\n>  # https://bitbucket.org/durin42/hg-git/src\n>  #\n>  \n> -test_description='Test biridectionality of remote-hg'\n> +test_description='Test bidirectionality of remote-hg'\n>  \n>  . ./test-lib.sh\n"},{"id":"206312","messageId":"20130108175001.GG4662@odin.tremily.us","threadId":"32005","inReplyTo":"7vboczfunc.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] remote-hg: Fix biridectionality -> bidirectionality typos","fromName":"W. Trevor King","fromEmail":"wking@tremily.us","sentAt":"2013-01-08T17:50:01Z","receivedAt":"2013-01-08T17:50:01Z","isPatch":true,"sender":{"key":"wking@tremily.us","avatar":"https://avatars.githubusercontent.com/u/209920?v=4"},"body":"On Tue, Jan 08, 2013 at 09:32:07AM -0800, Junio C Hamano wrote:\n> Thanks; didn't Documentation/SubmittingPatches ask you not to do PGP\n> multipart but send patches in plain text?\n\nGah.  I need to tell myself to reread that every time I send a patch\n:p.\n"},{"id":"206314","messageId":"7vtxqreebc.fsf@alter.siamese.dyndns.org","threadId":"32005","inReplyTo":"20130108175001.GG4662@odin.tremily.us","subject":"Re: [PATCH] remote-hg: Fix biridectionality -> bidirectionality typos","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-01-08T18:10:15Z","receivedAt":"2013-01-08T18:10:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"W. Trevor King\" <wking@tremily.us> writes:\n\n> On Tue, Jan 08, 2013 at 09:32:07AM -0800, Junio C Hamano wrote:\n>> Thanks; didn't Documentation/SubmittingPatches ask you not to do PGP\n>> multipart but send patches in plain text?\n>\n> Gah.  I need to tell myself to reread that every time I send a patch\n> :p.\n\nNo need to worry; thanks for a patch---applied to 'maint'.\n"}]}