hgext/schemes.py
author Martijn Pieters <mjpieters@fb.com>
Thu, 10 Mar 2016 16:04:09 +0000
changeset 28443 49d65663d7e4
parent 28379 27a9032374a7
child 29205 a0939666b836
permissions -rw-r--r--
fsmonitor: hook up state-enter, state-leave signals Keeping the codebase in sync with upstream: Watchman 4.4 introduced an advanced settling feature that allows publishing tools to notify subscribing tools of the boundaries for important filesystem operations. https://facebook.github.io/watchman/docs/cmd/subscribe.html#advanced-settling has more information about how this feature works. This diff connects a signal that we're calling `hg.update` to the mercurial update function so that mercurial can indirectly notify tools (such as IDEs or build machinery) when it is changing the working copy. This will allow those tools to pause their normal actions as the files are changing and defer them until the end of the operation. In addition to sending the enter/leave signals for the state, we are able to publish useful metadata along the same channel. In this case we are passing the following pieces of information: 1. destination revision hash 2. An estimate of the distance between the current state and the target state 3. A success indicator. 4. Whether it is a partial update The distance is estimate may be useful to tools that wish to change their strategy after the update has complete. For example, a large update may be efficient to deal with by walking some internal state in the subscriber rather than feeding every individual file notification through its normal (small) delta mechanism. We estimate the distance by comparing the repository revision number. In some cases we cannot come up with a number so we report 0. This is ok; we're offering this for informational purposes only and don't guarantee its accuracy. The success indicator is only really meaningful when we generate the state-leave notification; it indicates the overall success of the update.

# 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 import (
    cmdutil,
    error,
    extensions,
    hg,
    templater,
    util,
)
from mercurial.i18n import _

cmdtable = {}
command = cmdutil.command(cmdtable)
# Note for extension authors: ONLY specify testedwith = 'internal' 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 = 'internal'


class ShortRepository(object):
    def __init__(self, url, scheme, templater):
        self.scheme = scheme
        self.templater = templater
        self.url = url
        try:
            self.parts = max(map(int, re.findall(r'\{(\d+)\}', 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 (os.name == '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')