--- a/hgext/convert/hg.py
+++ b/hgext/convert/hg.py
@@ -1,20 +1,45 @@
# hg backend for convert extension
+# Note for hg->hg conversion: Old versions of Mercurial didn't trim
+# the whitespace from the ends of commit messages, but new versions
+# do. Changesets created by those older versions, then converted, may
+# thus have different hashes for changesets that are otherwise
+# identical.
+
+
import os, time
-from mercurial import hg
+from mercurial.i18n import _
+from mercurial.node import *
+from mercurial import hg, lock, revlog, util
-from common import NoRepo, converter_sink
+from common import NoRepo, commit, converter_source, converter_sink
-class convert_mercurial(converter_sink):
+class mercurial_sink(converter_sink):
def __init__(self, ui, path):
self.path = path
self.ui = ui
+ self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
+ self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
+ self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
+ self.lastbranch = None
try:
self.repo = hg.repository(self.ui, path)
except:
- raise NoRepo("could open hg repo %s" % path)
+ raise NoRepo("could not open hg repo %s as sink" % path)
+ self.lock = None
+ self.wlock = None
- def mapfile(self):
+ def before(self):
+ self.wlock = self.repo.wlock()
+ self.lock = self.repo.lock()
+ self.repo.dirstate.clear()
+
+ def after(self):
+ self.repo.dirstate.invalidate()
+ self.lock = None
+ self.wlock = None
+
+ def revmapfile(self):
return os.path.join(self.path, ".hg", "shamap")
def authorfile(self):
@@ -22,12 +47,15 @@ class convert_mercurial(converter_sink):
def getheads(self):
h = self.repo.changelog.heads()
- return [ hg.hex(x) for x in h ]
+ return [ hex(x) for x in h ]
def putfile(self, f, e, data):
self.repo.wwrite(f, data, e)
- if self.repo.dirstate.state(f) == '?':
- self.repo.dirstate.update([f], "a")
+ if f not in self.repo.dirstate:
+ self.repo.dirstate.normallookup(f)
+
+ def copyfile(self, source, dest):
+ self.repo.copy(source, dest)
def delfile(self, f):
try:
@@ -36,6 +64,30 @@ class convert_mercurial(converter_sink):
except:
pass
+ def setbranch(self, branch, pbranch, parents):
+ if (not self.clonebranches) or (branch == self.lastbranch):
+ return
+
+ self.lastbranch = branch
+ self.after()
+ if not branch:
+ branch = 'default'
+ if not pbranch:
+ pbranch = 'default'
+
+ branchpath = os.path.join(self.path, branch)
+ try:
+ self.repo = hg.repository(self.ui, branchpath)
+ except:
+ if not parents:
+ self.repo = hg.repository(self.ui, branchpath, create=True)
+ else:
+ self.ui.note(_('cloning branch %s to %s\n') % (pbranch, branch))
+ hg.clone(self.ui, os.path.join(self.path, pbranch),
+ branchpath, rev=parents, update=False,
+ stream=True)
+ self.repo = hg.repository(self.ui, branchpath)
+
def putcommit(self, files, parents, commit):
seen = {}
pl = []
@@ -51,16 +103,17 @@ class convert_mercurial(converter_sink):
text = commit.desc
extra = {}
- try:
- extra["branch"] = commit.branch
- except AttributeError:
- pass
+ if self.branchnames and commit.branch:
+ extra['branch'] = commit.branch
+ if commit.rev:
+ extra['convert_revision'] = commit.rev
while parents:
p1 = p2
p2 = parents.pop(0)
a = self.repo.rawcommit(files, text, commit.author, commit.date,
- hg.bin(p1), hg.bin(p2), extra=extra)
+ bin(p1), bin(p2), extra=extra)
+ self.repo.dirstate.clear()
text = "(octopus merge fixup)\n"
p2 = hg.hex(self.repo.changelog.tip())
@@ -89,6 +142,69 @@ class convert_mercurial(converter_sink):
f.close()
if not oldlines: self.repo.add([".hgtags"])
date = "%s 0" % int(time.mktime(time.gmtime()))
+ extra = {}
+ if self.tagsbranch != 'default':
+ extra['branch'] = self.tagsbranch
+ try:
+ tagparent = self.repo.changectx(self.tagsbranch).node()
+ except hg.RepoError, inst:
+ tagparent = nullid
self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
- date, self.repo.changelog.tip(), hg.nullid)
- return hg.hex(self.repo.changelog.tip())
+ date, tagparent, nullid)
+ return hex(self.repo.changelog.tip())
+
+class mercurial_source(converter_source):
+ def __init__(self, ui, path, rev=None):
+ converter_source.__init__(self, ui, path, rev)
+ self.repo = hg.repository(self.ui, path)
+ self.lastrev = None
+ self.lastctx = None
+
+ def changectx(self, rev):
+ if self.lastrev != rev:
+ self.lastctx = self.repo.changectx(rev)
+ self.lastrev = rev
+ return self.lastctx
+
+ def getheads(self):
+ if self.rev:
+ return [hex(self.repo.changectx(self.rev).node())]
+ else:
+ return [hex(node) for node in self.repo.heads()]
+
+ def getfile(self, name, rev):
+ try:
+ return self.changectx(rev).filectx(name).data()
+ except revlog.LookupError, err:
+ raise IOError(err)
+
+ def getmode(self, name, rev):
+ m = self.changectx(rev).manifest()
+ return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '')
+
+ def getchanges(self, rev):
+ ctx = self.changectx(rev)
+ m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
+ changes = [(name, rev) for name in m + a + r]
+ changes.sort()
+ return (changes, self.getcopies(ctx, m + a))
+
+ def getcopies(self, ctx, files):
+ copies = {}
+ for name in files:
+ try:
+ copies[name] = ctx.filectx(name).renamed()[0]
+ except TypeError:
+ pass
+ return copies
+
+ def getcommit(self, rev):
+ ctx = self.changectx(rev)
+ parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
+ return commit(author=ctx.user(), date=util.datestr(ctx.date()),
+ desc=ctx.description(), parents=parents,
+ branch=ctx.branch())
+
+ def gettags(self):
+ tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
+ return dict([(name, hex(node)) for name, node in tags])