Mercurial > hg-stable
changeset 32283:07da778f3b58
filectx: add an overlayfilectx class
The end goal is to make it possible to avoid potential expensive fctx.data()
when unnecessary.
While memctx is useful for creating new file contexts, there are many cases
where we could reuse an existing raw file revision (amend, histedit, rebase,
process a revision constructed by a remote peer, etc). The overlayfilectx
class is made to support such reuse cases. Together with a later patch, hash
calculation and expensive flag processor could be avoided.
author | Jun Wu <quark@fb.com> |
---|---|
date | Tue, 09 May 2017 20:23:21 -0700 |
parents | 067985c26773 |
children | 3de4c61b5087 |
files | mercurial/context.py |
diffstat | 1 files changed, 74 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/context.py Tue May 09 19:16:48 2017 -0700 +++ b/mercurial/context.py Tue May 09 20:23:21 2017 -0700 @@ -687,7 +687,9 @@ in the repo, workingfilectx: a filecontext that represents files from the working directory, - memfilectx: a filecontext that represents files in-memory.""" + memfilectx: a filecontext that represents files in-memory, + overlayfilectx: duplicate another filecontext with some fields overridden. + """ @propertycache def _filelog(self): return self._repo.file(self._path) @@ -2078,6 +2080,77 @@ """wraps repo.wwrite""" self._data = data +class overlayfilectx(committablefilectx): + """Like memfilectx but take an original filectx and optional parameters to + override parts of it. This is useful when fctx.data() is expensive (i.e. + flag processor is expensive) and raw data, flags, and filenode could be + reused (ex. rebase or mode-only amend a REVIDX_EXTSTORED file). + """ + + def __init__(self, originalfctx, datafunc=None, path=None, flags=None, + copied=None, ctx=None): + """originalfctx: filecontext to duplicate + + datafunc: None or a function to override data (file content). It is a + function to be lazy. path, flags, copied, ctx: None or overridden value + + copied could be (path, rev), or False. copied could also be just path, + and will be converted to (path, nullid). This simplifies some callers. + """ + + if path is None: + path = originalfctx.path() + if ctx is None: + ctx = originalfctx.changectx() + ctxmatch = lambda: True + else: + ctxmatch = lambda: ctx == originalfctx.changectx() + + repo = originalfctx.repo() + flog = originalfctx.filelog() + super(overlayfilectx, self).__init__(repo, path, flog, ctx) + + if copied is None: + copied = originalfctx.renamed() + copiedmatch = lambda: True + else: + if copied and not isinstance(copied, tuple): + # repo._filecommit will recalculate copyrev so nullid is okay + copied = (copied, nullid) + copiedmatch = lambda: copied == originalfctx.renamed() + + # When data, copied (could affect data), ctx (could affect filelog + # parents) are not overridden, rawdata, rawflags, and filenode may be + # reused (repo._filecommit should double check filelog parents). + # + # path, flags are not hashed in filelog (but in manifestlog) so they do + # not affect reusable here. + # + # If ctx or copied is overridden to a same value with originalfctx, + # still consider it's reusable. originalfctx.renamed() may be a bit + # expensive so it's not called unless necessary. Assuming datafunc is + # always expensive, do not call it for this "reusable" test. + reusable = datafunc is None and ctxmatch() and copiedmatch() + + if datafunc is None: + datafunc = originalfctx.data + if flags is None: + flags = originalfctx.flags() + + self._datafunc = datafunc + self._flags = flags + self._copied = copied + + if reusable: + # copy extra fields from originalfctx + attrs = ['rawdata', 'rawflags', '_filenode', '_filerev'] + for attr in attrs: + if util.safehasattr(originalfctx, attr): + setattr(self, attr, getattr(originalfctx, attr)) + + def data(self): + return self._datafunc() + class metadataonlyctx(committablectx): """Like memctx but it's reusing the manifest of different commit. Intended to be used by lightweight operations that are creating