Show More
@@ -166,6 +166,16 b' class revsetpredicate(_funcregistrarbase' | |||||
166 | Optional argument 'takeorder' indicates whether a predicate function |
|
166 | Optional argument 'takeorder' indicates whether a predicate function | |
167 | takes ordering policy as the last argument. |
|
167 | takes ordering policy as the last argument. | |
168 |
|
168 | |||
|
169 | Optional argument 'weight' indicates the estimated run-time cost, useful | |||
|
170 | for static optimization, default is 1. Higher weight means more expensive. | |||
|
171 | Usually, revsets that are fast and return only one revision has a weight of | |||
|
172 | 0.5 (ex. a symbol); revsets with O(changelog) complexity and read only the | |||
|
173 | changelog have weight 10 (ex. author); revsets reading manifest deltas have | |||
|
174 | weight 30 (ex. adds); revset reading manifest contents have weight 100 | |||
|
175 | (ex. contains). Note: those values are flexible. If the revset has a | |||
|
176 | same big-O time complexity as 'contains', but with a smaller constant, it | |||
|
177 | might have a weight of 90. | |||
|
178 | ||||
169 | 'revsetpredicate' instance in example above can be used to |
|
179 | 'revsetpredicate' instance in example above can be used to | |
170 | decorate multiple functions. |
|
180 | decorate multiple functions. | |
171 |
|
181 | |||
@@ -178,9 +188,10 b' class revsetpredicate(_funcregistrarbase' | |||||
178 | _getname = _funcregistrarbase._parsefuncdecl |
|
188 | _getname = _funcregistrarbase._parsefuncdecl | |
179 | _docformat = "``%s``\n %s" |
|
189 | _docformat = "``%s``\n %s" | |
180 |
|
190 | |||
181 | def _extrasetup(self, name, func, safe=False, takeorder=False): |
|
191 | def _extrasetup(self, name, func, safe=False, takeorder=False, weight=1): | |
182 | func._safe = safe |
|
192 | func._safe = safe | |
183 | func._takeorder = takeorder |
|
193 | func._takeorder = takeorder | |
|
194 | func._weight = weight | |||
184 |
|
195 | |||
185 | class filesetpredicate(_funcregistrarbase): |
|
196 | class filesetpredicate(_funcregistrarbase): | |
186 | """Decorator to register fileset predicate |
|
197 | """Decorator to register fileset predicate |
@@ -253,7 +253,7 b' def func(repo, subset, a, b, order):' | |||||
253 | # repo - current repository instance |
|
253 | # repo - current repository instance | |
254 | # subset - of revisions to be examined |
|
254 | # subset - of revisions to be examined | |
255 | # x - argument in tree form |
|
255 | # x - argument in tree form | |
256 | symbols = {} |
|
256 | symbols = revsetlang.symbols | |
257 |
|
257 | |||
258 | # symbols which can't be used for a DoS attack for any given input |
|
258 | # symbols which can't be used for a DoS attack for any given input | |
259 | # (e.g. those which accept regexes as plain strings shouldn't be included) |
|
259 | # (e.g. those which accept regexes as plain strings shouldn't be included) | |
@@ -276,7 +276,7 b' def _destmerge(repo, subset, x):' | |||||
276 | sourceset = getset(repo, fullreposet(repo), x) |
|
276 | sourceset = getset(repo, fullreposet(repo), x) | |
277 | return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)]) |
|
277 | return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)]) | |
278 |
|
278 | |||
279 | @predicate('adds(pattern)', safe=True) |
|
279 | @predicate('adds(pattern)', safe=True, weight=30) | |
280 | def adds(repo, subset, x): |
|
280 | def adds(repo, subset, x): | |
281 | """Changesets that add a file matching pattern. |
|
281 | """Changesets that add a file matching pattern. | |
282 |
|
282 | |||
@@ -288,7 +288,7 b' def adds(repo, subset, x):' | |||||
288 | pat = getstring(x, _("adds requires a pattern")) |
|
288 | pat = getstring(x, _("adds requires a pattern")) | |
289 | return checkstatus(repo, subset, pat, 1) |
|
289 | return checkstatus(repo, subset, pat, 1) | |
290 |
|
290 | |||
291 | @predicate('ancestor(*changeset)', safe=True) |
|
291 | @predicate('ancestor(*changeset)', safe=True, weight=0.5) | |
292 | def ancestor(repo, subset, x): |
|
292 | def ancestor(repo, subset, x): | |
293 | """A greatest common ancestor of the changesets. |
|
293 | """A greatest common ancestor of the changesets. | |
294 |
|
294 | |||
@@ -394,7 +394,7 b' def ancestorspec(repo, subset, x, n, ord' | |||||
394 | ps.add(r) |
|
394 | ps.add(r) | |
395 | return subset & ps |
|
395 | return subset & ps | |
396 |
|
396 | |||
397 | @predicate('author(string)', safe=True) |
|
397 | @predicate('author(string)', safe=True, weight=10) | |
398 | def author(repo, subset, x): |
|
398 | def author(repo, subset, x): | |
399 | """Alias for ``user(string)``. |
|
399 | """Alias for ``user(string)``. | |
400 | """ |
|
400 | """ | |
@@ -462,7 +462,7 b' def bookmark(repo, subset, x):' | |||||
462 | bms -= {node.nullrev} |
|
462 | bms -= {node.nullrev} | |
463 | return subset & bms |
|
463 | return subset & bms | |
464 |
|
464 | |||
465 | @predicate('branch(string or set)', safe=True) |
|
465 | @predicate('branch(string or set)', safe=True, weight=10) | |
466 | def branch(repo, subset, x): |
|
466 | def branch(repo, subset, x): | |
467 | """ |
|
467 | """ | |
468 | All changesets belonging to the given branch or the branches of the given |
|
468 | All changesets belonging to the given branch or the branches of the given | |
@@ -595,7 +595,7 b' def children(repo, subset, x):' | |||||
595 | cs = _children(repo, subset, s) |
|
595 | cs = _children(repo, subset, s) | |
596 | return subset & cs |
|
596 | return subset & cs | |
597 |
|
597 | |||
598 | @predicate('closed()', safe=True) |
|
598 | @predicate('closed()', safe=True, weight=10) | |
599 | def closed(repo, subset, x): |
|
599 | def closed(repo, subset, x): | |
600 | """Changeset is closed. |
|
600 | """Changeset is closed. | |
601 | """ |
|
601 | """ | |
@@ -604,7 +604,7 b' def closed(repo, subset, x):' | |||||
604 | return subset.filter(lambda r: repo[r].closesbranch(), |
|
604 | return subset.filter(lambda r: repo[r].closesbranch(), | |
605 | condrepr='<branch closed>') |
|
605 | condrepr='<branch closed>') | |
606 |
|
606 | |||
607 | @predicate('contains(pattern)') |
|
607 | @predicate('contains(pattern)', weight=100) | |
608 | def contains(repo, subset, x): |
|
608 | def contains(repo, subset, x): | |
609 | """The revision's manifest contains a file matching pattern (but might not |
|
609 | """The revision's manifest contains a file matching pattern (but might not | |
610 | modify it). See :hg:`help patterns` for information about file patterns. |
|
610 | modify it). See :hg:`help patterns` for information about file patterns. | |
@@ -654,7 +654,7 b' def converted(repo, subset, x):' | |||||
654 | return subset.filter(lambda r: _matchvalue(r), |
|
654 | return subset.filter(lambda r: _matchvalue(r), | |
655 | condrepr=('<converted %r>', rev)) |
|
655 | condrepr=('<converted %r>', rev)) | |
656 |
|
656 | |||
657 | @predicate('date(interval)', safe=True) |
|
657 | @predicate('date(interval)', safe=True, weight=10) | |
658 | def date(repo, subset, x): |
|
658 | def date(repo, subset, x): | |
659 | """Changesets within the interval, see :hg:`help dates`. |
|
659 | """Changesets within the interval, see :hg:`help dates`. | |
660 | """ |
|
660 | """ | |
@@ -664,7 +664,7 b' def date(repo, subset, x):' | |||||
664 | return subset.filter(lambda x: dm(repo[x].date()[0]), |
|
664 | return subset.filter(lambda x: dm(repo[x].date()[0]), | |
665 | condrepr=('<date %r>', ds)) |
|
665 | condrepr=('<date %r>', ds)) | |
666 |
|
666 | |||
667 | @predicate('desc(string)', safe=True) |
|
667 | @predicate('desc(string)', safe=True, weight=10) | |
668 | def desc(repo, subset, x): |
|
668 | def desc(repo, subset, x): | |
669 | """Search commit message for string. The match is case-insensitive. |
|
669 | """Search commit message for string. The match is case-insensitive. | |
670 |
|
670 | |||
@@ -722,7 +722,7 b' def _firstdescendants(repo, subset, x):' | |||||
722 | # Like ``descendants(set)`` but follows only the first parents. |
|
722 | # Like ``descendants(set)`` but follows only the first parents. | |
723 | return _descendants(repo, subset, x, followfirst=True) |
|
723 | return _descendants(repo, subset, x, followfirst=True) | |
724 |
|
724 | |||
725 | @predicate('destination([set])', safe=True) |
|
725 | @predicate('destination([set])', safe=True, weight=10) | |
726 | def destination(repo, subset, x): |
|
726 | def destination(repo, subset, x): | |
727 | """Changesets that were created by a graft, transplant or rebase operation, |
|
727 | """Changesets that were created by a graft, transplant or rebase operation, | |
728 | with the given revisions specified as the source. Omitting the optional set |
|
728 | with the given revisions specified as the source. Omitting the optional set | |
@@ -891,7 +891,7 b' def filelog(repo, subset, x):' | |||||
891 |
|
891 | |||
892 | return subset & s |
|
892 | return subset & s | |
893 |
|
893 | |||
894 | @predicate('first(set, [n])', safe=True, takeorder=True) |
|
894 | @predicate('first(set, [n])', safe=True, takeorder=True, weight=0) | |
895 | def first(repo, subset, x, order): |
|
895 | def first(repo, subset, x, order): | |
896 | """An alias for limit(). |
|
896 | """An alias for limit(). | |
897 | """ |
|
897 | """ | |
@@ -1012,7 +1012,7 b' def getall(repo, subset, x):' | |||||
1012 | getargs(x, 0, 0, _("all takes no arguments")) |
|
1012 | getargs(x, 0, 0, _("all takes no arguments")) | |
1013 | return subset & spanset(repo) # drop "null" if any |
|
1013 | return subset & spanset(repo) # drop "null" if any | |
1014 |
|
1014 | |||
1015 | @predicate('grep(regex)') |
|
1015 | @predicate('grep(regex)', weight=10) | |
1016 | def grep(repo, subset, x): |
|
1016 | def grep(repo, subset, x): | |
1017 | """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')`` |
|
1017 | """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')`` | |
1018 | to ensure special escape characters are handled correctly. Unlike |
|
1018 | to ensure special escape characters are handled correctly. Unlike | |
@@ -1097,7 +1097,7 b' def _matchfiles(repo, subset, x):' | |||||
1097 | 'exclude=%r, default=%r, rev=%r>', |
|
1097 | 'exclude=%r, default=%r, rev=%r>', | |
1098 | pats, inc, exc, default, rev)) |
|
1098 | pats, inc, exc, default, rev)) | |
1099 |
|
1099 | |||
1100 | @predicate('file(pattern)', safe=True) |
|
1100 | @predicate('file(pattern)', safe=True, weight=10) | |
1101 | def hasfile(repo, subset, x): |
|
1101 | def hasfile(repo, subset, x): | |
1102 | """Changesets affecting files matched by pattern. |
|
1102 | """Changesets affecting files matched by pattern. | |
1103 |
|
1103 | |||
@@ -1139,7 +1139,7 b' def hidden(repo, subset, x):' | |||||
1139 | hiddenrevs = repoview.filterrevs(repo, 'visible') |
|
1139 | hiddenrevs = repoview.filterrevs(repo, 'visible') | |
1140 | return subset & hiddenrevs |
|
1140 | return subset & hiddenrevs | |
1141 |
|
1141 | |||
1142 | @predicate('keyword(string)', safe=True) |
|
1142 | @predicate('keyword(string)', safe=True, weight=10) | |
1143 | def keyword(repo, subset, x): |
|
1143 | def keyword(repo, subset, x): | |
1144 | """Search commit message, user name, and names of changed files for |
|
1144 | """Search commit message, user name, and names of changed files for | |
1145 | string. The match is case-insensitive. |
|
1145 | string. The match is case-insensitive. | |
@@ -1157,7 +1157,7 b' def keyword(repo, subset, x):' | |||||
1157 |
|
1157 | |||
1158 | return subset.filter(matches, condrepr=('<keyword %r>', kw)) |
|
1158 | return subset.filter(matches, condrepr=('<keyword %r>', kw)) | |
1159 |
|
1159 | |||
1160 | @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True) |
|
1160 | @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0) | |
1161 | def limit(repo, subset, x, order): |
|
1161 | def limit(repo, subset, x, order): | |
1162 | """First n members of set, defaulting to 1, starting from offset. |
|
1162 | """First n members of set, defaulting to 1, starting from offset. | |
1163 | """ |
|
1163 | """ | |
@@ -1259,7 +1259,7 b' def minrev(repo, subset, x):' | |||||
1259 | pass |
|
1259 | pass | |
1260 | return baseset(datarepr=('<min %r, %r>', subset, os)) |
|
1260 | return baseset(datarepr=('<min %r, %r>', subset, os)) | |
1261 |
|
1261 | |||
1262 | @predicate('modifies(pattern)', safe=True) |
|
1262 | @predicate('modifies(pattern)', safe=True, weight=30) | |
1263 | def modifies(repo, subset, x): |
|
1263 | def modifies(repo, subset, x): | |
1264 | """Changesets modifying files matched by pattern. |
|
1264 | """Changesets modifying files matched by pattern. | |
1265 |
|
1265 | |||
@@ -1403,7 +1403,7 b' def origin(repo, subset, x):' | |||||
1403 | # some optimizations from the fact this is a baseset. |
|
1403 | # some optimizations from the fact this is a baseset. | |
1404 | return subset & o |
|
1404 | return subset & o | |
1405 |
|
1405 | |||
1406 | @predicate('outgoing([path])', safe=False) |
|
1406 | @predicate('outgoing([path])', safe=False, weight=10) | |
1407 | def outgoing(repo, subset, x): |
|
1407 | def outgoing(repo, subset, x): | |
1408 | """Changesets not found in the specified destination repository, or the |
|
1408 | """Changesets not found in the specified destination repository, or the | |
1409 | default push location. |
|
1409 | default push location. | |
@@ -1654,7 +1654,7 b' def remote(repo, subset, x):' | |||||
1654 | return baseset([r]) |
|
1654 | return baseset([r]) | |
1655 | return baseset() |
|
1655 | return baseset() | |
1656 |
|
1656 | |||
1657 | @predicate('removes(pattern)', safe=True) |
|
1657 | @predicate('removes(pattern)', safe=True, weight=30) | |
1658 | def removes(repo, subset, x): |
|
1658 | def removes(repo, subset, x): | |
1659 | """Changesets which remove files matching pattern. |
|
1659 | """Changesets which remove files matching pattern. | |
1660 |
|
1660 | |||
@@ -1794,7 +1794,7 b' def matching(repo, subset, x):' | |||||
1794 |
|
1794 | |||
1795 | return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs)) |
|
1795 | return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs)) | |
1796 |
|
1796 | |||
1797 | @predicate('reverse(set)', safe=True, takeorder=True) |
|
1797 | @predicate('reverse(set)', safe=True, takeorder=True, weight=0) | |
1798 | def reverse(repo, subset, x, order): |
|
1798 | def reverse(repo, subset, x, order): | |
1799 | """Reverse order of set. |
|
1799 | """Reverse order of set. | |
1800 | """ |
|
1800 | """ | |
@@ -1862,7 +1862,8 b' def _getsortargs(x):' | |||||
1862 |
|
1862 | |||
1863 | return args['set'], keyflags, opts |
|
1863 | return args['set'], keyflags, opts | |
1864 |
|
1864 | |||
1865 |
@predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True |
|
1865 | @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True, | |
|
1866 | weight=10) | |||
1866 | def sort(repo, subset, x, order): |
|
1867 | def sort(repo, subset, x, order): | |
1867 | """Sort set by keys. The default sort order is ascending, specify a key |
|
1868 | """Sort set by keys. The default sort order is ascending, specify a key | |
1868 | as ``-key`` to sort in descending order. |
|
1869 | as ``-key`` to sort in descending order. | |
@@ -2033,7 +2034,7 b' def orphan(repo, subset, x):' | |||||
2033 | return subset & orphan |
|
2034 | return subset & orphan | |
2034 |
|
2035 | |||
2035 |
|
2036 | |||
2036 | @predicate('user(string)', safe=True) |
|
2037 | @predicate('user(string)', safe=True, weight=10) | |
2037 | def user(repo, subset, x): |
|
2038 | def user(repo, subset, x): | |
2038 | """User name contains string. The match is case-insensitive. |
|
2039 | """User name contains string. The match is case-insensitive. | |
2039 |
|
2040 | |||
@@ -2042,7 +2043,7 b' def user(repo, subset, x):' | |||||
2042 | """ |
|
2043 | """ | |
2043 | return author(repo, subset, x) |
|
2044 | return author(repo, subset, x) | |
2044 |
|
2045 | |||
2045 | @predicate('wdir()', safe=True) |
|
2046 | @predicate('wdir()', safe=True, weight=0) | |
2046 | def wdir(repo, subset, x): |
|
2047 | def wdir(repo, subset, x): | |
2047 | """Working directory. (EXPERIMENTAL)""" |
|
2048 | """Working directory. (EXPERIMENTAL)""" | |
2048 | # i18n: "wdir" is a keyword |
|
2049 | # i18n: "wdir" is a keyword | |
@@ -2097,7 +2098,7 b' def _orderedintlist(repo, subset, x):' | |||||
2097 | return baseset([r for r in ls if r in s]) |
|
2098 | return baseset([r for r in ls if r in s]) | |
2098 |
|
2099 | |||
2099 | # for internal use |
|
2100 | # for internal use | |
2100 | @predicate('_intlist', safe=True, takeorder=True) |
|
2101 | @predicate('_intlist', safe=True, takeorder=True, weight=0) | |
2101 | def _intlist(repo, subset, x, order): |
|
2102 | def _intlist(repo, subset, x, order): | |
2102 | if order == followorder: |
|
2103 | if order == followorder: | |
2103 | # slow path to take the subset order |
|
2104 | # slow path to take the subset order |
@@ -49,6 +49,8 b' elements = {' | |||||
49 |
|
49 | |||
50 | keywords = {'and', 'or', 'not'} |
|
50 | keywords = {'and', 'or', 'not'} | |
51 |
|
51 | |||
|
52 | symbols = {} | |||
|
53 | ||||
52 | _quoteletters = {'"', "'"} |
|
54 | _quoteletters = {'"', "'"} | |
53 | _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%")) |
|
55 | _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%")) | |
54 |
|
56 | |||
@@ -441,21 +443,7 b' def _optimize(x):' | |||||
441 | elif op == 'func': |
|
443 | elif op == 'func': | |
442 | f = getsymbol(x[1]) |
|
444 | f = getsymbol(x[1]) | |
443 | wa, ta = _optimize(x[2]) |
|
445 | wa, ta = _optimize(x[2]) | |
444 | if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep', |
|
446 | w = getattr(symbols.get(f), '_weight', 1) | |
445 | 'keyword', 'outgoing', 'user', 'destination'): |
|
|||
446 | w = 10 # slow |
|
|||
447 | elif f in ('modifies', 'adds', 'removes'): |
|
|||
448 | w = 30 # slower |
|
|||
449 | elif f == "contains": |
|
|||
450 | w = 100 # very slow |
|
|||
451 | elif f == "ancestor": |
|
|||
452 | w = 0.5 |
|
|||
453 | elif f in ('reverse', 'limit', 'first', 'wdir', '_intlist'): |
|
|||
454 | w = 0 |
|
|||
455 | elif f == "sort": |
|
|||
456 | w = 10 # assume most sorts look at changelog |
|
|||
457 | else: |
|
|||
458 | w = 1 |
|
|||
459 | return w + wa, (op, x[1], ta) |
|
447 | return w + wa, (op, x[1], ta) | |
460 | raise ValueError('invalid operator %r' % op) |
|
448 | raise ValueError('invalid operator %r' % op) | |
461 |
|
449 |
General Comments 0
You need to be logged in to leave comments.
Login now