Mercurial > hg
diff hgext/narrow/narrowrevlog.py @ 36079:a2a6e724d61a
narrow: import experimental extension from narrowhg revision cb51d673e9c5
Adjustments:
* renamed src to hgext/narrow
* marked extension experimental
* added correct copyright header where it was missing
* updated hgrc extension enable line in library.sh
* renamed library.sh to narrow-library.sh
* dropped all files from repo root as they're not interesting
* dropped test-pyflakes.t, test-check-code.t and test-check-py3-compat.t
* renamed remaining tests to all be test-narrow-* when they didn't already
* fixed test-narrow-expanddirstate.t to refer to narrow and not narrowhg
* fixed tests that wanted `update -C .` instead of `merge --abort`
* corrected a two-space indent in narrowspec.py
* added a missing _() in narrowcommands.py
* fixed imports to pass the import checker
* narrow only adds its --include and --exclude to clone if sparse isn't
enabled to avoid breaking test-duplicateoptions.py. This is a kludge,
and we'll need to come up with a better solution in the future.
These were more or less the minimum to import something that would
pass tests and not create a bunch of files we'll never use.
Changes I intend to make as followups:
* rework the test-narrow-*-tree.t tests to use the new testcases
functionality in run-tests.py
* remove lots of monkeypatches of core things
Differential Revision: https://phab.mercurial-scm.org/D1974
author | Augie Fackler <augie@google.com> |
---|---|
date | Mon, 29 Jan 2018 16:19:33 -0500 |
parents | |
children | adc8e1fe2f46 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/narrow/narrowrevlog.py Mon Jan 29 16:19:33 2018 -0500 @@ -0,0 +1,163 @@ +# narrowrevlog.py - revlog storing irrelevant nodes as "ellipsis" nodes +# +# 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 import ( + manifest, + revlog, + util, +) + +ELLIPSIS_NODE_FLAG = 1 << 14 +revlog.REVIDX_KNOWN_FLAGS |= ELLIPSIS_NODE_FLAG +if (util.safehasattr(revlog, 'REVIDX_FLAGS_ORDER') and + ELLIPSIS_NODE_FLAG not in revlog.REVIDX_FLAGS_ORDER): + revlog.REVIDX_FLAGS_ORDER.append(ELLIPSIS_NODE_FLAG) + +def readtransform(self, text): + return text, False + +def writetransform(self, text): + return text, False + +def rawtransform(self, text): + return False + +if util.safehasattr(revlog, 'addflagprocessor'): + revlog.addflagprocessor(ELLIPSIS_NODE_FLAG, + (readtransform, writetransform, rawtransform)) + +def setup(): + # We just wanted to add the flag processor, which is done at module + # load time. + pass + +class excludeddir(manifest.treemanifest): + def __init__(self, dir, node): + super(excludeddir, self).__init__(dir) + self._node = node + # Add an empty file, which will be included by iterators and such, + # appearing as the directory itself (i.e. something like "dir/") + self._files[''] = node + self._flags[''] = 't' + + # Manifests outside the narrowspec should never be modified, so avoid + # copying. This makes a noticeable difference when there are very many + # directories outside the narrowspec. Also, it makes sense for the copy to + # be of the same type as the original, which would not happen with the + # super type's copy(). + def copy(self): + return self + +class excludeddirmanifestctx(manifest.treemanifestctx): + def __init__(self, dir, node): + self._dir = dir + self._node = node + + def read(self): + return excludeddir(self._dir, self._node) + + def write(self, *args): + raise AssertionError('Attempt to write manifest from excluded dir %s' % + self._dir) + +class excludedmanifestrevlog(manifest.manifestrevlog): + def __init__(self, dir): + self._dir = dir + + def __len__(self): + raise AssertionError('Attempt to get length of excluded dir %s' % + self._dir) + + def rev(self, node): + raise AssertionError('Attempt to get rev from excluded dir %s' % + self._dir) + + def linkrev(self, node): + raise AssertionError('Attempt to get linkrev from excluded dir %s' % + self._dir) + + def node(self, rev): + raise AssertionError('Attempt to get node from excluded dir %s' % + self._dir) + + def add(self, *args, **kwargs): + # We should never write entries in dirlogs outside the narrow clone. + # However, the method still gets called from writesubtree() in + # _addtree(), so we need to handle it. We should possibly make that + # avoid calling add() with a clean manifest (_dirty is always False + # in excludeddir instances). + pass + +def makenarrowmanifestrevlog(mfrevlog, repo): + if util.safehasattr(mfrevlog, '_narrowed'): + return + + class narrowmanifestrevlog(mfrevlog.__class__): + # This function is called via debug{revlog,index,data}, but also during + # at least some push operations. This will be used to wrap/exclude the + # child directories when using treemanifests. + def dirlog(self, dir): + if dir and not dir.endswith('/'): + dir = dir + '/' + if not repo.narrowmatch().visitdir(dir[:-1] or '.'): + return excludedmanifestrevlog(dir) + result = super(narrowmanifestrevlog, self).dirlog(dir) + makenarrowmanifestrevlog(result, repo) + return result + + mfrevlog.__class__ = narrowmanifestrevlog + mfrevlog._narrowed = True + +def makenarrowmanifestlog(mfl, repo): + class narrowmanifestlog(mfl.__class__): + def get(self, dir, node, verify=True): + if not repo.narrowmatch().visitdir(dir[:-1] or '.'): + return excludeddirmanifestctx(dir, node) + return super(narrowmanifestlog, self).get(dir, node, verify=verify) + mfl.__class__ = narrowmanifestlog + +def makenarrowfilelog(fl, narrowmatch): + class narrowfilelog(fl.__class__): + def renamed(self, node): + m = super(narrowfilelog, self).renamed(node) + if m and not narrowmatch(m[0]): + return None + return m + + def size(self, rev): + # We take advantage of the fact that remotefilelog + # lacks a node() method to just skip the + # rename-checking logic when on remotefilelog. This + # might be incorrect on other non-revlog-based storage + # engines, but for now this seems to be fine. + if util.safehasattr(self, 'node'): + node = self.node(rev) + # Because renamed() is overridden above to + # sometimes return None even if there is metadata + # in the revlog, size can be incorrect for + # copies/renames, so we need to make sure we call + # the super class's implementation of renamed() + # for the purpose of size calculation. + if super(narrowfilelog, self).renamed(node): + return len(self.read(node)) + return super(narrowfilelog, self).size(rev) + + def cmp(self, node, text): + different = super(narrowfilelog, self).cmp(node, text) + if different: + # Similar to size() above, if the file was copied from + # a file outside the narrowspec, the super class's + # would have returned True because we tricked it into + # thinking that the file was not renamed. + if super(narrowfilelog, self).renamed(node): + t2 = self.read(node) + return t2 != text + return different + + fl.__class__ = narrowfilelog