changeset 4513:ac2fe196ac9b

Turns convert.py into a real extension
author Edouard Gomez <ed.gomez@free.fr>
date Fri, 25 May 2007 00:56:48 +0200
parents 91709ba3cc88
children ec889780f28b
files hgext/convert/__init__.py
diffstat 1 files changed, 70 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/convert/__init__.py	Wed Jun 06 19:49:47 2007 +0200
+++ b/hgext/convert/__init__.py	Fri May 25 00:56:48 2007 +0200
@@ -1,52 +1,24 @@
-#!/usr/bin/env python
-#
-# This is a generalized framework for converting between SCM
-# repository formats.
-#
-# To use, run:
-#
-# convert-repo <source> [<dest> [<mapfile>]]
-#
-# Currently accepted source formats: git, cvs
-# Currently accepted destination formats: hg
+# convert.py Foreign SCM converter
 #
-# 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)
+# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
 #
-# 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.
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
 
 import sys, os, zlib, sha, time, re, locale, socket
-os.environ["HGENCODING"] = "utf-8"
-from mercurial import hg, ui, util, fancyopts
+from mercurial import hg, ui, util, commands
 
-class Abort(Exception): pass
+commands.norepo += " convert"
+
 class NoRepo(Exception): pass
 
 class commit(object):
     def __init__(self, **parts):
         for x in "author date desc parents".split():
             if not x in parts:
-                abort("commit missing field %s\n" % x)
+                raise util.Abort("commit missing field %s\n" % x)
         self.__dict__.update(parts)
 
-quiet = 0
-def status(msg):
-    if not quiet: sys.stdout.write(str(msg))
-
-def warn(msg):
-    sys.stderr.write(str(msg))
-
-def abort(msg):
-    raise Abort(msg)
-
 def recode(s):
     try:
         return s.decode("utf-8").encode("utf-8")
@@ -59,7 +31,7 @@
 class converter_source(object):
     """Conversion source interface"""
 
-    def __init__(self, path):
+    def __init__(self, ui, path):
         """Initialize conversion source (or raise NoRepo("message")
         exception if path is not a valid repository)"""
         raise NotImplementedError()
@@ -94,7 +66,7 @@
 class converter_sink(object):
     """Conversion sink (target) interface"""
 
-    def __init__(self, path):
+    def __init__(self, ui, path):
         """Initialize conversion sink (or raise NoRepo("message")
         exception if path is not a valid repository)"""
         raise NotImplementedError()
@@ -139,8 +111,9 @@
 
 # CVS conversion code inspired by hg-cvs-import and git-cvsimport
 class convert_cvs(converter_source):
-    def __init__(self, path):
+    def __init__(self, ui, path):
         self.path = path
+        self.ui = ui
         cvs = os.path.join(path, "CVS")
         if not os.path.exists(cvs):
             raise NoRepo("couldn't open CVS repo %s" % path)
@@ -223,7 +196,7 @@
         user, host = None, None
         cmd = ['cvs', 'server']
 
-        status("connecting to %s\n" % root)
+        self.ui.status("connecting to %s\n" % root)
 
         if root.startswith(":pserver:"):
             root = root[9:]
@@ -296,7 +269,7 @@
         self.writep.flush()
         r = self.readp.readline()
         if not r.startswith("Valid-requests"):
-            abort("server sucks\n")
+            raise util.Abort("server sucks\n")
         if "UseUnchanged" in r:
             self.writep.write("UseUnchanged\n")
             self.writep.flush()
@@ -336,14 +309,14 @@
                 if line == "ok\n":
                     return (data, "x" in mode and "x" or "")
                 elif line.startswith("E "):
-                    warn("cvs server: %s\n" % line[2:])
+                    self.ui.warn("cvs server: %s\n" % line[2:])
                 elif line.startswith("Remove"):
                     l = self.readp.readline()
                     l = self.readp.readline()
                     if l != "ok\n":
-                        abort("unknown CVS response: %s\n" % l)
+                        raise util.Abort("unknown CVS response: %s\n" % l)
                 else:
-                    abort("unknown CVS response: %s\n" % line)
+                    raise util.Abort("unknown CVS response: %s\n" % line)
 
     def getfile(self, file, rev):
         data, mode = self._getfile(file, rev)
@@ -370,10 +343,11 @@
         return self.tags
 
 class convert_git(converter_source):
-    def __init__(self, path):
+    def __init__(self, ui, path):
         if os.path.isdir(path + "/.git"):
             path += "/.git"
         self.path = path
