2016-03-29 04:44:56 +03:00
|
|
|
# fastmanifest.py
|
|
|
|
#
|
|
|
|
# Copyright 2016 Facebook, Inc.
|
|
|
|
#
|
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
"""
|
|
|
|
This extension adds fastmanifest, a treemanifest disk cache for speeding up
|
|
|
|
manifest comparison. It also contains utilities to investigate manifest access
|
|
|
|
patterns.
|
|
|
|
|
|
|
|
|
|
|
|
Configuration options:
|
|
|
|
|
|
|
|
[fastmanifest]
|
|
|
|
logfile = "" # Filename, is not empty will log access to any manifest
|
2016-04-13 00:00:35 +03:00
|
|
|
|
|
|
|
|
|
|
|
Description:
|
|
|
|
|
|
|
|
`manifestaccesslogger` logs manifest accessed to a logfile specified with
|
|
|
|
the option fastmanifest.logfile
|
|
|
|
|
|
|
|
`fastmanifesttocache` is a revset of relevant manifests to cache
|
|
|
|
|
|
|
|
`hybridmanifest` is a proxy class for flat and cached manifest that loads
|
|
|
|
manifest from cache or from disk.
|
|
|
|
It chooses what kind of manifest is relevant to create based on the operation,
|
|
|
|
ideally the fastest.
|
|
|
|
TODO instantiate fastmanifest when they are more suitable
|
|
|
|
|
|
|
|
`manifestcache` is the class handling the interface with the cache, it supports
|
|
|
|
caching flat and fast manifest and retrieving them.
|
|
|
|
TODO logic for loading fastmanifest
|
|
|
|
TODO logic for saving fastmanifest
|
|
|
|
TODO garbage collection
|
|
|
|
|
|
|
|
`manifestfactory` is a class whose method wraps manifest creating method of
|
|
|
|
manifest.manifest. It intercepts the calls to build hybridmanifest instead of
|
|
|
|
regularmanifests. We use a class for that to allow sharing the ui object that
|
|
|
|
is not normally accessible to manifests.
|
|
|
|
|
|
|
|
`debugcachemanifest` is a command calling `_cachemanifest`, a function to add
|
|
|
|
manifests to the cache and manipulate what is cached. It allows caching fast
|
|
|
|
and flat manifest, asynchronously and synchronously.
|
|
|
|
TODO handle asynchronous save
|
|
|
|
TODO size limit handling
|
2016-03-29 04:44:56 +03:00
|
|
|
"""
|
2016-04-13 00:00:35 +03:00
|
|
|
import os
|
2016-04-18 23:18:04 +03:00
|
|
|
import fastmanifest_wrapper
|
2016-04-13 00:00:35 +03:00
|
|
|
|
|
|
|
from mercurial import cmdutil
|
2016-03-29 04:44:56 +03:00
|
|
|
from mercurial import extensions
|
|
|
|
from mercurial import manifest
|
2016-04-01 02:00:06 +03:00
|
|
|
from mercurial import revset
|
2016-04-13 00:00:35 +03:00
|
|
|
from mercurial import revlog
|
|
|
|
from mercurial import scmutil
|
2016-04-08 21:46:18 +03:00
|
|
|
from mercurial import util
|
2016-03-29 04:44:56 +03:00
|
|
|
|
2016-04-13 00:00:35 +03:00
|
|
|
CACHE_SUBDIR = "manifestcache"
|
|
|
|
cmdtable = {}
|
|
|
|
command = cmdutil.command(cmdtable)
|
|
|
|
|
2016-03-29 04:44:56 +03:00
|
|
|
|
|
|
|
class manifestaccesslogger(object):
|
2016-04-08 21:46:18 +03:00
|
|
|
"""Class to log manifest access and confirm our assumptions"""
|
2016-03-29 04:44:56 +03:00
|
|
|
def __init__(self, logfile):
|
|
|
|
self._logfile = logfile
|
|
|
|
|
|
|
|
def revwrap(self, orig, *args, **kwargs):
|
2016-04-08 21:46:18 +03:00
|
|
|
"""Wraps manifest.rev and log access"""
|
2016-03-29 04:44:56 +03:00
|
|
|
r = orig(*args, **kwargs)
|
|
|
|
try:
|
|
|
|
with open(self._logfile, "a") as f:
|
|
|
|
f.write("%s\n" % r)
|
2016-04-11 21:13:14 +03:00
|
|
|
except EnvironmentError:
|
2016-03-29 04:44:56 +03:00
|
|
|
pass
|
|
|
|
return r
|
|
|
|
|
2016-04-11 21:13:14 +03:00
|
|
|
|
2016-04-01 02:00:06 +03:00
|
|
|
def fastmanifesttocache(repo, subset, x):
|
2016-04-08 21:46:18 +03:00
|
|
|
"""Revset of the interesting revisions to cache"""
|
2016-04-13 00:00:35 +03:00
|
|
|
return scmutil.revrange(repo, ["not public() + bookmark()"])
|
2016-04-08 21:46:18 +03:00
|
|
|
|
2016-04-01 02:00:06 +03:00
|
|
|
|
2016-04-08 21:46:18 +03:00
|
|
|
class hybridmanifest(object):
|
2016-04-01 02:00:06 +03:00
|
|
|
"""
|
2016-04-08 21:46:18 +03:00
|
|
|
Hybrid manifest that behaves like a lazy manifest.
|
|
|
|
|
|
|
|
Initialized with:
|
|
|
|
- loadflat a function to load a flat manifest from disk
|
|
|
|
- cache an object with mapping method to work with fast manifest from disk
|
|
|
|
|
|
|
|
For the moment, behaves like a lazymanifest since cachedmanifest is not
|
|
|
|
yet available.
|
|
|
|
"""
|
2016-04-13 00:00:35 +03:00
|
|
|
def __init__(self, loadflat, ui, flatcache=None, fastcache=None,
|
|
|
|
node=None):
|
2016-04-08 21:46:18 +03:00
|
|
|
self.loadflat = loadflat
|
2016-04-13 00:00:35 +03:00
|
|
|
self.__flatmanifest = None
|
|
|
|
self.flatcache = flatcache
|
|
|
|
self.__cachedmanifest = None
|
|
|
|
self.fastcache = fastcache
|
2016-04-11 21:13:14 +03:00
|
|
|
self.node = node
|
2016-04-13 00:00:35 +03:00
|
|
|
self.ui = ui
|
2016-04-18 23:18:04 +03:00
|
|
|
if self.ui:
|
|
|
|
self.debugfastmanifest = self.ui.configbool("fastmanifest",
|
|
|
|
"debugfastmanifest")
|
|
|
|
else:
|
|
|
|
self.debugfastmanifest = False
|
2016-04-13 00:00:35 +03:00
|
|
|
if self.node:
|
|
|
|
self.node = revlog.hex(self.node)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def _flatmanifest(self):
|
2016-04-13 00:00:35 +03:00
|
|
|
if not self.__flatmanifest:
|
|
|
|
# Cache lookup
|
|
|
|
if (self.node and self.flatcache
|
|
|
|
and self.flatcache.contains(self.node)):
|
|
|
|
self.__flatmanifest = self.flatcache.get(self.node)
|
|
|
|
if self.__flatmanifest:
|
|
|
|
self.ui.debug("cache hit for flatmanifest %s\n"
|
|
|
|
% self.node)
|
|
|
|
return self.__flatmanifest
|
|
|
|
if self.node:
|
|
|
|
self.ui.debug("cache miss for flatmanifest %s\n" % self.node)
|
|
|
|
|
|
|
|
# Disk lookup
|
|
|
|
self.__flatmanifest = self.loadflat()
|
|
|
|
if isinstance(self.__flatmanifest, hybridmanifest):
|
|
|
|
# See comment in extsetup to see why we have to do that
|
|
|
|
self.__flatmanifest = self.__flatmanifest._flatmanifest()
|
|
|
|
assert isinstance(self.__flatmanifest, manifest.manifestdict)
|
|
|
|
return self.__flatmanifest
|
|
|
|
|
2016-04-08 21:46:18 +03:00
|
|
|
def _cachedmanifest(self):
|
2016-04-13 00:00:35 +03:00
|
|
|
if not self.__cachedmanifest:
|
|
|
|
# Cache lookup
|
|
|
|
if (self.node and self.fastcache
|
|
|
|
and self.fastcache.contains(self.node)):
|
|
|
|
self.__cachedmanifest = self.fastcache.get(self.node)
|
|
|
|
if self.__cachedmanifest:
|
|
|
|
self.ui.debug("cache hit for fastmanifest %s\n"
|
|
|
|
% self.node)
|
|
|
|
return self.__cachedmanifest
|
2016-04-11 21:13:14 +03:00
|
|
|
return None
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def _incache(self):
|
2016-04-13 00:00:35 +03:00
|
|
|
if self.flatcache and self.node:
|
|
|
|
return self.flatcache.contains(self.node)
|
2016-04-11 21:13:14 +03:00
|
|
|
return False
|
2016-04-08 21:46:18 +03:00
|
|
|
|
2016-04-13 00:00:35 +03:00
|
|
|
def _manifest(self, operation):
|
|
|
|
# Get the manifest most suited for the operations (flat or cached)
|
|
|
|
# TODO return fastmanifest when suitable
|
2016-04-18 23:18:04 +03:00
|
|
|
if self.debugfastmanifest:
|
|
|
|
return fastmanifest_wrapper(self._flatmanifest().text())
|
2016-04-13 00:00:35 +03:00
|
|
|
return self._flatmanifest()
|
|
|
|
|
2016-04-08 21:46:18 +03:00
|
|
|
# Proxy all the manifest methods to the flatmanifest except magic methods
|
|
|
|
def __getattr__(self, name):
|
2016-04-13 00:00:35 +03:00
|
|
|
return getattr(self._manifest(name), name)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
# Magic methods should be proxied differently than __getattr__
|
|
|
|
# For the moment all methods they all use the _flatmanifest
|
|
|
|
def __iter__(self):
|
2016-04-13 00:00:35 +03:00
|
|
|
return self._manifest('__iter__').__iter__()
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def __contains__(self, key):
|
2016-04-13 00:00:35 +03:00
|
|
|
return self._manifest('__contains__').__contains__(key)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def __getitem__(self, key):
|
2016-04-13 00:00:35 +03:00
|
|
|
return self._manifest('__getitem__').__getitem__(key)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def __setitem__(self, key, val):
|
2016-04-13 00:00:35 +03:00
|
|
|
return self._manifest('__setitem__').__setitem__(key, val)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def __delitem__(self, key):
|
2016-04-13 00:00:35 +03:00
|
|
|
return self._manifest('__delitem__').__delitem__(key)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def __len__(self):
|
2016-04-13 00:00:35 +03:00
|
|
|
return self._manifest('__len__').__len__()
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def copy(self):
|
2016-04-13 00:00:35 +03:00
|
|
|
return hybridmanifest(loadflat=lambda: self._flatmanifest().copy(),
|
|
|
|
flatcache=self.flatcache,
|
|
|
|
fastcache=self.fastcache,
|
|
|
|
node=self.node,
|
|
|
|
ui=self.ui)
|
|
|
|
|
|
|
|
def matches(self, *args, **kwargs):
|
|
|
|
newload = lambda: self._flatmanifest().matches(*args, **kwargs)
|
|
|
|
return hybridmanifest(loadflat=newload,
|
|
|
|
flatcache=self.flatcache,
|
|
|
|
fastcache=self.fastcache,
|
|
|
|
ui=self.ui)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def diff(self, m2, *args, **kwargs):
|
2016-04-13 00:00:35 +03:00
|
|
|
self.ui.debug("performing diff\n")
|
2016-04-08 21:46:18 +03:00
|
|
|
# Find _m1 and _m2 of the same type, to provide the fastest computation
|
|
|
|
_m1, _m2 = None, None
|
|
|
|
|
|
|
|
if isinstance(m2, hybridmanifest):
|
2016-04-13 00:00:35 +03:00
|
|
|
self.ui.debug("other side is hybrid manifest\n")
|
2016-04-08 21:46:18 +03:00
|
|
|
# CACHE HIT
|
2016-04-13 00:00:35 +03:00
|
|
|
if self._incache() and m2._incache():
|
|
|
|
_m1, _m2 = self._cachedmanifest(), m2._cachedmanifest()
|
2016-04-08 21:46:18 +03:00
|
|
|
# _m1 or _m2 can be None if _incache was True if the cache
|
|
|
|
# got garbage collected in the meantime or entry is corrupted
|
|
|
|
if not _m1 or not _m2:
|
2016-04-13 00:00:35 +03:00
|
|
|
self.ui.debug("fallback to regular diff\n")
|
|
|
|
_m1, _m2 = self._flatmanifest(), m2._flatmanifest()
|
|
|
|
else:
|
|
|
|
self.ui.debug("fastmanifest diff\n")
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
# CACHE MISS
|
|
|
|
else:
|
2016-04-13 00:00:35 +03:00
|
|
|
self.ui.debug("fallback to regular diff\n")
|
|
|
|
_m1, _m2 = self._flatmanifest(), m2._flatmanifest()
|
2016-04-08 21:46:18 +03:00
|
|
|
else:
|
|
|
|
# This happens when diffing against a new manifest (like rev -1)
|
2016-04-13 00:00:35 +03:00
|
|
|
self.ui.debug("fallback to regular diff\n")
|
|
|
|
_m1, _m2 = self._flatmanifest(), m2
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
assert type(_m1) == type(_m2)
|
|
|
|
return _m1.diff(_m2, *args, **kwargs)
|
|
|
|
|
2016-04-11 21:13:14 +03:00
|
|
|
|
2016-04-13 00:00:35 +03:00
|
|
|
class manifestcache(object):
|
|
|
|
def __init__(self, opener, ui):
|
|
|
|
self.opener = opener
|
|
|
|
self.ui = ui
|
|
|
|
self.inmemorycache = {}
|
|
|
|
base = opener.join(None)
|
|
|
|
self.cachepath = os.path.join(base, CACHE_SUBDIR)
|
|
|
|
if not os.path.exists(self.cachepath):
|
|
|
|
os.makedirs(self.cachepath)
|
|
|
|
|
|
|
|
def keyprefix(self):
|
|
|
|
raise NotImplementedError("abstract method, should be overriden")
|
|
|
|
|
|
|
|
def load(self, data):
|
|
|
|
raise NotImplementedError("abstract method, should be overriden")
|
|
|
|
|
|
|
|
def dump(self, manifest):
|
|
|
|
raise NotImplementedError("abstract method, should be overriden")
|
|
|
|
|
|
|
|
def inmemorycachekey(self, key):
|
|
|
|
return (self.keyprefix(), key)
|
|
|
|
|
|
|
|
def filecachepath(self, key):
|
|
|
|
return os.path.join(self.cachepath, self.keyprefix() + key)
|
|
|
|
|
|
|
|
def get(self, key):
|
|
|
|
# In memory cache lookup
|
|
|
|
ident = self.inmemorycachekey(key)
|
|
|
|
r = self.inmemorycache.get(ident, None)
|
|
|
|
if r:
|
|
|
|
return r
|
|
|
|
|
|
|
|
# On disk cache lookup
|
|
|
|
try:
|
|
|
|
with open(self.filecachepath(key)) as f:
|
|
|
|
r = self.load(f.read())
|
|
|
|
except EnvironmentError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
# In memory cache update
|
|
|
|
if r:
|
|
|
|
self.inmemorycache[ident] = r
|
|
|
|
return r
|
|
|
|
|
|
|
|
def contains(self, key):
|
|
|
|
if self.inmemorycachekey(key) in self.inmemorycache:
|
|
|
|
return True
|
|
|
|
return os.path.exists(self.filecachepath(key))
|
|
|
|
|
|
|
|
def put(self, key, manifest):
|
|
|
|
if self.contains(key):
|
|
|
|
self.ui.debug("skipped %s, already cached\n" % key)
|
|
|
|
else:
|
|
|
|
self.ui.debug("caching revision %s\n" % key)
|
|
|
|
fh = util.atomictempfile(self.filecachepath(key), mode="w+")
|
|
|
|
try:
|
|
|
|
fh.write(self.dump(manifest))
|
|
|
|
finally:
|
|
|
|
fh.close()
|
|
|
|
|
|
|
|
def prune(self, limit):
|
|
|
|
# TODO logic to prune old entries
|
|
|
|
pass
|
|
|
|
|
|
|
|
# flatmanifestcache and fastmanifestcache are singletons
|
|
|
|
|
|
|
|
class flatmanifestcache(manifestcache):
|
|
|
|
_instance = None
|
2016-04-20 00:08:13 +03:00
|
|
|
@classmethod
|
|
|
|
def getinstance(cls, opener, ui):
|
2016-04-13 00:00:35 +03:00
|
|
|
if not cls._instance:
|
2016-04-20 00:08:13 +03:00
|
|
|
cls._instance = flatmanifestcache(opener, ui)
|
2016-04-13 00:00:35 +03:00
|
|
|
return cls._instance
|
|
|
|
|
|
|
|
def keyprefix(self):
|
|
|
|
return "flat"
|
|
|
|
|
|
|
|
def load(self, data):
|
|
|
|
return manifest.manifestdict(data)
|
|
|
|
|
|
|
|
def dump(self, manifest):
|
|
|
|
return manifest.text()
|
|
|
|
|
|
|
|
|
|
|
|
class fastmanifestcache(manifestcache):
|
|
|
|
_instance = None
|
2016-04-20 00:08:13 +03:00
|
|
|
@classmethod
|
|
|
|
def getinstance(cls, opener, ui):
|
2016-04-13 00:00:35 +03:00
|
|
|
if not cls._instance:
|
2016-04-20 00:08:13 +03:00
|
|
|
cls._instance = fastmanifestcache(opener, ui)
|
2016-04-13 00:00:35 +03:00
|
|
|
return cls._instance
|
|
|
|
|
|
|
|
def keyprefix(self):
|
|
|
|
return "fast"
|
|
|
|
|
|
|
|
def load(self, data):
|
|
|
|
raise NotImplementedError("TODO integrate with @ttung's code")
|
|
|
|
|
|
|
|
def dump(self, manifest):
|
|
|
|
raise NotImplementedError("TODO integrate with @ttung's code")
|
|
|
|
|
|
|
|
|
2016-04-08 21:46:18 +03:00
|
|
|
class manifestfactory(object):
|
2016-04-13 00:00:35 +03:00
|
|
|
def __init__(self, ui):
|
|
|
|
self.ui = ui
|
|
|
|
|
2016-04-11 21:13:14 +03:00
|
|
|
def newmanifest(self, orig, *args, **kwargs):
|
|
|
|
loadfn = lambda: orig(*args, **kwargs)
|
2016-04-20 00:08:13 +03:00
|
|
|
fastcache = fastmanifestcache.getinstance(args[0].opener, self.ui)
|
|
|
|
flatcache = flatmanifestcache.getinstance(args[0].opener, self.ui)
|
2016-04-13 00:00:35 +03:00
|
|
|
return hybridmanifest(loadflat=loadfn,
|
|
|
|
ui=self.ui,
|
|
|
|
flatcache=flatcache,
|
|
|
|
fastcache=fastcache)
|
2016-04-08 21:46:18 +03:00
|
|
|
|
|
|
|
def read(self, orig, *args, **kwargs):
|
|
|
|
loadfn = lambda: orig(*args, **kwargs)
|
2016-04-20 00:08:13 +03:00
|
|
|
fastcache = fastmanifestcache.getinstance(args[0].opener, self.ui)
|
|
|
|
flatcache = flatmanifestcache.getinstance(args[0].opener, self.ui)
|
2016-04-13 00:00:35 +03:00
|
|
|
return hybridmanifest(loadflat=loadfn,
|
|
|
|
ui=self.ui,
|
|
|
|
flatcache=flatcache,
|
|
|
|
fastcache=fastcache,
|
|
|
|
node=args[1])
|
|
|
|
|
|
|
|
|
|
|
|
def _cachemanifest(ui, repo, revs, flat, sync, limit):
|
|
|
|
ui.debug(("caching rev: %s , synchronous(%s), flat(%s)\n")
|
|
|
|
% (revs, sync, flat))
|
|
|
|
if flat:
|
2016-04-20 00:08:13 +03:00
|
|
|
cache = flatmanifestcache.getinstance(repo.store.opener, ui)
|
2016-04-13 00:00:35 +03:00
|
|
|
else:
|
2016-04-20 00:08:13 +03:00
|
|
|
cache = fastmanifestcache.getinstance(repo.store.opener, ui)
|
2016-04-13 00:00:35 +03:00
|
|
|
|
|
|
|
for rev in revs:
|
|
|
|
manifest = repo[rev].manifest()
|
|
|
|
nodehex = manifest.node
|
|
|
|
cache.put(nodehex, manifest)
|
|
|
|
|
|
|
|
if limit:
|
|
|
|
cache.prune(limit)
|
|
|
|
|
|
|
|
|
|
|
|
@command('^debugcachemanifest', [
|
|
|
|
('r', 'rev', [], 'cache the manifest for revs', 'REV'),
|
|
|
|
('f', 'flat', False, 'cache flat manifests instead of fast manifests', ''),
|
|
|
|
('a', 'all', False, 'cache all relevant revisions', ''),
|
|
|
|
('l', 'limit', False, 'limit size of total rev in bytes', 'BYTES'),
|
|
|
|
('s', 'synchronous', False, 'wait for completion to return', '')],
|
|
|
|
'hg debugcachemanifest')
|
|
|
|
def debugcachemanifest(ui, repo, *pats, **opts):
|
|
|
|
flat = opts["flat"]
|
|
|
|
sync = opts["synchronous"]
|
|
|
|
limit = opts["limit"]
|
|
|
|
if opts["all"]:
|
|
|
|
revs = scmutil.revrange(repo, ["fastmanifesttocache()"])
|
|
|
|
elif opts["rev"]:
|
|
|
|
revs = scmutil.revrange(repo, opts["rev"])
|
|
|
|
else:
|
|
|
|
revs = []
|
|
|
|
_cachemanifest(ui, repo, revs, flat, sync, limit)
|
2016-04-11 21:13:14 +03:00
|
|
|
|
2016-04-01 02:00:06 +03:00
|
|
|
|
2016-03-29 04:44:56 +03:00
|
|
|
def extsetup(ui):
|
|
|
|
logfile = ui.config("fastmanifest", "logfile", "")
|
2016-04-13 00:00:35 +03:00
|
|
|
factory = manifestfactory(ui)
|
2016-03-29 04:44:56 +03:00
|
|
|
if logfile:
|
|
|
|
logger = manifestaccesslogger(logfile)
|
|
|
|
extensions.wrapfunction(manifest.manifest, 'rev', logger.revwrap)
|
2016-04-08 21:46:18 +03:00
|
|
|
# Wraps all the function creating a manifestdict
|
2016-04-11 21:13:14 +03:00
|
|
|
# We have to do that because the logic to create manifest can take
|
|
|
|
# 7 different codepaths and we want to retain the node information
|
|
|
|
# that comes at the top level:
|
|
|
|
#
|
|
|
|
# read -> _newmanifest ---------------------------> manifestdict
|
|
|
|
#
|
|
|
|
# readshallowfast -> readshallow -----------------> manifestdict
|
|
|
|
# \ \------> _newmanifest --> manifestdict
|
|
|
|
# --> readshallowdelta ------------------------> manifestdict
|
|
|
|
# \->readdelta -------> _newmanifest --> manifestdict
|
|
|
|
# \->slowreaddelta --> _newmanifest --> manifestdict
|
|
|
|
#
|
|
|
|
# othermethods -----------------------------------> manifestdict
|
|
|
|
#
|
|
|
|
# We can have hybridmanifest that wraps one hybridmanifest in some
|
|
|
|
# codepath. We resolve to the correct flatmanifest when asked in the
|
|
|
|
# _flatmanifest method
|
|
|
|
#
|
|
|
|
# The recursion level is at most 2 because we wrap the two top level
|
|
|
|
# functions and _newmanifest (wrapped only for the case of -1)
|
|
|
|
|
|
|
|
extensions.wrapfunction(manifest.manifest, '_newmanifest',
|
|
|
|
factory.newmanifest)
|
|
|
|
extensions.wrapfunction(manifest.manifest, 'read', factory.read)
|
|
|
|
try:
|
|
|
|
extensions.wrapfunction(manifest.manifest, 'readshallowfast',
|
|
|
|
factory.read)
|
|
|
|
except AttributeError:
|
|
|
|
# The function didn't use to be defined in previous versions of hg
|
|
|
|
pass
|
2016-04-08 21:46:18 +03:00
|
|
|
|
2016-04-01 02:00:06 +03:00
|
|
|
revset.symbols['fastmanifesttocache'] = fastmanifesttocache
|
|
|
|
revset.safesymbols.add('fastmanifesttocache')
|
2016-04-13 00:00:35 +03:00
|
|
|
|