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