@@ -356,10 +356,9 @@ def dagrange(repo, subset, x, y):
def andset(repo, subset, x, y):
return getset(repo, getset(repo, subset, x), y)
-def orset(repo, subset, x, y):
- xl = getset(repo, subset, x)
- yl = getset(repo, subset, y)
- return xl + yl
+def orset(repo, subset, *xs):
+ rs = [getset(repo, subset, x) for x in xs]
+ return _combinesets(rs)
def notset(repo, subset, x):
return subset - getset(repo, subset, x)
@@ -2160,13 +2159,11 @@ def optimize(x, small):
return w, (op, tb, ta)
return w, (op, ta, tb)
elif op == 'or':
- wa, ta = optimize(x[1], False)
- wb, tb = optimize(x[2], False)
+ ws, ts = zip(*[optimize(y, False) for y in x[1:]])
# we can't reorder trees by weight because it would change the order.
# ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
- # if wb < wa:
- # tb, ta = ta, tb
- return max(wa, wb), (op, ta, tb)
+ # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
+ return max(ws), (op,) + ts
elif op == 'not':
# Optimize not public() to _notpublic() because we have a fast version
if x[1] == ('func', ('symbol', 'public'), None):
@@ -2384,7 +2381,7 @@ def _parsealiasdefn(defn, args):
tree, pos = p.parse(defn)
if pos != len(defn):
raise error.ParseError(_('invalid token'), pos)
- return tree
+ return parser.simplifyinfixops(tree, ('or',))
class revsetalias(object):
# whether own `error` information is already shown or not.
@@ -2515,7 +2512,7 @@ def parse(spec, lookup=None):
tree, pos = p.parse(spec, lookup=lookup)
if pos != len(spec):
raise error.ParseError(_("invalid token"), pos)
- return tree
+ return parser.simplifyinfixops(tree, ('or',))
def posttreebuilthook(tree, repo):
# hook for extensions to execute code on the optimized tree
@@ -1469,13 +1469,12 @@ glog always reorders nodes which explain
(group
(group
(or
- (or
- (func
- ('symbol', 'branch')
- ('string', 'default'))
- (func
- ('symbol', 'branch')
- ('string', 'branch')))
+ (func
+ ('symbol', 'branch')
+ ('string', 'default'))
+ (func
+ ('symbol', 'branch')
+ ('string', 'branch'))
(func
('symbol', 'branch')
('string', 'branch')))))
@@ -137,16 +137,15 @@ trivial
6
$ try '0|1|2'
(or
- (or
- ('symbol', '0')
- ('symbol', '1'))
+ ('symbol', '0')
+ ('symbol', '1')
('symbol', '2'))
* set:
<addset
+ <baseset [0]>,
<addset
- <baseset [0]>,
- <baseset [1]>>,
- <baseset [2]>>
+ <baseset [1]>,
+ <baseset [2]>>>
0
1
2
@@ -919,6 +918,49 @@ test that `or` operation skips duplicate
4
5
+test that chained `or` operations make balanced addsets
+
+ $ try '0:1|1:2|2:3|3:4|4:5'
+ (or
+ (range
+ ('symbol', '0')
+ ('symbol', '1'))
+ (range
+ ('symbol', '1')
+ ('symbol', '2'))
+ (range
+ ('symbol', '2')
+ ('symbol', '3'))
+ (range
+ ('symbol', '3')
+ ('symbol', '4'))
+ (range
+ ('symbol', '4')
+ ('symbol', '5')))
+ * set:
+ <addset
+ <addset
+ <spanset+ 0:1>,
+ <spanset+ 1:2>>,
+ <addset
+ <spanset+ 2:3>,
+ <addset
+ <spanset+ 3:4>,
+ <spanset+ 4:5>>>>
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+
+test that chained `or` operations never eat up stack (issue4624)
+(uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
+
+ $ hg log -T '{rev}\n' -r "`python -c "print '|'.join(['0:1'] * 500)"`"
+ 0
+ 1
+
check that conversion to only works
$ try --optimize '::3 - ::1'
(minus
@@ -1361,6 +1403,44 @@ test nesting and variable passing
<baseset [5]>
5
+test chained `or` operations are flattened at parsing phase
+
+ $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
+ $ try 'chainedorops(0:1, 1:2, 2:3)'
+ (func
+ ('symbol', 'chainedorops')
+ (list
+ (list
+ (range
+ ('symbol', '0')
+ ('symbol', '1'))
+ (range
+ ('symbol', '1')
+ ('symbol', '2')))
+ (range
+ ('symbol', '2')
+ ('symbol', '3'))))
+ (or
+ (range
+ ('symbol', '0')
+ ('symbol', '1'))
+ (range
+ ('symbol', '1')
+ ('symbol', '2'))
+ (range
+ ('symbol', '2')
+ ('symbol', '3')))
+ * set:
+ <addset
+ <spanset+ 0:1>,
+ <addset
+ <spanset+ 1:2>,
+ <spanset+ 2:3>>>
+ 0
+ 1
+ 2
+ 3
+
test variable isolation, variable placeholders are rewritten as string
then parsed and matched again as string. Check they do not leak too
far away.