[PATCH 11 of 11 sparse] dirstate: integrate sparse matcher with _ignore (API)
Gregory Szorc
gregory.szorc at gmail.com
Sat Jul 8 19:29:06 EDT 2017
# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1499555309 25200
# Sat Jul 08 16:08:29 2017 -0700
# Node ID 94f98bc84936defadb959e31012555dba170d8cd
# Parent a2867557f9c2314aeea19a946dfb8e167def4fb8
dirstate: integrate sparse matcher with _ignore (API)
Previously, the sparse extension monkeypatched dirstate._ignore
(using exotic means because dirstate._ignore was a file cache
property).
In this commit, we move the logic from dirstate_ignore to a new method,
_hgignorematch(). We replace dirstate._ignore with a regular property.
The implementation of that property obtains a sparse matcher. If one
is defined, it returns a unionmatcher.
The implementation is funcionally equivalent to the old code in
the sparse extension. However, the property caching of dirstate._ignore
has been dropped. This means that every attribute lookup will:
* resolve a sparse matcher by calling sparse.matcher()
* call matcher.always()
* possibly construct a new unionmatcher if the sparse matcher is
defined
The overhead is unfortunate. However, it is simple. sparse.matcher()
should be relatively fast. And I have plans for caching the sparse
matcher more intelligently to avoid all these lookups. Furthermore,
I don't see any obvious code paths where dirstate._ignore is resolved
in tight loops, so the perf impact may be mostly irrelevant.
After this commit, the sparse extension now only contains modifications
to commands: all code directly supporting sparse checkouts is now in
core!
diff --git a/hgext/sparse.py b/hgext/sparse.py
--- a/hgext/sparse.py
+++ b/hgext/sparse.py
@@ -78,11 +78,9 @@ from mercurial.i18n import _
from mercurial import (
cmdutil,
commands,
- dirstate,
error,
extensions,
hg,
- match as matchmod,
registrar,
sparse,
util,
@@ -103,23 +101,6 @@ def extsetup(ui):
_setupclone(ui)
_setuplog(ui)
_setupadd(ui)
- _setupdirstate(ui)
-
-def replacefilecache(cls, propname, replacement):
- """Replace a filecache property with a new class. This allows changing the
- cache invalidation condition."""
- origcls = cls
- assert callable(replacement)
- while cls is not object:
- if propname in cls.__dict__:
- orig = cls.__dict__[propname]
- setattr(cls, propname, replacement(orig))
- break
- cls = cls.__bases__[0]
-
- if cls is object:
- raise AttributeError(_("type '%s' has no property '%s'") % (origcls,
- propname))
def _setuplog(ui):
entry = commands.table['^log|history']
@@ -187,42 +168,6 @@ def _setupadd(ui):
extensions.wrapcommand(commands.table, 'add', _add)
-def _setupdirstate(ui):
- """Modify the dirstate to prevent stat'ing excluded files,
- and to prevent modifications to files outside the checkout.
- """
-
- # The atrocity below is needed to wrap dirstate._ignore. It is a cached
- # property, which means normal function wrapping doesn't work.
- class ignorewrapper(object):
- def __init__(self, orig):
- self.orig = orig
- self.origignore = None
- self.func = None
- self.sparsematch = None
-
- def __get__(self, obj, type=None):
- origignore = self.orig.__get__(obj)
-
- sparsematch = obj._sparsematcher
- if sparsematch.always():
- return origignore
-
- if self.sparsematch != sparsematch or self.origignore != origignore:
- self.func = matchmod.unionmatcher([
- origignore, matchmod.negatematcher(sparsematch)])
- self.sparsematch = sparsematch
- self.origignore = origignore
- return self.func
-
- def __set__(self, obj, value):
- return self.orig.__set__(obj, value)
-
- def __delete__(self, obj):
- return self.orig.__delete__(obj)
-
- replacefilecache(dirstate.dirstate, '_ignore', ignorewrapper)
-
@command('^debugsparse', [
('I', 'include', False, _('include files in the sparse checkout')),
('X', 'exclude', False, _('exclude files in the sparse checkout')),
diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -248,7 +248,7 @@ class dirstate(object):
return self._dirs
@rootcache('.hgignore')
- def _ignore(self):
+ def _hgignorematch(self):
files = self._ignorefiles()
if not files:
return matchmod.never(self._root, '')
@@ -256,6 +256,15 @@ class dirstate(object):
pats = ['include:%s' % f for f in files]
return matchmod.match(self._root, '', [], pats, warn=self._ui.warn)
+ @property
+ def _ignore(self):
+ sparsematch = self._sparsematcher
+ if sparsematch.always():
+ return self._hgignorematch
+
+ return matchmod.unionmatcher([self._hgignorematch,
+ matchmod.negatematcher(sparsematch)])
+
@propertycache
def _slash(self):
return self._ui.configbool('ui', 'slash') and pycompat.ossep != '/'
@@ -516,7 +525,7 @@ class dirstate(object):
for a in ("_map", "_copymap", "_identity",
"_filefoldmap", "_dirfoldmap", "_branch",
- "_pl", "_dirs", "_ignore", "_nonnormalset",
+ "_pl", "_dirs", "_hgignorematcher", "_nonnormalset",
"_otherparentset"):
if a in self.__dict__:
delattr(self, a)
More information about the Mercurial-devel
mailing list