@@ -472,12 +472,20 @@ def func(repo, subset, a, b):
# x - argument in tree form
symbols = {}
-def predicate(decl):
+# symbols which can't be used for a DoS attack for any given input
+# (e.g. those which accept regexes as plain strings shouldn't be included)
+# functions that just return a lot of changesets (like all) don't count here
+safesymbols = set()
+
+def predicate(decl, safe=False):
"""Return a decorator for revset predicate function
'decl' argument is the declaration (including argument list like
'adds(pattern)') or the name (for internal use only) of a
predicate.
+
+ Optional 'safe' argument means whether predicate should be putted
+ into safe symbol table or not (False, by default, for safety).
"""
def decorator(func):
i = decl.find('(')
@@ -486,6 +494,8 @@ def predicate(decl):
else:
name = decl # only for internal use
symbols[name] = func
+ if safe:
+ safesymbols.add(name)
if func.__doc__:
func.__doc__ = "``%s``\n %s" % (decl, func.__doc__.strip())
return func
@@ -503,7 +513,7 @@ def _destmerge(repo, subset, x):
getargs(x, 0, 0, _("_mergedefaultdest takes no arguments"))
return subset & baseset([destutil.destmerge(repo)])
-@predicate('adds(pattern)')
+@predicate('adds(pattern)', safe=True)
def adds(repo, subset, x):
"""Changesets that add a file matching pattern.
@@ -515,7 +525,7 @@ def adds(repo, subset, x):
pat = getstring(x, _("adds requires a pattern"))
return checkstatus(repo, subset, pat, 1)
-@predicate('ancestor(*changeset)')
+@predicate('ancestor(*changeset)', safe=True)
def ancestor(repo, subset, x):
"""A greatest common ancestor of the changesets.
@@ -547,13 +557,13 @@ def _ancestors(repo, subset, x, followfi
s = _revancestors(repo, heads, followfirst)
return subset & s
-@predicate('ancestors(set)')
+@predicate('ancestors(set)', safe=True)
def ancestors(repo, subset, x):
"""Changesets that are ancestors of a changeset in set.
"""
return _ancestors(repo, subset, x)
-@predicate('_firstancestors')
+@predicate('_firstancestors', safe=True)
def _firstancestors(repo, subset, x):
# ``_firstancestors(set)``
# Like ``ancestors(set)`` but follows only the first parents.
@@ -576,7 +586,7 @@ def ancestorspec(repo, subset, x, n):
ps.add(r)
return subset & ps
-@predicate('author(string)')
+@predicate('author(string)', safe=True)
def author(repo, subset, x):
"""Alias for ``user(string)``.
"""
@@ -585,7 +595,7 @@ def author(repo, subset, x):
kind, pattern, matcher = _substringmatcher(n)
return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
-@predicate('bisect(string)')
+@predicate('bisect(string)', safe=True)
def bisect(repo, subset, x):
"""Changesets marked in the specified bisect status:
@@ -604,11 +614,11 @@ def bisect(repo, subset, x):
# Backward-compatibility
# - no help entry so that we do not advertise it any more
-@predicate('bisected')
+@predicate('bisected', safe=True)
def bisected(repo, subset, x):
return bisect(repo, subset, x)
-@predicate('bookmark([name])')
+@predicate('bookmark([name])', safe=True)
def bookmark(repo, subset, x):
"""The named bookmark or all bookmarks.
@@ -646,7 +656,7 @@ def bookmark(repo, subset, x):
bms -= set([node.nullrev])
return subset & bms
-@predicate('branch(string or set)')
+@predicate('branch(string or set)', safe=True)
def branch(repo, subset, x):
"""
All changesets belonging to the given branch or the branches of the given
@@ -683,7 +693,7 @@ def branch(repo, subset, x):
c = s.__contains__
return subset.filter(lambda r: c(r) or getbi(r)[0] in b)
-@predicate('bumped()')
+@predicate('bumped()', safe=True)
def bumped(repo, subset, x):
"""Mutable changesets marked as successors of public changesets.
@@ -694,7 +704,7 @@ def bumped(repo, subset, x):
bumped = obsmod.getrevs(repo, 'bumped')
return subset & bumped
-@predicate('bundle()')
+@predicate('bundle()', safe=True)
def bundle(repo, subset, x):
"""Changesets in the bundle.
@@ -754,7 +764,7 @@ def _children(repo, narrow, parentset):
# This does not break because of other fullreposet misbehavior.
return baseset(cs)
-@predicate('children(set)')
+@predicate('children(set)', safe=True)
def children(repo, subset, x):
"""Child changesets of changesets in set.
"""
@@ -762,7 +772,7 @@ def children(repo, subset, x):
cs = _children(repo, subset, s)
return subset & cs
-@predicate('closed()')
+@predicate('closed()', safe=True)
def closed(repo, subset, x):
"""Changeset is closed.
"""
@@ -797,7 +807,7 @@ def contains(repo, subset, x):
return subset.filter(matches)
-@predicate('converted([id])')
+@predicate('converted([id])', safe=True)
def converted(repo, subset, x):
"""Changesets converted from the given identifier in the old repository if
present, or all converted changesets if no identifier is specified.
@@ -819,7 +829,7 @@ def converted(repo, subset, x):
return subset.filter(lambda r: _matchvalue(r))
-@predicate('date(interval)')
+@predicate('date(interval)', safe=True)
def date(repo, subset, x):
"""Changesets within the interval, see :hg:`help dates`.
"""
@@ -828,7 +838,7 @@ def date(repo, subset, x):
dm = util.matchdate(ds)
return subset.filter(lambda x: dm(repo[x].date()[0]))
-@predicate('desc(string)')
+@predicate('desc(string)', safe=True)
def desc(repo, subset, x):
"""Search commit message for string. The match is case-insensitive.
"""
@@ -860,19 +870,19 @@ def _descendants(repo, subset, x, follow
result = subset & result
return result
-@predicate('descendants(set)')
+@predicate('descendants(set)', safe=True)
def descendants(repo, subset, x):
"""Changesets which are descendants of changesets in set.
"""
return _descendants(repo, subset, x)
-@predicate('_firstdescendants')
+@predicate('_firstdescendants', safe=True)
def _firstdescendants(repo, subset, x):
# ``_firstdescendants(set)``
# Like ``descendants(set)`` but follows only the first parents.
return _descendants(repo, subset, x, followfirst=True)
-@predicate('destination([set])')
+@predicate('destination([set])', safe=True)
def destination(repo, subset, x):
"""Changesets that were created by a graft, transplant or rebase operation,
with the given revisions specified as the source. Omitting the optional set
@@ -916,7 +926,7 @@ def destination(repo, subset, x):
return subset.filter(dests.__contains__)
-@predicate('divergent()')
+@predicate('divergent()', safe=True)
def divergent(repo, subset, x):
"""
Final successors of changesets with an alternative set of final successors.
@@ -926,7 +936,7 @@ def divergent(repo, subset, x):
divergent = obsmod.getrevs(repo, 'divergent')
return subset & divergent
-@predicate('extinct()')
+@predicate('extinct()', safe=True)
def extinct(repo, subset, x):
"""Obsolete changesets with obsolete descendants only.
"""
@@ -935,7 +945,7 @@ def extinct(repo, subset, x):
extincts = obsmod.getrevs(repo, 'extinct')
return subset & extincts
-@predicate('extra(label, [value])')
+@predicate('extra(label, [value])', safe=True)
def extra(repo, subset, x):
"""Changesets with the given label in the extra metadata, with the given
optional value.
@@ -965,7 +975,7 @@ def extra(repo, subset, x):
return subset.filter(lambda r: _matchvalue(r))
-@predicate('filelog(pattern)')
+@predicate('filelog(pattern)', safe=True)
def filelog(repo, subset, x):
"""Changesets connected to the specified filelog.
@@ -1080,7 +1090,7 @@ def filelog(repo, subset, x):
return subset & s
-@predicate('first(set, [n])')
+@predicate('first(set, [n])', safe=True)
def first(repo, subset, x):
"""An alias for limit().
"""
@@ -1106,7 +1116,7 @@ def _follow(repo, subset, x, name, follo
return subset & s
-@predicate('follow([pattern])')
+@predicate('follow([pattern])', safe=True)
def follow(repo, subset, x):
"""
An alias for ``::.`` (ancestors of the working directory's first parent).
@@ -1115,14 +1125,14 @@ def follow(repo, subset, x):
"""
return _follow(repo, subset, x, 'follow')
-@predicate('_followfirst')
+@predicate('_followfirst', safe=True)
def _followfirst(repo, subset, x):
# ``followfirst([pattern])``
# Like ``follow([pattern])`` but follows only the first parent of
# every revisions or files revisions.
return _follow(repo, subset, x, '_followfirst', followfirst=True)
-@predicate('all()')
+@predicate('all()', safe=True)
def getall(repo, subset, x):
"""All changesets, the same as ``0:tip``.
"""
@@ -1151,7 +1161,7 @@ def grep(repo, subset, x):
return subset.filter(matches)
-@predicate('_matchfiles')
+@predicate('_matchfiles', safe=True)
def _matchfiles(repo, subset, x):
# _matchfiles takes a revset list of prefixed arguments:
#
@@ -1217,7 +1227,7 @@ def _matchfiles(repo, subset, x):
return subset.filter(matches)
-@predicate('file(pattern)')
+@predicate('file(pattern)', safe=True)
def hasfile(repo, subset, x):
"""Changesets affecting files matched by pattern.
@@ -1230,7 +1240,7 @@ def hasfile(repo, subset, x):
pat = getstring(x, _("file requires a pattern"))
return _matchfiles(repo, subset, ('string', 'p:' + pat))
-@predicate('head()')
+@predicate('head()', safe=True)
def head(repo, subset, x):
"""Changeset is a named branch head.
"""
@@ -1246,7 +1256,7 @@ def head(repo, subset, x):
# necessary to ensure we preserve the order in subset.
return baseset(hs) & subset
-@predicate('heads(set)')
+@predicate('heads(set)', safe=True)
def heads(repo, subset, x):
"""Members of set with no children in set.
"""
@@ -1254,7 +1264,7 @@ def heads(repo, subset, x):
ps = parents(repo, subset, x)
return s - ps
-@predicate('hidden()')
+@predicate('hidden()', safe=True)
def hidden(repo, subset, x):
"""Hidden changesets.
"""
@@ -1263,7 +1273,7 @@ def hidden(repo, subset, x):
hiddenrevs = repoview.filterrevs(repo, 'visible')
return subset & hiddenrevs
-@predicate('keyword(string)')
+@predicate('keyword(string)', safe=True)
def keyword(repo, subset, x):
"""Search commit message, user name, and names of changed files for
string. The match is case-insensitive.
@@ -1278,7 +1288,7 @@ def keyword(repo, subset, x):
return subset.filter(matches)
-@predicate('limit(set[, n[, offset]])')
+@predicate('limit(set[, n[, offset]])', safe=True)
def limit(repo, subset, x):
"""First n members of set, defaulting to 1, starting from offset.
"""
@@ -1314,7 +1324,7 @@ def limit(repo, subset, x):
result.append(y)
return baseset(result)
-@predicate('last(set, [n])')
+@predicate('last(set, [n])', safe=True)
def last(repo, subset, x):
"""Last n members of set, defaulting to 1.
"""
@@ -1340,7 +1350,7 @@ def last(repo, subset, x):
result.append(y)
return baseset(result)
-@predicate('max(set)')
+@predicate('max(set)', safe=True)
def maxrev(repo, subset, x):
"""Changeset with highest revision number in set.
"""
@@ -1355,7 +1365,7 @@ def maxrev(repo, subset, x):
pass
return baseset()
-@predicate('merge()')
+@predicate('merge()', safe=True)
def merge(repo, subset, x):
"""Changeset is a merge changeset.
"""
@@ -1364,7 +1374,7 @@ def merge(repo, subset, x):
cl = repo.changelog
return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
-@predicate('branchpoint()')
+@predicate('branchpoint()', safe=True)
def branchpoint(repo, subset, x):
"""Changesets with more than one child.
"""
@@ -1383,7 +1393,7 @@ def branchpoint(repo, subset, x):
parentscount[p - baserev] += 1
return subset.filter(lambda r: parentscount[r - baserev] > 1)
-@predicate('min(set)')
+@predicate('min(set)', safe=True)
def minrev(repo, subset, x):
"""Changeset with lowest revision number in set.
"""
@@ -1398,7 +1408,7 @@ def minrev(repo, subset, x):
pass
return baseset()
-@predicate('modifies(pattern)')
+@predicate('modifies(pattern)', safe=True)
def modifies(repo, subset, x):
"""Changesets modifying files matched by pattern.
@@ -1448,7 +1458,7 @@ def named(repo, subset, x):
names -= set([node.nullrev])
return subset & names
-@predicate('id(string)')
+@predicate('id(string)', safe=True)
def node_(repo, subset, x):
"""Revision non-ambiguously specified by the given hex string prefix.
"""
@@ -1472,7 +1482,7 @@ def node_(repo, subset, x):
result = baseset([rn])
return result & subset
-@predicate('obsolete()')
+@predicate('obsolete()', safe=True)
def obsolete(repo, subset, x):
"""Mutable changeset with a newer version."""
# i18n: "obsolete" is a keyword
@@ -1480,7 +1490,7 @@ def obsolete(repo, subset, x):
obsoletes = obsmod.getrevs(repo, 'obsolete')
return subset & obsoletes
-@predicate('only(set, [set])')
+@predicate('only(set, [set])', safe=True)
def only(repo, subset, x):
"""Changesets that are ancestors of the first set that are not ancestors
of any other head in the repo. If a second set is specified, the result
@@ -1506,7 +1516,7 @@ def only(repo, subset, x):
# some optimisations from the fact this is a baseset.
return subset & results
-@predicate('origin([set])')
+@predicate('origin([set])', safe=True)
def origin(repo, subset, x):
"""
Changesets that were specified as a source for the grafts, transplants or
@@ -1538,7 +1548,7 @@ def origin(repo, subset, x):
# some optimisations from the fact this is a baseset.
return subset & o
-@predicate('outgoing([path])')
+@predicate('outgoing([path])', safe=True)
def outgoing(repo, subset, x):
"""Changesets not found in the specified destination repository, or the
default push location.
@@ -1565,7 +1575,7 @@ def outgoing(repo, subset, x):
o = set([cl.rev(r) for r in outgoing.missing])
return subset & o
-@predicate('p1([set])')
+@predicate('p1([set])', safe=True)
def p1(repo, subset, x):
"""First parent of changesets in set, or the working directory.
"""
@@ -1584,7 +1594,7 @@ def p1(repo, subset, x):
# some optimisations from the fact this is a baseset.
return subset & ps
-@predicate('p2([set])')
+@predicate('p2([set])', safe=True)
def p2(repo, subset, x):
"""Second parent of changesets in set, or the working directory.
"""
@@ -1607,7 +1617,7 @@ def p2(repo, subset, x):
# some optimisations from the fact this is a baseset.
return subset & ps
-@predicate('parents([set])')
+@predicate('parents([set])', safe=True)
def parents(repo, subset, x):
"""
The set of all parents for all changesets in set, or the working directory.
@@ -1640,7 +1650,7 @@ def _phase(repo, subset, target):
condition = lambda r: phase(repo, r) == target
return subset.filter(condition, cache=False)
-@predicate('draft()')
+@predicate('draft()', safe=True)
def draft(repo, subset, x):
"""Changeset in draft phase."""
# i18n: "draft" is a keyword
@@ -1648,7 +1658,7 @@ def draft(repo, subset, x):
target = phases.draft
return _phase(repo, subset, target)
-@predicate('secret()')
+@predicate('secret()', safe=True)
def secret(repo, subset, x):
"""Changeset in secret phase."""
# i18n: "secret" is a keyword
@@ -1681,7 +1691,7 @@ def parentspec(repo, subset, x, n):
ps.add(parents[1])
return subset & ps
-@predicate('present(set)')
+@predicate('present(set)', safe=True)
def present(repo, subset, x):
"""An empty set, if any revision in set isn't found; otherwise,
all revisions in set.
@@ -1696,7 +1706,7 @@ def present(repo, subset, x):
return baseset()
# for internal use
-@predicate('_notpublic')
+@predicate('_notpublic', safe=True)
def _notpublic(repo, subset, x):
getargs(x, 0, 0, "_notpublic takes no arguments")
repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
@@ -1713,7 +1723,7 @@ def _notpublic(repo, subset, x):
condition = lambda r: phase(repo, r) != target
return subset.filter(condition, cache=False)
-@predicate('public()')
+@predicate('public()', safe=True)
def public(repo, subset, x):
"""Changeset in public phase."""
# i18n: "public" is a keyword
@@ -1723,7 +1733,7 @@ def public(repo, subset, x):
condition = lambda r: phase(repo, r) == target
return subset.filter(condition, cache=False)
-@predicate('remote([id [,path]])')
+@predicate('remote([id [,path]])', safe=True)
def remote(repo, subset, x):
"""Local revision that corresponds to the given identifier in a
remote repository, if present. Here, the '.' identifier is a
@@ -1758,7 +1768,7 @@ def remote(repo, subset, x):
return baseset([r])
return baseset()
-@predicate('removes(pattern)')
+@predicate('removes(pattern)', safe=True)
def removes(repo, subset, x):
"""Changesets which remove files matching pattern.
@@ -1770,7 +1780,7 @@ def removes(repo, subset, x):
pat = getstring(x, _("removes requires a pattern"))
return checkstatus(repo, subset, pat, 2)
-@predicate('rev(number)')
+@predicate('rev(number)', safe=True)
def rev(repo, subset, x):
"""Revision with the given numeric identifier.
"""
@@ -1786,7 +1796,7 @@ def rev(repo, subset, x):
return baseset()
return subset & baseset([l])
-@predicate('matching(revision [, field])')
+@predicate('matching(revision [, field])', safe=True)
def matching(repo, subset, x):
"""Changesets in which a given set of fields match the set of fields in the
selected revision or set.
@@ -1898,7 +1908,7 @@ def matching(repo, subset, x):
return subset.filter(matches)
-@predicate('reverse(set)')
+@predicate('reverse(set)', safe=True)
def reverse(repo, subset, x):
"""Reverse order of set.
"""
@@ -1906,7 +1916,7 @@ def reverse(repo, subset, x):
l.reverse()
return l
-@predicate('roots(set)')
+@predicate('roots(set)', safe=True)
def roots(repo, subset, x):
"""Changesets in set with no parent changeset in set.
"""
@@ -1919,7 +1929,7 @@ def roots(repo, subset, x):
return True
return subset & s.filter(filter)
-@predicate('sort(set[, [-]key...])')
+@predicate('sort(set[, [-]key...])', safe=True)
def sort(repo, subset, x):
"""Sort set by keys. The default sort order is ascending, specify a key
as ``-key`` to sort in descending order.
@@ -2031,7 +2041,7 @@ def _substringmatcher(pattern):
matcher = lambda s: pattern in s
return kind, pattern, matcher
-@predicate('tag([name])')
+@predicate('tag([name])', safe=True)
def tag(repo, subset, x):
"""The specified tag by name, or all tagged revisions if no name is given.
@@ -2060,11 +2070,11 @@ def tag(repo, subset, x):
s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
return subset & s
-@predicate('tagged')
+@predicate('tagged', safe=True)
def tagged(repo, subset, x):
return tag(repo, subset, x)
-@predicate('unstable()')
+@predicate('unstable()', safe=True)
def unstable(repo, subset, x):
"""Non-obsolete changesets with obsolete ancestors.
"""
@@ -2074,7 +2084,7 @@ def unstable(repo, subset, x):
return subset & unstables
-@predicate('user(string)')
+@predicate('user(string)', safe=True)
def user(repo, subset, x):
"""User name contains string. The match is case-insensitive.
@@ -2085,7 +2095,7 @@ def user(repo, subset, x):
return author(repo, subset, x)
# experimental
-@predicate('wdir')
+@predicate('wdir', safe=True)
def wdir(repo, subset, x):
# i18n: "wdir" is a keyword
getargs(x, 0, 0, _("wdir takes no arguments"))
@@ -2094,7 +2104,7 @@ def wdir(repo, subset, x):
return baseset()
# for internal use
-@predicate('_list')
+@predicate('_list', safe=True)
def _list(repo, subset, x):
s = getstring(x, "internal error")
if not s:
@@ -2124,7 +2134,7 @@ def _list(repo, subset, x):
return baseset(ls)
# for internal use
-@predicate('_intlist')
+@predicate('_intlist', safe=True)
def _intlist(repo, subset, x):
s = getstring(x, "internal error")
if not s:
@@ -2134,7 +2144,7 @@ def _intlist(repo, subset, x):
return baseset([r for r in ls if r in s])
# for internal use
-@predicate('_hexlist')
+@predicate('_hexlist', safe=True)
def _hexlist(repo, subset, x):
s = getstring(x, "internal error")
if not s:
@@ -2144,80 +2154,6 @@ def _hexlist(repo, subset, x):
s = subset
return baseset([r for r in ls if r in s])
-# symbols which can't be used for a DoS attack for any given input
-# (e.g. those which accept regexes as plain strings shouldn't be included)
-# functions that just return a lot of changesets (like all) don't count here
-safesymbols = set([
- "adds",
- "all",
- "ancestor",
- "ancestors",
- "_firstancestors",
- "author",
- "bisect",
- "bisected",
- "bookmark",
- "branch",
- "branchpoint",
- "bumped",
- "bundle",
- "children",
- "closed",
- "converted",
- "date",
- "desc",
- "descendants",
- "_firstdescendants",
- "destination",
- "divergent",
- "draft",
- "extinct",
- "extra",
- "file",
- "filelog",
- "first",
- "follow",
- "_followfirst",
- "head",
- "heads",
- "hidden",
- "id",
- "keyword",
- "last",
- "limit",
- "_matchfiles",
- "max",
- "merge",
- "min",
- "modifies",
- "obsolete",
- "only",
- "origin",
- "outgoing",
- "p1",
- "p2",
- "parents",
- "present",
- "public",
- "_notpublic",
- "remote",
- "removes",
- "rev",
- "reverse",
- "roots",
- "sort",
- "secret",
- "matching",
- "tag",
- "tagged",
- "user",
- "unstable",
- "wdir",
- "_list",
- "_intlist",
- "_hexlist",
-])
-
methods = {
"range": rangeset,
"dagrange": dagrange,