Patchwork [4,of,5,flagprocessor,v6] revlog: processflags()

login
register
mail settings
Submitter Remi Chaintron
Date Dec. 24, 2016, 4:36 p.m.
Message ID <c94d2907a470ca03b4a4.1482597390@remi-mbp2>
Download mbox | patch
Permalink /patch/18023/
State Changes Requested
Delegated to: Pierre-Yves David
Headers show

Comments

Remi Chaintron - Dec. 24, 2016, 4:36 p.m.
# HG changeset patch
# User Remi Chaintron <remi@fb.com>
# Date 1482596881 18000
#      Sat Dec 24 11:28:01 2016 -0500
# Node ID c94d2907a470ca03b4a4a8da514b66d2f8952bce
# Parent  b1d220e584e6fc861a40a5aeefa0c3b19ae09126
revlog: processflags()
Rémi Chaintron - Dec. 24, 2016, 4:51 p.m.
Forgot the commit message in this one, is there a way I can update this
single patch with the commit message?

Commit message:

revlog: processflags()

    Add the ability for revlog objects to process revision flags and apply
    registered transforms on read/write operations.

    This patch introduces the 'revlog.processflags()' method that looks at
revision
    flags and applies transforms registered on them. Due to the need to
handle
    non-commutative operations, flag transforms are applied in stable order
but the
    order in which the transforms are applied is reversed between read and
write operations.

    The 'flagtransform' namedtuple is also introduced, and is to be used by
    extensions to define the behavior of transforms via its 'apply' and
'applyraw'
    fields.
    The 'processflags()' method uses the 'raw' argument of 'revision()' and
    '_addrevision()' to determine which field to use when applying
transforms.

    The 'raw' argument usually indicates that the contents are not to be
modified by
    the transform, and might not be validable for hash integrity. This is
expected
    for specific workflows such as changegroup generation and debug
commands that
    are interested in the actual contents of revlogs rather than the
transformed
    contnts after application of a transform.

On Sat, 24 Dec 2016 at 11:36 Remi Chaintron <remi@fb.com> wrote:

> # HG changeset patch
> # User Remi Chaintron <remi@fb.com>
> # Date 1482596881 18000
> #      Sat Dec 24 11:28:01 2016 -0500
> # Node ID c94d2907a470ca03b4a4a8da514b66d2f8952bce
> # Parent  b1d220e584e6fc861a40a5aeefa0c3b19ae09126
> revlog: processflags()
>
> diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
> --- a/mercurial/bundlerepo.py
> +++ b/mercurial/bundlerepo.py
> @@ -148,7 +148,9 @@
>              delta = self._chunk(chain.pop())
>              text = mdiff.patches(text, [delta])
>
> -        self.checkhash(text, node, rev=rev)
> +        text, validatehash = self.processflags(text, self.flags(rev),
> raw=raw)
> +        if validatehash is True:
> +            self.checkhash(text, node, rev=rev)
>          self._cache = (node, rev, text)
>          return text
>
> diff --git a/mercurial/revlog.py b/mercurial/revlog.py
> --- a/mercurial/revlog.py
> +++ b/mercurial/revlog.py
> @@ -56,6 +56,10 @@
>  REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be
> verified
>  REVIDX_DEFAULT_FLAGS = 0
>  REVIDX_KNOWN_FLAGS = REVIDX_ISCENSORED
> +# stable order in which flags need to be processed and their transforms
> applied
> +REVIDX_FLAGS_ORDER = [
> +    REVIDX_ISCENSORED,
> +]
>
>  # max size of revlog with inline data
>  _maxinline = 131072
> @@ -65,6 +69,33 @@
>  LookupError = error.LookupError
>  CensoredNodeError = error.CensoredNodeError
>
> +# 'flagtransform' namedtuple type for extensions to use to register
> transforms
> +# on revlog flags.
> +# - The apply transform will be used as a default behavior.
> +# - The applyraw transform will be used for changegroup generation and
> debug
> +# methods.
> +flagtransform = collections.namedtuple('flagtransform', ['apply',
> 'applyraw'])
> +# Store flag transforms (cf. 'addflagtransform()' to register)
> +_flagtransforms = { }
> +
> +def addflagtransform(flag, transform):
> +    """Register transforms on revision data flags.
> +
> +    Invariant:
> +    - Flags need to be defined in REVIDX_KNOWN_FLAGS and
> REVIDX_FLAGS_ORDER.
> +    - Only one transform can be registered on a specific flag.
> +    - the transform must be of type 'flagtransform'
> +    """
> +    if not flag & REVIDX_KNOWN_FLAGS:
> +        raise error.Abort(_("cannot register transform on unknown flag."))
> +    if not flag in REVIDX_FLAGS_ORDER:
> +        raise error.Abort(_("flag need to be defined in
> REVIDX_FLAGS_ORDER."))
> +    if _flagtransforms.get(flag, None) is not None:
> +        raise error.Abort(_("cannot register multiple transforms on
> flag."))
> +    if not isinstance(transform, flagtransform):
> +        raise error.Abort(_("invalid transform type."))
> +    _flagtransforms[flag] = transform
> +
>  def getoffset(q):
>      return int(q >> 16)
>
> @@ -1248,7 +1279,11 @@
>              bins = bins[1:]
>
>          text = mdiff.patches(text, bins)
> -        self.checkhash(text, node, rev=rev)
> +
> +        text, validatehash = self.processflags(text, self.flags(rev),
> raw=raw)
> +        if validatehash:
> +            self.checkhash(text, node, rev=rev)
> +
>          self._cache = (node, rev, text)
>          return text
>
> @@ -1260,6 +1295,56 @@
>          """
>          return hash(text, p1, p2)
>
> +    def processflags(self, text, flags, raw=False, reverse=False):
> +        """Process revision flags and apply registered transforms.
> +
> +        ``text`` - the revision data to process
> +        ``flags`` - the revision flags
> +        ``raw`` - an optional argument describing if the raw transform
> should be
> +        applied.
> +        ``reverse`` - an optional argument describing whether the flags
> should
> +        be processed in order or reverse order.
> +
> +        This method processes the flags in the order (or reverse order if
> +        ``reverse`` is False) defined by REVIDX_FLAGS_ORDER, applying the
> +        transforms registered for present flags. The order of flags
> defined in
> +        REVIDX_FLAGS_ORDER needs to be stable for non-commutative
> +        operations.
> +
> +        If a ``flagtransform`` namedtuple has been registered on a flag,
> the
> +        ``raw`` argument is used to determine which transform apply. If
> ``raw``
> +        is True, the ``applyraw`` field is used, ``apply`` if not
> (default).
> +        If the ``raw`` argument is set, it indicates we are currently
> processing
> +        flags in the context of changegroup generation or debug commands.
> +
> +        Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is
> the
> +        processed text and ``validatehash`` is a bool indicating whether
> the
> +        returned text should be checked for hash integrity.
> +        """
> +        # Check all flags are known.
> +        if flags & ~REVIDX_KNOWN_FLAGS:
> +            raise RevlogError(_('incompatible revision flag %x') %
> +                              (flags & ~REVIDX_KNOWN_FLAGS))
> +        validatehash = True
> +        # Depending on the operation (read or write), the order might be
> +        # reversed due to non-commutative transforms.
> +        orderedflags = REVIDX_FLAGS_ORDER
> +        if reverse:
> +            orderedflags = reversed(orderedflags)
> +
> +        for flag in orderedflags:
> +            # If a transform has been registered for a known flag, apply
> and
> +            # update result tuple.
> +            if flag & flags:
> +                transform = _flagtransforms.get(flag, None)
> +                if transform is not None:
> +                    apply, applyraw = transform
> +                    f = apply
> +                    if raw:
> +                        f = applyraw
> +                    text, validatehash = f(self, text)
> +        return text, validatehash
> +
>      def checkhash(self, text, node, p1=None, p2=None, rev=None):
>          """Check node hash integrity.
>
> @@ -1447,7 +1532,11 @@
>                  btext[0] = mdiff.patch(basetext, delta)
>
>              try:
> -                self.checkhash(btext[0], node, p1=p1, p2=p2)
> +                btext[0], validatehash = self.processflags(btext[0],
> flags,
> +                                                           raw=raw,
> +                                                           reverse=True)
> +                if validatehash is True:
> +                    self.checkhash(btext[0], node, p1=p1, p2=p2)
>                  if flags & REVIDX_ISCENSORED:
>                      raise RevlogError(_('node %s is not censored') % node)
>              except CensoredNodeError:
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
Gregory Szorc - Dec. 24, 2016, 8:07 p.m.
On Sat, Dec 24, 2016 at 9:36 AM, Remi Chaintron <remi@fb.com> wrote:

> # HG changeset patch
> # User Remi Chaintron <remi@fb.com>
> # Date 1482596881 18000
> #      Sat Dec 24 11:28:01 2016 -0500
> # Node ID c94d2907a470ca03b4a4a8da514b66d2f8952bce
> # Parent  b1d220e584e6fc861a40a5aeefa0c3b19ae09126
> revlog: processflags()
>
> diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
> --- a/mercurial/bundlerepo.py
> +++ b/mercurial/bundlerepo.py
> @@ -148,7 +148,9 @@
>              delta = self._chunk(chain.pop())
>              text = mdiff.patches(text, [delta])
>
> -        self.checkhash(text, node, rev=rev)
> +        text, validatehash = self.processflags(text, self.flags(rev),
> raw=raw)
> +        if validatehash is True:
> +            self.checkhash(text, node, rev=rev)
>          self._cache = (node, rev, text)
>          return text
>
> diff --git a/mercurial/revlog.py b/mercurial/revlog.py
> --- a/mercurial/revlog.py
> +++ b/mercurial/revlog.py
> @@ -56,6 +56,10 @@
>  REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be
> verified
>  REVIDX_DEFAULT_FLAGS = 0
>  REVIDX_KNOWN_FLAGS = REVIDX_ISCENSORED
> +# stable order in which flags need to be processed and their transforms
> applied
> +REVIDX_FLAGS_ORDER = [
> +    REVIDX_ISCENSORED,
> +]
>
>  # max size of revlog with inline data
>  _maxinline = 131072
> @@ -65,6 +69,33 @@
>  LookupError = error.LookupError
>  CensoredNodeError = error.CensoredNodeError
>
> +# 'flagtransform' namedtuple type for extensions to use to register
> transforms
> +# on revlog flags.
> +# - The apply transform will be used as a default behavior.
> +# - The applyraw transform will be used for changegroup generation and
> debug
> +# methods.
> +flagtransform = collections.namedtuple('flagtransform', ['apply',
> 'applyraw'])
>

String literals in namedtuple declarations need u'' prefixes to prevent
Python 3 from barfing. This can likely be done as a follow-up, since Python
3 isn't officially supported.


> +# Store flag transforms (cf. 'addflagtransform()' to register)
> +_flagtransforms = { }
> +
> +def addflagtransform(flag, transform):
> +    """Register transforms on revision data flags.
> +
> +    Invariant:
> +    - Flags need to be defined in REVIDX_KNOWN_FLAGS and
> REVIDX_FLAGS_ORDER.
> +    - Only one transform can be registered on a specific flag.
> +    - the transform must be of type 'flagtransform'
> +    """
> +    if not flag & REVIDX_KNOWN_FLAGS:
> +        raise error.Abort(_("cannot register transform on unknown flag."))
> +    if not flag in REVIDX_FLAGS_ORDER:
> +        raise error.Abort(_("flag need to be defined in
> REVIDX_FLAGS_ORDER."))
> +    if _flagtransforms.get(flag, None) is not None:
> +        raise error.Abort(_("cannot register multiple transforms on
> flag."))
> +    if not isinstance(transform, flagtransform):
> +        raise error.Abort(_("invalid transform type."))
> +    _flagtransforms[flag] = transform
> +
>  def getoffset(q):
>      return int(q >> 16)
>
> @@ -1248,7 +1279,11 @@
>              bins = bins[1:]
>
>          text = mdiff.patches(text, bins)
> -        self.checkhash(text, node, rev=rev)
> +
> +        text, validatehash = self.processflags(text, self.flags(rev),
> raw=raw)
> +        if validatehash:
> +            self.checkhash(text, node, rev=rev)
> +
>          self._cache = (node, rev, text)
>          return text
>
> @@ -1260,6 +1295,56 @@
>          """
>          return hash(text, p1, p2)
>
> +    def processflags(self, text, flags, raw=False, reverse=False):
> +        """Process revision flags and apply registered transforms.
> +
> +        ``text`` - the revision data to process
> +        ``flags`` - the revision flags
> +        ``raw`` - an optional argument describing if the raw transform
> should be
> +        applied.
> +        ``reverse`` - an optional argument describing whether the flags
> should
> +        be processed in order or reverse order.
> +
> +        This method processes the flags in the order (or reverse order if
> +        ``reverse`` is False) defined by REVIDX_FLAGS_ORDER, applying the
> +        transforms registered for present flags. The order of flags
> defined in
> +        REVIDX_FLAGS_ORDER needs to be stable for non-commutative
> +        operations.
> +
> +        If a ``flagtransform`` namedtuple has been registered on a flag,
> the
> +        ``raw`` argument is used to determine which transform apply. If
> ``raw``
> +        is True, the ``applyraw`` field is used, ``apply`` if not
> (default).
> +        If the ``raw`` argument is set, it indicates we are currently
> processing
> +        flags in the context of changegroup generation or debug commands.
> +
> +        Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is
> the
> +        processed text and ``validatehash`` is a bool indicating whether
> the
> +        returned text should be checked for hash integrity.
> +        """
> +        # Check all flags are known.
> +        if flags & ~REVIDX_KNOWN_FLAGS:
> +            raise RevlogError(_('incompatible revision flag %x') %
> +                              (flags & ~REVIDX_KNOWN_FLAGS))
> +        validatehash = True
> +        # Depending on the operation (read or write), the order might be
> +        # reversed due to non-commutative transforms.
> +        orderedflags = REVIDX_FLAGS_ORDER
> +        if reverse:
> +            orderedflags = reversed(orderedflags)
> +
> +        for flag in orderedflags:
> +            # If a transform has been registered for a known flag, apply
> and
> +            # update result tuple.
> +            if flag & flags:
> +                transform = _flagtransforms.get(flag, None)
> +                if transform is not None:
> +                    apply, applyraw = transform
> +                    f = apply
> +                    if raw:
> +                        f = applyraw
> +                    text, validatehash = f(self, text)
> +        return text, validatehash
> +
>      def checkhash(self, text, node, p1=None, p2=None, rev=None):
>          """Check node hash integrity.
>
> @@ -1447,7 +1532,11 @@
>                  btext[0] = mdiff.patch(basetext, delta)
>
>              try:
> -                self.checkhash(btext[0], node, p1=p1, p2=p2)
> +                btext[0], validatehash = self.processflags(btext[0],
> flags,
> +                                                           raw=raw,
> +                                                           reverse=True)
> +                if validatehash is True:
> +                    self.checkhash(btext[0], node, p1=p1, p2=p2)
>                  if flags & REVIDX_ISCENSORED:
>                      raise RevlogError(_('node %s is not censored') % node)
>              except CensoredNodeError:
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
Rémi Chaintron - Dec. 30, 2016, 6:01 p.m.
Updated, will send with v7.
Thanks!

On Sat, 24 Dec 2016 at 15:07 Gregory Szorc <gregory.szorc@gmail.com> wrote:

> On Sat, Dec 24, 2016 at 9:36 AM, Remi Chaintron <remi@fb.com> wrote:
>
> # HG changeset patch
> # User Remi Chaintron <remi@fb.com>
> # Date 1482596881 18000
> #      Sat Dec 24 11:28:01 2016 -0500
> # Node ID c94d2907a470ca03b4a4a8da514b66d2f8952bce
> # Parent  b1d220e584e6fc861a40a5aeefa0c3b19ae09126
> revlog: processflags()
>
> diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
> --- a/mercurial/bundlerepo.py
> +++ b/mercurial/bundlerepo.py
> @@ -148,7 +148,9 @@
>              delta = self._chunk(chain.pop())
>              text = mdiff.patches(text, [delta])
>
> -        self.checkhash(text, node, rev=rev)
> +        text, validatehash = self.processflags(text, self.flags(rev),
> raw=raw)
> +        if validatehash is True:
> +            self.checkhash(text, node, rev=rev)
>          self._cache = (node, rev, text)
>          return text
>
> diff --git a/mercurial/revlog.py b/mercurial/revlog.py
> --- a/mercurial/revlog.py
> +++ b/mercurial/revlog.py
> @@ -56,6 +56,10 @@
>  REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be
> verified
>  REVIDX_DEFAULT_FLAGS = 0
>  REVIDX_KNOWN_FLAGS = REVIDX_ISCENSORED
> +# stable order in which flags need to be processed and their transforms
> applied
> +REVIDX_FLAGS_ORDER = [
> +    REVIDX_ISCENSORED,
> +]
>
>  # max size of revlog with inline data
>  _maxinline = 131072
> @@ -65,6 +69,33 @@
>  LookupError = error.LookupError
>  CensoredNodeError = error.CensoredNodeError
>
> +# 'flagtransform' namedtuple type for extensions to use to register
> transforms
> +# on revlog flags.
> +# - The apply transform will be used as a default behavior.
> +# - The applyraw transform will be used for changegroup generation and
> debug
> +# methods.
> +flagtransform = collections.namedtuple('flagtransform', ['apply',
> 'applyraw'])
>
>
> String literals in namedtuple declarations need u'' prefixes to prevent
> Python 3 from barfing. This can likely be done as a follow-up, since Python
> 3 isn't officially supported.
>
>
> +# Store flag transforms (cf. 'addflagtransform()' to register)
> +_flagtransforms = { }
> +
> +def addflagtransform(flag, transform):
> +    """Register transforms on revision data flags.
> +
> +    Invariant:
> +    - Flags need to be defined in REVIDX_KNOWN_FLAGS and
> REVIDX_FLAGS_ORDER.
> +    - Only one transform can be registered on a specific flag.
> +    - the transform must be of type 'flagtransform'
> +    """
> +    if not flag & REVIDX_KNOWN_FLAGS:
> +        raise error.Abort(_("cannot register transform on unknown flag."))
> +    if not flag in REVIDX_FLAGS_ORDER:
> +        raise error.Abort(_("flag need to be defined in
> REVIDX_FLAGS_ORDER."))
> +    if _flagtransforms.get(flag, None) is not None:
> +        raise error.Abort(_("cannot register multiple transforms on
> flag."))
> +    if not isinstance(transform, flagtransform):
> +        raise error.Abort(_("invalid transform type."))
> +    _flagtransforms[flag] = transform
> +
>  def getoffset(q):
>      return int(q >> 16)
>
> @@ -1248,7 +1279,11 @@
>              bins = bins[1:]
>
>          text = mdiff.patches(text, bins)
> -        self.checkhash(text, node, rev=rev)
> +
> +        text, validatehash = self.processflags(text, self.flags(rev),
> raw=raw)
> +        if validatehash:
> +            self.checkhash(text, node, rev=rev)
> +
>          self._cache = (node, rev, text)
>          return text
>
> @@ -1260,6 +1295,56 @@
>          """
>          return hash(text, p1, p2)
>
> +    def processflags(self, text, flags, raw=False, reverse=False):
> +        """Process revision flags and apply registered transforms.
> +
> +        ``text`` - the revision data to process
> +        ``flags`` - the revision flags
> +        ``raw`` - an optional argument describing if the raw transform
> should be
> +        applied.
> +        ``reverse`` - an optional argument describing whether the flags
> should
> +        be processed in order or reverse order.
> +
> +        This method processes the flags in the order (or reverse order if
> +        ``reverse`` is False) defined by REVIDX_FLAGS_ORDER, applying the
> +        transforms registered for present flags. The order of flags
> defined in
> +        REVIDX_FLAGS_ORDER needs to be stable for non-commutative
> +        operations.
> +
> +        If a ``flagtransform`` namedtuple has been registered on a flag,
> the
> +        ``raw`` argument is used to determine which transform apply. If
> ``raw``
> +        is True, the ``applyraw`` field is used, ``apply`` if not
> (default).
> +        If the ``raw`` argument is set, it indicates we are currently
> processing
> +        flags in the context of changegroup generation or debug commands.
> +
> +        Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is
> the
> +        processed text and ``validatehash`` is a bool indicating whether
> the
> +        returned text should be checked for hash integrity.
> +        """
> +        # Check all flags are known.
> +        if flags & ~REVIDX_KNOWN_FLAGS:
> +            raise RevlogError(_('incompatible revision flag %x') %
> +                              (flags & ~REVIDX_KNOWN_FLAGS))
> +        validatehash = True
> +        # Depending on the operation (read or write), the order might be
> +        # reversed due to non-commutative transforms.
> +        orderedflags = REVIDX_FLAGS_ORDER
> +        if reverse:
> +            orderedflags = reversed(orderedflags)
> +
> +        for flag in orderedflags:
> +            # If a transform has been registered for a known flag, apply
> and
> +            # update result tuple.
> +            if flag & flags:
> +                transform = _flagtransforms.get(flag, None)
> +                if transform is not None:
> +                    apply, applyraw = transform
> +                    f = apply
> +                    if raw:
> +                        f = applyraw
> +                    text, validatehash = f(self, text)
> +        return text, validatehash
> +
>      def checkhash(self, text, node, p1=None, p2=None, rev=None):
>          """Check node hash integrity.
>
> @@ -1447,7 +1532,11 @@
>                  btext[0] = mdiff.patch(basetext, delta)
>
>              try:
> -                self.checkhash(btext[0], node, p1=p1, p2=p2)
> +                btext[0], validatehash = self.processflags(btext[0],
> flags,
> +                                                           raw=raw,
> +                                                           reverse=True)
> +                if validatehash is True:
> +                    self.checkhash(btext[0], node, p1=p1, p2=p2)
>                  if flags & REVIDX_ISCENSORED:
>                      raise RevlogError(_('node %s is not censored') % node)
>              except CensoredNodeError:
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>

Patch

diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py

--- a/mercurial/bundlerepo.py

+++ b/mercurial/bundlerepo.py

@@ -148,7 +148,9 @@ 

             delta = self._chunk(chain.pop())
             text = mdiff.patches(text, [delta])
 
-        self.checkhash(text, node, rev=rev)

+        text, validatehash = self.processflags(text, self.flags(rev), raw=raw)

+        if validatehash is True:

+            self.checkhash(text, node, rev=rev)

         self._cache = (node, rev, text)
         return text
 
diff --git a/mercurial/revlog.py b/mercurial/revlog.py

--- a/mercurial/revlog.py

+++ b/mercurial/revlog.py

@@ -56,6 +56,10 @@ 

 REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
 REVIDX_DEFAULT_FLAGS = 0
 REVIDX_KNOWN_FLAGS = REVIDX_ISCENSORED
+# stable order in which flags need to be processed and their transforms applied

+REVIDX_FLAGS_ORDER = [

+    REVIDX_ISCENSORED,

+]

 
 # max size of revlog with inline data
 _maxinline = 131072
@@ -65,6 +69,33 @@ 

 LookupError = error.LookupError
 CensoredNodeError = error.CensoredNodeError
 
+# 'flagtransform' namedtuple type for extensions to use to register transforms

+# on revlog flags.

+# - The apply transform will be used as a default behavior.

+# - The applyraw transform will be used for changegroup generation and debug

+# methods.

+flagtransform = collections.namedtuple('flagtransform', ['apply', 'applyraw'])

+# Store flag transforms (cf. 'addflagtransform()' to register)

+_flagtransforms = { }

+

+def addflagtransform(flag, transform):

+    """Register transforms on revision data flags.

+

+    Invariant:

+    - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER.

+    - Only one transform can be registered on a specific flag.

+    - the transform must be of type 'flagtransform'

+    """

+    if not flag & REVIDX_KNOWN_FLAGS:

+        raise error.Abort(_("cannot register transform on unknown flag."))

+    if not flag in REVIDX_FLAGS_ORDER:

+        raise error.Abort(_("flag need to be defined in REVIDX_FLAGS_ORDER."))

+    if _flagtransforms.get(flag, None) is not None:

+        raise error.Abort(_("cannot register multiple transforms on flag."))

+    if not isinstance(transform, flagtransform):

+        raise error.Abort(_("invalid transform type."))

+    _flagtransforms[flag] = transform

+

 def getoffset(q):
     return int(q >> 16)
 
@@ -1248,7 +1279,11 @@ 

             bins = bins[1:]
 
         text = mdiff.patches(text, bins)
-        self.checkhash(text, node, rev=rev)

+

+        text, validatehash = self.processflags(text, self.flags(rev), raw=raw)

+        if validatehash:

+            self.checkhash(text, node, rev=rev)

+

         self._cache = (node, rev, text)
         return text
 
@@ -1260,6 +1295,56 @@ 

         """
         return hash(text, p1, p2)
 
+    def processflags(self, text, flags, raw=False, reverse=False):

+        """Process revision flags and apply registered transforms.

+

+        ``text`` - the revision data to process

+        ``flags`` - the revision flags

+        ``raw`` - an optional argument describing if the raw transform should be

+        applied.

+        ``reverse`` - an optional argument describing whether the flags should

+        be processed in order or reverse order.

+

+        This method processes the flags in the order (or reverse order if

+        ``reverse`` is False) defined by REVIDX_FLAGS_ORDER, applying the

+        transforms registered for present flags. The order of flags defined in

+        REVIDX_FLAGS_ORDER needs to be stable for non-commutative

+        operations.

+

+        If a ``flagtransform`` namedtuple has been registered on a flag, the

+        ``raw`` argument is used to determine which transform apply. If ``raw``

+        is True, the ``applyraw`` field is used, ``apply`` if not (default).

+        If the ``raw`` argument is set, it indicates we are currently processing

+        flags in the context of changegroup generation or debug commands.

+

+        Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the

+        processed text and ``validatehash`` is a bool indicating whether the

+        returned text should be checked for hash integrity.

+        """

+        # Check all flags are known.

+        if flags & ~REVIDX_KNOWN_FLAGS:

+            raise RevlogError(_('incompatible revision flag %x') %

+                              (flags & ~REVIDX_KNOWN_FLAGS))

+        validatehash = True

+        # Depending on the operation (read or write), the order might be

+        # reversed due to non-commutative transforms.

+        orderedflags = REVIDX_FLAGS_ORDER

+        if reverse:

+            orderedflags = reversed(orderedflags)

+

+        for flag in orderedflags:

+            # If a transform has been registered for a known flag, apply and

+            # update result tuple.

+            if flag & flags:

+                transform = _flagtransforms.get(flag, None)

+                if transform is not None:

+                    apply, applyraw = transform

+                    f = apply

+                    if raw:

+                        f = applyraw

+                    text, validatehash = f(self, text)

+        return text, validatehash

+

     def checkhash(self, text, node, p1=None, p2=None, rev=None):
         """Check node hash integrity.
 
@@ -1447,7 +1532,11 @@ 

                 btext[0] = mdiff.patch(basetext, delta)
 
             try:
-                self.checkhash(btext[0], node, p1=p1, p2=p2)

+                btext[0], validatehash = self.processflags(btext[0], flags,

+                                                           raw=raw,

+                                                           reverse=True)

+                if validatehash is True:

+                    self.checkhash(btext[0], node, p1=p1, p2=p2)

                 if flags & REVIDX_ISCENSORED:
                     raise RevlogError(_('node %s is not censored') % node)
             except CensoredNodeError: