D451: revset: remove order information from tree

quark (Jun Wu) phabricator at mercurial-scm.org
Sun Aug 20 04:43:13 EDT 2017


quark updated this revision to Diff 1103.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D451?vs=1100&id=1103

REVISION DETAIL
  https://phab.mercurial-scm.org/D451

AFFECTED FILES
  mercurial/revset.py
  mercurial/revsetlang.py
  tests/test-revset.t

CHANGE DETAILS

diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -166,8 +166,7 @@
     None)
   * optimized:
   (rangeall
-    None
-    define)
+    None)
   * set:
   <spanset+ 0:10>
   0
@@ -495,8 +494,7 @@
     ('symbol', 'foo')
     (func
       ('symbol', '_notpublic')
-      None
-      any))
+      None))
   hg: parse error: can't use a key-value pair in this context
   [255]
 
@@ -538,52 +536,43 @@
     (not
       (func
         ('symbol', 'public')
-        None
-        any)
-      define)
+        None))
     ('symbol', 'generations')
-    ('symbol', '0')
-    define)
+    ('symbol', '0'))
   * optimized:
   (relsubscript
     (func
       ('symbol', '_notpublic')
-      None
-      any)
+      None)
     ('symbol', 'generations')
-    ('symbol', '0')
-    define)
+    ('symbol', '0'))
 
 resolution of subscript and relation-subscript ternary operators:
 
   $ hg debugrevspec -p analyzed 'tip[0]'
   * analyzed:
   (subscript
     ('symbol', 'tip')
-    ('symbol', '0')
-    define)
+    ('symbol', '0'))
   hg: parse error: can't use a subscript in this context
   [255]
 
   $ hg debugrevspec -p analyzed 'tip#rel[0]'
   * analyzed:
   (relsubscript
     ('symbol', 'tip')
     ('symbol', 'rel')
-    ('symbol', '0')
-    define)
+    ('symbol', '0'))
   hg: parse error: unknown identifier: rel
   [255]
 
   $ hg debugrevspec -p analyzed '(tip#rel)[0]'
   * analyzed:
   (subscript
     (relation
       ('symbol', 'tip')
-      ('symbol', 'rel')
-      define)
-    ('symbol', '0')
-    define)
+      ('symbol', 'rel'))
+    ('symbol', '0'))
   hg: parse error: can't use a subscript in this context
   [255]
 
@@ -593,23 +582,19 @@
     (relsubscript
       ('symbol', 'tip')
       ('symbol', 'rel')
-      ('symbol', '0')
-      define)
-    ('symbol', '1')
-    define)
+      ('symbol', '0'))
+    ('symbol', '1'))
   hg: parse error: can't use a subscript in this context
   [255]
 
   $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
   * analyzed:
   (relsubscript
     (relation
       ('symbol', 'tip')
-      ('symbol', 'rel0')
-      define)
+      ('symbol', 'rel0'))
     ('symbol', 'rel1')
-    ('symbol', '1')
-    define)
+    ('symbol', '1'))
   hg: parse error: unknown identifier: rel1
   [255]
 
@@ -619,11 +604,9 @@
     (relsubscript
       ('symbol', 'tip')
       ('symbol', 'rel0')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     ('symbol', 'rel1')
-    ('symbol', '1')
-    define)
+    ('symbol', '1'))
   hg: parse error: unknown identifier: rel1
   [255]
 
@@ -700,20 +683,15 @@
     (or
       (list
         ('symbol', '0')
-        ('symbol', '1'))
-      define)
+        ('symbol', '1')))
     (not
-      ('symbol', '1')
-      follow)
-    define)
+      ('symbol', '1')))
   * optimized:
   (difference
     (func
       ('symbol', '_list')
-      ('string', '0\x001')
-      define)
-    ('symbol', '1')
-    define)
+      ('string', '0\x001'))
+    ('symbol', '1'))
   0
 
   $ hg debugrevspec -p unknown '0'
@@ -733,18 +711,16 @@
   (and
     (func
       ('symbol', 'r3232')
-      None
-      define)
-    ('symbol', '2')
-    define)
+      None)
+    ('symbol', '2'))
   * optimized:
-  (and
-    ('symbol', '2')
-    (func
-      ('symbol', 'r3232')
-      None
-      define)
-    define)
+  (func
+    ('symbol', '_flipand')
+    (list
+      ('symbol', '2')
+      (func
+        ('symbol', 'r3232')
+        None)))
   * analyzed set:
   <baseset [2]>
   * optimized set:
@@ -776,8 +752,7 @@
     None)
   * analyzed:
   (rangeall
-    None
-    define)
+    None)
   * set:
   <spanset+ 0:10>
   0
@@ -793,8 +768,7 @@
   $ try -p analyzed ':1'
   * analyzed:
   (rangepre
-    ('symbol', '1')
-    define)
+    ('symbol', '1'))
   * set:
   <spanset+ 0:2>
   0
