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