Patchwork lfs: register the flag processors per repository

login
register
mail settings
Submitter Matt Harbison
Date Oct. 4, 2018, 4:39 a.m.
Message ID <e010a7be6dc96ea7d48b.1538627952@Envy>
Download mbox | patch
Permalink /patch/35438/
State New
Headers show

Comments

Matt Harbison - Oct. 4, 2018, 4:39 a.m.
# HG changeset patch
# User Matt Harbison <matt_harbison@yahoo.com>
# Date 1538626646 14400
#      Thu Oct 04 00:17:26 2018 -0400
# Node ID e010a7be6dc96ea7d48be81a7c5e8a8ed8bf6c31
# Parent  7a347d362a455d84bccf34347171d89724b9c9df
lfs: register the flag processors per repository

Previously, enabling the extension for any repo in commandserver or hgweb would
enable the flags on all repos.  Since localrepo.resolverevlogstorevfsoptions()
is called so early, the svfs option needs to be forced on later in reposetup()
to handle the case where the extension loads, but no lfs requirement has been
set.  That way, the flags processor is enabled when committing or unbundling.
This will also ensure that the flags processor is available on all revlogs in a
long running process.

I'm not sure if we need a formal API to add options like this- narrow doesn't
seem to need it.  I'm also not sure if REPO_FEATURE_LFS should just be added
unconditionally in reposetup()- it's easy enough to check the requirements to
see if a blob was actually committed.
Yuya Nishihara - Oct. 4, 2018, 1:08 p.m.
On Thu, 04 Oct 2018 00:39:12 -0400, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison@yahoo.com>
> # Date 1538626646 14400
> #      Thu Oct 04 00:17:26 2018 -0400
> # Node ID e010a7be6dc96ea7d48be81a7c5e8a8ed8bf6c31
> # Parent  7a347d362a455d84bccf34347171d89724b9c9df
> lfs: register the flag processors per repository

> +    if b'lfs' in requirements:
> +        options[b'enableextstored'] = True
> +
>      if repository.NARROW_REQUIREMENT in requirements:
>          options[b'enableellipsis'] = True
>  
> diff --git a/mercurial/revlog.py b/mercurial/revlog.py
> --- a/mercurial/revlog.py
> +++ b/mercurial/revlog.py
> @@ -110,6 +110,26 @@ parsers = policy.importmod(r'parsers')
>      REVIDX_ISCENSORED: None,
>  }
>  
> +# Flag processors for REVIDX_EXTSTORED.
> +def extstoredreadprocessor(rl, text):
> +    raise error.ProgrammingError(b'extstored flags processor enabled without'
> +                                 b' wrapping processor function')
> +
> +def extstoredwriteprocessor(rl, text):
> +    raise error.ProgrammingError(b'extstored flags processor enabled without'
> +                                 b' wrapping processor function')
> +
> +def extstoredrawprocessor(rl, text):
> +    raise error.ProgrammingError(b'extstored flags processor enabled without'
> +                                 b' wrapping processor function')
> +
> +# Lambdas are needed here so that these methods can be wrapped by lfs.
> +extstoredprocessor = (
> +    lambda rl, text: extstoredreadprocessor(rl, text),
> +    lambda rl, text: extstoredwriteprocessor(rl, text),
> +    lambda rl, text: extstoredrawprocessor(rl, text),
> +)
> +
>  # Flag processors for REVIDX_ELLIPSIS.
>  def ellipsisreadprocessor(rl, text):
>      return text, False
> @@ -405,6 +425,8 @@ class revlog(object):
>                  self._srdensitythreshold = opts['sparse-read-density-threshold']
>              if 'sparse-read-min-gap-size' in opts:
>                  self._srmingapsize = opts['sparse-read-min-gap-size']
> +            if opts.get('enableextstored'):
> +                self._flagprocessors[REVIDX_EXTSTORED] = extstoredprocessor

I feel it's awkward that we have to wrap the stub extstoredprocessor and
pass in enableextstored=True.

