view hgext/schemes.py @ 33649:377e8ddaebef stable

pathauditor: disable cache of audited paths by default (issue5628) The initial attempt was to discard cache when appropriate, but it appears to be error prone. We had to carefully inspect all places where audit() is called e.g. without actually updating filesystem, before removing files and directories, etc. So, this patch disables the cache of audited paths by default, and enables it only for the following cases: - short-lived auditor objects - repo.vfs, repo.svfs, and repo.cachevfs, which are managed directories and considered sort of append-only (a file/directory would never be replaced with a symlink) There would be more cacheable vfs objects (e.g. mq.queue.opener), but I decided not to inspect all of them in this patch. We can make them cached later. Benchmark result: - using old clone of http://selenic.com/repo/linux-2.6/ (38319 files) - on tmpfs - run HGRCPATH=/dev/null hg up -q --time tip && hg up -q null - try 4 times and take the last three results original: real 7.480 secs (user 1.140+22.760 sys 0.150+1.690) real 8.010 secs (user 1.070+22.280 sys 0.170+2.120) real 7.470 secs (user 1.120+22.390 sys 0.120+1.910) clearcache (the other series): real 7.680 secs (user 1.120+23.420 sys 0.140+1.970) real 7.670 secs (user 1.110+23.620 sys 0.130+1.810) real 7.740 secs (user 1.090+23.510 sys 0.160+1.940) enable cache only for vfs and svfs (this series): real 8.730 secs (user 1.500+25.190 sys 0.260+2.260) real 8.750 secs (user 1.490+25.170 sys 0.250+2.340) real 9.010 secs (user 1.680+25.340 sys 0.280+2.540) remove cache function at all (for reference): real 9.620 secs (user 1.440+27.120 sys 0.250+2.980) real 9.420 secs (user 1.400+26.940 sys 0.320+3.130) real 9.760 secs (user 1.530+27.270 sys 0.250+2.970)
author Yuya Nishihara <yuya@tcha.org>
date Wed, 26 Jul 2017 22:10:15 +0900
parents 46ba2cdda476
children 75979c8d4572
line wrap: on
line source

# Copyright 2009, Alexander Solovyov <piranha@piranha.org.ua>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

"""extend schemes with shortcuts to repository swarms

This extension allows you to specify shortcuts for parent URLs with a
lot of repositories to act like a scheme, for example::

  [schemes]
  py = http://code.python.org/hg/

After that you can use it like::

  hg clone py://trunk/

Additionally there is support for some more complex schemas, for
example used by Google Code::

  [schemes]
  gcode = http://{1}.googlecode.com/hg/

The syntax is taken from Mercurial templates, and you have unlimited
number of variables, starting with ``{1}`` and continuing with
``{2}``, ``{3}`` and so on. This variables will receive parts of URL
supplied, split by ``/``. Anything not specified as ``{part}`` will be
just appended to an URL.

For convenience, the extension adds these schemes by default::

  [schemes]
  py = http://hg.python.org/
  bb = https://bitbucket.org/
  bb+ssh = ssh://hg@bitbucket.org/
  gcode = https://{1}.googlecode.com/hg/
  kiln = https://{1}.kilnhg.com/Repo/

You can override a predefined scheme by defining a new scheme with the
same name.
"""
from __future__ import absolute_import

import os
import re

from mercurial.i18n import _
from mercurial import (
    error,
    extensions,
    hg,
    pycompat,
    registrar,
    templater,
    util,
)

cmdtable = {}
command = registrar.command(cmdtable)
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
testedwith = 'ships-with-hg-core'

_partre = re.compile(br'\{(\d+)\}')

class ShortRepository(object):
    def __init__(self, url, scheme, templater):
        self.scheme = scheme
        self.templater = templater
        self.url = url
        try:
            self.parts = max(map(int, _partre.findall(self.url)))
        except ValueError:
            self.parts = 0

    def __repr__(self):
        return '<ShortRepository: %s>' % self.scheme

    def instance(self, ui, url, create):
        url = self.resolve(url)
        return hg._peerlookup(url).instance(ui, url, create)

    def resolve(self, url):
        # Should this use the util.url class, or is manual parsing better?
        try:
            url = url.split('://', 1)[1]
        except IndexError:
            raise error.Abort(_("no '://' in scheme url '%s'") % url)
        parts = url.split('/', self.parts)
        if len(parts) > self.parts:
            tail = parts[-1]
            parts = parts[:-1]
        else:
            tail = ''
        context = dict((str(i + 1), v) for i, v in enumerate(parts))
        return ''.join(self.templater.process(self.url, context)) + tail

def hasdriveletter(orig, path):
    if path:
        for scheme in schemes:
            if path.startswith(scheme + ':'):
                return False
    return orig(path)

schemes = {
    'py': 'http://hg.python.org/',
    'bb': 'https://bitbucket.org/',
    'bb+ssh': 'ssh://hg@bitbucket.org/',
    'gcode': 'https://{1}.googlecode.com/hg/',
    'kiln': 'https://{1}.kilnhg.com/Repo/'
    }

def extsetup(ui):
    schemes.update(dict(ui.configitems('schemes')))
    t = templater.engine(lambda x: x)
    for scheme, url in schemes.items():
        if (pycompat.osname == 'nt' and len(scheme) == 1 and scheme.isalpha()
            and os.path.exists('%s:\\' % scheme)):
            raise error.Abort(_('custom scheme %s:// conflicts with drive '
                               'letter %s:\\\n') % (scheme, scheme.upper()))
        hg.schemes[scheme] = ShortRepository(url, scheme, t)

    extensions.wrapfunction(util, 'hasdriveletter', hasdriveletter)

@command('debugexpandscheme', norepo=True)
def expandscheme(ui, url, **opts):
    """given a repo path, provide the scheme-expanded path
    """
    repo = hg._peerlookup(url)
    if isinstance(repo, ShortRepository):
        url = repo.resolve(url)
    ui.write(url + '\n')