@@ -805,9 +779,7 @@
     (or
       (list
         ('symbol', '1')
-        ('symbol', '2'))
-      define)
-    define)
+        ('symbol', '2'))))
   * set:
   <spanset+ 0:3>
   0
@@ -818,9 +790,7 @@
   (rangepre
     (and
       ('symbol', '1')
-      ('symbol', '2')
-      define)
-    define)
+      ('symbol', '2')))
   * set:
   <baseset []>
 
@@ -1643,11 +1613,8 @@
     (difference
       (range
         ('symbol', '8')
-        ('symbol', '9')
-        define)
-      ('symbol', '8')
-      define)
-    define)
+        ('symbol', '9'))
+      ('symbol', '8')))
   * set:
   <baseset+ [8, 9]>
   8
@@ -1663,8 +1630,7 @@
     ('symbol', 'only')
     (list
       ('symbol', '9')
-      ('symbol', '5'))
-    define)
+      ('symbol', '5')))
   * set:
   <baseset+ [2, 4, 8, 9]>
   2
@@ -1999,18 +1965,13 @@
     (and
       (range
         ('symbol', '3')
-        ('symbol', '0')
-        define)
+        ('symbol', '0'))
       (range
         ('symbol', '0')
-        ('symbol', '3')
-        follow)
-      define)
+        ('symbol', '3')))
     (range
       ('symbol', '2')
-      ('symbol', '1')
-      any)
-    define)
+      ('symbol', '1')))
   * set:
   <filteredset
     <filteredset
@@ -2039,13 +2000,10 @@
   (and
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (func
       ('symbol', '_list')
-      ('string', '0\x001\x002')
-      follow)
-    define)
+      ('string', '0\x001\x002')))
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2072,17 +2030,13 @@
   (and
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (or
       (list
         ('symbol', '2')
         (range
           ('symbol', '0')
-          ('symbol', '1')
-          follow))
-      follow)
-    define)
+          ('symbol', '1')))))
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2104,16 +2058,15 @@
       ('symbol', '_intlist')
       ('string', '0\x001\x002')))
   * optimized:
-  (and
-    (func
-      ('symbol', '_intlist')
-      ('string', '0\x001\x002')
-      follow)
-    (range
-      ('symbol', '2')
-      ('symbol', '0')
-      define)
-    define)
+  (func
+    ('symbol', '_flipand')
+    (list
+      (func
+        ('symbol', '_intlist')
+        ('string', '0\x001\x002'))
+      (range
+        ('symbol', '2')
+        ('symbol', '0'))))
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2134,13 +2087,10 @@
   (and
     (func
       ('symbol', '_intlist')
-      ('string', '0\x002\x001')
-      define)
+      ('string', '0\x002\x001'))
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      follow)
-    define)
+      ('symbol', '0')))
   * set:
   <filteredset
     <baseset [0, 2, 1]>,
@@ -2163,13 +2113,10 @@
   (and
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (func
       ('symbol', '_hexlist')
-      ('string', '*') (glob)
-      follow)
-    define)
+      ('string', '*'))) (glob)
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2187,16 +2134,15 @@
       ('symbol', '2')
       ('symbol', '0')))
   * optimized:
-  (and
-    (range
-      ('symbol', '2')
-      ('symbol', '0')
-      follow)
-    (func
-      ('symbol', '_hexlist')
-      ('string', '*') (glob)
-      define)
-    define)
+  (func
+    ('symbol', '_flipand')
+    (list
+      (range
+        ('symbol', '2')
+        ('symbol', '0'))
+      (func
+        ('symbol', '_hexlist')
+        ('string', '*')))) (glob)
   * set:
   <baseset [0, 2, 1]>
   0
@@ -2211,13 +2157,10 @@
   (difference
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (func
       ('symbol', '_list')
-      ('string', '0\x001')
-      any)
-    define)
+      ('string', '0\x001')))
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2230,19 +2173,14 @@
   (difference
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (and
       (range
         ('symbol', '0')
-        ('symbol', '2')
-        any)
+        ('symbol', '2'))
       (func
         ('symbol', '_list')
-        ('string', '0\x001')
-        any)
-      any)
-    define)
+        ('string', '0\x001'))))
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2259,9 +2197,7 @@
     ('symbol', 'present')
     (func
       ('symbol', '_list')
-      ('string', '2\x000\x001')
-      define)
-    define)
+      ('string', '2\x000\x001')))
   * set:
   <baseset [2, 0, 1]>
   2
@@ -2284,16 +2220,12 @@
   (and
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (func
       ('symbol', 'present')
       (func
         ('symbol', '_list')
-        ('string', '0\x001\x002')
-        follow)
-      follow)
-    define)
+        ('string', '0\x001\x002'))))
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2318,16 +2250,12 @@
   (and
     (range
       ('symbol', '0')
-      ('symbol', '2')
-      define)
+      ('symbol', '2'))
     (func
       ('symbol', 'reverse')
       (func
         ('symbol', 'all')
-        None
-        define)
-      follow)
-    define)
+        None)))
   * set:
   <filteredset
     <spanset+ 0:3>,
