{"thread":{"id":"6340","subject":"Mercurial to git converter.","startedAt":"2007-01-11T17:49:26Z","lastAt":"2008-06-17T09:12:18Z","messageCount":15,"participants":["Stelian Pop","Jakub Narebski","Shawn O. Pearce","Junio C Hamano","Nick Andrew","Johannes Schindelin","Mike Hommey"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"31483","messageId":"1168537766.22649.19.camel@localhost.localdomain","threadId":"6340","inReplyTo":null,"subject":"Mercurial to git converter.","fromName":"Stelian Pop","fromEmail":"stelian@popies.net","sentAt":"2007-01-11T17:49:26Z","receivedAt":"2007-01-11T17:49:26Z","isPatch":false,"sender":{"key":"stelian@popies.net","avatar":"https://gravatar.com/avatar/c99e49c3b3d851deb85e4557b368712c890a09985211f183bd151f8ab6357c03?d=mp&s=160"},"body":"Hi all,\n\nI switched recently from Mercurial to git for some of my projects, and\nneeded a conversion tool with branch support (unlike tailor). I didn't\nfind any, so I ended up writing my own one. \n\nhg-to-git.py can probably be greatly improved (it's a rather crude\ncombination of shell and python) but it does already work quite well for\nme. Features:\n\t- supports incremental conversion (keep a git repo in sync with a hg\none)\n\t- supports hg branches\n\t- converts hg tags \n\nFeel free to use it, improve it or even include it in mainline git (like\ngit-svn and friends).\n\nStelian.\n\n#! /usr/bin/python\n\n\"\"\" hg-to-svn.py - A Mercurial to GIT converter\n\n    Copyright (C)2007 Stelian Pop <stelian@popies.net>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2, or (at your option)\n    any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\"\"\"\n\nimport os, os.path, sys\nimport tempfile, popen2, pickle, getopt\nimport re\n\n# Maps hg version -> git version\nhgvers = {}\n# List of children for each hg revision\nhgchildren = {}\n# Current branch for each hg revision\nhgbranch = {}\n\n#------------------------------------------------------------------------------\n\ndef usage():\n\n        print \"\"\"\\\n%s: [OPTIONS] <hgprj>\n\noptions:\n    -s, --gitstate=FILE: name of the state to be saved/read\n                         for incrementals\n\nrequired:\n    hgprj:  name of the HG project to import (directory)\n\"\"\" % sys.argv[0]\n\n#------------------------------------------------------------------------------\n\ndef getgitenv(user, date):\n    env = ''\n    elems = re.compile('(.*?)\\s+<(.*)>').match(user)\n    if elems:\n        env += 'export GIT_AUTHOR_NAME=\"%s\" ;' % elems.group(1)\n        env += 'export GIT_COMMITER_NAME=\"%s\" ;' % elems.group(1)\n        env += 'export GIT_AUTHOR_EMAIL=\"%s\" ;' % elems.group(2)\n        env += 'export GIT_COMMITER_EMAIL=\"%s\" ;' % elems.group(2)\n    else:\n        env += 'export GIT_AUTHOR_NAME=\"%s\" ;' % user\n        env += 'export GIT_COMMITER_NAME=\"%s\" ;' % user\n        env += 'export GIT_AUTHOR_EMAIL= ;'\n        env += 'export GIT_COMMITER_EMAIL= ;'\n\n    env += 'export GIT_AUTHOR_DATE=\"%s\" ;' % date\n    env += 'export GIT_COMMITTER_DATE=\"%s\" ;' % date\n    return env\n\n#------------------------------------------------------------------------------\n\nstate = ''\n\ntry:\n    opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir='])\n    for o, a in opts:\n        if o in ('-s', '--gitstate'):\n            state = a\n            state = os.path.abspath(state)\n\n    if len(args) != 1:\n        raise('params')\nexcept:\n    usage()\n    sys.exit(1)\n\nhgprj = args[0]\nos.chdir(hgprj)\n\nif state:\n    if os.path.exists(state):\n        print 'State does exist, reading'\n        f = open(state, 'r')\n        hgvers = pickle.load(f)\n    else:\n        print 'State does not exist, first run'\n\ntip = os.popen('hg tip | head -1 | cut -f 2 -d :').read().strip()\nprint 'tip is', tip\n\n# Calculate the branches\nprint 'analysing the branches...'\nhgchildren[\"0\"] = ()\nhgbranch[\"0\"] = \"master\"\nfor cset in range(1, int(tip) + 1):\n    hgchildren[str(cset)] = ()\n    prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()\n    if len(prnts) > 0:\n        parent = prnts[0].strip()\n    else:\n        parent = str(cset - 1)\n    hgchildren[parent] += ( str(cset), )\n    if len(prnts) > 1:\n        mparent = prnts[1].strip()\n        hgchildren[mparent] += ( str(cset), )\n    else:\n        mparent = None\n\n    if mparent:\n        # For merge changesets, take either one, preferably the 'master' branch\n        if hgbranch[mparent] == 'master':\n            hgbranch[str(cset)] = 'master'\n        else:\n            hgbranch[str(cset)] = hgbranch[parent]\n    else:\n        # Normal changesets\n        # For first children, take the parent branch, for the others create a new branch\n        if hgchildren[parent][0] == str(cset):\n            hgbranch[str(cset)] = hgbranch[parent]\n        else:\n            hgbranch[str(cset)] = \"branch-\" + str(cset)\n\nif not hgvers.has_key(\"0\"):\n    print 'creating repository'\n    os.system('git-init-db')\n\n# loop through every hg changeset\nfor cset in range(int(tip) + 1):\n\n    # incremental, already seen\n    if hgvers.has_key(str(cset)):\n        continue\n\n    # get info\n    prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()\n    if len(prnts) > 0:\n        parent = prnts[0].strip()\n    else:\n        parent = str(cset - 1)\n    if len(prnts) > 1:\n        mparent = prnts[1].strip()\n    else:\n        mparent = None\n\n    (fdcomment, filecomment) = tempfile.mkstemp()\n    csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v ^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v ^description: | grep -v ^tag:' % cset).read().strip()\n    os.write(fdcomment, csetcomment)\n    os.close(fdcomment)\n\n    date = os.popen('hg log -r %d | grep ^date: | cut -f 2- -d :' % cset).read().strip()\n\n    tag = os.popen('hg log -r %d | grep ^tag: | cut -f 2- -d :' % cset).read().strip()\n\n    user = os.popen('hg log -r %d | grep ^user: | cut -f 2- -d :' % cset).read().strip()\n\n    print '-----------------------------------------'\n    print 'cset:', cset\n    print 'branch:', hgbranch[str(cset)]\n    print 'user:', user\n    print 'date:', date\n    print 'comment:', csetcomment\n    print 'parent:', parent\n    if mparent:\n        print 'mparent:', mparent\n    if tag:\n        print 'tag:', tag\n    print '-----------------------------------------'\n\n    # checkout the parent if necessary\n    if cset != 0:\n        if hgbranch[str(cset)] == \"branch-\" + str(cset):\n            print 'creating new branch', hgbranch[str(cset)]\n            os.system('git-checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))\n        else:\n            print 'checking out branch', hgbranch[str(cset)]\n            os.system('git-checkout %s' % hgbranch[str(cset)])\n\n    # merge\n    if mparent:\n        if hgbranch[parent] == hgbranch[str(cset)]:\n            otherbranch = hgbranch[mparent]\n        else:\n            otherbranch = hgbranch[parent]\n        print 'merging', otherbranch, 'into', hgbranch[str(cset)]\n        os.system(getgitenv(user, date) + 'git-merge --no-commit -s ours \"\" %s %s' % (hgbranch[str(cset)], otherbranch))\n\n    # remove everything except .git and .hg directories\n    os.system('find . \\( -path \"./.hg\" -o -path \"./.git\" \\) -prune -o ! -name \".\" -print | xargs rm -rf')\n\n    # repopulate with checkouted files\n    os.system('hg update -C %d' % cset)\n\n    # add new files\n    os.system('git-ls-files -x .hg --others | git-update-index --add --stdin')\n    # delete removed files\n    os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin')\n\n    # commit\n    os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment)\n    os.unlink(filecomment)\n\n    # tag\n    if tag and tag != 'tip':\n        os.system(getgitenv(user, date) + 'git-tag %s' % tag)\n\n    # delete branch if not used anymore...\n    if mparent and len(hgchildren[str(cset)]):\n        print \"Deleting unused branch:\", otherbranch\n        os.system('git-branch -d %s' % otherbranch)\n\n    # retrieve and record the version\n    vvv = os.popen('git-show | head -1').read()\n    vvv = vvv[vvv.index(' ') + 1 : ].strip()\n    print 'record', cset, '->', vvv\n    hgvers[str(cset)] = vvv\n\nos.system('git-repack -a -d')\n\n# write the state for incrementals\nif state:\n    print 'Writing state'\n    f = open(state, 'w')\n    pickle.dump(hgvers, f)\n\n# vim: et ts=8 sw=4 sts=4\n\n-- \nStelian Pop <stelian@popies.net>\n"},{"id":"31577","messageId":"eo8ngk$ja$1@sea.gmane.org","threadId":"6340","inReplyTo":"1168537766.22649.19.camel@localhost.localdomain","subject":"Re: Mercurial to git converter.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-12T19:28:37Z","receivedAt":"2007-01-12T19:28:37Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Stelian Pop wrote:\n\n> I switched recently from Mercurial to git for some of my projects, and\n> needed a conversion tool with branch support (unlike tailor). I didn't\n> find any, so I ended up writing my own one. \n> \n> hg-to-git.py can probably be greatly improved (it's a rather crude\n> combination of shell and python) but it does already work quite well for\n> me. Features:\n>       - supports incremental conversion (keep a git repo in sync \n>         with a hg repository)\n>       - supports hg branches\n>       - converts hg tags \n> \n> Feel free to use it, improve it or even include it in mainline git (like\n> git-svn and friends).\n\nThanks. Added to\n  http://git.or.cz/gitwiki/InterfacesFrontendsAndTools#hg-to-git\n\n(Perhaps you should send it as git patch adding file contrib/hg-to-git.py\nor contrib/git-hgimport/hg-to-git.py ?)\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"31582","messageId":"1168632860.29218.5.camel@voyager.dsnet","threadId":"6340","inReplyTo":"eo8ngk$ja$1@sea.gmane.org","subject":"Re: Mercurial to git converter.","fromName":"Stelian Pop","fromEmail":"stelian@popies.net","sentAt":"2007-01-12T20:14:20Z","receivedAt":"2007-01-12T20:14:20Z","isPatch":false,"sender":{"key":"stelian@popies.net","avatar":"https://gravatar.com/avatar/c99e49c3b3d851deb85e4557b368712c890a09985211f183bd151f8ab6357c03?d=mp&s=160"},"body":"Le vendredi 12 janvier 2007 à 20:28 +0100, Jakub Narebski a écrit :\n> Stelian Pop wrote:\n> \n> > I switched recently from Mercurial to git for some of my projects, and\n> > needed a conversion tool with branch support (unlike tailor). I didn't\n> > find any, so I ended up writing my own one. \n> > \n> > hg-to-git.py can probably be greatly improved (it's a rather crude\n> > combination of shell and python) but it does already work quite well for\n> > me. Features:\n> >       - supports incremental conversion (keep a git repo in sync \n> >         with a hg repository)\n> >       - supports hg branches\n> >       - converts hg tags \n> > \n> > Feel free to use it, improve it or even include it in mainline git (like\n> > git-svn and friends).\n> \n> Thanks. Added to\n>   http://git.or.cz/gitwiki/InterfacesFrontendsAndTools#hg-to-git\n\nThanks.\n\n> (Perhaps you should send it as git patch adding file contrib/hg-to-git.py\n> or contrib/git-hgimport/hg-to-git.py ?)\n\nSure, I can do that (patch against 'master', right ?)\n\nDo I put this in contrib/ as you suggest or directly in / (where friends\nlike git-p4import.py live) ? What is most likely to be accepted ?\n\nStelian.\n-- \nStelian Pop <stelian@popies.net>\n"},{"id":"31597","messageId":"1168639023.13640.2.camel@localhost.localdomain","threadId":"6340","inReplyTo":"1168632860.29218.5.camel@voyager.dsnet","subject":"[PATCH] Add hg-to-git conversion utility.","fromName":"Stelian Pop","fromEmail":"stelian@popies.net","sentAt":"2007-01-12T21:57:03Z","receivedAt":"2007-01-12T21:57:03Z","isPatch":true,"sender":{"key":"stelian@popies.net","avatar":"https://gravatar.com/avatar/c99e49c3b3d851deb85e4557b368712c890a09985211f183bd151f8ab6357c03?d=mp&s=160"},"body":"hg-to-git.py  is able to convert a Mercurial repository into a git one,\nand preserves the branches in the process (unlike tailor)\n\nhg-to-git.py can probably be greatly improved (it's a rather crude\ncombination of shell and python) but it does already work quite well for\nme. Features:\n\t- supports incremental conversion\n\t  (for keeping a git repo in sync with a hg one)\n\t- supports hg branches\n\t- converts hg tags\n\nSigned-off-by: Stelian Pop <stelian@popies.net>\n---\n Documentation/git-tools.txt     |    7 +\n contrib/hg-to-git/hg-to-git.py  |  233 +++++++++++++++++++++++++++++++++++++++\n contrib/hg-to-git/hg-to-git.txt |   22 ++++\n 3 files changed, 262 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-tools.txt b/Documentation/git-tools.txt\nindex 0914cbb..4d245d5 100644\n--- a/Documentation/git-tools.txt\n+++ b/Documentation/git-tools.txt\n@@ -68,6 +68,13 @@ History Viewers\n Foreign SCM interface\n ---------------------\n \n+   - *hg-to-git* (contrib/)\n+\n+   hg-to-git converts a Mercurial repository into a git one, and\n+   preserves the full branch history in the process. hg-to-git can\n+   also be used in an incremental way to keep the git repository\n+   in sync with the master Mercurial repository.\n+\n    - *git-svn* (contrib/)\n \n    git-svn is a simple conduit for changesets between a single Subversion\ndiff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py\nnew file mode 100755\nindex 0000000..37337ff\n--- /dev/null\n+++ b/contrib/hg-to-git/hg-to-git.py\n@@ -0,0 +1,233 @@\n+#! /usr/bin/python\n+\n+\"\"\" hg-to-svn.py - A Mercurial to GIT converter\n+\n+    Copyright (C)2007 Stelian Pop <stelian@popies.net>\n+\n+    This program is free software; you can redistribute it and/or modify\n+    it under the terms of the GNU General Public License as published by\n+    the Free Software Foundation; either version 2, or (at your option)\n+    any later version.\n+\n+    This program is distributed in the hope that it will be useful,\n+    but WITHOUT ANY WARRANTY; without even the implied warranty of\n+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+    GNU General Public License for more details.\n+\n+    You should have received a copy of the GNU General Public License\n+    along with this program; if not, write to the Free Software\n+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n+\"\"\"\n+\n+import os, os.path, sys\n+import tempfile, popen2, pickle, getopt\n+import re\n+\n+# Maps hg version -> git version\n+hgvers = {}\n+# List of children for each hg revision\n+hgchildren = {}\n+# Current branch for each hg revision\n+hgbranch = {}\n+\n+#------------------------------------------------------------------------------\n+\n+def usage():\n+\n+        print \"\"\"\\\n+%s: [OPTIONS] <hgprj>\n+\n+options:\n+    -s, --gitstate=FILE: name of the state to be saved/read\n+                         for incrementals\n+\n+required:\n+    hgprj:  name of the HG project to import (directory)\n+\"\"\" % sys.argv[0]\n+\n+#------------------------------------------------------------------------------\n+\n+def getgitenv(user, date):\n+    env = ''\n+    elems = re.compile('(.*?)\\s+<(.*)>').match(user)\n+    if elems:\n+        env += 'export GIT_AUTHOR_NAME=\"%s\" ;' % elems.group(1)\n+        env += 'export GIT_COMMITER_NAME=\"%s\" ;' % elems.group(1)\n+        env += 'export GIT_AUTHOR_EMAIL=\"%s\" ;' % elems.group(2)\n+        env += 'export GIT_COMMITER_EMAIL=\"%s\" ;' % elems.group(2)\n+    else:\n+        env += 'export GIT_AUTHOR_NAME=\"%s\" ;' % user\n+        env += 'export GIT_COMMITER_NAME=\"%s\" ;' % user\n+        env += 'export GIT_AUTHOR_EMAIL= ;'\n+        env += 'export GIT_COMMITER_EMAIL= ;'\n+\n+    env += 'export GIT_AUTHOR_DATE=\"%s\" ;' % date\n+    env += 'export GIT_COMMITTER_DATE=\"%s\" ;' % date\n+    return env\n+\n+#------------------------------------------------------------------------------\n+\n+state = ''\n+\n+try:\n+    opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir='])\n+    for o, a in opts:\n+        if o in ('-s', '--gitstate'):\n+            state = a\n+            state = os.path.abspath(state)\n+\n+    if len(args) != 1:\n+        raise('params')\n+except:\n+    usage()\n+    sys.exit(1)\n+\n+hgprj = args[0]\n+os.chdir(hgprj)\n+\n+if state:\n+    if os.path.exists(state):\n+        print 'State does exist, reading'\n+        f = open(state, 'r')\n+        hgvers = pickle.load(f)\n+    else:\n+        print 'State does not exist, first run'\n+\n+tip = os.popen('hg tip | head -1 | cut -f 2 -d :').read().strip()\n+print 'tip is', tip\n+\n+# Calculate the branches\n+print 'analysing the branches...'\n+hgchildren[\"0\"] = ()\n+hgbranch[\"0\"] = \"master\"\n+for cset in range(1, int(tip) + 1):\n+    hgchildren[str(cset)] = ()\n+    prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()\n+    if len(prnts) > 0:\n+        parent = prnts[0].strip()\n+    else:\n+        parent = str(cset - 1)\n+    hgchildren[parent] += ( str(cset), )\n+    if len(prnts) > 1:\n+        mparent = prnts[1].strip()\n+        hgchildren[mparent] += ( str(cset), )\n+    else:\n+        mparent = None\n+\n+    if mparent:\n+        # For merge changesets, take either one, preferably the 'master' branch\n+        if hgbranch[mparent] == 'master':\n+            hgbranch[str(cset)] = 'master'\n+        else:\n+            hgbranch[str(cset)] = hgbranch[parent]\n+    else:\n+        # Normal changesets\n+        # For first children, take the parent branch, for the others create a new branch\n+        if hgchildren[parent][0] == str(cset):\n+            hgbranch[str(cset)] = hgbranch[parent]\n+        else:\n+            hgbranch[str(cset)] = \"branch-\" + str(cset)\n+\n+if not hgvers.has_key(\"0\"):\n+    print 'creating repository'\n+    os.system('git-init-db')\n+\n+# loop through every hg changeset\n+for cset in range(int(tip) + 1):\n+\n+    # incremental, already seen\n+    if hgvers.has_key(str(cset)):\n+        continue\n+\n+    # get info\n+    prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()\n+    if len(prnts) > 0:\n+        parent = prnts[0].strip()\n+    else:\n+        parent = str(cset - 1)\n+    if len(prnts) > 1:\n+        mparent = prnts[1].strip()\n+    else:\n+        mparent = None\n+\n+    (fdcomment, filecomment) = tempfile.mkstemp()\n+    csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v ^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v ^description: | grep -v ^tag:' % cset).read().strip()\n+    os.write(fdcomment, csetcomment)\n+    os.close(fdcomment)\n+\n+    date = os.popen('hg log -r %d | grep ^date: | cut -f 2- -d :' % cset).read().strip()\n+\n+    tag = os.popen('hg log -r %d | grep ^tag: | cut -f 2- -d :' % cset).read().strip()\n+\n+    user = os.popen('hg log -r %d | grep ^user: | cut -f 2- -d :' % cset).read().strip()\n+\n+    print '-----------------------------------------'\n+    print 'cset:', cset\n+    print 'branch:', hgbranch[str(cset)]\n+    print 'user:', user\n+    print 'date:', date\n+    print 'comment:', csetcomment\n+    print 'parent:', parent\n+    if mparent:\n+        print 'mparent:', mparent\n+    if tag:\n+        print 'tag:', tag\n+    print '-----------------------------------------'\n+\n+    # checkout the parent if necessary\n+    if cset != 0:\n+        if hgbranch[str(cset)] == \"branch-\" + str(cset):\n+            print 'creating new branch', hgbranch[str(cset)]\n+            os.system('git-checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))\n+        else:\n+            print 'checking out branch', hgbranch[str(cset)]\n+            os.system('git-checkout %s' % hgbranch[str(cset)])\n+\n+    # merge\n+    if mparent:\n+        if hgbranch[parent] == hgbranch[str(cset)]:\n+            otherbranch = hgbranch[mparent]\n+        else:\n+            otherbranch = hgbranch[parent]\n+        print 'merging', otherbranch, 'into', hgbranch[str(cset)]\n+        os.system(getgitenv(user, date) + 'git-merge --no-commit -s ours \"\" %s %s' % (hgbranch[str(cset)], otherbranch))\n+\n+    # remove everything except .git and .hg directories\n+    os.system('find . \\( -path \"./.hg\" -o -path \"./.git\" \\) -prune -o ! -name \".\" -print | xargs rm -rf')\n+\n+    # repopulate with checkouted files\n+    os.system('hg update -C %d' % cset)\n+\n+    # add new files\n+    os.system('git-ls-files -x .hg --others | git-update-index --add --stdin')\n+    # delete removed files\n+    os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin')\n+\n+    # commit\n+    os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment)\n+    os.unlink(filecomment)\n+\n+    # tag\n+    if tag and tag != 'tip':\n+        os.system(getgitenv(user, date) + 'git-tag %s' % tag)\n+\n+    # delete branch if not used anymore...\n+    if mparent and len(hgchildren[str(cset)]):\n+        print \"Deleting unused branch:\", otherbranch\n+        os.system('git-branch -d %s' % otherbranch)\n+\n+    # retrieve and record the version\n+    vvv = os.popen('git-show | head -1').read()\n+    vvv = vvv[vvv.index(' ') + 1 : ].strip()\n+    print 'record', cset, '->', vvv\n+    hgvers[str(cset)] = vvv\n+\n+os.system('git-repack -a -d')\n+\n+# write the state for incrementals\n+if state:\n+    print 'Writing state'\n+    f = open(state, 'w')\n+    pickle.dump(hgvers, f)\n+\n+# vim: et ts=8 sw=4 sts=4\ndiff --git a/contrib/hg-to-git/hg-to-git.txt b/contrib/hg-to-git/hg-to-git.txt\nnew file mode 100644\nindex 0000000..cf1c735\n--- /dev/null\n+++ b/contrib/hg-to-git/hg-to-git.txt\n@@ -0,0 +1,22 @@\n+hg-to-git.py is able to convert a Mercurial repository into a git one,\n+and preserves the branches in the process (unlike tailor)\n+\n+hg-to-git.py can probably be greatly improved (it's a rather crude\n+combination of shell and python) but it does already work quite well for\n+me. Features:\n+\t- supports incremental conversion\n+\t  (for keeping a git repo in sync with a hg one)\n+        - supports hg branches\n+        - converts hg tags \n+\n+Note that the git repository will be created 'in place' (at the same\n+location as the source hg repo). You will have to manually remove the\n+'.hg' directory after the conversion.\n+\n+Also note that the incremental conversion uses 'simple' hg changesets\n+identifiers (ordinals, as opposed to SHA-1 ids), and since these ids\n+are not stable across different repositories the hg-to-git.py state file\n+is forever tied to one hg repository.\n+\n+-- \n+Stelian Pop <stelian@popies.net>\n-- \n1.4.4.4\n"},{"id":"31602","messageId":"20070112221525.GA20956@spearce.org","threadId":"6340","inReplyTo":"1168639023.13640.2.camel@localhost.localdomain","subject":"Re: [PATCH] Add hg-to-git conversion utility.","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-12T22:15:25Z","receivedAt":"2007-01-12T22:15:25Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Stelian Pop <stelian@popies.net> wrote:\n> hg-to-git.py  is able to convert a Mercurial repository into a git one,\n> and preserves the branches in the process (unlike tailor)\n\nCool!\n\nYou've already written the converter, but you may want to look at\nusing git-fast-import as the backend interface to Git.  I think\nyour code may come out shorter, be clearer, and will run faster.\n\nFor example Simon Schubert's cvs -> git converter was written in\none night (last night) in Ruby using git-fast-import as the backend\n(Simon already had the heavy lifting CVS portions done).  I peeked\nat his source and its quite readable, though maybe that's just the\nRuby talking.  ;-) Simon is apparently trying to import FreeBSD's\nCVS repository to Git.  A non-trivial task.\n\nChris Lee also has an svn -> git converter that uses gfi as its\nbackend. Its only 150 or so lines of Python and is also quite\nreadable.  Chris is apparently trying to import KDE to Git.\nAn equally non-trivial task.\n\nI think the common thread between the two implementations is\nthat there's less process forking and less manipulation of ugly\nenvironment variables.  So the import runs faster and its a little\neasier to organize the code.\n\n\nThe gitweb (and mirror URLs) for gfi is available here:\n\n  http://repo.or.cz/w/git/fastimport.git\n\nPost 1.5.0 I'm going to nudge Junio to get gfi added to the main\ngit.git repository.  (Right now I don't want to distract him from\nthe release.)\n\n-- \nShawn.\n"},{"id":"31606","messageId":"45A81328.9030109@popies.net","threadId":"6340","inReplyTo":"20070112221525.GA20956@spearce.org","subject":"Re: [PATCH] Add hg-to-git conversion utility.","fromName":"Stelian Pop","fromEmail":"stelian@popies.net","sentAt":"2007-01-12T23:00:56Z","receivedAt":"2007-01-12T23:00:56Z","isPatch":true,"sender":{"key":"stelian@popies.net","avatar":"https://gravatar.com/avatar/c99e49c3b3d851deb85e4557b368712c890a09985211f183bd151f8ab6357c03?d=mp&s=160"},"body":"Shawn O. Pearce a écrit :\n> Stelian Pop <stelian@popies.net> wrote:\n>> hg-to-git.py  is able to convert a Mercurial repository into a git one,\n>> and preserves the branches in the process (unlike tailor)\n> \n> Cool!\n> \n> You've already written the converter, but you may want to look at\n> using git-fast-import as the backend interface to Git.  I think\n> your code may come out shorter, be clearer, and will run faster.\n[...]\n> Post 1.5.0 I'm going to nudge Junio to get gfi added to the main\n> git.git repository.  (Right now I don't want to distract him from\n> the release.)\n\nRebasing hg-to-git.py on gfi will probably be a very good idea once gfi \nis in the tree.\n\nBut I wrote this since I had a need for it, and once my repositories \nconverted to git I'm not sure I'll need it again :) So don't count too \nmuch on me to do this rebasing :)\n\nBut who knows, on a cold rainy day...\n\n-- \nStelian Pop <stelian@popies.net>\n"},{"id":"31608","messageId":"7vzm8nq1lb.fsf@assigned-by-dhcp.cox.net","threadId":"6340","inReplyTo":"45A81328.9030109@popies.net","subject":"Re: [PATCH] Add hg-to-git conversion utility.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-12T23:14:56Z","receivedAt":"2007-01-12T23:14:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stelian Pop <stelian@popies.net> writes:\n\n> But I wrote this since I had a need for it, and once my repositories\n> converted to git I'm not sure I'll need it again :)\n\nIt is not a good strategy to say upfront that you are not going\nto maintain it, when you are submitting something for inclusion.\n"},{"id":"31610","messageId":"45A8173E.5060804@popies.net","threadId":"6340","inReplyTo":"7vzm8nq1lb.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Add hg-to-git conversion utility.","fromName":"Stelian Pop","fromEmail":"stelian@popies.net","sentAt":"2007-01-12T23:18:22Z","receivedAt":"2007-01-12T23:18:22Z","isPatch":true,"sender":{"key":"stelian@popies.net","avatar":"https://gravatar.com/avatar/c99e49c3b3d851deb85e4557b368712c890a09985211f183bd151f8ab6357c03?d=mp&s=160"},"body":"Junio C Hamano a écrit :\n> Stelian Pop <stelian@popies.net> writes:\n> \n>> But I wrote this since I had a need for it, and once my repositories\n>> converted to git I'm not sure I'll need it again :)\n> \n> It is not a good strategy to say upfront that you are not going\n> to maintain it, when you are submitting something for inclusion.\n\nHey, I didn't say I won't fix it if it breaks !\n\nI may not have the time and need to make it better than it already is \nthough.\n\n-- \nStelian Pop <stelian@popies.net>\n"},{"id":"79850","messageId":"loom.20080614T101342-542@post.gmane.org","threadId":"6340","inReplyTo":"1168537766.22649.19.camel@localhost.localdomain","subject":"Re: Mercurial to git converter.","fromName":"Nick Andrew","fromEmail":"nick@nick-andrew.net","sentAt":"2008-06-14T10:23:37Z","receivedAt":"2008-06-14T10:23:37Z","isPatch":false,"sender":{"key":"nick@nick-andrew.net","avatar":"https://gravatar.com/avatar/85f25a67ca6eaa4016ed374f6d07f3cd853c886aeb7e1507eb7dbc47b00082fe?d=mp&s=160"},"body":"Stelian Pop <stelian <at> popies.net> writes:\n\n> I switched recently from Mercurial to git for some of my projects, and\n> needed a conversion tool with branch support (unlike tailor). I didn't\n> find any, so I ended up writing my own one. \n> \n> hg-to-git.py can probably be greatly improved (it's a rather crude\n> combination of shell and python) but it does already work quite well for\n> me. [...]\n\nThanks for that. One small problem is that it does not get the changelog right \nif a commit is made on a local branch. It thinks the \"branch:\" line is part of \nthe changelog.\n\nHere's a patch to fix that:\n\ndiff -u -r1.1 hg-to-git.py\n--- hg-to-git.py\n+++ hg-to-git.py\n@@ -151,7 +151,7 @@\n         mparent = None\n\n     (fdcomment, filecomment) = tempfile.mkstemp()\n-    csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v \n^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v \n^description: | grep -v ^tag:' % cset).read().strip()\n+    csetcomment = os.popen('hg log -r %d -v | egrep -v \"^(changeset|parent|\nuser|date|files|description|tag|branch):\" ' % cset).read().strip()\n     os.write(fdcomment, csetcomment)\n     os.close(fdcomment)\n\n\nNick.\n"},{"id":"79861","messageId":"alpine.DEB.1.00.0806141648020.6439@racer","threadId":"6340","inReplyTo":"loom.20080614T101342-542@post.gmane.org","subject":"Re: Mercurial to git converter.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-14T15:53:04Z","receivedAt":"2008-06-14T15:53:04Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 14 Jun 2008, Nick Andrew wrote:\n\n> Stelian Pop <stelian <at> popies.net> writes:\n> \n> > I switched recently from Mercurial to git for some of my projects, and\n> > needed a conversion tool with branch support (unlike tailor). I didn't\n> > find any, so I ended up writing my own one. \n> > \n> > hg-to-git.py can probably be greatly improved (it's a rather crude \n> > combination of shell and python) but it does already work quite well \n> > for me. [...]\n> \n> Thanks for that. One small problem is that it does not get the changelog \n> right if a commit is made on a local branch. It thinks the \"branch:\" \n> line is part of the changelog.\n\nFWIW I switched my local mercurial2git conversion to the hg-fast-export \nsolution from \n\n\tgit://repo.or.cz/fast-export.git\n\nEven if it's documentation leaves almost completely to be desired, after \nfiguring out how to operate it by reading the source, it works pretty \nnicely, and fast, because it uses fast-import.\n\nOriginally, I did not try it because I thought it cannot operate in \nincremental mode, but it can.\n\nCiao,\nDscho\n"},{"id":"79862","messageId":"m3wsksotdq.fsf@localhost.localdomain","threadId":"6340","inReplyTo":"alpine.DEB.1.00.0806141648020.6439@racer","subject":"Re: Mercurial to git converter.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-06-14T16:48:14Z","receivedAt":"2008-06-14T16:48:14Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> On Sat, 14 Jun 2008, Nick Andrew wrote: \n>> Stelian Pop <stelian <at> popies.net> writes:\n>> \n>>> hg-to-git.py can probably be greatly improved (it's a rather crude \n>>> combination of shell and python) but it does already work quite well \n>>> for me. [...]\n[...]\n \n> FWIW I switched my local mercurial2git conversion to the hg-fast-export \n> solution from \n> \n> \tgit://repo.or.cz/fast-export.git\n>\n[...] \n> Originally, I did not try it because I thought it cannot operate in \n> incremental mode, but it can.\n\nIt can, or it can recently thanks to --export-marks / --import-marks\nwork?\n\n\nIt is nice that SCM-to-SCM converters seems to standarize on using\nfast-import language as intermediary.  \n\nIIRC fast-import by Shawn O. Pearce started as fast backend for first\ncvs2git (unfortunately fork and not extension of cvs2svn) by Jon Smirl\n(?) around time when Mozilla looked for SCM to move to from CVS.  (In\nthe end they choose Mercurial, mainly because of better Windows\nsupport, and I think also better Windows GUI; I wonder how it works\nfor them now?).\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"79998","messageId":"20080616022435.GS11793@spearce.org","threadId":"6340","inReplyTo":"m3wsksotdq.fsf@localhost.localdomain","subject":"Re: Mercurial to git converter.","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-06-16T02:24:35Z","receivedAt":"2008-06-16T02:24:35Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> > FWIW I switched my local mercurial2git conversion to the hg-fast-export \n> > solution from \n> > \n> > \tgit://repo.or.cz/fast-export.git\n> >\n> [...] \n> > Originally, I did not try it because I thought it cannot operate in \n> > incremental mode, but it can.\n> \n> It can, or it can recently thanks to --export-marks / --import-marks\n> work?\n\nThe recent --export-marks/import-marks work you speak of was adding\nthese options to git-fast-export, making it easier to perform an\nincremental _export_ from Git to another SCM.  I believe Dscho\nis speaking about an Hg->Git conversion, which would rely on\ngit-fast-import's previously existing --export-marks/import-marks\nfeature.\n \n> It is nice that SCM-to-SCM converters seems to standarize on using\n> fast-import language as intermediary.  \n\nTurns out the little format we knocked together for this is able\nto represent most conversions for SCMs.  I'm just happy users are\neasily able to convert into Git.\n\n> IIRC fast-import by Shawn O. Pearce started as fast backend for first\n> cvs2git (unfortunately fork and not extension of cvs2svn) by Jon Smirl\n> (?) around time when Mozilla looked for SCM to move to from CVS.  (In\n> the end they choose Mercurial, mainly because of better Windows\n> support, and I think also better Windows GUI; I wonder how it works\n> for them now?).\n\nYour history is correct.  The current version of cvs2svn also\nsupports cvs2git, and may also support cvs2bzr by using the\ngit-fast-import backend and bzr-fast-import as the parser.\n\n-- \nShawn.\n"},{"id":"80006","messageId":"20080616082106.GA10583@tull.net","threadId":"6340","inReplyTo":"alpine.DEB.1.00.0806141648020.6439@racer","subject":"Re: Mercurial to git converter.","fromName":"Nick Andrew","fromEmail":"nick@nick-andrew.net","sentAt":"2008-06-16T08:21:07Z","receivedAt":"2008-06-16T08:21:07Z","isPatch":false,"sender":{"key":"nick@nick-andrew.net","avatar":"https://gravatar.com/avatar/85f25a67ca6eaa4016ed374f6d07f3cd853c886aeb7e1507eb7dbc47b00082fe?d=mp&s=160"},"body":"On Sat, Jun 14, 2008 at 04:53:04PM +0100, Johannes Schindelin wrote:\n> FWIW I switched my local mercurial2git conversion to the hg-fast-export \n> solution from \n> \n> \tgit://repo.or.cz/fast-export.git\n\nYep.\n\nIn my case I needed to convert only a single repository with only a\nfew tens of commits, so the slow script was adequate for my use\n... except for that problem with the changelog messages - which was\ntrivially fixed, so I posted to the list only to document the fix.\n\nNick.\n-- \nPGP Key ID = 0x418487E7                      http://www.nick-andrew.net/\nPGP Key fingerprint = B3ED 6894 8E49 1770 C24A  67E3 6266 6EB9 4184 87E7\n"},{"id":"80007","messageId":"20080616082633.GA10752@glandium.org","threadId":"6340","inReplyTo":"alpine.DEB.1.00.0806141648020.6439@racer","subject":"Re: Mercurial to git converter.","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2008-06-16T08:26:33Z","receivedAt":"2008-06-16T08:26:33Z","isPatch":false,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Sat, Jun 14, 2008 at 04:53:04PM +0100, Johannes Schindelin wrote:\n> Hi,\n> \n> On Sat, 14 Jun 2008, Nick Andrew wrote:\n> \n> > Stelian Pop <stelian <at> popies.net> writes:\n> > \n> > > I switched recently from Mercurial to git for some of my projects, and\n> > > needed a conversion tool with branch support (unlike tailor). I didn't\n> > > find any, so I ended up writing my own one. \n> > > \n> > > hg-to-git.py can probably be greatly improved (it's a rather crude \n> > > combination of shell and python) but it does already work quite well \n> > > for me. [...]\n> > \n> > Thanks for that. One small problem is that it does not get the changelog \n> > right if a commit is made on a local branch. It thinks the \"branch:\" \n> > line is part of the changelog.\n> \n> FWIW I switched my local mercurial2git conversion to the hg-fast-export \n> solution from \n> \n> \tgit://repo.or.cz/fast-export.git\n\nWould you happen to have a clonable mirror or this ? repo.or.cz doesn't\nrespond.\n\nMike\n"},{"id":"80117","messageId":"alpine.DEB.1.00.0806171007000.6439@racer","threadId":"6340","inReplyTo":"20080616082633.GA10752@glandium.org","subject":"Re: Mercurial to git converter.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-17T09:12:18Z","receivedAt":"2008-06-17T09:12:18Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 16 Jun 2008, Mike Hommey wrote:\n\n> Would you happen to have a clonable mirror or this ? repo.or.cz doesn't \n> respond.\n\nI'm sorry, I was travelling all day yesterday.  repo.or.cz is back, but I \nhave a mirror for fast-export.git.  However, a few months ago, mugwump \nand me set up a mirror that should work (but AFAICT is http:// only): \nhttp://git.utsl.gen.nz/mirror/repo.or.cz/.\n\nCiao,\nDscho\n"}]}