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