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