view mercurial/statichttprepo.py @ 12536:208fc9ad6a48

alias: only allow global options before a shell alias, pass later ones through This patch refactors the dispatch code to change how arguments to shell aliases are handled. A separate "pass" to determine whether a command is a shell alias has been added. The rough steps dispatch now performs when a command is given are these: * Parse all arguments up to the command name. * If any arguments such as --repository or --cwd are given (which could change the config file used, and therefore the definition of aliases), they are taken into account. * We determine whether the command is a shell alias. * If so, execute the alias. The --repo and --cwd arguments are still in effect. Any arguments *after* the command name are passed unchanged through to the shell command (and interpolated as normal. * If the command is *not* a shell alias, the dispatching is effectively "reset" and reparsed as normal in its entirety. The net effect of this patch is to make shell alias commands behave as you would expect. Any arguments you give to a shell alias *after* the alias name are passed through unchanged. This lets you do something like the following: [alias] filereleased = !$HG log -r 'descendants(adds("$1")) and tagged()' -l1 $2 $3 $4 $5 $ hg filereleased hgext/bookmarks.py --style compact Previously the `--style compact` part would fail because Mercurial would interpret those arguments as arguments to the alias command itself (which doesn't take any arguments). Also: running something like `hg -R ~/src/hg-crew filereleased hgext/bookmarks.py` when `filereleased` is only defined in that repo's config will now work. These global arguments can *only* be given to a shell alias *before* the alias name. For example, this will *not* work in the above situation: $ hg filereleased -R ~/src/hg-crew hgext/bookmarks.py The reason for this is that you may want to pass arguments like --repository to the alias (or, more likely, their short versions like -R): [alias] own = !chown $@ `$HG root` $ hg own steve $ hg own -R steve
author Steve Losh <steve@stevelosh.com>
date Tue, 24 Aug 2010 18:25:33 -0400
parents aaf9968bd8b7
children 61c9bc3da402
line wrap: on
line source

# statichttprepo.py - simple http repository class for mercurial
#
# This provides read-only repo access to repositories exported via static http
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from i18n import _
import changelog, byterange, url, error
import localrepo, manifest, util, store
import urllib, urllib2, errno

class httprangereader(object):
    def __init__(self, url, opener):
        # we assume opener has HTTPRangeHandler
        self.url = url
        self.pos = 0
        self.opener = opener
        self.name = url
    def seek(self, pos):
        self.pos = pos
    def read(self, bytes=None):
        req = urllib2.Request(self.url)
        end = ''
        if bytes:
            end = self.pos + bytes - 1
        req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))

        try:
            f = self.opener.open(req)
            data = f.read()
            if hasattr(f, 'getcode'):
                # python 2.6+
                code = f.getcode()
            elif hasattr(f, 'code'):
                # undocumented attribute, seems to be set in 2.4 and 2.5
                code = f.code
            else:
                # Don't know how to check, hope for the best.
                code = 206
        except urllib2.HTTPError, inst:
            num = inst.code == 404 and errno.ENOENT or None
            raise IOError(num, inst)
        except urllib2.URLError, inst:
            raise IOError(None, inst.reason[1])

        if code == 200:
            # HTTPRangeHandler does nothing if remote does not support
            # Range headers and returns the full entity. Let's slice it.
            if bytes:
                data = data[self.pos:self.pos + bytes]
            else:
                data = data[self.pos:]
        elif bytes:
            data = data[:bytes]
        self.pos += len(data)
        return data
    def __iter__(self):
        return iter(self.read().splitlines(1))
    def close(self):
        pass

def build_opener(ui, authinfo):
    # urllib cannot handle URLs with embedded user or passwd
    urlopener = url.opener(ui, authinfo)
    urlopener.add_handler(byterange.HTTPRangeHandler())

    def opener(base):
        """return a function that opens files over http"""
        p = base
        def o(path, mode="r", atomictemp=None):
            if 'a' in mode or 'w' in mode:
                raise IOError('Permission denied')
            f = "/".join((p, urllib.quote(path)))
            return httprangereader(f, urlopener)
        return o

    opener.options = {'nonlazy': 1}
    return opener

class statichttprepository(localrepo.localrepository):
    def __init__(self, ui, path):
        self._url = path
        self.ui = ui

        self.root = path
        self.path, authinfo = url.getauthinfo(path.rstrip('/') + "/.hg")

        opener = build_opener(ui, authinfo)
        self.opener = opener(self.path)

        # find requirements
        try:
            requirements = self.opener("requires").read().splitlines()
        except IOError, inst:
            if inst.errno != errno.ENOENT:
                raise
            # check if it is a non-empty old-style repository
            try:
                self.opener("00changelog.i").read(1)
            except IOError, inst:
                if inst.errno != errno.ENOENT:
                    raise
                # we do not care about empty old-style repositories here
                msg = _("'%s' does not appear to be an hg repository") % path
                raise error.RepoError(msg)
            requirements = []

        # check them
        for r in requirements:
            if r not in self.supported:
                raise error.RepoError(_("requirement '%s' not supported") % r)

        # setup store
        def pjoin(a, b):
            return a + '/' + b
        self.store = store.store(requirements, self.path, opener, pjoin)
        self.spath = self.store.path
        self.sopener = self.store.opener
        self.sjoin = self.store.join

        self.manifest = manifest.manifest(self.sopener)
        self.changelog = changelog.changelog(self.sopener)
        self._tags = None
        self.nodetagscache = None
        self._branchcache = None
        self._branchcachetip = None
        self.encodepats = None
        self.decodepats = None
        self.capabilities.remove("pushkey")

    def url(self):
        return self._url

    def local(self):
        return False

    def lock(self, wait=True):
        raise util.Abort(_('cannot lock static-http repository'))

def instance(ui, path, create):
    if create:
        raise util.Abort(_('cannot create new static-http repository'))
    return statichttprepository(ui, path[7:])