Patchwork [06,of,11] filectx: add an overlayfilectx class

login
register
mail settings
Submitter Jun Wu
Date May 10, 2017, 8:34 a.m.
Message ID <963d6fa49835ce66db69.1494405265@x1c>
Download mbox | patch
Permalink /patch/20552/
State Accepted
Headers show

Comments

Jun Wu - May 10, 2017, 8:34 a.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1494386601 25200
#      Tue May 09 20:23:21 2017 -0700
# Node ID 963d6fa49835ce66db6961ca31b02241c979cb99
# Parent  f736ea580edbf4ec199b55af586b31f2798cbee4
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r 963d6fa49835
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.
Sean Farley - May 10, 2017, 5:39 p.m.
Jun Wu <quark@fb.com> writes:

> # HG changeset patch
> # User Jun Wu <quark@fb.com>
> # Date 1494386601 25200
> #      Tue May 09 20:23:21 2017 -0700
> # Node ID 963d6fa49835ce66db6961ca31b02241c979cb99
> # Parent  f736ea580edbf4ec199b55af586b31f2798cbee4
> # Available At https://bitbucket.org/quark-zju/hg-draft
> #              hg pull https://bitbucket.org/quark-zju/hg-draft -r 963d6fa49835
> 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.

I think I can live with this class for now. Eventually, though, I'd like
to consolidate (if possible) our use of metadataonly, workingcommitctx,
and this new overlayctx. Anyways, that can be done later.
Jun Wu - May 10, 2017, 5:42 p.m.
Excerpts from Sean Farley's message of 2017-05-10 10:39:01 -0700:
> I think I can live with this class for now. Eventually, though, I'd like
> to consolidate (if possible) our use of metadataonly, workingcommitctx,
> and this new overlayctx. Anyways, that can be done later.

For changectx, we do plan to merge them. Phil could talk about more details
with you tomorrow.
Sean Farley - May 10, 2017, 6:21 p.m.
Jun Wu <quark@fb.com> writes:

> Excerpts from Sean Farley's message of 2017-05-10 10:39:01 -0700:
>> I think I can live with this class for now. Eventually, though, I'd like
>> to consolidate (if possible) our use of metadataonly, workingcommitctx,
>> and this new overlayctx. Anyways, that can be done later.
>
> For changectx, we do plan to merge them. Phil could talk about more details
> with you tomorrow.

Ah, great!

Patch

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -688,5 +688,7 @@  class basefilectx(object):
     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):
@@ -2079,4 +2081,75 @@  class memfilectx(committablefilectx):
         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.