merge with crew
authorBenoit Boissinot <benoit.boissinot@ens-lyon.org>
Fri, 22 Dec 2006 22:53:16 +0100
changeset 3952 32c1653b7dad
parent 3951 cb66641cdee3 (current diff)
parent 3949 6257030134f4 (diff)
child 3953 fad134931327
child 3958 a8eb050c6489
merge with crew
--- a/contrib/convert-repo	Fri Dec 22 22:51:39 2006 +0100
+++ b/contrib/convert-repo	Fri Dec 22 22:53:16 2006 +0100
@@ -3,29 +3,41 @@
 # This is a generalized framework for converting between SCM
 # repository formats.
 #
-# In its current form, it's hardcoded to convert incrementally between
-# git and Mercurial.
-#
 # To use, run:
 #
-# convert-repo <git-dir> <hg-dir> <mapfile>
+# convert-repo <source> [<dest> [<mapfile>]]
 #
-# (don't forget to create the <hg-dir> repository beforehand)
+# Currently accepted source formats: git
+# Currently accepted destination formats: hg
 #
-# The <mapfile> is a simple text file that maps a git commit hash to
-# the hash in Mercurial for that version, like so:
+# If destination isn't given, a new Mercurial repo named <src>-hg will
+# be created. If <mapfile> isn't given, it will be put in a default
+# location (<dest>/.hg/shamap by default)
 #
-# <git hash> <mercurial hash>
+# The <mapfile> is a simple text file that maps each source commit ID to
+# the destination ID for that revision, like so:
+#
+# <source ID> <destination ID>
 #
 # If the file doesn't exist, it's automatically created.  It's updated
 # on each commit copied, so convert-repo can be interrupted and can
 # be run repeatedly to copy new commits.
 
 import sys, os, zlib, sha, time
+os.environ["HGENCODING"] = "utf-8"
+from mercurial import hg, ui, util, fancyopts
 
-os.environ["HGENCODING"] = "utf-8"
+class Abort(Exception): pass
+
+quiet = 0
+def status(msg):
+    if not quiet: sys.stdout.write(str(msg))
 
-from mercurial import hg, ui, util
+def warn(msg):
+    sys.stderr.write(str(msg))
+
+def abort(msg):
+    raise Abort(msg)
 
 def recode(s):
     try:
@@ -38,7 +50,11 @@
 
 class convert_git:
     def __init__(self, path):
+        if os.path.isdir(path + "/.git"):
+            path += "/.git"
         self.path = path
+        if not os.path.exists(path + "/HEAD"):
+            raise TypeError("couldn't open GIT repo %s" % path)
 
     def getheads(self):
         fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path)
@@ -110,7 +126,13 @@
     def __init__(self, path):
         self.path = path
         u = ui.ui()
-        self.repo = hg.repository(u, path)
+        try:
+            self.repo = hg.repository(u, path)
+        except:
+            raise TypeError("could open hg repo %s" % path)
+
+    def mapfile(self):
+        return os.path.join(self.path, ".hg", "shamap")
 
     def getheads(self):
         h = self.repo.changelog.heads()
@@ -170,7 +192,7 @@
         newlines.sort()
 
         if newlines != oldlines:
-            #print "updating tags"
+            status("updating tags\n")
             f = self.repo.wfile(".hgtags", "w")
             f.write("".join(newlines))
             f.close()
@@ -180,8 +202,21 @@
                                 date, self.repo.changelog.tip(), hg.nullid)
             return hg.hex(self.repo.changelog.tip())
 
+converters = [convert_git, convert_mercurial]
+
+def converter(path):
+    if not os.path.isdir(path):
+        abort("%s: not a directory\n" % path)
+    for c in converters:
+        try:
+            return c(path)
+        except TypeError:
+            pass
+    abort("%s: unknown repository type\n" % path)
+
 class convert:
     def __init__(self, source, dest, mapfile):
+
         self.source = source
         self.dest = dest
         self.mapfile = mapfile
@@ -272,17 +307,20 @@
         file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
 
     def convert(self):
+        status("scanning source...\n")
         heads = self.source.getheads()
         parents = self.walktree(heads)
+        status("sorting...\n")
         t = self.toposort(parents)
         t = [n for n in t if n not in self.map]
         num = len(t)
         c = None
 
+        status("converting...\n")
         for c in t:
             num -= 1
             desc = self.commitcache[c][3].splitlines()[0]
-            #print num, desc
+            status("%d %s\n" % (num, desc))
             self.copy(c)
 
         tags = self.source.gettags()
@@ -299,9 +337,40 @@
             if nrev:
                 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
 
-gitpath, hgpath, mapfile = sys.argv[1:]
-if os.path.isdir(gitpath + "/.git"):
-    gitpath += "/.git"
+def command(src, dest=None, mapfile=None, **opts):
+    srcc = converter(src)
+    if not hasattr(srcc, "getcommit"):
+        abort("%s: can't read from this repo type\n" % src)
+
+    if not dest:
+        dest = src + "-hg"
+        status("assuming destination %s\n" % dest)
+        if not os.path.isdir(dest):
+            status("creating repository %s\n" % dest)
+            os.system("hg init " + dest)
+    destc = converter(dest)
+    if not hasattr(destc, "putcommit"):
+        abort("%s: can't write to this repo type\n" % src)
 
-c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile)
-c.convert()
+    if not mapfile:
+        try:
+            mapfile = destc.mapfile()
+        except:
+            mapfile = os.path.join(destc, "map")
+
+    c = convert(srcc, destc, mapfile)
+    c.convert()
+
+options = [('q', 'quiet', None, 'suppress output')]
+opts = {}
+args = fancyopts.fancyopts(sys.argv[1:], options, opts)
+
+if opts['quiet']:
+    quiet = 1
+
+try:
+    command(*args, **opts)
+except Abort, inst:
+    warn(inst)
+except KeyboardInterrupt:
+    status("interrupted\n")