Mercurial > public > mercurial-scm > hg
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'): |