[PATCH 7 of 8] revset: detect integer list on parsing

Boris Feld boris.feld at octobus.net
Fri Jan 11 06:29:09 EST 2019


# HG changeset patch
# User Boris Feld <boris.feld at octobus.net>
# Date 1546575973 -3600
#      Fri Jan 04 05:26:13 2019 +0100
# Node ID 4a56fbdacff33c3985bbb84f2e19ddfbd48ed4fa
# Parent  438ea9b8a44c181d62741338c1d16b2031fdda41
# EXP-Topic revs-efficiency
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 4a56fbdacff3
revset: detect integer list on parsing

Right now, using "%ld" with `repo.revs("…%ld…", somerevs)` is very
inefficient, all items in `somerevs` will be serialized to ascii and then
reparsed as integers. If `somerevs` contains just an handful of entry this is
fine, however, when you get to thousands or hundreds of thousands of revisions
this becomes very slow.

To avoid this serialization we need to first detect this situation. The code
involved in the whole process is quite complex so we start simple and focus on
some "simple" but widespread cases.

So far we only detect the situation and don't do anything special about it.
The singled out will be serialized in `formatspec` in the same way as before.

diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py
--- a/mercurial/revsetlang.py
+++ b/mercurial/revsetlang.py
@@ -15,6 +15,7 @@ from . import (
     node,
     parser,
     pycompat,
+    smartset,
     util,
 )
 from .utils import (
@@ -682,6 +683,13 @@ def formatspec(expr, *args):
     for t, arg in parsed:
         if t is None:
             ret.append(arg)
+        if t == 'baseset':
+            if isinstance(arg, set):
+                arg = sorted(arg)
+            try:
+                ret.append(_formatintlist(list(arg)))
+            except (TypeError, ValueError):
+                raise error.ParseError(_('invalid argument for revspec'))
     return b''.join(ret)
 
 def _parseargs(expr, args):
@@ -690,7 +698,8 @@ def _parseargs(expr, args):
     return a list of tuple [(arg-type, arg-value)]
 
     Arg-type can be:
-    * None: a string ready to be concatenated into a final spec
+    * None:      a string ready to be concatenated into a final spec
+    * 'baseset': an iterable of revisions
     """
     expr = pycompat.bytestr(expr)
     argiter = iter(args)
@@ -720,10 +729,21 @@ def _parseargs(expr, args):
         if f:
             # a list of some type, might be expensive, do not replace
             pos += 1
+            islist = (d == 'l')
             try:
                 d = expr[pos]
             except IndexError:
                 raise error.ParseError(_('incomplete revspec format character'))
+            if islist and d == 'd' and arg:
+                # special case, we might be able to speedup the list of int case
+                safeinputtype = (list, tuple, set, smartset.abstractsmartset)
+                if isinstance(arg, safeinputtype):
+                    # we don't create a baseset yet, because it come with an
+                    # extra cost. If we are going to serialize it we better
+                    # skip it.
+                    ret.append(('baseset', arg))
+                    pos += 1
+                    continue
             try:
                 ret.append((None, f(list(arg), d)))
             except (TypeError, ValueError):


More information about the Mercurial-devel mailing list