hg.py
259 lines
| 8.7 KiB
| text/x-python
|
PythonLexer
|
r4536 | # hg backend for convert extension | ||
|
r5013 | # 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. | ||||
|
r4536 | import os, time | ||
|
r5014 | from mercurial.i18n import _ | ||
|
r5013 | from mercurial.node import * | ||
|
r5014 | from mercurial import hg, lock, revlog, util | ||
|
r4536 | |||
|
r5013 | from common import NoRepo, commit, converter_source, converter_sink | ||
|
r4536 | |||
|
r5013 | class mercurial_sink(converter_sink): | ||
|
r4763 | def __init__(self, ui, path): | ||
|
r5440 | converter_sink.__init__(self, ui, path) | ||
|
r5173 | self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True) | ||
self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False) | ||||
|
r5260 | self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default') | ||
|
r5173 | self.lastbranch = None | ||
|
r5441 | if os.path.isdir(path) and len(os.listdir(path)) > 0: | ||
try: | ||||
self.repo = hg.repository(self.ui, path) | ||||
ui.status(_('destination %s is a Mercurial repository\n') % | ||||
path) | ||||
except hg.RepoError, err: | ||||
ui.print_exc() | ||||
raise NoRepo(err.args[0]) | ||||
else: | ||||
try: | ||||
ui.status(_('initializing destination %s repository\n') % path) | ||||
self.repo = hg.repository(self.ui, path, create=True) | ||||
self.created.append(path) | ||||
except hg.RepoError, err: | ||||
ui.print_exc() | ||||
raise NoRepo("could not create hg repo %s as sink" % path) | ||||
|
r5014 | self.lock = None | ||
self.wlock = None | ||||
|
r5378 | self.filemapmode = False | ||
|
r5014 | |||
def before(self): | ||||
|
r5052 | self.wlock = self.repo.wlock() | ||
|
r5014 | self.lock = self.repo.lock() | ||
|
r5279 | self.repo.dirstate.clear() | ||
|
r5014 | |||
def after(self): | ||||
|
r5279 | self.repo.dirstate.invalidate() | ||
|
r5014 | self.lock = None | ||
self.wlock = None | ||||
|
r4536 | |||
|
r5011 | def revmapfile(self): | ||
|
r4536 | return os.path.join(self.path, ".hg", "shamap") | ||
|
r4589 | def authorfile(self): | ||
return os.path.join(self.path, ".hg", "authormap") | ||||
|
r4536 | def getheads(self): | ||
h = self.repo.changelog.heads() | ||||
|
r5017 | return [ hex(x) for x in h ] | ||
|
r4536 | |||
def putfile(self, f, e, data): | ||||
self.repo.wwrite(f, data, e) | ||||
|
r4906 | if f not in self.repo.dirstate: | ||
|
r5278 | self.repo.dirstate.normallookup(f) | ||
|
r4536 | |||
|
r4765 | def copyfile(self, source, dest): | ||
self.repo.copy(source, dest) | ||||
|
r4536 | def delfile(self, f): | ||
try: | ||||
|
r5343 | util.unlink(self.repo.wjoin(f)) | ||
|
r4536 | #self.repo.remove([f]) | ||
|
r5344 | except OSError: | ||
|
r4536 | pass | ||
|
r5173 | 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) | ||||
|
r5402 | self.before() | ||
|
r5173 | |||
|
r4536 | def putcommit(self, files, parents, commit): | ||
|
r5195 | seen = {} | ||
|
r4536 | pl = [] | ||
for p in parents: | ||||
if p not in seen: | ||||
pl.append(p) | ||||
seen[p] = 1 | ||||
parents = pl | ||||
|
r5378 | nparents = len(parents) | ||
if self.filemapmode and nparents == 1: | ||||
m1node = self.repo.changelog.read(bin(parents[0]))[0] | ||||
parent = parents[0] | ||||
|
r4536 | |||
if len(parents) < 2: parents.append("0" * 40) | ||||
if len(parents) < 2: parents.append("0" * 40) | ||||
p2 = parents.pop(0) | ||||
text = commit.desc | ||||
|
r5439 | extra = commit.extra.copy() | ||
|
r5038 | if self.branchnames and commit.branch: | ||
|
r4873 | extra['branch'] = commit.branch | ||
if commit.rev: | ||||
extra['convert_revision'] = commit.rev | ||||
|
r4957 | |||
|
r4536 | while parents: | ||
p1 = p2 | ||||
p2 = parents.pop(0) | ||||
a = self.repo.rawcommit(files, text, commit.author, commit.date, | ||||
|
r5017 | bin(p1), bin(p2), extra=extra) | ||
|
r5278 | self.repo.dirstate.clear() | ||
|
r4536 | text = "(octopus merge fixup)\n" | ||
p2 = hg.hex(self.repo.changelog.tip()) | ||||
|
r5378 | if self.filemapmode and nparents == 1: | ||
man = self.repo.manifest | ||||
mnode = self.repo.changelog.read(bin(p2))[0] | ||||
if not man.cmp(m1node, man.revision(mnode)): | ||||
self.repo.rollback() | ||||
self.repo.dirstate.clear() | ||||
return parent | ||||
|
r4536 | return p2 | ||
def puttags(self, tags): | ||||
try: | ||||
old = self.repo.wfile(".hgtags").read() | ||||
oldlines = old.splitlines(1) | ||||
oldlines.sort() | ||||
except: | ||||
oldlines = [] | ||||
k = tags.keys() | ||||
k.sort() | ||||
newlines = [] | ||||
for tag in k: | ||||
newlines.append("%s %s\n" % (tags[tag], tag)) | ||||
newlines.sort() | ||||
if newlines != oldlines: | ||||
self.ui.status("updating tags\n") | ||||
f = self.repo.wfile(".hgtags", "w") | ||||
f.write("".join(newlines)) | ||||
f.close() | ||||
if not oldlines: self.repo.add([".hgtags"]) | ||||
date = "%s 0" % int(time.mktime(time.gmtime())) | ||||
|
r5260 | extra = {} | ||
if self.tagsbranch != 'default': | ||||
extra['branch'] = self.tagsbranch | ||||
try: | ||||
tagparent = self.repo.changectx(self.tagsbranch).node() | ||||
except hg.RepoError, inst: | ||||
tagparent = nullid | ||||
|
r4536 | self.repo.rawcommit([".hgtags"], "update tags", "convert-repo", | ||
|
r5260 | date, tagparent, nullid) | ||
|
r5017 | return hex(self.repo.changelog.tip()) | ||
|
r5013 | |||
|
r5378 | def setfilemapmode(self, active): | ||
self.filemapmode = active | ||||
|
r5013 | class mercurial_source(converter_source): | ||
def __init__(self, ui, path, rev=None): | ||||
converter_source.__init__(self, ui, path, rev) | ||||
|
r5358 | try: | ||
self.repo = hg.repository(self.ui, path) | ||||
|
r5437 | # try to provoke an exception if this isn't really a hg | ||
# repo, but some other bogus compatible-looking url | ||||
self.repo.heads() | ||||
except hg.RepoError: | ||||
ui.print_exc() | ||||
|
r5358 | raise NoRepo("could not open hg repo %s as source" % path) | ||
|
r5013 | self.lastrev = None | ||
self.lastctx = None | ||||
|
r5379 | self._changescache = None | ||
|
r5013 | |||
def changectx(self, rev): | ||||
if self.lastrev != rev: | ||||
self.lastctx = self.repo.changectx(rev) | ||||
self.lastrev = rev | ||||
return self.lastctx | ||||
def getheads(self): | ||||
|
r5131 | if self.rev: | ||
return [hex(self.repo.changectx(self.rev).node())] | ||||
else: | ||||
return [hex(node) for node in self.repo.heads()] | ||||
|
r5013 | |||
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) | ||||
|
r5379 | if self._changescache and self._changescache[0] == rev: | ||
m, a, r = self._changescache[1] | ||||
else: | ||||
m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3] | ||||
|
r5013 | changes = [(name, rev) for name in m + a + r] | ||
changes.sort() | ||||
|
r5280 | return (changes, self.getcopies(ctx, m + a)) | ||
|
r5013 | |||
|
r5280 | def getcopies(self, ctx, files): | ||
|
r5013 | copies = {} | ||
|
r5280 | for name in files: | ||
|
r5013 | try: | ||
copies[name] = ctx.filectx(name).renamed()[0] | ||||
except TypeError: | ||||
pass | ||||
return copies | ||||
|
r5143 | |||
|
r5013 | 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, | ||||
|
r5439 | branch=ctx.branch(), extra=ctx.extra()) | ||
|
r5013 | |||
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]) | ||||
|
r5379 | |||
def getchangedfiles(self, rev, i): | ||||
ctx = self.changectx(rev) | ||||
i = i or 0 | ||||
changes = self.repo.status(ctx.parents()[i].node(), ctx.node())[:3] | ||||
if i == 0: | ||||
self._changescache = (rev, changes) | ||||
return changes[0] + changes[1] + changes[2] | ||||