I don't have a concrete idea, but maybe we can provide an API to register
a stock flagprocessor which won't be copied to the revlog instance by default.
Gregory Szorc - Oct. 4, 2018, 6:35 p.m.
On Thu, Oct 4, 2018 at 6:09 AM Yuya Nishihara <yuya@tcha.org> wrote:

> On Thu, 04 Oct 2018 00:39:12 -0400, Matt Harbison wrote:
> > # HG changeset patch
> > # User Matt Harbison <matt_harbison@yahoo.com>
> > # Date 1538626646 14400
> > #      Thu Oct 04 00:17:26 2018 -0400
> > # Node ID e010a7be6dc96ea7d48be81a7c5e8a8ed8bf6c31
> > # Parent  7a347d362a455d84bccf34347171d89724b9c9df
> > lfs: register the flag processors per repository
>
> > +    if b'lfs' in requirements:
> > +        options[b'enableextstored'] = True
> > +
> >      if repository.NARROW_REQUIREMENT in requirements:
> >          options[b'enableellipsis'] = True
> >
> > diff --git a/mercurial/revlog.py b/mercurial/revlog.py
> > --- a/mercurial/revlog.py
> > +++ b/mercurial/revlog.py
> > @@ -110,6 +110,26 @@ parsers = policy.importmod(r'parsers')
> >      REVIDX_ISCENSORED: None,
> >  }
> >
> > +# Flag processors for REVIDX_EXTSTORED.
> > +def extstoredreadprocessor(rl, text):
> > +    raise error.ProgrammingError(b'extstored flags processor enabled
> without'
> > +                                 b' wrapping processor function')
> > +
> > +def extstoredwriteprocessor(rl, text):
> > +    raise error.ProgrammingError(b'extstored flags processor enabled
> without'
> > +                                 b' wrapping processor function')
> > +
> > +def extstoredrawprocessor(rl, text):
> > +    raise error.ProgrammingError(b'extstored flags processor enabled
> without'
> > +                                 b' wrapping processor function')
> > +
> > +# Lambdas are needed here so that these methods can be wrapped by lfs.
> > +extstoredprocessor = (
> > +    lambda rl, text: extstoredreadprocessor(rl, text),
> > +    lambda rl, text: extstoredwriteprocessor(rl, text),
> > +    lambda rl, text: extstoredrawprocessor(rl, text),
> > +)
> > +
> >  # Flag processors for REVIDX_ELLIPSIS.
> >  def ellipsisreadprocessor(rl, text):
> >      return text, False
> > @@ -405,6 +425,8 @@ class revlog(object):
> >                  self._srdensitythreshold =
> opts['sparse-read-density-threshold']
> >              if 'sparse-read-min-gap-size' in opts:
> >                  self._srmingapsize = opts['sparse-read-min-gap-size']
> > +            if opts.get('enableextstored'):
> > +                self._flagprocessors[REVIDX_EXTSTORED] =
> extstoredprocessor
>
> I feel it's awkward that we have to wrap the stub extstoredprocessor and
> pass in enableextstored=True.
>
> I don't have a concrete idea, but maybe we can provide an API to register
> a stock flagprocessor which won't be copied to the revlog instance by
> default.
>

I also found this a bit awkward.

I wrote up a patch a few weeks ago that added an "extraflagprocessors"
argument or some such to revlog.__init__ that would allow extra flag
processors to be passed in. I never submitted it because I had originally
wrote it for censoring and I solved that problem by moving the censor flag
processor into revlog.py and introduced the "censorable=False" argument
instead.

But the flag processor for LFS is in the same boat and we may want to do
something similar. But since LFS's flag processors require code in the LFS
extension, we can't simply move the flag processors code to revlog.py.

