mercurial/revset.py
changeset 27587 c8dc480142a8
parent 27586 42910f9fffeb
child 27637 b502138f5faa
equal deleted inserted replaced
27586:42910f9fffeb 27587:c8dc480142a8
   472 #   repo - current repository instance
   472 #   repo - current repository instance
   473 #   subset - of revisions to be examined
   473 #   subset - of revisions to be examined
   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 
   480     Usage::
   485     Usage::
   481 
   486 
   493     """
   498     """
   494     table = symbols
   499     table = symbols
   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 
   501     Usage::
   516     Usage::
   502 
   517 
   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:`,
   670         bms = set([repo[r].rev()
   685         bms = set([repo[r].rev()
   671                    for r in repo._bookmarks.values()])
   686                    for r in repo._bookmarks.values()])
   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
   679     changesets.
   694     changesets.
   680 
   695 
   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"))
   821                     return True
   836                     return True
   822         return False
   837         return False
   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.
   830     """
   845     """
   831 
   846 
   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
  1104             backrevref[fr] = rev
  1119             backrevref[fr] = rev
  1105             s.add(rev)
  1120             s.add(rev)
  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     """
  1113     return limit(repo, subset, x)
  1128     return limit(repo, subset, x)
  1114 
  1129 
  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"))
  1175                 return True
  1190                 return True
  1176         return False
  1191         return False
  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     #
  1184     #   [p:foo, i:bar, x:baz]
  1199     #   [p:foo, i:bar, x:baz]
  1185     #
  1200     #
  1241                 return True
  1256                 return True
  1242         return False
  1257         return False
  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 
  1250     For a faster but less accurate result, consider using ``filelog()``
  1265     For a faster but less accurate result, consider using ``filelog()``
  1251     instead.
  1266     instead.
  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"))
  1364             break
  1379             break
  1365         elif y in subset:
  1380         elif y in subset:
  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     """
  1373     os = getset(repo, fullreposet(repo), x)
  1388     os = getset(repo, fullreposet(repo), x)
  1374     try:
  1389     try:
  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.
  1589     repo.ui.popbuffer()
  1604     repo.ui.popbuffer()
  1590     cl = repo.changelog
  1605     cl = repo.changelog
  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     """
  1598     if x is None:
  1613     if x is None:
  1599         p = repo[x].p1().rev()
  1614         p = repo[x].p1().rev()
  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     """
  1782         r = repo[n].rev()
  1797         r = repo[n].rev()
  1783         if r in subset:
  1798         if r in subset:
  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 
  1791     The pattern without explicit kind like ``glob:`` is expected to be
  1806     The pattern without explicit kind like ``glob:`` is expected to be
  1792     relative to the current directory and match against a file or a
  1807     relative to the current directory and match against a file or a
  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
  1943             if 0 <= p and p in s:
  1958             if 0 <= p and p in s:
  1944                 return False
  1959                 return False
  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.
  1952 
  1967 
  1953     The keys can be:
  1968     The keys can be:
  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,