comparison contrib/phabricator.py @ 33833:fb59192b4981

phabricator: add status to revision query language This patch adds status words (ex. `abandoned`, `accepted`, `needsreview`, `needsrevision`, `closed`) to the revision query language so people can select revision in a more flexible way. Test Plan: Try something like `phabread ':2 & accepted'`, `phabread ':105 - closed` and make sure they have desired outputs. Differential Revision: https://phab.mercurial-scm.org/D126
author Jun Wu <quark@fb.com>
date Tue, 18 Jul 2017 01:34:55 -0700
parents 539541779010
children 6e666cd59879
comparison
equal deleted inserted replaced
33832:539541779010 33833:fb59192b4981
486 '$$ &Yes $$ &No')): 486 '$$ &Yes $$ &No')):
487 return False 487 return False
488 488
489 return True 489 return True
490 490
491 _knownstatusnames = {'accepted', 'needsreview', 'needsrevision', 'closed',
492 'abandoned'}
493
494 def _getstatusname(drev):
495 """get normalized status name from a Differential Revision"""
496 return drev[r'statusName'].replace(' ', '').lower()
497
491 # Small language to specify differential revisions. Support symbols: (), :X, 498 # Small language to specify differential revisions. Support symbols: (), :X,
492 # +, and -. 499 # +, and -.
493 500
494 _elements = { 501 _elements = {
495 # token-type: binding-strength, primary, prefix, infix, suffix 502 # token-type: binding-strength, primary, prefix, infix, suffix
502 'symbol': (0, 'symbol', None, None, None), 509 'symbol': (0, 'symbol', None, None, None),
503 'end': (0, None, None, None, None), 510 'end': (0, None, None, None, None),
504 } 511 }
505 512
506 def _tokenize(text): 513 def _tokenize(text):
507 text = text.replace(' ', '') # remove space
508 view = memoryview(text) # zero-copy slice 514 view = memoryview(text) # zero-copy slice
509 special = '():+-&' 515 special = '():+-& '
510 pos = 0 516 pos = 0
511 length = len(text) 517 length = len(text)
512 while pos < length: 518 while pos < length:
513 symbol = ''.join(itertools.takewhile(lambda ch: ch not in special, 519 symbol = ''.join(itertools.takewhile(lambda ch: ch not in special,
514 view[pos:])) 520 view[pos:]))
515 if symbol: 521 if symbol:
516 yield ('symbol', symbol, pos) 522 yield ('symbol', symbol, pos)
517 pos += len(symbol) 523 pos += len(symbol)
518 else: # special char 524 else: # special char, ignore space
519 yield (text[pos], None, pos) 525 if text[pos] != ' ':
526 yield (text[pos], None, pos)
520 pos += 1 527 pos += 1
521 yield ('end', None, pos) 528 yield ('end', None, pos)
522 529
523 def _parse(text): 530 def _parse(text):
524 tree, pos = parser.parser(_elements).parse(_tokenize(text)) 531 tree, pos = parser.parser(_elements).parse(_tokenize(text))
642 tofetch = set(drevs) 649 tofetch = set(drevs)
643 for r in ancestordrevs: 650 for r in ancestordrevs:
644 tofetch.update(range(max(1, r - batchsize), r + 1)) 651 tofetch.update(range(max(1, r - batchsize), r + 1))
645 if drevs: 652 if drevs:
646 fetch({r'ids': list(tofetch)}) 653 fetch({r'ids': list(tofetch)})
647 getstack(list(ancestordrevs)) 654 validids = sorted(set(getstack(list(ancestordrevs))) | set(drevs))
648 655
649 # Walk through the tree, return smartsets 656 # Walk through the tree, return smartsets
650 def walk(tree): 657 def walk(tree):
651 op = tree[0] 658 op = tree[0]
652 if op == 'symbol': 659 if op == 'symbol':
653 drev = _parsedrev(tree[1]) 660 drev = _parsedrev(tree[1])
654 if drev: 661 if drev:
655 return smartset.baseset([drev]) 662 return smartset.baseset([drev])
663 elif tree[1] in _knownstatusnames:
664 drevs = [r for r in validids
665 if _getstatusname(prefetched[r]) == tree[1]]
666 return smartset.baseset(drevs)
656 else: 667 else:
657 raise error.Abort(_('unknown symbol: %s') % tree[1]) 668 raise error.Abort(_('unknown symbol: %s') % tree[1])
658 elif op in {'and_', 'add', 'sub'}: 669 elif op in {'and_', 'add', 'sub'}:
659 assert len(tree) == 3 670 assert len(tree) == 3
660 return getattr(operator, op)(walk(tree[1]), walk(tree[2])) 671 return getattr(operator, op)(walk(tree[1]), walk(tree[2]))
770 DREVSPEC could be a Differential Revision identity, like ``D123``, or just 781 DREVSPEC could be a Differential Revision identity, like ``D123``, or just
771 the number ``123``. It could also have common operators like ``+``, ``-``, 782 the number ``123``. It could also have common operators like ``+``, ``-``,
772 ``&``, ``(``, ``)`` for complex queries. Prefix ``:`` could be used to 783 ``&``, ``(``, ``)`` for complex queries. Prefix ``:`` could be used to
773 select a stack. 784 select a stack.
774 785
786 ``abandoned``, ``accepted``, ``closed``, ``needsreview``, ``needsrevision``
787 could be used to filter patches by status. For performance reason, they
788 only represent a subset of non-status selections and cannot be used alone.
789
775 For example, ``:D6+8-(2+D4)`` selects a stack up to D6, plus D8 and exclude 790 For example, ``:D6+8-(2+D4)`` selects a stack up to D6, plus D8 and exclude
776 D2 and D4. 791 D2 and D4. ``:D9 & needsreview`` selects "Needs Review" revisions in a
792 stack up to D9.
777 793
778 If --stack is given, follow dependencies information and read all patches. 794 If --stack is given, follow dependencies information and read all patches.
779 It is equivalent to the ``:`` operator. 795 It is equivalent to the ``:`` operator.
780 """ 796 """
781 if opts.get('stack'): 797 if opts.get('stack'):