I think our best bet is to pass a flag processor into revlog.__init__ -
either via an argument or as a key in the store/opener options. Store
options is simpler, as we'd only need to teach revlog.py to add extra flag
processors defined in a store options key and the LFS extension would only
need to monkeypatch localrepo.resolverevlogstorevfsoptions(). Adding an
argument to revlog.__init__ requires a bit more work to thread through
everything. If the LFS flag processor code were in core, I'd be fine with
introducing that argument. But since it isn't, I think going through the
store options is the way to go.
Matt Harbison - Oct. 5, 2018, 4:48 a.m.
On Thu, 04 Oct 2018 14:35:49 -0400, Gregory Szorc  
<gregory.szorc@gmail.com> wrote:

> On Thu, Oct 4, 2018 at 6:09 AM Yuya Nishihara <yuya@tcha.org> wrote:
>
>> On Thu, 04 Oct 2018 00:39:12 -0400, Matt Harbison wrote:
>> > # HG changeset patch
>> > # User Matt Harbison <matt_harbison@yahoo.com>
>> > # Date 1538626646 14400
>> > #      Thu Oct 04 00:17:26 2018 -0400
>> > # Node ID e010a7be6dc96ea7d48be81a7c5e8a8ed8bf6c31
>> > # Parent  7a347d362a455d84bccf34347171d89724b9c9df
>> > lfs: register the flag processors per repository
>>
>> > +    if b'lfs' in requirements:
>> > +        options[b'enableextstored'] = True
>> > +
>> >      if repository.NARROW_REQUIREMENT in requirements:
>> >          options[b'enableellipsis'] = True
>> >
>> > diff --git a/mercurial/revlog.py b/mercurial/revlog.py
>> > --- a/mercurial/revlog.py
>> > +++ b/mercurial/revlog.py
>> > @@ -110,6 +110,26 @@ parsers = policy.importmod(r'parsers')
>> >      REVIDX_ISCENSORED: None,
>> >  }
>> >
>> > +# Flag processors for REVIDX_EXTSTORED.
>> > +def extstoredreadprocessor(rl, text):
>> > +    raise error.ProgrammingError(b'extstored flags processor enabled
>> without'
>> > +                                 b' wrapping processor function')
>> > +
>> > +def extstoredwriteprocessor(rl, text):
>> > +    raise error.ProgrammingError(b'extstored flags processor enabled
>> without'
>> > +                                 b' wrapping processor function')
>> > +
>> > +def extstoredrawprocessor(rl, text):
>> > +    raise error.ProgrammingError(b'extstored flags processor enabled
>> without'
>> > +                                 b' wrapping processor function')
>> > +
>> > +# Lambdas are needed here so that these methods can be wrapped by  
>> lfs.
>> > +extstoredprocessor = (
>> > +    lambda rl, text: extstoredreadprocessor(rl, text),
>> > +    lambda rl, text: extstoredwriteprocessor(rl, text),
>> > +    lambda rl, text: extstoredrawprocessor(rl, text),
>> > +)
>> > +
>> >  # Flag processors for REVIDX_ELLIPSIS.
>> >  def ellipsisreadprocessor(rl, text):
>> >      return text, False
>> > @@ -405,6 +425,8 @@ class revlog(object):
>> >                  self._srdensitythreshold =
>> opts['sparse-read-density-threshold']
>> >              if 'sparse-read-min-gap-size' in opts:
>> >                  self._srmingapsize = opts['sparse-read-min-gap-size']
>> > +            if opts.get('enableextstored'):
>> > +                self._flagprocessors[REVIDX_EXTSTORED] =
>> extstoredprocessor
>>
>> I feel it's awkward that we have to wrap the stub extstoredprocessor and
>> pass in enableextstored=True.
>>
>> I don't have a concrete idea, but maybe we can provide an API to  
>> register
>> a stock flagprocessor which won't be copied to the revlog instance by
>> default.
>>
>
> I also found this a bit awkward.
>
> I wrote up a patch a few weeks ago that added an "extraflagprocessors"
> argument or some such to revlog.__init__ that would allow extra flag
> processors to be passed in. I never submitted it because I had originally
> wrote it for censoring and I solved that problem by moving the censor  
> flag
> processor into revlog.py and introduced the "censorable=False" argument
> instead.
>
> But the flag processor for LFS is in the same boat and we may want to do
> something similar. But since LFS's flag processors require code in the  
> LFS
> extension, we can't simply move the flag processors code to revlog.py.
>
> I think our best bet is to pass a flag processor into revlog.__init__ -
> either via an argument or as a key in the store/opener options. Store
> options is simpler, as we'd only need to teach revlog.py to add extra  
> flag
> processors defined in a store options key and the LFS extension would  
> only
> need to monkeypatch localrepo.resolverevlogstorevfsoptions(). Adding an
> argument to revlog.__init__ requires a bit more work to thread through
> everything. If the LFS flag processor code were in core, I'd be fine with
> introducing that argument. But since it isn't, I think going through the
> store options is the way to go.