@@ -2355,18 +2283,14 @@
   (and
     (range
       ('symbol', '0')
-      ('symbol', '2')
-      define)
+      ('symbol', '2'))
     (func
       ('symbol', 'sort')
       (list
         (func
           ('symbol', 'all')
-          None
-          define)
-        ('string', '-rev'))
-      follow)
-    define)
+          None)
+        ('string', '-rev'))))
   * set:
   <filteredset
     <spanset+ 0:3>,
@@ -2402,16 +2326,12 @@
   (and
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (func
       ('symbol', 'first')
       (func
         ('symbol', '_list')
-        ('string', '1\x000\x002')
-        define)
-      follow)
-    define)
+        ('string', '1\x000\x002'))))
   * set:
   <filteredset
     <baseset [1]>,
@@ -2435,16 +2355,12 @@
   (difference
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (func
       ('symbol', 'last')
       (func
         ('symbol', '_list')
-        ('string', '0\x002\x001')
-        define)
-      any)
-    define)
+        ('string', '0\x002\x001'))))
   * set:
   <filteredset
     <spanset- 0:3>,
@@ -2477,29 +2393,22 @@
   (and
     (range
       ('symbol', '2')
-      ('symbol', '0')
-      define)
+      ('symbol', '0'))
     (range
       (func
         ('symbol', '_list')
-        ('string', '1\x000\x002')
-        define)
+        ('string', '1\x000\x002'))
       (func
         ('symbol', '_list')
-        ('string', '0\x002\x001')
-        define)
-      follow)
-    define)
+        ('string', '0\x002\x001'))))
   * set:
   <filteredset
     <spanset- 0:3>,
     <baseset [1]>>
   1
 
- 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
- the ordering rule is determined before the rewrite; in this example,
- 'B' follows the order of the initial set, which is the same order as 'A'
- since 'A' also follows the order:
+ 'A & B' can be rewritten as 'B & A' by weight. When ordering needs to be
+ preserved (ex. taking the order of A), rewrite as '_flipand(B, A)'.
 
   $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
   (and
@@ -2513,16 +2422,15 @@
           ('symbol', '0')
           ('symbol', '1')))))
   * optimized:
-  (and
-    (func
-      ('symbol', '_list')
-      ('string', '2\x000\x001')
-      follow)
-    (func
-      ('symbol', 'contains')
-      ('string', 'glob:*')
-      define)
-    define)
+  (func
+    ('symbol', '_flipand')
+    (list
+      (func
+        ('symbol', '_list')
+        ('string', '2\x000\x001'))
+      (func
+        ('symbol', 'contains')
+        ('string', 'glob:*'))))
   * set:
   <filteredset
     <baseset+ [0, 1, 2]>,
@@ -2548,49 +2456,96 @@
           ('symbol', '2')
           ('symbol', '1')))))
   * optimized:
-  (and
-    (func
-      ('symbol', '_list')
-      ('string', '0\x002\x001')
-      follow)
-    (func
-      ('symbol', 'reverse')
+  (func
+    ('symbol', '_flipand')
+    (list
       (func
-        ('symbol', 'contains')
-        ('string', 'glob:*')
-        define)
-      define)
-    define)
+        ('symbol', '_list')
+        ('string', '0\x002\x001'))
+      (func
+        ('symbol', 'reverse')
+        (func
+          ('symbol', 'contains')
+          ('string', 'glob:*')))))
   * set:
   <filteredset
     <baseset- [0, 1, 2]>,
     <contains 'glob:*'>>
   2
   1
   0
 
+ revset implementation needs to support ordering, test "define" order support
+ of "dagrange":
+
+  $ cat > $TESTTMP/unsortable.py << EOF
+  > from __future__ import absolute_import
+  > from mercurial import registrar, revset
+  > revsetpredicate = registrar.revsetpredicate()
+  > @revsetpredicate('unsortable(x)')
+  > def unsortable(repo, subset, x):
+  >     """make x unsortable"""
+  >     s = revset.getset(repo, subset, x)
+  >     s.sort = lambda *args: None
+  >     s.reverse = lambda *args: None
+  >     return s
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > unsortable=$TESTTMP/unsortable.py
+  > EOF
+
+  $ try '_flipand(unsortable(2+0+1), _flipand(unsortable(0+2+1), 0::2))'
+  (func
+    ('symbol', '_flipand')
+    (list
+      (func
+        ('symbol', 'unsortable')
+        (or
+          (list
+            ('symbol', '2')
+            ('symbol', '0')
+            ('symbol', '1'))))
+      (func
+        ('symbol', '_flipand')
+        (list
+          (func
+            ('symbol', 'unsortable')
+            (or
+              (list
+                ('symbol', '0')
+                ('symbol', '2')
+                ('symbol', '1'))))
+          (dagrange
+            ('symbol', '0')
+            ('symbol', '2'))))))
+  * set:
+  <filteredset
+    <baseset [0, 2, 1]>,
+    <baseset+ [0, 1, 2]>>
+  0
+  2
+  1
+
+ WRONG: should take dagrange order (0, 1, 2)
+
  'A + B' can be rewritten to 'B + A' by weight only when the order doesn't
  matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):
 
   $ try -p optimized '0:2 & (reverse(contains("a")) + 2)'
   * optimized:
   (and
     (range
       ('symbol', '0')
-      ('symbol', '2')
-      define)
+      ('symbol', '2'))
     (or
       (list
         ('symbol', '2')
         (func
           ('symbol', 'reverse')
           (func
             ('symbol', 'contains')
-            ('string', 'a')
-            define)
-          follow))
-      follow)
-    define)
+            ('string', 'a'))))))
   * set:
   <filteredset
     <spanset+ 0:3>,
