##// END OF EJS Templates
bugzilla: use set instead of dict
bugzilla: use set instead of dict

File last commit:

r8250:1b60efdb default
r8455:a858b54d default
Show More
hg.py
338 lines | 12.2 KiB | text/x-python | PythonLexer
Martin Geisler
convert: add copyright and license headers to back-ends
r8250 # hg.py - hg backend for convert extension
#
# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
Brendan Cully
Split convert extension into common and repository type modules
r4536
Bryan O'Sullivan
convert: some tidyups, doc improvements, and test fixes...
r5556 # Notes 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.
#
# * By default, the source revision is stored in the converted
# revision. This will cause the converted revision to have a
# different identity than the source. To avoid this, use the
# following option: "--config convert.hg.saverev=false"
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013
Brendan Cully
Split convert extension into common and repository type modules
r4536 import os, time
Bryan O'Sullivan
convert: acquire/release locks periodically
r5014 from mercurial.i18n import _
Joel Rosdahl
Expand import * to allow Pyflakes to find problems
r6211 from mercurial.node import bin, hex, nullid
Matt Mackall
errors: move revlog errors...
r7633 from mercurial import hg, util, context, error
Brendan Cully
Split convert extension into common and repository type modules
r4536
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 from common import NoRepo, commit, converter_source, converter_sink
Brendan Cully
Split convert extension into common and repository type modules
r4536
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 class mercurial_sink(converter_sink):
Brendan Cully
convert: split converter into convertsource and convertsink
r4763 def __init__(self, ui, path):
Bryan O'Sullivan
convert: add default constructor for converter_sink
r5440 converter_sink.__init__(self, ui, path)
Brendan Cully
convert: hg: optionally create branches as clones...
r5173 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
Brendan Cully
convert: new config variable hg.tagsbranch controls which branch tags are committed to
r5260 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
Brendan Cully
convert: hg: optionally create branches as clones...
r5173 self.lastbranch = None
Bryan O'Sullivan
convert: refactor sink initialisation, to remove hardcoding of hg...
r5441 if os.path.isdir(path) and len(os.listdir(path)) > 0:
try:
self.repo = hg.repository(self.ui, path)
Patrick Mezard
convert: mercurial sink must be local
r5918 if not self.repo.local():
raise NoRepo(_('%s is not a local Mercurial repo') % path)
Matt Mackall
error: move repo errors...
r7637 except error.RepoError, err:
Matt Mackall
ui: print_exc() -> traceback()
r8206 ui.traceback()
Bryan O'Sullivan
convert: refactor sink initialisation, to remove hardcoding of hg...
r5441 raise NoRepo(err.args[0])
else:
try:
ui.status(_('initializing destination %s repository\n') % path)
self.repo = hg.repository(self.ui, path, create=True)
Patrick Mezard
convert: mercurial sink must be local
r5918 if not self.repo.local():
raise NoRepo(_('%s is not a local Mercurial repo') % path)
Bryan O'Sullivan
convert: refactor sink initialisation, to remove hardcoding of hg...
r5441 self.created.append(path)
Peter Arrenbrecht
cleanup: drop unused assignments
r7875 except error.RepoError:
Matt Mackall
ui: print_exc() -> traceback()
r8206 ui.traceback()
Bryan O'Sullivan
convert: refactor sink initialisation, to remove hardcoding of hg...
r5441 raise NoRepo("could not create hg repo %s as sink" % path)
Bryan O'Sullivan
convert: acquire/release locks periodically
r5014 self.lock = None
self.wlock = None
Alexis S. L. Carvalho
convert: add a mode where mercurial_sink skips empty revisions....
r5378 self.filemapmode = False
Bryan O'Sullivan
convert: acquire/release locks periodically
r5014
def before(self):
Patrick Mezard
test-convert: test before() and after() conversion actions
r5805 self.ui.debug(_('run hg sink pre-conversion action\n'))
Alexis S. L. Carvalho
convert: fix locking order
r5052 self.wlock = self.repo.wlock()
Bryan O'Sullivan
convert: acquire/release locks periodically
r5014 self.lock = self.repo.lock()
def after(self):
Patrick Mezard
test-convert: test before() and after() conversion actions
r5805 self.ui.debug(_('run hg sink post-conversion action\n'))
Ronny Pfannschmidt
switch lock releasing in the extensions from gc to explicit
r8112 self.lock.release()
self.wlock.release()
Brendan Cully
Split convert extension into common and repository type modules
r4536
Bryan O'Sullivan
convert: rename mapfile to revmapfile, so we can map more than just revs
r5011 def revmapfile(self):
Brendan Cully
Split convert extension into common and repository type modules
r4536 return os.path.join(self.path, ".hg", "shamap")
Edouard Gomez
convert extension: Add support for username mapping...
r4589 def authorfile(self):
return os.path.join(self.path, ".hg", "authormap")
Brendan Cully
Split convert extension into common and repository type modules
r4536 def getheads(self):
h = self.repo.changelog.heads()
Bryan O'Sullivan
convert: get rid of "hg." prefix where not needed
r5017 return [ hex(x) for x in h ]
Brendan Cully
Split convert extension into common and repository type modules
r4536
Patrick Mezard
convert: hg.clonebranches must pull missing parents (issue941)
r5934 def setbranch(self, branch, pbranches):
if not self.clonebranches:
Brendan Cully
convert: hg: optionally create branches as clones...
r5173 return
Patrick Mezard
convert: hg.clonebranches must pull missing parents (issue941)
r5934 setbranch = (branch != self.lastbranch)
Brendan Cully
convert: hg: optionally create branches as clones...
r5173 self.lastbranch = branch
if not branch:
branch = 'default'
Patrick Mezard
convert: hg.clonebranches must pull missing parents (issue941)
r5934 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
pbranch = pbranches and pbranches[0][1] or 'default'
Brendan Cully
convert: hg: optionally create branches as clones...
r5173
branchpath = os.path.join(self.path, branch)
Patrick Mezard
convert: hg.clonebranches must pull missing parents (issue941)
r5934 if setbranch:
self.after()
try:
self.repo = hg.repository(self.ui, branchpath)
except:
Brendan Cully
convert: hg: optionally create branches as clones...
r5173 self.repo = hg.repository(self.ui, branchpath, create=True)
Patrick Mezard
convert: hg.clonebranches must pull missing parents (issue941)
r5934 self.before()
# pbranches may bring revisions from other branches (merge parents)
# Make sure we have them, or pull them.
missings = {}
for b in pbranches:
try:
self.repo.lookup(b[0])
except:
missings.setdefault(b[1], []).append(b[0])
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Patrick Mezard
convert: hg.clonebranches must pull missing parents (issue941)
r5934 if missings:
self.after()
for pbranch, heads in missings.iteritems():
pbranchpath = os.path.join(self.path, pbranch)
prepo = hg.repository(self.ui, pbranchpath)
self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
self.before()
Brendan Cully
convert: hg: optionally create branches as clones...
r5173
Patrick Mezard
convert: reintegrate file retrieval code in sinks...
r6716 def putcommit(self, files, copies, parents, commit, source):
Patrick Mezard
convert: hg sink commits without working dir
r6717 files = dict(files)
def getfilectx(repo, memctx, f):
v = files[f]
data = source.getfile(f, v)
e = source.getmode(f, v)
return context.memfilectx(f, data, 'l' in e, 'x' in e, copies.get(f))
Brendan Cully
Split convert extension into common and repository type modules
r4536 pl = []
for p in parents:
Patrick Mezard
convert: hg sink commits without working dir
r6717 if p not in pl:
Brendan Cully
Split convert extension into common and repository type modules
r4536 pl.append(p)
parents = pl
Alexis S. L. Carvalho
convert: add a mode where mercurial_sink skips empty revisions....
r5378 nparents = len(parents)
if self.filemapmode and nparents == 1:
m1node = self.repo.changelog.read(bin(parents[0]))[0]
parent = parents[0]
Brendan Cully
Split convert extension into common and repository type modules
r4536
if len(parents) < 2: parents.append("0" * 40)
if len(parents) < 2: parents.append("0" * 40)
p2 = parents.pop(0)
text = commit.desc
Bryan O'Sullivan
convert: make contents of "extra" dict available from sources, for sinks....
r5439 extra = commit.extra.copy()
Bryan O'Sullivan
convert: add config option to turn off use of branch names
r5038 if self.branchnames and commit.branch:
Brendan Cully
convert: record the source revision in the changelog
r4873 extra['branch'] = commit.branch
if commit.rev:
extra['convert_revision'] = commit.rev
Thomas Arendsen Hein
removed trailing whitespace
r4957
Brendan Cully
Split convert extension into common and repository type modules
r4536 while parents:
p1 = p2
p2 = parents.pop(0)
Patrick Mezard
convert: hg sink commits without working dir
r6717 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(), getfilectx,
commit.author, commit.date, extra)
Peter Arrenbrecht
cleanup: drop variables for unused return values...
r7874 self.repo.commitctx(ctx)
Brendan Cully
Split convert extension into common and repository type modules
r4536 text = "(octopus merge fixup)\n"
Joel Rosdahl
Avoid importing mercurial.node/mercurial.repo stuff from mercurial.hg
r6217 p2 = hex(self.repo.changelog.tip())
Brendan Cully
Split convert extension into common and repository type modules
r4536
Alexis S. L. Carvalho
convert: add a mode where mercurial_sink skips empty revisions....
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()
return parent
Brendan Cully
Split convert extension into common and repository type modules
r4536 return p2
def puttags(self, tags):
Peter Arrenbrecht
cleanup: whitespace cleanup
r7877 try:
parentctx = self.repo[self.tagsbranch]
tagparent = parentctx.node()
except error.RepoError:
parentctx = None
tagparent = nullid
Brendan Cully
Split convert extension into common and repository type modules
r4536
Peter Arrenbrecht
cleanup: whitespace cleanup
r7877 try:
Matt Mackall
replace util.sort with sorted built-in...
r8209 oldlines = sorted(parentctx['.hgtags'].data().splitlines(1))
Peter Arrenbrecht
cleanup: whitespace cleanup
r7877 except:
oldlines = []
Brendan Cully
Split convert extension into common and repository type modules
r4536
Matt Mackall
replace util.sort with sorted built-in...
r8209 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
Peter Arrenbrecht
cleanup: whitespace cleanup
r7877 if newlines == oldlines:
return None
data = "".join(newlines)
def getfilectx(repo, memctx, f):
Patrick Mezard
convert: hg sink commits without working dir
r6717 return context.memfilectx(f, data, False, False, None)
Peter Arrenbrecht
cleanup: whitespace cleanup
r7877 self.ui.status(_("updating tags\n"))
date = "%s 0" % int(time.mktime(time.gmtime()))
extra = {'branch': self.tagsbranch}
ctx = context.memctx(self.repo, (tagparent, None), "update tags",
[".hgtags"], getfilectx, "convert-repo", date,
extra)
self.repo.commitctx(ctx)
return hex(self.repo.changelog.tip())
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013
Alexis S. L. Carvalho
convert: add a mode where mercurial_sink skips empty revisions....
r5378 def setfilemapmode(self, active):
self.filemapmode = active
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 class mercurial_source(converter_source):
def __init__(self, ui, path, rev=None):
converter_source.__init__(self, ui, path, rev)
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
self.ignored = {}
Matt Mackall
convert: change hg.saverev default to False...
r7815 self.saverev = ui.configbool('convert', 'hg.saverev', False)
Bryan O'Sullivan
convert: fail properly if we can't read a source hg repository
r5358 try:
self.repo = hg.repository(self.ui, path)
Bryan O'Sullivan
convert: report errors more meaningfully if run with --traceback
r5437 # try to provoke an exception if this isn't really a hg
# repo, but some other bogus compatible-looking url
Alexis S. L. Carvalho
convert: make sure mercurial_source has a local hg repo
r5522 if not self.repo.local():
Matt Mackall
error: move repo errors...
r7637 raise error.RepoError()
except error.RepoError:
Matt Mackall
ui: print_exc() -> traceback()
r8206 ui.traceback()
Alexis S. L. Carvalho
convert: make sure mercurial_source has a local hg repo
r5522 raise NoRepo("%s is not a local Mercurial repo" % path)
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 self.lastrev = None
self.lastctx = None
Alexis S. L. Carvalho
mercurial_source: add --filemap support
r5379 self._changescache = None
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 self.convertfp = None
Patrick Mezard
convert: implement startrev for hg source
r6885 # Restrict converted revisions to startrev descendants
startnode = ui.config('convert', 'hg.startrev')
if startnode is not None:
try:
startnode = self.repo.lookup(startnode)
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Dirkjan Ochtman
strip trailing whitespace, replace tabs by spaces
r6923 raise util.Abort(_('%s is not a valid start revision')
Patrick Mezard
convert: implement startrev for hg source
r6885 % startnode)
startrev = self.repo.changelog.rev(startnode)
children = {startnode: 1}
for rev in self.repo.changelog.descendants(startrev):
children[self.repo.changelog.node(rev)] = 1
self.keep = children.__contains__
else:
self.keep = util.always
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013
def changectx(self, rev):
if self.lastrev != rev:
Matt Mackall
use repo[changeid] to get a changectx
r6747 self.lastctx = self.repo[rev]
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 self.lastrev = rev
return self.lastctx
Patrick Mezard
convert: implement startrev for hg source
r6885 def parents(self, ctx):
Dirkjan Ochtman
strip trailing whitespace, replace tabs by spaces
r6923 return [p.node() for p in ctx.parents()
Patrick Mezard
convert: implement startrev for hg source
r6885 if p and self.keep(p.node())]
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 def getheads(self):
Bryan O'Sullivan
convert: only get history for requested revs when converting hg repo
r5131 if self.rev:
Patrick Mezard
convert: implement startrev for hg source
r6885 heads = [self.repo[self.rev].node()]
Bryan O'Sullivan
convert: only get history for requested revs when converting hg repo
r5131 else:
Patrick Mezard
convert: implement startrev for hg source
r6885 heads = self.repo.heads()
return [hex(h) for h in heads if self.keep(h)]
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013
def getfile(self, name, rev):
try:
Matt Mackall
use repo[changeid] to get a changectx
r6747 return self.changectx(rev)[name].data()
Matt Mackall
errors: move revlog errors...
r7633 except error.LookupError, err:
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 raise IOError(err)
def getmode(self, name, rev):
Matt Mackall
manifest: remove execf/linkf methods
r6749 return self.changectx(rev).manifest().flags(name)
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013
def getchanges(self, rev):
ctx = self.changectx(rev)
Patrick Mezard
convert: implement startrev for hg source
r6885 parents = self.parents(ctx)
if not parents:
Matt Mackall
replace util.sort with sorted built-in...
r8209 files = sorted(ctx.manifest())
Patrick Mezard
convert: correctly detect missing revlog for root revisions
r7232 if self.ignoreerrors:
# calling getcopies() is a simple way to detect missing
# revlogs and populate self.ignored
self.getcopies(ctx, files)
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 return [(f, rev) for f in files if f not in self.ignored], {}
Alexis S. L. Carvalho
mercurial_source: add --filemap support
r5379 if self._changescache and self._changescache[0] == rev:
m, a, r = self._changescache[1]
else:
Patrick Mezard
convert: implement startrev for hg source
r6885 m, a, r = self.repo.status(parents[0], ctx.node())[:3]
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 # getcopies() detects missing revlogs early, run it before
# filtering the changes.
copies = self.getcopies(ctx, m + a)
Thomas Arendsen Hein
Remove trailing spaces.
r7236 changes = [(name, rev) for name in m + a + r
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 if name not in self.ignored]
Matt Mackall
replace util.sort with sorted built-in...
r8209 return sorted(changes), copies
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013
Alexis S. L. Carvalho
convert: mercurial_source: also search for copies in modified files...
r5280 def getcopies(self, ctx, files):
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 copies = {}
Alexis S. L. Carvalho
convert: mercurial_source: also search for copies in modified files...
r5280 for name in files:
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 if name in self.ignored:
continue
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 try:
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 copysource, copynode = ctx.filectx(name).renamed()
if copysource in self.ignored or not self.keep(copynode):
continue
copies[name] = copysource
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 except TypeError:
pass
Matt Mackall
errors: move revlog errors...
r7633 except error.LookupError, e:
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 if not self.ignoreerrors:
raise
self.ignored[name] = 1
self.ui.warn(_('ignoring: %s\n') % e)
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 return copies
Thomas Arendsen Hein
Remove trailing spaces, fix indentation
r5143
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 def getcommit(self, rev):
ctx = self.changectx(rev)
Patrick Mezard
convert: implement startrev for hg source
r6885 parents = [hex(p) for p in self.parents(ctx)]
Bryan O'Sullivan
convert: some tidyups, doc improvements, and test fixes...
r5556 if self.saverev:
crev = rev
else:
crev = None
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013 return commit(author=ctx.user(), date=util.datestr(ctx.date()),
Bryan O'Sullivan
convert: some tidyups, doc improvements, and test fixes...
r5556 desc=ctx.description(), rev=crev, parents=parents,
Bryan O'Sullivan
convert: make contents of "extra" dict available from sources, for sinks....
r5439 branch=ctx.branch(), extra=ctx.extra())
Bryan O'Sullivan
convert: Support Mercurial as a source, as well as a sink
r5013
def gettags(self):
tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
Patrick Mezard
convert: implement startrev for hg source
r6885 return dict([(name, hex(node)) for name, node in tags
if self.keep(node)])
Alexis S. L. Carvalho
mercurial_source: add --filemap support
r5379
def getchangedfiles(self, rev, i):
ctx = self.changectx(rev)
Patrick Mezard
convert: implement startrev for hg source
r6885 parents = self.parents(ctx)
if not parents and i is None:
i = 0
changes = [], ctx.manifest().keys(), []
else:
i = i or 0
changes = self.repo.status(parents[i], ctx.node())[:3]
Patrick Mezard
convert: ignore hg source errors with hg.ignoreerrors (issue 1357)...
r7231 changes = [[f for f in l if f not in self.ignored] for l in changes]
Alexis S. L. Carvalho
mercurial_source: add --filemap support
r5379
if i == 0:
self._changescache = (rev, changes)
return changes[0] + changes[1] + changes[2]
Bryan O'Sullivan
convert: tell the source repository when a rev has been converted...
r5554 def converted(self, rev, destrev):
if self.convertfp is None:
self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
'a')
self.convertfp.write('%s %s\n' % (destrev, rev))
self.convertfp.flush()
Patrick Mezard
test-convert: test before() and after() conversion actions
r5805
def before(self):
self.ui.debug(_('run hg source pre-conversion action\n'))
def after(self):
self.ui.debug(_('run hg source post-conversion action\n'))