Should the revlog.addflagprocessor() method be copied to localrepo so that  
it still does the validation, and can also hide the fact that it's being  
tacked on to the store options?
Gregory Szorc - Oct. 31, 2018, 7:20 p.m.
On Thu, Oct 4, 2018 at 9:48 PM Matt Harbison <mharbison72@gmail.com> wrote:

> On Thu, 04 Oct 2018 14:35:49 -0400, Gregory Szorc
> <gregory.szorc@gmail.com> wrote:
>
> > On Thu, Oct 4, 2018 at 6:09 AM Yuya Nishihara <yuya@tcha.org> wrote:
> >
> >> On Thu, 04 Oct 2018 00:39:12 -0400, Matt Harbison wrote:
> >> > # HG changeset patch
> >> > # User Matt Harbison <matt_harbison@yahoo.com>
> >> > # Date 1538626646 14400
> >> > #      Thu Oct 04 00:17:26 2018 -0400
> >> > # Node ID e010a7be6dc96ea7d48be81a7c5e8a8ed8bf6c31
> >> > # Parent  7a347d362a455d84bccf34347171d89724b9c9df
> >> > lfs: register the flag processors per repository
> >>
> >> > +    if b'lfs' in requirements:
> >> > +        options[b'enableextstored'] = True
> >> > +
> >> >      if repository.NARROW_REQUIREMENT in requirements:
> >> >          options[b'enableellipsis'] = True
> >> >
> >> > diff --git a/mercurial/revlog.py b/mercurial/revlog.py
> >> > --- a/mercurial/revlog.py
> >> > +++ b/mercurial/revlog.py
> >> > @@ -110,6 +110,26 @@ parsers = policy.importmod(r'parsers')
> >> >      REVIDX_ISCENSORED: None,
> >> >  }
> >> >
> >> > +# Flag processors for REVIDX_EXTSTORED.
> >> > +def extstoredreadprocessor(rl, text):
> >> > +    raise error.ProgrammingError(b'extstored flags processor enabled
> >> without'
> >> > +                                 b' wrapping processor function')
> >> > +
> >> > +def extstoredwriteprocessor(rl, text):
> >> > +    raise error.ProgrammingError(b'extstored flags processor enabled
> >> without'
> >> > +                                 b' wrapping processor function')
> >> > +
> >> > +def extstoredrawprocessor(rl, text):
> >> > +    raise error.ProgrammingError(b'extstored flags processor enabled
> >> without'
> >> > +                                 b' wrapping processor function')
> >> > +
> >> > +# Lambdas are needed here so that these methods can be wrapped by
> >> lfs.
> >> > +extstoredprocessor = (
> >> > +    lambda rl, text: extstoredreadprocessor(rl, text),
> >> > +    lambda rl, text: extstoredwriteprocessor(rl, text),
> >> > +    lambda rl, text: extstoredrawprocessor(rl, text),
> >> > +)
> >> > +
> >> >  # Flag processors for REVIDX_ELLIPSIS.
> >> >  def ellipsisreadprocessor(rl, text):
> >> >      return text, False
> >> > @@ -405,6 +425,8 @@ class revlog(object):
> >> >                  self._srdensitythreshold =
> >> opts['sparse-read-density-threshold']
> >> >              if 'sparse-read-min-gap-size' in opts:
> >> >                  self._srmingapsize = opts['sparse-read-min-gap-size']
> >> > +            if opts.get('enableextstored'):
> >> > +                self._flagprocessors[REVIDX_EXTSTORED] =
> >> extstoredprocessor
> >>
> >> I feel it's awkward that we have to wrap the stub extstoredprocessor and
> >> pass in enableextstored=True.
> >>
> >> I don't have a concrete idea, but maybe we can provide an API to
> >> register
> >> a stock flagprocessor which won't be copied to the revlog instance by
> >> default.
> >>
> >
> > I also found this a bit awkward.
> >
> > I wrote up a patch a few weeks ago that added an "extraflagprocessors"
> > argument or some such to revlog.__init__ that would allow extra flag
> > processors to be passed in. I never submitted it because I had originally
> > wrote it for censoring and I solved that problem by moving the censor
> > flag
> > processor into revlog.py and introduced the "censorable=False" argument
> > instead.
> >
> > But the flag processor for LFS is in the same boat and we may want to do
> > something similar. But since LFS's flag processors require code in the
> > LFS
> > extension, we can't simply move the flag processors code to revlog.py.
> >
> > I think our best bet is to pass a flag processor into revlog.__init__ -
> > either via an argument or as a key in the store/opener options. Store
> > options is simpler, as we'd only need to teach revlog.py to add extra
> > flag
> > processors defined in a store options key and the LFS extension would
> > only
> > need to monkeypatch localrepo.resolverevlogstorevfsoptions(). Adding an
> > argument to revlog.__init__ requires a bit more work to thread through
> > everything. If the LFS flag processor code were in core, I'd be fine with
> > introducing that argument. But since it isn't, I think going through the
> > store options is the way to go.
>
> Should the revlog.addflagprocessor() method be copied to localrepo so
> that
> it still does the validation, and can also hide the fact that it's being
> tacked on to the store options?
>

