##// END OF EJS Templates
revset: add partial support for ancestor(wdir())...
Yuya Nishihara -
r38541:54d7aaa2 default
parent child Browse files
Show More
@@ -1,2247 +1,2248
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 dagop,
14 dagop,
15 destutil,
15 destutil,
16 encoding,
16 encoding,
17 error,
17 error,
18 hbisect,
18 hbisect,
19 match as matchmod,
19 match as matchmod,
20 node,
20 node,
21 obsolete as obsmod,
21 obsolete as obsmod,
22 obsutil,
22 obsutil,
23 pathutil,
23 pathutil,
24 phases,
24 phases,
25 pycompat,
25 pycompat,
26 registrar,
26 registrar,
27 repoview,
27 repoview,
28 revsetlang,
28 revsetlang,
29 scmutil,
29 scmutil,
30 smartset,
30 smartset,
31 stack as stackmod,
31 stack as stackmod,
32 util,
32 util,
33 )
33 )
34 from .utils import (
34 from .utils import (
35 dateutil,
35 dateutil,
36 stringutil,
36 stringutil,
37 )
37 )
38
38
39 # helpers for processing parsed tree
39 # helpers for processing parsed tree
40 getsymbol = revsetlang.getsymbol
40 getsymbol = revsetlang.getsymbol
41 getstring = revsetlang.getstring
41 getstring = revsetlang.getstring
42 getinteger = revsetlang.getinteger
42 getinteger = revsetlang.getinteger
43 getboolean = revsetlang.getboolean
43 getboolean = revsetlang.getboolean
44 getlist = revsetlang.getlist
44 getlist = revsetlang.getlist
45 getrange = revsetlang.getrange
45 getrange = revsetlang.getrange
46 getargs = revsetlang.getargs
46 getargs = revsetlang.getargs
47 getargsdict = revsetlang.getargsdict
47 getargsdict = revsetlang.getargsdict
48
48
49 baseset = smartset.baseset
49 baseset = smartset.baseset
50 generatorset = smartset.generatorset
50 generatorset = smartset.generatorset
51 spanset = smartset.spanset
51 spanset = smartset.spanset
52 fullreposet = smartset.fullreposet
52 fullreposet = smartset.fullreposet
53
53
54 # Constants for ordering requirement, used in getset():
54 # Constants for ordering requirement, used in getset():
55 #
55 #
56 # If 'define', any nested functions and operations MAY change the ordering of
56 # If 'define', any nested functions and operations MAY change the ordering of
57 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
57 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
58 # it). If 'follow', any nested functions and operations MUST take the ordering
58 # it). If 'follow', any nested functions and operations MUST take the ordering
59 # specified by the first operand to the '&' operator.
59 # specified by the first operand to the '&' operator.
60 #
60 #
61 # For instance,
61 # For instance,
62 #
62 #
63 # X & (Y | Z)
63 # X & (Y | Z)
64 # ^ ^^^^^^^
64 # ^ ^^^^^^^
65 # | follow
65 # | follow
66 # define
66 # define
67 #
67 #
68 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
68 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
69 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
69 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
70 #
70 #
71 # 'any' means the order doesn't matter. For instance,
71 # 'any' means the order doesn't matter. For instance,
72 #
72 #
73 # (X & !Y) | ancestors(Z)
73 # (X & !Y) | ancestors(Z)
74 # ^ ^
74 # ^ ^
75 # any any
75 # any any
76 #
76 #
77 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
77 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
78 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
78 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
79 # since 'ancestors' does not care about the order of its argument.
79 # since 'ancestors' does not care about the order of its argument.
80 #
80 #
81 # Currently, most revsets do not care about the order, so 'define' is
81 # Currently, most revsets do not care about the order, so 'define' is
82 # equivalent to 'follow' for them, and the resulting order is based on the
82 # equivalent to 'follow' for them, and the resulting order is based on the
83 # 'subset' parameter passed down to them:
83 # 'subset' parameter passed down to them:
84 #
84 #
85 # m = revset.match(...)
85 # m = revset.match(...)
86 # m(repo, subset, order=defineorder)
86 # m(repo, subset, order=defineorder)
87 # ^^^^^^
87 # ^^^^^^
88 # For most revsets, 'define' means using the order this subset provides
88 # For most revsets, 'define' means using the order this subset provides
89 #
89 #
90 # There are a few revsets that always redefine the order if 'define' is
90 # There are a few revsets that always redefine the order if 'define' is
91 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
91 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
92 anyorder = 'any' # don't care the order, could be even random-shuffled
92 anyorder = 'any' # don't care the order, could be even random-shuffled
93 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
93 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
94 followorder = 'follow' # MUST follow the current order
94 followorder = 'follow' # MUST follow the current order
95
95
96 # helpers
96 # helpers
97
97
98 def getset(repo, subset, x, order=defineorder):
98 def getset(repo, subset, x, order=defineorder):
99 if not x:
99 if not x:
100 raise error.ParseError(_("missing argument"))
100 raise error.ParseError(_("missing argument"))
101 return methods[x[0]](repo, subset, *x[1:], order=order)
101 return methods[x[0]](repo, subset, *x[1:], order=order)
102
102
103 def _getrevsource(repo, r):
103 def _getrevsource(repo, r):
104 extra = repo[r].extra()
104 extra = repo[r].extra()
105 for label in ('source', 'transplant_source', 'rebase_source'):
105 for label in ('source', 'transplant_source', 'rebase_source'):
106 if label in extra:
106 if label in extra:
107 try:
107 try:
108 return repo[extra[label]].rev()
108 return repo[extra[label]].rev()
109 except error.RepoLookupError:
109 except error.RepoLookupError:
110 pass
110 pass
111 return None
111 return None
112
112
113 def _sortedb(xs):
113 def _sortedb(xs):
114 return sorted(util.rapply(pycompat.maybebytestr, xs))
114 return sorted(util.rapply(pycompat.maybebytestr, xs))
115
115
116 # operator methods
116 # operator methods
117
117
118 def stringset(repo, subset, x, order):
118 def stringset(repo, subset, x, order):
119 if not x:
119 if not x:
120 raise error.ParseError(_("empty string is not a valid revision"))
120 raise error.ParseError(_("empty string is not a valid revision"))
121 x = scmutil.intrev(scmutil.revsymbol(repo, x))
121 x = scmutil.intrev(scmutil.revsymbol(repo, x))
122 if (x in subset
122 if (x in subset
123 or x == node.nullrev and isinstance(subset, fullreposet)):
123 or x == node.nullrev and isinstance(subset, fullreposet)):
124 return baseset([x])
124 return baseset([x])
125 return baseset()
125 return baseset()
126
126
127 def rangeset(repo, subset, x, y, order):
127 def rangeset(repo, subset, x, y, order):
128 m = getset(repo, fullreposet(repo), x)
128 m = getset(repo, fullreposet(repo), x)
129 n = getset(repo, fullreposet(repo), y)
129 n = getset(repo, fullreposet(repo), y)
130
130
131 if not m or not n:
131 if not m or not n:
132 return baseset()
132 return baseset()
133 return _makerangeset(repo, subset, m.first(), n.last(), order)
133 return _makerangeset(repo, subset, m.first(), n.last(), order)
134
134
135 def rangeall(repo, subset, x, order):
135 def rangeall(repo, subset, x, order):
136 assert x is None
136 assert x is None
137 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order)
137 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order)
138
138
139 def rangepre(repo, subset, y, order):
139 def rangepre(repo, subset, y, order):
140 # ':y' can't be rewritten to '0:y' since '0' may be hidden
140 # ':y' can't be rewritten to '0:y' since '0' may be hidden
141 n = getset(repo, fullreposet(repo), y)
141 n = getset(repo, fullreposet(repo), y)
142 if not n:
142 if not n:
143 return baseset()
143 return baseset()
144 return _makerangeset(repo, subset, 0, n.last(), order)
144 return _makerangeset(repo, subset, 0, n.last(), order)
145
145
146 def rangepost(repo, subset, x, order):
146 def rangepost(repo, subset, x, order):
147 m = getset(repo, fullreposet(repo), x)
147 m = getset(repo, fullreposet(repo), x)
148 if not m:
148 if not m:
149 return baseset()
149 return baseset()
150 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(),
150 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(),
151 order)
151 order)
152
152
153 def _makerangeset(repo, subset, m, n, order):
153 def _makerangeset(repo, subset, m, n, order):
154 if m == n:
154 if m == n:
155 r = baseset([m])
155 r = baseset([m])
156 elif n == node.wdirrev:
156 elif n == node.wdirrev:
157 r = spanset(repo, m, len(repo)) + baseset([n])
157 r = spanset(repo, m, len(repo)) + baseset([n])
158 elif m == node.wdirrev:
158 elif m == node.wdirrev:
159 r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
159 r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
160 elif m < n:
160 elif m < n:
161 r = spanset(repo, m, n + 1)
161 r = spanset(repo, m, n + 1)
162 else:
162 else:
163 r = spanset(repo, m, n - 1)
163 r = spanset(repo, m, n - 1)
164
164
165 if order == defineorder:
165 if order == defineorder:
166 return r & subset
166 return r & subset
167 else:
167 else:
168 # carrying the sorting over when possible would be more efficient
168 # carrying the sorting over when possible would be more efficient
169 return subset & r
169 return subset & r
170
170
171 def dagrange(repo, subset, x, y, order):
171 def dagrange(repo, subset, x, y, order):
172 r = fullreposet(repo)
172 r = fullreposet(repo)
173 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
173 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
174 includepath=True)
174 includepath=True)
175 return subset & xs
175 return subset & xs
176
176
177 def andset(repo, subset, x, y, order):
177 def andset(repo, subset, x, y, order):
178 if order == anyorder:
178 if order == anyorder:
179 yorder = anyorder
179 yorder = anyorder
180 else:
180 else:
181 yorder = followorder
181 yorder = followorder
182 return getset(repo, getset(repo, subset, x, order), y, yorder)
182 return getset(repo, getset(repo, subset, x, order), y, yorder)
183
183
184 def andsmallyset(repo, subset, x, y, order):
184 def andsmallyset(repo, subset, x, y, order):
185 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
185 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
186 if order == anyorder:
186 if order == anyorder:
187 yorder = anyorder
187 yorder = anyorder
188 else:
188 else:
189 yorder = followorder
189 yorder = followorder
190 return getset(repo, getset(repo, subset, y, yorder), x, order)
190 return getset(repo, getset(repo, subset, y, yorder), x, order)
191
191
192 def differenceset(repo, subset, x, y, order):
192 def differenceset(repo, subset, x, y, order):
193 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
193 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
194
194
195 def _orsetlist(repo, subset, xs, order):
195 def _orsetlist(repo, subset, xs, order):
196 assert xs
196 assert xs
197 if len(xs) == 1:
197 if len(xs) == 1:
198 return getset(repo, subset, xs[0], order)
198 return getset(repo, subset, xs[0], order)
199 p = len(xs) // 2
199 p = len(xs) // 2
200 a = _orsetlist(repo, subset, xs[:p], order)
200 a = _orsetlist(repo, subset, xs[:p], order)
201 b = _orsetlist(repo, subset, xs[p:], order)
201 b = _orsetlist(repo, subset, xs[p:], order)
202 return a + b
202 return a + b
203
203
204 def orset(repo, subset, x, order):
204 def orset(repo, subset, x, order):
205 xs = getlist(x)
205 xs = getlist(x)
206 if not xs:
206 if not xs:
207 return baseset()
207 return baseset()
208 if order == followorder:
208 if order == followorder:
209 # slow path to take the subset order
209 # slow path to take the subset order
210 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
210 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
211 else:
211 else:
212 return _orsetlist(repo, subset, xs, order)
212 return _orsetlist(repo, subset, xs, order)
213
213
214 def notset(repo, subset, x, order):
214 def notset(repo, subset, x, order):
215 return subset - getset(repo, subset, x, anyorder)
215 return subset - getset(repo, subset, x, anyorder)
216
216
217 def relationset(repo, subset, x, y, order):
217 def relationset(repo, subset, x, y, order):
218 raise error.ParseError(_("can't use a relation in this context"))
218 raise error.ParseError(_("can't use a relation in this context"))
219
219
220 def relsubscriptset(repo, subset, x, y, z, order):
220 def relsubscriptset(repo, subset, x, y, z, order):
221 # this is pretty basic implementation of 'x#y[z]' operator, still
221 # this is pretty basic implementation of 'x#y[z]' operator, still
222 # experimental so undocumented. see the wiki for further ideas.
222 # experimental so undocumented. see the wiki for further ideas.
223 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
223 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
224 rel = getsymbol(y)
224 rel = getsymbol(y)
225 n = getinteger(z, _("relation subscript must be an integer"))
225 n = getinteger(z, _("relation subscript must be an integer"))
226
226
227 # TODO: perhaps this should be a table of relation functions
227 # TODO: perhaps this should be a table of relation functions
228 if rel in ('g', 'generations'):
228 if rel in ('g', 'generations'):
229 # TODO: support range, rewrite tests, and drop startdepth argument
229 # TODO: support range, rewrite tests, and drop startdepth argument
230 # from ancestors() and descendants() predicates
230 # from ancestors() and descendants() predicates
231 if n <= 0:
231 if n <= 0:
232 n = -n
232 n = -n
233 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
233 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
234 else:
234 else:
235 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
235 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
236
236
237 raise error.UnknownIdentifier(rel, ['generations'])
237 raise error.UnknownIdentifier(rel, ['generations'])
238
238
239 def subscriptset(repo, subset, x, y, order):
239 def subscriptset(repo, subset, x, y, order):
240 raise error.ParseError(_("can't use a subscript in this context"))
240 raise error.ParseError(_("can't use a subscript in this context"))
241
241
242 def listset(repo, subset, *xs, **opts):
242 def listset(repo, subset, *xs, **opts):
243 raise error.ParseError(_("can't use a list in this context"),
243 raise error.ParseError(_("can't use a list in this context"),
244 hint=_('see hg help "revsets.x or y"'))
244 hint=_('see hg help "revsets.x or y"'))
245
245
246 def keyvaluepair(repo, subset, k, v, order):
246 def keyvaluepair(repo, subset, k, v, order):
247 raise error.ParseError(_("can't use a key-value pair in this context"))
247 raise error.ParseError(_("can't use a key-value pair in this context"))
248
248
249 def func(repo, subset, a, b, order):
249 def func(repo, subset, a, b, order):
250 f = getsymbol(a)
250 f = getsymbol(a)
251 if f in symbols:
251 if f in symbols:
252 func = symbols[f]
252 func = symbols[f]
253 if getattr(func, '_takeorder', False):
253 if getattr(func, '_takeorder', False):
254 return func(repo, subset, b, order)
254 return func(repo, subset, b, order)
255 return func(repo, subset, b)
255 return func(repo, subset, b)
256
256
257 keep = lambda fn: getattr(fn, '__doc__', None) is not None
257 keep = lambda fn: getattr(fn, '__doc__', None) is not None
258
258
259 syms = [s for (s, fn) in symbols.items() if keep(fn)]
259 syms = [s for (s, fn) in symbols.items() if keep(fn)]
260 raise error.UnknownIdentifier(f, syms)
260 raise error.UnknownIdentifier(f, syms)
261
261
262 # functions
262 # functions
263
263
264 # symbols are callables like:
264 # symbols are callables like:
265 # fn(repo, subset, x)
265 # fn(repo, subset, x)
266 # with:
266 # with:
267 # repo - current repository instance
267 # repo - current repository instance
268 # subset - of revisions to be examined
268 # subset - of revisions to be examined
269 # x - argument in tree form
269 # x - argument in tree form
270 symbols = revsetlang.symbols
270 symbols = revsetlang.symbols
271
271
272 # symbols which can't be used for a DoS attack for any given input
272 # symbols which can't be used for a DoS attack for any given input
273 # (e.g. those which accept regexes as plain strings shouldn't be included)
273 # (e.g. those which accept regexes as plain strings shouldn't be included)
274 # functions that just return a lot of changesets (like all) don't count here
274 # functions that just return a lot of changesets (like all) don't count here
275 safesymbols = set()
275 safesymbols = set()
276
276
277 predicate = registrar.revsetpredicate()
277 predicate = registrar.revsetpredicate()
278
278
279 @predicate('_destupdate')
279 @predicate('_destupdate')
280 def _destupdate(repo, subset, x):
280 def _destupdate(repo, subset, x):
281 # experimental revset for update destination
281 # experimental revset for update destination
282 args = getargsdict(x, 'limit', 'clean')
282 args = getargsdict(x, 'limit', 'clean')
283 return subset & baseset([destutil.destupdate(repo,
283 return subset & baseset([destutil.destupdate(repo,
284 **pycompat.strkwargs(args))[0]])
284 **pycompat.strkwargs(args))[0]])
285
285
286 @predicate('_destmerge')
286 @predicate('_destmerge')
287 def _destmerge(repo, subset, x):
287 def _destmerge(repo, subset, x):
288 # experimental revset for merge destination
288 # experimental revset for merge destination
289 sourceset = None
289 sourceset = None
290 if x is not None:
290 if x is not None:
291 sourceset = getset(repo, fullreposet(repo), x)
291 sourceset = getset(repo, fullreposet(repo), x)
292 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
292 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
293
293
294 @predicate('adds(pattern)', safe=True, weight=30)
294 @predicate('adds(pattern)', safe=True, weight=30)
295 def adds(repo, subset, x):
295 def adds(repo, subset, x):
296 """Changesets that add a file matching pattern.
296 """Changesets that add a file matching pattern.
297
297
298 The pattern without explicit kind like ``glob:`` is expected to be
298 The pattern without explicit kind like ``glob:`` is expected to be
299 relative to the current directory and match against a file or a
299 relative to the current directory and match against a file or a
300 directory.
300 directory.
301 """
301 """
302 # i18n: "adds" is a keyword
302 # i18n: "adds" is a keyword
303 pat = getstring(x, _("adds requires a pattern"))
303 pat = getstring(x, _("adds requires a pattern"))
304 return checkstatus(repo, subset, pat, 1)
304 return checkstatus(repo, subset, pat, 1)
305
305
306 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
306 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
307 def ancestor(repo, subset, x):
307 def ancestor(repo, subset, x):
308 """A greatest common ancestor of the changesets.
308 """A greatest common ancestor of the changesets.
309
309
310 Accepts 0 or more changesets.
310 Accepts 0 or more changesets.
311 Will return empty list when passed no args.
311 Will return empty list when passed no args.
312 Greatest common ancestor of a single changeset is that changeset.
312 Greatest common ancestor of a single changeset is that changeset.
313 """
313 """
314 reviter = iter(orset(repo, fullreposet(repo), x, order=anyorder))
314 reviter = iter(orset(repo, fullreposet(repo), x, order=anyorder))
315 try:
315 try:
316 anc = repo[next(reviter)]
316 anc = repo[next(reviter)]
317 except StopIteration:
317 except StopIteration:
318 return baseset()
318 return baseset()
319 for r in reviter:
319 for r in reviter:
320 anc = anc.ancestor(repo[r])
320 anc = anc.ancestor(repo[r])
321
321
322 if anc.rev() in subset:
322 r = scmutil.intrev(anc)
323 return baseset([anc.rev()])
323 if r in subset:
324 return baseset([r])
324 return baseset()
325 return baseset()
325
326
326 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
327 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
327 stopdepth=None):
328 stopdepth=None):
328 heads = getset(repo, fullreposet(repo), x)
329 heads = getset(repo, fullreposet(repo), x)
329 if not heads:
330 if not heads:
330 return baseset()
331 return baseset()
331 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
332 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
332 return subset & s
333 return subset & s
333
334
334 @predicate('ancestors(set[, depth])', safe=True)
335 @predicate('ancestors(set[, depth])', safe=True)
335 def ancestors(repo, subset, x):
336 def ancestors(repo, subset, x):
336 """Changesets that are ancestors of changesets in set, including the
337 """Changesets that are ancestors of changesets in set, including the
337 given changesets themselves.
338 given changesets themselves.
338
339
339 If depth is specified, the result only includes changesets up to
340 If depth is specified, the result only includes changesets up to
340 the specified generation.
341 the specified generation.
341 """
342 """
342 # startdepth is for internal use only until we can decide the UI
343 # startdepth is for internal use only until we can decide the UI
343 args = getargsdict(x, 'ancestors', 'set depth startdepth')
344 args = getargsdict(x, 'ancestors', 'set depth startdepth')
344 if 'set' not in args:
345 if 'set' not in args:
345 # i18n: "ancestors" is a keyword
346 # i18n: "ancestors" is a keyword
346 raise error.ParseError(_('ancestors takes at least 1 argument'))
347 raise error.ParseError(_('ancestors takes at least 1 argument'))
347 startdepth = stopdepth = None
348 startdepth = stopdepth = None
348 if 'startdepth' in args:
349 if 'startdepth' in args:
349 n = getinteger(args['startdepth'],
350 n = getinteger(args['startdepth'],
350 "ancestors expects an integer startdepth")
351 "ancestors expects an integer startdepth")
351 if n < 0:
352 if n < 0:
352 raise error.ParseError("negative startdepth")
353 raise error.ParseError("negative startdepth")
353 startdepth = n
354 startdepth = n
354 if 'depth' in args:
355 if 'depth' in args:
355 # i18n: "ancestors" is a keyword
356 # i18n: "ancestors" is a keyword
356 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
357 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
357 if n < 0:
358 if n < 0:
358 raise error.ParseError(_("negative depth"))
359 raise error.ParseError(_("negative depth"))
359 stopdepth = n + 1
360 stopdepth = n + 1
360 return _ancestors(repo, subset, args['set'],
361 return _ancestors(repo, subset, args['set'],
361 startdepth=startdepth, stopdepth=stopdepth)
362 startdepth=startdepth, stopdepth=stopdepth)
362
363
363 @predicate('_firstancestors', safe=True)
364 @predicate('_firstancestors', safe=True)
364 def _firstancestors(repo, subset, x):
365 def _firstancestors(repo, subset, x):
365 # ``_firstancestors(set)``
366 # ``_firstancestors(set)``
366 # Like ``ancestors(set)`` but follows only the first parents.
367 # Like ``ancestors(set)`` but follows only the first parents.
367 return _ancestors(repo, subset, x, followfirst=True)
368 return _ancestors(repo, subset, x, followfirst=True)
368
369
369 def _childrenspec(repo, subset, x, n, order):
370 def _childrenspec(repo, subset, x, n, order):
370 """Changesets that are the Nth child of a changeset
371 """Changesets that are the Nth child of a changeset
371 in set.
372 in set.
372 """
373 """
373 cs = set()
374 cs = set()
374 for r in getset(repo, fullreposet(repo), x):
375 for r in getset(repo, fullreposet(repo), x):
375 for i in range(n):
376 for i in range(n):
376 c = repo[r].children()
377 c = repo[r].children()
377 if len(c) == 0:
378 if len(c) == 0:
378 break
379 break
379 if len(c) > 1:
380 if len(c) > 1:
380 raise error.RepoLookupError(
381 raise error.RepoLookupError(
381 _("revision in set has more than one child"))
382 _("revision in set has more than one child"))
382 r = c[0].rev()
383 r = c[0].rev()
383 else:
384 else:
384 cs.add(r)
385 cs.add(r)
385 return subset & cs
386 return subset & cs
386
387
387 def ancestorspec(repo, subset, x, n, order):
388 def ancestorspec(repo, subset, x, n, order):
388 """``set~n``
389 """``set~n``
389 Changesets that are the Nth ancestor (first parents only) of a changeset
390 Changesets that are the Nth ancestor (first parents only) of a changeset
390 in set.
391 in set.
391 """
392 """
392 n = getinteger(n, _("~ expects a number"))
393 n = getinteger(n, _("~ expects a number"))
393 if n < 0:
394 if n < 0:
394 # children lookup
395 # children lookup
395 return _childrenspec(repo, subset, x, -n, order)
396 return _childrenspec(repo, subset, x, -n, order)
396 ps = set()
397 ps = set()
397 cl = repo.changelog
398 cl = repo.changelog
398 for r in getset(repo, fullreposet(repo), x):
399 for r in getset(repo, fullreposet(repo), x):
399 for i in range(n):
400 for i in range(n):
400 try:
401 try:
401 r = cl.parentrevs(r)[0]
402 r = cl.parentrevs(r)[0]
402 except error.WdirUnsupported:
403 except error.WdirUnsupported:
403 r = repo[r].parents()[0].rev()
404 r = repo[r].parents()[0].rev()
404 ps.add(r)
405 ps.add(r)
405 return subset & ps
406 return subset & ps
406
407
407 @predicate('author(string)', safe=True, weight=10)
408 @predicate('author(string)', safe=True, weight=10)
408 def author(repo, subset, x):
409 def author(repo, subset, x):
409 """Alias for ``user(string)``.
410 """Alias for ``user(string)``.
410 """
411 """
411 # i18n: "author" is a keyword
412 # i18n: "author" is a keyword
412 n = getstring(x, _("author requires a string"))
413 n = getstring(x, _("author requires a string"))
413 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
414 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
414 return subset.filter(lambda x: matcher(repo[x].user()),
415 return subset.filter(lambda x: matcher(repo[x].user()),
415 condrepr=('<user %r>', n))
416 condrepr=('<user %r>', n))
416
417
417 @predicate('bisect(string)', safe=True)
418 @predicate('bisect(string)', safe=True)
418 def bisect(repo, subset, x):
419 def bisect(repo, subset, x):
419 """Changesets marked in the specified bisect status:
420 """Changesets marked in the specified bisect status:
420
421
421 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
422 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
422 - ``goods``, ``bads`` : csets topologically good/bad
423 - ``goods``, ``bads`` : csets topologically good/bad
423 - ``range`` : csets taking part in the bisection
424 - ``range`` : csets taking part in the bisection
424 - ``pruned`` : csets that are goods, bads or skipped
425 - ``pruned`` : csets that are goods, bads or skipped
425 - ``untested`` : csets whose fate is yet unknown
426 - ``untested`` : csets whose fate is yet unknown
426 - ``ignored`` : csets ignored due to DAG topology
427 - ``ignored`` : csets ignored due to DAG topology
427 - ``current`` : the cset currently being bisected
428 - ``current`` : the cset currently being bisected
428 """
429 """
429 # i18n: "bisect" is a keyword
430 # i18n: "bisect" is a keyword
430 status = getstring(x, _("bisect requires a string")).lower()
431 status = getstring(x, _("bisect requires a string")).lower()
431 state = set(hbisect.get(repo, status))
432 state = set(hbisect.get(repo, status))
432 return subset & state
433 return subset & state
433
434
434 # Backward-compatibility
435 # Backward-compatibility
435 # - no help entry so that we do not advertise it any more
436 # - no help entry so that we do not advertise it any more
436 @predicate('bisected', safe=True)
437 @predicate('bisected', safe=True)
437 def bisected(repo, subset, x):
438 def bisected(repo, subset, x):
438 return bisect(repo, subset, x)
439 return bisect(repo, subset, x)
439
440
440 @predicate('bookmark([name])', safe=True)
441 @predicate('bookmark([name])', safe=True)
441 def bookmark(repo, subset, x):
442 def bookmark(repo, subset, x):
442 """The named bookmark or all bookmarks.
443 """The named bookmark or all bookmarks.
443
444
444 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
445 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
445 """
446 """
446 # i18n: "bookmark" is a keyword
447 # i18n: "bookmark" is a keyword
447 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
448 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
448 if args:
449 if args:
449 bm = getstring(args[0],
450 bm = getstring(args[0],
450 # i18n: "bookmark" is a keyword
451 # i18n: "bookmark" is a keyword
451 _('the argument to bookmark must be a string'))
452 _('the argument to bookmark must be a string'))
452 kind, pattern, matcher = stringutil.stringmatcher(bm)
453 kind, pattern, matcher = stringutil.stringmatcher(bm)
453 bms = set()
454 bms = set()
454 if kind == 'literal':
455 if kind == 'literal':
455 bmrev = repo._bookmarks.get(pattern, None)
456 bmrev = repo._bookmarks.get(pattern, None)
456 if not bmrev:
457 if not bmrev:
457 raise error.RepoLookupError(_("bookmark '%s' does not exist")
458 raise error.RepoLookupError(_("bookmark '%s' does not exist")
458 % pattern)
459 % pattern)
459 bms.add(repo[bmrev].rev())
460 bms.add(repo[bmrev].rev())
460 else:
461 else:
461 matchrevs = set()
462 matchrevs = set()
462 for name, bmrev in repo._bookmarks.iteritems():
463 for name, bmrev in repo._bookmarks.iteritems():
463 if matcher(name):
464 if matcher(name):
464 matchrevs.add(bmrev)
465 matchrevs.add(bmrev)
465 if not matchrevs:
466 if not matchrevs:
466 raise error.RepoLookupError(_("no bookmarks exist"
467 raise error.RepoLookupError(_("no bookmarks exist"
467 " that match '%s'") % pattern)
468 " that match '%s'") % pattern)
468 for bmrev in matchrevs:
469 for bmrev in matchrevs:
469 bms.add(repo[bmrev].rev())
470 bms.add(repo[bmrev].rev())
470 else:
471 else:
471 bms = {repo[r].rev() for r in repo._bookmarks.values()}
472 bms = {repo[r].rev() for r in repo._bookmarks.values()}
472 bms -= {node.nullrev}
473 bms -= {node.nullrev}
473 return subset & bms
474 return subset & bms
474
475
475 @predicate('branch(string or set)', safe=True, weight=10)
476 @predicate('branch(string or set)', safe=True, weight=10)
476 def branch(repo, subset, x):
477 def branch(repo, subset, x):
477 """
478 """
478 All changesets belonging to the given branch or the branches of the given
479 All changesets belonging to the given branch or the branches of the given
479 changesets.
480 changesets.
480
481
481 Pattern matching is supported for `string`. See
482 Pattern matching is supported for `string`. See
482 :hg:`help revisions.patterns`.
483 :hg:`help revisions.patterns`.
483 """
484 """
484 getbi = repo.revbranchcache().branchinfo
485 getbi = repo.revbranchcache().branchinfo
485 def getbranch(r):
486 def getbranch(r):
486 try:
487 try:
487 return getbi(r)[0]
488 return getbi(r)[0]
488 except error.WdirUnsupported:
489 except error.WdirUnsupported:
489 return repo[r].branch()
490 return repo[r].branch()
490
491
491 try:
492 try:
492 b = getstring(x, '')
493 b = getstring(x, '')
493 except error.ParseError:
494 except error.ParseError:
494 # not a string, but another revspec, e.g. tip()
495 # not a string, but another revspec, e.g. tip()
495 pass
496 pass
496 else:
497 else:
497 kind, pattern, matcher = stringutil.stringmatcher(b)
498 kind, pattern, matcher = stringutil.stringmatcher(b)
498 if kind == 'literal':
499 if kind == 'literal':
499 # note: falls through to the revspec case if no branch with
500 # note: falls through to the revspec case if no branch with
500 # this name exists and pattern kind is not specified explicitly
501 # this name exists and pattern kind is not specified explicitly
501 if pattern in repo.branchmap():
502 if pattern in repo.branchmap():
502 return subset.filter(lambda r: matcher(getbranch(r)),
503 return subset.filter(lambda r: matcher(getbranch(r)),
503 condrepr=('<branch %r>', b))
504 condrepr=('<branch %r>', b))
504 if b.startswith('literal:'):
505 if b.startswith('literal:'):
505 raise error.RepoLookupError(_("branch '%s' does not exist")
506 raise error.RepoLookupError(_("branch '%s' does not exist")
506 % pattern)
507 % pattern)
507 else:
508 else:
508 return subset.filter(lambda r: matcher(getbranch(r)),
509 return subset.filter(lambda r: matcher(getbranch(r)),
509 condrepr=('<branch %r>', b))
510 condrepr=('<branch %r>', b))
510
511
511 s = getset(repo, fullreposet(repo), x)
512 s = getset(repo, fullreposet(repo), x)
512 b = set()
513 b = set()
513 for r in s:
514 for r in s:
514 b.add(getbranch(r))
515 b.add(getbranch(r))
515 c = s.__contains__
516 c = s.__contains__
516 return subset.filter(lambda r: c(r) or getbranch(r) in b,
517 return subset.filter(lambda r: c(r) or getbranch(r) in b,
517 condrepr=lambda: '<branch %r>' % _sortedb(b))
518 condrepr=lambda: '<branch %r>' % _sortedb(b))
518
519
519 @predicate('phasedivergent()', safe=True)
520 @predicate('phasedivergent()', safe=True)
520 def phasedivergent(repo, subset, x):
521 def phasedivergent(repo, subset, x):
521 """Mutable changesets marked as successors of public changesets.
522 """Mutable changesets marked as successors of public changesets.
522
523
523 Only non-public and non-obsolete changesets can be `phasedivergent`.
524 Only non-public and non-obsolete changesets can be `phasedivergent`.
524 (EXPERIMENTAL)
525 (EXPERIMENTAL)
525 """
526 """
526 # i18n: "phasedivergent" is a keyword
527 # i18n: "phasedivergent" is a keyword
527 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
528 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
528 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
529 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
529 return subset & phasedivergent
530 return subset & phasedivergent
530
531
531 @predicate('bundle()', safe=True)
532 @predicate('bundle()', safe=True)
532 def bundle(repo, subset, x):
533 def bundle(repo, subset, x):
533 """Changesets in the bundle.
534 """Changesets in the bundle.
534
535
535 Bundle must be specified by the -R option."""
536 Bundle must be specified by the -R option."""
536
537
537 try:
538 try:
538 bundlerevs = repo.changelog.bundlerevs
539 bundlerevs = repo.changelog.bundlerevs
539 except AttributeError:
540 except AttributeError:
540 raise error.Abort(_("no bundle provided - specify with -R"))
541 raise error.Abort(_("no bundle provided - specify with -R"))
541 return subset & bundlerevs
542 return subset & bundlerevs
542
543
543 def checkstatus(repo, subset, pat, field):
544 def checkstatus(repo, subset, pat, field):
544 hasset = matchmod.patkind(pat) == 'set'
545 hasset = matchmod.patkind(pat) == 'set'
545
546
546 mcache = [None]
547 mcache = [None]
547 def matches(x):
548 def matches(x):
548 c = repo[x]
549 c = repo[x]
549 if not mcache[0] or hasset:
550 if not mcache[0] or hasset:
550 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
551 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
551 m = mcache[0]
552 m = mcache[0]
552 fname = None
553 fname = None
553 if not m.anypats() and len(m.files()) == 1:
554 if not m.anypats() and len(m.files()) == 1:
554 fname = m.files()[0]
555 fname = m.files()[0]
555 if fname is not None:
556 if fname is not None:
556 if fname not in c.files():
557 if fname not in c.files():
557 return False
558 return False
558 else:
559 else:
559 for f in c.files():
560 for f in c.files():
560 if m(f):
561 if m(f):
561 break
562 break
562 else:
563 else:
563 return False
564 return False
564 files = repo.status(c.p1().node(), c.node())[field]
565 files = repo.status(c.p1().node(), c.node())[field]
565 if fname is not None:
566 if fname is not None:
566 if fname in files:
567 if fname in files:
567 return True
568 return True
568 else:
569 else:
569 for f in files:
570 for f in files:
570 if m(f):
571 if m(f):
571 return True
572 return True
572
573
573 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
574 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
574
575
575 def _children(repo, subset, parentset):
576 def _children(repo, subset, parentset):
576 if not parentset:
577 if not parentset:
577 return baseset()
578 return baseset()
578 cs = set()
579 cs = set()
579 pr = repo.changelog.parentrevs
580 pr = repo.changelog.parentrevs
580 minrev = parentset.min()
581 minrev = parentset.min()
581 nullrev = node.nullrev
582 nullrev = node.nullrev
582 for r in subset:
583 for r in subset:
583 if r <= minrev:
584 if r <= minrev:
584 continue
585 continue
585 p1, p2 = pr(r)
586 p1, p2 = pr(r)
586 if p1 in parentset:
587 if p1 in parentset:
587 cs.add(r)
588 cs.add(r)
588 if p2 != nullrev and p2 in parentset:
589 if p2 != nullrev and p2 in parentset:
589 cs.add(r)
590 cs.add(r)
590 return baseset(cs)
591 return baseset(cs)
591
592
592 @predicate('children(set)', safe=True)
593 @predicate('children(set)', safe=True)
593 def children(repo, subset, x):
594 def children(repo, subset, x):
594 """Child changesets of changesets in set.
595 """Child changesets of changesets in set.
595 """
596 """
596 s = getset(repo, fullreposet(repo), x)
597 s = getset(repo, fullreposet(repo), x)
597 cs = _children(repo, subset, s)
598 cs = _children(repo, subset, s)
598 return subset & cs
599 return subset & cs
599
600
600 @predicate('closed()', safe=True, weight=10)
601 @predicate('closed()', safe=True, weight=10)
601 def closed(repo, subset, x):
602 def closed(repo, subset, x):
602 """Changeset is closed.
603 """Changeset is closed.
603 """
604 """
604 # i18n: "closed" is a keyword
605 # i18n: "closed" is a keyword
605 getargs(x, 0, 0, _("closed takes no arguments"))
606 getargs(x, 0, 0, _("closed takes no arguments"))
606 return subset.filter(lambda r: repo[r].closesbranch(),
607 return subset.filter(lambda r: repo[r].closesbranch(),
607 condrepr='<branch closed>')
608 condrepr='<branch closed>')
608
609
609 @predicate('contains(pattern)', weight=100)
610 @predicate('contains(pattern)', weight=100)
610 def contains(repo, subset, x):
611 def contains(repo, subset, x):
611 """The revision's manifest contains a file matching pattern (but might not
612 """The revision's manifest contains a file matching pattern (but might not
612 modify it). See :hg:`help patterns` for information about file patterns.
613 modify it). See :hg:`help patterns` for information about file patterns.
613
614
614 The pattern without explicit kind like ``glob:`` is expected to be
615 The pattern without explicit kind like ``glob:`` is expected to be
615 relative to the current directory and match against a file exactly
616 relative to the current directory and match against a file exactly
616 for efficiency.
617 for efficiency.
617 """
618 """
618 # i18n: "contains" is a keyword
619 # i18n: "contains" is a keyword
619 pat = getstring(x, _("contains requires a pattern"))
620 pat = getstring(x, _("contains requires a pattern"))
620
621
621 def matches(x):
622 def matches(x):
622 if not matchmod.patkind(pat):
623 if not matchmod.patkind(pat):
623 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
624 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
624 if pats in repo[x]:
625 if pats in repo[x]:
625 return True
626 return True
626 else:
627 else:
627 c = repo[x]
628 c = repo[x]
628 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
629 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
629 for f in c.manifest():
630 for f in c.manifest():
630 if m(f):
631 if m(f):
631 return True
632 return True
632 return False
633 return False
633
634
634 return subset.filter(matches, condrepr=('<contains %r>', pat))
635 return subset.filter(matches, condrepr=('<contains %r>', pat))
635
636
636 @predicate('converted([id])', safe=True)
637 @predicate('converted([id])', safe=True)
637 def converted(repo, subset, x):
638 def converted(repo, subset, x):
638 """Changesets converted from the given identifier in the old repository if
639 """Changesets converted from the given identifier in the old repository if
639 present, or all converted changesets if no identifier is specified.
640 present, or all converted changesets if no identifier is specified.
640 """
641 """
641
642
642 # There is exactly no chance of resolving the revision, so do a simple
643 # There is exactly no chance of resolving the revision, so do a simple
643 # string compare and hope for the best
644 # string compare and hope for the best
644
645
645 rev = None
646 rev = None
646 # i18n: "converted" is a keyword
647 # i18n: "converted" is a keyword
647 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
648 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
648 if l:
649 if l:
649 # i18n: "converted" is a keyword
650 # i18n: "converted" is a keyword
650 rev = getstring(l[0], _('converted requires a revision'))
651 rev = getstring(l[0], _('converted requires a revision'))
651
652
652 def _matchvalue(r):
653 def _matchvalue(r):
653 source = repo[r].extra().get('convert_revision', None)
654 source = repo[r].extra().get('convert_revision', None)
654 return source is not None and (rev is None or source.startswith(rev))
655 return source is not None and (rev is None or source.startswith(rev))
655
656
656 return subset.filter(lambda r: _matchvalue(r),
657 return subset.filter(lambda r: _matchvalue(r),
657 condrepr=('<converted %r>', rev))
658 condrepr=('<converted %r>', rev))
658
659
659 @predicate('date(interval)', safe=True, weight=10)
660 @predicate('date(interval)', safe=True, weight=10)
660 def date(repo, subset, x):
661 def date(repo, subset, x):
661 """Changesets within the interval, see :hg:`help dates`.
662 """Changesets within the interval, see :hg:`help dates`.
662 """
663 """
663 # i18n: "date" is a keyword
664 # i18n: "date" is a keyword
664 ds = getstring(x, _("date requires a string"))
665 ds = getstring(x, _("date requires a string"))
665 dm = dateutil.matchdate(ds)
666 dm = dateutil.matchdate(ds)
666 return subset.filter(lambda x: dm(repo[x].date()[0]),
667 return subset.filter(lambda x: dm(repo[x].date()[0]),
667 condrepr=('<date %r>', ds))
668 condrepr=('<date %r>', ds))
668
669
669 @predicate('desc(string)', safe=True, weight=10)
670 @predicate('desc(string)', safe=True, weight=10)
670 def desc(repo, subset, x):
671 def desc(repo, subset, x):
671 """Search commit message for string. The match is case-insensitive.
672 """Search commit message for string. The match is case-insensitive.
672
673
673 Pattern matching is supported for `string`. See
674 Pattern matching is supported for `string`. See
674 :hg:`help revisions.patterns`.
675 :hg:`help revisions.patterns`.
675 """
676 """
676 # i18n: "desc" is a keyword
677 # i18n: "desc" is a keyword
677 ds = getstring(x, _("desc requires a string"))
678 ds = getstring(x, _("desc requires a string"))
678
679
679 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
680 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
680
681
681 return subset.filter(lambda r: matcher(repo[r].description()),
682 return subset.filter(lambda r: matcher(repo[r].description()),
682 condrepr=('<desc %r>', ds))
683 condrepr=('<desc %r>', ds))
683
684
684 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
685 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
685 stopdepth=None):
686 stopdepth=None):
686 roots = getset(repo, fullreposet(repo), x)
687 roots = getset(repo, fullreposet(repo), x)
687 if not roots:
688 if not roots:
688 return baseset()
689 return baseset()
689 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
690 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
690 return subset & s
691 return subset & s
691
692
692 @predicate('descendants(set[, depth])', safe=True)
693 @predicate('descendants(set[, depth])', safe=True)
693 def descendants(repo, subset, x):
694 def descendants(repo, subset, x):
694 """Changesets which are descendants of changesets in set, including the
695 """Changesets which are descendants of changesets in set, including the
695 given changesets themselves.
696 given changesets themselves.
696
697
697 If depth is specified, the result only includes changesets up to
698 If depth is specified, the result only includes changesets up to
698 the specified generation.
699 the specified generation.
699 """
700 """
700 # startdepth is for internal use only until we can decide the UI
701 # startdepth is for internal use only until we can decide the UI
701 args = getargsdict(x, 'descendants', 'set depth startdepth')
702 args = getargsdict(x, 'descendants', 'set depth startdepth')
702 if 'set' not in args:
703 if 'set' not in args:
703 # i18n: "descendants" is a keyword
704 # i18n: "descendants" is a keyword
704 raise error.ParseError(_('descendants takes at least 1 argument'))
705 raise error.ParseError(_('descendants takes at least 1 argument'))
705 startdepth = stopdepth = None
706 startdepth = stopdepth = None
706 if 'startdepth' in args:
707 if 'startdepth' in args:
707 n = getinteger(args['startdepth'],
708 n = getinteger(args['startdepth'],
708 "descendants expects an integer startdepth")
709 "descendants expects an integer startdepth")
709 if n < 0:
710 if n < 0:
710 raise error.ParseError("negative startdepth")
711 raise error.ParseError("negative startdepth")
711 startdepth = n
712 startdepth = n
712 if 'depth' in args:
713 if 'depth' in args:
713 # i18n: "descendants" is a keyword
714 # i18n: "descendants" is a keyword
714 n = getinteger(args['depth'], _("descendants expects an integer depth"))
715 n = getinteger(args['depth'], _("descendants expects an integer depth"))
715 if n < 0:
716 if n < 0:
716 raise error.ParseError(_("negative depth"))
717 raise error.ParseError(_("negative depth"))
717 stopdepth = n + 1
718 stopdepth = n + 1
718 return _descendants(repo, subset, args['set'],
719 return _descendants(repo, subset, args['set'],
719 startdepth=startdepth, stopdepth=stopdepth)
720 startdepth=startdepth, stopdepth=stopdepth)
720
721
721 @predicate('_firstdescendants', safe=True)
722 @predicate('_firstdescendants', safe=True)
722 def _firstdescendants(repo, subset, x):
723 def _firstdescendants(repo, subset, x):
723 # ``_firstdescendants(set)``
724 # ``_firstdescendants(set)``
724 # Like ``descendants(set)`` but follows only the first parents.
725 # Like ``descendants(set)`` but follows only the first parents.
725 return _descendants(repo, subset, x, followfirst=True)
726 return _descendants(repo, subset, x, followfirst=True)
726
727
727 @predicate('destination([set])', safe=True, weight=10)
728 @predicate('destination([set])', safe=True, weight=10)
728 def destination(repo, subset, x):
729 def destination(repo, subset, x):
729 """Changesets that were created by a graft, transplant or rebase operation,
730 """Changesets that were created by a graft, transplant or rebase operation,
730 with the given revisions specified as the source. Omitting the optional set
731 with the given revisions specified as the source. Omitting the optional set
731 is the same as passing all().
732 is the same as passing all().
732 """
733 """
733 if x is not None:
734 if x is not None:
734 sources = getset(repo, fullreposet(repo), x)
735 sources = getset(repo, fullreposet(repo), x)
735 else:
736 else:
736 sources = fullreposet(repo)
737 sources = fullreposet(repo)
737
738
738 dests = set()
739 dests = set()
739
740
740 # subset contains all of the possible destinations that can be returned, so
741 # subset contains all of the possible destinations that can be returned, so
741 # iterate over them and see if their source(s) were provided in the arg set.
742 # iterate over them and see if their source(s) were provided in the arg set.
742 # Even if the immediate src of r is not in the arg set, src's source (or
743 # Even if the immediate src of r is not in the arg set, src's source (or
743 # further back) may be. Scanning back further than the immediate src allows
744 # further back) may be. Scanning back further than the immediate src allows
744 # transitive transplants and rebases to yield the same results as transitive
745 # transitive transplants and rebases to yield the same results as transitive
745 # grafts.
746 # grafts.
746 for r in subset:
747 for r in subset:
747 src = _getrevsource(repo, r)
748 src = _getrevsource(repo, r)
748 lineage = None
749 lineage = None
749
750
750 while src is not None:
751 while src is not None:
751 if lineage is None:
752 if lineage is None:
752 lineage = list()
753 lineage = list()
753
754
754 lineage.append(r)
755 lineage.append(r)
755
756
756 # The visited lineage is a match if the current source is in the arg
757 # The visited lineage is a match if the current source is in the arg
757 # set. Since every candidate dest is visited by way of iterating
758 # set. Since every candidate dest is visited by way of iterating
758 # subset, any dests further back in the lineage will be tested by a
759 # subset, any dests further back in the lineage will be tested by a
759 # different iteration over subset. Likewise, if the src was already
760 # different iteration over subset. Likewise, if the src was already
760 # selected, the current lineage can be selected without going back
761 # selected, the current lineage can be selected without going back
761 # further.
762 # further.
762 if src in sources or src in dests:
763 if src in sources or src in dests:
763 dests.update(lineage)
764 dests.update(lineage)
764 break
765 break
765
766
766 r = src
767 r = src
767 src = _getrevsource(repo, r)
768 src = _getrevsource(repo, r)
768
769
769 return subset.filter(dests.__contains__,
770 return subset.filter(dests.__contains__,
770 condrepr=lambda: '<destination %r>' % _sortedb(dests))
771 condrepr=lambda: '<destination %r>' % _sortedb(dests))
771
772
772 @predicate('contentdivergent()', safe=True)
773 @predicate('contentdivergent()', safe=True)
773 def contentdivergent(repo, subset, x):
774 def contentdivergent(repo, subset, x):
774 """
775 """
775 Final successors of changesets with an alternative set of final
776 Final successors of changesets with an alternative set of final
776 successors. (EXPERIMENTAL)
777 successors. (EXPERIMENTAL)
777 """
778 """
778 # i18n: "contentdivergent" is a keyword
779 # i18n: "contentdivergent" is a keyword
779 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
780 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
780 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
781 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
781 return subset & contentdivergent
782 return subset & contentdivergent
782
783
783 @predicate('extdata(source)', safe=False, weight=100)
784 @predicate('extdata(source)', safe=False, weight=100)
784 def extdata(repo, subset, x):
785 def extdata(repo, subset, x):
785 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
786 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
786 # i18n: "extdata" is a keyword
787 # i18n: "extdata" is a keyword
787 args = getargsdict(x, 'extdata', 'source')
788 args = getargsdict(x, 'extdata', 'source')
788 source = getstring(args.get('source'),
789 source = getstring(args.get('source'),
789 # i18n: "extdata" is a keyword
790 # i18n: "extdata" is a keyword
790 _('extdata takes at least 1 string argument'))
791 _('extdata takes at least 1 string argument'))
791 data = scmutil.extdatasource(repo, source)
792 data = scmutil.extdatasource(repo, source)
792 return subset & baseset(data)
793 return subset & baseset(data)
793
794
794 @predicate('extinct()', safe=True)
795 @predicate('extinct()', safe=True)
795 def extinct(repo, subset, x):
796 def extinct(repo, subset, x):
796 """Obsolete changesets with obsolete descendants only.
797 """Obsolete changesets with obsolete descendants only.
797 """
798 """
798 # i18n: "extinct" is a keyword
799 # i18n: "extinct" is a keyword
799 getargs(x, 0, 0, _("extinct takes no arguments"))
800 getargs(x, 0, 0, _("extinct takes no arguments"))
800 extincts = obsmod.getrevs(repo, 'extinct')
801 extincts = obsmod.getrevs(repo, 'extinct')
801 return subset & extincts
802 return subset & extincts
802
803
803 @predicate('extra(label, [value])', safe=True)
804 @predicate('extra(label, [value])', safe=True)
804 def extra(repo, subset, x):
805 def extra(repo, subset, x):
805 """Changesets with the given label in the extra metadata, with the given
806 """Changesets with the given label in the extra metadata, with the given
806 optional value.
807 optional value.
807
808
808 Pattern matching is supported for `value`. See
809 Pattern matching is supported for `value`. See
809 :hg:`help revisions.patterns`.
810 :hg:`help revisions.patterns`.
810 """
811 """
811 args = getargsdict(x, 'extra', 'label value')
812 args = getargsdict(x, 'extra', 'label value')
812 if 'label' not in args:
813 if 'label' not in args:
813 # i18n: "extra" is a keyword
814 # i18n: "extra" is a keyword
814 raise error.ParseError(_('extra takes at least 1 argument'))
815 raise error.ParseError(_('extra takes at least 1 argument'))
815 # i18n: "extra" is a keyword
816 # i18n: "extra" is a keyword
816 label = getstring(args['label'], _('first argument to extra must be '
817 label = getstring(args['label'], _('first argument to extra must be '
817 'a string'))
818 'a string'))
818 value = None
819 value = None
819
820
820 if 'value' in args:
821 if 'value' in args:
821 # i18n: "extra" is a keyword
822 # i18n: "extra" is a keyword
822 value = getstring(args['value'], _('second argument to extra must be '
823 value = getstring(args['value'], _('second argument to extra must be '
823 'a string'))
824 'a string'))
824 kind, value, matcher = stringutil.stringmatcher(value)
825 kind, value, matcher = stringutil.stringmatcher(value)
825
826
826 def _matchvalue(r):
827 def _matchvalue(r):
827 extra = repo[r].extra()
828 extra = repo[r].extra()
828 return label in extra and (value is None or matcher(extra[label]))
829 return label in extra and (value is None or matcher(extra[label]))
829
830
830 return subset.filter(lambda r: _matchvalue(r),
831 return subset.filter(lambda r: _matchvalue(r),
831 condrepr=('<extra[%r] %r>', label, value))
832 condrepr=('<extra[%r] %r>', label, value))
832
833
833 @predicate('filelog(pattern)', safe=True)
834 @predicate('filelog(pattern)', safe=True)
834 def filelog(repo, subset, x):
835 def filelog(repo, subset, x):
835 """Changesets connected to the specified filelog.
836 """Changesets connected to the specified filelog.
836
837
837 For performance reasons, visits only revisions mentioned in the file-level
838 For performance reasons, visits only revisions mentioned in the file-level
838 filelog, rather than filtering through all changesets (much faster, but
839 filelog, rather than filtering through all changesets (much faster, but
839 doesn't include deletes or duplicate changes). For a slower, more accurate
840 doesn't include deletes or duplicate changes). For a slower, more accurate
840 result, use ``file()``.
841 result, use ``file()``.
841
842
842 The pattern without explicit kind like ``glob:`` is expected to be
843 The pattern without explicit kind like ``glob:`` is expected to be
843 relative to the current directory and match against a file exactly
844 relative to the current directory and match against a file exactly
844 for efficiency.
845 for efficiency.
845
846
846 If some linkrev points to revisions filtered by the current repoview, we'll
847 If some linkrev points to revisions filtered by the current repoview, we'll
847 work around it to return a non-filtered value.
848 work around it to return a non-filtered value.
848 """
849 """
849
850
850 # i18n: "filelog" is a keyword
851 # i18n: "filelog" is a keyword
851 pat = getstring(x, _("filelog requires a pattern"))
852 pat = getstring(x, _("filelog requires a pattern"))
852 s = set()
853 s = set()
853 cl = repo.changelog
854 cl = repo.changelog
854
855
855 if not matchmod.patkind(pat):
856 if not matchmod.patkind(pat):
856 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
857 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
857 files = [f]
858 files = [f]
858 else:
859 else:
859 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
860 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
860 files = (f for f in repo[None] if m(f))
861 files = (f for f in repo[None] if m(f))
861
862
862 for f in files:
863 for f in files:
863 fl = repo.file(f)
864 fl = repo.file(f)
864 known = {}
865 known = {}
865 scanpos = 0
866 scanpos = 0
866 for fr in list(fl):
867 for fr in list(fl):
867 fn = fl.node(fr)
868 fn = fl.node(fr)
868 if fn in known:
869 if fn in known:
869 s.add(known[fn])
870 s.add(known[fn])
870 continue
871 continue
871
872
872 lr = fl.linkrev(fr)
873 lr = fl.linkrev(fr)
873 if lr in cl:
874 if lr in cl:
874 s.add(lr)
875 s.add(lr)
875 elif scanpos is not None:
876 elif scanpos is not None:
876 # lowest matching changeset is filtered, scan further
877 # lowest matching changeset is filtered, scan further
877 # ahead in changelog
878 # ahead in changelog
878 start = max(lr, scanpos) + 1
879 start = max(lr, scanpos) + 1
879 scanpos = None
880 scanpos = None
880 for r in cl.revs(start):
881 for r in cl.revs(start):
881 # minimize parsing of non-matching entries
882 # minimize parsing of non-matching entries
882 if f in cl.revision(r) and f in cl.readfiles(r):
883 if f in cl.revision(r) and f in cl.readfiles(r):
883 try:
884 try:
884 # try to use manifest delta fastpath
885 # try to use manifest delta fastpath
885 n = repo[r].filenode(f)
886 n = repo[r].filenode(f)
886 if n not in known:
887 if n not in known:
887 if n == fn:
888 if n == fn:
888 s.add(r)
889 s.add(r)
889 scanpos = r
890 scanpos = r
890 break
891 break
891 else:
892 else:
892 known[n] = r
893 known[n] = r
893 except error.ManifestLookupError:
894 except error.ManifestLookupError:
894 # deletion in changelog
895 # deletion in changelog
895 continue
896 continue
896
897
897 return subset & s
898 return subset & s
898
899
899 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
900 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
900 def first(repo, subset, x, order):
901 def first(repo, subset, x, order):
901 """An alias for limit().
902 """An alias for limit().
902 """
903 """
903 return limit(repo, subset, x, order)
904 return limit(repo, subset, x, order)
904
905
905 def _follow(repo, subset, x, name, followfirst=False):
906 def _follow(repo, subset, x, name, followfirst=False):
906 args = getargsdict(x, name, 'file startrev')
907 args = getargsdict(x, name, 'file startrev')
907 revs = None
908 revs = None
908 if 'startrev' in args:
909 if 'startrev' in args:
909 revs = getset(repo, fullreposet(repo), args['startrev'])
910 revs = getset(repo, fullreposet(repo), args['startrev'])
910 if 'file' in args:
911 if 'file' in args:
911 x = getstring(args['file'], _("%s expected a pattern") % name)
912 x = getstring(args['file'], _("%s expected a pattern") % name)
912 if revs is None:
913 if revs is None:
913 revs = [None]
914 revs = [None]
914 fctxs = []
915 fctxs = []
915 for r in revs:
916 for r in revs:
916 ctx = mctx = repo[r]
917 ctx = mctx = repo[r]
917 if r is None:
918 if r is None:
918 ctx = repo['.']
919 ctx = repo['.']
919 m = matchmod.match(repo.root, repo.getcwd(), [x],
920 m = matchmod.match(repo.root, repo.getcwd(), [x],
920 ctx=mctx, default='path')
921 ctx=mctx, default='path')
921 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
922 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
922 s = dagop.filerevancestors(fctxs, followfirst)
923 s = dagop.filerevancestors(fctxs, followfirst)
923 else:
924 else:
924 if revs is None:
925 if revs is None:
925 revs = baseset([repo['.'].rev()])
926 revs = baseset([repo['.'].rev()])
926 s = dagop.revancestors(repo, revs, followfirst)
927 s = dagop.revancestors(repo, revs, followfirst)
927
928
928 return subset & s
929 return subset & s
929
930
930 @predicate('follow([file[, startrev]])', safe=True)
931 @predicate('follow([file[, startrev]])', safe=True)
931 def follow(repo, subset, x):
932 def follow(repo, subset, x):
932 """
933 """
933 An alias for ``::.`` (ancestors of the working directory's first parent).
934 An alias for ``::.`` (ancestors of the working directory's first parent).
934 If file pattern is specified, the histories of files matching given
935 If file pattern is specified, the histories of files matching given
935 pattern in the revision given by startrev are followed, including copies.
936 pattern in the revision given by startrev are followed, including copies.
936 """
937 """
937 return _follow(repo, subset, x, 'follow')
938 return _follow(repo, subset, x, 'follow')
938
939
939 @predicate('_followfirst', safe=True)
940 @predicate('_followfirst', safe=True)
940 def _followfirst(repo, subset, x):
941 def _followfirst(repo, subset, x):
941 # ``followfirst([file[, startrev]])``
942 # ``followfirst([file[, startrev]])``
942 # Like ``follow([file[, startrev]])`` but follows only the first parent
943 # Like ``follow([file[, startrev]])`` but follows only the first parent
943 # of every revisions or files revisions.
944 # of every revisions or files revisions.
944 return _follow(repo, subset, x, '_followfirst', followfirst=True)
945 return _follow(repo, subset, x, '_followfirst', followfirst=True)
945
946
946 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
947 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
947 safe=True)
948 safe=True)
948 def followlines(repo, subset, x):
949 def followlines(repo, subset, x):
949 """Changesets modifying `file` in line range ('fromline', 'toline').
950 """Changesets modifying `file` in line range ('fromline', 'toline').
950
951
951 Line range corresponds to 'file' content at 'startrev' and should hence be
952 Line range corresponds to 'file' content at 'startrev' and should hence be
952 consistent with file size. If startrev is not specified, working directory's
953 consistent with file size. If startrev is not specified, working directory's
953 parent is used.
954 parent is used.
954
955
955 By default, ancestors of 'startrev' are returned. If 'descend' is True,
956 By default, ancestors of 'startrev' are returned. If 'descend' is True,
956 descendants of 'startrev' are returned though renames are (currently) not
957 descendants of 'startrev' are returned though renames are (currently) not
957 followed in this direction.
958 followed in this direction.
958 """
959 """
959 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
960 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
960 if len(args['lines']) != 1:
961 if len(args['lines']) != 1:
961 raise error.ParseError(_("followlines requires a line range"))
962 raise error.ParseError(_("followlines requires a line range"))
962
963
963 rev = '.'
964 rev = '.'
964 if 'startrev' in args:
965 if 'startrev' in args:
965 revs = getset(repo, fullreposet(repo), args['startrev'])
966 revs = getset(repo, fullreposet(repo), args['startrev'])
966 if len(revs) != 1:
967 if len(revs) != 1:
967 raise error.ParseError(
968 raise error.ParseError(
968 # i18n: "followlines" is a keyword
969 # i18n: "followlines" is a keyword
969 _("followlines expects exactly one revision"))
970 _("followlines expects exactly one revision"))
970 rev = revs.last()
971 rev = revs.last()
971
972
972 pat = getstring(args['file'], _("followlines requires a pattern"))
973 pat = getstring(args['file'], _("followlines requires a pattern"))
973 # i18n: "followlines" is a keyword
974 # i18n: "followlines" is a keyword
974 msg = _("followlines expects exactly one file")
975 msg = _("followlines expects exactly one file")
975 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
976 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
976 # i18n: "followlines" is a keyword
977 # i18n: "followlines" is a keyword
977 lr = getrange(args['lines'][0], _("followlines expects a line range"))
978 lr = getrange(args['lines'][0], _("followlines expects a line range"))
978 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
979 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
979 for a in lr]
980 for a in lr]
980 fromline, toline = util.processlinerange(fromline, toline)
981 fromline, toline = util.processlinerange(fromline, toline)
981
982
982 fctx = repo[rev].filectx(fname)
983 fctx = repo[rev].filectx(fname)
983 descend = False
984 descend = False
984 if 'descend' in args:
985 if 'descend' in args:
985 descend = getboolean(args['descend'],
986 descend = getboolean(args['descend'],
986 # i18n: "descend" is a keyword
987 # i18n: "descend" is a keyword
987 _("descend argument must be a boolean"))
988 _("descend argument must be a boolean"))
988 if descend:
989 if descend:
989 rs = generatorset(
990 rs = generatorset(
990 (c.rev() for c, _linerange
991 (c.rev() for c, _linerange
991 in dagop.blockdescendants(fctx, fromline, toline)),
992 in dagop.blockdescendants(fctx, fromline, toline)),
992 iterasc=True)
993 iterasc=True)
993 else:
994 else:
994 rs = generatorset(
995 rs = generatorset(
995 (c.rev() for c, _linerange
996 (c.rev() for c, _linerange
996 in dagop.blockancestors(fctx, fromline, toline)),
997 in dagop.blockancestors(fctx, fromline, toline)),
997 iterasc=False)
998 iterasc=False)
998 return subset & rs
999 return subset & rs
999
1000
1000 @predicate('all()', safe=True)
1001 @predicate('all()', safe=True)
1001 def getall(repo, subset, x):
1002 def getall(repo, subset, x):
1002 """All changesets, the same as ``0:tip``.
1003 """All changesets, the same as ``0:tip``.
1003 """
1004 """
1004 # i18n: "all" is a keyword
1005 # i18n: "all" is a keyword
1005 getargs(x, 0, 0, _("all takes no arguments"))
1006 getargs(x, 0, 0, _("all takes no arguments"))
1006 return subset & spanset(repo) # drop "null" if any
1007 return subset & spanset(repo) # drop "null" if any
1007
1008
1008 @predicate('grep(regex)', weight=10)
1009 @predicate('grep(regex)', weight=10)
1009 def grep(repo, subset, x):
1010 def grep(repo, subset, x):
1010 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1011 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1011 to ensure special escape characters are handled correctly. Unlike
1012 to ensure special escape characters are handled correctly. Unlike
1012 ``keyword(string)``, the match is case-sensitive.
1013 ``keyword(string)``, the match is case-sensitive.
1013 """
1014 """
1014 try:
1015 try:
1015 # i18n: "grep" is a keyword
1016 # i18n: "grep" is a keyword
1016 gr = re.compile(getstring(x, _("grep requires a string")))
1017 gr = re.compile(getstring(x, _("grep requires a string")))
1017 except re.error as e:
1018 except re.error as e:
1018 raise error.ParseError(
1019 raise error.ParseError(
1019 _('invalid match pattern: %s') % stringutil.forcebytestr(e))
1020 _('invalid match pattern: %s') % stringutil.forcebytestr(e))
1020
1021
1021 def matches(x):
1022 def matches(x):
1022 c = repo[x]
1023 c = repo[x]
1023 for e in c.files() + [c.user(), c.description()]:
1024 for e in c.files() + [c.user(), c.description()]:
1024 if gr.search(e):
1025 if gr.search(e):
1025 return True
1026 return True
1026 return False
1027 return False
1027
1028
1028 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1029 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1029
1030
1030 @predicate('_matchfiles', safe=True)
1031 @predicate('_matchfiles', safe=True)
1031 def _matchfiles(repo, subset, x):
1032 def _matchfiles(repo, subset, x):
1032 # _matchfiles takes a revset list of prefixed arguments:
1033 # _matchfiles takes a revset list of prefixed arguments:
1033 #
1034 #
1034 # [p:foo, i:bar, x:baz]
1035 # [p:foo, i:bar, x:baz]
1035 #
1036 #
1036 # builds a match object from them and filters subset. Allowed
1037 # builds a match object from them and filters subset. Allowed
1037 # prefixes are 'p:' for regular patterns, 'i:' for include
1038 # prefixes are 'p:' for regular patterns, 'i:' for include
1038 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1039 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1039 # a revision identifier, or the empty string to reference the
1040 # a revision identifier, or the empty string to reference the
1040 # working directory, from which the match object is
1041 # working directory, from which the match object is
1041 # initialized. Use 'd:' to set the default matching mode, default
1042 # initialized. Use 'd:' to set the default matching mode, default
1042 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1043 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1043
1044
1044 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1045 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1045 pats, inc, exc = [], [], []
1046 pats, inc, exc = [], [], []
1046 rev, default = None, None
1047 rev, default = None, None
1047 for arg in l:
1048 for arg in l:
1048 s = getstring(arg, "_matchfiles requires string arguments")
1049 s = getstring(arg, "_matchfiles requires string arguments")
1049 prefix, value = s[:2], s[2:]
1050 prefix, value = s[:2], s[2:]
1050 if prefix == 'p:':
1051 if prefix == 'p:':
1051 pats.append(value)
1052 pats.append(value)
1052 elif prefix == 'i:':
1053 elif prefix == 'i:':
1053 inc.append(value)
1054 inc.append(value)
1054 elif prefix == 'x:':
1055 elif prefix == 'x:':
1055 exc.append(value)
1056 exc.append(value)
1056 elif prefix == 'r:':
1057 elif prefix == 'r:':
1057 if rev is not None:
1058 if rev is not None:
1058 raise error.ParseError('_matchfiles expected at most one '
1059 raise error.ParseError('_matchfiles expected at most one '
1059 'revision')
1060 'revision')
1060 if value == '': # empty means working directory
1061 if value == '': # empty means working directory
1061 rev = node.wdirrev
1062 rev = node.wdirrev
1062 else:
1063 else:
1063 rev = value
1064 rev = value
1064 elif prefix == 'd:':
1065 elif prefix == 'd:':
1065 if default is not None:
1066 if default is not None:
1066 raise error.ParseError('_matchfiles expected at most one '
1067 raise error.ParseError('_matchfiles expected at most one '
1067 'default mode')
1068 'default mode')
1068 default = value
1069 default = value
1069 else:
1070 else:
1070 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1071 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1071 if not default:
1072 if not default:
1072 default = 'glob'
1073 default = 'glob'
1073 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1074 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1074
1075
1075 mcache = [None]
1076 mcache = [None]
1076
1077
1077 # This directly read the changelog data as creating changectx for all
1078 # This directly read the changelog data as creating changectx for all
1078 # revisions is quite expensive.
1079 # revisions is quite expensive.
1079 getfiles = repo.changelog.readfiles
1080 getfiles = repo.changelog.readfiles
1080 wdirrev = node.wdirrev
1081 wdirrev = node.wdirrev
1081 def matches(x):
1082 def matches(x):
1082 if x == wdirrev:
1083 if x == wdirrev:
1083 files = repo[x].files()
1084 files = repo[x].files()
1084 else:
1085 else:
1085 files = getfiles(x)
1086 files = getfiles(x)
1086
1087
1087 if not mcache[0] or (hasset and rev is None):
1088 if not mcache[0] or (hasset and rev is None):
1088 r = x if rev is None else rev
1089 r = x if rev is None else rev
1089 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1090 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1090 include=inc, exclude=exc, ctx=repo[r],
1091 include=inc, exclude=exc, ctx=repo[r],
1091 default=default)
1092 default=default)
1092 m = mcache[0]
1093 m = mcache[0]
1093
1094
1094 for f in files:
1095 for f in files:
1095 if m(f):
1096 if m(f):
1096 return True
1097 return True
1097 return False
1098 return False
1098
1099
1099 return subset.filter(matches,
1100 return subset.filter(matches,
1100 condrepr=('<matchfiles patterns=%r, include=%r '
1101 condrepr=('<matchfiles patterns=%r, include=%r '
1101 'exclude=%r, default=%r, rev=%r>',
1102 'exclude=%r, default=%r, rev=%r>',
1102 pats, inc, exc, default, rev))
1103 pats, inc, exc, default, rev))
1103
1104
1104 @predicate('file(pattern)', safe=True, weight=10)
1105 @predicate('file(pattern)', safe=True, weight=10)
1105 def hasfile(repo, subset, x):
1106 def hasfile(repo, subset, x):
1106 """Changesets affecting files matched by pattern.
1107 """Changesets affecting files matched by pattern.
1107
1108
1108 For a faster but less accurate result, consider using ``filelog()``
1109 For a faster but less accurate result, consider using ``filelog()``
1109 instead.
1110 instead.
1110
1111
1111 This predicate uses ``glob:`` as the default kind of pattern.
1112 This predicate uses ``glob:`` as the default kind of pattern.
1112 """
1113 """
1113 # i18n: "file" is a keyword
1114 # i18n: "file" is a keyword
1114 pat = getstring(x, _("file requires a pattern"))
1115 pat = getstring(x, _("file requires a pattern"))
1115 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1116 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1116
1117
1117 @predicate('head()', safe=True)
1118 @predicate('head()', safe=True)
1118 def head(repo, subset, x):
1119 def head(repo, subset, x):
1119 """Changeset is a named branch head.
1120 """Changeset is a named branch head.
1120 """
1121 """
1121 # i18n: "head" is a keyword
1122 # i18n: "head" is a keyword
1122 getargs(x, 0, 0, _("head takes no arguments"))
1123 getargs(x, 0, 0, _("head takes no arguments"))
1123 hs = set()
1124 hs = set()
1124 cl = repo.changelog
1125 cl = repo.changelog
1125 for ls in repo.branchmap().itervalues():
1126 for ls in repo.branchmap().itervalues():
1126 hs.update(cl.rev(h) for h in ls)
1127 hs.update(cl.rev(h) for h in ls)
1127 return subset & baseset(hs)
1128 return subset & baseset(hs)
1128
1129
1129 @predicate('heads(set)', safe=True, takeorder=True)
1130 @predicate('heads(set)', safe=True, takeorder=True)
1130 def heads(repo, subset, x, order):
1131 def heads(repo, subset, x, order):
1131 """Members of set with no children in set.
1132 """Members of set with no children in set.
1132 """
1133 """
1133 # argument set should never define order
1134 # argument set should never define order
1134 if order == defineorder:
1135 if order == defineorder:
1135 order = followorder
1136 order = followorder
1136 s = getset(repo, subset, x, order=order)
1137 s = getset(repo, subset, x, order=order)
1137 ps = parents(repo, subset, x)
1138 ps = parents(repo, subset, x)
1138 return s - ps
1139 return s - ps
1139
1140
1140 @predicate('hidden()', safe=True)
1141 @predicate('hidden()', safe=True)
1141 def hidden(repo, subset, x):
1142 def hidden(repo, subset, x):
1142 """Hidden changesets.
1143 """Hidden changesets.
1143 """
1144 """
1144 # i18n: "hidden" is a keyword
1145 # i18n: "hidden" is a keyword
1145 getargs(x, 0, 0, _("hidden takes no arguments"))
1146 getargs(x, 0, 0, _("hidden takes no arguments"))
1146 hiddenrevs = repoview.filterrevs(repo, 'visible')
1147 hiddenrevs = repoview.filterrevs(repo, 'visible')
1147 return subset & hiddenrevs
1148 return subset & hiddenrevs
1148
1149
1149 @predicate('keyword(string)', safe=True, weight=10)
1150 @predicate('keyword(string)', safe=True, weight=10)
1150 def keyword(repo, subset, x):
1151 def keyword(repo, subset, x):
1151 """Search commit message, user name, and names of changed files for
1152 """Search commit message, user name, and names of changed files for
1152 string. The match is case-insensitive.
1153 string. The match is case-insensitive.
1153
1154
1154 For a regular expression or case sensitive search of these fields, use
1155 For a regular expression or case sensitive search of these fields, use
1155 ``grep(regex)``.
1156 ``grep(regex)``.
1156 """
1157 """
1157 # i18n: "keyword" is a keyword
1158 # i18n: "keyword" is a keyword
1158 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1159 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1159
1160
1160 def matches(r):
1161 def matches(r):
1161 c = repo[r]
1162 c = repo[r]
1162 return any(kw in encoding.lower(t)
1163 return any(kw in encoding.lower(t)
1163 for t in c.files() + [c.user(), c.description()])
1164 for t in c.files() + [c.user(), c.description()])
1164
1165
1165 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1166 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1166
1167
1167 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1168 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1168 def limit(repo, subset, x, order):
1169 def limit(repo, subset, x, order):
1169 """First n members of set, defaulting to 1, starting from offset.
1170 """First n members of set, defaulting to 1, starting from offset.
1170 """
1171 """
1171 args = getargsdict(x, 'limit', 'set n offset')
1172 args = getargsdict(x, 'limit', 'set n offset')
1172 if 'set' not in args:
1173 if 'set' not in args:
1173 # i18n: "limit" is a keyword
1174 # i18n: "limit" is a keyword
1174 raise error.ParseError(_("limit requires one to three arguments"))
1175 raise error.ParseError(_("limit requires one to three arguments"))
1175 # i18n: "limit" is a keyword
1176 # i18n: "limit" is a keyword
1176 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1177 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1177 if lim < 0:
1178 if lim < 0:
1178 raise error.ParseError(_("negative number to select"))
1179 raise error.ParseError(_("negative number to select"))
1179 # i18n: "limit" is a keyword
1180 # i18n: "limit" is a keyword
1180 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1181 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1181 if ofs < 0:
1182 if ofs < 0:
1182 raise error.ParseError(_("negative offset"))
1183 raise error.ParseError(_("negative offset"))
1183 os = getset(repo, fullreposet(repo), args['set'])
1184 os = getset(repo, fullreposet(repo), args['set'])
1184 ls = os.slice(ofs, ofs + lim)
1185 ls = os.slice(ofs, ofs + lim)
1185 if order == followorder and lim > 1:
1186 if order == followorder and lim > 1:
1186 return subset & ls
1187 return subset & ls
1187 return ls & subset
1188 return ls & subset
1188
1189
1189 @predicate('last(set, [n])', safe=True, takeorder=True)
1190 @predicate('last(set, [n])', safe=True, takeorder=True)
1190 def last(repo, subset, x, order):
1191 def last(repo, subset, x, order):
1191 """Last n members of set, defaulting to 1.
1192 """Last n members of set, defaulting to 1.
1192 """
1193 """
1193 # i18n: "last" is a keyword
1194 # i18n: "last" is a keyword
1194 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1195 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1195 lim = 1
1196 lim = 1
1196 if len(l) == 2:
1197 if len(l) == 2:
1197 # i18n: "last" is a keyword
1198 # i18n: "last" is a keyword
1198 lim = getinteger(l[1], _("last expects a number"))
1199 lim = getinteger(l[1], _("last expects a number"))
1199 if lim < 0:
1200 if lim < 0:
1200 raise error.ParseError(_("negative number to select"))
1201 raise error.ParseError(_("negative number to select"))
1201 os = getset(repo, fullreposet(repo), l[0])
1202 os = getset(repo, fullreposet(repo), l[0])
1202 os.reverse()
1203 os.reverse()
1203 ls = os.slice(0, lim)
1204 ls = os.slice(0, lim)
1204 if order == followorder and lim > 1:
1205 if order == followorder and lim > 1:
1205 return subset & ls
1206 return subset & ls
1206 ls.reverse()
1207 ls.reverse()
1207 return ls & subset
1208 return ls & subset
1208
1209
1209 @predicate('max(set)', safe=True)
1210 @predicate('max(set)', safe=True)
1210 def maxrev(repo, subset, x):
1211 def maxrev(repo, subset, x):
1211 """Changeset with highest revision number in set.
1212 """Changeset with highest revision number in set.
1212 """
1213 """
1213 os = getset(repo, fullreposet(repo), x)
1214 os = getset(repo, fullreposet(repo), x)
1214 try:
1215 try:
1215 m = os.max()
1216 m = os.max()
1216 if m in subset:
1217 if m in subset:
1217 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1218 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1218 except ValueError:
1219 except ValueError:
1219 # os.max() throws a ValueError when the collection is empty.
1220 # os.max() throws a ValueError when the collection is empty.
1220 # Same as python's max().
1221 # Same as python's max().
1221 pass
1222 pass
1222 return baseset(datarepr=('<max %r, %r>', subset, os))
1223 return baseset(datarepr=('<max %r, %r>', subset, os))
1223
1224
1224 @predicate('merge()', safe=True)
1225 @predicate('merge()', safe=True)
1225 def merge(repo, subset, x):
1226 def merge(repo, subset, x):
1226 """Changeset is a merge changeset.
1227 """Changeset is a merge changeset.
1227 """
1228 """
1228 # i18n: "merge" is a keyword
1229 # i18n: "merge" is a keyword
1229 getargs(x, 0, 0, _("merge takes no arguments"))
1230 getargs(x, 0, 0, _("merge takes no arguments"))
1230 cl = repo.changelog
1231 cl = repo.changelog
1231 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1232 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1232 condrepr='<merge>')
1233 condrepr='<merge>')
1233
1234
1234 @predicate('branchpoint()', safe=True)
1235 @predicate('branchpoint()', safe=True)
1235 def branchpoint(repo, subset, x):
1236 def branchpoint(repo, subset, x):
1236 """Changesets with more than one child.
1237 """Changesets with more than one child.
1237 """
1238 """
1238 # i18n: "branchpoint" is a keyword
1239 # i18n: "branchpoint" is a keyword
1239 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1240 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1240 cl = repo.changelog
1241 cl = repo.changelog
1241 if not subset:
1242 if not subset:
1242 return baseset()
1243 return baseset()
1243 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1244 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1244 # (and if it is not, it should.)
1245 # (and if it is not, it should.)
1245 baserev = min(subset)
1246 baserev = min(subset)
1246 parentscount = [0]*(len(repo) - baserev)
1247 parentscount = [0]*(len(repo) - baserev)
1247 for r in cl.revs(start=baserev + 1):
1248 for r in cl.revs(start=baserev + 1):
1248 for p in cl.parentrevs(r):
1249 for p in cl.parentrevs(r):
1249 if p >= baserev:
1250 if p >= baserev:
1250 parentscount[p - baserev] += 1
1251 parentscount[p - baserev] += 1
1251 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1252 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1252 condrepr='<branchpoint>')
1253 condrepr='<branchpoint>')
1253
1254
1254 @predicate('min(set)', safe=True)
1255 @predicate('min(set)', safe=True)
1255 def minrev(repo, subset, x):
1256 def minrev(repo, subset, x):
1256 """Changeset with lowest revision number in set.
1257 """Changeset with lowest revision number in set.
1257 """
1258 """
1258 os = getset(repo, fullreposet(repo), x)
1259 os = getset(repo, fullreposet(repo), x)
1259 try:
1260 try:
1260 m = os.min()
1261 m = os.min()
1261 if m in subset:
1262 if m in subset:
1262 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1263 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1263 except ValueError:
1264 except ValueError:
1264 # os.min() throws a ValueError when the collection is empty.
1265 # os.min() throws a ValueError when the collection is empty.
1265 # Same as python's min().
1266 # Same as python's min().
1266 pass
1267 pass
1267 return baseset(datarepr=('<min %r, %r>', subset, os))
1268 return baseset(datarepr=('<min %r, %r>', subset, os))
1268
1269
1269 @predicate('modifies(pattern)', safe=True, weight=30)
1270 @predicate('modifies(pattern)', safe=True, weight=30)
1270 def modifies(repo, subset, x):
1271 def modifies(repo, subset, x):
1271 """Changesets modifying files matched by pattern.
1272 """Changesets modifying files matched by pattern.
1272
1273
1273 The pattern without explicit kind like ``glob:`` is expected to be
1274 The pattern without explicit kind like ``glob:`` is expected to be
1274 relative to the current directory and match against a file or a
1275 relative to the current directory and match against a file or a
1275 directory.
1276 directory.
1276 """
1277 """
1277 # i18n: "modifies" is a keyword
1278 # i18n: "modifies" is a keyword
1278 pat = getstring(x, _("modifies requires a pattern"))
1279 pat = getstring(x, _("modifies requires a pattern"))
1279 return checkstatus(repo, subset, pat, 0)
1280 return checkstatus(repo, subset, pat, 0)
1280
1281
1281 @predicate('named(namespace)')
1282 @predicate('named(namespace)')
1282 def named(repo, subset, x):
1283 def named(repo, subset, x):
1283 """The changesets in a given namespace.
1284 """The changesets in a given namespace.
1284
1285
1285 Pattern matching is supported for `namespace`. See
1286 Pattern matching is supported for `namespace`. See
1286 :hg:`help revisions.patterns`.
1287 :hg:`help revisions.patterns`.
1287 """
1288 """
1288 # i18n: "named" is a keyword
1289 # i18n: "named" is a keyword
1289 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1290 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1290
1291
1291 ns = getstring(args[0],
1292 ns = getstring(args[0],
1292 # i18n: "named" is a keyword
1293 # i18n: "named" is a keyword
1293 _('the argument to named must be a string'))
1294 _('the argument to named must be a string'))
1294 kind, pattern, matcher = stringutil.stringmatcher(ns)
1295 kind, pattern, matcher = stringutil.stringmatcher(ns)
1295 namespaces = set()
1296 namespaces = set()
1296 if kind == 'literal':
1297 if kind == 'literal':
1297 if pattern not in repo.names:
1298 if pattern not in repo.names:
1298 raise error.RepoLookupError(_("namespace '%s' does not exist")
1299 raise error.RepoLookupError(_("namespace '%s' does not exist")
1299 % ns)
1300 % ns)
1300 namespaces.add(repo.names[pattern])
1301 namespaces.add(repo.names[pattern])
1301 else:
1302 else:
1302 for name, ns in repo.names.iteritems():
1303 for name, ns in repo.names.iteritems():
1303 if matcher(name):
1304 if matcher(name):
1304 namespaces.add(ns)
1305 namespaces.add(ns)
1305 if not namespaces:
1306 if not namespaces:
1306 raise error.RepoLookupError(_("no namespace exists"
1307 raise error.RepoLookupError(_("no namespace exists"
1307 " that match '%s'") % pattern)
1308 " that match '%s'") % pattern)
1308
1309
1309 names = set()
1310 names = set()
1310 for ns in namespaces:
1311 for ns in namespaces:
1311 for name in ns.listnames(repo):
1312 for name in ns.listnames(repo):
1312 if name not in ns.deprecated:
1313 if name not in ns.deprecated:
1313 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1314 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1314
1315
1315 names -= {node.nullrev}
1316 names -= {node.nullrev}
1316 return subset & names
1317 return subset & names
1317
1318
1318 @predicate('id(string)', safe=True)
1319 @predicate('id(string)', safe=True)
1319 def node_(repo, subset, x):
1320 def node_(repo, subset, x):
1320 """Revision non-ambiguously specified by the given hex string prefix.
1321 """Revision non-ambiguously specified by the given hex string prefix.
1321 """
1322 """
1322 # i18n: "id" is a keyword
1323 # i18n: "id" is a keyword
1323 l = getargs(x, 1, 1, _("id requires one argument"))
1324 l = getargs(x, 1, 1, _("id requires one argument"))
1324 # i18n: "id" is a keyword
1325 # i18n: "id" is a keyword
1325 n = getstring(l[0], _("id requires a string"))
1326 n = getstring(l[0], _("id requires a string"))
1326 if len(n) == 40:
1327 if len(n) == 40:
1327 try:
1328 try:
1328 rn = repo.changelog.rev(node.bin(n))
1329 rn = repo.changelog.rev(node.bin(n))
1329 except error.WdirUnsupported:
1330 except error.WdirUnsupported:
1330 rn = node.wdirrev
1331 rn = node.wdirrev
1331 except (LookupError, TypeError):
1332 except (LookupError, TypeError):
1332 rn = None
1333 rn = None
1333 else:
1334 else:
1334 rn = None
1335 rn = None
1335 try:
1336 try:
1336 pm = scmutil.resolvehexnodeidprefix(repo, n)
1337 pm = scmutil.resolvehexnodeidprefix(repo, n)
1337 if pm is not None:
1338 if pm is not None:
1338 rn = repo.changelog.rev(pm)
1339 rn = repo.changelog.rev(pm)
1339 except LookupError:
1340 except LookupError:
1340 pass
1341 pass
1341 except error.WdirUnsupported:
1342 except error.WdirUnsupported:
1342 rn = node.wdirrev
1343 rn = node.wdirrev
1343
1344
1344 if rn is None:
1345 if rn is None:
1345 return baseset()
1346 return baseset()
1346 result = baseset([rn])
1347 result = baseset([rn])
1347 return result & subset
1348 return result & subset
1348
1349
1349 @predicate('none()', safe=True)
1350 @predicate('none()', safe=True)
1350 def none(repo, subset, x):
1351 def none(repo, subset, x):
1351 """No changesets.
1352 """No changesets.
1352 """
1353 """
1353 # i18n: "none" is a keyword
1354 # i18n: "none" is a keyword
1354 getargs(x, 0, 0, _("none takes no arguments"))
1355 getargs(x, 0, 0, _("none takes no arguments"))
1355 return baseset()
1356 return baseset()
1356
1357
1357 @predicate('obsolete()', safe=True)
1358 @predicate('obsolete()', safe=True)
1358 def obsolete(repo, subset, x):
1359 def obsolete(repo, subset, x):
1359 """Mutable changeset with a newer version."""
1360 """Mutable changeset with a newer version."""
1360 # i18n: "obsolete" is a keyword
1361 # i18n: "obsolete" is a keyword
1361 getargs(x, 0, 0, _("obsolete takes no arguments"))
1362 getargs(x, 0, 0, _("obsolete takes no arguments"))
1362 obsoletes = obsmod.getrevs(repo, 'obsolete')
1363 obsoletes = obsmod.getrevs(repo, 'obsolete')
1363 return subset & obsoletes
1364 return subset & obsoletes
1364
1365
1365 @predicate('only(set, [set])', safe=True)
1366 @predicate('only(set, [set])', safe=True)
1366 def only(repo, subset, x):
1367 def only(repo, subset, x):
1367 """Changesets that are ancestors of the first set that are not ancestors
1368 """Changesets that are ancestors of the first set that are not ancestors
1368 of any other head in the repo. If a second set is specified, the result
1369 of any other head in the repo. If a second set is specified, the result
1369 is ancestors of the first set that are not ancestors of the second set
1370 is ancestors of the first set that are not ancestors of the second set
1370 (i.e. ::<set1> - ::<set2>).
1371 (i.e. ::<set1> - ::<set2>).
1371 """
1372 """
1372 cl = repo.changelog
1373 cl = repo.changelog
1373 # i18n: "only" is a keyword
1374 # i18n: "only" is a keyword
1374 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1375 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1375 include = getset(repo, fullreposet(repo), args[0])
1376 include = getset(repo, fullreposet(repo), args[0])
1376 if len(args) == 1:
1377 if len(args) == 1:
1377 if not include:
1378 if not include:
1378 return baseset()
1379 return baseset()
1379
1380
1380 descendants = set(dagop.revdescendants(repo, include, False))
1381 descendants = set(dagop.revdescendants(repo, include, False))
1381 exclude = [rev for rev in cl.headrevs()
1382 exclude = [rev for rev in cl.headrevs()
1382 if not rev in descendants and not rev in include]
1383 if not rev in descendants and not rev in include]
1383 else:
1384 else:
1384 exclude = getset(repo, fullreposet(repo), args[1])
1385 exclude = getset(repo, fullreposet(repo), args[1])
1385
1386
1386 results = set(cl.findmissingrevs(common=exclude, heads=include))
1387 results = set(cl.findmissingrevs(common=exclude, heads=include))
1387 # XXX we should turn this into a baseset instead of a set, smartset may do
1388 # XXX we should turn this into a baseset instead of a set, smartset may do
1388 # some optimizations from the fact this is a baseset.
1389 # some optimizations from the fact this is a baseset.
1389 return subset & results
1390 return subset & results
1390
1391
1391 @predicate('origin([set])', safe=True)
1392 @predicate('origin([set])', safe=True)
1392 def origin(repo, subset, x):
1393 def origin(repo, subset, x):
1393 """
1394 """
1394 Changesets that were specified as a source for the grafts, transplants or
1395 Changesets that were specified as a source for the grafts, transplants or
1395 rebases that created the given revisions. Omitting the optional set is the
1396 rebases that created the given revisions. Omitting the optional set is the
1396 same as passing all(). If a changeset created by these operations is itself
1397 same as passing all(). If a changeset created by these operations is itself
1397 specified as a source for one of these operations, only the source changeset
1398 specified as a source for one of these operations, only the source changeset
1398 for the first operation is selected.
1399 for the first operation is selected.
1399 """
1400 """
1400 if x is not None:
1401 if x is not None:
1401 dests = getset(repo, fullreposet(repo), x)
1402 dests = getset(repo, fullreposet(repo), x)
1402 else:
1403 else:
1403 dests = fullreposet(repo)
1404 dests = fullreposet(repo)
1404
1405
1405 def _firstsrc(rev):
1406 def _firstsrc(rev):
1406 src = _getrevsource(repo, rev)
1407 src = _getrevsource(repo, rev)
1407 if src is None:
1408 if src is None:
1408 return None
1409 return None
1409
1410
1410 while True:
1411 while True:
1411 prev = _getrevsource(repo, src)
1412 prev = _getrevsource(repo, src)
1412
1413
1413 if prev is None:
1414 if prev is None:
1414 return src
1415 return src
1415 src = prev
1416 src = prev
1416
1417
1417 o = {_firstsrc(r) for r in dests}
1418 o = {_firstsrc(r) for r in dests}
1418 o -= {None}
1419 o -= {None}
1419 # XXX we should turn this into a baseset instead of a set, smartset may do
1420 # XXX we should turn this into a baseset instead of a set, smartset may do
1420 # some optimizations from the fact this is a baseset.
1421 # some optimizations from the fact this is a baseset.
1421 return subset & o
1422 return subset & o
1422
1423
1423 @predicate('outgoing([path])', safe=False, weight=10)
1424 @predicate('outgoing([path])', safe=False, weight=10)
1424 def outgoing(repo, subset, x):
1425 def outgoing(repo, subset, x):
1425 """Changesets not found in the specified destination repository, or the
1426 """Changesets not found in the specified destination repository, or the
1426 default push location.
1427 default push location.
1427 """
1428 """
1428 # Avoid cycles.
1429 # Avoid cycles.
1429 from . import (
1430 from . import (
1430 discovery,
1431 discovery,
1431 hg,
1432 hg,
1432 )
1433 )
1433 # i18n: "outgoing" is a keyword
1434 # i18n: "outgoing" is a keyword
1434 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1435 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1435 # i18n: "outgoing" is a keyword
1436 # i18n: "outgoing" is a keyword
1436 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1437 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1437 if not dest:
1438 if not dest:
1438 # ui.paths.getpath() explicitly tests for None, not just a boolean
1439 # ui.paths.getpath() explicitly tests for None, not just a boolean
1439 dest = None
1440 dest = None
1440 path = repo.ui.paths.getpath(dest, default=('default-push', 'default'))
1441 path = repo.ui.paths.getpath(dest, default=('default-push', 'default'))
1441 if not path:
1442 if not path:
1442 raise error.Abort(_('default repository not configured!'),
1443 raise error.Abort(_('default repository not configured!'),
1443 hint=_("see 'hg help config.paths'"))
1444 hint=_("see 'hg help config.paths'"))
1444 dest = path.pushloc or path.loc
1445 dest = path.pushloc or path.loc
1445 branches = path.branch, []
1446 branches = path.branch, []
1446
1447
1447 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1448 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1448 if revs:
1449 if revs:
1449 revs = [repo.lookup(rev) for rev in revs]
1450 revs = [repo.lookup(rev) for rev in revs]
1450 other = hg.peer(repo, {}, dest)
1451 other = hg.peer(repo, {}, dest)
1451 repo.ui.pushbuffer()
1452 repo.ui.pushbuffer()
1452 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1453 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1453 repo.ui.popbuffer()
1454 repo.ui.popbuffer()
1454 cl = repo.changelog
1455 cl = repo.changelog
1455 o = {cl.rev(r) for r in outgoing.missing}
1456 o = {cl.rev(r) for r in outgoing.missing}
1456 return subset & o
1457 return subset & o
1457
1458
1458 @predicate('p1([set])', safe=True)
1459 @predicate('p1([set])', safe=True)
1459 def p1(repo, subset, x):
1460 def p1(repo, subset, x):
1460 """First parent of changesets in set, or the working directory.
1461 """First parent of changesets in set, or the working directory.
1461 """
1462 """
1462 if x is None:
1463 if x is None:
1463 p = repo[x].p1().rev()
1464 p = repo[x].p1().rev()
1464 if p >= 0:
1465 if p >= 0:
1465 return subset & baseset([p])
1466 return subset & baseset([p])
1466 return baseset()
1467 return baseset()
1467
1468
1468 ps = set()
1469 ps = set()
1469 cl = repo.changelog
1470 cl = repo.changelog
1470 for r in getset(repo, fullreposet(repo), x):
1471 for r in getset(repo, fullreposet(repo), x):
1471 try:
1472 try:
1472 ps.add(cl.parentrevs(r)[0])
1473 ps.add(cl.parentrevs(r)[0])
1473 except error.WdirUnsupported:
1474 except error.WdirUnsupported:
1474 ps.add(repo[r].parents()[0].rev())
1475 ps.add(repo[r].parents()[0].rev())
1475 ps -= {node.nullrev}
1476 ps -= {node.nullrev}
1476 # XXX we should turn this into a baseset instead of a set, smartset may do
1477 # XXX we should turn this into a baseset instead of a set, smartset may do
1477 # some optimizations from the fact this is a baseset.
1478 # some optimizations from the fact this is a baseset.
1478 return subset & ps
1479 return subset & ps
1479
1480
1480 @predicate('p2([set])', safe=True)
1481 @predicate('p2([set])', safe=True)
1481 def p2(repo, subset, x):
1482 def p2(repo, subset, x):
1482 """Second parent of changesets in set, or the working directory.
1483 """Second parent of changesets in set, or the working directory.
1483 """
1484 """
1484 if x is None:
1485 if x is None:
1485 ps = repo[x].parents()
1486 ps = repo[x].parents()
1486 try:
1487 try:
1487 p = ps[1].rev()
1488 p = ps[1].rev()
1488 if p >= 0:
1489 if p >= 0:
1489 return subset & baseset([p])
1490 return subset & baseset([p])
1490 return baseset()
1491 return baseset()
1491 except IndexError:
1492 except IndexError:
1492 return baseset()
1493 return baseset()
1493
1494
1494 ps = set()
1495 ps = set()
1495 cl = repo.changelog
1496 cl = repo.changelog
1496 for r in getset(repo, fullreposet(repo), x):
1497 for r in getset(repo, fullreposet(repo), x):
1497 try:
1498 try:
1498 ps.add(cl.parentrevs(r)[1])
1499 ps.add(cl.parentrevs(r)[1])
1499 except error.WdirUnsupported:
1500 except error.WdirUnsupported:
1500 parents = repo[r].parents()
1501 parents = repo[r].parents()
1501 if len(parents) == 2:
1502 if len(parents) == 2:
1502 ps.add(parents[1])
1503 ps.add(parents[1])
1503 ps -= {node.nullrev}
1504 ps -= {node.nullrev}
1504 # XXX we should turn this into a baseset instead of a set, smartset may do
1505 # XXX we should turn this into a baseset instead of a set, smartset may do
1505 # some optimizations from the fact this is a baseset.
1506 # some optimizations from the fact this is a baseset.
1506 return subset & ps
1507 return subset & ps
1507
1508
1508 def parentpost(repo, subset, x, order):
1509 def parentpost(repo, subset, x, order):
1509 return p1(repo, subset, x)
1510 return p1(repo, subset, x)
1510
1511
1511 @predicate('parents([set])', safe=True)
1512 @predicate('parents([set])', safe=True)
1512 def parents(repo, subset, x):
1513 def parents(repo, subset, x):
1513 """
1514 """
1514 The set of all parents for all changesets in set, or the working directory.
1515 The set of all parents for all changesets in set, or the working directory.
1515 """
1516 """
1516 if x is None:
1517 if x is None:
1517 ps = set(p.rev() for p in repo[x].parents())
1518 ps = set(p.rev() for p in repo[x].parents())
1518 else:
1519 else:
1519 ps = set()
1520 ps = set()
1520 cl = repo.changelog
1521 cl = repo.changelog
1521 up = ps.update
1522 up = ps.update
1522 parentrevs = cl.parentrevs
1523 parentrevs = cl.parentrevs
1523 for r in getset(repo, fullreposet(repo), x):
1524 for r in getset(repo, fullreposet(repo), x):
1524 try:
1525 try:
1525 up(parentrevs(r))
1526 up(parentrevs(r))
1526 except error.WdirUnsupported:
1527 except error.WdirUnsupported:
1527 up(p.rev() for p in repo[r].parents())
1528 up(p.rev() for p in repo[r].parents())
1528 ps -= {node.nullrev}
1529 ps -= {node.nullrev}
1529 return subset & ps
1530 return subset & ps
1530
1531
1531 def _phase(repo, subset, *targets):
1532 def _phase(repo, subset, *targets):
1532 """helper to select all rev in <targets> phases"""
1533 """helper to select all rev in <targets> phases"""
1533 return repo._phasecache.getrevset(repo, targets, subset)
1534 return repo._phasecache.getrevset(repo, targets, subset)
1534
1535
1535 @predicate('draft()', safe=True)
1536 @predicate('draft()', safe=True)
1536 def draft(repo, subset, x):
1537 def draft(repo, subset, x):
1537 """Changeset in draft phase."""
1538 """Changeset in draft phase."""
1538 # i18n: "draft" is a keyword
1539 # i18n: "draft" is a keyword
1539 getargs(x, 0, 0, _("draft takes no arguments"))
1540 getargs(x, 0, 0, _("draft takes no arguments"))
1540 target = phases.draft
1541 target = phases.draft
1541 return _phase(repo, subset, target)
1542 return _phase(repo, subset, target)
1542
1543
1543 @predicate('secret()', safe=True)
1544 @predicate('secret()', safe=True)
1544 def secret(repo, subset, x):
1545 def secret(repo, subset, x):
1545 """Changeset in secret phase."""
1546 """Changeset in secret phase."""
1546 # i18n: "secret" is a keyword
1547 # i18n: "secret" is a keyword
1547 getargs(x, 0, 0, _("secret takes no arguments"))
1548 getargs(x, 0, 0, _("secret takes no arguments"))
1548 target = phases.secret
1549 target = phases.secret
1549 return _phase(repo, subset, target)
1550 return _phase(repo, subset, target)
1550
1551
1551 @predicate('stack([revs])', safe=True)
1552 @predicate('stack([revs])', safe=True)
1552 def stack(repo, subset, x):
1553 def stack(repo, subset, x):
1553 """Experimental revset for the stack of changesets or working directory
1554 """Experimental revset for the stack of changesets or working directory
1554 parent. (EXPERIMENTAL)
1555 parent. (EXPERIMENTAL)
1555 """
1556 """
1556 if x is None:
1557 if x is None:
1557 stacks = stackmod.getstack(repo, x)
1558 stacks = stackmod.getstack(repo, x)
1558 else:
1559 else:
1559 stacks = smartset.baseset([])
1560 stacks = smartset.baseset([])
1560 for revision in getset(repo, fullreposet(repo), x):
1561 for revision in getset(repo, fullreposet(repo), x):
1561 currentstack = stackmod.getstack(repo, revision)
1562 currentstack = stackmod.getstack(repo, revision)
1562 stacks = stacks + currentstack
1563 stacks = stacks + currentstack
1563
1564
1564 return subset & stacks
1565 return subset & stacks
1565
1566
1566 def parentspec(repo, subset, x, n, order):
1567 def parentspec(repo, subset, x, n, order):
1567 """``set^0``
1568 """``set^0``
1568 The set.
1569 The set.
1569 ``set^1`` (or ``set^``), ``set^2``
1570 ``set^1`` (or ``set^``), ``set^2``
1570 First or second parent, respectively, of all changesets in set.
1571 First or second parent, respectively, of all changesets in set.
1571 """
1572 """
1572 try:
1573 try:
1573 n = int(n[1])
1574 n = int(n[1])
1574 if n not in (0, 1, 2):
1575 if n not in (0, 1, 2):
1575 raise ValueError
1576 raise ValueError
1576 except (TypeError, ValueError):
1577 except (TypeError, ValueError):
1577 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1578 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1578 ps = set()
1579 ps = set()
1579 cl = repo.changelog
1580 cl = repo.changelog
1580 for r in getset(repo, fullreposet(repo), x):
1581 for r in getset(repo, fullreposet(repo), x):
1581 if n == 0:
1582 if n == 0:
1582 ps.add(r)
1583 ps.add(r)
1583 elif n == 1:
1584 elif n == 1:
1584 try:
1585 try:
1585 ps.add(cl.parentrevs(r)[0])
1586 ps.add(cl.parentrevs(r)[0])
1586 except error.WdirUnsupported:
1587 except error.WdirUnsupported:
1587 ps.add(repo[r].parents()[0].rev())
1588 ps.add(repo[r].parents()[0].rev())
1588 else:
1589 else:
1589 try:
1590 try:
1590 parents = cl.parentrevs(r)
1591 parents = cl.parentrevs(r)
1591 if parents[1] != node.nullrev:
1592 if parents[1] != node.nullrev:
1592 ps.add(parents[1])
1593 ps.add(parents[1])
1593 except error.WdirUnsupported:
1594 except error.WdirUnsupported:
1594 parents = repo[r].parents()
1595 parents = repo[r].parents()
1595 if len(parents) == 2:
1596 if len(parents) == 2:
1596 ps.add(parents[1].rev())
1597 ps.add(parents[1].rev())
1597 return subset & ps
1598 return subset & ps
1598
1599
1599 @predicate('present(set)', safe=True, takeorder=True)
1600 @predicate('present(set)', safe=True, takeorder=True)
1600 def present(repo, subset, x, order):
1601 def present(repo, subset, x, order):
1601 """An empty set, if any revision in set isn't found; otherwise,
1602 """An empty set, if any revision in set isn't found; otherwise,
1602 all revisions in set.
1603 all revisions in set.
1603
1604
1604 If any of specified revisions is not present in the local repository,
1605 If any of specified revisions is not present in the local repository,
1605 the query is normally aborted. But this predicate allows the query
1606 the query is normally aborted. But this predicate allows the query
1606 to continue even in such cases.
1607 to continue even in such cases.
1607 """
1608 """
1608 try:
1609 try:
1609 return getset(repo, subset, x, order)
1610 return getset(repo, subset, x, order)
1610 except error.RepoLookupError:
1611 except error.RepoLookupError:
1611 return baseset()
1612 return baseset()
1612
1613
1613 # for internal use
1614 # for internal use
1614 @predicate('_notpublic', safe=True)
1615 @predicate('_notpublic', safe=True)
1615 def _notpublic(repo, subset, x):
1616 def _notpublic(repo, subset, x):
1616 getargs(x, 0, 0, "_notpublic takes no arguments")
1617 getargs(x, 0, 0, "_notpublic takes no arguments")
1617 return _phase(repo, subset, phases.draft, phases.secret)
1618 return _phase(repo, subset, phases.draft, phases.secret)
1618
1619
1619 # for internal use
1620 # for internal use
1620 @predicate('_phaseandancestors(phasename, set)', safe=True)
1621 @predicate('_phaseandancestors(phasename, set)', safe=True)
1621 def _phaseandancestors(repo, subset, x):
1622 def _phaseandancestors(repo, subset, x):
1622 # equivalent to (phasename() & ancestors(set)) but more efficient
1623 # equivalent to (phasename() & ancestors(set)) but more efficient
1623 # phasename could be one of 'draft', 'secret', or '_notpublic'
1624 # phasename could be one of 'draft', 'secret', or '_notpublic'
1624 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1625 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1625 phasename = getsymbol(args[0])
1626 phasename = getsymbol(args[0])
1626 s = getset(repo, fullreposet(repo), args[1])
1627 s = getset(repo, fullreposet(repo), args[1])
1627
1628
1628 draft = phases.draft
1629 draft = phases.draft
1629 secret = phases.secret
1630 secret = phases.secret
1630 phasenamemap = {
1631 phasenamemap = {
1631 '_notpublic': draft,
1632 '_notpublic': draft,
1632 'draft': draft, # follow secret's ancestors
1633 'draft': draft, # follow secret's ancestors
1633 'secret': secret,
1634 'secret': secret,
1634 }
1635 }
1635 if phasename not in phasenamemap:
1636 if phasename not in phasenamemap:
1636 raise error.ParseError('%r is not a valid phasename' % phasename)
1637 raise error.ParseError('%r is not a valid phasename' % phasename)
1637
1638
1638 minimalphase = phasenamemap[phasename]
1639 minimalphase = phasenamemap[phasename]
1639 getphase = repo._phasecache.phase
1640 getphase = repo._phasecache.phase
1640
1641
1641 def cutfunc(rev):
1642 def cutfunc(rev):
1642 return getphase(repo, rev) < minimalphase
1643 return getphase(repo, rev) < minimalphase
1643
1644
1644 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1645 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1645
1646
1646 if phasename == 'draft': # need to remove secret changesets
1647 if phasename == 'draft': # need to remove secret changesets
1647 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1648 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1648 return subset & revs
1649 return subset & revs
1649
1650
1650 @predicate('public()', safe=True)
1651 @predicate('public()', safe=True)
1651 def public(repo, subset, x):
1652 def public(repo, subset, x):
1652 """Changeset in public phase."""
1653 """Changeset in public phase."""
1653 # i18n: "public" is a keyword
1654 # i18n: "public" is a keyword
1654 getargs(x, 0, 0, _("public takes no arguments"))
1655 getargs(x, 0, 0, _("public takes no arguments"))
1655 return _phase(repo, subset, phases.public)
1656 return _phase(repo, subset, phases.public)
1656
1657
1657 @predicate('remote([id [,path]])', safe=False)
1658 @predicate('remote([id [,path]])', safe=False)
1658 def remote(repo, subset, x):
1659 def remote(repo, subset, x):
1659 """Local revision that corresponds to the given identifier in a
1660 """Local revision that corresponds to the given identifier in a
1660 remote repository, if present. Here, the '.' identifier is a
1661 remote repository, if present. Here, the '.' identifier is a
1661 synonym for the current local branch.
1662 synonym for the current local branch.
1662 """
1663 """
1663
1664
1664 from . import hg # avoid start-up nasties
1665 from . import hg # avoid start-up nasties
1665 # i18n: "remote" is a keyword
1666 # i18n: "remote" is a keyword
1666 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1667 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1667
1668
1668 q = '.'
1669 q = '.'
1669 if len(l) > 0:
1670 if len(l) > 0:
1670 # i18n: "remote" is a keyword
1671 # i18n: "remote" is a keyword
1671 q = getstring(l[0], _("remote requires a string id"))
1672 q = getstring(l[0], _("remote requires a string id"))
1672 if q == '.':
1673 if q == '.':
1673 q = repo['.'].branch()
1674 q = repo['.'].branch()
1674
1675
1675 dest = ''
1676 dest = ''
1676 if len(l) > 1:
1677 if len(l) > 1:
1677 # i18n: "remote" is a keyword
1678 # i18n: "remote" is a keyword
1678 dest = getstring(l[1], _("remote requires a repository path"))
1679 dest = getstring(l[1], _("remote requires a repository path"))
1679 dest = repo.ui.expandpath(dest or 'default')
1680 dest = repo.ui.expandpath(dest or 'default')
1680 dest, branches = hg.parseurl(dest)
1681 dest, branches = hg.parseurl(dest)
1681 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1682 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1682 if revs:
1683 if revs:
1683 revs = [repo.lookup(rev) for rev in revs]
1684 revs = [repo.lookup(rev) for rev in revs]
1684 other = hg.peer(repo, {}, dest)
1685 other = hg.peer(repo, {}, dest)
1685 n = other.lookup(q)
1686 n = other.lookup(q)
1686 if n in repo:
1687 if n in repo:
1687 r = repo[n].rev()
1688 r = repo[n].rev()
1688 if r in subset:
1689 if r in subset:
1689 return baseset([r])
1690 return baseset([r])
1690 return baseset()
1691 return baseset()
1691
1692
1692 @predicate('removes(pattern)', safe=True, weight=30)
1693 @predicate('removes(pattern)', safe=True, weight=30)
1693 def removes(repo, subset, x):
1694 def removes(repo, subset, x):
1694 """Changesets which remove files matching pattern.
1695 """Changesets which remove files matching pattern.
1695
1696
1696 The pattern without explicit kind like ``glob:`` is expected to be
1697 The pattern without explicit kind like ``glob:`` is expected to be
1697 relative to the current directory and match against a file or a
1698 relative to the current directory and match against a file or a
1698 directory.
1699 directory.
1699 """
1700 """
1700 # i18n: "removes" is a keyword
1701 # i18n: "removes" is a keyword
1701 pat = getstring(x, _("removes requires a pattern"))
1702 pat = getstring(x, _("removes requires a pattern"))
1702 return checkstatus(repo, subset, pat, 2)
1703 return checkstatus(repo, subset, pat, 2)
1703
1704
1704 @predicate('rev(number)', safe=True)
1705 @predicate('rev(number)', safe=True)
1705 def rev(repo, subset, x):
1706 def rev(repo, subset, x):
1706 """Revision with the given numeric identifier.
1707 """Revision with the given numeric identifier.
1707 """
1708 """
1708 # i18n: "rev" is a keyword
1709 # i18n: "rev" is a keyword
1709 l = getargs(x, 1, 1, _("rev requires one argument"))
1710 l = getargs(x, 1, 1, _("rev requires one argument"))
1710 try:
1711 try:
1711 # i18n: "rev" is a keyword
1712 # i18n: "rev" is a keyword
1712 l = int(getstring(l[0], _("rev requires a number")))
1713 l = int(getstring(l[0], _("rev requires a number")))
1713 except (TypeError, ValueError):
1714 except (TypeError, ValueError):
1714 # i18n: "rev" is a keyword
1715 # i18n: "rev" is a keyword
1715 raise error.ParseError(_("rev expects a number"))
1716 raise error.ParseError(_("rev expects a number"))
1716 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1717 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1717 return baseset()
1718 return baseset()
1718 return subset & baseset([l])
1719 return subset & baseset([l])
1719
1720
1720 @predicate('matching(revision [, field])', safe=True)
1721 @predicate('matching(revision [, field])', safe=True)
1721 def matching(repo, subset, x):
1722 def matching(repo, subset, x):
1722 """Changesets in which a given set of fields match the set of fields in the
1723 """Changesets in which a given set of fields match the set of fields in the
1723 selected revision or set.
1724 selected revision or set.
1724
1725
1725 To match more than one field pass the list of fields to match separated
1726 To match more than one field pass the list of fields to match separated
1726 by spaces (e.g. ``author description``).
1727 by spaces (e.g. ``author description``).
1727
1728
1728 Valid fields are most regular revision fields and some special fields.
1729 Valid fields are most regular revision fields and some special fields.
1729
1730
1730 Regular revision fields are ``description``, ``author``, ``branch``,
1731 Regular revision fields are ``description``, ``author``, ``branch``,
1731 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1732 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1732 and ``diff``.
1733 and ``diff``.
1733 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1734 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1734 contents of the revision. Two revisions matching their ``diff`` will
1735 contents of the revision. Two revisions matching their ``diff`` will
1735 also match their ``files``.
1736 also match their ``files``.
1736
1737
1737 Special fields are ``summary`` and ``metadata``:
1738 Special fields are ``summary`` and ``metadata``:
1738 ``summary`` matches the first line of the description.
1739 ``summary`` matches the first line of the description.
1739 ``metadata`` is equivalent to matching ``description user date``
1740 ``metadata`` is equivalent to matching ``description user date``
1740 (i.e. it matches the main metadata fields).
1741 (i.e. it matches the main metadata fields).
1741
1742
1742 ``metadata`` is the default field which is used when no fields are
1743 ``metadata`` is the default field which is used when no fields are
1743 specified. You can match more than one field at a time.
1744 specified. You can match more than one field at a time.
1744 """
1745 """
1745 # i18n: "matching" is a keyword
1746 # i18n: "matching" is a keyword
1746 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1747 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1747
1748
1748 revs = getset(repo, fullreposet(repo), l[0])
1749 revs = getset(repo, fullreposet(repo), l[0])
1749
1750
1750 fieldlist = ['metadata']
1751 fieldlist = ['metadata']
1751 if len(l) > 1:
1752 if len(l) > 1:
1752 fieldlist = getstring(l[1],
1753 fieldlist = getstring(l[1],
1753 # i18n: "matching" is a keyword
1754 # i18n: "matching" is a keyword
1754 _("matching requires a string "
1755 _("matching requires a string "
1755 "as its second argument")).split()
1756 "as its second argument")).split()
1756
1757
1757 # Make sure that there are no repeated fields,
1758 # Make sure that there are no repeated fields,
1758 # expand the 'special' 'metadata' field type
1759 # expand the 'special' 'metadata' field type
1759 # and check the 'files' whenever we check the 'diff'
1760 # and check the 'files' whenever we check the 'diff'
1760 fields = []
1761 fields = []
1761 for field in fieldlist:
1762 for field in fieldlist:
1762 if field == 'metadata':
1763 if field == 'metadata':
1763 fields += ['user', 'description', 'date']
1764 fields += ['user', 'description', 'date']
1764 elif field == 'diff':
1765 elif field == 'diff':
1765 # a revision matching the diff must also match the files
1766 # a revision matching the diff must also match the files
1766 # since matching the diff is very costly, make sure to
1767 # since matching the diff is very costly, make sure to
1767 # also match the files first
1768 # also match the files first
1768 fields += ['files', 'diff']
1769 fields += ['files', 'diff']
1769 else:
1770 else:
1770 if field == 'author':
1771 if field == 'author':
1771 field = 'user'
1772 field = 'user'
1772 fields.append(field)
1773 fields.append(field)
1773 fields = set(fields)
1774 fields = set(fields)
1774 if 'summary' in fields and 'description' in fields:
1775 if 'summary' in fields and 'description' in fields:
1775 # If a revision matches its description it also matches its summary
1776 # If a revision matches its description it also matches its summary
1776 fields.discard('summary')
1777 fields.discard('summary')
1777
1778
1778 # We may want to match more than one field
1779 # We may want to match more than one field
1779 # Not all fields take the same amount of time to be matched
1780 # Not all fields take the same amount of time to be matched
1780 # Sort the selected fields in order of increasing matching cost
1781 # Sort the selected fields in order of increasing matching cost
1781 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1782 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1782 'files', 'description', 'substate', 'diff']
1783 'files', 'description', 'substate', 'diff']
1783 def fieldkeyfunc(f):
1784 def fieldkeyfunc(f):
1784 try:
1785 try:
1785 return fieldorder.index(f)
1786 return fieldorder.index(f)
1786 except ValueError:
1787 except ValueError:
1787 # assume an unknown field is very costly
1788 # assume an unknown field is very costly
1788 return len(fieldorder)
1789 return len(fieldorder)
1789 fields = list(fields)
1790 fields = list(fields)
1790 fields.sort(key=fieldkeyfunc)
1791 fields.sort(key=fieldkeyfunc)
1791
1792
1792 # Each field will be matched with its own "getfield" function
1793 # Each field will be matched with its own "getfield" function
1793 # which will be added to the getfieldfuncs array of functions
1794 # which will be added to the getfieldfuncs array of functions
1794 getfieldfuncs = []
1795 getfieldfuncs = []
1795 _funcs = {
1796 _funcs = {
1796 'user': lambda r: repo[r].user(),
1797 'user': lambda r: repo[r].user(),
1797 'branch': lambda r: repo[r].branch(),
1798 'branch': lambda r: repo[r].branch(),
1798 'date': lambda r: repo[r].date(),
1799 'date': lambda r: repo[r].date(),
1799 'description': lambda r: repo[r].description(),
1800 'description': lambda r: repo[r].description(),
1800 'files': lambda r: repo[r].files(),
1801 'files': lambda r: repo[r].files(),
1801 'parents': lambda r: repo[r].parents(),
1802 'parents': lambda r: repo[r].parents(),
1802 'phase': lambda r: repo[r].phase(),
1803 'phase': lambda r: repo[r].phase(),
1803 'substate': lambda r: repo[r].substate,
1804 'substate': lambda r: repo[r].substate,
1804 'summary': lambda r: repo[r].description().splitlines()[0],
1805 'summary': lambda r: repo[r].description().splitlines()[0],
1805 'diff': lambda r: list(repo[r].diff(opts={'git': True}),)
1806 'diff': lambda r: list(repo[r].diff(opts={'git': True}),)
1806 }
1807 }
1807 for info in fields:
1808 for info in fields:
1808 getfield = _funcs.get(info, None)
1809 getfield = _funcs.get(info, None)
1809 if getfield is None:
1810 if getfield is None:
1810 raise error.ParseError(
1811 raise error.ParseError(
1811 # i18n: "matching" is a keyword
1812 # i18n: "matching" is a keyword
1812 _("unexpected field name passed to matching: %s") % info)
1813 _("unexpected field name passed to matching: %s") % info)
1813 getfieldfuncs.append(getfield)
1814 getfieldfuncs.append(getfield)
1814 # convert the getfield array of functions into a "getinfo" function
1815 # convert the getfield array of functions into a "getinfo" function
1815 # which returns an array of field values (or a single value if there
1816 # which returns an array of field values (or a single value if there
1816 # is only one field to match)
1817 # is only one field to match)
1817 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1818 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1818
1819
1819 def matches(x):
1820 def matches(x):
1820 for rev in revs:
1821 for rev in revs:
1821 target = getinfo(rev)
1822 target = getinfo(rev)
1822 match = True
1823 match = True
1823 for n, f in enumerate(getfieldfuncs):
1824 for n, f in enumerate(getfieldfuncs):
1824 if target[n] != f(x):
1825 if target[n] != f(x):
1825 match = False
1826 match = False
1826 if match:
1827 if match:
1827 return True
1828 return True
1828 return False
1829 return False
1829
1830
1830 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1831 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1831
1832
1832 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1833 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1833 def reverse(repo, subset, x, order):
1834 def reverse(repo, subset, x, order):
1834 """Reverse order of set.
1835 """Reverse order of set.
1835 """
1836 """
1836 l = getset(repo, subset, x, order)
1837 l = getset(repo, subset, x, order)
1837 if order == defineorder:
1838 if order == defineorder:
1838 l.reverse()
1839 l.reverse()
1839 return l
1840 return l
1840
1841
1841 @predicate('roots(set)', safe=True)
1842 @predicate('roots(set)', safe=True)
1842 def roots(repo, subset, x):
1843 def roots(repo, subset, x):
1843 """Changesets in set with no parent changeset in set.
1844 """Changesets in set with no parent changeset in set.
1844 """
1845 """
1845 s = getset(repo, fullreposet(repo), x)
1846 s = getset(repo, fullreposet(repo), x)
1846 parents = repo.changelog.parentrevs
1847 parents = repo.changelog.parentrevs
1847 def filter(r):
1848 def filter(r):
1848 for p in parents(r):
1849 for p in parents(r):
1849 if 0 <= p and p in s:
1850 if 0 <= p and p in s:
1850 return False
1851 return False
1851 return True
1852 return True
1852 return subset & s.filter(filter, condrepr='<roots>')
1853 return subset & s.filter(filter, condrepr='<roots>')
1853
1854
1854 _sortkeyfuncs = {
1855 _sortkeyfuncs = {
1855 'rev': lambda c: c.rev(),
1856 'rev': lambda c: c.rev(),
1856 'branch': lambda c: c.branch(),
1857 'branch': lambda c: c.branch(),
1857 'desc': lambda c: c.description(),
1858 'desc': lambda c: c.description(),
1858 'user': lambda c: c.user(),
1859 'user': lambda c: c.user(),
1859 'author': lambda c: c.user(),
1860 'author': lambda c: c.user(),
1860 'date': lambda c: c.date()[0],
1861 'date': lambda c: c.date()[0],
1861 }
1862 }
1862
1863
1863 def _getsortargs(x):
1864 def _getsortargs(x):
1864 """Parse sort options into (set, [(key, reverse)], opts)"""
1865 """Parse sort options into (set, [(key, reverse)], opts)"""
1865 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1866 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1866 if 'set' not in args:
1867 if 'set' not in args:
1867 # i18n: "sort" is a keyword
1868 # i18n: "sort" is a keyword
1868 raise error.ParseError(_('sort requires one or two arguments'))
1869 raise error.ParseError(_('sort requires one or two arguments'))
1869 keys = "rev"
1870 keys = "rev"
1870 if 'keys' in args:
1871 if 'keys' in args:
1871 # i18n: "sort" is a keyword
1872 # i18n: "sort" is a keyword
1872 keys = getstring(args['keys'], _("sort spec must be a string"))
1873 keys = getstring(args['keys'], _("sort spec must be a string"))
1873
1874
1874 keyflags = []
1875 keyflags = []
1875 for k in keys.split():
1876 for k in keys.split():
1876 fk = k
1877 fk = k
1877 reverse = (k.startswith('-'))
1878 reverse = (k.startswith('-'))
1878 if reverse:
1879 if reverse:
1879 k = k[1:]
1880 k = k[1:]
1880 if k not in _sortkeyfuncs and k != 'topo':
1881 if k not in _sortkeyfuncs and k != 'topo':
1881 raise error.ParseError(
1882 raise error.ParseError(
1882 _("unknown sort key %r") % pycompat.bytestr(fk))
1883 _("unknown sort key %r") % pycompat.bytestr(fk))
1883 keyflags.append((k, reverse))
1884 keyflags.append((k, reverse))
1884
1885
1885 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1886 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1886 # i18n: "topo" is a keyword
1887 # i18n: "topo" is a keyword
1887 raise error.ParseError(_('topo sort order cannot be combined '
1888 raise error.ParseError(_('topo sort order cannot be combined '
1888 'with other sort keys'))
1889 'with other sort keys'))
1889
1890
1890 opts = {}
1891 opts = {}
1891 if 'topo.firstbranch' in args:
1892 if 'topo.firstbranch' in args:
1892 if any(k == 'topo' for k, reverse in keyflags):
1893 if any(k == 'topo' for k, reverse in keyflags):
1893 opts['topo.firstbranch'] = args['topo.firstbranch']
1894 opts['topo.firstbranch'] = args['topo.firstbranch']
1894 else:
1895 else:
1895 # i18n: "topo" and "topo.firstbranch" are keywords
1896 # i18n: "topo" and "topo.firstbranch" are keywords
1896 raise error.ParseError(_('topo.firstbranch can only be used '
1897 raise error.ParseError(_('topo.firstbranch can only be used '
1897 'when using the topo sort key'))
1898 'when using the topo sort key'))
1898
1899
1899 return args['set'], keyflags, opts
1900 return args['set'], keyflags, opts
1900
1901
1901 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1902 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1902 weight=10)
1903 weight=10)
1903 def sort(repo, subset, x, order):
1904 def sort(repo, subset, x, order):
1904 """Sort set by keys. The default sort order is ascending, specify a key
1905 """Sort set by keys. The default sort order is ascending, specify a key
1905 as ``-key`` to sort in descending order.
1906 as ``-key`` to sort in descending order.
1906
1907
1907 The keys can be:
1908 The keys can be:
1908
1909
1909 - ``rev`` for the revision number,
1910 - ``rev`` for the revision number,
1910 - ``branch`` for the branch name,
1911 - ``branch`` for the branch name,
1911 - ``desc`` for the commit message (description),
1912 - ``desc`` for the commit message (description),
1912 - ``user`` for user name (``author`` can be used as an alias),
1913 - ``user`` for user name (``author`` can be used as an alias),
1913 - ``date`` for the commit date
1914 - ``date`` for the commit date
1914 - ``topo`` for a reverse topographical sort
1915 - ``topo`` for a reverse topographical sort
1915
1916
1916 The ``topo`` sort order cannot be combined with other sort keys. This sort
1917 The ``topo`` sort order cannot be combined with other sort keys. This sort
1917 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1918 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1918 specifies what topographical branches to prioritize in the sort.
1919 specifies what topographical branches to prioritize in the sort.
1919
1920
1920 """
1921 """
1921 s, keyflags, opts = _getsortargs(x)
1922 s, keyflags, opts = _getsortargs(x)
1922 revs = getset(repo, subset, s, order)
1923 revs = getset(repo, subset, s, order)
1923
1924
1924 if not keyflags or order != defineorder:
1925 if not keyflags or order != defineorder:
1925 return revs
1926 return revs
1926 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1927 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1927 revs.sort(reverse=keyflags[0][1])
1928 revs.sort(reverse=keyflags[0][1])
1928 return revs
1929 return revs
1929 elif keyflags[0][0] == "topo":
1930 elif keyflags[0][0] == "topo":
1930 firstbranch = ()
1931 firstbranch = ()
1931 if 'topo.firstbranch' in opts:
1932 if 'topo.firstbranch' in opts:
1932 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1933 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1933 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1934 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1934 firstbranch),
1935 firstbranch),
1935 istopo=True)
1936 istopo=True)
1936 if keyflags[0][1]:
1937 if keyflags[0][1]:
1937 revs.reverse()
1938 revs.reverse()
1938 return revs
1939 return revs
1939
1940
1940 # sort() is guaranteed to be stable
1941 # sort() is guaranteed to be stable
1941 ctxs = [repo[r] for r in revs]
1942 ctxs = [repo[r] for r in revs]
1942 for k, reverse in reversed(keyflags):
1943 for k, reverse in reversed(keyflags):
1943 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1944 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1944 return baseset([c.rev() for c in ctxs])
1945 return baseset([c.rev() for c in ctxs])
1945
1946
1946 @predicate('subrepo([pattern])')
1947 @predicate('subrepo([pattern])')
1947 def subrepo(repo, subset, x):
1948 def subrepo(repo, subset, x):
1948 """Changesets that add, modify or remove the given subrepo. If no subrepo
1949 """Changesets that add, modify or remove the given subrepo. If no subrepo
1949 pattern is named, any subrepo changes are returned.
1950 pattern is named, any subrepo changes are returned.
1950 """
1951 """
1951 # i18n: "subrepo" is a keyword
1952 # i18n: "subrepo" is a keyword
1952 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1953 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1953 pat = None
1954 pat = None
1954 if len(args) != 0:
1955 if len(args) != 0:
1955 pat = getstring(args[0], _("subrepo requires a pattern"))
1956 pat = getstring(args[0], _("subrepo requires a pattern"))
1956
1957
1957 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1958 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1958
1959
1959 def submatches(names):
1960 def submatches(names):
1960 k, p, m = stringutil.stringmatcher(pat)
1961 k, p, m = stringutil.stringmatcher(pat)
1961 for name in names:
1962 for name in names:
1962 if m(name):
1963 if m(name):
1963 yield name
1964 yield name
1964
1965
1965 def matches(x):
1966 def matches(x):
1966 c = repo[x]
1967 c = repo[x]
1967 s = repo.status(c.p1().node(), c.node(), match=m)
1968 s = repo.status(c.p1().node(), c.node(), match=m)
1968
1969
1969 if pat is None:
1970 if pat is None:
1970 return s.added or s.modified or s.removed
1971 return s.added or s.modified or s.removed
1971
1972
1972 if s.added:
1973 if s.added:
1973 return any(submatches(c.substate.keys()))
1974 return any(submatches(c.substate.keys()))
1974
1975
1975 if s.modified:
1976 if s.modified:
1976 subs = set(c.p1().substate.keys())
1977 subs = set(c.p1().substate.keys())
1977 subs.update(c.substate.keys())
1978 subs.update(c.substate.keys())
1978
1979
1979 for path in submatches(subs):
1980 for path in submatches(subs):
1980 if c.p1().substate.get(path) != c.substate.get(path):
1981 if c.p1().substate.get(path) != c.substate.get(path):
1981 return True
1982 return True
1982
1983
1983 if s.removed:
1984 if s.removed:
1984 return any(submatches(c.p1().substate.keys()))
1985 return any(submatches(c.p1().substate.keys()))
1985
1986
1986 return False
1987 return False
1987
1988
1988 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1989 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1989
1990
1990 def _mapbynodefunc(repo, s, f):
1991 def _mapbynodefunc(repo, s, f):
1991 """(repo, smartset, [node] -> [node]) -> smartset
1992 """(repo, smartset, [node] -> [node]) -> smartset
1992
1993
1993 Helper method to map a smartset to another smartset given a function only
1994 Helper method to map a smartset to another smartset given a function only
1994 talking about nodes. Handles converting between rev numbers and nodes, and
1995 talking about nodes. Handles converting between rev numbers and nodes, and
1995 filtering.
1996 filtering.
1996 """
1997 """
1997 cl = repo.unfiltered().changelog
1998 cl = repo.unfiltered().changelog
1998 torev = cl.rev
1999 torev = cl.rev
1999 tonode = cl.node
2000 tonode = cl.node
2000 nodemap = cl.nodemap
2001 nodemap = cl.nodemap
2001 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
2002 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
2002 return smartset.baseset(result - repo.changelog.filteredrevs)
2003 return smartset.baseset(result - repo.changelog.filteredrevs)
2003
2004
2004 @predicate('successors(set)', safe=True)
2005 @predicate('successors(set)', safe=True)
2005 def successors(repo, subset, x):
2006 def successors(repo, subset, x):
2006 """All successors for set, including the given set themselves"""
2007 """All successors for set, including the given set themselves"""
2007 s = getset(repo, fullreposet(repo), x)
2008 s = getset(repo, fullreposet(repo), x)
2008 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2009 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2009 d = _mapbynodefunc(repo, s, f)
2010 d = _mapbynodefunc(repo, s, f)
2010 return subset & d
2011 return subset & d
2011
2012
2012 def _substringmatcher(pattern, casesensitive=True):
2013 def _substringmatcher(pattern, casesensitive=True):
2013 kind, pattern, matcher = stringutil.stringmatcher(
2014 kind, pattern, matcher = stringutil.stringmatcher(
2014 pattern, casesensitive=casesensitive)
2015 pattern, casesensitive=casesensitive)
2015 if kind == 'literal':
2016 if kind == 'literal':
2016 if not casesensitive:
2017 if not casesensitive:
2017 pattern = encoding.lower(pattern)
2018 pattern = encoding.lower(pattern)
2018 matcher = lambda s: pattern in encoding.lower(s)
2019 matcher = lambda s: pattern in encoding.lower(s)
2019 else:
2020 else:
2020 matcher = lambda s: pattern in s
2021 matcher = lambda s: pattern in s
2021 return kind, pattern, matcher
2022 return kind, pattern, matcher
2022
2023
2023 @predicate('tag([name])', safe=True)
2024 @predicate('tag([name])', safe=True)
2024 def tag(repo, subset, x):
2025 def tag(repo, subset, x):
2025 """The specified tag by name, or all tagged revisions if no name is given.
2026 """The specified tag by name, or all tagged revisions if no name is given.
2026
2027
2027 Pattern matching is supported for `name`. See
2028 Pattern matching is supported for `name`. See
2028 :hg:`help revisions.patterns`.
2029 :hg:`help revisions.patterns`.
2029 """
2030 """
2030 # i18n: "tag" is a keyword
2031 # i18n: "tag" is a keyword
2031 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2032 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2032 cl = repo.changelog
2033 cl = repo.changelog
2033 if args:
2034 if args:
2034 pattern = getstring(args[0],
2035 pattern = getstring(args[0],
2035 # i18n: "tag" is a keyword
2036 # i18n: "tag" is a keyword
2036 _('the argument to tag must be a string'))
2037 _('the argument to tag must be a string'))
2037 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2038 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2038 if kind == 'literal':
2039 if kind == 'literal':
2039 # avoid resolving all tags
2040 # avoid resolving all tags
2040 tn = repo._tagscache.tags.get(pattern, None)
2041 tn = repo._tagscache.tags.get(pattern, None)
2041 if tn is None:
2042 if tn is None:
2042 raise error.RepoLookupError(_("tag '%s' does not exist")
2043 raise error.RepoLookupError(_("tag '%s' does not exist")
2043 % pattern)
2044 % pattern)
2044 s = {repo[tn].rev()}
2045 s = {repo[tn].rev()}
2045 else:
2046 else:
2046 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2047 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2047 else:
2048 else:
2048 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2049 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2049 return subset & s
2050 return subset & s
2050
2051
2051 @predicate('tagged', safe=True)
2052 @predicate('tagged', safe=True)
2052 def tagged(repo, subset, x):
2053 def tagged(repo, subset, x):
2053 return tag(repo, subset, x)
2054 return tag(repo, subset, x)
2054
2055
2055 @predicate('orphan()', safe=True)
2056 @predicate('orphan()', safe=True)
2056 def orphan(repo, subset, x):
2057 def orphan(repo, subset, x):
2057 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2058 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2058 """
2059 """
2059 # i18n: "orphan" is a keyword
2060 # i18n: "orphan" is a keyword
2060 getargs(x, 0, 0, _("orphan takes no arguments"))
2061 getargs(x, 0, 0, _("orphan takes no arguments"))
2061 orphan = obsmod.getrevs(repo, 'orphan')
2062 orphan = obsmod.getrevs(repo, 'orphan')
2062 return subset & orphan
2063 return subset & orphan
2063
2064
2064
2065
2065 @predicate('user(string)', safe=True, weight=10)
2066 @predicate('user(string)', safe=True, weight=10)
2066 def user(repo, subset, x):
2067 def user(repo, subset, x):
2067 """User name contains string. The match is case-insensitive.
2068 """User name contains string. The match is case-insensitive.
2068
2069
2069 Pattern matching is supported for `string`. See
2070 Pattern matching is supported for `string`. See
2070 :hg:`help revisions.patterns`.
2071 :hg:`help revisions.patterns`.
2071 """
2072 """
2072 return author(repo, subset, x)
2073 return author(repo, subset, x)
2073
2074
2074 @predicate('wdir()', safe=True, weight=0)
2075 @predicate('wdir()', safe=True, weight=0)
2075 def wdir(repo, subset, x):
2076 def wdir(repo, subset, x):
2076 """Working directory. (EXPERIMENTAL)"""
2077 """Working directory. (EXPERIMENTAL)"""
2077 # i18n: "wdir" is a keyword
2078 # i18n: "wdir" is a keyword
2078 getargs(x, 0, 0, _("wdir takes no arguments"))
2079 getargs(x, 0, 0, _("wdir takes no arguments"))
2079 if node.wdirrev in subset or isinstance(subset, fullreposet):
2080 if node.wdirrev in subset or isinstance(subset, fullreposet):
2080 return baseset([node.wdirrev])
2081 return baseset([node.wdirrev])
2081 return baseset()
2082 return baseset()
2082
2083
2083 def _orderedlist(repo, subset, x):
2084 def _orderedlist(repo, subset, x):
2084 s = getstring(x, "internal error")
2085 s = getstring(x, "internal error")
2085 if not s:
2086 if not s:
2086 return baseset()
2087 return baseset()
2087 # remove duplicates here. it's difficult for caller to deduplicate sets
2088 # remove duplicates here. it's difficult for caller to deduplicate sets
2088 # because different symbols can point to the same rev.
2089 # because different symbols can point to the same rev.
2089 cl = repo.changelog
2090 cl = repo.changelog
2090 ls = []
2091 ls = []
2091 seen = set()
2092 seen = set()
2092 for t in s.split('\0'):
2093 for t in s.split('\0'):
2093 try:
2094 try:
2094 # fast path for integer revision
2095 # fast path for integer revision
2095 r = int(t)
2096 r = int(t)
2096 if ('%d' % r) != t or r not in cl:
2097 if ('%d' % r) != t or r not in cl:
2097 raise ValueError
2098 raise ValueError
2098 revs = [r]
2099 revs = [r]
2099 except ValueError:
2100 except ValueError:
2100 revs = stringset(repo, subset, t, defineorder)
2101 revs = stringset(repo, subset, t, defineorder)
2101
2102
2102 for r in revs:
2103 for r in revs:
2103 if r in seen:
2104 if r in seen:
2104 continue
2105 continue
2105 if (r in subset
2106 if (r in subset
2106 or r == node.nullrev and isinstance(subset, fullreposet)):
2107 or r == node.nullrev and isinstance(subset, fullreposet)):
2107 ls.append(r)
2108 ls.append(r)
2108 seen.add(r)
2109 seen.add(r)
2109 return baseset(ls)
2110 return baseset(ls)
2110
2111
2111 # for internal use
2112 # for internal use
2112 @predicate('_list', safe=True, takeorder=True)
2113 @predicate('_list', safe=True, takeorder=True)
2113 def _list(repo, subset, x, order):
2114 def _list(repo, subset, x, order):
2114 if order == followorder:
2115 if order == followorder:
2115 # slow path to take the subset order
2116 # slow path to take the subset order
2116 return subset & _orderedlist(repo, fullreposet(repo), x)
2117 return subset & _orderedlist(repo, fullreposet(repo), x)
2117 else:
2118 else:
2118 return _orderedlist(repo, subset, x)
2119 return _orderedlist(repo, subset, x)
2119
2120
2120 def _orderedintlist(repo, subset, x):
2121 def _orderedintlist(repo, subset, x):
2121 s = getstring(x, "internal error")
2122 s = getstring(x, "internal error")
2122 if not s:
2123 if not s:
2123 return baseset()
2124 return baseset()
2124 ls = [int(r) for r in s.split('\0')]
2125 ls = [int(r) for r in s.split('\0')]
2125 s = subset
2126 s = subset
2126 return baseset([r for r in ls if r in s])
2127 return baseset([r for r in ls if r in s])
2127
2128
2128 # for internal use
2129 # for internal use
2129 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2130 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2130 def _intlist(repo, subset, x, order):
2131 def _intlist(repo, subset, x, order):
2131 if order == followorder:
2132 if order == followorder:
2132 # slow path to take the subset order
2133 # slow path to take the subset order
2133 return subset & _orderedintlist(repo, fullreposet(repo), x)
2134 return subset & _orderedintlist(repo, fullreposet(repo), x)
2134 else:
2135 else:
2135 return _orderedintlist(repo, subset, x)
2136 return _orderedintlist(repo, subset, x)
2136
2137
2137 def _orderedhexlist(repo, subset, x):
2138 def _orderedhexlist(repo, subset, x):
2138 s = getstring(x, "internal error")
2139 s = getstring(x, "internal error")
2139 if not s:
2140 if not s:
2140 return baseset()
2141 return baseset()
2141 cl = repo.changelog
2142 cl = repo.changelog
2142 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2143 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2143 s = subset
2144 s = subset
2144 return baseset([r for r in ls if r in s])
2145 return baseset([r for r in ls if r in s])
2145
2146
2146 # for internal use
2147 # for internal use
2147 @predicate('_hexlist', safe=True, takeorder=True)
2148 @predicate('_hexlist', safe=True, takeorder=True)
2148 def _hexlist(repo, subset, x, order):
2149 def _hexlist(repo, subset, x, order):
2149 if order == followorder:
2150 if order == followorder:
2150 # slow path to take the subset order
2151 # slow path to take the subset order
2151 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2152 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2152 else:
2153 else:
2153 return _orderedhexlist(repo, subset, x)
2154 return _orderedhexlist(repo, subset, x)
2154
2155
2155 methods = {
2156 methods = {
2156 "range": rangeset,
2157 "range": rangeset,
2157 "rangeall": rangeall,
2158 "rangeall": rangeall,
2158 "rangepre": rangepre,
2159 "rangepre": rangepre,
2159 "rangepost": rangepost,
2160 "rangepost": rangepost,
2160 "dagrange": dagrange,
2161 "dagrange": dagrange,
2161 "string": stringset,
2162 "string": stringset,
2162 "symbol": stringset,
2163 "symbol": stringset,
2163 "and": andset,
2164 "and": andset,
2164 "andsmally": andsmallyset,
2165 "andsmally": andsmallyset,
2165 "or": orset,
2166 "or": orset,
2166 "not": notset,
2167 "not": notset,
2167 "difference": differenceset,
2168 "difference": differenceset,
2168 "relation": relationset,
2169 "relation": relationset,
2169 "relsubscript": relsubscriptset,
2170 "relsubscript": relsubscriptset,
2170 "subscript": subscriptset,
2171 "subscript": subscriptset,
2171 "list": listset,
2172 "list": listset,
2172 "keyvalue": keyvaluepair,
2173 "keyvalue": keyvaluepair,
2173 "func": func,
2174 "func": func,
2174 "ancestor": ancestorspec,
2175 "ancestor": ancestorspec,
2175 "parent": parentspec,
2176 "parent": parentspec,
2176 "parentpost": parentpost,
2177 "parentpost": parentpost,
2177 }
2178 }
2178
2179
2179 def lookupfn(repo):
2180 def lookupfn(repo):
2180 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2181 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2181
2182
2182 def match(ui, spec, lookup=None):
2183 def match(ui, spec, lookup=None):
2183 """Create a matcher for a single revision spec"""
2184 """Create a matcher for a single revision spec"""
2184 return matchany(ui, [spec], lookup=lookup)
2185 return matchany(ui, [spec], lookup=lookup)
2185
2186
2186 def matchany(ui, specs, lookup=None, localalias=None):
2187 def matchany(ui, specs, lookup=None, localalias=None):
2187 """Create a matcher that will include any revisions matching one of the
2188 """Create a matcher that will include any revisions matching one of the
2188 given specs
2189 given specs
2189
2190
2190 If lookup function is not None, the parser will first attempt to handle
2191 If lookup function is not None, the parser will first attempt to handle
2191 old-style ranges, which may contain operator characters.
2192 old-style ranges, which may contain operator characters.
2192
2193
2193 If localalias is not None, it is a dict {name: definitionstring}. It takes
2194 If localalias is not None, it is a dict {name: definitionstring}. It takes
2194 precedence over [revsetalias] config section.
2195 precedence over [revsetalias] config section.
2195 """
2196 """
2196 if not specs:
2197 if not specs:
2197 def mfunc(repo, subset=None):
2198 def mfunc(repo, subset=None):
2198 return baseset()
2199 return baseset()
2199 return mfunc
2200 return mfunc
2200 if not all(specs):
2201 if not all(specs):
2201 raise error.ParseError(_("empty query"))
2202 raise error.ParseError(_("empty query"))
2202 if len(specs) == 1:
2203 if len(specs) == 1:
2203 tree = revsetlang.parse(specs[0], lookup)
2204 tree = revsetlang.parse(specs[0], lookup)
2204 else:
2205 else:
2205 tree = ('or',
2206 tree = ('or',
2206 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2207 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2207
2208
2208 aliases = []
2209 aliases = []
2209 warn = None
2210 warn = None
2210 if ui:
2211 if ui:
2211 aliases.extend(ui.configitems('revsetalias'))
2212 aliases.extend(ui.configitems('revsetalias'))
2212 warn = ui.warn
2213 warn = ui.warn
2213 if localalias:
2214 if localalias:
2214 aliases.extend(localalias.items())
2215 aliases.extend(localalias.items())
2215 if aliases:
2216 if aliases:
2216 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2217 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2217 tree = revsetlang.foldconcat(tree)
2218 tree = revsetlang.foldconcat(tree)
2218 tree = revsetlang.analyze(tree)
2219 tree = revsetlang.analyze(tree)
2219 tree = revsetlang.optimize(tree)
2220 tree = revsetlang.optimize(tree)
2220 return makematcher(tree)
2221 return makematcher(tree)
2221
2222
2222 def makematcher(tree):
2223 def makematcher(tree):
2223 """Create a matcher from an evaluatable tree"""
2224 """Create a matcher from an evaluatable tree"""
2224 def mfunc(repo, subset=None, order=None):
2225 def mfunc(repo, subset=None, order=None):
2225 if order is None:
2226 if order is None:
2226 if subset is None:
2227 if subset is None:
2227 order = defineorder # 'x'
2228 order = defineorder # 'x'
2228 else:
2229 else:
2229 order = followorder # 'subset & x'
2230 order = followorder # 'subset & x'
2230 if subset is None:
2231 if subset is None:
2231 subset = fullreposet(repo)
2232 subset = fullreposet(repo)
2232 return getset(repo, subset, tree, order)
2233 return getset(repo, subset, tree, order)
2233 return mfunc
2234 return mfunc
2234
2235
2235 def loadpredicate(ui, extname, registrarobj):
2236 def loadpredicate(ui, extname, registrarobj):
2236 """Load revset predicates from specified registrarobj
2237 """Load revset predicates from specified registrarobj
2237 """
2238 """
2238 for name, func in registrarobj._table.iteritems():
2239 for name, func in registrarobj._table.iteritems():
2239 symbols[name] = func
2240 symbols[name] = func
2240 if func._safe:
2241 if func._safe:
2241 safesymbols.add(name)
2242 safesymbols.add(name)
2242
2243
2243 # load built-in predicates explicitly to setup safesymbols
2244 # load built-in predicates explicitly to setup safesymbols
2244 loadpredicate(None, None, predicate)
2245 loadpredicate(None, None, predicate)
2245
2246
2246 # tell hggettext to extract docstrings from these functions:
2247 # tell hggettext to extract docstrings from these functions:
2247 i18nfunctions = symbols.values()
2248 i18nfunctions = symbols.values()
@@ -1,2873 +1,2883
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols[b'r3232'] = r3232
19 > mercurial.revset.symbols[b'r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > drawdag=$TESTDIR/drawdag.py
23 > drawdag=$TESTDIR/drawdag.py
24 > testrevset=$TESTTMP/testrevset.py
24 > testrevset=$TESTTMP/testrevset.py
25 > EOF
25 > EOF
26
26
27 $ try() {
27 $ try() {
28 > hg debugrevspec --debug "$@"
28 > hg debugrevspec --debug "$@"
29 > }
29 > }
30
30
31 $ log() {
31 $ log() {
32 > hg log --template '{rev}\n' -r "$1"
32 > hg log --template '{rev}\n' -r "$1"
33 > }
33 > }
34
34
35 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 extension to build '_intlist()' and '_hexlist()', which is necessary because
36 these predicates use '\0' as a separator:
36 these predicates use '\0' as a separator:
37
37
38 $ cat <<EOF > debugrevlistspec.py
38 $ cat <<EOF > debugrevlistspec.py
39 > from __future__ import absolute_import
39 > from __future__ import absolute_import
40 > from mercurial import (
40 > from mercurial import (
41 > node as nodemod,
41 > node as nodemod,
42 > registrar,
42 > registrar,
43 > revset,
43 > revset,
44 > revsetlang,
44 > revsetlang,
45 > )
45 > )
46 > from mercurial.utils import stringutil
46 > from mercurial.utils import stringutil
47 > cmdtable = {}
47 > cmdtable = {}
48 > command = registrar.command(cmdtable)
48 > command = registrar.command(cmdtable)
49 > @command(b'debugrevlistspec',
49 > @command(b'debugrevlistspec',
50 > [(b'', b'optimize', None, b'print parsed tree after optimizing'),
50 > [(b'', b'optimize', None, b'print parsed tree after optimizing'),
51 > (b'', b'bin', None, b'unhexlify arguments')])
51 > (b'', b'bin', None, b'unhexlify arguments')])
52 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
52 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
53 > if opts['bin']:
53 > if opts['bin']:
54 > args = map(nodemod.bin, args)
54 > args = map(nodemod.bin, args)
55 > expr = revsetlang.formatspec(fmt, list(args))
55 > expr = revsetlang.formatspec(fmt, list(args))
56 > if ui.verbose:
56 > if ui.verbose:
57 > tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
57 > tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
58 > ui.note(revsetlang.prettyformat(tree), b"\n")
58 > ui.note(revsetlang.prettyformat(tree), b"\n")
59 > if opts["optimize"]:
59 > if opts["optimize"]:
60 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
60 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
61 > ui.note(b"* optimized:\n", revsetlang.prettyformat(opttree),
61 > ui.note(b"* optimized:\n", revsetlang.prettyformat(opttree),
62 > b"\n")
62 > b"\n")
63 > func = revset.match(ui, expr, lookup=revset.lookupfn(repo))
63 > func = revset.match(ui, expr, lookup=revset.lookupfn(repo))
64 > revs = func(repo)
64 > revs = func(repo)
65 > if ui.verbose:
65 > if ui.verbose:
66 > ui.note(b"* set:\n", stringutil.prettyrepr(revs), b"\n")
66 > ui.note(b"* set:\n", stringutil.prettyrepr(revs), b"\n")
67 > for c in revs:
67 > for c in revs:
68 > ui.write(b"%d\n" % c)
68 > ui.write(b"%d\n" % c)
69 > EOF
69 > EOF
70 $ cat <<EOF >> $HGRCPATH
70 $ cat <<EOF >> $HGRCPATH
71 > [extensions]
71 > [extensions]
72 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
72 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
73 > EOF
73 > EOF
74 $ trylist() {
74 $ trylist() {
75 > hg debugrevlistspec --debug "$@"
75 > hg debugrevlistspec --debug "$@"
76 > }
76 > }
77
77
78 $ hg init repo
78 $ hg init repo
79 $ cd repo
79 $ cd repo
80
80
81 $ echo a > a
81 $ echo a > a
82 $ hg branch a
82 $ hg branch a
83 marked working directory as branch a
83 marked working directory as branch a
84 (branches are permanent and global, did you want a bookmark?)
84 (branches are permanent and global, did you want a bookmark?)
85 $ hg ci -Aqm0
85 $ hg ci -Aqm0
86
86
87 $ echo b > b
87 $ echo b > b
88 $ hg branch b
88 $ hg branch b
89 marked working directory as branch b
89 marked working directory as branch b
90 $ hg ci -Aqm1
90 $ hg ci -Aqm1
91
91
92 $ rm a
92 $ rm a
93 $ hg branch a-b-c-
93 $ hg branch a-b-c-
94 marked working directory as branch a-b-c-
94 marked working directory as branch a-b-c-
95 $ hg ci -Aqm2 -u Bob
95 $ hg ci -Aqm2 -u Bob
96
96
97 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
97 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
98 2
98 2
99 $ hg log -r "extra('branch')" --template '{rev}\n'
99 $ hg log -r "extra('branch')" --template '{rev}\n'
100 0
100 0
101 1
101 1
102 2
102 2
103 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
103 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
104 0 a
104 0 a
105 2 a-b-c-
105 2 a-b-c-
106
106
107 $ hg co 1
107 $ hg co 1
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 $ hg branch +a+b+c+
109 $ hg branch +a+b+c+
110 marked working directory as branch +a+b+c+
110 marked working directory as branch +a+b+c+
111 $ hg ci -Aqm3
111 $ hg ci -Aqm3
112
112
113 $ hg co 2 # interleave
113 $ hg co 2 # interleave
114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
115 $ echo bb > b
115 $ echo bb > b
116 $ hg branch -- -a-b-c-
116 $ hg branch -- -a-b-c-
117 marked working directory as branch -a-b-c-
117 marked working directory as branch -a-b-c-
118 $ hg ci -Aqm4 -d "May 12 2005"
118 $ hg ci -Aqm4 -d "May 12 2005"
119
119
120 $ hg co 3
120 $ hg co 3
121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 $ hg branch !a/b/c/
122 $ hg branch !a/b/c/
123 marked working directory as branch !a/b/c/
123 marked working directory as branch !a/b/c/
124 $ hg ci -Aqm"5 bug"
124 $ hg ci -Aqm"5 bug"
125
125
126 $ hg merge 4
126 $ hg merge 4
127 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
127 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
128 (branch merge, don't forget to commit)
128 (branch merge, don't forget to commit)
129 $ hg branch _a_b_c_
129 $ hg branch _a_b_c_
130 marked working directory as branch _a_b_c_
130 marked working directory as branch _a_b_c_
131 $ hg ci -Aqm"6 issue619"
131 $ hg ci -Aqm"6 issue619"
132
132
133 $ hg branch .a.b.c.
133 $ hg branch .a.b.c.
134 marked working directory as branch .a.b.c.
134 marked working directory as branch .a.b.c.
135 $ hg ci -Aqm7
135 $ hg ci -Aqm7
136
136
137 $ hg branch all
137 $ hg branch all
138 marked working directory as branch all
138 marked working directory as branch all
139
139
140 $ hg co 4
140 $ hg co 4
141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 $ hg branch Γ©
142 $ hg branch Γ©
143 marked working directory as branch \xc3\xa9 (esc)
143 marked working directory as branch \xc3\xa9 (esc)
144 $ hg ci -Aqm9
144 $ hg ci -Aqm9
145
145
146 $ hg tag -r6 1.0
146 $ hg tag -r6 1.0
147 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
147 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
148
148
149 $ hg clone --quiet -U -r 7 . ../remote1
149 $ hg clone --quiet -U -r 7 . ../remote1
150 $ hg clone --quiet -U -r 8 . ../remote2
150 $ hg clone --quiet -U -r 8 . ../remote2
151 $ echo "[paths]" >> .hg/hgrc
151 $ echo "[paths]" >> .hg/hgrc
152 $ echo "default = ../remote1" >> .hg/hgrc
152 $ echo "default = ../remote1" >> .hg/hgrc
153
153
154 trivial
154 trivial
155
155
156 $ try 0:1
156 $ try 0:1
157 (range
157 (range
158 (symbol '0')
158 (symbol '0')
159 (symbol '1'))
159 (symbol '1'))
160 * set:
160 * set:
161 <spanset+ 0:2>
161 <spanset+ 0:2>
162 0
162 0
163 1
163 1
164 $ try --optimize :
164 $ try --optimize :
165 (rangeall
165 (rangeall
166 None)
166 None)
167 * optimized:
167 * optimized:
168 (rangeall
168 (rangeall
169 None)
169 None)
170 * set:
170 * set:
171 <spanset+ 0:10>
171 <spanset+ 0:10>
172 0
172 0
173 1
173 1
174 2
174 2
175 3
175 3
176 4
176 4
177 5
177 5
178 6
178 6
179 7
179 7
180 8
180 8
181 9
181 9
182 $ try 3::6
182 $ try 3::6
183 (dagrange
183 (dagrange
184 (symbol '3')
184 (symbol '3')
185 (symbol '6'))
185 (symbol '6'))
186 * set:
186 * set:
187 <baseset+ [3, 5, 6]>
187 <baseset+ [3, 5, 6]>
188 3
188 3
189 5
189 5
190 6
190 6
191 $ try '0|1|2'
191 $ try '0|1|2'
192 (or
192 (or
193 (list
193 (list
194 (symbol '0')
194 (symbol '0')
195 (symbol '1')
195 (symbol '1')
196 (symbol '2')))
196 (symbol '2')))
197 * set:
197 * set:
198 <baseset [0, 1, 2]>
198 <baseset [0, 1, 2]>
199 0
199 0
200 1
200 1
201 2
201 2
202
202
203 names that should work without quoting
203 names that should work without quoting
204
204
205 $ try a
205 $ try a
206 (symbol 'a')
206 (symbol 'a')
207 * set:
207 * set:
208 <baseset [0]>
208 <baseset [0]>
209 0
209 0
210 $ try b-a
210 $ try b-a
211 (minus
211 (minus
212 (symbol 'b')
212 (symbol 'b')
213 (symbol 'a'))
213 (symbol 'a'))
214 * set:
214 * set:
215 <filteredset
215 <filteredset
216 <baseset [1]>,
216 <baseset [1]>,
217 <not
217 <not
218 <baseset [0]>>>
218 <baseset [0]>>>
219 1
219 1
220 $ try _a_b_c_
220 $ try _a_b_c_
221 (symbol '_a_b_c_')
221 (symbol '_a_b_c_')
222 * set:
222 * set:
223 <baseset [6]>
223 <baseset [6]>
224 6
224 6
225 $ try _a_b_c_-a
225 $ try _a_b_c_-a
226 (minus
226 (minus
227 (symbol '_a_b_c_')
227 (symbol '_a_b_c_')
228 (symbol 'a'))
228 (symbol 'a'))
229 * set:
229 * set:
230 <filteredset
230 <filteredset
231 <baseset [6]>,
231 <baseset [6]>,
232 <not
232 <not
233 <baseset [0]>>>
233 <baseset [0]>>>
234 6
234 6
235 $ try .a.b.c.
235 $ try .a.b.c.
236 (symbol '.a.b.c.')
236 (symbol '.a.b.c.')
237 * set:
237 * set:
238 <baseset [7]>
238 <baseset [7]>
239 7
239 7
240 $ try .a.b.c.-a
240 $ try .a.b.c.-a
241 (minus
241 (minus
242 (symbol '.a.b.c.')
242 (symbol '.a.b.c.')
243 (symbol 'a'))
243 (symbol 'a'))
244 * set:
244 * set:
245 <filteredset
245 <filteredset
246 <baseset [7]>,
246 <baseset [7]>,
247 <not
247 <not
248 <baseset [0]>>>
248 <baseset [0]>>>
249 7
249 7
250
250
251 names that should be caught by fallback mechanism
251 names that should be caught by fallback mechanism
252
252
253 $ try -- '-a-b-c-'
253 $ try -- '-a-b-c-'
254 (symbol '-a-b-c-')
254 (symbol '-a-b-c-')
255 * set:
255 * set:
256 <baseset [4]>
256 <baseset [4]>
257 4
257 4
258 $ log -a-b-c-
258 $ log -a-b-c-
259 4
259 4
260 $ try '+a+b+c+'
260 $ try '+a+b+c+'
261 (symbol '+a+b+c+')
261 (symbol '+a+b+c+')
262 * set:
262 * set:
263 <baseset [3]>
263 <baseset [3]>
264 3
264 3
265 $ try '+a+b+c+:'
265 $ try '+a+b+c+:'
266 (rangepost
266 (rangepost
267 (symbol '+a+b+c+'))
267 (symbol '+a+b+c+'))
268 * set:
268 * set:
269 <spanset+ 3:10>
269 <spanset+ 3:10>
270 3
270 3
271 4
271 4
272 5
272 5
273 6
273 6
274 7
274 7
275 8
275 8
276 9
276 9
277 $ try ':+a+b+c+'
277 $ try ':+a+b+c+'
278 (rangepre
278 (rangepre
279 (symbol '+a+b+c+'))
279 (symbol '+a+b+c+'))
280 * set:
280 * set:
281 <spanset+ 0:4>
281 <spanset+ 0:4>
282 0
282 0
283 1
283 1
284 2
284 2
285 3
285 3
286 $ try -- '-a-b-c-:+a+b+c+'
286 $ try -- '-a-b-c-:+a+b+c+'
287 (range
287 (range
288 (symbol '-a-b-c-')
288 (symbol '-a-b-c-')
289 (symbol '+a+b+c+'))
289 (symbol '+a+b+c+'))
290 * set:
290 * set:
291 <spanset- 3:5>
291 <spanset- 3:5>
292 4
292 4
293 3
293 3
294 $ log '-a-b-c-:+a+b+c+'
294 $ log '-a-b-c-:+a+b+c+'
295 4
295 4
296 3
296 3
297
297
298 $ try -- -a-b-c--a # complains
298 $ try -- -a-b-c--a # complains
299 (minus
299 (minus
300 (minus
300 (minus
301 (minus
301 (minus
302 (negate
302 (negate
303 (symbol 'a'))
303 (symbol 'a'))
304 (symbol 'b'))
304 (symbol 'b'))
305 (symbol 'c'))
305 (symbol 'c'))
306 (negate
306 (negate
307 (symbol 'a')))
307 (symbol 'a')))
308 abort: unknown revision '-a'!
308 abort: unknown revision '-a'!
309 [255]
309 [255]
310 $ try Γ©
310 $ try Γ©
311 (symbol '\xc3\xa9')
311 (symbol '\xc3\xa9')
312 * set:
312 * set:
313 <baseset [9]>
313 <baseset [9]>
314 9
314 9
315
315
316 no quoting needed
316 no quoting needed
317
317
318 $ log ::a-b-c-
318 $ log ::a-b-c-
319 0
319 0
320 1
320 1
321 2
321 2
322
322
323 quoting needed
323 quoting needed
324
324
325 $ try '"-a-b-c-"-a'
325 $ try '"-a-b-c-"-a'
326 (minus
326 (minus
327 (string '-a-b-c-')
327 (string '-a-b-c-')
328 (symbol 'a'))
328 (symbol 'a'))
329 * set:
329 * set:
330 <filteredset
330 <filteredset
331 <baseset [4]>,
331 <baseset [4]>,
332 <not
332 <not
333 <baseset [0]>>>
333 <baseset [0]>>>
334 4
334 4
335
335
336 $ log '1 or 2'
336 $ log '1 or 2'
337 1
337 1
338 2
338 2
339 $ log '1|2'
339 $ log '1|2'
340 1
340 1
341 2
341 2
342 $ log '1 and 2'
342 $ log '1 and 2'
343 $ log '1&2'
343 $ log '1&2'
344 $ try '1&2|3' # precedence - and is higher
344 $ try '1&2|3' # precedence - and is higher
345 (or
345 (or
346 (list
346 (list
347 (and
347 (and
348 (symbol '1')
348 (symbol '1')
349 (symbol '2'))
349 (symbol '2'))
350 (symbol '3')))
350 (symbol '3')))
351 * set:
351 * set:
352 <addset
352 <addset
353 <baseset []>,
353 <baseset []>,
354 <baseset [3]>>
354 <baseset [3]>>
355 3
355 3
356 $ try '1|2&3'
356 $ try '1|2&3'
357 (or
357 (or
358 (list
358 (list
359 (symbol '1')
359 (symbol '1')
360 (and
360 (and
361 (symbol '2')
361 (symbol '2')
362 (symbol '3'))))
362 (symbol '3'))))
363 * set:
363 * set:
364 <addset
364 <addset
365 <baseset [1]>,
365 <baseset [1]>,
366 <baseset []>>
366 <baseset []>>
367 1
367 1
368 $ try '1&2&3' # associativity
368 $ try '1&2&3' # associativity
369 (and
369 (and
370 (and
370 (and
371 (symbol '1')
371 (symbol '1')
372 (symbol '2'))
372 (symbol '2'))
373 (symbol '3'))
373 (symbol '3'))
374 * set:
374 * set:
375 <baseset []>
375 <baseset []>
376 $ try '1|(2|3)'
376 $ try '1|(2|3)'
377 (or
377 (or
378 (list
378 (list
379 (symbol '1')
379 (symbol '1')
380 (group
380 (group
381 (or
381 (or
382 (list
382 (list
383 (symbol '2')
383 (symbol '2')
384 (symbol '3'))))))
384 (symbol '3'))))))
385 * set:
385 * set:
386 <addset
386 <addset
387 <baseset [1]>,
387 <baseset [1]>,
388 <baseset [2, 3]>>
388 <baseset [2, 3]>>
389 1
389 1
390 2
390 2
391 3
391 3
392 $ log '1.0' # tag
392 $ log '1.0' # tag
393 6
393 6
394 $ log 'a' # branch
394 $ log 'a' # branch
395 0
395 0
396 $ log '2785f51ee'
396 $ log '2785f51ee'
397 0
397 0
398 $ log 'date(2005)'
398 $ log 'date(2005)'
399 4
399 4
400 $ log 'date(this is a test)'
400 $ log 'date(this is a test)'
401 hg: parse error at 10: unexpected token: symbol
401 hg: parse error at 10: unexpected token: symbol
402 (date(this is a test)
402 (date(this is a test)
403 ^ here)
403 ^ here)
404 [255]
404 [255]
405 $ log 'date()'
405 $ log 'date()'
406 hg: parse error: date requires a string
406 hg: parse error: date requires a string
407 [255]
407 [255]
408 $ log 'date'
408 $ log 'date'
409 abort: unknown revision 'date'!
409 abort: unknown revision 'date'!
410 [255]
410 [255]
411 $ log 'date('
411 $ log 'date('
412 hg: parse error at 5: not a prefix: end
412 hg: parse error at 5: not a prefix: end
413 (date(
413 (date(
414 ^ here)
414 ^ here)
415 [255]
415 [255]
416 $ log 'date("\xy")'
416 $ log 'date("\xy")'
417 hg: parse error: invalid \x escape* (glob)
417 hg: parse error: invalid \x escape* (glob)
418 [255]
418 [255]
419 $ log 'date(tip)'
419 $ log 'date(tip)'
420 hg: parse error: invalid date: 'tip'
420 hg: parse error: invalid date: 'tip'
421 [255]
421 [255]
422 $ log '0:date'
422 $ log '0:date'
423 abort: unknown revision 'date'!
423 abort: unknown revision 'date'!
424 [255]
424 [255]
425 $ log '::"date"'
425 $ log '::"date"'
426 abort: unknown revision 'date'!
426 abort: unknown revision 'date'!
427 [255]
427 [255]
428 $ hg book date -r 4
428 $ hg book date -r 4
429 $ log '0:date'
429 $ log '0:date'
430 0
430 0
431 1
431 1
432 2
432 2
433 3
433 3
434 4
434 4
435 $ log '::date'
435 $ log '::date'
436 0
436 0
437 1
437 1
438 2
438 2
439 4
439 4
440 $ log '::"date"'
440 $ log '::"date"'
441 0
441 0
442 1
442 1
443 2
443 2
444 4
444 4
445 $ log 'date(2005) and 1::'
445 $ log 'date(2005) and 1::'
446 4
446 4
447 $ hg book -d date
447 $ hg book -d date
448
448
449 function name should be a symbol
449 function name should be a symbol
450
450
451 $ log '"date"(2005)'
451 $ log '"date"(2005)'
452 hg: parse error: not a symbol
452 hg: parse error: not a symbol
453 [255]
453 [255]
454
454
455 keyword arguments
455 keyword arguments
456
456
457 $ log 'extra(branch, value=a)'
457 $ log 'extra(branch, value=a)'
458 0
458 0
459
459
460 $ log 'extra(branch, a, b)'
460 $ log 'extra(branch, a, b)'
461 hg: parse error: extra takes at most 2 positional arguments
461 hg: parse error: extra takes at most 2 positional arguments
462 [255]
462 [255]
463 $ log 'extra(a, label=b)'
463 $ log 'extra(a, label=b)'
464 hg: parse error: extra got multiple values for keyword argument 'label'
464 hg: parse error: extra got multiple values for keyword argument 'label'
465 [255]
465 [255]
466 $ log 'extra(label=branch, default)'
466 $ log 'extra(label=branch, default)'
467 hg: parse error: extra got an invalid argument
467 hg: parse error: extra got an invalid argument
468 [255]
468 [255]
469 $ log 'extra(branch, foo+bar=baz)'
469 $ log 'extra(branch, foo+bar=baz)'
470 hg: parse error: extra got an invalid argument
470 hg: parse error: extra got an invalid argument
471 [255]
471 [255]
472 $ log 'extra(unknown=branch)'
472 $ log 'extra(unknown=branch)'
473 hg: parse error: extra got an unexpected keyword argument 'unknown'
473 hg: parse error: extra got an unexpected keyword argument 'unknown'
474 [255]
474 [255]
475
475
476 $ try 'foo=bar|baz'
476 $ try 'foo=bar|baz'
477 (keyvalue
477 (keyvalue
478 (symbol 'foo')
478 (symbol 'foo')
479 (or
479 (or
480 (list
480 (list
481 (symbol 'bar')
481 (symbol 'bar')
482 (symbol 'baz'))))
482 (symbol 'baz'))))
483 hg: parse error: can't use a key-value pair in this context
483 hg: parse error: can't use a key-value pair in this context
484 [255]
484 [255]
485
485
486 right-hand side should be optimized recursively
486 right-hand side should be optimized recursively
487
487
488 $ try --optimize 'foo=(not public())'
488 $ try --optimize 'foo=(not public())'
489 (keyvalue
489 (keyvalue
490 (symbol 'foo')
490 (symbol 'foo')
491 (group
491 (group
492 (not
492 (not
493 (func
493 (func
494 (symbol 'public')
494 (symbol 'public')
495 None))))
495 None))))
496 * optimized:
496 * optimized:
497 (keyvalue
497 (keyvalue
498 (symbol 'foo')
498 (symbol 'foo')
499 (func
499 (func
500 (symbol '_notpublic')
500 (symbol '_notpublic')
501 None))
501 None))
502 hg: parse error: can't use a key-value pair in this context
502 hg: parse error: can't use a key-value pair in this context
503 [255]
503 [255]
504
504
505 relation-subscript operator has the highest binding strength (as function call):
505 relation-subscript operator has the highest binding strength (as function call):
506
506
507 $ hg debugrevspec -p parsed 'tip:tip^#generations[-1]'
507 $ hg debugrevspec -p parsed 'tip:tip^#generations[-1]'
508 * parsed:
508 * parsed:
509 (range
509 (range
510 (symbol 'tip')
510 (symbol 'tip')
511 (relsubscript
511 (relsubscript
512 (parentpost
512 (parentpost
513 (symbol 'tip'))
513 (symbol 'tip'))
514 (symbol 'generations')
514 (symbol 'generations')
515 (negate
515 (negate
516 (symbol '1'))))
516 (symbol '1'))))
517 9
517 9
518 8
518 8
519 7
519 7
520 6
520 6
521 5
521 5
522 4
522 4
523
523
524 $ hg debugrevspec -p parsed --no-show-revs 'not public()#generations[0]'
524 $ hg debugrevspec -p parsed --no-show-revs 'not public()#generations[0]'
525 * parsed:
525 * parsed:
526 (not
526 (not
527 (relsubscript
527 (relsubscript
528 (func
528 (func
529 (symbol 'public')
529 (symbol 'public')
530 None)
530 None)
531 (symbol 'generations')
531 (symbol 'generations')
532 (symbol '0')))
532 (symbol '0')))
533
533
534 left-hand side of relation-subscript operator should be optimized recursively:
534 left-hand side of relation-subscript operator should be optimized recursively:
535
535
536 $ hg debugrevspec -p analyzed -p optimized --no-show-revs \
536 $ hg debugrevspec -p analyzed -p optimized --no-show-revs \
537 > '(not public())#generations[0]'
537 > '(not public())#generations[0]'
538 * analyzed:
538 * analyzed:
539 (relsubscript
539 (relsubscript
540 (not
540 (not
541 (func
541 (func
542 (symbol 'public')
542 (symbol 'public')
543 None))
543 None))
544 (symbol 'generations')
544 (symbol 'generations')
545 (symbol '0'))
545 (symbol '0'))
546 * optimized:
546 * optimized:
547 (relsubscript
547 (relsubscript
548 (func
548 (func
549 (symbol '_notpublic')
549 (symbol '_notpublic')
550 None)
550 None)
551 (symbol 'generations')
551 (symbol 'generations')
552 (symbol '0'))
552 (symbol '0'))
553
553
554 resolution of subscript and relation-subscript ternary operators:
554 resolution of subscript and relation-subscript ternary operators:
555
555
556 $ hg debugrevspec -p analyzed 'tip[0]'
556 $ hg debugrevspec -p analyzed 'tip[0]'
557 * analyzed:
557 * analyzed:
558 (subscript
558 (subscript
559 (symbol 'tip')
559 (symbol 'tip')
560 (symbol '0'))
560 (symbol '0'))
561 hg: parse error: can't use a subscript in this context
561 hg: parse error: can't use a subscript in this context
562 [255]
562 [255]
563
563
564 $ hg debugrevspec -p analyzed 'tip#rel[0]'
564 $ hg debugrevspec -p analyzed 'tip#rel[0]'
565 * analyzed:
565 * analyzed:
566 (relsubscript
566 (relsubscript
567 (symbol 'tip')
567 (symbol 'tip')
568 (symbol 'rel')
568 (symbol 'rel')
569 (symbol '0'))
569 (symbol '0'))
570 hg: parse error: unknown identifier: rel
570 hg: parse error: unknown identifier: rel
571 [255]
571 [255]
572
572
573 $ hg debugrevspec -p analyzed '(tip#rel)[0]'
573 $ hg debugrevspec -p analyzed '(tip#rel)[0]'
574 * analyzed:
574 * analyzed:
575 (subscript
575 (subscript
576 (relation
576 (relation
577 (symbol 'tip')
577 (symbol 'tip')
578 (symbol 'rel'))
578 (symbol 'rel'))
579 (symbol '0'))
579 (symbol '0'))
580 hg: parse error: can't use a subscript in this context
580 hg: parse error: can't use a subscript in this context
581 [255]
581 [255]
582
582
583 $ hg debugrevspec -p analyzed 'tip#rel[0][1]'
583 $ hg debugrevspec -p analyzed 'tip#rel[0][1]'
584 * analyzed:
584 * analyzed:
585 (subscript
585 (subscript
586 (relsubscript
586 (relsubscript
587 (symbol 'tip')
587 (symbol 'tip')
588 (symbol 'rel')
588 (symbol 'rel')
589 (symbol '0'))
589 (symbol '0'))
590 (symbol '1'))
590 (symbol '1'))
591 hg: parse error: can't use a subscript in this context
591 hg: parse error: can't use a subscript in this context
592 [255]
592 [255]
593
593
594 $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
594 $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
595 * analyzed:
595 * analyzed:
596 (relsubscript
596 (relsubscript
597 (relation
597 (relation
598 (symbol 'tip')
598 (symbol 'tip')
599 (symbol 'rel0'))
599 (symbol 'rel0'))
600 (symbol 'rel1')
600 (symbol 'rel1')
601 (symbol '1'))
601 (symbol '1'))
602 hg: parse error: unknown identifier: rel1
602 hg: parse error: unknown identifier: rel1
603 [255]
603 [255]
604
604
605 $ hg debugrevspec -p analyzed 'tip#rel0[0]#rel1[1]'
605 $ hg debugrevspec -p analyzed 'tip#rel0[0]#rel1[1]'
606 * analyzed:
606 * analyzed:
607 (relsubscript
607 (relsubscript
608 (relsubscript
608 (relsubscript
609 (symbol 'tip')
609 (symbol 'tip')
610 (symbol 'rel0')
610 (symbol 'rel0')
611 (symbol '0'))
611 (symbol '0'))
612 (symbol 'rel1')
612 (symbol 'rel1')
613 (symbol '1'))
613 (symbol '1'))
614 hg: parse error: unknown identifier: rel1
614 hg: parse error: unknown identifier: rel1
615 [255]
615 [255]
616
616
617 parse errors of relation, subscript and relation-subscript operators:
617 parse errors of relation, subscript and relation-subscript operators:
618
618
619 $ hg debugrevspec '[0]'
619 $ hg debugrevspec '[0]'
620 hg: parse error at 0: not a prefix: [
620 hg: parse error at 0: not a prefix: [
621 ([0]
621 ([0]
622 ^ here)
622 ^ here)
623 [255]
623 [255]
624 $ hg debugrevspec '.#'
624 $ hg debugrevspec '.#'
625 hg: parse error at 2: not a prefix: end
625 hg: parse error at 2: not a prefix: end
626 (.#
626 (.#
627 ^ here)
627 ^ here)
628 [255]
628 [255]
629 $ hg debugrevspec '#rel'
629 $ hg debugrevspec '#rel'
630 hg: parse error at 0: not a prefix: #
630 hg: parse error at 0: not a prefix: #
631 (#rel
631 (#rel
632 ^ here)
632 ^ here)
633 [255]
633 [255]
634 $ hg debugrevspec '.#rel[0'
634 $ hg debugrevspec '.#rel[0'
635 hg: parse error at 7: unexpected token: end
635 hg: parse error at 7: unexpected token: end
636 (.#rel[0
636 (.#rel[0
637 ^ here)
637 ^ here)
638 [255]
638 [255]
639 $ hg debugrevspec '.]'
639 $ hg debugrevspec '.]'
640 hg: parse error at 1: invalid token
640 hg: parse error at 1: invalid token
641 (.]
641 (.]
642 ^ here)
642 ^ here)
643 [255]
643 [255]
644
644
645 $ hg debugrevspec '.#generations[a]'
645 $ hg debugrevspec '.#generations[a]'
646 hg: parse error: relation subscript must be an integer
646 hg: parse error: relation subscript must be an integer
647 [255]
647 [255]
648 $ hg debugrevspec '.#generations[1-2]'
648 $ hg debugrevspec '.#generations[1-2]'
649 hg: parse error: relation subscript must be an integer
649 hg: parse error: relation subscript must be an integer
650 [255]
650 [255]
651
651
652 parsed tree at stages:
652 parsed tree at stages:
653
653
654 $ hg debugrevspec -p all '()'
654 $ hg debugrevspec -p all '()'
655 * parsed:
655 * parsed:
656 (group
656 (group
657 None)
657 None)
658 * expanded:
658 * expanded:
659 (group
659 (group
660 None)
660 None)
661 * concatenated:
661 * concatenated:
662 (group
662 (group
663 None)
663 None)
664 * analyzed:
664 * analyzed:
665 None
665 None
666 * optimized:
666 * optimized:
667 None
667 None
668 hg: parse error: missing argument
668 hg: parse error: missing argument
669 [255]
669 [255]
670
670
671 $ hg debugrevspec --no-optimized -p all '()'
671 $ hg debugrevspec --no-optimized -p all '()'
672 * parsed:
672 * parsed:
673 (group
673 (group
674 None)
674 None)
675 * expanded:
675 * expanded:
676 (group
676 (group
677 None)
677 None)
678 * concatenated:
678 * concatenated:
679 (group
679 (group
680 None)
680 None)
681 * analyzed:
681 * analyzed:
682 None
682 None
683 hg: parse error: missing argument
683 hg: parse error: missing argument
684 [255]
684 [255]
685
685
686 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
686 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
687 * parsed:
687 * parsed:
688 (minus
688 (minus
689 (group
689 (group
690 (or
690 (or
691 (list
691 (list
692 (symbol '0')
692 (symbol '0')
693 (symbol '1'))))
693 (symbol '1'))))
694 (symbol '1'))
694 (symbol '1'))
695 * analyzed:
695 * analyzed:
696 (and
696 (and
697 (or
697 (or
698 (list
698 (list
699 (symbol '0')
699 (symbol '0')
700 (symbol '1')))
700 (symbol '1')))
701 (not
701 (not
702 (symbol '1')))
702 (symbol '1')))
703 * optimized:
703 * optimized:
704 (difference
704 (difference
705 (func
705 (func
706 (symbol '_list')
706 (symbol '_list')
707 (string '0\x001'))
707 (string '0\x001'))
708 (symbol '1'))
708 (symbol '1'))
709 0
709 0
710
710
711 $ hg debugrevspec -p unknown '0'
711 $ hg debugrevspec -p unknown '0'
712 abort: invalid stage name: unknown
712 abort: invalid stage name: unknown
713 [255]
713 [255]
714
714
715 $ hg debugrevspec -p all --optimize '0'
715 $ hg debugrevspec -p all --optimize '0'
716 abort: cannot use --optimize with --show-stage
716 abort: cannot use --optimize with --show-stage
717 [255]
717 [255]
718
718
719 verify optimized tree:
719 verify optimized tree:
720
720
721 $ hg debugrevspec --verify '0|1'
721 $ hg debugrevspec --verify '0|1'
722
722
723 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
723 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
724 * analyzed:
724 * analyzed:
725 (and
725 (and
726 (func
726 (func
727 (symbol 'r3232')
727 (symbol 'r3232')
728 None)
728 None)
729 (symbol '2'))
729 (symbol '2'))
730 * optimized:
730 * optimized:
731 (andsmally
731 (andsmally
732 (func
732 (func
733 (symbol 'r3232')
733 (symbol 'r3232')
734 None)
734 None)
735 (symbol '2'))
735 (symbol '2'))
736 * analyzed set:
736 * analyzed set:
737 <baseset [2]>
737 <baseset [2]>
738 * optimized set:
738 * optimized set:
739 <baseset [2, 2]>
739 <baseset [2, 2]>
740 --- analyzed
740 --- analyzed
741 +++ optimized
741 +++ optimized
742 2
742 2
743 +2
743 +2
744 [1]
744 [1]
745
745
746 $ hg debugrevspec --no-optimized --verify-optimized '0'
746 $ hg debugrevspec --no-optimized --verify-optimized '0'
747 abort: cannot use --verify-optimized with --no-optimized
747 abort: cannot use --verify-optimized with --no-optimized
748 [255]
748 [255]
749
749
750 Test that symbols only get parsed as functions if there's an opening
750 Test that symbols only get parsed as functions if there's an opening
751 parenthesis.
751 parenthesis.
752
752
753 $ hg book only -r 9
753 $ hg book only -r 9
754 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
754 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
755 8
755 8
756 9
756 9
757
757
758 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
758 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
759 may be hidden (issue5385)
759 may be hidden (issue5385)
760
760
761 $ try -p parsed -p analyzed ':'
761 $ try -p parsed -p analyzed ':'
762 * parsed:
762 * parsed:
763 (rangeall
763 (rangeall
764 None)
764 None)
765 * analyzed:
765 * analyzed:
766 (rangeall
766 (rangeall
767 None)
767 None)
768 * set:
768 * set:
769 <spanset+ 0:10>
769 <spanset+ 0:10>
770 0
770 0
771 1
771 1
772 2
772 2
773 3
773 3
774 4
774 4
775 5
775 5
776 6
776 6
777 7
777 7
778 8
778 8
779 9
779 9
780 $ try -p analyzed ':1'
780 $ try -p analyzed ':1'
781 * analyzed:
781 * analyzed:
782 (rangepre
782 (rangepre
783 (symbol '1'))
783 (symbol '1'))
784 * set:
784 * set:
785 <spanset+ 0:2>
785 <spanset+ 0:2>
786 0
786 0
787 1
787 1
788 $ try -p analyzed ':(1|2)'
788 $ try -p analyzed ':(1|2)'
789 * analyzed:
789 * analyzed:
790 (rangepre
790 (rangepre
791 (or
791 (or
792 (list
792 (list
793 (symbol '1')
793 (symbol '1')
794 (symbol '2'))))
794 (symbol '2'))))
795 * set:
795 * set:
796 <spanset+ 0:3>
796 <spanset+ 0:3>
797 0
797 0
798 1
798 1
799 2
799 2
800 $ try -p analyzed ':(1&2)'
800 $ try -p analyzed ':(1&2)'
801 * analyzed:
801 * analyzed:
802 (rangepre
802 (rangepre
803 (and
803 (and
804 (symbol '1')
804 (symbol '1')
805 (symbol '2')))
805 (symbol '2')))
806 * set:
806 * set:
807 <baseset []>
807 <baseset []>
808
808
809 infix/suffix resolution of ^ operator (issue2884, issue5764):
809 infix/suffix resolution of ^ operator (issue2884, issue5764):
810
810
811 x^:y means (x^):y
811 x^:y means (x^):y
812
812
813 $ try '1^:2'
813 $ try '1^:2'
814 (range
814 (range
815 (parentpost
815 (parentpost
816 (symbol '1'))
816 (symbol '1'))
817 (symbol '2'))
817 (symbol '2'))
818 * set:
818 * set:
819 <spanset+ 0:3>
819 <spanset+ 0:3>
820 0
820 0
821 1
821 1
822 2
822 2
823
823
824 $ try '1^::2'
824 $ try '1^::2'
825 (dagrange
825 (dagrange
826 (parentpost
826 (parentpost
827 (symbol '1'))
827 (symbol '1'))
828 (symbol '2'))
828 (symbol '2'))
829 * set:
829 * set:
830 <baseset+ [0, 1, 2]>
830 <baseset+ [0, 1, 2]>
831 0
831 0
832 1
832 1
833 2
833 2
834
834
835 $ try '1^..2'
835 $ try '1^..2'
836 (dagrange
836 (dagrange
837 (parentpost
837 (parentpost
838 (symbol '1'))
838 (symbol '1'))
839 (symbol '2'))
839 (symbol '2'))
840 * set:
840 * set:
841 <baseset+ [0, 1, 2]>
841 <baseset+ [0, 1, 2]>
842 0
842 0
843 1
843 1
844 2
844 2
845
845
846 $ try '9^:'
846 $ try '9^:'
847 (rangepost
847 (rangepost
848 (parentpost
848 (parentpost
849 (symbol '9')))
849 (symbol '9')))
850 * set:
850 * set:
851 <spanset+ 8:10>
851 <spanset+ 8:10>
852 8
852 8
853 9
853 9
854
854
855 $ try '9^::'
855 $ try '9^::'
856 (dagrangepost
856 (dagrangepost
857 (parentpost
857 (parentpost
858 (symbol '9')))
858 (symbol '9')))
859 * set:
859 * set:
860 <generatorsetasc+>
860 <generatorsetasc+>
861 8
861 8
862 9
862 9
863
863
864 $ try '9^..'
864 $ try '9^..'
865 (dagrangepost
865 (dagrangepost
866 (parentpost
866 (parentpost
867 (symbol '9')))
867 (symbol '9')))
868 * set:
868 * set:
869 <generatorsetasc+>
869 <generatorsetasc+>
870 8
870 8
871 9
871 9
872
872
873 x^:y should be resolved before omitting group operators
873 x^:y should be resolved before omitting group operators
874
874
875 $ try '1^(:2)'
875 $ try '1^(:2)'
876 (parent
876 (parent
877 (symbol '1')
877 (symbol '1')
878 (group
878 (group
879 (rangepre
879 (rangepre
880 (symbol '2'))))
880 (symbol '2'))))
881 hg: parse error: ^ expects a number 0, 1, or 2
881 hg: parse error: ^ expects a number 0, 1, or 2
882 [255]
882 [255]
883
883
884 x^:y should be resolved recursively
884 x^:y should be resolved recursively
885
885
886 $ try 'sort(1^:2)'
886 $ try 'sort(1^:2)'
887 (func
887 (func
888 (symbol 'sort')
888 (symbol 'sort')
889 (range
889 (range
890 (parentpost
890 (parentpost
891 (symbol '1'))
891 (symbol '1'))
892 (symbol '2')))
892 (symbol '2')))
893 * set:
893 * set:
894 <spanset+ 0:3>
894 <spanset+ 0:3>
895 0
895 0
896 1
896 1
897 2
897 2
898
898
899 $ try '(3^:4)^:2'
899 $ try '(3^:4)^:2'
900 (range
900 (range
901 (parentpost
901 (parentpost
902 (group
902 (group
903 (range
903 (range
904 (parentpost
904 (parentpost
905 (symbol '3'))
905 (symbol '3'))
906 (symbol '4'))))
906 (symbol '4'))))
907 (symbol '2'))
907 (symbol '2'))
908 * set:
908 * set:
909 <spanset+ 0:3>
909 <spanset+ 0:3>
910 0
910 0
911 1
911 1
912 2
912 2
913
913
914 $ try '(3^::4)^::2'
914 $ try '(3^::4)^::2'
915 (dagrange
915 (dagrange
916 (parentpost
916 (parentpost
917 (group
917 (group
918 (dagrange
918 (dagrange
919 (parentpost
919 (parentpost
920 (symbol '3'))
920 (symbol '3'))
921 (symbol '4'))))
921 (symbol '4'))))
922 (symbol '2'))
922 (symbol '2'))
923 * set:
923 * set:
924 <baseset+ [0, 1, 2]>
924 <baseset+ [0, 1, 2]>
925 0
925 0
926 1
926 1
927 2
927 2
928
928
929 $ try '(9^:)^:'
929 $ try '(9^:)^:'
930 (rangepost
930 (rangepost
931 (parentpost
931 (parentpost
932 (group
932 (group
933 (rangepost
933 (rangepost
934 (parentpost
934 (parentpost
935 (symbol '9'))))))
935 (symbol '9'))))))
936 * set:
936 * set:
937 <spanset+ 4:10>
937 <spanset+ 4:10>
938 4
938 4
939 5
939 5
940 6
940 6
941 7
941 7
942 8
942 8
943 9
943 9
944
944
945 x^ in alias should also be resolved
945 x^ in alias should also be resolved
946
946
947 $ try 'A' --config 'revsetalias.A=1^:2'
947 $ try 'A' --config 'revsetalias.A=1^:2'
948 (symbol 'A')
948 (symbol 'A')
949 * expanded:
949 * expanded:
950 (range
950 (range
951 (parentpost
951 (parentpost
952 (symbol '1'))
952 (symbol '1'))
953 (symbol '2'))
953 (symbol '2'))
954 * set:
954 * set:
955 <spanset+ 0:3>
955 <spanset+ 0:3>
956 0
956 0
957 1
957 1
958 2
958 2
959
959
960 $ try 'A:2' --config 'revsetalias.A=1^'
960 $ try 'A:2' --config 'revsetalias.A=1^'
961 (range
961 (range
962 (symbol 'A')
962 (symbol 'A')
963 (symbol '2'))
963 (symbol '2'))
964 * expanded:
964 * expanded:
965 (range
965 (range
966 (parentpost
966 (parentpost
967 (symbol '1'))
967 (symbol '1'))
968 (symbol '2'))
968 (symbol '2'))
969 * set:
969 * set:
970 <spanset+ 0:3>
970 <spanset+ 0:3>
971 0
971 0
972 1
972 1
973 2
973 2
974
974
975 but not beyond the boundary of alias expansion, because the resolution should
975 but not beyond the boundary of alias expansion, because the resolution should
976 be made at the parsing stage
976 be made at the parsing stage
977
977
978 $ try '1^A' --config 'revsetalias.A=:2'
978 $ try '1^A' --config 'revsetalias.A=:2'
979 (parent
979 (parent
980 (symbol '1')
980 (symbol '1')
981 (symbol 'A'))
981 (symbol 'A'))
982 * expanded:
982 * expanded:
983 (parent
983 (parent
984 (symbol '1')
984 (symbol '1')
985 (rangepre
985 (rangepre
986 (symbol '2')))
986 (symbol '2')))
987 hg: parse error: ^ expects a number 0, 1, or 2
987 hg: parse error: ^ expects a number 0, 1, or 2
988 [255]
988 [255]
989
989
990 '::' itself isn't a valid expression
990 '::' itself isn't a valid expression
991
991
992 $ try '::'
992 $ try '::'
993 (dagrangeall
993 (dagrangeall
994 None)
994 None)
995 hg: parse error: can't use '::' in this context
995 hg: parse error: can't use '::' in this context
996 [255]
996 [255]
997
997
998 ancestor can accept 0 or more arguments
998 ancestor can accept 0 or more arguments
999
999
1000 $ log 'ancestor()'
1000 $ log 'ancestor()'
1001 $ log 'ancestor(1)'
1001 $ log 'ancestor(1)'
1002 1
1002 1
1003 $ log 'ancestor(4,5)'
1003 $ log 'ancestor(4,5)'
1004 1
1004 1
1005 $ log 'ancestor(4,5) and 4'
1005 $ log 'ancestor(4,5) and 4'
1006 $ log 'ancestor(0,0,1,3)'
1006 $ log 'ancestor(0,0,1,3)'
1007 0
1007 0
1008 $ log 'ancestor(3,1,5,3,5,1)'
1008 $ log 'ancestor(3,1,5,3,5,1)'
1009 1
1009 1
1010 $ log 'ancestor(0,1,3,5)'
1010 $ log 'ancestor(0,1,3,5)'
1011 0
1011 0
1012 $ log 'ancestor(1,2,3,4,5)'
1012 $ log 'ancestor(1,2,3,4,5)'
1013 1
1013 1
1014
1014
1015 test ancestors
1015 test ancestors
1016
1016
1017 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1017 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1018 @ 9
1018 @ 9
1019 o 8
1019 o 8
1020 | o 7
1020 | o 7
1021 | o 6
1021 | o 6
1022 |/|
1022 |/|
1023 | o 5
1023 | o 5
1024 o | 4
1024 o | 4
1025 | o 3
1025 | o 3
1026 o | 2
1026 o | 2
1027 |/
1027 |/
1028 o 1
1028 o 1
1029 o 0
1029 o 0
1030
1030
1031 $ log 'ancestors(5)'
1031 $ log 'ancestors(5)'
1032 0
1032 0
1033 1
1033 1
1034 3
1034 3
1035 5
1035 5
1036 $ log 'ancestor(ancestors(5))'
1036 $ log 'ancestor(ancestors(5))'
1037 0
1037 0
1038 $ log '::r3232()'
1038 $ log '::r3232()'
1039 0
1039 0
1040 1
1040 1
1041 2
1041 2
1042 3
1042 3
1043
1043
1044 test ancestors with depth limit
1044 test ancestors with depth limit
1045
1045
1046 (depth=0 selects the node itself)
1046 (depth=0 selects the node itself)
1047
1047
1048 $ log 'reverse(ancestors(9, depth=0))'
1048 $ log 'reverse(ancestors(9, depth=0))'
1049 9
1049 9
1050
1050
1051 (interleaved: '4' would be missing if heap queue were higher depth first)
1051 (interleaved: '4' would be missing if heap queue were higher depth first)
1052
1052
1053 $ log 'reverse(ancestors(8:9, depth=1))'
1053 $ log 'reverse(ancestors(8:9, depth=1))'
1054 9
1054 9
1055 8
1055 8
1056 4
1056 4
1057
1057
1058 (interleaved: '2' would be missing if heap queue were higher depth first)
1058 (interleaved: '2' would be missing if heap queue were higher depth first)
1059
1059
1060 $ log 'reverse(ancestors(7+8, depth=2))'
1060 $ log 'reverse(ancestors(7+8, depth=2))'
1061 8
1061 8
1062 7
1062 7
1063 6
1063 6
1064 5
1064 5
1065 4
1065 4
1066 2
1066 2
1067
1067
1068 (walk example above by separate queries)
1068 (walk example above by separate queries)
1069
1069
1070 $ log 'reverse(ancestors(8, depth=2)) + reverse(ancestors(7, depth=2))'
1070 $ log 'reverse(ancestors(8, depth=2)) + reverse(ancestors(7, depth=2))'
1071 8
1071 8
1072 4
1072 4
1073 2
1073 2
1074 7
1074 7
1075 6
1075 6
1076 5
1076 5
1077
1077
1078 (walk 2nd and 3rd ancestors)
1078 (walk 2nd and 3rd ancestors)
1079
1079
1080 $ log 'reverse(ancestors(7, depth=3, startdepth=2))'
1080 $ log 'reverse(ancestors(7, depth=3, startdepth=2))'
1081 5
1081 5
1082 4
1082 4
1083 3
1083 3
1084 2
1084 2
1085
1085
1086 (interleaved: '4' would be missing if higher-depth ancestors weren't scanned)
1086 (interleaved: '4' would be missing if higher-depth ancestors weren't scanned)
1087
1087
1088 $ log 'reverse(ancestors(7+8, depth=2, startdepth=2))'
1088 $ log 'reverse(ancestors(7+8, depth=2, startdepth=2))'
1089 5
1089 5
1090 4
1090 4
1091 2
1091 2
1092
1092
1093 (note that 'ancestors(x, depth=y, startdepth=z)' does not identical to
1093 (note that 'ancestors(x, depth=y, startdepth=z)' does not identical to
1094 'ancestors(x, depth=y) - ancestors(x, depth=z-1)' because a node may have
1094 'ancestors(x, depth=y) - ancestors(x, depth=z-1)' because a node may have
1095 multiple depths)
1095 multiple depths)
1096
1096
1097 $ log 'reverse(ancestors(7+8, depth=2) - ancestors(7+8, depth=1))'
1097 $ log 'reverse(ancestors(7+8, depth=2) - ancestors(7+8, depth=1))'
1098 5
1098 5
1099 2
1099 2
1100
1100
1101 test bad arguments passed to ancestors()
1101 test bad arguments passed to ancestors()
1102
1102
1103 $ log 'ancestors(., depth=-1)'
1103 $ log 'ancestors(., depth=-1)'
1104 hg: parse error: negative depth
1104 hg: parse error: negative depth
1105 [255]
1105 [255]
1106 $ log 'ancestors(., depth=foo)'
1106 $ log 'ancestors(., depth=foo)'
1107 hg: parse error: ancestors expects an integer depth
1107 hg: parse error: ancestors expects an integer depth
1108 [255]
1108 [255]
1109
1109
1110 test descendants
1110 test descendants
1111
1111
1112 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1112 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1113 @ 9
1113 @ 9
1114 o 8
1114 o 8
1115 | o 7
1115 | o 7
1116 | o 6
1116 | o 6
1117 |/|
1117 |/|
1118 | o 5
1118 | o 5
1119 o | 4
1119 o | 4
1120 | o 3
1120 | o 3
1121 o | 2
1121 o | 2
1122 |/
1122 |/
1123 o 1
1123 o 1
1124 o 0
1124 o 0
1125
1125
1126 (null is ultimate root and has optimized path)
1126 (null is ultimate root and has optimized path)
1127
1127
1128 $ log 'null:4 & descendants(null)'
1128 $ log 'null:4 & descendants(null)'
1129 -1
1129 -1
1130 0
1130 0
1131 1
1131 1
1132 2
1132 2
1133 3
1133 3
1134 4
1134 4
1135
1135
1136 (including merge)
1136 (including merge)
1137
1137
1138 $ log ':8 & descendants(2)'
1138 $ log ':8 & descendants(2)'
1139 2
1139 2
1140 4
1140 4
1141 6
1141 6
1142 7
1142 7
1143 8
1143 8
1144
1144
1145 (multiple roots)
1145 (multiple roots)
1146
1146
1147 $ log ':8 & descendants(2+5)'
1147 $ log ':8 & descendants(2+5)'
1148 2
1148 2
1149 4
1149 4
1150 5
1150 5
1151 6
1151 6
1152 7
1152 7
1153 8
1153 8
1154
1154
1155 test descendants with depth limit
1155 test descendants with depth limit
1156
1156
1157 (depth=0 selects the node itself)
1157 (depth=0 selects the node itself)
1158
1158
1159 $ log 'descendants(0, depth=0)'
1159 $ log 'descendants(0, depth=0)'
1160 0
1160 0
1161 $ log 'null: & descendants(null, depth=0)'
1161 $ log 'null: & descendants(null, depth=0)'
1162 -1
1162 -1
1163
1163
1164 (p2 = null should be ignored)
1164 (p2 = null should be ignored)
1165
1165
1166 $ log 'null: & descendants(null, depth=2)'
1166 $ log 'null: & descendants(null, depth=2)'
1167 -1
1167 -1
1168 0
1168 0
1169 1
1169 1
1170
1170
1171 (multiple paths: depth(6) = (2, 3))
1171 (multiple paths: depth(6) = (2, 3))
1172
1172
1173 $ log 'descendants(1+3, depth=2)'
1173 $ log 'descendants(1+3, depth=2)'
1174 1
1174 1
1175 2
1175 2
1176 3
1176 3
1177 4
1177 4
1178 5
1178 5
1179 6
1179 6
1180
1180
1181 (multiple paths: depth(5) = (1, 2), depth(6) = (2, 3))
1181 (multiple paths: depth(5) = (1, 2), depth(6) = (2, 3))
1182
1182
1183 $ log 'descendants(3+1, depth=2, startdepth=2)'
1183 $ log 'descendants(3+1, depth=2, startdepth=2)'
1184 4
1184 4
1185 5
1185 5
1186 6
1186 6
1187
1187
1188 (multiple depths: depth(6) = (0, 2, 4), search for depth=2)
1188 (multiple depths: depth(6) = (0, 2, 4), search for depth=2)
1189
1189
1190 $ log 'descendants(0+3+6, depth=3, startdepth=1)'
1190 $ log 'descendants(0+3+6, depth=3, startdepth=1)'
1191 1
1191 1
1192 2
1192 2
1193 3
1193 3
1194 4
1194 4
1195 5
1195 5
1196 6
1196 6
1197 7
1197 7
1198
1198
1199 (multiple depths: depth(6) = (0, 4), no match)
1199 (multiple depths: depth(6) = (0, 4), no match)
1200
1200
1201 $ log 'descendants(0+6, depth=3, startdepth=1)'
1201 $ log 'descendants(0+6, depth=3, startdepth=1)'
1202 1
1202 1
1203 2
1203 2
1204 3
1204 3
1205 4
1205 4
1206 5
1206 5
1207 7
1207 7
1208
1208
1209 test ancestors/descendants relation subscript:
1209 test ancestors/descendants relation subscript:
1210
1210
1211 $ log 'tip#generations[0]'
1211 $ log 'tip#generations[0]'
1212 9
1212 9
1213 $ log '.#generations[-1]'
1213 $ log '.#generations[-1]'
1214 8
1214 8
1215 $ log '.#g[(-1)]'
1215 $ log '.#g[(-1)]'
1216 8
1216 8
1217
1217
1218 $ hg debugrevspec -p parsed 'roots(:)#g[2]'
1218 $ hg debugrevspec -p parsed 'roots(:)#g[2]'
1219 * parsed:
1219 * parsed:
1220 (relsubscript
1220 (relsubscript
1221 (func
1221 (func
1222 (symbol 'roots')
1222 (symbol 'roots')
1223 (rangeall
1223 (rangeall
1224 None))
1224 None))
1225 (symbol 'g')
1225 (symbol 'g')
1226 (symbol '2'))
1226 (symbol '2'))
1227 2
1227 2
1228 3
1228 3
1229
1229
1230 test author
1230 test author
1231
1231
1232 $ log 'author(bob)'
1232 $ log 'author(bob)'
1233 2
1233 2
1234 $ log 'author("re:bob|test")'
1234 $ log 'author("re:bob|test")'
1235 0
1235 0
1236 1
1236 1
1237 2
1237 2
1238 3
1238 3
1239 4
1239 4
1240 5
1240 5
1241 6
1241 6
1242 7
1242 7
1243 8
1243 8
1244 9
1244 9
1245 $ log 'author(r"re:\S")'
1245 $ log 'author(r"re:\S")'
1246 0
1246 0
1247 1
1247 1
1248 2
1248 2
1249 3
1249 3
1250 4
1250 4
1251 5
1251 5
1252 6
1252 6
1253 7
1253 7
1254 8
1254 8
1255 9
1255 9
1256 $ log 'branch(Γ©)'
1256 $ log 'branch(Γ©)'
1257 8
1257 8
1258 9
1258 9
1259 $ log 'branch(a)'
1259 $ log 'branch(a)'
1260 0
1260 0
1261 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
1261 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
1262 0 a
1262 0 a
1263 2 a-b-c-
1263 2 a-b-c-
1264 3 +a+b+c+
1264 3 +a+b+c+
1265 4 -a-b-c-
1265 4 -a-b-c-
1266 5 !a/b/c/
1266 5 !a/b/c/
1267 6 _a_b_c_
1267 6 _a_b_c_
1268 7 .a.b.c.
1268 7 .a.b.c.
1269 $ log 'children(ancestor(4,5))'
1269 $ log 'children(ancestor(4,5))'
1270 2
1270 2
1271 3
1271 3
1272
1272
1273 $ log 'children(4)'
1273 $ log 'children(4)'
1274 6
1274 6
1275 8
1275 8
1276 $ log 'children(null)'
1276 $ log 'children(null)'
1277 0
1277 0
1278
1278
1279 $ log 'closed()'
1279 $ log 'closed()'
1280 $ log 'contains(a)'
1280 $ log 'contains(a)'
1281 0
1281 0
1282 1
1282 1
1283 3
1283 3
1284 5
1284 5
1285 $ log 'contains("../repo/a")'
1285 $ log 'contains("../repo/a")'
1286 0
1286 0
1287 1
1287 1
1288 3
1288 3
1289 5
1289 5
1290 $ log 'desc(B)'
1290 $ log 'desc(B)'
1291 5
1291 5
1292 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
1292 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
1293 5 5 bug
1293 5 5 bug
1294 6 6 issue619
1294 6 6 issue619
1295 $ log 'descendants(2 or 3)'
1295 $ log 'descendants(2 or 3)'
1296 2
1296 2
1297 3
1297 3
1298 4
1298 4
1299 5
1299 5
1300 6
1300 6
1301 7
1301 7
1302 8
1302 8
1303 9
1303 9
1304 $ log 'file("b*")'
1304 $ log 'file("b*")'
1305 1
1305 1
1306 4
1306 4
1307 $ log 'filelog("b")'
1307 $ log 'filelog("b")'
1308 1
1308 1
1309 4
1309 4
1310 $ log 'filelog("../repo/b")'
1310 $ log 'filelog("../repo/b")'
1311 1
1311 1
1312 4
1312 4
1313 $ log 'follow()'
1313 $ log 'follow()'
1314 0
1314 0
1315 1
1315 1
1316 2
1316 2
1317 4
1317 4
1318 8
1318 8
1319 9
1319 9
1320 $ log 'grep("issue\d+")'
1320 $ log 'grep("issue\d+")'
1321 6
1321 6
1322 $ try 'grep("(")' # invalid regular expression
1322 $ try 'grep("(")' # invalid regular expression
1323 (func
1323 (func
1324 (symbol 'grep')
1324 (symbol 'grep')
1325 (string '('))
1325 (string '('))
1326 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \),.*) (re)
1326 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \),.*) (re)
1327 [255]
1327 [255]
1328 $ try 'grep("\bissue\d+")'
1328 $ try 'grep("\bissue\d+")'
1329 (func
1329 (func
1330 (symbol 'grep')
1330 (symbol 'grep')
1331 (string '\x08issue\\d+'))
1331 (string '\x08issue\\d+'))
1332 * set:
1332 * set:
1333 <filteredset
1333 <filteredset
1334 <fullreposet+ 0:10>,
1334 <fullreposet+ 0:10>,
1335 <grep '\x08issue\\d+'>>
1335 <grep '\x08issue\\d+'>>
1336 $ try 'grep(r"\bissue\d+")'
1336 $ try 'grep(r"\bissue\d+")'
1337 (func
1337 (func
1338 (symbol 'grep')
1338 (symbol 'grep')
1339 (string '\\bissue\\d+'))
1339 (string '\\bissue\\d+'))
1340 * set:
1340 * set:
1341 <filteredset
1341 <filteredset
1342 <fullreposet+ 0:10>,
1342 <fullreposet+ 0:10>,
1343 <grep '\\bissue\\d+'>>
1343 <grep '\\bissue\\d+'>>
1344 6
1344 6
1345 $ try 'grep(r"\")'
1345 $ try 'grep(r"\")'
1346 hg: parse error at 7: unterminated string
1346 hg: parse error at 7: unterminated string
1347 (grep(r"\")
1347 (grep(r"\")
1348 ^ here)
1348 ^ here)
1349 [255]
1349 [255]
1350 $ log 'head()'
1350 $ log 'head()'
1351 0
1351 0
1352 1
1352 1
1353 2
1353 2
1354 3
1354 3
1355 4
1355 4
1356 5
1356 5
1357 6
1357 6
1358 7
1358 7
1359 9
1359 9
1360
1360
1361 Test heads
1361 Test heads
1362
1362
1363 $ log 'heads(6::)'
1363 $ log 'heads(6::)'
1364 7
1364 7
1365
1365
1366 heads() can be computed in subset '9:'
1366 heads() can be computed in subset '9:'
1367
1367
1368 $ hg debugrevspec -s '9: & heads(all())'
1368 $ hg debugrevspec -s '9: & heads(all())'
1369 * set:
1369 * set:
1370 <filteredset
1370 <filteredset
1371 <filteredset
1371 <filteredset
1372 <baseset [9]>,
1372 <baseset [9]>,
1373 <spanset+ 0:10>>,
1373 <spanset+ 0:10>>,
1374 <not
1374 <not
1375 <filteredset
1375 <filteredset
1376 <baseset [9]>, set([0, 1, 2, 3, 4, 5, 6, 8])>>>
1376 <baseset [9]>, set([0, 1, 2, 3, 4, 5, 6, 8])>>>
1377 9
1377 9
1378
1378
1379 but should follow the order of the subset
1379 but should follow the order of the subset
1380
1380
1381 $ log 'heads(all())'
1381 $ log 'heads(all())'
1382 7
1382 7
1383 9
1383 9
1384 $ log 'heads(tip:0)'
1384 $ log 'heads(tip:0)'
1385 7
1385 7
1386 9
1386 9
1387 $ log 'tip:0 & heads(all())'
1387 $ log 'tip:0 & heads(all())'
1388 9
1388 9
1389 7
1389 7
1390 $ log 'tip:0 & heads(0:tip)'
1390 $ log 'tip:0 & heads(0:tip)'
1391 9
1391 9
1392 7
1392 7
1393
1393
1394 $ log 'keyword(issue)'
1394 $ log 'keyword(issue)'
1395 6
1395 6
1396 $ log 'keyword("test a")'
1396 $ log 'keyword("test a")'
1397
1397
1398 Test first (=limit) and last
1398 Test first (=limit) and last
1399
1399
1400 $ log 'limit(head(), 1)'
1400 $ log 'limit(head(), 1)'
1401 0
1401 0
1402 $ log 'limit(author("re:bob|test"), 3, 5)'
1402 $ log 'limit(author("re:bob|test"), 3, 5)'
1403 5
1403 5
1404 6
1404 6
1405 7
1405 7
1406 $ log 'limit(author("re:bob|test"), offset=6)'
1406 $ log 'limit(author("re:bob|test"), offset=6)'
1407 6
1407 6
1408 $ log 'limit(author("re:bob|test"), offset=10)'
1408 $ log 'limit(author("re:bob|test"), offset=10)'
1409 $ log 'limit(all(), 1, -1)'
1409 $ log 'limit(all(), 1, -1)'
1410 hg: parse error: negative offset
1410 hg: parse error: negative offset
1411 [255]
1411 [255]
1412 $ log 'limit(all(), -1)'
1412 $ log 'limit(all(), -1)'
1413 hg: parse error: negative number to select
1413 hg: parse error: negative number to select
1414 [255]
1414 [255]
1415 $ log 'limit(all(), 0)'
1415 $ log 'limit(all(), 0)'
1416
1416
1417 $ log 'last(all(), -1)'
1417 $ log 'last(all(), -1)'
1418 hg: parse error: negative number to select
1418 hg: parse error: negative number to select
1419 [255]
1419 [255]
1420 $ log 'last(all(), 0)'
1420 $ log 'last(all(), 0)'
1421 $ log 'last(all(), 1)'
1421 $ log 'last(all(), 1)'
1422 9
1422 9
1423 $ log 'last(all(), 2)'
1423 $ log 'last(all(), 2)'
1424 8
1424 8
1425 9
1425 9
1426
1426
1427 Test smartset.slice() by first/last()
1427 Test smartset.slice() by first/last()
1428
1428
1429 (using unoptimized set, filteredset as example)
1429 (using unoptimized set, filteredset as example)
1430
1430
1431 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1431 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1432 * set:
1432 * set:
1433 <filteredset
1433 <filteredset
1434 <spanset+ 0:8>,
1434 <spanset+ 0:8>,
1435 <branch 're:'>>
1435 <branch 're:'>>
1436 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1436 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1437 4
1437 4
1438 5
1438 5
1439 6
1439 6
1440 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1440 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1441 3
1441 3
1442 2
1442 2
1443 1
1443 1
1444 $ log 'last(0:7 & branch("re:"), 2)'
1444 $ log 'last(0:7 & branch("re:"), 2)'
1445 6
1445 6
1446 7
1446 7
1447
1447
1448 (using baseset)
1448 (using baseset)
1449
1449
1450 $ hg debugrevspec --no-show-revs -s 0+1+2+3+4+5+6+7
1450 $ hg debugrevspec --no-show-revs -s 0+1+2+3+4+5+6+7
1451 * set:
1451 * set:
1452 <baseset [0, 1, 2, 3, 4, 5, 6, 7]>
1452 <baseset [0, 1, 2, 3, 4, 5, 6, 7]>
1453 $ hg debugrevspec --no-show-revs -s 0::7
1453 $ hg debugrevspec --no-show-revs -s 0::7
1454 * set:
1454 * set:
1455 <baseset+ [0, 1, 2, 3, 4, 5, 6, 7]>
1455 <baseset+ [0, 1, 2, 3, 4, 5, 6, 7]>
1456 $ log 'limit(0+1+2+3+4+5+6+7, 3, 4)'
1456 $ log 'limit(0+1+2+3+4+5+6+7, 3, 4)'
1457 4
1457 4
1458 5
1458 5
1459 6
1459 6
1460 $ log 'limit(sort(0::7, rev), 3, 4)'
1460 $ log 'limit(sort(0::7, rev), 3, 4)'
1461 4
1461 4
1462 5
1462 5
1463 6
1463 6
1464 $ log 'limit(sort(0::7, -rev), 3, 4)'
1464 $ log 'limit(sort(0::7, -rev), 3, 4)'
1465 3
1465 3
1466 2
1466 2
1467 1
1467 1
1468 $ log 'last(sort(0::7, rev), 2)'
1468 $ log 'last(sort(0::7, rev), 2)'
1469 6
1469 6
1470 7
1470 7
1471 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 6)'
1471 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 6)'
1472 * set:
1472 * set:
1473 <baseset+ [6, 7]>
1473 <baseset+ [6, 7]>
1474 6
1474 6
1475 7
1475 7
1476 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 9)'
1476 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 9)'
1477 * set:
1477 * set:
1478 <baseset+ []>
1478 <baseset+ []>
1479 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 6)'
1479 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 6)'
1480 * set:
1480 * set:
1481 <baseset- [0, 1]>
1481 <baseset- [0, 1]>
1482 1
1482 1
1483 0
1483 0
1484 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 9)'
1484 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 9)'
1485 * set:
1485 * set:
1486 <baseset- []>
1486 <baseset- []>
1487 $ hg debugrevspec -s 'limit(0::7, 0)'
1487 $ hg debugrevspec -s 'limit(0::7, 0)'
1488 * set:
1488 * set:
1489 <baseset+ []>
1489 <baseset+ []>
1490
1490
1491 (using spanset)
1491 (using spanset)
1492
1492
1493 $ hg debugrevspec --no-show-revs -s 0:7
1493 $ hg debugrevspec --no-show-revs -s 0:7
1494 * set:
1494 * set:
1495 <spanset+ 0:8>
1495 <spanset+ 0:8>
1496 $ log 'limit(0:7, 3, 4)'
1496 $ log 'limit(0:7, 3, 4)'
1497 4
1497 4
1498 5
1498 5
1499 6
1499 6
1500 $ log 'limit(7:0, 3, 4)'
1500 $ log 'limit(7:0, 3, 4)'
1501 3
1501 3
1502 2
1502 2
1503 1
1503 1
1504 $ log 'limit(0:7, 3, 6)'
1504 $ log 'limit(0:7, 3, 6)'
1505 6
1505 6
1506 7
1506 7
1507 $ log 'limit(7:0, 3, 6)'
1507 $ log 'limit(7:0, 3, 6)'
1508 1
1508 1
1509 0
1509 0
1510 $ log 'last(0:7, 2)'
1510 $ log 'last(0:7, 2)'
1511 6
1511 6
1512 7
1512 7
1513 $ hg debugrevspec -s 'limit(0:7, 3, 6)'
1513 $ hg debugrevspec -s 'limit(0:7, 3, 6)'
1514 * set:
1514 * set:
1515 <spanset+ 6:8>
1515 <spanset+ 6:8>
1516 6
1516 6
1517 7
1517 7
1518 $ hg debugrevspec -s 'limit(0:7, 3, 9)'
1518 $ hg debugrevspec -s 'limit(0:7, 3, 9)'
1519 * set:
1519 * set:
1520 <spanset+ 8:8>
1520 <spanset+ 8:8>
1521 $ hg debugrevspec -s 'limit(7:0, 3, 6)'
1521 $ hg debugrevspec -s 'limit(7:0, 3, 6)'
1522 * set:
1522 * set:
1523 <spanset- 0:2>
1523 <spanset- 0:2>
1524 1
1524 1
1525 0
1525 0
1526 $ hg debugrevspec -s 'limit(7:0, 3, 9)'
1526 $ hg debugrevspec -s 'limit(7:0, 3, 9)'
1527 * set:
1527 * set:
1528 <spanset- 0:0>
1528 <spanset- 0:0>
1529 $ hg debugrevspec -s 'limit(0:7, 0)'
1529 $ hg debugrevspec -s 'limit(0:7, 0)'
1530 * set:
1530 * set:
1531 <spanset+ 0:0>
1531 <spanset+ 0:0>
1532
1532
1533 Test order of first/last revisions
1533 Test order of first/last revisions
1534
1534
1535 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1535 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1536 * set:
1536 * set:
1537 <filteredset
1537 <filteredset
1538 <spanset- 2:5>,
1538 <spanset- 2:5>,
1539 <spanset+ 3:10>>
1539 <spanset+ 3:10>>
1540 4
1540 4
1541 3
1541 3
1542
1542
1543 $ hg debugrevspec -s '3: & first(4:0, 3)'
1543 $ hg debugrevspec -s '3: & first(4:0, 3)'
1544 * set:
1544 * set:
1545 <filteredset
1545 <filteredset
1546 <spanset+ 3:10>,
1546 <spanset+ 3:10>,
1547 <spanset- 2:5>>
1547 <spanset- 2:5>>
1548 3
1548 3
1549 4
1549 4
1550
1550
1551 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1551 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1552 * set:
1552 * set:
1553 <filteredset
1553 <filteredset
1554 <spanset- 0:3>,
1554 <spanset- 0:3>,
1555 <spanset+ 0:2>>
1555 <spanset+ 0:2>>
1556 1
1556 1
1557 0
1557 0
1558
1558
1559 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1559 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1560 * set:
1560 * set:
1561 <filteredset
1561 <filteredset
1562 <spanset+ 0:2>,
1562 <spanset+ 0:2>,
1563 <spanset+ 0:3>>
1563 <spanset+ 0:3>>
1564 0
1564 0
1565 1
1565 1
1566
1566
1567 Test scmutil.revsingle() should return the last revision
1567 Test scmutil.revsingle() should return the last revision
1568
1568
1569 $ hg debugrevspec -s 'last(0::)'
1569 $ hg debugrevspec -s 'last(0::)'
1570 * set:
1570 * set:
1571 <baseset slice=0:1
1571 <baseset slice=0:1
1572 <generatorsetasc->>
1572 <generatorsetasc->>
1573 9
1573 9
1574 $ hg identify -r '0::' --num
1574 $ hg identify -r '0::' --num
1575 9
1575 9
1576
1576
1577 Test matching
1577 Test matching
1578
1578
1579 $ log 'matching(6)'
1579 $ log 'matching(6)'
1580 6
1580 6
1581 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1581 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1582 6
1582 6
1583 7
1583 7
1584
1584
1585 Testing min and max
1585 Testing min and max
1586
1586
1587 max: simple
1587 max: simple
1588
1588
1589 $ log 'max(contains(a))'
1589 $ log 'max(contains(a))'
1590 5
1590 5
1591
1591
1592 max: simple on unordered set)
1592 max: simple on unordered set)
1593
1593
1594 $ log 'max((4+0+2+5+7) and contains(a))'
1594 $ log 'max((4+0+2+5+7) and contains(a))'
1595 5
1595 5
1596
1596
1597 max: no result
1597 max: no result
1598
1598
1599 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1599 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1600
1600
1601 max: no result on unordered set
1601 max: no result on unordered set
1602
1602
1603 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1603 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1604
1604
1605 min: simple
1605 min: simple
1606
1606
1607 $ log 'min(contains(a))'
1607 $ log 'min(contains(a))'
1608 0
1608 0
1609
1609
1610 min: simple on unordered set
1610 min: simple on unordered set
1611
1611
1612 $ log 'min((4+0+2+5+7) and contains(a))'
1612 $ log 'min((4+0+2+5+7) and contains(a))'
1613 0
1613 0
1614
1614
1615 min: empty
1615 min: empty
1616
1616
1617 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1617 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1618
1618
1619 min: empty on unordered set
1619 min: empty on unordered set
1620
1620
1621 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1621 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1622
1622
1623
1623
1624 $ log 'merge()'
1624 $ log 'merge()'
1625 6
1625 6
1626 $ log 'branchpoint()'
1626 $ log 'branchpoint()'
1627 1
1627 1
1628 4
1628 4
1629 $ log 'modifies(b)'
1629 $ log 'modifies(b)'
1630 4
1630 4
1631 $ log 'modifies("path:b")'
1631 $ log 'modifies("path:b")'
1632 4
1632 4
1633 $ log 'modifies("*")'
1633 $ log 'modifies("*")'
1634 4
1634 4
1635 6
1635 6
1636 $ log 'modifies("set:modified()")'
1636 $ log 'modifies("set:modified()")'
1637 4
1637 4
1638 $ log 'id(5)'
1638 $ log 'id(5)'
1639 2
1639 2
1640 $ log 'only(9)'
1640 $ log 'only(9)'
1641 8
1641 8
1642 9
1642 9
1643 $ log 'only(8)'
1643 $ log 'only(8)'
1644 8
1644 8
1645 $ log 'only(9, 5)'
1645 $ log 'only(9, 5)'
1646 2
1646 2
1647 4
1647 4
1648 8
1648 8
1649 9
1649 9
1650 $ log 'only(7 + 9, 5 + 2)'
1650 $ log 'only(7 + 9, 5 + 2)'
1651 4
1651 4
1652 6
1652 6
1653 7
1653 7
1654 8
1654 8
1655 9
1655 9
1656
1656
1657 Test empty set input
1657 Test empty set input
1658 $ log 'only(p2())'
1658 $ log 'only(p2())'
1659 $ log 'only(p1(), p2())'
1659 $ log 'only(p1(), p2())'
1660 0
1660 0
1661 1
1661 1
1662 2
1662 2
1663 4
1663 4
1664 8
1664 8
1665 9
1665 9
1666
1666
1667 Test '%' operator
1667 Test '%' operator
1668
1668
1669 $ log '9%'
1669 $ log '9%'
1670 8
1670 8
1671 9
1671 9
1672 $ log '9%5'
1672 $ log '9%5'
1673 2
1673 2
1674 4
1674 4
1675 8
1675 8
1676 9
1676 9
1677 $ log '(7 + 9)%(5 + 2)'
1677 $ log '(7 + 9)%(5 + 2)'
1678 4
1678 4
1679 6
1679 6
1680 7
1680 7
1681 8
1681 8
1682 9
1682 9
1683
1683
1684 Test operand of '%' is optimized recursively (issue4670)
1684 Test operand of '%' is optimized recursively (issue4670)
1685
1685
1686 $ try --optimize '8:9-8%'
1686 $ try --optimize '8:9-8%'
1687 (onlypost
1687 (onlypost
1688 (minus
1688 (minus
1689 (range
1689 (range
1690 (symbol '8')
1690 (symbol '8')
1691 (symbol '9'))
1691 (symbol '9'))
1692 (symbol '8')))
1692 (symbol '8')))
1693 * optimized:
1693 * optimized:
1694 (func
1694 (func
1695 (symbol 'only')
1695 (symbol 'only')
1696 (difference
1696 (difference
1697 (range
1697 (range
1698 (symbol '8')
1698 (symbol '8')
1699 (symbol '9'))
1699 (symbol '9'))
1700 (symbol '8')))
1700 (symbol '8')))
1701 * set:
1701 * set:
1702 <baseset+ [8, 9]>
1702 <baseset+ [8, 9]>
1703 8
1703 8
1704 9
1704 9
1705 $ try --optimize '(9)%(5)'
1705 $ try --optimize '(9)%(5)'
1706 (only
1706 (only
1707 (group
1707 (group
1708 (symbol '9'))
1708 (symbol '9'))
1709 (group
1709 (group
1710 (symbol '5')))
1710 (symbol '5')))
1711 * optimized:
1711 * optimized:
1712 (func
1712 (func
1713 (symbol 'only')
1713 (symbol 'only')
1714 (list
1714 (list
1715 (symbol '9')
1715 (symbol '9')
1716 (symbol '5')))
1716 (symbol '5')))
1717 * set:
1717 * set:
1718 <baseset+ [2, 4, 8, 9]>
1718 <baseset+ [2, 4, 8, 9]>
1719 2
1719 2
1720 4
1720 4
1721 8
1721 8
1722 9
1722 9
1723
1723
1724 Test the order of operations
1724 Test the order of operations
1725
1725
1726 $ log '7 + 9%5 + 2'
1726 $ log '7 + 9%5 + 2'
1727 7
1727 7
1728 2
1728 2
1729 4
1729 4
1730 8
1730 8
1731 9
1731 9
1732
1732
1733 Test explicit numeric revision
1733 Test explicit numeric revision
1734 $ log 'rev(-2)'
1734 $ log 'rev(-2)'
1735 $ log 'rev(-1)'
1735 $ log 'rev(-1)'
1736 -1
1736 -1
1737 $ log 'rev(0)'
1737 $ log 'rev(0)'
1738 0
1738 0
1739 $ log 'rev(9)'
1739 $ log 'rev(9)'
1740 9
1740 9
1741 $ log 'rev(10)'
1741 $ log 'rev(10)'
1742 $ log 'rev(tip)'
1742 $ log 'rev(tip)'
1743 hg: parse error: rev expects a number
1743 hg: parse error: rev expects a number
1744 [255]
1744 [255]
1745
1745
1746 Test hexadecimal revision
1746 Test hexadecimal revision
1747 $ log 'id(2)'
1747 $ log 'id(2)'
1748 $ log 'id(23268)'
1748 $ log 'id(23268)'
1749 4
1749 4
1750 $ log 'id(2785f51eece)'
1750 $ log 'id(2785f51eece)'
1751 0
1751 0
1752 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1752 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1753 8
1753 8
1754 $ log 'id(d5d0dcbdc4a)'
1754 $ log 'id(d5d0dcbdc4a)'
1755 $ log 'id(d5d0dcbdc4w)'
1755 $ log 'id(d5d0dcbdc4w)'
1756 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1756 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1757 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1757 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1758 $ log 'id(1.0)'
1758 $ log 'id(1.0)'
1759 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1759 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1760
1760
1761 Test null revision
1761 Test null revision
1762 $ log '(null)'
1762 $ log '(null)'
1763 -1
1763 -1
1764 $ log '(null:0)'
1764 $ log '(null:0)'
1765 -1
1765 -1
1766 0
1766 0
1767 $ log '(0:null)'
1767 $ log '(0:null)'
1768 0
1768 0
1769 -1
1769 -1
1770 $ log 'null::0'
1770 $ log 'null::0'
1771 -1
1771 -1
1772 0
1772 0
1773 $ log 'null:tip - 0:'
1773 $ log 'null:tip - 0:'
1774 -1
1774 -1
1775 $ log 'null: and null::' | head -1
1775 $ log 'null: and null::' | head -1
1776 -1
1776 -1
1777 $ log 'null: or 0:' | head -2
1777 $ log 'null: or 0:' | head -2
1778 -1
1778 -1
1779 0
1779 0
1780 $ log 'ancestors(null)'
1780 $ log 'ancestors(null)'
1781 -1
1781 -1
1782 $ log 'reverse(null:)' | tail -2
1782 $ log 'reverse(null:)' | tail -2
1783 0
1783 0
1784 -1
1784 -1
1785 $ log 'first(null:)'
1785 $ log 'first(null:)'
1786 -1
1786 -1
1787 $ log 'min(null:)'
1787 $ log 'min(null:)'
1788 BROKEN: should be '-1'
1788 BROKEN: should be '-1'
1789 $ log 'tip:null and all()' | tail -2
1789 $ log 'tip:null and all()' | tail -2
1790 1
1790 1
1791 0
1791 0
1792
1792
1793 Test working-directory revision
1793 Test working-directory revision
1794 $ hg debugrevspec 'wdir()'
1794 $ hg debugrevspec 'wdir()'
1795 2147483647
1795 2147483647
1796 $ hg debugrevspec 'wdir()^'
1796 $ hg debugrevspec 'wdir()^'
1797 9
1797 9
1798 $ hg up 7
1798 $ hg up 7
1799 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1799 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1800 $ hg debugrevspec 'wdir()^'
1800 $ hg debugrevspec 'wdir()^'
1801 7
1801 7
1802 $ hg debugrevspec 'wdir()^0'
1802 $ hg debugrevspec 'wdir()^0'
1803 2147483647
1803 2147483647
1804 $ hg debugrevspec 'wdir()~3'
1804 $ hg debugrevspec 'wdir()~3'
1805 5
1805 5
1806 $ hg debugrevspec 'ancestors(wdir())'
1806 $ hg debugrevspec 'ancestors(wdir())'
1807 0
1807 0
1808 1
1808 1
1809 2
1809 2
1810 3
1810 3
1811 4
1811 4
1812 5
1812 5
1813 6
1813 6
1814 7
1814 7
1815 2147483647
1815 2147483647
1816 $ hg debugrevspec '0:wdir() & ancestor(wdir())'
1817 2147483647
1818 $ hg debugrevspec '0:wdir() & ancestor(.:wdir())'
1819 4
1820 $ hg debugrevspec '0:wdir() & ancestor(wdir(), wdir())'
1821 2147483647
1822 $ hg debugrevspec '0:wdir() & ancestor(wdir(), tip)'
1823 4
1824 $ hg debugrevspec 'null:wdir() & ancestor(wdir(), null)'
1825 -1
1816 $ hg debugrevspec 'wdir()~0'
1826 $ hg debugrevspec 'wdir()~0'
1817 2147483647
1827 2147483647
1818 $ hg debugrevspec 'p1(wdir())'
1828 $ hg debugrevspec 'p1(wdir())'
1819 7
1829 7
1820 $ hg debugrevspec 'p2(wdir())'
1830 $ hg debugrevspec 'p2(wdir())'
1821 $ hg debugrevspec 'parents(wdir())'
1831 $ hg debugrevspec 'parents(wdir())'
1822 7
1832 7
1823 $ hg debugrevspec 'wdir()^1'
1833 $ hg debugrevspec 'wdir()^1'
1824 7
1834 7
1825 $ hg debugrevspec 'wdir()^2'
1835 $ hg debugrevspec 'wdir()^2'
1826 $ hg debugrevspec 'wdir()^3'
1836 $ hg debugrevspec 'wdir()^3'
1827 hg: parse error: ^ expects a number 0, 1, or 2
1837 hg: parse error: ^ expects a number 0, 1, or 2
1828 [255]
1838 [255]
1829 For tests consistency
1839 For tests consistency
1830 $ hg up 9
1840 $ hg up 9
1831 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1841 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1832 $ hg debugrevspec 'tip or wdir()'
1842 $ hg debugrevspec 'tip or wdir()'
1833 9
1843 9
1834 2147483647
1844 2147483647
1835 $ hg debugrevspec '0:tip and wdir()'
1845 $ hg debugrevspec '0:tip and wdir()'
1836 $ log '0:wdir()' | tail -3
1846 $ log '0:wdir()' | tail -3
1837 8
1847 8
1838 9
1848 9
1839 2147483647
1849 2147483647
1840 $ log 'wdir():0' | head -3
1850 $ log 'wdir():0' | head -3
1841 2147483647
1851 2147483647
1842 9
1852 9
1843 8
1853 8
1844 $ log 'wdir():wdir()'
1854 $ log 'wdir():wdir()'
1845 2147483647
1855 2147483647
1846 $ log '(all() + wdir()) & min(. + wdir())'
1856 $ log '(all() + wdir()) & min(. + wdir())'
1847 9
1857 9
1848 $ log '(all() + wdir()) & max(. + wdir())'
1858 $ log '(all() + wdir()) & max(. + wdir())'
1849 2147483647
1859 2147483647
1850 $ log 'first(wdir() + .)'
1860 $ log 'first(wdir() + .)'
1851 2147483647
1861 2147483647
1852 $ log 'last(. + wdir())'
1862 $ log 'last(. + wdir())'
1853 2147483647
1863 2147483647
1854
1864
1855 Test working-directory integer revision and node id
1865 Test working-directory integer revision and node id
1856 (BUG: '0:wdir()' is still needed to populate wdir revision)
1866 (BUG: '0:wdir()' is still needed to populate wdir revision)
1857
1867
1858 $ hg debugrevspec '0:wdir() & 2147483647'
1868 $ hg debugrevspec '0:wdir() & 2147483647'
1859 2147483647
1869 2147483647
1860 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1870 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1861 2147483647
1871 2147483647
1862 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1872 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1863 2147483647
1873 2147483647
1864 $ hg debugrevspec '0:wdir() & ffffffffffff'
1874 $ hg debugrevspec '0:wdir() & ffffffffffff'
1865 2147483647
1875 2147483647
1866 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1876 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1867 2147483647
1877 2147483647
1868 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1878 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1869 2147483647
1879 2147483647
1870
1880
1871 $ cd ..
1881 $ cd ..
1872
1882
1873 Test short 'ff...' hash collision
1883 Test short 'ff...' hash collision
1874 (BUG: '0:wdir()' is still needed to populate wdir revision)
1884 (BUG: '0:wdir()' is still needed to populate wdir revision)
1875
1885
1876 $ hg init wdir-hashcollision
1886 $ hg init wdir-hashcollision
1877 $ cd wdir-hashcollision
1887 $ cd wdir-hashcollision
1878 $ cat <<EOF >> .hg/hgrc
1888 $ cat <<EOF >> .hg/hgrc
1879 > [experimental]
1889 > [experimental]
1880 > evolution.createmarkers=True
1890 > evolution.createmarkers=True
1881 > EOF
1891 > EOF
1882 $ echo 0 > a
1892 $ echo 0 > a
1883 $ hg ci -qAm 0
1893 $ hg ci -qAm 0
1884 $ for i in 2463 2961 6726 78127; do
1894 $ for i in 2463 2961 6726 78127; do
1885 > hg up -q 0
1895 > hg up -q 0
1886 > echo $i > a
1896 > echo $i > a
1887 > hg ci -qm $i
1897 > hg ci -qm $i
1888 > done
1898 > done
1889 $ hg up -q null
1899 $ hg up -q null
1890 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1900 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1891 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1901 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1892 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1902 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1893 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1903 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1894 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1904 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1895 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1905 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1896 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1906 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1897 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1907 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1898 obsoleted 1 changesets
1908 obsoleted 1 changesets
1899
1909
1900 $ hg debugrevspec '0:wdir() & fff'
1910 $ hg debugrevspec '0:wdir() & fff'
1901 abort: 00changelog.i@fff: ambiguous identifier!
1911 abort: 00changelog.i@fff: ambiguous identifier!
1902 [255]
1912 [255]
1903 $ hg debugrevspec '0:wdir() & ffff'
1913 $ hg debugrevspec '0:wdir() & ffff'
1904 abort: 00changelog.i@ffff: ambiguous identifier!
1914 abort: 00changelog.i@ffff: ambiguous identifier!
1905 [255]
1915 [255]
1906 $ hg debugrevspec '0:wdir() & fffb'
1916 $ hg debugrevspec '0:wdir() & fffb'
1907 abort: 00changelog.i@fffb: ambiguous identifier!
1917 abort: 00changelog.i@fffb: ambiguous identifier!
1908 [255]
1918 [255]
1909 BROKEN should be '2' (node lookup uses unfiltered repo)
1919 BROKEN should be '2' (node lookup uses unfiltered repo)
1910 $ hg debugrevspec '0:wdir() & id(fffb)'
1920 $ hg debugrevspec '0:wdir() & id(fffb)'
1911 BROKEN should be '2' (node lookup uses unfiltered repo)
1921 BROKEN should be '2' (node lookup uses unfiltered repo)
1912 $ hg debugrevspec '0:wdir() & ffff8'
1922 $ hg debugrevspec '0:wdir() & ffff8'
1913 4
1923 4
1914 $ hg debugrevspec '0:wdir() & fffff'
1924 $ hg debugrevspec '0:wdir() & fffff'
1915 2147483647
1925 2147483647
1916
1926
1917 $ cd ..
1927 $ cd ..
1918
1928
1919 Test branch() with wdir()
1929 Test branch() with wdir()
1920
1930
1921 $ cd repo
1931 $ cd repo
1922
1932
1923 $ log '0:wdir() & branch("literal:Γ©")'
1933 $ log '0:wdir() & branch("literal:Γ©")'
1924 8
1934 8
1925 9
1935 9
1926 2147483647
1936 2147483647
1927 $ log '0:wdir() & branch("re:Γ©")'
1937 $ log '0:wdir() & branch("re:Γ©")'
1928 8
1938 8
1929 9
1939 9
1930 2147483647
1940 2147483647
1931 $ log '0:wdir() & branch("re:^a")'
1941 $ log '0:wdir() & branch("re:^a")'
1932 0
1942 0
1933 2
1943 2
1934 $ log '0:wdir() & branch(8)'
1944 $ log '0:wdir() & branch(8)'
1935 8
1945 8
1936 9
1946 9
1937 2147483647
1947 2147483647
1938
1948
1939 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1949 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1940 itself isn't returned unless it is explicitly populated.
1950 itself isn't returned unless it is explicitly populated.
1941
1951
1942 $ log 'branch(wdir())'
1952 $ log 'branch(wdir())'
1943 8
1953 8
1944 9
1954 9
1945 $ log '0:wdir() & branch(wdir())'
1955 $ log '0:wdir() & branch(wdir())'
1946 8
1956 8
1947 9
1957 9
1948 2147483647
1958 2147483647
1949
1959
1950 $ log 'outgoing()'
1960 $ log 'outgoing()'
1951 8
1961 8
1952 9
1962 9
1953 $ log 'outgoing("../remote1")'
1963 $ log 'outgoing("../remote1")'
1954 8
1964 8
1955 9
1965 9
1956 $ log 'outgoing("../remote2")'
1966 $ log 'outgoing("../remote2")'
1957 3
1967 3
1958 5
1968 5
1959 6
1969 6
1960 7
1970 7
1961 9
1971 9
1962 $ log 'p1(merge())'
1972 $ log 'p1(merge())'
1963 5
1973 5
1964 $ log 'p2(merge())'
1974 $ log 'p2(merge())'
1965 4
1975 4
1966 $ log 'parents(merge())'
1976 $ log 'parents(merge())'
1967 4
1977 4
1968 5
1978 5
1969 $ log 'p1(branchpoint())'
1979 $ log 'p1(branchpoint())'
1970 0
1980 0
1971 2
1981 2
1972 $ log 'p2(branchpoint())'
1982 $ log 'p2(branchpoint())'
1973 $ log 'parents(branchpoint())'
1983 $ log 'parents(branchpoint())'
1974 0
1984 0
1975 2
1985 2
1976 $ log 'removes(a)'
1986 $ log 'removes(a)'
1977 2
1987 2
1978 6
1988 6
1979 $ log 'roots(all())'
1989 $ log 'roots(all())'
1980 0
1990 0
1981 $ log 'reverse(2 or 3 or 4 or 5)'
1991 $ log 'reverse(2 or 3 or 4 or 5)'
1982 5
1992 5
1983 4
1993 4
1984 3
1994 3
1985 2
1995 2
1986 $ log 'reverse(all())'
1996 $ log 'reverse(all())'
1987 9
1997 9
1988 8
1998 8
1989 7
1999 7
1990 6
2000 6
1991 5
2001 5
1992 4
2002 4
1993 3
2003 3
1994 2
2004 2
1995 1
2005 1
1996 0
2006 0
1997 $ log 'reverse(all()) & filelog(b)'
2007 $ log 'reverse(all()) & filelog(b)'
1998 4
2008 4
1999 1
2009 1
2000 $ log 'rev(5)'
2010 $ log 'rev(5)'
2001 5
2011 5
2002 $ log 'sort(limit(reverse(all()), 3))'
2012 $ log 'sort(limit(reverse(all()), 3))'
2003 7
2013 7
2004 8
2014 8
2005 9
2015 9
2006 $ log 'sort(2 or 3 or 4 or 5, date)'
2016 $ log 'sort(2 or 3 or 4 or 5, date)'
2007 2
2017 2
2008 3
2018 3
2009 5
2019 5
2010 4
2020 4
2011 $ log 'tagged()'
2021 $ log 'tagged()'
2012 6
2022 6
2013 $ log 'tag()'
2023 $ log 'tag()'
2014 6
2024 6
2015 $ log 'tag(1.0)'
2025 $ log 'tag(1.0)'
2016 6
2026 6
2017 $ log 'tag(tip)'
2027 $ log 'tag(tip)'
2018 9
2028 9
2019
2029
2020 Test order of revisions in compound expression
2030 Test order of revisions in compound expression
2021 ----------------------------------------------
2031 ----------------------------------------------
2022
2032
2023 The general rule is that only the outermost (= leftmost) predicate can
2033 The general rule is that only the outermost (= leftmost) predicate can
2024 enforce its ordering requirement. The other predicates should take the
2034 enforce its ordering requirement. The other predicates should take the
2025 ordering defined by it.
2035 ordering defined by it.
2026
2036
2027 'A & B' should follow the order of 'A':
2037 'A & B' should follow the order of 'A':
2028
2038
2029 $ log '2:0 & 0::2'
2039 $ log '2:0 & 0::2'
2030 2
2040 2
2031 1
2041 1
2032 0
2042 0
2033
2043
2034 'head()' combines sets in right order:
2044 'head()' combines sets in right order:
2035
2045
2036 $ log '2:0 & head()'
2046 $ log '2:0 & head()'
2037 2
2047 2
2038 1
2048 1
2039 0
2049 0
2040
2050
2041 'x:y' takes ordering parameter into account:
2051 'x:y' takes ordering parameter into account:
2042
2052
2043 $ try -p optimized '3:0 & 0:3 & not 2:1'
2053 $ try -p optimized '3:0 & 0:3 & not 2:1'
2044 * optimized:
2054 * optimized:
2045 (difference
2055 (difference
2046 (and
2056 (and
2047 (range
2057 (range
2048 (symbol '3')
2058 (symbol '3')
2049 (symbol '0'))
2059 (symbol '0'))
2050 (range
2060 (range
2051 (symbol '0')
2061 (symbol '0')
2052 (symbol '3')))
2062 (symbol '3')))
2053 (range
2063 (range
2054 (symbol '2')
2064 (symbol '2')
2055 (symbol '1')))
2065 (symbol '1')))
2056 * set:
2066 * set:
2057 <filteredset
2067 <filteredset
2058 <filteredset
2068 <filteredset
2059 <spanset- 0:4>,
2069 <spanset- 0:4>,
2060 <spanset+ 0:4>>,
2070 <spanset+ 0:4>>,
2061 <not
2071 <not
2062 <spanset+ 1:3>>>
2072 <spanset+ 1:3>>>
2063 3
2073 3
2064 0
2074 0
2065
2075
2066 'a + b', which is optimized to '_list(a b)', should take the ordering of
2076 'a + b', which is optimized to '_list(a b)', should take the ordering of
2067 the left expression:
2077 the left expression:
2068
2078
2069 $ try --optimize '2:0 & (0 + 1 + 2)'
2079 $ try --optimize '2:0 & (0 + 1 + 2)'
2070 (and
2080 (and
2071 (range
2081 (range
2072 (symbol '2')
2082 (symbol '2')
2073 (symbol '0'))
2083 (symbol '0'))
2074 (group
2084 (group
2075 (or
2085 (or
2076 (list
2086 (list
2077 (symbol '0')
2087 (symbol '0')
2078 (symbol '1')
2088 (symbol '1')
2079 (symbol '2')))))
2089 (symbol '2')))))
2080 * optimized:
2090 * optimized:
2081 (and
2091 (and
2082 (range
2092 (range
2083 (symbol '2')
2093 (symbol '2')
2084 (symbol '0'))
2094 (symbol '0'))
2085 (func
2095 (func
2086 (symbol '_list')
2096 (symbol '_list')
2087 (string '0\x001\x002')))
2097 (string '0\x001\x002')))
2088 * set:
2098 * set:
2089 <filteredset
2099 <filteredset
2090 <spanset- 0:3>,
2100 <spanset- 0:3>,
2091 <baseset [0, 1, 2]>>
2101 <baseset [0, 1, 2]>>
2092 2
2102 2
2093 1
2103 1
2094 0
2104 0
2095
2105
2096 'A + B' should take the ordering of the left expression:
2106 'A + B' should take the ordering of the left expression:
2097
2107
2098 $ try --optimize '2:0 & (0:1 + 2)'
2108 $ try --optimize '2:0 & (0:1 + 2)'
2099 (and
2109 (and
2100 (range
2110 (range
2101 (symbol '2')
2111 (symbol '2')
2102 (symbol '0'))
2112 (symbol '0'))
2103 (group
2113 (group
2104 (or
2114 (or
2105 (list
2115 (list
2106 (range
2116 (range
2107 (symbol '0')
2117 (symbol '0')
2108 (symbol '1'))
2118 (symbol '1'))
2109 (symbol '2')))))
2119 (symbol '2')))))
2110 * optimized:
2120 * optimized:
2111 (and
2121 (and
2112 (range
2122 (range
2113 (symbol '2')
2123 (symbol '2')
2114 (symbol '0'))
2124 (symbol '0'))
2115 (or
2125 (or
2116 (list
2126 (list
2117 (range
2127 (range
2118 (symbol '0')
2128 (symbol '0')
2119 (symbol '1'))
2129 (symbol '1'))
2120 (symbol '2'))))
2130 (symbol '2'))))
2121 * set:
2131 * set:
2122 <filteredset
2132 <filteredset
2123 <spanset- 0:3>,
2133 <spanset- 0:3>,
2124 <addset
2134 <addset
2125 <spanset+ 0:2>,
2135 <spanset+ 0:2>,
2126 <baseset [2]>>>
2136 <baseset [2]>>>
2127 2
2137 2
2128 1
2138 1
2129 0
2139 0
2130
2140
2131 '_intlist(a b)' should behave like 'a + b':
2141 '_intlist(a b)' should behave like 'a + b':
2132
2142
2133 $ trylist --optimize '2:0 & %ld' 0 1 2
2143 $ trylist --optimize '2:0 & %ld' 0 1 2
2134 (and
2144 (and
2135 (range
2145 (range
2136 (symbol '2')
2146 (symbol '2')
2137 (symbol '0'))
2147 (symbol '0'))
2138 (func
2148 (func
2139 (symbol '_intlist')
2149 (symbol '_intlist')
2140 (string '0\x001\x002')))
2150 (string '0\x001\x002')))
2141 * optimized:
2151 * optimized:
2142 (andsmally
2152 (andsmally
2143 (range
2153 (range
2144 (symbol '2')
2154 (symbol '2')
2145 (symbol '0'))
2155 (symbol '0'))
2146 (func
2156 (func
2147 (symbol '_intlist')
2157 (symbol '_intlist')
2148 (string '0\x001\x002')))
2158 (string '0\x001\x002')))
2149 * set:
2159 * set:
2150 <filteredset
2160 <filteredset
2151 <spanset- 0:3>,
2161 <spanset- 0:3>,
2152 <baseset+ [0, 1, 2]>>
2162 <baseset+ [0, 1, 2]>>
2153 2
2163 2
2154 1
2164 1
2155 0
2165 0
2156
2166
2157 $ trylist --optimize '%ld & 2:0' 0 2 1
2167 $ trylist --optimize '%ld & 2:0' 0 2 1
2158 (and
2168 (and
2159 (func
2169 (func
2160 (symbol '_intlist')
2170 (symbol '_intlist')
2161 (string '0\x002\x001'))
2171 (string '0\x002\x001'))
2162 (range
2172 (range
2163 (symbol '2')
2173 (symbol '2')
2164 (symbol '0')))
2174 (symbol '0')))
2165 * optimized:
2175 * optimized:
2166 (and
2176 (and
2167 (func
2177 (func
2168 (symbol '_intlist')
2178 (symbol '_intlist')
2169 (string '0\x002\x001'))
2179 (string '0\x002\x001'))
2170 (range
2180 (range
2171 (symbol '2')
2181 (symbol '2')
2172 (symbol '0')))
2182 (symbol '0')))
2173 * set:
2183 * set:
2174 <filteredset
2184 <filteredset
2175 <baseset [0, 2, 1]>,
2185 <baseset [0, 2, 1]>,
2176 <spanset- 0:3>>
2186 <spanset- 0:3>>
2177 0
2187 0
2178 2
2188 2
2179 1
2189 1
2180
2190
2181 '_hexlist(a b)' should behave like 'a + b':
2191 '_hexlist(a b)' should behave like 'a + b':
2182
2192
2183 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
2193 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
2184 (and
2194 (and
2185 (range
2195 (range
2186 (symbol '2')
2196 (symbol '2')
2187 (symbol '0'))
2197 (symbol '0'))
2188 (func
2198 (func
2189 (symbol '_hexlist')
2199 (symbol '_hexlist')
2190 (string '*'))) (glob)
2200 (string '*'))) (glob)
2191 * optimized:
2201 * optimized:
2192 (and
2202 (and
2193 (range
2203 (range
2194 (symbol '2')
2204 (symbol '2')
2195 (symbol '0'))
2205 (symbol '0'))
2196 (func
2206 (func
2197 (symbol '_hexlist')
2207 (symbol '_hexlist')
2198 (string '*'))) (glob)
2208 (string '*'))) (glob)
2199 * set:
2209 * set:
2200 <filteredset
2210 <filteredset
2201 <spanset- 0:3>,
2211 <spanset- 0:3>,
2202 <baseset [0, 1, 2]>>
2212 <baseset [0, 1, 2]>>
2203 2
2213 2
2204 1
2214 1
2205 0
2215 0
2206
2216
2207 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
2217 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
2208 (and
2218 (and
2209 (func
2219 (func
2210 (symbol '_hexlist')
2220 (symbol '_hexlist')
2211 (string '*')) (glob)
2221 (string '*')) (glob)
2212 (range
2222 (range
2213 (symbol '2')
2223 (symbol '2')
2214 (symbol '0')))
2224 (symbol '0')))
2215 * optimized:
2225 * optimized:
2216 (andsmally
2226 (andsmally
2217 (func
2227 (func
2218 (symbol '_hexlist')
2228 (symbol '_hexlist')
2219 (string '*')) (glob)
2229 (string '*')) (glob)
2220 (range
2230 (range
2221 (symbol '2')
2231 (symbol '2')
2222 (symbol '0')))
2232 (symbol '0')))
2223 * set:
2233 * set:
2224 <baseset [0, 2, 1]>
2234 <baseset [0, 2, 1]>
2225 0
2235 0
2226 2
2236 2
2227 1
2237 1
2228
2238
2229 '_list' should not go through the slow follow-order path if order doesn't
2239 '_list' should not go through the slow follow-order path if order doesn't
2230 matter:
2240 matter:
2231
2241
2232 $ try -p optimized '2:0 & not (0 + 1)'
2242 $ try -p optimized '2:0 & not (0 + 1)'
2233 * optimized:
2243 * optimized:
2234 (difference
2244 (difference
2235 (range
2245 (range
2236 (symbol '2')
2246 (symbol '2')
2237 (symbol '0'))
2247 (symbol '0'))
2238 (func
2248 (func
2239 (symbol '_list')
2249 (symbol '_list')
2240 (string '0\x001')))
2250 (string '0\x001')))
2241 * set:
2251 * set:
2242 <filteredset
2252 <filteredset
2243 <spanset- 0:3>,
2253 <spanset- 0:3>,
2244 <not
2254 <not
2245 <baseset [0, 1]>>>
2255 <baseset [0, 1]>>>
2246 2
2256 2
2247
2257
2248 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
2258 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
2249 * optimized:
2259 * optimized:
2250 (difference
2260 (difference
2251 (range
2261 (range
2252 (symbol '2')
2262 (symbol '2')
2253 (symbol '0'))
2263 (symbol '0'))
2254 (and
2264 (and
2255 (range
2265 (range
2256 (symbol '0')
2266 (symbol '0')
2257 (symbol '2'))
2267 (symbol '2'))
2258 (func
2268 (func
2259 (symbol '_list')
2269 (symbol '_list')
2260 (string '0\x001'))))
2270 (string '0\x001'))))
2261 * set:
2271 * set:
2262 <filteredset
2272 <filteredset
2263 <spanset- 0:3>,
2273 <spanset- 0:3>,
2264 <not
2274 <not
2265 <baseset [0, 1]>>>
2275 <baseset [0, 1]>>>
2266 2
2276 2
2267
2277
2268 because 'present()' does nothing other than suppressing an error, the
2278 because 'present()' does nothing other than suppressing an error, the
2269 ordering requirement should be forwarded to the nested expression
2279 ordering requirement should be forwarded to the nested expression
2270
2280
2271 $ try -p optimized 'present(2 + 0 + 1)'
2281 $ try -p optimized 'present(2 + 0 + 1)'
2272 * optimized:
2282 * optimized:
2273 (func
2283 (func
2274 (symbol 'present')
2284 (symbol 'present')
2275 (func
2285 (func
2276 (symbol '_list')
2286 (symbol '_list')
2277 (string '2\x000\x001')))
2287 (string '2\x000\x001')))
2278 * set:
2288 * set:
2279 <baseset [2, 0, 1]>
2289 <baseset [2, 0, 1]>
2280 2
2290 2
2281 0
2291 0
2282 1
2292 1
2283
2293
2284 $ try --optimize '2:0 & present(0 + 1 + 2)'
2294 $ try --optimize '2:0 & present(0 + 1 + 2)'
2285 (and
2295 (and
2286 (range
2296 (range
2287 (symbol '2')
2297 (symbol '2')
2288 (symbol '0'))
2298 (symbol '0'))
2289 (func
2299 (func
2290 (symbol 'present')
2300 (symbol 'present')
2291 (or
2301 (or
2292 (list
2302 (list
2293 (symbol '0')
2303 (symbol '0')
2294 (symbol '1')
2304 (symbol '1')
2295 (symbol '2')))))
2305 (symbol '2')))))
2296 * optimized:
2306 * optimized:
2297 (and
2307 (and
2298 (range
2308 (range
2299 (symbol '2')
2309 (symbol '2')
2300 (symbol '0'))
2310 (symbol '0'))
2301 (func
2311 (func
2302 (symbol 'present')
2312 (symbol 'present')
2303 (func
2313 (func
2304 (symbol '_list')
2314 (symbol '_list')
2305 (string '0\x001\x002'))))
2315 (string '0\x001\x002'))))
2306 * set:
2316 * set:
2307 <filteredset
2317 <filteredset
2308 <spanset- 0:3>,
2318 <spanset- 0:3>,
2309 <baseset [0, 1, 2]>>
2319 <baseset [0, 1, 2]>>
2310 2
2320 2
2311 1
2321 1
2312 0
2322 0
2313
2323
2314 'reverse()' should take effect only if it is the outermost expression:
2324 'reverse()' should take effect only if it is the outermost expression:
2315
2325
2316 $ try --optimize '0:2 & reverse(all())'
2326 $ try --optimize '0:2 & reverse(all())'
2317 (and
2327 (and
2318 (range
2328 (range
2319 (symbol '0')
2329 (symbol '0')
2320 (symbol '2'))
2330 (symbol '2'))
2321 (func
2331 (func
2322 (symbol 'reverse')
2332 (symbol 'reverse')
2323 (func
2333 (func
2324 (symbol 'all')
2334 (symbol 'all')
2325 None)))
2335 None)))
2326 * optimized:
2336 * optimized:
2327 (and
2337 (and
2328 (range
2338 (range
2329 (symbol '0')
2339 (symbol '0')
2330 (symbol '2'))
2340 (symbol '2'))
2331 (func
2341 (func
2332 (symbol 'reverse')
2342 (symbol 'reverse')
2333 (func
2343 (func
2334 (symbol 'all')
2344 (symbol 'all')
2335 None)))
2345 None)))
2336 * set:
2346 * set:
2337 <filteredset
2347 <filteredset
2338 <spanset+ 0:3>,
2348 <spanset+ 0:3>,
2339 <spanset+ 0:10>>
2349 <spanset+ 0:10>>
2340 0
2350 0
2341 1
2351 1
2342 2
2352 2
2343
2353
2344 'sort()' should take effect only if it is the outermost expression:
2354 'sort()' should take effect only if it is the outermost expression:
2345
2355
2346 $ try --optimize '0:2 & sort(all(), -rev)'
2356 $ try --optimize '0:2 & sort(all(), -rev)'
2347 (and
2357 (and
2348 (range
2358 (range
2349 (symbol '0')
2359 (symbol '0')
2350 (symbol '2'))
2360 (symbol '2'))
2351 (func
2361 (func
2352 (symbol 'sort')
2362 (symbol 'sort')
2353 (list
2363 (list
2354 (func
2364 (func
2355 (symbol 'all')
2365 (symbol 'all')
2356 None)
2366 None)
2357 (negate
2367 (negate
2358 (symbol 'rev')))))
2368 (symbol 'rev')))))
2359 * optimized:
2369 * optimized:
2360 (and
2370 (and
2361 (range
2371 (range
2362 (symbol '0')
2372 (symbol '0')
2363 (symbol '2'))
2373 (symbol '2'))
2364 (func
2374 (func
2365 (symbol 'sort')
2375 (symbol 'sort')
2366 (list
2376 (list
2367 (func
2377 (func
2368 (symbol 'all')
2378 (symbol 'all')
2369 None)
2379 None)
2370 (string '-rev'))))
2380 (string '-rev'))))
2371 * set:
2381 * set:
2372 <filteredset
2382 <filteredset
2373 <spanset+ 0:3>,
2383 <spanset+ 0:3>,
2374 <spanset+ 0:10>>
2384 <spanset+ 0:10>>
2375 0
2385 0
2376 1
2386 1
2377 2
2387 2
2378
2388
2379 invalid argument passed to noop sort():
2389 invalid argument passed to noop sort():
2380
2390
2381 $ log '0:2 & sort()'
2391 $ log '0:2 & sort()'
2382 hg: parse error: sort requires one or two arguments
2392 hg: parse error: sort requires one or two arguments
2383 [255]
2393 [255]
2384 $ log '0:2 & sort(all(), -invalid)'
2394 $ log '0:2 & sort(all(), -invalid)'
2385 hg: parse error: unknown sort key '-invalid'
2395 hg: parse error: unknown sort key '-invalid'
2386 [255]
2396 [255]
2387
2397
2388 for 'A & f(B)', 'B' should not be affected by the order of 'A':
2398 for 'A & f(B)', 'B' should not be affected by the order of 'A':
2389
2399
2390 $ try --optimize '2:0 & first(1 + 0 + 2)'
2400 $ try --optimize '2:0 & first(1 + 0 + 2)'
2391 (and
2401 (and
2392 (range
2402 (range
2393 (symbol '2')
2403 (symbol '2')
2394 (symbol '0'))
2404 (symbol '0'))
2395 (func
2405 (func
2396 (symbol 'first')
2406 (symbol 'first')
2397 (or
2407 (or
2398 (list
2408 (list
2399 (symbol '1')
2409 (symbol '1')
2400 (symbol '0')
2410 (symbol '0')
2401 (symbol '2')))))
2411 (symbol '2')))))
2402 * optimized:
2412 * optimized:
2403 (and
2413 (and
2404 (range
2414 (range
2405 (symbol '2')
2415 (symbol '2')
2406 (symbol '0'))
2416 (symbol '0'))
2407 (func
2417 (func
2408 (symbol 'first')
2418 (symbol 'first')
2409 (func
2419 (func
2410 (symbol '_list')
2420 (symbol '_list')
2411 (string '1\x000\x002'))))
2421 (string '1\x000\x002'))))
2412 * set:
2422 * set:
2413 <filteredset
2423 <filteredset
2414 <baseset [1]>,
2424 <baseset [1]>,
2415 <spanset- 0:3>>
2425 <spanset- 0:3>>
2416 1
2426 1
2417
2427
2418 $ try --optimize '2:0 & not last(0 + 2 + 1)'
2428 $ try --optimize '2:0 & not last(0 + 2 + 1)'
2419 (and
2429 (and
2420 (range
2430 (range
2421 (symbol '2')
2431 (symbol '2')
2422 (symbol '0'))
2432 (symbol '0'))
2423 (not
2433 (not
2424 (func
2434 (func
2425 (symbol 'last')
2435 (symbol 'last')
2426 (or
2436 (or
2427 (list
2437 (list
2428 (symbol '0')
2438 (symbol '0')
2429 (symbol '2')
2439 (symbol '2')
2430 (symbol '1'))))))
2440 (symbol '1'))))))
2431 * optimized:
2441 * optimized:
2432 (difference
2442 (difference
2433 (range
2443 (range
2434 (symbol '2')
2444 (symbol '2')
2435 (symbol '0'))
2445 (symbol '0'))
2436 (func
2446 (func
2437 (symbol 'last')
2447 (symbol 'last')
2438 (func
2448 (func
2439 (symbol '_list')
2449 (symbol '_list')
2440 (string '0\x002\x001'))))
2450 (string '0\x002\x001'))))
2441 * set:
2451 * set:
2442 <filteredset
2452 <filteredset
2443 <spanset- 0:3>,
2453 <spanset- 0:3>,
2444 <not
2454 <not
2445 <baseset [1]>>>
2455 <baseset [1]>>>
2446 2
2456 2
2447 0
2457 0
2448
2458
2449 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2459 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2450
2460
2451 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2461 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2452 (and
2462 (and
2453 (range
2463 (range
2454 (symbol '2')
2464 (symbol '2')
2455 (symbol '0'))
2465 (symbol '0'))
2456 (range
2466 (range
2457 (group
2467 (group
2458 (or
2468 (or
2459 (list
2469 (list
2460 (symbol '1')
2470 (symbol '1')
2461 (symbol '0')
2471 (symbol '0')
2462 (symbol '2'))))
2472 (symbol '2'))))
2463 (group
2473 (group
2464 (or
2474 (or
2465 (list
2475 (list
2466 (symbol '0')
2476 (symbol '0')
2467 (symbol '2')
2477 (symbol '2')
2468 (symbol '1'))))))
2478 (symbol '1'))))))
2469 * optimized:
2479 * optimized:
2470 (and
2480 (and
2471 (range
2481 (range
2472 (symbol '2')
2482 (symbol '2')
2473 (symbol '0'))
2483 (symbol '0'))
2474 (range
2484 (range
2475 (func
2485 (func
2476 (symbol '_list')
2486 (symbol '_list')
2477 (string '1\x000\x002'))
2487 (string '1\x000\x002'))
2478 (func
2488 (func
2479 (symbol '_list')
2489 (symbol '_list')
2480 (string '0\x002\x001'))))
2490 (string '0\x002\x001'))))
2481 * set:
2491 * set:
2482 <filteredset
2492 <filteredset
2483 <spanset- 0:3>,
2493 <spanset- 0:3>,
2484 <baseset [1]>>
2494 <baseset [1]>>
2485 1
2495 1
2486
2496
2487 'A & B' can be rewritten as 'flipand(B, A)' by weight.
2497 'A & B' can be rewritten as 'flipand(B, A)' by weight.
2488
2498
2489 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2499 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2490 (and
2500 (and
2491 (func
2501 (func
2492 (symbol 'contains')
2502 (symbol 'contains')
2493 (string 'glob:*'))
2503 (string 'glob:*'))
2494 (group
2504 (group
2495 (or
2505 (or
2496 (list
2506 (list
2497 (symbol '2')
2507 (symbol '2')
2498 (symbol '0')
2508 (symbol '0')
2499 (symbol '1')))))
2509 (symbol '1')))))
2500 * optimized:
2510 * optimized:
2501 (andsmally
2511 (andsmally
2502 (func
2512 (func
2503 (symbol 'contains')
2513 (symbol 'contains')
2504 (string 'glob:*'))
2514 (string 'glob:*'))
2505 (func
2515 (func
2506 (symbol '_list')
2516 (symbol '_list')
2507 (string '2\x000\x001')))
2517 (string '2\x000\x001')))
2508 * set:
2518 * set:
2509 <filteredset
2519 <filteredset
2510 <baseset+ [0, 1, 2]>,
2520 <baseset+ [0, 1, 2]>,
2511 <contains 'glob:*'>>
2521 <contains 'glob:*'>>
2512 0
2522 0
2513 1
2523 1
2514 2
2524 2
2515
2525
2516 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2526 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2517 the order appropriately:
2527 the order appropriately:
2518
2528
2519 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2529 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2520 (and
2530 (and
2521 (func
2531 (func
2522 (symbol 'reverse')
2532 (symbol 'reverse')
2523 (func
2533 (func
2524 (symbol 'contains')
2534 (symbol 'contains')
2525 (string 'glob:*')))
2535 (string 'glob:*')))
2526 (group
2536 (group
2527 (or
2537 (or
2528 (list
2538 (list
2529 (symbol '0')
2539 (symbol '0')
2530 (symbol '2')
2540 (symbol '2')
2531 (symbol '1')))))
2541 (symbol '1')))))
2532 * optimized:
2542 * optimized:
2533 (andsmally
2543 (andsmally
2534 (func
2544 (func
2535 (symbol 'reverse')
2545 (symbol 'reverse')
2536 (func
2546 (func
2537 (symbol 'contains')
2547 (symbol 'contains')
2538 (string 'glob:*')))
2548 (string 'glob:*')))
2539 (func
2549 (func
2540 (symbol '_list')
2550 (symbol '_list')
2541 (string '0\x002\x001')))
2551 (string '0\x002\x001')))
2542 * set:
2552 * set:
2543 <filteredset
2553 <filteredset
2544 <baseset- [0, 1, 2]>,
2554 <baseset- [0, 1, 2]>,
2545 <contains 'glob:*'>>
2555 <contains 'glob:*'>>
2546 2
2556 2
2547 1
2557 1
2548 0
2558 0
2549
2559
2550 test sort revset
2560 test sort revset
2551 --------------------------------------------
2561 --------------------------------------------
2552
2562
2553 test when adding two unordered revsets
2563 test when adding two unordered revsets
2554
2564
2555 $ log 'sort(keyword(issue) or modifies(b))'
2565 $ log 'sort(keyword(issue) or modifies(b))'
2556 4
2566 4
2557 6
2567 6
2558
2568
2559 test when sorting a reversed collection in the same way it is
2569 test when sorting a reversed collection in the same way it is
2560
2570
2561 $ log 'sort(reverse(all()), -rev)'
2571 $ log 'sort(reverse(all()), -rev)'
2562 9
2572 9
2563 8
2573 8
2564 7
2574 7
2565 6
2575 6
2566 5
2576 5
2567 4
2577 4
2568 3
2578 3
2569 2
2579 2
2570 1
2580 1
2571 0
2581 0
2572
2582
2573 test when sorting a reversed collection
2583 test when sorting a reversed collection
2574
2584
2575 $ log 'sort(reverse(all()), rev)'
2585 $ log 'sort(reverse(all()), rev)'
2576 0
2586 0
2577 1
2587 1
2578 2
2588 2
2579 3
2589 3
2580 4
2590 4
2581 5
2591 5
2582 6
2592 6
2583 7
2593 7
2584 8
2594 8
2585 9
2595 9
2586
2596
2587
2597
2588 test sorting two sorted collections in different orders
2598 test sorting two sorted collections in different orders
2589
2599
2590 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2600 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2591 2
2601 2
2592 6
2602 6
2593 8
2603 8
2594 9
2604 9
2595
2605
2596 test sorting two sorted collections in different orders backwards
2606 test sorting two sorted collections in different orders backwards
2597
2607
2598 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2608 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2599 9
2609 9
2600 8
2610 8
2601 6
2611 6
2602 2
2612 2
2603
2613
2604 test empty sort key which is noop
2614 test empty sort key which is noop
2605
2615
2606 $ log 'sort(0 + 2 + 1, "")'
2616 $ log 'sort(0 + 2 + 1, "")'
2607 0
2617 0
2608 2
2618 2
2609 1
2619 1
2610
2620
2611 test invalid sort keys
2621 test invalid sort keys
2612
2622
2613 $ log 'sort(all(), -invalid)'
2623 $ log 'sort(all(), -invalid)'
2614 hg: parse error: unknown sort key '-invalid'
2624 hg: parse error: unknown sort key '-invalid'
2615 [255]
2625 [255]
2616
2626
2617 $ cd ..
2627 $ cd ..
2618
2628
2619 test sorting by multiple keys including variable-length strings
2629 test sorting by multiple keys including variable-length strings
2620
2630
2621 $ hg init sorting
2631 $ hg init sorting
2622 $ cd sorting
2632 $ cd sorting
2623 $ cat <<EOF >> .hg/hgrc
2633 $ cat <<EOF >> .hg/hgrc
2624 > [ui]
2634 > [ui]
2625 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2635 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2626 > [templatealias]
2636 > [templatealias]
2627 > p5(s) = pad(s, 5)
2637 > p5(s) = pad(s, 5)
2628 > EOF
2638 > EOF
2629 $ hg branch -qf b12
2639 $ hg branch -qf b12
2630 $ hg ci -m m111 -u u112 -d '111 10800'
2640 $ hg ci -m m111 -u u112 -d '111 10800'
2631 $ hg branch -qf b11
2641 $ hg branch -qf b11
2632 $ hg ci -m m12 -u u111 -d '112 7200'
2642 $ hg ci -m m12 -u u111 -d '112 7200'
2633 $ hg branch -qf b111
2643 $ hg branch -qf b111
2634 $ hg ci -m m11 -u u12 -d '111 3600'
2644 $ hg ci -m m11 -u u12 -d '111 3600'
2635 $ hg branch -qf b112
2645 $ hg branch -qf b112
2636 $ hg ci -m m111 -u u11 -d '120 0'
2646 $ hg ci -m m111 -u u11 -d '120 0'
2637 $ hg branch -qf b111
2647 $ hg branch -qf b111
2638 $ hg ci -m m112 -u u111 -d '110 14400'
2648 $ hg ci -m m112 -u u111 -d '110 14400'
2639 created new head
2649 created new head
2640
2650
2641 compare revisions (has fast path):
2651 compare revisions (has fast path):
2642
2652
2643 $ hg log -r 'sort(all(), rev)'
2653 $ hg log -r 'sort(all(), rev)'
2644 0 b12 m111 u112 111 10800
2654 0 b12 m111 u112 111 10800
2645 1 b11 m12 u111 112 7200
2655 1 b11 m12 u111 112 7200
2646 2 b111 m11 u12 111 3600
2656 2 b111 m11 u12 111 3600
2647 3 b112 m111 u11 120 0
2657 3 b112 m111 u11 120 0
2648 4 b111 m112 u111 110 14400
2658 4 b111 m112 u111 110 14400
2649
2659
2650 $ hg log -r 'sort(all(), -rev)'
2660 $ hg log -r 'sort(all(), -rev)'
2651 4 b111 m112 u111 110 14400
2661 4 b111 m112 u111 110 14400
2652 3 b112 m111 u11 120 0
2662 3 b112 m111 u11 120 0
2653 2 b111 m11 u12 111 3600
2663 2 b111 m11 u12 111 3600
2654 1 b11 m12 u111 112 7200
2664 1 b11 m12 u111 112 7200
2655 0 b12 m111 u112 111 10800
2665 0 b12 m111 u112 111 10800
2656
2666
2657 compare variable-length strings (issue5218):
2667 compare variable-length strings (issue5218):
2658
2668
2659 $ hg log -r 'sort(all(), branch)'
2669 $ hg log -r 'sort(all(), branch)'
2660 1 b11 m12 u111 112 7200
2670 1 b11 m12 u111 112 7200
2661 2 b111 m11 u12 111 3600
2671 2 b111 m11 u12 111 3600
2662 4 b111 m112 u111 110 14400
2672 4 b111 m112 u111 110 14400
2663 3 b112 m111 u11 120 0
2673 3 b112 m111 u11 120 0
2664 0 b12 m111 u112 111 10800
2674 0 b12 m111 u112 111 10800
2665
2675
2666 $ hg log -r 'sort(all(), -branch)'
2676 $ hg log -r 'sort(all(), -branch)'
2667 0 b12 m111 u112 111 10800
2677 0 b12 m111 u112 111 10800
2668 3 b112 m111 u11 120 0
2678 3 b112 m111 u11 120 0
2669 2 b111 m11 u12 111 3600
2679 2 b111 m11 u12 111 3600
2670 4 b111 m112 u111 110 14400
2680 4 b111 m112 u111 110 14400
2671 1 b11 m12 u111 112 7200
2681 1 b11 m12 u111 112 7200
2672
2682
2673 $ hg log -r 'sort(all(), desc)'
2683 $ hg log -r 'sort(all(), desc)'
2674 2 b111 m11 u12 111 3600
2684 2 b111 m11 u12 111 3600
2675 0 b12 m111 u112 111 10800
2685 0 b12 m111 u112 111 10800
2676 3 b112 m111 u11 120 0
2686 3 b112 m111 u11 120 0
2677 4 b111 m112 u111 110 14400
2687 4 b111 m112 u111 110 14400
2678 1 b11 m12 u111 112 7200
2688 1 b11 m12 u111 112 7200
2679
2689
2680 $ hg log -r 'sort(all(), -desc)'
2690 $ hg log -r 'sort(all(), -desc)'
2681 1 b11 m12 u111 112 7200
2691 1 b11 m12 u111 112 7200
2682 4 b111 m112 u111 110 14400
2692 4 b111 m112 u111 110 14400
2683 0 b12 m111 u112 111 10800
2693 0 b12 m111 u112 111 10800
2684 3 b112 m111 u11 120 0
2694 3 b112 m111 u11 120 0
2685 2 b111 m11 u12 111 3600
2695 2 b111 m11 u12 111 3600
2686
2696
2687 $ hg log -r 'sort(all(), user)'
2697 $ hg log -r 'sort(all(), user)'
2688 3 b112 m111 u11 120 0
2698 3 b112 m111 u11 120 0
2689 1 b11 m12 u111 112 7200
2699 1 b11 m12 u111 112 7200
2690 4 b111 m112 u111 110 14400
2700 4 b111 m112 u111 110 14400
2691 0 b12 m111 u112 111 10800
2701 0 b12 m111 u112 111 10800
2692 2 b111 m11 u12 111 3600
2702 2 b111 m11 u12 111 3600
2693
2703
2694 $ hg log -r 'sort(all(), -user)'
2704 $ hg log -r 'sort(all(), -user)'
2695 2 b111 m11 u12 111 3600
2705 2 b111 m11 u12 111 3600
2696 0 b12 m111 u112 111 10800
2706 0 b12 m111 u112 111 10800
2697 1 b11 m12 u111 112 7200
2707 1 b11 m12 u111 112 7200
2698 4 b111 m112 u111 110 14400
2708 4 b111 m112 u111 110 14400
2699 3 b112 m111 u11 120 0
2709 3 b112 m111 u11 120 0
2700
2710
2701 compare dates (tz offset should have no effect):
2711 compare dates (tz offset should have no effect):
2702
2712
2703 $ hg log -r 'sort(all(), date)'
2713 $ hg log -r 'sort(all(), date)'
2704 4 b111 m112 u111 110 14400
2714 4 b111 m112 u111 110 14400
2705 0 b12 m111 u112 111 10800
2715 0 b12 m111 u112 111 10800
2706 2 b111 m11 u12 111 3600
2716 2 b111 m11 u12 111 3600
2707 1 b11 m12 u111 112 7200
2717 1 b11 m12 u111 112 7200
2708 3 b112 m111 u11 120 0
2718 3 b112 m111 u11 120 0
2709
2719
2710 $ hg log -r 'sort(all(), -date)'
2720 $ hg log -r 'sort(all(), -date)'
2711 3 b112 m111 u11 120 0
2721 3 b112 m111 u11 120 0
2712 1 b11 m12 u111 112 7200
2722 1 b11 m12 u111 112 7200
2713 0 b12 m111 u112 111 10800
2723 0 b12 m111 u112 111 10800
2714 2 b111 m11 u12 111 3600
2724 2 b111 m11 u12 111 3600
2715 4 b111 m112 u111 110 14400
2725 4 b111 m112 u111 110 14400
2716
2726
2717 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2727 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2718 because '-k' reverses the comparison, not the list itself:
2728 because '-k' reverses the comparison, not the list itself:
2719
2729
2720 $ hg log -r 'sort(0 + 2, date)'
2730 $ hg log -r 'sort(0 + 2, date)'
2721 0 b12 m111 u112 111 10800
2731 0 b12 m111 u112 111 10800
2722 2 b111 m11 u12 111 3600
2732 2 b111 m11 u12 111 3600
2723
2733
2724 $ hg log -r 'sort(0 + 2, -date)'
2734 $ hg log -r 'sort(0 + 2, -date)'
2725 0 b12 m111 u112 111 10800
2735 0 b12 m111 u112 111 10800
2726 2 b111 m11 u12 111 3600
2736 2 b111 m11 u12 111 3600
2727
2737
2728 $ hg log -r 'reverse(sort(0 + 2, date))'
2738 $ hg log -r 'reverse(sort(0 + 2, date))'
2729 2 b111 m11 u12 111 3600
2739 2 b111 m11 u12 111 3600
2730 0 b12 m111 u112 111 10800
2740 0 b12 m111 u112 111 10800
2731
2741
2732 sort by multiple keys:
2742 sort by multiple keys:
2733
2743
2734 $ hg log -r 'sort(all(), "branch -rev")'
2744 $ hg log -r 'sort(all(), "branch -rev")'
2735 1 b11 m12 u111 112 7200
2745 1 b11 m12 u111 112 7200
2736 4 b111 m112 u111 110 14400
2746 4 b111 m112 u111 110 14400
2737 2 b111 m11 u12 111 3600
2747 2 b111 m11 u12 111 3600
2738 3 b112 m111 u11 120 0
2748 3 b112 m111 u11 120 0
2739 0 b12 m111 u112 111 10800
2749 0 b12 m111 u112 111 10800
2740
2750
2741 $ hg log -r 'sort(all(), "-desc -date")'
2751 $ hg log -r 'sort(all(), "-desc -date")'
2742 1 b11 m12 u111 112 7200
2752 1 b11 m12 u111 112 7200
2743 4 b111 m112 u111 110 14400
2753 4 b111 m112 u111 110 14400
2744 3 b112 m111 u11 120 0
2754 3 b112 m111 u11 120 0
2745 0 b12 m111 u112 111 10800
2755 0 b12 m111 u112 111 10800
2746 2 b111 m11 u12 111 3600
2756 2 b111 m11 u12 111 3600
2747
2757
2748 $ hg log -r 'sort(all(), "user -branch date rev")'
2758 $ hg log -r 'sort(all(), "user -branch date rev")'
2749 3 b112 m111 u11 120 0
2759 3 b112 m111 u11 120 0
2750 4 b111 m112 u111 110 14400
2760 4 b111 m112 u111 110 14400
2751 1 b11 m12 u111 112 7200
2761 1 b11 m12 u111 112 7200
2752 0 b12 m111 u112 111 10800
2762 0 b12 m111 u112 111 10800
2753 2 b111 m11 u12 111 3600
2763 2 b111 m11 u12 111 3600
2754
2764
2755 toposort prioritises graph branches
2765 toposort prioritises graph branches
2756
2766
2757 $ hg up 2
2767 $ hg up 2
2758 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2768 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2759 $ touch a
2769 $ touch a
2760 $ hg addremove
2770 $ hg addremove
2761 adding a
2771 adding a
2762 $ hg ci -m 't1' -u 'tu' -d '130 0'
2772 $ hg ci -m 't1' -u 'tu' -d '130 0'
2763 created new head
2773 created new head
2764 $ echo 'a' >> a
2774 $ echo 'a' >> a
2765 $ hg ci -m 't2' -u 'tu' -d '130 0'
2775 $ hg ci -m 't2' -u 'tu' -d '130 0'
2766 $ hg book book1
2776 $ hg book book1
2767 $ hg up 4
2777 $ hg up 4
2768 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2778 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2769 (leaving bookmark book1)
2779 (leaving bookmark book1)
2770 $ touch a
2780 $ touch a
2771 $ hg addremove
2781 $ hg addremove
2772 adding a
2782 adding a
2773 $ hg ci -m 't3' -u 'tu' -d '130 0'
2783 $ hg ci -m 't3' -u 'tu' -d '130 0'
2774
2784
2775 $ hg log -r 'sort(all(), topo)'
2785 $ hg log -r 'sort(all(), topo)'
2776 7 b111 t3 tu 130 0
2786 7 b111 t3 tu 130 0
2777 4 b111 m112 u111 110 14400
2787 4 b111 m112 u111 110 14400
2778 3 b112 m111 u11 120 0
2788 3 b112 m111 u11 120 0
2779 6 b111 t2 tu 130 0
2789 6 b111 t2 tu 130 0
2780 5 b111 t1 tu 130 0
2790 5 b111 t1 tu 130 0
2781 2 b111 m11 u12 111 3600
2791 2 b111 m11 u12 111 3600
2782 1 b11 m12 u111 112 7200
2792 1 b11 m12 u111 112 7200
2783 0 b12 m111 u112 111 10800
2793 0 b12 m111 u112 111 10800
2784
2794
2785 $ hg log -r 'sort(all(), -topo)'
2795 $ hg log -r 'sort(all(), -topo)'
2786 0 b12 m111 u112 111 10800
2796 0 b12 m111 u112 111 10800
2787 1 b11 m12 u111 112 7200
2797 1 b11 m12 u111 112 7200
2788 2 b111 m11 u12 111 3600
2798 2 b111 m11 u12 111 3600
2789 5 b111 t1 tu 130 0
2799 5 b111 t1 tu 130 0
2790 6 b111 t2 tu 130 0
2800 6 b111 t2 tu 130 0
2791 3 b112 m111 u11 120 0
2801 3 b112 m111 u11 120 0
2792 4 b111 m112 u111 110 14400
2802 4 b111 m112 u111 110 14400
2793 7 b111 t3 tu 130 0
2803 7 b111 t3 tu 130 0
2794
2804
2795 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2805 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2796 6 b111 t2 tu 130 0
2806 6 b111 t2 tu 130 0
2797 5 b111 t1 tu 130 0
2807 5 b111 t1 tu 130 0
2798 7 b111 t3 tu 130 0
2808 7 b111 t3 tu 130 0
2799 4 b111 m112 u111 110 14400
2809 4 b111 m112 u111 110 14400
2800 3 b112 m111 u11 120 0
2810 3 b112 m111 u11 120 0
2801 2 b111 m11 u12 111 3600
2811 2 b111 m11 u12 111 3600
2802 1 b11 m12 u111 112 7200
2812 1 b11 m12 u111 112 7200
2803 0 b12 m111 u112 111 10800
2813 0 b12 m111 u112 111 10800
2804
2814
2805 topographical sorting can't be combined with other sort keys, and you can't
2815 topographical sorting can't be combined with other sort keys, and you can't
2806 use the topo.firstbranch option when topo sort is not active:
2816 use the topo.firstbranch option when topo sort is not active:
2807
2817
2808 $ hg log -r 'sort(all(), "topo user")'
2818 $ hg log -r 'sort(all(), "topo user")'
2809 hg: parse error: topo sort order cannot be combined with other sort keys
2819 hg: parse error: topo sort order cannot be combined with other sort keys
2810 [255]
2820 [255]
2811
2821
2812 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2822 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2813 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2823 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2814 [255]
2824 [255]
2815
2825
2816 topo.firstbranch should accept any kind of expressions:
2826 topo.firstbranch should accept any kind of expressions:
2817
2827
2818 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2828 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2819 0 b12 m111 u112 111 10800
2829 0 b12 m111 u112 111 10800
2820
2830
2821 $ cd ..
2831 $ cd ..
2822 $ cd repo
2832 $ cd repo
2823
2833
2824 test multiline revset with errors
2834 test multiline revset with errors
2825
2835
2826 $ echo > multiline-revset
2836 $ echo > multiline-revset
2827 $ echo '. +' >> multiline-revset
2837 $ echo '. +' >> multiline-revset
2828 $ echo '.^ +' >> multiline-revset
2838 $ echo '.^ +' >> multiline-revset
2829 $ hg log -r "`cat multiline-revset`"
2839 $ hg log -r "`cat multiline-revset`"
2830 hg: parse error at 9: not a prefix: end
2840 hg: parse error at 9: not a prefix: end
2831 ( . + .^ +
2841 ( . + .^ +
2832 ^ here)
2842 ^ here)
2833 [255]
2843 [255]
2834 $ hg debugrevspec -v 'revset(first(rev(0)))' -p all
2844 $ hg debugrevspec -v 'revset(first(rev(0)))' -p all
2835 * parsed:
2845 * parsed:
2836 (func
2846 (func
2837 (symbol 'revset')
2847 (symbol 'revset')
2838 (func
2848 (func
2839 (symbol 'first')
2849 (symbol 'first')
2840 (func
2850 (func
2841 (symbol 'rev')
2851 (symbol 'rev')
2842 (symbol '0'))))
2852 (symbol '0'))))
2843 * expanded:
2853 * expanded:
2844 (func
2854 (func
2845 (symbol 'revset')
2855 (symbol 'revset')
2846 (func
2856 (func
2847 (symbol 'first')
2857 (symbol 'first')
2848 (func
2858 (func
2849 (symbol 'rev')
2859 (symbol 'rev')
2850 (symbol '0'))))
2860 (symbol '0'))))
2851 * concatenated:
2861 * concatenated:
2852 (func
2862 (func
2853 (symbol 'revset')
2863 (symbol 'revset')
2854 (func
2864 (func
2855 (symbol 'first')
2865 (symbol 'first')
2856 (func
2866 (func
2857 (symbol 'rev')
2867 (symbol 'rev')
2858 (symbol '0'))))
2868 (symbol '0'))))
2859 * analyzed:
2869 * analyzed:
2860 (func
2870 (func
2861 (symbol 'first')
2871 (symbol 'first')
2862 (func
2872 (func
2863 (symbol 'rev')
2873 (symbol 'rev')
2864 (symbol '0')))
2874 (symbol '0')))
2865 * optimized:
2875 * optimized:
2866 (func
2876 (func
2867 (symbol 'first')
2877 (symbol 'first')
2868 (func
2878 (func
2869 (symbol 'rev')
2879 (symbol 'rev')
2870 (symbol '0')))
2880 (symbol '0')))
2871 * set:
2881 * set:
2872 <baseset+ [0]>
2882 <baseset+ [0]>
2873 0
2883 0
General Comments 0
You need to be logged in to leave comments. Login now