527 def _destmerge(repo, subset, x): |
542 def _destmerge(repo, subset, x): |
528 # experimental revset for merge destination |
543 # experimental revset for merge destination |
529 getargs(x, 0, 0, _("_mergedefaultdest takes no arguments")) |
544 getargs(x, 0, 0, _("_mergedefaultdest takes no arguments")) |
530 return subset & baseset([destutil.destmerge(repo)]) |
545 return subset & baseset([destutil.destmerge(repo)]) |
531 |
546 |
532 @predicate('adds(pattern)') |
547 @predicate('adds(pattern)', safe=True) |
533 def adds(repo, subset, x): |
548 def adds(repo, subset, x): |
534 """Changesets that add a file matching pattern. |
549 """Changesets that add a file matching pattern. |
535 |
550 |
536 The pattern without explicit kind like ``glob:`` is expected to be |
551 The pattern without explicit kind like ``glob:`` is expected to be |
537 relative to the current directory and match against a file or a |
552 relative to the current directory and match against a file or a |
539 """ |
554 """ |
540 # i18n: "adds" is a keyword |
555 # i18n: "adds" is a keyword |
541 pat = getstring(x, _("adds requires a pattern")) |
556 pat = getstring(x, _("adds requires a pattern")) |
542 return checkstatus(repo, subset, pat, 1) |
557 return checkstatus(repo, subset, pat, 1) |
543 |
558 |
544 @predicate('ancestor(*changeset)') |
559 @predicate('ancestor(*changeset)', safe=True) |
545 def ancestor(repo, subset, x): |
560 def ancestor(repo, subset, x): |
546 """A greatest common ancestor of the changesets. |
561 """A greatest common ancestor of the changesets. |
547 |
562 |
548 Accepts 0 or more changesets. |
563 Accepts 0 or more changesets. |
549 Will return empty list when passed no args. |
564 Will return empty list when passed no args. |
571 if not heads: |
586 if not heads: |
572 return baseset() |
587 return baseset() |
573 s = _revancestors(repo, heads, followfirst) |
588 s = _revancestors(repo, heads, followfirst) |
574 return subset & s |
589 return subset & s |
575 |
590 |
576 @predicate('ancestors(set)') |
591 @predicate('ancestors(set)', safe=True) |
577 def ancestors(repo, subset, x): |
592 def ancestors(repo, subset, x): |
578 """Changesets that are ancestors of a changeset in set. |
593 """Changesets that are ancestors of a changeset in set. |
579 """ |
594 """ |
580 return _ancestors(repo, subset, x) |
595 return _ancestors(repo, subset, x) |
581 |
596 |
582 @predicate('_firstancestors') |
597 @predicate('_firstancestors', safe=True) |
583 def _firstancestors(repo, subset, x): |
598 def _firstancestors(repo, subset, x): |
584 # ``_firstancestors(set)`` |
599 # ``_firstancestors(set)`` |
585 # Like ``ancestors(set)`` but follows only the first parents. |
600 # Like ``ancestors(set)`` but follows only the first parents. |
586 return _ancestors(repo, subset, x, followfirst=True) |
601 return _ancestors(repo, subset, x, followfirst=True) |
587 |
602 |
600 for i in range(n): |
615 for i in range(n): |
601 r = cl.parentrevs(r)[0] |
616 r = cl.parentrevs(r)[0] |
602 ps.add(r) |
617 ps.add(r) |
603 return subset & ps |
618 return subset & ps |
604 |
619 |
605 @predicate('author(string)') |
620 @predicate('author(string)', safe=True) |
606 def author(repo, subset, x): |
621 def author(repo, subset, x): |
607 """Alias for ``user(string)``. |
622 """Alias for ``user(string)``. |
608 """ |
623 """ |
609 # i18n: "author" is a keyword |
624 # i18n: "author" is a keyword |
610 n = encoding.lower(getstring(x, _("author requires a string"))) |
625 n = encoding.lower(getstring(x, _("author requires a string"))) |
611 kind, pattern, matcher = _substringmatcher(n) |
626 kind, pattern, matcher = _substringmatcher(n) |
612 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user()))) |
627 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user()))) |
613 |
628 |
614 @predicate('bisect(string)') |
629 @predicate('bisect(string)', safe=True) |
615 def bisect(repo, subset, x): |
630 def bisect(repo, subset, x): |
616 """Changesets marked in the specified bisect status: |
631 """Changesets marked in the specified bisect status: |
617 |
632 |
618 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip |
633 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip |
619 - ``goods``, ``bads`` : csets topologically good/bad |
634 - ``goods``, ``bads`` : csets topologically good/bad |
628 state = set(hbisect.get(repo, status)) |
643 state = set(hbisect.get(repo, status)) |
629 return subset & state |
644 return subset & state |
630 |
645 |
631 # Backward-compatibility |
646 # Backward-compatibility |
632 # - no help entry so that we do not advertise it any more |
647 # - no help entry so that we do not advertise it any more |
633 @predicate('bisected') |
648 @predicate('bisected', safe=True) |
634 def bisected(repo, subset, x): |
649 def bisected(repo, subset, x): |
635 return bisect(repo, subset, x) |
650 return bisect(repo, subset, x) |
636 |
651 |
637 @predicate('bookmark([name])') |
652 @predicate('bookmark([name])', safe=True) |
638 def bookmark(repo, subset, x): |
653 def bookmark(repo, subset, x): |
639 """The named bookmark or all bookmarks. |
654 """The named bookmark or all bookmarks. |
640 |
655 |
641 If `name` starts with `re:`, the remainder of the name is treated as |
656 If `name` starts with `re:`, the remainder of the name is treated as |
642 a regular expression. To match a bookmark that actually starts with `re:`, |
657 a regular expression. To match a bookmark that actually starts with `re:`, |
707 for r in s: |
722 for r in s: |
708 b.add(getbi(r)[0]) |
723 b.add(getbi(r)[0]) |
709 c = s.__contains__ |
724 c = s.__contains__ |
710 return subset.filter(lambda r: c(r) or getbi(r)[0] in b) |
725 return subset.filter(lambda r: c(r) or getbi(r)[0] in b) |
711 |
726 |
712 @predicate('bumped()') |
727 @predicate('bumped()', safe=True) |
713 def bumped(repo, subset, x): |
728 def bumped(repo, subset, x): |
714 """Mutable changesets marked as successors of public changesets. |
729 """Mutable changesets marked as successors of public changesets. |
715 |
730 |
716 Only non-public and non-obsolete changesets can be `bumped`. |
731 Only non-public and non-obsolete changesets can be `bumped`. |
717 """ |
732 """ |
718 # i18n: "bumped" is a keyword |
733 # i18n: "bumped" is a keyword |
719 getargs(x, 0, 0, _("bumped takes no arguments")) |
734 getargs(x, 0, 0, _("bumped takes no arguments")) |
720 bumped = obsmod.getrevs(repo, 'bumped') |
735 bumped = obsmod.getrevs(repo, 'bumped') |
721 return subset & bumped |
736 return subset & bumped |
722 |
737 |
723 @predicate('bundle()') |
738 @predicate('bundle()', safe=True) |
724 def bundle(repo, subset, x): |
739 def bundle(repo, subset, x): |
725 """Changesets in the bundle. |
740 """Changesets in the bundle. |
726 |
741 |
727 Bundle must be specified by the -R option.""" |
742 Bundle must be specified by the -R option.""" |
728 |
743 |
778 cs.add(r) |
793 cs.add(r) |
779 # XXX using a set to feed the baseset is wrong. Sets are not ordered. |
794 # XXX using a set to feed the baseset is wrong. Sets are not ordered. |
780 # This does not break because of other fullreposet misbehavior. |
795 # This does not break because of other fullreposet misbehavior. |
781 return baseset(cs) |
796 return baseset(cs) |
782 |
797 |
783 @predicate('children(set)') |
798 @predicate('children(set)', safe=True) |
784 def children(repo, subset, x): |
799 def children(repo, subset, x): |
785 """Child changesets of changesets in set. |
800 """Child changesets of changesets in set. |
786 """ |
801 """ |
787 s = getset(repo, fullreposet(repo), x) |
802 s = getset(repo, fullreposet(repo), x) |
788 cs = _children(repo, subset, s) |
803 cs = _children(repo, subset, s) |
789 return subset & cs |
804 return subset & cs |
790 |
805 |
791 @predicate('closed()') |
806 @predicate('closed()', safe=True) |
792 def closed(repo, subset, x): |
807 def closed(repo, subset, x): |
793 """Changeset is closed. |
808 """Changeset is closed. |
794 """ |
809 """ |
795 # i18n: "closed" is a keyword |
810 # i18n: "closed" is a keyword |
796 getargs(x, 0, 0, _("closed takes no arguments")) |
811 getargs(x, 0, 0, _("closed takes no arguments")) |
843 source = repo[r].extra().get('convert_revision', None) |
858 source = repo[r].extra().get('convert_revision', None) |
844 return source is not None and (rev is None or source.startswith(rev)) |
859 return source is not None and (rev is None or source.startswith(rev)) |
845 |
860 |
846 return subset.filter(lambda r: _matchvalue(r)) |
861 return subset.filter(lambda r: _matchvalue(r)) |
847 |
862 |
848 @predicate('date(interval)') |
863 @predicate('date(interval)', safe=True) |
849 def date(repo, subset, x): |
864 def date(repo, subset, x): |
850 """Changesets within the interval, see :hg:`help dates`. |
865 """Changesets within the interval, see :hg:`help dates`. |
851 """ |
866 """ |
852 # i18n: "date" is a keyword |
867 # i18n: "date" is a keyword |
853 ds = getstring(x, _("date requires a string")) |
868 ds = getstring(x, _("date requires a string")) |
854 dm = util.matchdate(ds) |
869 dm = util.matchdate(ds) |
855 return subset.filter(lambda x: dm(repo[x].date()[0])) |
870 return subset.filter(lambda x: dm(repo[x].date()[0])) |
856 |
871 |
857 @predicate('desc(string)') |
872 @predicate('desc(string)', safe=True) |
858 def desc(repo, subset, x): |
873 def desc(repo, subset, x): |
859 """Search commit message for string. The match is case-insensitive. |
874 """Search commit message for string. The match is case-insensitive. |
860 """ |
875 """ |
861 # i18n: "desc" is a keyword |
876 # i18n: "desc" is a keyword |
862 ds = encoding.lower(getstring(x, _("desc requires a string"))) |
877 ds = encoding.lower(getstring(x, _("desc requires a string"))) |
884 result.sort(reverse=True) |
899 result.sort(reverse=True) |
885 else: |
900 else: |
886 result = subset & result |
901 result = subset & result |
887 return result |
902 return result |
888 |
903 |
889 @predicate('descendants(set)') |
904 @predicate('descendants(set)', safe=True) |
890 def descendants(repo, subset, x): |
905 def descendants(repo, subset, x): |
891 """Changesets which are descendants of changesets in set. |
906 """Changesets which are descendants of changesets in set. |
892 """ |
907 """ |
893 return _descendants(repo, subset, x) |
908 return _descendants(repo, subset, x) |
894 |
909 |
895 @predicate('_firstdescendants') |
910 @predicate('_firstdescendants', safe=True) |
896 def _firstdescendants(repo, subset, x): |
911 def _firstdescendants(repo, subset, x): |
897 # ``_firstdescendants(set)`` |
912 # ``_firstdescendants(set)`` |
898 # Like ``descendants(set)`` but follows only the first parents. |
913 # Like ``descendants(set)`` but follows only the first parents. |
899 return _descendants(repo, subset, x, followfirst=True) |
914 return _descendants(repo, subset, x, followfirst=True) |
900 |
915 |
901 @predicate('destination([set])') |
916 @predicate('destination([set])', safe=True) |
902 def destination(repo, subset, x): |
917 def destination(repo, subset, x): |
903 """Changesets that were created by a graft, transplant or rebase operation, |
918 """Changesets that were created by a graft, transplant or rebase operation, |
904 with the given revisions specified as the source. Omitting the optional set |
919 with the given revisions specified as the source. Omitting the optional set |
905 is the same as passing all(). |
920 is the same as passing all(). |
906 """ |
921 """ |
940 r = src |
955 r = src |
941 src = _getrevsource(repo, r) |
956 src = _getrevsource(repo, r) |
942 |
957 |
943 return subset.filter(dests.__contains__) |
958 return subset.filter(dests.__contains__) |
944 |
959 |
945 @predicate('divergent()') |
960 @predicate('divergent()', safe=True) |
946 def divergent(repo, subset, x): |
961 def divergent(repo, subset, x): |
947 """ |
962 """ |
948 Final successors of changesets with an alternative set of final successors. |
963 Final successors of changesets with an alternative set of final successors. |
949 """ |
964 """ |
950 # i18n: "divergent" is a keyword |
965 # i18n: "divergent" is a keyword |
951 getargs(x, 0, 0, _("divergent takes no arguments")) |
966 getargs(x, 0, 0, _("divergent takes no arguments")) |
952 divergent = obsmod.getrevs(repo, 'divergent') |
967 divergent = obsmod.getrevs(repo, 'divergent') |
953 return subset & divergent |
968 return subset & divergent |
954 |
969 |
955 @predicate('extinct()') |
970 @predicate('extinct()', safe=True) |
956 def extinct(repo, subset, x): |
971 def extinct(repo, subset, x): |
957 """Obsolete changesets with obsolete descendants only. |
972 """Obsolete changesets with obsolete descendants only. |
958 """ |
973 """ |
959 # i18n: "extinct" is a keyword |
974 # i18n: "extinct" is a keyword |
960 getargs(x, 0, 0, _("extinct takes no arguments")) |
975 getargs(x, 0, 0, _("extinct takes no arguments")) |
961 extincts = obsmod.getrevs(repo, 'extinct') |
976 extincts = obsmod.getrevs(repo, 'extinct') |
962 return subset & extincts |
977 return subset & extincts |
963 |
978 |
964 @predicate('extra(label, [value])') |
979 @predicate('extra(label, [value])', safe=True) |
965 def extra(repo, subset, x): |
980 def extra(repo, subset, x): |
966 """Changesets with the given label in the extra metadata, with the given |
981 """Changesets with the given label in the extra metadata, with the given |
967 optional value. |
982 optional value. |
968 |
983 |
969 If `value` starts with `re:`, the remainder of the value is treated as |
984 If `value` starts with `re:`, the remainder of the value is treated as |
989 extra = repo[r].extra() |
1004 extra = repo[r].extra() |
990 return label in extra and (value is None or matcher(extra[label])) |
1005 return label in extra and (value is None or matcher(extra[label])) |
991 |
1006 |
992 return subset.filter(lambda r: _matchvalue(r)) |
1007 return subset.filter(lambda r: _matchvalue(r)) |
993 |
1008 |
994 @predicate('filelog(pattern)') |
1009 @predicate('filelog(pattern)', safe=True) |
995 def filelog(repo, subset, x): |
1010 def filelog(repo, subset, x): |
996 """Changesets connected to the specified filelog. |
1011 """Changesets connected to the specified filelog. |
997 |
1012 |
998 For performance reasons, visits only revisions mentioned in the file-level |
1013 For performance reasons, visits only revisions mentioned in the file-level |
999 filelog, rather than filtering through all changesets (much faster, but |
1014 filelog, rather than filtering through all changesets (much faster, but |
1130 else: |
1145 else: |
1131 s = _revancestors(repo, baseset([c.rev()]), followfirst) |
1146 s = _revancestors(repo, baseset([c.rev()]), followfirst) |
1132 |
1147 |
1133 return subset & s |
1148 return subset & s |
1134 |
1149 |
1135 @predicate('follow([pattern])') |
1150 @predicate('follow([pattern])', safe=True) |
1136 def follow(repo, subset, x): |
1151 def follow(repo, subset, x): |
1137 """ |
1152 """ |
1138 An alias for ``::.`` (ancestors of the working directory's first parent). |
1153 An alias for ``::.`` (ancestors of the working directory's first parent). |
1139 If pattern is specified, the histories of files matching given |
1154 If pattern is specified, the histories of files matching given |
1140 pattern is followed, including copies. |
1155 pattern is followed, including copies. |
1141 """ |
1156 """ |
1142 return _follow(repo, subset, x, 'follow') |
1157 return _follow(repo, subset, x, 'follow') |
1143 |
1158 |
1144 @predicate('_followfirst') |
1159 @predicate('_followfirst', safe=True) |
1145 def _followfirst(repo, subset, x): |
1160 def _followfirst(repo, subset, x): |
1146 # ``followfirst([pattern])`` |
1161 # ``followfirst([pattern])`` |
1147 # Like ``follow([pattern])`` but follows only the first parent of |
1162 # Like ``follow([pattern])`` but follows only the first parent of |
1148 # every revisions or files revisions. |
1163 # every revisions or files revisions. |
1149 return _follow(repo, subset, x, '_followfirst', followfirst=True) |
1164 return _follow(repo, subset, x, '_followfirst', followfirst=True) |
1150 |
1165 |
1151 @predicate('all()') |
1166 @predicate('all()', safe=True) |
1152 def getall(repo, subset, x): |
1167 def getall(repo, subset, x): |
1153 """All changesets, the same as ``0:tip``. |
1168 """All changesets, the same as ``0:tip``. |
1154 """ |
1169 """ |
1155 # i18n: "all" is a keyword |
1170 # i18n: "all" is a keyword |
1156 getargs(x, 0, 0, _("all takes no arguments")) |
1171 getargs(x, 0, 0, _("all takes no arguments")) |
1254 """ |
1269 """ |
1255 # i18n: "file" is a keyword |
1270 # i18n: "file" is a keyword |
1256 pat = getstring(x, _("file requires a pattern")) |
1271 pat = getstring(x, _("file requires a pattern")) |
1257 return _matchfiles(repo, subset, ('string', 'p:' + pat)) |
1272 return _matchfiles(repo, subset, ('string', 'p:' + pat)) |
1258 |
1273 |
1259 @predicate('head()') |
1274 @predicate('head()', safe=True) |
1260 def head(repo, subset, x): |
1275 def head(repo, subset, x): |
1261 """Changeset is a named branch head. |
1276 """Changeset is a named branch head. |
1262 """ |
1277 """ |
1263 # i18n: "head" is a keyword |
1278 # i18n: "head" is a keyword |
1264 getargs(x, 0, 0, _("head takes no arguments")) |
1279 getargs(x, 0, 0, _("head takes no arguments")) |
1270 # This does not break because of other fullreposet misbehavior. |
1285 # This does not break because of other fullreposet misbehavior. |
1271 # XXX We should combine with subset first: 'subset & baseset(...)'. This is |
1286 # XXX We should combine with subset first: 'subset & baseset(...)'. This is |
1272 # necessary to ensure we preserve the order in subset. |
1287 # necessary to ensure we preserve the order in subset. |
1273 return baseset(hs) & subset |
1288 return baseset(hs) & subset |
1274 |
1289 |
1275 @predicate('heads(set)') |
1290 @predicate('heads(set)', safe=True) |
1276 def heads(repo, subset, x): |
1291 def heads(repo, subset, x): |
1277 """Members of set with no children in set. |
1292 """Members of set with no children in set. |
1278 """ |
1293 """ |
1279 s = getset(repo, subset, x) |
1294 s = getset(repo, subset, x) |
1280 ps = parents(repo, subset, x) |
1295 ps = parents(repo, subset, x) |
1281 return s - ps |
1296 return s - ps |
1282 |
1297 |
1283 @predicate('hidden()') |
1298 @predicate('hidden()', safe=True) |
1284 def hidden(repo, subset, x): |
1299 def hidden(repo, subset, x): |
1285 """Hidden changesets. |
1300 """Hidden changesets. |
1286 """ |
1301 """ |
1287 # i18n: "hidden" is a keyword |
1302 # i18n: "hidden" is a keyword |
1288 getargs(x, 0, 0, _("hidden takes no arguments")) |
1303 getargs(x, 0, 0, _("hidden takes no arguments")) |
1289 hiddenrevs = repoview.filterrevs(repo, 'visible') |
1304 hiddenrevs = repoview.filterrevs(repo, 'visible') |
1290 return subset & hiddenrevs |
1305 return subset & hiddenrevs |
1291 |
1306 |
1292 @predicate('keyword(string)') |
1307 @predicate('keyword(string)', safe=True) |
1293 def keyword(repo, subset, x): |
1308 def keyword(repo, subset, x): |
1294 """Search commit message, user name, and names of changed files for |
1309 """Search commit message, user name, and names of changed files for |
1295 string. The match is case-insensitive. |
1310 string. The match is case-insensitive. |
1296 """ |
1311 """ |
1297 # i18n: "keyword" is a keyword |
1312 # i18n: "keyword" is a keyword |
1302 return any(kw in encoding.lower(t) |
1317 return any(kw in encoding.lower(t) |
1303 for t in c.files() + [c.user(), c.description()]) |
1318 for t in c.files() + [c.user(), c.description()]) |
1304 |
1319 |
1305 return subset.filter(matches) |
1320 return subset.filter(matches) |
1306 |
1321 |
1307 @predicate('limit(set[, n[, offset]])') |
1322 @predicate('limit(set[, n[, offset]])', safe=True) |
1308 def limit(repo, subset, x): |
1323 def limit(repo, subset, x): |
1309 """First n members of set, defaulting to 1, starting from offset. |
1324 """First n members of set, defaulting to 1, starting from offset. |
1310 """ |
1325 """ |
1311 args = getargsdict(x, 'limit', 'set n offset') |
1326 args = getargsdict(x, 'limit', 'set n offset') |
1312 if 'set' not in args: |
1327 if 'set' not in args: |
1338 break |
1353 break |
1339 elif y in subset: |
1354 elif y in subset: |
1340 result.append(y) |
1355 result.append(y) |
1341 return baseset(result) |
1356 return baseset(result) |
1342 |
1357 |
1343 @predicate('last(set, [n])') |
1358 @predicate('last(set, [n])', safe=True) |
1344 def last(repo, subset, x): |
1359 def last(repo, subset, x): |
1345 """Last n members of set, defaulting to 1. |
1360 """Last n members of set, defaulting to 1. |
1346 """ |
1361 """ |
1347 # i18n: "last" is a keyword |
1362 # i18n: "last" is a keyword |
1348 l = getargs(x, 1, 2, _("last requires one or two arguments")) |
1363 l = getargs(x, 1, 2, _("last requires one or two arguments")) |
1379 # os.max() throws a ValueError when the collection is empty. |
1394 # os.max() throws a ValueError when the collection is empty. |
1380 # Same as python's max(). |
1395 # Same as python's max(). |
1381 pass |
1396 pass |
1382 return baseset() |
1397 return baseset() |
1383 |
1398 |
1384 @predicate('merge()') |
1399 @predicate('merge()', safe=True) |
1385 def merge(repo, subset, x): |
1400 def merge(repo, subset, x): |
1386 """Changeset is a merge changeset. |
1401 """Changeset is a merge changeset. |
1387 """ |
1402 """ |
1388 # i18n: "merge" is a keyword |
1403 # i18n: "merge" is a keyword |
1389 getargs(x, 0, 0, _("merge takes no arguments")) |
1404 getargs(x, 0, 0, _("merge takes no arguments")) |
1390 cl = repo.changelog |
1405 cl = repo.changelog |
1391 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1) |
1406 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1) |
1392 |
1407 |
1393 @predicate('branchpoint()') |
1408 @predicate('branchpoint()', safe=True) |
1394 def branchpoint(repo, subset, x): |
1409 def branchpoint(repo, subset, x): |
1395 """Changesets with more than one child. |
1410 """Changesets with more than one child. |
1396 """ |
1411 """ |
1397 # i18n: "branchpoint" is a keyword |
1412 # i18n: "branchpoint" is a keyword |
1398 getargs(x, 0, 0, _("branchpoint takes no arguments")) |
1413 getargs(x, 0, 0, _("branchpoint takes no arguments")) |
1407 for p in cl.parentrevs(r): |
1422 for p in cl.parentrevs(r): |
1408 if p >= baserev: |
1423 if p >= baserev: |
1409 parentscount[p - baserev] += 1 |
1424 parentscount[p - baserev] += 1 |
1410 return subset.filter(lambda r: parentscount[r - baserev] > 1) |
1425 return subset.filter(lambda r: parentscount[r - baserev] > 1) |
1411 |
1426 |
1412 @predicate('min(set)') |
1427 @predicate('min(set)', safe=True) |
1413 def minrev(repo, subset, x): |
1428 def minrev(repo, subset, x): |
1414 """Changeset with lowest revision number in set. |
1429 """Changeset with lowest revision number in set. |
1415 """ |
1430 """ |
1416 os = getset(repo, fullreposet(repo), x) |
1431 os = getset(repo, fullreposet(repo), x) |
1417 try: |
1432 try: |
1422 # os.min() throws a ValueError when the collection is empty. |
1437 # os.min() throws a ValueError when the collection is empty. |
1423 # Same as python's min(). |
1438 # Same as python's min(). |
1424 pass |
1439 pass |
1425 return baseset() |
1440 return baseset() |
1426 |
1441 |
1427 @predicate('modifies(pattern)') |
1442 @predicate('modifies(pattern)', safe=True) |
1428 def modifies(repo, subset, x): |
1443 def modifies(repo, subset, x): |
1429 """Changesets modifying files matched by pattern. |
1444 """Changesets modifying files matched by pattern. |
1430 |
1445 |
1431 The pattern without explicit kind like ``glob:`` is expected to be |
1446 The pattern without explicit kind like ``glob:`` is expected to be |
1432 relative to the current directory and match against a file or a |
1447 relative to the current directory and match against a file or a |
1472 names.update(repo[n].rev() for n in ns.nodes(repo, name)) |
1487 names.update(repo[n].rev() for n in ns.nodes(repo, name)) |
1473 |
1488 |
1474 names -= set([node.nullrev]) |
1489 names -= set([node.nullrev]) |
1475 return subset & names |
1490 return subset & names |
1476 |
1491 |
1477 @predicate('id(string)') |
1492 @predicate('id(string)', safe=True) |
1478 def node_(repo, subset, x): |
1493 def node_(repo, subset, x): |
1479 """Revision non-ambiguously specified by the given hex string prefix. |
1494 """Revision non-ambiguously specified by the given hex string prefix. |
1480 """ |
1495 """ |
1481 # i18n: "id" is a keyword |
1496 # i18n: "id" is a keyword |
1482 l = getargs(x, 1, 1, _("id requires one argument")) |
1497 l = getargs(x, 1, 1, _("id requires one argument")) |
1496 if rn is None: |
1511 if rn is None: |
1497 return baseset() |
1512 return baseset() |
1498 result = baseset([rn]) |
1513 result = baseset([rn]) |
1499 return result & subset |
1514 return result & subset |
1500 |
1515 |
1501 @predicate('obsolete()') |
1516 @predicate('obsolete()', safe=True) |
1502 def obsolete(repo, subset, x): |
1517 def obsolete(repo, subset, x): |
1503 """Mutable changeset with a newer version.""" |
1518 """Mutable changeset with a newer version.""" |
1504 # i18n: "obsolete" is a keyword |
1519 # i18n: "obsolete" is a keyword |
1505 getargs(x, 0, 0, _("obsolete takes no arguments")) |
1520 getargs(x, 0, 0, _("obsolete takes no arguments")) |
1506 obsoletes = obsmod.getrevs(repo, 'obsolete') |
1521 obsoletes = obsmod.getrevs(repo, 'obsolete') |
1507 return subset & obsoletes |
1522 return subset & obsoletes |
1508 |
1523 |
1509 @predicate('only(set, [set])') |
1524 @predicate('only(set, [set])', safe=True) |
1510 def only(repo, subset, x): |
1525 def only(repo, subset, x): |
1511 """Changesets that are ancestors of the first set that are not ancestors |
1526 """Changesets that are ancestors of the first set that are not ancestors |
1512 of any other head in the repo. If a second set is specified, the result |
1527 of any other head in the repo. If a second set is specified, the result |
1513 is ancestors of the first set that are not ancestors of the second set |
1528 is ancestors of the first set that are not ancestors of the second set |
1514 (i.e. ::<set1> - ::<set2>). |
1529 (i.e. ::<set1> - ::<set2>). |
1530 results = set(cl.findmissingrevs(common=exclude, heads=include)) |
1545 results = set(cl.findmissingrevs(common=exclude, heads=include)) |
1531 # XXX we should turn this into a baseset instead of a set, smartset may do |
1546 # XXX we should turn this into a baseset instead of a set, smartset may do |
1532 # some optimisations from the fact this is a baseset. |
1547 # some optimisations from the fact this is a baseset. |
1533 return subset & results |
1548 return subset & results |
1534 |
1549 |
1535 @predicate('origin([set])') |
1550 @predicate('origin([set])', safe=True) |
1536 def origin(repo, subset, x): |
1551 def origin(repo, subset, x): |
1537 """ |
1552 """ |
1538 Changesets that were specified as a source for the grafts, transplants or |
1553 Changesets that were specified as a source for the grafts, transplants or |
1539 rebases that created the given revisions. Omitting the optional set is the |
1554 rebases that created the given revisions. Omitting the optional set is the |
1540 same as passing all(). If a changeset created by these operations is itself |
1555 same as passing all(). If a changeset created by these operations is itself |
1562 o -= set([None]) |
1577 o -= set([None]) |
1563 # XXX we should turn this into a baseset instead of a set, smartset may do |
1578 # XXX we should turn this into a baseset instead of a set, smartset may do |
1564 # some optimisations from the fact this is a baseset. |
1579 # some optimisations from the fact this is a baseset. |
1565 return subset & o |
1580 return subset & o |
1566 |
1581 |
1567 @predicate('outgoing([path])') |
1582 @predicate('outgoing([path])', safe=True) |
1568 def outgoing(repo, subset, x): |
1583 def outgoing(repo, subset, x): |
1569 """Changesets not found in the specified destination repository, or the |
1584 """Changesets not found in the specified destination repository, or the |
1570 default push location. |
1585 default push location. |
1571 """ |
1586 """ |
1572 # Avoid cycles. |
1587 # Avoid cycles. |
1608 ps -= set([node.nullrev]) |
1623 ps -= set([node.nullrev]) |
1609 # XXX we should turn this into a baseset instead of a set, smartset may do |
1624 # XXX we should turn this into a baseset instead of a set, smartset may do |
1610 # some optimisations from the fact this is a baseset. |
1625 # some optimisations from the fact this is a baseset. |
1611 return subset & ps |
1626 return subset & ps |
1612 |
1627 |
1613 @predicate('p2([set])') |
1628 @predicate('p2([set])', safe=True) |
1614 def p2(repo, subset, x): |
1629 def p2(repo, subset, x): |
1615 """Second parent of changesets in set, or the working directory. |
1630 """Second parent of changesets in set, or the working directory. |
1616 """ |
1631 """ |
1617 if x is None: |
1632 if x is None: |
1618 ps = repo[x].parents() |
1633 ps = repo[x].parents() |
1631 ps -= set([node.nullrev]) |
1646 ps -= set([node.nullrev]) |
1632 # XXX we should turn this into a baseset instead of a set, smartset may do |
1647 # XXX we should turn this into a baseset instead of a set, smartset may do |
1633 # some optimisations from the fact this is a baseset. |
1648 # some optimisations from the fact this is a baseset. |
1634 return subset & ps |
1649 return subset & ps |
1635 |
1650 |
1636 @predicate('parents([set])') |
1651 @predicate('parents([set])', safe=True) |
1637 def parents(repo, subset, x): |
1652 def parents(repo, subset, x): |
1638 """ |
1653 """ |
1639 The set of all parents for all changesets in set, or the working directory. |
1654 The set of all parents for all changesets in set, or the working directory. |
1640 """ |
1655 """ |
1641 if x is None: |
1656 if x is None: |
1664 else: |
1679 else: |
1665 phase = repo._phasecache.phase |
1680 phase = repo._phasecache.phase |
1666 condition = lambda r: phase(repo, r) == target |
1681 condition = lambda r: phase(repo, r) == target |
1667 return subset.filter(condition, cache=False) |
1682 return subset.filter(condition, cache=False) |
1668 |
1683 |
1669 @predicate('draft()') |
1684 @predicate('draft()', safe=True) |
1670 def draft(repo, subset, x): |
1685 def draft(repo, subset, x): |
1671 """Changeset in draft phase.""" |
1686 """Changeset in draft phase.""" |
1672 # i18n: "draft" is a keyword |
1687 # i18n: "draft" is a keyword |
1673 getargs(x, 0, 0, _("draft takes no arguments")) |
1688 getargs(x, 0, 0, _("draft takes no arguments")) |
1674 target = phases.draft |
1689 target = phases.draft |
1675 return _phase(repo, subset, target) |
1690 return _phase(repo, subset, target) |
1676 |
1691 |
1677 @predicate('secret()') |
1692 @predicate('secret()', safe=True) |
1678 def secret(repo, subset, x): |
1693 def secret(repo, subset, x): |
1679 """Changeset in secret phase.""" |
1694 """Changeset in secret phase.""" |
1680 # i18n: "secret" is a keyword |
1695 # i18n: "secret" is a keyword |
1681 getargs(x, 0, 0, _("secret takes no arguments")) |
1696 getargs(x, 0, 0, _("secret takes no arguments")) |
1682 target = phases.secret |
1697 target = phases.secret |
1705 parents = cl.parentrevs(r) |
1720 parents = cl.parentrevs(r) |
1706 if len(parents) > 1: |
1721 if len(parents) > 1: |
1707 ps.add(parents[1]) |
1722 ps.add(parents[1]) |
1708 return subset & ps |
1723 return subset & ps |
1709 |
1724 |
1710 @predicate('present(set)') |
1725 @predicate('present(set)', safe=True) |
1711 def present(repo, subset, x): |
1726 def present(repo, subset, x): |
1712 """An empty set, if any revision in set isn't found; otherwise, |
1727 """An empty set, if any revision in set isn't found; otherwise, |
1713 all revisions in set. |
1728 all revisions in set. |
1714 |
1729 |
1715 If any of specified revisions is not present in the local repository, |
1730 If any of specified revisions is not present in the local repository, |
1720 return getset(repo, subset, x) |
1735 return getset(repo, subset, x) |
1721 except error.RepoLookupError: |
1736 except error.RepoLookupError: |
1722 return baseset() |
1737 return baseset() |
1723 |
1738 |
1724 # for internal use |
1739 # for internal use |
1725 @predicate('_notpublic') |
1740 @predicate('_notpublic', safe=True) |
1726 def _notpublic(repo, subset, x): |
1741 def _notpublic(repo, subset, x): |
1727 getargs(x, 0, 0, "_notpublic takes no arguments") |
1742 getargs(x, 0, 0, "_notpublic takes no arguments") |
1728 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded |
1743 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded |
1729 if repo._phasecache._phasesets: |
1744 if repo._phasecache._phasesets: |
1730 s = set() |
1745 s = set() |
1737 phase = repo._phasecache.phase |
1752 phase = repo._phasecache.phase |
1738 target = phases.public |
1753 target = phases.public |
1739 condition = lambda r: phase(repo, r) != target |
1754 condition = lambda r: phase(repo, r) != target |
1740 return subset.filter(condition, cache=False) |
1755 return subset.filter(condition, cache=False) |
1741 |
1756 |
1742 @predicate('public()') |
1757 @predicate('public()', safe=True) |
1743 def public(repo, subset, x): |
1758 def public(repo, subset, x): |
1744 """Changeset in public phase.""" |
1759 """Changeset in public phase.""" |
1745 # i18n: "public" is a keyword |
1760 # i18n: "public" is a keyword |
1746 getargs(x, 0, 0, _("public takes no arguments")) |
1761 getargs(x, 0, 0, _("public takes no arguments")) |
1747 phase = repo._phasecache.phase |
1762 phase = repo._phasecache.phase |
1748 target = phases.public |
1763 target = phases.public |
1749 condition = lambda r: phase(repo, r) == target |
1764 condition = lambda r: phase(repo, r) == target |
1750 return subset.filter(condition, cache=False) |
1765 return subset.filter(condition, cache=False) |
1751 |
1766 |
1752 @predicate('remote([id [,path]])') |
1767 @predicate('remote([id [,path]])', safe=True) |
1753 def remote(repo, subset, x): |
1768 def remote(repo, subset, x): |
1754 """Local revision that corresponds to the given identifier in a |
1769 """Local revision that corresponds to the given identifier in a |
1755 remote repository, if present. Here, the '.' identifier is a |
1770 remote repository, if present. Here, the '.' identifier is a |
1756 synonym for the current local branch. |
1771 synonym for the current local branch. |
1757 """ |
1772 """ |
1794 """ |
1809 """ |
1795 # i18n: "removes" is a keyword |
1810 # i18n: "removes" is a keyword |
1796 pat = getstring(x, _("removes requires a pattern")) |
1811 pat = getstring(x, _("removes requires a pattern")) |
1797 return checkstatus(repo, subset, pat, 2) |
1812 return checkstatus(repo, subset, pat, 2) |
1798 |
1813 |
1799 @predicate('rev(number)') |
1814 @predicate('rev(number)', safe=True) |
1800 def rev(repo, subset, x): |
1815 def rev(repo, subset, x): |
1801 """Revision with the given numeric identifier. |
1816 """Revision with the given numeric identifier. |
1802 """ |
1817 """ |
1803 # i18n: "rev" is a keyword |
1818 # i18n: "rev" is a keyword |
1804 l = getargs(x, 1, 1, _("rev requires one argument")) |
1819 l = getargs(x, 1, 1, _("rev requires one argument")) |
1810 raise error.ParseError(_("rev expects a number")) |
1825 raise error.ParseError(_("rev expects a number")) |
1811 if l not in repo.changelog and l != node.nullrev: |
1826 if l not in repo.changelog and l != node.nullrev: |
1812 return baseset() |
1827 return baseset() |
1813 return subset & baseset([l]) |
1828 return subset & baseset([l]) |
1814 |
1829 |
1815 @predicate('matching(revision [, field])') |
1830 @predicate('matching(revision [, field])', safe=True) |
1816 def matching(repo, subset, x): |
1831 def matching(repo, subset, x): |
1817 """Changesets in which a given set of fields match the set of fields in the |
1832 """Changesets in which a given set of fields match the set of fields in the |
1818 selected revision or set. |
1833 selected revision or set. |
1819 |
1834 |
1820 To match more than one field pass the list of fields to match separated |
1835 To match more than one field pass the list of fields to match separated |
1922 return True |
1937 return True |
1923 return False |
1938 return False |
1924 |
1939 |
1925 return subset.filter(matches) |
1940 return subset.filter(matches) |
1926 |
1941 |
1927 @predicate('reverse(set)') |
1942 @predicate('reverse(set)', safe=True) |
1928 def reverse(repo, subset, x): |
1943 def reverse(repo, subset, x): |
1929 """Reverse order of set. |
1944 """Reverse order of set. |
1930 """ |
1945 """ |
1931 l = getset(repo, subset, x) |
1946 l = getset(repo, subset, x) |
1932 l.reverse() |
1947 l.reverse() |
1933 return l |
1948 return l |
1934 |
1949 |
1935 @predicate('roots(set)') |
1950 @predicate('roots(set)', safe=True) |
1936 def roots(repo, subset, x): |
1951 def roots(repo, subset, x): |
1937 """Changesets in set with no parent changeset in set. |
1952 """Changesets in set with no parent changeset in set. |
1938 """ |
1953 """ |
1939 s = getset(repo, fullreposet(repo), x) |
1954 s = getset(repo, fullreposet(repo), x) |
1940 parents = repo.changelog.parentrevs |
1955 parents = repo.changelog.parentrevs |
2055 kind, pattern, matcher = util.stringmatcher(pattern) |
2070 kind, pattern, matcher = util.stringmatcher(pattern) |
2056 if kind == 'literal': |
2071 if kind == 'literal': |
2057 matcher = lambda s: pattern in s |
2072 matcher = lambda s: pattern in s |
2058 return kind, pattern, matcher |
2073 return kind, pattern, matcher |
2059 |
2074 |
2060 @predicate('tag([name])') |
2075 @predicate('tag([name])', safe=True) |
2061 def tag(repo, subset, x): |
2076 def tag(repo, subset, x): |
2062 """The specified tag by name, or all tagged revisions if no name is given. |
2077 """The specified tag by name, or all tagged revisions if no name is given. |
2063 |
2078 |
2064 If `name` starts with `re:`, the remainder of the name is treated as |
2079 If `name` starts with `re:`, the remainder of the name is treated as |
2065 a regular expression. To match a tag that actually starts with `re:`, |
2080 a regular expression. To match a tag that actually starts with `re:`, |
2084 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)]) |
2099 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)]) |
2085 else: |
2100 else: |
2086 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip']) |
2101 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip']) |
2087 return subset & s |
2102 return subset & s |
2088 |
2103 |
2089 @predicate('tagged') |
2104 @predicate('tagged', safe=True) |
2090 def tagged(repo, subset, x): |
2105 def tagged(repo, subset, x): |
2091 return tag(repo, subset, x) |
2106 return tag(repo, subset, x) |
2092 |
2107 |
2093 @predicate('unstable()') |
2108 @predicate('unstable()', safe=True) |
2094 def unstable(repo, subset, x): |
2109 def unstable(repo, subset, x): |
2095 """Non-obsolete changesets with obsolete ancestors. |
2110 """Non-obsolete changesets with obsolete ancestors. |
2096 """ |
2111 """ |
2097 # i18n: "unstable" is a keyword |
2112 # i18n: "unstable" is a keyword |
2098 getargs(x, 0, 0, _("unstable takes no arguments")) |
2113 getargs(x, 0, 0, _("unstable takes no arguments")) |
2099 unstables = obsmod.getrevs(repo, 'unstable') |
2114 unstables = obsmod.getrevs(repo, 'unstable') |
2100 return subset & unstables |
2115 return subset & unstables |
2101 |
2116 |
2102 |
2117 |
2103 @predicate('user(string)') |
2118 @predicate('user(string)', safe=True) |
2104 def user(repo, subset, x): |
2119 def user(repo, subset, x): |
2105 """User name contains string. The match is case-insensitive. |
2120 """User name contains string. The match is case-insensitive. |
2106 |
2121 |
2107 If `string` starts with `re:`, the remainder of the string is treated as |
2122 If `string` starts with `re:`, the remainder of the string is treated as |
2108 a regular expression. To match a user that actually contains `re:`, use |
2123 a regular expression. To match a user that actually contains `re:`, use |
2109 the prefix `literal:`. |
2124 the prefix `literal:`. |
2110 """ |
2125 """ |
2111 return author(repo, subset, x) |
2126 return author(repo, subset, x) |
2112 |
2127 |
2113 # experimental |
2128 # experimental |
2114 @predicate('wdir') |
2129 @predicate('wdir', safe=True) |
2115 def wdir(repo, subset, x): |
2130 def wdir(repo, subset, x): |
2116 # i18n: "wdir" is a keyword |
2131 # i18n: "wdir" is a keyword |
2117 getargs(x, 0, 0, _("wdir takes no arguments")) |
2132 getargs(x, 0, 0, _("wdir takes no arguments")) |
2118 if node.wdirrev in subset or isinstance(subset, fullreposet): |
2133 if node.wdirrev in subset or isinstance(subset, fullreposet): |
2119 return baseset([node.wdirrev]) |
2134 return baseset([node.wdirrev]) |
2120 return baseset() |
2135 return baseset() |
2121 |
2136 |
2122 # for internal use |
2137 # for internal use |
2123 @predicate('_list') |
2138 @predicate('_list', safe=True) |
2124 def _list(repo, subset, x): |
2139 def _list(repo, subset, x): |
2125 s = getstring(x, "internal error") |
2140 s = getstring(x, "internal error") |
2126 if not s: |
2141 if not s: |
2127 return baseset() |
2142 return baseset() |
2128 # remove duplicates here. it's difficult for caller to deduplicate sets |
2143 # remove duplicates here. it's difficult for caller to deduplicate sets |
2148 ls.append(r) |
2163 ls.append(r) |
2149 seen.add(r) |
2164 seen.add(r) |
2150 return baseset(ls) |
2165 return baseset(ls) |
2151 |
2166 |
2152 # for internal use |
2167 # for internal use |
2153 @predicate('_intlist') |
2168 @predicate('_intlist', safe=True) |
2154 def _intlist(repo, subset, x): |
2169 def _intlist(repo, subset, x): |
2155 s = getstring(x, "internal error") |
2170 s = getstring(x, "internal error") |
2156 if not s: |
2171 if not s: |
2157 return baseset() |
2172 return baseset() |
2158 ls = [int(r) for r in s.split('\0')] |
2173 ls = [int(r) for r in s.split('\0')] |
2159 s = subset |
2174 s = subset |
2160 return baseset([r for r in ls if r in s]) |
2175 return baseset([r for r in ls if r in s]) |
2161 |
2176 |
2162 # for internal use |
2177 # for internal use |
2163 @predicate('_hexlist') |
2178 @predicate('_hexlist', safe=True) |
2164 def _hexlist(repo, subset, x): |
2179 def _hexlist(repo, subset, x): |
2165 s = getstring(x, "internal error") |
2180 s = getstring(x, "internal error") |
2166 if not s: |
2181 if not s: |
2167 return baseset() |
2182 return baseset() |
2168 cl = repo.changelog |
2183 cl = repo.changelog |
2169 ls = [cl.rev(node.bin(r)) for r in s.split('\0')] |
2184 ls = [cl.rev(node.bin(r)) for r in s.split('\0')] |
2170 s = subset |
2185 s = subset |
2171 return baseset([r for r in ls if r in s]) |
2186 return baseset([r for r in ls if r in s]) |
2172 |
|
2173 # symbols which can't be used for a DoS attack for any given input |
|
2174 # (e.g. those which accept regexes as plain strings shouldn't be included) |
|
2175 # functions that just return a lot of changesets (like all) don't count here |
|
2176 safesymbols = set([ |
|
2177 "adds", |
|
2178 "all", |
|
2179 "ancestor", |
|
2180 "ancestors", |
|
2181 "_firstancestors", |
|
2182 "author", |
|
2183 "bisect", |
|
2184 "bisected", |
|
2185 "bookmark", |
|
2186 "branch", |
|
2187 "branchpoint", |
|
2188 "bumped", |
|
2189 "bundle", |
|
2190 "children", |
|
2191 "closed", |
|
2192 "converted", |
|
2193 "date", |
|
2194 "desc", |
|
2195 "descendants", |
|
2196 "_firstdescendants", |
|
2197 "destination", |
|
2198 "divergent", |
|
2199 "draft", |
|
2200 "extinct", |
|
2201 "extra", |
|
2202 "file", |
|
2203 "filelog", |
|
2204 "first", |
|
2205 "follow", |
|
2206 "_followfirst", |
|
2207 "head", |
|
2208 "heads", |
|
2209 "hidden", |
|
2210 "id", |
|
2211 "keyword", |
|
2212 "last", |
|
2213 "limit", |
|
2214 "_matchfiles", |
|
2215 "max", |
|
2216 "merge", |
|
2217 "min", |
|
2218 "modifies", |
|
2219 "obsolete", |
|
2220 "only", |
|
2221 "origin", |
|
2222 "outgoing", |
|
2223 "p1", |
|
2224 "p2", |
|
2225 "parents", |
|
2226 "present", |
|
2227 "public", |
|
2228 "_notpublic", |
|
2229 "remote", |
|
2230 "removes", |
|
2231 "rev", |
|
2232 "reverse", |
|
2233 "roots", |
|
2234 "sort", |
|
2235 "secret", |
|
2236 "matching", |
|
2237 "tag", |
|
2238 "tagged", |
|
2239 "user", |
|
2240 "unstable", |
|
2241 "wdir", |
|
2242 "_list", |
|
2243 "_intlist", |
|
2244 "_hexlist", |
|
2245 ]) |
|
2246 |
2187 |
2247 methods = { |
2188 methods = { |
2248 "range": rangeset, |
2189 "range": rangeset, |
2249 "dagrange": dagrange, |
2190 "dagrange": dagrange, |
2250 "string": stringset, |
2191 "string": stringset, |