Found this email when triaging my inbox.

I know some other flag processor changes landed after this. Are you
satisfied with the current state of things or do you think we should do
more?
Matt Harbison - Nov. 1, 2018, 12:46 a.m.
> On Oct 31, 2018, at 3:20 PM, Gregory Szorc <gregory.szorc@gmail.com> wrote:
> 
>> On Thu, Oct 4, 2018 at 9:48 PM Matt Harbison <mharbison72@gmail.com> wrote:
>> On Thu, 04 Oct 2018 14:35:49 -0400, Gregory Szorc  
>> <gregory.szorc@gmail.com> wrote:
>> 
>> > On Thu, Oct 4, 2018 at 6:09 AM Yuya Nishihara <yuya@tcha.org> wrote:
>> >
>> >> On Thu, 04 Oct 2018 00:39:12 -0400, Matt Harbison wrote:
>> >> > # HG changeset patch
>> >> > # User Matt Harbison <matt_harbison@yahoo.com>
>> >> > # Date 1538626646 14400
>> >> > #      Thu Oct 04 00:17:26 2018 -0400
>> >> > # Node ID e010a7be6dc96ea7d48be81a7c5e8a8ed8bf6c31
>> >> > # Parent  7a347d362a455d84bccf34347171d89724b9c9df
>> >> > lfs: register the flag processors per repository
>> >>
>> >> > +    if b'lfs' in requirements:
>> >> > +        options[b'enableextstored'] = True
>> >> > +
>> >> >      if repository.NARROW_REQUIREMENT in requirements:
>> >> >          options[b'enableellipsis'] = True
>> >> >
>> >> > diff --git a/mercurial/revlog.py b/mercurial/revlog.py
>> >> > --- a/mercurial/revlog.py
>> >> > +++ b/mercurial/revlog.py
>> >> > @@ -110,6 +110,26 @@ parsers = policy.importmod(r'parsers')
>> >> >      REVIDX_ISCENSORED: None,
>> >> >  }
>> >> >
>> >> > +# Flag processors for REVIDX_EXTSTORED.
>> >> > +def extstoredreadprocessor(rl, text):
>> >> > +    raise error.ProgrammingError(b'extstored flags processor enabled
>> >> without'
>> >> > +                                 b' wrapping processor function')
>> >> > +
>> >> > +def extstoredwriteprocessor(rl, text):
>> >> > +    raise error.ProgrammingError(b'extstored flags processor enabled
>> >> without'
>> >> > +                                 b' wrapping processor function')
>> >> > +
>> >> > +def extstoredrawprocessor(rl, text):
>> >> > +    raise error.ProgrammingError(b'extstored flags processor enabled
>> >> without'
>> >> > +                                 b' wrapping processor function')
>> >> > +
>> >> > +# Lambdas are needed here so that these methods can be wrapped by  
>> >> lfs.
>> >> > +extstoredprocessor = (
>> >> > +    lambda rl, text: extstoredreadprocessor(rl, text),
>> >> > +    lambda rl, text: extstoredwriteprocessor(rl, text),
>> >> > +    lambda rl, text: extstoredrawprocessor(rl, text),
>> >> > +)
>> >> > +
>> >> >  # Flag processors for REVIDX_ELLIPSIS.
>> >> >  def ellipsisreadprocessor(rl, text):
>> >> >      return text, False
>> >> > @@ -405,6 +425,8 @@ class revlog(object):
>> >> >                  self._srdensitythreshold =
>> >> opts['sparse-read-density-threshold']
>> >> >              if 'sparse-read-min-gap-size' in opts:
>> >> >                  self._srmingapsize = opts['sparse-read-min-gap-size']
>> >> > +            if opts.get('enableextstored'):
>> >> > +                self._flagprocessors[REVIDX_EXTSTORED] =
>> >> extstoredprocessor
>> >>
>> >> I feel it's awkward that we have to wrap the stub extstoredprocessor and
>> >> pass in enableextstored=True.
>> >>
>> >> I don't have a concrete idea, but maybe we can provide an API to  
>> >> register
>> >> a stock flagprocessor which won't be copied to the revlog instance by
>> >> default.
>> >>
>> >
>> > I also found this a bit awkward.
>> >
>> > I wrote up a patch a few weeks ago that added an "extraflagprocessors"
>> > argument or some such to revlog.__init__ that would allow extra flag
>> > processors to be passed in. I never submitted it because I had originally
>> > wrote it for censoring and I solved that problem by moving the censor  
>> > flag
>> > processor into revlog.py and introduced the "censorable=False" argument
>> > instead.
>> >
>> > But the flag processor for LFS is in the same boat and we may want to do
>> > something similar. But since LFS's flag processors require code in the  
>> > LFS
>> > extension, we can't simply move the flag processors code to revlog.py.
>> >
>> > I think our best bet is to pass a flag processor into revlog.__init__ -
>> > either via an argument or as a key in the store/opener options. Store
>> > options is simpler, as we'd only need to teach revlog.py to add extra  
>> > flag
>> > processors defined in a store options key and the LFS extension would  
>> > only
>> > need to monkeypatch localrepo.resolverevlogstorevfsoptions(). Adding an
>> > argument to revlog.__init__ requires a bit more work to thread through
>> > everything. If the LFS flag processor code were in core, I'd be fine with
>> > introducing that argument. But since it isn't, I think going through the
>> > store options is the way to go.
>> 
>> Should the revlog.addflagprocessor() method be copied to localrepo so that  
>> it still does the validation, and can also hide the fact that it's being  
>> tacked on to the store options?
> 
> Found this email when triaging my inbox.
> 
> I know some other flag processor changes landed after this. Are you satisfied with the current state of things or do you think we should do more?

