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