Mercurial > public > mercurial-scm > hg-stable
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 |