hgext/narrow/narrowrepo.py
author Augie Fackler <augie@google.com>
Mon, 29 Jan 2018 16:19:33 -0500
changeset 36079 a2a6e724d61a
child 36088 8c31187b6717
permissions -rw-r--r--
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

# narrowrepo.py - repository which supports narrow revlogs, lazy loading
#
# 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 (
    bundlerepo,
    localrepo,
    match as matchmod,
    scmutil,
)

from .. import (
    share,
)

from . import (
    narrowrevlog,
    narrowspec,
)

requirement = 'narrowhg'

def wrappostshare(orig, sourcerepo, destrepo, **kwargs):
    orig(sourcerepo, destrepo, **kwargs)
    if requirement in sourcerepo.requirements:
        with destrepo.wlock():
            with destrepo.vfs('shared', 'a') as fp:
                fp.write(narrowspec.FILENAME + '\n')

def unsharenarrowspec(orig, ui, repo, repopath):
    if (requirement in repo.requirements
        and repo.path == repopath and repo.shared()):
        srcrepo = share._getsrcrepo(repo)
        with srcrepo.vfs(narrowspec.FILENAME) as f:
            spec = f.read()
        with repo.vfs(narrowspec.FILENAME, 'w') as f:
            f.write(spec)
    return orig(ui, repo, repopath)

def wraprepo(repo, opts_narrow):
    """Enables narrow clone functionality on a single local repository."""

    cacheprop = localrepo.storecache
    if isinstance(repo, bundlerepo.bundlerepository):
        # We have to use a different caching property decorator for
        # bundlerepo because storecache blows up in strange ways on a
        # bundlerepo. Fortunately, there's no risk of data changing in
        # a bundlerepo.
        cacheprop = lambda name: localrepo.unfilteredpropertycache

    class narrowrepository(repo.__class__):

        def _constructmanifest(self):
            manifest = super(narrowrepository, self)._constructmanifest()
            narrowrevlog.makenarrowmanifestrevlog(manifest, repo)
            return manifest

        @cacheprop('00manifest.i')
        def manifestlog(self):
            mfl = super(narrowrepository, self).manifestlog
            narrowrevlog.makenarrowmanifestlog(mfl, self)
            return mfl

        def file(self, f):
            fl = super(narrowrepository, self).file(f)
            narrowrevlog.makenarrowfilelog(fl, self.narrowmatch())
            return fl

        @localrepo.repofilecache(narrowspec.FILENAME)
        def narrowpats(self):
            return narrowspec.load(self)

        @localrepo.repofilecache(narrowspec.FILENAME)
        def _narrowmatch(self):
            include, exclude = self.narrowpats
            if not opts_narrow and not include and not exclude:
                return matchmod.always(self.root, '')
            return narrowspec.match(self.root, include=include, exclude=exclude)

        # TODO(martinvonz): make this property-like instead?
        def narrowmatch(self):
            return self._narrowmatch

        def setnarrowpats(self, newincludes, newexcludes):
            narrowspec.save(self, newincludes, newexcludes)
            self.invalidate(clearfilecache=True)

        # I'm not sure this is the right place to do this filter.
        # context._manifestmatches() would probably be better, or perhaps
        # move it to a later place, in case some of the callers do want to know
        # which directories changed. This seems to work for now, though.
        def status(self, *args, **kwargs):
            s = super(narrowrepository, self).status(*args, **kwargs)
            narrowmatch = self.narrowmatch()
            modified = filter(narrowmatch, s.modified)
            added = filter(narrowmatch, s.added)
            removed = filter(narrowmatch, s.removed)
            deleted = filter(narrowmatch, s.deleted)
            unknown = filter(narrowmatch, s.unknown)
            ignored = filter(narrowmatch, s.ignored)
            clean = filter(narrowmatch, s.clean)
            return scmutil.status(modified, added, removed, deleted, unknown,
                                  ignored, clean)

    repo.__class__ = narrowrepository