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