comparison mercurial/revset.py @ 29936:09a84e747c88

revset: pass around ordering flags to operations Some operations and functions will need them to fix ordering bugs.
author Yuya Nishihara <yuya@tcha.org>
date Sun, 07 Aug 2016 17:46:12 +0900
parents d2d1be3009ca
children 91a95ad985d8
comparison
equal deleted inserted replaced
29935:d2d1be3009ca 29936:09a84e747c88
358 if (x in subset 358 if (x in subset
359 or x == node.nullrev and isinstance(subset, fullreposet)): 359 or x == node.nullrev and isinstance(subset, fullreposet)):
360 return baseset([x]) 360 return baseset([x])
361 return baseset() 361 return baseset()
362 362
363 def rangeset(repo, subset, x, y): 363 def rangeset(repo, subset, x, y, order):
364 m = getset(repo, fullreposet(repo), x) 364 m = getset(repo, fullreposet(repo), x)
365 n = getset(repo, fullreposet(repo), y) 365 n = getset(repo, fullreposet(repo), y)
366 366
367 if not m or not n: 367 if not m or not n:
368 return baseset() 368 return baseset()
383 # 383 #
384 # This has performance implication, carrying the sorting over when possible 384 # This has performance implication, carrying the sorting over when possible
385 # would be more efficient. 385 # would be more efficient.
386 return r & subset 386 return r & subset
387 387
388 def dagrange(repo, subset, x, y): 388 def dagrange(repo, subset, x, y, order):
389 r = fullreposet(repo) 389 r = fullreposet(repo)
390 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y), 390 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
391 includepath=True) 391 includepath=True)
392 return subset & xs 392 return subset & xs
393 393
394 def andset(repo, subset, x, y): 394 def andset(repo, subset, x, y, order):
395 return getset(repo, getset(repo, subset, x), y) 395 return getset(repo, getset(repo, subset, x), y)
396 396
397 def differenceset(repo, subset, x, y): 397 def differenceset(repo, subset, x, y, order):
398 return getset(repo, subset, x) - getset(repo, subset, y) 398 return getset(repo, subset, x) - getset(repo, subset, y)
399 399
400 def _orsetlist(repo, subset, xs): 400 def _orsetlist(repo, subset, xs):
401 assert xs 401 assert xs
402 if len(xs) == 1: 402 if len(xs) == 1:
404 p = len(xs) // 2 404 p = len(xs) // 2
405 a = _orsetlist(repo, subset, xs[:p]) 405 a = _orsetlist(repo, subset, xs[:p])
406 b = _orsetlist(repo, subset, xs[p:]) 406 b = _orsetlist(repo, subset, xs[p:])
407 return a + b 407 return a + b
408 408
409 def orset(repo, subset, x): 409 def orset(repo, subset, x, order):
410 return _orsetlist(repo, subset, getlist(x)) 410 return _orsetlist(repo, subset, getlist(x))
411 411
412 def notset(repo, subset, x): 412 def notset(repo, subset, x, order):
413 return subset - getset(repo, subset, x) 413 return subset - getset(repo, subset, x)
414 414
415 def listset(repo, subset, *xs): 415 def listset(repo, subset, *xs):
416 raise error.ParseError(_("can't use a list in this context"), 416 raise error.ParseError(_("can't use a list in this context"),
417 hint=_('see hg help "revsets.x or y"')) 417 hint=_('see hg help "revsets.x or y"'))
418 418
419 def keyvaluepair(repo, subset, k, v): 419 def keyvaluepair(repo, subset, k, v):
420 raise error.ParseError(_("can't use a key-value pair in this context")) 420 raise error.ParseError(_("can't use a key-value pair in this context"))
421 421
422 def func(repo, subset, a, b): 422 def func(repo, subset, a, b, order):
423 f = getsymbol(a) 423 f = getsymbol(a)
424 if f in symbols: 424 if f in symbols:
425 return symbols[f](repo, subset, b) 425 return symbols[f](repo, subset, b)
426 426
427 keep = lambda fn: getattr(fn, '__doc__', None) is not None 427 keep = lambda fn: getattr(fn, '__doc__', None) is not None
514 def _firstancestors(repo, subset, x): 514 def _firstancestors(repo, subset, x):
515 # ``_firstancestors(set)`` 515 # ``_firstancestors(set)``
516 # Like ``ancestors(set)`` but follows only the first parents. 516 # Like ``ancestors(set)`` but follows only the first parents.
517 return _ancestors(repo, subset, x, followfirst=True) 517 return _ancestors(repo, subset, x, followfirst=True)
518 518
519 def ancestorspec(repo, subset, x, n): 519 def ancestorspec(repo, subset, x, n, order):
520 """``set~n`` 520 """``set~n``
521 Changesets that are the Nth ancestor (first parents only) of a changeset 521 Changesets that are the Nth ancestor (first parents only) of a changeset
522 in set. 522 in set.
523 """ 523 """
524 try: 524 try:
1526 ps -= set([node.nullrev]) 1526 ps -= set([node.nullrev])
1527 # XXX we should turn this into a baseset instead of a set, smartset may do 1527 # XXX we should turn this into a baseset instead of a set, smartset may do
1528 # some optimisations from the fact this is a baseset. 1528 # some optimisations from the fact this is a baseset.
1529 return subset & ps 1529 return subset & ps
1530 1530
1531 def parentpost(repo, subset, x): 1531 def parentpost(repo, subset, x, order):
1532 return p1(repo, subset, x) 1532 return p1(repo, subset, x)
1533 1533
1534 @predicate('parents([set])', safe=True) 1534 @predicate('parents([set])', safe=True)
1535 def parents(repo, subset, x): 1535 def parents(repo, subset, x):
1536 """ 1536 """
1579 # i18n: "secret" is a keyword 1579 # i18n: "secret" is a keyword
1580 getargs(x, 0, 0, _("secret takes no arguments")) 1580 getargs(x, 0, 0, _("secret takes no arguments"))
1581 target = phases.secret 1581 target = phases.secret
1582 return _phase(repo, subset, target) 1582 return _phase(repo, subset, target)
1583 1583
1584 def parentspec(repo, subset, x, n): 1584 def parentspec(repo, subset, x, n, order):
1585 """``set^0`` 1585 """``set^0``
1586 The set. 1586 The set.
1587 ``set^1`` (or ``set^``), ``set^2`` 1587 ``set^1`` (or ``set^``), ``set^2``
1588 First or second parent, respectively, of all changesets in set. 1588 First or second parent, respectively, of all changesets in set.
1589 """ 1589 """
2424 elif op in ('string', 'symbol'): 2424 elif op in ('string', 'symbol'):
2425 return x 2425 return x
2426 elif op == 'and': 2426 elif op == 'and':
2427 ta = _analyze(x[1], order) 2427 ta = _analyze(x[1], order)
2428 tb = _analyze(x[2], _tofolloworder[order]) 2428 tb = _analyze(x[2], _tofolloworder[order])
2429 return (op, ta, tb) 2429 return (op, ta, tb, order)
2430 elif op == 'or': 2430 elif op == 'or':
2431 return (op, _analyze(x[1], order)) 2431 return (op, _analyze(x[1], order), order)
2432 elif op == 'not': 2432 elif op == 'not':
2433 return (op, _analyze(x[1], anyorder)) 2433 return (op, _analyze(x[1], anyorder), order)
2434 elif op == 'parentpost': 2434 elif op == 'parentpost':
2435 return (op, _analyze(x[1], defineorder)) 2435 return (op, _analyze(x[1], defineorder), order)
2436 elif op == 'group': 2436 elif op == 'group':
2437 return _analyze(x[1], order) 2437 return _analyze(x[1], order)
2438 elif op in ('dagrange', 'range', 'parent', 'ancestor'): 2438 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2439 ta = _analyze(x[1], defineorder) 2439 ta = _analyze(x[1], defineorder)
2440 tb = _analyze(x[2], defineorder) 2440 tb = _analyze(x[2], defineorder)
2441 return (op, ta, tb) 2441 return (op, ta, tb, order)
2442 elif op == 'list': 2442 elif op == 'list':
2443 return (op,) + tuple(_analyze(y, order) for y in x[1:]) 2443 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2444 elif op == 'keyvalue': 2444 elif op == 'keyvalue':
2445 return (op, x[1], _analyze(x[2], order)) 2445 return (op, x[1], _analyze(x[2], order))
2446 elif op == 'func': 2446 elif op == 'func':
2447 return (op, x[1], _analyze(x[2], defineorder)) 2447 return (op, x[1], _analyze(x[2], defineorder), order)
2448 raise ValueError('invalid operator %r' % op) 2448 raise ValueError('invalid operator %r' % op)
2449 2449
2450 def analyze(x, order=defineorder): 2450 def analyze(x, order=defineorder):
2451 """Transform raw parsed tree to evaluatable tree which can be fed to 2451 """Transform raw parsed tree to evaluatable tree which can be fed to
2452 optimize() or getset() 2452 optimize() or getset()
2471 if op in ('string', 'symbol'): 2471 if op in ('string', 'symbol'):
2472 return smallbonus, x # single revisions are small 2472 return smallbonus, x # single revisions are small
2473 elif op == 'and': 2473 elif op == 'and':
2474 wa, ta = _optimize(x[1], True) 2474 wa, ta = _optimize(x[1], True)
2475 wb, tb = _optimize(x[2], True) 2475 wb, tb = _optimize(x[2], True)
2476 order = x[3]
2476 w = min(wa, wb) 2477 w = min(wa, wb)
2477 2478
2478 # (::x and not ::y)/(not ::y and ::x) have a fast path 2479 # (::x and not ::y)/(not ::y and ::x) have a fast path
2479 tm = _matchonly(ta, tb) or _matchonly(tb, ta) 2480 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2480 if tm: 2481 if tm:
2481 return w, ('func', ('symbol', 'only'), tm) 2482 return w, ('func', ('symbol', 'only'), tm, order)
2482 2483
2483 if tb is not None and tb[0] == 'not': 2484 if tb is not None and tb[0] == 'not':
2484 return wa, ('difference', ta, tb[1]) 2485 return wa, ('difference', ta, tb[1], order)
2485 2486
2486 if wa > wb: 2487 if wa > wb:
2487 return w, (op, tb, ta) 2488 return w, (op, tb, ta, order)
2488 return w, (op, ta, tb) 2489 return w, (op, ta, tb, order)
2489 elif op == 'or': 2490 elif op == 'or':
2490 # fast path for machine-generated expression, that is likely to have 2491 # fast path for machine-generated expression, that is likely to have
2491 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()' 2492 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2493 order = x[2]
2492 ws, ts, ss = [], [], [] 2494 ws, ts, ss = [], [], []
2493 def flushss(): 2495 def flushss():
2494 if not ss: 2496 if not ss:
2495 return 2497 return
2496 if len(ss) == 1: 2498 if len(ss) == 1:
2497 w, t = ss[0] 2499 w, t = ss[0]
2498 else: 2500 else:
2499 s = '\0'.join(t[1] for w, t in ss) 2501 s = '\0'.join(t[1] for w, t in ss)
2500 y = ('func', ('symbol', '_list'), ('string', s)) 2502 y = ('func', ('symbol', '_list'), ('string', s), order)
2501 w, t = _optimize(y, False) 2503 w, t = _optimize(y, False)
2502 ws.append(w) 2504 ws.append(w)
2503 ts.append(t) 2505 ts.append(t)
2504 del ss[:] 2506 del ss[:]
2505 for y in getlist(x[1]): 2507 for y in getlist(x[1]):
2514 if len(ts) == 1: 2516 if len(ts) == 1:
2515 return ws[0], ts[0] # 'or' operation is fully optimized out 2517 return ws[0], ts[0] # 'or' operation is fully optimized out
2516 # we can't reorder trees by weight because it would change the order. 2518 # we can't reorder trees by weight because it would change the order.
2517 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a") 2519 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2518 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0])) 2520 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2519 return max(ws), (op, ('list',) + tuple(ts)) 2521 return max(ws), (op, ('list',) + tuple(ts), order)
2520 elif op == 'not': 2522 elif op == 'not':
2521 # Optimize not public() to _notpublic() because we have a fast version 2523 # Optimize not public() to _notpublic() because we have a fast version
2522 if x[1] == ('func', ('symbol', 'public'), None): 2524 if x[1][:3] == ('func', ('symbol', 'public'), None):
2523 newsym = ('func', ('symbol', '_notpublic'), None) 2525 order = x[1][3]
2526 newsym = ('func', ('symbol', '_notpublic'), None, order)
2524 o = _optimize(newsym, not small) 2527 o = _optimize(newsym, not small)
2525 return o[0], o[1] 2528 return o[0], o[1]
2526 else: 2529 else:
2527 o = _optimize(x[1], not small) 2530 o = _optimize(x[1], not small)
2528 return o[0], (op, o[1]) 2531 order = x[2]
2532 return o[0], (op, o[1], order)
2529 elif op == 'parentpost': 2533 elif op == 'parentpost':
2530 o = _optimize(x[1], small) 2534 o = _optimize(x[1], small)
2531 return o[0], (op, o[1]) 2535 order = x[2]
2536 return o[0], (op, o[1], order)
2532 elif op in ('dagrange', 'range', 'parent', 'ancestor'): 2537 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2533 wa, ta = _optimize(x[1], small) 2538 wa, ta = _optimize(x[1], small)
2534 wb, tb = _optimize(x[2], small) 2539 wb, tb = _optimize(x[2], small)
2535 return wa + wb, (op, ta, tb) 2540 order = x[3]
2541 return wa + wb, (op, ta, tb, order)
2536 elif op == 'list': 2542 elif op == 'list':
2537 ws, ts = zip(*(_optimize(y, small) for y in x[1:])) 2543 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2538 return sum(ws), (op,) + ts 2544 return sum(ws), (op,) + ts
2539 elif op == 'keyvalue': 2545 elif op == 'keyvalue':
2540 w, t = _optimize(x[2], small) 2546 w, t = _optimize(x[2], small)
2555 w = 0 2561 w = 0
2556 elif f == "sort": 2562 elif f == "sort":
2557 w = 10 # assume most sorts look at changelog 2563 w = 10 # assume most sorts look at changelog
2558 else: 2564 else:
2559 w = 1 2565 w = 1
2560 return w + wa, (op, x[1], ta) 2566 order = x[3]
2567 return w + wa, (op, x[1], ta, order)
2561 raise ValueError('invalid operator %r' % op) 2568 raise ValueError('invalid operator %r' % op)
2562 2569
2563 def optimize(tree): 2570 def optimize(tree):
2564 """Optimize evaluatable tree 2571 """Optimize evaluatable tree
2565 2572