+        self.ui = ui
         if not os.path.exists(path + "/objects"):
             raise NoRepo("couldn't open GIT repo %s" % path)
 
@@ -456,11 +430,11 @@
         return tags
 
 class convert_mercurial(converter_sink):
-    def __init__(self, path):
+    def __init__(self, ui, path):
         self.path = path
-        u = ui.ui()
+        self.ui = ui
         try:
-            self.repo = hg.repository(u, path)
+            self.repo = hg.repository(self.ui, path)
         except:
             raise NoRepo("could open hg repo %s" % path)
 
@@ -530,7 +504,7 @@
         newlines.sort()
 
         if newlines != oldlines:
-            status("updating tags\n")
+            self.ui.status("updating tags\n")
             f = self.repo.wfile(".hgtags", "w")
             f.write("".join(newlines))
             f.close()
@@ -542,21 +516,22 @@
 
 converters = [convert_cvs, convert_git, convert_mercurial]
 
-def converter(path):
+def converter(ui, path):
     if not os.path.isdir(path):
-        abort("%s: not a directory\n" % path)
+        raise util.Abort("%s: not a directory\n" % path)
     for c in converters:
         try:
-            return c(path)
+            return c(ui, path)
         except NoRepo:
             pass
-    abort("%s: unknown repository type\n" % path)
+    raise util.Abort("%s: unknown repository type\n" % path)
 
 class convert(object):
-    def __init__(self, source, dest, mapfile, opts):
+    def __init__(self, ui, source, dest, mapfile, opts):
 
         self.source = source
         self.dest = dest
+        self.ui = ui
         self.mapfile = mapfile
         self.opts = opts
         self.commitcache = {}
@@ -627,7 +602,7 @@
                     for c in children[n]:
                         visit.insert(0, c)
 
-        if opts.get('datesort'):
+        if self.opts.get('datesort'):
             depth = {}
             for n in s:
                 depth[n] = 0
@@ -660,21 +635,21 @@
         file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev]))
 
     def convert(self):
-        status("scanning source...\n")
+        self.ui.status("scanning source...\n")
         heads = self.source.getheads()
         parents = self.walktree(heads)
-        status("sorting...\n")
+        self.ui.status("sorting...\n")
         t = self.toposort(parents)
         num = len(t)
         c = None
 
-        status("converting...\n")
+        self.ui.status("converting...\n")
         for c in t:
             num -= 1
             desc = self.commitcache[c].desc
             if "\n" in desc:
                 desc = desc.splitlines()[0]
-            status("%d %s\n" % (num, desc))
+            self.ui.status("%d %s\n" % (num, desc))
             self.copy(c)
 
         tags = self.source.gettags()
@@ -691,20 +666,43 @@
             if nrev:
                 file(self.mapfile, "a").write("%s %s\n" % (c, nrev))
 
-def command(src, dest=None, mapfile=None, **opts):
-    srcc = converter(src)
+def _convert(ui, src, dest=None, mapfile=None, **opts):
+    '''Convert a foreign SCM repository to a Mercurial one.
+
+    Accepted source formats:
+    - GIT
+    - CVS
+
+    Accepted destination formats:
+    - Mercurial
+
+    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)
+
+    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.
+    '''
+
+    srcc = converter(ui, src)
     if not hasattr(srcc, "getcommit"):
-        abort("%s: can't read from this repo type\n" % src)
+        raise util.Abort("%s: can't read from this repo type\n" % src)
 
     if not dest:
         dest = src + "-hg"
-        status("assuming destination %s\n" % dest)
+        ui.status("assuming destination %s\n" % dest)
         if not os.path.isdir(dest):
-            status("creating repository %s\n" % dest)
+            ui.status("creating repository %s\n" % dest)
             os.system("hg init " + dest)
-    destc = converter(dest)
+    destc = converter(ui, dest)
     if not hasattr(destc, "putcommit"):
-        abort("%s: can't write to this repo type\n" % src)
+        raise util.Abort("%s: can't write to this repo type\n" % src)
 
     if not mapfile:
         try:
@@ -712,20 +710,11 @@
         except:
             mapfile = os.path.join(destc, "map")
 
-    c = convert(srcc, destc, mapfile, opts)
+    c = convert(ui, srcc, destc, mapfile, opts)
     c.convert()
 
-options = [('q', 'quiet', None, 'suppress output'),
-           ('', 'datesort', None, 'try to sort changesets by date')]
-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")
+cmdtable = {
+    "convert": (_convert,
+                [('', 'datesort', None, 'try to sort changesets by date')],
+                'hg convert [OPTIONS] <src> [dst [map]]'),
+}