@@ -2605,23 +2560,20 @@
 
   $ try -p optimized '(reverse(contains("a")) + 2) & 0:2'
   * optimized:
-  (and
-    (range
-      ('symbol', '0')
-      ('symbol', '2')
-      follow)
-    (or
-      (list
-        (func
-          ('symbol', 'reverse')
+  (func
+    ('symbol', '_flipand')
+    (list
+      (range
+        ('symbol', '0')
+        ('symbol', '2'))
+      (or
+        (list
           (func
-            ('symbol', 'contains')
-            ('string', 'a')
-            define)
-          define)
-        ('symbol', '2'))
-      define)
-    define)
+            ('symbol', 'reverse')
+            (func
+              ('symbol', 'contains')
+              ('string', 'a')))
+          ('symbol', '2')))))
   * set:
   <addset
     <filteredset
@@ -2992,7 +2944,7 @@
   * set:
   <addset+
     <generatorset+>,
-    <baseset- [1, 3, 5]>>
+    <baseset+ [1, 3, 5]>>
   0
   1
   2
@@ -3016,8 +2968,7 @@
   * optimized:
   (func
     ('symbol', '_list')
-    ('string', '0\x001\x002\x00-2\x00tip\x00null')
-    define)
+    ('string', '0\x001\x002\x00-2\x00tip\x00null'))
   * set:
   <baseset [0, 1, 2, 8, 9, -1]>
   0
@@ -3040,13 +2991,10 @@
     (list
       (func
         ('symbol', '_list')
-        ('string', '0\x001')
-        define)
+        ('string', '0\x001'))
       (range
         ('symbol', '2')
-        ('symbol', '3')
-        define))
-    define)
+        ('symbol', '3'))))
   * set:
   <addset
     <baseset [0, 1]>,
@@ -3073,18 +3021,14 @@
     (list
       (range
         ('symbol', '0')
-        ('symbol', '1')
-        define)
+        ('symbol', '1'))
       ('symbol', '2')
       (range
         ('symbol', '3')
-        ('symbol', '4')
-        define)
+        ('symbol', '4'))
       (func
         ('symbol', '_list')
-        ('string', '5\x006')
-        define))
-    define)
+        ('string', '5\x006'))))
   * set:
   <addset
     <addset
@@ -3111,8 +3055,7 @@
       ('symbol', '1')
       ('symbol', '2')
       ('symbol', '3')
-      ('symbol', '4'))
-    define)
+      ('symbol', '4')))
   * set:
   <addset
     <addset
@@ -3232,8 +3175,7 @@
   (or
     (list
       ('symbol', '0')
-      None)
-    define)
+      None))
   hg: parse error: missing argument
   [255]
 
@@ -3263,8 +3205,7 @@
     ('symbol', 'only')
     (list
       ('symbol', '3')
-      ('symbol', '1'))
-    define)
+      ('symbol', '1')))
   * set:
   <baseset+ [3]>
   3
@@ -3281,8 +3222,7 @@
     ('symbol', 'only')
     (list
       ('symbol', '1')
-      ('symbol', '3'))
-    define)
+      ('symbol', '3')))
   * set:
   <baseset+ []>
   $ try --optimize 'not ::2 and ::6'
@@ -3297,8 +3237,7 @@
     ('symbol', 'only')
     (list
       ('symbol', '6')
-      ('symbol', '2'))
-    define)
+      ('symbol', '2')))
   * set:
   <baseset+ [3, 4, 5, 6]>
   3
@@ -3319,8 +3258,7 @@
     ('symbol', 'only')
     (list
       ('symbol', '6')
-      ('symbol', '4'))
-    define)
+      ('symbol', '4')))
   * set:
   <baseset+ [3, 5, 6]>
   3
@@ -3336,13 +3274,13 @@
     (group
       None))
   * optimized:
-  (and
-    None
-    (func
-      ('symbol', 'ancestors')
-      ('symbol', '1')
-      define)
-    define)
+  (func
+    ('symbol', '_flipand')
+    (list
+      None
+      (func
+        ('symbol', 'ancestors')
+        ('symbol', '1'))))
   hg: parse error: missing argument
   [255]
 
