hgext/narrow/narrowdirstate.py
author Martin von Zweigbergk <martinvonz@google.com>
Fri, 03 Aug 2018 11:09:41 -0700
changeset 38908 ad24b581e4d9
parent 38875 fed6fe856333
child 39961 1a7d901a0a0c
permissions -rw-r--r--
narrow: call narrowspec.{save,restore,clear}backup directly I want to move .hg/narrowspec to .hg/store/narrowspec and we need to decouple the narrowspec update from the dirstate update for that. This patch lets the callers call the narrowspec backup functions directly, in addition to the dirstate backup functions. The narrowspec methods are made to check if narrowing is enabled. For that, a repo instance was needed, which all the callers luckily already had available. Differential Revision: https://phab.mercurial-scm.org/D4096

# 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):
            dirstate = repo.dirstate
            narrowmatch = repo.narrowmatch()
            for f in args:
                if f is not None and not narrowmatch(f) and f not in dirstate:
                    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