# HG changeset patch # User Matt Mackall # Date 1166653906 21600 # Node ID 0fab73b3f4538002f340c9167497c86b5b5b4eeb # Parent ac02810132ca20f02a88afd77a48bc33f60e09c6 convert-repo: add some smarts autodetect source and destination repo types autodetect read and write capabilities of converters default destination directory (-hg) default map file (/.hg/shamap) more verbose by default add a -q switch add IO functions diff --git a/contrib/convert-repo b/contrib/convert-repo --- a/contrib/convert-repo +++ b/contrib/convert-repo @@ -22,10 +22,20 @@ # 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 +48,11 @@ def recode(s): 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 +124,13 @@ class convert_mercurial: 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 +190,7 @@ class convert_mercurial: 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 +200,21 @@ class convert_mercurial: 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 +305,20 @@ class convert: 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 +335,40 @@ class convert: 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")