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