Show More
@@ -474,6 +474,11 b' def func(repo, subset, a, b):' | |||||
474 | # x - argument in tree form |
|
474 | # x - argument in tree form | |
475 | symbols = {} |
|
475 | symbols = {} | |
476 |
|
476 | |||
|
477 | # symbols which can't be used for a DoS attack for any given input | |||
|
478 | # (e.g. those which accept regexes as plain strings shouldn't be included) | |||
|
479 | # functions that just return a lot of changesets (like all) don't count here | |||
|
480 | safesymbols = set() | |||
|
481 | ||||
477 | class predicate(registrar.funcregistrar): |
|
482 | class predicate(registrar.funcregistrar): | |
478 | """Decorator to register revset predicate |
|
483 | """Decorator to register revset predicate | |
479 |
|
484 | |||
@@ -495,6 +500,16 b' class predicate(registrar.funcregistrar)' | |||||
495 | formatdoc = "``%s``\n %s" |
|
500 | formatdoc = "``%s``\n %s" | |
496 | getname = registrar.funcregistrar.parsefuncdecl |
|
501 | getname = registrar.funcregistrar.parsefuncdecl | |
497 |
|
502 | |||
|
503 | def __init__(self, decl, safe=False): | |||
|
504 | """'safe' indicates whether a predicate is safe for DoS attack | |||
|
505 | """ | |||
|
506 | super(predicate, self).__init__(decl) | |||
|
507 | self.safe = safe | |||
|
508 | ||||
|
509 | def extraaction(self, name, func): | |||
|
510 | if self.safe: | |||
|
511 | safesymbols.add(name) | |||
|
512 | ||||
498 | class extpredicate(registrar.delayregistrar): |
|
513 | class extpredicate(registrar.delayregistrar): | |
499 | """Decorator to register revset predicate in extensions |
|
514 | """Decorator to register revset predicate in extensions | |
500 |
|
515 | |||
@@ -529,7 +544,7 b' def _destmerge(repo, subset, x):' | |||||
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 | |||
@@ -541,7 +556,7 b' def adds(repo, subset, x):' | |||||
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 | |||
@@ -573,13 +588,13 b' def _ancestors(repo, subset, x, followfi' | |||||
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. | |
@@ -602,7 +617,7 b' def ancestorspec(repo, subset, x, n):' | |||||
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 | """ | |
@@ -611,7 +626,7 b' def author(repo, subset, x):' | |||||
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 | |||
@@ -630,11 +645,11 b' def bisect(repo, subset, x):' | |||||
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 | |||
@@ -672,7 +687,7 b' def bookmark(repo, subset, x):' | |||||
672 | bms -= set([node.nullrev]) |
|
687 | bms -= set([node.nullrev]) | |
673 | return subset & bms |
|
688 | return subset & bms | |
674 |
|
689 | |||
675 | @predicate('branch(string or set)') |
|
690 | @predicate('branch(string or set)', safe=True) | |
676 | def branch(repo, subset, x): |
|
691 | def branch(repo, subset, x): | |
677 | """ |
|
692 | """ | |
678 | All changesets belonging to the given branch or the branches of the given |
|
693 | All changesets belonging to the given branch or the branches of the given | |
@@ -709,7 +724,7 b' def branch(repo, subset, x):' | |||||
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 | |||
@@ -720,7 +735,7 b' def bumped(repo, subset, x):' | |||||
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 | |||
@@ -780,7 +795,7 b' def _children(repo, narrow, parentset):' | |||||
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 | """ | |
@@ -788,7 +803,7 b' def children(repo, subset, 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 | """ | |
@@ -823,7 +838,7 b' def contains(repo, subset, x):' | |||||
823 |
|
838 | |||
824 | return subset.filter(matches) |
|
839 | return subset.filter(matches) | |
825 |
|
840 | |||
826 | @predicate('converted([id])') |
|
841 | @predicate('converted([id])', safe=True) | |
827 | def converted(repo, subset, x): |
|
842 | def converted(repo, subset, x): | |
828 | """Changesets converted from the given identifier in the old repository if |
|
843 | """Changesets converted from the given identifier in the old repository if | |
829 | present, or all converted changesets if no identifier is specified. |
|
844 | present, or all converted changesets if no identifier is specified. | |
@@ -845,7 +860,7 b' def converted(repo, subset, x):' | |||||
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 | """ | |
@@ -854,7 +869,7 b' def date(repo, subset, x):' | |||||
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 | """ | |
@@ -886,19 +901,19 b' def _descendants(repo, subset, x, follow' | |||||
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 | |
@@ -942,7 +957,7 b' def destination(repo, subset, x):' | |||||
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. | |
@@ -952,7 +967,7 b' def divergent(repo, subset, x):' | |||||
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 | """ | |
@@ -961,7 +976,7 b' def extinct(repo, subset, x):' | |||||
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. | |
@@ -991,7 +1006,7 b' def extra(repo, subset, x):' | |||||
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 | |||
@@ -1106,7 +1121,7 b' def filelog(repo, subset, x):' | |||||
1106 |
|
1121 | |||
1107 | return subset & s |
|
1122 | return subset & s | |
1108 |
|
1123 | |||
1109 | @predicate('first(set, [n])') |
|
1124 | @predicate('first(set, [n])', safe=True) | |
1110 | def first(repo, subset, x): |
|
1125 | def first(repo, subset, x): | |
1111 | """An alias for limit(). |
|
1126 | """An alias for limit(). | |
1112 | """ |
|
1127 | """ | |
@@ -1132,7 +1147,7 b' def _follow(repo, subset, x, name, follo' | |||||
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). | |
@@ -1141,14 +1156,14 b' def follow(repo, subset, x):' | |||||
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 | """ | |
@@ -1177,7 +1192,7 b' def grep(repo, subset, x):' | |||||
1177 |
|
1192 | |||
1178 | return subset.filter(matches) |
|
1193 | return subset.filter(matches) | |
1179 |
|
1194 | |||
1180 | @predicate('_matchfiles') |
|
1195 | @predicate('_matchfiles', safe=True) | |
1181 | def _matchfiles(repo, subset, x): |
|
1196 | def _matchfiles(repo, subset, x): | |
1182 | # _matchfiles takes a revset list of prefixed arguments: |
|
1197 | # _matchfiles takes a revset list of prefixed arguments: | |
1183 | # |
|
1198 | # | |
@@ -1243,7 +1258,7 b' def _matchfiles(repo, subset, x):' | |||||
1243 |
|
1258 | |||
1244 | return subset.filter(matches) |
|
1259 | return subset.filter(matches) | |
1245 |
|
1260 | |||
1246 | @predicate('file(pattern)') |
|
1261 | @predicate('file(pattern)', safe=True) | |
1247 | def hasfile(repo, subset, x): |
|
1262 | def hasfile(repo, subset, x): | |
1248 | """Changesets affecting files matched by pattern. |
|
1263 | """Changesets affecting files matched by pattern. | |
1249 |
|
1264 | |||
@@ -1256,7 +1271,7 b' def hasfile(repo, subset, x):' | |||||
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 | """ | |
@@ -1272,7 +1287,7 b' def head(repo, subset, x):' | |||||
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 | """ | |
@@ -1280,7 +1295,7 b' def heads(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 | """ | |
@@ -1289,7 +1304,7 b' def hidden(repo, subset, x):' | |||||
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. | |
@@ -1304,7 +1319,7 b' def keyword(repo, subset, x):' | |||||
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 | """ | |
@@ -1340,7 +1355,7 b' def limit(repo, subset, x):' | |||||
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 | """ | |
@@ -1366,7 +1381,7 b' def last(repo, subset, x):' | |||||
1366 | result.append(y) |
|
1381 | result.append(y) | |
1367 | return baseset(result) |
|
1382 | return baseset(result) | |
1368 |
|
1383 | |||
1369 | @predicate('max(set)') |
|
1384 | @predicate('max(set)', safe=True) | |
1370 | def maxrev(repo, subset, x): |
|
1385 | def maxrev(repo, subset, x): | |
1371 | """Changeset with highest revision number in set. |
|
1386 | """Changeset with highest revision number in set. | |
1372 | """ |
|
1387 | """ | |
@@ -1381,7 +1396,7 b' def maxrev(repo, subset, x):' | |||||
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 | """ | |
@@ -1390,7 +1405,7 b' def merge(repo, subset, x):' | |||||
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 | """ | |
@@ -1409,7 +1424,7 b' def branchpoint(repo, subset, x):' | |||||
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 | """ | |
@@ -1424,7 +1439,7 b' def minrev(repo, subset, x):' | |||||
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 | |||
@@ -1474,7 +1489,7 b' def named(repo, subset, x):' | |||||
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 | """ | |
@@ -1498,7 +1513,7 b' def node_(repo, subset, x):' | |||||
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 | |
@@ -1506,7 +1521,7 b' def obsolete(repo, subset, x):' | |||||
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 | |
@@ -1532,7 +1547,7 b' def only(repo, subset, x):' | |||||
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 | |
@@ -1564,7 +1579,7 b' def origin(repo, subset, x):' | |||||
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. | |
@@ -1591,7 +1606,7 b' def outgoing(repo, subset, x):' | |||||
1591 | o = set([cl.rev(r) for r in outgoing.missing]) |
|
1606 | o = set([cl.rev(r) for r in outgoing.missing]) | |
1592 | return subset & o |
|
1607 | return subset & o | |
1593 |
|
1608 | |||
1594 | @predicate('p1([set])') |
|
1609 | @predicate('p1([set])', safe=True) | |
1595 | def p1(repo, subset, x): |
|
1610 | def p1(repo, subset, x): | |
1596 | """First parent of changesets in set, or the working directory. |
|
1611 | """First parent of changesets in set, or the working directory. | |
1597 | """ |
|
1612 | """ | |
@@ -1610,7 +1625,7 b' def p1(repo, subset, x):' | |||||
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 | """ | |
@@ -1633,7 +1648,7 b' def p2(repo, subset, x):' | |||||
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. | |
@@ -1666,7 +1681,7 b' def _phase(repo, subset, target):' | |||||
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 | |
@@ -1674,7 +1689,7 b' def draft(repo, subset, x):' | |||||
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 | |
@@ -1707,7 +1722,7 b' def parentspec(repo, subset, x, n):' | |||||
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. | |
@@ -1722,7 +1737,7 b' def present(repo, subset, x):' | |||||
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 | |
@@ -1739,7 +1754,7 b' def _notpublic(repo, subset, x):' | |||||
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 | |
@@ -1749,7 +1764,7 b' def public(repo, subset, x):' | |||||
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 | |
@@ -1784,7 +1799,7 b' def remote(repo, subset, x):' | |||||
1784 | return baseset([r]) |
|
1799 | return baseset([r]) | |
1785 | return baseset() |
|
1800 | return baseset() | |
1786 |
|
1801 | |||
1787 | @predicate('removes(pattern)') |
|
1802 | @predicate('removes(pattern)', safe=True) | |
1788 | def removes(repo, subset, x): |
|
1803 | def removes(repo, subset, x): | |
1789 | """Changesets which remove files matching pattern. |
|
1804 | """Changesets which remove files matching pattern. | |
1790 |
|
1805 | |||
@@ -1796,7 +1811,7 b' def removes(repo, subset, x):' | |||||
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 | """ | |
@@ -1812,7 +1827,7 b' def rev(repo, subset, x):' | |||||
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. | |
@@ -1924,7 +1939,7 b' def matching(repo, subset, x):' | |||||
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 | """ | |
@@ -1932,7 +1947,7 b' def reverse(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 | """ | |
@@ -1945,7 +1960,7 b' def roots(repo, subset, x):' | |||||
1945 | return True |
|
1960 | return True | |
1946 | return subset & s.filter(filter) |
|
1961 | return subset & s.filter(filter) | |
1947 |
|
1962 | |||
1948 | @predicate('sort(set[, [-]key...])') |
|
1963 | @predicate('sort(set[, [-]key...])', safe=True) | |
1949 | def sort(repo, subset, x): |
|
1964 | def sort(repo, subset, x): | |
1950 | """Sort set by keys. The default sort order is ascending, specify a key |
|
1965 | """Sort set by keys. The default sort order is ascending, specify a key | |
1951 | as ``-key`` to sort in descending order. |
|
1966 | as ``-key`` to sort in descending order. | |
@@ -2057,7 +2072,7 b' def _substringmatcher(pattern):' | |||||
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 | |||
@@ -2086,11 +2101,11 b' def tag(repo, subset, x):' | |||||
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 | """ | |
@@ -2100,7 +2115,7 b' def unstable(repo, subset, x):' | |||||
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 | |||
@@ -2111,7 +2126,7 b' def user(repo, subset, x):' | |||||
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")) | |
@@ -2120,7 +2135,7 b' def wdir(repo, subset, x):' | |||||
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: | |
@@ -2150,7 +2165,7 b' def _list(repo, subset, x):' | |||||
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: | |
@@ -2160,7 +2175,7 b' def _intlist(repo, subset, x):' | |||||
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: | |
@@ -2170,80 +2185,6 b' def _hexlist(repo, subset, x):' | |||||
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 |
|
2187 | |||
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 |
|
||||
2247 | methods = { |
|
2188 | methods = { | |
2248 | "range": rangeset, |
|
2189 | "range": rangeset, | |
2249 | "dagrange": dagrange, |
|
2190 | "dagrange": dagrange, |
General Comments 0
You need to be logged in to leave comments.
Login now