Mercurial > public > mercurial-scm > hg
comparison mercurial/revset.py @ 27584:fc7c8cac6a4b
revset: use decorator to register a function as revset predicate
Using decorator can localize changes for adding (or removing) a revset
predicate function in source code.
It is also useful to pick predicates up for specific purpose. For
example, subsequent patch marks predicates as "safe" by decorator.
This patch defines 'parsefuncdecl()' in 'funcregistrar' class, because
this implementation can be uesd by other decorator class for fileset
predicate and template function.
author | FUJIWARA Katsunori <foozy@lares.dti.ne.jp> |
---|---|
date | Tue, 29 Dec 2015 23:58:30 +0900 |
parents | c60a9c16ae25 |
children | 42910f9fffeb |
comparison
equal
deleted
inserted
replaced
27583:37d50250b696 | 27584:fc7c8cac6a4b |
---|---|
20 node, | 20 node, |
21 obsolete as obsmod, | 21 obsolete as obsmod, |
22 parser, | 22 parser, |
23 pathutil, | 23 pathutil, |
24 phases, | 24 phases, |
25 registrar, | |
25 repoview, | 26 repoview, |
26 util, | 27 util, |
27 ) | 28 ) |
28 | 29 |
29 def _revancestors(repo, revs, followfirst): | 30 def _revancestors(repo, revs, followfirst): |
463 syms = [s for (s, fn) in symbols.items() if keep(fn)] | 464 syms = [s for (s, fn) in symbols.items() if keep(fn)] |
464 raise error.UnknownIdentifier(a[1], syms) | 465 raise error.UnknownIdentifier(a[1], syms) |
465 | 466 |
466 # functions | 467 # functions |
467 | 468 |
469 # symbols are callables like: | |
470 # fn(repo, subset, x) | |
471 # with: | |
472 # repo - current repository instance | |
473 # subset - of revisions to be examined | |
474 # x - argument in tree form | |
475 symbols = {} | |
476 | |
477 class predicate(registrar.funcregistrar): | |
478 """Decorator to register revset predicate | |
479 | |
480 Usage:: | |
481 | |
482 @predicate('mypredicate(arg1, arg2[, arg3])') | |
483 def mypredicatefunc(repo, subset, x): | |
484 '''Explanation of this revset predicate .... | |
485 ''' | |
486 pass | |
487 | |
488 The first string argument of the constructor is used also in | |
489 online help. | |
490 """ | |
491 table = symbols | |
492 formatdoc = "``%s``\n %s" | |
493 getname = registrar.funcregistrar.parsefuncdecl | |
494 | |
495 @predicate('_destupdate') | |
468 def _destupdate(repo, subset, x): | 496 def _destupdate(repo, subset, x): |
469 # experimental revset for update destination | 497 # experimental revset for update destination |
470 args = getargsdict(x, 'limit', 'clean check') | 498 args = getargsdict(x, 'limit', 'clean check') |
471 return subset & baseset([destutil.destupdate(repo, **args)[0]]) | 499 return subset & baseset([destutil.destupdate(repo, **args)[0]]) |
472 | 500 |
501 @predicate('_destmerge') | |
473 def _destmerge(repo, subset, x): | 502 def _destmerge(repo, subset, x): |
474 # experimental revset for merge destination | 503 # experimental revset for merge destination |
475 getargs(x, 0, 0, _("_mergedefaultdest takes no arguments")) | 504 getargs(x, 0, 0, _("_mergedefaultdest takes no arguments")) |
476 return subset & baseset([destutil.destmerge(repo)]) | 505 return subset & baseset([destutil.destmerge(repo)]) |
477 | 506 |
507 @predicate('adds(pattern)') | |
478 def adds(repo, subset, x): | 508 def adds(repo, subset, x): |
479 """``adds(pattern)`` | 509 """Changesets that add a file matching pattern. |
480 Changesets that add a file matching pattern. | |
481 | 510 |
482 The pattern without explicit kind like ``glob:`` is expected to be | 511 The pattern without explicit kind like ``glob:`` is expected to be |
483 relative to the current directory and match against a file or a | 512 relative to the current directory and match against a file or a |
484 directory. | 513 directory. |
485 """ | 514 """ |
486 # i18n: "adds" is a keyword | 515 # i18n: "adds" is a keyword |
487 pat = getstring(x, _("adds requires a pattern")) | 516 pat = getstring(x, _("adds requires a pattern")) |
488 return checkstatus(repo, subset, pat, 1) | 517 return checkstatus(repo, subset, pat, 1) |
489 | 518 |
519 @predicate('ancestor(*changeset)') | |
490 def ancestor(repo, subset, x): | 520 def ancestor(repo, subset, x): |
491 """``ancestor(*changeset)`` | 521 """A greatest common ancestor of the changesets. |
492 A greatest common ancestor of the changesets. | |
493 | 522 |
494 Accepts 0 or more changesets. | 523 Accepts 0 or more changesets. |
495 Will return empty list when passed no args. | 524 Will return empty list when passed no args. |
496 Greatest common ancestor of a single changeset is that changeset. | 525 Greatest common ancestor of a single changeset is that changeset. |
497 """ | 526 """ |
517 if not heads: | 546 if not heads: |
518 return baseset() | 547 return baseset() |
519 s = _revancestors(repo, heads, followfirst) | 548 s = _revancestors(repo, heads, followfirst) |
520 return subset & s | 549 return subset & s |
521 | 550 |
551 @predicate('ancestors(set)') | |
522 def ancestors(repo, subset, x): | 552 def ancestors(repo, subset, x): |
523 """``ancestors(set)`` | 553 """Changesets that are ancestors of a changeset in set. |
524 Changesets that are ancestors of a changeset in set. | |
525 """ | 554 """ |
526 return _ancestors(repo, subset, x) | 555 return _ancestors(repo, subset, x) |
527 | 556 |
557 @predicate('_firstancestors') | |
528 def _firstancestors(repo, subset, x): | 558 def _firstancestors(repo, subset, x): |
529 # ``_firstancestors(set)`` | 559 # ``_firstancestors(set)`` |
530 # Like ``ancestors(set)`` but follows only the first parents. | 560 # Like ``ancestors(set)`` but follows only the first parents. |
531 return _ancestors(repo, subset, x, followfirst=True) | 561 return _ancestors(repo, subset, x, followfirst=True) |
532 | 562 |
545 for i in range(n): | 575 for i in range(n): |
546 r = cl.parentrevs(r)[0] | 576 r = cl.parentrevs(r)[0] |
547 ps.add(r) | 577 ps.add(r) |
548 return subset & ps | 578 return subset & ps |
549 | 579 |
580 @predicate('author(string)') | |
550 def author(repo, subset, x): | 581 def author(repo, subset, x): |
551 """``author(string)`` | 582 """Alias for ``user(string)``. |
552 Alias for ``user(string)``. | |
553 """ | 583 """ |
554 # i18n: "author" is a keyword | 584 # i18n: "author" is a keyword |
555 n = encoding.lower(getstring(x, _("author requires a string"))) | 585 n = encoding.lower(getstring(x, _("author requires a string"))) |
556 kind, pattern, matcher = _substringmatcher(n) | 586 kind, pattern, matcher = _substringmatcher(n) |
557 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user()))) | 587 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user()))) |
558 | 588 |
589 @predicate('bisect(string)') | |
559 def bisect(repo, subset, x): | 590 def bisect(repo, subset, x): |
560 """``bisect(string)`` | 591 """Changesets marked in the specified bisect status: |
561 Changesets marked in the specified bisect status: | |
562 | 592 |
563 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip | 593 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip |
564 - ``goods``, ``bads`` : csets topologically good/bad | 594 - ``goods``, ``bads`` : csets topologically good/bad |
565 - ``range`` : csets taking part in the bisection | 595 - ``range`` : csets taking part in the bisection |
566 - ``pruned`` : csets that are goods, bads or skipped | 596 - ``pruned`` : csets that are goods, bads or skipped |
573 state = set(hbisect.get(repo, status)) | 603 state = set(hbisect.get(repo, status)) |
574 return subset & state | 604 return subset & state |
575 | 605 |
576 # Backward-compatibility | 606 # Backward-compatibility |
577 # - no help entry so that we do not advertise it any more | 607 # - no help entry so that we do not advertise it any more |
608 @predicate('bisected') | |
578 def bisected(repo, subset, x): | 609 def bisected(repo, subset, x): |
579 return bisect(repo, subset, x) | 610 return bisect(repo, subset, x) |
580 | 611 |
612 @predicate('bookmark([name])') | |
581 def bookmark(repo, subset, x): | 613 def bookmark(repo, subset, x): |
582 """``bookmark([name])`` | 614 """The named bookmark or all bookmarks. |
583 The named bookmark or all bookmarks. | |
584 | 615 |
585 If `name` starts with `re:`, the remainder of the name is treated as | 616 If `name` starts with `re:`, the remainder of the name is treated as |
586 a regular expression. To match a bookmark that actually starts with `re:`, | 617 a regular expression. To match a bookmark that actually starts with `re:`, |
587 use the prefix `literal:`. | 618 use the prefix `literal:`. |
588 """ | 619 """ |
614 bms = set([repo[r].rev() | 645 bms = set([repo[r].rev() |
615 for r in repo._bookmarks.values()]) | 646 for r in repo._bookmarks.values()]) |
616 bms -= set([node.nullrev]) | 647 bms -= set([node.nullrev]) |
617 return subset & bms | 648 return subset & bms |
618 | 649 |
650 @predicate('branch(string or set)') | |
619 def branch(repo, subset, x): | 651 def branch(repo, subset, x): |
620 """``branch(string or set)`` | 652 """ |
621 All changesets belonging to the given branch or the branches of the given | 653 All changesets belonging to the given branch or the branches of the given |
622 changesets. | 654 changesets. |
623 | 655 |
624 If `string` starts with `re:`, the remainder of the name is treated as | 656 If `string` starts with `re:`, the remainder of the name is treated as |
625 a regular expression. To match a branch that actually starts with `re:`, | 657 a regular expression. To match a branch that actually starts with `re:`, |
650 for r in s: | 682 for r in s: |
651 b.add(getbi(r)[0]) | 683 b.add(getbi(r)[0]) |
652 c = s.__contains__ | 684 c = s.__contains__ |
653 return subset.filter(lambda r: c(r) or getbi(r)[0] in b) | 685 return subset.filter(lambda r: c(r) or getbi(r)[0] in b) |
654 | 686 |
687 @predicate('bumped()') | |
655 def bumped(repo, subset, x): | 688 def bumped(repo, subset, x): |
656 """``bumped()`` | 689 """Mutable changesets marked as successors of public changesets. |
657 Mutable changesets marked as successors of public changesets. | |
658 | 690 |
659 Only non-public and non-obsolete changesets can be `bumped`. | 691 Only non-public and non-obsolete changesets can be `bumped`. |
660 """ | 692 """ |
661 # i18n: "bumped" is a keyword | 693 # i18n: "bumped" is a keyword |
662 getargs(x, 0, 0, _("bumped takes no arguments")) | 694 getargs(x, 0, 0, _("bumped takes no arguments")) |
663 bumped = obsmod.getrevs(repo, 'bumped') | 695 bumped = obsmod.getrevs(repo, 'bumped') |
664 return subset & bumped | 696 return subset & bumped |
665 | 697 |
698 @predicate('bundle()') | |
666 def bundle(repo, subset, x): | 699 def bundle(repo, subset, x): |
667 """``bundle()`` | 700 """Changesets in the bundle. |
668 Changesets in the bundle. | |
669 | 701 |
670 Bundle must be specified by the -R option.""" | 702 Bundle must be specified by the -R option.""" |
671 | 703 |
672 try: | 704 try: |
673 bundlerevs = repo.changelog.bundlerevs | 705 bundlerevs = repo.changelog.bundlerevs |
721 cs.add(r) | 753 cs.add(r) |
722 # XXX using a set to feed the baseset is wrong. Sets are not ordered. | 754 # XXX using a set to feed the baseset is wrong. Sets are not ordered. |
723 # This does not break because of other fullreposet misbehavior. | 755 # This does not break because of other fullreposet misbehavior. |
724 return baseset(cs) | 756 return baseset(cs) |
725 | 757 |
758 @predicate('children(set)') | |
726 def children(repo, subset, x): | 759 def children(repo, subset, x): |
727 """``children(set)`` | 760 """Child changesets of changesets in set. |
728 Child changesets of changesets in set. | |
729 """ | 761 """ |
730 s = getset(repo, fullreposet(repo), x) | 762 s = getset(repo, fullreposet(repo), x) |
731 cs = _children(repo, subset, s) | 763 cs = _children(repo, subset, s) |
732 return subset & cs | 764 return subset & cs |
733 | 765 |
766 @predicate('closed()') | |
734 def closed(repo, subset, x): | 767 def closed(repo, subset, x): |
735 """``closed()`` | 768 """Changeset is closed. |
736 Changeset is closed. | |
737 """ | 769 """ |
738 # i18n: "closed" is a keyword | 770 # i18n: "closed" is a keyword |
739 getargs(x, 0, 0, _("closed takes no arguments")) | 771 getargs(x, 0, 0, _("closed takes no arguments")) |
740 return subset.filter(lambda r: repo[r].closesbranch()) | 772 return subset.filter(lambda r: repo[r].closesbranch()) |
741 | 773 |
774 @predicate('contains(pattern)') | |
742 def contains(repo, subset, x): | 775 def contains(repo, subset, x): |
743 """``contains(pattern)`` | 776 """The revision's manifest contains a file matching pattern (but might not |
744 The revision's manifest contains a file matching pattern (but might not | |
745 modify it). See :hg:`help patterns` for information about file patterns. | 777 modify it). See :hg:`help patterns` for information about file patterns. |
746 | 778 |
747 The pattern without explicit kind like ``glob:`` is expected to be | 779 The pattern without explicit kind like ``glob:`` is expected to be |
748 relative to the current directory and match against a file exactly | 780 relative to the current directory and match against a file exactly |
749 for efficiency. | 781 for efficiency. |
764 return True | 796 return True |
765 return False | 797 return False |
766 | 798 |
767 return subset.filter(matches) | 799 return subset.filter(matches) |
768 | 800 |
801 @predicate('converted([id])') | |
769 def converted(repo, subset, x): | 802 def converted(repo, subset, x): |
770 """``converted([id])`` | 803 """Changesets converted from the given identifier in the old repository if |
771 Changesets converted from the given identifier in the old repository if | |
772 present, or all converted changesets if no identifier is specified. | 804 present, or all converted changesets if no identifier is specified. |
773 """ | 805 """ |
774 | 806 |
775 # There is exactly no chance of resolving the revision, so do a simple | 807 # There is exactly no chance of resolving the revision, so do a simple |
776 # string compare and hope for the best | 808 # string compare and hope for the best |
786 source = repo[r].extra().get('convert_revision', None) | 818 source = repo[r].extra().get('convert_revision', None) |
787 return source is not None and (rev is None or source.startswith(rev)) | 819 return source is not None and (rev is None or source.startswith(rev)) |
788 | 820 |
789 return subset.filter(lambda r: _matchvalue(r)) | 821 return subset.filter(lambda r: _matchvalue(r)) |
790 | 822 |
823 @predicate('date(interval)') | |
791 def date(repo, subset, x): | 824 def date(repo, subset, x): |
792 """``date(interval)`` | 825 """Changesets within the interval, see :hg:`help dates`. |
793 Changesets within the interval, see :hg:`help dates`. | |
794 """ | 826 """ |
795 # i18n: "date" is a keyword | 827 # i18n: "date" is a keyword |
796 ds = getstring(x, _("date requires a string")) | 828 ds = getstring(x, _("date requires a string")) |
797 dm = util.matchdate(ds) | 829 dm = util.matchdate(ds) |
798 return subset.filter(lambda x: dm(repo[x].date()[0])) | 830 return subset.filter(lambda x: dm(repo[x].date()[0])) |
799 | 831 |
832 @predicate('desc(string)') | |
800 def desc(repo, subset, x): | 833 def desc(repo, subset, x): |
801 """``desc(string)`` | 834 """Search commit message for string. The match is case-insensitive. |
802 Search commit message for string. The match is case-insensitive. | |
803 """ | 835 """ |
804 # i18n: "desc" is a keyword | 836 # i18n: "desc" is a keyword |
805 ds = encoding.lower(getstring(x, _("desc requires a string"))) | 837 ds = encoding.lower(getstring(x, _("desc requires a string"))) |
806 | 838 |
807 def matches(x): | 839 def matches(x): |
827 result.sort(reverse=True) | 859 result.sort(reverse=True) |
828 else: | 860 else: |
829 result = subset & result | 861 result = subset & result |
830 return result | 862 return result |
831 | 863 |
864 @predicate('descendants(set)') | |
832 def descendants(repo, subset, x): | 865 def descendants(repo, subset, x): |
833 """``descendants(set)`` | 866 """Changesets which are descendants of changesets in set. |
834 Changesets which are descendants of changesets in set. | |
835 """ | 867 """ |
836 return _descendants(repo, subset, x) | 868 return _descendants(repo, subset, x) |
837 | 869 |
870 @predicate('_firstdescendants') | |
838 def _firstdescendants(repo, subset, x): | 871 def _firstdescendants(repo, subset, x): |
839 # ``_firstdescendants(set)`` | 872 # ``_firstdescendants(set)`` |
840 # Like ``descendants(set)`` but follows only the first parents. | 873 # Like ``descendants(set)`` but follows only the first parents. |
841 return _descendants(repo, subset, x, followfirst=True) | 874 return _descendants(repo, subset, x, followfirst=True) |
842 | 875 |
876 @predicate('destination([set])') | |
843 def destination(repo, subset, x): | 877 def destination(repo, subset, x): |
844 """``destination([set])`` | 878 """Changesets that were created by a graft, transplant or rebase operation, |
845 Changesets that were created by a graft, transplant or rebase operation, | |
846 with the given revisions specified as the source. Omitting the optional set | 879 with the given revisions specified as the source. Omitting the optional set |
847 is the same as passing all(). | 880 is the same as passing all(). |
848 """ | 881 """ |
849 if x is not None: | 882 if x is not None: |
850 sources = getset(repo, fullreposet(repo), x) | 883 sources = getset(repo, fullreposet(repo), x) |
882 r = src | 915 r = src |
883 src = _getrevsource(repo, r) | 916 src = _getrevsource(repo, r) |
884 | 917 |
885 return subset.filter(dests.__contains__) | 918 return subset.filter(dests.__contains__) |
886 | 919 |
920 @predicate('divergent()') | |
887 def divergent(repo, subset, x): | 921 def divergent(repo, subset, x): |
888 """``divergent()`` | 922 """ |
889 Final successors of changesets with an alternative set of final successors. | 923 Final successors of changesets with an alternative set of final successors. |
890 """ | 924 """ |
891 # i18n: "divergent" is a keyword | 925 # i18n: "divergent" is a keyword |
892 getargs(x, 0, 0, _("divergent takes no arguments")) | 926 getargs(x, 0, 0, _("divergent takes no arguments")) |
893 divergent = obsmod.getrevs(repo, 'divergent') | 927 divergent = obsmod.getrevs(repo, 'divergent') |
894 return subset & divergent | 928 return subset & divergent |
895 | 929 |
930 @predicate('extinct()') | |
896 def extinct(repo, subset, x): | 931 def extinct(repo, subset, x): |
897 """``extinct()`` | 932 """Obsolete changesets with obsolete descendants only. |
898 Obsolete changesets with obsolete descendants only. | |
899 """ | 933 """ |
900 # i18n: "extinct" is a keyword | 934 # i18n: "extinct" is a keyword |
901 getargs(x, 0, 0, _("extinct takes no arguments")) | 935 getargs(x, 0, 0, _("extinct takes no arguments")) |
902 extincts = obsmod.getrevs(repo, 'extinct') | 936 extincts = obsmod.getrevs(repo, 'extinct') |
903 return subset & extincts | 937 return subset & extincts |
904 | 938 |
939 @predicate('extra(label, [value])') | |
905 def extra(repo, subset, x): | 940 def extra(repo, subset, x): |
906 """``extra(label, [value])`` | 941 """Changesets with the given label in the extra metadata, with the given |
907 Changesets with the given label in the extra metadata, with the given | |
908 optional value. | 942 optional value. |
909 | 943 |
910 If `value` starts with `re:`, the remainder of the value is treated as | 944 If `value` starts with `re:`, the remainder of the value is treated as |
911 a regular expression. To match a value that actually starts with `re:`, | 945 a regular expression. To match a value that actually starts with `re:`, |
912 use the prefix `literal:`. | 946 use the prefix `literal:`. |
930 extra = repo[r].extra() | 964 extra = repo[r].extra() |
931 return label in extra and (value is None or matcher(extra[label])) | 965 return label in extra and (value is None or matcher(extra[label])) |
932 | 966 |
933 return subset.filter(lambda r: _matchvalue(r)) | 967 return subset.filter(lambda r: _matchvalue(r)) |
934 | 968 |
969 @predicate('filelog(pattern)') | |
935 def filelog(repo, subset, x): | 970 def filelog(repo, subset, x): |
936 """``filelog(pattern)`` | 971 """Changesets connected to the specified filelog. |
937 Changesets connected to the specified filelog. | |
938 | 972 |
939 For performance reasons, visits only revisions mentioned in the file-level | 973 For performance reasons, visits only revisions mentioned in the file-level |
940 filelog, rather than filtering through all changesets (much faster, but | 974 filelog, rather than filtering through all changesets (much faster, but |
941 doesn't include deletes or duplicate changes). For a slower, more accurate | 975 doesn't include deletes or duplicate changes). For a slower, more accurate |
942 result, use ``file()``. | 976 result, use ``file()``. |
1045 backrevref[fr] = rev | 1079 backrevref[fr] = rev |
1046 s.add(rev) | 1080 s.add(rev) |
1047 | 1081 |
1048 return subset & s | 1082 return subset & s |
1049 | 1083 |
1084 @predicate('first(set, [n])') | |
1050 def first(repo, subset, x): | 1085 def first(repo, subset, x): |
1051 """``first(set, [n])`` | 1086 """An alias for limit(). |
1052 An alias for limit(). | |
1053 """ | 1087 """ |
1054 return limit(repo, subset, x) | 1088 return limit(repo, subset, x) |
1055 | 1089 |
1056 def _follow(repo, subset, x, name, followfirst=False): | 1090 def _follow(repo, subset, x, name, followfirst=False): |
1057 l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name) | 1091 l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name) |
1071 else: | 1105 else: |
1072 s = _revancestors(repo, baseset([c.rev()]), followfirst) | 1106 s = _revancestors(repo, baseset([c.rev()]), followfirst) |
1073 | 1107 |
1074 return subset & s | 1108 return subset & s |
1075 | 1109 |
1110 @predicate('follow([pattern])') | |
1076 def follow(repo, subset, x): | 1111 def follow(repo, subset, x): |
1077 """``follow([pattern])`` | 1112 """ |
1078 An alias for ``::.`` (ancestors of the working directory's first parent). | 1113 An alias for ``::.`` (ancestors of the working directory's first parent). |
1079 If pattern is specified, the histories of files matching given | 1114 If pattern is specified, the histories of files matching given |
1080 pattern is followed, including copies. | 1115 pattern is followed, including copies. |
1081 """ | 1116 """ |
1082 return _follow(repo, subset, x, 'follow') | 1117 return _follow(repo, subset, x, 'follow') |
1083 | 1118 |
1119 @predicate('_followfirst') | |
1084 def _followfirst(repo, subset, x): | 1120 def _followfirst(repo, subset, x): |
1085 # ``followfirst([pattern])`` | 1121 # ``followfirst([pattern])`` |
1086 # Like ``follow([pattern])`` but follows only the first parent of | 1122 # Like ``follow([pattern])`` but follows only the first parent of |
1087 # every revisions or files revisions. | 1123 # every revisions or files revisions. |
1088 return _follow(repo, subset, x, '_followfirst', followfirst=True) | 1124 return _follow(repo, subset, x, '_followfirst', followfirst=True) |
1089 | 1125 |
1126 @predicate('all()') | |
1090 def getall(repo, subset, x): | 1127 def getall(repo, subset, x): |
1091 """``all()`` | 1128 """All changesets, the same as ``0:tip``. |
1092 All changesets, the same as ``0:tip``. | |
1093 """ | 1129 """ |
1094 # i18n: "all" is a keyword | 1130 # i18n: "all" is a keyword |
1095 getargs(x, 0, 0, _("all takes no arguments")) | 1131 getargs(x, 0, 0, _("all takes no arguments")) |
1096 return subset & spanset(repo) # drop "null" if any | 1132 return subset & spanset(repo) # drop "null" if any |
1097 | 1133 |
1134 @predicate('grep(regex)') | |
1098 def grep(repo, subset, x): | 1135 def grep(repo, subset, x): |
1099 """``grep(regex)`` | 1136 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')`` |
1100 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')`` | |
1101 to ensure special escape characters are handled correctly. Unlike | 1137 to ensure special escape characters are handled correctly. Unlike |
1102 ``keyword(string)``, the match is case-sensitive. | 1138 ``keyword(string)``, the match is case-sensitive. |
1103 """ | 1139 """ |
1104 try: | 1140 try: |
1105 # i18n: "grep" is a keyword | 1141 # i18n: "grep" is a keyword |
1114 return True | 1150 return True |
1115 return False | 1151 return False |
1116 | 1152 |
1117 return subset.filter(matches) | 1153 return subset.filter(matches) |
1118 | 1154 |
1155 @predicate('_matchfiles') | |
1119 def _matchfiles(repo, subset, x): | 1156 def _matchfiles(repo, subset, x): |
1120 # _matchfiles takes a revset list of prefixed arguments: | 1157 # _matchfiles takes a revset list of prefixed arguments: |
1121 # | 1158 # |
1122 # [p:foo, i:bar, x:baz] | 1159 # [p:foo, i:bar, x:baz] |
1123 # | 1160 # |
1179 return True | 1216 return True |
1180 return False | 1217 return False |
1181 | 1218 |
1182 return subset.filter(matches) | 1219 return subset.filter(matches) |
1183 | 1220 |
1221 @predicate('file(pattern)') | |
1184 def hasfile(repo, subset, x): | 1222 def hasfile(repo, subset, x): |
1185 """``file(pattern)`` | 1223 """Changesets affecting files matched by pattern. |
1186 Changesets affecting files matched by pattern. | |
1187 | 1224 |
1188 For a faster but less accurate result, consider using ``filelog()`` | 1225 For a faster but less accurate result, consider using ``filelog()`` |
1189 instead. | 1226 instead. |
1190 | 1227 |
1191 This predicate uses ``glob:`` as the default kind of pattern. | 1228 This predicate uses ``glob:`` as the default kind of pattern. |
1192 """ | 1229 """ |
1193 # i18n: "file" is a keyword | 1230 # i18n: "file" is a keyword |
1194 pat = getstring(x, _("file requires a pattern")) | 1231 pat = getstring(x, _("file requires a pattern")) |
1195 return _matchfiles(repo, subset, ('string', 'p:' + pat)) | 1232 return _matchfiles(repo, subset, ('string', 'p:' + pat)) |
1196 | 1233 |
1234 @predicate('head()') | |
1197 def head(repo, subset, x): | 1235 def head(repo, subset, x): |
1198 """``head()`` | 1236 """Changeset is a named branch head. |
1199 Changeset is a named branch head. | |
1200 """ | 1237 """ |
1201 # i18n: "head" is a keyword | 1238 # i18n: "head" is a keyword |
1202 getargs(x, 0, 0, _("head takes no arguments")) | 1239 getargs(x, 0, 0, _("head takes no arguments")) |
1203 hs = set() | 1240 hs = set() |
1204 cl = repo.changelog | 1241 cl = repo.changelog |
1208 # This does not break because of other fullreposet misbehavior. | 1245 # This does not break because of other fullreposet misbehavior. |
1209 # XXX We should combine with subset first: 'subset & baseset(...)'. This is | 1246 # XXX We should combine with subset first: 'subset & baseset(...)'. This is |
1210 # necessary to ensure we preserve the order in subset. | 1247 # necessary to ensure we preserve the order in subset. |
1211 return baseset(hs) & subset | 1248 return baseset(hs) & subset |
1212 | 1249 |
1250 @predicate('heads(set)') | |
1213 def heads(repo, subset, x): | 1251 def heads(repo, subset, x): |
1214 """``heads(set)`` | 1252 """Members of set with no children in set. |
1215 Members of set with no children in set. | |
1216 """ | 1253 """ |
1217 s = getset(repo, subset, x) | 1254 s = getset(repo, subset, x) |
1218 ps = parents(repo, subset, x) | 1255 ps = parents(repo, subset, x) |
1219 return s - ps | 1256 return s - ps |
1220 | 1257 |
1258 @predicate('hidden()') | |
1221 def hidden(repo, subset, x): | 1259 def hidden(repo, subset, x): |
1222 """``hidden()`` | 1260 """Hidden changesets. |
1223 Hidden changesets. | |
1224 """ | 1261 """ |
1225 # i18n: "hidden" is a keyword | 1262 # i18n: "hidden" is a keyword |
1226 getargs(x, 0, 0, _("hidden takes no arguments")) | 1263 getargs(x, 0, 0, _("hidden takes no arguments")) |
1227 hiddenrevs = repoview.filterrevs(repo, 'visible') | 1264 hiddenrevs = repoview.filterrevs(repo, 'visible') |
1228 return subset & hiddenrevs | 1265 return subset & hiddenrevs |
1229 | 1266 |
1267 @predicate('keyword(string)') | |
1230 def keyword(repo, subset, x): | 1268 def keyword(repo, subset, x): |
1231 """``keyword(string)`` | 1269 """Search commit message, user name, and names of changed files for |
1232 Search commit message, user name, and names of changed files for | |
1233 string. The match is case-insensitive. | 1270 string. The match is case-insensitive. |
1234 """ | 1271 """ |
1235 # i18n: "keyword" is a keyword | 1272 # i18n: "keyword" is a keyword |
1236 kw = encoding.lower(getstring(x, _("keyword requires a string"))) | 1273 kw = encoding.lower(getstring(x, _("keyword requires a string"))) |
1237 | 1274 |
1240 return any(kw in encoding.lower(t) | 1277 return any(kw in encoding.lower(t) |
1241 for t in c.files() + [c.user(), c.description()]) | 1278 for t in c.files() + [c.user(), c.description()]) |
1242 | 1279 |
1243 return subset.filter(matches) | 1280 return subset.filter(matches) |
1244 | 1281 |
1282 @predicate('limit(set[, n[, offset]])') | |
1245 def limit(repo, subset, x): | 1283 def limit(repo, subset, x): |
1246 """``limit(set[, n[, offset]])`` | 1284 """First n members of set, defaulting to 1, starting from offset. |
1247 First n members of set, defaulting to 1, starting from offset. | |
1248 """ | 1285 """ |
1249 args = getargsdict(x, 'limit', 'set n offset') | 1286 args = getargsdict(x, 'limit', 'set n offset') |
1250 if 'set' not in args: | 1287 if 'set' not in args: |
1251 # i18n: "limit" is a keyword | 1288 # i18n: "limit" is a keyword |
1252 raise error.ParseError(_("limit requires one to three arguments")) | 1289 raise error.ParseError(_("limit requires one to three arguments")) |
1276 break | 1313 break |
1277 elif y in subset: | 1314 elif y in subset: |
1278 result.append(y) | 1315 result.append(y) |
1279 return baseset(result) | 1316 return baseset(result) |
1280 | 1317 |
1318 @predicate('last(set, [n])') | |
1281 def last(repo, subset, x): | 1319 def last(repo, subset, x): |
1282 """``last(set, [n])`` | 1320 """Last n members of set, defaulting to 1. |
1283 Last n members of set, defaulting to 1. | |
1284 """ | 1321 """ |
1285 # i18n: "last" is a keyword | 1322 # i18n: "last" is a keyword |
1286 l = getargs(x, 1, 2, _("last requires one or two arguments")) | 1323 l = getargs(x, 1, 2, _("last requires one or two arguments")) |
1287 try: | 1324 try: |
1288 lim = 1 | 1325 lim = 1 |
1302 break | 1339 break |
1303 elif y in subset: | 1340 elif y in subset: |
1304 result.append(y) | 1341 result.append(y) |
1305 return baseset(result) | 1342 return baseset(result) |
1306 | 1343 |
1344 @predicate('max(set)') | |
1307 def maxrev(repo, subset, x): | 1345 def maxrev(repo, subset, x): |
1308 """``max(set)`` | 1346 """Changeset with highest revision number in set. |
1309 Changeset with highest revision number in set. | |
1310 """ | 1347 """ |
1311 os = getset(repo, fullreposet(repo), x) | 1348 os = getset(repo, fullreposet(repo), x) |
1312 try: | 1349 try: |
1313 m = os.max() | 1350 m = os.max() |
1314 if m in subset: | 1351 if m in subset: |
1317 # os.max() throws a ValueError when the collection is empty. | 1354 # os.max() throws a ValueError when the collection is empty. |
1318 # Same as python's max(). | 1355 # Same as python's max(). |
1319 pass | 1356 pass |
1320 return baseset() | 1357 return baseset() |
1321 | 1358 |
1359 @predicate('merge()') | |
1322 def merge(repo, subset, x): | 1360 def merge(repo, subset, x): |
1323 """``merge()`` | 1361 """Changeset is a merge changeset. |
1324 Changeset is a merge changeset. | |
1325 """ | 1362 """ |
1326 # i18n: "merge" is a keyword | 1363 # i18n: "merge" is a keyword |
1327 getargs(x, 0, 0, _("merge takes no arguments")) | 1364 getargs(x, 0, 0, _("merge takes no arguments")) |
1328 cl = repo.changelog | 1365 cl = repo.changelog |
1329 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1) | 1366 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1) |
1330 | 1367 |
1368 @predicate('branchpoint()') | |
1331 def branchpoint(repo, subset, x): | 1369 def branchpoint(repo, subset, x): |
1332 """``branchpoint()`` | 1370 """Changesets with more than one child. |
1333 Changesets with more than one child. | |
1334 """ | 1371 """ |
1335 # i18n: "branchpoint" is a keyword | 1372 # i18n: "branchpoint" is a keyword |
1336 getargs(x, 0, 0, _("branchpoint takes no arguments")) | 1373 getargs(x, 0, 0, _("branchpoint takes no arguments")) |
1337 cl = repo.changelog | 1374 cl = repo.changelog |
1338 if not subset: | 1375 if not subset: |
1345 for p in cl.parentrevs(r): | 1382 for p in cl.parentrevs(r): |
1346 if p >= baserev: | 1383 if p >= baserev: |
1347 parentscount[p - baserev] += 1 | 1384 parentscount[p - baserev] += 1 |
1348 return subset.filter(lambda r: parentscount[r - baserev] > 1) | 1385 return subset.filter(lambda r: parentscount[r - baserev] > 1) |
1349 | 1386 |
1387 @predicate('min(set)') | |
1350 def minrev(repo, subset, x): | 1388 def minrev(repo, subset, x): |
1351 """``min(set)`` | 1389 """Changeset with lowest revision number in set. |
1352 Changeset with lowest revision number in set. | |
1353 """ | 1390 """ |
1354 os = getset(repo, fullreposet(repo), x) | 1391 os = getset(repo, fullreposet(repo), x) |
1355 try: | 1392 try: |
1356 m = os.min() | 1393 m = os.min() |
1357 if m in subset: | 1394 if m in subset: |
1360 # os.min() throws a ValueError when the collection is empty. | 1397 # os.min() throws a ValueError when the collection is empty. |
1361 # Same as python's min(). | 1398 # Same as python's min(). |
1362 pass | 1399 pass |
1363 return baseset() | 1400 return baseset() |
1364 | 1401 |
1402 @predicate('modifies(pattern)') | |
1365 def modifies(repo, subset, x): | 1403 def modifies(repo, subset, x): |
1366 """``modifies(pattern)`` | 1404 """Changesets modifying files matched by pattern. |
1367 Changesets modifying files matched by pattern. | |
1368 | 1405 |
1369 The pattern without explicit kind like ``glob:`` is expected to be | 1406 The pattern without explicit kind like ``glob:`` is expected to be |
1370 relative to the current directory and match against a file or a | 1407 relative to the current directory and match against a file or a |
1371 directory. | 1408 directory. |
1372 """ | 1409 """ |
1373 # i18n: "modifies" is a keyword | 1410 # i18n: "modifies" is a keyword |
1374 pat = getstring(x, _("modifies requires a pattern")) | 1411 pat = getstring(x, _("modifies requires a pattern")) |
1375 return checkstatus(repo, subset, pat, 0) | 1412 return checkstatus(repo, subset, pat, 0) |
1376 | 1413 |
1414 @predicate('named(namespace)') | |
1377 def named(repo, subset, x): | 1415 def named(repo, subset, x): |
1378 """``named(namespace)`` | 1416 """The changesets in a given namespace. |
1379 The changesets in a given namespace. | |
1380 | 1417 |
1381 If `namespace` starts with `re:`, the remainder of the string is treated as | 1418 If `namespace` starts with `re:`, the remainder of the string is treated as |
1382 a regular expression. To match a namespace that actually starts with `re:`, | 1419 a regular expression. To match a namespace that actually starts with `re:`, |
1383 use the prefix `literal:`. | 1420 use the prefix `literal:`. |
1384 """ | 1421 """ |
1410 names.update(repo[n].rev() for n in ns.nodes(repo, name)) | 1447 names.update(repo[n].rev() for n in ns.nodes(repo, name)) |
1411 | 1448 |
1412 names -= set([node.nullrev]) | 1449 names -= set([node.nullrev]) |
1413 return subset & names | 1450 return subset & names |
1414 | 1451 |
1452 @predicate('id(string)') | |
1415 def node_(repo, subset, x): | 1453 def node_(repo, subset, x): |
1416 """``id(string)`` | 1454 """Revision non-ambiguously specified by the given hex string prefix. |
1417 Revision non-ambiguously specified by the given hex string prefix. | |
1418 """ | 1455 """ |
1419 # i18n: "id" is a keyword | 1456 # i18n: "id" is a keyword |
1420 l = getargs(x, 1, 1, _("id requires one argument")) | 1457 l = getargs(x, 1, 1, _("id requires one argument")) |
1421 # i18n: "id" is a keyword | 1458 # i18n: "id" is a keyword |
1422 n = getstring(l[0], _("id requires a string")) | 1459 n = getstring(l[0], _("id requires a string")) |
1434 if rn is None: | 1471 if rn is None: |
1435 return baseset() | 1472 return baseset() |
1436 result = baseset([rn]) | 1473 result = baseset([rn]) |
1437 return result & subset | 1474 return result & subset |
1438 | 1475 |
1476 @predicate('obsolete()') | |
1439 def obsolete(repo, subset, x): | 1477 def obsolete(repo, subset, x): |
1440 """``obsolete()`` | 1478 """Mutable changeset with a newer version.""" |
1441 Mutable changeset with a newer version.""" | |
1442 # i18n: "obsolete" is a keyword | 1479 # i18n: "obsolete" is a keyword |
1443 getargs(x, 0, 0, _("obsolete takes no arguments")) | 1480 getargs(x, 0, 0, _("obsolete takes no arguments")) |
1444 obsoletes = obsmod.getrevs(repo, 'obsolete') | 1481 obsoletes = obsmod.getrevs(repo, 'obsolete') |
1445 return subset & obsoletes | 1482 return subset & obsoletes |
1446 | 1483 |
1484 @predicate('only(set, [set])') | |
1447 def only(repo, subset, x): | 1485 def only(repo, subset, x): |
1448 """``only(set, [set])`` | 1486 """Changesets that are ancestors of the first set that are not ancestors |
1449 Changesets that are ancestors of the first set that are not ancestors | |
1450 of any other head in the repo. If a second set is specified, the result | 1487 of any other head in the repo. If a second set is specified, the result |
1451 is ancestors of the first set that are not ancestors of the second set | 1488 is ancestors of the first set that are not ancestors of the second set |
1452 (i.e. ::<set1> - ::<set2>). | 1489 (i.e. ::<set1> - ::<set2>). |
1453 """ | 1490 """ |
1454 cl = repo.changelog | 1491 cl = repo.changelog |
1468 results = set(cl.findmissingrevs(common=exclude, heads=include)) | 1505 results = set(cl.findmissingrevs(common=exclude, heads=include)) |
1469 # XXX we should turn this into a baseset instead of a set, smartset may do | 1506 # XXX we should turn this into a baseset instead of a set, smartset may do |
1470 # some optimisations from the fact this is a baseset. | 1507 # some optimisations from the fact this is a baseset. |
1471 return subset & results | 1508 return subset & results |
1472 | 1509 |
1510 @predicate('origin([set])') | |
1473 def origin(repo, subset, x): | 1511 def origin(repo, subset, x): |
1474 """``origin([set])`` | 1512 """ |
1475 Changesets that were specified as a source for the grafts, transplants or | 1513 Changesets that were specified as a source for the grafts, transplants or |
1476 rebases that created the given revisions. Omitting the optional set is the | 1514 rebases that created the given revisions. Omitting the optional set is the |
1477 same as passing all(). If a changeset created by these operations is itself | 1515 same as passing all(). If a changeset created by these operations is itself |
1478 specified as a source for one of these operations, only the source changeset | 1516 specified as a source for one of these operations, only the source changeset |
1479 for the first operation is selected. | 1517 for the first operation is selected. |
1499 o -= set([None]) | 1537 o -= set([None]) |
1500 # XXX we should turn this into a baseset instead of a set, smartset may do | 1538 # XXX we should turn this into a baseset instead of a set, smartset may do |
1501 # some optimisations from the fact this is a baseset. | 1539 # some optimisations from the fact this is a baseset. |
1502 return subset & o | 1540 return subset & o |
1503 | 1541 |
1542 @predicate('outgoing([path])') | |
1504 def outgoing(repo, subset, x): | 1543 def outgoing(repo, subset, x): |
1505 """``outgoing([path])`` | 1544 """Changesets not found in the specified destination repository, or the |
1506 Changesets not found in the specified destination repository, or the | |
1507 default push location. | 1545 default push location. |
1508 """ | 1546 """ |
1509 # Avoid cycles. | 1547 # Avoid cycles. |
1510 from . import ( | 1548 from . import ( |
1511 discovery, | 1549 discovery, |
1526 repo.ui.popbuffer() | 1564 repo.ui.popbuffer() |
1527 cl = repo.changelog | 1565 cl = repo.changelog |
1528 o = set([cl.rev(r) for r in outgoing.missing]) | 1566 o = set([cl.rev(r) for r in outgoing.missing]) |
1529 return subset & o | 1567 return subset & o |
1530 | 1568 |
1569 @predicate('p1([set])') | |
1531 def p1(repo, subset, x): | 1570 def p1(repo, subset, x): |
1532 """``p1([set])`` | 1571 """First parent of changesets in set, or the working directory. |
1533 First parent of changesets in set, or the working directory. | |
1534 """ | 1572 """ |
1535 if x is None: | 1573 if x is None: |
1536 p = repo[x].p1().rev() | 1574 p = repo[x].p1().rev() |
1537 if p >= 0: | 1575 if p >= 0: |
1538 return subset & baseset([p]) | 1576 return subset & baseset([p]) |
1545 ps -= set([node.nullrev]) | 1583 ps -= set([node.nullrev]) |
1546 # XXX we should turn this into a baseset instead of a set, smartset may do | 1584 # XXX we should turn this into a baseset instead of a set, smartset may do |
1547 # some optimisations from the fact this is a baseset. | 1585 # some optimisations from the fact this is a baseset. |
1548 return subset & ps | 1586 return subset & ps |
1549 | 1587 |
1588 @predicate('p2([set])') | |
1550 def p2(repo, subset, x): | 1589 def p2(repo, subset, x): |
1551 """``p2([set])`` | 1590 """Second parent of changesets in set, or the working directory. |
1552 Second parent of changesets in set, or the working directory. | |
1553 """ | 1591 """ |
1554 if x is None: | 1592 if x is None: |
1555 ps = repo[x].parents() | 1593 ps = repo[x].parents() |
1556 try: | 1594 try: |
1557 p = ps[1].rev() | 1595 p = ps[1].rev() |
1568 ps -= set([node.nullrev]) | 1606 ps -= set([node.nullrev]) |
1569 # XXX we should turn this into a baseset instead of a set, smartset may do | 1607 # XXX we should turn this into a baseset instead of a set, smartset may do |
1570 # some optimisations from the fact this is a baseset. | 1608 # some optimisations from the fact this is a baseset. |
1571 return subset & ps | 1609 return subset & ps |
1572 | 1610 |
1611 @predicate('parents([set])') | |
1573 def parents(repo, subset, x): | 1612 def parents(repo, subset, x): |
1574 """``parents([set])`` | 1613 """ |
1575 The set of all parents for all changesets in set, or the working directory. | 1614 The set of all parents for all changesets in set, or the working directory. |
1576 """ | 1615 """ |
1577 if x is None: | 1616 if x is None: |
1578 ps = set(p.rev() for p in repo[x].parents()) | 1617 ps = set(p.rev() for p in repo[x].parents()) |
1579 else: | 1618 else: |
1600 else: | 1639 else: |
1601 phase = repo._phasecache.phase | 1640 phase = repo._phasecache.phase |
1602 condition = lambda r: phase(repo, r) == target | 1641 condition = lambda r: phase(repo, r) == target |
1603 return subset.filter(condition, cache=False) | 1642 return subset.filter(condition, cache=False) |
1604 | 1643 |
1644 @predicate('draft()') | |
1605 def draft(repo, subset, x): | 1645 def draft(repo, subset, x): |
1606 """``draft()`` | 1646 """Changeset in draft phase.""" |
1607 Changeset in draft phase.""" | |
1608 # i18n: "draft" is a keyword | 1647 # i18n: "draft" is a keyword |
1609 getargs(x, 0, 0, _("draft takes no arguments")) | 1648 getargs(x, 0, 0, _("draft takes no arguments")) |
1610 target = phases.draft | 1649 target = phases.draft |
1611 return _phase(repo, subset, target) | 1650 return _phase(repo, subset, target) |
1612 | 1651 |
1652 @predicate('secret()') | |
1613 def secret(repo, subset, x): | 1653 def secret(repo, subset, x): |
1614 """``secret()`` | 1654 """Changeset in secret phase.""" |
1615 Changeset in secret phase.""" | |
1616 # i18n: "secret" is a keyword | 1655 # i18n: "secret" is a keyword |
1617 getargs(x, 0, 0, _("secret takes no arguments")) | 1656 getargs(x, 0, 0, _("secret takes no arguments")) |
1618 target = phases.secret | 1657 target = phases.secret |
1619 return _phase(repo, subset, target) | 1658 return _phase(repo, subset, target) |
1620 | 1659 |
1641 parents = cl.parentrevs(r) | 1680 parents = cl.parentrevs(r) |
1642 if len(parents) > 1: | 1681 if len(parents) > 1: |
1643 ps.add(parents[1]) | 1682 ps.add(parents[1]) |
1644 return subset & ps | 1683 return subset & ps |
1645 | 1684 |
1685 @predicate('present(set)') | |
1646 def present(repo, subset, x): | 1686 def present(repo, subset, x): |
1647 """``present(set)`` | 1687 """An empty set, if any revision in set isn't found; otherwise, |
1648 An empty set, if any revision in set isn't found; otherwise, | |
1649 all revisions in set. | 1688 all revisions in set. |
1650 | 1689 |
1651 If any of specified revisions is not present in the local repository, | 1690 If any of specified revisions is not present in the local repository, |
1652 the query is normally aborted. But this predicate allows the query | 1691 the query is normally aborted. But this predicate allows the query |
1653 to continue even in such cases. | 1692 to continue even in such cases. |
1656 return getset(repo, subset, x) | 1695 return getset(repo, subset, x) |
1657 except error.RepoLookupError: | 1696 except error.RepoLookupError: |
1658 return baseset() | 1697 return baseset() |
1659 | 1698 |
1660 # for internal use | 1699 # for internal use |
1700 @predicate('_notpublic') | |
1661 def _notpublic(repo, subset, x): | 1701 def _notpublic(repo, subset, x): |
1662 getargs(x, 0, 0, "_notpublic takes no arguments") | 1702 getargs(x, 0, 0, "_notpublic takes no arguments") |
1663 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded | 1703 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded |
1664 if repo._phasecache._phasesets: | 1704 if repo._phasecache._phasesets: |
1665 s = set() | 1705 s = set() |
1672 phase = repo._phasecache.phase | 1712 phase = repo._phasecache.phase |
1673 target = phases.public | 1713 target = phases.public |
1674 condition = lambda r: phase(repo, r) != target | 1714 condition = lambda r: phase(repo, r) != target |
1675 return subset.filter(condition, cache=False) | 1715 return subset.filter(condition, cache=False) |
1676 | 1716 |
1717 @predicate('public()') | |
1677 def public(repo, subset, x): | 1718 def public(repo, subset, x): |
1678 """``public()`` | 1719 """Changeset in public phase.""" |
1679 Changeset in public phase.""" | |
1680 # i18n: "public" is a keyword | 1720 # i18n: "public" is a keyword |
1681 getargs(x, 0, 0, _("public takes no arguments")) | 1721 getargs(x, 0, 0, _("public takes no arguments")) |
1682 phase = repo._phasecache.phase | 1722 phase = repo._phasecache.phase |
1683 target = phases.public | 1723 target = phases.public |
1684 condition = lambda r: phase(repo, r) == target | 1724 condition = lambda r: phase(repo, r) == target |
1685 return subset.filter(condition, cache=False) | 1725 return subset.filter(condition, cache=False) |
1686 | 1726 |
1727 @predicate('remote([id [,path]])') | |
1687 def remote(repo, subset, x): | 1728 def remote(repo, subset, x): |
1688 """``remote([id [,path]])`` | 1729 """Local revision that corresponds to the given identifier in a |
1689 Local revision that corresponds to the given identifier in a | |
1690 remote repository, if present. Here, the '.' identifier is a | 1730 remote repository, if present. Here, the '.' identifier is a |
1691 synonym for the current local branch. | 1731 synonym for the current local branch. |
1692 """ | 1732 """ |
1693 | 1733 |
1694 from . import hg # avoid start-up nasties | 1734 from . import hg # avoid start-up nasties |
1717 r = repo[n].rev() | 1757 r = repo[n].rev() |
1718 if r in subset: | 1758 if r in subset: |
1719 return baseset([r]) | 1759 return baseset([r]) |
1720 return baseset() | 1760 return baseset() |
1721 | 1761 |
1762 @predicate('removes(pattern)') | |
1722 def removes(repo, subset, x): | 1763 def removes(repo, subset, x): |
1723 """``removes(pattern)`` | 1764 """Changesets which remove files matching pattern. |
1724 Changesets which remove files matching pattern. | |
1725 | 1765 |
1726 The pattern without explicit kind like ``glob:`` is expected to be | 1766 The pattern without explicit kind like ``glob:`` is expected to be |
1727 relative to the current directory and match against a file or a | 1767 relative to the current directory and match against a file or a |
1728 directory. | 1768 directory. |
1729 """ | 1769 """ |
1730 # i18n: "removes" is a keyword | 1770 # i18n: "removes" is a keyword |
1731 pat = getstring(x, _("removes requires a pattern")) | 1771 pat = getstring(x, _("removes requires a pattern")) |
1732 return checkstatus(repo, subset, pat, 2) | 1772 return checkstatus(repo, subset, pat, 2) |
1733 | 1773 |
1774 @predicate('rev(number)') | |
1734 def rev(repo, subset, x): | 1775 def rev(repo, subset, x): |
1735 """``rev(number)`` | 1776 """Revision with the given numeric identifier. |
1736 Revision with the given numeric identifier. | |
1737 """ | 1777 """ |
1738 # i18n: "rev" is a keyword | 1778 # i18n: "rev" is a keyword |
1739 l = getargs(x, 1, 1, _("rev requires one argument")) | 1779 l = getargs(x, 1, 1, _("rev requires one argument")) |
1740 try: | 1780 try: |
1741 # i18n: "rev" is a keyword | 1781 # i18n: "rev" is a keyword |
1745 raise error.ParseError(_("rev expects a number")) | 1785 raise error.ParseError(_("rev expects a number")) |
1746 if l not in repo.changelog and l != node.nullrev: | 1786 if l not in repo.changelog and l != node.nullrev: |
1747 return baseset() | 1787 return baseset() |
1748 return subset & baseset([l]) | 1788 return subset & baseset([l]) |
1749 | 1789 |
1790 @predicate('matching(revision [, field])') | |
1750 def matching(repo, subset, x): | 1791 def matching(repo, subset, x): |
1751 """``matching(revision [, field])`` | 1792 """Changesets in which a given set of fields match the set of fields in the |
1752 Changesets in which a given set of fields match the set of fields in the | |
1753 selected revision or set. | 1793 selected revision or set. |
1754 | 1794 |
1755 To match more than one field pass the list of fields to match separated | 1795 To match more than one field pass the list of fields to match separated |
1756 by spaces (e.g. ``author description``). | 1796 by spaces (e.g. ``author description``). |
1757 | 1797 |
1857 return True | 1897 return True |
1858 return False | 1898 return False |
1859 | 1899 |
1860 return subset.filter(matches) | 1900 return subset.filter(matches) |
1861 | 1901 |
1902 @predicate('reverse(set)') | |
1862 def reverse(repo, subset, x): | 1903 def reverse(repo, subset, x): |
1863 """``reverse(set)`` | 1904 """Reverse order of set. |
1864 Reverse order of set. | |
1865 """ | 1905 """ |
1866 l = getset(repo, subset, x) | 1906 l = getset(repo, subset, x) |
1867 l.reverse() | 1907 l.reverse() |
1868 return l | 1908 return l |
1869 | 1909 |
1910 @predicate('roots(set)') | |
1870 def roots(repo, subset, x): | 1911 def roots(repo, subset, x): |
1871 """``roots(set)`` | 1912 """Changesets in set with no parent changeset in set. |
1872 Changesets in set with no parent changeset in set. | |
1873 """ | 1913 """ |
1874 s = getset(repo, fullreposet(repo), x) | 1914 s = getset(repo, fullreposet(repo), x) |
1875 parents = repo.changelog.parentrevs | 1915 parents = repo.changelog.parentrevs |
1876 def filter(r): | 1916 def filter(r): |
1877 for p in parents(r): | 1917 for p in parents(r): |
1878 if 0 <= p and p in s: | 1918 if 0 <= p and p in s: |
1879 return False | 1919 return False |
1880 return True | 1920 return True |
1881 return subset & s.filter(filter) | 1921 return subset & s.filter(filter) |
1882 | 1922 |
1923 @predicate('sort(set[, [-]key...])') | |
1883 def sort(repo, subset, x): | 1924 def sort(repo, subset, x): |
1884 """``sort(set[, [-]key...])`` | 1925 """Sort set by keys. The default sort order is ascending, specify a key |
1885 Sort set by keys. The default sort order is ascending, specify a key | |
1886 as ``-key`` to sort in descending order. | 1926 as ``-key`` to sort in descending order. |
1887 | 1927 |
1888 The keys can be: | 1928 The keys can be: |
1889 | 1929 |
1890 - ``rev`` for the revision number, | 1930 - ``rev`` for the revision number, |
1941 e.append(r) | 1981 e.append(r) |
1942 l.append(e) | 1982 l.append(e) |
1943 l.sort() | 1983 l.sort() |
1944 return baseset([e[-1] for e in l]) | 1984 return baseset([e[-1] for e in l]) |
1945 | 1985 |
1986 @predicate('subrepo([pattern])') | |
1946 def subrepo(repo, subset, x): | 1987 def subrepo(repo, subset, x): |
1947 """``subrepo([pattern])`` | 1988 """Changesets that add, modify or remove the given subrepo. If no subrepo |
1948 Changesets that add, modify or remove the given subrepo. If no subrepo | |
1949 pattern is named, any subrepo changes are returned. | 1989 pattern is named, any subrepo changes are returned. |
1950 """ | 1990 """ |
1951 # i18n: "subrepo" is a keyword | 1991 # i18n: "subrepo" is a keyword |
1952 args = getargs(x, 0, 1, _('subrepo takes at most one argument')) | 1992 args = getargs(x, 0, 1, _('subrepo takes at most one argument')) |
1953 if len(args) != 0: | 1993 if len(args) != 0: |
1990 kind, pattern, matcher = util.stringmatcher(pattern) | 2030 kind, pattern, matcher = util.stringmatcher(pattern) |
1991 if kind == 'literal': | 2031 if kind == 'literal': |
1992 matcher = lambda s: pattern in s | 2032 matcher = lambda s: pattern in s |
1993 return kind, pattern, matcher | 2033 return kind, pattern, matcher |
1994 | 2034 |
2035 @predicate('tag([name])') | |
1995 def tag(repo, subset, x): | 2036 def tag(repo, subset, x): |
1996 """``tag([name])`` | 2037 """The specified tag by name, or all tagged revisions if no name is given. |
1997 The specified tag by name, or all tagged revisions if no name is given. | |
1998 | 2038 |
1999 If `name` starts with `re:`, the remainder of the name is treated as | 2039 If `name` starts with `re:`, the remainder of the name is treated as |
2000 a regular expression. To match a tag that actually starts with `re:`, | 2040 a regular expression. To match a tag that actually starts with `re:`, |
2001 use the prefix `literal:`. | 2041 use the prefix `literal:`. |
2002 """ | 2042 """ |
2019 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)]) | 2059 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)]) |
2020 else: | 2060 else: |
2021 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip']) | 2061 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip']) |
2022 return subset & s | 2062 return subset & s |
2023 | 2063 |
2064 @predicate('tagged') | |
2024 def tagged(repo, subset, x): | 2065 def tagged(repo, subset, x): |
2025 return tag(repo, subset, x) | 2066 return tag(repo, subset, x) |
2026 | 2067 |
2068 @predicate('unstable()') | |
2027 def unstable(repo, subset, x): | 2069 def unstable(repo, subset, x): |
2028 """``unstable()`` | 2070 """Non-obsolete changesets with obsolete ancestors. |
2029 Non-obsolete changesets with obsolete ancestors. | |
2030 """ | 2071 """ |
2031 # i18n: "unstable" is a keyword | 2072 # i18n: "unstable" is a keyword |
2032 getargs(x, 0, 0, _("unstable takes no arguments")) | 2073 getargs(x, 0, 0, _("unstable takes no arguments")) |
2033 unstables = obsmod.getrevs(repo, 'unstable') | 2074 unstables = obsmod.getrevs(repo, 'unstable') |
2034 return subset & unstables | 2075 return subset & unstables |
2035 | 2076 |
2036 | 2077 |
2078 @predicate('user(string)') | |
2037 def user(repo, subset, x): | 2079 def user(repo, subset, x): |
2038 """``user(string)`` | 2080 """User name contains string. The match is case-insensitive. |
2039 User name contains string. The match is case-insensitive. | |
2040 | 2081 |
2041 If `string` starts with `re:`, the remainder of the string is treated as | 2082 If `string` starts with `re:`, the remainder of the string is treated as |
2042 a regular expression. To match a user that actually contains `re:`, use | 2083 a regular expression. To match a user that actually contains `re:`, use |
2043 the prefix `literal:`. | 2084 the prefix `literal:`. |
2044 """ | 2085 """ |
2045 return author(repo, subset, x) | 2086 return author(repo, subset, x) |
2046 | 2087 |
2047 # experimental | 2088 # experimental |
2089 @predicate('wdir') | |
2048 def wdir(repo, subset, x): | 2090 def wdir(repo, subset, x): |
2049 # i18n: "wdir" is a keyword | 2091 # i18n: "wdir" is a keyword |
2050 getargs(x, 0, 0, _("wdir takes no arguments")) | 2092 getargs(x, 0, 0, _("wdir takes no arguments")) |
2051 if node.wdirrev in subset or isinstance(subset, fullreposet): | 2093 if node.wdirrev in subset or isinstance(subset, fullreposet): |
2052 return baseset([node.wdirrev]) | 2094 return baseset([node.wdirrev]) |
2053 return baseset() | 2095 return baseset() |
2054 | 2096 |
2055 # for internal use | 2097 # for internal use |
2098 @predicate('_list') | |
2056 def _list(repo, subset, x): | 2099 def _list(repo, subset, x): |
2057 s = getstring(x, "internal error") | 2100 s = getstring(x, "internal error") |
2058 if not s: | 2101 if not s: |
2059 return baseset() | 2102 return baseset() |
2060 # remove duplicates here. it's difficult for caller to deduplicate sets | 2103 # remove duplicates here. it's difficult for caller to deduplicate sets |
2080 ls.append(r) | 2123 ls.append(r) |
2081 seen.add(r) | 2124 seen.add(r) |
2082 return baseset(ls) | 2125 return baseset(ls) |
2083 | 2126 |
2084 # for internal use | 2127 # for internal use |
2128 @predicate('_intlist') | |
2085 def _intlist(repo, subset, x): | 2129 def _intlist(repo, subset, x): |
2086 s = getstring(x, "internal error") | 2130 s = getstring(x, "internal error") |
2087 if not s: | 2131 if not s: |
2088 return baseset() | 2132 return baseset() |
2089 ls = [int(r) for r in s.split('\0')] | 2133 ls = [int(r) for r in s.split('\0')] |
2090 s = subset | 2134 s = subset |
2091 return baseset([r for r in ls if r in s]) | 2135 return baseset([r for r in ls if r in s]) |
2092 | 2136 |
2093 # for internal use | 2137 # for internal use |
2138 @predicate('_hexlist') | |
2094 def _hexlist(repo, subset, x): | 2139 def _hexlist(repo, subset, x): |
2095 s = getstring(x, "internal error") | 2140 s = getstring(x, "internal error") |
2096 if not s: | 2141 if not s: |
2097 return baseset() | 2142 return baseset() |
2098 cl = repo.changelog | 2143 cl = repo.changelog |
2099 ls = [cl.rev(node.bin(r)) for r in s.split('\0')] | 2144 ls = [cl.rev(node.bin(r)) for r in s.split('\0')] |
2100 s = subset | 2145 s = subset |
2101 return baseset([r for r in ls if r in s]) | 2146 return baseset([r for r in ls if r in s]) |
2102 | |
2103 symbols = { | |
2104 "_destupdate": _destupdate, | |
2105 "_destmerge": _destmerge, | |
2106 "adds": adds, | |
2107 "all": getall, | |
2108 "ancestor": ancestor, | |
2109 "ancestors": ancestors, | |
2110 "_firstancestors": _firstancestors, | |
2111 "author": author, | |
2112 "bisect": bisect, | |
2113 "bisected": bisected, | |
2114 "bookmark": bookmark, | |
2115 "branch": branch, | |
2116 "branchpoint": branchpoint, | |
2117 "bumped": bumped, | |
2118 "bundle": bundle, | |
2119 "children": children, | |
2120 "closed": closed, | |
2121 "contains": contains, | |
2122 "converted": converted, | |
2123 "date": date, | |
2124 "desc": desc, | |
2125 "descendants": descendants, | |
2126 "_firstdescendants": _firstdescendants, | |
2127 "destination": destination, | |
2128 "divergent": divergent, | |
2129 "draft": draft, | |
2130 "extinct": extinct, | |
2131 "extra": extra, | |
2132 "file": hasfile, | |
2133 "filelog": filelog, | |
2134 "first": first, | |
2135 "follow": follow, | |
2136 "_followfirst": _followfirst, | |
2137 "grep": grep, | |
2138 "head": head, | |
2139 "heads": heads, | |
2140 "hidden": hidden, | |
2141 "id": node_, | |
2142 "keyword": keyword, | |
2143 "last": last, | |
2144 "limit": limit, | |
2145 "_matchfiles": _matchfiles, | |
2146 "max": maxrev, | |
2147 "merge": merge, | |
2148 "min": minrev, | |
2149 "modifies": modifies, | |
2150 "named": named, | |
2151 "obsolete": obsolete, | |
2152 "only": only, | |
2153 "origin": origin, | |
2154 "outgoing": outgoing, | |
2155 "p1": p1, | |
2156 "p2": p2, | |
2157 "parents": parents, | |
2158 "present": present, | |
2159 "public": public, | |
2160 "_notpublic": _notpublic, | |
2161 "remote": remote, | |
2162 "removes": removes, | |
2163 "rev": rev, | |
2164 "reverse": reverse, | |
2165 "roots": roots, | |
2166 "sort": sort, | |
2167 "secret": secret, | |
2168 "subrepo": subrepo, | |
2169 "matching": matching, | |
2170 "tag": tag, | |
2171 "tagged": tagged, | |
2172 "user": user, | |
2173 "unstable": unstable, | |
2174 "wdir": wdir, | |
2175 "_list": _list, | |
2176 "_intlist": _intlist, | |
2177 "_hexlist": _hexlist, | |
2178 } | |
2179 | 2147 |
2180 # symbols which can't be used for a DoS attack for any given input | 2148 # symbols which can't be used for a DoS attack for any given input |
2181 # (e.g. those which accept regexes as plain strings shouldn't be included) | 2149 # (e.g. those which accept regexes as plain strings shouldn't be included) |
2182 # functions that just return a lot of changesets (like all) don't count here | 2150 # functions that just return a lot of changesets (like all) don't count here |
2183 safesymbols = set([ | 2151 safesymbols = set([ |