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