@@ -3353,15 +3291,12 @@
   (difference
     (func
       ('symbol', 'ancestors')
-      ('symbol', '6')
-      define)
+      ('symbol', '6'))
     (func
       ('symbol', 'ancestors')
       (list
         ('symbol', '4')
-        ('symbol', '1'))
-      any)
-    define)
+        ('symbol', '1'))))
   0
   1
   3
@@ -3374,13 +3309,10 @@
       ('symbol', 'ancestors')
       (list
         ('symbol', '6')
-        ('symbol', '1'))
-      define)
+        ('symbol', '1')))
     (func
       ('symbol', 'ancestors')
-      ('symbol', '4')
-      any)
-    define)
+      ('symbol', '4')))
   5
   6
 
@@ -3394,15 +3326,12 @@
       ('symbol', 'ancestors')
       (keyvalue
         ('symbol', 'set')
-        ('symbol', '6'))
-      define)
+        ('symbol', '6')))
     (func
       ('symbol', 'ancestors')
       (keyvalue
         ('symbol', 'set')
-        ('symbol', '4'))
-      any)
-    define)
+        ('symbol', '4'))))
   3
   5
   6
@@ -4452,6 +4381,9 @@
   > printprevset = $TESTTMP/printprevset.py
   > EOF
 
+#if chg
+  $ hg --config revsetalias.P=1 log -r . -T '\n' >/dev/null
+#endif
   $ hg --config revsetalias.P=1 log -r . -T '\n'
   P=[1]
   $ P=3 hg --config revsetalias.P=2 log -r . -T '\n'
diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py
--- a/mercurial/revsetlang.py
+++ b/mercurial/revsetlang.py
@@ -283,25 +283,32 @@
 # 'y()' can either enforce its ordering requirement or take the ordering
 # specified by 'x()' because 'not()' doesn't care the order.
 #
-# Transition of ordering requirement:
+# Practically, if a revset function does not care much about ordering (more
+# precisely, it is not intended to "define" ordering, and does not need certain
+# ways to write fast paths), it should assume "follow" ordering, like:
+#
+#    @revsetpredicate('myrevset')
+#    def myrevset(repo, subset, x):
+#        myset = ...  # calculated independently from subset (no fast path)
+#        return subset & myset  # CORRECT: preserves "subset" order
 #
-# 1. starts with 'define'
-# 2. shifts to 'follow' by 'x & y'
-# 3. changes back to 'define' on function call 'f(x)' or function-like
-#    operation 'x (f) y' because 'f' may have its own ordering requirement
-#    for 'x' and 'y' (e.g. 'first(x)')
+# If a revset function can be used to define ordering (ex. "sort"), or it has a
+# fast path that does not do "return subset & ...", it needs to take the
+# "order" argument:
 #
+#   @revsetpredicate('myrevset', takeorder=True)
+#   def myrevset(repo, subset, x, order):
+#       ....
+#       if order == defineorder:
+#           return myset & subset  # CORRECT: "myset" defines order
+#       elif order == followorder:
+#           return subset & myset  # CORRECT: "subset" defines order
+#       else:
+#           # choose whatever faster here
 anyorder = 'any'        # don't care the order
 defineorder = 'define'  # should define the order
 followorder = 'follow'  # must follow the current order
 
-# transition table for 'x & y', from the current expression 'x' to 'y'
-_tofolloworder = {
-    anyorder: anyorder,
-    defineorder: followorder,
-    followorder: followorder,
-}
-
 def _matchonly(revs, bases):
     """
     >>> f = lambda *args: _matchonly(*map(parse, args))
@@ -340,78 +347,54 @@
 
     return (op,) + tuple(_fixops(y) for y in x[1:])
 
-def _analyze(x, order):
+def _analyze(x):
     if x is None:
         return x
 
     op = x[0]
     if op == 'minus':
-        return _analyze(('and', x[1], ('not', x[2])), order)
+        return _analyze(('and', x[1], ('not', x[2])))
     elif op == 'only':
         t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
-        return _analyze(t, order)
+        return _analyze(t)
     elif op == 'onlypost':
-        return _analyze(('func', ('symbol', 'only'), x[1]), order)
+        return _analyze(('func', ('symbol', 'only'), x[1]))
     elif op == 'dagrangepre':
-        return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
+        return _analyze(('func', ('symbol', 'ancestors'), x[1]))
     elif op == 'dagrangepost':
-        return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
+        return _analyze(('func', ('symbol', 'descendants'), x[1]))
     elif op == 'negate':
         s = getstring(x[1], _("can't negate that"))
-        return _analyze(('string', '-' + s), order)
+        return _analyze(('string', '-' + s))
     elif op in ('string', 'symbol'):
         return x
-    elif op == 'and':
-        ta = _analyze(x[1], order)
-        tb = _analyze(x[2], _tofolloworder[order])
-        return (op, ta, tb, order)
-    elif op == 'or':
-        return (op, _analyze(x[1], order), order)
-    elif op == 'not':
-        return (op, _analyze(x[1], anyorder), order)
     elif op == 'rangeall':
-        return (op, None, order)
-    elif op in ('rangepre', 'rangepost', 'parentpost'):
-        return (op, _analyze(x[1], defineorder), order)
+        return (op, None)
     elif op == 'group':
-        return _analyze(x[1], order)
-    elif op in ('dagrange', 'range', 'parent', 'ancestor', 'relation',
-                'subscript'):
-        ta = _analyze(x[1], defineorder)
-        tb = _analyze(x[2], defineorder)
-        return (op, ta, tb, order)
-    elif op == 'relsubscript':
-        ta = _analyze(x[1], defineorder)
-        tb = _analyze(x[2], defineorder)
-        tc = _analyze(x[3], defineorder)
-        return (op, ta, tb, tc, order)
-    elif op == 'list':
-        return (op,) + tuple(_analyze(y, order) for y in x[1:])
+        return _analyze(x[1])
+    elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost', 'and',
+                'dagrange', 'range', 'parent', 'ancestor', 'relation',
+                'subscript', 'relsubscript', 'list'}:
+        return (op,) + tuple(_analyze(y) for y in x[1:])
     elif op == 'keyvalue':
-        return (op, x[1], _analyze(x[2], order))
+        return (op, x[1], _analyze(x[2]))
     elif op == 'func':
-        f = getsymbol(x[1])
-        d = defineorder
-        if f == 'present':
-            # 'present(set)' is known to return the argument set with no
-            # modification, so forward the current order to its argument
-            d = order
-        return (op, x[1], _analyze(x[2], d), order)
+        return (op, x[1], _analyze(x[2]))
     raise ValueError('invalid operator %r' % op)
 
-def analyze(x, order=defineorder):
+def analyze(x):
     """Transform raw parsed tree to evaluatable tree which can be fed to
     optimize() or getset()
 
     All pseudo operations should be mapped to real operations or functions
     defined in methods or symbols table respectively.
