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