hgext/narrow/narrowdirstate.py
author Martin von Zweigbergk <martinvonz@google.com>
Mon, 01 Oct 2018 15:29:31 -0700
changeset 39960 1a7d901a0a0c
parent 38869 ad24b581e4d9
child 40086 41fcdfe3bfeb
permissions -rw-r--r--
narrow: avoid looking up dirstate again when editing dirstate The narrow extension overrides the dirstate editing functions to restrict paths outside the narrowspec. These overrides access the dirstate from repo.dirstate instead of using the "self" reference passed to the overridden functions. I don't see a reason for this and it caused me problems with a later patch (it caused infinite recursion when I modified localrepo.dirstate()), so let's change it. Differential Revision: https://phab.mercurial-scm.org/D4829

# narrowdirstate.py - extensions to mercurial dirstate to support narrow clones
#
# Copyright 2017 Google, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from __future__ import absolute_import

from mercurial.i18n import _
from mercurial import (
    error,
    match as matchmod,
)

def wrapdirstate(repo, dirstate):
    """Add narrow spec dirstate ignore, block changes outside narrow spec."""

    def _editfunc(fn):
        def _wrapper(self, *args):
            narrowmatch = repo.narrowmatch()
            for f in args:
                if f is not None and not narrowmatch(f) and f not in self:
                    raise error.Abort(_("cannot track '%s' - it is outside " +
                        "the narrow clone") % f)
            return fn(self, *args)
        return _wrapper

    class narrowdirstate(dirstate.__class__):
        def walk(self, match, subrepos, unknown, ignored, full=True,
                 narrowonly=True):
            if narrowonly:
                # hack to not exclude explicitly-specified paths so that they
                # can be warned later on e.g. dirstate.add()
                em = matchmod.exact(match._root, match._cwd, match.files())
                nm = matchmod.unionmatcher([repo.narrowmatch(), em])
                match = matchmod.intersectmatchers(match, nm)
            return super(narrowdirstate, self).walk(match, subrepos, unknown,
                                                    ignored, full)

        # Prevent adding/editing/copying/deleting files that are outside the
        # sparse checkout
        @_editfunc
        def normal(self, *args):
            return super(narrowdirstate, self).normal(*args)

        @_editfunc
        def add(self, *args):
            return super(narrowdirstate, self).add(*args)

        @_editfunc
        def normallookup(self, *args):
            return super(narrowdirstate, self).normallookup(*args)

        @_editfunc
        def copy(self, *args):
            return super(narrowdirstate, self).copy(*args)

        @_editfunc
        def remove(self, *args):
            return super(narrowdirstate, self).remove(*args)

        @_editfunc
        def merge(self, *args):
            return super(narrowdirstate, self).merge(*args)

        def rebuild(self, parent, allfiles, changedfiles=None):
            if changedfiles is None:
                # Rebuilding entire dirstate, let's filter allfiles to match the
                # narrowspec.
                allfiles = [f for f in allfiles if repo.narrowmatch()(f)]
            super(narrowdirstate, self).rebuild(parent, allfiles, changedfiles)

    dirstate.__class__ = narrowdirstate
    return dirstate