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