changeset 41221:73203cdfe3fe

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.
author Boris Feld <boris.feld@octobus.net>
date Fri, 04 Jan 2019 05:26:13 +0100
parents 8d26026b3335
children 8aca89a694d4
files mercurial/revsetlang.py
diffstat 1 files changed, 22 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/revsetlang.py	Fri Jan 04 05:16:57 2019 +0100
+++ b/mercurial/revsetlang.py	Fri Jan 04 05:26:13 2019 +0100
@@ -15,6 +15,7 @@
     node,
     parser,
     pycompat,
+    smartset,
     util,
 )
 from .utils import (
@@ -682,6 +683,10 @@
     for t, arg in parsed:
         if t is None:
             ret.append(arg)
+        elif t == 'baseset':
+            if isinstance(arg, set):
+                arg = sorted(arg)
+            ret.append(_formatintlist(list(arg)))
         else:
             raise error.ProgrammingError("unknown revspec item type: %r" % t)
     return b''.join(ret)
@@ -692,7 +697,8 @@
     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)
@@ -722,10 +728,25 @@
         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
+                #
+                # We have been very conservative here for the first version.
+                # Other types (eg: generator) are probably fine, but we did not
+                # wanted to take any risk>
+                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):