##// END OF EJS Templates
revsets: define a none() revset...
Martin von Zweigbergk -
r38294:f1d55ae2 default
parent child Browse files
Show More
@@ -1,2239 +1,2247 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 dagop,
14 dagop,
15 destutil,
15 destutil,
16 encoding,
16 encoding,
17 error,
17 error,
18 hbisect,
18 hbisect,
19 match as matchmod,
19 match as matchmod,
20 node,
20 node,
21 obsolete as obsmod,
21 obsolete as obsmod,
22 obsutil,
22 obsutil,
23 pathutil,
23 pathutil,
24 phases,
24 phases,
25 pycompat,
25 pycompat,
26 registrar,
26 registrar,
27 repoview,
27 repoview,
28 revsetlang,
28 revsetlang,
29 scmutil,
29 scmutil,
30 smartset,
30 smartset,
31 stack as stackmod,
31 stack as stackmod,
32 util,
32 util,
33 )
33 )
34 from .utils import (
34 from .utils import (
35 dateutil,
35 dateutil,
36 stringutil,
36 stringutil,
37 )
37 )
38
38
39 # helpers for processing parsed tree
39 # helpers for processing parsed tree
40 getsymbol = revsetlang.getsymbol
40 getsymbol = revsetlang.getsymbol
41 getstring = revsetlang.getstring
41 getstring = revsetlang.getstring
42 getinteger = revsetlang.getinteger
42 getinteger = revsetlang.getinteger
43 getboolean = revsetlang.getboolean
43 getboolean = revsetlang.getboolean
44 getlist = revsetlang.getlist
44 getlist = revsetlang.getlist
45 getrange = revsetlang.getrange
45 getrange = revsetlang.getrange
46 getargs = revsetlang.getargs
46 getargs = revsetlang.getargs
47 getargsdict = revsetlang.getargsdict
47 getargsdict = revsetlang.getargsdict
48
48
49 baseset = smartset.baseset
49 baseset = smartset.baseset
50 generatorset = smartset.generatorset
50 generatorset = smartset.generatorset
51 spanset = smartset.spanset
51 spanset = smartset.spanset
52 fullreposet = smartset.fullreposet
52 fullreposet = smartset.fullreposet
53
53
54 # Constants for ordering requirement, used in getset():
54 # Constants for ordering requirement, used in getset():
55 #
55 #
56 # If 'define', any nested functions and operations MAY change the ordering of
56 # If 'define', any nested functions and operations MAY change the ordering of
57 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
57 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
58 # it). If 'follow', any nested functions and operations MUST take the ordering
58 # it). If 'follow', any nested functions and operations MUST take the ordering
59 # specified by the first operand to the '&' operator.
59 # specified by the first operand to the '&' operator.
60 #
60 #
61 # For instance,
61 # For instance,
62 #
62 #
63 # X & (Y | Z)
63 # X & (Y | Z)
64 # ^ ^^^^^^^
64 # ^ ^^^^^^^
65 # | follow
65 # | follow
66 # define
66 # define
67 #
67 #
68 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
68 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
69 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
69 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
70 #
70 #
71 # 'any' means the order doesn't matter. For instance,
71 # 'any' means the order doesn't matter. For instance,
72 #
72 #
73 # (X & !Y) | ancestors(Z)
73 # (X & !Y) | ancestors(Z)
74 # ^ ^
74 # ^ ^
75 # any any
75 # any any
76 #
76 #
77 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
77 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
78 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
78 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
79 # since 'ancestors' does not care about the order of its argument.
79 # since 'ancestors' does not care about the order of its argument.
80 #
80 #
81 # Currently, most revsets do not care about the order, so 'define' is
81 # Currently, most revsets do not care about the order, so 'define' is
82 # equivalent to 'follow' for them, and the resulting order is based on the
82 # equivalent to 'follow' for them, and the resulting order is based on the
83 # 'subset' parameter passed down to them:
83 # 'subset' parameter passed down to them:
84 #
84 #
85 # m = revset.match(...)
85 # m = revset.match(...)
86 # m(repo, subset, order=defineorder)
86 # m(repo, subset, order=defineorder)
87 # ^^^^^^
87 # ^^^^^^
88 # For most revsets, 'define' means using the order this subset provides
88 # For most revsets, 'define' means using the order this subset provides
89 #
89 #
90 # There are a few revsets that always redefine the order if 'define' is
90 # There are a few revsets that always redefine the order if 'define' is
91 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
91 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
92 anyorder = 'any' # don't care the order, could be even random-shuffled
92 anyorder = 'any' # don't care the order, could be even random-shuffled
93 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
93 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
94 followorder = 'follow' # MUST follow the current order
94 followorder = 'follow' # MUST follow the current order
95
95
96 # helpers
96 # helpers
97
97
98 def getset(repo, subset, x, order=defineorder):
98 def getset(repo, subset, x, order=defineorder):
99 if not x:
99 if not x:
100 raise error.ParseError(_("missing argument"))
100 raise error.ParseError(_("missing argument"))
101 return methods[x[0]](repo, subset, *x[1:], order=order)
101 return methods[x[0]](repo, subset, *x[1:], order=order)
102
102
103 def _getrevsource(repo, r):
103 def _getrevsource(repo, r):
104 extra = repo[r].extra()
104 extra = repo[r].extra()
105 for label in ('source', 'transplant_source', 'rebase_source'):
105 for label in ('source', 'transplant_source', 'rebase_source'):
106 if label in extra:
106 if label in extra:
107 try:
107 try:
108 return repo[extra[label]].rev()
108 return repo[extra[label]].rev()
109 except error.RepoLookupError:
109 except error.RepoLookupError:
110 pass
110 pass
111 return None
111 return None
112
112
113 def _sortedb(xs):
113 def _sortedb(xs):
114 return sorted(util.rapply(pycompat.maybebytestr, xs))
114 return sorted(util.rapply(pycompat.maybebytestr, xs))
115
115
116 # operator methods
116 # operator methods
117
117
118 def stringset(repo, subset, x, order):
118 def stringset(repo, subset, x, order):
119 if not x:
119 if not x:
120 raise error.ParseError(_("empty string is not a valid revision"))
120 raise error.ParseError(_("empty string is not a valid revision"))
121 x = scmutil.intrev(scmutil.revsymbol(repo, x))
121 x = scmutil.intrev(scmutil.revsymbol(repo, x))
122 if (x in subset
122 if (x in subset
123 or x == node.nullrev and isinstance(subset, fullreposet)):
123 or x == node.nullrev and isinstance(subset, fullreposet)):
124 return baseset([x])
124 return baseset([x])
125 return baseset()
125 return baseset()
126
126
127 def rangeset(repo, subset, x, y, order):
127 def rangeset(repo, subset, x, y, order):
128 m = getset(repo, fullreposet(repo), x)
128 m = getset(repo, fullreposet(repo), x)
129 n = getset(repo, fullreposet(repo), y)
129 n = getset(repo, fullreposet(repo), y)
130
130
131 if not m or not n:
131 if not m or not n:
132 return baseset()
132 return baseset()
133 return _makerangeset(repo, subset, m.first(), n.last(), order)
133 return _makerangeset(repo, subset, m.first(), n.last(), order)
134
134
135 def rangeall(repo, subset, x, order):
135 def rangeall(repo, subset, x, order):
136 assert x is None
136 assert x is None
137 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order)
137 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order)
138
138
139 def rangepre(repo, subset, y, order):
139 def rangepre(repo, subset, y, order):
140 # ':y' can't be rewritten to '0:y' since '0' may be hidden
140 # ':y' can't be rewritten to '0:y' since '0' may be hidden
141 n = getset(repo, fullreposet(repo), y)
141 n = getset(repo, fullreposet(repo), y)
142 if not n:
142 if not n:
143 return baseset()
143 return baseset()
144 return _makerangeset(repo, subset, 0, n.last(), order)
144 return _makerangeset(repo, subset, 0, n.last(), order)
145
145
146 def rangepost(repo, subset, x, order):
146 def rangepost(repo, subset, x, order):
147 m = getset(repo, fullreposet(repo), x)
147 m = getset(repo, fullreposet(repo), x)
148 if not m:
148 if not m:
149 return baseset()
149 return baseset()
150 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(),
150 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(),
151 order)
151 order)
152
152
153 def _makerangeset(repo, subset, m, n, order):
153 def _makerangeset(repo, subset, m, n, order):
154 if m == n:
154 if m == n:
155 r = baseset([m])
155 r = baseset([m])
156 elif n == node.wdirrev:
156 elif n == node.wdirrev:
157 r = spanset(repo, m, len(repo)) + baseset([n])
157 r = spanset(repo, m, len(repo)) + baseset([n])
158 elif m == node.wdirrev:
158 elif m == node.wdirrev:
159 r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
159 r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
160 elif m < n:
160 elif m < n:
161 r = spanset(repo, m, n + 1)
161 r = spanset(repo, m, n + 1)
162 else:
162 else:
163 r = spanset(repo, m, n - 1)
163 r = spanset(repo, m, n - 1)
164
164
165 if order == defineorder:
165 if order == defineorder:
166 return r & subset
166 return r & subset
167 else:
167 else:
168 # carrying the sorting over when possible would be more efficient
168 # carrying the sorting over when possible would be more efficient
169 return subset & r
169 return subset & r
170
170
171 def dagrange(repo, subset, x, y, order):
171 def dagrange(repo, subset, x, y, order):
172 r = fullreposet(repo)
172 r = fullreposet(repo)
173 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
173 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
174 includepath=True)
174 includepath=True)
175 return subset & xs
175 return subset & xs
176
176
177 def andset(repo, subset, x, y, order):
177 def andset(repo, subset, x, y, order):
178 if order == anyorder:
178 if order == anyorder:
179 yorder = anyorder
179 yorder = anyorder
180 else:
180 else:
181 yorder = followorder
181 yorder = followorder
182 return getset(repo, getset(repo, subset, x, order), y, yorder)
182 return getset(repo, getset(repo, subset, x, order), y, yorder)
183
183
184 def andsmallyset(repo, subset, x, y, order):
184 def andsmallyset(repo, subset, x, y, order):
185 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
185 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
186 if order == anyorder:
186 if order == anyorder:
187 yorder = anyorder
187 yorder = anyorder
188 else:
188 else:
189 yorder = followorder
189 yorder = followorder
190 return getset(repo, getset(repo, subset, y, yorder), x, order)
190 return getset(repo, getset(repo, subset, y, yorder), x, order)
191
191
192 def differenceset(repo, subset, x, y, order):
192 def differenceset(repo, subset, x, y, order):
193 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
193 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
194
194
195 def _orsetlist(repo, subset, xs, order):
195 def _orsetlist(repo, subset, xs, order):
196 assert xs
196 assert xs
197 if len(xs) == 1:
197 if len(xs) == 1:
198 return getset(repo, subset, xs[0], order)
198 return getset(repo, subset, xs[0], order)
199 p = len(xs) // 2
199 p = len(xs) // 2
200 a = _orsetlist(repo, subset, xs[:p], order)
200 a = _orsetlist(repo, subset, xs[:p], order)
201 b = _orsetlist(repo, subset, xs[p:], order)
201 b = _orsetlist(repo, subset, xs[p:], order)
202 return a + b
202 return a + b
203
203
204 def orset(repo, subset, x, order):
204 def orset(repo, subset, x, order):
205 xs = getlist(x)
205 xs = getlist(x)
206 if order == followorder:
206 if order == followorder:
207 # slow path to take the subset order
207 # slow path to take the subset order
208 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
208 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
209 else:
209 else:
210 return _orsetlist(repo, subset, xs, order)
210 return _orsetlist(repo, subset, xs, order)
211
211
212 def notset(repo, subset, x, order):
212 def notset(repo, subset, x, order):
213 return subset - getset(repo, subset, x, anyorder)
213 return subset - getset(repo, subset, x, anyorder)
214
214
215 def relationset(repo, subset, x, y, order):
215 def relationset(repo, subset, x, y, order):
216 raise error.ParseError(_("can't use a relation in this context"))
216 raise error.ParseError(_("can't use a relation in this context"))
217
217
218 def relsubscriptset(repo, subset, x, y, z, order):
218 def relsubscriptset(repo, subset, x, y, z, order):
219 # this is pretty basic implementation of 'x#y[z]' operator, still
219 # this is pretty basic implementation of 'x#y[z]' operator, still
220 # experimental so undocumented. see the wiki for further ideas.
220 # experimental so undocumented. see the wiki for further ideas.
221 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
221 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
222 rel = getsymbol(y)
222 rel = getsymbol(y)
223 n = getinteger(z, _("relation subscript must be an integer"))
223 n = getinteger(z, _("relation subscript must be an integer"))
224
224
225 # TODO: perhaps this should be a table of relation functions
225 # TODO: perhaps this should be a table of relation functions
226 if rel in ('g', 'generations'):
226 if rel in ('g', 'generations'):
227 # TODO: support range, rewrite tests, and drop startdepth argument
227 # TODO: support range, rewrite tests, and drop startdepth argument
228 # from ancestors() and descendants() predicates
228 # from ancestors() and descendants() predicates
229 if n <= 0:
229 if n <= 0:
230 n = -n
230 n = -n
231 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
231 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
232 else:
232 else:
233 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
233 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
234
234
235 raise error.UnknownIdentifier(rel, ['generations'])
235 raise error.UnknownIdentifier(rel, ['generations'])
236
236
237 def subscriptset(repo, subset, x, y, order):
237 def subscriptset(repo, subset, x, y, order):
238 raise error.ParseError(_("can't use a subscript in this context"))
238 raise error.ParseError(_("can't use a subscript in this context"))
239
239
240 def listset(repo, subset, *xs, **opts):
240 def listset(repo, subset, *xs, **opts):
241 raise error.ParseError(_("can't use a list in this context"),
241 raise error.ParseError(_("can't use a list in this context"),
242 hint=_('see hg help "revsets.x or y"'))
242 hint=_('see hg help "revsets.x or y"'))
243
243
244 def keyvaluepair(repo, subset, k, v, order):
244 def keyvaluepair(repo, subset, k, v, order):
245 raise error.ParseError(_("can't use a key-value pair in this context"))
245 raise error.ParseError(_("can't use a key-value pair in this context"))
246
246
247 def func(repo, subset, a, b, order):
247 def func(repo, subset, a, b, order):
248 f = getsymbol(a)
248 f = getsymbol(a)
249 if f in symbols:
249 if f in symbols:
250 func = symbols[f]
250 func = symbols[f]
251 if getattr(func, '_takeorder', False):
251 if getattr(func, '_takeorder', False):
252 return func(repo, subset, b, order)
252 return func(repo, subset, b, order)
253 return func(repo, subset, b)
253 return func(repo, subset, b)
254
254
255 keep = lambda fn: getattr(fn, '__doc__', None) is not None
255 keep = lambda fn: getattr(fn, '__doc__', None) is not None
256
256
257 syms = [s for (s, fn) in symbols.items() if keep(fn)]
257 syms = [s for (s, fn) in symbols.items() if keep(fn)]
258 raise error.UnknownIdentifier(f, syms)
258 raise error.UnknownIdentifier(f, syms)
259
259
260 # functions
260 # functions
261
261
262 # symbols are callables like:
262 # symbols are callables like:
263 # fn(repo, subset, x)
263 # fn(repo, subset, x)
264 # with:
264 # with:
265 # repo - current repository instance
265 # repo - current repository instance
266 # subset - of revisions to be examined
266 # subset - of revisions to be examined
267 # x - argument in tree form
267 # x - argument in tree form
268 symbols = revsetlang.symbols
268 symbols = revsetlang.symbols
269
269
270 # symbols which can't be used for a DoS attack for any given input
270 # symbols which can't be used for a DoS attack for any given input
271 # (e.g. those which accept regexes as plain strings shouldn't be included)
271 # (e.g. those which accept regexes as plain strings shouldn't be included)
272 # functions that just return a lot of changesets (like all) don't count here
272 # functions that just return a lot of changesets (like all) don't count here
273 safesymbols = set()
273 safesymbols = set()
274
274
275 predicate = registrar.revsetpredicate()
275 predicate = registrar.revsetpredicate()
276
276
277 @predicate('_destupdate')
277 @predicate('_destupdate')
278 def _destupdate(repo, subset, x):
278 def _destupdate(repo, subset, x):
279 # experimental revset for update destination
279 # experimental revset for update destination
280 args = getargsdict(x, 'limit', 'clean')
280 args = getargsdict(x, 'limit', 'clean')
281 return subset & baseset([destutil.destupdate(repo,
281 return subset & baseset([destutil.destupdate(repo,
282 **pycompat.strkwargs(args))[0]])
282 **pycompat.strkwargs(args))[0]])
283
283
284 @predicate('_destmerge')
284 @predicate('_destmerge')
285 def _destmerge(repo, subset, x):
285 def _destmerge(repo, subset, x):
286 # experimental revset for merge destination
286 # experimental revset for merge destination
287 sourceset = None
287 sourceset = None
288 if x is not None:
288 if x is not None:
289 sourceset = getset(repo, fullreposet(repo), x)
289 sourceset = getset(repo, fullreposet(repo), x)
290 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
290 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
291
291
292 @predicate('adds(pattern)', safe=True, weight=30)
292 @predicate('adds(pattern)', safe=True, weight=30)
293 def adds(repo, subset, x):
293 def adds(repo, subset, x):
294 """Changesets that add a file matching pattern.
294 """Changesets that add a file matching pattern.
295
295
296 The pattern without explicit kind like ``glob:`` is expected to be
296 The pattern without explicit kind like ``glob:`` is expected to be
297 relative to the current directory and match against a file or a
297 relative to the current directory and match against a file or a
298 directory.
298 directory.
299 """
299 """
300 # i18n: "adds" is a keyword
300 # i18n: "adds" is a keyword
301 pat = getstring(x, _("adds requires a pattern"))
301 pat = getstring(x, _("adds requires a pattern"))
302 return checkstatus(repo, subset, pat, 1)
302 return checkstatus(repo, subset, pat, 1)
303
303
304 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
304 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
305 def ancestor(repo, subset, x):
305 def ancestor(repo, subset, x):
306 """A greatest common ancestor of the changesets.
306 """A greatest common ancestor of the changesets.
307
307
308 Accepts 0 or more changesets.
308 Accepts 0 or more changesets.
309 Will return empty list when passed no args.
309 Will return empty list when passed no args.
310 Greatest common ancestor of a single changeset is that changeset.
310 Greatest common ancestor of a single changeset is that changeset.
311 """
311 """
312 # i18n: "ancestor" is a keyword
312 # i18n: "ancestor" is a keyword
313 l = getlist(x)
313 l = getlist(x)
314 rl = fullreposet(repo)
314 rl = fullreposet(repo)
315 anc = None
315 anc = None
316
316
317 # (getset(repo, rl, i) for i in l) generates a list of lists
317 # (getset(repo, rl, i) for i in l) generates a list of lists
318 for revs in (getset(repo, rl, i) for i in l):
318 for revs in (getset(repo, rl, i) for i in l):
319 for r in revs:
319 for r in revs:
320 if anc is None:
320 if anc is None:
321 anc = repo[r]
321 anc = repo[r]
322 else:
322 else:
323 anc = anc.ancestor(repo[r])
323 anc = anc.ancestor(repo[r])
324
324
325 if anc is not None and anc.rev() in subset:
325 if anc is not None and anc.rev() in subset:
326 return baseset([anc.rev()])
326 return baseset([anc.rev()])
327 return baseset()
327 return baseset()
328
328
329 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
329 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
330 stopdepth=None):
330 stopdepth=None):
331 heads = getset(repo, fullreposet(repo), x)
331 heads = getset(repo, fullreposet(repo), x)
332 if not heads:
332 if not heads:
333 return baseset()
333 return baseset()
334 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
334 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
335 return subset & s
335 return subset & s
336
336
337 @predicate('ancestors(set[, depth])', safe=True)
337 @predicate('ancestors(set[, depth])', safe=True)
338 def ancestors(repo, subset, x):
338 def ancestors(repo, subset, x):
339 """Changesets that are ancestors of changesets in set, including the
339 """Changesets that are ancestors of changesets in set, including the
340 given changesets themselves.
340 given changesets themselves.
341
341
342 If depth is specified, the result only includes changesets up to
342 If depth is specified, the result only includes changesets up to
343 the specified generation.
343 the specified generation.
344 """
344 """
345 # startdepth is for internal use only until we can decide the UI
345 # startdepth is for internal use only until we can decide the UI
346 args = getargsdict(x, 'ancestors', 'set depth startdepth')
346 args = getargsdict(x, 'ancestors', 'set depth startdepth')
347 if 'set' not in args:
347 if 'set' not in args:
348 # i18n: "ancestors" is a keyword
348 # i18n: "ancestors" is a keyword
349 raise error.ParseError(_('ancestors takes at least 1 argument'))
349 raise error.ParseError(_('ancestors takes at least 1 argument'))
350 startdepth = stopdepth = None
350 startdepth = stopdepth = None
351 if 'startdepth' in args:
351 if 'startdepth' in args:
352 n = getinteger(args['startdepth'],
352 n = getinteger(args['startdepth'],
353 "ancestors expects an integer startdepth")
353 "ancestors expects an integer startdepth")
354 if n < 0:
354 if n < 0:
355 raise error.ParseError("negative startdepth")
355 raise error.ParseError("negative startdepth")
356 startdepth = n
356 startdepth = n
357 if 'depth' in args:
357 if 'depth' in args:
358 # i18n: "ancestors" is a keyword
358 # i18n: "ancestors" is a keyword
359 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
359 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
360 if n < 0:
360 if n < 0:
361 raise error.ParseError(_("negative depth"))
361 raise error.ParseError(_("negative depth"))
362 stopdepth = n + 1
362 stopdepth = n + 1
363 return _ancestors(repo, subset, args['set'],
363 return _ancestors(repo, subset, args['set'],
364 startdepth=startdepth, stopdepth=stopdepth)
364 startdepth=startdepth, stopdepth=stopdepth)
365
365
366 @predicate('_firstancestors', safe=True)
366 @predicate('_firstancestors', safe=True)
367 def _firstancestors(repo, subset, x):
367 def _firstancestors(repo, subset, x):
368 # ``_firstancestors(set)``
368 # ``_firstancestors(set)``
369 # Like ``ancestors(set)`` but follows only the first parents.
369 # Like ``ancestors(set)`` but follows only the first parents.
370 return _ancestors(repo, subset, x, followfirst=True)
370 return _ancestors(repo, subset, x, followfirst=True)
371
371
372 def _childrenspec(repo, subset, x, n, order):
372 def _childrenspec(repo, subset, x, n, order):
373 """Changesets that are the Nth child of a changeset
373 """Changesets that are the Nth child of a changeset
374 in set.
374 in set.
375 """
375 """
376 cs = set()
376 cs = set()
377 for r in getset(repo, fullreposet(repo), x):
377 for r in getset(repo, fullreposet(repo), x):
378 for i in range(n):
378 for i in range(n):
379 c = repo[r].children()
379 c = repo[r].children()
380 if len(c) == 0:
380 if len(c) == 0:
381 break
381 break
382 if len(c) > 1:
382 if len(c) > 1:
383 raise error.RepoLookupError(
383 raise error.RepoLookupError(
384 _("revision in set has more than one child"))
384 _("revision in set has more than one child"))
385 r = c[0].rev()
385 r = c[0].rev()
386 else:
386 else:
387 cs.add(r)
387 cs.add(r)
388 return subset & cs
388 return subset & cs
389
389
390 def ancestorspec(repo, subset, x, n, order):
390 def ancestorspec(repo, subset, x, n, order):
391 """``set~n``
391 """``set~n``
392 Changesets that are the Nth ancestor (first parents only) of a changeset
392 Changesets that are the Nth ancestor (first parents only) of a changeset
393 in set.
393 in set.
394 """
394 """
395 n = getinteger(n, _("~ expects a number"))
395 n = getinteger(n, _("~ expects a number"))
396 if n < 0:
396 if n < 0:
397 # children lookup
397 # children lookup
398 return _childrenspec(repo, subset, x, -n, order)
398 return _childrenspec(repo, subset, x, -n, order)
399 ps = set()
399 ps = set()
400 cl = repo.changelog
400 cl = repo.changelog
401 for r in getset(repo, fullreposet(repo), x):
401 for r in getset(repo, fullreposet(repo), x):
402 for i in range(n):
402 for i in range(n):
403 try:
403 try:
404 r = cl.parentrevs(r)[0]
404 r = cl.parentrevs(r)[0]
405 except error.WdirUnsupported:
405 except error.WdirUnsupported:
406 r = repo[r].parents()[0].rev()
406 r = repo[r].parents()[0].rev()
407 ps.add(r)
407 ps.add(r)
408 return subset & ps
408 return subset & ps
409
409
410 @predicate('author(string)', safe=True, weight=10)
410 @predicate('author(string)', safe=True, weight=10)
411 def author(repo, subset, x):
411 def author(repo, subset, x):
412 """Alias for ``user(string)``.
412 """Alias for ``user(string)``.
413 """
413 """
414 # i18n: "author" is a keyword
414 # i18n: "author" is a keyword
415 n = getstring(x, _("author requires a string"))
415 n = getstring(x, _("author requires a string"))
416 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
416 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
417 return subset.filter(lambda x: matcher(repo[x].user()),
417 return subset.filter(lambda x: matcher(repo[x].user()),
418 condrepr=('<user %r>', n))
418 condrepr=('<user %r>', n))
419
419
420 @predicate('bisect(string)', safe=True)
420 @predicate('bisect(string)', safe=True)
421 def bisect(repo, subset, x):
421 def bisect(repo, subset, x):
422 """Changesets marked in the specified bisect status:
422 """Changesets marked in the specified bisect status:
423
423
424 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
424 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
425 - ``goods``, ``bads`` : csets topologically good/bad
425 - ``goods``, ``bads`` : csets topologically good/bad
426 - ``range`` : csets taking part in the bisection
426 - ``range`` : csets taking part in the bisection
427 - ``pruned`` : csets that are goods, bads or skipped
427 - ``pruned`` : csets that are goods, bads or skipped
428 - ``untested`` : csets whose fate is yet unknown
428 - ``untested`` : csets whose fate is yet unknown
429 - ``ignored`` : csets ignored due to DAG topology
429 - ``ignored`` : csets ignored due to DAG topology
430 - ``current`` : the cset currently being bisected
430 - ``current`` : the cset currently being bisected
431 """
431 """
432 # i18n: "bisect" is a keyword
432 # i18n: "bisect" is a keyword
433 status = getstring(x, _("bisect requires a string")).lower()
433 status = getstring(x, _("bisect requires a string")).lower()
434 state = set(hbisect.get(repo, status))
434 state = set(hbisect.get(repo, status))
435 return subset & state
435 return subset & state
436
436
437 # Backward-compatibility
437 # Backward-compatibility
438 # - no help entry so that we do not advertise it any more
438 # - no help entry so that we do not advertise it any more
439 @predicate('bisected', safe=True)
439 @predicate('bisected', safe=True)
440 def bisected(repo, subset, x):
440 def bisected(repo, subset, x):
441 return bisect(repo, subset, x)
441 return bisect(repo, subset, x)
442
442
443 @predicate('bookmark([name])', safe=True)
443 @predicate('bookmark([name])', safe=True)
444 def bookmark(repo, subset, x):
444 def bookmark(repo, subset, x):
445 """The named bookmark or all bookmarks.
445 """The named bookmark or all bookmarks.
446
446
447 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
447 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
448 """
448 """
449 # i18n: "bookmark" is a keyword
449 # i18n: "bookmark" is a keyword
450 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
450 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
451 if args:
451 if args:
452 bm = getstring(args[0],
452 bm = getstring(args[0],
453 # i18n: "bookmark" is a keyword
453 # i18n: "bookmark" is a keyword
454 _('the argument to bookmark must be a string'))
454 _('the argument to bookmark must be a string'))
455 kind, pattern, matcher = stringutil.stringmatcher(bm)
455 kind, pattern, matcher = stringutil.stringmatcher(bm)
456 bms = set()
456 bms = set()
457 if kind == 'literal':
457 if kind == 'literal':
458 bmrev = repo._bookmarks.get(pattern, None)
458 bmrev = repo._bookmarks.get(pattern, None)
459 if not bmrev:
459 if not bmrev:
460 raise error.RepoLookupError(_("bookmark '%s' does not exist")
460 raise error.RepoLookupError(_("bookmark '%s' does not exist")
461 % pattern)
461 % pattern)
462 bms.add(repo[bmrev].rev())
462 bms.add(repo[bmrev].rev())
463 else:
463 else:
464 matchrevs = set()
464 matchrevs = set()
465 for name, bmrev in repo._bookmarks.iteritems():
465 for name, bmrev in repo._bookmarks.iteritems():
466 if matcher(name):
466 if matcher(name):
467 matchrevs.add(bmrev)
467 matchrevs.add(bmrev)
468 if not matchrevs:
468 if not matchrevs:
469 raise error.RepoLookupError(_("no bookmarks exist"
469 raise error.RepoLookupError(_("no bookmarks exist"
470 " that match '%s'") % pattern)
470 " that match '%s'") % pattern)
471 for bmrev in matchrevs:
471 for bmrev in matchrevs:
472 bms.add(repo[bmrev].rev())
472 bms.add(repo[bmrev].rev())
473 else:
473 else:
474 bms = {repo[r].rev() for r in repo._bookmarks.values()}
474 bms = {repo[r].rev() for r in repo._bookmarks.values()}
475 bms -= {node.nullrev}
475 bms -= {node.nullrev}
476 return subset & bms
476 return subset & bms
477
477
478 @predicate('branch(string or set)', safe=True, weight=10)
478 @predicate('branch(string or set)', safe=True, weight=10)
479 def branch(repo, subset, x):
479 def branch(repo, subset, x):
480 """
480 """
481 All changesets belonging to the given branch or the branches of the given
481 All changesets belonging to the given branch or the branches of the given
482 changesets.
482 changesets.
483
483
484 Pattern matching is supported for `string`. See
484 Pattern matching is supported for `string`. See
485 :hg:`help revisions.patterns`.
485 :hg:`help revisions.patterns`.
486 """
486 """
487 getbi = repo.revbranchcache().branchinfo
487 getbi = repo.revbranchcache().branchinfo
488 def getbranch(r):
488 def getbranch(r):
489 try:
489 try:
490 return getbi(r)[0]
490 return getbi(r)[0]
491 except error.WdirUnsupported:
491 except error.WdirUnsupported:
492 return repo[r].branch()
492 return repo[r].branch()
493
493
494 try:
494 try:
495 b = getstring(x, '')
495 b = getstring(x, '')
496 except error.ParseError:
496 except error.ParseError:
497 # not a string, but another revspec, e.g. tip()
497 # not a string, but another revspec, e.g. tip()
498 pass
498 pass
499 else:
499 else:
500 kind, pattern, matcher = stringutil.stringmatcher(b)
500 kind, pattern, matcher = stringutil.stringmatcher(b)
501 if kind == 'literal':
501 if kind == 'literal':
502 # note: falls through to the revspec case if no branch with
502 # note: falls through to the revspec case if no branch with
503 # this name exists and pattern kind is not specified explicitly
503 # this name exists and pattern kind is not specified explicitly
504 if pattern in repo.branchmap():
504 if pattern in repo.branchmap():
505 return subset.filter(lambda r: matcher(getbranch(r)),
505 return subset.filter(lambda r: matcher(getbranch(r)),
506 condrepr=('<branch %r>', b))
506 condrepr=('<branch %r>', b))
507 if b.startswith('literal:'):
507 if b.startswith('literal:'):
508 raise error.RepoLookupError(_("branch '%s' does not exist")
508 raise error.RepoLookupError(_("branch '%s' does not exist")
509 % pattern)
509 % pattern)
510 else:
510 else:
511 return subset.filter(lambda r: matcher(getbranch(r)),
511 return subset.filter(lambda r: matcher(getbranch(r)),
512 condrepr=('<branch %r>', b))
512 condrepr=('<branch %r>', b))
513
513
514 s = getset(repo, fullreposet(repo), x)
514 s = getset(repo, fullreposet(repo), x)
515 b = set()
515 b = set()
516 for r in s:
516 for r in s:
517 b.add(getbranch(r))
517 b.add(getbranch(r))
518 c = s.__contains__
518 c = s.__contains__
519 return subset.filter(lambda r: c(r) or getbranch(r) in b,
519 return subset.filter(lambda r: c(r) or getbranch(r) in b,
520 condrepr=lambda: '<branch %r>' % _sortedb(b))
520 condrepr=lambda: '<branch %r>' % _sortedb(b))
521
521
522 @predicate('phasedivergent()', safe=True)
522 @predicate('phasedivergent()', safe=True)
523 def phasedivergent(repo, subset, x):
523 def phasedivergent(repo, subset, x):
524 """Mutable changesets marked as successors of public changesets.
524 """Mutable changesets marked as successors of public changesets.
525
525
526 Only non-public and non-obsolete changesets can be `phasedivergent`.
526 Only non-public and non-obsolete changesets can be `phasedivergent`.
527 (EXPERIMENTAL)
527 (EXPERIMENTAL)
528 """
528 """
529 # i18n: "phasedivergent" is a keyword
529 # i18n: "phasedivergent" is a keyword
530 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
530 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
531 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
531 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
532 return subset & phasedivergent
532 return subset & phasedivergent
533
533
534 @predicate('bundle()', safe=True)
534 @predicate('bundle()', safe=True)
535 def bundle(repo, subset, x):
535 def bundle(repo, subset, x):
536 """Changesets in the bundle.
536 """Changesets in the bundle.
537
537
538 Bundle must be specified by the -R option."""
538 Bundle must be specified by the -R option."""
539
539
540 try:
540 try:
541 bundlerevs = repo.changelog.bundlerevs
541 bundlerevs = repo.changelog.bundlerevs
542 except AttributeError:
542 except AttributeError:
543 raise error.Abort(_("no bundle provided - specify with -R"))
543 raise error.Abort(_("no bundle provided - specify with -R"))
544 return subset & bundlerevs
544 return subset & bundlerevs
545
545
546 def checkstatus(repo, subset, pat, field):
546 def checkstatus(repo, subset, pat, field):
547 hasset = matchmod.patkind(pat) == 'set'
547 hasset = matchmod.patkind(pat) == 'set'
548
548
549 mcache = [None]
549 mcache = [None]
550 def matches(x):
550 def matches(x):
551 c = repo[x]
551 c = repo[x]
552 if not mcache[0] or hasset:
552 if not mcache[0] or hasset:
553 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
553 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
554 m = mcache[0]
554 m = mcache[0]
555 fname = None
555 fname = None
556 if not m.anypats() and len(m.files()) == 1:
556 if not m.anypats() and len(m.files()) == 1:
557 fname = m.files()[0]
557 fname = m.files()[0]
558 if fname is not None:
558 if fname is not None:
559 if fname not in c.files():
559 if fname not in c.files():
560 return False
560 return False
561 else:
561 else:
562 for f in c.files():
562 for f in c.files():
563 if m(f):
563 if m(f):
564 break
564 break
565 else:
565 else:
566 return False
566 return False
567 files = repo.status(c.p1().node(), c.node())[field]
567 files = repo.status(c.p1().node(), c.node())[field]
568 if fname is not None:
568 if fname is not None:
569 if fname in files:
569 if fname in files:
570 return True
570 return True
571 else:
571 else:
572 for f in files:
572 for f in files:
573 if m(f):
573 if m(f):
574 return True
574 return True
575
575
576 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
576 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
577
577
578 def _children(repo, subset, parentset):
578 def _children(repo, subset, parentset):
579 if not parentset:
579 if not parentset:
580 return baseset()
580 return baseset()
581 cs = set()
581 cs = set()
582 pr = repo.changelog.parentrevs
582 pr = repo.changelog.parentrevs
583 minrev = parentset.min()
583 minrev = parentset.min()
584 nullrev = node.nullrev
584 nullrev = node.nullrev
585 for r in subset:
585 for r in subset:
586 if r <= minrev:
586 if r <= minrev:
587 continue
587 continue
588 p1, p2 = pr(r)
588 p1, p2 = pr(r)
589 if p1 in parentset:
589 if p1 in parentset:
590 cs.add(r)
590 cs.add(r)
591 if p2 != nullrev and p2 in parentset:
591 if p2 != nullrev and p2 in parentset:
592 cs.add(r)
592 cs.add(r)
593 return baseset(cs)
593 return baseset(cs)
594
594
595 @predicate('children(set)', safe=True)
595 @predicate('children(set)', safe=True)
596 def children(repo, subset, x):
596 def children(repo, subset, x):
597 """Child changesets of changesets in set.
597 """Child changesets of changesets in set.
598 """
598 """
599 s = getset(repo, fullreposet(repo), x)
599 s = getset(repo, fullreposet(repo), x)
600 cs = _children(repo, subset, s)
600 cs = _children(repo, subset, s)
601 return subset & cs
601 return subset & cs
602
602
603 @predicate('closed()', safe=True, weight=10)
603 @predicate('closed()', safe=True, weight=10)
604 def closed(repo, subset, x):
604 def closed(repo, subset, x):
605 """Changeset is closed.
605 """Changeset is closed.
606 """
606 """
607 # i18n: "closed" is a keyword
607 # i18n: "closed" is a keyword
608 getargs(x, 0, 0, _("closed takes no arguments"))
608 getargs(x, 0, 0, _("closed takes no arguments"))
609 return subset.filter(lambda r: repo[r].closesbranch(),
609 return subset.filter(lambda r: repo[r].closesbranch(),
610 condrepr='<branch closed>')
610 condrepr='<branch closed>')
611
611
612 @predicate('contains(pattern)', weight=100)
612 @predicate('contains(pattern)', weight=100)
613 def contains(repo, subset, x):
613 def contains(repo, subset, x):
614 """The revision's manifest contains a file matching pattern (but might not
614 """The revision's manifest contains a file matching pattern (but might not
615 modify it). See :hg:`help patterns` for information about file patterns.
615 modify it). See :hg:`help patterns` for information about file patterns.
616
616
617 The pattern without explicit kind like ``glob:`` is expected to be
617 The pattern without explicit kind like ``glob:`` is expected to be
618 relative to the current directory and match against a file exactly
618 relative to the current directory and match against a file exactly
619 for efficiency.
619 for efficiency.
620 """
620 """
621 # i18n: "contains" is a keyword
621 # i18n: "contains" is a keyword
622 pat = getstring(x, _("contains requires a pattern"))
622 pat = getstring(x, _("contains requires a pattern"))
623
623
624 def matches(x):
624 def matches(x):
625 if not matchmod.patkind(pat):
625 if not matchmod.patkind(pat):
626 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
626 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
627 if pats in repo[x]:
627 if pats in repo[x]:
628 return True
628 return True
629 else:
629 else:
630 c = repo[x]
630 c = repo[x]
631 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
631 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
632 for f in c.manifest():
632 for f in c.manifest():
633 if m(f):
633 if m(f):
634 return True
634 return True
635 return False
635 return False
636
636
637 return subset.filter(matches, condrepr=('<contains %r>', pat))
637 return subset.filter(matches, condrepr=('<contains %r>', pat))
638
638
639 @predicate('converted([id])', safe=True)
639 @predicate('converted([id])', safe=True)
640 def converted(repo, subset, x):
640 def converted(repo, subset, x):
641 """Changesets converted from the given identifier in the old repository if
641 """Changesets converted from the given identifier in the old repository if
642 present, or all converted changesets if no identifier is specified.
642 present, or all converted changesets if no identifier is specified.
643 """
643 """
644
644
645 # There is exactly no chance of resolving the revision, so do a simple
645 # There is exactly no chance of resolving the revision, so do a simple
646 # string compare and hope for the best
646 # string compare and hope for the best
647
647
648 rev = None
648 rev = None
649 # i18n: "converted" is a keyword
649 # i18n: "converted" is a keyword
650 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
650 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
651 if l:
651 if l:
652 # i18n: "converted" is a keyword
652 # i18n: "converted" is a keyword
653 rev = getstring(l[0], _('converted requires a revision'))
653 rev = getstring(l[0], _('converted requires a revision'))
654
654
655 def _matchvalue(r):
655 def _matchvalue(r):
656 source = repo[r].extra().get('convert_revision', None)
656 source = repo[r].extra().get('convert_revision', None)
657 return source is not None and (rev is None or source.startswith(rev))
657 return source is not None and (rev is None or source.startswith(rev))
658
658
659 return subset.filter(lambda r: _matchvalue(r),
659 return subset.filter(lambda r: _matchvalue(r),
660 condrepr=('<converted %r>', rev))
660 condrepr=('<converted %r>', rev))
661
661
662 @predicate('date(interval)', safe=True, weight=10)
662 @predicate('date(interval)', safe=True, weight=10)
663 def date(repo, subset, x):
663 def date(repo, subset, x):
664 """Changesets within the interval, see :hg:`help dates`.
664 """Changesets within the interval, see :hg:`help dates`.
665 """
665 """
666 # i18n: "date" is a keyword
666 # i18n: "date" is a keyword
667 ds = getstring(x, _("date requires a string"))
667 ds = getstring(x, _("date requires a string"))
668 dm = dateutil.matchdate(ds)
668 dm = dateutil.matchdate(ds)
669 return subset.filter(lambda x: dm(repo[x].date()[0]),
669 return subset.filter(lambda x: dm(repo[x].date()[0]),
670 condrepr=('<date %r>', ds))
670 condrepr=('<date %r>', ds))
671
671
672 @predicate('desc(string)', safe=True, weight=10)
672 @predicate('desc(string)', safe=True, weight=10)
673 def desc(repo, subset, x):
673 def desc(repo, subset, x):
674 """Search commit message for string. The match is case-insensitive.
674 """Search commit message for string. The match is case-insensitive.
675
675
676 Pattern matching is supported for `string`. See
676 Pattern matching is supported for `string`. See
677 :hg:`help revisions.patterns`.
677 :hg:`help revisions.patterns`.
678 """
678 """
679 # i18n: "desc" is a keyword
679 # i18n: "desc" is a keyword
680 ds = getstring(x, _("desc requires a string"))
680 ds = getstring(x, _("desc requires a string"))
681
681
682 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
682 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
683
683
684 return subset.filter(lambda r: matcher(repo[r].description()),
684 return subset.filter(lambda r: matcher(repo[r].description()),
685 condrepr=('<desc %r>', ds))
685 condrepr=('<desc %r>', ds))
686
686
687 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
687 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
688 stopdepth=None):
688 stopdepth=None):
689 roots = getset(repo, fullreposet(repo), x)
689 roots = getset(repo, fullreposet(repo), x)
690 if not roots:
690 if not roots:
691 return baseset()
691 return baseset()
692 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
692 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
693 return subset & s
693 return subset & s
694
694
695 @predicate('descendants(set[, depth])', safe=True)
695 @predicate('descendants(set[, depth])', safe=True)
696 def descendants(repo, subset, x):
696 def descendants(repo, subset, x):
697 """Changesets which are descendants of changesets in set, including the
697 """Changesets which are descendants of changesets in set, including the
698 given changesets themselves.
698 given changesets themselves.
699
699
700 If depth is specified, the result only includes changesets up to
700 If depth is specified, the result only includes changesets up to
701 the specified generation.
701 the specified generation.
702 """
702 """
703 # startdepth is for internal use only until we can decide the UI
703 # startdepth is for internal use only until we can decide the UI
704 args = getargsdict(x, 'descendants', 'set depth startdepth')
704 args = getargsdict(x, 'descendants', 'set depth startdepth')
705 if 'set' not in args:
705 if 'set' not in args:
706 # i18n: "descendants" is a keyword
706 # i18n: "descendants" is a keyword
707 raise error.ParseError(_('descendants takes at least 1 argument'))
707 raise error.ParseError(_('descendants takes at least 1 argument'))
708 startdepth = stopdepth = None
708 startdepth = stopdepth = None
709 if 'startdepth' in args:
709 if 'startdepth' in args:
710 n = getinteger(args['startdepth'],
710 n = getinteger(args['startdepth'],
711 "descendants expects an integer startdepth")
711 "descendants expects an integer startdepth")
712 if n < 0:
712 if n < 0:
713 raise error.ParseError("negative startdepth")
713 raise error.ParseError("negative startdepth")
714 startdepth = n
714 startdepth = n
715 if 'depth' in args:
715 if 'depth' in args:
716 # i18n: "descendants" is a keyword
716 # i18n: "descendants" is a keyword
717 n = getinteger(args['depth'], _("descendants expects an integer depth"))
717 n = getinteger(args['depth'], _("descendants expects an integer depth"))
718 if n < 0:
718 if n < 0:
719 raise error.ParseError(_("negative depth"))
719 raise error.ParseError(_("negative depth"))
720 stopdepth = n + 1
720 stopdepth = n + 1
721 return _descendants(repo, subset, args['set'],
721 return _descendants(repo, subset, args['set'],
722 startdepth=startdepth, stopdepth=stopdepth)
722 startdepth=startdepth, stopdepth=stopdepth)
723
723
724 @predicate('_firstdescendants', safe=True)
724 @predicate('_firstdescendants', safe=True)
725 def _firstdescendants(repo, subset, x):
725 def _firstdescendants(repo, subset, x):
726 # ``_firstdescendants(set)``
726 # ``_firstdescendants(set)``
727 # Like ``descendants(set)`` but follows only the first parents.
727 # Like ``descendants(set)`` but follows only the first parents.
728 return _descendants(repo, subset, x, followfirst=True)
728 return _descendants(repo, subset, x, followfirst=True)
729
729
730 @predicate('destination([set])', safe=True, weight=10)
730 @predicate('destination([set])', safe=True, weight=10)
731 def destination(repo, subset, x):
731 def destination(repo, subset, x):
732 """Changesets that were created by a graft, transplant or rebase operation,
732 """Changesets that were created by a graft, transplant or rebase operation,
733 with the given revisions specified as the source. Omitting the optional set
733 with the given revisions specified as the source. Omitting the optional set
734 is the same as passing all().
734 is the same as passing all().
735 """
735 """
736 if x is not None:
736 if x is not None:
737 sources = getset(repo, fullreposet(repo), x)
737 sources = getset(repo, fullreposet(repo), x)
738 else:
738 else:
739 sources = fullreposet(repo)
739 sources = fullreposet(repo)
740
740
741 dests = set()
741 dests = set()
742
742
743 # subset contains all of the possible destinations that can be returned, so
743 # subset contains all of the possible destinations that can be returned, so
744 # iterate over them and see if their source(s) were provided in the arg set.
744 # iterate over them and see if their source(s) were provided in the arg set.
745 # Even if the immediate src of r is not in the arg set, src's source (or
745 # Even if the immediate src of r is not in the arg set, src's source (or
746 # further back) may be. Scanning back further than the immediate src allows
746 # further back) may be. Scanning back further than the immediate src allows
747 # transitive transplants and rebases to yield the same results as transitive
747 # transitive transplants and rebases to yield the same results as transitive
748 # grafts.
748 # grafts.
749 for r in subset:
749 for r in subset:
750 src = _getrevsource(repo, r)
750 src = _getrevsource(repo, r)
751 lineage = None
751 lineage = None
752
752
753 while src is not None:
753 while src is not None:
754 if lineage is None:
754 if lineage is None:
755 lineage = list()
755 lineage = list()
756
756
757 lineage.append(r)
757 lineage.append(r)
758
758
759 # The visited lineage is a match if the current source is in the arg
759 # The visited lineage is a match if the current source is in the arg
760 # set. Since every candidate dest is visited by way of iterating
760 # set. Since every candidate dest is visited by way of iterating
761 # subset, any dests further back in the lineage will be tested by a
761 # subset, any dests further back in the lineage will be tested by a
762 # different iteration over subset. Likewise, if the src was already
762 # different iteration over subset. Likewise, if the src was already
763 # selected, the current lineage can be selected without going back
763 # selected, the current lineage can be selected without going back
764 # further.
764 # further.
765 if src in sources or src in dests:
765 if src in sources or src in dests:
766 dests.update(lineage)
766 dests.update(lineage)
767 break
767 break
768
768
769 r = src
769 r = src
770 src = _getrevsource(repo, r)
770 src = _getrevsource(repo, r)
771
771
772 return subset.filter(dests.__contains__,
772 return subset.filter(dests.__contains__,
773 condrepr=lambda: '<destination %r>' % _sortedb(dests))
773 condrepr=lambda: '<destination %r>' % _sortedb(dests))
774
774
775 @predicate('contentdivergent()', safe=True)
775 @predicate('contentdivergent()', safe=True)
776 def contentdivergent(repo, subset, x):
776 def contentdivergent(repo, subset, x):
777 """
777 """
778 Final successors of changesets with an alternative set of final
778 Final successors of changesets with an alternative set of final
779 successors. (EXPERIMENTAL)
779 successors. (EXPERIMENTAL)
780 """
780 """
781 # i18n: "contentdivergent" is a keyword
781 # i18n: "contentdivergent" is a keyword
782 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
782 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
783 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
783 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
784 return subset & contentdivergent
784 return subset & contentdivergent
785
785
786 @predicate('extdata(source)', safe=False, weight=100)
786 @predicate('extdata(source)', safe=False, weight=100)
787 def extdata(repo, subset, x):
787 def extdata(repo, subset, x):
788 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
788 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
789 # i18n: "extdata" is a keyword
789 # i18n: "extdata" is a keyword
790 args = getargsdict(x, 'extdata', 'source')
790 args = getargsdict(x, 'extdata', 'source')
791 source = getstring(args.get('source'),
791 source = getstring(args.get('source'),
792 # i18n: "extdata" is a keyword
792 # i18n: "extdata" is a keyword
793 _('extdata takes at least 1 string argument'))
793 _('extdata takes at least 1 string argument'))
794 data = scmutil.extdatasource(repo, source)
794 data = scmutil.extdatasource(repo, source)
795 return subset & baseset(data)
795 return subset & baseset(data)
796
796
797 @predicate('extinct()', safe=True)
797 @predicate('extinct()', safe=True)
798 def extinct(repo, subset, x):
798 def extinct(repo, subset, x):
799 """Obsolete changesets with obsolete descendants only.
799 """Obsolete changesets with obsolete descendants only.
800 """
800 """
801 # i18n: "extinct" is a keyword
801 # i18n: "extinct" is a keyword
802 getargs(x, 0, 0, _("extinct takes no arguments"))
802 getargs(x, 0, 0, _("extinct takes no arguments"))
803 extincts = obsmod.getrevs(repo, 'extinct')
803 extincts = obsmod.getrevs(repo, 'extinct')
804 return subset & extincts
804 return subset & extincts
805
805
806 @predicate('extra(label, [value])', safe=True)
806 @predicate('extra(label, [value])', safe=True)
807 def extra(repo, subset, x):
807 def extra(repo, subset, x):
808 """Changesets with the given label in the extra metadata, with the given
808 """Changesets with the given label in the extra metadata, with the given
809 optional value.
809 optional value.
810
810
811 Pattern matching is supported for `value`. See
811 Pattern matching is supported for `value`. See
812 :hg:`help revisions.patterns`.
812 :hg:`help revisions.patterns`.
813 """
813 """
814 args = getargsdict(x, 'extra', 'label value')
814 args = getargsdict(x, 'extra', 'label value')
815 if 'label' not in args:
815 if 'label' not in args:
816 # i18n: "extra" is a keyword
816 # i18n: "extra" is a keyword
817 raise error.ParseError(_('extra takes at least 1 argument'))
817 raise error.ParseError(_('extra takes at least 1 argument'))
818 # i18n: "extra" is a keyword
818 # i18n: "extra" is a keyword
819 label = getstring(args['label'], _('first argument to extra must be '
819 label = getstring(args['label'], _('first argument to extra must be '
820 'a string'))
820 'a string'))
821 value = None
821 value = None
822
822
823 if 'value' in args:
823 if 'value' in args:
824 # i18n: "extra" is a keyword
824 # i18n: "extra" is a keyword
825 value = getstring(args['value'], _('second argument to extra must be '
825 value = getstring(args['value'], _('second argument to extra must be '
826 'a string'))
826 'a string'))
827 kind, value, matcher = stringutil.stringmatcher(value)
827 kind, value, matcher = stringutil.stringmatcher(value)
828
828
829 def _matchvalue(r):
829 def _matchvalue(r):
830 extra = repo[r].extra()
830 extra = repo[r].extra()
831 return label in extra and (value is None or matcher(extra[label]))
831 return label in extra and (value is None or matcher(extra[label]))
832
832
833 return subset.filter(lambda r: _matchvalue(r),
833 return subset.filter(lambda r: _matchvalue(r),
834 condrepr=('<extra[%r] %r>', label, value))
834 condrepr=('<extra[%r] %r>', label, value))
835
835
836 @predicate('filelog(pattern)', safe=True)
836 @predicate('filelog(pattern)', safe=True)
837 def filelog(repo, subset, x):
837 def filelog(repo, subset, x):
838 """Changesets connected to the specified filelog.
838 """Changesets connected to the specified filelog.
839
839
840 For performance reasons, visits only revisions mentioned in the file-level
840 For performance reasons, visits only revisions mentioned in the file-level
841 filelog, rather than filtering through all changesets (much faster, but
841 filelog, rather than filtering through all changesets (much faster, but
842 doesn't include deletes or duplicate changes). For a slower, more accurate
842 doesn't include deletes or duplicate changes). For a slower, more accurate
843 result, use ``file()``.
843 result, use ``file()``.
844
844
845 The pattern without explicit kind like ``glob:`` is expected to be
845 The pattern without explicit kind like ``glob:`` is expected to be
846 relative to the current directory and match against a file exactly
846 relative to the current directory and match against a file exactly
847 for efficiency.
847 for efficiency.
848
848
849 If some linkrev points to revisions filtered by the current repoview, we'll
849 If some linkrev points to revisions filtered by the current repoview, we'll
850 work around it to return a non-filtered value.
850 work around it to return a non-filtered value.
851 """
851 """
852
852
853 # i18n: "filelog" is a keyword
853 # i18n: "filelog" is a keyword
854 pat = getstring(x, _("filelog requires a pattern"))
854 pat = getstring(x, _("filelog requires a pattern"))
855 s = set()
855 s = set()
856 cl = repo.changelog
856 cl = repo.changelog
857
857
858 if not matchmod.patkind(pat):
858 if not matchmod.patkind(pat):
859 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
859 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
860 files = [f]
860 files = [f]
861 else:
861 else:
862 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
862 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
863 files = (f for f in repo[None] if m(f))
863 files = (f for f in repo[None] if m(f))
864
864
865 for f in files:
865 for f in files:
866 fl = repo.file(f)
866 fl = repo.file(f)
867 known = {}
867 known = {}
868 scanpos = 0
868 scanpos = 0
869 for fr in list(fl):
869 for fr in list(fl):
870 fn = fl.node(fr)
870 fn = fl.node(fr)
871 if fn in known:
871 if fn in known:
872 s.add(known[fn])
872 s.add(known[fn])
873 continue
873 continue
874
874
875 lr = fl.linkrev(fr)
875 lr = fl.linkrev(fr)
876 if lr in cl:
876 if lr in cl:
877 s.add(lr)
877 s.add(lr)
878 elif scanpos is not None:
878 elif scanpos is not None:
879 # lowest matching changeset is filtered, scan further
879 # lowest matching changeset is filtered, scan further
880 # ahead in changelog
880 # ahead in changelog
881 start = max(lr, scanpos) + 1
881 start = max(lr, scanpos) + 1
882 scanpos = None
882 scanpos = None
883 for r in cl.revs(start):
883 for r in cl.revs(start):
884 # minimize parsing of non-matching entries
884 # minimize parsing of non-matching entries
885 if f in cl.revision(r) and f in cl.readfiles(r):
885 if f in cl.revision(r) and f in cl.readfiles(r):
886 try:
886 try:
887 # try to use manifest delta fastpath
887 # try to use manifest delta fastpath
888 n = repo[r].filenode(f)
888 n = repo[r].filenode(f)
889 if n not in known:
889 if n not in known:
890 if n == fn:
890 if n == fn:
891 s.add(r)
891 s.add(r)
892 scanpos = r
892 scanpos = r
893 break
893 break
894 else:
894 else:
895 known[n] = r
895 known[n] = r
896 except error.ManifestLookupError:
896 except error.ManifestLookupError:
897 # deletion in changelog
897 # deletion in changelog
898 continue
898 continue
899
899
900 return subset & s
900 return subset & s
901
901
902 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
902 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
903 def first(repo, subset, x, order):
903 def first(repo, subset, x, order):
904 """An alias for limit().
904 """An alias for limit().
905 """
905 """
906 return limit(repo, subset, x, order)
906 return limit(repo, subset, x, order)
907
907
908 def _follow(repo, subset, x, name, followfirst=False):
908 def _follow(repo, subset, x, name, followfirst=False):
909 args = getargsdict(x, name, 'file startrev')
909 args = getargsdict(x, name, 'file startrev')
910 revs = None
910 revs = None
911 if 'startrev' in args:
911 if 'startrev' in args:
912 revs = getset(repo, fullreposet(repo), args['startrev'])
912 revs = getset(repo, fullreposet(repo), args['startrev'])
913 if 'file' in args:
913 if 'file' in args:
914 x = getstring(args['file'], _("%s expected a pattern") % name)
914 x = getstring(args['file'], _("%s expected a pattern") % name)
915 if revs is None:
915 if revs is None:
916 revs = [None]
916 revs = [None]
917 fctxs = []
917 fctxs = []
918 for r in revs:
918 for r in revs:
919 ctx = mctx = repo[r]
919 ctx = mctx = repo[r]
920 if r is None:
920 if r is None:
921 ctx = repo['.']
921 ctx = repo['.']
922 m = matchmod.match(repo.root, repo.getcwd(), [x],
922 m = matchmod.match(repo.root, repo.getcwd(), [x],
923 ctx=mctx, default='path')
923 ctx=mctx, default='path')
924 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
924 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
925 s = dagop.filerevancestors(fctxs, followfirst)
925 s = dagop.filerevancestors(fctxs, followfirst)
926 else:
926 else:
927 if revs is None:
927 if revs is None:
928 revs = baseset([repo['.'].rev()])
928 revs = baseset([repo['.'].rev()])
929 s = dagop.revancestors(repo, revs, followfirst)
929 s = dagop.revancestors(repo, revs, followfirst)
930
930
931 return subset & s
931 return subset & s
932
932
933 @predicate('follow([file[, startrev]])', safe=True)
933 @predicate('follow([file[, startrev]])', safe=True)
934 def follow(repo, subset, x):
934 def follow(repo, subset, x):
935 """
935 """
936 An alias for ``::.`` (ancestors of the working directory's first parent).
936 An alias for ``::.`` (ancestors of the working directory's first parent).
937 If file pattern is specified, the histories of files matching given
937 If file pattern is specified, the histories of files matching given
938 pattern in the revision given by startrev are followed, including copies.
938 pattern in the revision given by startrev are followed, including copies.
939 """
939 """
940 return _follow(repo, subset, x, 'follow')
940 return _follow(repo, subset, x, 'follow')
941
941
942 @predicate('_followfirst', safe=True)
942 @predicate('_followfirst', safe=True)
943 def _followfirst(repo, subset, x):
943 def _followfirst(repo, subset, x):
944 # ``followfirst([file[, startrev]])``
944 # ``followfirst([file[, startrev]])``
945 # Like ``follow([file[, startrev]])`` but follows only the first parent
945 # Like ``follow([file[, startrev]])`` but follows only the first parent
946 # of every revisions or files revisions.
946 # of every revisions or files revisions.
947 return _follow(repo, subset, x, '_followfirst', followfirst=True)
947 return _follow(repo, subset, x, '_followfirst', followfirst=True)
948
948
949 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
949 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
950 safe=True)
950 safe=True)
951 def followlines(repo, subset, x):
951 def followlines(repo, subset, x):
952 """Changesets modifying `file` in line range ('fromline', 'toline').
952 """Changesets modifying `file` in line range ('fromline', 'toline').
953
953
954 Line range corresponds to 'file' content at 'startrev' and should hence be
954 Line range corresponds to 'file' content at 'startrev' and should hence be
955 consistent with file size. If startrev is not specified, working directory's
955 consistent with file size. If startrev is not specified, working directory's
956 parent is used.
956 parent is used.
957
957
958 By default, ancestors of 'startrev' are returned. If 'descend' is True,
958 By default, ancestors of 'startrev' are returned. If 'descend' is True,
959 descendants of 'startrev' are returned though renames are (currently) not
959 descendants of 'startrev' are returned though renames are (currently) not
960 followed in this direction.
960 followed in this direction.
961 """
961 """
962 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
962 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
963 if len(args['lines']) != 1:
963 if len(args['lines']) != 1:
964 raise error.ParseError(_("followlines requires a line range"))
964 raise error.ParseError(_("followlines requires a line range"))
965
965
966 rev = '.'
966 rev = '.'
967 if 'startrev' in args:
967 if 'startrev' in args:
968 revs = getset(repo, fullreposet(repo), args['startrev'])
968 revs = getset(repo, fullreposet(repo), args['startrev'])
969 if len(revs) != 1:
969 if len(revs) != 1:
970 raise error.ParseError(
970 raise error.ParseError(
971 # i18n: "followlines" is a keyword
971 # i18n: "followlines" is a keyword
972 _("followlines expects exactly one revision"))
972 _("followlines expects exactly one revision"))
973 rev = revs.last()
973 rev = revs.last()
974
974
975 pat = getstring(args['file'], _("followlines requires a pattern"))
975 pat = getstring(args['file'], _("followlines requires a pattern"))
976 # i18n: "followlines" is a keyword
976 # i18n: "followlines" is a keyword
977 msg = _("followlines expects exactly one file")
977 msg = _("followlines expects exactly one file")
978 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
978 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
979 # i18n: "followlines" is a keyword
979 # i18n: "followlines" is a keyword
980 lr = getrange(args['lines'][0], _("followlines expects a line range"))
980 lr = getrange(args['lines'][0], _("followlines expects a line range"))
981 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
981 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
982 for a in lr]
982 for a in lr]
983 fromline, toline = util.processlinerange(fromline, toline)
983 fromline, toline = util.processlinerange(fromline, toline)
984
984
985 fctx = repo[rev].filectx(fname)
985 fctx = repo[rev].filectx(fname)
986 descend = False
986 descend = False
987 if 'descend' in args:
987 if 'descend' in args:
988 descend = getboolean(args['descend'],
988 descend = getboolean(args['descend'],
989 # i18n: "descend" is a keyword
989 # i18n: "descend" is a keyword
990 _("descend argument must be a boolean"))
990 _("descend argument must be a boolean"))
991 if descend:
991 if descend:
992 rs = generatorset(
992 rs = generatorset(
993 (c.rev() for c, _linerange
993 (c.rev() for c, _linerange
994 in dagop.blockdescendants(fctx, fromline, toline)),
994 in dagop.blockdescendants(fctx, fromline, toline)),
995 iterasc=True)
995 iterasc=True)
996 else:
996 else:
997 rs = generatorset(
997 rs = generatorset(
998 (c.rev() for c, _linerange
998 (c.rev() for c, _linerange
999 in dagop.blockancestors(fctx, fromline, toline)),
999 in dagop.blockancestors(fctx, fromline, toline)),
1000 iterasc=False)
1000 iterasc=False)
1001 return subset & rs
1001 return subset & rs
1002
1002
1003 @predicate('all()', safe=True)
1003 @predicate('all()', safe=True)
1004 def getall(repo, subset, x):
1004 def getall(repo, subset, x):
1005 """All changesets, the same as ``0:tip``.
1005 """All changesets, the same as ``0:tip``.
1006 """
1006 """
1007 # i18n: "all" is a keyword
1007 # i18n: "all" is a keyword
1008 getargs(x, 0, 0, _("all takes no arguments"))
1008 getargs(x, 0, 0, _("all takes no arguments"))
1009 return subset & spanset(repo) # drop "null" if any
1009 return subset & spanset(repo) # drop "null" if any
1010
1010
1011 @predicate('grep(regex)', weight=10)
1011 @predicate('grep(regex)', weight=10)
1012 def grep(repo, subset, x):
1012 def grep(repo, subset, x):
1013 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1013 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1014 to ensure special escape characters are handled correctly. Unlike
1014 to ensure special escape characters are handled correctly. Unlike
1015 ``keyword(string)``, the match is case-sensitive.
1015 ``keyword(string)``, the match is case-sensitive.
1016 """
1016 """
1017 try:
1017 try:
1018 # i18n: "grep" is a keyword
1018 # i18n: "grep" is a keyword
1019 gr = re.compile(getstring(x, _("grep requires a string")))
1019 gr = re.compile(getstring(x, _("grep requires a string")))
1020 except re.error as e:
1020 except re.error as e:
1021 raise error.ParseError(
1021 raise error.ParseError(
1022 _('invalid match pattern: %s') % stringutil.forcebytestr(e))
1022 _('invalid match pattern: %s') % stringutil.forcebytestr(e))
1023
1023
1024 def matches(x):
1024 def matches(x):
1025 c = repo[x]
1025 c = repo[x]
1026 for e in c.files() + [c.user(), c.description()]:
1026 for e in c.files() + [c.user(), c.description()]:
1027 if gr.search(e):
1027 if gr.search(e):
1028 return True
1028 return True
1029 return False
1029 return False
1030
1030
1031 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1031 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1032
1032
1033 @predicate('_matchfiles', safe=True)
1033 @predicate('_matchfiles', safe=True)
1034 def _matchfiles(repo, subset, x):
1034 def _matchfiles(repo, subset, x):
1035 # _matchfiles takes a revset list of prefixed arguments:
1035 # _matchfiles takes a revset list of prefixed arguments:
1036 #
1036 #
1037 # [p:foo, i:bar, x:baz]
1037 # [p:foo, i:bar, x:baz]
1038 #
1038 #
1039 # builds a match object from them and filters subset. Allowed
1039 # builds a match object from them and filters subset. Allowed
1040 # prefixes are 'p:' for regular patterns, 'i:' for include
1040 # prefixes are 'p:' for regular patterns, 'i:' for include
1041 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1041 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1042 # a revision identifier, or the empty string to reference the
1042 # a revision identifier, or the empty string to reference the
1043 # working directory, from which the match object is
1043 # working directory, from which the match object is
1044 # initialized. Use 'd:' to set the default matching mode, default
1044 # initialized. Use 'd:' to set the default matching mode, default
1045 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1045 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1046
1046
1047 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1047 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1048 pats, inc, exc = [], [], []
1048 pats, inc, exc = [], [], []
1049 rev, default = None, None
1049 rev, default = None, None
1050 for arg in l:
1050 for arg in l:
1051 s = getstring(arg, "_matchfiles requires string arguments")
1051 s = getstring(arg, "_matchfiles requires string arguments")
1052 prefix, value = s[:2], s[2:]
1052 prefix, value = s[:2], s[2:]
1053 if prefix == 'p:':
1053 if prefix == 'p:':
1054 pats.append(value)
1054 pats.append(value)
1055 elif prefix == 'i:':
1055 elif prefix == 'i:':
1056 inc.append(value)
1056 inc.append(value)
1057 elif prefix == 'x:':
1057 elif prefix == 'x:':
1058 exc.append(value)
1058 exc.append(value)
1059 elif prefix == 'r:':
1059 elif prefix == 'r:':
1060 if rev is not None:
1060 if rev is not None:
1061 raise error.ParseError('_matchfiles expected at most one '
1061 raise error.ParseError('_matchfiles expected at most one '
1062 'revision')
1062 'revision')
1063 if value == '': # empty means working directory
1063 if value == '': # empty means working directory
1064 rev = node.wdirrev
1064 rev = node.wdirrev
1065 else:
1065 else:
1066 rev = value
1066 rev = value
1067 elif prefix == 'd:':
1067 elif prefix == 'd:':
1068 if default is not None:
1068 if default is not None:
1069 raise error.ParseError('_matchfiles expected at most one '
1069 raise error.ParseError('_matchfiles expected at most one '
1070 'default mode')
1070 'default mode')
1071 default = value
1071 default = value
1072 else:
1072 else:
1073 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1073 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1074 if not default:
1074 if not default:
1075 default = 'glob'
1075 default = 'glob'
1076 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1076 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1077
1077
1078 mcache = [None]
1078 mcache = [None]
1079
1079
1080 # This directly read the changelog data as creating changectx for all
1080 # This directly read the changelog data as creating changectx for all
1081 # revisions is quite expensive.
1081 # revisions is quite expensive.
1082 getfiles = repo.changelog.readfiles
1082 getfiles = repo.changelog.readfiles
1083 wdirrev = node.wdirrev
1083 wdirrev = node.wdirrev
1084 def matches(x):
1084 def matches(x):
1085 if x == wdirrev:
1085 if x == wdirrev:
1086 files = repo[x].files()
1086 files = repo[x].files()
1087 else:
1087 else:
1088 files = getfiles(x)
1088 files = getfiles(x)
1089
1089
1090 if not mcache[0] or (hasset and rev is None):
1090 if not mcache[0] or (hasset and rev is None):
1091 r = x if rev is None else rev
1091 r = x if rev is None else rev
1092 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1092 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1093 include=inc, exclude=exc, ctx=repo[r],
1093 include=inc, exclude=exc, ctx=repo[r],
1094 default=default)
1094 default=default)
1095 m = mcache[0]
1095 m = mcache[0]
1096
1096
1097 for f in files:
1097 for f in files:
1098 if m(f):
1098 if m(f):
1099 return True
1099 return True
1100 return False
1100 return False
1101
1101
1102 return subset.filter(matches,
1102 return subset.filter(matches,
1103 condrepr=('<matchfiles patterns=%r, include=%r '
1103 condrepr=('<matchfiles patterns=%r, include=%r '
1104 'exclude=%r, default=%r, rev=%r>',
1104 'exclude=%r, default=%r, rev=%r>',
1105 pats, inc, exc, default, rev))
1105 pats, inc, exc, default, rev))
1106
1106
1107 @predicate('file(pattern)', safe=True, weight=10)
1107 @predicate('file(pattern)', safe=True, weight=10)
1108 def hasfile(repo, subset, x):
1108 def hasfile(repo, subset, x):
1109 """Changesets affecting files matched by pattern.
1109 """Changesets affecting files matched by pattern.
1110
1110
1111 For a faster but less accurate result, consider using ``filelog()``
1111 For a faster but less accurate result, consider using ``filelog()``
1112 instead.
1112 instead.
1113
1113
1114 This predicate uses ``glob:`` as the default kind of pattern.
1114 This predicate uses ``glob:`` as the default kind of pattern.
1115 """
1115 """
1116 # i18n: "file" is a keyword
1116 # i18n: "file" is a keyword
1117 pat = getstring(x, _("file requires a pattern"))
1117 pat = getstring(x, _("file requires a pattern"))
1118 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1118 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1119
1119
1120 @predicate('head()', safe=True)
1120 @predicate('head()', safe=True)
1121 def head(repo, subset, x):
1121 def head(repo, subset, x):
1122 """Changeset is a named branch head.
1122 """Changeset is a named branch head.
1123 """
1123 """
1124 # i18n: "head" is a keyword
1124 # i18n: "head" is a keyword
1125 getargs(x, 0, 0, _("head takes no arguments"))
1125 getargs(x, 0, 0, _("head takes no arguments"))
1126 hs = set()
1126 hs = set()
1127 cl = repo.changelog
1127 cl = repo.changelog
1128 for ls in repo.branchmap().itervalues():
1128 for ls in repo.branchmap().itervalues():
1129 hs.update(cl.rev(h) for h in ls)
1129 hs.update(cl.rev(h) for h in ls)
1130 return subset & baseset(hs)
1130 return subset & baseset(hs)
1131
1131
1132 @predicate('heads(set)', safe=True)
1132 @predicate('heads(set)', safe=True)
1133 def heads(repo, subset, x):
1133 def heads(repo, subset, x):
1134 """Members of set with no children in set.
1134 """Members of set with no children in set.
1135 """
1135 """
1136 s = getset(repo, subset, x)
1136 s = getset(repo, subset, x)
1137 ps = parents(repo, subset, x)
1137 ps = parents(repo, subset, x)
1138 return s - ps
1138 return s - ps
1139
1139
1140 @predicate('hidden()', safe=True)
1140 @predicate('hidden()', safe=True)
1141 def hidden(repo, subset, x):
1141 def hidden(repo, subset, x):
1142 """Hidden changesets.
1142 """Hidden changesets.
1143 """
1143 """
1144 # i18n: "hidden" is a keyword
1144 # i18n: "hidden" is a keyword
1145 getargs(x, 0, 0, _("hidden takes no arguments"))
1145 getargs(x, 0, 0, _("hidden takes no arguments"))
1146 hiddenrevs = repoview.filterrevs(repo, 'visible')
1146 hiddenrevs = repoview.filterrevs(repo, 'visible')
1147 return subset & hiddenrevs
1147 return subset & hiddenrevs
1148
1148
1149 @predicate('keyword(string)', safe=True, weight=10)
1149 @predicate('keyword(string)', safe=True, weight=10)
1150 def keyword(repo, subset, x):
1150 def keyword(repo, subset, x):
1151 """Search commit message, user name, and names of changed files for
1151 """Search commit message, user name, and names of changed files for
1152 string. The match is case-insensitive.
1152 string. The match is case-insensitive.
1153
1153
1154 For a regular expression or case sensitive search of these fields, use
1154 For a regular expression or case sensitive search of these fields, use
1155 ``grep(regex)``.
1155 ``grep(regex)``.
1156 """
1156 """
1157 # i18n: "keyword" is a keyword
1157 # i18n: "keyword" is a keyword
1158 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1158 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1159
1159
1160 def matches(r):
1160 def matches(r):
1161 c = repo[r]
1161 c = repo[r]
1162 return any(kw in encoding.lower(t)
1162 return any(kw in encoding.lower(t)
1163 for t in c.files() + [c.user(), c.description()])
1163 for t in c.files() + [c.user(), c.description()])
1164
1164
1165 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1165 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1166
1166
1167 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1167 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1168 def limit(repo, subset, x, order):
1168 def limit(repo, subset, x, order):
1169 """First n members of set, defaulting to 1, starting from offset.
1169 """First n members of set, defaulting to 1, starting from offset.
1170 """
1170 """
1171 args = getargsdict(x, 'limit', 'set n offset')
1171 args = getargsdict(x, 'limit', 'set n offset')
1172 if 'set' not in args:
1172 if 'set' not in args:
1173 # i18n: "limit" is a keyword
1173 # i18n: "limit" is a keyword
1174 raise error.ParseError(_("limit requires one to three arguments"))
1174 raise error.ParseError(_("limit requires one to three arguments"))
1175 # i18n: "limit" is a keyword
1175 # i18n: "limit" is a keyword
1176 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1176 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1177 if lim < 0:
1177 if lim < 0:
1178 raise error.ParseError(_("negative number to select"))
1178 raise error.ParseError(_("negative number to select"))
1179 # i18n: "limit" is a keyword
1179 # i18n: "limit" is a keyword
1180 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1180 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1181 if ofs < 0:
1181 if ofs < 0:
1182 raise error.ParseError(_("negative offset"))
1182 raise error.ParseError(_("negative offset"))
1183 os = getset(repo, fullreposet(repo), args['set'])
1183 os = getset(repo, fullreposet(repo), args['set'])
1184 ls = os.slice(ofs, ofs + lim)
1184 ls = os.slice(ofs, ofs + lim)
1185 if order == followorder and lim > 1:
1185 if order == followorder and lim > 1:
1186 return subset & ls
1186 return subset & ls
1187 return ls & subset
1187 return ls & subset
1188
1188
1189 @predicate('last(set, [n])', safe=True, takeorder=True)
1189 @predicate('last(set, [n])', safe=True, takeorder=True)
1190 def last(repo, subset, x, order):
1190 def last(repo, subset, x, order):
1191 """Last n members of set, defaulting to 1.
1191 """Last n members of set, defaulting to 1.
1192 """
1192 """
1193 # i18n: "last" is a keyword
1193 # i18n: "last" is a keyword
1194 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1194 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1195 lim = 1
1195 lim = 1
1196 if len(l) == 2:
1196 if len(l) == 2:
1197 # i18n: "last" is a keyword
1197 # i18n: "last" is a keyword
1198 lim = getinteger(l[1], _("last expects a number"))
1198 lim = getinteger(l[1], _("last expects a number"))
1199 if lim < 0:
1199 if lim < 0:
1200 raise error.ParseError(_("negative number to select"))
1200 raise error.ParseError(_("negative number to select"))
1201 os = getset(repo, fullreposet(repo), l[0])
1201 os = getset(repo, fullreposet(repo), l[0])
1202 os.reverse()
1202 os.reverse()
1203 ls = os.slice(0, lim)
1203 ls = os.slice(0, lim)
1204 if order == followorder and lim > 1:
1204 if order == followorder and lim > 1:
1205 return subset & ls
1205 return subset & ls
1206 ls.reverse()
1206 ls.reverse()
1207 return ls & subset
1207 return ls & subset
1208
1208
1209 @predicate('max(set)', safe=True)
1209 @predicate('max(set)', safe=True)
1210 def maxrev(repo, subset, x):
1210 def maxrev(repo, subset, x):
1211 """Changeset with highest revision number in set.
1211 """Changeset with highest revision number in set.
1212 """
1212 """
1213 os = getset(repo, fullreposet(repo), x)
1213 os = getset(repo, fullreposet(repo), x)
1214 try:
1214 try:
1215 m = os.max()
1215 m = os.max()
1216 if m in subset:
1216 if m in subset:
1217 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1217 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1218 except ValueError:
1218 except ValueError:
1219 # os.max() throws a ValueError when the collection is empty.
1219 # os.max() throws a ValueError when the collection is empty.
1220 # Same as python's max().
1220 # Same as python's max().
1221 pass
1221 pass
1222 return baseset(datarepr=('<max %r, %r>', subset, os))
1222 return baseset(datarepr=('<max %r, %r>', subset, os))
1223
1223
1224 @predicate('merge()', safe=True)
1224 @predicate('merge()', safe=True)
1225 def merge(repo, subset, x):
1225 def merge(repo, subset, x):
1226 """Changeset is a merge changeset.
1226 """Changeset is a merge changeset.
1227 """
1227 """
1228 # i18n: "merge" is a keyword
1228 # i18n: "merge" is a keyword
1229 getargs(x, 0, 0, _("merge takes no arguments"))
1229 getargs(x, 0, 0, _("merge takes no arguments"))
1230 cl = repo.changelog
1230 cl = repo.changelog
1231 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1231 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1232 condrepr='<merge>')
1232 condrepr='<merge>')
1233
1233
1234 @predicate('branchpoint()', safe=True)
1234 @predicate('branchpoint()', safe=True)
1235 def branchpoint(repo, subset, x):
1235 def branchpoint(repo, subset, x):
1236 """Changesets with more than one child.
1236 """Changesets with more than one child.
1237 """
1237 """
1238 # i18n: "branchpoint" is a keyword
1238 # i18n: "branchpoint" is a keyword
1239 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1239 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1240 cl = repo.changelog
1240 cl = repo.changelog
1241 if not subset:
1241 if not subset:
1242 return baseset()
1242 return baseset()
1243 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1243 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1244 # (and if it is not, it should.)
1244 # (and if it is not, it should.)
1245 baserev = min(subset)
1245 baserev = min(subset)
1246 parentscount = [0]*(len(repo) - baserev)
1246 parentscount = [0]*(len(repo) - baserev)
1247 for r in cl.revs(start=baserev + 1):
1247 for r in cl.revs(start=baserev + 1):
1248 for p in cl.parentrevs(r):
1248 for p in cl.parentrevs(r):
1249 if p >= baserev:
1249 if p >= baserev:
1250 parentscount[p - baserev] += 1
1250 parentscount[p - baserev] += 1
1251 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1251 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1252 condrepr='<branchpoint>')
1252 condrepr='<branchpoint>')
1253
1253
1254 @predicate('min(set)', safe=True)
1254 @predicate('min(set)', safe=True)
1255 def minrev(repo, subset, x):
1255 def minrev(repo, subset, x):
1256 """Changeset with lowest revision number in set.
1256 """Changeset with lowest revision number in set.
1257 """
1257 """
1258 os = getset(repo, fullreposet(repo), x)
1258 os = getset(repo, fullreposet(repo), x)
1259 try:
1259 try:
1260 m = os.min()
1260 m = os.min()
1261 if m in subset:
1261 if m in subset:
1262 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1262 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1263 except ValueError:
1263 except ValueError:
1264 # os.min() throws a ValueError when the collection is empty.
1264 # os.min() throws a ValueError when the collection is empty.
1265 # Same as python's min().
1265 # Same as python's min().
1266 pass
1266 pass
1267 return baseset(datarepr=('<min %r, %r>', subset, os))
1267 return baseset(datarepr=('<min %r, %r>', subset, os))
1268
1268
1269 @predicate('modifies(pattern)', safe=True, weight=30)
1269 @predicate('modifies(pattern)', safe=True, weight=30)
1270 def modifies(repo, subset, x):
1270 def modifies(repo, subset, x):
1271 """Changesets modifying files matched by pattern.
1271 """Changesets modifying files matched by pattern.
1272
1272
1273 The pattern without explicit kind like ``glob:`` is expected to be
1273 The pattern without explicit kind like ``glob:`` is expected to be
1274 relative to the current directory and match against a file or a
1274 relative to the current directory and match against a file or a
1275 directory.
1275 directory.
1276 """
1276 """
1277 # i18n: "modifies" is a keyword
1277 # i18n: "modifies" is a keyword
1278 pat = getstring(x, _("modifies requires a pattern"))
1278 pat = getstring(x, _("modifies requires a pattern"))
1279 return checkstatus(repo, subset, pat, 0)
1279 return checkstatus(repo, subset, pat, 0)
1280
1280
1281 @predicate('named(namespace)')
1281 @predicate('named(namespace)')
1282 def named(repo, subset, x):
1282 def named(repo, subset, x):
1283 """The changesets in a given namespace.
1283 """The changesets in a given namespace.
1284
1284
1285 Pattern matching is supported for `namespace`. See
1285 Pattern matching is supported for `namespace`. See
1286 :hg:`help revisions.patterns`.
1286 :hg:`help revisions.patterns`.
1287 """
1287 """
1288 # i18n: "named" is a keyword
1288 # i18n: "named" is a keyword
1289 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1289 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1290
1290
1291 ns = getstring(args[0],
1291 ns = getstring(args[0],
1292 # i18n: "named" is a keyword
1292 # i18n: "named" is a keyword
1293 _('the argument to named must be a string'))
1293 _('the argument to named must be a string'))
1294 kind, pattern, matcher = stringutil.stringmatcher(ns)
1294 kind, pattern, matcher = stringutil.stringmatcher(ns)
1295 namespaces = set()
1295 namespaces = set()
1296 if kind == 'literal':
1296 if kind == 'literal':
1297 if pattern not in repo.names:
1297 if pattern not in repo.names:
1298 raise error.RepoLookupError(_("namespace '%s' does not exist")
1298 raise error.RepoLookupError(_("namespace '%s' does not exist")
1299 % ns)
1299 % ns)
1300 namespaces.add(repo.names[pattern])
1300 namespaces.add(repo.names[pattern])
1301 else:
1301 else:
1302 for name, ns in repo.names.iteritems():
1302 for name, ns in repo.names.iteritems():
1303 if matcher(name):
1303 if matcher(name):
1304 namespaces.add(ns)
1304 namespaces.add(ns)
1305 if not namespaces:
1305 if not namespaces:
1306 raise error.RepoLookupError(_("no namespace exists"
1306 raise error.RepoLookupError(_("no namespace exists"
1307 " that match '%s'") % pattern)
1307 " that match '%s'") % pattern)
1308
1308
1309 names = set()
1309 names = set()
1310 for ns in namespaces:
1310 for ns in namespaces:
1311 for name in ns.listnames(repo):
1311 for name in ns.listnames(repo):
1312 if name not in ns.deprecated:
1312 if name not in ns.deprecated:
1313 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1313 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1314
1314
1315 names -= {node.nullrev}
1315 names -= {node.nullrev}
1316 return subset & names
1316 return subset & names
1317
1317
1318 @predicate('id(string)', safe=True)
1318 @predicate('id(string)', safe=True)
1319 def node_(repo, subset, x):
1319 def node_(repo, subset, x):
1320 """Revision non-ambiguously specified by the given hex string prefix.
1320 """Revision non-ambiguously specified by the given hex string prefix.
1321 """
1321 """
1322 # i18n: "id" is a keyword
1322 # i18n: "id" is a keyword
1323 l = getargs(x, 1, 1, _("id requires one argument"))
1323 l = getargs(x, 1, 1, _("id requires one argument"))
1324 # i18n: "id" is a keyword
1324 # i18n: "id" is a keyword
1325 n = getstring(l[0], _("id requires a string"))
1325 n = getstring(l[0], _("id requires a string"))
1326 if len(n) == 40:
1326 if len(n) == 40:
1327 try:
1327 try:
1328 rn = repo.changelog.rev(node.bin(n))
1328 rn = repo.changelog.rev(node.bin(n))
1329 except error.WdirUnsupported:
1329 except error.WdirUnsupported:
1330 rn = node.wdirrev
1330 rn = node.wdirrev
1331 except (LookupError, TypeError):
1331 except (LookupError, TypeError):
1332 rn = None
1332 rn = None
1333 else:
1333 else:
1334 rn = None
1334 rn = None
1335 try:
1335 try:
1336 pm = scmutil.resolvehexnodeidprefix(repo, n)
1336 pm = scmutil.resolvehexnodeidprefix(repo, n)
1337 if pm is not None:
1337 if pm is not None:
1338 rn = repo.changelog.rev(pm)
1338 rn = repo.changelog.rev(pm)
1339 except LookupError:
1339 except LookupError:
1340 pass
1340 pass
1341 except error.WdirUnsupported:
1341 except error.WdirUnsupported:
1342 rn = node.wdirrev
1342 rn = node.wdirrev
1343
1343
1344 if rn is None:
1344 if rn is None:
1345 return baseset()
1345 return baseset()
1346 result = baseset([rn])
1346 result = baseset([rn])
1347 return result & subset
1347 return result & subset
1348
1348
1349 @predicate('none()', safe=True)
1350 def none(repo, subset, x):
1351 """No changesets.
1352 """
1353 # i18n: "none" is a keyword
1354 getargs(x, 0, 0, _("none takes no arguments"))
1355 return baseset()
1356
1349 @predicate('obsolete()', safe=True)
1357 @predicate('obsolete()', safe=True)
1350 def obsolete(repo, subset, x):
1358 def obsolete(repo, subset, x):
1351 """Mutable changeset with a newer version."""
1359 """Mutable changeset with a newer version."""
1352 # i18n: "obsolete" is a keyword
1360 # i18n: "obsolete" is a keyword
1353 getargs(x, 0, 0, _("obsolete takes no arguments"))
1361 getargs(x, 0, 0, _("obsolete takes no arguments"))
1354 obsoletes = obsmod.getrevs(repo, 'obsolete')
1362 obsoletes = obsmod.getrevs(repo, 'obsolete')
1355 return subset & obsoletes
1363 return subset & obsoletes
1356
1364
1357 @predicate('only(set, [set])', safe=True)
1365 @predicate('only(set, [set])', safe=True)
1358 def only(repo, subset, x):
1366 def only(repo, subset, x):
1359 """Changesets that are ancestors of the first set that are not ancestors
1367 """Changesets that are ancestors of the first set that are not ancestors
1360 of any other head in the repo. If a second set is specified, the result
1368 of any other head in the repo. If a second set is specified, the result
1361 is ancestors of the first set that are not ancestors of the second set
1369 is ancestors of the first set that are not ancestors of the second set
1362 (i.e. ::<set1> - ::<set2>).
1370 (i.e. ::<set1> - ::<set2>).
1363 """
1371 """
1364 cl = repo.changelog
1372 cl = repo.changelog
1365 # i18n: "only" is a keyword
1373 # i18n: "only" is a keyword
1366 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1374 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1367 include = getset(repo, fullreposet(repo), args[0])
1375 include = getset(repo, fullreposet(repo), args[0])
1368 if len(args) == 1:
1376 if len(args) == 1:
1369 if not include:
1377 if not include:
1370 return baseset()
1378 return baseset()
1371
1379
1372 descendants = set(dagop.revdescendants(repo, include, False))
1380 descendants = set(dagop.revdescendants(repo, include, False))
1373 exclude = [rev for rev in cl.headrevs()
1381 exclude = [rev for rev in cl.headrevs()
1374 if not rev in descendants and not rev in include]
1382 if not rev in descendants and not rev in include]
1375 else:
1383 else:
1376 exclude = getset(repo, fullreposet(repo), args[1])
1384 exclude = getset(repo, fullreposet(repo), args[1])
1377
1385
1378 results = set(cl.findmissingrevs(common=exclude, heads=include))
1386 results = set(cl.findmissingrevs(common=exclude, heads=include))
1379 # XXX we should turn this into a baseset instead of a set, smartset may do
1387 # XXX we should turn this into a baseset instead of a set, smartset may do
1380 # some optimizations from the fact this is a baseset.
1388 # some optimizations from the fact this is a baseset.
1381 return subset & results
1389 return subset & results
1382
1390
1383 @predicate('origin([set])', safe=True)
1391 @predicate('origin([set])', safe=True)
1384 def origin(repo, subset, x):
1392 def origin(repo, subset, x):
1385 """
1393 """
1386 Changesets that were specified as a source for the grafts, transplants or
1394 Changesets that were specified as a source for the grafts, transplants or
1387 rebases that created the given revisions. Omitting the optional set is the
1395 rebases that created the given revisions. Omitting the optional set is the
1388 same as passing all(). If a changeset created by these operations is itself
1396 same as passing all(). If a changeset created by these operations is itself
1389 specified as a source for one of these operations, only the source changeset
1397 specified as a source for one of these operations, only the source changeset
1390 for the first operation is selected.
1398 for the first operation is selected.
1391 """
1399 """
1392 if x is not None:
1400 if x is not None:
1393 dests = getset(repo, fullreposet(repo), x)
1401 dests = getset(repo, fullreposet(repo), x)
1394 else:
1402 else:
1395 dests = fullreposet(repo)
1403 dests = fullreposet(repo)
1396
1404
1397 def _firstsrc(rev):
1405 def _firstsrc(rev):
1398 src = _getrevsource(repo, rev)
1406 src = _getrevsource(repo, rev)
1399 if src is None:
1407 if src is None:
1400 return None
1408 return None
1401
1409
1402 while True:
1410 while True:
1403 prev = _getrevsource(repo, src)
1411 prev = _getrevsource(repo, src)
1404
1412
1405 if prev is None:
1413 if prev is None:
1406 return src
1414 return src
1407 src = prev
1415 src = prev
1408
1416
1409 o = {_firstsrc(r) for r in dests}
1417 o = {_firstsrc(r) for r in dests}
1410 o -= {None}
1418 o -= {None}
1411 # XXX we should turn this into a baseset instead of a set, smartset may do
1419 # XXX we should turn this into a baseset instead of a set, smartset may do
1412 # some optimizations from the fact this is a baseset.
1420 # some optimizations from the fact this is a baseset.
1413 return subset & o
1421 return subset & o
1414
1422
1415 @predicate('outgoing([path])', safe=False, weight=10)
1423 @predicate('outgoing([path])', safe=False, weight=10)
1416 def outgoing(repo, subset, x):
1424 def outgoing(repo, subset, x):
1417 """Changesets not found in the specified destination repository, or the
1425 """Changesets not found in the specified destination repository, or the
1418 default push location.
1426 default push location.
1419 """
1427 """
1420 # Avoid cycles.
1428 # Avoid cycles.
1421 from . import (
1429 from . import (
1422 discovery,
1430 discovery,
1423 hg,
1431 hg,
1424 )
1432 )
1425 # i18n: "outgoing" is a keyword
1433 # i18n: "outgoing" is a keyword
1426 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1434 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1427 # i18n: "outgoing" is a keyword
1435 # i18n: "outgoing" is a keyword
1428 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1436 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1429 if not dest:
1437 if not dest:
1430 # ui.paths.getpath() explicitly tests for None, not just a boolean
1438 # ui.paths.getpath() explicitly tests for None, not just a boolean
1431 dest = None
1439 dest = None
1432 path = repo.ui.paths.getpath(dest, default=('default-push', 'default'))
1440 path = repo.ui.paths.getpath(dest, default=('default-push', 'default'))
1433 if not path:
1441 if not path:
1434 raise error.Abort(_('default repository not configured!'),
1442 raise error.Abort(_('default repository not configured!'),
1435 hint=_("see 'hg help config.paths'"))
1443 hint=_("see 'hg help config.paths'"))
1436 dest = path.pushloc or path.loc
1444 dest = path.pushloc or path.loc
1437 branches = path.branch, []
1445 branches = path.branch, []
1438
1446
1439 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1447 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1440 if revs:
1448 if revs:
1441 revs = [repo.lookup(rev) for rev in revs]
1449 revs = [repo.lookup(rev) for rev in revs]
1442 other = hg.peer(repo, {}, dest)
1450 other = hg.peer(repo, {}, dest)
1443 repo.ui.pushbuffer()
1451 repo.ui.pushbuffer()
1444 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1452 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1445 repo.ui.popbuffer()
1453 repo.ui.popbuffer()
1446 cl = repo.changelog
1454 cl = repo.changelog
1447 o = {cl.rev(r) for r in outgoing.missing}
1455 o = {cl.rev(r) for r in outgoing.missing}
1448 return subset & o
1456 return subset & o
1449
1457
1450 @predicate('p1([set])', safe=True)
1458 @predicate('p1([set])', safe=True)
1451 def p1(repo, subset, x):
1459 def p1(repo, subset, x):
1452 """First parent of changesets in set, or the working directory.
1460 """First parent of changesets in set, or the working directory.
1453 """
1461 """
1454 if x is None:
1462 if x is None:
1455 p = repo[x].p1().rev()
1463 p = repo[x].p1().rev()
1456 if p >= 0:
1464 if p >= 0:
1457 return subset & baseset([p])
1465 return subset & baseset([p])
1458 return baseset()
1466 return baseset()
1459
1467
1460 ps = set()
1468 ps = set()
1461 cl = repo.changelog
1469 cl = repo.changelog
1462 for r in getset(repo, fullreposet(repo), x):
1470 for r in getset(repo, fullreposet(repo), x):
1463 try:
1471 try:
1464 ps.add(cl.parentrevs(r)[0])
1472 ps.add(cl.parentrevs(r)[0])
1465 except error.WdirUnsupported:
1473 except error.WdirUnsupported:
1466 ps.add(repo[r].parents()[0].rev())
1474 ps.add(repo[r].parents()[0].rev())
1467 ps -= {node.nullrev}
1475 ps -= {node.nullrev}
1468 # XXX we should turn this into a baseset instead of a set, smartset may do
1476 # XXX we should turn this into a baseset instead of a set, smartset may do
1469 # some optimizations from the fact this is a baseset.
1477 # some optimizations from the fact this is a baseset.
1470 return subset & ps
1478 return subset & ps
1471
1479
1472 @predicate('p2([set])', safe=True)
1480 @predicate('p2([set])', safe=True)
1473 def p2(repo, subset, x):
1481 def p2(repo, subset, x):
1474 """Second parent of changesets in set, or the working directory.
1482 """Second parent of changesets in set, or the working directory.
1475 """
1483 """
1476 if x is None:
1484 if x is None:
1477 ps = repo[x].parents()
1485 ps = repo[x].parents()
1478 try:
1486 try:
1479 p = ps[1].rev()
1487 p = ps[1].rev()
1480 if p >= 0:
1488 if p >= 0:
1481 return subset & baseset([p])
1489 return subset & baseset([p])
1482 return baseset()
1490 return baseset()
1483 except IndexError:
1491 except IndexError:
1484 return baseset()
1492 return baseset()
1485
1493
1486 ps = set()
1494 ps = set()
1487 cl = repo.changelog
1495 cl = repo.changelog
1488 for r in getset(repo, fullreposet(repo), x):
1496 for r in getset(repo, fullreposet(repo), x):
1489 try:
1497 try:
1490 ps.add(cl.parentrevs(r)[1])
1498 ps.add(cl.parentrevs(r)[1])
1491 except error.WdirUnsupported:
1499 except error.WdirUnsupported:
1492 parents = repo[r].parents()
1500 parents = repo[r].parents()
1493 if len(parents) == 2:
1501 if len(parents) == 2:
1494 ps.add(parents[1])
1502 ps.add(parents[1])
1495 ps -= {node.nullrev}
1503 ps -= {node.nullrev}
1496 # XXX we should turn this into a baseset instead of a set, smartset may do
1504 # XXX we should turn this into a baseset instead of a set, smartset may do
1497 # some optimizations from the fact this is a baseset.
1505 # some optimizations from the fact this is a baseset.
1498 return subset & ps
1506 return subset & ps
1499
1507
1500 def parentpost(repo, subset, x, order):
1508 def parentpost(repo, subset, x, order):
1501 return p1(repo, subset, x)
1509 return p1(repo, subset, x)
1502
1510
1503 @predicate('parents([set])', safe=True)
1511 @predicate('parents([set])', safe=True)
1504 def parents(repo, subset, x):
1512 def parents(repo, subset, x):
1505 """
1513 """
1506 The set of all parents for all changesets in set, or the working directory.
1514 The set of all parents for all changesets in set, or the working directory.
1507 """
1515 """
1508 if x is None:
1516 if x is None:
1509 ps = set(p.rev() for p in repo[x].parents())
1517 ps = set(p.rev() for p in repo[x].parents())
1510 else:
1518 else:
1511 ps = set()
1519 ps = set()
1512 cl = repo.changelog
1520 cl = repo.changelog
1513 up = ps.update
1521 up = ps.update
1514 parentrevs = cl.parentrevs
1522 parentrevs = cl.parentrevs
1515 for r in getset(repo, fullreposet(repo), x):
1523 for r in getset(repo, fullreposet(repo), x):
1516 try:
1524 try:
1517 up(parentrevs(r))
1525 up(parentrevs(r))
1518 except error.WdirUnsupported:
1526 except error.WdirUnsupported:
1519 up(p.rev() for p in repo[r].parents())
1527 up(p.rev() for p in repo[r].parents())
1520 ps -= {node.nullrev}
1528 ps -= {node.nullrev}
1521 return subset & ps
1529 return subset & ps
1522
1530
1523 def _phase(repo, subset, *targets):
1531 def _phase(repo, subset, *targets):
1524 """helper to select all rev in <targets> phases"""
1532 """helper to select all rev in <targets> phases"""
1525 return repo._phasecache.getrevset(repo, targets, subset)
1533 return repo._phasecache.getrevset(repo, targets, subset)
1526
1534
1527 @predicate('draft()', safe=True)
1535 @predicate('draft()', safe=True)
1528 def draft(repo, subset, x):
1536 def draft(repo, subset, x):
1529 """Changeset in draft phase."""
1537 """Changeset in draft phase."""
1530 # i18n: "draft" is a keyword
1538 # i18n: "draft" is a keyword
1531 getargs(x, 0, 0, _("draft takes no arguments"))
1539 getargs(x, 0, 0, _("draft takes no arguments"))
1532 target = phases.draft
1540 target = phases.draft
1533 return _phase(repo, subset, target)
1541 return _phase(repo, subset, target)
1534
1542
1535 @predicate('secret()', safe=True)
1543 @predicate('secret()', safe=True)
1536 def secret(repo, subset, x):
1544 def secret(repo, subset, x):
1537 """Changeset in secret phase."""
1545 """Changeset in secret phase."""
1538 # i18n: "secret" is a keyword
1546 # i18n: "secret" is a keyword
1539 getargs(x, 0, 0, _("secret takes no arguments"))
1547 getargs(x, 0, 0, _("secret takes no arguments"))
1540 target = phases.secret
1548 target = phases.secret
1541 return _phase(repo, subset, target)
1549 return _phase(repo, subset, target)
1542
1550
1543 @predicate('stack([revs])', safe=True)
1551 @predicate('stack([revs])', safe=True)
1544 def stack(repo, subset, x):
1552 def stack(repo, subset, x):
1545 """Experimental revset for the stack of changesets or working directory
1553 """Experimental revset for the stack of changesets or working directory
1546 parent. (EXPERIMENTAL)
1554 parent. (EXPERIMENTAL)
1547 """
1555 """
1548 if x is None:
1556 if x is None:
1549 stacks = stackmod.getstack(repo, x)
1557 stacks = stackmod.getstack(repo, x)
1550 else:
1558 else:
1551 stacks = smartset.baseset([])
1559 stacks = smartset.baseset([])
1552 for revision in getset(repo, fullreposet(repo), x):
1560 for revision in getset(repo, fullreposet(repo), x):
1553 currentstack = stackmod.getstack(repo, revision)
1561 currentstack = stackmod.getstack(repo, revision)
1554 stacks = stacks + currentstack
1562 stacks = stacks + currentstack
1555
1563
1556 return subset & stacks
1564 return subset & stacks
1557
1565
1558 def parentspec(repo, subset, x, n, order):
1566 def parentspec(repo, subset, x, n, order):
1559 """``set^0``
1567 """``set^0``
1560 The set.
1568 The set.
1561 ``set^1`` (or ``set^``), ``set^2``
1569 ``set^1`` (or ``set^``), ``set^2``
1562 First or second parent, respectively, of all changesets in set.
1570 First or second parent, respectively, of all changesets in set.
1563 """
1571 """
1564 try:
1572 try:
1565 n = int(n[1])
1573 n = int(n[1])
1566 if n not in (0, 1, 2):
1574 if n not in (0, 1, 2):
1567 raise ValueError
1575 raise ValueError
1568 except (TypeError, ValueError):
1576 except (TypeError, ValueError):
1569 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1577 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1570 ps = set()
1578 ps = set()
1571 cl = repo.changelog
1579 cl = repo.changelog
1572 for r in getset(repo, fullreposet(repo), x):
1580 for r in getset(repo, fullreposet(repo), x):
1573 if n == 0:
1581 if n == 0:
1574 ps.add(r)
1582 ps.add(r)
1575 elif n == 1:
1583 elif n == 1:
1576 try:
1584 try:
1577 ps.add(cl.parentrevs(r)[0])
1585 ps.add(cl.parentrevs(r)[0])
1578 except error.WdirUnsupported:
1586 except error.WdirUnsupported:
1579 ps.add(repo[r].parents()[0].rev())
1587 ps.add(repo[r].parents()[0].rev())
1580 else:
1588 else:
1581 try:
1589 try:
1582 parents = cl.parentrevs(r)
1590 parents = cl.parentrevs(r)
1583 if parents[1] != node.nullrev:
1591 if parents[1] != node.nullrev:
1584 ps.add(parents[1])
1592 ps.add(parents[1])
1585 except error.WdirUnsupported:
1593 except error.WdirUnsupported:
1586 parents = repo[r].parents()
1594 parents = repo[r].parents()
1587 if len(parents) == 2:
1595 if len(parents) == 2:
1588 ps.add(parents[1].rev())
1596 ps.add(parents[1].rev())
1589 return subset & ps
1597 return subset & ps
1590
1598
1591 @predicate('present(set)', safe=True, takeorder=True)
1599 @predicate('present(set)', safe=True, takeorder=True)
1592 def present(repo, subset, x, order):
1600 def present(repo, subset, x, order):
1593 """An empty set, if any revision in set isn't found; otherwise,
1601 """An empty set, if any revision in set isn't found; otherwise,
1594 all revisions in set.
1602 all revisions in set.
1595
1603
1596 If any of specified revisions is not present in the local repository,
1604 If any of specified revisions is not present in the local repository,
1597 the query is normally aborted. But this predicate allows the query
1605 the query is normally aborted. But this predicate allows the query
1598 to continue even in such cases.
1606 to continue even in such cases.
1599 """
1607 """
1600 try:
1608 try:
1601 return getset(repo, subset, x, order)
1609 return getset(repo, subset, x, order)
1602 except error.RepoLookupError:
1610 except error.RepoLookupError:
1603 return baseset()
1611 return baseset()
1604
1612
1605 # for internal use
1613 # for internal use
1606 @predicate('_notpublic', safe=True)
1614 @predicate('_notpublic', safe=True)
1607 def _notpublic(repo, subset, x):
1615 def _notpublic(repo, subset, x):
1608 getargs(x, 0, 0, "_notpublic takes no arguments")
1616 getargs(x, 0, 0, "_notpublic takes no arguments")
1609 return _phase(repo, subset, phases.draft, phases.secret)
1617 return _phase(repo, subset, phases.draft, phases.secret)
1610
1618
1611 # for internal use
1619 # for internal use
1612 @predicate('_phaseandancestors(phasename, set)', safe=True)
1620 @predicate('_phaseandancestors(phasename, set)', safe=True)
1613 def _phaseandancestors(repo, subset, x):
1621 def _phaseandancestors(repo, subset, x):
1614 # equivalent to (phasename() & ancestors(set)) but more efficient
1622 # equivalent to (phasename() & ancestors(set)) but more efficient
1615 # phasename could be one of 'draft', 'secret', or '_notpublic'
1623 # phasename could be one of 'draft', 'secret', or '_notpublic'
1616 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1624 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1617 phasename = getsymbol(args[0])
1625 phasename = getsymbol(args[0])
1618 s = getset(repo, fullreposet(repo), args[1])
1626 s = getset(repo, fullreposet(repo), args[1])
1619
1627
1620 draft = phases.draft
1628 draft = phases.draft
1621 secret = phases.secret
1629 secret = phases.secret
1622 phasenamemap = {
1630 phasenamemap = {
1623 '_notpublic': draft,
1631 '_notpublic': draft,
1624 'draft': draft, # follow secret's ancestors
1632 'draft': draft, # follow secret's ancestors
1625 'secret': secret,
1633 'secret': secret,
1626 }
1634 }
1627 if phasename not in phasenamemap:
1635 if phasename not in phasenamemap:
1628 raise error.ParseError('%r is not a valid phasename' % phasename)
1636 raise error.ParseError('%r is not a valid phasename' % phasename)
1629
1637
1630 minimalphase = phasenamemap[phasename]
1638 minimalphase = phasenamemap[phasename]
1631 getphase = repo._phasecache.phase
1639 getphase = repo._phasecache.phase
1632
1640
1633 def cutfunc(rev):
1641 def cutfunc(rev):
1634 return getphase(repo, rev) < minimalphase
1642 return getphase(repo, rev) < minimalphase
1635
1643
1636 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1644 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1637
1645
1638 if phasename == 'draft': # need to remove secret changesets
1646 if phasename == 'draft': # need to remove secret changesets
1639 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1647 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1640 return subset & revs
1648 return subset & revs
1641
1649
1642 @predicate('public()', safe=True)
1650 @predicate('public()', safe=True)
1643 def public(repo, subset, x):
1651 def public(repo, subset, x):
1644 """Changeset in public phase."""
1652 """Changeset in public phase."""
1645 # i18n: "public" is a keyword
1653 # i18n: "public" is a keyword
1646 getargs(x, 0, 0, _("public takes no arguments"))
1654 getargs(x, 0, 0, _("public takes no arguments"))
1647 return _phase(repo, subset, phases.public)
1655 return _phase(repo, subset, phases.public)
1648
1656
1649 @predicate('remote([id [,path]])', safe=False)
1657 @predicate('remote([id [,path]])', safe=False)
1650 def remote(repo, subset, x):
1658 def remote(repo, subset, x):
1651 """Local revision that corresponds to the given identifier in a
1659 """Local revision that corresponds to the given identifier in a
1652 remote repository, if present. Here, the '.' identifier is a
1660 remote repository, if present. Here, the '.' identifier is a
1653 synonym for the current local branch.
1661 synonym for the current local branch.
1654 """
1662 """
1655
1663
1656 from . import hg # avoid start-up nasties
1664 from . import hg # avoid start-up nasties
1657 # i18n: "remote" is a keyword
1665 # i18n: "remote" is a keyword
1658 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1666 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1659
1667
1660 q = '.'
1668 q = '.'
1661 if len(l) > 0:
1669 if len(l) > 0:
1662 # i18n: "remote" is a keyword
1670 # i18n: "remote" is a keyword
1663 q = getstring(l[0], _("remote requires a string id"))
1671 q = getstring(l[0], _("remote requires a string id"))
1664 if q == '.':
1672 if q == '.':
1665 q = repo['.'].branch()
1673 q = repo['.'].branch()
1666
1674
1667 dest = ''
1675 dest = ''
1668 if len(l) > 1:
1676 if len(l) > 1:
1669 # i18n: "remote" is a keyword
1677 # i18n: "remote" is a keyword
1670 dest = getstring(l[1], _("remote requires a repository path"))
1678 dest = getstring(l[1], _("remote requires a repository path"))
1671 dest = repo.ui.expandpath(dest or 'default')
1679 dest = repo.ui.expandpath(dest or 'default')
1672 dest, branches = hg.parseurl(dest)
1680 dest, branches = hg.parseurl(dest)
1673 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1681 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1674 if revs:
1682 if revs:
1675 revs = [repo.lookup(rev) for rev in revs]
1683 revs = [repo.lookup(rev) for rev in revs]
1676 other = hg.peer(repo, {}, dest)
1684 other = hg.peer(repo, {}, dest)
1677 n = other.lookup(q)
1685 n = other.lookup(q)
1678 if n in repo:
1686 if n in repo:
1679 r = repo[n].rev()
1687 r = repo[n].rev()
1680 if r in subset:
1688 if r in subset:
1681 return baseset([r])
1689 return baseset([r])
1682 return baseset()
1690 return baseset()
1683
1691
1684 @predicate('removes(pattern)', safe=True, weight=30)
1692 @predicate('removes(pattern)', safe=True, weight=30)
1685 def removes(repo, subset, x):
1693 def removes(repo, subset, x):
1686 """Changesets which remove files matching pattern.
1694 """Changesets which remove files matching pattern.
1687
1695
1688 The pattern without explicit kind like ``glob:`` is expected to be
1696 The pattern without explicit kind like ``glob:`` is expected to be
1689 relative to the current directory and match against a file or a
1697 relative to the current directory and match against a file or a
1690 directory.
1698 directory.
1691 """
1699 """
1692 # i18n: "removes" is a keyword
1700 # i18n: "removes" is a keyword
1693 pat = getstring(x, _("removes requires a pattern"))
1701 pat = getstring(x, _("removes requires a pattern"))
1694 return checkstatus(repo, subset, pat, 2)
1702 return checkstatus(repo, subset, pat, 2)
1695
1703
1696 @predicate('rev(number)', safe=True)
1704 @predicate('rev(number)', safe=True)
1697 def rev(repo, subset, x):
1705 def rev(repo, subset, x):
1698 """Revision with the given numeric identifier.
1706 """Revision with the given numeric identifier.
1699 """
1707 """
1700 # i18n: "rev" is a keyword
1708 # i18n: "rev" is a keyword
1701 l = getargs(x, 1, 1, _("rev requires one argument"))
1709 l = getargs(x, 1, 1, _("rev requires one argument"))
1702 try:
1710 try:
1703 # i18n: "rev" is a keyword
1711 # i18n: "rev" is a keyword
1704 l = int(getstring(l[0], _("rev requires a number")))
1712 l = int(getstring(l[0], _("rev requires a number")))
1705 except (TypeError, ValueError):
1713 except (TypeError, ValueError):
1706 # i18n: "rev" is a keyword
1714 # i18n: "rev" is a keyword
1707 raise error.ParseError(_("rev expects a number"))
1715 raise error.ParseError(_("rev expects a number"))
1708 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1716 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1709 return baseset()
1717 return baseset()
1710 return subset & baseset([l])
1718 return subset & baseset([l])
1711
1719
1712 @predicate('matching(revision [, field])', safe=True)
1720 @predicate('matching(revision [, field])', safe=True)
1713 def matching(repo, subset, x):
1721 def matching(repo, subset, x):
1714 """Changesets in which a given set of fields match the set of fields in the
1722 """Changesets in which a given set of fields match the set of fields in the
1715 selected revision or set.
1723 selected revision or set.
1716
1724
1717 To match more than one field pass the list of fields to match separated
1725 To match more than one field pass the list of fields to match separated
1718 by spaces (e.g. ``author description``).
1726 by spaces (e.g. ``author description``).
1719
1727
1720 Valid fields are most regular revision fields and some special fields.
1728 Valid fields are most regular revision fields and some special fields.
1721
1729
1722 Regular revision fields are ``description``, ``author``, ``branch``,
1730 Regular revision fields are ``description``, ``author``, ``branch``,
1723 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1731 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1724 and ``diff``.
1732 and ``diff``.
1725 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1733 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1726 contents of the revision. Two revisions matching their ``diff`` will
1734 contents of the revision. Two revisions matching their ``diff`` will
1727 also match their ``files``.
1735 also match their ``files``.
1728
1736
1729 Special fields are ``summary`` and ``metadata``:
1737 Special fields are ``summary`` and ``metadata``:
1730 ``summary`` matches the first line of the description.
1738 ``summary`` matches the first line of the description.
1731 ``metadata`` is equivalent to matching ``description user date``
1739 ``metadata`` is equivalent to matching ``description user date``
1732 (i.e. it matches the main metadata fields).
1740 (i.e. it matches the main metadata fields).
1733
1741
1734 ``metadata`` is the default field which is used when no fields are
1742 ``metadata`` is the default field which is used when no fields are
1735 specified. You can match more than one field at a time.
1743 specified. You can match more than one field at a time.
1736 """
1744 """
1737 # i18n: "matching" is a keyword
1745 # i18n: "matching" is a keyword
1738 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1746 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1739
1747
1740 revs = getset(repo, fullreposet(repo), l[0])
1748 revs = getset(repo, fullreposet(repo), l[0])
1741
1749
1742 fieldlist = ['metadata']
1750 fieldlist = ['metadata']
1743 if len(l) > 1:
1751 if len(l) > 1:
1744 fieldlist = getstring(l[1],
1752 fieldlist = getstring(l[1],
1745 # i18n: "matching" is a keyword
1753 # i18n: "matching" is a keyword
1746 _("matching requires a string "
1754 _("matching requires a string "
1747 "as its second argument")).split()
1755 "as its second argument")).split()
1748
1756
1749 # Make sure that there are no repeated fields,
1757 # Make sure that there are no repeated fields,
1750 # expand the 'special' 'metadata' field type
1758 # expand the 'special' 'metadata' field type
1751 # and check the 'files' whenever we check the 'diff'
1759 # and check the 'files' whenever we check the 'diff'
1752 fields = []
1760 fields = []
1753 for field in fieldlist:
1761 for field in fieldlist:
1754 if field == 'metadata':
1762 if field == 'metadata':
1755 fields += ['user', 'description', 'date']
1763 fields += ['user', 'description', 'date']
1756 elif field == 'diff':
1764 elif field == 'diff':
1757 # a revision matching the diff must also match the files
1765 # a revision matching the diff must also match the files
1758 # since matching the diff is very costly, make sure to
1766 # since matching the diff is very costly, make sure to
1759 # also match the files first
1767 # also match the files first
1760 fields += ['files', 'diff']
1768 fields += ['files', 'diff']
1761 else:
1769 else:
1762 if field == 'author':
1770 if field == 'author':
1763 field = 'user'
1771 field = 'user'
1764 fields.append(field)
1772 fields.append(field)
1765 fields = set(fields)
1773 fields = set(fields)
1766 if 'summary' in fields and 'description' in fields:
1774 if 'summary' in fields and 'description' in fields:
1767 # If a revision matches its description it also matches its summary
1775 # If a revision matches its description it also matches its summary
1768 fields.discard('summary')
1776 fields.discard('summary')
1769
1777
1770 # We may want to match more than one field
1778 # We may want to match more than one field
1771 # Not all fields take the same amount of time to be matched
1779 # Not all fields take the same amount of time to be matched
1772 # Sort the selected fields in order of increasing matching cost
1780 # Sort the selected fields in order of increasing matching cost
1773 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1781 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1774 'files', 'description', 'substate', 'diff']
1782 'files', 'description', 'substate', 'diff']
1775 def fieldkeyfunc(f):
1783 def fieldkeyfunc(f):
1776 try:
1784 try:
1777 return fieldorder.index(f)
1785 return fieldorder.index(f)
1778 except ValueError:
1786 except ValueError:
1779 # assume an unknown field is very costly
1787 # assume an unknown field is very costly
1780 return len(fieldorder)
1788 return len(fieldorder)
1781 fields = list(fields)
1789 fields = list(fields)
1782 fields.sort(key=fieldkeyfunc)
1790 fields.sort(key=fieldkeyfunc)
1783
1791
1784 # Each field will be matched with its own "getfield" function
1792 # Each field will be matched with its own "getfield" function
1785 # which will be added to the getfieldfuncs array of functions
1793 # which will be added to the getfieldfuncs array of functions
1786 getfieldfuncs = []
1794 getfieldfuncs = []
1787 _funcs = {
1795 _funcs = {
1788 'user': lambda r: repo[r].user(),
1796 'user': lambda r: repo[r].user(),
1789 'branch': lambda r: repo[r].branch(),
1797 'branch': lambda r: repo[r].branch(),
1790 'date': lambda r: repo[r].date(),
1798 'date': lambda r: repo[r].date(),
1791 'description': lambda r: repo[r].description(),
1799 'description': lambda r: repo[r].description(),
1792 'files': lambda r: repo[r].files(),
1800 'files': lambda r: repo[r].files(),
1793 'parents': lambda r: repo[r].parents(),
1801 'parents': lambda r: repo[r].parents(),
1794 'phase': lambda r: repo[r].phase(),
1802 'phase': lambda r: repo[r].phase(),
1795 'substate': lambda r: repo[r].substate,
1803 'substate': lambda r: repo[r].substate,
1796 'summary': lambda r: repo[r].description().splitlines()[0],
1804 'summary': lambda r: repo[r].description().splitlines()[0],
1797 'diff': lambda r: list(repo[r].diff(git=True),)
1805 'diff': lambda r: list(repo[r].diff(git=True),)
1798 }
1806 }
1799 for info in fields:
1807 for info in fields:
1800 getfield = _funcs.get(info, None)
1808 getfield = _funcs.get(info, None)
1801 if getfield is None:
1809 if getfield is None:
1802 raise error.ParseError(
1810 raise error.ParseError(
1803 # i18n: "matching" is a keyword
1811 # i18n: "matching" is a keyword
1804 _("unexpected field name passed to matching: %s") % info)
1812 _("unexpected field name passed to matching: %s") % info)
1805 getfieldfuncs.append(getfield)
1813 getfieldfuncs.append(getfield)
1806 # convert the getfield array of functions into a "getinfo" function
1814 # convert the getfield array of functions into a "getinfo" function
1807 # which returns an array of field values (or a single value if there
1815 # which returns an array of field values (or a single value if there
1808 # is only one field to match)
1816 # is only one field to match)
1809 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1817 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1810
1818
1811 def matches(x):
1819 def matches(x):
1812 for rev in revs:
1820 for rev in revs:
1813 target = getinfo(rev)
1821 target = getinfo(rev)
1814 match = True
1822 match = True
1815 for n, f in enumerate(getfieldfuncs):
1823 for n, f in enumerate(getfieldfuncs):
1816 if target[n] != f(x):
1824 if target[n] != f(x):
1817 match = False
1825 match = False
1818 if match:
1826 if match:
1819 return True
1827 return True
1820 return False
1828 return False
1821
1829
1822 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1830 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1823
1831
1824 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1832 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1825 def reverse(repo, subset, x, order):
1833 def reverse(repo, subset, x, order):
1826 """Reverse order of set.
1834 """Reverse order of set.
1827 """
1835 """
1828 l = getset(repo, subset, x, order)
1836 l = getset(repo, subset, x, order)
1829 if order == defineorder:
1837 if order == defineorder:
1830 l.reverse()
1838 l.reverse()
1831 return l
1839 return l
1832
1840
1833 @predicate('roots(set)', safe=True)
1841 @predicate('roots(set)', safe=True)
1834 def roots(repo, subset, x):
1842 def roots(repo, subset, x):
1835 """Changesets in set with no parent changeset in set.
1843 """Changesets in set with no parent changeset in set.
1836 """
1844 """
1837 s = getset(repo, fullreposet(repo), x)
1845 s = getset(repo, fullreposet(repo), x)
1838 parents = repo.changelog.parentrevs
1846 parents = repo.changelog.parentrevs
1839 def filter(r):
1847 def filter(r):
1840 for p in parents(r):
1848 for p in parents(r):
1841 if 0 <= p and p in s:
1849 if 0 <= p and p in s:
1842 return False
1850 return False
1843 return True
1851 return True
1844 return subset & s.filter(filter, condrepr='<roots>')
1852 return subset & s.filter(filter, condrepr='<roots>')
1845
1853
1846 _sortkeyfuncs = {
1854 _sortkeyfuncs = {
1847 'rev': lambda c: c.rev(),
1855 'rev': lambda c: c.rev(),
1848 'branch': lambda c: c.branch(),
1856 'branch': lambda c: c.branch(),
1849 'desc': lambda c: c.description(),
1857 'desc': lambda c: c.description(),
1850 'user': lambda c: c.user(),
1858 'user': lambda c: c.user(),
1851 'author': lambda c: c.user(),
1859 'author': lambda c: c.user(),
1852 'date': lambda c: c.date()[0],
1860 'date': lambda c: c.date()[0],
1853 }
1861 }
1854
1862
1855 def _getsortargs(x):
1863 def _getsortargs(x):
1856 """Parse sort options into (set, [(key, reverse)], opts)"""
1864 """Parse sort options into (set, [(key, reverse)], opts)"""
1857 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1865 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1858 if 'set' not in args:
1866 if 'set' not in args:
1859 # i18n: "sort" is a keyword
1867 # i18n: "sort" is a keyword
1860 raise error.ParseError(_('sort requires one or two arguments'))
1868 raise error.ParseError(_('sort requires one or two arguments'))
1861 keys = "rev"
1869 keys = "rev"
1862 if 'keys' in args:
1870 if 'keys' in args:
1863 # i18n: "sort" is a keyword
1871 # i18n: "sort" is a keyword
1864 keys = getstring(args['keys'], _("sort spec must be a string"))
1872 keys = getstring(args['keys'], _("sort spec must be a string"))
1865
1873
1866 keyflags = []
1874 keyflags = []
1867 for k in keys.split():
1875 for k in keys.split():
1868 fk = k
1876 fk = k
1869 reverse = (k.startswith('-'))
1877 reverse = (k.startswith('-'))
1870 if reverse:
1878 if reverse:
1871 k = k[1:]
1879 k = k[1:]
1872 if k not in _sortkeyfuncs and k != 'topo':
1880 if k not in _sortkeyfuncs and k != 'topo':
1873 raise error.ParseError(
1881 raise error.ParseError(
1874 _("unknown sort key %r") % pycompat.bytestr(fk))
1882 _("unknown sort key %r") % pycompat.bytestr(fk))
1875 keyflags.append((k, reverse))
1883 keyflags.append((k, reverse))
1876
1884
1877 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1885 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1878 # i18n: "topo" is a keyword
1886 # i18n: "topo" is a keyword
1879 raise error.ParseError(_('topo sort order cannot be combined '
1887 raise error.ParseError(_('topo sort order cannot be combined '
1880 'with other sort keys'))
1888 'with other sort keys'))
1881
1889
1882 opts = {}
1890 opts = {}
1883 if 'topo.firstbranch' in args:
1891 if 'topo.firstbranch' in args:
1884 if any(k == 'topo' for k, reverse in keyflags):
1892 if any(k == 'topo' for k, reverse in keyflags):
1885 opts['topo.firstbranch'] = args['topo.firstbranch']
1893 opts['topo.firstbranch'] = args['topo.firstbranch']
1886 else:
1894 else:
1887 # i18n: "topo" and "topo.firstbranch" are keywords
1895 # i18n: "topo" and "topo.firstbranch" are keywords
1888 raise error.ParseError(_('topo.firstbranch can only be used '
1896 raise error.ParseError(_('topo.firstbranch can only be used '
1889 'when using the topo sort key'))
1897 'when using the topo sort key'))
1890
1898
1891 return args['set'], keyflags, opts
1899 return args['set'], keyflags, opts
1892
1900
1893 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1901 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1894 weight=10)
1902 weight=10)
1895 def sort(repo, subset, x, order):
1903 def sort(repo, subset, x, order):
1896 """Sort set by keys. The default sort order is ascending, specify a key
1904 """Sort set by keys. The default sort order is ascending, specify a key
1897 as ``-key`` to sort in descending order.
1905 as ``-key`` to sort in descending order.
1898
1906
1899 The keys can be:
1907 The keys can be:
1900
1908
1901 - ``rev`` for the revision number,
1909 - ``rev`` for the revision number,
1902 - ``branch`` for the branch name,
1910 - ``branch`` for the branch name,
1903 - ``desc`` for the commit message (description),
1911 - ``desc`` for the commit message (description),
1904 - ``user`` for user name (``author`` can be used as an alias),
1912 - ``user`` for user name (``author`` can be used as an alias),
1905 - ``date`` for the commit date
1913 - ``date`` for the commit date
1906 - ``topo`` for a reverse topographical sort
1914 - ``topo`` for a reverse topographical sort
1907
1915
1908 The ``topo`` sort order cannot be combined with other sort keys. This sort
1916 The ``topo`` sort order cannot be combined with other sort keys. This sort
1909 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1917 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1910 specifies what topographical branches to prioritize in the sort.
1918 specifies what topographical branches to prioritize in the sort.
1911
1919
1912 """
1920 """
1913 s, keyflags, opts = _getsortargs(x)
1921 s, keyflags, opts = _getsortargs(x)
1914 revs = getset(repo, subset, s, order)
1922 revs = getset(repo, subset, s, order)
1915
1923
1916 if not keyflags or order != defineorder:
1924 if not keyflags or order != defineorder:
1917 return revs
1925 return revs
1918 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1926 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1919 revs.sort(reverse=keyflags[0][1])
1927 revs.sort(reverse=keyflags[0][1])
1920 return revs
1928 return revs
1921 elif keyflags[0][0] == "topo":
1929 elif keyflags[0][0] == "topo":
1922 firstbranch = ()
1930 firstbranch = ()
1923 if 'topo.firstbranch' in opts:
1931 if 'topo.firstbranch' in opts:
1924 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1932 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1925 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1933 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1926 firstbranch),
1934 firstbranch),
1927 istopo=True)
1935 istopo=True)
1928 if keyflags[0][1]:
1936 if keyflags[0][1]:
1929 revs.reverse()
1937 revs.reverse()
1930 return revs
1938 return revs
1931
1939
1932 # sort() is guaranteed to be stable
1940 # sort() is guaranteed to be stable
1933 ctxs = [repo[r] for r in revs]
1941 ctxs = [repo[r] for r in revs]
1934 for k, reverse in reversed(keyflags):
1942 for k, reverse in reversed(keyflags):
1935 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1943 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1936 return baseset([c.rev() for c in ctxs])
1944 return baseset([c.rev() for c in ctxs])
1937
1945
1938 @predicate('subrepo([pattern])')
1946 @predicate('subrepo([pattern])')
1939 def subrepo(repo, subset, x):
1947 def subrepo(repo, subset, x):
1940 """Changesets that add, modify or remove the given subrepo. If no subrepo
1948 """Changesets that add, modify or remove the given subrepo. If no subrepo
1941 pattern is named, any subrepo changes are returned.
1949 pattern is named, any subrepo changes are returned.
1942 """
1950 """
1943 # i18n: "subrepo" is a keyword
1951 # i18n: "subrepo" is a keyword
1944 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1952 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1945 pat = None
1953 pat = None
1946 if len(args) != 0:
1954 if len(args) != 0:
1947 pat = getstring(args[0], _("subrepo requires a pattern"))
1955 pat = getstring(args[0], _("subrepo requires a pattern"))
1948
1956
1949 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1957 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1950
1958
1951 def submatches(names):
1959 def submatches(names):
1952 k, p, m = stringutil.stringmatcher(pat)
1960 k, p, m = stringutil.stringmatcher(pat)
1953 for name in names:
1961 for name in names:
1954 if m(name):
1962 if m(name):
1955 yield name
1963 yield name
1956
1964
1957 def matches(x):
1965 def matches(x):
1958 c = repo[x]
1966 c = repo[x]
1959 s = repo.status(c.p1().node(), c.node(), match=m)
1967 s = repo.status(c.p1().node(), c.node(), match=m)
1960
1968
1961 if pat is None:
1969 if pat is None:
1962 return s.added or s.modified or s.removed
1970 return s.added or s.modified or s.removed
1963
1971
1964 if s.added:
1972 if s.added:
1965 return any(submatches(c.substate.keys()))
1973 return any(submatches(c.substate.keys()))
1966
1974
1967 if s.modified:
1975 if s.modified:
1968 subs = set(c.p1().substate.keys())
1976 subs = set(c.p1().substate.keys())
1969 subs.update(c.substate.keys())
1977 subs.update(c.substate.keys())
1970
1978
1971 for path in submatches(subs):
1979 for path in submatches(subs):
1972 if c.p1().substate.get(path) != c.substate.get(path):
1980 if c.p1().substate.get(path) != c.substate.get(path):
1973 return True
1981 return True
1974
1982
1975 if s.removed:
1983 if s.removed:
1976 return any(submatches(c.p1().substate.keys()))
1984 return any(submatches(c.p1().substate.keys()))
1977
1985
1978 return False
1986 return False
1979
1987
1980 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1988 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1981
1989
1982 def _mapbynodefunc(repo, s, f):
1990 def _mapbynodefunc(repo, s, f):
1983 """(repo, smartset, [node] -> [node]) -> smartset
1991 """(repo, smartset, [node] -> [node]) -> smartset
1984
1992
1985 Helper method to map a smartset to another smartset given a function only
1993 Helper method to map a smartset to another smartset given a function only
1986 talking about nodes. Handles converting between rev numbers and nodes, and
1994 talking about nodes. Handles converting between rev numbers and nodes, and
1987 filtering.
1995 filtering.
1988 """
1996 """
1989 cl = repo.unfiltered().changelog
1997 cl = repo.unfiltered().changelog
1990 torev = cl.rev
1998 torev = cl.rev
1991 tonode = cl.node
1999 tonode = cl.node
1992 nodemap = cl.nodemap
2000 nodemap = cl.nodemap
1993 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
2001 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
1994 return smartset.baseset(result - repo.changelog.filteredrevs)
2002 return smartset.baseset(result - repo.changelog.filteredrevs)
1995
2003
1996 @predicate('successors(set)', safe=True)
2004 @predicate('successors(set)', safe=True)
1997 def successors(repo, subset, x):
2005 def successors(repo, subset, x):
1998 """All successors for set, including the given set themselves"""
2006 """All successors for set, including the given set themselves"""
1999 s = getset(repo, fullreposet(repo), x)
2007 s = getset(repo, fullreposet(repo), x)
2000 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2008 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2001 d = _mapbynodefunc(repo, s, f)
2009 d = _mapbynodefunc(repo, s, f)
2002 return subset & d
2010 return subset & d
2003
2011
2004 def _substringmatcher(pattern, casesensitive=True):
2012 def _substringmatcher(pattern, casesensitive=True):
2005 kind, pattern, matcher = stringutil.stringmatcher(
2013 kind, pattern, matcher = stringutil.stringmatcher(
2006 pattern, casesensitive=casesensitive)
2014 pattern, casesensitive=casesensitive)
2007 if kind == 'literal':
2015 if kind == 'literal':
2008 if not casesensitive:
2016 if not casesensitive:
2009 pattern = encoding.lower(pattern)
2017 pattern = encoding.lower(pattern)
2010 matcher = lambda s: pattern in encoding.lower(s)
2018 matcher = lambda s: pattern in encoding.lower(s)
2011 else:
2019 else:
2012 matcher = lambda s: pattern in s
2020 matcher = lambda s: pattern in s
2013 return kind, pattern, matcher
2021 return kind, pattern, matcher
2014
2022
2015 @predicate('tag([name])', safe=True)
2023 @predicate('tag([name])', safe=True)
2016 def tag(repo, subset, x):
2024 def tag(repo, subset, x):
2017 """The specified tag by name, or all tagged revisions if no name is given.
2025 """The specified tag by name, or all tagged revisions if no name is given.
2018
2026
2019 Pattern matching is supported for `name`. See
2027 Pattern matching is supported for `name`. See
2020 :hg:`help revisions.patterns`.
2028 :hg:`help revisions.patterns`.
2021 """
2029 """
2022 # i18n: "tag" is a keyword
2030 # i18n: "tag" is a keyword
2023 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2031 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2024 cl = repo.changelog
2032 cl = repo.changelog
2025 if args:
2033 if args:
2026 pattern = getstring(args[0],
2034 pattern = getstring(args[0],
2027 # i18n: "tag" is a keyword
2035 # i18n: "tag" is a keyword
2028 _('the argument to tag must be a string'))
2036 _('the argument to tag must be a string'))
2029 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2037 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2030 if kind == 'literal':
2038 if kind == 'literal':
2031 # avoid resolving all tags
2039 # avoid resolving all tags
2032 tn = repo._tagscache.tags.get(pattern, None)
2040 tn = repo._tagscache.tags.get(pattern, None)
2033 if tn is None:
2041 if tn is None:
2034 raise error.RepoLookupError(_("tag '%s' does not exist")
2042 raise error.RepoLookupError(_("tag '%s' does not exist")
2035 % pattern)
2043 % pattern)
2036 s = {repo[tn].rev()}
2044 s = {repo[tn].rev()}
2037 else:
2045 else:
2038 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2046 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2039 else:
2047 else:
2040 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2048 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2041 return subset & s
2049 return subset & s
2042
2050
2043 @predicate('tagged', safe=True)
2051 @predicate('tagged', safe=True)
2044 def tagged(repo, subset, x):
2052 def tagged(repo, subset, x):
2045 return tag(repo, subset, x)
2053 return tag(repo, subset, x)
2046
2054
2047 @predicate('orphan()', safe=True)
2055 @predicate('orphan()', safe=True)
2048 def orphan(repo, subset, x):
2056 def orphan(repo, subset, x):
2049 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2057 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2050 """
2058 """
2051 # i18n: "orphan" is a keyword
2059 # i18n: "orphan" is a keyword
2052 getargs(x, 0, 0, _("orphan takes no arguments"))
2060 getargs(x, 0, 0, _("orphan takes no arguments"))
2053 orphan = obsmod.getrevs(repo, 'orphan')
2061 orphan = obsmod.getrevs(repo, 'orphan')
2054 return subset & orphan
2062 return subset & orphan
2055
2063
2056
2064
2057 @predicate('user(string)', safe=True, weight=10)
2065 @predicate('user(string)', safe=True, weight=10)
2058 def user(repo, subset, x):
2066 def user(repo, subset, x):
2059 """User name contains string. The match is case-insensitive.
2067 """User name contains string. The match is case-insensitive.
2060
2068
2061 Pattern matching is supported for `string`. See
2069 Pattern matching is supported for `string`. See
2062 :hg:`help revisions.patterns`.
2070 :hg:`help revisions.patterns`.
2063 """
2071 """
2064 return author(repo, subset, x)
2072 return author(repo, subset, x)
2065
2073
2066 @predicate('wdir()', safe=True, weight=0)
2074 @predicate('wdir()', safe=True, weight=0)
2067 def wdir(repo, subset, x):
2075 def wdir(repo, subset, x):
2068 """Working directory. (EXPERIMENTAL)"""
2076 """Working directory. (EXPERIMENTAL)"""
2069 # i18n: "wdir" is a keyword
2077 # i18n: "wdir" is a keyword
2070 getargs(x, 0, 0, _("wdir takes no arguments"))
2078 getargs(x, 0, 0, _("wdir takes no arguments"))
2071 if node.wdirrev in subset or isinstance(subset, fullreposet):
2079 if node.wdirrev in subset or isinstance(subset, fullreposet):
2072 return baseset([node.wdirrev])
2080 return baseset([node.wdirrev])
2073 return baseset()
2081 return baseset()
2074
2082
2075 def _orderedlist(repo, subset, x):
2083 def _orderedlist(repo, subset, x):
2076 s = getstring(x, "internal error")
2084 s = getstring(x, "internal error")
2077 if not s:
2085 if not s:
2078 return baseset()
2086 return baseset()
2079 # remove duplicates here. it's difficult for caller to deduplicate sets
2087 # remove duplicates here. it's difficult for caller to deduplicate sets
2080 # because different symbols can point to the same rev.
2088 # because different symbols can point to the same rev.
2081 cl = repo.changelog
2089 cl = repo.changelog
2082 ls = []
2090 ls = []
2083 seen = set()
2091 seen = set()
2084 for t in s.split('\0'):
2092 for t in s.split('\0'):
2085 try:
2093 try:
2086 # fast path for integer revision
2094 # fast path for integer revision
2087 r = int(t)
2095 r = int(t)
2088 if ('%d' % r) != t or r not in cl:
2096 if ('%d' % r) != t or r not in cl:
2089 raise ValueError
2097 raise ValueError
2090 revs = [r]
2098 revs = [r]
2091 except ValueError:
2099 except ValueError:
2092 revs = stringset(repo, subset, t, defineorder)
2100 revs = stringset(repo, subset, t, defineorder)
2093
2101
2094 for r in revs:
2102 for r in revs:
2095 if r in seen:
2103 if r in seen:
2096 continue
2104 continue
2097 if (r in subset
2105 if (r in subset
2098 or r == node.nullrev and isinstance(subset, fullreposet)):
2106 or r == node.nullrev and isinstance(subset, fullreposet)):
2099 ls.append(r)
2107 ls.append(r)
2100 seen.add(r)
2108 seen.add(r)
2101 return baseset(ls)
2109 return baseset(ls)
2102
2110
2103 # for internal use
2111 # for internal use
2104 @predicate('_list', safe=True, takeorder=True)
2112 @predicate('_list', safe=True, takeorder=True)
2105 def _list(repo, subset, x, order):
2113 def _list(repo, subset, x, order):
2106 if order == followorder:
2114 if order == followorder:
2107 # slow path to take the subset order
2115 # slow path to take the subset order
2108 return subset & _orderedlist(repo, fullreposet(repo), x)
2116 return subset & _orderedlist(repo, fullreposet(repo), x)
2109 else:
2117 else:
2110 return _orderedlist(repo, subset, x)
2118 return _orderedlist(repo, subset, x)
2111
2119
2112 def _orderedintlist(repo, subset, x):
2120 def _orderedintlist(repo, subset, x):
2113 s = getstring(x, "internal error")
2121 s = getstring(x, "internal error")
2114 if not s:
2122 if not s:
2115 return baseset()
2123 return baseset()
2116 ls = [int(r) for r in s.split('\0')]
2124 ls = [int(r) for r in s.split('\0')]
2117 s = subset
2125 s = subset
2118 return baseset([r for r in ls if r in s])
2126 return baseset([r for r in ls if r in s])
2119
2127
2120 # for internal use
2128 # for internal use
2121 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2129 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2122 def _intlist(repo, subset, x, order):
2130 def _intlist(repo, subset, x, order):
2123 if order == followorder:
2131 if order == followorder:
2124 # slow path to take the subset order
2132 # slow path to take the subset order
2125 return subset & _orderedintlist(repo, fullreposet(repo), x)
2133 return subset & _orderedintlist(repo, fullreposet(repo), x)
2126 else:
2134 else:
2127 return _orderedintlist(repo, subset, x)
2135 return _orderedintlist(repo, subset, x)
2128
2136
2129 def _orderedhexlist(repo, subset, x):
2137 def _orderedhexlist(repo, subset, x):
2130 s = getstring(x, "internal error")
2138 s = getstring(x, "internal error")
2131 if not s:
2139 if not s:
2132 return baseset()
2140 return baseset()
2133 cl = repo.changelog
2141 cl = repo.changelog
2134 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2142 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2135 s = subset
2143 s = subset
2136 return baseset([r for r in ls if r in s])
2144 return baseset([r for r in ls if r in s])
2137
2145
2138 # for internal use
2146 # for internal use
2139 @predicate('_hexlist', safe=True, takeorder=True)
2147 @predicate('_hexlist', safe=True, takeorder=True)
2140 def _hexlist(repo, subset, x, order):
2148 def _hexlist(repo, subset, x, order):
2141 if order == followorder:
2149 if order == followorder:
2142 # slow path to take the subset order
2150 # slow path to take the subset order
2143 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2151 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2144 else:
2152 else:
2145 return _orderedhexlist(repo, subset, x)
2153 return _orderedhexlist(repo, subset, x)
2146
2154
2147 methods = {
2155 methods = {
2148 "range": rangeset,
2156 "range": rangeset,
2149 "rangeall": rangeall,
2157 "rangeall": rangeall,
2150 "rangepre": rangepre,
2158 "rangepre": rangepre,
2151 "rangepost": rangepost,
2159 "rangepost": rangepost,
2152 "dagrange": dagrange,
2160 "dagrange": dagrange,
2153 "string": stringset,
2161 "string": stringset,
2154 "symbol": stringset,
2162 "symbol": stringset,
2155 "and": andset,
2163 "and": andset,
2156 "andsmally": andsmallyset,
2164 "andsmally": andsmallyset,
2157 "or": orset,
2165 "or": orset,
2158 "not": notset,
2166 "not": notset,
2159 "difference": differenceset,
2167 "difference": differenceset,
2160 "relation": relationset,
2168 "relation": relationset,
2161 "relsubscript": relsubscriptset,
2169 "relsubscript": relsubscriptset,
2162 "subscript": subscriptset,
2170 "subscript": subscriptset,
2163 "list": listset,
2171 "list": listset,
2164 "keyvalue": keyvaluepair,
2172 "keyvalue": keyvaluepair,
2165 "func": func,
2173 "func": func,
2166 "ancestor": ancestorspec,
2174 "ancestor": ancestorspec,
2167 "parent": parentspec,
2175 "parent": parentspec,
2168 "parentpost": parentpost,
2176 "parentpost": parentpost,
2169 }
2177 }
2170
2178
2171 def lookupfn(repo):
2179 def lookupfn(repo):
2172 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2180 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2173
2181
2174 def match(ui, spec, lookup=None):
2182 def match(ui, spec, lookup=None):
2175 """Create a matcher for a single revision spec"""
2183 """Create a matcher for a single revision spec"""
2176 return matchany(ui, [spec], lookup=lookup)
2184 return matchany(ui, [spec], lookup=lookup)
2177
2185
2178 def matchany(ui, specs, lookup=None, localalias=None):
2186 def matchany(ui, specs, lookup=None, localalias=None):
2179 """Create a matcher that will include any revisions matching one of the
2187 """Create a matcher that will include any revisions matching one of the
2180 given specs
2188 given specs
2181
2189
2182 If lookup function is not None, the parser will first attempt to handle
2190 If lookup function is not None, the parser will first attempt to handle
2183 old-style ranges, which may contain operator characters.
2191 old-style ranges, which may contain operator characters.
2184
2192
2185 If localalias is not None, it is a dict {name: definitionstring}. It takes
2193 If localalias is not None, it is a dict {name: definitionstring}. It takes
2186 precedence over [revsetalias] config section.
2194 precedence over [revsetalias] config section.
2187 """
2195 """
2188 if not specs:
2196 if not specs:
2189 def mfunc(repo, subset=None):
2197 def mfunc(repo, subset=None):
2190 return baseset()
2198 return baseset()
2191 return mfunc
2199 return mfunc
2192 if not all(specs):
2200 if not all(specs):
2193 raise error.ParseError(_("empty query"))
2201 raise error.ParseError(_("empty query"))
2194 if len(specs) == 1:
2202 if len(specs) == 1:
2195 tree = revsetlang.parse(specs[0], lookup)
2203 tree = revsetlang.parse(specs[0], lookup)
2196 else:
2204 else:
2197 tree = ('or',
2205 tree = ('or',
2198 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2206 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2199
2207
2200 aliases = []
2208 aliases = []
2201 warn = None
2209 warn = None
2202 if ui:
2210 if ui:
2203 aliases.extend(ui.configitems('revsetalias'))
2211 aliases.extend(ui.configitems('revsetalias'))
2204 warn = ui.warn
2212 warn = ui.warn
2205 if localalias:
2213 if localalias:
2206 aliases.extend(localalias.items())
2214 aliases.extend(localalias.items())
2207 if aliases:
2215 if aliases:
2208 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2216 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2209 tree = revsetlang.foldconcat(tree)
2217 tree = revsetlang.foldconcat(tree)
2210 tree = revsetlang.analyze(tree)
2218 tree = revsetlang.analyze(tree)
2211 tree = revsetlang.optimize(tree)
2219 tree = revsetlang.optimize(tree)
2212 return makematcher(tree)
2220 return makematcher(tree)
2213
2221
2214 def makematcher(tree):
2222 def makematcher(tree):
2215 """Create a matcher from an evaluatable tree"""
2223 """Create a matcher from an evaluatable tree"""
2216 def mfunc(repo, subset=None, order=None):
2224 def mfunc(repo, subset=None, order=None):
2217 if order is None:
2225 if order is None:
2218 if subset is None:
2226 if subset is None:
2219 order = defineorder # 'x'
2227 order = defineorder # 'x'
2220 else:
2228 else:
2221 order = followorder # 'subset & x'
2229 order = followorder # 'subset & x'
2222 if subset is None:
2230 if subset is None:
2223 subset = fullreposet(repo)
2231 subset = fullreposet(repo)
2224 return getset(repo, subset, tree, order)
2232 return getset(repo, subset, tree, order)
2225 return mfunc
2233 return mfunc
2226
2234
2227 def loadpredicate(ui, extname, registrarobj):
2235 def loadpredicate(ui, extname, registrarobj):
2228 """Load revset predicates from specified registrarobj
2236 """Load revset predicates from specified registrarobj
2229 """
2237 """
2230 for name, func in registrarobj._table.iteritems():
2238 for name, func in registrarobj._table.iteritems():
2231 symbols[name] = func
2239 symbols[name] = func
2232 if func._safe:
2240 if func._safe:
2233 safesymbols.add(name)
2241 safesymbols.add(name)
2234
2242
2235 # load built-in predicates explicitly to setup safesymbols
2243 # load built-in predicates explicitly to setup safesymbols
2236 loadpredicate(None, None, predicate)
2244 loadpredicate(None, None, predicate)
2237
2245
2238 # tell hggettext to extract docstrings from these functions:
2246 # tell hggettext to extract docstrings from these functions:
2239 i18nfunctions = symbols.values()
2247 i18nfunctions = symbols.values()
@@ -1,1833 +1,1836 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat >> $HGRCPATH << EOF
3 $ cat >> $HGRCPATH << EOF
4 > [extensions]
4 > [extensions]
5 > drawdag=$TESTDIR/drawdag.py
5 > drawdag=$TESTDIR/drawdag.py
6 > EOF
6 > EOF
7
7
8 $ try() {
8 $ try() {
9 > hg debugrevspec --debug "$@"
9 > hg debugrevspec --debug "$@"
10 > }
10 > }
11
11
12 $ log() {
12 $ log() {
13 > hg log --template '{rev}\n' -r "$1"
13 > hg log --template '{rev}\n' -r "$1"
14 > }
14 > }
15
15
16 $ hg init repo
16 $ hg init repo
17 $ cd repo
17 $ cd repo
18
18
19 $ echo a > a
19 $ echo a > a
20 $ hg branch a
20 $ hg branch a
21 marked working directory as branch a
21 marked working directory as branch a
22 (branches are permanent and global, did you want a bookmark?)
22 (branches are permanent and global, did you want a bookmark?)
23 $ hg ci -Aqm0
23 $ hg ci -Aqm0
24
24
25 $ echo b > b
25 $ echo b > b
26 $ hg branch b
26 $ hg branch b
27 marked working directory as branch b
27 marked working directory as branch b
28 $ hg ci -Aqm1
28 $ hg ci -Aqm1
29
29
30 $ rm a
30 $ rm a
31 $ hg branch a-b-c-
31 $ hg branch a-b-c-
32 marked working directory as branch a-b-c-
32 marked working directory as branch a-b-c-
33 $ hg ci -Aqm2 -u Bob
33 $ hg ci -Aqm2 -u Bob
34
34
35 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
35 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
36 2
36 2
37 $ hg log -r "extra('branch')" --template '{rev}\n'
37 $ hg log -r "extra('branch')" --template '{rev}\n'
38 0
38 0
39 1
39 1
40 2
40 2
41 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
41 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
42 0 a
42 0 a
43 2 a-b-c-
43 2 a-b-c-
44
44
45 $ hg co 1
45 $ hg co 1
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ hg branch +a+b+c+
47 $ hg branch +a+b+c+
48 marked working directory as branch +a+b+c+
48 marked working directory as branch +a+b+c+
49 $ hg ci -Aqm3
49 $ hg ci -Aqm3
50
50
51 $ hg co 2 # interleave
51 $ hg co 2 # interleave
52 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
53 $ echo bb > b
53 $ echo bb > b
54 $ hg branch -- -a-b-c-
54 $ hg branch -- -a-b-c-
55 marked working directory as branch -a-b-c-
55 marked working directory as branch -a-b-c-
56 $ hg ci -Aqm4 -d "May 12 2005"
56 $ hg ci -Aqm4 -d "May 12 2005"
57
57
58 $ hg co 3
58 $ hg co 3
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 $ hg branch !a/b/c/
60 $ hg branch !a/b/c/
61 marked working directory as branch !a/b/c/
61 marked working directory as branch !a/b/c/
62 $ hg ci -Aqm"5 bug"
62 $ hg ci -Aqm"5 bug"
63
63
64 $ hg merge 4
64 $ hg merge 4
65 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
65 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 (branch merge, don't forget to commit)
66 (branch merge, don't forget to commit)
67 $ hg branch _a_b_c_
67 $ hg branch _a_b_c_
68 marked working directory as branch _a_b_c_
68 marked working directory as branch _a_b_c_
69 $ hg ci -Aqm"6 issue619"
69 $ hg ci -Aqm"6 issue619"
70
70
71 $ hg branch .a.b.c.
71 $ hg branch .a.b.c.
72 marked working directory as branch .a.b.c.
72 marked working directory as branch .a.b.c.
73 $ hg ci -Aqm7
73 $ hg ci -Aqm7
74
74
75 $ hg branch all
75 $ hg branch all
76 marked working directory as branch all
76 marked working directory as branch all
77
77
78 $ hg co 4
78 $ hg co 4
79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 $ hg branch Γ©
80 $ hg branch Γ©
81 marked working directory as branch \xc3\xa9 (esc)
81 marked working directory as branch \xc3\xa9 (esc)
82 $ hg ci -Aqm9
82 $ hg ci -Aqm9
83
83
84 $ hg tag -r6 1.0
84 $ hg tag -r6 1.0
85 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
85 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
86
86
87 $ hg clone --quiet -U -r 7 . ../remote1
87 $ hg clone --quiet -U -r 7 . ../remote1
88 $ hg clone --quiet -U -r 8 . ../remote2
88 $ hg clone --quiet -U -r 8 . ../remote2
89 $ echo "[paths]" >> .hg/hgrc
89 $ echo "[paths]" >> .hg/hgrc
90 $ echo "default = ../remote1" >> .hg/hgrc
90 $ echo "default = ../remote1" >> .hg/hgrc
91
91
92 test subtracting something from an addset
92 test subtracting something from an addset
93
93
94 $ log '(outgoing() or removes(a)) - removes(a)'
94 $ log '(outgoing() or removes(a)) - removes(a)'
95 8
95 8
96 9
96 9
97
97
98 test intersecting something with an addset
98 test intersecting something with an addset
99
99
100 $ log 'parents(outgoing() or removes(a))'
100 $ log 'parents(outgoing() or removes(a))'
101 1
101 1
102 4
102 4
103 5
103 5
104 8
104 8
105
105
106 test that `or` operation combines elements in the right order:
106 test that `or` operation combines elements in the right order:
107
107
108 $ log '3:4 or 2:5'
108 $ log '3:4 or 2:5'
109 3
109 3
110 4
110 4
111 2
111 2
112 5
112 5
113 $ log '3:4 or 5:2'
113 $ log '3:4 or 5:2'
114 3
114 3
115 4
115 4
116 5
116 5
117 2
117 2
118 $ log 'sort(3:4 or 2:5)'
118 $ log 'sort(3:4 or 2:5)'
119 2
119 2
120 3
120 3
121 4
121 4
122 5
122 5
123 $ log 'sort(3:4 or 5:2)'
123 $ log 'sort(3:4 or 5:2)'
124 2
124 2
125 3
125 3
126 4
126 4
127 5
127 5
128
128
129 test that more than one `-r`s are combined in the right order and deduplicated:
129 test that more than one `-r`s are combined in the right order and deduplicated:
130
130
131 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
131 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
132 3
132 3
133 4
133 4
134 5
134 5
135 2
135 2
136 0
136 0
137 1
137 1
138
138
139 test that `or` operation skips duplicated revisions from right-hand side
139 test that `or` operation skips duplicated revisions from right-hand side
140
140
141 $ try 'reverse(1::5) or ancestors(4)'
141 $ try 'reverse(1::5) or ancestors(4)'
142 (or
142 (or
143 (list
143 (list
144 (func
144 (func
145 (symbol 'reverse')
145 (symbol 'reverse')
146 (dagrange
146 (dagrange
147 (symbol '1')
147 (symbol '1')
148 (symbol '5')))
148 (symbol '5')))
149 (func
149 (func
150 (symbol 'ancestors')
150 (symbol 'ancestors')
151 (symbol '4'))))
151 (symbol '4'))))
152 * set:
152 * set:
153 <addset
153 <addset
154 <baseset- [1, 3, 5]>,
154 <baseset- [1, 3, 5]>,
155 <generatorsetdesc+>>
155 <generatorsetdesc+>>
156 5
156 5
157 3
157 3
158 1
158 1
159 0
159 0
160 2
160 2
161 4
161 4
162 $ try 'sort(ancestors(4) or reverse(1::5))'
162 $ try 'sort(ancestors(4) or reverse(1::5))'
163 (func
163 (func
164 (symbol 'sort')
164 (symbol 'sort')
165 (or
165 (or
166 (list
166 (list
167 (func
167 (func
168 (symbol 'ancestors')
168 (symbol 'ancestors')
169 (symbol '4'))
169 (symbol '4'))
170 (func
170 (func
171 (symbol 'reverse')
171 (symbol 'reverse')
172 (dagrange
172 (dagrange
173 (symbol '1')
173 (symbol '1')
174 (symbol '5'))))))
174 (symbol '5'))))))
175 * set:
175 * set:
176 <addset+
176 <addset+
177 <generatorsetdesc+>,
177 <generatorsetdesc+>,
178 <baseset- [1, 3, 5]>>
178 <baseset- [1, 3, 5]>>
179 0
179 0
180 1
180 1
181 2
181 2
182 3
182 3
183 4
183 4
184 5
184 5
185
185
186 test optimization of trivial `or` operation
186 test optimization of trivial `or` operation
187
187
188 $ try --optimize '0|(1)|"2"|-2|tip|null'
188 $ try --optimize '0|(1)|"2"|-2|tip|null'
189 (or
189 (or
190 (list
190 (list
191 (symbol '0')
191 (symbol '0')
192 (group
192 (group
193 (symbol '1'))
193 (symbol '1'))
194 (string '2')
194 (string '2')
195 (negate
195 (negate
196 (symbol '2'))
196 (symbol '2'))
197 (symbol 'tip')
197 (symbol 'tip')
198 (symbol 'null')))
198 (symbol 'null')))
199 * optimized:
199 * optimized:
200 (func
200 (func
201 (symbol '_list')
201 (symbol '_list')
202 (string '0\x001\x002\x00-2\x00tip\x00null'))
202 (string '0\x001\x002\x00-2\x00tip\x00null'))
203 * set:
203 * set:
204 <baseset [0, 1, 2, 8, 9, -1]>
204 <baseset [0, 1, 2, 8, 9, -1]>
205 0
205 0
206 1
206 1
207 2
207 2
208 8
208 8
209 9
209 9
210 -1
210 -1
211
211
212 $ try --optimize '0|1|2:3'
212 $ try --optimize '0|1|2:3'
213 (or
213 (or
214 (list
214 (list
215 (symbol '0')
215 (symbol '0')
216 (symbol '1')
216 (symbol '1')
217 (range
217 (range
218 (symbol '2')
218 (symbol '2')
219 (symbol '3'))))
219 (symbol '3'))))
220 * optimized:
220 * optimized:
221 (or
221 (or
222 (list
222 (list
223 (func
223 (func
224 (symbol '_list')
224 (symbol '_list')
225 (string '0\x001'))
225 (string '0\x001'))
226 (range
226 (range
227 (symbol '2')
227 (symbol '2')
228 (symbol '3'))))
228 (symbol '3'))))
229 * set:
229 * set:
230 <addset
230 <addset
231 <baseset [0, 1]>,
231 <baseset [0, 1]>,
232 <spanset+ 2:4>>
232 <spanset+ 2:4>>
233 0
233 0
234 1
234 1
235 2
235 2
236 3
236 3
237
237
238 $ try --optimize '0:1|2|3:4|5|6'
238 $ try --optimize '0:1|2|3:4|5|6'
239 (or
239 (or
240 (list
240 (list
241 (range
241 (range
242 (symbol '0')
242 (symbol '0')
243 (symbol '1'))
243 (symbol '1'))
244 (symbol '2')
244 (symbol '2')
245 (range
245 (range
246 (symbol '3')
246 (symbol '3')
247 (symbol '4'))
247 (symbol '4'))
248 (symbol '5')
248 (symbol '5')
249 (symbol '6')))
249 (symbol '6')))
250 * optimized:
250 * optimized:
251 (or
251 (or
252 (list
252 (list
253 (range
253 (range
254 (symbol '0')
254 (symbol '0')
255 (symbol '1'))
255 (symbol '1'))
256 (symbol '2')
256 (symbol '2')
257 (range
257 (range
258 (symbol '3')
258 (symbol '3')
259 (symbol '4'))
259 (symbol '4'))
260 (func
260 (func
261 (symbol '_list')
261 (symbol '_list')
262 (string '5\x006'))))
262 (string '5\x006'))))
263 * set:
263 * set:
264 <addset
264 <addset
265 <addset
265 <addset
266 <spanset+ 0:2>,
266 <spanset+ 0:2>,
267 <baseset [2]>>,
267 <baseset [2]>>,
268 <addset
268 <addset
269 <spanset+ 3:5>,
269 <spanset+ 3:5>,
270 <baseset [5, 6]>>>
270 <baseset [5, 6]>>>
271 0
271 0
272 1
272 1
273 2
273 2
274 3
274 3
275 4
275 4
276 5
276 5
277 6
277 6
278
278
279 unoptimized `or` looks like this
279 unoptimized `or` looks like this
280
280
281 $ try --no-optimized -p analyzed '0|1|2|3|4'
281 $ try --no-optimized -p analyzed '0|1|2|3|4'
282 * analyzed:
282 * analyzed:
283 (or
283 (or
284 (list
284 (list
285 (symbol '0')
285 (symbol '0')
286 (symbol '1')
286 (symbol '1')
287 (symbol '2')
287 (symbol '2')
288 (symbol '3')
288 (symbol '3')
289 (symbol '4')))
289 (symbol '4')))
290 * set:
290 * set:
291 <addset
291 <addset
292 <addset
292 <addset
293 <baseset [0]>,
293 <baseset [0]>,
294 <baseset [1]>>,
294 <baseset [1]>>,
295 <addset
295 <addset
296 <baseset [2]>,
296 <baseset [2]>,
297 <addset
297 <addset
298 <baseset [3]>,
298 <baseset [3]>,
299 <baseset [4]>>>>
299 <baseset [4]>>>>
300 0
300 0
301 1
301 1
302 2
302 2
303 3
303 3
304 4
304 4
305
305
306 test that `_list` should be narrowed by provided `subset`
306 test that `_list` should be narrowed by provided `subset`
307
307
308 $ log '0:2 and (null|1|2|3)'
308 $ log '0:2 and (null|1|2|3)'
309 1
309 1
310 2
310 2
311
311
312 test that `_list` should remove duplicates
312 test that `_list` should remove duplicates
313
313
314 $ log '0|1|2|1|2|-1|tip'
314 $ log '0|1|2|1|2|-1|tip'
315 0
315 0
316 1
316 1
317 2
317 2
318 9
318 9
319
319
320 test unknown revision in `_list`
320 test unknown revision in `_list`
321
321
322 $ log '0|unknown'
322 $ log '0|unknown'
323 abort: unknown revision 'unknown'!
323 abort: unknown revision 'unknown'!
324 [255]
324 [255]
325
325
326 test integer range in `_list`
326 test integer range in `_list`
327
327
328 $ log '-1|-10'
328 $ log '-1|-10'
329 9
329 9
330 0
330 0
331
331
332 $ log '-10|-11'
332 $ log '-10|-11'
333 abort: unknown revision '-11'!
333 abort: unknown revision '-11'!
334 [255]
334 [255]
335
335
336 $ log '9|10'
336 $ log '9|10'
337 abort: unknown revision '10'!
337 abort: unknown revision '10'!
338 [255]
338 [255]
339
339
340 test '0000' != '0' in `_list`
340 test '0000' != '0' in `_list`
341
341
342 $ log '0|0000'
342 $ log '0|0000'
343 0
343 0
344 -1
344 -1
345
345
346 test ',' in `_list`
346 test ',' in `_list`
347 $ log '0,1'
347 $ log '0,1'
348 hg: parse error: can't use a list in this context
348 hg: parse error: can't use a list in this context
349 (see hg help "revsets.x or y")
349 (see hg help "revsets.x or y")
350 [255]
350 [255]
351 $ try '0,1,2'
351 $ try '0,1,2'
352 (list
352 (list
353 (symbol '0')
353 (symbol '0')
354 (symbol '1')
354 (symbol '1')
355 (symbol '2'))
355 (symbol '2'))
356 hg: parse error: can't use a list in this context
356 hg: parse error: can't use a list in this context
357 (see hg help "revsets.x or y")
357 (see hg help "revsets.x or y")
358 [255]
358 [255]
359
359
360 test that chained `or` operations make balanced addsets
360 test that chained `or` operations make balanced addsets
361
361
362 $ try '0:1|1:2|2:3|3:4|4:5'
362 $ try '0:1|1:2|2:3|3:4|4:5'
363 (or
363 (or
364 (list
364 (list
365 (range
365 (range
366 (symbol '0')
366 (symbol '0')
367 (symbol '1'))
367 (symbol '1'))
368 (range
368 (range
369 (symbol '1')
369 (symbol '1')
370 (symbol '2'))
370 (symbol '2'))
371 (range
371 (range
372 (symbol '2')
372 (symbol '2')
373 (symbol '3'))
373 (symbol '3'))
374 (range
374 (range
375 (symbol '3')
375 (symbol '3')
376 (symbol '4'))
376 (symbol '4'))
377 (range
377 (range
378 (symbol '4')
378 (symbol '4')
379 (symbol '5'))))
379 (symbol '5'))))
380 * set:
380 * set:
381 <addset
381 <addset
382 <addset
382 <addset
383 <spanset+ 0:2>,
383 <spanset+ 0:2>,
384 <spanset+ 1:3>>,
384 <spanset+ 1:3>>,
385 <addset
385 <addset
386 <spanset+ 2:4>,
386 <spanset+ 2:4>,
387 <addset
387 <addset
388 <spanset+ 3:5>,
388 <spanset+ 3:5>,
389 <spanset+ 4:6>>>>
389 <spanset+ 4:6>>>>
390 0
390 0
391 1
391 1
392 2
392 2
393 3
393 3
394 4
394 4
395 5
395 5
396
396
397 no crash by empty group "()" while optimizing `or` operations
397 no crash by empty group "()" while optimizing `or` operations
398
398
399 $ try --optimize '0|()'
399 $ try --optimize '0|()'
400 (or
400 (or
401 (list
401 (list
402 (symbol '0')
402 (symbol '0')
403 (group
403 (group
404 None)))
404 None)))
405 * optimized:
405 * optimized:
406 (or
406 (or
407 (list
407 (list
408 (symbol '0')
408 (symbol '0')
409 None))
409 None))
410 hg: parse error: missing argument
410 hg: parse error: missing argument
411 [255]
411 [255]
412
412
413 test that chained `or` operations never eat up stack (issue4624)
413 test that chained `or` operations never eat up stack (issue4624)
414 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
414 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
415
415
416 $ hg log -T '{rev}\n' -r `$PYTHON -c "print '+'.join(['0:1'] * 500)"`
416 $ hg log -T '{rev}\n' -r `$PYTHON -c "print '+'.join(['0:1'] * 500)"`
417 0
417 0
418 1
418 1
419
419
420 test that repeated `-r` options never eat up stack (issue4565)
420 test that repeated `-r` options never eat up stack (issue4565)
421 (uses `-r 0::1` to avoid possible optimization at old-style parser)
421 (uses `-r 0::1` to avoid possible optimization at old-style parser)
422
422
423 $ hg log -T '{rev}\n' `$PYTHON -c "for i in range(500): print '-r 0::1 ',"`
423 $ hg log -T '{rev}\n' `$PYTHON -c "for i in range(500): print '-r 0::1 ',"`
424 0
424 0
425 1
425 1
426
426
427 check that conversion to only works
427 check that conversion to only works
428 $ try --optimize '::3 - ::1'
428 $ try --optimize '::3 - ::1'
429 (minus
429 (minus
430 (dagrangepre
430 (dagrangepre
431 (symbol '3'))
431 (symbol '3'))
432 (dagrangepre
432 (dagrangepre
433 (symbol '1')))
433 (symbol '1')))
434 * optimized:
434 * optimized:
435 (func
435 (func
436 (symbol 'only')
436 (symbol 'only')
437 (list
437 (list
438 (symbol '3')
438 (symbol '3')
439 (symbol '1')))
439 (symbol '1')))
440 * set:
440 * set:
441 <baseset+ [3]>
441 <baseset+ [3]>
442 3
442 3
443 $ try --optimize 'ancestors(1) - ancestors(3)'
443 $ try --optimize 'ancestors(1) - ancestors(3)'
444 (minus
444 (minus
445 (func
445 (func
446 (symbol 'ancestors')
446 (symbol 'ancestors')
447 (symbol '1'))
447 (symbol '1'))
448 (func
448 (func
449 (symbol 'ancestors')
449 (symbol 'ancestors')
450 (symbol '3')))
450 (symbol '3')))
451 * optimized:
451 * optimized:
452 (func
452 (func
453 (symbol 'only')
453 (symbol 'only')
454 (list
454 (list
455 (symbol '1')
455 (symbol '1')
456 (symbol '3')))
456 (symbol '3')))
457 * set:
457 * set:
458 <baseset+ []>
458 <baseset+ []>
459 $ try --optimize 'not ::2 and ::6'
459 $ try --optimize 'not ::2 and ::6'
460 (and
460 (and
461 (not
461 (not
462 (dagrangepre
462 (dagrangepre
463 (symbol '2')))
463 (symbol '2')))
464 (dagrangepre
464 (dagrangepre
465 (symbol '6')))
465 (symbol '6')))
466 * optimized:
466 * optimized:
467 (func
467 (func
468 (symbol 'only')
468 (symbol 'only')
469 (list
469 (list
470 (symbol '6')
470 (symbol '6')
471 (symbol '2')))
471 (symbol '2')))
472 * set:
472 * set:
473 <baseset+ [3, 4, 5, 6]>
473 <baseset+ [3, 4, 5, 6]>
474 3
474 3
475 4
475 4
476 5
476 5
477 6
477 6
478 $ try --optimize 'ancestors(6) and not ancestors(4)'
478 $ try --optimize 'ancestors(6) and not ancestors(4)'
479 (and
479 (and
480 (func
480 (func
481 (symbol 'ancestors')
481 (symbol 'ancestors')
482 (symbol '6'))
482 (symbol '6'))
483 (not
483 (not
484 (func
484 (func
485 (symbol 'ancestors')
485 (symbol 'ancestors')
486 (symbol '4'))))
486 (symbol '4'))))
487 * optimized:
487 * optimized:
488 (func
488 (func
489 (symbol 'only')
489 (symbol 'only')
490 (list
490 (list
491 (symbol '6')
491 (symbol '6')
492 (symbol '4')))
492 (symbol '4')))
493 * set:
493 * set:
494 <baseset+ [3, 5, 6]>
494 <baseset+ [3, 5, 6]>
495 3
495 3
496 5
496 5
497 6
497 6
498
498
499 no crash by empty group "()" while optimizing to "only()"
499 no crash by empty group "()" while optimizing to "only()"
500
500
501 $ try --optimize '::1 and ()'
501 $ try --optimize '::1 and ()'
502 (and
502 (and
503 (dagrangepre
503 (dagrangepre
504 (symbol '1'))
504 (symbol '1'))
505 (group
505 (group
506 None))
506 None))
507 * optimized:
507 * optimized:
508 (andsmally
508 (andsmally
509 (func
509 (func
510 (symbol 'ancestors')
510 (symbol 'ancestors')
511 (symbol '1'))
511 (symbol '1'))
512 None)
512 None)
513 hg: parse error: missing argument
513 hg: parse error: missing argument
514 [255]
514 [255]
515
515
516 optimization to only() works only if ancestors() takes only one argument
516 optimization to only() works only if ancestors() takes only one argument
517
517
518 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
518 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
519 * optimized:
519 * optimized:
520 (difference
520 (difference
521 (func
521 (func
522 (symbol 'ancestors')
522 (symbol 'ancestors')
523 (symbol '6'))
523 (symbol '6'))
524 (func
524 (func
525 (symbol 'ancestors')
525 (symbol 'ancestors')
526 (list
526 (list
527 (symbol '4')
527 (symbol '4')
528 (symbol '1'))))
528 (symbol '1'))))
529 0
529 0
530 1
530 1
531 3
531 3
532 5
532 5
533 6
533 6
534 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
534 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
535 * optimized:
535 * optimized:
536 (difference
536 (difference
537 (func
537 (func
538 (symbol 'ancestors')
538 (symbol 'ancestors')
539 (list
539 (list
540 (symbol '6')
540 (symbol '6')
541 (symbol '1')))
541 (symbol '1')))
542 (func
542 (func
543 (symbol 'ancestors')
543 (symbol 'ancestors')
544 (symbol '4')))
544 (symbol '4')))
545 5
545 5
546 6
546 6
547
547
548 optimization disabled if keyword arguments passed (because we're too lazy
548 optimization disabled if keyword arguments passed (because we're too lazy
549 to support it)
549 to support it)
550
550
551 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
551 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
552 * optimized:
552 * optimized:
553 (difference
553 (difference
554 (func
554 (func
555 (symbol 'ancestors')
555 (symbol 'ancestors')
556 (keyvalue
556 (keyvalue
557 (symbol 'set')
557 (symbol 'set')
558 (symbol '6')))
558 (symbol '6')))
559 (func
559 (func
560 (symbol 'ancestors')
560 (symbol 'ancestors')
561 (keyvalue
561 (keyvalue
562 (symbol 'set')
562 (symbol 'set')
563 (symbol '4'))))
563 (symbol '4'))))
564 3
564 3
565 5
565 5
566 6
566 6
567
567
568 invalid function call should not be optimized to only()
568 invalid function call should not be optimized to only()
569
569
570 $ log '"ancestors"(6) and not ancestors(4)'
570 $ log '"ancestors"(6) and not ancestors(4)'
571 hg: parse error: not a symbol
571 hg: parse error: not a symbol
572 [255]
572 [255]
573
573
574 $ log 'ancestors(6) and not "ancestors"(4)'
574 $ log 'ancestors(6) and not "ancestors"(4)'
575 hg: parse error: not a symbol
575 hg: parse error: not a symbol
576 [255]
576 [255]
577
577
578 test empty string
578 test empty string
579
579
580 $ log ''
580 $ log ''
581 hg: parse error: empty query
581 hg: parse error: empty query
582 [255]
582 [255]
583 $ log 'parents("")'
583 $ log 'parents("")'
584 hg: parse error: empty string is not a valid revision
584 hg: parse error: empty string is not a valid revision
585 [255]
585 [255]
586
586
587 test empty revset
588 $ hg log 'none()'
589
587 we can use patterns when searching for tags
590 we can use patterns when searching for tags
588
591
589 $ log 'tag("1..*")'
592 $ log 'tag("1..*")'
590 abort: tag '1..*' does not exist!
593 abort: tag '1..*' does not exist!
591 [255]
594 [255]
592 $ log 'tag("re:1..*")'
595 $ log 'tag("re:1..*")'
593 6
596 6
594 $ log 'tag("re:[0-9].[0-9]")'
597 $ log 'tag("re:[0-9].[0-9]")'
595 6
598 6
596 $ log 'tag("literal:1.0")'
599 $ log 'tag("literal:1.0")'
597 6
600 6
598 $ log 'tag("re:0..*")'
601 $ log 'tag("re:0..*")'
599
602
600 $ log 'tag(unknown)'
603 $ log 'tag(unknown)'
601 abort: tag 'unknown' does not exist!
604 abort: tag 'unknown' does not exist!
602 [255]
605 [255]
603 $ log 'tag("re:unknown")'
606 $ log 'tag("re:unknown")'
604 $ log 'present(tag("unknown"))'
607 $ log 'present(tag("unknown"))'
605 $ log 'present(tag("re:unknown"))'
608 $ log 'present(tag("re:unknown"))'
606 $ log 'branch(unknown)'
609 $ log 'branch(unknown)'
607 abort: unknown revision 'unknown'!
610 abort: unknown revision 'unknown'!
608 [255]
611 [255]
609 $ log 'branch("literal:unknown")'
612 $ log 'branch("literal:unknown")'
610 abort: branch 'unknown' does not exist!
613 abort: branch 'unknown' does not exist!
611 [255]
614 [255]
612 $ log 'branch("re:unknown")'
615 $ log 'branch("re:unknown")'
613 $ log 'present(branch("unknown"))'
616 $ log 'present(branch("unknown"))'
614 $ log 'present(branch("re:unknown"))'
617 $ log 'present(branch("re:unknown"))'
615 $ log 'user(bob)'
618 $ log 'user(bob)'
616 2
619 2
617
620
618 $ log '4::8'
621 $ log '4::8'
619 4
622 4
620 8
623 8
621 $ log '4:8'
624 $ log '4:8'
622 4
625 4
623 5
626 5
624 6
627 6
625 7
628 7
626 8
629 8
627
630
628 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
631 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
629 4
632 4
630 2
633 2
631 5
634 5
632
635
633 $ log 'not 0 and 0:2'
636 $ log 'not 0 and 0:2'
634 1
637 1
635 2
638 2
636 $ log 'not 1 and 0:2'
639 $ log 'not 1 and 0:2'
637 0
640 0
638 2
641 2
639 $ log 'not 2 and 0:2'
642 $ log 'not 2 and 0:2'
640 0
643 0
641 1
644 1
642 $ log '(1 and 2)::'
645 $ log '(1 and 2)::'
643 $ log '(1 and 2):'
646 $ log '(1 and 2):'
644 $ log '(1 and 2):3'
647 $ log '(1 and 2):3'
645 $ log 'sort(head(), -rev)'
648 $ log 'sort(head(), -rev)'
646 9
649 9
647 7
650 7
648 6
651 6
649 5
652 5
650 4
653 4
651 3
654 3
652 2
655 2
653 1
656 1
654 0
657 0
655 $ log '4::8 - 8'
658 $ log '4::8 - 8'
656 4
659 4
657
660
658 matching() should preserve the order of the input set:
661 matching() should preserve the order of the input set:
659
662
660 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
663 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
661 2
664 2
662 3
665 3
663 1
666 1
664
667
665 $ log 'named("unknown")'
668 $ log 'named("unknown")'
666 abort: namespace 'unknown' does not exist!
669 abort: namespace 'unknown' does not exist!
667 [255]
670 [255]
668 $ log 'named("re:unknown")'
671 $ log 'named("re:unknown")'
669 abort: no namespace exists that match 'unknown'!
672 abort: no namespace exists that match 'unknown'!
670 [255]
673 [255]
671 $ log 'present(named("unknown"))'
674 $ log 'present(named("unknown"))'
672 $ log 'present(named("re:unknown"))'
675 $ log 'present(named("re:unknown"))'
673
676
674 $ log 'tag()'
677 $ log 'tag()'
675 6
678 6
676 $ log 'named("tags")'
679 $ log 'named("tags")'
677 6
680 6
678
681
679 issue2437
682 issue2437
680
683
681 $ log '3 and p1(5)'
684 $ log '3 and p1(5)'
682 3
685 3
683 $ log '4 and p2(6)'
686 $ log '4 and p2(6)'
684 4
687 4
685 $ log '1 and parents(:2)'
688 $ log '1 and parents(:2)'
686 1
689 1
687 $ log '2 and children(1:)'
690 $ log '2 and children(1:)'
688 2
691 2
689 $ log 'roots(all()) or roots(all())'
692 $ log 'roots(all()) or roots(all())'
690 0
693 0
691 $ hg debugrevspec 'roots(all()) or roots(all())'
694 $ hg debugrevspec 'roots(all()) or roots(all())'
692 0
695 0
693 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
696 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
694 9
697 9
695 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
698 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
696 4
699 4
697
700
698 issue2654: report a parse error if the revset was not completely parsed
701 issue2654: report a parse error if the revset was not completely parsed
699
702
700 $ log '1 OR 2'
703 $ log '1 OR 2'
701 hg: parse error at 2: invalid token
704 hg: parse error at 2: invalid token
702 (1 OR 2
705 (1 OR 2
703 ^ here)
706 ^ here)
704 [255]
707 [255]
705
708
706 or operator should preserve ordering:
709 or operator should preserve ordering:
707 $ log 'reverse(2::4) or tip'
710 $ log 'reverse(2::4) or tip'
708 4
711 4
709 2
712 2
710 9
713 9
711
714
712 parentrevspec
715 parentrevspec
713
716
714 $ log 'merge()^0'
717 $ log 'merge()^0'
715 6
718 6
716 $ log 'merge()^'
719 $ log 'merge()^'
717 5
720 5
718 $ log 'merge()^1'
721 $ log 'merge()^1'
719 5
722 5
720 $ log 'merge()^2'
723 $ log 'merge()^2'
721 4
724 4
722 $ log '(not merge())^2'
725 $ log '(not merge())^2'
723 $ log 'merge()^^'
726 $ log 'merge()^^'
724 3
727 3
725 $ log 'merge()^1^'
728 $ log 'merge()^1^'
726 3
729 3
727 $ log 'merge()^^^'
730 $ log 'merge()^^^'
728 1
731 1
729
732
730 $ hg debugrevspec -s '(merge() | 0)~-1'
733 $ hg debugrevspec -s '(merge() | 0)~-1'
731 * set:
734 * set:
732 <baseset+ [1, 7]>
735 <baseset+ [1, 7]>
733 1
736 1
734 7
737 7
735 $ log 'merge()~-1'
738 $ log 'merge()~-1'
736 7
739 7
737 $ log 'tip~-1'
740 $ log 'tip~-1'
738 $ log '(tip | merge())~-1'
741 $ log '(tip | merge())~-1'
739 7
742 7
740 $ log 'merge()~0'
743 $ log 'merge()~0'
741 6
744 6
742 $ log 'merge()~1'
745 $ log 'merge()~1'
743 5
746 5
744 $ log 'merge()~2'
747 $ log 'merge()~2'
745 3
748 3
746 $ log 'merge()~2^1'
749 $ log 'merge()~2^1'
747 1
750 1
748 $ log 'merge()~3'
751 $ log 'merge()~3'
749 1
752 1
750
753
751 $ log '(-3:tip)^'
754 $ log '(-3:tip)^'
752 4
755 4
753 6
756 6
754 8
757 8
755
758
756 $ log 'tip^foo'
759 $ log 'tip^foo'
757 hg: parse error: ^ expects a number 0, 1, or 2
760 hg: parse error: ^ expects a number 0, 1, or 2
758 [255]
761 [255]
759
762
760 $ log 'branchpoint()~-1'
763 $ log 'branchpoint()~-1'
761 abort: revision in set has more than one child!
764 abort: revision in set has more than one child!
762 [255]
765 [255]
763
766
764 Bogus function gets suggestions
767 Bogus function gets suggestions
765 $ log 'add()'
768 $ log 'add()'
766 hg: parse error: unknown identifier: add
769 hg: parse error: unknown identifier: add
767 (did you mean adds?)
770 (did you mean adds?)
768 [255]
771 [255]
769 $ log 'added()'
772 $ log 'added()'
770 hg: parse error: unknown identifier: added
773 hg: parse error: unknown identifier: added
771 (did you mean adds?)
774 (did you mean adds?)
772 [255]
775 [255]
773 $ log 'remo()'
776 $ log 'remo()'
774 hg: parse error: unknown identifier: remo
777 hg: parse error: unknown identifier: remo
775 (did you mean one of remote, removes?)
778 (did you mean one of remote, removes?)
776 [255]
779 [255]
777 $ log 'babar()'
780 $ log 'babar()'
778 hg: parse error: unknown identifier: babar
781 hg: parse error: unknown identifier: babar
779 [255]
782 [255]
780
783
781 Bogus function with a similar internal name doesn't suggest the internal name
784 Bogus function with a similar internal name doesn't suggest the internal name
782 $ log 'matches()'
785 $ log 'matches()'
783 hg: parse error: unknown identifier: matches
786 hg: parse error: unknown identifier: matches
784 (did you mean matching?)
787 (did you mean matching?)
785 [255]
788 [255]
786
789
787 Undocumented functions aren't suggested as similar either
790 Undocumented functions aren't suggested as similar either
788 $ log 'tagged2()'
791 $ log 'tagged2()'
789 hg: parse error: unknown identifier: tagged2
792 hg: parse error: unknown identifier: tagged2
790 [255]
793 [255]
791
794
792 multiple revspecs
795 multiple revspecs
793
796
794 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
797 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
795 8
798 8
796 9
799 9
797 4
800 4
798 5
801 5
799 6
802 6
800 7
803 7
801
804
802 test usage in revpair (with "+")
805 test usage in revpair (with "+")
803
806
804 (real pair)
807 (real pair)
805
808
806 $ hg diff -r 'tip^^' -r 'tip'
809 $ hg diff -r 'tip^^' -r 'tip'
807 diff -r 2326846efdab -r 24286f4ae135 .hgtags
810 diff -r 2326846efdab -r 24286f4ae135 .hgtags
808 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
811 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
809 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
812 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
810 @@ -0,0 +1,1 @@
813 @@ -0,0 +1,1 @@
811 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
814 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
812 $ hg diff -r 'tip^^::tip'
815 $ hg diff -r 'tip^^::tip'
813 diff -r 2326846efdab -r 24286f4ae135 .hgtags
816 diff -r 2326846efdab -r 24286f4ae135 .hgtags
814 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
817 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
815 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
818 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
816 @@ -0,0 +1,1 @@
819 @@ -0,0 +1,1 @@
817 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
820 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
818
821
819 (single rev)
822 (single rev)
820
823
821 $ hg diff -r 'tip^' -r 'tip^'
824 $ hg diff -r 'tip^' -r 'tip^'
822 $ hg diff -r 'tip^:tip^'
825 $ hg diff -r 'tip^:tip^'
823
826
824 (single rev that does not looks like a range)
827 (single rev that does not looks like a range)
825
828
826 $ hg diff -r 'tip^::tip^ or tip^'
829 $ hg diff -r 'tip^::tip^ or tip^'
827 diff -r d5d0dcbdc4d9 .hgtags
830 diff -r d5d0dcbdc4d9 .hgtags
828 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
831 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
829 +++ b/.hgtags * (glob)
832 +++ b/.hgtags * (glob)
830 @@ -0,0 +1,1 @@
833 @@ -0,0 +1,1 @@
831 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
834 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
832 $ hg diff -r 'tip^ or tip^'
835 $ hg diff -r 'tip^ or tip^'
833 diff -r d5d0dcbdc4d9 .hgtags
836 diff -r d5d0dcbdc4d9 .hgtags
834 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
837 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
835 +++ b/.hgtags * (glob)
838 +++ b/.hgtags * (glob)
836 @@ -0,0 +1,1 @@
839 @@ -0,0 +1,1 @@
837 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
840 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
838
841
839 (no rev)
842 (no rev)
840
843
841 $ hg diff -r 'author("babar") or author("celeste")'
844 $ hg diff -r 'author("babar") or author("celeste")'
842 abort: empty revision range
845 abort: empty revision range
843 [255]
846 [255]
844
847
845 aliases:
848 aliases:
846
849
847 $ echo '[revsetalias]' >> .hg/hgrc
850 $ echo '[revsetalias]' >> .hg/hgrc
848 $ echo 'm = merge()' >> .hg/hgrc
851 $ echo 'm = merge()' >> .hg/hgrc
849 (revset aliases can override builtin revsets)
852 (revset aliases can override builtin revsets)
850 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
853 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
851 $ echo 'sincem = descendants(m)' >> .hg/hgrc
854 $ echo 'sincem = descendants(m)' >> .hg/hgrc
852 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
855 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
853 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
856 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
854 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
857 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
855
858
856 $ try m
859 $ try m
857 (symbol 'm')
860 (symbol 'm')
858 * expanded:
861 * expanded:
859 (func
862 (func
860 (symbol 'merge')
863 (symbol 'merge')
861 None)
864 None)
862 * set:
865 * set:
863 <filteredset
866 <filteredset
864 <fullreposet+ 0:10>,
867 <fullreposet+ 0:10>,
865 <merge>>
868 <merge>>
866 6
869 6
867
870
868 $ HGPLAIN=1
871 $ HGPLAIN=1
869 $ export HGPLAIN
872 $ export HGPLAIN
870 $ try m
873 $ try m
871 (symbol 'm')
874 (symbol 'm')
872 abort: unknown revision 'm'!
875 abort: unknown revision 'm'!
873 [255]
876 [255]
874
877
875 $ HGPLAINEXCEPT=revsetalias
878 $ HGPLAINEXCEPT=revsetalias
876 $ export HGPLAINEXCEPT
879 $ export HGPLAINEXCEPT
877 $ try m
880 $ try m
878 (symbol 'm')
881 (symbol 'm')
879 * expanded:
882 * expanded:
880 (func
883 (func
881 (symbol 'merge')
884 (symbol 'merge')
882 None)
885 None)
883 * set:
886 * set:
884 <filteredset
887 <filteredset
885 <fullreposet+ 0:10>,
888 <fullreposet+ 0:10>,
886 <merge>>
889 <merge>>
887 6
890 6
888
891
889 $ unset HGPLAIN
892 $ unset HGPLAIN
890 $ unset HGPLAINEXCEPT
893 $ unset HGPLAINEXCEPT
891
894
892 $ try 'p2(.)'
895 $ try 'p2(.)'
893 (func
896 (func
894 (symbol 'p2')
897 (symbol 'p2')
895 (symbol '.'))
898 (symbol '.'))
896 * expanded:
899 * expanded:
897 (func
900 (func
898 (symbol 'p1')
901 (symbol 'p1')
899 (symbol '.'))
902 (symbol '.'))
900 * set:
903 * set:
901 <baseset+ [8]>
904 <baseset+ [8]>
902 8
905 8
903
906
904 $ HGPLAIN=1
907 $ HGPLAIN=1
905 $ export HGPLAIN
908 $ export HGPLAIN
906 $ try 'p2(.)'
909 $ try 'p2(.)'
907 (func
910 (func
908 (symbol 'p2')
911 (symbol 'p2')
909 (symbol '.'))
912 (symbol '.'))
910 * set:
913 * set:
911 <baseset+ []>
914 <baseset+ []>
912
915
913 $ HGPLAINEXCEPT=revsetalias
916 $ HGPLAINEXCEPT=revsetalias
914 $ export HGPLAINEXCEPT
917 $ export HGPLAINEXCEPT
915 $ try 'p2(.)'
918 $ try 'p2(.)'
916 (func
919 (func
917 (symbol 'p2')
920 (symbol 'p2')
918 (symbol '.'))
921 (symbol '.'))
919 * expanded:
922 * expanded:
920 (func
923 (func
921 (symbol 'p1')
924 (symbol 'p1')
922 (symbol '.'))
925 (symbol '.'))
923 * set:
926 * set:
924 <baseset+ [8]>
927 <baseset+ [8]>
925 8
928 8
926
929
927 $ unset HGPLAIN
930 $ unset HGPLAIN
928 $ unset HGPLAINEXCEPT
931 $ unset HGPLAINEXCEPT
929
932
930 test alias recursion
933 test alias recursion
931
934
932 $ try sincem
935 $ try sincem
933 (symbol 'sincem')
936 (symbol 'sincem')
934 * expanded:
937 * expanded:
935 (func
938 (func
936 (symbol 'descendants')
939 (symbol 'descendants')
937 (func
940 (func
938 (symbol 'merge')
941 (symbol 'merge')
939 None))
942 None))
940 * set:
943 * set:
941 <generatorsetasc+>
944 <generatorsetasc+>
942 6
945 6
943 7
946 7
944
947
945 test infinite recursion
948 test infinite recursion
946
949
947 $ echo 'recurse1 = recurse2' >> .hg/hgrc
950 $ echo 'recurse1 = recurse2' >> .hg/hgrc
948 $ echo 'recurse2 = recurse1' >> .hg/hgrc
951 $ echo 'recurse2 = recurse1' >> .hg/hgrc
949 $ try recurse1
952 $ try recurse1
950 (symbol 'recurse1')
953 (symbol 'recurse1')
951 hg: parse error: infinite expansion of revset alias "recurse1" detected
954 hg: parse error: infinite expansion of revset alias "recurse1" detected
952 [255]
955 [255]
953
956
954 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
957 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
955 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
958 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
956 $ try "level2(level1(1, 2), 3)"
959 $ try "level2(level1(1, 2), 3)"
957 (func
960 (func
958 (symbol 'level2')
961 (symbol 'level2')
959 (list
962 (list
960 (func
963 (func
961 (symbol 'level1')
964 (symbol 'level1')
962 (list
965 (list
963 (symbol '1')
966 (symbol '1')
964 (symbol '2')))
967 (symbol '2')))
965 (symbol '3')))
968 (symbol '3')))
966 * expanded:
969 * expanded:
967 (or
970 (or
968 (list
971 (list
969 (symbol '3')
972 (symbol '3')
970 (or
973 (or
971 (list
974 (list
972 (symbol '1')
975 (symbol '1')
973 (symbol '2')))))
976 (symbol '2')))))
974 * set:
977 * set:
975 <addset
978 <addset
976 <baseset [3]>,
979 <baseset [3]>,
977 <baseset [1, 2]>>
980 <baseset [1, 2]>>
978 3
981 3
979 1
982 1
980 2
983 2
981
984
982 test nesting and variable passing
985 test nesting and variable passing
983
986
984 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
987 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
985 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
988 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
986 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
989 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
987 $ try 'nested(2:5)'
990 $ try 'nested(2:5)'
988 (func
991 (func
989 (symbol 'nested')
992 (symbol 'nested')
990 (range
993 (range
991 (symbol '2')
994 (symbol '2')
992 (symbol '5')))
995 (symbol '5')))
993 * expanded:
996 * expanded:
994 (func
997 (func
995 (symbol 'max')
998 (symbol 'max')
996 (range
999 (range
997 (symbol '2')
1000 (symbol '2')
998 (symbol '5')))
1001 (symbol '5')))
999 * set:
1002 * set:
1000 <baseset
1003 <baseset
1001 <max
1004 <max
1002 <fullreposet+ 0:10>,
1005 <fullreposet+ 0:10>,
1003 <spanset+ 2:6>>>
1006 <spanset+ 2:6>>>
1004 5
1007 5
1005
1008
1006 test chained `or` operations are flattened at parsing phase
1009 test chained `or` operations are flattened at parsing phase
1007
1010
1008 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1011 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1009 $ try 'chainedorops(0:1, 1:2, 2:3)'
1012 $ try 'chainedorops(0:1, 1:2, 2:3)'
1010 (func
1013 (func
1011 (symbol 'chainedorops')
1014 (symbol 'chainedorops')
1012 (list
1015 (list
1013 (range
1016 (range
1014 (symbol '0')
1017 (symbol '0')
1015 (symbol '1'))
1018 (symbol '1'))
1016 (range
1019 (range
1017 (symbol '1')
1020 (symbol '1')
1018 (symbol '2'))
1021 (symbol '2'))
1019 (range
1022 (range
1020 (symbol '2')
1023 (symbol '2')
1021 (symbol '3'))))
1024 (symbol '3'))))
1022 * expanded:
1025 * expanded:
1023 (or
1026 (or
1024 (list
1027 (list
1025 (range
1028 (range
1026 (symbol '0')
1029 (symbol '0')
1027 (symbol '1'))
1030 (symbol '1'))
1028 (range
1031 (range
1029 (symbol '1')
1032 (symbol '1')
1030 (symbol '2'))
1033 (symbol '2'))
1031 (range
1034 (range
1032 (symbol '2')
1035 (symbol '2')
1033 (symbol '3'))))
1036 (symbol '3'))))
1034 * set:
1037 * set:
1035 <addset
1038 <addset
1036 <spanset+ 0:2>,
1039 <spanset+ 0:2>,
1037 <addset
1040 <addset
1038 <spanset+ 1:3>,
1041 <spanset+ 1:3>,
1039 <spanset+ 2:4>>>
1042 <spanset+ 2:4>>>
1040 0
1043 0
1041 1
1044 1
1042 2
1045 2
1043 3
1046 3
1044
1047
1045 test variable isolation, variable placeholders are rewritten as string
1048 test variable isolation, variable placeholders are rewritten as string
1046 then parsed and matched again as string. Check they do not leak too
1049 then parsed and matched again as string. Check they do not leak too
1047 far away.
1050 far away.
1048
1051
1049 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1052 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1050 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1053 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1051 $ try 'callinjection(2:5)'
1054 $ try 'callinjection(2:5)'
1052 (func
1055 (func
1053 (symbol 'callinjection')
1056 (symbol 'callinjection')
1054 (range
1057 (range
1055 (symbol '2')
1058 (symbol '2')
1056 (symbol '5')))
1059 (symbol '5')))
1057 * expanded:
1060 * expanded:
1058 (func
1061 (func
1059 (symbol 'descendants')
1062 (symbol 'descendants')
1060 (func
1063 (func
1061 (symbol 'max')
1064 (symbol 'max')
1062 (string '$1')))
1065 (string '$1')))
1063 abort: unknown revision '$1'!
1066 abort: unknown revision '$1'!
1064 [255]
1067 [255]
1065
1068
1066 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
1069 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
1067 but 'all()' should never be substituted to '0()'.
1070 but 'all()' should never be substituted to '0()'.
1068
1071
1069 $ echo 'universe = all()' >> .hg/hgrc
1072 $ echo 'universe = all()' >> .hg/hgrc
1070 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
1073 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
1071 $ try 'shadowall(0)'
1074 $ try 'shadowall(0)'
1072 (func
1075 (func
1073 (symbol 'shadowall')
1076 (symbol 'shadowall')
1074 (symbol '0'))
1077 (symbol '0'))
1075 * expanded:
1078 * expanded:
1076 (and
1079 (and
1077 (symbol '0')
1080 (symbol '0')
1078 (func
1081 (func
1079 (symbol 'all')
1082 (symbol 'all')
1080 None))
1083 None))
1081 * set:
1084 * set:
1082 <filteredset
1085 <filteredset
1083 <baseset [0]>,
1086 <baseset [0]>,
1084 <spanset+ 0:10>>
1087 <spanset+ 0:10>>
1085 0
1088 0
1086
1089
1087 test unknown reference:
1090 test unknown reference:
1088
1091
1089 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
1092 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
1090 (func
1093 (func
1091 (symbol 'unknownref')
1094 (symbol 'unknownref')
1092 (symbol '0'))
1095 (symbol '0'))
1093 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
1096 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
1094 [255]
1097 [255]
1095
1098
1096 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1099 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1097 (symbol 'tip')
1100 (symbol 'tip')
1098 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
1101 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
1099 * set:
1102 * set:
1100 <baseset [9]>
1103 <baseset [9]>
1101 9
1104 9
1102
1105
1103 $ try 'tip'
1106 $ try 'tip'
1104 (symbol 'tip')
1107 (symbol 'tip')
1105 * set:
1108 * set:
1106 <baseset [9]>
1109 <baseset [9]>
1107 9
1110 9
1108
1111
1109 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1112 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1110 (symbol 'tip')
1113 (symbol 'tip')
1111 warning: bad declaration of revset alias "bad name": at 4: invalid token
1114 warning: bad declaration of revset alias "bad name": at 4: invalid token
1112 * set:
1115 * set:
1113 <baseset [9]>
1116 <baseset [9]>
1114 9
1117 9
1115 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1118 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1116 $ try 'strictreplacing("foo", tip)'
1119 $ try 'strictreplacing("foo", tip)'
1117 (func
1120 (func
1118 (symbol 'strictreplacing')
1121 (symbol 'strictreplacing')
1119 (list
1122 (list
1120 (string 'foo')
1123 (string 'foo')
1121 (symbol 'tip')))
1124 (symbol 'tip')))
1122 * expanded:
1125 * expanded:
1123 (or
1126 (or
1124 (list
1127 (list
1125 (symbol 'tip')
1128 (symbol 'tip')
1126 (func
1129 (func
1127 (symbol 'desc')
1130 (symbol 'desc')
1128 (string '$1'))))
1131 (string '$1'))))
1129 * set:
1132 * set:
1130 <addset
1133 <addset
1131 <baseset [9]>,
1134 <baseset [9]>,
1132 <filteredset
1135 <filteredset
1133 <fullreposet+ 0:10>,
1136 <fullreposet+ 0:10>,
1134 <desc '$1'>>>
1137 <desc '$1'>>>
1135 9
1138 9
1136
1139
1137 $ try 'd(2:5)'
1140 $ try 'd(2:5)'
1138 (func
1141 (func
1139 (symbol 'd')
1142 (symbol 'd')
1140 (range
1143 (range
1141 (symbol '2')
1144 (symbol '2')
1142 (symbol '5')))
1145 (symbol '5')))
1143 * expanded:
1146 * expanded:
1144 (func
1147 (func
1145 (symbol 'reverse')
1148 (symbol 'reverse')
1146 (func
1149 (func
1147 (symbol 'sort')
1150 (symbol 'sort')
1148 (list
1151 (list
1149 (range
1152 (range
1150 (symbol '2')
1153 (symbol '2')
1151 (symbol '5'))
1154 (symbol '5'))
1152 (symbol 'date'))))
1155 (symbol 'date'))))
1153 * set:
1156 * set:
1154 <baseset [4, 5, 3, 2]>
1157 <baseset [4, 5, 3, 2]>
1155 4
1158 4
1156 5
1159 5
1157 3
1160 3
1158 2
1161 2
1159 $ try 'rs(2 or 3, date)'
1162 $ try 'rs(2 or 3, date)'
1160 (func
1163 (func
1161 (symbol 'rs')
1164 (symbol 'rs')
1162 (list
1165 (list
1163 (or
1166 (or
1164 (list
1167 (list
1165 (symbol '2')
1168 (symbol '2')
1166 (symbol '3')))
1169 (symbol '3')))
1167 (symbol 'date')))
1170 (symbol 'date')))
1168 * expanded:
1171 * expanded:
1169 (func
1172 (func
1170 (symbol 'reverse')
1173 (symbol 'reverse')
1171 (func
1174 (func
1172 (symbol 'sort')
1175 (symbol 'sort')
1173 (list
1176 (list
1174 (or
1177 (or
1175 (list
1178 (list
1176 (symbol '2')
1179 (symbol '2')
1177 (symbol '3')))
1180 (symbol '3')))
1178 (symbol 'date'))))
1181 (symbol 'date'))))
1179 * set:
1182 * set:
1180 <baseset [3, 2]>
1183 <baseset [3, 2]>
1181 3
1184 3
1182 2
1185 2
1183 $ try 'rs()'
1186 $ try 'rs()'
1184 (func
1187 (func
1185 (symbol 'rs')
1188 (symbol 'rs')
1186 None)
1189 None)
1187 hg: parse error: invalid number of arguments: 0
1190 hg: parse error: invalid number of arguments: 0
1188 [255]
1191 [255]
1189 $ try 'rs(2)'
1192 $ try 'rs(2)'
1190 (func
1193 (func
1191 (symbol 'rs')
1194 (symbol 'rs')
1192 (symbol '2'))
1195 (symbol '2'))
1193 hg: parse error: invalid number of arguments: 1
1196 hg: parse error: invalid number of arguments: 1
1194 [255]
1197 [255]
1195 $ try 'rs(2, data, 7)'
1198 $ try 'rs(2, data, 7)'
1196 (func
1199 (func
1197 (symbol 'rs')
1200 (symbol 'rs')
1198 (list
1201 (list
1199 (symbol '2')
1202 (symbol '2')
1200 (symbol 'data')
1203 (symbol 'data')
1201 (symbol '7')))
1204 (symbol '7')))
1202 hg: parse error: invalid number of arguments: 3
1205 hg: parse error: invalid number of arguments: 3
1203 [255]
1206 [255]
1204 $ try 'rs4(2 or 3, x, x, date)'
1207 $ try 'rs4(2 or 3, x, x, date)'
1205 (func
1208 (func
1206 (symbol 'rs4')
1209 (symbol 'rs4')
1207 (list
1210 (list
1208 (or
1211 (or
1209 (list
1212 (list
1210 (symbol '2')
1213 (symbol '2')
1211 (symbol '3')))
1214 (symbol '3')))
1212 (symbol 'x')
1215 (symbol 'x')
1213 (symbol 'x')
1216 (symbol 'x')
1214 (symbol 'date')))
1217 (symbol 'date')))
1215 * expanded:
1218 * expanded:
1216 (func
1219 (func
1217 (symbol 'reverse')
1220 (symbol 'reverse')
1218 (func
1221 (func
1219 (symbol 'sort')
1222 (symbol 'sort')
1220 (list
1223 (list
1221 (or
1224 (or
1222 (list
1225 (list
1223 (symbol '2')
1226 (symbol '2')
1224 (symbol '3')))
1227 (symbol '3')))
1225 (symbol 'date'))))
1228 (symbol 'date'))))
1226 * set:
1229 * set:
1227 <baseset [3, 2]>
1230 <baseset [3, 2]>
1228 3
1231 3
1229 2
1232 2
1230
1233
1231 issue4553: check that revset aliases override existing hash prefix
1234 issue4553: check that revset aliases override existing hash prefix
1232
1235
1233 $ hg log -qr e
1236 $ hg log -qr e
1234 6:e0cc66ef77e8
1237 6:e0cc66ef77e8
1235
1238
1236 $ hg log -qr e --config revsetalias.e="all()"
1239 $ hg log -qr e --config revsetalias.e="all()"
1237 0:2785f51eece5
1240 0:2785f51eece5
1238 1:d75937da8da0
1241 1:d75937da8da0
1239 2:5ed5505e9f1c
1242 2:5ed5505e9f1c
1240 3:8528aa5637f2
1243 3:8528aa5637f2
1241 4:2326846efdab
1244 4:2326846efdab
1242 5:904fa392b941
1245 5:904fa392b941
1243 6:e0cc66ef77e8
1246 6:e0cc66ef77e8
1244 7:013af1973af4
1247 7:013af1973af4
1245 8:d5d0dcbdc4d9
1248 8:d5d0dcbdc4d9
1246 9:24286f4ae135
1249 9:24286f4ae135
1247
1250
1248 $ hg log -qr e: --config revsetalias.e="0"
1251 $ hg log -qr e: --config revsetalias.e="0"
1249 0:2785f51eece5
1252 0:2785f51eece5
1250 1:d75937da8da0
1253 1:d75937da8da0
1251 2:5ed5505e9f1c
1254 2:5ed5505e9f1c
1252 3:8528aa5637f2
1255 3:8528aa5637f2
1253 4:2326846efdab
1256 4:2326846efdab
1254 5:904fa392b941
1257 5:904fa392b941
1255 6:e0cc66ef77e8
1258 6:e0cc66ef77e8
1256 7:013af1973af4
1259 7:013af1973af4
1257 8:d5d0dcbdc4d9
1260 8:d5d0dcbdc4d9
1258 9:24286f4ae135
1261 9:24286f4ae135
1259
1262
1260 $ hg log -qr :e --config revsetalias.e="9"
1263 $ hg log -qr :e --config revsetalias.e="9"
1261 0:2785f51eece5
1264 0:2785f51eece5
1262 1:d75937da8da0
1265 1:d75937da8da0
1263 2:5ed5505e9f1c
1266 2:5ed5505e9f1c
1264 3:8528aa5637f2
1267 3:8528aa5637f2
1265 4:2326846efdab
1268 4:2326846efdab
1266 5:904fa392b941
1269 5:904fa392b941
1267 6:e0cc66ef77e8
1270 6:e0cc66ef77e8
1268 7:013af1973af4
1271 7:013af1973af4
1269 8:d5d0dcbdc4d9
1272 8:d5d0dcbdc4d9
1270 9:24286f4ae135
1273 9:24286f4ae135
1271
1274
1272 $ hg log -qr e:
1275 $ hg log -qr e:
1273 6:e0cc66ef77e8
1276 6:e0cc66ef77e8
1274 7:013af1973af4
1277 7:013af1973af4
1275 8:d5d0dcbdc4d9
1278 8:d5d0dcbdc4d9
1276 9:24286f4ae135
1279 9:24286f4ae135
1277
1280
1278 $ hg log -qr :e
1281 $ hg log -qr :e
1279 0:2785f51eece5
1282 0:2785f51eece5
1280 1:d75937da8da0
1283 1:d75937da8da0
1281 2:5ed5505e9f1c
1284 2:5ed5505e9f1c
1282 3:8528aa5637f2
1285 3:8528aa5637f2
1283 4:2326846efdab
1286 4:2326846efdab
1284 5:904fa392b941
1287 5:904fa392b941
1285 6:e0cc66ef77e8
1288 6:e0cc66ef77e8
1286
1289
1287 issue2549 - correct optimizations
1290 issue2549 - correct optimizations
1288
1291
1289 $ try 'limit(1 or 2 or 3, 2) and not 2'
1292 $ try 'limit(1 or 2 or 3, 2) and not 2'
1290 (and
1293 (and
1291 (func
1294 (func
1292 (symbol 'limit')
1295 (symbol 'limit')
1293 (list
1296 (list
1294 (or
1297 (or
1295 (list
1298 (list
1296 (symbol '1')
1299 (symbol '1')
1297 (symbol '2')
1300 (symbol '2')
1298 (symbol '3')))
1301 (symbol '3')))
1299 (symbol '2')))
1302 (symbol '2')))
1300 (not
1303 (not
1301 (symbol '2')))
1304 (symbol '2')))
1302 * set:
1305 * set:
1303 <filteredset
1306 <filteredset
1304 <baseset [1, 2]>,
1307 <baseset [1, 2]>,
1305 <not
1308 <not
1306 <baseset [2]>>>
1309 <baseset [2]>>>
1307 1
1310 1
1308 $ try 'max(1 or 2) and not 2'
1311 $ try 'max(1 or 2) and not 2'
1309 (and
1312 (and
1310 (func
1313 (func
1311 (symbol 'max')
1314 (symbol 'max')
1312 (or
1315 (or
1313 (list
1316 (list
1314 (symbol '1')
1317 (symbol '1')
1315 (symbol '2'))))
1318 (symbol '2'))))
1316 (not
1319 (not
1317 (symbol '2')))
1320 (symbol '2')))
1318 * set:
1321 * set:
1319 <filteredset
1322 <filteredset
1320 <baseset
1323 <baseset
1321 <max
1324 <max
1322 <fullreposet+ 0:10>,
1325 <fullreposet+ 0:10>,
1323 <baseset [1, 2]>>>,
1326 <baseset [1, 2]>>>,
1324 <not
1327 <not
1325 <baseset [2]>>>
1328 <baseset [2]>>>
1326 $ try 'min(1 or 2) and not 1'
1329 $ try 'min(1 or 2) and not 1'
1327 (and
1330 (and
1328 (func
1331 (func
1329 (symbol 'min')
1332 (symbol 'min')
1330 (or
1333 (or
1331 (list
1334 (list
1332 (symbol '1')
1335 (symbol '1')
1333 (symbol '2'))))
1336 (symbol '2'))))
1334 (not
1337 (not
1335 (symbol '1')))
1338 (symbol '1')))
1336 * set:
1339 * set:
1337 <filteredset
1340 <filteredset
1338 <baseset
1341 <baseset
1339 <min
1342 <min
1340 <fullreposet+ 0:10>,
1343 <fullreposet+ 0:10>,
1341 <baseset [1, 2]>>>,
1344 <baseset [1, 2]>>>,
1342 <not
1345 <not
1343 <baseset [1]>>>
1346 <baseset [1]>>>
1344 $ try 'last(1 or 2, 1) and not 2'
1347 $ try 'last(1 or 2, 1) and not 2'
1345 (and
1348 (and
1346 (func
1349 (func
1347 (symbol 'last')
1350 (symbol 'last')
1348 (list
1351 (list
1349 (or
1352 (or
1350 (list
1353 (list
1351 (symbol '1')
1354 (symbol '1')
1352 (symbol '2')))
1355 (symbol '2')))
1353 (symbol '1')))
1356 (symbol '1')))
1354 (not
1357 (not
1355 (symbol '2')))
1358 (symbol '2')))
1356 * set:
1359 * set:
1357 <filteredset
1360 <filteredset
1358 <baseset [2]>,
1361 <baseset [2]>,
1359 <not
1362 <not
1360 <baseset [2]>>>
1363 <baseset [2]>>>
1361
1364
1362 issue4289 - ordering of built-ins
1365 issue4289 - ordering of built-ins
1363 $ hg log -M -q -r 3:2
1366 $ hg log -M -q -r 3:2
1364 3:8528aa5637f2
1367 3:8528aa5637f2
1365 2:5ed5505e9f1c
1368 2:5ed5505e9f1c
1366
1369
1367 test revsets started with 40-chars hash (issue3669)
1370 test revsets started with 40-chars hash (issue3669)
1368
1371
1369 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1372 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1370 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1373 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1371 9
1374 9
1372 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1375 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1373 8
1376 8
1374
1377
1375 test or-ed indirect predicates (issue3775)
1378 test or-ed indirect predicates (issue3775)
1376
1379
1377 $ log '6 or 6^1' | sort
1380 $ log '6 or 6^1' | sort
1378 5
1381 5
1379 6
1382 6
1380 $ log '6^1 or 6' | sort
1383 $ log '6^1 or 6' | sort
1381 5
1384 5
1382 6
1385 6
1383 $ log '4 or 4~1' | sort
1386 $ log '4 or 4~1' | sort
1384 2
1387 2
1385 4
1388 4
1386 $ log '4~1 or 4' | sort
1389 $ log '4~1 or 4' | sort
1387 2
1390 2
1388 4
1391 4
1389 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1392 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1390 0
1393 0
1391 1
1394 1
1392 2
1395 2
1393 3
1396 3
1394 4
1397 4
1395 5
1398 5
1396 6
1399 6
1397 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1400 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1398 0
1401 0
1399 1
1402 1
1400 2
1403 2
1401 3
1404 3
1402 4
1405 4
1403 5
1406 5
1404 6
1407 6
1405
1408
1406 tests for 'remote()' predicate:
1409 tests for 'remote()' predicate:
1407 #. (csets in remote) (id) (remote)
1410 #. (csets in remote) (id) (remote)
1408 1. less than local current branch "default"
1411 1. less than local current branch "default"
1409 2. same with local specified "default"
1412 2. same with local specified "default"
1410 3. more than local specified specified
1413 3. more than local specified specified
1411
1414
1412 $ hg clone --quiet -U . ../remote3
1415 $ hg clone --quiet -U . ../remote3
1413 $ cd ../remote3
1416 $ cd ../remote3
1414 $ hg update -q 7
1417 $ hg update -q 7
1415 $ echo r > r
1418 $ echo r > r
1416 $ hg ci -Aqm 10
1419 $ hg ci -Aqm 10
1417 $ log 'remote()'
1420 $ log 'remote()'
1418 7
1421 7
1419 $ log 'remote("a-b-c-")'
1422 $ log 'remote("a-b-c-")'
1420 2
1423 2
1421 $ cd ../repo
1424 $ cd ../repo
1422 $ log 'remote(".a.b.c.", "../remote3")'
1425 $ log 'remote(".a.b.c.", "../remote3")'
1423
1426
1424 tests for concatenation of strings/symbols by "##"
1427 tests for concatenation of strings/symbols by "##"
1425
1428
1426 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1429 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1427 (_concat
1430 (_concat
1428 (_concat
1431 (_concat
1429 (_concat
1432 (_concat
1430 (symbol '278')
1433 (symbol '278')
1431 (string '5f5'))
1434 (string '5f5'))
1432 (symbol '1ee'))
1435 (symbol '1ee'))
1433 (string 'ce5'))
1436 (string 'ce5'))
1434 * concatenated:
1437 * concatenated:
1435 (string '2785f51eece5')
1438 (string '2785f51eece5')
1436 * set:
1439 * set:
1437 <baseset [0]>
1440 <baseset [0]>
1438 0
1441 0
1439
1442
1440 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1443 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1441 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1444 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1442 (func
1445 (func
1443 (symbol 'cat4')
1446 (symbol 'cat4')
1444 (list
1447 (list
1445 (symbol '278')
1448 (symbol '278')
1446 (string '5f5')
1449 (string '5f5')
1447 (symbol '1ee')
1450 (symbol '1ee')
1448 (string 'ce5')))
1451 (string 'ce5')))
1449 * expanded:
1452 * expanded:
1450 (_concat
1453 (_concat
1451 (_concat
1454 (_concat
1452 (_concat
1455 (_concat
1453 (symbol '278')
1456 (symbol '278')
1454 (string '5f5'))
1457 (string '5f5'))
1455 (symbol '1ee'))
1458 (symbol '1ee'))
1456 (string 'ce5'))
1459 (string 'ce5'))
1457 * concatenated:
1460 * concatenated:
1458 (string '2785f51eece5')
1461 (string '2785f51eece5')
1459 * set:
1462 * set:
1460 <baseset [0]>
1463 <baseset [0]>
1461 0
1464 0
1462
1465
1463 (check concatenation in alias nesting)
1466 (check concatenation in alias nesting)
1464
1467
1465 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1468 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1466 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1469 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1467 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1470 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1468 0
1471 0
1469
1472
1470 (check operator priority)
1473 (check operator priority)
1471
1474
1472 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1475 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1473 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1476 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1474 0
1477 0
1475 4
1478 4
1476
1479
1477 $ cd ..
1480 $ cd ..
1478
1481
1479 prepare repository that has "default" branches of multiple roots
1482 prepare repository that has "default" branches of multiple roots
1480
1483
1481 $ hg init namedbranch
1484 $ hg init namedbranch
1482 $ cd namedbranch
1485 $ cd namedbranch
1483
1486
1484 $ echo default0 >> a
1487 $ echo default0 >> a
1485 $ hg ci -Aqm0
1488 $ hg ci -Aqm0
1486 $ echo default1 >> a
1489 $ echo default1 >> a
1487 $ hg ci -m1
1490 $ hg ci -m1
1488
1491
1489 $ hg branch -q stable
1492 $ hg branch -q stable
1490 $ echo stable2 >> a
1493 $ echo stable2 >> a
1491 $ hg ci -m2
1494 $ hg ci -m2
1492 $ echo stable3 >> a
1495 $ echo stable3 >> a
1493 $ hg ci -m3
1496 $ hg ci -m3
1494
1497
1495 $ hg update -q null
1498 $ hg update -q null
1496 $ echo default4 >> a
1499 $ echo default4 >> a
1497 $ hg ci -Aqm4
1500 $ hg ci -Aqm4
1498 $ echo default5 >> a
1501 $ echo default5 >> a
1499 $ hg ci -m5
1502 $ hg ci -m5
1500
1503
1501 "null" revision belongs to "default" branch (issue4683)
1504 "null" revision belongs to "default" branch (issue4683)
1502
1505
1503 $ log 'branch(null)'
1506 $ log 'branch(null)'
1504 0
1507 0
1505 1
1508 1
1506 4
1509 4
1507 5
1510 5
1508
1511
1509 "null" revision belongs to "default" branch, but it shouldn't appear in set
1512 "null" revision belongs to "default" branch, but it shouldn't appear in set
1510 unless explicitly specified (issue4682)
1513 unless explicitly specified (issue4682)
1511
1514
1512 $ log 'children(branch(default))'
1515 $ log 'children(branch(default))'
1513 1
1516 1
1514 2
1517 2
1515 5
1518 5
1516
1519
1517 $ cd ..
1520 $ cd ..
1518
1521
1519 test author/desc/keyword in problematic encoding
1522 test author/desc/keyword in problematic encoding
1520 # unicode: cp932:
1523 # unicode: cp932:
1521 # u30A2 0x83 0x41(= 'A')
1524 # u30A2 0x83 0x41(= 'A')
1522 # u30C2 0x83 0x61(= 'a')
1525 # u30C2 0x83 0x61(= 'a')
1523
1526
1524 $ hg init problematicencoding
1527 $ hg init problematicencoding
1525 $ cd problematicencoding
1528 $ cd problematicencoding
1526
1529
1527 $ $PYTHON > setup.sh <<EOF
1530 $ $PYTHON > setup.sh <<EOF
1528 > print u'''
1531 > print u'''
1529 > echo a > text
1532 > echo a > text
1530 > hg add text
1533 > hg add text
1531 > hg --encoding utf-8 commit -u '\u30A2' -m none
1534 > hg --encoding utf-8 commit -u '\u30A2' -m none
1532 > echo b > text
1535 > echo b > text
1533 > hg --encoding utf-8 commit -u '\u30C2' -m none
1536 > hg --encoding utf-8 commit -u '\u30C2' -m none
1534 > echo c > text
1537 > echo c > text
1535 > hg --encoding utf-8 commit -u none -m '\u30A2'
1538 > hg --encoding utf-8 commit -u none -m '\u30A2'
1536 > echo d > text
1539 > echo d > text
1537 > hg --encoding utf-8 commit -u none -m '\u30C2'
1540 > hg --encoding utf-8 commit -u none -m '\u30C2'
1538 > '''.encode('utf-8')
1541 > '''.encode('utf-8')
1539 > EOF
1542 > EOF
1540 $ sh < setup.sh
1543 $ sh < setup.sh
1541
1544
1542 test in problematic encoding
1545 test in problematic encoding
1543 $ $PYTHON > test.sh <<EOF
1546 $ $PYTHON > test.sh <<EOF
1544 > print u'''
1547 > print u'''
1545 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1548 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1546 > echo ====
1549 > echo ====
1547 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1550 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1548 > echo ====
1551 > echo ====
1549 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1552 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1550 > echo ====
1553 > echo ====
1551 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1554 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1552 > echo ====
1555 > echo ====
1553 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1556 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1554 > echo ====
1557 > echo ====
1555 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1558 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1556 > '''.encode('cp932')
1559 > '''.encode('cp932')
1557 > EOF
1560 > EOF
1558 $ sh < test.sh
1561 $ sh < test.sh
1559 0
1562 0
1560 ====
1563 ====
1561 1
1564 1
1562 ====
1565 ====
1563 2
1566 2
1564 ====
1567 ====
1565 3
1568 3
1566 ====
1569 ====
1567 0
1570 0
1568 2
1571 2
1569 ====
1572 ====
1570 1
1573 1
1571 3
1574 3
1572
1575
1573 test error message of bad revset
1576 test error message of bad revset
1574 $ hg log -r 'foo\\'
1577 $ hg log -r 'foo\\'
1575 hg: parse error at 3: syntax error in revset 'foo\\'
1578 hg: parse error at 3: syntax error in revset 'foo\\'
1576 (foo\\
1579 (foo\\
1577 ^ here)
1580 ^ here)
1578 [255]
1581 [255]
1579
1582
1580 $ cd ..
1583 $ cd ..
1581
1584
1582 Test that revset predicate of extension isn't loaded at failure of
1585 Test that revset predicate of extension isn't loaded at failure of
1583 loading it
1586 loading it
1584
1587
1585 $ cd repo
1588 $ cd repo
1586
1589
1587 $ cat <<EOF > $TESTTMP/custompredicate.py
1590 $ cat <<EOF > $TESTTMP/custompredicate.py
1588 > from mercurial import error, registrar, revset
1591 > from mercurial import error, registrar, revset
1589 >
1592 >
1590 > revsetpredicate = registrar.revsetpredicate()
1593 > revsetpredicate = registrar.revsetpredicate()
1591 >
1594 >
1592 > @revsetpredicate(b'custom1()')
1595 > @revsetpredicate(b'custom1()')
1593 > def custom1(repo, subset, x):
1596 > def custom1(repo, subset, x):
1594 > return revset.baseset([1])
1597 > return revset.baseset([1])
1595 >
1598 >
1596 > raise error.Abort(b'intentional failure of loading extension')
1599 > raise error.Abort(b'intentional failure of loading extension')
1597 > EOF
1600 > EOF
1598 $ cat <<EOF > .hg/hgrc
1601 $ cat <<EOF > .hg/hgrc
1599 > [extensions]
1602 > [extensions]
1600 > custompredicate = $TESTTMP/custompredicate.py
1603 > custompredicate = $TESTTMP/custompredicate.py
1601 > EOF
1604 > EOF
1602
1605
1603 $ hg debugrevspec "custom1()"
1606 $ hg debugrevspec "custom1()"
1604 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
1607 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
1605 hg: parse error: unknown identifier: custom1
1608 hg: parse error: unknown identifier: custom1
1606 [255]
1609 [255]
1607
1610
1608 Test repo.anyrevs with customized revset overrides
1611 Test repo.anyrevs with customized revset overrides
1609
1612
1610 $ cat > $TESTTMP/printprevset.py <<EOF
1613 $ cat > $TESTTMP/printprevset.py <<EOF
1611 > from mercurial import encoding, registrar
1614 > from mercurial import encoding, registrar
1612 > cmdtable = {}
1615 > cmdtable = {}
1613 > command = registrar.command(cmdtable)
1616 > command = registrar.command(cmdtable)
1614 > @command(b'printprevset')
1617 > @command(b'printprevset')
1615 > def printprevset(ui, repo):
1618 > def printprevset(ui, repo):
1616 > alias = {}
1619 > alias = {}
1617 > p = encoding.environ.get(b'P')
1620 > p = encoding.environ.get(b'P')
1618 > if p:
1621 > if p:
1619 > alias[b'P'] = p
1622 > alias[b'P'] = p
1620 > revs = repo.anyrevs([b'P'], user=True, localalias=alias)
1623 > revs = repo.anyrevs([b'P'], user=True, localalias=alias)
1621 > ui.write(b'P=%r\n' % list(revs))
1624 > ui.write(b'P=%r\n' % list(revs))
1622 > EOF
1625 > EOF
1623
1626
1624 $ cat >> .hg/hgrc <<EOF
1627 $ cat >> .hg/hgrc <<EOF
1625 > custompredicate = !
1628 > custompredicate = !
1626 > printprevset = $TESTTMP/printprevset.py
1629 > printprevset = $TESTTMP/printprevset.py
1627 > EOF
1630 > EOF
1628
1631
1629 $ hg --config revsetalias.P=1 printprevset
1632 $ hg --config revsetalias.P=1 printprevset
1630 P=[1]
1633 P=[1]
1631 $ P=3 hg --config revsetalias.P=2 printprevset
1634 $ P=3 hg --config revsetalias.P=2 printprevset
1632 P=[3]
1635 P=[3]
1633
1636
1634 $ cd ..
1637 $ cd ..
1635
1638
1636 Test obsstore related revsets
1639 Test obsstore related revsets
1637
1640
1638 $ hg init repo1
1641 $ hg init repo1
1639 $ cd repo1
1642 $ cd repo1
1640 $ cat <<EOF >> .hg/hgrc
1643 $ cat <<EOF >> .hg/hgrc
1641 > [experimental]
1644 > [experimental]
1642 > evolution.createmarkers=True
1645 > evolution.createmarkers=True
1643 > EOF
1646 > EOF
1644
1647
1645 $ hg debugdrawdag <<'EOS'
1648 $ hg debugdrawdag <<'EOS'
1646 > F G
1649 > F G
1647 > |/ # split: B -> E, F
1650 > |/ # split: B -> E, F
1648 > B C D E # amend: B -> C -> D
1651 > B C D E # amend: B -> C -> D
1649 > \|/ | # amend: F -> G
1652 > \|/ | # amend: F -> G
1650 > A A Z # amend: A -> Z
1653 > A A Z # amend: A -> Z
1651 > EOS
1654 > EOS
1652 3 new orphan changesets
1655 3 new orphan changesets
1653 3 new content-divergent changesets
1656 3 new content-divergent changesets
1654
1657
1655 $ hg log -r 'successors(Z)' -T '{desc}\n'
1658 $ hg log -r 'successors(Z)' -T '{desc}\n'
1656 Z
1659 Z
1657
1660
1658 $ hg log -r 'successors(F)' -T '{desc}\n'
1661 $ hg log -r 'successors(F)' -T '{desc}\n'
1659 F
1662 F
1660 G
1663 G
1661
1664
1662 $ hg tag --remove --local C D E F G
1665 $ hg tag --remove --local C D E F G
1663
1666
1664 $ hg log -r 'successors(B)' -T '{desc}\n'
1667 $ hg log -r 'successors(B)' -T '{desc}\n'
1665 B
1668 B
1666 D
1669 D
1667 E
1670 E
1668 G
1671 G
1669
1672
1670 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
1673 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
1671 B
1674 B
1672 C
1675 C
1673 D
1676 D
1674 E
1677 E
1675 F
1678 F
1676 G
1679 G
1677
1680
1678 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
1681 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
1679 D
1682 D
1680 E
1683 E
1681 G
1684 G
1682
1685
1683 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
1686 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
1684 A
1687 A
1685 Z
1688 Z
1686 B
1689 B
1687
1690
1688 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
1691 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
1689 Z
1692 Z
1690
1693
1691 Test `draft() & ::x` optimization
1694 Test `draft() & ::x` optimization
1692
1695
1693 $ hg init $TESTTMP/repo2
1696 $ hg init $TESTTMP/repo2
1694 $ cd $TESTTMP/repo2
1697 $ cd $TESTTMP/repo2
1695 $ hg debugdrawdag <<'EOS'
1698 $ hg debugdrawdag <<'EOS'
1696 > P5 S1
1699 > P5 S1
1697 > | |
1700 > | |
1698 > S2 | D3
1701 > S2 | D3
1699 > \|/
1702 > \|/
1700 > P4
1703 > P4
1701 > |
1704 > |
1702 > P3 D2
1705 > P3 D2
1703 > | |
1706 > | |
1704 > P2 D1
1707 > P2 D1
1705 > |/
1708 > |/
1706 > P1
1709 > P1
1707 > |
1710 > |
1708 > P0
1711 > P0
1709 > EOS
1712 > EOS
1710 $ hg phase --public -r P5
1713 $ hg phase --public -r P5
1711 $ hg phase --force --secret -r S1+S2
1714 $ hg phase --force --secret -r S1+S2
1712 $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)'
1715 $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)'
1713 o 8 P5 public
1716 o 8 P5 public
1714 |
1717 |
1715 | o 10 S1 secret
1718 | o 10 S1 secret
1716 | |
1719 | |
1717 | o 7 D3 draft
1720 | o 7 D3 draft
1718 |/
1721 |/
1719 | o 9 S2 secret
1722 | o 9 S2 secret
1720 |/
1723 |/
1721 o 6 P4 public
1724 o 6 P4 public
1722 |
1725 |
1723 o 5 P3 public
1726 o 5 P3 public
1724 |
1727 |
1725 o 3 P2 public
1728 o 3 P2 public
1726 |
1729 |
1727 | o 4 D2 draft
1730 | o 4 D2 draft
1728 | |
1731 | |
1729 | o 2 D1 draft
1732 | o 2 D1 draft
1730 |/
1733 |/
1731 o 1 P1 public
1734 o 1 P1 public
1732 |
1735 |
1733 o 0 P0 public
1736 o 0 P0 public
1734
1737
1735 $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)'
1738 $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)'
1736 * analyzed:
1739 * analyzed:
1737 (and
1740 (and
1738 (func
1741 (func
1739 (symbol 'draft')
1742 (symbol 'draft')
1740 None)
1743 None)
1741 (func
1744 (func
1742 (symbol 'ancestors')
1745 (symbol 'ancestors')
1743 (or
1746 (or
1744 (list
1747 (list
1745 (and
1748 (and
1746 (or
1749 (or
1747 (list
1750 (list
1748 (symbol 'S1')
1751 (symbol 'S1')
1749 (symbol 'D1')
1752 (symbol 'D1')
1750 (symbol 'P5')))
1753 (symbol 'P5')))
1751 (not
1754 (not
1752 (symbol 'D3')))
1755 (symbol 'D3')))
1753 (symbol 'S2')))))
1756 (symbol 'S2')))))
1754 * optimized:
1757 * optimized:
1755 (func
1758 (func
1756 (symbol '_phaseandancestors')
1759 (symbol '_phaseandancestors')
1757 (list
1760 (list
1758 (symbol 'draft')
1761 (symbol 'draft')
1759 (or
1762 (or
1760 (list
1763 (list
1761 (difference
1764 (difference
1762 (func
1765 (func
1763 (symbol '_list')
1766 (symbol '_list')
1764 (string 'S1\x00D1\x00P5'))
1767 (string 'S1\x00D1\x00P5'))
1765 (symbol 'D3'))
1768 (symbol 'D3'))
1766 (symbol 'S2')))))
1769 (symbol 'S2')))))
1767 $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9'
1770 $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9'
1768 * analyzed:
1771 * analyzed:
1769 (and
1772 (and
1770 (func
1773 (func
1771 (symbol 'secret')
1774 (symbol 'secret')
1772 None)
1775 None)
1773 (func
1776 (func
1774 (symbol 'ancestors')
1777 (symbol 'ancestors')
1775 (symbol '9')))
1778 (symbol '9')))
1776 * optimized:
1779 * optimized:
1777 (func
1780 (func
1778 (symbol '_phaseandancestors')
1781 (symbol '_phaseandancestors')
1779 (list
1782 (list
1780 (symbol 'secret')
1783 (symbol 'secret')
1781 (symbol '9')))
1784 (symbol '9')))
1782 $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )'
1785 $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )'
1783 * analyzed:
1786 * analyzed:
1784 (and
1787 (and
1785 (symbol '7')
1788 (symbol '7')
1786 (and
1789 (and
1787 (not
1790 (not
1788 (func
1791 (func
1789 (symbol 'public')
1792 (symbol 'public')
1790 None))
1793 None))
1791 (func
1794 (func
1792 (symbol 'ancestors')
1795 (symbol 'ancestors')
1793 (func
1796 (func
1794 (symbol 'tag')
1797 (symbol 'tag')
1795 None))))
1798 None))))
1796 * optimized:
1799 * optimized:
1797 (and
1800 (and
1798 (symbol '7')
1801 (symbol '7')
1799 (func
1802 (func
1800 (symbol '_phaseandancestors')
1803 (symbol '_phaseandancestors')
1801 (list
1804 (list
1802 (symbol '_notpublic')
1805 (symbol '_notpublic')
1803 (func
1806 (func
1804 (symbol 'tag')
1807 (symbol 'tag')
1805 None))))
1808 None))))
1806 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)'
1809 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)'
1807 * optimized:
1810 * optimized:
1808 (and
1811 (and
1809 (func
1812 (func
1810 (symbol '_notpublic')
1813 (symbol '_notpublic')
1811 None)
1814 None)
1812 (func
1815 (func
1813 (symbol 'ancestors')
1816 (symbol 'ancestors')
1814 (list
1817 (list
1815 (func
1818 (func
1816 (symbol '_list')
1819 (symbol '_list')
1817 (string 'S1\x00D2\x00P5'))
1820 (string 'S1\x00D2\x00P5'))
1818 (symbol '1'))))
1821 (symbol '1'))))
1819 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)'
1822 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)'
1820 * optimized:
1823 * optimized:
1821 (and
1824 (and
1822 (func
1825 (func
1823 (symbol '_notpublic')
1826 (symbol '_notpublic')
1824 None)
1827 None)
1825 (func
1828 (func
1826 (symbol 'ancestors')
1829 (symbol 'ancestors')
1827 (list
1830 (list
1828 (func
1831 (func
1829 (symbol '_list')
1832 (symbol '_list')
1830 (string 'S1\x00D2\x00P5'))
1833 (string 'S1\x00D2\x00P5'))
1831 (keyvalue
1834 (keyvalue
1832 (symbol 'depth')
1835 (symbol 'depth')
1833 (symbol '1')))))
1836 (symbol '1')))))
General Comments 0
You need to be logged in to leave comments. Login now