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