I think what we have is sufficient for now anyway.

Patch

diff --git a/hgext/lfs/__init__.py b/hgext/lfs/__init__.py
--- a/hgext/lfs/__init__.py
+++ b/hgext/lfs/__init__.py
@@ -218,6 +218,7 @@  def reposetup(ui, repo):
 
     repo.svfs.lfslocalblobstore = blobstore.local(repo)
     repo.svfs.lfsremoteblobstore = blobstore.remote(repo)
+    repo.svfs.options[b'enableextstored'] = True
 
     class lfsrepo(repo.__class__):
         @localrepo.unfilteredmethod
@@ -334,14 +335,9 @@  def extsetup(ui):
     wrapfunction(context.basefilectx, 'isbinary', wrapper.filectxisbinary)
     context.basefilectx.islfs = wrapper.filectxislfs
 
-    revlog.addflagprocessor(
-        revlog.REVIDX_EXTSTORED,
-        (
-            wrapper.readfromstore,
-            wrapper.writetostore,
-            wrapper.bypasscheckhash,
-        ),
-    )
+    wrapfunction(revlog, 'extstoredrawprocessor', wrapper.bypasscheckhash)
+    wrapfunction(revlog, 'extstoredreadprocessor', wrapper.readfromstore)
+    wrapfunction(revlog, 'extstoredwriteprocessor', wrapper.writetostore)
 
     scmutil.fileprefetchhooks.add('lfs', wrapper._prefetchfiles)
 
