Commit f505a84d authored by Tristan Seligmann's avatar Tristan Seligmann

Upstream snapsnot 1.8.7+1517-b3e41b0d50a2

parent ef9caf1c
repo: f2636cfed11500fdc47d1e3822d8e4a2bd636bf7
node: 49d324e11856f5d65bd7f83f7ffb3a2f07bf82c1
branch: stable
tag: 1.8.6
node: b3e41b0d50a2fefdfea1d465016699cfdcf04dcc
branch: default
latesttag: 1.8.7
latesttagdistance: 23
changessincelatesttag: 149
syntax:glob
build
*.egg-info
*.orig
*.py,cover
*.pyc
*.pyo
.DS_Store
*.swp
*~
.DS_Store
.coverage
cover
*.py,cover
.noseids
.project
.pydevproject
.settings
.tox
MANIFEST
build
cover
dist
*.egg-info
hgsubversion/__version__.py
nbproject
.project
.pydevproject
.settings
*.orig
.noseids
tests/fixtures/temp
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
......@@ -20,3 +20,5 @@ dde1ade36a49d3d0e1b4b8bd384a6797665b5081 1.8.1
759cafce6becef077fb1a152b554a05ff66b04cd 1.8.3
89997a5fc18163c5f65b83272b4521cdbf29984e 1.8.4
bd979667611d9df733c61251e7668899f3e77a8f 1.8.5
49d324e11856f5d65bd7f83f7ffb3a2f07bf82c1 1.8.6
051a517b473b7bbb7f2176ed38a34fcee96fceee 1.8.7
......@@ -196,34 +196,41 @@ hg.schemes.update({ 'file': _lookup, 'http': svnrepo, 'https': svnrepo,
if hgutil.safehasattr(commands, 'optionalrepo'):
commands.optionalrepo += ' svn'
cmdtable = {
"svn":
(svncommands.svn,
[('u', 'svn-url', '', 'path to the Subversion server.'),
('', 'stupid', False, 'be stupid and use diffy replay.'),
('A', 'authors', '', 'username mapping filename'),
('', 'filemap', '',
'remap file to exclude paths or include only certain paths'),
('', 'force', False, 'force an operation to happen'),
('', 'username', '', 'username for authentication'),
('', 'password', '', 'password for authentication'),
('r', 'rev', '', 'Mercurial revision'),
('', 'unsafe-skip-uuid-check', False,
'skip repository uuid check in rebuildmeta'),
],
'hg svn <subcommand> ...',
),
}
svncommandopts = [
('u', 'svn-url', '', 'path to the Subversion server.'),
('', 'stupid', False, 'be stupid and use diffy replay.'),
('A', 'authors', '', 'username mapping filename'),
('', 'filemap', '',
'remap file to exclude paths or include only certain paths'),
('', 'force', False, 'force an operation to happen'),
('', 'username', '', 'username for authentication'),
('', 'password', '', 'password for authentication'),
('r', 'rev', '', 'Mercurial revision'),
('', 'unsafe-skip-uuid-check', False,
'skip repository uuid check in rebuildmeta'),
]
svnusage = 'hg svn <subcommand> ...'
# only these methods are public
__all__ = ('cmdtable', 'reposetup', 'uisetup')
# set up templatekeywords (written this way to maintain backwards compatibility
# until we drop support for 3.7)
# set up commands and templatekeywords (written this way to maintain backwards
# compatibility until we drop support for 3.7 for templatekeywords and 4.3 for
# commands)
cmdtable = {
"svn": (svncommands.svn, svncommandopts, svnusage),
}
try:
from mercurial import registrar
templatekeyword = registrar.templatekeyword()
loadkeyword = lambda registrarobj: None # no-op
if hgutil.safehasattr(registrar, 'command'):
cmdtable = {}
command = registrar.command(cmdtable)
@command('svn', svncommandopts, svnusage)
def svncommand(*args, **kwargs):
return svncommands.svn(*args, **kwargs)
except (ImportError, AttributeError):
# registrar.templatekeyword isn't available = loading by old hg
......@@ -269,6 +276,8 @@ def svnuuidkw(**args):
""":svnuuid: String. Converted subversion revision repository identifier."""
return _templatehelper(args['ctx'], 'svnuuid')
loadkeyword(templatekeyword)
def listsvnkeys(repo):
keys = {}
repo = repo.local()
......
......@@ -106,6 +106,9 @@ class RevisionData(object):
self.clear()
def clear(self):
oldstore = getattr(self, 'store', None)
if oldstore is not None:
oldstore.close()
self.store = FileStore(util.getfilestoresize(self.ui))
self.added = set()
self.deleted = {}
......@@ -578,6 +581,12 @@ class HgEditor(svnwrap.Editor):
try:
if not self.meta.is_path_valid(path):
return
# are we skipping this branch entirely?
br_path, branch = self.meta.split_branch_path(path)[:2]
if self.meta.skipbranch(branch):
return
try:
handler(window)
except AssertionError, e: # pragma: no cover
......
......@@ -352,6 +352,25 @@ settings:
Password stores are only supported with the SWIG bindings.
``hgsubversion.revmapimpl``
Set the revision map implementation. ``plain`` which is simple and works
well for small repos. ``sqlite`` is a sqlite based implementation that
works better for large repos with a lot of revisions. The default is
``plain``.
If it is set to an implementation different from what the repo is using,
a migration will run automatically when the revision map is accessed.
``hgsubversion.sqlitepragmas``
A list of sqlite PRAGMA statements to tweak sqlite. Each item should be
in the format ``key=value`` without ``PRAGMA``, or spaces, or quotation
marks. Refer to https://www.sqlite.org/pragma.html for possible options.
For example, setting it to ``synchronous=0, journal_mode=memory`` will
give you better performance at the cost of possible database corruption.
``hgsubversion.stupid``
Setting this boolean option to true will force using a slower method for
pulling revisions from Subversion. This method is compatible with servers
......
......@@ -18,7 +18,9 @@ class CustomLayout(base.BaseLayout):
self.svn_to_hg = {}
self.hg_to_svn = {}
for hg_branch, svn_path in meta.ui.configitems('hgsubversionbranch'):
meta._gen_cachedconfig('custombranches', {}, configname='hgsubversionbranch')
for hg_branch, svn_path in meta.custombranches.iteritems():
hg_branch = hg_branch.strip()
if hg_branch == 'default' or not hg_branch:
......
This diff is collapsed.
......@@ -65,13 +65,13 @@ def _convert_rev(ui, meta, svn, r, tbdelta, firstrun):
editor.current.rev = r
editor.setsvn(svn)
if firstrun and meta.firstpulled <= 0:
if firstrun and meta.revmap.firstpulled <= 0:
# We know nothing about this project, so fetch everything before
# trying to apply deltas.
ui.debug('replay: fetching full revision\n')
svn.get_revision(r.revnum, editor)
else:
svn.get_replay(r.revnum, editor, meta.firstpulled)
svn.get_replay(r.revnum, editor, meta.revmap.firstpulled)
editor.close()
current = editor.current
......@@ -103,7 +103,7 @@ def _convert_rev(ui, meta, svn, r, tbdelta, firstrun):
closebranches = {}
for branch in tbdelta['branches'][1]:
branchedits = meta.revmap.branchedits(branch, rev)
branchedits = meta.revmap.branchedits(branch, rev.revnum)
if len(branchedits) < 1:
# can't close a branch that never existed
continue
......@@ -121,6 +121,12 @@ def _convert_rev(ui, meta, svn, r, tbdelta, firstrun):
if branch in current.emptybranches and files:
del current.emptybranches[branch]
if meta.skipbranch(branch):
# make sure we also get rid of it from emptybranches
if branch in current.emptybranches:
del current.emptybranches[branch]
continue
files = dict(files)
parents = meta.get_parent_revision(rev.revnum, branch), revlog.nullid
if parents[0] in closedrevs and branch in meta.closebranches:
......@@ -175,6 +181,8 @@ def _convert_rev(ui, meta, svn, r, tbdelta, firstrun):
copied=copied)
meta.mapbranch(extra)
if 'branch' not in extra:
extra['branch'] = 'default'
current_ctx = context.memctx(meta.repo,
parents,
meta.getmessage(rev),
......@@ -195,6 +203,9 @@ def _convert_rev(ui, meta, svn, r, tbdelta, firstrun):
# 2. handle branches that need to be committed without any files
for branch in current.emptybranches:
if meta.skipbranch(branch):
continue
ha = meta.get_parent_revision(rev.revnum, branch)
if ha == node.nullid:
continue
......
......@@ -568,7 +568,7 @@ def fetch_branchrev(svn, meta, branch, branchpath, r, parentctx):
return files, filectxfn
def checkbranch(meta, r, branch):
branchedits = meta.revmap.branchedits(branch, r)
branchedits = meta.revmap.branchedits(branch, r.revnum)
if not branchedits:
return None
branchtip = branchedits[0][1]
......@@ -689,6 +689,10 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
date = meta.fixdate(r.date)
check_deleted_branches = set(tbdelta['branches'][1])
for b in branches:
if meta.skipbranch(b):
continue
parentctx = meta.repo[meta.get_parent_revision(r.revnum, b)]
tag = meta.get_path_tag(meta.remotename(b))
kind = svn.checkpath(branches[b], r.revnum)
......@@ -704,7 +708,7 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
# path does not support this case with svn >= 1.7. We can fix
# it, or we can force the existing fetch_branchrev() path. Do
# the latter for now.
incremental = (meta.firstpulled > 0 and
incremental = (meta.revmap.firstpulled > 0 and
parentctx.rev() != node.nullrev and
not firstrun)
......
......@@ -64,8 +64,12 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
youngest = 0
startrev = 0
sofar = []
branchinfo = {}
if not partial:
hgutil.unlinkpath(meta.revmap_file, ignoremissing=True)
revmap = meta.revmap
if partial:
try:
# we can't use meta.lastpulled here because we are bootstraping the
......@@ -75,9 +79,8 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
youngestpath = os.path.join(meta.metapath, 'lastpulled')
if os.path.exists(youngestpath):
youngest = util.load(youngestpath)
sofar = list(maps.RevMap.readmapfile(meta.revmap_file))
if sofar and len(sofar[-1].split(' ', 2)) > 1:
lasthash = sofar[-1].split(' ', 2)[1]
lasthash = revmap.lasthash
if len(revmap) > 0 and lasthash:
startrev = repo[lasthash].rev() + 1
branchinfo = util.load(meta.branch_info_file)
foundpartialinfo = True
......@@ -91,9 +94,9 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
except AttributeError:
ui.status('no metadata available -- doing a full rebuild\n')
revmap = open(meta.revmap_file, 'w')
revmap.write('%d\n' % maps.RevMap.VERSION)
revmap.writelines(sofar)
if not partial:
revmap.clear()
last_rev = -1
if not partial and os.path.exists(meta.tagfile):
os.unlink(meta.tagfile)
......@@ -107,13 +110,8 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
# it would make us use O(revisions^2) time, so we perform an extra traversal
# of the repository instead. During this traversal, we find all converted
# changesets that close a branch, and store their first parent
for rev in xrange(startrev, len(repo)):
ui.progress('prepare', rev - startrev, total=numrevs)
try:
ctx = repo[rev]
except error.RepoError:
# this revision is hidden
continue
for ctx in util.get_contexts(repo, startrev):
ui.progress('prepare', ctx.rev() - startrev, total=numrevs)
convinfo = util.getsvnrev(ctx, None)
if not convinfo:
......@@ -137,16 +135,11 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
else:
closed.add(parentctx.rev())
meta.lastpulled = youngest
ui.progress('prepare', None, total=numrevs)
for rev in xrange(startrev, len(repo)):
ui.progress('rebuild', rev-startrev, total=numrevs)
try:
ctx = repo[rev]
except error.RepoError:
# this revision is hidden
continue
revmapbuf = []
for ctx in util.get_contexts(repo, startrev):
ui.progress('rebuild', ctx.rev() - startrev, total=numrevs)
convinfo = util.getsvnrev(ctx, None)
if not convinfo:
......@@ -226,7 +219,7 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
continue
branch = meta.layoutobj.localname(commitpath)
revmap.write('%s %s %s\n' % (revision, ctx.hex(), branch or ''))
revmapbuf.append((revision, branch, ctx.node()))
revision = int(revision)
if revision > last_rev:
......@@ -254,7 +247,7 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
branch = meta.layoutobj.localname(parentpath)
break
if rev in closed:
if ctx.rev() in closed:
# a direct child of this changeset closes the branch; drop it
branchinfo.pop(branch, None)
elif ctx.extra().get('close'):
......@@ -276,6 +269,7 @@ def _buildmeta(ui, repo, args, partial=False, skipuuid=False):
int(parentrev),
revision)
revmap.batchset(revmapbuf, youngest)
ui.progress('rebuild', None, total=numrevs)
# save off branch info
......@@ -347,7 +341,7 @@ def genignore(ui, repo, force=False, **opts):
raise error.RepoError("There is no Mercurial repository"
" here (.hg not found)")
ignpath = repo.wjoin('.hgignore')
ignpath = repo.wvfs.join('.hgignore')
if not force and os.path.exists(ignpath):
raise hgutil.Abort('not overwriting existing .hgignore, try --force?')
svn = svnrepo.svnremoterepo(repo.ui).svn
......@@ -369,7 +363,7 @@ def genignore(ui, repo, force=False, **opts):
lines = props['svn:ignore'].strip().split('\n')
ignorelines += [dir and (dir + '/' + prop) or prop for prop in lines if prop.strip()]
repo.wopener('.hgignore', 'w').write('\n'.join(ignorelines) + '\n')
repo.wvfs('.hgignore', 'w').write('\n'.join(ignorelines) + '\n')
def info(ui, repo, **opts):
......
......@@ -88,7 +88,7 @@ class BadDefinition(Exception):
pass
re_defold = re.compile(r'^\s*(.*?)\s+(?:-r\s*(\d+|\{REV\})\s+)?([a-zA-Z+]+://.*)\s*$')
re_defnew = re.compile(r'^\s*(?:-r\s*(\d+|\{REV\})\s+)?((?:[a-zA-Z+]+://|\^/).*)\s+(\S+)\s*$')
re_defnew = re.compile(r'^\s*(?:-r\s*(\d+|\{REV\})\s+)?((?:[a-zA-Z+]+://|\^/)\S*)\s+(\S+)\s*$')
re_scheme = re.compile(r'^[a-zA-Z+]+://')
def parsedefinition(line):
......@@ -120,13 +120,84 @@ def parsedefinition(line):
class RelativeSourceError(Exception):
pass
def resolvedots(url):
"""
Fix references that include .. entries.
Scans a URL for .. type entries and resolves them but will not allow any
number of ..s to take us out of domain so http://.. will raise an exception.
Tests, (Don't know how to construct a round trip for this so doctest):
>>> # Relative URL within servers svn area
>>> resolvedots(
... "http://some.svn.server/svn/some_repo/../other_repo")
'http://some.svn.server/svn/other_repo'
>>> # Complex One
>>> resolvedots(
... "http://some.svn.server/svn/repo/../other/repo/../../other_repo")
'http://some.svn.server/svn/other_repo'
>>> # Another Complex One
>>> resolvedots(
... "http://some.svn.server/svn/repo/dir/subdir/../../../other_repo/dir")
'http://some.svn.server/svn/other_repo/dir'
>>> # Last Complex One - SVN Allows this & seen it used even if it is BAD!
>>> resolvedots(
... "http://svn.server/svn/my_repo/dir/subdir/../../other_dir")
'http://svn.server/svn/my_repo/other_dir'
>>> # Outside the SVN Area might be OK
>>> resolvedots(
... "http://svn.server/svn/some_repo/../../other_svn_repo")
'http://svn.server/other_svn_repo'
>>> # Complex One
>>> resolvedots(
... "http://some.svn.server/svn/repo/../other/repo/../../other_repo")
'http://some.svn.server/svn/other_repo'
>>> # On another server is not a relative URL should give an exception
>>> resolvedots(
... "http://some.svn.server/svn/some_repo/../../../other_server")
Traceback (most recent call last):
...
RelativeSourceError: Relative URL cannot be to another server
"""
orig = url.split('/')
fixed = []
for item in orig:
if item != '..':
fixed.append(item)
elif len(fixed) > 3: # Don't allow things to go out of domain
fixed.pop()
else:
raise RelativeSourceError(
'Relative URL cannot be to another server')
return '/'.join(fixed)
def resolvesource(ui, svnroot, source):
""" Resolve the source as either matching the scheme re or by resolving
relative URLs which start with ^ and my include relative .. references.
>>> root = 'http://some.svn.server/svn/some_repo'
>>> resolvesource(None, root, 'http://other.svn.server')
'http://other.svn.server'
>>> resolvesource(None, root, 'ssh://other.svn.server')
'ssh://other.svn.server'
>>> resolvesource(None, root, '^/other_repo')
'http://some.svn.server/svn/some_repo/other_repo'
>>> resolvesource(None, root, '^/sub_repo')
'http://some.svn.server/svn/some_repo/sub_repo'
>>> resolvesource(None, root, '^/../other_repo')
'http://some.svn.server/svn/other_repo'
>>> resolvesource(None, root, '^/../../../server/other_repo')
Traceback (most recent call last):
...
RelativeSourceError: Relative URL cannot be to another server
"""
if re_scheme.search(source):
return source
if source.startswith('^/'):
if svnroot is None:
raise RelativeSourceError()
return svnroot + source[1:]
return resolvedots(svnroot + source[1:])
ui.warn(_('ignoring unsupported non-fully qualified external: %r\n'
% source))
return None
......@@ -218,7 +289,7 @@ class externalsupdater:
self.ui = ui
def update(self, wpath, rev, source, pegrev):
path = self.repo.wjoin(wpath)
path = self.repo.wvfs.join(wpath)
revspec = []
if rev:
revspec = ['-r', rev]
......@@ -232,7 +303,7 @@ class externalsupdater:
if source == exturl:
if extrev != rev:
self.ui.status(_('updating external on %s@%s\n') %
(wpath, rev or 'HEAD'))
(wpath, rev or pegrev or 'HEAD'))
cwd = os.path.join(self.repo.root, path)
self.svn(['update'] + revspec, cwd)
return
......@@ -245,11 +316,12 @@ class externalsupdater:
pegrev = rev
if pegrev:
source = '%s@%s' % (source, pegrev)
self.ui.status(_('fetching external %s@%s\n') % (wpath, rev or 'HEAD'))
self.ui.status(_('fetching external %s@%s\n') %
(wpath, rev or pegrev or 'HEAD'))
self.svn(['co'] + revspec + [source, dest], cwd)
def delete(self, wpath):
path = self.repo.wjoin(wpath)
path = self.repo.wvfs.join(wpath)
if os.path.isdir(path):
self.ui.status(_('removing external %s\n') % wpath)
......@@ -268,12 +340,12 @@ class externalsupdater:
def svn(self, args, cwd):
args = ['svn'] + args
self.ui.debug(_('updating externals: %r, cwd=%s\n') % (args, cwd))
self.ui.note(_('updating externals: %r, cwd=%s\n') % (args, cwd))
shell = os.name == 'nt'
p = subprocess.Popen(args, cwd=cwd, shell=shell,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout:
self.ui.note(line)
self.ui.debug(line)
p.wait()
if p.returncode != 0:
raise hgutil.Abort("subprocess '%s' failed" % ' '.join(args))
......@@ -296,7 +368,7 @@ def updateexternals(ui, args, repo, **opts):
# Retrieve current externals status
try:
oldext = file(repo.join('svn/externals'), 'rb').read()
oldext = file(repo.vfs.join('svn/externals'), 'rb').read()
except IOError:
oldext = ''
newext = ''
......@@ -314,7 +386,7 @@ def updateexternals(ui, args, repo, **opts):
else:
raise hgutil.Abort(_('unknown update actions: %r') % action)
file(repo.join('svn/externals'), 'wb').write(newext)
file(repo.vfs.join('svn/externals'), 'wb').write(newext)
def getchanges(ui, repo, parentctx, exts):
"""Take a parent changectx and the new externals definitions as an
......@@ -421,18 +493,21 @@ class svnsubrepo(subrepo.svnsubrepo):
state = (source, state[1])
return super(svnsubrepo, self).get(state, *args, **kwargs)
def dirty(self, ignoreupdate=False):
def dirty(self, ignoreupdate=False, missing=False):
# You cannot compare anything with HEAD. Just accept it
# can be anything.
if hasattr(self, '_wcrevs'):
if hgutil.safehasattr(self, '_wcrevs'):
wcrevs = self._wcrevs()
else:
wcrev = self._wcrev()
wcrevs = (wcrev, wcrev)
if (('HEAD' in wcrevs or self._state[1] == 'HEAD' or
self._state[1] in wcrevs or ignoreupdate)
and not self._wcchanged()[0]):
return False
shouldcheck = ('HEAD' in wcrevs or self._state[1] == 'HEAD' or
self._state[1] in wcrevs or ignoreupdate or missing)
if shouldcheck:
changes, extchanges, wcmissing = self._wcchanged()
changed = changes or (missing and wcmissing)
if not changed:
return False
return True
def commit(self, text, user, date):
......@@ -447,3 +522,7 @@ class svnsubrepo(subrepo.svnsubrepo):
if self._state[1] == 'HEAD':
return 'HEAD'
return super(svnsubrepo, self).basestate()
if __name__ == "__main__":
import doctest
doctest.testmod()
......@@ -26,8 +26,7 @@ class SVNMeta(object):
# simple and public variables
self.ui = repo.ui
self.repo = repo
self.path = os.path.normpath(repo.join('..'))
self.firstpulled = 0
self.path = os.path.normpath(repo.vfs.join('..'))
self.lastdate = '1970-01-01 00:00:00 -0000'
self.addedtags = {}
self.deletedtags = {}
......@@ -52,9 +51,9 @@ class SVNMeta(object):
self.subdir = subdir
# generated properties that have a persistent file stored on disk
self._gen_cachedconfig('lastpulled', 0, configname=False)
self._gen_cachedconfig('defaultauthors', True)
self._gen_cachedconfig('caseignoreauthors', False)
self._gen_cachedconfig('mapauthorscmd', None)
self._gen_cachedconfig('defaulthost', self.uuid)
self._gen_cachedconfig('usebranchnames', True)
self._gen_cachedconfig('defaultmessage', '')
......@@ -69,7 +68,7 @@ class SVNMeta(object):
"""Return a cached value for a config option. If the cache is uninitialized
then try to read its value from disk. Option can be overridden by the
commandline.
name: property name, e.g. 'lastpulled'
name: property name, e.g. 'defaultauthors'
filename: name of file in .hg/svn
configname: commandline option name
default: default value
......@@ -94,6 +93,8 @@ class SVNMeta(object):
c = self.ui.configint('hgsubversion', configname, default)
elif isinstance(default, list):
c = self.ui.configlist('hgsubversion', configname, default)
elif isinstance(default, dict):
c = dict(self.ui.configitems(configname))
else:
c = self.ui.config('hgsubversion', configname, default)
......@@ -136,14 +137,14 @@ class SVNMeta(object):
filename = name
if configname is None:
configname = name
prop = property(lambda x: self._get_cachedconfig(name,
filename,
configname,
default,
pre=pre),
lambda x, y: self._set_cachedconfig(y,
name,
filename))
prop = property(lambda x: x._get_cachedconfig(name,
filename,
configname,
default,
pre=pre),
lambda x, y: x._set_cachedconfig(y,
name,
filename))
setattr(SVNMeta, name, prop)
def layout_from_subversion(self, svn, revision=None):
......@@ -218,7 +219,7 @@ class SVNMeta(object):
@property
def editor(self):
if not hasattr(self, '_editor'):
if not hgutil.safehasattr(self, '_editor'):
self._editor = editor.HgEditor(self)
return self._editor
......@@ -284,13 +285,15 @@ class SVNMeta(object):
return os.path.join(self.metapath, 'branch_info')
@property
def authors_file(self):
def authormap_file(self):
return os.path.join(self.metapath, 'authors')
@property
def authors(self):
if self._authors is None:
self._authors = maps.AuthorMap(self)
self._authors = maps.AuthorMap(
self.ui, self.authormap_file, self.defaulthost,
self.caseignoreauthors, self.mapauthorscmd, self.defaultauthors)
return self._authors
@property
......@@ -300,7 +303,7 @@ class SVNMeta(object):
@property
def filemap(self):
if self._filemap is None:
self._filemap = maps.FileMap(self)
self._filemap = maps.FileMap(self.ui, self.filemap_file)
return self._filemap
@property
......@@ -310,7 +313,7 @@ class SVNMeta(object):
@property
def branchmap(self):
if self._branchmap is None:
self._branchmap = maps.BranchMap(self)
self._branchmap = maps.BranchMap(self.ui, self.branchmap_file)
return self._branchmap
@property
......@@ -321,7 +324,7 @@ class SVNMeta(object):
@property
def tags(self):
if self._tags is None:
self._tags = maps.Tags(self)
self._tags = maps.Tags(self.ui, self.tagfile)
return self._tags
@property
......@@ -332,7 +335,7 @@ class SVNMeta(object):
@property
def tagmap(self):
if self._tagmap is None:
self._tagmap = maps.TagMap(self)
self._tagmap = maps.TagMap(self.ui, self.tagmap_file)
return self._tagmap
@property
......@@ -342,9 +345,34 @@ class SVNMeta(object):
@property
def revmap(self):
if self._revmap is None:
self._revmap = maps.RevMap(self)
lastpulled_path = os.path.join(self.metapath, 'lastpulled')
opts = {}
if self.revmapclass is maps.SqliteRevMap:
# sqlite revmap takes an optional option: sqlitepragmas
opts['sqlitepragmas'] = self.ui.configlist(
'hgsubversion', 'sqlitepragmas')
self._revmap = sel