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