diff --git a/hgext/lfs/wrapper.py b/hgext/lfs/wrapper.py
--- a/hgext/lfs/wrapper.py
+++ b/hgext/lfs/wrapper.py
@@ -50,10 +50,10 @@  def _capabilities(orig, repo, proto):
         caps.append('lfs')
     return caps
 
-def bypasscheckhash(self, text):
+def bypasscheckhash(orig, self, text):
     return False
 
-def readfromstore(self, text):
+def readfromstore(orig, self, text):
     """Read filelog content from local blobstore transform for flagprocessor.
 
     Default tranform for flagprocessor, returning contents from blobstore.
@@ -81,7 +81,7 @@  def readfromstore(self, text):
 
     return (text, True)
 
-def writetostore(self, text):
+def writetostore(orig, self, text):
     # hg filelog metadata (includes rename, etc)
     hgmeta, offset = storageutil.parsemeta(text)
     if offset and offset > 0:
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -763,6 +763,9 @@  def resolverevlogstorevfsoptions(ui, req
         if r.startswith(b'exp-compression-'):
             options[b'compengine'] = r[len(b'exp-compression-'):]
 
+    if b'lfs' in requirements:
+        options[b'enableextstored'] = True
+
     if repository.NARROW_REQUIREMENT in requirements:
         options[b'enableellipsis'] = True
 
diff --git a/mercurial/revlog.py b/mercurial/revlog.py
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -110,6 +110,26 @@  parsers = policy.importmod(r'parsers')
     REVIDX_ISCENSORED: None,
 }
 
+# Flag processors for REVIDX_EXTSTORED.
+def extstoredreadprocessor(rl, text):
+    raise error.ProgrammingError(b'extstored flags processor enabled without'
+                                 b' wrapping processor function')
+
+def extstoredwriteprocessor(rl, text):
+    raise error.ProgrammingError(b'extstored flags processor enabled without'
+                                 b' wrapping processor function')
+
+def extstoredrawprocessor(rl, text):
+    raise error.ProgrammingError(b'extstored flags processor enabled without'
+                                 b' wrapping processor function')
+
+# Lambdas are needed here so that these methods can be wrapped by lfs.
+extstoredprocessor = (
+    lambda rl, text: extstoredreadprocessor(rl, text),
+    lambda rl, text: extstoredwriteprocessor(rl, text),
+    lambda rl, text: extstoredrawprocessor(rl, text),
+)
+
 # Flag processors for REVIDX_ELLIPSIS.
 def ellipsisreadprocessor(rl, text):
     return text, False
@@ -405,6 +425,8 @@  class revlog(object):
                 self._srdensitythreshold = opts['sparse-read-density-threshold']
             if 'sparse-read-min-gap-size' in opts:
                 self._srmingapsize = opts['sparse-read-min-gap-size']
+            if opts.get('enableextstored'):
+                self._flagprocessors[REVIDX_EXTSTORED] = extstoredprocessor
             if opts.get('enableellipsis'):
                 self._flagprocessors[REVIDX_ELLIPSIS] = ellipsisprocessor