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