+    """
+    return _analyze(x)
 
-    'order' specifies how the current expression 'x' is ordered (see the
-    constants defined above.)
-    """
-    return _analyze(x, order)
-
-def _optimize(x, small):
+def _optimize(x, small, preserveorder):
+    # preserveorder: when set to True, certain optimization that may break
+    # ordering will be skipped. For example, "or" and "and" list will have
+    # their arguments order preserved.
     if x is None:
         return 0, x
 
@@ -423,41 +406,41 @@
     if op in ('string', 'symbol'):
         return smallbonus, x # single revisions are small
     elif op == 'and':
-        wa, ta = _optimize(x[1], True)
-        wb, tb = _optimize(x[2], True)
-        order = x[3]
+        wa, ta = _optimize(x[1], True, preserveorder)
+        wb, tb = _optimize(x[2], True, False)
         w = min(wa, wb)
 
         # (::x and not ::y)/(not ::y and ::x) have a fast path
         tm = _matchonly(ta, tb) or _matchonly(tb, ta)
         if tm:
-            return w, ('func', ('symbol', 'only'), tm, order)
+            return w, ('func', ('symbol', 'only'), tm)
 
         if tb is not None and tb[0] == 'not':
-            return wa, ('difference', ta, tb[1], order)
-
+            return wa, ('difference', ta, tb[1])
         if wa > wb:
-            return w, (op, tb, ta, order)
-        return w, (op, ta, tb, order)
+            if preserveorder:
+                return w, ('func', ('symbol', '_flipand'), ('list', tb, ta))
+            else:
+                return w, (op, tb, ta)
+        return w, (op, ta, tb)
     elif op == 'or':
         # fast path for machine-generated expression, that is likely to have
         # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
-        order = x[2]
         ws, ts, ss = [], [], []
         def flushss():
             if not ss:
                 return
             if len(ss) == 1:
                 w, t = ss[0]
             else:
                 s = '\0'.join(t[1] for w, t in ss)
-                y = ('func', ('symbol', '_list'), ('string', s), order)
-                w, t = _optimize(y, False)
+                y = ('func', ('symbol', '_list'), ('string', s))
+                w, t = _optimize(y, False, preserveorder)
             ws.append(w)
             ts.append(t)
             del ss[:]
         for y in getlist(x[1]):
-            w, t = _optimize(y, False)
+            w, t = _optimize(y, False, preserveorder)
             if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
                 ss.append((w, t))
                 continue
@@ -467,49 +450,43 @@
         flushss()
         if len(ts) == 1:
             return ws[0], ts[0] # 'or' operation is fully optimized out
-        if order != defineorder:
+        if not preserveorder:
             # reorder by weight only when f(a + b) == f(b + a)
             ts = [wt[1] for wt in sorted(zip(ws, ts), key=lambda wt: wt[0])]
-        return max(ws), (op, ('list',) + tuple(ts), order)
+        return max(ws), (op, ('list',) + tuple(ts))
     elif op == 'not':
         # Optimize not public() to _notpublic() because we have a fast version
         if x[1][:3] == ('func', ('symbol', 'public'), None):
