##// END OF EJS Templates
smartset: extract method to slice abstractsmartset...
Yuya Nishihara -
r32819:4710cc4d default
parent child Browse files
Show More
@@ -1,2357 +1,2339
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 scmutil,
27 scmutil,
28 smartset,
28 smartset,
29 util,
29 util,
30 )
30 )
31
31
32 # helpers for processing parsed tree
32 # helpers for processing parsed tree
33 getsymbol = revsetlang.getsymbol
33 getsymbol = revsetlang.getsymbol
34 getstring = revsetlang.getstring
34 getstring = revsetlang.getstring
35 getinteger = revsetlang.getinteger
35 getinteger = revsetlang.getinteger
36 getboolean = revsetlang.getboolean
36 getboolean = revsetlang.getboolean
37 getlist = revsetlang.getlist
37 getlist = revsetlang.getlist
38 getrange = revsetlang.getrange
38 getrange = revsetlang.getrange
39 getargs = revsetlang.getargs
39 getargs = revsetlang.getargs
40 getargsdict = revsetlang.getargsdict
40 getargsdict = revsetlang.getargsdict
41
41
42 # constants used as an argument of match() and matchany()
42 # constants used as an argument of match() and matchany()
43 anyorder = revsetlang.anyorder
43 anyorder = revsetlang.anyorder
44 defineorder = revsetlang.defineorder
44 defineorder = revsetlang.defineorder
45 followorder = revsetlang.followorder
45 followorder = revsetlang.followorder
46
46
47 baseset = smartset.baseset
47 baseset = smartset.baseset
48 generatorset = smartset.generatorset
48 generatorset = smartset.generatorset
49 spanset = smartset.spanset
49 spanset = smartset.spanset
50 fullreposet = smartset.fullreposet
50 fullreposet = smartset.fullreposet
51
51
52 def _revancestors(repo, revs, followfirst):
52 def _revancestors(repo, revs, followfirst):
53 """Like revlog.ancestors(), but supports followfirst."""
53 """Like revlog.ancestors(), but supports followfirst."""
54 if followfirst:
54 if followfirst:
55 cut = 1
55 cut = 1
56 else:
56 else:
57 cut = None
57 cut = None
58 cl = repo.changelog
58 cl = repo.changelog
59
59
60 def iterate():
60 def iterate():
61 revs.sort(reverse=True)
61 revs.sort(reverse=True)
62 irevs = iter(revs)
62 irevs = iter(revs)
63 h = []
63 h = []
64
64
65 inputrev = next(irevs, None)
65 inputrev = next(irevs, None)
66 if inputrev is not None:
66 if inputrev is not None:
67 heapq.heappush(h, -inputrev)
67 heapq.heappush(h, -inputrev)
68
68
69 seen = set()
69 seen = set()
70 while h:
70 while h:
71 current = -heapq.heappop(h)
71 current = -heapq.heappop(h)
72 if current == inputrev:
72 if current == inputrev:
73 inputrev = next(irevs, None)
73 inputrev = next(irevs, None)
74 if inputrev is not None:
74 if inputrev is not None:
75 heapq.heappush(h, -inputrev)
75 heapq.heappush(h, -inputrev)
76 if current not in seen:
76 if current not in seen:
77 seen.add(current)
77 seen.add(current)
78 yield current
78 yield current
79 try:
79 try:
80 for parent in cl.parentrevs(current)[:cut]:
80 for parent in cl.parentrevs(current)[:cut]:
81 if parent != node.nullrev:
81 if parent != node.nullrev:
82 heapq.heappush(h, -parent)
82 heapq.heappush(h, -parent)
83 except error.WdirUnsupported:
83 except error.WdirUnsupported:
84 for parent in repo[current].parents()[:cut]:
84 for parent in repo[current].parents()[:cut]:
85 if parent.rev() != node.nullrev:
85 if parent.rev() != node.nullrev:
86 heapq.heappush(h, -parent.rev())
86 heapq.heappush(h, -parent.rev())
87
87
88 return generatorset(iterate(), iterasc=False)
88 return generatorset(iterate(), iterasc=False)
89
89
90 def _revdescendants(repo, revs, followfirst):
90 def _revdescendants(repo, revs, followfirst):
91 """Like revlog.descendants() but supports followfirst."""
91 """Like revlog.descendants() but supports followfirst."""
92 if followfirst:
92 if followfirst:
93 cut = 1
93 cut = 1
94 else:
94 else:
95 cut = None
95 cut = None
96
96
97 def iterate():
97 def iterate():
98 cl = repo.changelog
98 cl = repo.changelog
99 # XXX this should be 'parentset.min()' assuming 'parentset' is a
99 # XXX this should be 'parentset.min()' assuming 'parentset' is a
100 # smartset (and if it is not, it should.)
100 # smartset (and if it is not, it should.)
101 first = min(revs)
101 first = min(revs)
102 nullrev = node.nullrev
102 nullrev = node.nullrev
103 if first == nullrev:
103 if first == nullrev:
104 # Are there nodes with a null first parent and a non-null
104 # Are there nodes with a null first parent and a non-null
105 # second one? Maybe. Do we care? Probably not.
105 # second one? Maybe. Do we care? Probably not.
106 for i in cl:
106 for i in cl:
107 yield i
107 yield i
108 else:
108 else:
109 seen = set(revs)
109 seen = set(revs)
110 for i in cl.revs(first + 1):
110 for i in cl.revs(first + 1):
111 for x in cl.parentrevs(i)[:cut]:
111 for x in cl.parentrevs(i)[:cut]:
112 if x != nullrev and x in seen:
112 if x != nullrev and x in seen:
113 seen.add(i)
113 seen.add(i)
114 yield i
114 yield i
115 break
115 break
116
116
117 return generatorset(iterate(), iterasc=True)
117 return generatorset(iterate(), iterasc=True)
118
118
119 def _reachablerootspure(repo, minroot, roots, heads, includepath):
119 def _reachablerootspure(repo, minroot, roots, heads, includepath):
120 """return (heads(::<roots> and ::<heads>))
120 """return (heads(::<roots> and ::<heads>))
121
121
122 If includepath is True, return (<roots>::<heads>)."""
122 If includepath is True, return (<roots>::<heads>)."""
123 if not roots:
123 if not roots:
124 return []
124 return []
125 parentrevs = repo.changelog.parentrevs
125 parentrevs = repo.changelog.parentrevs
126 roots = set(roots)
126 roots = set(roots)
127 visit = list(heads)
127 visit = list(heads)
128 reachable = set()
128 reachable = set()
129 seen = {}
129 seen = {}
130 # prefetch all the things! (because python is slow)
130 # prefetch all the things! (because python is slow)
131 reached = reachable.add
131 reached = reachable.add
132 dovisit = visit.append
132 dovisit = visit.append
133 nextvisit = visit.pop
133 nextvisit = visit.pop
134 # open-code the post-order traversal due to the tiny size of
134 # open-code the post-order traversal due to the tiny size of
135 # sys.getrecursionlimit()
135 # sys.getrecursionlimit()
136 while visit:
136 while visit:
137 rev = nextvisit()
137 rev = nextvisit()
138 if rev in roots:
138 if rev in roots:
139 reached(rev)
139 reached(rev)
140 if not includepath:
140 if not includepath:
141 continue
141 continue
142 parents = parentrevs(rev)
142 parents = parentrevs(rev)
143 seen[rev] = parents
143 seen[rev] = parents
144 for parent in parents:
144 for parent in parents:
145 if parent >= minroot and parent not in seen:
145 if parent >= minroot and parent not in seen:
146 dovisit(parent)
146 dovisit(parent)
147 if not reachable:
147 if not reachable:
148 return baseset()
148 return baseset()
149 if not includepath:
149 if not includepath:
150 return reachable
150 return reachable
151 for rev in sorted(seen):
151 for rev in sorted(seen):
152 for parent in seen[rev]:
152 for parent in seen[rev]:
153 if parent in reachable:
153 if parent in reachable:
154 reached(rev)
154 reached(rev)
155 return reachable
155 return reachable
156
156
157 def reachableroots(repo, roots, heads, includepath=False):
157 def reachableroots(repo, roots, heads, includepath=False):
158 """return (heads(::<roots> and ::<heads>))
158 """return (heads(::<roots> and ::<heads>))
159
159
160 If includepath is True, return (<roots>::<heads>)."""
160 If includepath is True, return (<roots>::<heads>)."""
161 if not roots:
161 if not roots:
162 return baseset()
162 return baseset()
163 minroot = roots.min()
163 minroot = roots.min()
164 roots = list(roots)
164 roots = list(roots)
165 heads = list(heads)
165 heads = list(heads)
166 try:
166 try:
167 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
167 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
168 except AttributeError:
168 except AttributeError:
169 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
169 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
170 revs = baseset(revs)
170 revs = baseset(revs)
171 revs.sort()
171 revs.sort()
172 return revs
172 return revs
173
173
174 # helpers
174 # helpers
175
175
176 def getset(repo, subset, x):
176 def getset(repo, subset, x):
177 if not x:
177 if not x:
178 raise error.ParseError(_("missing argument"))
178 raise error.ParseError(_("missing argument"))
179 return methods[x[0]](repo, subset, *x[1:])
179 return methods[x[0]](repo, subset, *x[1:])
180
180
181 def _getrevsource(repo, r):
181 def _getrevsource(repo, r):
182 extra = repo[r].extra()
182 extra = repo[r].extra()
183 for label in ('source', 'transplant_source', 'rebase_source'):
183 for label in ('source', 'transplant_source', 'rebase_source'):
184 if label in extra:
184 if label in extra:
185 try:
185 try:
186 return repo[extra[label]].rev()
186 return repo[extra[label]].rev()
187 except error.RepoLookupError:
187 except error.RepoLookupError:
188 pass
188 pass
189 return None
189 return None
190
190
191 # operator methods
191 # operator methods
192
192
193 def stringset(repo, subset, x):
193 def stringset(repo, subset, x):
194 x = scmutil.intrev(repo[x])
194 x = scmutil.intrev(repo[x])
195 if (x in subset
195 if (x in subset
196 or x == node.nullrev and isinstance(subset, fullreposet)):
196 or x == node.nullrev and isinstance(subset, fullreposet)):
197 return baseset([x])
197 return baseset([x])
198 return baseset()
198 return baseset()
199
199
200 def rangeset(repo, subset, x, y, order):
200 def rangeset(repo, subset, x, y, order):
201 m = getset(repo, fullreposet(repo), x)
201 m = getset(repo, fullreposet(repo), x)
202 n = getset(repo, fullreposet(repo), y)
202 n = getset(repo, fullreposet(repo), y)
203
203
204 if not m or not n:
204 if not m or not n:
205 return baseset()
205 return baseset()
206 return _makerangeset(repo, subset, m.first(), n.last(), order)
206 return _makerangeset(repo, subset, m.first(), n.last(), order)
207
207
208 def rangeall(repo, subset, x, order):
208 def rangeall(repo, subset, x, order):
209 assert x is None
209 assert x is None
210 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
210 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
211
211
212 def rangepre(repo, subset, y, order):
212 def rangepre(repo, subset, y, order):
213 # ':y' can't be rewritten to '0:y' since '0' may be hidden
213 # ':y' can't be rewritten to '0:y' since '0' may be hidden
214 n = getset(repo, fullreposet(repo), y)
214 n = getset(repo, fullreposet(repo), y)
215 if not n:
215 if not n:
216 return baseset()
216 return baseset()
217 return _makerangeset(repo, subset, 0, n.last(), order)
217 return _makerangeset(repo, subset, 0, n.last(), order)
218
218
219 def rangepost(repo, subset, x, order):
219 def rangepost(repo, subset, x, order):
220 m = getset(repo, fullreposet(repo), x)
220 m = getset(repo, fullreposet(repo), x)
221 if not m:
221 if not m:
222 return baseset()
222 return baseset()
223 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
223 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
224
224
225 def _makerangeset(repo, subset, m, n, order):
225 def _makerangeset(repo, subset, m, n, order):
226 if m == n:
226 if m == n:
227 r = baseset([m])
227 r = baseset([m])
228 elif n == node.wdirrev:
228 elif n == node.wdirrev:
229 r = spanset(repo, m, len(repo)) + baseset([n])
229 r = spanset(repo, m, len(repo)) + baseset([n])
230 elif m == node.wdirrev:
230 elif m == node.wdirrev:
231 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
231 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
232 elif m < n:
232 elif m < n:
233 r = spanset(repo, m, n + 1)
233 r = spanset(repo, m, n + 1)
234 else:
234 else:
235 r = spanset(repo, m, n - 1)
235 r = spanset(repo, m, n - 1)
236
236
237 if order == defineorder:
237 if order == defineorder:
238 return r & subset
238 return r & subset
239 else:
239 else:
240 # carrying the sorting over when possible would be more efficient
240 # carrying the sorting over when possible would be more efficient
241 return subset & r
241 return subset & r
242
242
243 def dagrange(repo, subset, x, y, order):
243 def dagrange(repo, subset, x, y, order):
244 r = fullreposet(repo)
244 r = fullreposet(repo)
245 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
245 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
246 includepath=True)
246 includepath=True)
247 return subset & xs
247 return subset & xs
248
248
249 def andset(repo, subset, x, y, order):
249 def andset(repo, subset, x, y, order):
250 return getset(repo, getset(repo, subset, x), y)
250 return getset(repo, getset(repo, subset, x), y)
251
251
252 def differenceset(repo, subset, x, y, order):
252 def differenceset(repo, subset, x, y, order):
253 return getset(repo, subset, x) - getset(repo, subset, y)
253 return getset(repo, subset, x) - getset(repo, subset, y)
254
254
255 def _orsetlist(repo, subset, xs):
255 def _orsetlist(repo, subset, xs):
256 assert xs
256 assert xs
257 if len(xs) == 1:
257 if len(xs) == 1:
258 return getset(repo, subset, xs[0])
258 return getset(repo, subset, xs[0])
259 p = len(xs) // 2
259 p = len(xs) // 2
260 a = _orsetlist(repo, subset, xs[:p])
260 a = _orsetlist(repo, subset, xs[:p])
261 b = _orsetlist(repo, subset, xs[p:])
261 b = _orsetlist(repo, subset, xs[p:])
262 return a + b
262 return a + b
263
263
264 def orset(repo, subset, x, order):
264 def orset(repo, subset, x, order):
265 xs = getlist(x)
265 xs = getlist(x)
266 if order == followorder:
266 if order == followorder:
267 # slow path to take the subset order
267 # slow path to take the subset order
268 return subset & _orsetlist(repo, fullreposet(repo), xs)
268 return subset & _orsetlist(repo, fullreposet(repo), xs)
269 else:
269 else:
270 return _orsetlist(repo, subset, xs)
270 return _orsetlist(repo, subset, xs)
271
271
272 def notset(repo, subset, x, order):
272 def notset(repo, subset, x, order):
273 return subset - getset(repo, subset, x)
273 return subset - getset(repo, subset, x)
274
274
275 def listset(repo, subset, *xs):
275 def listset(repo, subset, *xs):
276 raise error.ParseError(_("can't use a list in this context"),
276 raise error.ParseError(_("can't use a list in this context"),
277 hint=_('see hg help "revsets.x or y"'))
277 hint=_('see hg help "revsets.x or y"'))
278
278
279 def keyvaluepair(repo, subset, k, v):
279 def keyvaluepair(repo, subset, k, v):
280 raise error.ParseError(_("can't use a key-value pair in this context"))
280 raise error.ParseError(_("can't use a key-value pair in this context"))
281
281
282 def func(repo, subset, a, b, order):
282 def func(repo, subset, a, b, order):
283 f = getsymbol(a)
283 f = getsymbol(a)
284 if f in symbols:
284 if f in symbols:
285 func = symbols[f]
285 func = symbols[f]
286 if getattr(func, '_takeorder', False):
286 if getattr(func, '_takeorder', False):
287 return func(repo, subset, b, order)
287 return func(repo, subset, b, order)
288 return func(repo, subset, b)
288 return func(repo, subset, b)
289
289
290 keep = lambda fn: getattr(fn, '__doc__', None) is not None
290 keep = lambda fn: getattr(fn, '__doc__', None) is not None
291
291
292 syms = [s for (s, fn) in symbols.items() if keep(fn)]
292 syms = [s for (s, fn) in symbols.items() if keep(fn)]
293 raise error.UnknownIdentifier(f, syms)
293 raise error.UnknownIdentifier(f, syms)
294
294
295 # functions
295 # functions
296
296
297 # symbols are callables like:
297 # symbols are callables like:
298 # fn(repo, subset, x)
298 # fn(repo, subset, x)
299 # with:
299 # with:
300 # repo - current repository instance
300 # repo - current repository instance
301 # subset - of revisions to be examined
301 # subset - of revisions to be examined
302 # x - argument in tree form
302 # x - argument in tree form
303 symbols = {}
303 symbols = {}
304
304
305 # symbols which can't be used for a DoS attack for any given input
305 # symbols which can't be used for a DoS attack for any given input
306 # (e.g. those which accept regexes as plain strings shouldn't be included)
306 # (e.g. those which accept regexes as plain strings shouldn't be included)
307 # functions that just return a lot of changesets (like all) don't count here
307 # functions that just return a lot of changesets (like all) don't count here
308 safesymbols = set()
308 safesymbols = set()
309
309
310 predicate = registrar.revsetpredicate()
310 predicate = registrar.revsetpredicate()
311
311
312 @predicate('_destupdate')
312 @predicate('_destupdate')
313 def _destupdate(repo, subset, x):
313 def _destupdate(repo, subset, x):
314 # experimental revset for update destination
314 # experimental revset for update destination
315 args = getargsdict(x, 'limit', 'clean')
315 args = getargsdict(x, 'limit', 'clean')
316 return subset & baseset([destutil.destupdate(repo, **args)[0]])
316 return subset & baseset([destutil.destupdate(repo, **args)[0]])
317
317
318 @predicate('_destmerge')
318 @predicate('_destmerge')
319 def _destmerge(repo, subset, x):
319 def _destmerge(repo, subset, x):
320 # experimental revset for merge destination
320 # experimental revset for merge destination
321 sourceset = None
321 sourceset = None
322 if x is not None:
322 if x is not None:
323 sourceset = getset(repo, fullreposet(repo), x)
323 sourceset = getset(repo, fullreposet(repo), x)
324 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
324 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
325
325
326 @predicate('adds(pattern)', safe=True)
326 @predicate('adds(pattern)', safe=True)
327 def adds(repo, subset, x):
327 def adds(repo, subset, x):
328 """Changesets that add a file matching pattern.
328 """Changesets that add a file matching pattern.
329
329
330 The pattern without explicit kind like ``glob:`` is expected to be
330 The pattern without explicit kind like ``glob:`` is expected to be
331 relative to the current directory and match against a file or a
331 relative to the current directory and match against a file or a
332 directory.
332 directory.
333 """
333 """
334 # i18n: "adds" is a keyword
334 # i18n: "adds" is a keyword
335 pat = getstring(x, _("adds requires a pattern"))
335 pat = getstring(x, _("adds requires a pattern"))
336 return checkstatus(repo, subset, pat, 1)
336 return checkstatus(repo, subset, pat, 1)
337
337
338 @predicate('ancestor(*changeset)', safe=True)
338 @predicate('ancestor(*changeset)', safe=True)
339 def ancestor(repo, subset, x):
339 def ancestor(repo, subset, x):
340 """A greatest common ancestor of the changesets.
340 """A greatest common ancestor of the changesets.
341
341
342 Accepts 0 or more changesets.
342 Accepts 0 or more changesets.
343 Will return empty list when passed no args.
343 Will return empty list when passed no args.
344 Greatest common ancestor of a single changeset is that changeset.
344 Greatest common ancestor of a single changeset is that changeset.
345 """
345 """
346 # i18n: "ancestor" is a keyword
346 # i18n: "ancestor" is a keyword
347 l = getlist(x)
347 l = getlist(x)
348 rl = fullreposet(repo)
348 rl = fullreposet(repo)
349 anc = None
349 anc = None
350
350
351 # (getset(repo, rl, i) for i in l) generates a list of lists
351 # (getset(repo, rl, i) for i in l) generates a list of lists
352 for revs in (getset(repo, rl, i) for i in l):
352 for revs in (getset(repo, rl, i) for i in l):
353 for r in revs:
353 for r in revs:
354 if anc is None:
354 if anc is None:
355 anc = repo[r]
355 anc = repo[r]
356 else:
356 else:
357 anc = anc.ancestor(repo[r])
357 anc = anc.ancestor(repo[r])
358
358
359 if anc is not None and anc.rev() in subset:
359 if anc is not None and anc.rev() in subset:
360 return baseset([anc.rev()])
360 return baseset([anc.rev()])
361 return baseset()
361 return baseset()
362
362
363 def _ancestors(repo, subset, x, followfirst=False):
363 def _ancestors(repo, subset, x, followfirst=False):
364 heads = getset(repo, fullreposet(repo), x)
364 heads = getset(repo, fullreposet(repo), x)
365 if not heads:
365 if not heads:
366 return baseset()
366 return baseset()
367 s = _revancestors(repo, heads, followfirst)
367 s = _revancestors(repo, heads, followfirst)
368 return subset & s
368 return subset & s
369
369
370 @predicate('ancestors(set)', safe=True)
370 @predicate('ancestors(set)', safe=True)
371 def ancestors(repo, subset, x):
371 def ancestors(repo, subset, x):
372 """Changesets that are ancestors of a changeset in set.
372 """Changesets that are ancestors of a changeset in set.
373 """
373 """
374 return _ancestors(repo, subset, x)
374 return _ancestors(repo, subset, x)
375
375
376 @predicate('_firstancestors', safe=True)
376 @predicate('_firstancestors', safe=True)
377 def _firstancestors(repo, subset, x):
377 def _firstancestors(repo, subset, x):
378 # ``_firstancestors(set)``
378 # ``_firstancestors(set)``
379 # Like ``ancestors(set)`` but follows only the first parents.
379 # Like ``ancestors(set)`` but follows only the first parents.
380 return _ancestors(repo, subset, x, followfirst=True)
380 return _ancestors(repo, subset, x, followfirst=True)
381
381
382 def _childrenspec(repo, subset, x, n, order):
382 def _childrenspec(repo, subset, x, n, order):
383 """Changesets that are the Nth child of a changeset
383 """Changesets that are the Nth child of a changeset
384 in set.
384 in set.
385 """
385 """
386 cs = set()
386 cs = set()
387 for r in getset(repo, fullreposet(repo), x):
387 for r in getset(repo, fullreposet(repo), x):
388 for i in range(n):
388 for i in range(n):
389 c = repo[r].children()
389 c = repo[r].children()
390 if len(c) == 0:
390 if len(c) == 0:
391 break
391 break
392 if len(c) > 1:
392 if len(c) > 1:
393 raise error.RepoLookupError(
393 raise error.RepoLookupError(
394 _("revision in set has more than one child"))
394 _("revision in set has more than one child"))
395 r = c[0]
395 r = c[0]
396 else:
396 else:
397 cs.add(r)
397 cs.add(r)
398 return subset & cs
398 return subset & cs
399
399
400 def ancestorspec(repo, subset, x, n, order):
400 def ancestorspec(repo, subset, x, n, order):
401 """``set~n``
401 """``set~n``
402 Changesets that are the Nth ancestor (first parents only) of a changeset
402 Changesets that are the Nth ancestor (first parents only) of a changeset
403 in set.
403 in set.
404 """
404 """
405 n = getinteger(n, _("~ expects a number"))
405 n = getinteger(n, _("~ expects a number"))
406 if n < 0:
406 if n < 0:
407 # children lookup
407 # children lookup
408 return _childrenspec(repo, subset, x, -n, order)
408 return _childrenspec(repo, subset, x, -n, order)
409 ps = set()
409 ps = set()
410 cl = repo.changelog
410 cl = repo.changelog
411 for r in getset(repo, fullreposet(repo), x):
411 for r in getset(repo, fullreposet(repo), x):
412 for i in range(n):
412 for i in range(n):
413 try:
413 try:
414 r = cl.parentrevs(r)[0]
414 r = cl.parentrevs(r)[0]
415 except error.WdirUnsupported:
415 except error.WdirUnsupported:
416 r = repo[r].parents()[0].rev()
416 r = repo[r].parents()[0].rev()
417 ps.add(r)
417 ps.add(r)
418 return subset & ps
418 return subset & ps
419
419
420 @predicate('author(string)', safe=True)
420 @predicate('author(string)', safe=True)
421 def author(repo, subset, x):
421 def author(repo, subset, x):
422 """Alias for ``user(string)``.
422 """Alias for ``user(string)``.
423 """
423 """
424 # i18n: "author" is a keyword
424 # i18n: "author" is a keyword
425 n = getstring(x, _("author requires a string"))
425 n = getstring(x, _("author requires a string"))
426 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
426 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
427 return subset.filter(lambda x: matcher(repo[x].user()),
427 return subset.filter(lambda x: matcher(repo[x].user()),
428 condrepr=('<user %r>', n))
428 condrepr=('<user %r>', n))
429
429
430 @predicate('bisect(string)', safe=True)
430 @predicate('bisect(string)', safe=True)
431 def bisect(repo, subset, x):
431 def bisect(repo, subset, x):
432 """Changesets marked in the specified bisect status:
432 """Changesets marked in the specified bisect status:
433
433
434 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
434 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
435 - ``goods``, ``bads`` : csets topologically good/bad
435 - ``goods``, ``bads`` : csets topologically good/bad
436 - ``range`` : csets taking part in the bisection
436 - ``range`` : csets taking part in the bisection
437 - ``pruned`` : csets that are goods, bads or skipped
437 - ``pruned`` : csets that are goods, bads or skipped
438 - ``untested`` : csets whose fate is yet unknown
438 - ``untested`` : csets whose fate is yet unknown
439 - ``ignored`` : csets ignored due to DAG topology
439 - ``ignored`` : csets ignored due to DAG topology
440 - ``current`` : the cset currently being bisected
440 - ``current`` : the cset currently being bisected
441 """
441 """
442 # i18n: "bisect" is a keyword
442 # i18n: "bisect" is a keyword
443 status = getstring(x, _("bisect requires a string")).lower()
443 status = getstring(x, _("bisect requires a string")).lower()
444 state = set(hbisect.get(repo, status))
444 state = set(hbisect.get(repo, status))
445 return subset & state
445 return subset & state
446
446
447 # Backward-compatibility
447 # Backward-compatibility
448 # - no help entry so that we do not advertise it any more
448 # - no help entry so that we do not advertise it any more
449 @predicate('bisected', safe=True)
449 @predicate('bisected', safe=True)
450 def bisected(repo, subset, x):
450 def bisected(repo, subset, x):
451 return bisect(repo, subset, x)
451 return bisect(repo, subset, x)
452
452
453 @predicate('bookmark([name])', safe=True)
453 @predicate('bookmark([name])', safe=True)
454 def bookmark(repo, subset, x):
454 def bookmark(repo, subset, x):
455 """The named bookmark or all bookmarks.
455 """The named bookmark or all bookmarks.
456
456
457 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
457 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
458 """
458 """
459 # i18n: "bookmark" is a keyword
459 # i18n: "bookmark" is a keyword
460 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
460 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
461 if args:
461 if args:
462 bm = getstring(args[0],
462 bm = getstring(args[0],
463 # i18n: "bookmark" is a keyword
463 # i18n: "bookmark" is a keyword
464 _('the argument to bookmark must be a string'))
464 _('the argument to bookmark must be a string'))
465 kind, pattern, matcher = util.stringmatcher(bm)
465 kind, pattern, matcher = util.stringmatcher(bm)
466 bms = set()
466 bms = set()
467 if kind == 'literal':
467 if kind == 'literal':
468 bmrev = repo._bookmarks.get(pattern, None)
468 bmrev = repo._bookmarks.get(pattern, None)
469 if not bmrev:
469 if not bmrev:
470 raise error.RepoLookupError(_("bookmark '%s' does not exist")
470 raise error.RepoLookupError(_("bookmark '%s' does not exist")
471 % pattern)
471 % pattern)
472 bms.add(repo[bmrev].rev())
472 bms.add(repo[bmrev].rev())
473 else:
473 else:
474 matchrevs = set()
474 matchrevs = set()
475 for name, bmrev in repo._bookmarks.iteritems():
475 for name, bmrev in repo._bookmarks.iteritems():
476 if matcher(name):
476 if matcher(name):
477 matchrevs.add(bmrev)
477 matchrevs.add(bmrev)
478 if not matchrevs:
478 if not matchrevs:
479 raise error.RepoLookupError(_("no bookmarks exist"
479 raise error.RepoLookupError(_("no bookmarks exist"
480 " that match '%s'") % pattern)
480 " that match '%s'") % pattern)
481 for bmrev in matchrevs:
481 for bmrev in matchrevs:
482 bms.add(repo[bmrev].rev())
482 bms.add(repo[bmrev].rev())
483 else:
483 else:
484 bms = {repo[r].rev() for r in repo._bookmarks.values()}
484 bms = {repo[r].rev() for r in repo._bookmarks.values()}
485 bms -= {node.nullrev}
485 bms -= {node.nullrev}
486 return subset & bms
486 return subset & bms
487
487
488 @predicate('branch(string or set)', safe=True)
488 @predicate('branch(string or set)', safe=True)
489 def branch(repo, subset, x):
489 def branch(repo, subset, x):
490 """
490 """
491 All changesets belonging to the given branch or the branches of the given
491 All changesets belonging to the given branch or the branches of the given
492 changesets.
492 changesets.
493
493
494 Pattern matching is supported for `string`. See
494 Pattern matching is supported for `string`. See
495 :hg:`help revisions.patterns`.
495 :hg:`help revisions.patterns`.
496 """
496 """
497 getbi = repo.revbranchcache().branchinfo
497 getbi = repo.revbranchcache().branchinfo
498 def getbranch(r):
498 def getbranch(r):
499 try:
499 try:
500 return getbi(r)[0]
500 return getbi(r)[0]
501 except error.WdirUnsupported:
501 except error.WdirUnsupported:
502 return repo[r].branch()
502 return repo[r].branch()
503
503
504 try:
504 try:
505 b = getstring(x, '')
505 b = getstring(x, '')
506 except error.ParseError:
506 except error.ParseError:
507 # not a string, but another revspec, e.g. tip()
507 # not a string, but another revspec, e.g. tip()
508 pass
508 pass
509 else:
509 else:
510 kind, pattern, matcher = util.stringmatcher(b)
510 kind, pattern, matcher = util.stringmatcher(b)
511 if kind == 'literal':
511 if kind == 'literal':
512 # note: falls through to the revspec case if no branch with
512 # note: falls through to the revspec case if no branch with
513 # this name exists and pattern kind is not specified explicitly
513 # this name exists and pattern kind is not specified explicitly
514 if pattern in repo.branchmap():
514 if pattern in repo.branchmap():
515 return subset.filter(lambda r: matcher(getbranch(r)),
515 return subset.filter(lambda r: matcher(getbranch(r)),
516 condrepr=('<branch %r>', b))
516 condrepr=('<branch %r>', b))
517 if b.startswith('literal:'):
517 if b.startswith('literal:'):
518 raise error.RepoLookupError(_("branch '%s' does not exist")
518 raise error.RepoLookupError(_("branch '%s' does not exist")
519 % pattern)
519 % pattern)
520 else:
520 else:
521 return subset.filter(lambda r: matcher(getbranch(r)),
521 return subset.filter(lambda r: matcher(getbranch(r)),
522 condrepr=('<branch %r>', b))
522 condrepr=('<branch %r>', b))
523
523
524 s = getset(repo, fullreposet(repo), x)
524 s = getset(repo, fullreposet(repo), x)
525 b = set()
525 b = set()
526 for r in s:
526 for r in s:
527 b.add(getbranch(r))
527 b.add(getbranch(r))
528 c = s.__contains__
528 c = s.__contains__
529 return subset.filter(lambda r: c(r) or getbranch(r) in b,
529 return subset.filter(lambda r: c(r) or getbranch(r) in b,
530 condrepr=lambda: '<branch %r>' % sorted(b))
530 condrepr=lambda: '<branch %r>' % sorted(b))
531
531
532 @predicate('bumped()', safe=True)
532 @predicate('bumped()', safe=True)
533 def bumped(repo, subset, x):
533 def bumped(repo, subset, x):
534 """Mutable changesets marked as successors of public changesets.
534 """Mutable changesets marked as successors of public changesets.
535
535
536 Only non-public and non-obsolete changesets can be `bumped`.
536 Only non-public and non-obsolete changesets can be `bumped`.
537 """
537 """
538 # i18n: "bumped" is a keyword
538 # i18n: "bumped" is a keyword
539 getargs(x, 0, 0, _("bumped takes no arguments"))
539 getargs(x, 0, 0, _("bumped takes no arguments"))
540 bumped = obsmod.getrevs(repo, 'bumped')
540 bumped = obsmod.getrevs(repo, 'bumped')
541 return subset & bumped
541 return subset & bumped
542
542
543 @predicate('bundle()', safe=True)
543 @predicate('bundle()', safe=True)
544 def bundle(repo, subset, x):
544 def bundle(repo, subset, x):
545 """Changesets in the bundle.
545 """Changesets in the bundle.
546
546
547 Bundle must be specified by the -R option."""
547 Bundle must be specified by the -R option."""
548
548
549 try:
549 try:
550 bundlerevs = repo.changelog.bundlerevs
550 bundlerevs = repo.changelog.bundlerevs
551 except AttributeError:
551 except AttributeError:
552 raise error.Abort(_("no bundle provided - specify with -R"))
552 raise error.Abort(_("no bundle provided - specify with -R"))
553 return subset & bundlerevs
553 return subset & bundlerevs
554
554
555 def checkstatus(repo, subset, pat, field):
555 def checkstatus(repo, subset, pat, field):
556 hasset = matchmod.patkind(pat) == 'set'
556 hasset = matchmod.patkind(pat) == 'set'
557
557
558 mcache = [None]
558 mcache = [None]
559 def matches(x):
559 def matches(x):
560 c = repo[x]
560 c = repo[x]
561 if not mcache[0] or hasset:
561 if not mcache[0] or hasset:
562 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
562 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
563 m = mcache[0]
563 m = mcache[0]
564 fname = None
564 fname = None
565 if not m.anypats() and len(m.files()) == 1:
565 if not m.anypats() and len(m.files()) == 1:
566 fname = m.files()[0]
566 fname = m.files()[0]
567 if fname is not None:
567 if fname is not None:
568 if fname not in c.files():
568 if fname not in c.files():
569 return False
569 return False
570 else:
570 else:
571 for f in c.files():
571 for f in c.files():
572 if m(f):
572 if m(f):
573 break
573 break
574 else:
574 else:
575 return False
575 return False
576 files = repo.status(c.p1().node(), c.node())[field]
576 files = repo.status(c.p1().node(), c.node())[field]
577 if fname is not None:
577 if fname is not None:
578 if fname in files:
578 if fname in files:
579 return True
579 return True
580 else:
580 else:
581 for f in files:
581 for f in files:
582 if m(f):
582 if m(f):
583 return True
583 return True
584
584
585 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
585 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
586
586
587 def _children(repo, subset, parentset):
587 def _children(repo, subset, parentset):
588 if not parentset:
588 if not parentset:
589 return baseset()
589 return baseset()
590 cs = set()
590 cs = set()
591 pr = repo.changelog.parentrevs
591 pr = repo.changelog.parentrevs
592 minrev = parentset.min()
592 minrev = parentset.min()
593 nullrev = node.nullrev
593 nullrev = node.nullrev
594 for r in subset:
594 for r in subset:
595 if r <= minrev:
595 if r <= minrev:
596 continue
596 continue
597 p1, p2 = pr(r)
597 p1, p2 = pr(r)
598 if p1 in parentset:
598 if p1 in parentset:
599 cs.add(r)
599 cs.add(r)
600 if p2 != nullrev and p2 in parentset:
600 if p2 != nullrev and p2 in parentset:
601 cs.add(r)
601 cs.add(r)
602 return baseset(cs)
602 return baseset(cs)
603
603
604 @predicate('children(set)', safe=True)
604 @predicate('children(set)', safe=True)
605 def children(repo, subset, x):
605 def children(repo, subset, x):
606 """Child changesets of changesets in set.
606 """Child changesets of changesets in set.
607 """
607 """
608 s = getset(repo, fullreposet(repo), x)
608 s = getset(repo, fullreposet(repo), x)
609 cs = _children(repo, subset, s)
609 cs = _children(repo, subset, s)
610 return subset & cs
610 return subset & cs
611
611
612 @predicate('closed()', safe=True)
612 @predicate('closed()', safe=True)
613 def closed(repo, subset, x):
613 def closed(repo, subset, x):
614 """Changeset is closed.
614 """Changeset is closed.
615 """
615 """
616 # i18n: "closed" is a keyword
616 # i18n: "closed" is a keyword
617 getargs(x, 0, 0, _("closed takes no arguments"))
617 getargs(x, 0, 0, _("closed takes no arguments"))
618 return subset.filter(lambda r: repo[r].closesbranch(),
618 return subset.filter(lambda r: repo[r].closesbranch(),
619 condrepr='<branch closed>')
619 condrepr='<branch closed>')
620
620
621 @predicate('contains(pattern)')
621 @predicate('contains(pattern)')
622 def contains(repo, subset, x):
622 def contains(repo, subset, x):
623 """The revision's manifest contains a file matching pattern (but might not
623 """The revision's manifest contains a file matching pattern (but might not
624 modify it). See :hg:`help patterns` for information about file patterns.
624 modify it). See :hg:`help patterns` for information about file patterns.
625
625
626 The pattern without explicit kind like ``glob:`` is expected to be
626 The pattern without explicit kind like ``glob:`` is expected to be
627 relative to the current directory and match against a file exactly
627 relative to the current directory and match against a file exactly
628 for efficiency.
628 for efficiency.
629 """
629 """
630 # i18n: "contains" is a keyword
630 # i18n: "contains" is a keyword
631 pat = getstring(x, _("contains requires a pattern"))
631 pat = getstring(x, _("contains requires a pattern"))
632
632
633 def matches(x):
633 def matches(x):
634 if not matchmod.patkind(pat):
634 if not matchmod.patkind(pat):
635 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
635 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
636 if pats in repo[x]:
636 if pats in repo[x]:
637 return True
637 return True
638 else:
638 else:
639 c = repo[x]
639 c = repo[x]
640 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
640 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
641 for f in c.manifest():
641 for f in c.manifest():
642 if m(f):
642 if m(f):
643 return True
643 return True
644 return False
644 return False
645
645
646 return subset.filter(matches, condrepr=('<contains %r>', pat))
646 return subset.filter(matches, condrepr=('<contains %r>', pat))
647
647
648 @predicate('converted([id])', safe=True)
648 @predicate('converted([id])', safe=True)
649 def converted(repo, subset, x):
649 def converted(repo, subset, x):
650 """Changesets converted from the given identifier in the old repository if
650 """Changesets converted from the given identifier in the old repository if
651 present, or all converted changesets if no identifier is specified.
651 present, or all converted changesets if no identifier is specified.
652 """
652 """
653
653
654 # There is exactly no chance of resolving the revision, so do a simple
654 # There is exactly no chance of resolving the revision, so do a simple
655 # string compare and hope for the best
655 # string compare and hope for the best
656
656
657 rev = None
657 rev = None
658 # i18n: "converted" is a keyword
658 # i18n: "converted" is a keyword
659 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
659 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
660 if l:
660 if l:
661 # i18n: "converted" is a keyword
661 # i18n: "converted" is a keyword
662 rev = getstring(l[0], _('converted requires a revision'))
662 rev = getstring(l[0], _('converted requires a revision'))
663
663
664 def _matchvalue(r):
664 def _matchvalue(r):
665 source = repo[r].extra().get('convert_revision', None)
665 source = repo[r].extra().get('convert_revision', None)
666 return source is not None and (rev is None or source.startswith(rev))
666 return source is not None and (rev is None or source.startswith(rev))
667
667
668 return subset.filter(lambda r: _matchvalue(r),
668 return subset.filter(lambda r: _matchvalue(r),
669 condrepr=('<converted %r>', rev))
669 condrepr=('<converted %r>', rev))
670
670
671 @predicate('date(interval)', safe=True)
671 @predicate('date(interval)', safe=True)
672 def date(repo, subset, x):
672 def date(repo, subset, x):
673 """Changesets within the interval, see :hg:`help dates`.
673 """Changesets within the interval, see :hg:`help dates`.
674 """
674 """
675 # i18n: "date" is a keyword
675 # i18n: "date" is a keyword
676 ds = getstring(x, _("date requires a string"))
676 ds = getstring(x, _("date requires a string"))
677 dm = util.matchdate(ds)
677 dm = util.matchdate(ds)
678 return subset.filter(lambda x: dm(repo[x].date()[0]),
678 return subset.filter(lambda x: dm(repo[x].date()[0]),
679 condrepr=('<date %r>', ds))
679 condrepr=('<date %r>', ds))
680
680
681 @predicate('desc(string)', safe=True)
681 @predicate('desc(string)', safe=True)
682 def desc(repo, subset, x):
682 def desc(repo, subset, x):
683 """Search commit message for string. The match is case-insensitive.
683 """Search commit message for string. The match is case-insensitive.
684
684
685 Pattern matching is supported for `string`. See
685 Pattern matching is supported for `string`. See
686 :hg:`help revisions.patterns`.
686 :hg:`help revisions.patterns`.
687 """
687 """
688 # i18n: "desc" is a keyword
688 # i18n: "desc" is a keyword
689 ds = getstring(x, _("desc requires a string"))
689 ds = getstring(x, _("desc requires a string"))
690
690
691 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
691 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
692
692
693 return subset.filter(lambda r: matcher(repo[r].description()),
693 return subset.filter(lambda r: matcher(repo[r].description()),
694 condrepr=('<desc %r>', ds))
694 condrepr=('<desc %r>', ds))
695
695
696 def _descendants(repo, subset, x, followfirst=False):
696 def _descendants(repo, subset, x, followfirst=False):
697 roots = getset(repo, fullreposet(repo), x)
697 roots = getset(repo, fullreposet(repo), x)
698 if not roots:
698 if not roots:
699 return baseset()
699 return baseset()
700 s = _revdescendants(repo, roots, followfirst)
700 s = _revdescendants(repo, roots, followfirst)
701
701
702 # Both sets need to be ascending in order to lazily return the union
702 # Both sets need to be ascending in order to lazily return the union
703 # in the correct order.
703 # in the correct order.
704 base = subset & roots
704 base = subset & roots
705 desc = subset & s
705 desc = subset & s
706 result = base + desc
706 result = base + desc
707 if subset.isascending():
707 if subset.isascending():
708 result.sort()
708 result.sort()
709 elif subset.isdescending():
709 elif subset.isdescending():
710 result.sort(reverse=True)
710 result.sort(reverse=True)
711 else:
711 else:
712 result = subset & result
712 result = subset & result
713 return result
713 return result
714
714
715 @predicate('descendants(set)', safe=True)
715 @predicate('descendants(set)', safe=True)
716 def descendants(repo, subset, x):
716 def descendants(repo, subset, x):
717 """Changesets which are descendants of changesets in set.
717 """Changesets which are descendants of changesets in set.
718 """
718 """
719 return _descendants(repo, subset, x)
719 return _descendants(repo, subset, x)
720
720
721 @predicate('_firstdescendants', safe=True)
721 @predicate('_firstdescendants', safe=True)
722 def _firstdescendants(repo, subset, x):
722 def _firstdescendants(repo, subset, x):
723 # ``_firstdescendants(set)``
723 # ``_firstdescendants(set)``
724 # Like ``descendants(set)`` but follows only the first parents.
724 # Like ``descendants(set)`` but follows only the first parents.
725 return _descendants(repo, subset, x, followfirst=True)
725 return _descendants(repo, subset, x, followfirst=True)
726
726
727 @predicate('destination([set])', safe=True)
727 @predicate('destination([set])', safe=True)
728 def destination(repo, subset, x):
728 def destination(repo, subset, x):
729 """Changesets that were created by a graft, transplant or rebase operation,
729 """Changesets that were created by a graft, transplant or rebase operation,
730 with the given revisions specified as the source. Omitting the optional set
730 with the given revisions specified as the source. Omitting the optional set
731 is the same as passing all().
731 is the same as passing all().
732 """
732 """
733 if x is not None:
733 if x is not None:
734 sources = getset(repo, fullreposet(repo), x)
734 sources = getset(repo, fullreposet(repo), x)
735 else:
735 else:
736 sources = fullreposet(repo)
736 sources = fullreposet(repo)
737
737
738 dests = set()
738 dests = set()
739
739
740 # subset contains all of the possible destinations that can be returned, so
740 # subset contains all of the possible destinations that can be returned, so
741 # iterate over them and see if their source(s) were provided in the arg set.
741 # iterate over them and see if their source(s) were provided in the arg set.
742 # Even if the immediate src of r is not in the arg set, src's source (or
742 # Even if the immediate src of r is not in the arg set, src's source (or
743 # further back) may be. Scanning back further than the immediate src allows
743 # further back) may be. Scanning back further than the immediate src allows
744 # transitive transplants and rebases to yield the same results as transitive
744 # transitive transplants and rebases to yield the same results as transitive
745 # grafts.
745 # grafts.
746 for r in subset:
746 for r in subset:
747 src = _getrevsource(repo, r)
747 src = _getrevsource(repo, r)
748 lineage = None
748 lineage = None
749
749
750 while src is not None:
750 while src is not None:
751 if lineage is None:
751 if lineage is None:
752 lineage = list()
752 lineage = list()
753
753
754 lineage.append(r)
754 lineage.append(r)
755
755
756 # The visited lineage is a match if the current source is in the arg
756 # The visited lineage is a match if the current source is in the arg
757 # set. Since every candidate dest is visited by way of iterating
757 # set. Since every candidate dest is visited by way of iterating
758 # subset, any dests further back in the lineage will be tested by a
758 # subset, any dests further back in the lineage will be tested by a
759 # different iteration over subset. Likewise, if the src was already
759 # different iteration over subset. Likewise, if the src was already
760 # selected, the current lineage can be selected without going back
760 # selected, the current lineage can be selected without going back
761 # further.
761 # further.
762 if src in sources or src in dests:
762 if src in sources or src in dests:
763 dests.update(lineage)
763 dests.update(lineage)
764 break
764 break
765
765
766 r = src
766 r = src
767 src = _getrevsource(repo, r)
767 src = _getrevsource(repo, r)
768
768
769 return subset.filter(dests.__contains__,
769 return subset.filter(dests.__contains__,
770 condrepr=lambda: '<destination %r>' % sorted(dests))
770 condrepr=lambda: '<destination %r>' % sorted(dests))
771
771
772 @predicate('divergent()', safe=True)
772 @predicate('divergent()', safe=True)
773 def divergent(repo, subset, x):
773 def divergent(repo, subset, x):
774 """
774 """
775 Final successors of changesets with an alternative set of final successors.
775 Final successors of changesets with an alternative set of final successors.
776 """
776 """
777 # i18n: "divergent" is a keyword
777 # i18n: "divergent" is a keyword
778 getargs(x, 0, 0, _("divergent takes no arguments"))
778 getargs(x, 0, 0, _("divergent takes no arguments"))
779 divergent = obsmod.getrevs(repo, 'divergent')
779 divergent = obsmod.getrevs(repo, 'divergent')
780 return subset & divergent
780 return subset & divergent
781
781
782 @predicate('extinct()', safe=True)
782 @predicate('extinct()', safe=True)
783 def extinct(repo, subset, x):
783 def extinct(repo, subset, x):
784 """Obsolete changesets with obsolete descendants only.
784 """Obsolete changesets with obsolete descendants only.
785 """
785 """
786 # i18n: "extinct" is a keyword
786 # i18n: "extinct" is a keyword
787 getargs(x, 0, 0, _("extinct takes no arguments"))
787 getargs(x, 0, 0, _("extinct takes no arguments"))
788 extincts = obsmod.getrevs(repo, 'extinct')
788 extincts = obsmod.getrevs(repo, 'extinct')
789 return subset & extincts
789 return subset & extincts
790
790
791 @predicate('extra(label, [value])', safe=True)
791 @predicate('extra(label, [value])', safe=True)
792 def extra(repo, subset, x):
792 def extra(repo, subset, x):
793 """Changesets with the given label in the extra metadata, with the given
793 """Changesets with the given label in the extra metadata, with the given
794 optional value.
794 optional value.
795
795
796 Pattern matching is supported for `value`. See
796 Pattern matching is supported for `value`. See
797 :hg:`help revisions.patterns`.
797 :hg:`help revisions.patterns`.
798 """
798 """
799 args = getargsdict(x, 'extra', 'label value')
799 args = getargsdict(x, 'extra', 'label value')
800 if 'label' not in args:
800 if 'label' not in args:
801 # i18n: "extra" is a keyword
801 # i18n: "extra" is a keyword
802 raise error.ParseError(_('extra takes at least 1 argument'))
802 raise error.ParseError(_('extra takes at least 1 argument'))
803 # i18n: "extra" is a keyword
803 # i18n: "extra" is a keyword
804 label = getstring(args['label'], _('first argument to extra must be '
804 label = getstring(args['label'], _('first argument to extra must be '
805 'a string'))
805 'a string'))
806 value = None
806 value = None
807
807
808 if 'value' in args:
808 if 'value' in args:
809 # i18n: "extra" is a keyword
809 # i18n: "extra" is a keyword
810 value = getstring(args['value'], _('second argument to extra must be '
810 value = getstring(args['value'], _('second argument to extra must be '
811 'a string'))
811 'a string'))
812 kind, value, matcher = util.stringmatcher(value)
812 kind, value, matcher = util.stringmatcher(value)
813
813
814 def _matchvalue(r):
814 def _matchvalue(r):
815 extra = repo[r].extra()
815 extra = repo[r].extra()
816 return label in extra and (value is None or matcher(extra[label]))
816 return label in extra and (value is None or matcher(extra[label]))
817
817
818 return subset.filter(lambda r: _matchvalue(r),
818 return subset.filter(lambda r: _matchvalue(r),
819 condrepr=('<extra[%r] %r>', label, value))
819 condrepr=('<extra[%r] %r>', label, value))
820
820
821 @predicate('filelog(pattern)', safe=True)
821 @predicate('filelog(pattern)', safe=True)
822 def filelog(repo, subset, x):
822 def filelog(repo, subset, x):
823 """Changesets connected to the specified filelog.
823 """Changesets connected to the specified filelog.
824
824
825 For performance reasons, visits only revisions mentioned in the file-level
825 For performance reasons, visits only revisions mentioned in the file-level
826 filelog, rather than filtering through all changesets (much faster, but
826 filelog, rather than filtering through all changesets (much faster, but
827 doesn't include deletes or duplicate changes). For a slower, more accurate
827 doesn't include deletes or duplicate changes). For a slower, more accurate
828 result, use ``file()``.
828 result, use ``file()``.
829
829
830 The pattern without explicit kind like ``glob:`` is expected to be
830 The pattern without explicit kind like ``glob:`` is expected to be
831 relative to the current directory and match against a file exactly
831 relative to the current directory and match against a file exactly
832 for efficiency.
832 for efficiency.
833
833
834 If some linkrev points to revisions filtered by the current repoview, we'll
834 If some linkrev points to revisions filtered by the current repoview, we'll
835 work around it to return a non-filtered value.
835 work around it to return a non-filtered value.
836 """
836 """
837
837
838 # i18n: "filelog" is a keyword
838 # i18n: "filelog" is a keyword
839 pat = getstring(x, _("filelog requires a pattern"))
839 pat = getstring(x, _("filelog requires a pattern"))
840 s = set()
840 s = set()
841 cl = repo.changelog
841 cl = repo.changelog
842
842
843 if not matchmod.patkind(pat):
843 if not matchmod.patkind(pat):
844 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
844 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
845 files = [f]
845 files = [f]
846 else:
846 else:
847 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
847 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
848 files = (f for f in repo[None] if m(f))
848 files = (f for f in repo[None] if m(f))
849
849
850 for f in files:
850 for f in files:
851 fl = repo.file(f)
851 fl = repo.file(f)
852 known = {}
852 known = {}
853 scanpos = 0
853 scanpos = 0
854 for fr in list(fl):
854 for fr in list(fl):
855 fn = fl.node(fr)
855 fn = fl.node(fr)
856 if fn in known:
856 if fn in known:
857 s.add(known[fn])
857 s.add(known[fn])
858 continue
858 continue
859
859
860 lr = fl.linkrev(fr)
860 lr = fl.linkrev(fr)
861 if lr in cl:
861 if lr in cl:
862 s.add(lr)
862 s.add(lr)
863 elif scanpos is not None:
863 elif scanpos is not None:
864 # lowest matching changeset is filtered, scan further
864 # lowest matching changeset is filtered, scan further
865 # ahead in changelog
865 # ahead in changelog
866 start = max(lr, scanpos) + 1
866 start = max(lr, scanpos) + 1
867 scanpos = None
867 scanpos = None
868 for r in cl.revs(start):
868 for r in cl.revs(start):
869 # minimize parsing of non-matching entries
869 # minimize parsing of non-matching entries
870 if f in cl.revision(r) and f in cl.readfiles(r):
870 if f in cl.revision(r) and f in cl.readfiles(r):
871 try:
871 try:
872 # try to use manifest delta fastpath
872 # try to use manifest delta fastpath
873 n = repo[r].filenode(f)
873 n = repo[r].filenode(f)
874 if n not in known:
874 if n not in known:
875 if n == fn:
875 if n == fn:
876 s.add(r)
876 s.add(r)
877 scanpos = r
877 scanpos = r
878 break
878 break
879 else:
879 else:
880 known[n] = r
880 known[n] = r
881 except error.ManifestLookupError:
881 except error.ManifestLookupError:
882 # deletion in changelog
882 # deletion in changelog
883 continue
883 continue
884
884
885 return subset & s
885 return subset & s
886
886
887 @predicate('first(set, [n])', safe=True, takeorder=True)
887 @predicate('first(set, [n])', safe=True, takeorder=True)
888 def first(repo, subset, x, order):
888 def first(repo, subset, x, order):
889 """An alias for limit().
889 """An alias for limit().
890 """
890 """
891 return limit(repo, subset, x, order)
891 return limit(repo, subset, x, order)
892
892
893 def _follow(repo, subset, x, name, followfirst=False):
893 def _follow(repo, subset, x, name, followfirst=False):
894 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
894 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
895 "and an optional revset") % name)
895 "and an optional revset") % name)
896 c = repo['.']
896 c = repo['.']
897 if l:
897 if l:
898 x = getstring(l[0], _("%s expected a pattern") % name)
898 x = getstring(l[0], _("%s expected a pattern") % name)
899 rev = None
899 rev = None
900 if len(l) >= 2:
900 if len(l) >= 2:
901 revs = getset(repo, fullreposet(repo), l[1])
901 revs = getset(repo, fullreposet(repo), l[1])
902 if len(revs) != 1:
902 if len(revs) != 1:
903 raise error.RepoLookupError(
903 raise error.RepoLookupError(
904 _("%s expected one starting revision") % name)
904 _("%s expected one starting revision") % name)
905 rev = revs.last()
905 rev = revs.last()
906 c = repo[rev]
906 c = repo[rev]
907 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
907 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
908 ctx=repo[rev], default='path')
908 ctx=repo[rev], default='path')
909
909
910 files = c.manifest().walk(matcher)
910 files = c.manifest().walk(matcher)
911
911
912 s = set()
912 s = set()
913 for fname in files:
913 for fname in files:
914 fctx = c[fname]
914 fctx = c[fname]
915 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
915 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
916 # include the revision responsible for the most recent version
916 # include the revision responsible for the most recent version
917 s.add(fctx.introrev())
917 s.add(fctx.introrev())
918 else:
918 else:
919 s = _revancestors(repo, baseset([c.rev()]), followfirst)
919 s = _revancestors(repo, baseset([c.rev()]), followfirst)
920
920
921 return subset & s
921 return subset & s
922
922
923 @predicate('follow([pattern[, startrev]])', safe=True)
923 @predicate('follow([pattern[, startrev]])', safe=True)
924 def follow(repo, subset, x):
924 def follow(repo, subset, x):
925 """
925 """
926 An alias for ``::.`` (ancestors of the working directory's first parent).
926 An alias for ``::.`` (ancestors of the working directory's first parent).
927 If pattern is specified, the histories of files matching given
927 If pattern is specified, the histories of files matching given
928 pattern in the revision given by startrev are followed, including copies.
928 pattern in the revision given by startrev are followed, including copies.
929 """
929 """
930 return _follow(repo, subset, x, 'follow')
930 return _follow(repo, subset, x, 'follow')
931
931
932 @predicate('_followfirst', safe=True)
932 @predicate('_followfirst', safe=True)
933 def _followfirst(repo, subset, x):
933 def _followfirst(repo, subset, x):
934 # ``followfirst([pattern[, startrev]])``
934 # ``followfirst([pattern[, startrev]])``
935 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
935 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
936 # of every revisions or files revisions.
936 # of every revisions or files revisions.
937 return _follow(repo, subset, x, '_followfirst', followfirst=True)
937 return _follow(repo, subset, x, '_followfirst', followfirst=True)
938
938
939 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
939 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
940 safe=True)
940 safe=True)
941 def followlines(repo, subset, x):
941 def followlines(repo, subset, x):
942 """Changesets modifying `file` in line range ('fromline', 'toline').
942 """Changesets modifying `file` in line range ('fromline', 'toline').
943
943
944 Line range corresponds to 'file' content at 'startrev' and should hence be
944 Line range corresponds to 'file' content at 'startrev' and should hence be
945 consistent with file size. If startrev is not specified, working directory's
945 consistent with file size. If startrev is not specified, working directory's
946 parent is used.
946 parent is used.
947
947
948 By default, ancestors of 'startrev' are returned. If 'descend' is True,
948 By default, ancestors of 'startrev' are returned. If 'descend' is True,
949 descendants of 'startrev' are returned though renames are (currently) not
949 descendants of 'startrev' are returned though renames are (currently) not
950 followed in this direction.
950 followed in this direction.
951 """
951 """
952 from . import context # avoid circular import issues
952 from . import context # avoid circular import issues
953
953
954 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
954 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
955 if len(args['lines']) != 1:
955 if len(args['lines']) != 1:
956 raise error.ParseError(_("followlines requires a line range"))
956 raise error.ParseError(_("followlines requires a line range"))
957
957
958 rev = '.'
958 rev = '.'
959 if 'startrev' in args:
959 if 'startrev' in args:
960 revs = getset(repo, fullreposet(repo), args['startrev'])
960 revs = getset(repo, fullreposet(repo), args['startrev'])
961 if len(revs) != 1:
961 if len(revs) != 1:
962 raise error.ParseError(
962 raise error.ParseError(
963 # i18n: "followlines" is a keyword
963 # i18n: "followlines" is a keyword
964 _("followlines expects exactly one revision"))
964 _("followlines expects exactly one revision"))
965 rev = revs.last()
965 rev = revs.last()
966
966
967 pat = getstring(args['file'], _("followlines requires a pattern"))
967 pat = getstring(args['file'], _("followlines requires a pattern"))
968 if not matchmod.patkind(pat):
968 if not matchmod.patkind(pat):
969 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
969 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
970 else:
970 else:
971 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
971 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
972 files = [f for f in repo[rev] if m(f)]
972 files = [f for f in repo[rev] if m(f)]
973 if len(files) != 1:
973 if len(files) != 1:
974 # i18n: "followlines" is a keyword
974 # i18n: "followlines" is a keyword
975 raise error.ParseError(_("followlines expects exactly one file"))
975 raise error.ParseError(_("followlines expects exactly one file"))
976 fname = files[0]
976 fname = files[0]
977
977
978 # i18n: "followlines" is a keyword
978 # i18n: "followlines" is a keyword
979 lr = getrange(args['lines'][0], _("followlines expects a line range"))
979 lr = getrange(args['lines'][0], _("followlines expects a line range"))
980 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
980 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
981 for a in lr]
981 for a in lr]
982 fromline, toline = util.processlinerange(fromline, toline)
982 fromline, toline = util.processlinerange(fromline, toline)
983
983
984 fctx = repo[rev].filectx(fname)
984 fctx = repo[rev].filectx(fname)
985 descend = False
985 descend = False
986 if 'descend' in args:
986 if 'descend' in args:
987 descend = getboolean(args['descend'],
987 descend = getboolean(args['descend'],
988 # i18n: "descend" is a keyword
988 # i18n: "descend" is a keyword
989 _("descend argument must be a boolean"))
989 _("descend argument must be a boolean"))
990 if descend:
990 if descend:
991 rs = generatorset(
991 rs = generatorset(
992 (c.rev() for c, _linerange
992 (c.rev() for c, _linerange
993 in context.blockdescendants(fctx, fromline, toline)),
993 in context.blockdescendants(fctx, fromline, toline)),
994 iterasc=True)
994 iterasc=True)
995 else:
995 else:
996 rs = generatorset(
996 rs = generatorset(
997 (c.rev() for c, _linerange
997 (c.rev() for c, _linerange
998 in context.blockancestors(fctx, fromline, toline)),
998 in context.blockancestors(fctx, fromline, toline)),
999 iterasc=False)
999 iterasc=False)
1000 return subset & rs
1000 return subset & rs
1001
1001
1002 @predicate('all()', safe=True)
1002 @predicate('all()', safe=True)
1003 def getall(repo, subset, x):
1003 def getall(repo, subset, x):
1004 """All changesets, the same as ``0:tip``.
1004 """All changesets, the same as ``0:tip``.
1005 """
1005 """
1006 # i18n: "all" is a keyword
1006 # i18n: "all" is a keyword
1007 getargs(x, 0, 0, _("all takes no arguments"))
1007 getargs(x, 0, 0, _("all takes no arguments"))
1008 return subset & spanset(repo) # drop "null" if any
1008 return subset & spanset(repo) # drop "null" if any
1009
1009
1010 @predicate('grep(regex)')
1010 @predicate('grep(regex)')
1011 def grep(repo, subset, x):
1011 def grep(repo, subset, x):
1012 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1012 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1013 to ensure special escape characters are handled correctly. Unlike
1013 to ensure special escape characters are handled correctly. Unlike
1014 ``keyword(string)``, the match is case-sensitive.
1014 ``keyword(string)``, the match is case-sensitive.
1015 """
1015 """
1016 try:
1016 try:
1017 # i18n: "grep" is a keyword
1017 # i18n: "grep" is a keyword
1018 gr = re.compile(getstring(x, _("grep requires a string")))
1018 gr = re.compile(getstring(x, _("grep requires a string")))
1019 except re.error as e:
1019 except re.error as e:
1020 raise error.ParseError(_('invalid match pattern: %s') % e)
1020 raise error.ParseError(_('invalid match pattern: %s') % e)
1021
1021
1022 def matches(x):
1022 def matches(x):
1023 c = repo[x]
1023 c = repo[x]
1024 for e in c.files() + [c.user(), c.description()]:
1024 for e in c.files() + [c.user(), c.description()]:
1025 if gr.search(e):
1025 if gr.search(e):
1026 return True
1026 return True
1027 return False
1027 return False
1028
1028
1029 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1029 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1030
1030
1031 @predicate('_matchfiles', safe=True)
1031 @predicate('_matchfiles', safe=True)
1032 def _matchfiles(repo, subset, x):
1032 def _matchfiles(repo, subset, x):
1033 # _matchfiles takes a revset list of prefixed arguments:
1033 # _matchfiles takes a revset list of prefixed arguments:
1034 #
1034 #
1035 # [p:foo, i:bar, x:baz]
1035 # [p:foo, i:bar, x:baz]
1036 #
1036 #
1037 # builds a match object from them and filters subset. Allowed
1037 # builds a match object from them and filters subset. Allowed
1038 # prefixes are 'p:' for regular patterns, 'i:' for include
1038 # prefixes are 'p:' for regular patterns, 'i:' for include
1039 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1039 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1040 # a revision identifier, or the empty string to reference the
1040 # a revision identifier, or the empty string to reference the
1041 # working directory, from which the match object is
1041 # working directory, from which the match object is
1042 # initialized. Use 'd:' to set the default matching mode, default
1042 # initialized. Use 'd:' to set the default matching mode, default
1043 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1043 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1044
1044
1045 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1045 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1046 pats, inc, exc = [], [], []
1046 pats, inc, exc = [], [], []
1047 rev, default = None, None
1047 rev, default = None, None
1048 for arg in l:
1048 for arg in l:
1049 s = getstring(arg, "_matchfiles requires string arguments")
1049 s = getstring(arg, "_matchfiles requires string arguments")
1050 prefix, value = s[:2], s[2:]
1050 prefix, value = s[:2], s[2:]
1051 if prefix == 'p:':
1051 if prefix == 'p:':
1052 pats.append(value)
1052 pats.append(value)
1053 elif prefix == 'i:':
1053 elif prefix == 'i:':
1054 inc.append(value)
1054 inc.append(value)
1055 elif prefix == 'x:':
1055 elif prefix == 'x:':
1056 exc.append(value)
1056 exc.append(value)
1057 elif prefix == 'r:':
1057 elif prefix == 'r:':
1058 if rev is not None:
1058 if rev is not None:
1059 raise error.ParseError('_matchfiles expected at most one '
1059 raise error.ParseError('_matchfiles expected at most one '
1060 'revision')
1060 'revision')
1061 if value != '': # empty means working directory; leave rev as None
1061 if value != '': # empty means working directory; leave rev as None
1062 rev = value
1062 rev = value
1063 elif prefix == 'd:':
1063 elif prefix == 'd:':
1064 if default is not None:
1064 if default is not None:
1065 raise error.ParseError('_matchfiles expected at most one '
1065 raise error.ParseError('_matchfiles expected at most one '
1066 'default mode')
1066 'default mode')
1067 default = value
1067 default = value
1068 else:
1068 else:
1069 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1069 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1070 if not default:
1070 if not default:
1071 default = 'glob'
1071 default = 'glob'
1072
1072
1073 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1073 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1074 exclude=exc, ctx=repo[rev], default=default)
1074 exclude=exc, ctx=repo[rev], default=default)
1075
1075
1076 # This directly read the changelog data as creating changectx for all
1076 # This directly read the changelog data as creating changectx for all
1077 # revisions is quite expensive.
1077 # revisions is quite expensive.
1078 getfiles = repo.changelog.readfiles
1078 getfiles = repo.changelog.readfiles
1079 wdirrev = node.wdirrev
1079 wdirrev = node.wdirrev
1080 def matches(x):
1080 def matches(x):
1081 if x == wdirrev:
1081 if x == wdirrev:
1082 files = repo[x].files()
1082 files = repo[x].files()
1083 else:
1083 else:
1084 files = getfiles(x)
1084 files = getfiles(x)
1085 for f in files:
1085 for f in files:
1086 if m(f):
1086 if m(f):
1087 return True
1087 return True
1088 return False
1088 return False
1089
1089
1090 return subset.filter(matches,
1090 return subset.filter(matches,
1091 condrepr=('<matchfiles patterns=%r, include=%r '
1091 condrepr=('<matchfiles patterns=%r, include=%r '
1092 'exclude=%r, default=%r, rev=%r>',
1092 'exclude=%r, default=%r, rev=%r>',
1093 pats, inc, exc, default, rev))
1093 pats, inc, exc, default, rev))
1094
1094
1095 @predicate('file(pattern)', safe=True)
1095 @predicate('file(pattern)', safe=True)
1096 def hasfile(repo, subset, x):
1096 def hasfile(repo, subset, x):
1097 """Changesets affecting files matched by pattern.
1097 """Changesets affecting files matched by pattern.
1098
1098
1099 For a faster but less accurate result, consider using ``filelog()``
1099 For a faster but less accurate result, consider using ``filelog()``
1100 instead.
1100 instead.
1101
1101
1102 This predicate uses ``glob:`` as the default kind of pattern.
1102 This predicate uses ``glob:`` as the default kind of pattern.
1103 """
1103 """
1104 # i18n: "file" is a keyword
1104 # i18n: "file" is a keyword
1105 pat = getstring(x, _("file requires a pattern"))
1105 pat = getstring(x, _("file requires a pattern"))
1106 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1106 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1107
1107
1108 @predicate('head()', safe=True)
1108 @predicate('head()', safe=True)
1109 def head(repo, subset, x):
1109 def head(repo, subset, x):
1110 """Changeset is a named branch head.
1110 """Changeset is a named branch head.
1111 """
1111 """
1112 # i18n: "head" is a keyword
1112 # i18n: "head" is a keyword
1113 getargs(x, 0, 0, _("head takes no arguments"))
1113 getargs(x, 0, 0, _("head takes no arguments"))
1114 hs = set()
1114 hs = set()
1115 cl = repo.changelog
1115 cl = repo.changelog
1116 for ls in repo.branchmap().itervalues():
1116 for ls in repo.branchmap().itervalues():
1117 hs.update(cl.rev(h) for h in ls)
1117 hs.update(cl.rev(h) for h in ls)
1118 return subset & baseset(hs)
1118 return subset & baseset(hs)
1119
1119
1120 @predicate('heads(set)', safe=True)
1120 @predicate('heads(set)', safe=True)
1121 def heads(repo, subset, x):
1121 def heads(repo, subset, x):
1122 """Members of set with no children in set.
1122 """Members of set with no children in set.
1123 """
1123 """
1124 s = getset(repo, subset, x)
1124 s = getset(repo, subset, x)
1125 ps = parents(repo, subset, x)
1125 ps = parents(repo, subset, x)
1126 return s - ps
1126 return s - ps
1127
1127
1128 @predicate('hidden()', safe=True)
1128 @predicate('hidden()', safe=True)
1129 def hidden(repo, subset, x):
1129 def hidden(repo, subset, x):
1130 """Hidden changesets.
1130 """Hidden changesets.
1131 """
1131 """
1132 # i18n: "hidden" is a keyword
1132 # i18n: "hidden" is a keyword
1133 getargs(x, 0, 0, _("hidden takes no arguments"))
1133 getargs(x, 0, 0, _("hidden takes no arguments"))
1134 hiddenrevs = repoview.filterrevs(repo, 'visible')
1134 hiddenrevs = repoview.filterrevs(repo, 'visible')
1135 return subset & hiddenrevs
1135 return subset & hiddenrevs
1136
1136
1137 @predicate('keyword(string)', safe=True)
1137 @predicate('keyword(string)', safe=True)
1138 def keyword(repo, subset, x):
1138 def keyword(repo, subset, x):
1139 """Search commit message, user name, and names of changed files for
1139 """Search commit message, user name, and names of changed files for
1140 string. The match is case-insensitive.
1140 string. The match is case-insensitive.
1141
1141
1142 For a regular expression or case sensitive search of these fields, use
1142 For a regular expression or case sensitive search of these fields, use
1143 ``grep(regex)``.
1143 ``grep(regex)``.
1144 """
1144 """
1145 # i18n: "keyword" is a keyword
1145 # i18n: "keyword" is a keyword
1146 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1146 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1147
1147
1148 def matches(r):
1148 def matches(r):
1149 c = repo[r]
1149 c = repo[r]
1150 return any(kw in encoding.lower(t)
1150 return any(kw in encoding.lower(t)
1151 for t in c.files() + [c.user(), c.description()])
1151 for t in c.files() + [c.user(), c.description()])
1152
1152
1153 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1153 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1154
1154
1155 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True)
1155 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True)
1156 def limit(repo, subset, x, order):
1156 def limit(repo, subset, x, order):
1157 """First n members of set, defaulting to 1, starting from offset.
1157 """First n members of set, defaulting to 1, starting from offset.
1158 """
1158 """
1159 args = getargsdict(x, 'limit', 'set n offset')
1159 args = getargsdict(x, 'limit', 'set n offset')
1160 if 'set' not in args:
1160 if 'set' not in args:
1161 # i18n: "limit" is a keyword
1161 # i18n: "limit" is a keyword
1162 raise error.ParseError(_("limit requires one to three arguments"))
1162 raise error.ParseError(_("limit requires one to three arguments"))
1163 # i18n: "limit" is a keyword
1163 # i18n: "limit" is a keyword
1164 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1164 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1165 if lim < 0:
1165 if lim < 0:
1166 raise error.ParseError(_("negative number to select"))
1166 raise error.ParseError(_("negative number to select"))
1167 # i18n: "limit" is a keyword
1167 # i18n: "limit" is a keyword
1168 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1168 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1169 if ofs < 0:
1169 if ofs < 0:
1170 raise error.ParseError(_("negative offset"))
1170 raise error.ParseError(_("negative offset"))
1171 os = getset(repo, fullreposet(repo), args['set'])
1171 os = getset(repo, fullreposet(repo), args['set'])
1172 result = []
1172 ls = os.slice(ofs, ofs + lim)
1173 it = iter(os)
1174 for x in xrange(ofs):
1175 y = next(it, None)
1176 if y is None:
1177 break
1178 for x in xrange(lim):
1179 y = next(it, None)
1180 if y is None:
1181 break
1182 result.append(y)
1183 ls = baseset(result, datarepr=('<limit n=%d, offset=%d, %r>', lim, ofs, os))
1184 if order == followorder and lim > 1:
1173 if order == followorder and lim > 1:
1185 return subset & ls
1174 return subset & ls
1186 return ls & subset
1175 return ls & subset
1187
1176
1188 @predicate('last(set, [n])', safe=True, takeorder=True)
1177 @predicate('last(set, [n])', safe=True, takeorder=True)
1189 def last(repo, subset, x, order):
1178 def last(repo, subset, x, order):
1190 """Last n members of set, defaulting to 1.
1179 """Last n members of set, defaulting to 1.
1191 """
1180 """
1192 # i18n: "last" is a keyword
1181 # i18n: "last" is a keyword
1193 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1182 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1194 lim = 1
1183 lim = 1
1195 if len(l) == 2:
1184 if len(l) == 2:
1196 # i18n: "last" is a keyword
1185 # i18n: "last" is a keyword
1197 lim = getinteger(l[1], _("last expects a number"))
1186 lim = getinteger(l[1], _("last expects a number"))
1198 if lim < 0:
1187 if lim < 0:
1199 raise error.ParseError(_("negative number to select"))
1188 raise error.ParseError(_("negative number to select"))
1200 os = getset(repo, fullreposet(repo), l[0])
1189 os = getset(repo, fullreposet(repo), l[0])
1201 os.reverse()
1190 os.reverse()
1202 result = []
1191 ls = os.slice(0, lim)
1203 it = iter(os)
1204 for x in xrange(lim):
1205 y = next(it, None)
1206 if y is None:
1207 break
1208 result.append(y)
1209 ls = baseset(result, datarepr=('<last n=%d, %r>', lim, os))
1210 if order == followorder and lim > 1:
1192 if order == followorder and lim > 1:
1211 return subset & ls
1193 return subset & ls
1212 ls.reverse()
1194 ls.reverse()
1213 return ls & subset
1195 return ls & subset
1214
1196
1215 @predicate('max(set)', safe=True)
1197 @predicate('max(set)', safe=True)
1216 def maxrev(repo, subset, x):
1198 def maxrev(repo, subset, x):
1217 """Changeset with highest revision number in set.
1199 """Changeset with highest revision number in set.
1218 """
1200 """
1219 os = getset(repo, fullreposet(repo), x)
1201 os = getset(repo, fullreposet(repo), x)
1220 try:
1202 try:
1221 m = os.max()
1203 m = os.max()
1222 if m in subset:
1204 if m in subset:
1223 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1205 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1224 except ValueError:
1206 except ValueError:
1225 # os.max() throws a ValueError when the collection is empty.
1207 # os.max() throws a ValueError when the collection is empty.
1226 # Same as python's max().
1208 # Same as python's max().
1227 pass
1209 pass
1228 return baseset(datarepr=('<max %r, %r>', subset, os))
1210 return baseset(datarepr=('<max %r, %r>', subset, os))
1229
1211
1230 @predicate('merge()', safe=True)
1212 @predicate('merge()', safe=True)
1231 def merge(repo, subset, x):
1213 def merge(repo, subset, x):
1232 """Changeset is a merge changeset.
1214 """Changeset is a merge changeset.
1233 """
1215 """
1234 # i18n: "merge" is a keyword
1216 # i18n: "merge" is a keyword
1235 getargs(x, 0, 0, _("merge takes no arguments"))
1217 getargs(x, 0, 0, _("merge takes no arguments"))
1236 cl = repo.changelog
1218 cl = repo.changelog
1237 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1219 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1238 condrepr='<merge>')
1220 condrepr='<merge>')
1239
1221
1240 @predicate('branchpoint()', safe=True)
1222 @predicate('branchpoint()', safe=True)
1241 def branchpoint(repo, subset, x):
1223 def branchpoint(repo, subset, x):
1242 """Changesets with more than one child.
1224 """Changesets with more than one child.
1243 """
1225 """
1244 # i18n: "branchpoint" is a keyword
1226 # i18n: "branchpoint" is a keyword
1245 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1227 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1246 cl = repo.changelog
1228 cl = repo.changelog
1247 if not subset:
1229 if not subset:
1248 return baseset()
1230 return baseset()
1249 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1231 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1250 # (and if it is not, it should.)
1232 # (and if it is not, it should.)
1251 baserev = min(subset)
1233 baserev = min(subset)
1252 parentscount = [0]*(len(repo) - baserev)
1234 parentscount = [0]*(len(repo) - baserev)
1253 for r in cl.revs(start=baserev + 1):
1235 for r in cl.revs(start=baserev + 1):
1254 for p in cl.parentrevs(r):
1236 for p in cl.parentrevs(r):
1255 if p >= baserev:
1237 if p >= baserev:
1256 parentscount[p - baserev] += 1
1238 parentscount[p - baserev] += 1
1257 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1239 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1258 condrepr='<branchpoint>')
1240 condrepr='<branchpoint>')
1259
1241
1260 @predicate('min(set)', safe=True)
1242 @predicate('min(set)', safe=True)
1261 def minrev(repo, subset, x):
1243 def minrev(repo, subset, x):
1262 """Changeset with lowest revision number in set.
1244 """Changeset with lowest revision number in set.
1263 """
1245 """
1264 os = getset(repo, fullreposet(repo), x)
1246 os = getset(repo, fullreposet(repo), x)
1265 try:
1247 try:
1266 m = os.min()
1248 m = os.min()
1267 if m in subset:
1249 if m in subset:
1268 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1250 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1269 except ValueError:
1251 except ValueError:
1270 # os.min() throws a ValueError when the collection is empty.
1252 # os.min() throws a ValueError when the collection is empty.
1271 # Same as python's min().
1253 # Same as python's min().
1272 pass
1254 pass
1273 return baseset(datarepr=('<min %r, %r>', subset, os))
1255 return baseset(datarepr=('<min %r, %r>', subset, os))
1274
1256
1275 @predicate('modifies(pattern)', safe=True)
1257 @predicate('modifies(pattern)', safe=True)
1276 def modifies(repo, subset, x):
1258 def modifies(repo, subset, x):
1277 """Changesets modifying files matched by pattern.
1259 """Changesets modifying files matched by pattern.
1278
1260
1279 The pattern without explicit kind like ``glob:`` is expected to be
1261 The pattern without explicit kind like ``glob:`` is expected to be
1280 relative to the current directory and match against a file or a
1262 relative to the current directory and match against a file or a
1281 directory.
1263 directory.
1282 """
1264 """
1283 # i18n: "modifies" is a keyword
1265 # i18n: "modifies" is a keyword
1284 pat = getstring(x, _("modifies requires a pattern"))
1266 pat = getstring(x, _("modifies requires a pattern"))
1285 return checkstatus(repo, subset, pat, 0)
1267 return checkstatus(repo, subset, pat, 0)
1286
1268
1287 @predicate('named(namespace)')
1269 @predicate('named(namespace)')
1288 def named(repo, subset, x):
1270 def named(repo, subset, x):
1289 """The changesets in a given namespace.
1271 """The changesets in a given namespace.
1290
1272
1291 Pattern matching is supported for `namespace`. See
1273 Pattern matching is supported for `namespace`. See
1292 :hg:`help revisions.patterns`.
1274 :hg:`help revisions.patterns`.
1293 """
1275 """
1294 # i18n: "named" is a keyword
1276 # i18n: "named" is a keyword
1295 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1277 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1296
1278
1297 ns = getstring(args[0],
1279 ns = getstring(args[0],
1298 # i18n: "named" is a keyword
1280 # i18n: "named" is a keyword
1299 _('the argument to named must be a string'))
1281 _('the argument to named must be a string'))
1300 kind, pattern, matcher = util.stringmatcher(ns)
1282 kind, pattern, matcher = util.stringmatcher(ns)
1301 namespaces = set()
1283 namespaces = set()
1302 if kind == 'literal':
1284 if kind == 'literal':
1303 if pattern not in repo.names:
1285 if pattern not in repo.names:
1304 raise error.RepoLookupError(_("namespace '%s' does not exist")
1286 raise error.RepoLookupError(_("namespace '%s' does not exist")
1305 % ns)
1287 % ns)
1306 namespaces.add(repo.names[pattern])
1288 namespaces.add(repo.names[pattern])
1307 else:
1289 else:
1308 for name, ns in repo.names.iteritems():
1290 for name, ns in repo.names.iteritems():
1309 if matcher(name):
1291 if matcher(name):
1310 namespaces.add(ns)
1292 namespaces.add(ns)
1311 if not namespaces:
1293 if not namespaces:
1312 raise error.RepoLookupError(_("no namespace exists"
1294 raise error.RepoLookupError(_("no namespace exists"
1313 " that match '%s'") % pattern)
1295 " that match '%s'") % pattern)
1314
1296
1315 names = set()
1297 names = set()
1316 for ns in namespaces:
1298 for ns in namespaces:
1317 for name in ns.listnames(repo):
1299 for name in ns.listnames(repo):
1318 if name not in ns.deprecated:
1300 if name not in ns.deprecated:
1319 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1301 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1320
1302
1321 names -= {node.nullrev}
1303 names -= {node.nullrev}
1322 return subset & names
1304 return subset & names
1323
1305
1324 @predicate('id(string)', safe=True)
1306 @predicate('id(string)', safe=True)
1325 def node_(repo, subset, x):
1307 def node_(repo, subset, x):
1326 """Revision non-ambiguously specified by the given hex string prefix.
1308 """Revision non-ambiguously specified by the given hex string prefix.
1327 """
1309 """
1328 # i18n: "id" is a keyword
1310 # i18n: "id" is a keyword
1329 l = getargs(x, 1, 1, _("id requires one argument"))
1311 l = getargs(x, 1, 1, _("id requires one argument"))
1330 # i18n: "id" is a keyword
1312 # i18n: "id" is a keyword
1331 n = getstring(l[0], _("id requires a string"))
1313 n = getstring(l[0], _("id requires a string"))
1332 if len(n) == 40:
1314 if len(n) == 40:
1333 try:
1315 try:
1334 rn = repo.changelog.rev(node.bin(n))
1316 rn = repo.changelog.rev(node.bin(n))
1335 except error.WdirUnsupported:
1317 except error.WdirUnsupported:
1336 rn = node.wdirrev
1318 rn = node.wdirrev
1337 except (LookupError, TypeError):
1319 except (LookupError, TypeError):
1338 rn = None
1320 rn = None
1339 else:
1321 else:
1340 rn = None
1322 rn = None
1341 try:
1323 try:
1342 pm = repo.changelog._partialmatch(n)
1324 pm = repo.changelog._partialmatch(n)
1343 if pm is not None:
1325 if pm is not None:
1344 rn = repo.changelog.rev(pm)
1326 rn = repo.changelog.rev(pm)
1345 except error.WdirUnsupported:
1327 except error.WdirUnsupported:
1346 rn = node.wdirrev
1328 rn = node.wdirrev
1347
1329
1348 if rn is None:
1330 if rn is None:
1349 return baseset()
1331 return baseset()
1350 result = baseset([rn])
1332 result = baseset([rn])
1351 return result & subset
1333 return result & subset
1352
1334
1353 @predicate('obsolete()', safe=True)
1335 @predicate('obsolete()', safe=True)
1354 def obsolete(repo, subset, x):
1336 def obsolete(repo, subset, x):
1355 """Mutable changeset with a newer version."""
1337 """Mutable changeset with a newer version."""
1356 # i18n: "obsolete" is a keyword
1338 # i18n: "obsolete" is a keyword
1357 getargs(x, 0, 0, _("obsolete takes no arguments"))
1339 getargs(x, 0, 0, _("obsolete takes no arguments"))
1358 obsoletes = obsmod.getrevs(repo, 'obsolete')
1340 obsoletes = obsmod.getrevs(repo, 'obsolete')
1359 return subset & obsoletes
1341 return subset & obsoletes
1360
1342
1361 @predicate('only(set, [set])', safe=True)
1343 @predicate('only(set, [set])', safe=True)
1362 def only(repo, subset, x):
1344 def only(repo, subset, x):
1363 """Changesets that are ancestors of the first set that are not ancestors
1345 """Changesets that are ancestors of the first set that are not ancestors
1364 of any other head in the repo. If a second set is specified, the result
1346 of any other head in the repo. If a second set is specified, the result
1365 is ancestors of the first set that are not ancestors of the second set
1347 is ancestors of the first set that are not ancestors of the second set
1366 (i.e. ::<set1> - ::<set2>).
1348 (i.e. ::<set1> - ::<set2>).
1367 """
1349 """
1368 cl = repo.changelog
1350 cl = repo.changelog
1369 # i18n: "only" is a keyword
1351 # i18n: "only" is a keyword
1370 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1352 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1371 include = getset(repo, fullreposet(repo), args[0])
1353 include = getset(repo, fullreposet(repo), args[0])
1372 if len(args) == 1:
1354 if len(args) == 1:
1373 if not include:
1355 if not include:
1374 return baseset()
1356 return baseset()
1375
1357
1376 descendants = set(_revdescendants(repo, include, False))
1358 descendants = set(_revdescendants(repo, include, False))
1377 exclude = [rev for rev in cl.headrevs()
1359 exclude = [rev for rev in cl.headrevs()
1378 if not rev in descendants and not rev in include]
1360 if not rev in descendants and not rev in include]
1379 else:
1361 else:
1380 exclude = getset(repo, fullreposet(repo), args[1])
1362 exclude = getset(repo, fullreposet(repo), args[1])
1381
1363
1382 results = set(cl.findmissingrevs(common=exclude, heads=include))
1364 results = set(cl.findmissingrevs(common=exclude, heads=include))
1383 # XXX we should turn this into a baseset instead of a set, smartset may do
1365 # XXX we should turn this into a baseset instead of a set, smartset may do
1384 # some optimizations from the fact this is a baseset.
1366 # some optimizations from the fact this is a baseset.
1385 return subset & results
1367 return subset & results
1386
1368
1387 @predicate('origin([set])', safe=True)
1369 @predicate('origin([set])', safe=True)
1388 def origin(repo, subset, x):
1370 def origin(repo, subset, x):
1389 """
1371 """
1390 Changesets that were specified as a source for the grafts, transplants or
1372 Changesets that were specified as a source for the grafts, transplants or
1391 rebases that created the given revisions. Omitting the optional set is the
1373 rebases that created the given revisions. Omitting the optional set is the
1392 same as passing all(). If a changeset created by these operations is itself
1374 same as passing all(). If a changeset created by these operations is itself
1393 specified as a source for one of these operations, only the source changeset
1375 specified as a source for one of these operations, only the source changeset
1394 for the first operation is selected.
1376 for the first operation is selected.
1395 """
1377 """
1396 if x is not None:
1378 if x is not None:
1397 dests = getset(repo, fullreposet(repo), x)
1379 dests = getset(repo, fullreposet(repo), x)
1398 else:
1380 else:
1399 dests = fullreposet(repo)
1381 dests = fullreposet(repo)
1400
1382
1401 def _firstsrc(rev):
1383 def _firstsrc(rev):
1402 src = _getrevsource(repo, rev)
1384 src = _getrevsource(repo, rev)
1403 if src is None:
1385 if src is None:
1404 return None
1386 return None
1405
1387
1406 while True:
1388 while True:
1407 prev = _getrevsource(repo, src)
1389 prev = _getrevsource(repo, src)
1408
1390
1409 if prev is None:
1391 if prev is None:
1410 return src
1392 return src
1411 src = prev
1393 src = prev
1412
1394
1413 o = {_firstsrc(r) for r in dests}
1395 o = {_firstsrc(r) for r in dests}
1414 o -= {None}
1396 o -= {None}
1415 # XXX we should turn this into a baseset instead of a set, smartset may do
1397 # XXX we should turn this into a baseset instead of a set, smartset may do
1416 # some optimizations from the fact this is a baseset.
1398 # some optimizations from the fact this is a baseset.
1417 return subset & o
1399 return subset & o
1418
1400
1419 @predicate('outgoing([path])', safe=False)
1401 @predicate('outgoing([path])', safe=False)
1420 def outgoing(repo, subset, x):
1402 def outgoing(repo, subset, x):
1421 """Changesets not found in the specified destination repository, or the
1403 """Changesets not found in the specified destination repository, or the
1422 default push location.
1404 default push location.
1423 """
1405 """
1424 # Avoid cycles.
1406 # Avoid cycles.
1425 from . import (
1407 from . import (
1426 discovery,
1408 discovery,
1427 hg,
1409 hg,
1428 )
1410 )
1429 # i18n: "outgoing" is a keyword
1411 # i18n: "outgoing" is a keyword
1430 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1412 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1431 # i18n: "outgoing" is a keyword
1413 # i18n: "outgoing" is a keyword
1432 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1414 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1433 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1415 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1434 dest, branches = hg.parseurl(dest)
1416 dest, branches = hg.parseurl(dest)
1435 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1417 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1436 if revs:
1418 if revs:
1437 revs = [repo.lookup(rev) for rev in revs]
1419 revs = [repo.lookup(rev) for rev in revs]
1438 other = hg.peer(repo, {}, dest)
1420 other = hg.peer(repo, {}, dest)
1439 repo.ui.pushbuffer()
1421 repo.ui.pushbuffer()
1440 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1422 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1441 repo.ui.popbuffer()
1423 repo.ui.popbuffer()
1442 cl = repo.changelog
1424 cl = repo.changelog
1443 o = {cl.rev(r) for r in outgoing.missing}
1425 o = {cl.rev(r) for r in outgoing.missing}
1444 return subset & o
1426 return subset & o
1445
1427
1446 @predicate('p1([set])', safe=True)
1428 @predicate('p1([set])', safe=True)
1447 def p1(repo, subset, x):
1429 def p1(repo, subset, x):
1448 """First parent of changesets in set, or the working directory.
1430 """First parent of changesets in set, or the working directory.
1449 """
1431 """
1450 if x is None:
1432 if x is None:
1451 p = repo[x].p1().rev()
1433 p = repo[x].p1().rev()
1452 if p >= 0:
1434 if p >= 0:
1453 return subset & baseset([p])
1435 return subset & baseset([p])
1454 return baseset()
1436 return baseset()
1455
1437
1456 ps = set()
1438 ps = set()
1457 cl = repo.changelog
1439 cl = repo.changelog
1458 for r in getset(repo, fullreposet(repo), x):
1440 for r in getset(repo, fullreposet(repo), x):
1459 try:
1441 try:
1460 ps.add(cl.parentrevs(r)[0])
1442 ps.add(cl.parentrevs(r)[0])
1461 except error.WdirUnsupported:
1443 except error.WdirUnsupported:
1462 ps.add(repo[r].parents()[0].rev())
1444 ps.add(repo[r].parents()[0].rev())
1463 ps -= {node.nullrev}
1445 ps -= {node.nullrev}
1464 # XXX we should turn this into a baseset instead of a set, smartset may do
1446 # XXX we should turn this into a baseset instead of a set, smartset may do
1465 # some optimizations from the fact this is a baseset.
1447 # some optimizations from the fact this is a baseset.
1466 return subset & ps
1448 return subset & ps
1467
1449
1468 @predicate('p2([set])', safe=True)
1450 @predicate('p2([set])', safe=True)
1469 def p2(repo, subset, x):
1451 def p2(repo, subset, x):
1470 """Second parent of changesets in set, or the working directory.
1452 """Second parent of changesets in set, or the working directory.
1471 """
1453 """
1472 if x is None:
1454 if x is None:
1473 ps = repo[x].parents()
1455 ps = repo[x].parents()
1474 try:
1456 try:
1475 p = ps[1].rev()
1457 p = ps[1].rev()
1476 if p >= 0:
1458 if p >= 0:
1477 return subset & baseset([p])
1459 return subset & baseset([p])
1478 return baseset()
1460 return baseset()
1479 except IndexError:
1461 except IndexError:
1480 return baseset()
1462 return baseset()
1481
1463
1482 ps = set()
1464 ps = set()
1483 cl = repo.changelog
1465 cl = repo.changelog
1484 for r in getset(repo, fullreposet(repo), x):
1466 for r in getset(repo, fullreposet(repo), x):
1485 try:
1467 try:
1486 ps.add(cl.parentrevs(r)[1])
1468 ps.add(cl.parentrevs(r)[1])
1487 except error.WdirUnsupported:
1469 except error.WdirUnsupported:
1488 parents = repo[r].parents()
1470 parents = repo[r].parents()
1489 if len(parents) == 2:
1471 if len(parents) == 2:
1490 ps.add(parents[1])
1472 ps.add(parents[1])
1491 ps -= {node.nullrev}
1473 ps -= {node.nullrev}
1492 # XXX we should turn this into a baseset instead of a set, smartset may do
1474 # XXX we should turn this into a baseset instead of a set, smartset may do
1493 # some optimizations from the fact this is a baseset.
1475 # some optimizations from the fact this is a baseset.
1494 return subset & ps
1476 return subset & ps
1495
1477
1496 def parentpost(repo, subset, x, order):
1478 def parentpost(repo, subset, x, order):
1497 return p1(repo, subset, x)
1479 return p1(repo, subset, x)
1498
1480
1499 @predicate('parents([set])', safe=True)
1481 @predicate('parents([set])', safe=True)
1500 def parents(repo, subset, x):
1482 def parents(repo, subset, x):
1501 """
1483 """
1502 The set of all parents for all changesets in set, or the working directory.
1484 The set of all parents for all changesets in set, or the working directory.
1503 """
1485 """
1504 if x is None:
1486 if x is None:
1505 ps = set(p.rev() for p in repo[x].parents())
1487 ps = set(p.rev() for p in repo[x].parents())
1506 else:
1488 else:
1507 ps = set()
1489 ps = set()
1508 cl = repo.changelog
1490 cl = repo.changelog
1509 up = ps.update
1491 up = ps.update
1510 parentrevs = cl.parentrevs
1492 parentrevs = cl.parentrevs
1511 for r in getset(repo, fullreposet(repo), x):
1493 for r in getset(repo, fullreposet(repo), x):
1512 try:
1494 try:
1513 up(parentrevs(r))
1495 up(parentrevs(r))
1514 except error.WdirUnsupported:
1496 except error.WdirUnsupported:
1515 up(p.rev() for p in repo[r].parents())
1497 up(p.rev() for p in repo[r].parents())
1516 ps -= {node.nullrev}
1498 ps -= {node.nullrev}
1517 return subset & ps
1499 return subset & ps
1518
1500
1519 def _phase(repo, subset, *targets):
1501 def _phase(repo, subset, *targets):
1520 """helper to select all rev in <targets> phases"""
1502 """helper to select all rev in <targets> phases"""
1521 s = repo._phasecache.getrevset(repo, targets)
1503 s = repo._phasecache.getrevset(repo, targets)
1522 return subset & s
1504 return subset & s
1523
1505
1524 @predicate('draft()', safe=True)
1506 @predicate('draft()', safe=True)
1525 def draft(repo, subset, x):
1507 def draft(repo, subset, x):
1526 """Changeset in draft phase."""
1508 """Changeset in draft phase."""
1527 # i18n: "draft" is a keyword
1509 # i18n: "draft" is a keyword
1528 getargs(x, 0, 0, _("draft takes no arguments"))
1510 getargs(x, 0, 0, _("draft takes no arguments"))
1529 target = phases.draft
1511 target = phases.draft
1530 return _phase(repo, subset, target)
1512 return _phase(repo, subset, target)
1531
1513
1532 @predicate('secret()', safe=True)
1514 @predicate('secret()', safe=True)
1533 def secret(repo, subset, x):
1515 def secret(repo, subset, x):
1534 """Changeset in secret phase."""
1516 """Changeset in secret phase."""
1535 # i18n: "secret" is a keyword
1517 # i18n: "secret" is a keyword
1536 getargs(x, 0, 0, _("secret takes no arguments"))
1518 getargs(x, 0, 0, _("secret takes no arguments"))
1537 target = phases.secret
1519 target = phases.secret
1538 return _phase(repo, subset, target)
1520 return _phase(repo, subset, target)
1539
1521
1540 def parentspec(repo, subset, x, n, order):
1522 def parentspec(repo, subset, x, n, order):
1541 """``set^0``
1523 """``set^0``
1542 The set.
1524 The set.
1543 ``set^1`` (or ``set^``), ``set^2``
1525 ``set^1`` (or ``set^``), ``set^2``
1544 First or second parent, respectively, of all changesets in set.
1526 First or second parent, respectively, of all changesets in set.
1545 """
1527 """
1546 try:
1528 try:
1547 n = int(n[1])
1529 n = int(n[1])
1548 if n not in (0, 1, 2):
1530 if n not in (0, 1, 2):
1549 raise ValueError
1531 raise ValueError
1550 except (TypeError, ValueError):
1532 except (TypeError, ValueError):
1551 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1533 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1552 ps = set()
1534 ps = set()
1553 cl = repo.changelog
1535 cl = repo.changelog
1554 for r in getset(repo, fullreposet(repo), x):
1536 for r in getset(repo, fullreposet(repo), x):
1555 if n == 0:
1537 if n == 0:
1556 ps.add(r)
1538 ps.add(r)
1557 elif n == 1:
1539 elif n == 1:
1558 try:
1540 try:
1559 ps.add(cl.parentrevs(r)[0])
1541 ps.add(cl.parentrevs(r)[0])
1560 except error.WdirUnsupported:
1542 except error.WdirUnsupported:
1561 ps.add(repo[r].parents()[0].rev())
1543 ps.add(repo[r].parents()[0].rev())
1562 else:
1544 else:
1563 try:
1545 try:
1564 parents = cl.parentrevs(r)
1546 parents = cl.parentrevs(r)
1565 if parents[1] != node.nullrev:
1547 if parents[1] != node.nullrev:
1566 ps.add(parents[1])
1548 ps.add(parents[1])
1567 except error.WdirUnsupported:
1549 except error.WdirUnsupported:
1568 parents = repo[r].parents()
1550 parents = repo[r].parents()
1569 if len(parents) == 2:
1551 if len(parents) == 2:
1570 ps.add(parents[1].rev())
1552 ps.add(parents[1].rev())
1571 return subset & ps
1553 return subset & ps
1572
1554
1573 @predicate('present(set)', safe=True)
1555 @predicate('present(set)', safe=True)
1574 def present(repo, subset, x):
1556 def present(repo, subset, x):
1575 """An empty set, if any revision in set isn't found; otherwise,
1557 """An empty set, if any revision in set isn't found; otherwise,
1576 all revisions in set.
1558 all revisions in set.
1577
1559
1578 If any of specified revisions is not present in the local repository,
1560 If any of specified revisions is not present in the local repository,
1579 the query is normally aborted. But this predicate allows the query
1561 the query is normally aborted. But this predicate allows the query
1580 to continue even in such cases.
1562 to continue even in such cases.
1581 """
1563 """
1582 try:
1564 try:
1583 return getset(repo, subset, x)
1565 return getset(repo, subset, x)
1584 except error.RepoLookupError:
1566 except error.RepoLookupError:
1585 return baseset()
1567 return baseset()
1586
1568
1587 # for internal use
1569 # for internal use
1588 @predicate('_notpublic', safe=True)
1570 @predicate('_notpublic', safe=True)
1589 def _notpublic(repo, subset, x):
1571 def _notpublic(repo, subset, x):
1590 getargs(x, 0, 0, "_notpublic takes no arguments")
1572 getargs(x, 0, 0, "_notpublic takes no arguments")
1591 return _phase(repo, subset, phases.draft, phases.secret)
1573 return _phase(repo, subset, phases.draft, phases.secret)
1592
1574
1593 @predicate('public()', safe=True)
1575 @predicate('public()', safe=True)
1594 def public(repo, subset, x):
1576 def public(repo, subset, x):
1595 """Changeset in public phase."""
1577 """Changeset in public phase."""
1596 # i18n: "public" is a keyword
1578 # i18n: "public" is a keyword
1597 getargs(x, 0, 0, _("public takes no arguments"))
1579 getargs(x, 0, 0, _("public takes no arguments"))
1598 phase = repo._phasecache.phase
1580 phase = repo._phasecache.phase
1599 target = phases.public
1581 target = phases.public
1600 condition = lambda r: phase(repo, r) == target
1582 condition = lambda r: phase(repo, r) == target
1601 return subset.filter(condition, condrepr=('<phase %r>', target),
1583 return subset.filter(condition, condrepr=('<phase %r>', target),
1602 cache=False)
1584 cache=False)
1603
1585
1604 @predicate('remote([id [,path]])', safe=False)
1586 @predicate('remote([id [,path]])', safe=False)
1605 def remote(repo, subset, x):
1587 def remote(repo, subset, x):
1606 """Local revision that corresponds to the given identifier in a
1588 """Local revision that corresponds to the given identifier in a
1607 remote repository, if present. Here, the '.' identifier is a
1589 remote repository, if present. Here, the '.' identifier is a
1608 synonym for the current local branch.
1590 synonym for the current local branch.
1609 """
1591 """
1610
1592
1611 from . import hg # avoid start-up nasties
1593 from . import hg # avoid start-up nasties
1612 # i18n: "remote" is a keyword
1594 # i18n: "remote" is a keyword
1613 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1595 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1614
1596
1615 q = '.'
1597 q = '.'
1616 if len(l) > 0:
1598 if len(l) > 0:
1617 # i18n: "remote" is a keyword
1599 # i18n: "remote" is a keyword
1618 q = getstring(l[0], _("remote requires a string id"))
1600 q = getstring(l[0], _("remote requires a string id"))
1619 if q == '.':
1601 if q == '.':
1620 q = repo['.'].branch()
1602 q = repo['.'].branch()
1621
1603
1622 dest = ''
1604 dest = ''
1623 if len(l) > 1:
1605 if len(l) > 1:
1624 # i18n: "remote" is a keyword
1606 # i18n: "remote" is a keyword
1625 dest = getstring(l[1], _("remote requires a repository path"))
1607 dest = getstring(l[1], _("remote requires a repository path"))
1626 dest = repo.ui.expandpath(dest or 'default')
1608 dest = repo.ui.expandpath(dest or 'default')
1627 dest, branches = hg.parseurl(dest)
1609 dest, branches = hg.parseurl(dest)
1628 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1610 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1629 if revs:
1611 if revs:
1630 revs = [repo.lookup(rev) for rev in revs]
1612 revs = [repo.lookup(rev) for rev in revs]
1631 other = hg.peer(repo, {}, dest)
1613 other = hg.peer(repo, {}, dest)
1632 n = other.lookup(q)
1614 n = other.lookup(q)
1633 if n in repo:
1615 if n in repo:
1634 r = repo[n].rev()
1616 r = repo[n].rev()
1635 if r in subset:
1617 if r in subset:
1636 return baseset([r])
1618 return baseset([r])
1637 return baseset()
1619 return baseset()
1638
1620
1639 @predicate('removes(pattern)', safe=True)
1621 @predicate('removes(pattern)', safe=True)
1640 def removes(repo, subset, x):
1622 def removes(repo, subset, x):
1641 """Changesets which remove files matching pattern.
1623 """Changesets which remove files matching pattern.
1642
1624
1643 The pattern without explicit kind like ``glob:`` is expected to be
1625 The pattern without explicit kind like ``glob:`` is expected to be
1644 relative to the current directory and match against a file or a
1626 relative to the current directory and match against a file or a
1645 directory.
1627 directory.
1646 """
1628 """
1647 # i18n: "removes" is a keyword
1629 # i18n: "removes" is a keyword
1648 pat = getstring(x, _("removes requires a pattern"))
1630 pat = getstring(x, _("removes requires a pattern"))
1649 return checkstatus(repo, subset, pat, 2)
1631 return checkstatus(repo, subset, pat, 2)
1650
1632
1651 @predicate('rev(number)', safe=True)
1633 @predicate('rev(number)', safe=True)
1652 def rev(repo, subset, x):
1634 def rev(repo, subset, x):
1653 """Revision with the given numeric identifier.
1635 """Revision with the given numeric identifier.
1654 """
1636 """
1655 # i18n: "rev" is a keyword
1637 # i18n: "rev" is a keyword
1656 l = getargs(x, 1, 1, _("rev requires one argument"))
1638 l = getargs(x, 1, 1, _("rev requires one argument"))
1657 try:
1639 try:
1658 # i18n: "rev" is a keyword
1640 # i18n: "rev" is a keyword
1659 l = int(getstring(l[0], _("rev requires a number")))
1641 l = int(getstring(l[0], _("rev requires a number")))
1660 except (TypeError, ValueError):
1642 except (TypeError, ValueError):
1661 # i18n: "rev" is a keyword
1643 # i18n: "rev" is a keyword
1662 raise error.ParseError(_("rev expects a number"))
1644 raise error.ParseError(_("rev expects a number"))
1663 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1645 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1664 return baseset()
1646 return baseset()
1665 return subset & baseset([l])
1647 return subset & baseset([l])
1666
1648
1667 @predicate('matching(revision [, field])', safe=True)
1649 @predicate('matching(revision [, field])', safe=True)
1668 def matching(repo, subset, x):
1650 def matching(repo, subset, x):
1669 """Changesets in which a given set of fields match the set of fields in the
1651 """Changesets in which a given set of fields match the set of fields in the
1670 selected revision or set.
1652 selected revision or set.
1671
1653
1672 To match more than one field pass the list of fields to match separated
1654 To match more than one field pass the list of fields to match separated
1673 by spaces (e.g. ``author description``).
1655 by spaces (e.g. ``author description``).
1674
1656
1675 Valid fields are most regular revision fields and some special fields.
1657 Valid fields are most regular revision fields and some special fields.
1676
1658
1677 Regular revision fields are ``description``, ``author``, ``branch``,
1659 Regular revision fields are ``description``, ``author``, ``branch``,
1678 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1660 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1679 and ``diff``.
1661 and ``diff``.
1680 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1662 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1681 contents of the revision. Two revisions matching their ``diff`` will
1663 contents of the revision. Two revisions matching their ``diff`` will
1682 also match their ``files``.
1664 also match their ``files``.
1683
1665
1684 Special fields are ``summary`` and ``metadata``:
1666 Special fields are ``summary`` and ``metadata``:
1685 ``summary`` matches the first line of the description.
1667 ``summary`` matches the first line of the description.
1686 ``metadata`` is equivalent to matching ``description user date``
1668 ``metadata`` is equivalent to matching ``description user date``
1687 (i.e. it matches the main metadata fields).
1669 (i.e. it matches the main metadata fields).
1688
1670
1689 ``metadata`` is the default field which is used when no fields are
1671 ``metadata`` is the default field which is used when no fields are
1690 specified. You can match more than one field at a time.
1672 specified. You can match more than one field at a time.
1691 """
1673 """
1692 # i18n: "matching" is a keyword
1674 # i18n: "matching" is a keyword
1693 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1675 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1694
1676
1695 revs = getset(repo, fullreposet(repo), l[0])
1677 revs = getset(repo, fullreposet(repo), l[0])
1696
1678
1697 fieldlist = ['metadata']
1679 fieldlist = ['metadata']
1698 if len(l) > 1:
1680 if len(l) > 1:
1699 fieldlist = getstring(l[1],
1681 fieldlist = getstring(l[1],
1700 # i18n: "matching" is a keyword
1682 # i18n: "matching" is a keyword
1701 _("matching requires a string "
1683 _("matching requires a string "
1702 "as its second argument")).split()
1684 "as its second argument")).split()
1703
1685
1704 # Make sure that there are no repeated fields,
1686 # Make sure that there are no repeated fields,
1705 # expand the 'special' 'metadata' field type
1687 # expand the 'special' 'metadata' field type
1706 # and check the 'files' whenever we check the 'diff'
1688 # and check the 'files' whenever we check the 'diff'
1707 fields = []
1689 fields = []
1708 for field in fieldlist:
1690 for field in fieldlist:
1709 if field == 'metadata':
1691 if field == 'metadata':
1710 fields += ['user', 'description', 'date']
1692 fields += ['user', 'description', 'date']
1711 elif field == 'diff':
1693 elif field == 'diff':
1712 # a revision matching the diff must also match the files
1694 # a revision matching the diff must also match the files
1713 # since matching the diff is very costly, make sure to
1695 # since matching the diff is very costly, make sure to
1714 # also match the files first
1696 # also match the files first
1715 fields += ['files', 'diff']
1697 fields += ['files', 'diff']
1716 else:
1698 else:
1717 if field == 'author':
1699 if field == 'author':
1718 field = 'user'
1700 field = 'user'
1719 fields.append(field)
1701 fields.append(field)
1720 fields = set(fields)
1702 fields = set(fields)
1721 if 'summary' in fields and 'description' in fields:
1703 if 'summary' in fields and 'description' in fields:
1722 # If a revision matches its description it also matches its summary
1704 # If a revision matches its description it also matches its summary
1723 fields.discard('summary')
1705 fields.discard('summary')
1724
1706
1725 # We may want to match more than one field
1707 # We may want to match more than one field
1726 # Not all fields take the same amount of time to be matched
1708 # Not all fields take the same amount of time to be matched
1727 # Sort the selected fields in order of increasing matching cost
1709 # Sort the selected fields in order of increasing matching cost
1728 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1710 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1729 'files', 'description', 'substate', 'diff']
1711 'files', 'description', 'substate', 'diff']
1730 def fieldkeyfunc(f):
1712 def fieldkeyfunc(f):
1731 try:
1713 try:
1732 return fieldorder.index(f)
1714 return fieldorder.index(f)
1733 except ValueError:
1715 except ValueError:
1734 # assume an unknown field is very costly
1716 # assume an unknown field is very costly
1735 return len(fieldorder)
1717 return len(fieldorder)
1736 fields = list(fields)
1718 fields = list(fields)
1737 fields.sort(key=fieldkeyfunc)
1719 fields.sort(key=fieldkeyfunc)
1738
1720
1739 # Each field will be matched with its own "getfield" function
1721 # Each field will be matched with its own "getfield" function
1740 # which will be added to the getfieldfuncs array of functions
1722 # which will be added to the getfieldfuncs array of functions
1741 getfieldfuncs = []
1723 getfieldfuncs = []
1742 _funcs = {
1724 _funcs = {
1743 'user': lambda r: repo[r].user(),
1725 'user': lambda r: repo[r].user(),
1744 'branch': lambda r: repo[r].branch(),
1726 'branch': lambda r: repo[r].branch(),
1745 'date': lambda r: repo[r].date(),
1727 'date': lambda r: repo[r].date(),
1746 'description': lambda r: repo[r].description(),
1728 'description': lambda r: repo[r].description(),
1747 'files': lambda r: repo[r].files(),
1729 'files': lambda r: repo[r].files(),
1748 'parents': lambda r: repo[r].parents(),
1730 'parents': lambda r: repo[r].parents(),
1749 'phase': lambda r: repo[r].phase(),
1731 'phase': lambda r: repo[r].phase(),
1750 'substate': lambda r: repo[r].substate,
1732 'substate': lambda r: repo[r].substate,
1751 'summary': lambda r: repo[r].description().splitlines()[0],
1733 'summary': lambda r: repo[r].description().splitlines()[0],
1752 'diff': lambda r: list(repo[r].diff(git=True),)
1734 'diff': lambda r: list(repo[r].diff(git=True),)
1753 }
1735 }
1754 for info in fields:
1736 for info in fields:
1755 getfield = _funcs.get(info, None)
1737 getfield = _funcs.get(info, None)
1756 if getfield is None:
1738 if getfield is None:
1757 raise error.ParseError(
1739 raise error.ParseError(
1758 # i18n: "matching" is a keyword
1740 # i18n: "matching" is a keyword
1759 _("unexpected field name passed to matching: %s") % info)
1741 _("unexpected field name passed to matching: %s") % info)
1760 getfieldfuncs.append(getfield)
1742 getfieldfuncs.append(getfield)
1761 # convert the getfield array of functions into a "getinfo" function
1743 # convert the getfield array of functions into a "getinfo" function
1762 # which returns an array of field values (or a single value if there
1744 # which returns an array of field values (or a single value if there
1763 # is only one field to match)
1745 # is only one field to match)
1764 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1746 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1765
1747
1766 def matches(x):
1748 def matches(x):
1767 for rev in revs:
1749 for rev in revs:
1768 target = getinfo(rev)
1750 target = getinfo(rev)
1769 match = True
1751 match = True
1770 for n, f in enumerate(getfieldfuncs):
1752 for n, f in enumerate(getfieldfuncs):
1771 if target[n] != f(x):
1753 if target[n] != f(x):
1772 match = False
1754 match = False
1773 if match:
1755 if match:
1774 return True
1756 return True
1775 return False
1757 return False
1776
1758
1777 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1759 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1778
1760
1779 @predicate('reverse(set)', safe=True, takeorder=True)
1761 @predicate('reverse(set)', safe=True, takeorder=True)
1780 def reverse(repo, subset, x, order):
1762 def reverse(repo, subset, x, order):
1781 """Reverse order of set.
1763 """Reverse order of set.
1782 """
1764 """
1783 l = getset(repo, subset, x)
1765 l = getset(repo, subset, x)
1784 if order == defineorder:
1766 if order == defineorder:
1785 l.reverse()
1767 l.reverse()
1786 return l
1768 return l
1787
1769
1788 @predicate('roots(set)', safe=True)
1770 @predicate('roots(set)', safe=True)
1789 def roots(repo, subset, x):
1771 def roots(repo, subset, x):
1790 """Changesets in set with no parent changeset in set.
1772 """Changesets in set with no parent changeset in set.
1791 """
1773 """
1792 s = getset(repo, fullreposet(repo), x)
1774 s = getset(repo, fullreposet(repo), x)
1793 parents = repo.changelog.parentrevs
1775 parents = repo.changelog.parentrevs
1794 def filter(r):
1776 def filter(r):
1795 for p in parents(r):
1777 for p in parents(r):
1796 if 0 <= p and p in s:
1778 if 0 <= p and p in s:
1797 return False
1779 return False
1798 return True
1780 return True
1799 return subset & s.filter(filter, condrepr='<roots>')
1781 return subset & s.filter(filter, condrepr='<roots>')
1800
1782
1801 _sortkeyfuncs = {
1783 _sortkeyfuncs = {
1802 'rev': lambda c: c.rev(),
1784 'rev': lambda c: c.rev(),
1803 'branch': lambda c: c.branch(),
1785 'branch': lambda c: c.branch(),
1804 'desc': lambda c: c.description(),
1786 'desc': lambda c: c.description(),
1805 'user': lambda c: c.user(),
1787 'user': lambda c: c.user(),
1806 'author': lambda c: c.user(),
1788 'author': lambda c: c.user(),
1807 'date': lambda c: c.date()[0],
1789 'date': lambda c: c.date()[0],
1808 }
1790 }
1809
1791
1810 def _getsortargs(x):
1792 def _getsortargs(x):
1811 """Parse sort options into (set, [(key, reverse)], opts)"""
1793 """Parse sort options into (set, [(key, reverse)], opts)"""
1812 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1794 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1813 if 'set' not in args:
1795 if 'set' not in args:
1814 # i18n: "sort" is a keyword
1796 # i18n: "sort" is a keyword
1815 raise error.ParseError(_('sort requires one or two arguments'))
1797 raise error.ParseError(_('sort requires one or two arguments'))
1816 keys = "rev"
1798 keys = "rev"
1817 if 'keys' in args:
1799 if 'keys' in args:
1818 # i18n: "sort" is a keyword
1800 # i18n: "sort" is a keyword
1819 keys = getstring(args['keys'], _("sort spec must be a string"))
1801 keys = getstring(args['keys'], _("sort spec must be a string"))
1820
1802
1821 keyflags = []
1803 keyflags = []
1822 for k in keys.split():
1804 for k in keys.split():
1823 fk = k
1805 fk = k
1824 reverse = (k[0] == '-')
1806 reverse = (k[0] == '-')
1825 if reverse:
1807 if reverse:
1826 k = k[1:]
1808 k = k[1:]
1827 if k not in _sortkeyfuncs and k != 'topo':
1809 if k not in _sortkeyfuncs and k != 'topo':
1828 raise error.ParseError(_("unknown sort key %r") % fk)
1810 raise error.ParseError(_("unknown sort key %r") % fk)
1829 keyflags.append((k, reverse))
1811 keyflags.append((k, reverse))
1830
1812
1831 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1813 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1832 # i18n: "topo" is a keyword
1814 # i18n: "topo" is a keyword
1833 raise error.ParseError(_('topo sort order cannot be combined '
1815 raise error.ParseError(_('topo sort order cannot be combined '
1834 'with other sort keys'))
1816 'with other sort keys'))
1835
1817
1836 opts = {}
1818 opts = {}
1837 if 'topo.firstbranch' in args:
1819 if 'topo.firstbranch' in args:
1838 if any(k == 'topo' for k, reverse in keyflags):
1820 if any(k == 'topo' for k, reverse in keyflags):
1839 opts['topo.firstbranch'] = args['topo.firstbranch']
1821 opts['topo.firstbranch'] = args['topo.firstbranch']
1840 else:
1822 else:
1841 # i18n: "topo" and "topo.firstbranch" are keywords
1823 # i18n: "topo" and "topo.firstbranch" are keywords
1842 raise error.ParseError(_('topo.firstbranch can only be used '
1824 raise error.ParseError(_('topo.firstbranch can only be used '
1843 'when using the topo sort key'))
1825 'when using the topo sort key'))
1844
1826
1845 return args['set'], keyflags, opts
1827 return args['set'], keyflags, opts
1846
1828
1847 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1829 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1848 def sort(repo, subset, x, order):
1830 def sort(repo, subset, x, order):
1849 """Sort set by keys. The default sort order is ascending, specify a key
1831 """Sort set by keys. The default sort order is ascending, specify a key
1850 as ``-key`` to sort in descending order.
1832 as ``-key`` to sort in descending order.
1851
1833
1852 The keys can be:
1834 The keys can be:
1853
1835
1854 - ``rev`` for the revision number,
1836 - ``rev`` for the revision number,
1855 - ``branch`` for the branch name,
1837 - ``branch`` for the branch name,
1856 - ``desc`` for the commit message (description),
1838 - ``desc`` for the commit message (description),
1857 - ``user`` for user name (``author`` can be used as an alias),
1839 - ``user`` for user name (``author`` can be used as an alias),
1858 - ``date`` for the commit date
1840 - ``date`` for the commit date
1859 - ``topo`` for a reverse topographical sort
1841 - ``topo`` for a reverse topographical sort
1860
1842
1861 The ``topo`` sort order cannot be combined with other sort keys. This sort
1843 The ``topo`` sort order cannot be combined with other sort keys. This sort
1862 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1844 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1863 specifies what topographical branches to prioritize in the sort.
1845 specifies what topographical branches to prioritize in the sort.
1864
1846
1865 """
1847 """
1866 s, keyflags, opts = _getsortargs(x)
1848 s, keyflags, opts = _getsortargs(x)
1867 revs = getset(repo, subset, s)
1849 revs = getset(repo, subset, s)
1868
1850
1869 if not keyflags or order != defineorder:
1851 if not keyflags or order != defineorder:
1870 return revs
1852 return revs
1871 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1853 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1872 revs.sort(reverse=keyflags[0][1])
1854 revs.sort(reverse=keyflags[0][1])
1873 return revs
1855 return revs
1874 elif keyflags[0][0] == "topo":
1856 elif keyflags[0][0] == "topo":
1875 firstbranch = ()
1857 firstbranch = ()
1876 if 'topo.firstbranch' in opts:
1858 if 'topo.firstbranch' in opts:
1877 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1859 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1878 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1860 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1879 istopo=True)
1861 istopo=True)
1880 if keyflags[0][1]:
1862 if keyflags[0][1]:
1881 revs.reverse()
1863 revs.reverse()
1882 return revs
1864 return revs
1883
1865
1884 # sort() is guaranteed to be stable
1866 # sort() is guaranteed to be stable
1885 ctxs = [repo[r] for r in revs]
1867 ctxs = [repo[r] for r in revs]
1886 for k, reverse in reversed(keyflags):
1868 for k, reverse in reversed(keyflags):
1887 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1869 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1888 return baseset([c.rev() for c in ctxs])
1870 return baseset([c.rev() for c in ctxs])
1889
1871
1890 def _toposort(revs, parentsfunc, firstbranch=()):
1872 def _toposort(revs, parentsfunc, firstbranch=()):
1891 """Yield revisions from heads to roots one (topo) branch at a time.
1873 """Yield revisions from heads to roots one (topo) branch at a time.
1892
1874
1893 This function aims to be used by a graph generator that wishes to minimize
1875 This function aims to be used by a graph generator that wishes to minimize
1894 the number of parallel branches and their interleaving.
1876 the number of parallel branches and their interleaving.
1895
1877
1896 Example iteration order (numbers show the "true" order in a changelog):
1878 Example iteration order (numbers show the "true" order in a changelog):
1897
1879
1898 o 4
1880 o 4
1899 |
1881 |
1900 o 1
1882 o 1
1901 |
1883 |
1902 | o 3
1884 | o 3
1903 | |
1885 | |
1904 | o 2
1886 | o 2
1905 |/
1887 |/
1906 o 0
1888 o 0
1907
1889
1908 Note that the ancestors of merges are understood by the current
1890 Note that the ancestors of merges are understood by the current
1909 algorithm to be on the same branch. This means no reordering will
1891 algorithm to be on the same branch. This means no reordering will
1910 occur behind a merge.
1892 occur behind a merge.
1911 """
1893 """
1912
1894
1913 ### Quick summary of the algorithm
1895 ### Quick summary of the algorithm
1914 #
1896 #
1915 # This function is based around a "retention" principle. We keep revisions
1897 # This function is based around a "retention" principle. We keep revisions
1916 # in memory until we are ready to emit a whole branch that immediately
1898 # in memory until we are ready to emit a whole branch that immediately
1917 # "merges" into an existing one. This reduces the number of parallel
1899 # "merges" into an existing one. This reduces the number of parallel
1918 # branches with interleaved revisions.
1900 # branches with interleaved revisions.
1919 #
1901 #
1920 # During iteration revs are split into two groups:
1902 # During iteration revs are split into two groups:
1921 # A) revision already emitted
1903 # A) revision already emitted
1922 # B) revision in "retention". They are stored as different subgroups.
1904 # B) revision in "retention". They are stored as different subgroups.
1923 #
1905 #
1924 # for each REV, we do the following logic:
1906 # for each REV, we do the following logic:
1925 #
1907 #
1926 # 1) if REV is a parent of (A), we will emit it. If there is a
1908 # 1) if REV is a parent of (A), we will emit it. If there is a
1927 # retention group ((B) above) that is blocked on REV being
1909 # retention group ((B) above) that is blocked on REV being
1928 # available, we emit all the revisions out of that retention
1910 # available, we emit all the revisions out of that retention
1929 # group first.
1911 # group first.
1930 #
1912 #
1931 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1913 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1932 # available, if such subgroup exist, we add REV to it and the subgroup is
1914 # available, if such subgroup exist, we add REV to it and the subgroup is
1933 # now awaiting for REV.parents() to be available.
1915 # now awaiting for REV.parents() to be available.
1934 #
1916 #
1935 # 3) finally if no such group existed in (B), we create a new subgroup.
1917 # 3) finally if no such group existed in (B), we create a new subgroup.
1936 #
1918 #
1937 #
1919 #
1938 # To bootstrap the algorithm, we emit the tipmost revision (which
1920 # To bootstrap the algorithm, we emit the tipmost revision (which
1939 # puts it in group (A) from above).
1921 # puts it in group (A) from above).
1940
1922
1941 revs.sort(reverse=True)
1923 revs.sort(reverse=True)
1942
1924
1943 # Set of parents of revision that have been emitted. They can be considered
1925 # Set of parents of revision that have been emitted. They can be considered
1944 # unblocked as the graph generator is already aware of them so there is no
1926 # unblocked as the graph generator is already aware of them so there is no
1945 # need to delay the revisions that reference them.
1927 # need to delay the revisions that reference them.
1946 #
1928 #
1947 # If someone wants to prioritize a branch over the others, pre-filling this
1929 # If someone wants to prioritize a branch over the others, pre-filling this
1948 # set will force all other branches to wait until this branch is ready to be
1930 # set will force all other branches to wait until this branch is ready to be
1949 # emitted.
1931 # emitted.
1950 unblocked = set(firstbranch)
1932 unblocked = set(firstbranch)
1951
1933
1952 # list of groups waiting to be displayed, each group is defined by:
1934 # list of groups waiting to be displayed, each group is defined by:
1953 #
1935 #
1954 # (revs: lists of revs waiting to be displayed,
1936 # (revs: lists of revs waiting to be displayed,
1955 # blocked: set of that cannot be displayed before those in 'revs')
1937 # blocked: set of that cannot be displayed before those in 'revs')
1956 #
1938 #
1957 # The second value ('blocked') correspond to parents of any revision in the
1939 # The second value ('blocked') correspond to parents of any revision in the
1958 # group ('revs') that is not itself contained in the group. The main idea
1940 # group ('revs') that is not itself contained in the group. The main idea
1959 # of this algorithm is to delay as much as possible the emission of any
1941 # of this algorithm is to delay as much as possible the emission of any
1960 # revision. This means waiting for the moment we are about to display
1942 # revision. This means waiting for the moment we are about to display
1961 # these parents to display the revs in a group.
1943 # these parents to display the revs in a group.
1962 #
1944 #
1963 # This first implementation is smart until it encounters a merge: it will
1945 # This first implementation is smart until it encounters a merge: it will
1964 # emit revs as soon as any parent is about to be emitted and can grow an
1946 # emit revs as soon as any parent is about to be emitted and can grow an
1965 # arbitrary number of revs in 'blocked'. In practice this mean we properly
1947 # arbitrary number of revs in 'blocked'. In practice this mean we properly
1966 # retains new branches but gives up on any special ordering for ancestors
1948 # retains new branches but gives up on any special ordering for ancestors
1967 # of merges. The implementation can be improved to handle this better.
1949 # of merges. The implementation can be improved to handle this better.
1968 #
1950 #
1969 # The first subgroup is special. It corresponds to all the revision that
1951 # The first subgroup is special. It corresponds to all the revision that
1970 # were already emitted. The 'revs' lists is expected to be empty and the
1952 # were already emitted. The 'revs' lists is expected to be empty and the
1971 # 'blocked' set contains the parents revisions of already emitted revision.
1953 # 'blocked' set contains the parents revisions of already emitted revision.
1972 #
1954 #
1973 # You could pre-seed the <parents> set of groups[0] to a specific
1955 # You could pre-seed the <parents> set of groups[0] to a specific
1974 # changesets to select what the first emitted branch should be.
1956 # changesets to select what the first emitted branch should be.
1975 groups = [([], unblocked)]
1957 groups = [([], unblocked)]
1976 pendingheap = []
1958 pendingheap = []
1977 pendingset = set()
1959 pendingset = set()
1978
1960
1979 heapq.heapify(pendingheap)
1961 heapq.heapify(pendingheap)
1980 heappop = heapq.heappop
1962 heappop = heapq.heappop
1981 heappush = heapq.heappush
1963 heappush = heapq.heappush
1982 for currentrev in revs:
1964 for currentrev in revs:
1983 # Heap works with smallest element, we want highest so we invert
1965 # Heap works with smallest element, we want highest so we invert
1984 if currentrev not in pendingset:
1966 if currentrev not in pendingset:
1985 heappush(pendingheap, -currentrev)
1967 heappush(pendingheap, -currentrev)
1986 pendingset.add(currentrev)
1968 pendingset.add(currentrev)
1987 # iterates on pending rev until after the current rev have been
1969 # iterates on pending rev until after the current rev have been
1988 # processed.
1970 # processed.
1989 rev = None
1971 rev = None
1990 while rev != currentrev:
1972 while rev != currentrev:
1991 rev = -heappop(pendingheap)
1973 rev = -heappop(pendingheap)
1992 pendingset.remove(rev)
1974 pendingset.remove(rev)
1993
1975
1994 # Seek for a subgroup blocked, waiting for the current revision.
1976 # Seek for a subgroup blocked, waiting for the current revision.
1995 matching = [i for i, g in enumerate(groups) if rev in g[1]]
1977 matching = [i for i, g in enumerate(groups) if rev in g[1]]
1996
1978
1997 if matching:
1979 if matching:
1998 # The main idea is to gather together all sets that are blocked
1980 # The main idea is to gather together all sets that are blocked
1999 # on the same revision.
1981 # on the same revision.
2000 #
1982 #
2001 # Groups are merged when a common blocking ancestor is
1983 # Groups are merged when a common blocking ancestor is
2002 # observed. For example, given two groups:
1984 # observed. For example, given two groups:
2003 #
1985 #
2004 # revs [5, 4] waiting for 1
1986 # revs [5, 4] waiting for 1
2005 # revs [3, 2] waiting for 1
1987 # revs [3, 2] waiting for 1
2006 #
1988 #
2007 # These two groups will be merged when we process
1989 # These two groups will be merged when we process
2008 # 1. In theory, we could have merged the groups when
1990 # 1. In theory, we could have merged the groups when
2009 # we added 2 to the group it is now in (we could have
1991 # we added 2 to the group it is now in (we could have
2010 # noticed the groups were both blocked on 1 then), but
1992 # noticed the groups were both blocked on 1 then), but
2011 # the way it works now makes the algorithm simpler.
1993 # the way it works now makes the algorithm simpler.
2012 #
1994 #
2013 # We also always keep the oldest subgroup first. We can
1995 # We also always keep the oldest subgroup first. We can
2014 # probably improve the behavior by having the longest set
1996 # probably improve the behavior by having the longest set
2015 # first. That way, graph algorithms could minimise the length
1997 # first. That way, graph algorithms could minimise the length
2016 # of parallel lines their drawing. This is currently not done.
1998 # of parallel lines their drawing. This is currently not done.
2017 targetidx = matching.pop(0)
1999 targetidx = matching.pop(0)
2018 trevs, tparents = groups[targetidx]
2000 trevs, tparents = groups[targetidx]
2019 for i in matching:
2001 for i in matching:
2020 gr = groups[i]
2002 gr = groups[i]
2021 trevs.extend(gr[0])
2003 trevs.extend(gr[0])
2022 tparents |= gr[1]
2004 tparents |= gr[1]
2023 # delete all merged subgroups (except the one we kept)
2005 # delete all merged subgroups (except the one we kept)
2024 # (starting from the last subgroup for performance and
2006 # (starting from the last subgroup for performance and
2025 # sanity reasons)
2007 # sanity reasons)
2026 for i in reversed(matching):
2008 for i in reversed(matching):
2027 del groups[i]
2009 del groups[i]
2028 else:
2010 else:
2029 # This is a new head. We create a new subgroup for it.
2011 # This is a new head. We create a new subgroup for it.
2030 targetidx = len(groups)
2012 targetidx = len(groups)
2031 groups.append(([], {rev}))
2013 groups.append(([], {rev}))
2032
2014
2033 gr = groups[targetidx]
2015 gr = groups[targetidx]
2034
2016
2035 # We now add the current nodes to this subgroups. This is done
2017 # We now add the current nodes to this subgroups. This is done
2036 # after the subgroup merging because all elements from a subgroup
2018 # after the subgroup merging because all elements from a subgroup
2037 # that relied on this rev must precede it.
2019 # that relied on this rev must precede it.
2038 #
2020 #
2039 # we also update the <parents> set to include the parents of the
2021 # we also update the <parents> set to include the parents of the
2040 # new nodes.
2022 # new nodes.
2041 if rev == currentrev: # only display stuff in rev
2023 if rev == currentrev: # only display stuff in rev
2042 gr[0].append(rev)
2024 gr[0].append(rev)
2043 gr[1].remove(rev)
2025 gr[1].remove(rev)
2044 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2026 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2045 gr[1].update(parents)
2027 gr[1].update(parents)
2046 for p in parents:
2028 for p in parents:
2047 if p not in pendingset:
2029 if p not in pendingset:
2048 pendingset.add(p)
2030 pendingset.add(p)
2049 heappush(pendingheap, -p)
2031 heappush(pendingheap, -p)
2050
2032
2051 # Look for a subgroup to display
2033 # Look for a subgroup to display
2052 #
2034 #
2053 # When unblocked is empty (if clause), we were not waiting for any
2035 # When unblocked is empty (if clause), we were not waiting for any
2054 # revisions during the first iteration (if no priority was given) or
2036 # revisions during the first iteration (if no priority was given) or
2055 # if we emitted a whole disconnected set of the graph (reached a
2037 # if we emitted a whole disconnected set of the graph (reached a
2056 # root). In that case we arbitrarily take the oldest known
2038 # root). In that case we arbitrarily take the oldest known
2057 # subgroup. The heuristic could probably be better.
2039 # subgroup. The heuristic could probably be better.
2058 #
2040 #
2059 # Otherwise (elif clause) if the subgroup is blocked on
2041 # Otherwise (elif clause) if the subgroup is blocked on
2060 # a revision we just emitted, we can safely emit it as
2042 # a revision we just emitted, we can safely emit it as
2061 # well.
2043 # well.
2062 if not unblocked:
2044 if not unblocked:
2063 if len(groups) > 1: # display other subset
2045 if len(groups) > 1: # display other subset
2064 targetidx = 1
2046 targetidx = 1
2065 gr = groups[1]
2047 gr = groups[1]
2066 elif not gr[1] & unblocked:
2048 elif not gr[1] & unblocked:
2067 gr = None
2049 gr = None
2068
2050
2069 if gr is not None:
2051 if gr is not None:
2070 # update the set of awaited revisions with the one from the
2052 # update the set of awaited revisions with the one from the
2071 # subgroup
2053 # subgroup
2072 unblocked |= gr[1]
2054 unblocked |= gr[1]
2073 # output all revisions in the subgroup
2055 # output all revisions in the subgroup
2074 for r in gr[0]:
2056 for r in gr[0]:
2075 yield r
2057 yield r
2076 # delete the subgroup that you just output
2058 # delete the subgroup that you just output
2077 # unless it is groups[0] in which case you just empty it.
2059 # unless it is groups[0] in which case you just empty it.
2078 if targetidx:
2060 if targetidx:
2079 del groups[targetidx]
2061 del groups[targetidx]
2080 else:
2062 else:
2081 gr[0][:] = []
2063 gr[0][:] = []
2082 # Check if we have some subgroup waiting for revisions we are not going to
2064 # Check if we have some subgroup waiting for revisions we are not going to
2083 # iterate over
2065 # iterate over
2084 for g in groups:
2066 for g in groups:
2085 for r in g[0]:
2067 for r in g[0]:
2086 yield r
2068 yield r
2087
2069
2088 @predicate('subrepo([pattern])')
2070 @predicate('subrepo([pattern])')
2089 def subrepo(repo, subset, x):
2071 def subrepo(repo, subset, x):
2090 """Changesets that add, modify or remove the given subrepo. If no subrepo
2072 """Changesets that add, modify or remove the given subrepo. If no subrepo
2091 pattern is named, any subrepo changes are returned.
2073 pattern is named, any subrepo changes are returned.
2092 """
2074 """
2093 # i18n: "subrepo" is a keyword
2075 # i18n: "subrepo" is a keyword
2094 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2076 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2095 pat = None
2077 pat = None
2096 if len(args) != 0:
2078 if len(args) != 0:
2097 pat = getstring(args[0], _("subrepo requires a pattern"))
2079 pat = getstring(args[0], _("subrepo requires a pattern"))
2098
2080
2099 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2081 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2100
2082
2101 def submatches(names):
2083 def submatches(names):
2102 k, p, m = util.stringmatcher(pat)
2084 k, p, m = util.stringmatcher(pat)
2103 for name in names:
2085 for name in names:
2104 if m(name):
2086 if m(name):
2105 yield name
2087 yield name
2106
2088
2107 def matches(x):
2089 def matches(x):
2108 c = repo[x]
2090 c = repo[x]
2109 s = repo.status(c.p1().node(), c.node(), match=m)
2091 s = repo.status(c.p1().node(), c.node(), match=m)
2110
2092
2111 if pat is None:
2093 if pat is None:
2112 return s.added or s.modified or s.removed
2094 return s.added or s.modified or s.removed
2113
2095
2114 if s.added:
2096 if s.added:
2115 return any(submatches(c.substate.keys()))
2097 return any(submatches(c.substate.keys()))
2116
2098
2117 if s.modified:
2099 if s.modified:
2118 subs = set(c.p1().substate.keys())
2100 subs = set(c.p1().substate.keys())
2119 subs.update(c.substate.keys())
2101 subs.update(c.substate.keys())
2120
2102
2121 for path in submatches(subs):
2103 for path in submatches(subs):
2122 if c.p1().substate.get(path) != c.substate.get(path):
2104 if c.p1().substate.get(path) != c.substate.get(path):
2123 return True
2105 return True
2124
2106
2125 if s.removed:
2107 if s.removed:
2126 return any(submatches(c.p1().substate.keys()))
2108 return any(submatches(c.p1().substate.keys()))
2127
2109
2128 return False
2110 return False
2129
2111
2130 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2112 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2131
2113
2132 def _substringmatcher(pattern, casesensitive=True):
2114 def _substringmatcher(pattern, casesensitive=True):
2133 kind, pattern, matcher = util.stringmatcher(pattern,
2115 kind, pattern, matcher = util.stringmatcher(pattern,
2134 casesensitive=casesensitive)
2116 casesensitive=casesensitive)
2135 if kind == 'literal':
2117 if kind == 'literal':
2136 if not casesensitive:
2118 if not casesensitive:
2137 pattern = encoding.lower(pattern)
2119 pattern = encoding.lower(pattern)
2138 matcher = lambda s: pattern in encoding.lower(s)
2120 matcher = lambda s: pattern in encoding.lower(s)
2139 else:
2121 else:
2140 matcher = lambda s: pattern in s
2122 matcher = lambda s: pattern in s
2141 return kind, pattern, matcher
2123 return kind, pattern, matcher
2142
2124
2143 @predicate('tag([name])', safe=True)
2125 @predicate('tag([name])', safe=True)
2144 def tag(repo, subset, x):
2126 def tag(repo, subset, x):
2145 """The specified tag by name, or all tagged revisions if no name is given.
2127 """The specified tag by name, or all tagged revisions if no name is given.
2146
2128
2147 Pattern matching is supported for `name`. See
2129 Pattern matching is supported for `name`. See
2148 :hg:`help revisions.patterns`.
2130 :hg:`help revisions.patterns`.
2149 """
2131 """
2150 # i18n: "tag" is a keyword
2132 # i18n: "tag" is a keyword
2151 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2133 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2152 cl = repo.changelog
2134 cl = repo.changelog
2153 if args:
2135 if args:
2154 pattern = getstring(args[0],
2136 pattern = getstring(args[0],
2155 # i18n: "tag" is a keyword
2137 # i18n: "tag" is a keyword
2156 _('the argument to tag must be a string'))
2138 _('the argument to tag must be a string'))
2157 kind, pattern, matcher = util.stringmatcher(pattern)
2139 kind, pattern, matcher = util.stringmatcher(pattern)
2158 if kind == 'literal':
2140 if kind == 'literal':
2159 # avoid resolving all tags
2141 # avoid resolving all tags
2160 tn = repo._tagscache.tags.get(pattern, None)
2142 tn = repo._tagscache.tags.get(pattern, None)
2161 if tn is None:
2143 if tn is None:
2162 raise error.RepoLookupError(_("tag '%s' does not exist")
2144 raise error.RepoLookupError(_("tag '%s' does not exist")
2163 % pattern)
2145 % pattern)
2164 s = {repo[tn].rev()}
2146 s = {repo[tn].rev()}
2165 else:
2147 else:
2166 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2148 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2167 else:
2149 else:
2168 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2150 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2169 return subset & s
2151 return subset & s
2170
2152
2171 @predicate('tagged', safe=True)
2153 @predicate('tagged', safe=True)
2172 def tagged(repo, subset, x):
2154 def tagged(repo, subset, x):
2173 return tag(repo, subset, x)
2155 return tag(repo, subset, x)
2174
2156
2175 @predicate('unstable()', safe=True)
2157 @predicate('unstable()', safe=True)
2176 def unstable(repo, subset, x):
2158 def unstable(repo, subset, x):
2177 """Non-obsolete changesets with obsolete ancestors.
2159 """Non-obsolete changesets with obsolete ancestors.
2178 """
2160 """
2179 # i18n: "unstable" is a keyword
2161 # i18n: "unstable" is a keyword
2180 getargs(x, 0, 0, _("unstable takes no arguments"))
2162 getargs(x, 0, 0, _("unstable takes no arguments"))
2181 unstables = obsmod.getrevs(repo, 'unstable')
2163 unstables = obsmod.getrevs(repo, 'unstable')
2182 return subset & unstables
2164 return subset & unstables
2183
2165
2184
2166
2185 @predicate('user(string)', safe=True)
2167 @predicate('user(string)', safe=True)
2186 def user(repo, subset, x):
2168 def user(repo, subset, x):
2187 """User name contains string. The match is case-insensitive.
2169 """User name contains string. The match is case-insensitive.
2188
2170
2189 Pattern matching is supported for `string`. See
2171 Pattern matching is supported for `string`. See
2190 :hg:`help revisions.patterns`.
2172 :hg:`help revisions.patterns`.
2191 """
2173 """
2192 return author(repo, subset, x)
2174 return author(repo, subset, x)
2193
2175
2194 @predicate('wdir()', safe=True)
2176 @predicate('wdir()', safe=True)
2195 def wdir(repo, subset, x):
2177 def wdir(repo, subset, x):
2196 """Working directory. (EXPERIMENTAL)"""
2178 """Working directory. (EXPERIMENTAL)"""
2197 # i18n: "wdir" is a keyword
2179 # i18n: "wdir" is a keyword
2198 getargs(x, 0, 0, _("wdir takes no arguments"))
2180 getargs(x, 0, 0, _("wdir takes no arguments"))
2199 if node.wdirrev in subset or isinstance(subset, fullreposet):
2181 if node.wdirrev in subset or isinstance(subset, fullreposet):
2200 return baseset([node.wdirrev])
2182 return baseset([node.wdirrev])
2201 return baseset()
2183 return baseset()
2202
2184
2203 def _orderedlist(repo, subset, x):
2185 def _orderedlist(repo, subset, x):
2204 s = getstring(x, "internal error")
2186 s = getstring(x, "internal error")
2205 if not s:
2187 if not s:
2206 return baseset()
2188 return baseset()
2207 # remove duplicates here. it's difficult for caller to deduplicate sets
2189 # remove duplicates here. it's difficult for caller to deduplicate sets
2208 # because different symbols can point to the same rev.
2190 # because different symbols can point to the same rev.
2209 cl = repo.changelog
2191 cl = repo.changelog
2210 ls = []
2192 ls = []
2211 seen = set()
2193 seen = set()
2212 for t in s.split('\0'):
2194 for t in s.split('\0'):
2213 try:
2195 try:
2214 # fast path for integer revision
2196 # fast path for integer revision
2215 r = int(t)
2197 r = int(t)
2216 if str(r) != t or r not in cl:
2198 if str(r) != t or r not in cl:
2217 raise ValueError
2199 raise ValueError
2218 revs = [r]
2200 revs = [r]
2219 except ValueError:
2201 except ValueError:
2220 revs = stringset(repo, subset, t)
2202 revs = stringset(repo, subset, t)
2221
2203
2222 for r in revs:
2204 for r in revs:
2223 if r in seen:
2205 if r in seen:
2224 continue
2206 continue
2225 if (r in subset
2207 if (r in subset
2226 or r == node.nullrev and isinstance(subset, fullreposet)):
2208 or r == node.nullrev and isinstance(subset, fullreposet)):
2227 ls.append(r)
2209 ls.append(r)
2228 seen.add(r)
2210 seen.add(r)
2229 return baseset(ls)
2211 return baseset(ls)
2230
2212
2231 # for internal use
2213 # for internal use
2232 @predicate('_list', safe=True, takeorder=True)
2214 @predicate('_list', safe=True, takeorder=True)
2233 def _list(repo, subset, x, order):
2215 def _list(repo, subset, x, order):
2234 if order == followorder:
2216 if order == followorder:
2235 # slow path to take the subset order
2217 # slow path to take the subset order
2236 return subset & _orderedlist(repo, fullreposet(repo), x)
2218 return subset & _orderedlist(repo, fullreposet(repo), x)
2237 else:
2219 else:
2238 return _orderedlist(repo, subset, x)
2220 return _orderedlist(repo, subset, x)
2239
2221
2240 def _orderedintlist(repo, subset, x):
2222 def _orderedintlist(repo, subset, x):
2241 s = getstring(x, "internal error")
2223 s = getstring(x, "internal error")
2242 if not s:
2224 if not s:
2243 return baseset()
2225 return baseset()
2244 ls = [int(r) for r in s.split('\0')]
2226 ls = [int(r) for r in s.split('\0')]
2245 s = subset
2227 s = subset
2246 return baseset([r for r in ls if r in s])
2228 return baseset([r for r in ls if r in s])
2247
2229
2248 # for internal use
2230 # for internal use
2249 @predicate('_intlist', safe=True, takeorder=True)
2231 @predicate('_intlist', safe=True, takeorder=True)
2250 def _intlist(repo, subset, x, order):
2232 def _intlist(repo, subset, x, order):
2251 if order == followorder:
2233 if order == followorder:
2252 # slow path to take the subset order
2234 # slow path to take the subset order
2253 return subset & _orderedintlist(repo, fullreposet(repo), x)
2235 return subset & _orderedintlist(repo, fullreposet(repo), x)
2254 else:
2236 else:
2255 return _orderedintlist(repo, subset, x)
2237 return _orderedintlist(repo, subset, x)
2256
2238
2257 def _orderedhexlist(repo, subset, x):
2239 def _orderedhexlist(repo, subset, x):
2258 s = getstring(x, "internal error")
2240 s = getstring(x, "internal error")
2259 if not s:
2241 if not s:
2260 return baseset()
2242 return baseset()
2261 cl = repo.changelog
2243 cl = repo.changelog
2262 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2244 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2263 s = subset
2245 s = subset
2264 return baseset([r for r in ls if r in s])
2246 return baseset([r for r in ls if r in s])
2265
2247
2266 # for internal use
2248 # for internal use
2267 @predicate('_hexlist', safe=True, takeorder=True)
2249 @predicate('_hexlist', safe=True, takeorder=True)
2268 def _hexlist(repo, subset, x, order):
2250 def _hexlist(repo, subset, x, order):
2269 if order == followorder:
2251 if order == followorder:
2270 # slow path to take the subset order
2252 # slow path to take the subset order
2271 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2253 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2272 else:
2254 else:
2273 return _orderedhexlist(repo, subset, x)
2255 return _orderedhexlist(repo, subset, x)
2274
2256
2275 methods = {
2257 methods = {
2276 "range": rangeset,
2258 "range": rangeset,
2277 "rangeall": rangeall,
2259 "rangeall": rangeall,
2278 "rangepre": rangepre,
2260 "rangepre": rangepre,
2279 "rangepost": rangepost,
2261 "rangepost": rangepost,
2280 "dagrange": dagrange,
2262 "dagrange": dagrange,
2281 "string": stringset,
2263 "string": stringset,
2282 "symbol": stringset,
2264 "symbol": stringset,
2283 "and": andset,
2265 "and": andset,
2284 "or": orset,
2266 "or": orset,
2285 "not": notset,
2267 "not": notset,
2286 "difference": differenceset,
2268 "difference": differenceset,
2287 "list": listset,
2269 "list": listset,
2288 "keyvalue": keyvaluepair,
2270 "keyvalue": keyvaluepair,
2289 "func": func,
2271 "func": func,
2290 "ancestor": ancestorspec,
2272 "ancestor": ancestorspec,
2291 "parent": parentspec,
2273 "parent": parentspec,
2292 "parentpost": parentpost,
2274 "parentpost": parentpost,
2293 }
2275 }
2294
2276
2295 def posttreebuilthook(tree, repo):
2277 def posttreebuilthook(tree, repo):
2296 # hook for extensions to execute code on the optimized tree
2278 # hook for extensions to execute code on the optimized tree
2297 pass
2279 pass
2298
2280
2299 def match(ui, spec, repo=None, order=defineorder):
2281 def match(ui, spec, repo=None, order=defineorder):
2300 """Create a matcher for a single revision spec
2282 """Create a matcher for a single revision spec
2301
2283
2302 If order=followorder, a matcher takes the ordering specified by the input
2284 If order=followorder, a matcher takes the ordering specified by the input
2303 set.
2285 set.
2304 """
2286 """
2305 return matchany(ui, [spec], repo=repo, order=order)
2287 return matchany(ui, [spec], repo=repo, order=order)
2306
2288
2307 def matchany(ui, specs, repo=None, order=defineorder):
2289 def matchany(ui, specs, repo=None, order=defineorder):
2308 """Create a matcher that will include any revisions matching one of the
2290 """Create a matcher that will include any revisions matching one of the
2309 given specs
2291 given specs
2310
2292
2311 If order=followorder, a matcher takes the ordering specified by the input
2293 If order=followorder, a matcher takes the ordering specified by the input
2312 set.
2294 set.
2313 """
2295 """
2314 if not specs:
2296 if not specs:
2315 def mfunc(repo, subset=None):
2297 def mfunc(repo, subset=None):
2316 return baseset()
2298 return baseset()
2317 return mfunc
2299 return mfunc
2318 if not all(specs):
2300 if not all(specs):
2319 raise error.ParseError(_("empty query"))
2301 raise error.ParseError(_("empty query"))
2320 lookup = None
2302 lookup = None
2321 if repo:
2303 if repo:
2322 lookup = repo.__contains__
2304 lookup = repo.__contains__
2323 if len(specs) == 1:
2305 if len(specs) == 1:
2324 tree = revsetlang.parse(specs[0], lookup)
2306 tree = revsetlang.parse(specs[0], lookup)
2325 else:
2307 else:
2326 tree = ('or',
2308 tree = ('or',
2327 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2309 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2328
2310
2329 if ui:
2311 if ui:
2330 tree = revsetlang.expandaliases(ui, tree)
2312 tree = revsetlang.expandaliases(ui, tree)
2331 tree = revsetlang.foldconcat(tree)
2313 tree = revsetlang.foldconcat(tree)
2332 tree = revsetlang.analyze(tree, order)
2314 tree = revsetlang.analyze(tree, order)
2333 tree = revsetlang.optimize(tree)
2315 tree = revsetlang.optimize(tree)
2334 posttreebuilthook(tree, repo)
2316 posttreebuilthook(tree, repo)
2335 return makematcher(tree)
2317 return makematcher(tree)
2336
2318
2337 def makematcher(tree):
2319 def makematcher(tree):
2338 """Create a matcher from an evaluatable tree"""
2320 """Create a matcher from an evaluatable tree"""
2339 def mfunc(repo, subset=None):
2321 def mfunc(repo, subset=None):
2340 if subset is None:
2322 if subset is None:
2341 subset = fullreposet(repo)
2323 subset = fullreposet(repo)
2342 return getset(repo, subset, tree)
2324 return getset(repo, subset, tree)
2343 return mfunc
2325 return mfunc
2344
2326
2345 def loadpredicate(ui, extname, registrarobj):
2327 def loadpredicate(ui, extname, registrarobj):
2346 """Load revset predicates from specified registrarobj
2328 """Load revset predicates from specified registrarobj
2347 """
2329 """
2348 for name, func in registrarobj._table.iteritems():
2330 for name, func in registrarobj._table.iteritems():
2349 symbols[name] = func
2331 symbols[name] = func
2350 if func._safe:
2332 if func._safe:
2351 safesymbols.add(name)
2333 safesymbols.add(name)
2352
2334
2353 # load built-in predicates explicitly to setup safesymbols
2335 # load built-in predicates explicitly to setup safesymbols
2354 loadpredicate(None, None, predicate)
2336 loadpredicate(None, None, predicate)
2355
2337
2356 # tell hggettext to extract docstrings from these functions:
2338 # tell hggettext to extract docstrings from these functions:
2357 i18nfunctions = symbols.values()
2339 i18nfunctions = symbols.values()
@@ -1,1070 +1,1093
1 # smartset.py - data structure for revision set
1 # smartset.py - data structure for revision set
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 from . import (
10 from . import (
11 error,
11 util,
12 util,
12 )
13 )
13
14
14 def _formatsetrepr(r):
15 def _formatsetrepr(r):
15 """Format an optional printable representation of a set
16 """Format an optional printable representation of a set
16
17
17 ======== =================================
18 ======== =================================
18 type(r) example
19 type(r) example
19 ======== =================================
20 ======== =================================
20 tuple ('<not %r>', other)
21 tuple ('<not %r>', other)
21 str '<branch closed>'
22 str '<branch closed>'
22 callable lambda: '<branch %r>' % sorted(b)
23 callable lambda: '<branch %r>' % sorted(b)
23 object other
24 object other
24 ======== =================================
25 ======== =================================
25 """
26 """
26 if r is None:
27 if r is None:
27 return ''
28 return ''
28 elif isinstance(r, tuple):
29 elif isinstance(r, tuple):
29 return r[0] % r[1:]
30 return r[0] % r[1:]
30 elif isinstance(r, str):
31 elif isinstance(r, str):
31 return r
32 return r
32 elif callable(r):
33 elif callable(r):
33 return r()
34 return r()
34 else:
35 else:
35 return repr(r)
36 return repr(r)
36
37
37 class abstractsmartset(object):
38 class abstractsmartset(object):
38
39
39 def __nonzero__(self):
40 def __nonzero__(self):
40 """True if the smartset is not empty"""
41 """True if the smartset is not empty"""
41 raise NotImplementedError()
42 raise NotImplementedError()
42
43
43 __bool__ = __nonzero__
44 __bool__ = __nonzero__
44
45
45 def __contains__(self, rev):
46 def __contains__(self, rev):
46 """provide fast membership testing"""
47 """provide fast membership testing"""
47 raise NotImplementedError()
48 raise NotImplementedError()
48
49
49 def __iter__(self):
50 def __iter__(self):
50 """iterate the set in the order it is supposed to be iterated"""
51 """iterate the set in the order it is supposed to be iterated"""
51 raise NotImplementedError()
52 raise NotImplementedError()
52
53
53 # Attributes containing a function to perform a fast iteration in a given
54 # Attributes containing a function to perform a fast iteration in a given
54 # direction. A smartset can have none, one, or both defined.
55 # direction. A smartset can have none, one, or both defined.
55 #
56 #
56 # Default value is None instead of a function returning None to avoid
57 # Default value is None instead of a function returning None to avoid
57 # initializing an iterator just for testing if a fast method exists.
58 # initializing an iterator just for testing if a fast method exists.
58 fastasc = None
59 fastasc = None
59 fastdesc = None
60 fastdesc = None
60
61
61 def isascending(self):
62 def isascending(self):
62 """True if the set will iterate in ascending order"""
63 """True if the set will iterate in ascending order"""
63 raise NotImplementedError()
64 raise NotImplementedError()
64
65
65 def isdescending(self):
66 def isdescending(self):
66 """True if the set will iterate in descending order"""
67 """True if the set will iterate in descending order"""
67 raise NotImplementedError()
68 raise NotImplementedError()
68
69
69 def istopo(self):
70 def istopo(self):
70 """True if the set will iterate in topographical order"""
71 """True if the set will iterate in topographical order"""
71 raise NotImplementedError()
72 raise NotImplementedError()
72
73
73 def min(self):
74 def min(self):
74 """return the minimum element in the set"""
75 """return the minimum element in the set"""
75 if self.fastasc is None:
76 if self.fastasc is None:
76 v = min(self)
77 v = min(self)
77 else:
78 else:
78 for v in self.fastasc():
79 for v in self.fastasc():
79 break
80 break
80 else:
81 else:
81 raise ValueError('arg is an empty sequence')
82 raise ValueError('arg is an empty sequence')
82 self.min = lambda: v
83 self.min = lambda: v
83 return v
84 return v
84
85
85 def max(self):
86 def max(self):
86 """return the maximum element in the set"""
87 """return the maximum element in the set"""
87 if self.fastdesc is None:
88 if self.fastdesc is None:
88 return max(self)
89 return max(self)
89 else:
90 else:
90 for v in self.fastdesc():
91 for v in self.fastdesc():
91 break
92 break
92 else:
93 else:
93 raise ValueError('arg is an empty sequence')
94 raise ValueError('arg is an empty sequence')
94 self.max = lambda: v
95 self.max = lambda: v
95 return v
96 return v
96
97
97 def first(self):
98 def first(self):
98 """return the first element in the set (user iteration perspective)
99 """return the first element in the set (user iteration perspective)
99
100
100 Return None if the set is empty"""
101 Return None if the set is empty"""
101 raise NotImplementedError()
102 raise NotImplementedError()
102
103
103 def last(self):
104 def last(self):
104 """return the last element in the set (user iteration perspective)
105 """return the last element in the set (user iteration perspective)
105
106
106 Return None if the set is empty"""
107 Return None if the set is empty"""
107 raise NotImplementedError()
108 raise NotImplementedError()
108
109
109 def __len__(self):
110 def __len__(self):
110 """return the length of the smartsets
111 """return the length of the smartsets
111
112
112 This can be expensive on smartset that could be lazy otherwise."""
113 This can be expensive on smartset that could be lazy otherwise."""
113 raise NotImplementedError()
114 raise NotImplementedError()
114
115
115 def reverse(self):
116 def reverse(self):
116 """reverse the expected iteration order"""
117 """reverse the expected iteration order"""
117 raise NotImplementedError()
118 raise NotImplementedError()
118
119
119 def sort(self, reverse=True):
120 def sort(self, reverse=True):
120 """get the set to iterate in an ascending or descending order"""
121 """get the set to iterate in an ascending or descending order"""
121 raise NotImplementedError()
122 raise NotImplementedError()
122
123
123 def __and__(self, other):
124 def __and__(self, other):
124 """Returns a new object with the intersection of the two collections.
125 """Returns a new object with the intersection of the two collections.
125
126
126 This is part of the mandatory API for smartset."""
127 This is part of the mandatory API for smartset."""
127 if isinstance(other, fullreposet):
128 if isinstance(other, fullreposet):
128 return self
129 return self
129 return self.filter(other.__contains__, condrepr=other, cache=False)
130 return self.filter(other.__contains__, condrepr=other, cache=False)
130
131
131 def __add__(self, other):
132 def __add__(self, other):
132 """Returns a new object with the union of the two collections.
133 """Returns a new object with the union of the two collections.
133
134
134 This is part of the mandatory API for smartset."""
135 This is part of the mandatory API for smartset."""
135 return addset(self, other)
136 return addset(self, other)
136
137
137 def __sub__(self, other):
138 def __sub__(self, other):
138 """Returns a new object with the substraction of the two collections.
139 """Returns a new object with the substraction of the two collections.
139
140
140 This is part of the mandatory API for smartset."""
141 This is part of the mandatory API for smartset."""
141 c = other.__contains__
142 c = other.__contains__
142 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
143 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
143 cache=False)
144 cache=False)
144
145
145 def filter(self, condition, condrepr=None, cache=True):
146 def filter(self, condition, condrepr=None, cache=True):
146 """Returns this smartset filtered by condition as a new smartset.
147 """Returns this smartset filtered by condition as a new smartset.
147
148
148 `condition` is a callable which takes a revision number and returns a
149 `condition` is a callable which takes a revision number and returns a
149 boolean. Optional `condrepr` provides a printable representation of
150 boolean. Optional `condrepr` provides a printable representation of
150 the given `condition`.
151 the given `condition`.
151
152
152 This is part of the mandatory API for smartset."""
153 This is part of the mandatory API for smartset."""
153 # builtin cannot be cached. but do not needs to
154 # builtin cannot be cached. but do not needs to
154 if cache and util.safehasattr(condition, 'func_code'):
155 if cache and util.safehasattr(condition, 'func_code'):
155 condition = util.cachefunc(condition)
156 condition = util.cachefunc(condition)
156 return filteredset(self, condition, condrepr)
157 return filteredset(self, condition, condrepr)
157
158
159 def slice(self, start, stop):
160 """Return new smartset that contains selected elements from this set"""
161 if start < 0 or stop < 0:
162 raise error.ProgrammingError('negative index not allowed')
163 return self._slice(start, stop)
164
165 def _slice(self, start, stop):
166 # sub classes may override this. start and stop must not be negative,
167 # but start > stop is allowed, which should be an empty set.
168 ys = []
169 it = iter(self)
170 for x in xrange(start):
171 y = next(it, None)
172 if y is None:
173 break
174 for x in xrange(stop - start):
175 y = next(it, None)
176 if y is None:
177 break
178 ys.append(y)
179 return baseset(ys, datarepr=('slice=%d:%d %r', start, stop, self))
180
158 class baseset(abstractsmartset):
181 class baseset(abstractsmartset):
159 """Basic data structure that represents a revset and contains the basic
182 """Basic data structure that represents a revset and contains the basic
160 operation that it should be able to perform.
183 operation that it should be able to perform.
161
184
162 Every method in this class should be implemented by any smartset class.
185 Every method in this class should be implemented by any smartset class.
163
186
164 This class could be constructed by an (unordered) set, or an (ordered)
187 This class could be constructed by an (unordered) set, or an (ordered)
165 list-like object. If a set is provided, it'll be sorted lazily.
188 list-like object. If a set is provided, it'll be sorted lazily.
166
189
167 >>> x = [4, 0, 7, 6]
190 >>> x = [4, 0, 7, 6]
168 >>> y = [5, 6, 7, 3]
191 >>> y = [5, 6, 7, 3]
169
192
170 Construct by a set:
193 Construct by a set:
171 >>> xs = baseset(set(x))
194 >>> xs = baseset(set(x))
172 >>> ys = baseset(set(y))
195 >>> ys = baseset(set(y))
173 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
196 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
174 [[0, 4, 6, 7, 3, 5], [6, 7], [0, 4]]
197 [[0, 4, 6, 7, 3, 5], [6, 7], [0, 4]]
175 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
198 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
176 ['addset', 'baseset', 'baseset']
199 ['addset', 'baseset', 'baseset']
177
200
178 Construct by a list-like:
201 Construct by a list-like:
179 >>> xs = baseset(x)
202 >>> xs = baseset(x)
180 >>> ys = baseset(i for i in y)
203 >>> ys = baseset(i for i in y)
181 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
204 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
182 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
205 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
183 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
206 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
184 ['addset', 'filteredset', 'filteredset']
207 ['addset', 'filteredset', 'filteredset']
185
208
186 Populate "_set" fields in the lists so set optimization may be used:
209 Populate "_set" fields in the lists so set optimization may be used:
187 >>> [1 in xs, 3 in ys]
210 >>> [1 in xs, 3 in ys]
188 [False, True]
211 [False, True]
189
212
190 Without sort(), results won't be changed:
213 Without sort(), results won't be changed:
191 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
214 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
192 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
215 [[4, 0, 7, 6, 5, 3], [7, 6], [4, 0]]
193 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
216 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
194 ['addset', 'filteredset', 'filteredset']
217 ['addset', 'filteredset', 'filteredset']
195
218
196 With sort(), set optimization could be used:
219 With sort(), set optimization could be used:
197 >>> xs.sort(reverse=True)
220 >>> xs.sort(reverse=True)
198 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
221 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
199 [[7, 6, 4, 0, 5, 3], [7, 6], [4, 0]]
222 [[7, 6, 4, 0, 5, 3], [7, 6], [4, 0]]
200 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
223 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
201 ['addset', 'baseset', 'baseset']
224 ['addset', 'baseset', 'baseset']
202
225
203 >>> ys.sort()
226 >>> ys.sort()
204 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
227 >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
205 [[7, 6, 4, 0, 3, 5], [7, 6], [4, 0]]
228 [[7, 6, 4, 0, 3, 5], [7, 6], [4, 0]]
206 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
229 >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
207 ['addset', 'baseset', 'baseset']
230 ['addset', 'baseset', 'baseset']
208
231
209 istopo is preserved across set operations
232 istopo is preserved across set operations
210 >>> xs = baseset(set(x), istopo=True)
233 >>> xs = baseset(set(x), istopo=True)
211 >>> rs = xs & ys
234 >>> rs = xs & ys
212 >>> type(rs).__name__
235 >>> type(rs).__name__
213 'baseset'
236 'baseset'
214 >>> rs._istopo
237 >>> rs._istopo
215 True
238 True
216 """
239 """
217 def __init__(self, data=(), datarepr=None, istopo=False):
240 def __init__(self, data=(), datarepr=None, istopo=False):
218 """
241 """
219 datarepr: a tuple of (format, obj, ...), a function or an object that
242 datarepr: a tuple of (format, obj, ...), a function or an object that
220 provides a printable representation of the given data.
243 provides a printable representation of the given data.
221 """
244 """
222 self._ascending = None
245 self._ascending = None
223 self._istopo = istopo
246 self._istopo = istopo
224 if isinstance(data, set):
247 if isinstance(data, set):
225 # converting set to list has a cost, do it lazily
248 # converting set to list has a cost, do it lazily
226 self._set = data
249 self._set = data
227 # set has no order we pick one for stability purpose
250 # set has no order we pick one for stability purpose
228 self._ascending = True
251 self._ascending = True
229 else:
252 else:
230 if not isinstance(data, list):
253 if not isinstance(data, list):
231 data = list(data)
254 data = list(data)
232 self._list = data
255 self._list = data
233 self._datarepr = datarepr
256 self._datarepr = datarepr
234
257
235 @util.propertycache
258 @util.propertycache
236 def _set(self):
259 def _set(self):
237 return set(self._list)
260 return set(self._list)
238
261
239 @util.propertycache
262 @util.propertycache
240 def _asclist(self):
263 def _asclist(self):
241 asclist = self._list[:]
264 asclist = self._list[:]
242 asclist.sort()
265 asclist.sort()
243 return asclist
266 return asclist
244
267
245 @util.propertycache
268 @util.propertycache
246 def _list(self):
269 def _list(self):
247 # _list is only lazily constructed if we have _set
270 # _list is only lazily constructed if we have _set
248 assert r'_set' in self.__dict__
271 assert r'_set' in self.__dict__
249 return list(self._set)
272 return list(self._set)
250
273
251 def __iter__(self):
274 def __iter__(self):
252 if self._ascending is None:
275 if self._ascending is None:
253 return iter(self._list)
276 return iter(self._list)
254 elif self._ascending:
277 elif self._ascending:
255 return iter(self._asclist)
278 return iter(self._asclist)
256 else:
279 else:
257 return reversed(self._asclist)
280 return reversed(self._asclist)
258
281
259 def fastasc(self):
282 def fastasc(self):
260 return iter(self._asclist)
283 return iter(self._asclist)
261
284
262 def fastdesc(self):
285 def fastdesc(self):
263 return reversed(self._asclist)
286 return reversed(self._asclist)
264
287
265 @util.propertycache
288 @util.propertycache
266 def __contains__(self):
289 def __contains__(self):
267 return self._set.__contains__
290 return self._set.__contains__
268
291
269 def __nonzero__(self):
292 def __nonzero__(self):
270 return bool(len(self))
293 return bool(len(self))
271
294
272 __bool__ = __nonzero__
295 __bool__ = __nonzero__
273
296
274 def sort(self, reverse=False):
297 def sort(self, reverse=False):
275 self._ascending = not bool(reverse)
298 self._ascending = not bool(reverse)
276 self._istopo = False
299 self._istopo = False
277
300
278 def reverse(self):
301 def reverse(self):
279 if self._ascending is None:
302 if self._ascending is None:
280 self._list.reverse()
303 self._list.reverse()
281 else:
304 else:
282 self._ascending = not self._ascending
305 self._ascending = not self._ascending
283 self._istopo = False
306 self._istopo = False
284
307
285 def __len__(self):
308 def __len__(self):
286 if '_list' in self.__dict__:
309 if '_list' in self.__dict__:
287 return len(self._list)
310 return len(self._list)
288 else:
311 else:
289 return len(self._set)
312 return len(self._set)
290
313
291 def isascending(self):
314 def isascending(self):
292 """Returns True if the collection is ascending order, False if not.
315 """Returns True if the collection is ascending order, False if not.
293
316
294 This is part of the mandatory API for smartset."""
317 This is part of the mandatory API for smartset."""
295 if len(self) <= 1:
318 if len(self) <= 1:
296 return True
319 return True
297 return self._ascending is not None and self._ascending
320 return self._ascending is not None and self._ascending
298
321
299 def isdescending(self):
322 def isdescending(self):
300 """Returns True if the collection is descending order, False if not.
323 """Returns True if the collection is descending order, False if not.
301
324
302 This is part of the mandatory API for smartset."""
325 This is part of the mandatory API for smartset."""
303 if len(self) <= 1:
326 if len(self) <= 1:
304 return True
327 return True
305 return self._ascending is not None and not self._ascending
328 return self._ascending is not None and not self._ascending
306
329
307 def istopo(self):
330 def istopo(self):
308 """Is the collection is in topographical order or not.
331 """Is the collection is in topographical order or not.
309
332
310 This is part of the mandatory API for smartset."""
333 This is part of the mandatory API for smartset."""
311 if len(self) <= 1:
334 if len(self) <= 1:
312 return True
335 return True
313 return self._istopo
336 return self._istopo
314
337
315 def first(self):
338 def first(self):
316 if self:
339 if self:
317 if self._ascending is None:
340 if self._ascending is None:
318 return self._list[0]
341 return self._list[0]
319 elif self._ascending:
342 elif self._ascending:
320 return self._asclist[0]
343 return self._asclist[0]
321 else:
344 else:
322 return self._asclist[-1]
345 return self._asclist[-1]
323 return None
346 return None
324
347
325 def last(self):
348 def last(self):
326 if self:
349 if self:
327 if self._ascending is None:
350 if self._ascending is None:
328 return self._list[-1]
351 return self._list[-1]
329 elif self._ascending:
352 elif self._ascending:
330 return self._asclist[-1]
353 return self._asclist[-1]
331 else:
354 else:
332 return self._asclist[0]
355 return self._asclist[0]
333 return None
356 return None
334
357
335 def _fastsetop(self, other, op):
358 def _fastsetop(self, other, op):
336 # try to use native set operations as fast paths
359 # try to use native set operations as fast paths
337 if (type(other) is baseset and '_set' in other.__dict__ and '_set' in
360 if (type(other) is baseset and '_set' in other.__dict__ and '_set' in
338 self.__dict__ and self._ascending is not None):
361 self.__dict__ and self._ascending is not None):
339 s = baseset(data=getattr(self._set, op)(other._set),
362 s = baseset(data=getattr(self._set, op)(other._set),
340 istopo=self._istopo)
363 istopo=self._istopo)
341 s._ascending = self._ascending
364 s._ascending = self._ascending
342 else:
365 else:
343 s = getattr(super(baseset, self), op)(other)
366 s = getattr(super(baseset, self), op)(other)
344 return s
367 return s
345
368
346 def __and__(self, other):
369 def __and__(self, other):
347 return self._fastsetop(other, '__and__')
370 return self._fastsetop(other, '__and__')
348
371
349 def __sub__(self, other):
372 def __sub__(self, other):
350 return self._fastsetop(other, '__sub__')
373 return self._fastsetop(other, '__sub__')
351
374
352 def __repr__(self):
375 def __repr__(self):
353 d = {None: '', False: '-', True: '+'}[self._ascending]
376 d = {None: '', False: '-', True: '+'}[self._ascending]
354 s = _formatsetrepr(self._datarepr)
377 s = _formatsetrepr(self._datarepr)
355 if not s:
378 if not s:
356 l = self._list
379 l = self._list
357 # if _list has been built from a set, it might have a different
380 # if _list has been built from a set, it might have a different
358 # order from one python implementation to another.
381 # order from one python implementation to another.
359 # We fallback to the sorted version for a stable output.
382 # We fallback to the sorted version for a stable output.
360 if self._ascending is not None:
383 if self._ascending is not None:
361 l = self._asclist
384 l = self._asclist
362 s = repr(l)
385 s = repr(l)
363 return '<%s%s %s>' % (type(self).__name__, d, s)
386 return '<%s%s %s>' % (type(self).__name__, d, s)
364
387
365 class filteredset(abstractsmartset):
388 class filteredset(abstractsmartset):
366 """Duck type for baseset class which iterates lazily over the revisions in
389 """Duck type for baseset class which iterates lazily over the revisions in
367 the subset and contains a function which tests for membership in the
390 the subset and contains a function which tests for membership in the
368 revset
391 revset
369 """
392 """
370 def __init__(self, subset, condition=lambda x: True, condrepr=None):
393 def __init__(self, subset, condition=lambda x: True, condrepr=None):
371 """
394 """
372 condition: a function that decide whether a revision in the subset
395 condition: a function that decide whether a revision in the subset
373 belongs to the revset or not.
396 belongs to the revset or not.
374 condrepr: a tuple of (format, obj, ...), a function or an object that
397 condrepr: a tuple of (format, obj, ...), a function or an object that
375 provides a printable representation of the given condition.
398 provides a printable representation of the given condition.
376 """
399 """
377 self._subset = subset
400 self._subset = subset
378 self._condition = condition
401 self._condition = condition
379 self._condrepr = condrepr
402 self._condrepr = condrepr
380
403
381 def __contains__(self, x):
404 def __contains__(self, x):
382 return x in self._subset and self._condition(x)
405 return x in self._subset and self._condition(x)
383
406
384 def __iter__(self):
407 def __iter__(self):
385 return self._iterfilter(self._subset)
408 return self._iterfilter(self._subset)
386
409
387 def _iterfilter(self, it):
410 def _iterfilter(self, it):
388 cond = self._condition
411 cond = self._condition
389 for x in it:
412 for x in it:
390 if cond(x):
413 if cond(x):
391 yield x
414 yield x
392
415
393 @property
416 @property
394 def fastasc(self):
417 def fastasc(self):
395 it = self._subset.fastasc
418 it = self._subset.fastasc
396 if it is None:
419 if it is None:
397 return None
420 return None
398 return lambda: self._iterfilter(it())
421 return lambda: self._iterfilter(it())
399
422
400 @property
423 @property
401 def fastdesc(self):
424 def fastdesc(self):
402 it = self._subset.fastdesc
425 it = self._subset.fastdesc
403 if it is None:
426 if it is None:
404 return None
427 return None
405 return lambda: self._iterfilter(it())
428 return lambda: self._iterfilter(it())
406
429
407 def __nonzero__(self):
430 def __nonzero__(self):
408 fast = None
431 fast = None
409 candidates = [self.fastasc if self.isascending() else None,
432 candidates = [self.fastasc if self.isascending() else None,
410 self.fastdesc if self.isdescending() else None,
433 self.fastdesc if self.isdescending() else None,
411 self.fastasc,
434 self.fastasc,
412 self.fastdesc]
435 self.fastdesc]
413 for candidate in candidates:
436 for candidate in candidates:
414 if candidate is not None:
437 if candidate is not None:
415 fast = candidate
438 fast = candidate
416 break
439 break
417
440
418 if fast is not None:
441 if fast is not None:
419 it = fast()
442 it = fast()
420 else:
443 else:
421 it = self
444 it = self
422
445
423 for r in it:
446 for r in it:
424 return True
447 return True
425 return False
448 return False
426
449
427 __bool__ = __nonzero__
450 __bool__ = __nonzero__
428
451
429 def __len__(self):
452 def __len__(self):
430 # Basic implementation to be changed in future patches.
453 # Basic implementation to be changed in future patches.
431 # until this gets improved, we use generator expression
454 # until this gets improved, we use generator expression
432 # here, since list comprehensions are free to call __len__ again
455 # here, since list comprehensions are free to call __len__ again
433 # causing infinite recursion
456 # causing infinite recursion
434 l = baseset(r for r in self)
457 l = baseset(r for r in self)
435 return len(l)
458 return len(l)
436
459
437 def sort(self, reverse=False):
460 def sort(self, reverse=False):
438 self._subset.sort(reverse=reverse)
461 self._subset.sort(reverse=reverse)
439
462
440 def reverse(self):
463 def reverse(self):
441 self._subset.reverse()
464 self._subset.reverse()
442
465
443 def isascending(self):
466 def isascending(self):
444 return self._subset.isascending()
467 return self._subset.isascending()
445
468
446 def isdescending(self):
469 def isdescending(self):
447 return self._subset.isdescending()
470 return self._subset.isdescending()
448
471
449 def istopo(self):
472 def istopo(self):
450 return self._subset.istopo()
473 return self._subset.istopo()
451
474
452 def first(self):
475 def first(self):
453 for x in self:
476 for x in self:
454 return x
477 return x
455 return None
478 return None
456
479
457 def last(self):
480 def last(self):
458 it = None
481 it = None
459 if self.isascending():
482 if self.isascending():
460 it = self.fastdesc
483 it = self.fastdesc
461 elif self.isdescending():
484 elif self.isdescending():
462 it = self.fastasc
485 it = self.fastasc
463 if it is not None:
486 if it is not None:
464 for x in it():
487 for x in it():
465 return x
488 return x
466 return None #empty case
489 return None #empty case
467 else:
490 else:
468 x = None
491 x = None
469 for x in self:
492 for x in self:
470 pass
493 pass
471 return x
494 return x
472
495
473 def __repr__(self):
496 def __repr__(self):
474 xs = [repr(self._subset)]
497 xs = [repr(self._subset)]
475 s = _formatsetrepr(self._condrepr)
498 s = _formatsetrepr(self._condrepr)
476 if s:
499 if s:
477 xs.append(s)
500 xs.append(s)
478 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
501 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
479
502
480 def _iterordered(ascending, iter1, iter2):
503 def _iterordered(ascending, iter1, iter2):
481 """produce an ordered iteration from two iterators with the same order
504 """produce an ordered iteration from two iterators with the same order
482
505
483 The ascending is used to indicated the iteration direction.
506 The ascending is used to indicated the iteration direction.
484 """
507 """
485 choice = max
508 choice = max
486 if ascending:
509 if ascending:
487 choice = min
510 choice = min
488
511
489 val1 = None
512 val1 = None
490 val2 = None
513 val2 = None
491 try:
514 try:
492 # Consume both iterators in an ordered way until one is empty
515 # Consume both iterators in an ordered way until one is empty
493 while True:
516 while True:
494 if val1 is None:
517 if val1 is None:
495 val1 = next(iter1)
518 val1 = next(iter1)
496 if val2 is None:
519 if val2 is None:
497 val2 = next(iter2)
520 val2 = next(iter2)
498 n = choice(val1, val2)
521 n = choice(val1, val2)
499 yield n
522 yield n
500 if val1 == n:
523 if val1 == n:
501 val1 = None
524 val1 = None
502 if val2 == n:
525 if val2 == n:
503 val2 = None
526 val2 = None
504 except StopIteration:
527 except StopIteration:
505 # Flush any remaining values and consume the other one
528 # Flush any remaining values and consume the other one
506 it = iter2
529 it = iter2
507 if val1 is not None:
530 if val1 is not None:
508 yield val1
531 yield val1
509 it = iter1
532 it = iter1
510 elif val2 is not None:
533 elif val2 is not None:
511 # might have been equality and both are empty
534 # might have been equality and both are empty
512 yield val2
535 yield val2
513 for val in it:
536 for val in it:
514 yield val
537 yield val
515
538
516 class addset(abstractsmartset):
539 class addset(abstractsmartset):
517 """Represent the addition of two sets
540 """Represent the addition of two sets
518
541
519 Wrapper structure for lazily adding two structures without losing much
542 Wrapper structure for lazily adding two structures without losing much
520 performance on the __contains__ method
543 performance on the __contains__ method
521
544
522 If the ascending attribute is set, that means the two structures are
545 If the ascending attribute is set, that means the two structures are
523 ordered in either an ascending or descending way. Therefore, we can add
546 ordered in either an ascending or descending way. Therefore, we can add
524 them maintaining the order by iterating over both at the same time
547 them maintaining the order by iterating over both at the same time
525
548
526 >>> xs = baseset([0, 3, 2])
549 >>> xs = baseset([0, 3, 2])
527 >>> ys = baseset([5, 2, 4])
550 >>> ys = baseset([5, 2, 4])
528
551
529 >>> rs = addset(xs, ys)
552 >>> rs = addset(xs, ys)
530 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
553 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
531 (True, True, False, True, 0, 4)
554 (True, True, False, True, 0, 4)
532 >>> rs = addset(xs, baseset([]))
555 >>> rs = addset(xs, baseset([]))
533 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
556 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
534 (True, True, False, 0, 2)
557 (True, True, False, 0, 2)
535 >>> rs = addset(baseset([]), baseset([]))
558 >>> rs = addset(baseset([]), baseset([]))
536 >>> bool(rs), 0 in rs, rs.first(), rs.last()
559 >>> bool(rs), 0 in rs, rs.first(), rs.last()
537 (False, False, None, None)
560 (False, False, None, None)
538
561
539 iterate unsorted:
562 iterate unsorted:
540 >>> rs = addset(xs, ys)
563 >>> rs = addset(xs, ys)
541 >>> # (use generator because pypy could call len())
564 >>> # (use generator because pypy could call len())
542 >>> list(x for x in rs) # without _genlist
565 >>> list(x for x in rs) # without _genlist
543 [0, 3, 2, 5, 4]
566 [0, 3, 2, 5, 4]
544 >>> assert not rs._genlist
567 >>> assert not rs._genlist
545 >>> len(rs)
568 >>> len(rs)
546 5
569 5
547 >>> [x for x in rs] # with _genlist
570 >>> [x for x in rs] # with _genlist
548 [0, 3, 2, 5, 4]
571 [0, 3, 2, 5, 4]
549 >>> assert rs._genlist
572 >>> assert rs._genlist
550
573
551 iterate ascending:
574 iterate ascending:
552 >>> rs = addset(xs, ys, ascending=True)
575 >>> rs = addset(xs, ys, ascending=True)
553 >>> # (use generator because pypy could call len())
576 >>> # (use generator because pypy could call len())
554 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
577 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
555 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
578 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
556 >>> assert not rs._asclist
579 >>> assert not rs._asclist
557 >>> len(rs)
580 >>> len(rs)
558 5
581 5
559 >>> [x for x in rs], [x for x in rs.fastasc()]
582 >>> [x for x in rs], [x for x in rs.fastasc()]
560 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
583 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
561 >>> assert rs._asclist
584 >>> assert rs._asclist
562
585
563 iterate descending:
586 iterate descending:
564 >>> rs = addset(xs, ys, ascending=False)
587 >>> rs = addset(xs, ys, ascending=False)
565 >>> # (use generator because pypy could call len())
588 >>> # (use generator because pypy could call len())
566 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
589 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
567 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
590 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
568 >>> assert not rs._asclist
591 >>> assert not rs._asclist
569 >>> len(rs)
592 >>> len(rs)
570 5
593 5
571 >>> [x for x in rs], [x for x in rs.fastdesc()]
594 >>> [x for x in rs], [x for x in rs.fastdesc()]
572 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
595 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
573 >>> assert rs._asclist
596 >>> assert rs._asclist
574
597
575 iterate ascending without fastasc:
598 iterate ascending without fastasc:
576 >>> rs = addset(xs, generatorset(ys), ascending=True)
599 >>> rs = addset(xs, generatorset(ys), ascending=True)
577 >>> assert rs.fastasc is None
600 >>> assert rs.fastasc is None
578 >>> [x for x in rs]
601 >>> [x for x in rs]
579 [0, 2, 3, 4, 5]
602 [0, 2, 3, 4, 5]
580
603
581 iterate descending without fastdesc:
604 iterate descending without fastdesc:
582 >>> rs = addset(generatorset(xs), ys, ascending=False)
605 >>> rs = addset(generatorset(xs), ys, ascending=False)
583 >>> assert rs.fastdesc is None
606 >>> assert rs.fastdesc is None
584 >>> [x for x in rs]
607 >>> [x for x in rs]
585 [5, 4, 3, 2, 0]
608 [5, 4, 3, 2, 0]
586 """
609 """
587 def __init__(self, revs1, revs2, ascending=None):
610 def __init__(self, revs1, revs2, ascending=None):
588 self._r1 = revs1
611 self._r1 = revs1
589 self._r2 = revs2
612 self._r2 = revs2
590 self._iter = None
613 self._iter = None
591 self._ascending = ascending
614 self._ascending = ascending
592 self._genlist = None
615 self._genlist = None
593 self._asclist = None
616 self._asclist = None
594
617
595 def __len__(self):
618 def __len__(self):
596 return len(self._list)
619 return len(self._list)
597
620
598 def __nonzero__(self):
621 def __nonzero__(self):
599 return bool(self._r1) or bool(self._r2)
622 return bool(self._r1) or bool(self._r2)
600
623
601 __bool__ = __nonzero__
624 __bool__ = __nonzero__
602
625
603 @util.propertycache
626 @util.propertycache
604 def _list(self):
627 def _list(self):
605 if not self._genlist:
628 if not self._genlist:
606 self._genlist = baseset(iter(self))
629 self._genlist = baseset(iter(self))
607 return self._genlist
630 return self._genlist
608
631
609 def __iter__(self):
632 def __iter__(self):
610 """Iterate over both collections without repeating elements
633 """Iterate over both collections without repeating elements
611
634
612 If the ascending attribute is not set, iterate over the first one and
635 If the ascending attribute is not set, iterate over the first one and
613 then over the second one checking for membership on the first one so we
636 then over the second one checking for membership on the first one so we
614 dont yield any duplicates.
637 dont yield any duplicates.
615
638
616 If the ascending attribute is set, iterate over both collections at the
639 If the ascending attribute is set, iterate over both collections at the
617 same time, yielding only one value at a time in the given order.
640 same time, yielding only one value at a time in the given order.
618 """
641 """
619 if self._ascending is None:
642 if self._ascending is None:
620 if self._genlist:
643 if self._genlist:
621 return iter(self._genlist)
644 return iter(self._genlist)
622 def arbitraryordergen():
645 def arbitraryordergen():
623 for r in self._r1:
646 for r in self._r1:
624 yield r
647 yield r
625 inr1 = self._r1.__contains__
648 inr1 = self._r1.__contains__
626 for r in self._r2:
649 for r in self._r2:
627 if not inr1(r):
650 if not inr1(r):
628 yield r
651 yield r
629 return arbitraryordergen()
652 return arbitraryordergen()
630 # try to use our own fast iterator if it exists
653 # try to use our own fast iterator if it exists
631 self._trysetasclist()
654 self._trysetasclist()
632 if self._ascending:
655 if self._ascending:
633 attr = 'fastasc'
656 attr = 'fastasc'
634 else:
657 else:
635 attr = 'fastdesc'
658 attr = 'fastdesc'
636 it = getattr(self, attr)
659 it = getattr(self, attr)
637 if it is not None:
660 if it is not None:
638 return it()
661 return it()
639 # maybe half of the component supports fast
662 # maybe half of the component supports fast
640 # get iterator for _r1
663 # get iterator for _r1
641 iter1 = getattr(self._r1, attr)
664 iter1 = getattr(self._r1, attr)
642 if iter1 is None:
665 if iter1 is None:
643 # let's avoid side effect (not sure it matters)
666 # let's avoid side effect (not sure it matters)
644 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
667 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
645 else:
668 else:
646 iter1 = iter1()
669 iter1 = iter1()
647 # get iterator for _r2
670 # get iterator for _r2
648 iter2 = getattr(self._r2, attr)
671 iter2 = getattr(self._r2, attr)
649 if iter2 is None:
672 if iter2 is None:
650 # let's avoid side effect (not sure it matters)
673 # let's avoid side effect (not sure it matters)
651 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
674 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
652 else:
675 else:
653 iter2 = iter2()
676 iter2 = iter2()
654 return _iterordered(self._ascending, iter1, iter2)
677 return _iterordered(self._ascending, iter1, iter2)
655
678
656 def _trysetasclist(self):
679 def _trysetasclist(self):
657 """populate the _asclist attribute if possible and necessary"""
680 """populate the _asclist attribute if possible and necessary"""
658 if self._genlist is not None and self._asclist is None:
681 if self._genlist is not None and self._asclist is None:
659 self._asclist = sorted(self._genlist)
682 self._asclist = sorted(self._genlist)
660
683
661 @property
684 @property
662 def fastasc(self):
685 def fastasc(self):
663 self._trysetasclist()
686 self._trysetasclist()
664 if self._asclist is not None:
687 if self._asclist is not None:
665 return self._asclist.__iter__
688 return self._asclist.__iter__
666 iter1 = self._r1.fastasc
689 iter1 = self._r1.fastasc
667 iter2 = self._r2.fastasc
690 iter2 = self._r2.fastasc
668 if None in (iter1, iter2):
691 if None in (iter1, iter2):
669 return None
692 return None
670 return lambda: _iterordered(True, iter1(), iter2())
693 return lambda: _iterordered(True, iter1(), iter2())
671
694
672 @property
695 @property
673 def fastdesc(self):
696 def fastdesc(self):
674 self._trysetasclist()
697 self._trysetasclist()
675 if self._asclist is not None:
698 if self._asclist is not None:
676 return self._asclist.__reversed__
699 return self._asclist.__reversed__
677 iter1 = self._r1.fastdesc
700 iter1 = self._r1.fastdesc
678 iter2 = self._r2.fastdesc
701 iter2 = self._r2.fastdesc
679 if None in (iter1, iter2):
702 if None in (iter1, iter2):
680 return None
703 return None
681 return lambda: _iterordered(False, iter1(), iter2())
704 return lambda: _iterordered(False, iter1(), iter2())
682
705
683 def __contains__(self, x):
706 def __contains__(self, x):
684 return x in self._r1 or x in self._r2
707 return x in self._r1 or x in self._r2
685
708
686 def sort(self, reverse=False):
709 def sort(self, reverse=False):
687 """Sort the added set
710 """Sort the added set
688
711
689 For this we use the cached list with all the generated values and if we
712 For this we use the cached list with all the generated values and if we
690 know they are ascending or descending we can sort them in a smart way.
713 know they are ascending or descending we can sort them in a smart way.
691 """
714 """
692 self._ascending = not reverse
715 self._ascending = not reverse
693
716
694 def isascending(self):
717 def isascending(self):
695 return self._ascending is not None and self._ascending
718 return self._ascending is not None and self._ascending
696
719
697 def isdescending(self):
720 def isdescending(self):
698 return self._ascending is not None and not self._ascending
721 return self._ascending is not None and not self._ascending
699
722
700 def istopo(self):
723 def istopo(self):
701 # not worth the trouble asserting if the two sets combined are still
724 # not worth the trouble asserting if the two sets combined are still
702 # in topographical order. Use the sort() predicate to explicitly sort
725 # in topographical order. Use the sort() predicate to explicitly sort
703 # again instead.
726 # again instead.
704 return False
727 return False
705
728
706 def reverse(self):
729 def reverse(self):
707 if self._ascending is None:
730 if self._ascending is None:
708 self._list.reverse()
731 self._list.reverse()
709 else:
732 else:
710 self._ascending = not self._ascending
733 self._ascending = not self._ascending
711
734
712 def first(self):
735 def first(self):
713 for x in self:
736 for x in self:
714 return x
737 return x
715 return None
738 return None
716
739
717 def last(self):
740 def last(self):
718 self.reverse()
741 self.reverse()
719 val = self.first()
742 val = self.first()
720 self.reverse()
743 self.reverse()
721 return val
744 return val
722
745
723 def __repr__(self):
746 def __repr__(self):
724 d = {None: '', False: '-', True: '+'}[self._ascending]
747 d = {None: '', False: '-', True: '+'}[self._ascending]
725 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
748 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
726
749
727 class generatorset(abstractsmartset):
750 class generatorset(abstractsmartset):
728 """Wrap a generator for lazy iteration
751 """Wrap a generator for lazy iteration
729
752
730 Wrapper structure for generators that provides lazy membership and can
753 Wrapper structure for generators that provides lazy membership and can
731 be iterated more than once.
754 be iterated more than once.
732 When asked for membership it generates values until either it finds the
755 When asked for membership it generates values until either it finds the
733 requested one or has gone through all the elements in the generator
756 requested one or has gone through all the elements in the generator
734 """
757 """
735 def __init__(self, gen, iterasc=None):
758 def __init__(self, gen, iterasc=None):
736 """
759 """
737 gen: a generator producing the values for the generatorset.
760 gen: a generator producing the values for the generatorset.
738 """
761 """
739 self._gen = gen
762 self._gen = gen
740 self._asclist = None
763 self._asclist = None
741 self._cache = {}
764 self._cache = {}
742 self._genlist = []
765 self._genlist = []
743 self._finished = False
766 self._finished = False
744 self._ascending = True
767 self._ascending = True
745 if iterasc is not None:
768 if iterasc is not None:
746 if iterasc:
769 if iterasc:
747 self.fastasc = self._iterator
770 self.fastasc = self._iterator
748 self.__contains__ = self._asccontains
771 self.__contains__ = self._asccontains
749 else:
772 else:
750 self.fastdesc = self._iterator
773 self.fastdesc = self._iterator
751 self.__contains__ = self._desccontains
774 self.__contains__ = self._desccontains
752
775
753 def __nonzero__(self):
776 def __nonzero__(self):
754 # Do not use 'for r in self' because it will enforce the iteration
777 # Do not use 'for r in self' because it will enforce the iteration
755 # order (default ascending), possibly unrolling a whole descending
778 # order (default ascending), possibly unrolling a whole descending
756 # iterator.
779 # iterator.
757 if self._genlist:
780 if self._genlist:
758 return True
781 return True
759 for r in self._consumegen():
782 for r in self._consumegen():
760 return True
783 return True
761 return False
784 return False
762
785
763 __bool__ = __nonzero__
786 __bool__ = __nonzero__
764
787
765 def __contains__(self, x):
788 def __contains__(self, x):
766 if x in self._cache:
789 if x in self._cache:
767 return self._cache[x]
790 return self._cache[x]
768
791
769 # Use new values only, as existing values would be cached.
792 # Use new values only, as existing values would be cached.
770 for l in self._consumegen():
793 for l in self._consumegen():
771 if l == x:
794 if l == x:
772 return True
795 return True
773
796
774 self._cache[x] = False
797 self._cache[x] = False
775 return False
798 return False
776
799
777 def _asccontains(self, x):
800 def _asccontains(self, x):
778 """version of contains optimised for ascending generator"""
801 """version of contains optimised for ascending generator"""
779 if x in self._cache:
802 if x in self._cache:
780 return self._cache[x]
803 return self._cache[x]
781
804
782 # Use new values only, as existing values would be cached.
805 # Use new values only, as existing values would be cached.
783 for l in self._consumegen():
806 for l in self._consumegen():
784 if l == x:
807 if l == x:
785 return True
808 return True
786 if l > x:
809 if l > x:
787 break
810 break
788
811
789 self._cache[x] = False
812 self._cache[x] = False
790 return False
813 return False
791
814
792 def _desccontains(self, x):
815 def _desccontains(self, x):
793 """version of contains optimised for descending generator"""
816 """version of contains optimised for descending generator"""
794 if x in self._cache:
817 if x in self._cache:
795 return self._cache[x]
818 return self._cache[x]
796
819
797 # Use new values only, as existing values would be cached.
820 # Use new values only, as existing values would be cached.
798 for l in self._consumegen():
821 for l in self._consumegen():
799 if l == x:
822 if l == x:
800 return True
823 return True
801 if l < x:
824 if l < x:
802 break
825 break
803
826
804 self._cache[x] = False
827 self._cache[x] = False
805 return False
828 return False
806
829
807 def __iter__(self):
830 def __iter__(self):
808 if self._ascending:
831 if self._ascending:
809 it = self.fastasc
832 it = self.fastasc
810 else:
833 else:
811 it = self.fastdesc
834 it = self.fastdesc
812 if it is not None:
835 if it is not None:
813 return it()
836 return it()
814 # we need to consume the iterator
837 # we need to consume the iterator
815 for x in self._consumegen():
838 for x in self._consumegen():
816 pass
839 pass
817 # recall the same code
840 # recall the same code
818 return iter(self)
841 return iter(self)
819
842
820 def _iterator(self):
843 def _iterator(self):
821 if self._finished:
844 if self._finished:
822 return iter(self._genlist)
845 return iter(self._genlist)
823
846
824 # We have to use this complex iteration strategy to allow multiple
847 # We have to use this complex iteration strategy to allow multiple
825 # iterations at the same time. We need to be able to catch revision
848 # iterations at the same time. We need to be able to catch revision
826 # removed from _consumegen and added to genlist in another instance.
849 # removed from _consumegen and added to genlist in another instance.
827 #
850 #
828 # Getting rid of it would provide an about 15% speed up on this
851 # Getting rid of it would provide an about 15% speed up on this
829 # iteration.
852 # iteration.
830 genlist = self._genlist
853 genlist = self._genlist
831 nextgen = self._consumegen()
854 nextgen = self._consumegen()
832 _len, _next = len, next # cache global lookup
855 _len, _next = len, next # cache global lookup
833 def gen():
856 def gen():
834 i = 0
857 i = 0
835 while True:
858 while True:
836 if i < _len(genlist):
859 if i < _len(genlist):
837 yield genlist[i]
860 yield genlist[i]
838 else:
861 else:
839 yield _next(nextgen)
862 yield _next(nextgen)
840 i += 1
863 i += 1
841 return gen()
864 return gen()
842
865
843 def _consumegen(self):
866 def _consumegen(self):
844 cache = self._cache
867 cache = self._cache
845 genlist = self._genlist.append
868 genlist = self._genlist.append
846 for item in self._gen:
869 for item in self._gen:
847 cache[item] = True
870 cache[item] = True
848 genlist(item)
871 genlist(item)
849 yield item
872 yield item
850 if not self._finished:
873 if not self._finished:
851 self._finished = True
874 self._finished = True
852 asc = self._genlist[:]
875 asc = self._genlist[:]
853 asc.sort()
876 asc.sort()
854 self._asclist = asc
877 self._asclist = asc
855 self.fastasc = asc.__iter__
878 self.fastasc = asc.__iter__
856 self.fastdesc = asc.__reversed__
879 self.fastdesc = asc.__reversed__
857
880
858 def __len__(self):
881 def __len__(self):
859 for x in self._consumegen():
882 for x in self._consumegen():
860 pass
883 pass
861 return len(self._genlist)
884 return len(self._genlist)
862
885
863 def sort(self, reverse=False):
886 def sort(self, reverse=False):
864 self._ascending = not reverse
887 self._ascending = not reverse
865
888
866 def reverse(self):
889 def reverse(self):
867 self._ascending = not self._ascending
890 self._ascending = not self._ascending
868
891
869 def isascending(self):
892 def isascending(self):
870 return self._ascending
893 return self._ascending
871
894
872 def isdescending(self):
895 def isdescending(self):
873 return not self._ascending
896 return not self._ascending
874
897
875 def istopo(self):
898 def istopo(self):
876 # not worth the trouble asserting if the two sets combined are still
899 # not worth the trouble asserting if the two sets combined are still
877 # in topographical order. Use the sort() predicate to explicitly sort
900 # in topographical order. Use the sort() predicate to explicitly sort
878 # again instead.
901 # again instead.
879 return False
902 return False
880
903
881 def first(self):
904 def first(self):
882 if self._ascending:
905 if self._ascending:
883 it = self.fastasc
906 it = self.fastasc
884 else:
907 else:
885 it = self.fastdesc
908 it = self.fastdesc
886 if it is None:
909 if it is None:
887 # we need to consume all and try again
910 # we need to consume all and try again
888 for x in self._consumegen():
911 for x in self._consumegen():
889 pass
912 pass
890 return self.first()
913 return self.first()
891 return next(it(), None)
914 return next(it(), None)
892
915
893 def last(self):
916 def last(self):
894 if self._ascending:
917 if self._ascending:
895 it = self.fastdesc
918 it = self.fastdesc
896 else:
919 else:
897 it = self.fastasc
920 it = self.fastasc
898 if it is None:
921 if it is None:
899 # we need to consume all and try again
922 # we need to consume all and try again
900 for x in self._consumegen():
923 for x in self._consumegen():
901 pass
924 pass
902 return self.first()
925 return self.first()
903 return next(it(), None)
926 return next(it(), None)
904
927
905 def __repr__(self):
928 def __repr__(self):
906 d = {False: '-', True: '+'}[self._ascending]
929 d = {False: '-', True: '+'}[self._ascending]
907 return '<%s%s>' % (type(self).__name__, d)
930 return '<%s%s>' % (type(self).__name__, d)
908
931
909 def spanset(repo, start=0, end=None):
932 def spanset(repo, start=0, end=None):
910 """Create a spanset that represents a range of repository revisions
933 """Create a spanset that represents a range of repository revisions
911
934
912 start: first revision included the set (default to 0)
935 start: first revision included the set (default to 0)
913 end: first revision excluded (last+1) (default to len(repo))
936 end: first revision excluded (last+1) (default to len(repo))
914
937
915 Spanset will be descending if `end` < `start`.
938 Spanset will be descending if `end` < `start`.
916 """
939 """
917 if end is None:
940 if end is None:
918 end = len(repo)
941 end = len(repo)
919 ascending = start <= end
942 ascending = start <= end
920 if not ascending:
943 if not ascending:
921 start, end = end + 1, start + 1
944 start, end = end + 1, start + 1
922 return _spanset(start, end, ascending, repo.changelog.filteredrevs)
945 return _spanset(start, end, ascending, repo.changelog.filteredrevs)
923
946
924 class _spanset(abstractsmartset):
947 class _spanset(abstractsmartset):
925 """Duck type for baseset class which represents a range of revisions and
948 """Duck type for baseset class which represents a range of revisions and
926 can work lazily and without having all the range in memory
949 can work lazily and without having all the range in memory
927
950
928 Note that spanset(x, y) behave almost like xrange(x, y) except for two
951 Note that spanset(x, y) behave almost like xrange(x, y) except for two
929 notable points:
952 notable points:
930 - when x < y it will be automatically descending,
953 - when x < y it will be automatically descending,
931 - revision filtered with this repoview will be skipped.
954 - revision filtered with this repoview will be skipped.
932
955
933 """
956 """
934 def __init__(self, start, end, ascending, hiddenrevs):
957 def __init__(self, start, end, ascending, hiddenrevs):
935 self._start = start
958 self._start = start
936 self._end = end
959 self._end = end
937 self._ascending = ascending
960 self._ascending = ascending
938 self._hiddenrevs = hiddenrevs
961 self._hiddenrevs = hiddenrevs
939
962
940 def sort(self, reverse=False):
963 def sort(self, reverse=False):
941 self._ascending = not reverse
964 self._ascending = not reverse
942
965
943 def reverse(self):
966 def reverse(self):
944 self._ascending = not self._ascending
967 self._ascending = not self._ascending
945
968
946 def istopo(self):
969 def istopo(self):
947 # not worth the trouble asserting if the two sets combined are still
970 # not worth the trouble asserting if the two sets combined are still
948 # in topographical order. Use the sort() predicate to explicitly sort
971 # in topographical order. Use the sort() predicate to explicitly sort
949 # again instead.
972 # again instead.
950 return False
973 return False
951
974
952 def _iterfilter(self, iterrange):
975 def _iterfilter(self, iterrange):
953 s = self._hiddenrevs
976 s = self._hiddenrevs
954 for r in iterrange:
977 for r in iterrange:
955 if r not in s:
978 if r not in s:
956 yield r
979 yield r
957
980
958 def __iter__(self):
981 def __iter__(self):
959 if self._ascending:
982 if self._ascending:
960 return self.fastasc()
983 return self.fastasc()
961 else:
984 else:
962 return self.fastdesc()
985 return self.fastdesc()
963
986
964 def fastasc(self):
987 def fastasc(self):
965 iterrange = xrange(self._start, self._end)
988 iterrange = xrange(self._start, self._end)
966 if self._hiddenrevs:
989 if self._hiddenrevs:
967 return self._iterfilter(iterrange)
990 return self._iterfilter(iterrange)
968 return iter(iterrange)
991 return iter(iterrange)
969
992
970 def fastdesc(self):
993 def fastdesc(self):
971 iterrange = xrange(self._end - 1, self._start - 1, -1)
994 iterrange = xrange(self._end - 1, self._start - 1, -1)
972 if self._hiddenrevs:
995 if self._hiddenrevs:
973 return self._iterfilter(iterrange)
996 return self._iterfilter(iterrange)
974 return iter(iterrange)
997 return iter(iterrange)
975
998
976 def __contains__(self, rev):
999 def __contains__(self, rev):
977 hidden = self._hiddenrevs
1000 hidden = self._hiddenrevs
978 return ((self._start <= rev < self._end)
1001 return ((self._start <= rev < self._end)
979 and not (hidden and rev in hidden))
1002 and not (hidden and rev in hidden))
980
1003
981 def __nonzero__(self):
1004 def __nonzero__(self):
982 for r in self:
1005 for r in self:
983 return True
1006 return True
984 return False
1007 return False
985
1008
986 __bool__ = __nonzero__
1009 __bool__ = __nonzero__
987
1010
988 def __len__(self):
1011 def __len__(self):
989 if not self._hiddenrevs:
1012 if not self._hiddenrevs:
990 return abs(self._end - self._start)
1013 return abs(self._end - self._start)
991 else:
1014 else:
992 count = 0
1015 count = 0
993 start = self._start
1016 start = self._start
994 end = self._end
1017 end = self._end
995 for rev in self._hiddenrevs:
1018 for rev in self._hiddenrevs:
996 if (end < rev <= start) or (start <= rev < end):
1019 if (end < rev <= start) or (start <= rev < end):
997 count += 1
1020 count += 1
998 return abs(self._end - self._start) - count
1021 return abs(self._end - self._start) - count
999
1022
1000 def isascending(self):
1023 def isascending(self):
1001 return self._ascending
1024 return self._ascending
1002
1025
1003 def isdescending(self):
1026 def isdescending(self):
1004 return not self._ascending
1027 return not self._ascending
1005
1028
1006 def first(self):
1029 def first(self):
1007 if self._ascending:
1030 if self._ascending:
1008 it = self.fastasc
1031 it = self.fastasc
1009 else:
1032 else:
1010 it = self.fastdesc
1033 it = self.fastdesc
1011 for x in it():
1034 for x in it():
1012 return x
1035 return x
1013 return None
1036 return None
1014
1037
1015 def last(self):
1038 def last(self):
1016 if self._ascending:
1039 if self._ascending:
1017 it = self.fastdesc
1040 it = self.fastdesc
1018 else:
1041 else:
1019 it = self.fastasc
1042 it = self.fastasc
1020 for x in it():
1043 for x in it():
1021 return x
1044 return x
1022 return None
1045 return None
1023
1046
1024 def __repr__(self):
1047 def __repr__(self):
1025 d = {False: '-', True: '+'}[self._ascending]
1048 d = {False: '-', True: '+'}[self._ascending]
1026 return '<%s%s %d:%d>' % (type(self).__name__.lstrip('_'), d,
1049 return '<%s%s %d:%d>' % (type(self).__name__.lstrip('_'), d,
1027 self._start, self._end)
1050 self._start, self._end)
1028
1051
1029 class fullreposet(_spanset):
1052 class fullreposet(_spanset):
1030 """a set containing all revisions in the repo
1053 """a set containing all revisions in the repo
1031
1054
1032 This class exists to host special optimization and magic to handle virtual
1055 This class exists to host special optimization and magic to handle virtual
1033 revisions such as "null".
1056 revisions such as "null".
1034 """
1057 """
1035
1058
1036 def __init__(self, repo):
1059 def __init__(self, repo):
1037 super(fullreposet, self).__init__(0, len(repo), True,
1060 super(fullreposet, self).__init__(0, len(repo), True,
1038 repo.changelog.filteredrevs)
1061 repo.changelog.filteredrevs)
1039
1062
1040 def __and__(self, other):
1063 def __and__(self, other):
1041 """As self contains the whole repo, all of the other set should also be
1064 """As self contains the whole repo, all of the other set should also be
1042 in self. Therefore `self & other = other`.
1065 in self. Therefore `self & other = other`.
1043
1066
1044 This boldly assumes the other contains valid revs only.
1067 This boldly assumes the other contains valid revs only.
1045 """
1068 """
1046 # other not a smartset, make is so
1069 # other not a smartset, make is so
1047 if not util.safehasattr(other, 'isascending'):
1070 if not util.safehasattr(other, 'isascending'):
1048 # filter out hidden revision
1071 # filter out hidden revision
1049 # (this boldly assumes all smartset are pure)
1072 # (this boldly assumes all smartset are pure)
1050 #
1073 #
1051 # `other` was used with "&", let's assume this is a set like
1074 # `other` was used with "&", let's assume this is a set like
1052 # object.
1075 # object.
1053 other = baseset(other - self._hiddenrevs)
1076 other = baseset(other - self._hiddenrevs)
1054
1077
1055 other.sort(reverse=self.isdescending())
1078 other.sort(reverse=self.isdescending())
1056 return other
1079 return other
1057
1080
1058 def prettyformat(revs):
1081 def prettyformat(revs):
1059 lines = []
1082 lines = []
1060 rs = repr(revs)
1083 rs = repr(revs)
1061 p = 0
1084 p = 0
1062 while p < len(rs):
1085 while p < len(rs):
1063 q = rs.find('<', p + 1)
1086 q = rs.find('<', p + 1)
1064 if q < 0:
1087 if q < 0:
1065 q = len(rs)
1088 q = len(rs)
1066 l = rs.count('<', 0, p) - rs.count('>', 0, p)
1089 l = rs.count('<', 0, p) - rs.count('>', 0, p)
1067 assert l >= 0
1090 assert l >= 0
1068 lines.append((l, rs[p:q].rstrip()))
1091 lines.append((l, rs[p:q].rstrip()))
1069 p = q
1092 p = q
1070 return '\n'.join(' ' * l + s for l, s in lines)
1093 return '\n'.join(' ' * l + s for l, s in lines)
@@ -1,3922 +1,3935
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols['r3232'] = r3232
19 > mercurial.revset.symbols['r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > testrevset=$TESTTMP/testrevset.py
23 > testrevset=$TESTTMP/testrevset.py
24 > EOF
24 > EOF
25
25
26 $ try() {
26 $ try() {
27 > hg debugrevspec --debug "$@"
27 > hg debugrevspec --debug "$@"
28 > }
28 > }
29
29
30 $ log() {
30 $ log() {
31 > hg log --template '{rev}\n' -r "$1"
31 > hg log --template '{rev}\n' -r "$1"
32 > }
32 > }
33
33
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 these predicates use '\0' as a separator:
35 these predicates use '\0' as a separator:
36
36
37 $ cat <<EOF > debugrevlistspec.py
37 $ cat <<EOF > debugrevlistspec.py
38 > from __future__ import absolute_import
38 > from __future__ import absolute_import
39 > from mercurial import (
39 > from mercurial import (
40 > node as nodemod,
40 > node as nodemod,
41 > registrar,
41 > registrar,
42 > revset,
42 > revset,
43 > revsetlang,
43 > revsetlang,
44 > smartset,
44 > smartset,
45 > )
45 > )
46 > cmdtable = {}
46 > cmdtable = {}
47 > command = registrar.command(cmdtable)
47 > command = registrar.command(cmdtable)
48 > @command('debugrevlistspec',
48 > @command('debugrevlistspec',
49 > [('', 'optimize', None, 'print parsed tree after optimizing'),
49 > [('', 'optimize', None, 'print parsed tree after optimizing'),
50 > ('', 'bin', None, 'unhexlify arguments')])
50 > ('', 'bin', None, 'unhexlify arguments')])
51 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
51 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
52 > if opts['bin']:
52 > if opts['bin']:
53 > args = map(nodemod.bin, args)
53 > args = map(nodemod.bin, args)
54 > expr = revsetlang.formatspec(fmt, list(args))
54 > expr = revsetlang.formatspec(fmt, list(args))
55 > if ui.verbose:
55 > if ui.verbose:
56 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
56 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
57 > ui.note(revsetlang.prettyformat(tree), "\n")
57 > ui.note(revsetlang.prettyformat(tree), "\n")
58 > if opts["optimize"]:
58 > if opts["optimize"]:
59 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
59 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
60 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
60 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
61 > "\n")
61 > "\n")
62 > func = revset.match(ui, expr, repo)
62 > func = revset.match(ui, expr, repo)
63 > revs = func(repo)
63 > revs = func(repo)
64 > if ui.verbose:
64 > if ui.verbose:
65 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
65 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
66 > for c in revs:
66 > for c in revs:
67 > ui.write("%s\n" % c)
67 > ui.write("%s\n" % c)
68 > EOF
68 > EOF
69 $ cat <<EOF >> $HGRCPATH
69 $ cat <<EOF >> $HGRCPATH
70 > [extensions]
70 > [extensions]
71 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
71 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
72 > EOF
72 > EOF
73 $ trylist() {
73 $ trylist() {
74 > hg debugrevlistspec --debug "$@"
74 > hg debugrevlistspec --debug "$@"
75 > }
75 > }
76
76
77 $ hg init repo
77 $ hg init repo
78 $ cd repo
78 $ cd repo
79
79
80 $ echo a > a
80 $ echo a > a
81 $ hg branch a
81 $ hg branch a
82 marked working directory as branch a
82 marked working directory as branch a
83 (branches are permanent and global, did you want a bookmark?)
83 (branches are permanent and global, did you want a bookmark?)
84 $ hg ci -Aqm0
84 $ hg ci -Aqm0
85
85
86 $ echo b > b
86 $ echo b > b
87 $ hg branch b
87 $ hg branch b
88 marked working directory as branch b
88 marked working directory as branch b
89 $ hg ci -Aqm1
89 $ hg ci -Aqm1
90
90
91 $ rm a
91 $ rm a
92 $ hg branch a-b-c-
92 $ hg branch a-b-c-
93 marked working directory as branch a-b-c-
93 marked working directory as branch a-b-c-
94 $ hg ci -Aqm2 -u Bob
94 $ hg ci -Aqm2 -u Bob
95
95
96 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
96 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
97 2
97 2
98 $ hg log -r "extra('branch')" --template '{rev}\n'
98 $ hg log -r "extra('branch')" --template '{rev}\n'
99 0
99 0
100 1
100 1
101 2
101 2
102 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
102 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
103 0 a
103 0 a
104 2 a-b-c-
104 2 a-b-c-
105
105
106 $ hg co 1
106 $ hg co 1
107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 $ hg branch +a+b+c+
108 $ hg branch +a+b+c+
109 marked working directory as branch +a+b+c+
109 marked working directory as branch +a+b+c+
110 $ hg ci -Aqm3
110 $ hg ci -Aqm3
111
111
112 $ hg co 2 # interleave
112 $ hg co 2 # interleave
113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 $ echo bb > b
114 $ echo bb > b
115 $ hg branch -- -a-b-c-
115 $ hg branch -- -a-b-c-
116 marked working directory as branch -a-b-c-
116 marked working directory as branch -a-b-c-
117 $ hg ci -Aqm4 -d "May 12 2005"
117 $ hg ci -Aqm4 -d "May 12 2005"
118
118
119 $ hg co 3
119 $ hg co 3
120 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 $ hg branch !a/b/c/
121 $ hg branch !a/b/c/
122 marked working directory as branch !a/b/c/
122 marked working directory as branch !a/b/c/
123 $ hg ci -Aqm"5 bug"
123 $ hg ci -Aqm"5 bug"
124
124
125 $ hg merge 4
125 $ hg merge 4
126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
127 (branch merge, don't forget to commit)
127 (branch merge, don't forget to commit)
128 $ hg branch _a_b_c_
128 $ hg branch _a_b_c_
129 marked working directory as branch _a_b_c_
129 marked working directory as branch _a_b_c_
130 $ hg ci -Aqm"6 issue619"
130 $ hg ci -Aqm"6 issue619"
131
131
132 $ hg branch .a.b.c.
132 $ hg branch .a.b.c.
133 marked working directory as branch .a.b.c.
133 marked working directory as branch .a.b.c.
134 $ hg ci -Aqm7
134 $ hg ci -Aqm7
135
135
136 $ hg branch all
136 $ hg branch all
137 marked working directory as branch all
137 marked working directory as branch all
138
138
139 $ hg co 4
139 $ hg co 4
140 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 $ hg branch Γ©
141 $ hg branch Γ©
142 marked working directory as branch \xc3\xa9 (esc)
142 marked working directory as branch \xc3\xa9 (esc)
143 $ hg ci -Aqm9
143 $ hg ci -Aqm9
144
144
145 $ hg tag -r6 1.0
145 $ hg tag -r6 1.0
146 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
146 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
147
147
148 $ hg clone --quiet -U -r 7 . ../remote1
148 $ hg clone --quiet -U -r 7 . ../remote1
149 $ hg clone --quiet -U -r 8 . ../remote2
149 $ hg clone --quiet -U -r 8 . ../remote2
150 $ echo "[paths]" >> .hg/hgrc
150 $ echo "[paths]" >> .hg/hgrc
151 $ echo "default = ../remote1" >> .hg/hgrc
151 $ echo "default = ../remote1" >> .hg/hgrc
152
152
153 trivial
153 trivial
154
154
155 $ try 0:1
155 $ try 0:1
156 (range
156 (range
157 ('symbol', '0')
157 ('symbol', '0')
158 ('symbol', '1'))
158 ('symbol', '1'))
159 * set:
159 * set:
160 <spanset+ 0:2>
160 <spanset+ 0:2>
161 0
161 0
162 1
162 1
163 $ try --optimize :
163 $ try --optimize :
164 (rangeall
164 (rangeall
165 None)
165 None)
166 * optimized:
166 * optimized:
167 (rangeall
167 (rangeall
168 None
168 None
169 define)
169 define)
170 * set:
170 * set:
171 <spanset+ 0:10>
171 <spanset+ 0:10>
172 0
172 0
173 1
173 1
174 2
174 2
175 3
175 3
176 4
176 4
177 5
177 5
178 6
178 6
179 7
179 7
180 8
180 8
181 9
181 9
182 $ try 3::6
182 $ try 3::6
183 (dagrange
183 (dagrange
184 ('symbol', '3')
184 ('symbol', '3')
185 ('symbol', '6'))
185 ('symbol', '6'))
186 * set:
186 * set:
187 <baseset+ [3, 5, 6]>
187 <baseset+ [3, 5, 6]>
188 3
188 3
189 5
189 5
190 6
190 6
191 $ try '0|1|2'
191 $ try '0|1|2'
192 (or
192 (or
193 (list
193 (list
194 ('symbol', '0')
194 ('symbol', '0')
195 ('symbol', '1')
195 ('symbol', '1')
196 ('symbol', '2')))
196 ('symbol', '2')))
197 * set:
197 * set:
198 <baseset [0, 1, 2]>
198 <baseset [0, 1, 2]>
199 0
199 0
200 1
200 1
201 2
201 2
202
202
203 names that should work without quoting
203 names that should work without quoting
204
204
205 $ try a
205 $ try a
206 ('symbol', 'a')
206 ('symbol', 'a')
207 * set:
207 * set:
208 <baseset [0]>
208 <baseset [0]>
209 0
209 0
210 $ try b-a
210 $ try b-a
211 (minus
211 (minus
212 ('symbol', 'b')
212 ('symbol', 'b')
213 ('symbol', 'a'))
213 ('symbol', 'a'))
214 * set:
214 * set:
215 <filteredset
215 <filteredset
216 <baseset [1]>,
216 <baseset [1]>,
217 <not
217 <not
218 <baseset [0]>>>
218 <baseset [0]>>>
219 1
219 1
220 $ try _a_b_c_
220 $ try _a_b_c_
221 ('symbol', '_a_b_c_')
221 ('symbol', '_a_b_c_')
222 * set:
222 * set:
223 <baseset [6]>
223 <baseset [6]>
224 6
224 6
225 $ try _a_b_c_-a
225 $ try _a_b_c_-a
226 (minus
226 (minus
227 ('symbol', '_a_b_c_')
227 ('symbol', '_a_b_c_')
228 ('symbol', 'a'))
228 ('symbol', 'a'))
229 * set:
229 * set:
230 <filteredset
230 <filteredset
231 <baseset [6]>,
231 <baseset [6]>,
232 <not
232 <not
233 <baseset [0]>>>
233 <baseset [0]>>>
234 6
234 6
235 $ try .a.b.c.
235 $ try .a.b.c.
236 ('symbol', '.a.b.c.')
236 ('symbol', '.a.b.c.')
237 * set:
237 * set:
238 <baseset [7]>
238 <baseset [7]>
239 7
239 7
240 $ try .a.b.c.-a
240 $ try .a.b.c.-a
241 (minus
241 (minus
242 ('symbol', '.a.b.c.')
242 ('symbol', '.a.b.c.')
243 ('symbol', 'a'))
243 ('symbol', 'a'))
244 * set:
244 * set:
245 <filteredset
245 <filteredset
246 <baseset [7]>,
246 <baseset [7]>,
247 <not
247 <not
248 <baseset [0]>>>
248 <baseset [0]>>>
249 7
249 7
250
250
251 names that should be caught by fallback mechanism
251 names that should be caught by fallback mechanism
252
252
253 $ try -- '-a-b-c-'
253 $ try -- '-a-b-c-'
254 ('symbol', '-a-b-c-')
254 ('symbol', '-a-b-c-')
255 * set:
255 * set:
256 <baseset [4]>
256 <baseset [4]>
257 4
257 4
258 $ log -a-b-c-
258 $ log -a-b-c-
259 4
259 4
260 $ try '+a+b+c+'
260 $ try '+a+b+c+'
261 ('symbol', '+a+b+c+')
261 ('symbol', '+a+b+c+')
262 * set:
262 * set:
263 <baseset [3]>
263 <baseset [3]>
264 3
264 3
265 $ try '+a+b+c+:'
265 $ try '+a+b+c+:'
266 (rangepost
266 (rangepost
267 ('symbol', '+a+b+c+'))
267 ('symbol', '+a+b+c+'))
268 * set:
268 * set:
269 <spanset+ 3:10>
269 <spanset+ 3:10>
270 3
270 3
271 4
271 4
272 5
272 5
273 6
273 6
274 7
274 7
275 8
275 8
276 9
276 9
277 $ try ':+a+b+c+'
277 $ try ':+a+b+c+'
278 (rangepre
278 (rangepre
279 ('symbol', '+a+b+c+'))
279 ('symbol', '+a+b+c+'))
280 * set:
280 * set:
281 <spanset+ 0:4>
281 <spanset+ 0:4>
282 0
282 0
283 1
283 1
284 2
284 2
285 3
285 3
286 $ try -- '-a-b-c-:+a+b+c+'
286 $ try -- '-a-b-c-:+a+b+c+'
287 (range
287 (range
288 ('symbol', '-a-b-c-')
288 ('symbol', '-a-b-c-')
289 ('symbol', '+a+b+c+'))
289 ('symbol', '+a+b+c+'))
290 * set:
290 * set:
291 <spanset- 3:5>
291 <spanset- 3:5>
292 4
292 4
293 3
293 3
294 $ log '-a-b-c-:+a+b+c+'
294 $ log '-a-b-c-:+a+b+c+'
295 4
295 4
296 3
296 3
297
297
298 $ try -- -a-b-c--a # complains
298 $ try -- -a-b-c--a # complains
299 (minus
299 (minus
300 (minus
300 (minus
301 (minus
301 (minus
302 (negate
302 (negate
303 ('symbol', 'a'))
303 ('symbol', 'a'))
304 ('symbol', 'b'))
304 ('symbol', 'b'))
305 ('symbol', 'c'))
305 ('symbol', 'c'))
306 (negate
306 (negate
307 ('symbol', 'a')))
307 ('symbol', 'a')))
308 abort: unknown revision '-a'!
308 abort: unknown revision '-a'!
309 [255]
309 [255]
310 $ try Γ©
310 $ try Γ©
311 ('symbol', '\xc3\xa9')
311 ('symbol', '\xc3\xa9')
312 * set:
312 * set:
313 <baseset [9]>
313 <baseset [9]>
314 9
314 9
315
315
316 no quoting needed
316 no quoting needed
317
317
318 $ log ::a-b-c-
318 $ log ::a-b-c-
319 0
319 0
320 1
320 1
321 2
321 2
322
322
323 quoting needed
323 quoting needed
324
324
325 $ try '"-a-b-c-"-a'
325 $ try '"-a-b-c-"-a'
326 (minus
326 (minus
327 ('string', '-a-b-c-')
327 ('string', '-a-b-c-')
328 ('symbol', 'a'))
328 ('symbol', 'a'))
329 * set:
329 * set:
330 <filteredset
330 <filteredset
331 <baseset [4]>,
331 <baseset [4]>,
332 <not
332 <not
333 <baseset [0]>>>
333 <baseset [0]>>>
334 4
334 4
335
335
336 $ log '1 or 2'
336 $ log '1 or 2'
337 1
337 1
338 2
338 2
339 $ log '1|2'
339 $ log '1|2'
340 1
340 1
341 2
341 2
342 $ log '1 and 2'
342 $ log '1 and 2'
343 $ log '1&2'
343 $ log '1&2'
344 $ try '1&2|3' # precedence - and is higher
344 $ try '1&2|3' # precedence - and is higher
345 (or
345 (or
346 (list
346 (list
347 (and
347 (and
348 ('symbol', '1')
348 ('symbol', '1')
349 ('symbol', '2'))
349 ('symbol', '2'))
350 ('symbol', '3')))
350 ('symbol', '3')))
351 * set:
351 * set:
352 <addset
352 <addset
353 <baseset []>,
353 <baseset []>,
354 <baseset [3]>>
354 <baseset [3]>>
355 3
355 3
356 $ try '1|2&3'
356 $ try '1|2&3'
357 (or
357 (or
358 (list
358 (list
359 ('symbol', '1')
359 ('symbol', '1')
360 (and
360 (and
361 ('symbol', '2')
361 ('symbol', '2')
362 ('symbol', '3'))))
362 ('symbol', '3'))))
363 * set:
363 * set:
364 <addset
364 <addset
365 <baseset [1]>,
365 <baseset [1]>,
366 <baseset []>>
366 <baseset []>>
367 1
367 1
368 $ try '1&2&3' # associativity
368 $ try '1&2&3' # associativity
369 (and
369 (and
370 (and
370 (and
371 ('symbol', '1')
371 ('symbol', '1')
372 ('symbol', '2'))
372 ('symbol', '2'))
373 ('symbol', '3'))
373 ('symbol', '3'))
374 * set:
374 * set:
375 <baseset []>
375 <baseset []>
376 $ try '1|(2|3)'
376 $ try '1|(2|3)'
377 (or
377 (or
378 (list
378 (list
379 ('symbol', '1')
379 ('symbol', '1')
380 (group
380 (group
381 (or
381 (or
382 (list
382 (list
383 ('symbol', '2')
383 ('symbol', '2')
384 ('symbol', '3'))))))
384 ('symbol', '3'))))))
385 * set:
385 * set:
386 <addset
386 <addset
387 <baseset [1]>,
387 <baseset [1]>,
388 <baseset [2, 3]>>
388 <baseset [2, 3]>>
389 1
389 1
390 2
390 2
391 3
391 3
392 $ log '1.0' # tag
392 $ log '1.0' # tag
393 6
393 6
394 $ log 'a' # branch
394 $ log 'a' # branch
395 0
395 0
396 $ log '2785f51ee'
396 $ log '2785f51ee'
397 0
397 0
398 $ log 'date(2005)'
398 $ log 'date(2005)'
399 4
399 4
400 $ log 'date(this is a test)'
400 $ log 'date(this is a test)'
401 hg: parse error at 10: unexpected token: symbol
401 hg: parse error at 10: unexpected token: symbol
402 [255]
402 [255]
403 $ log 'date()'
403 $ log 'date()'
404 hg: parse error: date requires a string
404 hg: parse error: date requires a string
405 [255]
405 [255]
406 $ log 'date'
406 $ log 'date'
407 abort: unknown revision 'date'!
407 abort: unknown revision 'date'!
408 [255]
408 [255]
409 $ log 'date('
409 $ log 'date('
410 hg: parse error at 5: not a prefix: end
410 hg: parse error at 5: not a prefix: end
411 [255]
411 [255]
412 $ log 'date("\xy")'
412 $ log 'date("\xy")'
413 hg: parse error: invalid \x escape
413 hg: parse error: invalid \x escape
414 [255]
414 [255]
415 $ log 'date(tip)'
415 $ log 'date(tip)'
416 hg: parse error: invalid date: 'tip'
416 hg: parse error: invalid date: 'tip'
417 [255]
417 [255]
418 $ log '0:date'
418 $ log '0:date'
419 abort: unknown revision 'date'!
419 abort: unknown revision 'date'!
420 [255]
420 [255]
421 $ log '::"date"'
421 $ log '::"date"'
422 abort: unknown revision 'date'!
422 abort: unknown revision 'date'!
423 [255]
423 [255]
424 $ hg book date -r 4
424 $ hg book date -r 4
425 $ log '0:date'
425 $ log '0:date'
426 0
426 0
427 1
427 1
428 2
428 2
429 3
429 3
430 4
430 4
431 $ log '::date'
431 $ log '::date'
432 0
432 0
433 1
433 1
434 2
434 2
435 4
435 4
436 $ log '::"date"'
436 $ log '::"date"'
437 0
437 0
438 1
438 1
439 2
439 2
440 4
440 4
441 $ log 'date(2005) and 1::'
441 $ log 'date(2005) and 1::'
442 4
442 4
443 $ hg book -d date
443 $ hg book -d date
444
444
445 function name should be a symbol
445 function name should be a symbol
446
446
447 $ log '"date"(2005)'
447 $ log '"date"(2005)'
448 hg: parse error: not a symbol
448 hg: parse error: not a symbol
449 [255]
449 [255]
450
450
451 keyword arguments
451 keyword arguments
452
452
453 $ log 'extra(branch, value=a)'
453 $ log 'extra(branch, value=a)'
454 0
454 0
455
455
456 $ log 'extra(branch, a, b)'
456 $ log 'extra(branch, a, b)'
457 hg: parse error: extra takes at most 2 positional arguments
457 hg: parse error: extra takes at most 2 positional arguments
458 [255]
458 [255]
459 $ log 'extra(a, label=b)'
459 $ log 'extra(a, label=b)'
460 hg: parse error: extra got multiple values for keyword argument 'label'
460 hg: parse error: extra got multiple values for keyword argument 'label'
461 [255]
461 [255]
462 $ log 'extra(label=branch, default)'
462 $ log 'extra(label=branch, default)'
463 hg: parse error: extra got an invalid argument
463 hg: parse error: extra got an invalid argument
464 [255]
464 [255]
465 $ log 'extra(branch, foo+bar=baz)'
465 $ log 'extra(branch, foo+bar=baz)'
466 hg: parse error: extra got an invalid argument
466 hg: parse error: extra got an invalid argument
467 [255]
467 [255]
468 $ log 'extra(unknown=branch)'
468 $ log 'extra(unknown=branch)'
469 hg: parse error: extra got an unexpected keyword argument 'unknown'
469 hg: parse error: extra got an unexpected keyword argument 'unknown'
470 [255]
470 [255]
471
471
472 $ try 'foo=bar|baz'
472 $ try 'foo=bar|baz'
473 (keyvalue
473 (keyvalue
474 ('symbol', 'foo')
474 ('symbol', 'foo')
475 (or
475 (or
476 (list
476 (list
477 ('symbol', 'bar')
477 ('symbol', 'bar')
478 ('symbol', 'baz'))))
478 ('symbol', 'baz'))))
479 hg: parse error: can't use a key-value pair in this context
479 hg: parse error: can't use a key-value pair in this context
480 [255]
480 [255]
481
481
482 right-hand side should be optimized recursively
482 right-hand side should be optimized recursively
483
483
484 $ try --optimize 'foo=(not public())'
484 $ try --optimize 'foo=(not public())'
485 (keyvalue
485 (keyvalue
486 ('symbol', 'foo')
486 ('symbol', 'foo')
487 (group
487 (group
488 (not
488 (not
489 (func
489 (func
490 ('symbol', 'public')
490 ('symbol', 'public')
491 None))))
491 None))))
492 * optimized:
492 * optimized:
493 (keyvalue
493 (keyvalue
494 ('symbol', 'foo')
494 ('symbol', 'foo')
495 (func
495 (func
496 ('symbol', '_notpublic')
496 ('symbol', '_notpublic')
497 None
497 None
498 any))
498 any))
499 hg: parse error: can't use a key-value pair in this context
499 hg: parse error: can't use a key-value pair in this context
500 [255]
500 [255]
501
501
502 parsed tree at stages:
502 parsed tree at stages:
503
503
504 $ hg debugrevspec -p all '()'
504 $ hg debugrevspec -p all '()'
505 * parsed:
505 * parsed:
506 (group
506 (group
507 None)
507 None)
508 * expanded:
508 * expanded:
509 (group
509 (group
510 None)
510 None)
511 * concatenated:
511 * concatenated:
512 (group
512 (group
513 None)
513 None)
514 * analyzed:
514 * analyzed:
515 None
515 None
516 * optimized:
516 * optimized:
517 None
517 None
518 hg: parse error: missing argument
518 hg: parse error: missing argument
519 [255]
519 [255]
520
520
521 $ hg debugrevspec --no-optimized -p all '()'
521 $ hg debugrevspec --no-optimized -p all '()'
522 * parsed:
522 * parsed:
523 (group
523 (group
524 None)
524 None)
525 * expanded:
525 * expanded:
526 (group
526 (group
527 None)
527 None)
528 * concatenated:
528 * concatenated:
529 (group
529 (group
530 None)
530 None)
531 * analyzed:
531 * analyzed:
532 None
532 None
533 hg: parse error: missing argument
533 hg: parse error: missing argument
534 [255]
534 [255]
535
535
536 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
536 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
537 * parsed:
537 * parsed:
538 (minus
538 (minus
539 (group
539 (group
540 (or
540 (or
541 (list
541 (list
542 ('symbol', '0')
542 ('symbol', '0')
543 ('symbol', '1'))))
543 ('symbol', '1'))))
544 ('symbol', '1'))
544 ('symbol', '1'))
545 * analyzed:
545 * analyzed:
546 (and
546 (and
547 (or
547 (or
548 (list
548 (list
549 ('symbol', '0')
549 ('symbol', '0')
550 ('symbol', '1'))
550 ('symbol', '1'))
551 define)
551 define)
552 (not
552 (not
553 ('symbol', '1')
553 ('symbol', '1')
554 follow)
554 follow)
555 define)
555 define)
556 * optimized:
556 * optimized:
557 (difference
557 (difference
558 (func
558 (func
559 ('symbol', '_list')
559 ('symbol', '_list')
560 ('string', '0\x001')
560 ('string', '0\x001')
561 define)
561 define)
562 ('symbol', '1')
562 ('symbol', '1')
563 define)
563 define)
564 0
564 0
565
565
566 $ hg debugrevspec -p unknown '0'
566 $ hg debugrevspec -p unknown '0'
567 abort: invalid stage name: unknown
567 abort: invalid stage name: unknown
568 [255]
568 [255]
569
569
570 $ hg debugrevspec -p all --optimize '0'
570 $ hg debugrevspec -p all --optimize '0'
571 abort: cannot use --optimize with --show-stage
571 abort: cannot use --optimize with --show-stage
572 [255]
572 [255]
573
573
574 verify optimized tree:
574 verify optimized tree:
575
575
576 $ hg debugrevspec --verify '0|1'
576 $ hg debugrevspec --verify '0|1'
577
577
578 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
578 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
579 * analyzed:
579 * analyzed:
580 (and
580 (and
581 (func
581 (func
582 ('symbol', 'r3232')
582 ('symbol', 'r3232')
583 None
583 None
584 define)
584 define)
585 ('symbol', '2')
585 ('symbol', '2')
586 define)
586 define)
587 * optimized:
587 * optimized:
588 (and
588 (and
589 ('symbol', '2')
589 ('symbol', '2')
590 (func
590 (func
591 ('symbol', 'r3232')
591 ('symbol', 'r3232')
592 None
592 None
593 define)
593 define)
594 define)
594 define)
595 * analyzed set:
595 * analyzed set:
596 <baseset [2]>
596 <baseset [2]>
597 * optimized set:
597 * optimized set:
598 <baseset [2, 2]>
598 <baseset [2, 2]>
599 --- analyzed
599 --- analyzed
600 +++ optimized
600 +++ optimized
601 2
601 2
602 +2
602 +2
603 [1]
603 [1]
604
604
605 $ hg debugrevspec --no-optimized --verify-optimized '0'
605 $ hg debugrevspec --no-optimized --verify-optimized '0'
606 abort: cannot use --verify-optimized with --no-optimized
606 abort: cannot use --verify-optimized with --no-optimized
607 [255]
607 [255]
608
608
609 Test that symbols only get parsed as functions if there's an opening
609 Test that symbols only get parsed as functions if there's an opening
610 parenthesis.
610 parenthesis.
611
611
612 $ hg book only -r 9
612 $ hg book only -r 9
613 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
613 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
614 8
614 8
615 9
615 9
616
616
617 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
617 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
618 may be hidden (issue5385)
618 may be hidden (issue5385)
619
619
620 $ try -p parsed -p analyzed ':'
620 $ try -p parsed -p analyzed ':'
621 * parsed:
621 * parsed:
622 (rangeall
622 (rangeall
623 None)
623 None)
624 * analyzed:
624 * analyzed:
625 (rangeall
625 (rangeall
626 None
626 None
627 define)
627 define)
628 * set:
628 * set:
629 <spanset+ 0:10>
629 <spanset+ 0:10>
630 0
630 0
631 1
631 1
632 2
632 2
633 3
633 3
634 4
634 4
635 5
635 5
636 6
636 6
637 7
637 7
638 8
638 8
639 9
639 9
640 $ try -p analyzed ':1'
640 $ try -p analyzed ':1'
641 * analyzed:
641 * analyzed:
642 (rangepre
642 (rangepre
643 ('symbol', '1')
643 ('symbol', '1')
644 define)
644 define)
645 * set:
645 * set:
646 <spanset+ 0:2>
646 <spanset+ 0:2>
647 0
647 0
648 1
648 1
649 $ try -p analyzed ':(1|2)'
649 $ try -p analyzed ':(1|2)'
650 * analyzed:
650 * analyzed:
651 (rangepre
651 (rangepre
652 (or
652 (or
653 (list
653 (list
654 ('symbol', '1')
654 ('symbol', '1')
655 ('symbol', '2'))
655 ('symbol', '2'))
656 define)
656 define)
657 define)
657 define)
658 * set:
658 * set:
659 <spanset+ 0:3>
659 <spanset+ 0:3>
660 0
660 0
661 1
661 1
662 2
662 2
663 $ try -p analyzed ':(1&2)'
663 $ try -p analyzed ':(1&2)'
664 * analyzed:
664 * analyzed:
665 (rangepre
665 (rangepre
666 (and
666 (and
667 ('symbol', '1')
667 ('symbol', '1')
668 ('symbol', '2')
668 ('symbol', '2')
669 define)
669 define)
670 define)
670 define)
671 * set:
671 * set:
672 <baseset []>
672 <baseset []>
673
673
674 infix/suffix resolution of ^ operator (issue2884):
674 infix/suffix resolution of ^ operator (issue2884):
675
675
676 x^:y means (x^):y
676 x^:y means (x^):y
677
677
678 $ try '1^:2'
678 $ try '1^:2'
679 (range
679 (range
680 (parentpost
680 (parentpost
681 ('symbol', '1'))
681 ('symbol', '1'))
682 ('symbol', '2'))
682 ('symbol', '2'))
683 * set:
683 * set:
684 <spanset+ 0:3>
684 <spanset+ 0:3>
685 0
685 0
686 1
686 1
687 2
687 2
688
688
689 $ try '1^::2'
689 $ try '1^::2'
690 (dagrange
690 (dagrange
691 (parentpost
691 (parentpost
692 ('symbol', '1'))
692 ('symbol', '1'))
693 ('symbol', '2'))
693 ('symbol', '2'))
694 * set:
694 * set:
695 <baseset+ [0, 1, 2]>
695 <baseset+ [0, 1, 2]>
696 0
696 0
697 1
697 1
698 2
698 2
699
699
700 $ try '9^:'
700 $ try '9^:'
701 (rangepost
701 (rangepost
702 (parentpost
702 (parentpost
703 ('symbol', '9')))
703 ('symbol', '9')))
704 * set:
704 * set:
705 <spanset+ 8:10>
705 <spanset+ 8:10>
706 8
706 8
707 9
707 9
708
708
709 x^:y should be resolved before omitting group operators
709 x^:y should be resolved before omitting group operators
710
710
711 $ try '1^(:2)'
711 $ try '1^(:2)'
712 (parent
712 (parent
713 ('symbol', '1')
713 ('symbol', '1')
714 (group
714 (group
715 (rangepre
715 (rangepre
716 ('symbol', '2'))))
716 ('symbol', '2'))))
717 hg: parse error: ^ expects a number 0, 1, or 2
717 hg: parse error: ^ expects a number 0, 1, or 2
718 [255]
718 [255]
719
719
720 x^:y should be resolved recursively
720 x^:y should be resolved recursively
721
721
722 $ try 'sort(1^:2)'
722 $ try 'sort(1^:2)'
723 (func
723 (func
724 ('symbol', 'sort')
724 ('symbol', 'sort')
725 (range
725 (range
726 (parentpost
726 (parentpost
727 ('symbol', '1'))
727 ('symbol', '1'))
728 ('symbol', '2')))
728 ('symbol', '2')))
729 * set:
729 * set:
730 <spanset+ 0:3>
730 <spanset+ 0:3>
731 0
731 0
732 1
732 1
733 2
733 2
734
734
735 $ try '(3^:4)^:2'
735 $ try '(3^:4)^:2'
736 (range
736 (range
737 (parentpost
737 (parentpost
738 (group
738 (group
739 (range
739 (range
740 (parentpost
740 (parentpost
741 ('symbol', '3'))
741 ('symbol', '3'))
742 ('symbol', '4'))))
742 ('symbol', '4'))))
743 ('symbol', '2'))
743 ('symbol', '2'))
744 * set:
744 * set:
745 <spanset+ 0:3>
745 <spanset+ 0:3>
746 0
746 0
747 1
747 1
748 2
748 2
749
749
750 $ try '(3^::4)^::2'
750 $ try '(3^::4)^::2'
751 (dagrange
751 (dagrange
752 (parentpost
752 (parentpost
753 (group
753 (group
754 (dagrange
754 (dagrange
755 (parentpost
755 (parentpost
756 ('symbol', '3'))
756 ('symbol', '3'))
757 ('symbol', '4'))))
757 ('symbol', '4'))))
758 ('symbol', '2'))
758 ('symbol', '2'))
759 * set:
759 * set:
760 <baseset+ [0, 1, 2]>
760 <baseset+ [0, 1, 2]>
761 0
761 0
762 1
762 1
763 2
763 2
764
764
765 $ try '(9^:)^:'
765 $ try '(9^:)^:'
766 (rangepost
766 (rangepost
767 (parentpost
767 (parentpost
768 (group
768 (group
769 (rangepost
769 (rangepost
770 (parentpost
770 (parentpost
771 ('symbol', '9'))))))
771 ('symbol', '9'))))))
772 * set:
772 * set:
773 <spanset+ 4:10>
773 <spanset+ 4:10>
774 4
774 4
775 5
775 5
776 6
776 6
777 7
777 7
778 8
778 8
779 9
779 9
780
780
781 x^ in alias should also be resolved
781 x^ in alias should also be resolved
782
782
783 $ try 'A' --config 'revsetalias.A=1^:2'
783 $ try 'A' --config 'revsetalias.A=1^:2'
784 ('symbol', 'A')
784 ('symbol', 'A')
785 * expanded:
785 * expanded:
786 (range
786 (range
787 (parentpost
787 (parentpost
788 ('symbol', '1'))
788 ('symbol', '1'))
789 ('symbol', '2'))
789 ('symbol', '2'))
790 * set:
790 * set:
791 <spanset+ 0:3>
791 <spanset+ 0:3>
792 0
792 0
793 1
793 1
794 2
794 2
795
795
796 $ try 'A:2' --config 'revsetalias.A=1^'
796 $ try 'A:2' --config 'revsetalias.A=1^'
797 (range
797 (range
798 ('symbol', 'A')
798 ('symbol', 'A')
799 ('symbol', '2'))
799 ('symbol', '2'))
800 * expanded:
800 * expanded:
801 (range
801 (range
802 (parentpost
802 (parentpost
803 ('symbol', '1'))
803 ('symbol', '1'))
804 ('symbol', '2'))
804 ('symbol', '2'))
805 * set:
805 * set:
806 <spanset+ 0:3>
806 <spanset+ 0:3>
807 0
807 0
808 1
808 1
809 2
809 2
810
810
811 but not beyond the boundary of alias expansion, because the resolution should
811 but not beyond the boundary of alias expansion, because the resolution should
812 be made at the parsing stage
812 be made at the parsing stage
813
813
814 $ try '1^A' --config 'revsetalias.A=:2'
814 $ try '1^A' --config 'revsetalias.A=:2'
815 (parent
815 (parent
816 ('symbol', '1')
816 ('symbol', '1')
817 ('symbol', 'A'))
817 ('symbol', 'A'))
818 * expanded:
818 * expanded:
819 (parent
819 (parent
820 ('symbol', '1')
820 ('symbol', '1')
821 (rangepre
821 (rangepre
822 ('symbol', '2')))
822 ('symbol', '2')))
823 hg: parse error: ^ expects a number 0, 1, or 2
823 hg: parse error: ^ expects a number 0, 1, or 2
824 [255]
824 [255]
825
825
826 ancestor can accept 0 or more arguments
826 ancestor can accept 0 or more arguments
827
827
828 $ log 'ancestor()'
828 $ log 'ancestor()'
829 $ log 'ancestor(1)'
829 $ log 'ancestor(1)'
830 1
830 1
831 $ log 'ancestor(4,5)'
831 $ log 'ancestor(4,5)'
832 1
832 1
833 $ log 'ancestor(4,5) and 4'
833 $ log 'ancestor(4,5) and 4'
834 $ log 'ancestor(0,0,1,3)'
834 $ log 'ancestor(0,0,1,3)'
835 0
835 0
836 $ log 'ancestor(3,1,5,3,5,1)'
836 $ log 'ancestor(3,1,5,3,5,1)'
837 1
837 1
838 $ log 'ancestor(0,1,3,5)'
838 $ log 'ancestor(0,1,3,5)'
839 0
839 0
840 $ log 'ancestor(1,2,3,4,5)'
840 $ log 'ancestor(1,2,3,4,5)'
841 1
841 1
842
842
843 test ancestors
843 test ancestors
844
844
845 $ log 'ancestors(5)'
845 $ log 'ancestors(5)'
846 0
846 0
847 1
847 1
848 3
848 3
849 5
849 5
850 $ log 'ancestor(ancestors(5))'
850 $ log 'ancestor(ancestors(5))'
851 0
851 0
852 $ log '::r3232()'
852 $ log '::r3232()'
853 0
853 0
854 1
854 1
855 2
855 2
856 3
856 3
857
857
858 $ log 'author(bob)'
858 $ log 'author(bob)'
859 2
859 2
860 $ log 'author("re:bob|test")'
860 $ log 'author("re:bob|test")'
861 0
861 0
862 1
862 1
863 2
863 2
864 3
864 3
865 4
865 4
866 5
866 5
867 6
867 6
868 7
868 7
869 8
869 8
870 9
870 9
871 $ log 'author(r"re:\S")'
871 $ log 'author(r"re:\S")'
872 0
872 0
873 1
873 1
874 2
874 2
875 3
875 3
876 4
876 4
877 5
877 5
878 6
878 6
879 7
879 7
880 8
880 8
881 9
881 9
882 $ log 'branch(Γ©)'
882 $ log 'branch(Γ©)'
883 8
883 8
884 9
884 9
885 $ log 'branch(a)'
885 $ log 'branch(a)'
886 0
886 0
887 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
887 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
888 0 a
888 0 a
889 2 a-b-c-
889 2 a-b-c-
890 3 +a+b+c+
890 3 +a+b+c+
891 4 -a-b-c-
891 4 -a-b-c-
892 5 !a/b/c/
892 5 !a/b/c/
893 6 _a_b_c_
893 6 _a_b_c_
894 7 .a.b.c.
894 7 .a.b.c.
895 $ log 'children(ancestor(4,5))'
895 $ log 'children(ancestor(4,5))'
896 2
896 2
897 3
897 3
898
898
899 $ log 'children(4)'
899 $ log 'children(4)'
900 6
900 6
901 8
901 8
902 $ log 'children(null)'
902 $ log 'children(null)'
903 0
903 0
904
904
905 $ log 'closed()'
905 $ log 'closed()'
906 $ log 'contains(a)'
906 $ log 'contains(a)'
907 0
907 0
908 1
908 1
909 3
909 3
910 5
910 5
911 $ log 'contains("../repo/a")'
911 $ log 'contains("../repo/a")'
912 0
912 0
913 1
913 1
914 3
914 3
915 5
915 5
916 $ log 'desc(B)'
916 $ log 'desc(B)'
917 5
917 5
918 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
918 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
919 5 5 bug
919 5 5 bug
920 6 6 issue619
920 6 6 issue619
921 $ log 'descendants(2 or 3)'
921 $ log 'descendants(2 or 3)'
922 2
922 2
923 3
923 3
924 4
924 4
925 5
925 5
926 6
926 6
927 7
927 7
928 8
928 8
929 9
929 9
930 $ log 'file("b*")'
930 $ log 'file("b*")'
931 1
931 1
932 4
932 4
933 $ log 'filelog("b")'
933 $ log 'filelog("b")'
934 1
934 1
935 4
935 4
936 $ log 'filelog("../repo/b")'
936 $ log 'filelog("../repo/b")'
937 1
937 1
938 4
938 4
939 $ log 'follow()'
939 $ log 'follow()'
940 0
940 0
941 1
941 1
942 2
942 2
943 4
943 4
944 8
944 8
945 9
945 9
946 $ log 'grep("issue\d+")'
946 $ log 'grep("issue\d+")'
947 6
947 6
948 $ try 'grep("(")' # invalid regular expression
948 $ try 'grep("(")' # invalid regular expression
949 (func
949 (func
950 ('symbol', 'grep')
950 ('symbol', 'grep')
951 ('string', '('))
951 ('string', '('))
952 hg: parse error: invalid match pattern: unbalanced parenthesis
952 hg: parse error: invalid match pattern: unbalanced parenthesis
953 [255]
953 [255]
954 $ try 'grep("\bissue\d+")'
954 $ try 'grep("\bissue\d+")'
955 (func
955 (func
956 ('symbol', 'grep')
956 ('symbol', 'grep')
957 ('string', '\x08issue\\d+'))
957 ('string', '\x08issue\\d+'))
958 * set:
958 * set:
959 <filteredset
959 <filteredset
960 <fullreposet+ 0:10>,
960 <fullreposet+ 0:10>,
961 <grep '\x08issue\\d+'>>
961 <grep '\x08issue\\d+'>>
962 $ try 'grep(r"\bissue\d+")'
962 $ try 'grep(r"\bissue\d+")'
963 (func
963 (func
964 ('symbol', 'grep')
964 ('symbol', 'grep')
965 ('string', '\\bissue\\d+'))
965 ('string', '\\bissue\\d+'))
966 * set:
966 * set:
967 <filteredset
967 <filteredset
968 <fullreposet+ 0:10>,
968 <fullreposet+ 0:10>,
969 <grep '\\bissue\\d+'>>
969 <grep '\\bissue\\d+'>>
970 6
970 6
971 $ try 'grep(r"\")'
971 $ try 'grep(r"\")'
972 hg: parse error at 7: unterminated string
972 hg: parse error at 7: unterminated string
973 [255]
973 [255]
974 $ log 'head()'
974 $ log 'head()'
975 0
975 0
976 1
976 1
977 2
977 2
978 3
978 3
979 4
979 4
980 5
980 5
981 6
981 6
982 7
982 7
983 9
983 9
984 $ log 'heads(6::)'
984 $ log 'heads(6::)'
985 7
985 7
986 $ log 'keyword(issue)'
986 $ log 'keyword(issue)'
987 6
987 6
988 $ log 'keyword("test a")'
988 $ log 'keyword("test a")'
989
989
990 Test first (=limit) and last
990 Test first (=limit) and last
991
991
992 $ log 'limit(head(), 1)'
992 $ log 'limit(head(), 1)'
993 0
993 0
994 $ log 'limit(author("re:bob|test"), 3, 5)'
994 $ log 'limit(author("re:bob|test"), 3, 5)'
995 5
995 5
996 6
996 6
997 7
997 7
998 $ log 'limit(author("re:bob|test"), offset=6)'
998 $ log 'limit(author("re:bob|test"), offset=6)'
999 6
999 6
1000 $ log 'limit(author("re:bob|test"), offset=10)'
1000 $ log 'limit(author("re:bob|test"), offset=10)'
1001 $ log 'limit(all(), 1, -1)'
1001 $ log 'limit(all(), 1, -1)'
1002 hg: parse error: negative offset
1002 hg: parse error: negative offset
1003 [255]
1003 [255]
1004 $ log 'limit(all(), -1)'
1004 $ log 'limit(all(), -1)'
1005 hg: parse error: negative number to select
1005 hg: parse error: negative number to select
1006 [255]
1006 [255]
1007 $ log 'limit(all(), 0)'
1007 $ log 'limit(all(), 0)'
1008
1008
1009 $ log 'last(all(), -1)'
1009 $ log 'last(all(), -1)'
1010 hg: parse error: negative number to select
1010 hg: parse error: negative number to select
1011 [255]
1011 [255]
1012 $ log 'last(all(), 0)'
1012 $ log 'last(all(), 0)'
1013 $ log 'last(all(), 1)'
1013 $ log 'last(all(), 1)'
1014 9
1014 9
1015 $ log 'last(all(), 2)'
1015 $ log 'last(all(), 2)'
1016 8
1016 8
1017 9
1017 9
1018
1018
1019 Test smartset.slice() by first/last()
1020
1021 (using unoptimized set, filteredset as example)
1022
1023 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1024 * set:
1025 <filteredset
1026 <spanset+ 0:8>,
1027 <branch 're:'>>
1028 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1029 4
1030 5
1031 6
1032 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1033 3
1034 2
1035 1
1036 $ log 'last(0:7 & branch("re:"), 2)'
1037 6
1038 7
1039
1019 Test order of first/last revisions
1040 Test order of first/last revisions
1020
1041
1021 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1042 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1022 * set:
1043 * set:
1023 <filteredset
1044 <filteredset
1024 <baseset
1045 <baseset slice=0:3
1025 <limit n=3, offset=0,
1046 <spanset- 0:5>>,
1026 <spanset- 0:5>>>,
1027 <spanset+ 3:10>>
1047 <spanset+ 3:10>>
1028 4
1048 4
1029 3
1049 3
1030
1050
1031 $ hg debugrevspec -s '3: & first(4:0, 3)'
1051 $ hg debugrevspec -s '3: & first(4:0, 3)'
1032 * set:
1052 * set:
1033 <filteredset
1053 <filteredset
1034 <spanset+ 3:10>,
1054 <spanset+ 3:10>,
1035 <baseset
1055 <baseset slice=0:3
1036 <limit n=3, offset=0,
1056 <spanset- 0:5>>>
1037 <spanset- 0:5>>>>
1038 3
1057 3
1039 4
1058 4
1040
1059
1041 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1060 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1042 * set:
1061 * set:
1043 <filteredset
1062 <filteredset
1044 <baseset
1063 <baseset slice=0:3
1045 <last n=3,
1064 <spanset+ 0:5>>,
1046 <spanset+ 0:5>>>,
1047 <spanset+ 0:2>>
1065 <spanset+ 0:2>>
1048 1
1066 1
1049 0
1067 0
1050
1068
1051 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1069 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1052 * set:
1070 * set:
1053 <filteredset
1071 <filteredset
1054 <spanset+ 0:2>,
1072 <spanset+ 0:2>,
1055 <baseset
1073 <baseset slice=0:3
1056 <last n=3,
1074 <spanset+ 0:5>>>
1057 <spanset+ 0:5>>>>
1058 0
1075 0
1059 1
1076 1
1060
1077
1061 Test matching
1078 Test matching
1062
1079
1063 $ log 'matching(6)'
1080 $ log 'matching(6)'
1064 6
1081 6
1065 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1082 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1066 6
1083 6
1067 7
1084 7
1068
1085
1069 Testing min and max
1086 Testing min and max
1070
1087
1071 max: simple
1088 max: simple
1072
1089
1073 $ log 'max(contains(a))'
1090 $ log 'max(contains(a))'
1074 5
1091 5
1075
1092
1076 max: simple on unordered set)
1093 max: simple on unordered set)
1077
1094
1078 $ log 'max((4+0+2+5+7) and contains(a))'
1095 $ log 'max((4+0+2+5+7) and contains(a))'
1079 5
1096 5
1080
1097
1081 max: no result
1098 max: no result
1082
1099
1083 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1100 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1084
1101
1085 max: no result on unordered set
1102 max: no result on unordered set
1086
1103
1087 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1104 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1088
1105
1089 min: simple
1106 min: simple
1090
1107
1091 $ log 'min(contains(a))'
1108 $ log 'min(contains(a))'
1092 0
1109 0
1093
1110
1094 min: simple on unordered set
1111 min: simple on unordered set
1095
1112
1096 $ log 'min((4+0+2+5+7) and contains(a))'
1113 $ log 'min((4+0+2+5+7) and contains(a))'
1097 0
1114 0
1098
1115
1099 min: empty
1116 min: empty
1100
1117
1101 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1118 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1102
1119
1103 min: empty on unordered set
1120 min: empty on unordered set
1104
1121
1105 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1122 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1106
1123
1107
1124
1108 $ log 'merge()'
1125 $ log 'merge()'
1109 6
1126 6
1110 $ log 'branchpoint()'
1127 $ log 'branchpoint()'
1111 1
1128 1
1112 4
1129 4
1113 $ log 'modifies(b)'
1130 $ log 'modifies(b)'
1114 4
1131 4
1115 $ log 'modifies("path:b")'
1132 $ log 'modifies("path:b")'
1116 4
1133 4
1117 $ log 'modifies("*")'
1134 $ log 'modifies("*")'
1118 4
1135 4
1119 6
1136 6
1120 $ log 'modifies("set:modified()")'
1137 $ log 'modifies("set:modified()")'
1121 4
1138 4
1122 $ log 'id(5)'
1139 $ log 'id(5)'
1123 2
1140 2
1124 $ log 'only(9)'
1141 $ log 'only(9)'
1125 8
1142 8
1126 9
1143 9
1127 $ log 'only(8)'
1144 $ log 'only(8)'
1128 8
1145 8
1129 $ log 'only(9, 5)'
1146 $ log 'only(9, 5)'
1130 2
1147 2
1131 4
1148 4
1132 8
1149 8
1133 9
1150 9
1134 $ log 'only(7 + 9, 5 + 2)'
1151 $ log 'only(7 + 9, 5 + 2)'
1135 4
1152 4
1136 6
1153 6
1137 7
1154 7
1138 8
1155 8
1139 9
1156 9
1140
1157
1141 Test empty set input
1158 Test empty set input
1142 $ log 'only(p2())'
1159 $ log 'only(p2())'
1143 $ log 'only(p1(), p2())'
1160 $ log 'only(p1(), p2())'
1144 0
1161 0
1145 1
1162 1
1146 2
1163 2
1147 4
1164 4
1148 8
1165 8
1149 9
1166 9
1150
1167
1151 Test '%' operator
1168 Test '%' operator
1152
1169
1153 $ log '9%'
1170 $ log '9%'
1154 8
1171 8
1155 9
1172 9
1156 $ log '9%5'
1173 $ log '9%5'
1157 2
1174 2
1158 4
1175 4
1159 8
1176 8
1160 9
1177 9
1161 $ log '(7 + 9)%(5 + 2)'
1178 $ log '(7 + 9)%(5 + 2)'
1162 4
1179 4
1163 6
1180 6
1164 7
1181 7
1165 8
1182 8
1166 9
1183 9
1167
1184
1168 Test operand of '%' is optimized recursively (issue4670)
1185 Test operand of '%' is optimized recursively (issue4670)
1169
1186
1170 $ try --optimize '8:9-8%'
1187 $ try --optimize '8:9-8%'
1171 (onlypost
1188 (onlypost
1172 (minus
1189 (minus
1173 (range
1190 (range
1174 ('symbol', '8')
1191 ('symbol', '8')
1175 ('symbol', '9'))
1192 ('symbol', '9'))
1176 ('symbol', '8')))
1193 ('symbol', '8')))
1177 * optimized:
1194 * optimized:
1178 (func
1195 (func
1179 ('symbol', 'only')
1196 ('symbol', 'only')
1180 (difference
1197 (difference
1181 (range
1198 (range
1182 ('symbol', '8')
1199 ('symbol', '8')
1183 ('symbol', '9')
1200 ('symbol', '9')
1184 define)
1201 define)
1185 ('symbol', '8')
1202 ('symbol', '8')
1186 define)
1203 define)
1187 define)
1204 define)
1188 * set:
1205 * set:
1189 <baseset+ [8, 9]>
1206 <baseset+ [8, 9]>
1190 8
1207 8
1191 9
1208 9
1192 $ try --optimize '(9)%(5)'
1209 $ try --optimize '(9)%(5)'
1193 (only
1210 (only
1194 (group
1211 (group
1195 ('symbol', '9'))
1212 ('symbol', '9'))
1196 (group
1213 (group
1197 ('symbol', '5')))
1214 ('symbol', '5')))
1198 * optimized:
1215 * optimized:
1199 (func
1216 (func
1200 ('symbol', 'only')
1217 ('symbol', 'only')
1201 (list
1218 (list
1202 ('symbol', '9')
1219 ('symbol', '9')
1203 ('symbol', '5'))
1220 ('symbol', '5'))
1204 define)
1221 define)
1205 * set:
1222 * set:
1206 <baseset+ [2, 4, 8, 9]>
1223 <baseset+ [2, 4, 8, 9]>
1207 2
1224 2
1208 4
1225 4
1209 8
1226 8
1210 9
1227 9
1211
1228
1212 Test the order of operations
1229 Test the order of operations
1213
1230
1214 $ log '7 + 9%5 + 2'
1231 $ log '7 + 9%5 + 2'
1215 7
1232 7
1216 2
1233 2
1217 4
1234 4
1218 8
1235 8
1219 9
1236 9
1220
1237
1221 Test explicit numeric revision
1238 Test explicit numeric revision
1222 $ log 'rev(-2)'
1239 $ log 'rev(-2)'
1223 $ log 'rev(-1)'
1240 $ log 'rev(-1)'
1224 -1
1241 -1
1225 $ log 'rev(0)'
1242 $ log 'rev(0)'
1226 0
1243 0
1227 $ log 'rev(9)'
1244 $ log 'rev(9)'
1228 9
1245 9
1229 $ log 'rev(10)'
1246 $ log 'rev(10)'
1230 $ log 'rev(tip)'
1247 $ log 'rev(tip)'
1231 hg: parse error: rev expects a number
1248 hg: parse error: rev expects a number
1232 [255]
1249 [255]
1233
1250
1234 Test hexadecimal revision
1251 Test hexadecimal revision
1235 $ log 'id(2)'
1252 $ log 'id(2)'
1236 abort: 00changelog.i@2: ambiguous identifier!
1253 abort: 00changelog.i@2: ambiguous identifier!
1237 [255]
1254 [255]
1238 $ log 'id(23268)'
1255 $ log 'id(23268)'
1239 4
1256 4
1240 $ log 'id(2785f51eece)'
1257 $ log 'id(2785f51eece)'
1241 0
1258 0
1242 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1259 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1243 8
1260 8
1244 $ log 'id(d5d0dcbdc4a)'
1261 $ log 'id(d5d0dcbdc4a)'
1245 $ log 'id(d5d0dcbdc4w)'
1262 $ log 'id(d5d0dcbdc4w)'
1246 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1263 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1247 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1264 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1248 $ log 'id(1.0)'
1265 $ log 'id(1.0)'
1249 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1266 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1250
1267
1251 Test null revision
1268 Test null revision
1252 $ log '(null)'
1269 $ log '(null)'
1253 -1
1270 -1
1254 $ log '(null:0)'
1271 $ log '(null:0)'
1255 -1
1272 -1
1256 0
1273 0
1257 $ log '(0:null)'
1274 $ log '(0:null)'
1258 0
1275 0
1259 -1
1276 -1
1260 $ log 'null::0'
1277 $ log 'null::0'
1261 -1
1278 -1
1262 0
1279 0
1263 $ log 'null:tip - 0:'
1280 $ log 'null:tip - 0:'
1264 -1
1281 -1
1265 $ log 'null: and null::' | head -1
1282 $ log 'null: and null::' | head -1
1266 -1
1283 -1
1267 $ log 'null: or 0:' | head -2
1284 $ log 'null: or 0:' | head -2
1268 -1
1285 -1
1269 0
1286 0
1270 $ log 'ancestors(null)'
1287 $ log 'ancestors(null)'
1271 -1
1288 -1
1272 $ log 'reverse(null:)' | tail -2
1289 $ log 'reverse(null:)' | tail -2
1273 0
1290 0
1274 -1
1291 -1
1275 $ log 'first(null:)'
1292 $ log 'first(null:)'
1276 -1
1293 -1
1277 $ log 'min(null:)'
1294 $ log 'min(null:)'
1278 BROKEN: should be '-1'
1295 BROKEN: should be '-1'
1279 $ log 'tip:null and all()' | tail -2
1296 $ log 'tip:null and all()' | tail -2
1280 1
1297 1
1281 0
1298 0
1282
1299
1283 Test working-directory revision
1300 Test working-directory revision
1284 $ hg debugrevspec 'wdir()'
1301 $ hg debugrevspec 'wdir()'
1285 2147483647
1302 2147483647
1286 $ hg debugrevspec 'wdir()^'
1303 $ hg debugrevspec 'wdir()^'
1287 9
1304 9
1288 $ hg up 7
1305 $ hg up 7
1289 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1306 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1290 $ hg debugrevspec 'wdir()^'
1307 $ hg debugrevspec 'wdir()^'
1291 7
1308 7
1292 $ hg debugrevspec 'wdir()^0'
1309 $ hg debugrevspec 'wdir()^0'
1293 2147483647
1310 2147483647
1294 $ hg debugrevspec 'wdir()~3'
1311 $ hg debugrevspec 'wdir()~3'
1295 5
1312 5
1296 $ hg debugrevspec 'ancestors(wdir())'
1313 $ hg debugrevspec 'ancestors(wdir())'
1297 0
1314 0
1298 1
1315 1
1299 2
1316 2
1300 3
1317 3
1301 4
1318 4
1302 5
1319 5
1303 6
1320 6
1304 7
1321 7
1305 2147483647
1322 2147483647
1306 $ hg debugrevspec 'wdir()~0'
1323 $ hg debugrevspec 'wdir()~0'
1307 2147483647
1324 2147483647
1308 $ hg debugrevspec 'p1(wdir())'
1325 $ hg debugrevspec 'p1(wdir())'
1309 7
1326 7
1310 $ hg debugrevspec 'p2(wdir())'
1327 $ hg debugrevspec 'p2(wdir())'
1311 $ hg debugrevspec 'parents(wdir())'
1328 $ hg debugrevspec 'parents(wdir())'
1312 7
1329 7
1313 $ hg debugrevspec 'wdir()^1'
1330 $ hg debugrevspec 'wdir()^1'
1314 7
1331 7
1315 $ hg debugrevspec 'wdir()^2'
1332 $ hg debugrevspec 'wdir()^2'
1316 $ hg debugrevspec 'wdir()^3'
1333 $ hg debugrevspec 'wdir()^3'
1317 hg: parse error: ^ expects a number 0, 1, or 2
1334 hg: parse error: ^ expects a number 0, 1, or 2
1318 [255]
1335 [255]
1319 For tests consistency
1336 For tests consistency
1320 $ hg up 9
1337 $ hg up 9
1321 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1338 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1322 $ hg debugrevspec 'tip or wdir()'
1339 $ hg debugrevspec 'tip or wdir()'
1323 9
1340 9
1324 2147483647
1341 2147483647
1325 $ hg debugrevspec '0:tip and wdir()'
1342 $ hg debugrevspec '0:tip and wdir()'
1326 $ log '0:wdir()' | tail -3
1343 $ log '0:wdir()' | tail -3
1327 8
1344 8
1328 9
1345 9
1329 2147483647
1346 2147483647
1330 $ log 'wdir():0' | head -3
1347 $ log 'wdir():0' | head -3
1331 2147483647
1348 2147483647
1332 9
1349 9
1333 8
1350 8
1334 $ log 'wdir():wdir()'
1351 $ log 'wdir():wdir()'
1335 2147483647
1352 2147483647
1336 $ log '(all() + wdir()) & min(. + wdir())'
1353 $ log '(all() + wdir()) & min(. + wdir())'
1337 9
1354 9
1338 $ log '(all() + wdir()) & max(. + wdir())'
1355 $ log '(all() + wdir()) & max(. + wdir())'
1339 2147483647
1356 2147483647
1340 $ log 'first(wdir() + .)'
1357 $ log 'first(wdir() + .)'
1341 2147483647
1358 2147483647
1342 $ log 'last(. + wdir())'
1359 $ log 'last(. + wdir())'
1343 2147483647
1360 2147483647
1344
1361
1345 Test working-directory integer revision and node id
1362 Test working-directory integer revision and node id
1346 (BUG: '0:wdir()' is still needed to populate wdir revision)
1363 (BUG: '0:wdir()' is still needed to populate wdir revision)
1347
1364
1348 $ hg debugrevspec '0:wdir() & 2147483647'
1365 $ hg debugrevspec '0:wdir() & 2147483647'
1349 2147483647
1366 2147483647
1350 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1367 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1351 2147483647
1368 2147483647
1352 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1369 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1353 2147483647
1370 2147483647
1354 $ hg debugrevspec '0:wdir() & ffffffffffff'
1371 $ hg debugrevspec '0:wdir() & ffffffffffff'
1355 2147483647
1372 2147483647
1356 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1373 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1357 2147483647
1374 2147483647
1358 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1375 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1359 2147483647
1376 2147483647
1360
1377
1361 $ cd ..
1378 $ cd ..
1362
1379
1363 Test short 'ff...' hash collision
1380 Test short 'ff...' hash collision
1364 (BUG: '0:wdir()' is still needed to populate wdir revision)
1381 (BUG: '0:wdir()' is still needed to populate wdir revision)
1365
1382
1366 $ hg init wdir-hashcollision
1383 $ hg init wdir-hashcollision
1367 $ cd wdir-hashcollision
1384 $ cd wdir-hashcollision
1368 $ cat <<EOF >> .hg/hgrc
1385 $ cat <<EOF >> .hg/hgrc
1369 > [experimental]
1386 > [experimental]
1370 > evolution = createmarkers
1387 > evolution = createmarkers
1371 > EOF
1388 > EOF
1372 $ echo 0 > a
1389 $ echo 0 > a
1373 $ hg ci -qAm 0
1390 $ hg ci -qAm 0
1374 $ for i in 2463 2961 6726 78127; do
1391 $ for i in 2463 2961 6726 78127; do
1375 > hg up -q 0
1392 > hg up -q 0
1376 > echo $i > a
1393 > echo $i > a
1377 > hg ci -qm $i
1394 > hg ci -qm $i
1378 > done
1395 > done
1379 $ hg up -q null
1396 $ hg up -q null
1380 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1397 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1381 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1398 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1382 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1399 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1383 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1400 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1384 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1401 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1385 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1402 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1386 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1403 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1387 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1404 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1388
1405
1389 $ hg debugrevspec '0:wdir() & fff'
1406 $ hg debugrevspec '0:wdir() & fff'
1390 abort: 00changelog.i@fff: ambiguous identifier!
1407 abort: 00changelog.i@fff: ambiguous identifier!
1391 [255]
1408 [255]
1392 $ hg debugrevspec '0:wdir() & ffff'
1409 $ hg debugrevspec '0:wdir() & ffff'
1393 abort: 00changelog.i@ffff: ambiguous identifier!
1410 abort: 00changelog.i@ffff: ambiguous identifier!
1394 [255]
1411 [255]
1395 $ hg debugrevspec '0:wdir() & fffb'
1412 $ hg debugrevspec '0:wdir() & fffb'
1396 abort: 00changelog.i@fffb: ambiguous identifier!
1413 abort: 00changelog.i@fffb: ambiguous identifier!
1397 [255]
1414 [255]
1398 BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8)
1415 BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8)
1399 $ hg debugrevspec '0:wdir() & id(fffb)'
1416 $ hg debugrevspec '0:wdir() & id(fffb)'
1400 2
1417 2
1401 $ hg debugrevspec '0:wdir() & ffff8'
1418 $ hg debugrevspec '0:wdir() & ffff8'
1402 4
1419 4
1403 $ hg debugrevspec '0:wdir() & fffff'
1420 $ hg debugrevspec '0:wdir() & fffff'
1404 2147483647
1421 2147483647
1405
1422
1406 $ cd ..
1423 $ cd ..
1407
1424
1408 Test branch() with wdir()
1425 Test branch() with wdir()
1409
1426
1410 $ cd repo
1427 $ cd repo
1411
1428
1412 $ log '0:wdir() & branch("literal:Γ©")'
1429 $ log '0:wdir() & branch("literal:Γ©")'
1413 8
1430 8
1414 9
1431 9
1415 2147483647
1432 2147483647
1416 $ log '0:wdir() & branch("re:Γ©")'
1433 $ log '0:wdir() & branch("re:Γ©")'
1417 8
1434 8
1418 9
1435 9
1419 2147483647
1436 2147483647
1420 $ log '0:wdir() & branch("re:^a")'
1437 $ log '0:wdir() & branch("re:^a")'
1421 0
1438 0
1422 2
1439 2
1423 $ log '0:wdir() & branch(8)'
1440 $ log '0:wdir() & branch(8)'
1424 8
1441 8
1425 9
1442 9
1426 2147483647
1443 2147483647
1427
1444
1428 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1445 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1429 itself isn't returned unless it is explicitly populated.
1446 itself isn't returned unless it is explicitly populated.
1430
1447
1431 $ log 'branch(wdir())'
1448 $ log 'branch(wdir())'
1432 8
1449 8
1433 9
1450 9
1434 $ log '0:wdir() & branch(wdir())'
1451 $ log '0:wdir() & branch(wdir())'
1435 8
1452 8
1436 9
1453 9
1437 2147483647
1454 2147483647
1438
1455
1439 $ log 'outgoing()'
1456 $ log 'outgoing()'
1440 8
1457 8
1441 9
1458 9
1442 $ log 'outgoing("../remote1")'
1459 $ log 'outgoing("../remote1")'
1443 8
1460 8
1444 9
1461 9
1445 $ log 'outgoing("../remote2")'
1462 $ log 'outgoing("../remote2")'
1446 3
1463 3
1447 5
1464 5
1448 6
1465 6
1449 7
1466 7
1450 9
1467 9
1451 $ log 'p1(merge())'
1468 $ log 'p1(merge())'
1452 5
1469 5
1453 $ log 'p2(merge())'
1470 $ log 'p2(merge())'
1454 4
1471 4
1455 $ log 'parents(merge())'
1472 $ log 'parents(merge())'
1456 4
1473 4
1457 5
1474 5
1458 $ log 'p1(branchpoint())'
1475 $ log 'p1(branchpoint())'
1459 0
1476 0
1460 2
1477 2
1461 $ log 'p2(branchpoint())'
1478 $ log 'p2(branchpoint())'
1462 $ log 'parents(branchpoint())'
1479 $ log 'parents(branchpoint())'
1463 0
1480 0
1464 2
1481 2
1465 $ log 'removes(a)'
1482 $ log 'removes(a)'
1466 2
1483 2
1467 6
1484 6
1468 $ log 'roots(all())'
1485 $ log 'roots(all())'
1469 0
1486 0
1470 $ log 'reverse(2 or 3 or 4 or 5)'
1487 $ log 'reverse(2 or 3 or 4 or 5)'
1471 5
1488 5
1472 4
1489 4
1473 3
1490 3
1474 2
1491 2
1475 $ log 'reverse(all())'
1492 $ log 'reverse(all())'
1476 9
1493 9
1477 8
1494 8
1478 7
1495 7
1479 6
1496 6
1480 5
1497 5
1481 4
1498 4
1482 3
1499 3
1483 2
1500 2
1484 1
1501 1
1485 0
1502 0
1486 $ log 'reverse(all()) & filelog(b)'
1503 $ log 'reverse(all()) & filelog(b)'
1487 4
1504 4
1488 1
1505 1
1489 $ log 'rev(5)'
1506 $ log 'rev(5)'
1490 5
1507 5
1491 $ log 'sort(limit(reverse(all()), 3))'
1508 $ log 'sort(limit(reverse(all()), 3))'
1492 7
1509 7
1493 8
1510 8
1494 9
1511 9
1495 $ log 'sort(2 or 3 or 4 or 5, date)'
1512 $ log 'sort(2 or 3 or 4 or 5, date)'
1496 2
1513 2
1497 3
1514 3
1498 5
1515 5
1499 4
1516 4
1500 $ log 'tagged()'
1517 $ log 'tagged()'
1501 6
1518 6
1502 $ log 'tag()'
1519 $ log 'tag()'
1503 6
1520 6
1504 $ log 'tag(1.0)'
1521 $ log 'tag(1.0)'
1505 6
1522 6
1506 $ log 'tag(tip)'
1523 $ log 'tag(tip)'
1507 9
1524 9
1508
1525
1509 Test order of revisions in compound expression
1526 Test order of revisions in compound expression
1510 ----------------------------------------------
1527 ----------------------------------------------
1511
1528
1512 The general rule is that only the outermost (= leftmost) predicate can
1529 The general rule is that only the outermost (= leftmost) predicate can
1513 enforce its ordering requirement. The other predicates should take the
1530 enforce its ordering requirement. The other predicates should take the
1514 ordering defined by it.
1531 ordering defined by it.
1515
1532
1516 'A & B' should follow the order of 'A':
1533 'A & B' should follow the order of 'A':
1517
1534
1518 $ log '2:0 & 0::2'
1535 $ log '2:0 & 0::2'
1519 2
1536 2
1520 1
1537 1
1521 0
1538 0
1522
1539
1523 'head()' combines sets in right order:
1540 'head()' combines sets in right order:
1524
1541
1525 $ log '2:0 & head()'
1542 $ log '2:0 & head()'
1526 2
1543 2
1527 1
1544 1
1528 0
1545 0
1529
1546
1530 'x:y' takes ordering parameter into account:
1547 'x:y' takes ordering parameter into account:
1531
1548
1532 $ try -p optimized '3:0 & 0:3 & not 2:1'
1549 $ try -p optimized '3:0 & 0:3 & not 2:1'
1533 * optimized:
1550 * optimized:
1534 (difference
1551 (difference
1535 (and
1552 (and
1536 (range
1553 (range
1537 ('symbol', '3')
1554 ('symbol', '3')
1538 ('symbol', '0')
1555 ('symbol', '0')
1539 define)
1556 define)
1540 (range
1557 (range
1541 ('symbol', '0')
1558 ('symbol', '0')
1542 ('symbol', '3')
1559 ('symbol', '3')
1543 follow)
1560 follow)
1544 define)
1561 define)
1545 (range
1562 (range
1546 ('symbol', '2')
1563 ('symbol', '2')
1547 ('symbol', '1')
1564 ('symbol', '1')
1548 any)
1565 any)
1549 define)
1566 define)
1550 * set:
1567 * set:
1551 <filteredset
1568 <filteredset
1552 <filteredset
1569 <filteredset
1553 <spanset- 0:4>,
1570 <spanset- 0:4>,
1554 <spanset+ 0:4>>,
1571 <spanset+ 0:4>>,
1555 <not
1572 <not
1556 <spanset+ 1:3>>>
1573 <spanset+ 1:3>>>
1557 3
1574 3
1558 0
1575 0
1559
1576
1560 'a + b', which is optimized to '_list(a b)', should take the ordering of
1577 'a + b', which is optimized to '_list(a b)', should take the ordering of
1561 the left expression:
1578 the left expression:
1562
1579
1563 $ try --optimize '2:0 & (0 + 1 + 2)'
1580 $ try --optimize '2:0 & (0 + 1 + 2)'
1564 (and
1581 (and
1565 (range
1582 (range
1566 ('symbol', '2')
1583 ('symbol', '2')
1567 ('symbol', '0'))
1584 ('symbol', '0'))
1568 (group
1585 (group
1569 (or
1586 (or
1570 (list
1587 (list
1571 ('symbol', '0')
1588 ('symbol', '0')
1572 ('symbol', '1')
1589 ('symbol', '1')
1573 ('symbol', '2')))))
1590 ('symbol', '2')))))
1574 * optimized:
1591 * optimized:
1575 (and
1592 (and
1576 (range
1593 (range
1577 ('symbol', '2')
1594 ('symbol', '2')
1578 ('symbol', '0')
1595 ('symbol', '0')
1579 define)
1596 define)
1580 (func
1597 (func
1581 ('symbol', '_list')
1598 ('symbol', '_list')
1582 ('string', '0\x001\x002')
1599 ('string', '0\x001\x002')
1583 follow)
1600 follow)
1584 define)
1601 define)
1585 * set:
1602 * set:
1586 <filteredset
1603 <filteredset
1587 <spanset- 0:3>,
1604 <spanset- 0:3>,
1588 <baseset [0, 1, 2]>>
1605 <baseset [0, 1, 2]>>
1589 2
1606 2
1590 1
1607 1
1591 0
1608 0
1592
1609
1593 'A + B' should take the ordering of the left expression:
1610 'A + B' should take the ordering of the left expression:
1594
1611
1595 $ try --optimize '2:0 & (0:1 + 2)'
1612 $ try --optimize '2:0 & (0:1 + 2)'
1596 (and
1613 (and
1597 (range
1614 (range
1598 ('symbol', '2')
1615 ('symbol', '2')
1599 ('symbol', '0'))
1616 ('symbol', '0'))
1600 (group
1617 (group
1601 (or
1618 (or
1602 (list
1619 (list
1603 (range
1620 (range
1604 ('symbol', '0')
1621 ('symbol', '0')
1605 ('symbol', '1'))
1622 ('symbol', '1'))
1606 ('symbol', '2')))))
1623 ('symbol', '2')))))
1607 * optimized:
1624 * optimized:
1608 (and
1625 (and
1609 (range
1626 (range
1610 ('symbol', '2')
1627 ('symbol', '2')
1611 ('symbol', '0')
1628 ('symbol', '0')
1612 define)
1629 define)
1613 (or
1630 (or
1614 (list
1631 (list
1615 ('symbol', '2')
1632 ('symbol', '2')
1616 (range
1633 (range
1617 ('symbol', '0')
1634 ('symbol', '0')
1618 ('symbol', '1')
1635 ('symbol', '1')
1619 follow))
1636 follow))
1620 follow)
1637 follow)
1621 define)
1638 define)
1622 * set:
1639 * set:
1623 <filteredset
1640 <filteredset
1624 <spanset- 0:3>,
1641 <spanset- 0:3>,
1625 <addset
1642 <addset
1626 <baseset [2]>,
1643 <baseset [2]>,
1627 <spanset+ 0:2>>>
1644 <spanset+ 0:2>>>
1628 2
1645 2
1629 1
1646 1
1630 0
1647 0
1631
1648
1632 '_intlist(a b)' should behave like 'a + b':
1649 '_intlist(a b)' should behave like 'a + b':
1633
1650
1634 $ trylist --optimize '2:0 & %ld' 0 1 2
1651 $ trylist --optimize '2:0 & %ld' 0 1 2
1635 (and
1652 (and
1636 (range
1653 (range
1637 ('symbol', '2')
1654 ('symbol', '2')
1638 ('symbol', '0'))
1655 ('symbol', '0'))
1639 (func
1656 (func
1640 ('symbol', '_intlist')
1657 ('symbol', '_intlist')
1641 ('string', '0\x001\x002')))
1658 ('string', '0\x001\x002')))
1642 * optimized:
1659 * optimized:
1643 (and
1660 (and
1644 (func
1661 (func
1645 ('symbol', '_intlist')
1662 ('symbol', '_intlist')
1646 ('string', '0\x001\x002')
1663 ('string', '0\x001\x002')
1647 follow)
1664 follow)
1648 (range
1665 (range
1649 ('symbol', '2')
1666 ('symbol', '2')
1650 ('symbol', '0')
1667 ('symbol', '0')
1651 define)
1668 define)
1652 define)
1669 define)
1653 * set:
1670 * set:
1654 <filteredset
1671 <filteredset
1655 <spanset- 0:3>,
1672 <spanset- 0:3>,
1656 <baseset+ [0, 1, 2]>>
1673 <baseset+ [0, 1, 2]>>
1657 2
1674 2
1658 1
1675 1
1659 0
1676 0
1660
1677
1661 $ trylist --optimize '%ld & 2:0' 0 2 1
1678 $ trylist --optimize '%ld & 2:0' 0 2 1
1662 (and
1679 (and
1663 (func
1680 (func
1664 ('symbol', '_intlist')
1681 ('symbol', '_intlist')
1665 ('string', '0\x002\x001'))
1682 ('string', '0\x002\x001'))
1666 (range
1683 (range
1667 ('symbol', '2')
1684 ('symbol', '2')
1668 ('symbol', '0')))
1685 ('symbol', '0')))
1669 * optimized:
1686 * optimized:
1670 (and
1687 (and
1671 (func
1688 (func
1672 ('symbol', '_intlist')
1689 ('symbol', '_intlist')
1673 ('string', '0\x002\x001')
1690 ('string', '0\x002\x001')
1674 define)
1691 define)
1675 (range
1692 (range
1676 ('symbol', '2')
1693 ('symbol', '2')
1677 ('symbol', '0')
1694 ('symbol', '0')
1678 follow)
1695 follow)
1679 define)
1696 define)
1680 * set:
1697 * set:
1681 <filteredset
1698 <filteredset
1682 <baseset [0, 2, 1]>,
1699 <baseset [0, 2, 1]>,
1683 <spanset- 0:3>>
1700 <spanset- 0:3>>
1684 0
1701 0
1685 2
1702 2
1686 1
1703 1
1687
1704
1688 '_hexlist(a b)' should behave like 'a + b':
1705 '_hexlist(a b)' should behave like 'a + b':
1689
1706
1690 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1707 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1691 (and
1708 (and
1692 (range
1709 (range
1693 ('symbol', '2')
1710 ('symbol', '2')
1694 ('symbol', '0'))
1711 ('symbol', '0'))
1695 (func
1712 (func
1696 ('symbol', '_hexlist')
1713 ('symbol', '_hexlist')
1697 ('string', '*'))) (glob)
1714 ('string', '*'))) (glob)
1698 * optimized:
1715 * optimized:
1699 (and
1716 (and
1700 (range
1717 (range
1701 ('symbol', '2')
1718 ('symbol', '2')
1702 ('symbol', '0')
1719 ('symbol', '0')
1703 define)
1720 define)
1704 (func
1721 (func
1705 ('symbol', '_hexlist')
1722 ('symbol', '_hexlist')
1706 ('string', '*') (glob)
1723 ('string', '*') (glob)
1707 follow)
1724 follow)
1708 define)
1725 define)
1709 * set:
1726 * set:
1710 <filteredset
1727 <filteredset
1711 <spanset- 0:3>,
1728 <spanset- 0:3>,
1712 <baseset [0, 1, 2]>>
1729 <baseset [0, 1, 2]>>
1713 2
1730 2
1714 1
1731 1
1715 0
1732 0
1716
1733
1717 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1734 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1718 (and
1735 (and
1719 (func
1736 (func
1720 ('symbol', '_hexlist')
1737 ('symbol', '_hexlist')
1721 ('string', '*')) (glob)
1738 ('string', '*')) (glob)
1722 (range
1739 (range
1723 ('symbol', '2')
1740 ('symbol', '2')
1724 ('symbol', '0')))
1741 ('symbol', '0')))
1725 * optimized:
1742 * optimized:
1726 (and
1743 (and
1727 (range
1744 (range
1728 ('symbol', '2')
1745 ('symbol', '2')
1729 ('symbol', '0')
1746 ('symbol', '0')
1730 follow)
1747 follow)
1731 (func
1748 (func
1732 ('symbol', '_hexlist')
1749 ('symbol', '_hexlist')
1733 ('string', '*') (glob)
1750 ('string', '*') (glob)
1734 define)
1751 define)
1735 define)
1752 define)
1736 * set:
1753 * set:
1737 <baseset [0, 2, 1]>
1754 <baseset [0, 2, 1]>
1738 0
1755 0
1739 2
1756 2
1740 1
1757 1
1741
1758
1742 '_list' should not go through the slow follow-order path if order doesn't
1759 '_list' should not go through the slow follow-order path if order doesn't
1743 matter:
1760 matter:
1744
1761
1745 $ try -p optimized '2:0 & not (0 + 1)'
1762 $ try -p optimized '2:0 & not (0 + 1)'
1746 * optimized:
1763 * optimized:
1747 (difference
1764 (difference
1748 (range
1765 (range
1749 ('symbol', '2')
1766 ('symbol', '2')
1750 ('symbol', '0')
1767 ('symbol', '0')
1751 define)
1768 define)
1752 (func
1769 (func
1753 ('symbol', '_list')
1770 ('symbol', '_list')
1754 ('string', '0\x001')
1771 ('string', '0\x001')
1755 any)
1772 any)
1756 define)
1773 define)
1757 * set:
1774 * set:
1758 <filteredset
1775 <filteredset
1759 <spanset- 0:3>,
1776 <spanset- 0:3>,
1760 <not
1777 <not
1761 <baseset [0, 1]>>>
1778 <baseset [0, 1]>>>
1762 2
1779 2
1763
1780
1764 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1781 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1765 * optimized:
1782 * optimized:
1766 (difference
1783 (difference
1767 (range
1784 (range
1768 ('symbol', '2')
1785 ('symbol', '2')
1769 ('symbol', '0')
1786 ('symbol', '0')
1770 define)
1787 define)
1771 (and
1788 (and
1772 (range
1789 (range
1773 ('symbol', '0')
1790 ('symbol', '0')
1774 ('symbol', '2')
1791 ('symbol', '2')
1775 any)
1792 any)
1776 (func
1793 (func
1777 ('symbol', '_list')
1794 ('symbol', '_list')
1778 ('string', '0\x001')
1795 ('string', '0\x001')
1779 any)
1796 any)
1780 any)
1797 any)
1781 define)
1798 define)
1782 * set:
1799 * set:
1783 <filteredset
1800 <filteredset
1784 <spanset- 0:3>,
1801 <spanset- 0:3>,
1785 <not
1802 <not
1786 <baseset [0, 1]>>>
1803 <baseset [0, 1]>>>
1787 2
1804 2
1788
1805
1789 because 'present()' does nothing other than suppressing an error, the
1806 because 'present()' does nothing other than suppressing an error, the
1790 ordering requirement should be forwarded to the nested expression
1807 ordering requirement should be forwarded to the nested expression
1791
1808
1792 $ try -p optimized 'present(2 + 0 + 1)'
1809 $ try -p optimized 'present(2 + 0 + 1)'
1793 * optimized:
1810 * optimized:
1794 (func
1811 (func
1795 ('symbol', 'present')
1812 ('symbol', 'present')
1796 (func
1813 (func
1797 ('symbol', '_list')
1814 ('symbol', '_list')
1798 ('string', '2\x000\x001')
1815 ('string', '2\x000\x001')
1799 define)
1816 define)
1800 define)
1817 define)
1801 * set:
1818 * set:
1802 <baseset [2, 0, 1]>
1819 <baseset [2, 0, 1]>
1803 2
1820 2
1804 0
1821 0
1805 1
1822 1
1806
1823
1807 $ try --optimize '2:0 & present(0 + 1 + 2)'
1824 $ try --optimize '2:0 & present(0 + 1 + 2)'
1808 (and
1825 (and
1809 (range
1826 (range
1810 ('symbol', '2')
1827 ('symbol', '2')
1811 ('symbol', '0'))
1828 ('symbol', '0'))
1812 (func
1829 (func
1813 ('symbol', 'present')
1830 ('symbol', 'present')
1814 (or
1831 (or
1815 (list
1832 (list
1816 ('symbol', '0')
1833 ('symbol', '0')
1817 ('symbol', '1')
1834 ('symbol', '1')
1818 ('symbol', '2')))))
1835 ('symbol', '2')))))
1819 * optimized:
1836 * optimized:
1820 (and
1837 (and
1821 (range
1838 (range
1822 ('symbol', '2')
1839 ('symbol', '2')
1823 ('symbol', '0')
1840 ('symbol', '0')
1824 define)
1841 define)
1825 (func
1842 (func
1826 ('symbol', 'present')
1843 ('symbol', 'present')
1827 (func
1844 (func
1828 ('symbol', '_list')
1845 ('symbol', '_list')
1829 ('string', '0\x001\x002')
1846 ('string', '0\x001\x002')
1830 follow)
1847 follow)
1831 follow)
1848 follow)
1832 define)
1849 define)
1833 * set:
1850 * set:
1834 <filteredset
1851 <filteredset
1835 <spanset- 0:3>,
1852 <spanset- 0:3>,
1836 <baseset [0, 1, 2]>>
1853 <baseset [0, 1, 2]>>
1837 2
1854 2
1838 1
1855 1
1839 0
1856 0
1840
1857
1841 'reverse()' should take effect only if it is the outermost expression:
1858 'reverse()' should take effect only if it is the outermost expression:
1842
1859
1843 $ try --optimize '0:2 & reverse(all())'
1860 $ try --optimize '0:2 & reverse(all())'
1844 (and
1861 (and
1845 (range
1862 (range
1846 ('symbol', '0')
1863 ('symbol', '0')
1847 ('symbol', '2'))
1864 ('symbol', '2'))
1848 (func
1865 (func
1849 ('symbol', 'reverse')
1866 ('symbol', 'reverse')
1850 (func
1867 (func
1851 ('symbol', 'all')
1868 ('symbol', 'all')
1852 None)))
1869 None)))
1853 * optimized:
1870 * optimized:
1854 (and
1871 (and
1855 (range
1872 (range
1856 ('symbol', '0')
1873 ('symbol', '0')
1857 ('symbol', '2')
1874 ('symbol', '2')
1858 define)
1875 define)
1859 (func
1876 (func
1860 ('symbol', 'reverse')
1877 ('symbol', 'reverse')
1861 (func
1878 (func
1862 ('symbol', 'all')
1879 ('symbol', 'all')
1863 None
1880 None
1864 define)
1881 define)
1865 follow)
1882 follow)
1866 define)
1883 define)
1867 * set:
1884 * set:
1868 <filteredset
1885 <filteredset
1869 <spanset+ 0:3>,
1886 <spanset+ 0:3>,
1870 <spanset+ 0:10>>
1887 <spanset+ 0:10>>
1871 0
1888 0
1872 1
1889 1
1873 2
1890 2
1874
1891
1875 'sort()' should take effect only if it is the outermost expression:
1892 'sort()' should take effect only if it is the outermost expression:
1876
1893
1877 $ try --optimize '0:2 & sort(all(), -rev)'
1894 $ try --optimize '0:2 & sort(all(), -rev)'
1878 (and
1895 (and
1879 (range
1896 (range
1880 ('symbol', '0')
1897 ('symbol', '0')
1881 ('symbol', '2'))
1898 ('symbol', '2'))
1882 (func
1899 (func
1883 ('symbol', 'sort')
1900 ('symbol', 'sort')
1884 (list
1901 (list
1885 (func
1902 (func
1886 ('symbol', 'all')
1903 ('symbol', 'all')
1887 None)
1904 None)
1888 (negate
1905 (negate
1889 ('symbol', 'rev')))))
1906 ('symbol', 'rev')))))
1890 * optimized:
1907 * optimized:
1891 (and
1908 (and
1892 (range
1909 (range
1893 ('symbol', '0')
1910 ('symbol', '0')
1894 ('symbol', '2')
1911 ('symbol', '2')
1895 define)
1912 define)
1896 (func
1913 (func
1897 ('symbol', 'sort')
1914 ('symbol', 'sort')
1898 (list
1915 (list
1899 (func
1916 (func
1900 ('symbol', 'all')
1917 ('symbol', 'all')
1901 None
1918 None
1902 define)
1919 define)
1903 ('string', '-rev'))
1920 ('string', '-rev'))
1904 follow)
1921 follow)
1905 define)
1922 define)
1906 * set:
1923 * set:
1907 <filteredset
1924 <filteredset
1908 <spanset+ 0:3>,
1925 <spanset+ 0:3>,
1909 <spanset+ 0:10>>
1926 <spanset+ 0:10>>
1910 0
1927 0
1911 1
1928 1
1912 2
1929 2
1913
1930
1914 invalid argument passed to noop sort():
1931 invalid argument passed to noop sort():
1915
1932
1916 $ log '0:2 & sort()'
1933 $ log '0:2 & sort()'
1917 hg: parse error: sort requires one or two arguments
1934 hg: parse error: sort requires one or two arguments
1918 [255]
1935 [255]
1919 $ log '0:2 & sort(all(), -invalid)'
1936 $ log '0:2 & sort(all(), -invalid)'
1920 hg: parse error: unknown sort key '-invalid'
1937 hg: parse error: unknown sort key '-invalid'
1921 [255]
1938 [255]
1922
1939
1923 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1940 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1924
1941
1925 $ try --optimize '2:0 & first(1 + 0 + 2)'
1942 $ try --optimize '2:0 & first(1 + 0 + 2)'
1926 (and
1943 (and
1927 (range
1944 (range
1928 ('symbol', '2')
1945 ('symbol', '2')
1929 ('symbol', '0'))
1946 ('symbol', '0'))
1930 (func
1947 (func
1931 ('symbol', 'first')
1948 ('symbol', 'first')
1932 (or
1949 (or
1933 (list
1950 (list
1934 ('symbol', '1')
1951 ('symbol', '1')
1935 ('symbol', '0')
1952 ('symbol', '0')
1936 ('symbol', '2')))))
1953 ('symbol', '2')))))
1937 * optimized:
1954 * optimized:
1938 (and
1955 (and
1939 (range
1956 (range
1940 ('symbol', '2')
1957 ('symbol', '2')
1941 ('symbol', '0')
1958 ('symbol', '0')
1942 define)
1959 define)
1943 (func
1960 (func
1944 ('symbol', 'first')
1961 ('symbol', 'first')
1945 (func
1962 (func
1946 ('symbol', '_list')
1963 ('symbol', '_list')
1947 ('string', '1\x000\x002')
1964 ('string', '1\x000\x002')
1948 define)
1965 define)
1949 follow)
1966 follow)
1950 define)
1967 define)
1951 * set:
1968 * set:
1952 <filteredset
1969 <filteredset
1953 <baseset
1970 <baseset slice=0:1
1954 <limit n=1, offset=0,
1971 <baseset [1, 0, 2]>>,
1955 <baseset [1, 0, 2]>>>,
1956 <spanset- 0:3>>
1972 <spanset- 0:3>>
1957 1
1973 1
1958
1974
1959 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1975 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1960 (and
1976 (and
1961 (range
1977 (range
1962 ('symbol', '2')
1978 ('symbol', '2')
1963 ('symbol', '0'))
1979 ('symbol', '0'))
1964 (not
1980 (not
1965 (func
1981 (func
1966 ('symbol', 'last')
1982 ('symbol', 'last')
1967 (or
1983 (or
1968 (list
1984 (list
1969 ('symbol', '0')
1985 ('symbol', '0')
1970 ('symbol', '2')
1986 ('symbol', '2')
1971 ('symbol', '1'))))))
1987 ('symbol', '1'))))))
1972 * optimized:
1988 * optimized:
1973 (difference
1989 (difference
1974 (range
1990 (range
1975 ('symbol', '2')
1991 ('symbol', '2')
1976 ('symbol', '0')
1992 ('symbol', '0')
1977 define)
1993 define)
1978 (func
1994 (func
1979 ('symbol', 'last')
1995 ('symbol', 'last')
1980 (func
1996 (func
1981 ('symbol', '_list')
1997 ('symbol', '_list')
1982 ('string', '0\x002\x001')
1998 ('string', '0\x002\x001')
1983 define)
1999 define)
1984 any)
2000 any)
1985 define)
2001 define)
1986 * set:
2002 * set:
1987 <filteredset
2003 <filteredset
1988 <spanset- 0:3>,
2004 <spanset- 0:3>,
1989 <not
2005 <not
1990 <baseset
2006 <baseset slice=0:1
1991 <last n=1,
2007 <baseset [1, 2, 0]>>>>
1992 <baseset [1, 2, 0]>>>>>
1993 2
2008 2
1994 0
2009 0
1995
2010
1996 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2011 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1997
2012
1998 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2013 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1999 (and
2014 (and
2000 (range
2015 (range
2001 ('symbol', '2')
2016 ('symbol', '2')
2002 ('symbol', '0'))
2017 ('symbol', '0'))
2003 (range
2018 (range
2004 (group
2019 (group
2005 (or
2020 (or
2006 (list
2021 (list
2007 ('symbol', '1')
2022 ('symbol', '1')
2008 ('symbol', '0')
2023 ('symbol', '0')
2009 ('symbol', '2'))))
2024 ('symbol', '2'))))
2010 (group
2025 (group
2011 (or
2026 (or
2012 (list
2027 (list
2013 ('symbol', '0')
2028 ('symbol', '0')
2014 ('symbol', '2')
2029 ('symbol', '2')
2015 ('symbol', '1'))))))
2030 ('symbol', '1'))))))
2016 * optimized:
2031 * optimized:
2017 (and
2032 (and
2018 (range
2033 (range
2019 ('symbol', '2')
2034 ('symbol', '2')
2020 ('symbol', '0')
2035 ('symbol', '0')
2021 define)
2036 define)
2022 (range
2037 (range
2023 (func
2038 (func
2024 ('symbol', '_list')
2039 ('symbol', '_list')
2025 ('string', '1\x000\x002')
2040 ('string', '1\x000\x002')
2026 define)
2041 define)
2027 (func
2042 (func
2028 ('symbol', '_list')
2043 ('symbol', '_list')
2029 ('string', '0\x002\x001')
2044 ('string', '0\x002\x001')
2030 define)
2045 define)
2031 follow)
2046 follow)
2032 define)
2047 define)
2033 * set:
2048 * set:
2034 <filteredset
2049 <filteredset
2035 <spanset- 0:3>,
2050 <spanset- 0:3>,
2036 <baseset [1]>>
2051 <baseset [1]>>
2037 1
2052 1
2038
2053
2039 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
2054 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
2040 the ordering rule is determined before the rewrite; in this example,
2055 the ordering rule is determined before the rewrite; in this example,
2041 'B' follows the order of the initial set, which is the same order as 'A'
2056 'B' follows the order of the initial set, which is the same order as 'A'
2042 since 'A' also follows the order:
2057 since 'A' also follows the order:
2043
2058
2044 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2059 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2045 (and
2060 (and
2046 (func
2061 (func
2047 ('symbol', 'contains')
2062 ('symbol', 'contains')
2048 ('string', 'glob:*'))
2063 ('string', 'glob:*'))
2049 (group
2064 (group
2050 (or
2065 (or
2051 (list
2066 (list
2052 ('symbol', '2')
2067 ('symbol', '2')
2053 ('symbol', '0')
2068 ('symbol', '0')
2054 ('symbol', '1')))))
2069 ('symbol', '1')))))
2055 * optimized:
2070 * optimized:
2056 (and
2071 (and
2057 (func
2072 (func
2058 ('symbol', '_list')
2073 ('symbol', '_list')
2059 ('string', '2\x000\x001')
2074 ('string', '2\x000\x001')
2060 follow)
2075 follow)
2061 (func
2076 (func
2062 ('symbol', 'contains')
2077 ('symbol', 'contains')
2063 ('string', 'glob:*')
2078 ('string', 'glob:*')
2064 define)
2079 define)
2065 define)
2080 define)
2066 * set:
2081 * set:
2067 <filteredset
2082 <filteredset
2068 <baseset+ [0, 1, 2]>,
2083 <baseset+ [0, 1, 2]>,
2069 <contains 'glob:*'>>
2084 <contains 'glob:*'>>
2070 0
2085 0
2071 1
2086 1
2072 2
2087 2
2073
2088
2074 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2089 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2075 the order appropriately:
2090 the order appropriately:
2076
2091
2077 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2092 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2078 (and
2093 (and
2079 (func
2094 (func
2080 ('symbol', 'reverse')
2095 ('symbol', 'reverse')
2081 (func
2096 (func
2082 ('symbol', 'contains')
2097 ('symbol', 'contains')
2083 ('string', 'glob:*')))
2098 ('string', 'glob:*')))
2084 (group
2099 (group
2085 (or
2100 (or
2086 (list
2101 (list
2087 ('symbol', '0')
2102 ('symbol', '0')
2088 ('symbol', '2')
2103 ('symbol', '2')
2089 ('symbol', '1')))))
2104 ('symbol', '1')))))
2090 * optimized:
2105 * optimized:
2091 (and
2106 (and
2092 (func
2107 (func
2093 ('symbol', '_list')
2108 ('symbol', '_list')
2094 ('string', '0\x002\x001')
2109 ('string', '0\x002\x001')
2095 follow)
2110 follow)
2096 (func
2111 (func
2097 ('symbol', 'reverse')
2112 ('symbol', 'reverse')
2098 (func
2113 (func
2099 ('symbol', 'contains')
2114 ('symbol', 'contains')
2100 ('string', 'glob:*')
2115 ('string', 'glob:*')
2101 define)
2116 define)
2102 define)
2117 define)
2103 define)
2118 define)
2104 * set:
2119 * set:
2105 <filteredset
2120 <filteredset
2106 <baseset- [0, 1, 2]>,
2121 <baseset- [0, 1, 2]>,
2107 <contains 'glob:*'>>
2122 <contains 'glob:*'>>
2108 2
2123 2
2109 1
2124 1
2110 0
2125 0
2111
2126
2112 'A + B' can be rewritten to 'B + A' by weight only when the order doesn't
2127 'A + B' can be rewritten to 'B + A' by weight only when the order doesn't
2113 matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):
2128 matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):
2114
2129
2115 $ try -p optimized '0:2 & (reverse(contains("a")) + 2)'
2130 $ try -p optimized '0:2 & (reverse(contains("a")) + 2)'
2116 * optimized:
2131 * optimized:
2117 (and
2132 (and
2118 (range
2133 (range
2119 ('symbol', '0')
2134 ('symbol', '0')
2120 ('symbol', '2')
2135 ('symbol', '2')
2121 define)
2136 define)
2122 (or
2137 (or
2123 (list
2138 (list
2124 ('symbol', '2')
2139 ('symbol', '2')
2125 (func
2140 (func
2126 ('symbol', 'reverse')
2141 ('symbol', 'reverse')
2127 (func
2142 (func
2128 ('symbol', 'contains')
2143 ('symbol', 'contains')
2129 ('string', 'a')
2144 ('string', 'a')
2130 define)
2145 define)
2131 follow))
2146 follow))
2132 follow)
2147 follow)
2133 define)
2148 define)
2134 * set:
2149 * set:
2135 <filteredset
2150 <filteredset
2136 <spanset+ 0:3>,
2151 <spanset+ 0:3>,
2137 <addset
2152 <addset
2138 <baseset [2]>,
2153 <baseset [2]>,
2139 <filteredset
2154 <filteredset
2140 <fullreposet+ 0:10>,
2155 <fullreposet+ 0:10>,
2141 <contains 'a'>>>>
2156 <contains 'a'>>>>
2142 0
2157 0
2143 1
2158 1
2144 2
2159 2
2145
2160
2146 $ try -p optimized '(reverse(contains("a")) + 2) & 0:2'
2161 $ try -p optimized '(reverse(contains("a")) + 2) & 0:2'
2147 * optimized:
2162 * optimized:
2148 (and
2163 (and
2149 (range
2164 (range
2150 ('symbol', '0')
2165 ('symbol', '0')
2151 ('symbol', '2')
2166 ('symbol', '2')
2152 follow)
2167 follow)
2153 (or
2168 (or
2154 (list
2169 (list
2155 (func
2170 (func
2156 ('symbol', 'reverse')
2171 ('symbol', 'reverse')
2157 (func
2172 (func
2158 ('symbol', 'contains')
2173 ('symbol', 'contains')
2159 ('string', 'a')
2174 ('string', 'a')
2160 define)
2175 define)
2161 define)
2176 define)
2162 ('symbol', '2'))
2177 ('symbol', '2'))
2163 define)
2178 define)
2164 define)
2179 define)
2165 * set:
2180 * set:
2166 <addset
2181 <addset
2167 <filteredset
2182 <filteredset
2168 <spanset- 0:3>,
2183 <spanset- 0:3>,
2169 <contains 'a'>>,
2184 <contains 'a'>>,
2170 <baseset [2]>>
2185 <baseset [2]>>
2171 1
2186 1
2172 0
2187 0
2173 2
2188 2
2174
2189
2175 test sort revset
2190 test sort revset
2176 --------------------------------------------
2191 --------------------------------------------
2177
2192
2178 test when adding two unordered revsets
2193 test when adding two unordered revsets
2179
2194
2180 $ log 'sort(keyword(issue) or modifies(b))'
2195 $ log 'sort(keyword(issue) or modifies(b))'
2181 4
2196 4
2182 6
2197 6
2183
2198
2184 test when sorting a reversed collection in the same way it is
2199 test when sorting a reversed collection in the same way it is
2185
2200
2186 $ log 'sort(reverse(all()), -rev)'
2201 $ log 'sort(reverse(all()), -rev)'
2187 9
2202 9
2188 8
2203 8
2189 7
2204 7
2190 6
2205 6
2191 5
2206 5
2192 4
2207 4
2193 3
2208 3
2194 2
2209 2
2195 1
2210 1
2196 0
2211 0
2197
2212
2198 test when sorting a reversed collection
2213 test when sorting a reversed collection
2199
2214
2200 $ log 'sort(reverse(all()), rev)'
2215 $ log 'sort(reverse(all()), rev)'
2201 0
2216 0
2202 1
2217 1
2203 2
2218 2
2204 3
2219 3
2205 4
2220 4
2206 5
2221 5
2207 6
2222 6
2208 7
2223 7
2209 8
2224 8
2210 9
2225 9
2211
2226
2212
2227
2213 test sorting two sorted collections in different orders
2228 test sorting two sorted collections in different orders
2214
2229
2215 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2230 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2216 2
2231 2
2217 6
2232 6
2218 8
2233 8
2219 9
2234 9
2220
2235
2221 test sorting two sorted collections in different orders backwards
2236 test sorting two sorted collections in different orders backwards
2222
2237
2223 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2238 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2224 9
2239 9
2225 8
2240 8
2226 6
2241 6
2227 2
2242 2
2228
2243
2229 test empty sort key which is noop
2244 test empty sort key which is noop
2230
2245
2231 $ log 'sort(0 + 2 + 1, "")'
2246 $ log 'sort(0 + 2 + 1, "")'
2232 0
2247 0
2233 2
2248 2
2234 1
2249 1
2235
2250
2236 test invalid sort keys
2251 test invalid sort keys
2237
2252
2238 $ log 'sort(all(), -invalid)'
2253 $ log 'sort(all(), -invalid)'
2239 hg: parse error: unknown sort key '-invalid'
2254 hg: parse error: unknown sort key '-invalid'
2240 [255]
2255 [255]
2241
2256
2242 $ cd ..
2257 $ cd ..
2243
2258
2244 test sorting by multiple keys including variable-length strings
2259 test sorting by multiple keys including variable-length strings
2245
2260
2246 $ hg init sorting
2261 $ hg init sorting
2247 $ cd sorting
2262 $ cd sorting
2248 $ cat <<EOF >> .hg/hgrc
2263 $ cat <<EOF >> .hg/hgrc
2249 > [ui]
2264 > [ui]
2250 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2265 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2251 > [templatealias]
2266 > [templatealias]
2252 > p5(s) = pad(s, 5)
2267 > p5(s) = pad(s, 5)
2253 > EOF
2268 > EOF
2254 $ hg branch -qf b12
2269 $ hg branch -qf b12
2255 $ hg ci -m m111 -u u112 -d '111 10800'
2270 $ hg ci -m m111 -u u112 -d '111 10800'
2256 $ hg branch -qf b11
2271 $ hg branch -qf b11
2257 $ hg ci -m m12 -u u111 -d '112 7200'
2272 $ hg ci -m m12 -u u111 -d '112 7200'
2258 $ hg branch -qf b111
2273 $ hg branch -qf b111
2259 $ hg ci -m m11 -u u12 -d '111 3600'
2274 $ hg ci -m m11 -u u12 -d '111 3600'
2260 $ hg branch -qf b112
2275 $ hg branch -qf b112
2261 $ hg ci -m m111 -u u11 -d '120 0'
2276 $ hg ci -m m111 -u u11 -d '120 0'
2262 $ hg branch -qf b111
2277 $ hg branch -qf b111
2263 $ hg ci -m m112 -u u111 -d '110 14400'
2278 $ hg ci -m m112 -u u111 -d '110 14400'
2264 created new head
2279 created new head
2265
2280
2266 compare revisions (has fast path):
2281 compare revisions (has fast path):
2267
2282
2268 $ hg log -r 'sort(all(), rev)'
2283 $ hg log -r 'sort(all(), rev)'
2269 0 b12 m111 u112 111 10800
2284 0 b12 m111 u112 111 10800
2270 1 b11 m12 u111 112 7200
2285 1 b11 m12 u111 112 7200
2271 2 b111 m11 u12 111 3600
2286 2 b111 m11 u12 111 3600
2272 3 b112 m111 u11 120 0
2287 3 b112 m111 u11 120 0
2273 4 b111 m112 u111 110 14400
2288 4 b111 m112 u111 110 14400
2274
2289
2275 $ hg log -r 'sort(all(), -rev)'
2290 $ hg log -r 'sort(all(), -rev)'
2276 4 b111 m112 u111 110 14400
2291 4 b111 m112 u111 110 14400
2277 3 b112 m111 u11 120 0
2292 3 b112 m111 u11 120 0
2278 2 b111 m11 u12 111 3600
2293 2 b111 m11 u12 111 3600
2279 1 b11 m12 u111 112 7200
2294 1 b11 m12 u111 112 7200
2280 0 b12 m111 u112 111 10800
2295 0 b12 m111 u112 111 10800
2281
2296
2282 compare variable-length strings (issue5218):
2297 compare variable-length strings (issue5218):
2283
2298
2284 $ hg log -r 'sort(all(), branch)'
2299 $ hg log -r 'sort(all(), branch)'
2285 1 b11 m12 u111 112 7200
2300 1 b11 m12 u111 112 7200
2286 2 b111 m11 u12 111 3600
2301 2 b111 m11 u12 111 3600
2287 4 b111 m112 u111 110 14400
2302 4 b111 m112 u111 110 14400
2288 3 b112 m111 u11 120 0
2303 3 b112 m111 u11 120 0
2289 0 b12 m111 u112 111 10800
2304 0 b12 m111 u112 111 10800
2290
2305
2291 $ hg log -r 'sort(all(), -branch)'
2306 $ hg log -r 'sort(all(), -branch)'
2292 0 b12 m111 u112 111 10800
2307 0 b12 m111 u112 111 10800
2293 3 b112 m111 u11 120 0
2308 3 b112 m111 u11 120 0
2294 2 b111 m11 u12 111 3600
2309 2 b111 m11 u12 111 3600
2295 4 b111 m112 u111 110 14400
2310 4 b111 m112 u111 110 14400
2296 1 b11 m12 u111 112 7200
2311 1 b11 m12 u111 112 7200
2297
2312
2298 $ hg log -r 'sort(all(), desc)'
2313 $ hg log -r 'sort(all(), desc)'
2299 2 b111 m11 u12 111 3600
2314 2 b111 m11 u12 111 3600
2300 0 b12 m111 u112 111 10800
2315 0 b12 m111 u112 111 10800
2301 3 b112 m111 u11 120 0
2316 3 b112 m111 u11 120 0
2302 4 b111 m112 u111 110 14400
2317 4 b111 m112 u111 110 14400
2303 1 b11 m12 u111 112 7200
2318 1 b11 m12 u111 112 7200
2304
2319
2305 $ hg log -r 'sort(all(), -desc)'
2320 $ hg log -r 'sort(all(), -desc)'
2306 1 b11 m12 u111 112 7200
2321 1 b11 m12 u111 112 7200
2307 4 b111 m112 u111 110 14400
2322 4 b111 m112 u111 110 14400
2308 0 b12 m111 u112 111 10800
2323 0 b12 m111 u112 111 10800
2309 3 b112 m111 u11 120 0
2324 3 b112 m111 u11 120 0
2310 2 b111 m11 u12 111 3600
2325 2 b111 m11 u12 111 3600
2311
2326
2312 $ hg log -r 'sort(all(), user)'
2327 $ hg log -r 'sort(all(), user)'
2313 3 b112 m111 u11 120 0
2328 3 b112 m111 u11 120 0
2314 1 b11 m12 u111 112 7200
2329 1 b11 m12 u111 112 7200
2315 4 b111 m112 u111 110 14400
2330 4 b111 m112 u111 110 14400
2316 0 b12 m111 u112 111 10800
2331 0 b12 m111 u112 111 10800
2317 2 b111 m11 u12 111 3600
2332 2 b111 m11 u12 111 3600
2318
2333
2319 $ hg log -r 'sort(all(), -user)'
2334 $ hg log -r 'sort(all(), -user)'
2320 2 b111 m11 u12 111 3600
2335 2 b111 m11 u12 111 3600
2321 0 b12 m111 u112 111 10800
2336 0 b12 m111 u112 111 10800
2322 1 b11 m12 u111 112 7200
2337 1 b11 m12 u111 112 7200
2323 4 b111 m112 u111 110 14400
2338 4 b111 m112 u111 110 14400
2324 3 b112 m111 u11 120 0
2339 3 b112 m111 u11 120 0
2325
2340
2326 compare dates (tz offset should have no effect):
2341 compare dates (tz offset should have no effect):
2327
2342
2328 $ hg log -r 'sort(all(), date)'
2343 $ hg log -r 'sort(all(), date)'
2329 4 b111 m112 u111 110 14400
2344 4 b111 m112 u111 110 14400
2330 0 b12 m111 u112 111 10800
2345 0 b12 m111 u112 111 10800
2331 2 b111 m11 u12 111 3600
2346 2 b111 m11 u12 111 3600
2332 1 b11 m12 u111 112 7200
2347 1 b11 m12 u111 112 7200
2333 3 b112 m111 u11 120 0
2348 3 b112 m111 u11 120 0
2334
2349
2335 $ hg log -r 'sort(all(), -date)'
2350 $ hg log -r 'sort(all(), -date)'
2336 3 b112 m111 u11 120 0
2351 3 b112 m111 u11 120 0
2337 1 b11 m12 u111 112 7200
2352 1 b11 m12 u111 112 7200
2338 0 b12 m111 u112 111 10800
2353 0 b12 m111 u112 111 10800
2339 2 b111 m11 u12 111 3600
2354 2 b111 m11 u12 111 3600
2340 4 b111 m112 u111 110 14400
2355 4 b111 m112 u111 110 14400
2341
2356
2342 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2357 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2343 because '-k' reverses the comparison, not the list itself:
2358 because '-k' reverses the comparison, not the list itself:
2344
2359
2345 $ hg log -r 'sort(0 + 2, date)'
2360 $ hg log -r 'sort(0 + 2, date)'
2346 0 b12 m111 u112 111 10800
2361 0 b12 m111 u112 111 10800
2347 2 b111 m11 u12 111 3600
2362 2 b111 m11 u12 111 3600
2348
2363
2349 $ hg log -r 'sort(0 + 2, -date)'
2364 $ hg log -r 'sort(0 + 2, -date)'
2350 0 b12 m111 u112 111 10800
2365 0 b12 m111 u112 111 10800
2351 2 b111 m11 u12 111 3600
2366 2 b111 m11 u12 111 3600
2352
2367
2353 $ hg log -r 'reverse(sort(0 + 2, date))'
2368 $ hg log -r 'reverse(sort(0 + 2, date))'
2354 2 b111 m11 u12 111 3600
2369 2 b111 m11 u12 111 3600
2355 0 b12 m111 u112 111 10800
2370 0 b12 m111 u112 111 10800
2356
2371
2357 sort by multiple keys:
2372 sort by multiple keys:
2358
2373
2359 $ hg log -r 'sort(all(), "branch -rev")'
2374 $ hg log -r 'sort(all(), "branch -rev")'
2360 1 b11 m12 u111 112 7200
2375 1 b11 m12 u111 112 7200
2361 4 b111 m112 u111 110 14400
2376 4 b111 m112 u111 110 14400
2362 2 b111 m11 u12 111 3600
2377 2 b111 m11 u12 111 3600
2363 3 b112 m111 u11 120 0
2378 3 b112 m111 u11 120 0
2364 0 b12 m111 u112 111 10800
2379 0 b12 m111 u112 111 10800
2365
2380
2366 $ hg log -r 'sort(all(), "-desc -date")'
2381 $ hg log -r 'sort(all(), "-desc -date")'
2367 1 b11 m12 u111 112 7200
2382 1 b11 m12 u111 112 7200
2368 4 b111 m112 u111 110 14400
2383 4 b111 m112 u111 110 14400
2369 3 b112 m111 u11 120 0
2384 3 b112 m111 u11 120 0
2370 0 b12 m111 u112 111 10800
2385 0 b12 m111 u112 111 10800
2371 2 b111 m11 u12 111 3600
2386 2 b111 m11 u12 111 3600
2372
2387
2373 $ hg log -r 'sort(all(), "user -branch date rev")'
2388 $ hg log -r 'sort(all(), "user -branch date rev")'
2374 3 b112 m111 u11 120 0
2389 3 b112 m111 u11 120 0
2375 4 b111 m112 u111 110 14400
2390 4 b111 m112 u111 110 14400
2376 1 b11 m12 u111 112 7200
2391 1 b11 m12 u111 112 7200
2377 0 b12 m111 u112 111 10800
2392 0 b12 m111 u112 111 10800
2378 2 b111 m11 u12 111 3600
2393 2 b111 m11 u12 111 3600
2379
2394
2380 toposort prioritises graph branches
2395 toposort prioritises graph branches
2381
2396
2382 $ hg up 2
2397 $ hg up 2
2383 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2398 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2384 $ touch a
2399 $ touch a
2385 $ hg addremove
2400 $ hg addremove
2386 adding a
2401 adding a
2387 $ hg ci -m 't1' -u 'tu' -d '130 0'
2402 $ hg ci -m 't1' -u 'tu' -d '130 0'
2388 created new head
2403 created new head
2389 $ echo 'a' >> a
2404 $ echo 'a' >> a
2390 $ hg ci -m 't2' -u 'tu' -d '130 0'
2405 $ hg ci -m 't2' -u 'tu' -d '130 0'
2391 $ hg book book1
2406 $ hg book book1
2392 $ hg up 4
2407 $ hg up 4
2393 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2408 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2394 (leaving bookmark book1)
2409 (leaving bookmark book1)
2395 $ touch a
2410 $ touch a
2396 $ hg addremove
2411 $ hg addremove
2397 adding a
2412 adding a
2398 $ hg ci -m 't3' -u 'tu' -d '130 0'
2413 $ hg ci -m 't3' -u 'tu' -d '130 0'
2399
2414
2400 $ hg log -r 'sort(all(), topo)'
2415 $ hg log -r 'sort(all(), topo)'
2401 7 b111 t3 tu 130 0
2416 7 b111 t3 tu 130 0
2402 4 b111 m112 u111 110 14400
2417 4 b111 m112 u111 110 14400
2403 3 b112 m111 u11 120 0
2418 3 b112 m111 u11 120 0
2404 6 b111 t2 tu 130 0
2419 6 b111 t2 tu 130 0
2405 5 b111 t1 tu 130 0
2420 5 b111 t1 tu 130 0
2406 2 b111 m11 u12 111 3600
2421 2 b111 m11 u12 111 3600
2407 1 b11 m12 u111 112 7200
2422 1 b11 m12 u111 112 7200
2408 0 b12 m111 u112 111 10800
2423 0 b12 m111 u112 111 10800
2409
2424
2410 $ hg log -r 'sort(all(), -topo)'
2425 $ hg log -r 'sort(all(), -topo)'
2411 0 b12 m111 u112 111 10800
2426 0 b12 m111 u112 111 10800
2412 1 b11 m12 u111 112 7200
2427 1 b11 m12 u111 112 7200
2413 2 b111 m11 u12 111 3600
2428 2 b111 m11 u12 111 3600
2414 5 b111 t1 tu 130 0
2429 5 b111 t1 tu 130 0
2415 6 b111 t2 tu 130 0
2430 6 b111 t2 tu 130 0
2416 3 b112 m111 u11 120 0
2431 3 b112 m111 u11 120 0
2417 4 b111 m112 u111 110 14400
2432 4 b111 m112 u111 110 14400
2418 7 b111 t3 tu 130 0
2433 7 b111 t3 tu 130 0
2419
2434
2420 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2435 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2421 6 b111 t2 tu 130 0
2436 6 b111 t2 tu 130 0
2422 5 b111 t1 tu 130 0
2437 5 b111 t1 tu 130 0
2423 7 b111 t3 tu 130 0
2438 7 b111 t3 tu 130 0
2424 4 b111 m112 u111 110 14400
2439 4 b111 m112 u111 110 14400
2425 3 b112 m111 u11 120 0
2440 3 b112 m111 u11 120 0
2426 2 b111 m11 u12 111 3600
2441 2 b111 m11 u12 111 3600
2427 1 b11 m12 u111 112 7200
2442 1 b11 m12 u111 112 7200
2428 0 b12 m111 u112 111 10800
2443 0 b12 m111 u112 111 10800
2429
2444
2430 topographical sorting can't be combined with other sort keys, and you can't
2445 topographical sorting can't be combined with other sort keys, and you can't
2431 use the topo.firstbranch option when topo sort is not active:
2446 use the topo.firstbranch option when topo sort is not active:
2432
2447
2433 $ hg log -r 'sort(all(), "topo user")'
2448 $ hg log -r 'sort(all(), "topo user")'
2434 hg: parse error: topo sort order cannot be combined with other sort keys
2449 hg: parse error: topo sort order cannot be combined with other sort keys
2435 [255]
2450 [255]
2436
2451
2437 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2452 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2438 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2453 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2439 [255]
2454 [255]
2440
2455
2441 topo.firstbranch should accept any kind of expressions:
2456 topo.firstbranch should accept any kind of expressions:
2442
2457
2443 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2458 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2444 0 b12 m111 u112 111 10800
2459 0 b12 m111 u112 111 10800
2445
2460
2446 $ cd ..
2461 $ cd ..
2447 $ cd repo
2462 $ cd repo
2448
2463
2449 test subtracting something from an addset
2464 test subtracting something from an addset
2450
2465
2451 $ log '(outgoing() or removes(a)) - removes(a)'
2466 $ log '(outgoing() or removes(a)) - removes(a)'
2452 8
2467 8
2453 9
2468 9
2454
2469
2455 test intersecting something with an addset
2470 test intersecting something with an addset
2456
2471
2457 $ log 'parents(outgoing() or removes(a))'
2472 $ log 'parents(outgoing() or removes(a))'
2458 1
2473 1
2459 4
2474 4
2460 5
2475 5
2461 8
2476 8
2462
2477
2463 test that `or` operation combines elements in the right order:
2478 test that `or` operation combines elements in the right order:
2464
2479
2465 $ log '3:4 or 2:5'
2480 $ log '3:4 or 2:5'
2466 3
2481 3
2467 4
2482 4
2468 2
2483 2
2469 5
2484 5
2470 $ log '3:4 or 5:2'
2485 $ log '3:4 or 5:2'
2471 3
2486 3
2472 4
2487 4
2473 5
2488 5
2474 2
2489 2
2475 $ log 'sort(3:4 or 2:5)'
2490 $ log 'sort(3:4 or 2:5)'
2476 2
2491 2
2477 3
2492 3
2478 4
2493 4
2479 5
2494 5
2480 $ log 'sort(3:4 or 5:2)'
2495 $ log 'sort(3:4 or 5:2)'
2481 2
2496 2
2482 3
2497 3
2483 4
2498 4
2484 5
2499 5
2485
2500
2486 test that more than one `-r`s are combined in the right order and deduplicated:
2501 test that more than one `-r`s are combined in the right order and deduplicated:
2487
2502
2488 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2503 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2489 3
2504 3
2490 4
2505 4
2491 5
2506 5
2492 2
2507 2
2493 0
2508 0
2494 1
2509 1
2495
2510
2496 test that `or` operation skips duplicated revisions from right-hand side
2511 test that `or` operation skips duplicated revisions from right-hand side
2497
2512
2498 $ try 'reverse(1::5) or ancestors(4)'
2513 $ try 'reverse(1::5) or ancestors(4)'
2499 (or
2514 (or
2500 (list
2515 (list
2501 (func
2516 (func
2502 ('symbol', 'reverse')
2517 ('symbol', 'reverse')
2503 (dagrange
2518 (dagrange
2504 ('symbol', '1')
2519 ('symbol', '1')
2505 ('symbol', '5')))
2520 ('symbol', '5')))
2506 (func
2521 (func
2507 ('symbol', 'ancestors')
2522 ('symbol', 'ancestors')
2508 ('symbol', '4'))))
2523 ('symbol', '4'))))
2509 * set:
2524 * set:
2510 <addset
2525 <addset
2511 <baseset- [1, 3, 5]>,
2526 <baseset- [1, 3, 5]>,
2512 <generatorset+>>
2527 <generatorset+>>
2513 5
2528 5
2514 3
2529 3
2515 1
2530 1
2516 0
2531 0
2517 2
2532 2
2518 4
2533 4
2519 $ try 'sort(ancestors(4) or reverse(1::5))'
2534 $ try 'sort(ancestors(4) or reverse(1::5))'
2520 (func
2535 (func
2521 ('symbol', 'sort')
2536 ('symbol', 'sort')
2522 (or
2537 (or
2523 (list
2538 (list
2524 (func
2539 (func
2525 ('symbol', 'ancestors')
2540 ('symbol', 'ancestors')
2526 ('symbol', '4'))
2541 ('symbol', '4'))
2527 (func
2542 (func
2528 ('symbol', 'reverse')
2543 ('symbol', 'reverse')
2529 (dagrange
2544 (dagrange
2530 ('symbol', '1')
2545 ('symbol', '1')
2531 ('symbol', '5'))))))
2546 ('symbol', '5'))))))
2532 * set:
2547 * set:
2533 <addset+
2548 <addset+
2534 <generatorset+>,
2549 <generatorset+>,
2535 <baseset- [1, 3, 5]>>
2550 <baseset- [1, 3, 5]>>
2536 0
2551 0
2537 1
2552 1
2538 2
2553 2
2539 3
2554 3
2540 4
2555 4
2541 5
2556 5
2542
2557
2543 test optimization of trivial `or` operation
2558 test optimization of trivial `or` operation
2544
2559
2545 $ try --optimize '0|(1)|"2"|-2|tip|null'
2560 $ try --optimize '0|(1)|"2"|-2|tip|null'
2546 (or
2561 (or
2547 (list
2562 (list
2548 ('symbol', '0')
2563 ('symbol', '0')
2549 (group
2564 (group
2550 ('symbol', '1'))
2565 ('symbol', '1'))
2551 ('string', '2')
2566 ('string', '2')
2552 (negate
2567 (negate
2553 ('symbol', '2'))
2568 ('symbol', '2'))
2554 ('symbol', 'tip')
2569 ('symbol', 'tip')
2555 ('symbol', 'null')))
2570 ('symbol', 'null')))
2556 * optimized:
2571 * optimized:
2557 (func
2572 (func
2558 ('symbol', '_list')
2573 ('symbol', '_list')
2559 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2574 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2560 define)
2575 define)
2561 * set:
2576 * set:
2562 <baseset [0, 1, 2, 8, 9, -1]>
2577 <baseset [0, 1, 2, 8, 9, -1]>
2563 0
2578 0
2564 1
2579 1
2565 2
2580 2
2566 8
2581 8
2567 9
2582 9
2568 -1
2583 -1
2569
2584
2570 $ try --optimize '0|1|2:3'
2585 $ try --optimize '0|1|2:3'
2571 (or
2586 (or
2572 (list
2587 (list
2573 ('symbol', '0')
2588 ('symbol', '0')
2574 ('symbol', '1')
2589 ('symbol', '1')
2575 (range
2590 (range
2576 ('symbol', '2')
2591 ('symbol', '2')
2577 ('symbol', '3'))))
2592 ('symbol', '3'))))
2578 * optimized:
2593 * optimized:
2579 (or
2594 (or
2580 (list
2595 (list
2581 (func
2596 (func
2582 ('symbol', '_list')
2597 ('symbol', '_list')
2583 ('string', '0\x001')
2598 ('string', '0\x001')
2584 define)
2599 define)
2585 (range
2600 (range
2586 ('symbol', '2')
2601 ('symbol', '2')
2587 ('symbol', '3')
2602 ('symbol', '3')
2588 define))
2603 define))
2589 define)
2604 define)
2590 * set:
2605 * set:
2591 <addset
2606 <addset
2592 <baseset [0, 1]>,
2607 <baseset [0, 1]>,
2593 <spanset+ 2:4>>
2608 <spanset+ 2:4>>
2594 0
2609 0
2595 1
2610 1
2596 2
2611 2
2597 3
2612 3
2598
2613
2599 $ try --optimize '0:1|2|3:4|5|6'
2614 $ try --optimize '0:1|2|3:4|5|6'
2600 (or
2615 (or
2601 (list
2616 (list
2602 (range
2617 (range
2603 ('symbol', '0')
2618 ('symbol', '0')
2604 ('symbol', '1'))
2619 ('symbol', '1'))
2605 ('symbol', '2')
2620 ('symbol', '2')
2606 (range
2621 (range
2607 ('symbol', '3')
2622 ('symbol', '3')
2608 ('symbol', '4'))
2623 ('symbol', '4'))
2609 ('symbol', '5')
2624 ('symbol', '5')
2610 ('symbol', '6')))
2625 ('symbol', '6')))
2611 * optimized:
2626 * optimized:
2612 (or
2627 (or
2613 (list
2628 (list
2614 (range
2629 (range
2615 ('symbol', '0')
2630 ('symbol', '0')
2616 ('symbol', '1')
2631 ('symbol', '1')
2617 define)
2632 define)
2618 ('symbol', '2')
2633 ('symbol', '2')
2619 (range
2634 (range
2620 ('symbol', '3')
2635 ('symbol', '3')
2621 ('symbol', '4')
2636 ('symbol', '4')
2622 define)
2637 define)
2623 (func
2638 (func
2624 ('symbol', '_list')
2639 ('symbol', '_list')
2625 ('string', '5\x006')
2640 ('string', '5\x006')
2626 define))
2641 define))
2627 define)
2642 define)
2628 * set:
2643 * set:
2629 <addset
2644 <addset
2630 <addset
2645 <addset
2631 <spanset+ 0:2>,
2646 <spanset+ 0:2>,
2632 <baseset [2]>>,
2647 <baseset [2]>>,
2633 <addset
2648 <addset
2634 <spanset+ 3:5>,
2649 <spanset+ 3:5>,
2635 <baseset [5, 6]>>>
2650 <baseset [5, 6]>>>
2636 0
2651 0
2637 1
2652 1
2638 2
2653 2
2639 3
2654 3
2640 4
2655 4
2641 5
2656 5
2642 6
2657 6
2643
2658
2644 unoptimized `or` looks like this
2659 unoptimized `or` looks like this
2645
2660
2646 $ try --no-optimized -p analyzed '0|1|2|3|4'
2661 $ try --no-optimized -p analyzed '0|1|2|3|4'
2647 * analyzed:
2662 * analyzed:
2648 (or
2663 (or
2649 (list
2664 (list
2650 ('symbol', '0')
2665 ('symbol', '0')
2651 ('symbol', '1')
2666 ('symbol', '1')
2652 ('symbol', '2')
2667 ('symbol', '2')
2653 ('symbol', '3')
2668 ('symbol', '3')
2654 ('symbol', '4'))
2669 ('symbol', '4'))
2655 define)
2670 define)
2656 * set:
2671 * set:
2657 <addset
2672 <addset
2658 <addset
2673 <addset
2659 <baseset [0]>,
2674 <baseset [0]>,
2660 <baseset [1]>>,
2675 <baseset [1]>>,
2661 <addset
2676 <addset
2662 <baseset [2]>,
2677 <baseset [2]>,
2663 <addset
2678 <addset
2664 <baseset [3]>,
2679 <baseset [3]>,
2665 <baseset [4]>>>>
2680 <baseset [4]>>>>
2666 0
2681 0
2667 1
2682 1
2668 2
2683 2
2669 3
2684 3
2670 4
2685 4
2671
2686
2672 test that `_list` should be narrowed by provided `subset`
2687 test that `_list` should be narrowed by provided `subset`
2673
2688
2674 $ log '0:2 and (null|1|2|3)'
2689 $ log '0:2 and (null|1|2|3)'
2675 1
2690 1
2676 2
2691 2
2677
2692
2678 test that `_list` should remove duplicates
2693 test that `_list` should remove duplicates
2679
2694
2680 $ log '0|1|2|1|2|-1|tip'
2695 $ log '0|1|2|1|2|-1|tip'
2681 0
2696 0
2682 1
2697 1
2683 2
2698 2
2684 9
2699 9
2685
2700
2686 test unknown revision in `_list`
2701 test unknown revision in `_list`
2687
2702
2688 $ log '0|unknown'
2703 $ log '0|unknown'
2689 abort: unknown revision 'unknown'!
2704 abort: unknown revision 'unknown'!
2690 [255]
2705 [255]
2691
2706
2692 test integer range in `_list`
2707 test integer range in `_list`
2693
2708
2694 $ log '-1|-10'
2709 $ log '-1|-10'
2695 9
2710 9
2696 0
2711 0
2697
2712
2698 $ log '-10|-11'
2713 $ log '-10|-11'
2699 abort: unknown revision '-11'!
2714 abort: unknown revision '-11'!
2700 [255]
2715 [255]
2701
2716
2702 $ log '9|10'
2717 $ log '9|10'
2703 abort: unknown revision '10'!
2718 abort: unknown revision '10'!
2704 [255]
2719 [255]
2705
2720
2706 test '0000' != '0' in `_list`
2721 test '0000' != '0' in `_list`
2707
2722
2708 $ log '0|0000'
2723 $ log '0|0000'
2709 0
2724 0
2710 -1
2725 -1
2711
2726
2712 test ',' in `_list`
2727 test ',' in `_list`
2713 $ log '0,1'
2728 $ log '0,1'
2714 hg: parse error: can't use a list in this context
2729 hg: parse error: can't use a list in this context
2715 (see hg help "revsets.x or y")
2730 (see hg help "revsets.x or y")
2716 [255]
2731 [255]
2717 $ try '0,1,2'
2732 $ try '0,1,2'
2718 (list
2733 (list
2719 ('symbol', '0')
2734 ('symbol', '0')
2720 ('symbol', '1')
2735 ('symbol', '1')
2721 ('symbol', '2'))
2736 ('symbol', '2'))
2722 hg: parse error: can't use a list in this context
2737 hg: parse error: can't use a list in this context
2723 (see hg help "revsets.x or y")
2738 (see hg help "revsets.x or y")
2724 [255]
2739 [255]
2725
2740
2726 test that chained `or` operations make balanced addsets
2741 test that chained `or` operations make balanced addsets
2727
2742
2728 $ try '0:1|1:2|2:3|3:4|4:5'
2743 $ try '0:1|1:2|2:3|3:4|4:5'
2729 (or
2744 (or
2730 (list
2745 (list
2731 (range
2746 (range
2732 ('symbol', '0')
2747 ('symbol', '0')
2733 ('symbol', '1'))
2748 ('symbol', '1'))
2734 (range
2749 (range
2735 ('symbol', '1')
2750 ('symbol', '1')
2736 ('symbol', '2'))
2751 ('symbol', '2'))
2737 (range
2752 (range
2738 ('symbol', '2')
2753 ('symbol', '2')
2739 ('symbol', '3'))
2754 ('symbol', '3'))
2740 (range
2755 (range
2741 ('symbol', '3')
2756 ('symbol', '3')
2742 ('symbol', '4'))
2757 ('symbol', '4'))
2743 (range
2758 (range
2744 ('symbol', '4')
2759 ('symbol', '4')
2745 ('symbol', '5'))))
2760 ('symbol', '5'))))
2746 * set:
2761 * set:
2747 <addset
2762 <addset
2748 <addset
2763 <addset
2749 <spanset+ 0:2>,
2764 <spanset+ 0:2>,
2750 <spanset+ 1:3>>,
2765 <spanset+ 1:3>>,
2751 <addset
2766 <addset
2752 <spanset+ 2:4>,
2767 <spanset+ 2:4>,
2753 <addset
2768 <addset
2754 <spanset+ 3:5>,
2769 <spanset+ 3:5>,
2755 <spanset+ 4:6>>>>
2770 <spanset+ 4:6>>>>
2756 0
2771 0
2757 1
2772 1
2758 2
2773 2
2759 3
2774 3
2760 4
2775 4
2761 5
2776 5
2762
2777
2763 no crash by empty group "()" while optimizing `or` operations
2778 no crash by empty group "()" while optimizing `or` operations
2764
2779
2765 $ try --optimize '0|()'
2780 $ try --optimize '0|()'
2766 (or
2781 (or
2767 (list
2782 (list
2768 ('symbol', '0')
2783 ('symbol', '0')
2769 (group
2784 (group
2770 None)))
2785 None)))
2771 * optimized:
2786 * optimized:
2772 (or
2787 (or
2773 (list
2788 (list
2774 ('symbol', '0')
2789 ('symbol', '0')
2775 None)
2790 None)
2776 define)
2791 define)
2777 hg: parse error: missing argument
2792 hg: parse error: missing argument
2778 [255]
2793 [255]
2779
2794
2780 test that chained `or` operations never eat up stack (issue4624)
2795 test that chained `or` operations never eat up stack (issue4624)
2781 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2796 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2782
2797
2783 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2798 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2784 0
2799 0
2785 1
2800 1
2786
2801
2787 test that repeated `-r` options never eat up stack (issue4565)
2802 test that repeated `-r` options never eat up stack (issue4565)
2788 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2803 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2789
2804
2790 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2805 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2791 0
2806 0
2792 1
2807 1
2793
2808
2794 check that conversion to only works
2809 check that conversion to only works
2795 $ try --optimize '::3 - ::1'
2810 $ try --optimize '::3 - ::1'
2796 (minus
2811 (minus
2797 (dagrangepre
2812 (dagrangepre
2798 ('symbol', '3'))
2813 ('symbol', '3'))
2799 (dagrangepre
2814 (dagrangepre
2800 ('symbol', '1')))
2815 ('symbol', '1')))
2801 * optimized:
2816 * optimized:
2802 (func
2817 (func
2803 ('symbol', 'only')
2818 ('symbol', 'only')
2804 (list
2819 (list
2805 ('symbol', '3')
2820 ('symbol', '3')
2806 ('symbol', '1'))
2821 ('symbol', '1'))
2807 define)
2822 define)
2808 * set:
2823 * set:
2809 <baseset+ [3]>
2824 <baseset+ [3]>
2810 3
2825 3
2811 $ try --optimize 'ancestors(1) - ancestors(3)'
2826 $ try --optimize 'ancestors(1) - ancestors(3)'
2812 (minus
2827 (minus
2813 (func
2828 (func
2814 ('symbol', 'ancestors')
2829 ('symbol', 'ancestors')
2815 ('symbol', '1'))
2830 ('symbol', '1'))
2816 (func
2831 (func
2817 ('symbol', 'ancestors')
2832 ('symbol', 'ancestors')
2818 ('symbol', '3')))
2833 ('symbol', '3')))
2819 * optimized:
2834 * optimized:
2820 (func
2835 (func
2821 ('symbol', 'only')
2836 ('symbol', 'only')
2822 (list
2837 (list
2823 ('symbol', '1')
2838 ('symbol', '1')
2824 ('symbol', '3'))
2839 ('symbol', '3'))
2825 define)
2840 define)
2826 * set:
2841 * set:
2827 <baseset+ []>
2842 <baseset+ []>
2828 $ try --optimize 'not ::2 and ::6'
2843 $ try --optimize 'not ::2 and ::6'
2829 (and
2844 (and
2830 (not
2845 (not
2831 (dagrangepre
2846 (dagrangepre
2832 ('symbol', '2')))
2847 ('symbol', '2')))
2833 (dagrangepre
2848 (dagrangepre
2834 ('symbol', '6')))
2849 ('symbol', '6')))
2835 * optimized:
2850 * optimized:
2836 (func
2851 (func
2837 ('symbol', 'only')
2852 ('symbol', 'only')
2838 (list
2853 (list
2839 ('symbol', '6')
2854 ('symbol', '6')
2840 ('symbol', '2'))
2855 ('symbol', '2'))
2841 define)
2856 define)
2842 * set:
2857 * set:
2843 <baseset+ [3, 4, 5, 6]>
2858 <baseset+ [3, 4, 5, 6]>
2844 3
2859 3
2845 4
2860 4
2846 5
2861 5
2847 6
2862 6
2848 $ try --optimize 'ancestors(6) and not ancestors(4)'
2863 $ try --optimize 'ancestors(6) and not ancestors(4)'
2849 (and
2864 (and
2850 (func
2865 (func
2851 ('symbol', 'ancestors')
2866 ('symbol', 'ancestors')
2852 ('symbol', '6'))
2867 ('symbol', '6'))
2853 (not
2868 (not
2854 (func
2869 (func
2855 ('symbol', 'ancestors')
2870 ('symbol', 'ancestors')
2856 ('symbol', '4'))))
2871 ('symbol', '4'))))
2857 * optimized:
2872 * optimized:
2858 (func
2873 (func
2859 ('symbol', 'only')
2874 ('symbol', 'only')
2860 (list
2875 (list
2861 ('symbol', '6')
2876 ('symbol', '6')
2862 ('symbol', '4'))
2877 ('symbol', '4'))
2863 define)
2878 define)
2864 * set:
2879 * set:
2865 <baseset+ [3, 5, 6]>
2880 <baseset+ [3, 5, 6]>
2866 3
2881 3
2867 5
2882 5
2868 6
2883 6
2869
2884
2870 no crash by empty group "()" while optimizing to "only()"
2885 no crash by empty group "()" while optimizing to "only()"
2871
2886
2872 $ try --optimize '::1 and ()'
2887 $ try --optimize '::1 and ()'
2873 (and
2888 (and
2874 (dagrangepre
2889 (dagrangepre
2875 ('symbol', '1'))
2890 ('symbol', '1'))
2876 (group
2891 (group
2877 None))
2892 None))
2878 * optimized:
2893 * optimized:
2879 (and
2894 (and
2880 None
2895 None
2881 (func
2896 (func
2882 ('symbol', 'ancestors')
2897 ('symbol', 'ancestors')
2883 ('symbol', '1')
2898 ('symbol', '1')
2884 define)
2899 define)
2885 define)
2900 define)
2886 hg: parse error: missing argument
2901 hg: parse error: missing argument
2887 [255]
2902 [255]
2888
2903
2889 invalid function call should not be optimized to only()
2904 invalid function call should not be optimized to only()
2890
2905
2891 $ log '"ancestors"(6) and not ancestors(4)'
2906 $ log '"ancestors"(6) and not ancestors(4)'
2892 hg: parse error: not a symbol
2907 hg: parse error: not a symbol
2893 [255]
2908 [255]
2894
2909
2895 $ log 'ancestors(6) and not "ancestors"(4)'
2910 $ log 'ancestors(6) and not "ancestors"(4)'
2896 hg: parse error: not a symbol
2911 hg: parse error: not a symbol
2897 [255]
2912 [255]
2898
2913
2899 we can use patterns when searching for tags
2914 we can use patterns when searching for tags
2900
2915
2901 $ log 'tag("1..*")'
2916 $ log 'tag("1..*")'
2902 abort: tag '1..*' does not exist!
2917 abort: tag '1..*' does not exist!
2903 [255]
2918 [255]
2904 $ log 'tag("re:1..*")'
2919 $ log 'tag("re:1..*")'
2905 6
2920 6
2906 $ log 'tag("re:[0-9].[0-9]")'
2921 $ log 'tag("re:[0-9].[0-9]")'
2907 6
2922 6
2908 $ log 'tag("literal:1.0")'
2923 $ log 'tag("literal:1.0")'
2909 6
2924 6
2910 $ log 'tag("re:0..*")'
2925 $ log 'tag("re:0..*")'
2911
2926
2912 $ log 'tag(unknown)'
2927 $ log 'tag(unknown)'
2913 abort: tag 'unknown' does not exist!
2928 abort: tag 'unknown' does not exist!
2914 [255]
2929 [255]
2915 $ log 'tag("re:unknown")'
2930 $ log 'tag("re:unknown")'
2916 $ log 'present(tag("unknown"))'
2931 $ log 'present(tag("unknown"))'
2917 $ log 'present(tag("re:unknown"))'
2932 $ log 'present(tag("re:unknown"))'
2918 $ log 'branch(unknown)'
2933 $ log 'branch(unknown)'
2919 abort: unknown revision 'unknown'!
2934 abort: unknown revision 'unknown'!
2920 [255]
2935 [255]
2921 $ log 'branch("literal:unknown")'
2936 $ log 'branch("literal:unknown")'
2922 abort: branch 'unknown' does not exist!
2937 abort: branch 'unknown' does not exist!
2923 [255]
2938 [255]
2924 $ log 'branch("re:unknown")'
2939 $ log 'branch("re:unknown")'
2925 $ log 'present(branch("unknown"))'
2940 $ log 'present(branch("unknown"))'
2926 $ log 'present(branch("re:unknown"))'
2941 $ log 'present(branch("re:unknown"))'
2927 $ log 'user(bob)'
2942 $ log 'user(bob)'
2928 2
2943 2
2929
2944
2930 $ log '4::8'
2945 $ log '4::8'
2931 4
2946 4
2932 8
2947 8
2933 $ log '4:8'
2948 $ log '4:8'
2934 4
2949 4
2935 5
2950 5
2936 6
2951 6
2937 7
2952 7
2938 8
2953 8
2939
2954
2940 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2955 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2941 4
2956 4
2942 2
2957 2
2943 5
2958 5
2944
2959
2945 $ log 'not 0 and 0:2'
2960 $ log 'not 0 and 0:2'
2946 1
2961 1
2947 2
2962 2
2948 $ log 'not 1 and 0:2'
2963 $ log 'not 1 and 0:2'
2949 0
2964 0
2950 2
2965 2
2951 $ log 'not 2 and 0:2'
2966 $ log 'not 2 and 0:2'
2952 0
2967 0
2953 1
2968 1
2954 $ log '(1 and 2)::'
2969 $ log '(1 and 2)::'
2955 $ log '(1 and 2):'
2970 $ log '(1 and 2):'
2956 $ log '(1 and 2):3'
2971 $ log '(1 and 2):3'
2957 $ log 'sort(head(), -rev)'
2972 $ log 'sort(head(), -rev)'
2958 9
2973 9
2959 7
2974 7
2960 6
2975 6
2961 5
2976 5
2962 4
2977 4
2963 3
2978 3
2964 2
2979 2
2965 1
2980 1
2966 0
2981 0
2967 $ log '4::8 - 8'
2982 $ log '4::8 - 8'
2968 4
2983 4
2969
2984
2970 matching() should preserve the order of the input set:
2985 matching() should preserve the order of the input set:
2971
2986
2972 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2987 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2973 2
2988 2
2974 3
2989 3
2975 1
2990 1
2976
2991
2977 $ log 'named("unknown")'
2992 $ log 'named("unknown")'
2978 abort: namespace 'unknown' does not exist!
2993 abort: namespace 'unknown' does not exist!
2979 [255]
2994 [255]
2980 $ log 'named("re:unknown")'
2995 $ log 'named("re:unknown")'
2981 abort: no namespace exists that match 'unknown'!
2996 abort: no namespace exists that match 'unknown'!
2982 [255]
2997 [255]
2983 $ log 'present(named("unknown"))'
2998 $ log 'present(named("unknown"))'
2984 $ log 'present(named("re:unknown"))'
2999 $ log 'present(named("re:unknown"))'
2985
3000
2986 $ log 'tag()'
3001 $ log 'tag()'
2987 6
3002 6
2988 $ log 'named("tags")'
3003 $ log 'named("tags")'
2989 6
3004 6
2990
3005
2991 issue2437
3006 issue2437
2992
3007
2993 $ log '3 and p1(5)'
3008 $ log '3 and p1(5)'
2994 3
3009 3
2995 $ log '4 and p2(6)'
3010 $ log '4 and p2(6)'
2996 4
3011 4
2997 $ log '1 and parents(:2)'
3012 $ log '1 and parents(:2)'
2998 1
3013 1
2999 $ log '2 and children(1:)'
3014 $ log '2 and children(1:)'
3000 2
3015 2
3001 $ log 'roots(all()) or roots(all())'
3016 $ log 'roots(all()) or roots(all())'
3002 0
3017 0
3003 $ hg debugrevspec 'roots(all()) or roots(all())'
3018 $ hg debugrevspec 'roots(all()) or roots(all())'
3004 0
3019 0
3005 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
3020 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
3006 9
3021 9
3007 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
3022 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
3008 4
3023 4
3009
3024
3010 issue2654: report a parse error if the revset was not completely parsed
3025 issue2654: report a parse error if the revset was not completely parsed
3011
3026
3012 $ log '1 OR 2'
3027 $ log '1 OR 2'
3013 hg: parse error at 2: invalid token
3028 hg: parse error at 2: invalid token
3014 [255]
3029 [255]
3015
3030
3016 or operator should preserve ordering:
3031 or operator should preserve ordering:
3017 $ log 'reverse(2::4) or tip'
3032 $ log 'reverse(2::4) or tip'
3018 4
3033 4
3019 2
3034 2
3020 9
3035 9
3021
3036
3022 parentrevspec
3037 parentrevspec
3023
3038
3024 $ log 'merge()^0'
3039 $ log 'merge()^0'
3025 6
3040 6
3026 $ log 'merge()^'
3041 $ log 'merge()^'
3027 5
3042 5
3028 $ log 'merge()^1'
3043 $ log 'merge()^1'
3029 5
3044 5
3030 $ log 'merge()^2'
3045 $ log 'merge()^2'
3031 4
3046 4
3032 $ log '(not merge())^2'
3047 $ log '(not merge())^2'
3033 $ log 'merge()^^'
3048 $ log 'merge()^^'
3034 3
3049 3
3035 $ log 'merge()^1^'
3050 $ log 'merge()^1^'
3036 3
3051 3
3037 $ log 'merge()^^^'
3052 $ log 'merge()^^^'
3038 1
3053 1
3039
3054
3040 $ log '(merge() | 0)~-1'
3055 $ log '(merge() | 0)~-1'
3041 7
3056 7
3042 1
3057 1
3043 $ log 'merge()~-1'
3058 $ log 'merge()~-1'
3044 7
3059 7
3045 $ log 'tip~-1'
3060 $ log 'tip~-1'
3046 $ log '(tip | merge())~-1'
3061 $ log '(tip | merge())~-1'
3047 7
3062 7
3048 $ log 'merge()~0'
3063 $ log 'merge()~0'
3049 6
3064 6
3050 $ log 'merge()~1'
3065 $ log 'merge()~1'
3051 5
3066 5
3052 $ log 'merge()~2'
3067 $ log 'merge()~2'
3053 3
3068 3
3054 $ log 'merge()~2^1'
3069 $ log 'merge()~2^1'
3055 1
3070 1
3056 $ log 'merge()~3'
3071 $ log 'merge()~3'
3057 1
3072 1
3058
3073
3059 $ log '(-3:tip)^'
3074 $ log '(-3:tip)^'
3060 4
3075 4
3061 6
3076 6
3062 8
3077 8
3063
3078
3064 $ log 'tip^foo'
3079 $ log 'tip^foo'
3065 hg: parse error: ^ expects a number 0, 1, or 2
3080 hg: parse error: ^ expects a number 0, 1, or 2
3066 [255]
3081 [255]
3067
3082
3068 $ log 'branchpoint()~-1'
3083 $ log 'branchpoint()~-1'
3069 abort: revision in set has more than one child!
3084 abort: revision in set has more than one child!
3070 [255]
3085 [255]
3071
3086
3072 Bogus function gets suggestions
3087 Bogus function gets suggestions
3073 $ log 'add()'
3088 $ log 'add()'
3074 hg: parse error: unknown identifier: add
3089 hg: parse error: unknown identifier: add
3075 (did you mean adds?)
3090 (did you mean adds?)
3076 [255]
3091 [255]
3077 $ log 'added()'
3092 $ log 'added()'
3078 hg: parse error: unknown identifier: added
3093 hg: parse error: unknown identifier: added
3079 (did you mean adds?)
3094 (did you mean adds?)
3080 [255]
3095 [255]
3081 $ log 'remo()'
3096 $ log 'remo()'
3082 hg: parse error: unknown identifier: remo
3097 hg: parse error: unknown identifier: remo
3083 (did you mean one of remote, removes?)
3098 (did you mean one of remote, removes?)
3084 [255]
3099 [255]
3085 $ log 'babar()'
3100 $ log 'babar()'
3086 hg: parse error: unknown identifier: babar
3101 hg: parse error: unknown identifier: babar
3087 [255]
3102 [255]
3088
3103
3089 Bogus function with a similar internal name doesn't suggest the internal name
3104 Bogus function with a similar internal name doesn't suggest the internal name
3090 $ log 'matches()'
3105 $ log 'matches()'
3091 hg: parse error: unknown identifier: matches
3106 hg: parse error: unknown identifier: matches
3092 (did you mean matching?)
3107 (did you mean matching?)
3093 [255]
3108 [255]
3094
3109
3095 Undocumented functions aren't suggested as similar either
3110 Undocumented functions aren't suggested as similar either
3096 $ log 'tagged2()'
3111 $ log 'tagged2()'
3097 hg: parse error: unknown identifier: tagged2
3112 hg: parse error: unknown identifier: tagged2
3098 [255]
3113 [255]
3099
3114
3100 multiple revspecs
3115 multiple revspecs
3101
3116
3102 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
3117 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
3103 8
3118 8
3104 9
3119 9
3105 4
3120 4
3106 5
3121 5
3107 6
3122 6
3108 7
3123 7
3109
3124
3110 test usage in revpair (with "+")
3125 test usage in revpair (with "+")
3111
3126
3112 (real pair)
3127 (real pair)
3113
3128
3114 $ hg diff -r 'tip^^' -r 'tip'
3129 $ hg diff -r 'tip^^' -r 'tip'
3115 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3130 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3116 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3131 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3117 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3132 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3118 @@ -0,0 +1,1 @@
3133 @@ -0,0 +1,1 @@
3119 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3134 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3120 $ hg diff -r 'tip^^::tip'
3135 $ hg diff -r 'tip^^::tip'
3121 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3136 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3137 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3123 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3138 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3124 @@ -0,0 +1,1 @@
3139 @@ -0,0 +1,1 @@
3125 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3140 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3126
3141
3127 (single rev)
3142 (single rev)
3128
3143
3129 $ hg diff -r 'tip^' -r 'tip^'
3144 $ hg diff -r 'tip^' -r 'tip^'
3130 $ hg diff -r 'tip^:tip^'
3145 $ hg diff -r 'tip^:tip^'
3131
3146
3132 (single rev that does not looks like a range)
3147 (single rev that does not looks like a range)
3133
3148
3134 $ hg diff -r 'tip^::tip^ or tip^'
3149 $ hg diff -r 'tip^::tip^ or tip^'
3135 diff -r d5d0dcbdc4d9 .hgtags
3150 diff -r d5d0dcbdc4d9 .hgtags
3136 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3151 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3137 +++ b/.hgtags * (glob)
3152 +++ b/.hgtags * (glob)
3138 @@ -0,0 +1,1 @@
3153 @@ -0,0 +1,1 @@
3139 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3154 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3140 $ hg diff -r 'tip^ or tip^'
3155 $ hg diff -r 'tip^ or tip^'
3141 diff -r d5d0dcbdc4d9 .hgtags
3156 diff -r d5d0dcbdc4d9 .hgtags
3142 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3157 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3143 +++ b/.hgtags * (glob)
3158 +++ b/.hgtags * (glob)
3144 @@ -0,0 +1,1 @@
3159 @@ -0,0 +1,1 @@
3145 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3160 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3146
3161
3147 (no rev)
3162 (no rev)
3148
3163
3149 $ hg diff -r 'author("babar") or author("celeste")'
3164 $ hg diff -r 'author("babar") or author("celeste")'
3150 abort: empty revision range
3165 abort: empty revision range
3151 [255]
3166 [255]
3152
3167
3153 aliases:
3168 aliases:
3154
3169
3155 $ echo '[revsetalias]' >> .hg/hgrc
3170 $ echo '[revsetalias]' >> .hg/hgrc
3156 $ echo 'm = merge()' >> .hg/hgrc
3171 $ echo 'm = merge()' >> .hg/hgrc
3157 (revset aliases can override builtin revsets)
3172 (revset aliases can override builtin revsets)
3158 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
3173 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
3159 $ echo 'sincem = descendants(m)' >> .hg/hgrc
3174 $ echo 'sincem = descendants(m)' >> .hg/hgrc
3160 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
3175 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
3161 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3176 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3162 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3177 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3163
3178
3164 $ try m
3179 $ try m
3165 ('symbol', 'm')
3180 ('symbol', 'm')
3166 * expanded:
3181 * expanded:
3167 (func
3182 (func
3168 ('symbol', 'merge')
3183 ('symbol', 'merge')
3169 None)
3184 None)
3170 * set:
3185 * set:
3171 <filteredset
3186 <filteredset
3172 <fullreposet+ 0:10>,
3187 <fullreposet+ 0:10>,
3173 <merge>>
3188 <merge>>
3174 6
3189 6
3175
3190
3176 $ HGPLAIN=1
3191 $ HGPLAIN=1
3177 $ export HGPLAIN
3192 $ export HGPLAIN
3178 $ try m
3193 $ try m
3179 ('symbol', 'm')
3194 ('symbol', 'm')
3180 abort: unknown revision 'm'!
3195 abort: unknown revision 'm'!
3181 [255]
3196 [255]
3182
3197
3183 $ HGPLAINEXCEPT=revsetalias
3198 $ HGPLAINEXCEPT=revsetalias
3184 $ export HGPLAINEXCEPT
3199 $ export HGPLAINEXCEPT
3185 $ try m
3200 $ try m
3186 ('symbol', 'm')
3201 ('symbol', 'm')
3187 * expanded:
3202 * expanded:
3188 (func
3203 (func
3189 ('symbol', 'merge')
3204 ('symbol', 'merge')
3190 None)
3205 None)
3191 * set:
3206 * set:
3192 <filteredset
3207 <filteredset
3193 <fullreposet+ 0:10>,
3208 <fullreposet+ 0:10>,
3194 <merge>>
3209 <merge>>
3195 6
3210 6
3196
3211
3197 $ unset HGPLAIN
3212 $ unset HGPLAIN
3198 $ unset HGPLAINEXCEPT
3213 $ unset HGPLAINEXCEPT
3199
3214
3200 $ try 'p2(.)'
3215 $ try 'p2(.)'
3201 (func
3216 (func
3202 ('symbol', 'p2')
3217 ('symbol', 'p2')
3203 ('symbol', '.'))
3218 ('symbol', '.'))
3204 * expanded:
3219 * expanded:
3205 (func
3220 (func
3206 ('symbol', 'p1')
3221 ('symbol', 'p1')
3207 ('symbol', '.'))
3222 ('symbol', '.'))
3208 * set:
3223 * set:
3209 <baseset+ [8]>
3224 <baseset+ [8]>
3210 8
3225 8
3211
3226
3212 $ HGPLAIN=1
3227 $ HGPLAIN=1
3213 $ export HGPLAIN
3228 $ export HGPLAIN
3214 $ try 'p2(.)'
3229 $ try 'p2(.)'
3215 (func
3230 (func
3216 ('symbol', 'p2')
3231 ('symbol', 'p2')
3217 ('symbol', '.'))
3232 ('symbol', '.'))
3218 * set:
3233 * set:
3219 <baseset+ []>
3234 <baseset+ []>
3220
3235
3221 $ HGPLAINEXCEPT=revsetalias
3236 $ HGPLAINEXCEPT=revsetalias
3222 $ export HGPLAINEXCEPT
3237 $ export HGPLAINEXCEPT
3223 $ try 'p2(.)'
3238 $ try 'p2(.)'
3224 (func
3239 (func
3225 ('symbol', 'p2')
3240 ('symbol', 'p2')
3226 ('symbol', '.'))
3241 ('symbol', '.'))
3227 * expanded:
3242 * expanded:
3228 (func
3243 (func
3229 ('symbol', 'p1')
3244 ('symbol', 'p1')
3230 ('symbol', '.'))
3245 ('symbol', '.'))
3231 * set:
3246 * set:
3232 <baseset+ [8]>
3247 <baseset+ [8]>
3233 8
3248 8
3234
3249
3235 $ unset HGPLAIN
3250 $ unset HGPLAIN
3236 $ unset HGPLAINEXCEPT
3251 $ unset HGPLAINEXCEPT
3237
3252
3238 test alias recursion
3253 test alias recursion
3239
3254
3240 $ try sincem
3255 $ try sincem
3241 ('symbol', 'sincem')
3256 ('symbol', 'sincem')
3242 * expanded:
3257 * expanded:
3243 (func
3258 (func
3244 ('symbol', 'descendants')
3259 ('symbol', 'descendants')
3245 (func
3260 (func
3246 ('symbol', 'merge')
3261 ('symbol', 'merge')
3247 None))
3262 None))
3248 * set:
3263 * set:
3249 <addset+
3264 <addset+
3250 <filteredset
3265 <filteredset
3251 <fullreposet+ 0:10>,
3266 <fullreposet+ 0:10>,
3252 <merge>>,
3267 <merge>>,
3253 <generatorset+>>
3268 <generatorset+>>
3254 6
3269 6
3255 7
3270 7
3256
3271
3257 test infinite recursion
3272 test infinite recursion
3258
3273
3259 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3274 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3260 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3275 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3261 $ try recurse1
3276 $ try recurse1
3262 ('symbol', 'recurse1')
3277 ('symbol', 'recurse1')
3263 hg: parse error: infinite expansion of revset alias "recurse1" detected
3278 hg: parse error: infinite expansion of revset alias "recurse1" detected
3264 [255]
3279 [255]
3265
3280
3266 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3281 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3267 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3282 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3268 $ try "level2(level1(1, 2), 3)"
3283 $ try "level2(level1(1, 2), 3)"
3269 (func
3284 (func
3270 ('symbol', 'level2')
3285 ('symbol', 'level2')
3271 (list
3286 (list
3272 (func
3287 (func
3273 ('symbol', 'level1')
3288 ('symbol', 'level1')
3274 (list
3289 (list
3275 ('symbol', '1')
3290 ('symbol', '1')
3276 ('symbol', '2')))
3291 ('symbol', '2')))
3277 ('symbol', '3')))
3292 ('symbol', '3')))
3278 * expanded:
3293 * expanded:
3279 (or
3294 (or
3280 (list
3295 (list
3281 ('symbol', '3')
3296 ('symbol', '3')
3282 (or
3297 (or
3283 (list
3298 (list
3284 ('symbol', '1')
3299 ('symbol', '1')
3285 ('symbol', '2')))))
3300 ('symbol', '2')))))
3286 * set:
3301 * set:
3287 <addset
3302 <addset
3288 <baseset [3]>,
3303 <baseset [3]>,
3289 <baseset [1, 2]>>
3304 <baseset [1, 2]>>
3290 3
3305 3
3291 1
3306 1
3292 2
3307 2
3293
3308
3294 test nesting and variable passing
3309 test nesting and variable passing
3295
3310
3296 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3311 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3297 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3312 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3298 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3313 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3299 $ try 'nested(2:5)'
3314 $ try 'nested(2:5)'
3300 (func
3315 (func
3301 ('symbol', 'nested')
3316 ('symbol', 'nested')
3302 (range
3317 (range
3303 ('symbol', '2')
3318 ('symbol', '2')
3304 ('symbol', '5')))
3319 ('symbol', '5')))
3305 * expanded:
3320 * expanded:
3306 (func
3321 (func
3307 ('symbol', 'max')
3322 ('symbol', 'max')
3308 (range
3323 (range
3309 ('symbol', '2')
3324 ('symbol', '2')
3310 ('symbol', '5')))
3325 ('symbol', '5')))
3311 * set:
3326 * set:
3312 <baseset
3327 <baseset
3313 <max
3328 <max
3314 <fullreposet+ 0:10>,
3329 <fullreposet+ 0:10>,
3315 <spanset+ 2:6>>>
3330 <spanset+ 2:6>>>
3316 5
3331 5
3317
3332
3318 test chained `or` operations are flattened at parsing phase
3333 test chained `or` operations are flattened at parsing phase
3319
3334
3320 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3335 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3321 $ try 'chainedorops(0:1, 1:2, 2:3)'
3336 $ try 'chainedorops(0:1, 1:2, 2:3)'
3322 (func
3337 (func
3323 ('symbol', 'chainedorops')
3338 ('symbol', 'chainedorops')
3324 (list
3339 (list
3325 (range
3340 (range
3326 ('symbol', '0')
3341 ('symbol', '0')
3327 ('symbol', '1'))
3342 ('symbol', '1'))
3328 (range
3343 (range
3329 ('symbol', '1')
3344 ('symbol', '1')
3330 ('symbol', '2'))
3345 ('symbol', '2'))
3331 (range
3346 (range
3332 ('symbol', '2')
3347 ('symbol', '2')
3333 ('symbol', '3'))))
3348 ('symbol', '3'))))
3334 * expanded:
3349 * expanded:
3335 (or
3350 (or
3336 (list
3351 (list
3337 (range
3352 (range
3338 ('symbol', '0')
3353 ('symbol', '0')
3339 ('symbol', '1'))
3354 ('symbol', '1'))
3340 (range
3355 (range
3341 ('symbol', '1')
3356 ('symbol', '1')
3342 ('symbol', '2'))
3357 ('symbol', '2'))
3343 (range
3358 (range
3344 ('symbol', '2')
3359 ('symbol', '2')
3345 ('symbol', '3'))))
3360 ('symbol', '3'))))
3346 * set:
3361 * set:
3347 <addset
3362 <addset
3348 <spanset+ 0:2>,
3363 <spanset+ 0:2>,
3349 <addset
3364 <addset
3350 <spanset+ 1:3>,
3365 <spanset+ 1:3>,
3351 <spanset+ 2:4>>>
3366 <spanset+ 2:4>>>
3352 0
3367 0
3353 1
3368 1
3354 2
3369 2
3355 3
3370 3
3356
3371
3357 test variable isolation, variable placeholders are rewritten as string
3372 test variable isolation, variable placeholders are rewritten as string
3358 then parsed and matched again as string. Check they do not leak too
3373 then parsed and matched again as string. Check they do not leak too
3359 far away.
3374 far away.
3360
3375
3361 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3376 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3362 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3377 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3363 $ try 'callinjection(2:5)'
3378 $ try 'callinjection(2:5)'
3364 (func
3379 (func
3365 ('symbol', 'callinjection')
3380 ('symbol', 'callinjection')
3366 (range
3381 (range
3367 ('symbol', '2')
3382 ('symbol', '2')
3368 ('symbol', '5')))
3383 ('symbol', '5')))
3369 * expanded:
3384 * expanded:
3370 (func
3385 (func
3371 ('symbol', 'descendants')
3386 ('symbol', 'descendants')
3372 (func
3387 (func
3373 ('symbol', 'max')
3388 ('symbol', 'max')
3374 ('string', '$1')))
3389 ('string', '$1')))
3375 abort: unknown revision '$1'!
3390 abort: unknown revision '$1'!
3376 [255]
3391 [255]
3377
3392
3378 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3393 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3379 but 'all()' should never be substituted to '0()'.
3394 but 'all()' should never be substituted to '0()'.
3380
3395
3381 $ echo 'universe = all()' >> .hg/hgrc
3396 $ echo 'universe = all()' >> .hg/hgrc
3382 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3397 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3383 $ try 'shadowall(0)'
3398 $ try 'shadowall(0)'
3384 (func
3399 (func
3385 ('symbol', 'shadowall')
3400 ('symbol', 'shadowall')
3386 ('symbol', '0'))
3401 ('symbol', '0'))
3387 * expanded:
3402 * expanded:
3388 (and
3403 (and
3389 ('symbol', '0')
3404 ('symbol', '0')
3390 (func
3405 (func
3391 ('symbol', 'all')
3406 ('symbol', 'all')
3392 None))
3407 None))
3393 * set:
3408 * set:
3394 <filteredset
3409 <filteredset
3395 <baseset [0]>,
3410 <baseset [0]>,
3396 <spanset+ 0:10>>
3411 <spanset+ 0:10>>
3397 0
3412 0
3398
3413
3399 test unknown reference:
3414 test unknown reference:
3400
3415
3401 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3416 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3402 (func
3417 (func
3403 ('symbol', 'unknownref')
3418 ('symbol', 'unknownref')
3404 ('symbol', '0'))
3419 ('symbol', '0'))
3405 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3420 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3406 [255]
3421 [255]
3407
3422
3408 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3423 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3409 ('symbol', 'tip')
3424 ('symbol', 'tip')
3410 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3425 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3411 * set:
3426 * set:
3412 <baseset [9]>
3427 <baseset [9]>
3413 9
3428 9
3414
3429
3415 $ try 'tip'
3430 $ try 'tip'
3416 ('symbol', 'tip')
3431 ('symbol', 'tip')
3417 * set:
3432 * set:
3418 <baseset [9]>
3433 <baseset [9]>
3419 9
3434 9
3420
3435
3421 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3436 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3422 ('symbol', 'tip')
3437 ('symbol', 'tip')
3423 warning: bad declaration of revset alias "bad name": at 4: invalid token
3438 warning: bad declaration of revset alias "bad name": at 4: invalid token
3424 * set:
3439 * set:
3425 <baseset [9]>
3440 <baseset [9]>
3426 9
3441 9
3427 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3442 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3428 $ try 'strictreplacing("foo", tip)'
3443 $ try 'strictreplacing("foo", tip)'
3429 (func
3444 (func
3430 ('symbol', 'strictreplacing')
3445 ('symbol', 'strictreplacing')
3431 (list
3446 (list
3432 ('string', 'foo')
3447 ('string', 'foo')
3433 ('symbol', 'tip')))
3448 ('symbol', 'tip')))
3434 * expanded:
3449 * expanded:
3435 (or
3450 (or
3436 (list
3451 (list
3437 ('symbol', 'tip')
3452 ('symbol', 'tip')
3438 (func
3453 (func
3439 ('symbol', 'desc')
3454 ('symbol', 'desc')
3440 ('string', '$1'))))
3455 ('string', '$1'))))
3441 * set:
3456 * set:
3442 <addset
3457 <addset
3443 <baseset [9]>,
3458 <baseset [9]>,
3444 <filteredset
3459 <filteredset
3445 <fullreposet+ 0:10>,
3460 <fullreposet+ 0:10>,
3446 <desc '$1'>>>
3461 <desc '$1'>>>
3447 9
3462 9
3448
3463
3449 $ try 'd(2:5)'
3464 $ try 'd(2:5)'
3450 (func
3465 (func
3451 ('symbol', 'd')
3466 ('symbol', 'd')
3452 (range
3467 (range
3453 ('symbol', '2')
3468 ('symbol', '2')
3454 ('symbol', '5')))
3469 ('symbol', '5')))
3455 * expanded:
3470 * expanded:
3456 (func
3471 (func
3457 ('symbol', 'reverse')
3472 ('symbol', 'reverse')
3458 (func
3473 (func
3459 ('symbol', 'sort')
3474 ('symbol', 'sort')
3460 (list
3475 (list
3461 (range
3476 (range
3462 ('symbol', '2')
3477 ('symbol', '2')
3463 ('symbol', '5'))
3478 ('symbol', '5'))
3464 ('symbol', 'date'))))
3479 ('symbol', 'date'))))
3465 * set:
3480 * set:
3466 <baseset [4, 5, 3, 2]>
3481 <baseset [4, 5, 3, 2]>
3467 4
3482 4
3468 5
3483 5
3469 3
3484 3
3470 2
3485 2
3471 $ try 'rs(2 or 3, date)'
3486 $ try 'rs(2 or 3, date)'
3472 (func
3487 (func
3473 ('symbol', 'rs')
3488 ('symbol', 'rs')
3474 (list
3489 (list
3475 (or
3490 (or
3476 (list
3491 (list
3477 ('symbol', '2')
3492 ('symbol', '2')
3478 ('symbol', '3')))
3493 ('symbol', '3')))
3479 ('symbol', 'date')))
3494 ('symbol', 'date')))
3480 * expanded:
3495 * expanded:
3481 (func
3496 (func
3482 ('symbol', 'reverse')
3497 ('symbol', 'reverse')
3483 (func
3498 (func
3484 ('symbol', 'sort')
3499 ('symbol', 'sort')
3485 (list
3500 (list
3486 (or
3501 (or
3487 (list
3502 (list
3488 ('symbol', '2')
3503 ('symbol', '2')
3489 ('symbol', '3')))
3504 ('symbol', '3')))
3490 ('symbol', 'date'))))
3505 ('symbol', 'date'))))
3491 * set:
3506 * set:
3492 <baseset [3, 2]>
3507 <baseset [3, 2]>
3493 3
3508 3
3494 2
3509 2
3495 $ try 'rs()'
3510 $ try 'rs()'
3496 (func
3511 (func
3497 ('symbol', 'rs')
3512 ('symbol', 'rs')
3498 None)
3513 None)
3499 hg: parse error: invalid number of arguments: 0
3514 hg: parse error: invalid number of arguments: 0
3500 [255]
3515 [255]
3501 $ try 'rs(2)'
3516 $ try 'rs(2)'
3502 (func
3517 (func
3503 ('symbol', 'rs')
3518 ('symbol', 'rs')
3504 ('symbol', '2'))
3519 ('symbol', '2'))
3505 hg: parse error: invalid number of arguments: 1
3520 hg: parse error: invalid number of arguments: 1
3506 [255]
3521 [255]
3507 $ try 'rs(2, data, 7)'
3522 $ try 'rs(2, data, 7)'
3508 (func
3523 (func
3509 ('symbol', 'rs')
3524 ('symbol', 'rs')
3510 (list
3525 (list
3511 ('symbol', '2')
3526 ('symbol', '2')
3512 ('symbol', 'data')
3527 ('symbol', 'data')
3513 ('symbol', '7')))
3528 ('symbol', '7')))
3514 hg: parse error: invalid number of arguments: 3
3529 hg: parse error: invalid number of arguments: 3
3515 [255]
3530 [255]
3516 $ try 'rs4(2 or 3, x, x, date)'
3531 $ try 'rs4(2 or 3, x, x, date)'
3517 (func
3532 (func
3518 ('symbol', 'rs4')
3533 ('symbol', 'rs4')
3519 (list
3534 (list
3520 (or
3535 (or
3521 (list
3536 (list
3522 ('symbol', '2')
3537 ('symbol', '2')
3523 ('symbol', '3')))
3538 ('symbol', '3')))
3524 ('symbol', 'x')
3539 ('symbol', 'x')
3525 ('symbol', 'x')
3540 ('symbol', 'x')
3526 ('symbol', 'date')))
3541 ('symbol', 'date')))
3527 * expanded:
3542 * expanded:
3528 (func
3543 (func
3529 ('symbol', 'reverse')
3544 ('symbol', 'reverse')
3530 (func
3545 (func
3531 ('symbol', 'sort')
3546 ('symbol', 'sort')
3532 (list
3547 (list
3533 (or
3548 (or
3534 (list
3549 (list
3535 ('symbol', '2')
3550 ('symbol', '2')
3536 ('symbol', '3')))
3551 ('symbol', '3')))
3537 ('symbol', 'date'))))
3552 ('symbol', 'date'))))
3538 * set:
3553 * set:
3539 <baseset [3, 2]>
3554 <baseset [3, 2]>
3540 3
3555 3
3541 2
3556 2
3542
3557
3543 issue4553: check that revset aliases override existing hash prefix
3558 issue4553: check that revset aliases override existing hash prefix
3544
3559
3545 $ hg log -qr e
3560 $ hg log -qr e
3546 6:e0cc66ef77e8
3561 6:e0cc66ef77e8
3547
3562
3548 $ hg log -qr e --config revsetalias.e="all()"
3563 $ hg log -qr e --config revsetalias.e="all()"
3549 0:2785f51eece5
3564 0:2785f51eece5
3550 1:d75937da8da0
3565 1:d75937da8da0
3551 2:5ed5505e9f1c
3566 2:5ed5505e9f1c
3552 3:8528aa5637f2
3567 3:8528aa5637f2
3553 4:2326846efdab
3568 4:2326846efdab
3554 5:904fa392b941
3569 5:904fa392b941
3555 6:e0cc66ef77e8
3570 6:e0cc66ef77e8
3556 7:013af1973af4
3571 7:013af1973af4
3557 8:d5d0dcbdc4d9
3572 8:d5d0dcbdc4d9
3558 9:24286f4ae135
3573 9:24286f4ae135
3559
3574
3560 $ hg log -qr e: --config revsetalias.e="0"
3575 $ hg log -qr e: --config revsetalias.e="0"
3561 0:2785f51eece5
3576 0:2785f51eece5
3562 1:d75937da8da0
3577 1:d75937da8da0
3563 2:5ed5505e9f1c
3578 2:5ed5505e9f1c
3564 3:8528aa5637f2
3579 3:8528aa5637f2
3565 4:2326846efdab
3580 4:2326846efdab
3566 5:904fa392b941
3581 5:904fa392b941
3567 6:e0cc66ef77e8
3582 6:e0cc66ef77e8
3568 7:013af1973af4
3583 7:013af1973af4
3569 8:d5d0dcbdc4d9
3584 8:d5d0dcbdc4d9
3570 9:24286f4ae135
3585 9:24286f4ae135
3571
3586
3572 $ hg log -qr :e --config revsetalias.e="9"
3587 $ hg log -qr :e --config revsetalias.e="9"
3573 0:2785f51eece5
3588 0:2785f51eece5
3574 1:d75937da8da0
3589 1:d75937da8da0
3575 2:5ed5505e9f1c
3590 2:5ed5505e9f1c
3576 3:8528aa5637f2
3591 3:8528aa5637f2
3577 4:2326846efdab
3592 4:2326846efdab
3578 5:904fa392b941
3593 5:904fa392b941
3579 6:e0cc66ef77e8
3594 6:e0cc66ef77e8
3580 7:013af1973af4
3595 7:013af1973af4
3581 8:d5d0dcbdc4d9
3596 8:d5d0dcbdc4d9
3582 9:24286f4ae135
3597 9:24286f4ae135
3583
3598
3584 $ hg log -qr e:
3599 $ hg log -qr e:
3585 6:e0cc66ef77e8
3600 6:e0cc66ef77e8
3586 7:013af1973af4
3601 7:013af1973af4
3587 8:d5d0dcbdc4d9
3602 8:d5d0dcbdc4d9
3588 9:24286f4ae135
3603 9:24286f4ae135
3589
3604
3590 $ hg log -qr :e
3605 $ hg log -qr :e
3591 0:2785f51eece5
3606 0:2785f51eece5
3592 1:d75937da8da0
3607 1:d75937da8da0
3593 2:5ed5505e9f1c
3608 2:5ed5505e9f1c
3594 3:8528aa5637f2
3609 3:8528aa5637f2
3595 4:2326846efdab
3610 4:2326846efdab
3596 5:904fa392b941
3611 5:904fa392b941
3597 6:e0cc66ef77e8
3612 6:e0cc66ef77e8
3598
3613
3599 issue2549 - correct optimizations
3614 issue2549 - correct optimizations
3600
3615
3601 $ try 'limit(1 or 2 or 3, 2) and not 2'
3616 $ try 'limit(1 or 2 or 3, 2) and not 2'
3602 (and
3617 (and
3603 (func
3618 (func
3604 ('symbol', 'limit')
3619 ('symbol', 'limit')
3605 (list
3620 (list
3606 (or
3621 (or
3607 (list
3622 (list
3608 ('symbol', '1')
3623 ('symbol', '1')
3609 ('symbol', '2')
3624 ('symbol', '2')
3610 ('symbol', '3')))
3625 ('symbol', '3')))
3611 ('symbol', '2')))
3626 ('symbol', '2')))
3612 (not
3627 (not
3613 ('symbol', '2')))
3628 ('symbol', '2')))
3614 * set:
3629 * set:
3615 <filteredset
3630 <filteredset
3616 <baseset
3631 <baseset slice=0:2
3617 <limit n=2, offset=0,
3632 <baseset [1, 2, 3]>>,
3618 <baseset [1, 2, 3]>>>,
3619 <not
3633 <not
3620 <baseset [2]>>>
3634 <baseset [2]>>>
3621 1
3635 1
3622 $ try 'max(1 or 2) and not 2'
3636 $ try 'max(1 or 2) and not 2'
3623 (and
3637 (and
3624 (func
3638 (func
3625 ('symbol', 'max')
3639 ('symbol', 'max')
3626 (or
3640 (or
3627 (list
3641 (list
3628 ('symbol', '1')
3642 ('symbol', '1')
3629 ('symbol', '2'))))
3643 ('symbol', '2'))))
3630 (not
3644 (not
3631 ('symbol', '2')))
3645 ('symbol', '2')))
3632 * set:
3646 * set:
3633 <filteredset
3647 <filteredset
3634 <baseset
3648 <baseset
3635 <max
3649 <max
3636 <fullreposet+ 0:10>,
3650 <fullreposet+ 0:10>,
3637 <baseset [1, 2]>>>,
3651 <baseset [1, 2]>>>,
3638 <not
3652 <not
3639 <baseset [2]>>>
3653 <baseset [2]>>>
3640 $ try 'min(1 or 2) and not 1'
3654 $ try 'min(1 or 2) and not 1'
3641 (and
3655 (and
3642 (func
3656 (func
3643 ('symbol', 'min')
3657 ('symbol', 'min')
3644 (or
3658 (or
3645 (list
3659 (list
3646 ('symbol', '1')
3660 ('symbol', '1')
3647 ('symbol', '2'))))
3661 ('symbol', '2'))))
3648 (not
3662 (not
3649 ('symbol', '1')))
3663 ('symbol', '1')))
3650 * set:
3664 * set:
3651 <filteredset
3665 <filteredset
3652 <baseset
3666 <baseset
3653 <min
3667 <min
3654 <fullreposet+ 0:10>,
3668 <fullreposet+ 0:10>,
3655 <baseset [1, 2]>>>,
3669 <baseset [1, 2]>>>,
3656 <not
3670 <not
3657 <baseset [1]>>>
3671 <baseset [1]>>>
3658 $ try 'last(1 or 2, 1) and not 2'
3672 $ try 'last(1 or 2, 1) and not 2'
3659 (and
3673 (and
3660 (func
3674 (func
3661 ('symbol', 'last')
3675 ('symbol', 'last')
3662 (list
3676 (list
3663 (or
3677 (or
3664 (list
3678 (list
3665 ('symbol', '1')
3679 ('symbol', '1')
3666 ('symbol', '2')))
3680 ('symbol', '2')))
3667 ('symbol', '1')))
3681 ('symbol', '1')))
3668 (not
3682 (not
3669 ('symbol', '2')))
3683 ('symbol', '2')))
3670 * set:
3684 * set:
3671 <filteredset
3685 <filteredset
3672 <baseset
3686 <baseset slice=0:1
3673 <last n=1,
3687 <baseset [2, 1]>>,
3674 <baseset [2, 1]>>>,
3675 <not
3688 <not
3676 <baseset [2]>>>
3689 <baseset [2]>>>
3677
3690
3678 issue4289 - ordering of built-ins
3691 issue4289 - ordering of built-ins
3679 $ hg log -M -q -r 3:2
3692 $ hg log -M -q -r 3:2
3680 3:8528aa5637f2
3693 3:8528aa5637f2
3681 2:5ed5505e9f1c
3694 2:5ed5505e9f1c
3682
3695
3683 test revsets started with 40-chars hash (issue3669)
3696 test revsets started with 40-chars hash (issue3669)
3684
3697
3685 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3698 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3686 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3699 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3687 9
3700 9
3688 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3701 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3689 8
3702 8
3690
3703
3691 test or-ed indirect predicates (issue3775)
3704 test or-ed indirect predicates (issue3775)
3692
3705
3693 $ log '6 or 6^1' | sort
3706 $ log '6 or 6^1' | sort
3694 5
3707 5
3695 6
3708 6
3696 $ log '6^1 or 6' | sort
3709 $ log '6^1 or 6' | sort
3697 5
3710 5
3698 6
3711 6
3699 $ log '4 or 4~1' | sort
3712 $ log '4 or 4~1' | sort
3700 2
3713 2
3701 4
3714 4
3702 $ log '4~1 or 4' | sort
3715 $ log '4~1 or 4' | sort
3703 2
3716 2
3704 4
3717 4
3705 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3718 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3706 0
3719 0
3707 1
3720 1
3708 2
3721 2
3709 3
3722 3
3710 4
3723 4
3711 5
3724 5
3712 6
3725 6
3713 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3726 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3714 0
3727 0
3715 1
3728 1
3716 2
3729 2
3717 3
3730 3
3718 4
3731 4
3719 5
3732 5
3720 6
3733 6
3721
3734
3722 tests for 'remote()' predicate:
3735 tests for 'remote()' predicate:
3723 #. (csets in remote) (id) (remote)
3736 #. (csets in remote) (id) (remote)
3724 1. less than local current branch "default"
3737 1. less than local current branch "default"
3725 2. same with local specified "default"
3738 2. same with local specified "default"
3726 3. more than local specified specified
3739 3. more than local specified specified
3727
3740
3728 $ hg clone --quiet -U . ../remote3
3741 $ hg clone --quiet -U . ../remote3
3729 $ cd ../remote3
3742 $ cd ../remote3
3730 $ hg update -q 7
3743 $ hg update -q 7
3731 $ echo r > r
3744 $ echo r > r
3732 $ hg ci -Aqm 10
3745 $ hg ci -Aqm 10
3733 $ log 'remote()'
3746 $ log 'remote()'
3734 7
3747 7
3735 $ log 'remote("a-b-c-")'
3748 $ log 'remote("a-b-c-")'
3736 2
3749 2
3737 $ cd ../repo
3750 $ cd ../repo
3738 $ log 'remote(".a.b.c.", "../remote3")'
3751 $ log 'remote(".a.b.c.", "../remote3")'
3739
3752
3740 tests for concatenation of strings/symbols by "##"
3753 tests for concatenation of strings/symbols by "##"
3741
3754
3742 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3755 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3743 (_concat
3756 (_concat
3744 (_concat
3757 (_concat
3745 (_concat
3758 (_concat
3746 ('symbol', '278')
3759 ('symbol', '278')
3747 ('string', '5f5'))
3760 ('string', '5f5'))
3748 ('symbol', '1ee'))
3761 ('symbol', '1ee'))
3749 ('string', 'ce5'))
3762 ('string', 'ce5'))
3750 * concatenated:
3763 * concatenated:
3751 ('string', '2785f51eece5')
3764 ('string', '2785f51eece5')
3752 * set:
3765 * set:
3753 <baseset [0]>
3766 <baseset [0]>
3754 0
3767 0
3755
3768
3756 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3769 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3757 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3770 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3758 (func
3771 (func
3759 ('symbol', 'cat4')
3772 ('symbol', 'cat4')
3760 (list
3773 (list
3761 ('symbol', '278')
3774 ('symbol', '278')
3762 ('string', '5f5')
3775 ('string', '5f5')
3763 ('symbol', '1ee')
3776 ('symbol', '1ee')
3764 ('string', 'ce5')))
3777 ('string', 'ce5')))
3765 * expanded:
3778 * expanded:
3766 (_concat
3779 (_concat
3767 (_concat
3780 (_concat
3768 (_concat
3781 (_concat
3769 ('symbol', '278')
3782 ('symbol', '278')
3770 ('string', '5f5'))
3783 ('string', '5f5'))
3771 ('symbol', '1ee'))
3784 ('symbol', '1ee'))
3772 ('string', 'ce5'))
3785 ('string', 'ce5'))
3773 * concatenated:
3786 * concatenated:
3774 ('string', '2785f51eece5')
3787 ('string', '2785f51eece5')
3775 * set:
3788 * set:
3776 <baseset [0]>
3789 <baseset [0]>
3777 0
3790 0
3778
3791
3779 (check concatenation in alias nesting)
3792 (check concatenation in alias nesting)
3780
3793
3781 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3794 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3782 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3795 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3783 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3796 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3784 0
3797 0
3785
3798
3786 (check operator priority)
3799 (check operator priority)
3787
3800
3788 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3801 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3789 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3802 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3790 0
3803 0
3791 4
3804 4
3792
3805
3793 $ cd ..
3806 $ cd ..
3794
3807
3795 prepare repository that has "default" branches of multiple roots
3808 prepare repository that has "default" branches of multiple roots
3796
3809
3797 $ hg init namedbranch
3810 $ hg init namedbranch
3798 $ cd namedbranch
3811 $ cd namedbranch
3799
3812
3800 $ echo default0 >> a
3813 $ echo default0 >> a
3801 $ hg ci -Aqm0
3814 $ hg ci -Aqm0
3802 $ echo default1 >> a
3815 $ echo default1 >> a
3803 $ hg ci -m1
3816 $ hg ci -m1
3804
3817
3805 $ hg branch -q stable
3818 $ hg branch -q stable
3806 $ echo stable2 >> a
3819 $ echo stable2 >> a
3807 $ hg ci -m2
3820 $ hg ci -m2
3808 $ echo stable3 >> a
3821 $ echo stable3 >> a
3809 $ hg ci -m3
3822 $ hg ci -m3
3810
3823
3811 $ hg update -q null
3824 $ hg update -q null
3812 $ echo default4 >> a
3825 $ echo default4 >> a
3813 $ hg ci -Aqm4
3826 $ hg ci -Aqm4
3814 $ echo default5 >> a
3827 $ echo default5 >> a
3815 $ hg ci -m5
3828 $ hg ci -m5
3816
3829
3817 "null" revision belongs to "default" branch (issue4683)
3830 "null" revision belongs to "default" branch (issue4683)
3818
3831
3819 $ log 'branch(null)'
3832 $ log 'branch(null)'
3820 0
3833 0
3821 1
3834 1
3822 4
3835 4
3823 5
3836 5
3824
3837
3825 "null" revision belongs to "default" branch, but it shouldn't appear in set
3838 "null" revision belongs to "default" branch, but it shouldn't appear in set
3826 unless explicitly specified (issue4682)
3839 unless explicitly specified (issue4682)
3827
3840
3828 $ log 'children(branch(default))'
3841 $ log 'children(branch(default))'
3829 1
3842 1
3830 2
3843 2
3831 5
3844 5
3832
3845
3833 $ cd ..
3846 $ cd ..
3834
3847
3835 test author/desc/keyword in problematic encoding
3848 test author/desc/keyword in problematic encoding
3836 # unicode: cp932:
3849 # unicode: cp932:
3837 # u30A2 0x83 0x41(= 'A')
3850 # u30A2 0x83 0x41(= 'A')
3838 # u30C2 0x83 0x61(= 'a')
3851 # u30C2 0x83 0x61(= 'a')
3839
3852
3840 $ hg init problematicencoding
3853 $ hg init problematicencoding
3841 $ cd problematicencoding
3854 $ cd problematicencoding
3842
3855
3843 $ python > setup.sh <<EOF
3856 $ python > setup.sh <<EOF
3844 > print u'''
3857 > print u'''
3845 > echo a > text
3858 > echo a > text
3846 > hg add text
3859 > hg add text
3847 > hg --encoding utf-8 commit -u '\u30A2' -m none
3860 > hg --encoding utf-8 commit -u '\u30A2' -m none
3848 > echo b > text
3861 > echo b > text
3849 > hg --encoding utf-8 commit -u '\u30C2' -m none
3862 > hg --encoding utf-8 commit -u '\u30C2' -m none
3850 > echo c > text
3863 > echo c > text
3851 > hg --encoding utf-8 commit -u none -m '\u30A2'
3864 > hg --encoding utf-8 commit -u none -m '\u30A2'
3852 > echo d > text
3865 > echo d > text
3853 > hg --encoding utf-8 commit -u none -m '\u30C2'
3866 > hg --encoding utf-8 commit -u none -m '\u30C2'
3854 > '''.encode('utf-8')
3867 > '''.encode('utf-8')
3855 > EOF
3868 > EOF
3856 $ sh < setup.sh
3869 $ sh < setup.sh
3857
3870
3858 test in problematic encoding
3871 test in problematic encoding
3859 $ python > test.sh <<EOF
3872 $ python > test.sh <<EOF
3860 > print u'''
3873 > print u'''
3861 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3874 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3862 > echo ====
3875 > echo ====
3863 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3876 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3864 > echo ====
3877 > echo ====
3865 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3878 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3866 > echo ====
3879 > echo ====
3867 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3880 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3868 > echo ====
3881 > echo ====
3869 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3882 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3870 > echo ====
3883 > echo ====
3871 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3884 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3872 > '''.encode('cp932')
3885 > '''.encode('cp932')
3873 > EOF
3886 > EOF
3874 $ sh < test.sh
3887 $ sh < test.sh
3875 0
3888 0
3876 ====
3889 ====
3877 1
3890 1
3878 ====
3891 ====
3879 2
3892 2
3880 ====
3893 ====
3881 3
3894 3
3882 ====
3895 ====
3883 0
3896 0
3884 2
3897 2
3885 ====
3898 ====
3886 1
3899 1
3887 3
3900 3
3888
3901
3889 test error message of bad revset
3902 test error message of bad revset
3890 $ hg log -r 'foo\\'
3903 $ hg log -r 'foo\\'
3891 hg: parse error at 3: syntax error in revset 'foo\\'
3904 hg: parse error at 3: syntax error in revset 'foo\\'
3892 [255]
3905 [255]
3893
3906
3894 $ cd ..
3907 $ cd ..
3895
3908
3896 Test that revset predicate of extension isn't loaded at failure of
3909 Test that revset predicate of extension isn't loaded at failure of
3897 loading it
3910 loading it
3898
3911
3899 $ cd repo
3912 $ cd repo
3900
3913
3901 $ cat <<EOF > $TESTTMP/custompredicate.py
3914 $ cat <<EOF > $TESTTMP/custompredicate.py
3902 > from mercurial import error, registrar, revset
3915 > from mercurial import error, registrar, revset
3903 >
3916 >
3904 > revsetpredicate = registrar.revsetpredicate()
3917 > revsetpredicate = registrar.revsetpredicate()
3905 >
3918 >
3906 > @revsetpredicate('custom1()')
3919 > @revsetpredicate('custom1()')
3907 > def custom1(repo, subset, x):
3920 > def custom1(repo, subset, x):
3908 > return revset.baseset([1])
3921 > return revset.baseset([1])
3909 >
3922 >
3910 > raise error.Abort('intentional failure of loading extension')
3923 > raise error.Abort('intentional failure of loading extension')
3911 > EOF
3924 > EOF
3912 $ cat <<EOF > .hg/hgrc
3925 $ cat <<EOF > .hg/hgrc
3913 > [extensions]
3926 > [extensions]
3914 > custompredicate = $TESTTMP/custompredicate.py
3927 > custompredicate = $TESTTMP/custompredicate.py
3915 > EOF
3928 > EOF
3916
3929
3917 $ hg debugrevspec "custom1()"
3930 $ hg debugrevspec "custom1()"
3918 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3931 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3919 hg: parse error: unknown identifier: custom1
3932 hg: parse error: unknown identifier: custom1
3920 [255]
3933 [255]
3921
3934
3922 $ cd ..
3935 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now