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