-            order = x[1][3]
-            newsym = ('func', ('symbol', '_notpublic'), None, order)
-            o = _optimize(newsym, not small)
+            newsym = ('func', ('symbol', '_notpublic'), None)
+            o = _optimize(newsym, not small, False)
             return o[0], o[1]
         else:
-            o = _optimize(x[1], not small)
-            order = x[2]
-            return o[0], (op, o[1], order)
+            o = _optimize(x[1], not small, False)
+            return o[0], (op, o[1])
     elif op == 'rangeall':
         return smallbonus, x
     elif op in ('rangepre', 'rangepost', 'parentpost'):
-        o = _optimize(x[1], small)
-        order = x[2]
-        return o[0], (op, o[1], order)
+        o = _optimize(x[1], small, False)
+        return o[0], (op, o[1])
     elif op in ('dagrange', 'range'):
-        wa, ta = _optimize(x[1], small)
-        wb, tb = _optimize(x[2], small)
-        order = x[3]
-        return wa + wb, (op, ta, tb, order)
+        wa, ta = _optimize(x[1], small, False)
+        wb, tb = _optimize(x[2], small, False)
+        return wa + wb, (op, ta, tb)
     elif op in ('parent', 'ancestor', 'relation', 'subscript'):
-        w, t = _optimize(x[1], small)
-        order = x[3]
-        return w, (op, t, x[2], order)
+        w, t = _optimize(x[1], small, False)
+        return w, (op, t, x[2])
     elif op == 'relsubscript':
-        w, t = _optimize(x[1], small)
-        order = x[4]
-        return w, (op, t, x[2], x[3], order)
+        w, t = _optimize(x[1], small, False)
+        return w, (op, t, x[2], x[3])
     elif op == 'list':
-        ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
+        ws, ts = zip(*(_optimize(y, small, preserveorder) for y in x[1:]))
         return sum(ws), (op,) + ts
     elif op == 'keyvalue':
-        w, t = _optimize(x[2], small)
+        w, t = _optimize(x[2], small, True)
         return w, (op, x[1], t)
     elif op == 'func':
         f = getsymbol(x[1])
-        wa, ta = _optimize(x[2], small)
+        wa, ta = _optimize(x[2], small, True)
         if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
                  'keyword', 'outgoing', 'user', 'destination'):
             w = 10 # slow
@@ -525,16 +502,18 @@
             w = 10 # assume most sorts look at changelog
         else:
             w = 1
-        order = x[3]
-        return w + wa, (op, x[1], ta, order)
+        return w + wa, (op, x[1], ta)
     raise ValueError('invalid operator %r' % op)
 
-def optimize(tree):
+def optimize(tree, preserveorder=True):
     """Optimize evaluatable tree
 
     All pseudo operations should be transformed beforehand.
+
+    If preserveorder is True, certain optimizations that may change ordering
+    won't be performed.
     """
-    _weight, newtree = _optimize(tree, small=True)
+    _weight, newtree = _optimize(tree, small=True, preserveorder=preserveorder)
     return newtree
 
 # the set of valid characters for the initial letter of symbols in
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -52,10 +52,10 @@
 
 # helpers
 
-def getset(repo, subset, x):
+def getset(repo, subset, x, order=anyorder):
     if not x:
         raise error.ParseError(_("missing argument"))
-    return methods[x[0]](repo, subset, *x[1:])
+    return methods[x[0]](repo, subset, *x[1:], order=order)
 
 def _getrevsource(repo, r):
     extra = repo[r].extra()
@@ -69,7 +69,7 @@
 
 # operator methods
 
-def stringset(repo, subset, x):
+def stringset(repo, subset, x, order=anyorder):
     x = scmutil.intrev(repo[x])
     if (x in subset
         or x == node.nullrev and isinstance(subset, fullreposet)):
@@ -126,27 +126,31 @@
     return subset & xs
 
 def andset(repo, subset, x, y, order):
-    return getset(repo, getset(repo, subset, x), y)
+    if order == anyorder:
+        yorder = anyorder
+    else:
+        yorder = followorder
+    return getset(repo, getset(repo, subset, x, order), y, yorder)
 
 def differenceset(repo, subset, x, y, order):
-    return getset(repo, subset, x) - getset(repo, subset, y)
+    return getset(repo, subset, x, order) - getset(repo, subset, y)
 
-def _orsetlist(repo, subset, xs):
+def _orsetlist(repo, subset, xs, order):
     assert xs
     if len(xs) == 1:
-        return getset(repo, subset, xs[0])
+        return getset(repo, subset, xs[0], order)
     p = len(xs) // 2
-    a = _orsetlist(repo, subset, xs[:p])
-    b = _orsetlist(repo, subset, xs[p:])
+    a = _orsetlist(repo, subset, xs[:p], order)
+    b = _orsetlist(repo, subset, xs[p:], order)
     return a + b
 
 def orset(repo, subset, x, order):
     xs = getlist(x)
     if order == followorder:
         # slow path to take the subset order
-        return subset & _orsetlist(repo, fullreposet(repo), xs)
+        return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
     else:
