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