-        return _orsetlist(repo, subset, xs)
+        return _orsetlist(repo, subset, xs, order)
 
 def notset(repo, subset, x, order):
     return subset - getset(repo, subset, x)
@@ -176,11 +180,11 @@
 def subscriptset(repo, subset, x, y, order):
     raise error.ParseError(_("can't use a subscript in this context"))
 
-def listset(repo, subset, *xs):
+def listset(repo, subset, *xs, **opts):
     raise error.ParseError(_("can't use a list in this context"),
                            hint=_('see hg help "revsets.x or y"'))
 
-def keyvaluepair(repo, subset, k, v):
+def keyvaluepair(repo, subset, k, v, order):
     raise error.ParseError(_("can't use a key-value pair in this context"))
 
 def func(repo, subset, a, b, order):
@@ -878,6 +882,18 @@
 
     return subset & s
 
+ at predicate('_flipand(x, y)', safe=True, takeorder=True)
+def _flipand(repo, subset, args, order):
+    """Equivalent to ``y and x``, but faster when x is small"""
+    x, y = getargs(args, 2, 2, _("missing argument"))
+    if order == defineorder:
+        # NOTE: In theory this could be "anyorder". But many revsets do not
+        # handle ordering correctly. This has to be followorder for now.
+        xorder = followorder
+    else:
+        xorder = order
+    return getset(repo, getset(repo, subset, x, xorder), y, order)
+
 @predicate('follow([pattern[, startrev]])', safe=True)
 def follow(repo, subset, x):
     """
@@ -1124,7 +1140,7 @@
     ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
     if ofs < 0:
         raise error.ParseError(_("negative offset"))
-    os = getset(repo, fullreposet(repo), args['set'])
+    os = getset(repo, fullreposet(repo), args['set'], defineorder)
     ls = os.slice(ofs, ofs + lim)
     if order == followorder and lim > 1:
         return subset & ls
@@ -1142,7 +1158,7 @@
         lim = getinteger(l[1], _("last expects a number"))
     if lim < 0:
         raise error.ParseError(_("negative number to select"))
-    os = getset(repo, fullreposet(repo), l[0])
+    os = getset(repo, fullreposet(repo), l[0], defineorder)
     os.reverse()
     ls = os.slice(0, lim)
     if order == followorder and lim > 1:
@@ -1508,17 +1524,17 @@
                     ps.add(parents[1].rev())
     return subset & ps
 
- at predicate('present(set)', safe=True)
-def present(repo, subset, x):
+ at predicate('present(set)', safe=True, takeorder=True)
+def present(repo, subset, x, order):
     """An empty set, if any revision in set isn't found; otherwise,
     all revisions in set.
 
     If any of specified revisions is not present in the local repository,
     the query is normally aborted. But this predicate allows the query
     to continue even in such cases.
     """
     try:
-        return getset(repo, subset, x)
+        return getset(repo, subset, x, order)
     except error.RepoLookupError:
         return baseset()
 
@@ -1718,9 +1734,11 @@
 def reverse(repo, subset, x, order):
     """Reverse order of set.
     """
-    l = getset(repo, subset, x)
     if order == defineorder:
+        l = getset(repo, subset, x, order)
         l.reverse()
+    else:
+        l = getset(repo, subset, x, anyorder)
     return l
 
 @predicate('roots(set)', safe=True)
@@ -1802,7 +1820,16 @@
 
     """
     s, keyflags, opts = _getsortargs(x)
-    revs = getset(repo, subset, s)
+
+    # If sort keys contains "rev", the sort result will be definitive no matter
+    # what order the input revs have, we can use "anyorder". If sort keys are
+    # empty, we treat it as "whatever order is okay", and also use "anyorder".
+    if not keyflags or any('rev' == k[0] for k in keyflags):
+        sorder = anyorder
+    else:
+        sorder = order
+
+    revs = getset(repo, subset, s, sorder)
 
     if not keyflags or order != defineorder:
         return revs
@@ -2113,17 +2140,17 @@
     if aliases:
         tree = revsetlang.expandaliases(tree, aliases, warn=warn)
     tree = revsetlang.foldconcat(tree)
-    tree = revsetlang.analyze(tree, order)
-    tree = revsetlang.optimize(tree)
+    tree = revsetlang.analyze(tree)
+    tree = revsetlang.optimize(tree, preserveorder=(order == defineorder))
     posttreebuilthook(tree, repo)
-    return makematcher(tree)
+    return makematcher(tree, order)
 
-def makematcher(tree):
+def makematcher(tree, order=defineorder):
     """Create a matcher from an evaluatable tree"""
     def mfunc(repo, subset=None):
         if subset is None:
             subset = fullreposet(repo)
-        return getset(repo, subset, tree)
+        return getset(repo, subset, tree, order)
     return mfunc
 
 def loadpredicate(ui, extname, registrarobj):



To: quark, #hg-reviewers
Cc: yuja, mercurial-devel


More information about the Mercurial-devel mailing list