##// END OF EJS Templates
revset: make head() honor order of subset...
Martin von Zweigbergk -
r29408:785cadec default
parent child Browse files
Show More
@@ -1,3668 +1,3666 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11 import re
11 import re
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 destutil,
15 destutil,
16 encoding,
16 encoding,
17 error,
17 error,
18 hbisect,
18 hbisect,
19 match as matchmod,
19 match as matchmod,
20 node,
20 node,
21 obsolete as obsmod,
21 obsolete as obsmod,
22 parser,
22 parser,
23 pathutil,
23 pathutil,
24 phases,
24 phases,
25 registrar,
25 registrar,
26 repoview,
26 repoview,
27 util,
27 util,
28 )
28 )
29
29
30 def _revancestors(repo, revs, followfirst):
30 def _revancestors(repo, revs, followfirst):
31 """Like revlog.ancestors(), but supports followfirst."""
31 """Like revlog.ancestors(), but supports followfirst."""
32 if followfirst:
32 if followfirst:
33 cut = 1
33 cut = 1
34 else:
34 else:
35 cut = None
35 cut = None
36 cl = repo.changelog
36 cl = repo.changelog
37
37
38 def iterate():
38 def iterate():
39 revs.sort(reverse=True)
39 revs.sort(reverse=True)
40 irevs = iter(revs)
40 irevs = iter(revs)
41 h = []
41 h = []
42
42
43 inputrev = next(irevs, None)
43 inputrev = next(irevs, None)
44 if inputrev is not None:
44 if inputrev is not None:
45 heapq.heappush(h, -inputrev)
45 heapq.heappush(h, -inputrev)
46
46
47 seen = set()
47 seen = set()
48 while h:
48 while h:
49 current = -heapq.heappop(h)
49 current = -heapq.heappop(h)
50 if current == inputrev:
50 if current == inputrev:
51 inputrev = next(irevs, None)
51 inputrev = next(irevs, None)
52 if inputrev is not None:
52 if inputrev is not None:
53 heapq.heappush(h, -inputrev)
53 heapq.heappush(h, -inputrev)
54 if current not in seen:
54 if current not in seen:
55 seen.add(current)
55 seen.add(current)
56 yield current
56 yield current
57 for parent in cl.parentrevs(current)[:cut]:
57 for parent in cl.parentrevs(current)[:cut]:
58 if parent != node.nullrev:
58 if parent != node.nullrev:
59 heapq.heappush(h, -parent)
59 heapq.heappush(h, -parent)
60
60
61 return generatorset(iterate(), iterasc=False)
61 return generatorset(iterate(), iterasc=False)
62
62
63 def _revdescendants(repo, revs, followfirst):
63 def _revdescendants(repo, revs, followfirst):
64 """Like revlog.descendants() but supports followfirst."""
64 """Like revlog.descendants() but supports followfirst."""
65 if followfirst:
65 if followfirst:
66 cut = 1
66 cut = 1
67 else:
67 else:
68 cut = None
68 cut = None
69
69
70 def iterate():
70 def iterate():
71 cl = repo.changelog
71 cl = repo.changelog
72 # XXX this should be 'parentset.min()' assuming 'parentset' is a
72 # XXX this should be 'parentset.min()' assuming 'parentset' is a
73 # smartset (and if it is not, it should.)
73 # smartset (and if it is not, it should.)
74 first = min(revs)
74 first = min(revs)
75 nullrev = node.nullrev
75 nullrev = node.nullrev
76 if first == nullrev:
76 if first == nullrev:
77 # Are there nodes with a null first parent and a non-null
77 # Are there nodes with a null first parent and a non-null
78 # second one? Maybe. Do we care? Probably not.
78 # second one? Maybe. Do we care? Probably not.
79 for i in cl:
79 for i in cl:
80 yield i
80 yield i
81 else:
81 else:
82 seen = set(revs)
82 seen = set(revs)
83 for i in cl.revs(first + 1):
83 for i in cl.revs(first + 1):
84 for x in cl.parentrevs(i)[:cut]:
84 for x in cl.parentrevs(i)[:cut]:
85 if x != nullrev and x in seen:
85 if x != nullrev and x in seen:
86 seen.add(i)
86 seen.add(i)
87 yield i
87 yield i
88 break
88 break
89
89
90 return generatorset(iterate(), iterasc=True)
90 return generatorset(iterate(), iterasc=True)
91
91
92 def _reachablerootspure(repo, minroot, roots, heads, includepath):
92 def _reachablerootspure(repo, minroot, roots, heads, includepath):
93 """return (heads(::<roots> and ::<heads>))
93 """return (heads(::<roots> and ::<heads>))
94
94
95 If includepath is True, return (<roots>::<heads>)."""
95 If includepath is True, return (<roots>::<heads>)."""
96 if not roots:
96 if not roots:
97 return []
97 return []
98 parentrevs = repo.changelog.parentrevs
98 parentrevs = repo.changelog.parentrevs
99 roots = set(roots)
99 roots = set(roots)
100 visit = list(heads)
100 visit = list(heads)
101 reachable = set()
101 reachable = set()
102 seen = {}
102 seen = {}
103 # prefetch all the things! (because python is slow)
103 # prefetch all the things! (because python is slow)
104 reached = reachable.add
104 reached = reachable.add
105 dovisit = visit.append
105 dovisit = visit.append
106 nextvisit = visit.pop
106 nextvisit = visit.pop
107 # open-code the post-order traversal due to the tiny size of
107 # open-code the post-order traversal due to the tiny size of
108 # sys.getrecursionlimit()
108 # sys.getrecursionlimit()
109 while visit:
109 while visit:
110 rev = nextvisit()
110 rev = nextvisit()
111 if rev in roots:
111 if rev in roots:
112 reached(rev)
112 reached(rev)
113 if not includepath:
113 if not includepath:
114 continue
114 continue
115 parents = parentrevs(rev)
115 parents = parentrevs(rev)
116 seen[rev] = parents
116 seen[rev] = parents
117 for parent in parents:
117 for parent in parents:
118 if parent >= minroot and parent not in seen:
118 if parent >= minroot and parent not in seen:
119 dovisit(parent)
119 dovisit(parent)
120 if not reachable:
120 if not reachable:
121 return baseset()
121 return baseset()
122 if not includepath:
122 if not includepath:
123 return reachable
123 return reachable
124 for rev in sorted(seen):
124 for rev in sorted(seen):
125 for parent in seen[rev]:
125 for parent in seen[rev]:
126 if parent in reachable:
126 if parent in reachable:
127 reached(rev)
127 reached(rev)
128 return reachable
128 return reachable
129
129
130 def reachableroots(repo, roots, heads, includepath=False):
130 def reachableroots(repo, roots, heads, includepath=False):
131 """return (heads(::<roots> and ::<heads>))
131 """return (heads(::<roots> and ::<heads>))
132
132
133 If includepath is True, return (<roots>::<heads>)."""
133 If includepath is True, return (<roots>::<heads>)."""
134 if not roots:
134 if not roots:
135 return baseset()
135 return baseset()
136 minroot = roots.min()
136 minroot = roots.min()
137 roots = list(roots)
137 roots = list(roots)
138 heads = list(heads)
138 heads = list(heads)
139 try:
139 try:
140 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
140 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
141 except AttributeError:
141 except AttributeError:
142 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
142 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
143 revs = baseset(revs)
143 revs = baseset(revs)
144 revs.sort()
144 revs.sort()
145 return revs
145 return revs
146
146
147 elements = {
147 elements = {
148 # token-type: binding-strength, primary, prefix, infix, suffix
148 # token-type: binding-strength, primary, prefix, infix, suffix
149 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
149 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
150 "##": (20, None, None, ("_concat", 20), None),
150 "##": (20, None, None, ("_concat", 20), None),
151 "~": (18, None, None, ("ancestor", 18), None),
151 "~": (18, None, None, ("ancestor", 18), None),
152 "^": (18, None, None, ("parent", 18), ("parentpost", 18)),
152 "^": (18, None, None, ("parent", 18), ("parentpost", 18)),
153 "-": (5, None, ("negate", 19), ("minus", 5), None),
153 "-": (5, None, ("negate", 19), ("minus", 5), None),
154 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17),
154 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17),
155 ("dagrangepost", 17)),
155 ("dagrangepost", 17)),
156 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17),
156 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17),
157 ("dagrangepost", 17)),
157 ("dagrangepost", 17)),
158 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), ("rangepost", 15)),
158 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), ("rangepost", 15)),
159 "not": (10, None, ("not", 10), None, None),
159 "not": (10, None, ("not", 10), None, None),
160 "!": (10, None, ("not", 10), None, None),
160 "!": (10, None, ("not", 10), None, None),
161 "and": (5, None, None, ("and", 5), None),
161 "and": (5, None, None, ("and", 5), None),
162 "&": (5, None, None, ("and", 5), None),
162 "&": (5, None, None, ("and", 5), None),
163 "%": (5, None, None, ("only", 5), ("onlypost", 5)),
163 "%": (5, None, None, ("only", 5), ("onlypost", 5)),
164 "or": (4, None, None, ("or", 4), None),
164 "or": (4, None, None, ("or", 4), None),
165 "|": (4, None, None, ("or", 4), None),
165 "|": (4, None, None, ("or", 4), None),
166 "+": (4, None, None, ("or", 4), None),
166 "+": (4, None, None, ("or", 4), None),
167 "=": (3, None, None, ("keyvalue", 3), None),
167 "=": (3, None, None, ("keyvalue", 3), None),
168 ",": (2, None, None, ("list", 2), None),
168 ",": (2, None, None, ("list", 2), None),
169 ")": (0, None, None, None, None),
169 ")": (0, None, None, None, None),
170 "symbol": (0, "symbol", None, None, None),
170 "symbol": (0, "symbol", None, None, None),
171 "string": (0, "string", None, None, None),
171 "string": (0, "string", None, None, None),
172 "end": (0, None, None, None, None),
172 "end": (0, None, None, None, None),
173 }
173 }
174
174
175 keywords = set(['and', 'or', 'not'])
175 keywords = set(['and', 'or', 'not'])
176
176
177 # default set of valid characters for the initial letter of symbols
177 # default set of valid characters for the initial letter of symbols
178 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
178 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
179 if c.isalnum() or c in '._@' or ord(c) > 127)
179 if c.isalnum() or c in '._@' or ord(c) > 127)
180
180
181 # default set of valid characters for non-initial letters of symbols
181 # default set of valid characters for non-initial letters of symbols
182 _symletters = set(c for c in [chr(i) for i in xrange(256)]
182 _symletters = set(c for c in [chr(i) for i in xrange(256)]
183 if c.isalnum() or c in '-._/@' or ord(c) > 127)
183 if c.isalnum() or c in '-._/@' or ord(c) > 127)
184
184
185 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
185 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
186 '''
186 '''
187 Parse a revset statement into a stream of tokens
187 Parse a revset statement into a stream of tokens
188
188
189 ``syminitletters`` is the set of valid characters for the initial
189 ``syminitletters`` is the set of valid characters for the initial
190 letter of symbols.
190 letter of symbols.
191
191
192 By default, character ``c`` is recognized as valid for initial
192 By default, character ``c`` is recognized as valid for initial
193 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
193 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
194
194
195 ``symletters`` is the set of valid characters for non-initial
195 ``symletters`` is the set of valid characters for non-initial
196 letters of symbols.
196 letters of symbols.
197
197
198 By default, character ``c`` is recognized as valid for non-initial
198 By default, character ``c`` is recognized as valid for non-initial
199 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
199 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
200
200
201 Check that @ is a valid unquoted token character (issue3686):
201 Check that @ is a valid unquoted token character (issue3686):
202 >>> list(tokenize("@::"))
202 >>> list(tokenize("@::"))
203 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
203 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
204
204
205 '''
205 '''
206 if syminitletters is None:
206 if syminitletters is None:
207 syminitletters = _syminitletters
207 syminitletters = _syminitletters
208 if symletters is None:
208 if symletters is None:
209 symletters = _symletters
209 symletters = _symletters
210
210
211 if program and lookup:
211 if program and lookup:
212 # attempt to parse old-style ranges first to deal with
212 # attempt to parse old-style ranges first to deal with
213 # things like old-tag which contain query metacharacters
213 # things like old-tag which contain query metacharacters
214 parts = program.split(':', 1)
214 parts = program.split(':', 1)
215 if all(lookup(sym) for sym in parts if sym):
215 if all(lookup(sym) for sym in parts if sym):
216 if parts[0]:
216 if parts[0]:
217 yield ('symbol', parts[0], 0)
217 yield ('symbol', parts[0], 0)
218 if len(parts) > 1:
218 if len(parts) > 1:
219 s = len(parts[0])
219 s = len(parts[0])
220 yield (':', None, s)
220 yield (':', None, s)
221 if parts[1]:
221 if parts[1]:
222 yield ('symbol', parts[1], s + 1)
222 yield ('symbol', parts[1], s + 1)
223 yield ('end', None, len(program))
223 yield ('end', None, len(program))
224 return
224 return
225
225
226 pos, l = 0, len(program)
226 pos, l = 0, len(program)
227 while pos < l:
227 while pos < l:
228 c = program[pos]
228 c = program[pos]
229 if c.isspace(): # skip inter-token whitespace
229 if c.isspace(): # skip inter-token whitespace
230 pass
230 pass
231 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
231 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
232 yield ('::', None, pos)
232 yield ('::', None, pos)
233 pos += 1 # skip ahead
233 pos += 1 # skip ahead
234 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
234 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
235 yield ('..', None, pos)
235 yield ('..', None, pos)
236 pos += 1 # skip ahead
236 pos += 1 # skip ahead
237 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
237 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
238 yield ('##', None, pos)
238 yield ('##', None, pos)
239 pos += 1 # skip ahead
239 pos += 1 # skip ahead
240 elif c in "():=,-|&+!~^%": # handle simple operators
240 elif c in "():=,-|&+!~^%": # handle simple operators
241 yield (c, None, pos)
241 yield (c, None, pos)
242 elif (c in '"\'' or c == 'r' and
242 elif (c in '"\'' or c == 'r' and
243 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
243 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
244 if c == 'r':
244 if c == 'r':
245 pos += 1
245 pos += 1
246 c = program[pos]
246 c = program[pos]
247 decode = lambda x: x
247 decode = lambda x: x
248 else:
248 else:
249 decode = parser.unescapestr
249 decode = parser.unescapestr
250 pos += 1
250 pos += 1
251 s = pos
251 s = pos
252 while pos < l: # find closing quote
252 while pos < l: # find closing quote
253 d = program[pos]
253 d = program[pos]
254 if d == '\\': # skip over escaped characters
254 if d == '\\': # skip over escaped characters
255 pos += 2
255 pos += 2
256 continue
256 continue
257 if d == c:
257 if d == c:
258 yield ('string', decode(program[s:pos]), s)
258 yield ('string', decode(program[s:pos]), s)
259 break
259 break
260 pos += 1
260 pos += 1
261 else:
261 else:
262 raise error.ParseError(_("unterminated string"), s)
262 raise error.ParseError(_("unterminated string"), s)
263 # gather up a symbol/keyword
263 # gather up a symbol/keyword
264 elif c in syminitletters:
264 elif c in syminitletters:
265 s = pos
265 s = pos
266 pos += 1
266 pos += 1
267 while pos < l: # find end of symbol
267 while pos < l: # find end of symbol
268 d = program[pos]
268 d = program[pos]
269 if d not in symletters:
269 if d not in symletters:
270 break
270 break
271 if d == '.' and program[pos - 1] == '.': # special case for ..
271 if d == '.' and program[pos - 1] == '.': # special case for ..
272 pos -= 1
272 pos -= 1
273 break
273 break
274 pos += 1
274 pos += 1
275 sym = program[s:pos]
275 sym = program[s:pos]
276 if sym in keywords: # operator keywords
276 if sym in keywords: # operator keywords
277 yield (sym, None, s)
277 yield (sym, None, s)
278 elif '-' in sym:
278 elif '-' in sym:
279 # some jerk gave us foo-bar-baz, try to check if it's a symbol
279 # some jerk gave us foo-bar-baz, try to check if it's a symbol
280 if lookup and lookup(sym):
280 if lookup and lookup(sym):
281 # looks like a real symbol
281 # looks like a real symbol
282 yield ('symbol', sym, s)
282 yield ('symbol', sym, s)
283 else:
283 else:
284 # looks like an expression
284 # looks like an expression
285 parts = sym.split('-')
285 parts = sym.split('-')
286 for p in parts[:-1]:
286 for p in parts[:-1]:
287 if p: # possible consecutive -
287 if p: # possible consecutive -
288 yield ('symbol', p, s)
288 yield ('symbol', p, s)
289 s += len(p)
289 s += len(p)
290 yield ('-', None, pos)
290 yield ('-', None, pos)
291 s += 1
291 s += 1
292 if parts[-1]: # possible trailing -
292 if parts[-1]: # possible trailing -
293 yield ('symbol', parts[-1], s)
293 yield ('symbol', parts[-1], s)
294 else:
294 else:
295 yield ('symbol', sym, s)
295 yield ('symbol', sym, s)
296 pos -= 1
296 pos -= 1
297 else:
297 else:
298 raise error.ParseError(_("syntax error in revset '%s'") %
298 raise error.ParseError(_("syntax error in revset '%s'") %
299 program, pos)
299 program, pos)
300 pos += 1
300 pos += 1
301 yield ('end', None, pos)
301 yield ('end', None, pos)
302
302
303 # helpers
303 # helpers
304
304
305 def getstring(x, err):
305 def getstring(x, err):
306 if x and (x[0] == 'string' or x[0] == 'symbol'):
306 if x and (x[0] == 'string' or x[0] == 'symbol'):
307 return x[1]
307 return x[1]
308 raise error.ParseError(err)
308 raise error.ParseError(err)
309
309
310 def getlist(x):
310 def getlist(x):
311 if not x:
311 if not x:
312 return []
312 return []
313 if x[0] == 'list':
313 if x[0] == 'list':
314 return list(x[1:])
314 return list(x[1:])
315 return [x]
315 return [x]
316
316
317 def getargs(x, min, max, err):
317 def getargs(x, min, max, err):
318 l = getlist(x)
318 l = getlist(x)
319 if len(l) < min or (max >= 0 and len(l) > max):
319 if len(l) < min or (max >= 0 and len(l) > max):
320 raise error.ParseError(err)
320 raise error.ParseError(err)
321 return l
321 return l
322
322
323 def getargsdict(x, funcname, keys):
323 def getargsdict(x, funcname, keys):
324 return parser.buildargsdict(getlist(x), funcname, keys.split(),
324 return parser.buildargsdict(getlist(x), funcname, keys.split(),
325 keyvaluenode='keyvalue', keynode='symbol')
325 keyvaluenode='keyvalue', keynode='symbol')
326
326
327 def getset(repo, subset, x):
327 def getset(repo, subset, x):
328 if not x:
328 if not x:
329 raise error.ParseError(_("missing argument"))
329 raise error.ParseError(_("missing argument"))
330 s = methods[x[0]](repo, subset, *x[1:])
330 s = methods[x[0]](repo, subset, *x[1:])
331 if util.safehasattr(s, 'isascending'):
331 if util.safehasattr(s, 'isascending'):
332 return s
332 return s
333 # else case should not happen, because all non-func are internal,
333 # else case should not happen, because all non-func are internal,
334 # ignoring for now.
334 # ignoring for now.
335 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
335 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
336 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
336 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
337 % x[1][1],
337 % x[1][1],
338 '3.9')
338 '3.9')
339 return baseset(s)
339 return baseset(s)
340
340
341 def _getrevsource(repo, r):
341 def _getrevsource(repo, r):
342 extra = repo[r].extra()
342 extra = repo[r].extra()
343 for label in ('source', 'transplant_source', 'rebase_source'):
343 for label in ('source', 'transplant_source', 'rebase_source'):
344 if label in extra:
344 if label in extra:
345 try:
345 try:
346 return repo[extra[label]].rev()
346 return repo[extra[label]].rev()
347 except error.RepoLookupError:
347 except error.RepoLookupError:
348 pass
348 pass
349 return None
349 return None
350
350
351 # operator methods
351 # operator methods
352
352
353 def stringset(repo, subset, x):
353 def stringset(repo, subset, x):
354 x = repo[x].rev()
354 x = repo[x].rev()
355 if (x in subset
355 if (x in subset
356 or x == node.nullrev and isinstance(subset, fullreposet)):
356 or x == node.nullrev and isinstance(subset, fullreposet)):
357 return baseset([x])
357 return baseset([x])
358 return baseset()
358 return baseset()
359
359
360 def rangeset(repo, subset, x, y):
360 def rangeset(repo, subset, x, y):
361 m = getset(repo, fullreposet(repo), x)
361 m = getset(repo, fullreposet(repo), x)
362 n = getset(repo, fullreposet(repo), y)
362 n = getset(repo, fullreposet(repo), y)
363
363
364 if not m or not n:
364 if not m or not n:
365 return baseset()
365 return baseset()
366 m, n = m.first(), n.last()
366 m, n = m.first(), n.last()
367
367
368 if m == n:
368 if m == n:
369 r = baseset([m])
369 r = baseset([m])
370 elif n == node.wdirrev:
370 elif n == node.wdirrev:
371 r = spanset(repo, m, len(repo)) + baseset([n])
371 r = spanset(repo, m, len(repo)) + baseset([n])
372 elif m == node.wdirrev:
372 elif m == node.wdirrev:
373 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
373 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
374 elif m < n:
374 elif m < n:
375 r = spanset(repo, m, n + 1)
375 r = spanset(repo, m, n + 1)
376 else:
376 else:
377 r = spanset(repo, m, n - 1)
377 r = spanset(repo, m, n - 1)
378 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
378 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
379 # necessary to ensure we preserve the order in subset.
379 # necessary to ensure we preserve the order in subset.
380 #
380 #
381 # This has performance implication, carrying the sorting over when possible
381 # This has performance implication, carrying the sorting over when possible
382 # would be more efficient.
382 # would be more efficient.
383 return r & subset
383 return r & subset
384
384
385 def dagrange(repo, subset, x, y):
385 def dagrange(repo, subset, x, y):
386 r = fullreposet(repo)
386 r = fullreposet(repo)
387 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
387 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
388 includepath=True)
388 includepath=True)
389 return subset & xs
389 return subset & xs
390
390
391 def andset(repo, subset, x, y):
391 def andset(repo, subset, x, y):
392 return getset(repo, getset(repo, subset, x), y)
392 return getset(repo, getset(repo, subset, x), y)
393
393
394 def differenceset(repo, subset, x, y):
394 def differenceset(repo, subset, x, y):
395 return getset(repo, subset, x) - getset(repo, subset, y)
395 return getset(repo, subset, x) - getset(repo, subset, y)
396
396
397 def orset(repo, subset, *xs):
397 def orset(repo, subset, *xs):
398 assert xs
398 assert xs
399 if len(xs) == 1:
399 if len(xs) == 1:
400 return getset(repo, subset, xs[0])
400 return getset(repo, subset, xs[0])
401 p = len(xs) // 2
401 p = len(xs) // 2
402 a = orset(repo, subset, *xs[:p])
402 a = orset(repo, subset, *xs[:p])
403 b = orset(repo, subset, *xs[p:])
403 b = orset(repo, subset, *xs[p:])
404 return a + b
404 return a + b
405
405
406 def notset(repo, subset, x):
406 def notset(repo, subset, x):
407 return subset - getset(repo, subset, x)
407 return subset - getset(repo, subset, x)
408
408
409 def listset(repo, subset, *xs):
409 def listset(repo, subset, *xs):
410 raise error.ParseError(_("can't use a list in this context"),
410 raise error.ParseError(_("can't use a list in this context"),
411 hint=_('see hg help "revsets.x or y"'))
411 hint=_('see hg help "revsets.x or y"'))
412
412
413 def keyvaluepair(repo, subset, k, v):
413 def keyvaluepair(repo, subset, k, v):
414 raise error.ParseError(_("can't use a key-value pair in this context"))
414 raise error.ParseError(_("can't use a key-value pair in this context"))
415
415
416 def func(repo, subset, a, b):
416 def func(repo, subset, a, b):
417 if a[0] == 'symbol' and a[1] in symbols:
417 if a[0] == 'symbol' and a[1] in symbols:
418 return symbols[a[1]](repo, subset, b)
418 return symbols[a[1]](repo, subset, b)
419
419
420 keep = lambda fn: getattr(fn, '__doc__', None) is not None
420 keep = lambda fn: getattr(fn, '__doc__', None) is not None
421
421
422 syms = [s for (s, fn) in symbols.items() if keep(fn)]
422 syms = [s for (s, fn) in symbols.items() if keep(fn)]
423 raise error.UnknownIdentifier(a[1], syms)
423 raise error.UnknownIdentifier(a[1], syms)
424
424
425 # functions
425 # functions
426
426
427 # symbols are callables like:
427 # symbols are callables like:
428 # fn(repo, subset, x)
428 # fn(repo, subset, x)
429 # with:
429 # with:
430 # repo - current repository instance
430 # repo - current repository instance
431 # subset - of revisions to be examined
431 # subset - of revisions to be examined
432 # x - argument in tree form
432 # x - argument in tree form
433 symbols = {}
433 symbols = {}
434
434
435 # symbols which can't be used for a DoS attack for any given input
435 # symbols which can't be used for a DoS attack for any given input
436 # (e.g. those which accept regexes as plain strings shouldn't be included)
436 # (e.g. those which accept regexes as plain strings shouldn't be included)
437 # functions that just return a lot of changesets (like all) don't count here
437 # functions that just return a lot of changesets (like all) don't count here
438 safesymbols = set()
438 safesymbols = set()
439
439
440 predicate = registrar.revsetpredicate()
440 predicate = registrar.revsetpredicate()
441
441
442 @predicate('_destupdate')
442 @predicate('_destupdate')
443 def _destupdate(repo, subset, x):
443 def _destupdate(repo, subset, x):
444 # experimental revset for update destination
444 # experimental revset for update destination
445 args = getargsdict(x, 'limit', 'clean check')
445 args = getargsdict(x, 'limit', 'clean check')
446 return subset & baseset([destutil.destupdate(repo, **args)[0]])
446 return subset & baseset([destutil.destupdate(repo, **args)[0]])
447
447
448 @predicate('_destmerge')
448 @predicate('_destmerge')
449 def _destmerge(repo, subset, x):
449 def _destmerge(repo, subset, x):
450 # experimental revset for merge destination
450 # experimental revset for merge destination
451 sourceset = None
451 sourceset = None
452 if x is not None:
452 if x is not None:
453 sourceset = getset(repo, fullreposet(repo), x)
453 sourceset = getset(repo, fullreposet(repo), x)
454 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
454 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
455
455
456 @predicate('adds(pattern)', safe=True)
456 @predicate('adds(pattern)', safe=True)
457 def adds(repo, subset, x):
457 def adds(repo, subset, x):
458 """Changesets that add a file matching pattern.
458 """Changesets that add a file matching pattern.
459
459
460 The pattern without explicit kind like ``glob:`` is expected to be
460 The pattern without explicit kind like ``glob:`` is expected to be
461 relative to the current directory and match against a file or a
461 relative to the current directory and match against a file or a
462 directory.
462 directory.
463 """
463 """
464 # i18n: "adds" is a keyword
464 # i18n: "adds" is a keyword
465 pat = getstring(x, _("adds requires a pattern"))
465 pat = getstring(x, _("adds requires a pattern"))
466 return checkstatus(repo, subset, pat, 1)
466 return checkstatus(repo, subset, pat, 1)
467
467
468 @predicate('ancestor(*changeset)', safe=True)
468 @predicate('ancestor(*changeset)', safe=True)
469 def ancestor(repo, subset, x):
469 def ancestor(repo, subset, x):
470 """A greatest common ancestor of the changesets.
470 """A greatest common ancestor of the changesets.
471
471
472 Accepts 0 or more changesets.
472 Accepts 0 or more changesets.
473 Will return empty list when passed no args.
473 Will return empty list when passed no args.
474 Greatest common ancestor of a single changeset is that changeset.
474 Greatest common ancestor of a single changeset is that changeset.
475 """
475 """
476 # i18n: "ancestor" is a keyword
476 # i18n: "ancestor" is a keyword
477 l = getlist(x)
477 l = getlist(x)
478 rl = fullreposet(repo)
478 rl = fullreposet(repo)
479 anc = None
479 anc = None
480
480
481 # (getset(repo, rl, i) for i in l) generates a list of lists
481 # (getset(repo, rl, i) for i in l) generates a list of lists
482 for revs in (getset(repo, rl, i) for i in l):
482 for revs in (getset(repo, rl, i) for i in l):
483 for r in revs:
483 for r in revs:
484 if anc is None:
484 if anc is None:
485 anc = repo[r]
485 anc = repo[r]
486 else:
486 else:
487 anc = anc.ancestor(repo[r])
487 anc = anc.ancestor(repo[r])
488
488
489 if anc is not None and anc.rev() in subset:
489 if anc is not None and anc.rev() in subset:
490 return baseset([anc.rev()])
490 return baseset([anc.rev()])
491 return baseset()
491 return baseset()
492
492
493 def _ancestors(repo, subset, x, followfirst=False):
493 def _ancestors(repo, subset, x, followfirst=False):
494 heads = getset(repo, fullreposet(repo), x)
494 heads = getset(repo, fullreposet(repo), x)
495 if not heads:
495 if not heads:
496 return baseset()
496 return baseset()
497 s = _revancestors(repo, heads, followfirst)
497 s = _revancestors(repo, heads, followfirst)
498 return subset & s
498 return subset & s
499
499
500 @predicate('ancestors(set)', safe=True)
500 @predicate('ancestors(set)', safe=True)
501 def ancestors(repo, subset, x):
501 def ancestors(repo, subset, x):
502 """Changesets that are ancestors of a changeset in set.
502 """Changesets that are ancestors of a changeset in set.
503 """
503 """
504 return _ancestors(repo, subset, x)
504 return _ancestors(repo, subset, x)
505
505
506 @predicate('_firstancestors', safe=True)
506 @predicate('_firstancestors', safe=True)
507 def _firstancestors(repo, subset, x):
507 def _firstancestors(repo, subset, x):
508 # ``_firstancestors(set)``
508 # ``_firstancestors(set)``
509 # Like ``ancestors(set)`` but follows only the first parents.
509 # Like ``ancestors(set)`` but follows only the first parents.
510 return _ancestors(repo, subset, x, followfirst=True)
510 return _ancestors(repo, subset, x, followfirst=True)
511
511
512 def ancestorspec(repo, subset, x, n):
512 def ancestorspec(repo, subset, x, n):
513 """``set~n``
513 """``set~n``
514 Changesets that are the Nth ancestor (first parents only) of a changeset
514 Changesets that are the Nth ancestor (first parents only) of a changeset
515 in set.
515 in set.
516 """
516 """
517 try:
517 try:
518 n = int(n[1])
518 n = int(n[1])
519 except (TypeError, ValueError):
519 except (TypeError, ValueError):
520 raise error.ParseError(_("~ expects a number"))
520 raise error.ParseError(_("~ expects a number"))
521 ps = set()
521 ps = set()
522 cl = repo.changelog
522 cl = repo.changelog
523 for r in getset(repo, fullreposet(repo), x):
523 for r in getset(repo, fullreposet(repo), x):
524 for i in range(n):
524 for i in range(n):
525 r = cl.parentrevs(r)[0]
525 r = cl.parentrevs(r)[0]
526 ps.add(r)
526 ps.add(r)
527 return subset & ps
527 return subset & ps
528
528
529 @predicate('author(string)', safe=True)
529 @predicate('author(string)', safe=True)
530 def author(repo, subset, x):
530 def author(repo, subset, x):
531 """Alias for ``user(string)``.
531 """Alias for ``user(string)``.
532 """
532 """
533 # i18n: "author" is a keyword
533 # i18n: "author" is a keyword
534 n = encoding.lower(getstring(x, _("author requires a string")))
534 n = encoding.lower(getstring(x, _("author requires a string")))
535 kind, pattern, matcher = _substringmatcher(n)
535 kind, pattern, matcher = _substringmatcher(n)
536 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
536 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
537 condrepr=('<user %r>', n))
537 condrepr=('<user %r>', n))
538
538
539 @predicate('bisect(string)', safe=True)
539 @predicate('bisect(string)', safe=True)
540 def bisect(repo, subset, x):
540 def bisect(repo, subset, x):
541 """Changesets marked in the specified bisect status:
541 """Changesets marked in the specified bisect status:
542
542
543 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
543 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
544 - ``goods``, ``bads`` : csets topologically good/bad
544 - ``goods``, ``bads`` : csets topologically good/bad
545 - ``range`` : csets taking part in the bisection
545 - ``range`` : csets taking part in the bisection
546 - ``pruned`` : csets that are goods, bads or skipped
546 - ``pruned`` : csets that are goods, bads or skipped
547 - ``untested`` : csets whose fate is yet unknown
547 - ``untested`` : csets whose fate is yet unknown
548 - ``ignored`` : csets ignored due to DAG topology
548 - ``ignored`` : csets ignored due to DAG topology
549 - ``current`` : the cset currently being bisected
549 - ``current`` : the cset currently being bisected
550 """
550 """
551 # i18n: "bisect" is a keyword
551 # i18n: "bisect" is a keyword
552 status = getstring(x, _("bisect requires a string")).lower()
552 status = getstring(x, _("bisect requires a string")).lower()
553 state = set(hbisect.get(repo, status))
553 state = set(hbisect.get(repo, status))
554 return subset & state
554 return subset & state
555
555
556 # Backward-compatibility
556 # Backward-compatibility
557 # - no help entry so that we do not advertise it any more
557 # - no help entry so that we do not advertise it any more
558 @predicate('bisected', safe=True)
558 @predicate('bisected', safe=True)
559 def bisected(repo, subset, x):
559 def bisected(repo, subset, x):
560 return bisect(repo, subset, x)
560 return bisect(repo, subset, x)
561
561
562 @predicate('bookmark([name])', safe=True)
562 @predicate('bookmark([name])', safe=True)
563 def bookmark(repo, subset, x):
563 def bookmark(repo, subset, x):
564 """The named bookmark or all bookmarks.
564 """The named bookmark or all bookmarks.
565
565
566 If `name` starts with `re:`, the remainder of the name is treated as
566 If `name` starts with `re:`, the remainder of the name is treated as
567 a regular expression. To match a bookmark that actually starts with `re:`,
567 a regular expression. To match a bookmark that actually starts with `re:`,
568 use the prefix `literal:`.
568 use the prefix `literal:`.
569 """
569 """
570 # i18n: "bookmark" is a keyword
570 # i18n: "bookmark" is a keyword
571 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
571 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
572 if args:
572 if args:
573 bm = getstring(args[0],
573 bm = getstring(args[0],
574 # i18n: "bookmark" is a keyword
574 # i18n: "bookmark" is a keyword
575 _('the argument to bookmark must be a string'))
575 _('the argument to bookmark must be a string'))
576 kind, pattern, matcher = util.stringmatcher(bm)
576 kind, pattern, matcher = util.stringmatcher(bm)
577 bms = set()
577 bms = set()
578 if kind == 'literal':
578 if kind == 'literal':
579 bmrev = repo._bookmarks.get(pattern, None)
579 bmrev = repo._bookmarks.get(pattern, None)
580 if not bmrev:
580 if not bmrev:
581 raise error.RepoLookupError(_("bookmark '%s' does not exist")
581 raise error.RepoLookupError(_("bookmark '%s' does not exist")
582 % pattern)
582 % pattern)
583 bms.add(repo[bmrev].rev())
583 bms.add(repo[bmrev].rev())
584 else:
584 else:
585 matchrevs = set()
585 matchrevs = set()
586 for name, bmrev in repo._bookmarks.iteritems():
586 for name, bmrev in repo._bookmarks.iteritems():
587 if matcher(name):
587 if matcher(name):
588 matchrevs.add(bmrev)
588 matchrevs.add(bmrev)
589 if not matchrevs:
589 if not matchrevs:
590 raise error.RepoLookupError(_("no bookmarks exist"
590 raise error.RepoLookupError(_("no bookmarks exist"
591 " that match '%s'") % pattern)
591 " that match '%s'") % pattern)
592 for bmrev in matchrevs:
592 for bmrev in matchrevs:
593 bms.add(repo[bmrev].rev())
593 bms.add(repo[bmrev].rev())
594 else:
594 else:
595 bms = set([repo[r].rev()
595 bms = set([repo[r].rev()
596 for r in repo._bookmarks.values()])
596 for r in repo._bookmarks.values()])
597 bms -= set([node.nullrev])
597 bms -= set([node.nullrev])
598 return subset & bms
598 return subset & bms
599
599
600 @predicate('branch(string or set)', safe=True)
600 @predicate('branch(string or set)', safe=True)
601 def branch(repo, subset, x):
601 def branch(repo, subset, x):
602 """
602 """
603 All changesets belonging to the given branch or the branches of the given
603 All changesets belonging to the given branch or the branches of the given
604 changesets.
604 changesets.
605
605
606 If `string` starts with `re:`, the remainder of the name is treated as
606 If `string` starts with `re:`, the remainder of the name is treated as
607 a regular expression. To match a branch that actually starts with `re:`,
607 a regular expression. To match a branch that actually starts with `re:`,
608 use the prefix `literal:`.
608 use the prefix `literal:`.
609 """
609 """
610 getbi = repo.revbranchcache().branchinfo
610 getbi = repo.revbranchcache().branchinfo
611
611
612 try:
612 try:
613 b = getstring(x, '')
613 b = getstring(x, '')
614 except error.ParseError:
614 except error.ParseError:
615 # not a string, but another revspec, e.g. tip()
615 # not a string, but another revspec, e.g. tip()
616 pass
616 pass
617 else:
617 else:
618 kind, pattern, matcher = util.stringmatcher(b)
618 kind, pattern, matcher = util.stringmatcher(b)
619 if kind == 'literal':
619 if kind == 'literal':
620 # note: falls through to the revspec case if no branch with
620 # note: falls through to the revspec case if no branch with
621 # this name exists and pattern kind is not specified explicitly
621 # this name exists and pattern kind is not specified explicitly
622 if pattern in repo.branchmap():
622 if pattern in repo.branchmap():
623 return subset.filter(lambda r: matcher(getbi(r)[0]),
623 return subset.filter(lambda r: matcher(getbi(r)[0]),
624 condrepr=('<branch %r>', b))
624 condrepr=('<branch %r>', b))
625 if b.startswith('literal:'):
625 if b.startswith('literal:'):
626 raise error.RepoLookupError(_("branch '%s' does not exist")
626 raise error.RepoLookupError(_("branch '%s' does not exist")
627 % pattern)
627 % pattern)
628 else:
628 else:
629 return subset.filter(lambda r: matcher(getbi(r)[0]),
629 return subset.filter(lambda r: matcher(getbi(r)[0]),
630 condrepr=('<branch %r>', b))
630 condrepr=('<branch %r>', b))
631
631
632 s = getset(repo, fullreposet(repo), x)
632 s = getset(repo, fullreposet(repo), x)
633 b = set()
633 b = set()
634 for r in s:
634 for r in s:
635 b.add(getbi(r)[0])
635 b.add(getbi(r)[0])
636 c = s.__contains__
636 c = s.__contains__
637 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
637 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
638 condrepr=lambda: '<branch %r>' % sorted(b))
638 condrepr=lambda: '<branch %r>' % sorted(b))
639
639
640 @predicate('bumped()', safe=True)
640 @predicate('bumped()', safe=True)
641 def bumped(repo, subset, x):
641 def bumped(repo, subset, x):
642 """Mutable changesets marked as successors of public changesets.
642 """Mutable changesets marked as successors of public changesets.
643
643
644 Only non-public and non-obsolete changesets can be `bumped`.
644 Only non-public and non-obsolete changesets can be `bumped`.
645 """
645 """
646 # i18n: "bumped" is a keyword
646 # i18n: "bumped" is a keyword
647 getargs(x, 0, 0, _("bumped takes no arguments"))
647 getargs(x, 0, 0, _("bumped takes no arguments"))
648 bumped = obsmod.getrevs(repo, 'bumped')
648 bumped = obsmod.getrevs(repo, 'bumped')
649 return subset & bumped
649 return subset & bumped
650
650
651 @predicate('bundle()', safe=True)
651 @predicate('bundle()', safe=True)
652 def bundle(repo, subset, x):
652 def bundle(repo, subset, x):
653 """Changesets in the bundle.
653 """Changesets in the bundle.
654
654
655 Bundle must be specified by the -R option."""
655 Bundle must be specified by the -R option."""
656
656
657 try:
657 try:
658 bundlerevs = repo.changelog.bundlerevs
658 bundlerevs = repo.changelog.bundlerevs
659 except AttributeError:
659 except AttributeError:
660 raise error.Abort(_("no bundle provided - specify with -R"))
660 raise error.Abort(_("no bundle provided - specify with -R"))
661 return subset & bundlerevs
661 return subset & bundlerevs
662
662
663 def checkstatus(repo, subset, pat, field):
663 def checkstatus(repo, subset, pat, field):
664 hasset = matchmod.patkind(pat) == 'set'
664 hasset = matchmod.patkind(pat) == 'set'
665
665
666 mcache = [None]
666 mcache = [None]
667 def matches(x):
667 def matches(x):
668 c = repo[x]
668 c = repo[x]
669 if not mcache[0] or hasset:
669 if not mcache[0] or hasset:
670 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
670 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
671 m = mcache[0]
671 m = mcache[0]
672 fname = None
672 fname = None
673 if not m.anypats() and len(m.files()) == 1:
673 if not m.anypats() and len(m.files()) == 1:
674 fname = m.files()[0]
674 fname = m.files()[0]
675 if fname is not None:
675 if fname is not None:
676 if fname not in c.files():
676 if fname not in c.files():
677 return False
677 return False
678 else:
678 else:
679 for f in c.files():
679 for f in c.files():
680 if m(f):
680 if m(f):
681 break
681 break
682 else:
682 else:
683 return False
683 return False
684 files = repo.status(c.p1().node(), c.node())[field]
684 files = repo.status(c.p1().node(), c.node())[field]
685 if fname is not None:
685 if fname is not None:
686 if fname in files:
686 if fname in files:
687 return True
687 return True
688 else:
688 else:
689 for f in files:
689 for f in files:
690 if m(f):
690 if m(f):
691 return True
691 return True
692
692
693 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
693 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
694
694
695 def _children(repo, subset, parentset):
695 def _children(repo, subset, parentset):
696 if not parentset:
696 if not parentset:
697 return baseset()
697 return baseset()
698 cs = set()
698 cs = set()
699 pr = repo.changelog.parentrevs
699 pr = repo.changelog.parentrevs
700 minrev = parentset.min()
700 minrev = parentset.min()
701 for r in subset:
701 for r in subset:
702 if r <= minrev:
702 if r <= minrev:
703 continue
703 continue
704 for p in pr(r):
704 for p in pr(r):
705 if p in parentset:
705 if p in parentset:
706 cs.add(r)
706 cs.add(r)
707 return baseset(cs)
707 return baseset(cs)
708
708
709 @predicate('children(set)', safe=True)
709 @predicate('children(set)', safe=True)
710 def children(repo, subset, x):
710 def children(repo, subset, x):
711 """Child changesets of changesets in set.
711 """Child changesets of changesets in set.
712 """
712 """
713 s = getset(repo, fullreposet(repo), x)
713 s = getset(repo, fullreposet(repo), x)
714 cs = _children(repo, subset, s)
714 cs = _children(repo, subset, s)
715 return subset & cs
715 return subset & cs
716
716
717 @predicate('closed()', safe=True)
717 @predicate('closed()', safe=True)
718 def closed(repo, subset, x):
718 def closed(repo, subset, x):
719 """Changeset is closed.
719 """Changeset is closed.
720 """
720 """
721 # i18n: "closed" is a keyword
721 # i18n: "closed" is a keyword
722 getargs(x, 0, 0, _("closed takes no arguments"))
722 getargs(x, 0, 0, _("closed takes no arguments"))
723 return subset.filter(lambda r: repo[r].closesbranch(),
723 return subset.filter(lambda r: repo[r].closesbranch(),
724 condrepr='<branch closed>')
724 condrepr='<branch closed>')
725
725
726 @predicate('contains(pattern)')
726 @predicate('contains(pattern)')
727 def contains(repo, subset, x):
727 def contains(repo, subset, x):
728 """The revision's manifest contains a file matching pattern (but might not
728 """The revision's manifest contains a file matching pattern (but might not
729 modify it). See :hg:`help patterns` for information about file patterns.
729 modify it). See :hg:`help patterns` for information about file patterns.
730
730
731 The pattern without explicit kind like ``glob:`` is expected to be
731 The pattern without explicit kind like ``glob:`` is expected to be
732 relative to the current directory and match against a file exactly
732 relative to the current directory and match against a file exactly
733 for efficiency.
733 for efficiency.
734 """
734 """
735 # i18n: "contains" is a keyword
735 # i18n: "contains" is a keyword
736 pat = getstring(x, _("contains requires a pattern"))
736 pat = getstring(x, _("contains requires a pattern"))
737
737
738 def matches(x):
738 def matches(x):
739 if not matchmod.patkind(pat):
739 if not matchmod.patkind(pat):
740 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
740 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
741 if pats in repo[x]:
741 if pats in repo[x]:
742 return True
742 return True
743 else:
743 else:
744 c = repo[x]
744 c = repo[x]
745 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
745 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
746 for f in c.manifest():
746 for f in c.manifest():
747 if m(f):
747 if m(f):
748 return True
748 return True
749 return False
749 return False
750
750
751 return subset.filter(matches, condrepr=('<contains %r>', pat))
751 return subset.filter(matches, condrepr=('<contains %r>', pat))
752
752
753 @predicate('converted([id])', safe=True)
753 @predicate('converted([id])', safe=True)
754 def converted(repo, subset, x):
754 def converted(repo, subset, x):
755 """Changesets converted from the given identifier in the old repository if
755 """Changesets converted from the given identifier in the old repository if
756 present, or all converted changesets if no identifier is specified.
756 present, or all converted changesets if no identifier is specified.
757 """
757 """
758
758
759 # There is exactly no chance of resolving the revision, so do a simple
759 # There is exactly no chance of resolving the revision, so do a simple
760 # string compare and hope for the best
760 # string compare and hope for the best
761
761
762 rev = None
762 rev = None
763 # i18n: "converted" is a keyword
763 # i18n: "converted" is a keyword
764 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
764 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
765 if l:
765 if l:
766 # i18n: "converted" is a keyword
766 # i18n: "converted" is a keyword
767 rev = getstring(l[0], _('converted requires a revision'))
767 rev = getstring(l[0], _('converted requires a revision'))
768
768
769 def _matchvalue(r):
769 def _matchvalue(r):
770 source = repo[r].extra().get('convert_revision', None)
770 source = repo[r].extra().get('convert_revision', None)
771 return source is not None and (rev is None or source.startswith(rev))
771 return source is not None and (rev is None or source.startswith(rev))
772
772
773 return subset.filter(lambda r: _matchvalue(r),
773 return subset.filter(lambda r: _matchvalue(r),
774 condrepr=('<converted %r>', rev))
774 condrepr=('<converted %r>', rev))
775
775
776 @predicate('date(interval)', safe=True)
776 @predicate('date(interval)', safe=True)
777 def date(repo, subset, x):
777 def date(repo, subset, x):
778 """Changesets within the interval, see :hg:`help dates`.
778 """Changesets within the interval, see :hg:`help dates`.
779 """
779 """
780 # i18n: "date" is a keyword
780 # i18n: "date" is a keyword
781 ds = getstring(x, _("date requires a string"))
781 ds = getstring(x, _("date requires a string"))
782 dm = util.matchdate(ds)
782 dm = util.matchdate(ds)
783 return subset.filter(lambda x: dm(repo[x].date()[0]),
783 return subset.filter(lambda x: dm(repo[x].date()[0]),
784 condrepr=('<date %r>', ds))
784 condrepr=('<date %r>', ds))
785
785
786 @predicate('desc(string)', safe=True)
786 @predicate('desc(string)', safe=True)
787 def desc(repo, subset, x):
787 def desc(repo, subset, x):
788 """Search commit message for string. The match is case-insensitive.
788 """Search commit message for string. The match is case-insensitive.
789 """
789 """
790 # i18n: "desc" is a keyword
790 # i18n: "desc" is a keyword
791 ds = encoding.lower(getstring(x, _("desc requires a string")))
791 ds = encoding.lower(getstring(x, _("desc requires a string")))
792
792
793 def matches(x):
793 def matches(x):
794 c = repo[x]
794 c = repo[x]
795 return ds in encoding.lower(c.description())
795 return ds in encoding.lower(c.description())
796
796
797 return subset.filter(matches, condrepr=('<desc %r>', ds))
797 return subset.filter(matches, condrepr=('<desc %r>', ds))
798
798
799 def _descendants(repo, subset, x, followfirst=False):
799 def _descendants(repo, subset, x, followfirst=False):
800 roots = getset(repo, fullreposet(repo), x)
800 roots = getset(repo, fullreposet(repo), x)
801 if not roots:
801 if not roots:
802 return baseset()
802 return baseset()
803 s = _revdescendants(repo, roots, followfirst)
803 s = _revdescendants(repo, roots, followfirst)
804
804
805 # Both sets need to be ascending in order to lazily return the union
805 # Both sets need to be ascending in order to lazily return the union
806 # in the correct order.
806 # in the correct order.
807 base = subset & roots
807 base = subset & roots
808 desc = subset & s
808 desc = subset & s
809 result = base + desc
809 result = base + desc
810 if subset.isascending():
810 if subset.isascending():
811 result.sort()
811 result.sort()
812 elif subset.isdescending():
812 elif subset.isdescending():
813 result.sort(reverse=True)
813 result.sort(reverse=True)
814 else:
814 else:
815 result = subset & result
815 result = subset & result
816 return result
816 return result
817
817
818 @predicate('descendants(set)', safe=True)
818 @predicate('descendants(set)', safe=True)
819 def descendants(repo, subset, x):
819 def descendants(repo, subset, x):
820 """Changesets which are descendants of changesets in set.
820 """Changesets which are descendants of changesets in set.
821 """
821 """
822 return _descendants(repo, subset, x)
822 return _descendants(repo, subset, x)
823
823
824 @predicate('_firstdescendants', safe=True)
824 @predicate('_firstdescendants', safe=True)
825 def _firstdescendants(repo, subset, x):
825 def _firstdescendants(repo, subset, x):
826 # ``_firstdescendants(set)``
826 # ``_firstdescendants(set)``
827 # Like ``descendants(set)`` but follows only the first parents.
827 # Like ``descendants(set)`` but follows only the first parents.
828 return _descendants(repo, subset, x, followfirst=True)
828 return _descendants(repo, subset, x, followfirst=True)
829
829
830 @predicate('destination([set])', safe=True)
830 @predicate('destination([set])', safe=True)
831 def destination(repo, subset, x):
831 def destination(repo, subset, x):
832 """Changesets that were created by a graft, transplant or rebase operation,
832 """Changesets that were created by a graft, transplant or rebase operation,
833 with the given revisions specified as the source. Omitting the optional set
833 with the given revisions specified as the source. Omitting the optional set
834 is the same as passing all().
834 is the same as passing all().
835 """
835 """
836 if x is not None:
836 if x is not None:
837 sources = getset(repo, fullreposet(repo), x)
837 sources = getset(repo, fullreposet(repo), x)
838 else:
838 else:
839 sources = fullreposet(repo)
839 sources = fullreposet(repo)
840
840
841 dests = set()
841 dests = set()
842
842
843 # subset contains all of the possible destinations that can be returned, so
843 # subset contains all of the possible destinations that can be returned, so
844 # iterate over them and see if their source(s) were provided in the arg set.
844 # iterate over them and see if their source(s) were provided in the arg set.
845 # Even if the immediate src of r is not in the arg set, src's source (or
845 # Even if the immediate src of r is not in the arg set, src's source (or
846 # further back) may be. Scanning back further than the immediate src allows
846 # further back) may be. Scanning back further than the immediate src allows
847 # transitive transplants and rebases to yield the same results as transitive
847 # transitive transplants and rebases to yield the same results as transitive
848 # grafts.
848 # grafts.
849 for r in subset:
849 for r in subset:
850 src = _getrevsource(repo, r)
850 src = _getrevsource(repo, r)
851 lineage = None
851 lineage = None
852
852
853 while src is not None:
853 while src is not None:
854 if lineage is None:
854 if lineage is None:
855 lineage = list()
855 lineage = list()
856
856
857 lineage.append(r)
857 lineage.append(r)
858
858
859 # The visited lineage is a match if the current source is in the arg
859 # The visited lineage is a match if the current source is in the arg
860 # set. Since every candidate dest is visited by way of iterating
860 # set. Since every candidate dest is visited by way of iterating
861 # subset, any dests further back in the lineage will be tested by a
861 # subset, any dests further back in the lineage will be tested by a
862 # different iteration over subset. Likewise, if the src was already
862 # different iteration over subset. Likewise, if the src was already
863 # selected, the current lineage can be selected without going back
863 # selected, the current lineage can be selected without going back
864 # further.
864 # further.
865 if src in sources or src in dests:
865 if src in sources or src in dests:
866 dests.update(lineage)
866 dests.update(lineage)
867 break
867 break
868
868
869 r = src
869 r = src
870 src = _getrevsource(repo, r)
870 src = _getrevsource(repo, r)
871
871
872 return subset.filter(dests.__contains__,
872 return subset.filter(dests.__contains__,
873 condrepr=lambda: '<destination %r>' % sorted(dests))
873 condrepr=lambda: '<destination %r>' % sorted(dests))
874
874
875 @predicate('divergent()', safe=True)
875 @predicate('divergent()', safe=True)
876 def divergent(repo, subset, x):
876 def divergent(repo, subset, x):
877 """
877 """
878 Final successors of changesets with an alternative set of final successors.
878 Final successors of changesets with an alternative set of final successors.
879 """
879 """
880 # i18n: "divergent" is a keyword
880 # i18n: "divergent" is a keyword
881 getargs(x, 0, 0, _("divergent takes no arguments"))
881 getargs(x, 0, 0, _("divergent takes no arguments"))
882 divergent = obsmod.getrevs(repo, 'divergent')
882 divergent = obsmod.getrevs(repo, 'divergent')
883 return subset & divergent
883 return subset & divergent
884
884
885 @predicate('extinct()', safe=True)
885 @predicate('extinct()', safe=True)
886 def extinct(repo, subset, x):
886 def extinct(repo, subset, x):
887 """Obsolete changesets with obsolete descendants only.
887 """Obsolete changesets with obsolete descendants only.
888 """
888 """
889 # i18n: "extinct" is a keyword
889 # i18n: "extinct" is a keyword
890 getargs(x, 0, 0, _("extinct takes no arguments"))
890 getargs(x, 0, 0, _("extinct takes no arguments"))
891 extincts = obsmod.getrevs(repo, 'extinct')
891 extincts = obsmod.getrevs(repo, 'extinct')
892 return subset & extincts
892 return subset & extincts
893
893
894 @predicate('extra(label, [value])', safe=True)
894 @predicate('extra(label, [value])', safe=True)
895 def extra(repo, subset, x):
895 def extra(repo, subset, x):
896 """Changesets with the given label in the extra metadata, with the given
896 """Changesets with the given label in the extra metadata, with the given
897 optional value.
897 optional value.
898
898
899 If `value` starts with `re:`, the remainder of the value is treated as
899 If `value` starts with `re:`, the remainder of the value is treated as
900 a regular expression. To match a value that actually starts with `re:`,
900 a regular expression. To match a value that actually starts with `re:`,
901 use the prefix `literal:`.
901 use the prefix `literal:`.
902 """
902 """
903 args = getargsdict(x, 'extra', 'label value')
903 args = getargsdict(x, 'extra', 'label value')
904 if 'label' not in args:
904 if 'label' not in args:
905 # i18n: "extra" is a keyword
905 # i18n: "extra" is a keyword
906 raise error.ParseError(_('extra takes at least 1 argument'))
906 raise error.ParseError(_('extra takes at least 1 argument'))
907 # i18n: "extra" is a keyword
907 # i18n: "extra" is a keyword
908 label = getstring(args['label'], _('first argument to extra must be '
908 label = getstring(args['label'], _('first argument to extra must be '
909 'a string'))
909 'a string'))
910 value = None
910 value = None
911
911
912 if 'value' in args:
912 if 'value' in args:
913 # i18n: "extra" is a keyword
913 # i18n: "extra" is a keyword
914 value = getstring(args['value'], _('second argument to extra must be '
914 value = getstring(args['value'], _('second argument to extra must be '
915 'a string'))
915 'a string'))
916 kind, value, matcher = util.stringmatcher(value)
916 kind, value, matcher = util.stringmatcher(value)
917
917
918 def _matchvalue(r):
918 def _matchvalue(r):
919 extra = repo[r].extra()
919 extra = repo[r].extra()
920 return label in extra and (value is None or matcher(extra[label]))
920 return label in extra and (value is None or matcher(extra[label]))
921
921
922 return subset.filter(lambda r: _matchvalue(r),
922 return subset.filter(lambda r: _matchvalue(r),
923 condrepr=('<extra[%r] %r>', label, value))
923 condrepr=('<extra[%r] %r>', label, value))
924
924
925 @predicate('filelog(pattern)', safe=True)
925 @predicate('filelog(pattern)', safe=True)
926 def filelog(repo, subset, x):
926 def filelog(repo, subset, x):
927 """Changesets connected to the specified filelog.
927 """Changesets connected to the specified filelog.
928
928
929 For performance reasons, visits only revisions mentioned in the file-level
929 For performance reasons, visits only revisions mentioned in the file-level
930 filelog, rather than filtering through all changesets (much faster, but
930 filelog, rather than filtering through all changesets (much faster, but
931 doesn't include deletes or duplicate changes). For a slower, more accurate
931 doesn't include deletes or duplicate changes). For a slower, more accurate
932 result, use ``file()``.
932 result, use ``file()``.
933
933
934 The pattern without explicit kind like ``glob:`` is expected to be
934 The pattern without explicit kind like ``glob:`` is expected to be
935 relative to the current directory and match against a file exactly
935 relative to the current directory and match against a file exactly
936 for efficiency.
936 for efficiency.
937
937
938 If some linkrev points to revisions filtered by the current repoview, we'll
938 If some linkrev points to revisions filtered by the current repoview, we'll
939 work around it to return a non-filtered value.
939 work around it to return a non-filtered value.
940 """
940 """
941
941
942 # i18n: "filelog" is a keyword
942 # i18n: "filelog" is a keyword
943 pat = getstring(x, _("filelog requires a pattern"))
943 pat = getstring(x, _("filelog requires a pattern"))
944 s = set()
944 s = set()
945 cl = repo.changelog
945 cl = repo.changelog
946
946
947 if not matchmod.patkind(pat):
947 if not matchmod.patkind(pat):
948 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
948 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
949 files = [f]
949 files = [f]
950 else:
950 else:
951 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
951 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
952 files = (f for f in repo[None] if m(f))
952 files = (f for f in repo[None] if m(f))
953
953
954 for f in files:
954 for f in files:
955 fl = repo.file(f)
955 fl = repo.file(f)
956 known = {}
956 known = {}
957 scanpos = 0
957 scanpos = 0
958 for fr in list(fl):
958 for fr in list(fl):
959 fn = fl.node(fr)
959 fn = fl.node(fr)
960 if fn in known:
960 if fn in known:
961 s.add(known[fn])
961 s.add(known[fn])
962 continue
962 continue
963
963
964 lr = fl.linkrev(fr)
964 lr = fl.linkrev(fr)
965 if lr in cl:
965 if lr in cl:
966 s.add(lr)
966 s.add(lr)
967 elif scanpos is not None:
967 elif scanpos is not None:
968 # lowest matching changeset is filtered, scan further
968 # lowest matching changeset is filtered, scan further
969 # ahead in changelog
969 # ahead in changelog
970 start = max(lr, scanpos) + 1
970 start = max(lr, scanpos) + 1
971 scanpos = None
971 scanpos = None
972 for r in cl.revs(start):
972 for r in cl.revs(start):
973 # minimize parsing of non-matching entries
973 # minimize parsing of non-matching entries
974 if f in cl.revision(r) and f in cl.readfiles(r):
974 if f in cl.revision(r) and f in cl.readfiles(r):
975 try:
975 try:
976 # try to use manifest delta fastpath
976 # try to use manifest delta fastpath
977 n = repo[r].filenode(f)
977 n = repo[r].filenode(f)
978 if n not in known:
978 if n not in known:
979 if n == fn:
979 if n == fn:
980 s.add(r)
980 s.add(r)
981 scanpos = r
981 scanpos = r
982 break
982 break
983 else:
983 else:
984 known[n] = r
984 known[n] = r
985 except error.ManifestLookupError:
985 except error.ManifestLookupError:
986 # deletion in changelog
986 # deletion in changelog
987 continue
987 continue
988
988
989 return subset & s
989 return subset & s
990
990
991 @predicate('first(set, [n])', safe=True)
991 @predicate('first(set, [n])', safe=True)
992 def first(repo, subset, x):
992 def first(repo, subset, x):
993 """An alias for limit().
993 """An alias for limit().
994 """
994 """
995 return limit(repo, subset, x)
995 return limit(repo, subset, x)
996
996
997 def _follow(repo, subset, x, name, followfirst=False):
997 def _follow(repo, subset, x, name, followfirst=False):
998 l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name)
998 l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name)
999 c = repo['.']
999 c = repo['.']
1000 if l:
1000 if l:
1001 x = getstring(l[0], _("%s expected a pattern") % name)
1001 x = getstring(l[0], _("%s expected a pattern") % name)
1002 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1002 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1003 ctx=repo[None], default='path')
1003 ctx=repo[None], default='path')
1004
1004
1005 files = c.manifest().walk(matcher)
1005 files = c.manifest().walk(matcher)
1006
1006
1007 s = set()
1007 s = set()
1008 for fname in files:
1008 for fname in files:
1009 fctx = c[fname]
1009 fctx = c[fname]
1010 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1010 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1011 # include the revision responsible for the most recent version
1011 # include the revision responsible for the most recent version
1012 s.add(fctx.introrev())
1012 s.add(fctx.introrev())
1013 else:
1013 else:
1014 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1014 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1015
1015
1016 return subset & s
1016 return subset & s
1017
1017
1018 @predicate('follow([pattern])', safe=True)
1018 @predicate('follow([pattern])', safe=True)
1019 def follow(repo, subset, x):
1019 def follow(repo, subset, x):
1020 """
1020 """
1021 An alias for ``::.`` (ancestors of the working directory's first parent).
1021 An alias for ``::.`` (ancestors of the working directory's first parent).
1022 If pattern is specified, the histories of files matching given
1022 If pattern is specified, the histories of files matching given
1023 pattern is followed, including copies.
1023 pattern is followed, including copies.
1024 """
1024 """
1025 return _follow(repo, subset, x, 'follow')
1025 return _follow(repo, subset, x, 'follow')
1026
1026
1027 @predicate('_followfirst', safe=True)
1027 @predicate('_followfirst', safe=True)
1028 def _followfirst(repo, subset, x):
1028 def _followfirst(repo, subset, x):
1029 # ``followfirst([pattern])``
1029 # ``followfirst([pattern])``
1030 # Like ``follow([pattern])`` but follows only the first parent of
1030 # Like ``follow([pattern])`` but follows only the first parent of
1031 # every revisions or files revisions.
1031 # every revisions or files revisions.
1032 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1032 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1033
1033
1034 @predicate('all()', safe=True)
1034 @predicate('all()', safe=True)
1035 def getall(repo, subset, x):
1035 def getall(repo, subset, x):
1036 """All changesets, the same as ``0:tip``.
1036 """All changesets, the same as ``0:tip``.
1037 """
1037 """
1038 # i18n: "all" is a keyword
1038 # i18n: "all" is a keyword
1039 getargs(x, 0, 0, _("all takes no arguments"))
1039 getargs(x, 0, 0, _("all takes no arguments"))
1040 return subset & spanset(repo) # drop "null" if any
1040 return subset & spanset(repo) # drop "null" if any
1041
1041
1042 @predicate('grep(regex)')
1042 @predicate('grep(regex)')
1043 def grep(repo, subset, x):
1043 def grep(repo, subset, x):
1044 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1044 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1045 to ensure special escape characters are handled correctly. Unlike
1045 to ensure special escape characters are handled correctly. Unlike
1046 ``keyword(string)``, the match is case-sensitive.
1046 ``keyword(string)``, the match is case-sensitive.
1047 """
1047 """
1048 try:
1048 try:
1049 # i18n: "grep" is a keyword
1049 # i18n: "grep" is a keyword
1050 gr = re.compile(getstring(x, _("grep requires a string")))
1050 gr = re.compile(getstring(x, _("grep requires a string")))
1051 except re.error as e:
1051 except re.error as e:
1052 raise error.ParseError(_('invalid match pattern: %s') % e)
1052 raise error.ParseError(_('invalid match pattern: %s') % e)
1053
1053
1054 def matches(x):
1054 def matches(x):
1055 c = repo[x]
1055 c = repo[x]
1056 for e in c.files() + [c.user(), c.description()]:
1056 for e in c.files() + [c.user(), c.description()]:
1057 if gr.search(e):
1057 if gr.search(e):
1058 return True
1058 return True
1059 return False
1059 return False
1060
1060
1061 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1061 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1062
1062
1063 @predicate('_matchfiles', safe=True)
1063 @predicate('_matchfiles', safe=True)
1064 def _matchfiles(repo, subset, x):
1064 def _matchfiles(repo, subset, x):
1065 # _matchfiles takes a revset list of prefixed arguments:
1065 # _matchfiles takes a revset list of prefixed arguments:
1066 #
1066 #
1067 # [p:foo, i:bar, x:baz]
1067 # [p:foo, i:bar, x:baz]
1068 #
1068 #
1069 # builds a match object from them and filters subset. Allowed
1069 # builds a match object from them and filters subset. Allowed
1070 # prefixes are 'p:' for regular patterns, 'i:' for include
1070 # prefixes are 'p:' for regular patterns, 'i:' for include
1071 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1071 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1072 # a revision identifier, or the empty string to reference the
1072 # a revision identifier, or the empty string to reference the
1073 # working directory, from which the match object is
1073 # working directory, from which the match object is
1074 # initialized. Use 'd:' to set the default matching mode, default
1074 # initialized. Use 'd:' to set the default matching mode, default
1075 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1075 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1076
1076
1077 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1077 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1078 pats, inc, exc = [], [], []
1078 pats, inc, exc = [], [], []
1079 rev, default = None, None
1079 rev, default = None, None
1080 for arg in l:
1080 for arg in l:
1081 s = getstring(arg, "_matchfiles requires string arguments")
1081 s = getstring(arg, "_matchfiles requires string arguments")
1082 prefix, value = s[:2], s[2:]
1082 prefix, value = s[:2], s[2:]
1083 if prefix == 'p:':
1083 if prefix == 'p:':
1084 pats.append(value)
1084 pats.append(value)
1085 elif prefix == 'i:':
1085 elif prefix == 'i:':
1086 inc.append(value)
1086 inc.append(value)
1087 elif prefix == 'x:':
1087 elif prefix == 'x:':
1088 exc.append(value)
1088 exc.append(value)
1089 elif prefix == 'r:':
1089 elif prefix == 'r:':
1090 if rev is not None:
1090 if rev is not None:
1091 raise error.ParseError('_matchfiles expected at most one '
1091 raise error.ParseError('_matchfiles expected at most one '
1092 'revision')
1092 'revision')
1093 if value != '': # empty means working directory; leave rev as None
1093 if value != '': # empty means working directory; leave rev as None
1094 rev = value
1094 rev = value
1095 elif prefix == 'd:':
1095 elif prefix == 'd:':
1096 if default is not None:
1096 if default is not None:
1097 raise error.ParseError('_matchfiles expected at most one '
1097 raise error.ParseError('_matchfiles expected at most one '
1098 'default mode')
1098 'default mode')
1099 default = value
1099 default = value
1100 else:
1100 else:
1101 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1101 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1102 if not default:
1102 if not default:
1103 default = 'glob'
1103 default = 'glob'
1104
1104
1105 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1105 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1106 exclude=exc, ctx=repo[rev], default=default)
1106 exclude=exc, ctx=repo[rev], default=default)
1107
1107
1108 # This directly read the changelog data as creating changectx for all
1108 # This directly read the changelog data as creating changectx for all
1109 # revisions is quite expensive.
1109 # revisions is quite expensive.
1110 getfiles = repo.changelog.readfiles
1110 getfiles = repo.changelog.readfiles
1111 wdirrev = node.wdirrev
1111 wdirrev = node.wdirrev
1112 def matches(x):
1112 def matches(x):
1113 if x == wdirrev:
1113 if x == wdirrev:
1114 files = repo[x].files()
1114 files = repo[x].files()
1115 else:
1115 else:
1116 files = getfiles(x)
1116 files = getfiles(x)
1117 for f in files:
1117 for f in files:
1118 if m(f):
1118 if m(f):
1119 return True
1119 return True
1120 return False
1120 return False
1121
1121
1122 return subset.filter(matches,
1122 return subset.filter(matches,
1123 condrepr=('<matchfiles patterns=%r, include=%r '
1123 condrepr=('<matchfiles patterns=%r, include=%r '
1124 'exclude=%r, default=%r, rev=%r>',
1124 'exclude=%r, default=%r, rev=%r>',
1125 pats, inc, exc, default, rev))
1125 pats, inc, exc, default, rev))
1126
1126
1127 @predicate('file(pattern)', safe=True)
1127 @predicate('file(pattern)', safe=True)
1128 def hasfile(repo, subset, x):
1128 def hasfile(repo, subset, x):
1129 """Changesets affecting files matched by pattern.
1129 """Changesets affecting files matched by pattern.
1130
1130
1131 For a faster but less accurate result, consider using ``filelog()``
1131 For a faster but less accurate result, consider using ``filelog()``
1132 instead.
1132 instead.
1133
1133
1134 This predicate uses ``glob:`` as the default kind of pattern.
1134 This predicate uses ``glob:`` as the default kind of pattern.
1135 """
1135 """
1136 # i18n: "file" is a keyword
1136 # i18n: "file" is a keyword
1137 pat = getstring(x, _("file requires a pattern"))
1137 pat = getstring(x, _("file requires a pattern"))
1138 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1138 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1139
1139
1140 @predicate('head()', safe=True)
1140 @predicate('head()', safe=True)
1141 def head(repo, subset, x):
1141 def head(repo, subset, x):
1142 """Changeset is a named branch head.
1142 """Changeset is a named branch head.
1143 """
1143 """
1144 # i18n: "head" is a keyword
1144 # i18n: "head" is a keyword
1145 getargs(x, 0, 0, _("head takes no arguments"))
1145 getargs(x, 0, 0, _("head takes no arguments"))
1146 hs = set()
1146 hs = set()
1147 cl = repo.changelog
1147 cl = repo.changelog
1148 for ls in repo.branchmap().itervalues():
1148 for ls in repo.branchmap().itervalues():
1149 hs.update(cl.rev(h) for h in ls)
1149 hs.update(cl.rev(h) for h in ls)
1150 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
1150 return subset & baseset(hs)
1151 # necessary to ensure we preserve the order in subset.
1152 return baseset(hs) & subset
1153
1151
1154 @predicate('heads(set)', safe=True)
1152 @predicate('heads(set)', safe=True)
1155 def heads(repo, subset, x):
1153 def heads(repo, subset, x):
1156 """Members of set with no children in set.
1154 """Members of set with no children in set.
1157 """
1155 """
1158 s = getset(repo, subset, x)
1156 s = getset(repo, subset, x)
1159 ps = parents(repo, subset, x)
1157 ps = parents(repo, subset, x)
1160 return s - ps
1158 return s - ps
1161
1159
1162 @predicate('hidden()', safe=True)
1160 @predicate('hidden()', safe=True)
1163 def hidden(repo, subset, x):
1161 def hidden(repo, subset, x):
1164 """Hidden changesets.
1162 """Hidden changesets.
1165 """
1163 """
1166 # i18n: "hidden" is a keyword
1164 # i18n: "hidden" is a keyword
1167 getargs(x, 0, 0, _("hidden takes no arguments"))
1165 getargs(x, 0, 0, _("hidden takes no arguments"))
1168 hiddenrevs = repoview.filterrevs(repo, 'visible')
1166 hiddenrevs = repoview.filterrevs(repo, 'visible')
1169 return subset & hiddenrevs
1167 return subset & hiddenrevs
1170
1168
1171 @predicate('keyword(string)', safe=True)
1169 @predicate('keyword(string)', safe=True)
1172 def keyword(repo, subset, x):
1170 def keyword(repo, subset, x):
1173 """Search commit message, user name, and names of changed files for
1171 """Search commit message, user name, and names of changed files for
1174 string. The match is case-insensitive.
1172 string. The match is case-insensitive.
1175 """
1173 """
1176 # i18n: "keyword" is a keyword
1174 # i18n: "keyword" is a keyword
1177 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1175 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1178
1176
1179 def matches(r):
1177 def matches(r):
1180 c = repo[r]
1178 c = repo[r]
1181 return any(kw in encoding.lower(t)
1179 return any(kw in encoding.lower(t)
1182 for t in c.files() + [c.user(), c.description()])
1180 for t in c.files() + [c.user(), c.description()])
1183
1181
1184 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1182 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1185
1183
1186 @predicate('limit(set[, n[, offset]])', safe=True)
1184 @predicate('limit(set[, n[, offset]])', safe=True)
1187 def limit(repo, subset, x):
1185 def limit(repo, subset, x):
1188 """First n members of set, defaulting to 1, starting from offset.
1186 """First n members of set, defaulting to 1, starting from offset.
1189 """
1187 """
1190 args = getargsdict(x, 'limit', 'set n offset')
1188 args = getargsdict(x, 'limit', 'set n offset')
1191 if 'set' not in args:
1189 if 'set' not in args:
1192 # i18n: "limit" is a keyword
1190 # i18n: "limit" is a keyword
1193 raise error.ParseError(_("limit requires one to three arguments"))
1191 raise error.ParseError(_("limit requires one to three arguments"))
1194 try:
1192 try:
1195 lim, ofs = 1, 0
1193 lim, ofs = 1, 0
1196 if 'n' in args:
1194 if 'n' in args:
1197 # i18n: "limit" is a keyword
1195 # i18n: "limit" is a keyword
1198 lim = int(getstring(args['n'], _("limit requires a number")))
1196 lim = int(getstring(args['n'], _("limit requires a number")))
1199 if 'offset' in args:
1197 if 'offset' in args:
1200 # i18n: "limit" is a keyword
1198 # i18n: "limit" is a keyword
1201 ofs = int(getstring(args['offset'], _("limit requires a number")))
1199 ofs = int(getstring(args['offset'], _("limit requires a number")))
1202 if ofs < 0:
1200 if ofs < 0:
1203 raise error.ParseError(_("negative offset"))
1201 raise error.ParseError(_("negative offset"))
1204 except (TypeError, ValueError):
1202 except (TypeError, ValueError):
1205 # i18n: "limit" is a keyword
1203 # i18n: "limit" is a keyword
1206 raise error.ParseError(_("limit expects a number"))
1204 raise error.ParseError(_("limit expects a number"))
1207 os = getset(repo, fullreposet(repo), args['set'])
1205 os = getset(repo, fullreposet(repo), args['set'])
1208 result = []
1206 result = []
1209 it = iter(os)
1207 it = iter(os)
1210 for x in xrange(ofs):
1208 for x in xrange(ofs):
1211 y = next(it, None)
1209 y = next(it, None)
1212 if y is None:
1210 if y is None:
1213 break
1211 break
1214 for x in xrange(lim):
1212 for x in xrange(lim):
1215 y = next(it, None)
1213 y = next(it, None)
1216 if y is None:
1214 if y is None:
1217 break
1215 break
1218 elif y in subset:
1216 elif y in subset:
1219 result.append(y)
1217 result.append(y)
1220 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1218 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1221 lim, ofs, subset, os))
1219 lim, ofs, subset, os))
1222
1220
1223 @predicate('last(set, [n])', safe=True)
1221 @predicate('last(set, [n])', safe=True)
1224 def last(repo, subset, x):
1222 def last(repo, subset, x):
1225 """Last n members of set, defaulting to 1.
1223 """Last n members of set, defaulting to 1.
1226 """
1224 """
1227 # i18n: "last" is a keyword
1225 # i18n: "last" is a keyword
1228 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1226 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1229 try:
1227 try:
1230 lim = 1
1228 lim = 1
1231 if len(l) == 2:
1229 if len(l) == 2:
1232 # i18n: "last" is a keyword
1230 # i18n: "last" is a keyword
1233 lim = int(getstring(l[1], _("last requires a number")))
1231 lim = int(getstring(l[1], _("last requires a number")))
1234 except (TypeError, ValueError):
1232 except (TypeError, ValueError):
1235 # i18n: "last" is a keyword
1233 # i18n: "last" is a keyword
1236 raise error.ParseError(_("last expects a number"))
1234 raise error.ParseError(_("last expects a number"))
1237 os = getset(repo, fullreposet(repo), l[0])
1235 os = getset(repo, fullreposet(repo), l[0])
1238 os.reverse()
1236 os.reverse()
1239 result = []
1237 result = []
1240 it = iter(os)
1238 it = iter(os)
1241 for x in xrange(lim):
1239 for x in xrange(lim):
1242 y = next(it, None)
1240 y = next(it, None)
1243 if y is None:
1241 if y is None:
1244 break
1242 break
1245 elif y in subset:
1243 elif y in subset:
1246 result.append(y)
1244 result.append(y)
1247 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1245 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1248
1246
1249 @predicate('max(set)', safe=True)
1247 @predicate('max(set)', safe=True)
1250 def maxrev(repo, subset, x):
1248 def maxrev(repo, subset, x):
1251 """Changeset with highest revision number in set.
1249 """Changeset with highest revision number in set.
1252 """
1250 """
1253 os = getset(repo, fullreposet(repo), x)
1251 os = getset(repo, fullreposet(repo), x)
1254 try:
1252 try:
1255 m = os.max()
1253 m = os.max()
1256 if m in subset:
1254 if m in subset:
1257 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1255 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1258 except ValueError:
1256 except ValueError:
1259 # os.max() throws a ValueError when the collection is empty.
1257 # os.max() throws a ValueError when the collection is empty.
1260 # Same as python's max().
1258 # Same as python's max().
1261 pass
1259 pass
1262 return baseset(datarepr=('<max %r, %r>', subset, os))
1260 return baseset(datarepr=('<max %r, %r>', subset, os))
1263
1261
1264 @predicate('merge()', safe=True)
1262 @predicate('merge()', safe=True)
1265 def merge(repo, subset, x):
1263 def merge(repo, subset, x):
1266 """Changeset is a merge changeset.
1264 """Changeset is a merge changeset.
1267 """
1265 """
1268 # i18n: "merge" is a keyword
1266 # i18n: "merge" is a keyword
1269 getargs(x, 0, 0, _("merge takes no arguments"))
1267 getargs(x, 0, 0, _("merge takes no arguments"))
1270 cl = repo.changelog
1268 cl = repo.changelog
1271 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1269 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1272 condrepr='<merge>')
1270 condrepr='<merge>')
1273
1271
1274 @predicate('branchpoint()', safe=True)
1272 @predicate('branchpoint()', safe=True)
1275 def branchpoint(repo, subset, x):
1273 def branchpoint(repo, subset, x):
1276 """Changesets with more than one child.
1274 """Changesets with more than one child.
1277 """
1275 """
1278 # i18n: "branchpoint" is a keyword
1276 # i18n: "branchpoint" is a keyword
1279 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1277 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1280 cl = repo.changelog
1278 cl = repo.changelog
1281 if not subset:
1279 if not subset:
1282 return baseset()
1280 return baseset()
1283 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1281 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1284 # (and if it is not, it should.)
1282 # (and if it is not, it should.)
1285 baserev = min(subset)
1283 baserev = min(subset)
1286 parentscount = [0]*(len(repo) - baserev)
1284 parentscount = [0]*(len(repo) - baserev)
1287 for r in cl.revs(start=baserev + 1):
1285 for r in cl.revs(start=baserev + 1):
1288 for p in cl.parentrevs(r):
1286 for p in cl.parentrevs(r):
1289 if p >= baserev:
1287 if p >= baserev:
1290 parentscount[p - baserev] += 1
1288 parentscount[p - baserev] += 1
1291 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1289 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1292 condrepr='<branchpoint>')
1290 condrepr='<branchpoint>')
1293
1291
1294 @predicate('min(set)', safe=True)
1292 @predicate('min(set)', safe=True)
1295 def minrev(repo, subset, x):
1293 def minrev(repo, subset, x):
1296 """Changeset with lowest revision number in set.
1294 """Changeset with lowest revision number in set.
1297 """
1295 """
1298 os = getset(repo, fullreposet(repo), x)
1296 os = getset(repo, fullreposet(repo), x)
1299 try:
1297 try:
1300 m = os.min()
1298 m = os.min()
1301 if m in subset:
1299 if m in subset:
1302 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1300 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1303 except ValueError:
1301 except ValueError:
1304 # os.min() throws a ValueError when the collection is empty.
1302 # os.min() throws a ValueError when the collection is empty.
1305 # Same as python's min().
1303 # Same as python's min().
1306 pass
1304 pass
1307 return baseset(datarepr=('<min %r, %r>', subset, os))
1305 return baseset(datarepr=('<min %r, %r>', subset, os))
1308
1306
1309 @predicate('modifies(pattern)', safe=True)
1307 @predicate('modifies(pattern)', safe=True)
1310 def modifies(repo, subset, x):
1308 def modifies(repo, subset, x):
1311 """Changesets modifying files matched by pattern.
1309 """Changesets modifying files matched by pattern.
1312
1310
1313 The pattern without explicit kind like ``glob:`` is expected to be
1311 The pattern without explicit kind like ``glob:`` is expected to be
1314 relative to the current directory and match against a file or a
1312 relative to the current directory and match against a file or a
1315 directory.
1313 directory.
1316 """
1314 """
1317 # i18n: "modifies" is a keyword
1315 # i18n: "modifies" is a keyword
1318 pat = getstring(x, _("modifies requires a pattern"))
1316 pat = getstring(x, _("modifies requires a pattern"))
1319 return checkstatus(repo, subset, pat, 0)
1317 return checkstatus(repo, subset, pat, 0)
1320
1318
1321 @predicate('named(namespace)')
1319 @predicate('named(namespace)')
1322 def named(repo, subset, x):
1320 def named(repo, subset, x):
1323 """The changesets in a given namespace.
1321 """The changesets in a given namespace.
1324
1322
1325 If `namespace` starts with `re:`, the remainder of the string is treated as
1323 If `namespace` starts with `re:`, the remainder of the string is treated as
1326 a regular expression. To match a namespace that actually starts with `re:`,
1324 a regular expression. To match a namespace that actually starts with `re:`,
1327 use the prefix `literal:`.
1325 use the prefix `literal:`.
1328 """
1326 """
1329 # i18n: "named" is a keyword
1327 # i18n: "named" is a keyword
1330 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1328 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1331
1329
1332 ns = getstring(args[0],
1330 ns = getstring(args[0],
1333 # i18n: "named" is a keyword
1331 # i18n: "named" is a keyword
1334 _('the argument to named must be a string'))
1332 _('the argument to named must be a string'))
1335 kind, pattern, matcher = util.stringmatcher(ns)
1333 kind, pattern, matcher = util.stringmatcher(ns)
1336 namespaces = set()
1334 namespaces = set()
1337 if kind == 'literal':
1335 if kind == 'literal':
1338 if pattern not in repo.names:
1336 if pattern not in repo.names:
1339 raise error.RepoLookupError(_("namespace '%s' does not exist")
1337 raise error.RepoLookupError(_("namespace '%s' does not exist")
1340 % ns)
1338 % ns)
1341 namespaces.add(repo.names[pattern])
1339 namespaces.add(repo.names[pattern])
1342 else:
1340 else:
1343 for name, ns in repo.names.iteritems():
1341 for name, ns in repo.names.iteritems():
1344 if matcher(name):
1342 if matcher(name):
1345 namespaces.add(ns)
1343 namespaces.add(ns)
1346 if not namespaces:
1344 if not namespaces:
1347 raise error.RepoLookupError(_("no namespace exists"
1345 raise error.RepoLookupError(_("no namespace exists"
1348 " that match '%s'") % pattern)
1346 " that match '%s'") % pattern)
1349
1347
1350 names = set()
1348 names = set()
1351 for ns in namespaces:
1349 for ns in namespaces:
1352 for name in ns.listnames(repo):
1350 for name in ns.listnames(repo):
1353 if name not in ns.deprecated:
1351 if name not in ns.deprecated:
1354 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1352 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1355
1353
1356 names -= set([node.nullrev])
1354 names -= set([node.nullrev])
1357 return subset & names
1355 return subset & names
1358
1356
1359 @predicate('id(string)', safe=True)
1357 @predicate('id(string)', safe=True)
1360 def node_(repo, subset, x):
1358 def node_(repo, subset, x):
1361 """Revision non-ambiguously specified by the given hex string prefix.
1359 """Revision non-ambiguously specified by the given hex string prefix.
1362 """
1360 """
1363 # i18n: "id" is a keyword
1361 # i18n: "id" is a keyword
1364 l = getargs(x, 1, 1, _("id requires one argument"))
1362 l = getargs(x, 1, 1, _("id requires one argument"))
1365 # i18n: "id" is a keyword
1363 # i18n: "id" is a keyword
1366 n = getstring(l[0], _("id requires a string"))
1364 n = getstring(l[0], _("id requires a string"))
1367 if len(n) == 40:
1365 if len(n) == 40:
1368 try:
1366 try:
1369 rn = repo.changelog.rev(node.bin(n))
1367 rn = repo.changelog.rev(node.bin(n))
1370 except (LookupError, TypeError):
1368 except (LookupError, TypeError):
1371 rn = None
1369 rn = None
1372 else:
1370 else:
1373 rn = None
1371 rn = None
1374 pm = repo.changelog._partialmatch(n)
1372 pm = repo.changelog._partialmatch(n)
1375 if pm is not None:
1373 if pm is not None:
1376 rn = repo.changelog.rev(pm)
1374 rn = repo.changelog.rev(pm)
1377
1375
1378 if rn is None:
1376 if rn is None:
1379 return baseset()
1377 return baseset()
1380 result = baseset([rn])
1378 result = baseset([rn])
1381 return result & subset
1379 return result & subset
1382
1380
1383 @predicate('obsolete()', safe=True)
1381 @predicate('obsolete()', safe=True)
1384 def obsolete(repo, subset, x):
1382 def obsolete(repo, subset, x):
1385 """Mutable changeset with a newer version."""
1383 """Mutable changeset with a newer version."""
1386 # i18n: "obsolete" is a keyword
1384 # i18n: "obsolete" is a keyword
1387 getargs(x, 0, 0, _("obsolete takes no arguments"))
1385 getargs(x, 0, 0, _("obsolete takes no arguments"))
1388 obsoletes = obsmod.getrevs(repo, 'obsolete')
1386 obsoletes = obsmod.getrevs(repo, 'obsolete')
1389 return subset & obsoletes
1387 return subset & obsoletes
1390
1388
1391 @predicate('only(set, [set])', safe=True)
1389 @predicate('only(set, [set])', safe=True)
1392 def only(repo, subset, x):
1390 def only(repo, subset, x):
1393 """Changesets that are ancestors of the first set that are not ancestors
1391 """Changesets that are ancestors of the first set that are not ancestors
1394 of any other head in the repo. If a second set is specified, the result
1392 of any other head in the repo. If a second set is specified, the result
1395 is ancestors of the first set that are not ancestors of the second set
1393 is ancestors of the first set that are not ancestors of the second set
1396 (i.e. ::<set1> - ::<set2>).
1394 (i.e. ::<set1> - ::<set2>).
1397 """
1395 """
1398 cl = repo.changelog
1396 cl = repo.changelog
1399 # i18n: "only" is a keyword
1397 # i18n: "only" is a keyword
1400 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1398 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1401 include = getset(repo, fullreposet(repo), args[0])
1399 include = getset(repo, fullreposet(repo), args[0])
1402 if len(args) == 1:
1400 if len(args) == 1:
1403 if not include:
1401 if not include:
1404 return baseset()
1402 return baseset()
1405
1403
1406 descendants = set(_revdescendants(repo, include, False))
1404 descendants = set(_revdescendants(repo, include, False))
1407 exclude = [rev for rev in cl.headrevs()
1405 exclude = [rev for rev in cl.headrevs()
1408 if not rev in descendants and not rev in include]
1406 if not rev in descendants and not rev in include]
1409 else:
1407 else:
1410 exclude = getset(repo, fullreposet(repo), args[1])
1408 exclude = getset(repo, fullreposet(repo), args[1])
1411
1409
1412 results = set(cl.findmissingrevs(common=exclude, heads=include))
1410 results = set(cl.findmissingrevs(common=exclude, heads=include))
1413 # XXX we should turn this into a baseset instead of a set, smartset may do
1411 # XXX we should turn this into a baseset instead of a set, smartset may do
1414 # some optimisations from the fact this is a baseset.
1412 # some optimisations from the fact this is a baseset.
1415 return subset & results
1413 return subset & results
1416
1414
1417 @predicate('origin([set])', safe=True)
1415 @predicate('origin([set])', safe=True)
1418 def origin(repo, subset, x):
1416 def origin(repo, subset, x):
1419 """
1417 """
1420 Changesets that were specified as a source for the grafts, transplants or
1418 Changesets that were specified as a source for the grafts, transplants or
1421 rebases that created the given revisions. Omitting the optional set is the
1419 rebases that created the given revisions. Omitting the optional set is the
1422 same as passing all(). If a changeset created by these operations is itself
1420 same as passing all(). If a changeset created by these operations is itself
1423 specified as a source for one of these operations, only the source changeset
1421 specified as a source for one of these operations, only the source changeset
1424 for the first operation is selected.
1422 for the first operation is selected.
1425 """
1423 """
1426 if x is not None:
1424 if x is not None:
1427 dests = getset(repo, fullreposet(repo), x)
1425 dests = getset(repo, fullreposet(repo), x)
1428 else:
1426 else:
1429 dests = fullreposet(repo)
1427 dests = fullreposet(repo)
1430
1428
1431 def _firstsrc(rev):
1429 def _firstsrc(rev):
1432 src = _getrevsource(repo, rev)
1430 src = _getrevsource(repo, rev)
1433 if src is None:
1431 if src is None:
1434 return None
1432 return None
1435
1433
1436 while True:
1434 while True:
1437 prev = _getrevsource(repo, src)
1435 prev = _getrevsource(repo, src)
1438
1436
1439 if prev is None:
1437 if prev is None:
1440 return src
1438 return src
1441 src = prev
1439 src = prev
1442
1440
1443 o = set([_firstsrc(r) for r in dests])
1441 o = set([_firstsrc(r) for r in dests])
1444 o -= set([None])
1442 o -= set([None])
1445 # XXX we should turn this into a baseset instead of a set, smartset may do
1443 # XXX we should turn this into a baseset instead of a set, smartset may do
1446 # some optimisations from the fact this is a baseset.
1444 # some optimisations from the fact this is a baseset.
1447 return subset & o
1445 return subset & o
1448
1446
1449 @predicate('outgoing([path])', safe=True)
1447 @predicate('outgoing([path])', safe=True)
1450 def outgoing(repo, subset, x):
1448 def outgoing(repo, subset, x):
1451 """Changesets not found in the specified destination repository, or the
1449 """Changesets not found in the specified destination repository, or the
1452 default push location.
1450 default push location.
1453 """
1451 """
1454 # Avoid cycles.
1452 # Avoid cycles.
1455 from . import (
1453 from . import (
1456 discovery,
1454 discovery,
1457 hg,
1455 hg,
1458 )
1456 )
1459 # i18n: "outgoing" is a keyword
1457 # i18n: "outgoing" is a keyword
1460 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1458 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1461 # i18n: "outgoing" is a keyword
1459 # i18n: "outgoing" is a keyword
1462 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1460 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1463 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1461 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1464 dest, branches = hg.parseurl(dest)
1462 dest, branches = hg.parseurl(dest)
1465 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1463 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1466 if revs:
1464 if revs:
1467 revs = [repo.lookup(rev) for rev in revs]
1465 revs = [repo.lookup(rev) for rev in revs]
1468 other = hg.peer(repo, {}, dest)
1466 other = hg.peer(repo, {}, dest)
1469 repo.ui.pushbuffer()
1467 repo.ui.pushbuffer()
1470 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1468 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1471 repo.ui.popbuffer()
1469 repo.ui.popbuffer()
1472 cl = repo.changelog
1470 cl = repo.changelog
1473 o = set([cl.rev(r) for r in outgoing.missing])
1471 o = set([cl.rev(r) for r in outgoing.missing])
1474 return subset & o
1472 return subset & o
1475
1473
1476 @predicate('p1([set])', safe=True)
1474 @predicate('p1([set])', safe=True)
1477 def p1(repo, subset, x):
1475 def p1(repo, subset, x):
1478 """First parent of changesets in set, or the working directory.
1476 """First parent of changesets in set, or the working directory.
1479 """
1477 """
1480 if x is None:
1478 if x is None:
1481 p = repo[x].p1().rev()
1479 p = repo[x].p1().rev()
1482 if p >= 0:
1480 if p >= 0:
1483 return subset & baseset([p])
1481 return subset & baseset([p])
1484 return baseset()
1482 return baseset()
1485
1483
1486 ps = set()
1484 ps = set()
1487 cl = repo.changelog
1485 cl = repo.changelog
1488 for r in getset(repo, fullreposet(repo), x):
1486 for r in getset(repo, fullreposet(repo), x):
1489 ps.add(cl.parentrevs(r)[0])
1487 ps.add(cl.parentrevs(r)[0])
1490 ps -= set([node.nullrev])
1488 ps -= set([node.nullrev])
1491 # XXX we should turn this into a baseset instead of a set, smartset may do
1489 # XXX we should turn this into a baseset instead of a set, smartset may do
1492 # some optimisations from the fact this is a baseset.
1490 # some optimisations from the fact this is a baseset.
1493 return subset & ps
1491 return subset & ps
1494
1492
1495 @predicate('p2([set])', safe=True)
1493 @predicate('p2([set])', safe=True)
1496 def p2(repo, subset, x):
1494 def p2(repo, subset, x):
1497 """Second parent of changesets in set, or the working directory.
1495 """Second parent of changesets in set, or the working directory.
1498 """
1496 """
1499 if x is None:
1497 if x is None:
1500 ps = repo[x].parents()
1498 ps = repo[x].parents()
1501 try:
1499 try:
1502 p = ps[1].rev()
1500 p = ps[1].rev()
1503 if p >= 0:
1501 if p >= 0:
1504 return subset & baseset([p])
1502 return subset & baseset([p])
1505 return baseset()
1503 return baseset()
1506 except IndexError:
1504 except IndexError:
1507 return baseset()
1505 return baseset()
1508
1506
1509 ps = set()
1507 ps = set()
1510 cl = repo.changelog
1508 cl = repo.changelog
1511 for r in getset(repo, fullreposet(repo), x):
1509 for r in getset(repo, fullreposet(repo), x):
1512 ps.add(cl.parentrevs(r)[1])
1510 ps.add(cl.parentrevs(r)[1])
1513 ps -= set([node.nullrev])
1511 ps -= set([node.nullrev])
1514 # XXX we should turn this into a baseset instead of a set, smartset may do
1512 # XXX we should turn this into a baseset instead of a set, smartset may do
1515 # some optimisations from the fact this is a baseset.
1513 # some optimisations from the fact this is a baseset.
1516 return subset & ps
1514 return subset & ps
1517
1515
1518 @predicate('parents([set])', safe=True)
1516 @predicate('parents([set])', safe=True)
1519 def parents(repo, subset, x):
1517 def parents(repo, subset, x):
1520 """
1518 """
1521 The set of all parents for all changesets in set, or the working directory.
1519 The set of all parents for all changesets in set, or the working directory.
1522 """
1520 """
1523 if x is None:
1521 if x is None:
1524 ps = set(p.rev() for p in repo[x].parents())
1522 ps = set(p.rev() for p in repo[x].parents())
1525 else:
1523 else:
1526 ps = set()
1524 ps = set()
1527 cl = repo.changelog
1525 cl = repo.changelog
1528 up = ps.update
1526 up = ps.update
1529 parentrevs = cl.parentrevs
1527 parentrevs = cl.parentrevs
1530 for r in getset(repo, fullreposet(repo), x):
1528 for r in getset(repo, fullreposet(repo), x):
1531 if r == node.wdirrev:
1529 if r == node.wdirrev:
1532 up(p.rev() for p in repo[r].parents())
1530 up(p.rev() for p in repo[r].parents())
1533 else:
1531 else:
1534 up(parentrevs(r))
1532 up(parentrevs(r))
1535 ps -= set([node.nullrev])
1533 ps -= set([node.nullrev])
1536 return subset & ps
1534 return subset & ps
1537
1535
1538 def _phase(repo, subset, target):
1536 def _phase(repo, subset, target):
1539 """helper to select all rev in phase <target>"""
1537 """helper to select all rev in phase <target>"""
1540 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1538 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1541 if repo._phasecache._phasesets:
1539 if repo._phasecache._phasesets:
1542 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1540 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1543 s = baseset(s)
1541 s = baseset(s)
1544 s.sort() # set are non ordered, so we enforce ascending
1542 s.sort() # set are non ordered, so we enforce ascending
1545 return subset & s
1543 return subset & s
1546 else:
1544 else:
1547 phase = repo._phasecache.phase
1545 phase = repo._phasecache.phase
1548 condition = lambda r: phase(repo, r) == target
1546 condition = lambda r: phase(repo, r) == target
1549 return subset.filter(condition, condrepr=('<phase %r>', target),
1547 return subset.filter(condition, condrepr=('<phase %r>', target),
1550 cache=False)
1548 cache=False)
1551
1549
1552 @predicate('draft()', safe=True)
1550 @predicate('draft()', safe=True)
1553 def draft(repo, subset, x):
1551 def draft(repo, subset, x):
1554 """Changeset in draft phase."""
1552 """Changeset in draft phase."""
1555 # i18n: "draft" is a keyword
1553 # i18n: "draft" is a keyword
1556 getargs(x, 0, 0, _("draft takes no arguments"))
1554 getargs(x, 0, 0, _("draft takes no arguments"))
1557 target = phases.draft
1555 target = phases.draft
1558 return _phase(repo, subset, target)
1556 return _phase(repo, subset, target)
1559
1557
1560 @predicate('secret()', safe=True)
1558 @predicate('secret()', safe=True)
1561 def secret(repo, subset, x):
1559 def secret(repo, subset, x):
1562 """Changeset in secret phase."""
1560 """Changeset in secret phase."""
1563 # i18n: "secret" is a keyword
1561 # i18n: "secret" is a keyword
1564 getargs(x, 0, 0, _("secret takes no arguments"))
1562 getargs(x, 0, 0, _("secret takes no arguments"))
1565 target = phases.secret
1563 target = phases.secret
1566 return _phase(repo, subset, target)
1564 return _phase(repo, subset, target)
1567
1565
1568 def parentspec(repo, subset, x, n):
1566 def parentspec(repo, subset, x, n):
1569 """``set^0``
1567 """``set^0``
1570 The set.
1568 The set.
1571 ``set^1`` (or ``set^``), ``set^2``
1569 ``set^1`` (or ``set^``), ``set^2``
1572 First or second parent, respectively, of all changesets in set.
1570 First or second parent, respectively, of all changesets in set.
1573 """
1571 """
1574 try:
1572 try:
1575 n = int(n[1])
1573 n = int(n[1])
1576 if n not in (0, 1, 2):
1574 if n not in (0, 1, 2):
1577 raise ValueError
1575 raise ValueError
1578 except (TypeError, ValueError):
1576 except (TypeError, ValueError):
1579 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1577 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1580 ps = set()
1578 ps = set()
1581 cl = repo.changelog
1579 cl = repo.changelog
1582 for r in getset(repo, fullreposet(repo), x):
1580 for r in getset(repo, fullreposet(repo), x):
1583 if n == 0:
1581 if n == 0:
1584 ps.add(r)
1582 ps.add(r)
1585 elif n == 1:
1583 elif n == 1:
1586 ps.add(cl.parentrevs(r)[0])
1584 ps.add(cl.parentrevs(r)[0])
1587 elif n == 2:
1585 elif n == 2:
1588 parents = cl.parentrevs(r)
1586 parents = cl.parentrevs(r)
1589 if len(parents) > 1:
1587 if len(parents) > 1:
1590 ps.add(parents[1])
1588 ps.add(parents[1])
1591 return subset & ps
1589 return subset & ps
1592
1590
1593 @predicate('present(set)', safe=True)
1591 @predicate('present(set)', safe=True)
1594 def present(repo, subset, x):
1592 def present(repo, subset, x):
1595 """An empty set, if any revision in set isn't found; otherwise,
1593 """An empty set, if any revision in set isn't found; otherwise,
1596 all revisions in set.
1594 all revisions in set.
1597
1595
1598 If any of specified revisions is not present in the local repository,
1596 If any of specified revisions is not present in the local repository,
1599 the query is normally aborted. But this predicate allows the query
1597 the query is normally aborted. But this predicate allows the query
1600 to continue even in such cases.
1598 to continue even in such cases.
1601 """
1599 """
1602 try:
1600 try:
1603 return getset(repo, subset, x)
1601 return getset(repo, subset, x)
1604 except error.RepoLookupError:
1602 except error.RepoLookupError:
1605 return baseset()
1603 return baseset()
1606
1604
1607 # for internal use
1605 # for internal use
1608 @predicate('_notpublic', safe=True)
1606 @predicate('_notpublic', safe=True)
1609 def _notpublic(repo, subset, x):
1607 def _notpublic(repo, subset, x):
1610 getargs(x, 0, 0, "_notpublic takes no arguments")
1608 getargs(x, 0, 0, "_notpublic takes no arguments")
1611 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1609 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1612 if repo._phasecache._phasesets:
1610 if repo._phasecache._phasesets:
1613 s = set()
1611 s = set()
1614 for u in repo._phasecache._phasesets[1:]:
1612 for u in repo._phasecache._phasesets[1:]:
1615 s.update(u)
1613 s.update(u)
1616 s = baseset(s - repo.changelog.filteredrevs)
1614 s = baseset(s - repo.changelog.filteredrevs)
1617 s.sort()
1615 s.sort()
1618 return subset & s
1616 return subset & s
1619 else:
1617 else:
1620 phase = repo._phasecache.phase
1618 phase = repo._phasecache.phase
1621 target = phases.public
1619 target = phases.public
1622 condition = lambda r: phase(repo, r) != target
1620 condition = lambda r: phase(repo, r) != target
1623 return subset.filter(condition, condrepr=('<phase %r>', target),
1621 return subset.filter(condition, condrepr=('<phase %r>', target),
1624 cache=False)
1622 cache=False)
1625
1623
1626 @predicate('public()', safe=True)
1624 @predicate('public()', safe=True)
1627 def public(repo, subset, x):
1625 def public(repo, subset, x):
1628 """Changeset in public phase."""
1626 """Changeset in public phase."""
1629 # i18n: "public" is a keyword
1627 # i18n: "public" is a keyword
1630 getargs(x, 0, 0, _("public takes no arguments"))
1628 getargs(x, 0, 0, _("public takes no arguments"))
1631 phase = repo._phasecache.phase
1629 phase = repo._phasecache.phase
1632 target = phases.public
1630 target = phases.public
1633 condition = lambda r: phase(repo, r) == target
1631 condition = lambda r: phase(repo, r) == target
1634 return subset.filter(condition, condrepr=('<phase %r>', target),
1632 return subset.filter(condition, condrepr=('<phase %r>', target),
1635 cache=False)
1633 cache=False)
1636
1634
1637 @predicate('remote([id [,path]])', safe=True)
1635 @predicate('remote([id [,path]])', safe=True)
1638 def remote(repo, subset, x):
1636 def remote(repo, subset, x):
1639 """Local revision that corresponds to the given identifier in a
1637 """Local revision that corresponds to the given identifier in a
1640 remote repository, if present. Here, the '.' identifier is a
1638 remote repository, if present. Here, the '.' identifier is a
1641 synonym for the current local branch.
1639 synonym for the current local branch.
1642 """
1640 """
1643
1641
1644 from . import hg # avoid start-up nasties
1642 from . import hg # avoid start-up nasties
1645 # i18n: "remote" is a keyword
1643 # i18n: "remote" is a keyword
1646 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1644 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1647
1645
1648 q = '.'
1646 q = '.'
1649 if len(l) > 0:
1647 if len(l) > 0:
1650 # i18n: "remote" is a keyword
1648 # i18n: "remote" is a keyword
1651 q = getstring(l[0], _("remote requires a string id"))
1649 q = getstring(l[0], _("remote requires a string id"))
1652 if q == '.':
1650 if q == '.':
1653 q = repo['.'].branch()
1651 q = repo['.'].branch()
1654
1652
1655 dest = ''
1653 dest = ''
1656 if len(l) > 1:
1654 if len(l) > 1:
1657 # i18n: "remote" is a keyword
1655 # i18n: "remote" is a keyword
1658 dest = getstring(l[1], _("remote requires a repository path"))
1656 dest = getstring(l[1], _("remote requires a repository path"))
1659 dest = repo.ui.expandpath(dest or 'default')
1657 dest = repo.ui.expandpath(dest or 'default')
1660 dest, branches = hg.parseurl(dest)
1658 dest, branches = hg.parseurl(dest)
1661 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1659 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1662 if revs:
1660 if revs:
1663 revs = [repo.lookup(rev) for rev in revs]
1661 revs = [repo.lookup(rev) for rev in revs]
1664 other = hg.peer(repo, {}, dest)
1662 other = hg.peer(repo, {}, dest)
1665 n = other.lookup(q)
1663 n = other.lookup(q)
1666 if n in repo:
1664 if n in repo:
1667 r = repo[n].rev()
1665 r = repo[n].rev()
1668 if r in subset:
1666 if r in subset:
1669 return baseset([r])
1667 return baseset([r])
1670 return baseset()
1668 return baseset()
1671
1669
1672 @predicate('removes(pattern)', safe=True)
1670 @predicate('removes(pattern)', safe=True)
1673 def removes(repo, subset, x):
1671 def removes(repo, subset, x):
1674 """Changesets which remove files matching pattern.
1672 """Changesets which remove files matching pattern.
1675
1673
1676 The pattern without explicit kind like ``glob:`` is expected to be
1674 The pattern without explicit kind like ``glob:`` is expected to be
1677 relative to the current directory and match against a file or a
1675 relative to the current directory and match against a file or a
1678 directory.
1676 directory.
1679 """
1677 """
1680 # i18n: "removes" is a keyword
1678 # i18n: "removes" is a keyword
1681 pat = getstring(x, _("removes requires a pattern"))
1679 pat = getstring(x, _("removes requires a pattern"))
1682 return checkstatus(repo, subset, pat, 2)
1680 return checkstatus(repo, subset, pat, 2)
1683
1681
1684 @predicate('rev(number)', safe=True)
1682 @predicate('rev(number)', safe=True)
1685 def rev(repo, subset, x):
1683 def rev(repo, subset, x):
1686 """Revision with the given numeric identifier.
1684 """Revision with the given numeric identifier.
1687 """
1685 """
1688 # i18n: "rev" is a keyword
1686 # i18n: "rev" is a keyword
1689 l = getargs(x, 1, 1, _("rev requires one argument"))
1687 l = getargs(x, 1, 1, _("rev requires one argument"))
1690 try:
1688 try:
1691 # i18n: "rev" is a keyword
1689 # i18n: "rev" is a keyword
1692 l = int(getstring(l[0], _("rev requires a number")))
1690 l = int(getstring(l[0], _("rev requires a number")))
1693 except (TypeError, ValueError):
1691 except (TypeError, ValueError):
1694 # i18n: "rev" is a keyword
1692 # i18n: "rev" is a keyword
1695 raise error.ParseError(_("rev expects a number"))
1693 raise error.ParseError(_("rev expects a number"))
1696 if l not in repo.changelog and l != node.nullrev:
1694 if l not in repo.changelog and l != node.nullrev:
1697 return baseset()
1695 return baseset()
1698 return subset & baseset([l])
1696 return subset & baseset([l])
1699
1697
1700 @predicate('matching(revision [, field])', safe=True)
1698 @predicate('matching(revision [, field])', safe=True)
1701 def matching(repo, subset, x):
1699 def matching(repo, subset, x):
1702 """Changesets in which a given set of fields match the set of fields in the
1700 """Changesets in which a given set of fields match the set of fields in the
1703 selected revision or set.
1701 selected revision or set.
1704
1702
1705 To match more than one field pass the list of fields to match separated
1703 To match more than one field pass the list of fields to match separated
1706 by spaces (e.g. ``author description``).
1704 by spaces (e.g. ``author description``).
1707
1705
1708 Valid fields are most regular revision fields and some special fields.
1706 Valid fields are most regular revision fields and some special fields.
1709
1707
1710 Regular revision fields are ``description``, ``author``, ``branch``,
1708 Regular revision fields are ``description``, ``author``, ``branch``,
1711 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1709 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1712 and ``diff``.
1710 and ``diff``.
1713 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1711 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1714 contents of the revision. Two revisions matching their ``diff`` will
1712 contents of the revision. Two revisions matching their ``diff`` will
1715 also match their ``files``.
1713 also match their ``files``.
1716
1714
1717 Special fields are ``summary`` and ``metadata``:
1715 Special fields are ``summary`` and ``metadata``:
1718 ``summary`` matches the first line of the description.
1716 ``summary`` matches the first line of the description.
1719 ``metadata`` is equivalent to matching ``description user date``
1717 ``metadata`` is equivalent to matching ``description user date``
1720 (i.e. it matches the main metadata fields).
1718 (i.e. it matches the main metadata fields).
1721
1719
1722 ``metadata`` is the default field which is used when no fields are
1720 ``metadata`` is the default field which is used when no fields are
1723 specified. You can match more than one field at a time.
1721 specified. You can match more than one field at a time.
1724 """
1722 """
1725 # i18n: "matching" is a keyword
1723 # i18n: "matching" is a keyword
1726 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1724 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1727
1725
1728 revs = getset(repo, fullreposet(repo), l[0])
1726 revs = getset(repo, fullreposet(repo), l[0])
1729
1727
1730 fieldlist = ['metadata']
1728 fieldlist = ['metadata']
1731 if len(l) > 1:
1729 if len(l) > 1:
1732 fieldlist = getstring(l[1],
1730 fieldlist = getstring(l[1],
1733 # i18n: "matching" is a keyword
1731 # i18n: "matching" is a keyword
1734 _("matching requires a string "
1732 _("matching requires a string "
1735 "as its second argument")).split()
1733 "as its second argument")).split()
1736
1734
1737 # Make sure that there are no repeated fields,
1735 # Make sure that there are no repeated fields,
1738 # expand the 'special' 'metadata' field type
1736 # expand the 'special' 'metadata' field type
1739 # and check the 'files' whenever we check the 'diff'
1737 # and check the 'files' whenever we check the 'diff'
1740 fields = []
1738 fields = []
1741 for field in fieldlist:
1739 for field in fieldlist:
1742 if field == 'metadata':
1740 if field == 'metadata':
1743 fields += ['user', 'description', 'date']
1741 fields += ['user', 'description', 'date']
1744 elif field == 'diff':
1742 elif field == 'diff':
1745 # a revision matching the diff must also match the files
1743 # a revision matching the diff must also match the files
1746 # since matching the diff is very costly, make sure to
1744 # since matching the diff is very costly, make sure to
1747 # also match the files first
1745 # also match the files first
1748 fields += ['files', 'diff']
1746 fields += ['files', 'diff']
1749 else:
1747 else:
1750 if field == 'author':
1748 if field == 'author':
1751 field = 'user'
1749 field = 'user'
1752 fields.append(field)
1750 fields.append(field)
1753 fields = set(fields)
1751 fields = set(fields)
1754 if 'summary' in fields and 'description' in fields:
1752 if 'summary' in fields and 'description' in fields:
1755 # If a revision matches its description it also matches its summary
1753 # If a revision matches its description it also matches its summary
1756 fields.discard('summary')
1754 fields.discard('summary')
1757
1755
1758 # We may want to match more than one field
1756 # We may want to match more than one field
1759 # Not all fields take the same amount of time to be matched
1757 # Not all fields take the same amount of time to be matched
1760 # Sort the selected fields in order of increasing matching cost
1758 # Sort the selected fields in order of increasing matching cost
1761 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1759 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1762 'files', 'description', 'substate', 'diff']
1760 'files', 'description', 'substate', 'diff']
1763 def fieldkeyfunc(f):
1761 def fieldkeyfunc(f):
1764 try:
1762 try:
1765 return fieldorder.index(f)
1763 return fieldorder.index(f)
1766 except ValueError:
1764 except ValueError:
1767 # assume an unknown field is very costly
1765 # assume an unknown field is very costly
1768 return len(fieldorder)
1766 return len(fieldorder)
1769 fields = list(fields)
1767 fields = list(fields)
1770 fields.sort(key=fieldkeyfunc)
1768 fields.sort(key=fieldkeyfunc)
1771
1769
1772 # Each field will be matched with its own "getfield" function
1770 # Each field will be matched with its own "getfield" function
1773 # which will be added to the getfieldfuncs array of functions
1771 # which will be added to the getfieldfuncs array of functions
1774 getfieldfuncs = []
1772 getfieldfuncs = []
1775 _funcs = {
1773 _funcs = {
1776 'user': lambda r: repo[r].user(),
1774 'user': lambda r: repo[r].user(),
1777 'branch': lambda r: repo[r].branch(),
1775 'branch': lambda r: repo[r].branch(),
1778 'date': lambda r: repo[r].date(),
1776 'date': lambda r: repo[r].date(),
1779 'description': lambda r: repo[r].description(),
1777 'description': lambda r: repo[r].description(),
1780 'files': lambda r: repo[r].files(),
1778 'files': lambda r: repo[r].files(),
1781 'parents': lambda r: repo[r].parents(),
1779 'parents': lambda r: repo[r].parents(),
1782 'phase': lambda r: repo[r].phase(),
1780 'phase': lambda r: repo[r].phase(),
1783 'substate': lambda r: repo[r].substate,
1781 'substate': lambda r: repo[r].substate,
1784 'summary': lambda r: repo[r].description().splitlines()[0],
1782 'summary': lambda r: repo[r].description().splitlines()[0],
1785 'diff': lambda r: list(repo[r].diff(git=True),)
1783 'diff': lambda r: list(repo[r].diff(git=True),)
1786 }
1784 }
1787 for info in fields:
1785 for info in fields:
1788 getfield = _funcs.get(info, None)
1786 getfield = _funcs.get(info, None)
1789 if getfield is None:
1787 if getfield is None:
1790 raise error.ParseError(
1788 raise error.ParseError(
1791 # i18n: "matching" is a keyword
1789 # i18n: "matching" is a keyword
1792 _("unexpected field name passed to matching: %s") % info)
1790 _("unexpected field name passed to matching: %s") % info)
1793 getfieldfuncs.append(getfield)
1791 getfieldfuncs.append(getfield)
1794 # convert the getfield array of functions into a "getinfo" function
1792 # convert the getfield array of functions into a "getinfo" function
1795 # which returns an array of field values (or a single value if there
1793 # which returns an array of field values (or a single value if there
1796 # is only one field to match)
1794 # is only one field to match)
1797 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1795 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1798
1796
1799 def matches(x):
1797 def matches(x):
1800 for rev in revs:
1798 for rev in revs:
1801 target = getinfo(rev)
1799 target = getinfo(rev)
1802 match = True
1800 match = True
1803 for n, f in enumerate(getfieldfuncs):
1801 for n, f in enumerate(getfieldfuncs):
1804 if target[n] != f(x):
1802 if target[n] != f(x):
1805 match = False
1803 match = False
1806 if match:
1804 if match:
1807 return True
1805 return True
1808 return False
1806 return False
1809
1807
1810 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1808 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1811
1809
1812 @predicate('reverse(set)', safe=True)
1810 @predicate('reverse(set)', safe=True)
1813 def reverse(repo, subset, x):
1811 def reverse(repo, subset, x):
1814 """Reverse order of set.
1812 """Reverse order of set.
1815 """
1813 """
1816 l = getset(repo, subset, x)
1814 l = getset(repo, subset, x)
1817 l.reverse()
1815 l.reverse()
1818 return l
1816 return l
1819
1817
1820 @predicate('roots(set)', safe=True)
1818 @predicate('roots(set)', safe=True)
1821 def roots(repo, subset, x):
1819 def roots(repo, subset, x):
1822 """Changesets in set with no parent changeset in set.
1820 """Changesets in set with no parent changeset in set.
1823 """
1821 """
1824 s = getset(repo, fullreposet(repo), x)
1822 s = getset(repo, fullreposet(repo), x)
1825 parents = repo.changelog.parentrevs
1823 parents = repo.changelog.parentrevs
1826 def filter(r):
1824 def filter(r):
1827 for p in parents(r):
1825 for p in parents(r):
1828 if 0 <= p and p in s:
1826 if 0 <= p and p in s:
1829 return False
1827 return False
1830 return True
1828 return True
1831 return subset & s.filter(filter, condrepr='<roots>')
1829 return subset & s.filter(filter, condrepr='<roots>')
1832
1830
1833 _sortkeyfuncs = {
1831 _sortkeyfuncs = {
1834 'rev': lambda c: c.rev(),
1832 'rev': lambda c: c.rev(),
1835 'branch': lambda c: c.branch(),
1833 'branch': lambda c: c.branch(),
1836 'desc': lambda c: c.description(),
1834 'desc': lambda c: c.description(),
1837 'user': lambda c: c.user(),
1835 'user': lambda c: c.user(),
1838 'author': lambda c: c.user(),
1836 'author': lambda c: c.user(),
1839 'date': lambda c: c.date()[0],
1837 'date': lambda c: c.date()[0],
1840 }
1838 }
1841
1839
1842 def _getsortargs(x):
1840 def _getsortargs(x):
1843 """Parse sort options into (set, [(key, reverse)], opts)"""
1841 """Parse sort options into (set, [(key, reverse)], opts)"""
1844 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1842 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1845 if 'set' not in args:
1843 if 'set' not in args:
1846 # i18n: "sort" is a keyword
1844 # i18n: "sort" is a keyword
1847 raise error.ParseError(_('sort requires one or two arguments'))
1845 raise error.ParseError(_('sort requires one or two arguments'))
1848 keys = "rev"
1846 keys = "rev"
1849 if 'keys' in args:
1847 if 'keys' in args:
1850 # i18n: "sort" is a keyword
1848 # i18n: "sort" is a keyword
1851 keys = getstring(args['keys'], _("sort spec must be a string"))
1849 keys = getstring(args['keys'], _("sort spec must be a string"))
1852
1850
1853 keyflags = []
1851 keyflags = []
1854 for k in keys.split():
1852 for k in keys.split():
1855 fk = k
1853 fk = k
1856 reverse = (k[0] == '-')
1854 reverse = (k[0] == '-')
1857 if reverse:
1855 if reverse:
1858 k = k[1:]
1856 k = k[1:]
1859 if k not in _sortkeyfuncs and k != 'topo':
1857 if k not in _sortkeyfuncs and k != 'topo':
1860 raise error.ParseError(_("unknown sort key %r") % fk)
1858 raise error.ParseError(_("unknown sort key %r") % fk)
1861 keyflags.append((k, reverse))
1859 keyflags.append((k, reverse))
1862
1860
1863 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1861 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1864 # i18n: "topo" is a keyword
1862 # i18n: "topo" is a keyword
1865 raise error.ParseError(_(
1863 raise error.ParseError(_(
1866 'topo sort order cannot be combined with other sort keys'))
1864 'topo sort order cannot be combined with other sort keys'))
1867
1865
1868 opts = {}
1866 opts = {}
1869 if 'topo.firstbranch' in args:
1867 if 'topo.firstbranch' in args:
1870 if any(k == 'topo' for k, reverse in keyflags):
1868 if any(k == 'topo' for k, reverse in keyflags):
1871 opts['topo.firstbranch'] = args['topo.firstbranch']
1869 opts['topo.firstbranch'] = args['topo.firstbranch']
1872 else:
1870 else:
1873 # i18n: "topo" and "topo.firstbranch" are keywords
1871 # i18n: "topo" and "topo.firstbranch" are keywords
1874 raise error.ParseError(_(
1872 raise error.ParseError(_(
1875 'topo.firstbranch can only be used when using the topo sort '
1873 'topo.firstbranch can only be used when using the topo sort '
1876 'key'))
1874 'key'))
1877
1875
1878 return args['set'], keyflags, opts
1876 return args['set'], keyflags, opts
1879
1877
1880 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1878 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1881 def sort(repo, subset, x):
1879 def sort(repo, subset, x):
1882 """Sort set by keys. The default sort order is ascending, specify a key
1880 """Sort set by keys. The default sort order is ascending, specify a key
1883 as ``-key`` to sort in descending order.
1881 as ``-key`` to sort in descending order.
1884
1882
1885 The keys can be:
1883 The keys can be:
1886
1884
1887 - ``rev`` for the revision number,
1885 - ``rev`` for the revision number,
1888 - ``branch`` for the branch name,
1886 - ``branch`` for the branch name,
1889 - ``desc`` for the commit message (description),
1887 - ``desc`` for the commit message (description),
1890 - ``user`` for user name (``author`` can be used as an alias),
1888 - ``user`` for user name (``author`` can be used as an alias),
1891 - ``date`` for the commit date
1889 - ``date`` for the commit date
1892 - ``topo`` for a reverse topographical sort
1890 - ``topo`` for a reverse topographical sort
1893
1891
1894 The ``topo`` sort order cannot be combined with other sort keys. This sort
1892 The ``topo`` sort order cannot be combined with other sort keys. This sort
1895 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1893 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1896 specifies what topographical branches to prioritize in the sort.
1894 specifies what topographical branches to prioritize in the sort.
1897
1895
1898 """
1896 """
1899 s, keyflags, opts = _getsortargs(x)
1897 s, keyflags, opts = _getsortargs(x)
1900 revs = getset(repo, subset, s)
1898 revs = getset(repo, subset, s)
1901
1899
1902 if not keyflags:
1900 if not keyflags:
1903 return revs
1901 return revs
1904 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1902 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1905 revs.sort(reverse=keyflags[0][1])
1903 revs.sort(reverse=keyflags[0][1])
1906 return revs
1904 return revs
1907 elif keyflags[0][0] == "topo":
1905 elif keyflags[0][0] == "topo":
1908 firstbranch = ()
1906 firstbranch = ()
1909 if 'topo.firstbranch' in opts:
1907 if 'topo.firstbranch' in opts:
1910 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1908 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1911 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1909 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1912 istopo=True)
1910 istopo=True)
1913 if keyflags[0][1]:
1911 if keyflags[0][1]:
1914 revs.reverse()
1912 revs.reverse()
1915 return revs
1913 return revs
1916
1914
1917 # sort() is guaranteed to be stable
1915 # sort() is guaranteed to be stable
1918 ctxs = [repo[r] for r in revs]
1916 ctxs = [repo[r] for r in revs]
1919 for k, reverse in reversed(keyflags):
1917 for k, reverse in reversed(keyflags):
1920 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1918 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1921 return baseset([c.rev() for c in ctxs])
1919 return baseset([c.rev() for c in ctxs])
1922
1920
1923 def _toposort(revs, parentsfunc, firstbranch=()):
1921 def _toposort(revs, parentsfunc, firstbranch=()):
1924 """Yield revisions from heads to roots one (topo) branch at a time.
1922 """Yield revisions from heads to roots one (topo) branch at a time.
1925
1923
1926 This function aims to be used by a graph generator that wishes to minimize
1924 This function aims to be used by a graph generator that wishes to minimize
1927 the number of parallel branches and their interleaving.
1925 the number of parallel branches and their interleaving.
1928
1926
1929 Example iteration order (numbers show the "true" order in a changelog):
1927 Example iteration order (numbers show the "true" order in a changelog):
1930
1928
1931 o 4
1929 o 4
1932 |
1930 |
1933 o 1
1931 o 1
1934 |
1932 |
1935 | o 3
1933 | o 3
1936 | |
1934 | |
1937 | o 2
1935 | o 2
1938 |/
1936 |/
1939 o 0
1937 o 0
1940
1938
1941 Note that the ancestors of merges are understood by the current
1939 Note that the ancestors of merges are understood by the current
1942 algorithm to be on the same branch. This means no reordering will
1940 algorithm to be on the same branch. This means no reordering will
1943 occur behind a merge.
1941 occur behind a merge.
1944 """
1942 """
1945
1943
1946 ### Quick summary of the algorithm
1944 ### Quick summary of the algorithm
1947 #
1945 #
1948 # This function is based around a "retention" principle. We keep revisions
1946 # This function is based around a "retention" principle. We keep revisions
1949 # in memory until we are ready to emit a whole branch that immediately
1947 # in memory until we are ready to emit a whole branch that immediately
1950 # "merges" into an existing one. This reduces the number of parallel
1948 # "merges" into an existing one. This reduces the number of parallel
1951 # branches with interleaved revisions.
1949 # branches with interleaved revisions.
1952 #
1950 #
1953 # During iteration revs are split into two groups:
1951 # During iteration revs are split into two groups:
1954 # A) revision already emitted
1952 # A) revision already emitted
1955 # B) revision in "retention". They are stored as different subgroups.
1953 # B) revision in "retention". They are stored as different subgroups.
1956 #
1954 #
1957 # for each REV, we do the following logic:
1955 # for each REV, we do the following logic:
1958 #
1956 #
1959 # 1) if REV is a parent of (A), we will emit it. If there is a
1957 # 1) if REV is a parent of (A), we will emit it. If there is a
1960 # retention group ((B) above) that is blocked on REV being
1958 # retention group ((B) above) that is blocked on REV being
1961 # available, we emit all the revisions out of that retention
1959 # available, we emit all the revisions out of that retention
1962 # group first.
1960 # group first.
1963 #
1961 #
1964 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1962 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1965 # available, if such subgroup exist, we add REV to it and the subgroup is
1963 # available, if such subgroup exist, we add REV to it and the subgroup is
1966 # now awaiting for REV.parents() to be available.
1964 # now awaiting for REV.parents() to be available.
1967 #
1965 #
1968 # 3) finally if no such group existed in (B), we create a new subgroup.
1966 # 3) finally if no such group existed in (B), we create a new subgroup.
1969 #
1967 #
1970 #
1968 #
1971 # To bootstrap the algorithm, we emit the tipmost revision (which
1969 # To bootstrap the algorithm, we emit the tipmost revision (which
1972 # puts it in group (A) from above).
1970 # puts it in group (A) from above).
1973
1971
1974 revs.sort(reverse=True)
1972 revs.sort(reverse=True)
1975
1973
1976 # Set of parents of revision that have been emitted. They can be considered
1974 # Set of parents of revision that have been emitted. They can be considered
1977 # unblocked as the graph generator is already aware of them so there is no
1975 # unblocked as the graph generator is already aware of them so there is no
1978 # need to delay the revisions that reference them.
1976 # need to delay the revisions that reference them.
1979 #
1977 #
1980 # If someone wants to prioritize a branch over the others, pre-filling this
1978 # If someone wants to prioritize a branch over the others, pre-filling this
1981 # set will force all other branches to wait until this branch is ready to be
1979 # set will force all other branches to wait until this branch is ready to be
1982 # emitted.
1980 # emitted.
1983 unblocked = set(firstbranch)
1981 unblocked = set(firstbranch)
1984
1982
1985 # list of groups waiting to be displayed, each group is defined by:
1983 # list of groups waiting to be displayed, each group is defined by:
1986 #
1984 #
1987 # (revs: lists of revs waiting to be displayed,
1985 # (revs: lists of revs waiting to be displayed,
1988 # blocked: set of that cannot be displayed before those in 'revs')
1986 # blocked: set of that cannot be displayed before those in 'revs')
1989 #
1987 #
1990 # The second value ('blocked') correspond to parents of any revision in the
1988 # The second value ('blocked') correspond to parents of any revision in the
1991 # group ('revs') that is not itself contained in the group. The main idea
1989 # group ('revs') that is not itself contained in the group. The main idea
1992 # of this algorithm is to delay as much as possible the emission of any
1990 # of this algorithm is to delay as much as possible the emission of any
1993 # revision. This means waiting for the moment we are about to display
1991 # revision. This means waiting for the moment we are about to display
1994 # these parents to display the revs in a group.
1992 # these parents to display the revs in a group.
1995 #
1993 #
1996 # This first implementation is smart until it encounters a merge: it will
1994 # This first implementation is smart until it encounters a merge: it will
1997 # emit revs as soon as any parent is about to be emitted and can grow an
1995 # emit revs as soon as any parent is about to be emitted and can grow an
1998 # arbitrary number of revs in 'blocked'. In practice this mean we properly
1996 # arbitrary number of revs in 'blocked'. In practice this mean we properly
1999 # retains new branches but gives up on any special ordering for ancestors
1997 # retains new branches but gives up on any special ordering for ancestors
2000 # of merges. The implementation can be improved to handle this better.
1998 # of merges. The implementation can be improved to handle this better.
2001 #
1999 #
2002 # The first subgroup is special. It corresponds to all the revision that
2000 # The first subgroup is special. It corresponds to all the revision that
2003 # were already emitted. The 'revs' lists is expected to be empty and the
2001 # were already emitted. The 'revs' lists is expected to be empty and the
2004 # 'blocked' set contains the parents revisions of already emitted revision.
2002 # 'blocked' set contains the parents revisions of already emitted revision.
2005 #
2003 #
2006 # You could pre-seed the <parents> set of groups[0] to a specific
2004 # You could pre-seed the <parents> set of groups[0] to a specific
2007 # changesets to select what the first emitted branch should be.
2005 # changesets to select what the first emitted branch should be.
2008 groups = [([], unblocked)]
2006 groups = [([], unblocked)]
2009 pendingheap = []
2007 pendingheap = []
2010 pendingset = set()
2008 pendingset = set()
2011
2009
2012 heapq.heapify(pendingheap)
2010 heapq.heapify(pendingheap)
2013 heappop = heapq.heappop
2011 heappop = heapq.heappop
2014 heappush = heapq.heappush
2012 heappush = heapq.heappush
2015 for currentrev in revs:
2013 for currentrev in revs:
2016 # Heap works with smallest element, we want highest so we invert
2014 # Heap works with smallest element, we want highest so we invert
2017 if currentrev not in pendingset:
2015 if currentrev not in pendingset:
2018 heappush(pendingheap, -currentrev)
2016 heappush(pendingheap, -currentrev)
2019 pendingset.add(currentrev)
2017 pendingset.add(currentrev)
2020 # iterates on pending rev until after the current rev have been
2018 # iterates on pending rev until after the current rev have been
2021 # processed.
2019 # processed.
2022 rev = None
2020 rev = None
2023 while rev != currentrev:
2021 while rev != currentrev:
2024 rev = -heappop(pendingheap)
2022 rev = -heappop(pendingheap)
2025 pendingset.remove(rev)
2023 pendingset.remove(rev)
2026
2024
2027 # Seek for a subgroup blocked, waiting for the current revision.
2025 # Seek for a subgroup blocked, waiting for the current revision.
2028 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2026 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2029
2027
2030 if matching:
2028 if matching:
2031 # The main idea is to gather together all sets that are blocked
2029 # The main idea is to gather together all sets that are blocked
2032 # on the same revision.
2030 # on the same revision.
2033 #
2031 #
2034 # Groups are merged when a common blocking ancestor is
2032 # Groups are merged when a common blocking ancestor is
2035 # observed. For example, given two groups:
2033 # observed. For example, given two groups:
2036 #
2034 #
2037 # revs [5, 4] waiting for 1
2035 # revs [5, 4] waiting for 1
2038 # revs [3, 2] waiting for 1
2036 # revs [3, 2] waiting for 1
2039 #
2037 #
2040 # These two groups will be merged when we process
2038 # These two groups will be merged when we process
2041 # 1. In theory, we could have merged the groups when
2039 # 1. In theory, we could have merged the groups when
2042 # we added 2 to the group it is now in (we could have
2040 # we added 2 to the group it is now in (we could have
2043 # noticed the groups were both blocked on 1 then), but
2041 # noticed the groups were both blocked on 1 then), but
2044 # the way it works now makes the algorithm simpler.
2042 # the way it works now makes the algorithm simpler.
2045 #
2043 #
2046 # We also always keep the oldest subgroup first. We can
2044 # We also always keep the oldest subgroup first. We can
2047 # probably improve the behavior by having the longest set
2045 # probably improve the behavior by having the longest set
2048 # first. That way, graph algorithms could minimise the length
2046 # first. That way, graph algorithms could minimise the length
2049 # of parallel lines their drawing. This is currently not done.
2047 # of parallel lines their drawing. This is currently not done.
2050 targetidx = matching.pop(0)
2048 targetidx = matching.pop(0)
2051 trevs, tparents = groups[targetidx]
2049 trevs, tparents = groups[targetidx]
2052 for i in matching:
2050 for i in matching:
2053 gr = groups[i]
2051 gr = groups[i]
2054 trevs.extend(gr[0])
2052 trevs.extend(gr[0])
2055 tparents |= gr[1]
2053 tparents |= gr[1]
2056 # delete all merged subgroups (except the one we kept)
2054 # delete all merged subgroups (except the one we kept)
2057 # (starting from the last subgroup for performance and
2055 # (starting from the last subgroup for performance and
2058 # sanity reasons)
2056 # sanity reasons)
2059 for i in reversed(matching):
2057 for i in reversed(matching):
2060 del groups[i]
2058 del groups[i]
2061 else:
2059 else:
2062 # This is a new head. We create a new subgroup for it.
2060 # This is a new head. We create a new subgroup for it.
2063 targetidx = len(groups)
2061 targetidx = len(groups)
2064 groups.append(([], set([rev])))
2062 groups.append(([], set([rev])))
2065
2063
2066 gr = groups[targetidx]
2064 gr = groups[targetidx]
2067
2065
2068 # We now add the current nodes to this subgroups. This is done
2066 # We now add the current nodes to this subgroups. This is done
2069 # after the subgroup merging because all elements from a subgroup
2067 # after the subgroup merging because all elements from a subgroup
2070 # that relied on this rev must precede it.
2068 # that relied on this rev must precede it.
2071 #
2069 #
2072 # we also update the <parents> set to include the parents of the
2070 # we also update the <parents> set to include the parents of the
2073 # new nodes.
2071 # new nodes.
2074 if rev == currentrev: # only display stuff in rev
2072 if rev == currentrev: # only display stuff in rev
2075 gr[0].append(rev)
2073 gr[0].append(rev)
2076 gr[1].remove(rev)
2074 gr[1].remove(rev)
2077 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2075 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2078 gr[1].update(parents)
2076 gr[1].update(parents)
2079 for p in parents:
2077 for p in parents:
2080 if p not in pendingset:
2078 if p not in pendingset:
2081 pendingset.add(p)
2079 pendingset.add(p)
2082 heappush(pendingheap, -p)
2080 heappush(pendingheap, -p)
2083
2081
2084 # Look for a subgroup to display
2082 # Look for a subgroup to display
2085 #
2083 #
2086 # When unblocked is empty (if clause), we were not waiting for any
2084 # When unblocked is empty (if clause), we were not waiting for any
2087 # revisions during the first iteration (if no priority was given) or
2085 # revisions during the first iteration (if no priority was given) or
2088 # if we emitted a whole disconnected set of the graph (reached a
2086 # if we emitted a whole disconnected set of the graph (reached a
2089 # root). In that case we arbitrarily take the oldest known
2087 # root). In that case we arbitrarily take the oldest known
2090 # subgroup. The heuristic could probably be better.
2088 # subgroup. The heuristic could probably be better.
2091 #
2089 #
2092 # Otherwise (elif clause) if the subgroup is blocked on
2090 # Otherwise (elif clause) if the subgroup is blocked on
2093 # a revision we just emitted, we can safely emit it as
2091 # a revision we just emitted, we can safely emit it as
2094 # well.
2092 # well.
2095 if not unblocked:
2093 if not unblocked:
2096 if len(groups) > 1: # display other subset
2094 if len(groups) > 1: # display other subset
2097 targetidx = 1
2095 targetidx = 1
2098 gr = groups[1]
2096 gr = groups[1]
2099 elif not gr[1] & unblocked:
2097 elif not gr[1] & unblocked:
2100 gr = None
2098 gr = None
2101
2099
2102 if gr is not None:
2100 if gr is not None:
2103 # update the set of awaited revisions with the one from the
2101 # update the set of awaited revisions with the one from the
2104 # subgroup
2102 # subgroup
2105 unblocked |= gr[1]
2103 unblocked |= gr[1]
2106 # output all revisions in the subgroup
2104 # output all revisions in the subgroup
2107 for r in gr[0]:
2105 for r in gr[0]:
2108 yield r
2106 yield r
2109 # delete the subgroup that you just output
2107 # delete the subgroup that you just output
2110 # unless it is groups[0] in which case you just empty it.
2108 # unless it is groups[0] in which case you just empty it.
2111 if targetidx:
2109 if targetidx:
2112 del groups[targetidx]
2110 del groups[targetidx]
2113 else:
2111 else:
2114 gr[0][:] = []
2112 gr[0][:] = []
2115 # Check if we have some subgroup waiting for revisions we are not going to
2113 # Check if we have some subgroup waiting for revisions we are not going to
2116 # iterate over
2114 # iterate over
2117 for g in groups:
2115 for g in groups:
2118 for r in g[0]:
2116 for r in g[0]:
2119 yield r
2117 yield r
2120
2118
2121 @predicate('subrepo([pattern])')
2119 @predicate('subrepo([pattern])')
2122 def subrepo(repo, subset, x):
2120 def subrepo(repo, subset, x):
2123 """Changesets that add, modify or remove the given subrepo. If no subrepo
2121 """Changesets that add, modify or remove the given subrepo. If no subrepo
2124 pattern is named, any subrepo changes are returned.
2122 pattern is named, any subrepo changes are returned.
2125 """
2123 """
2126 # i18n: "subrepo" is a keyword
2124 # i18n: "subrepo" is a keyword
2127 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2125 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2128 pat = None
2126 pat = None
2129 if len(args) != 0:
2127 if len(args) != 0:
2130 pat = getstring(args[0], _("subrepo requires a pattern"))
2128 pat = getstring(args[0], _("subrepo requires a pattern"))
2131
2129
2132 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2130 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2133
2131
2134 def submatches(names):
2132 def submatches(names):
2135 k, p, m = util.stringmatcher(pat)
2133 k, p, m = util.stringmatcher(pat)
2136 for name in names:
2134 for name in names:
2137 if m(name):
2135 if m(name):
2138 yield name
2136 yield name
2139
2137
2140 def matches(x):
2138 def matches(x):
2141 c = repo[x]
2139 c = repo[x]
2142 s = repo.status(c.p1().node(), c.node(), match=m)
2140 s = repo.status(c.p1().node(), c.node(), match=m)
2143
2141
2144 if pat is None:
2142 if pat is None:
2145 return s.added or s.modified or s.removed
2143 return s.added or s.modified or s.removed
2146
2144
2147 if s.added:
2145 if s.added:
2148 return any(submatches(c.substate.keys()))
2146 return any(submatches(c.substate.keys()))
2149
2147
2150 if s.modified:
2148 if s.modified:
2151 subs = set(c.p1().substate.keys())
2149 subs = set(c.p1().substate.keys())
2152 subs.update(c.substate.keys())
2150 subs.update(c.substate.keys())
2153
2151
2154 for path in submatches(subs):
2152 for path in submatches(subs):
2155 if c.p1().substate.get(path) != c.substate.get(path):
2153 if c.p1().substate.get(path) != c.substate.get(path):
2156 return True
2154 return True
2157
2155
2158 if s.removed:
2156 if s.removed:
2159 return any(submatches(c.p1().substate.keys()))
2157 return any(submatches(c.p1().substate.keys()))
2160
2158
2161 return False
2159 return False
2162
2160
2163 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2161 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2164
2162
2165 def _substringmatcher(pattern):
2163 def _substringmatcher(pattern):
2166 kind, pattern, matcher = util.stringmatcher(pattern)
2164 kind, pattern, matcher = util.stringmatcher(pattern)
2167 if kind == 'literal':
2165 if kind == 'literal':
2168 matcher = lambda s: pattern in s
2166 matcher = lambda s: pattern in s
2169 return kind, pattern, matcher
2167 return kind, pattern, matcher
2170
2168
2171 @predicate('tag([name])', safe=True)
2169 @predicate('tag([name])', safe=True)
2172 def tag(repo, subset, x):
2170 def tag(repo, subset, x):
2173 """The specified tag by name, or all tagged revisions if no name is given.
2171 """The specified tag by name, or all tagged revisions if no name is given.
2174
2172
2175 If `name` starts with `re:`, the remainder of the name is treated as
2173 If `name` starts with `re:`, the remainder of the name is treated as
2176 a regular expression. To match a tag that actually starts with `re:`,
2174 a regular expression. To match a tag that actually starts with `re:`,
2177 use the prefix `literal:`.
2175 use the prefix `literal:`.
2178 """
2176 """
2179 # i18n: "tag" is a keyword
2177 # i18n: "tag" is a keyword
2180 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2178 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2181 cl = repo.changelog
2179 cl = repo.changelog
2182 if args:
2180 if args:
2183 pattern = getstring(args[0],
2181 pattern = getstring(args[0],
2184 # i18n: "tag" is a keyword
2182 # i18n: "tag" is a keyword
2185 _('the argument to tag must be a string'))
2183 _('the argument to tag must be a string'))
2186 kind, pattern, matcher = util.stringmatcher(pattern)
2184 kind, pattern, matcher = util.stringmatcher(pattern)
2187 if kind == 'literal':
2185 if kind == 'literal':
2188 # avoid resolving all tags
2186 # avoid resolving all tags
2189 tn = repo._tagscache.tags.get(pattern, None)
2187 tn = repo._tagscache.tags.get(pattern, None)
2190 if tn is None:
2188 if tn is None:
2191 raise error.RepoLookupError(_("tag '%s' does not exist")
2189 raise error.RepoLookupError(_("tag '%s' does not exist")
2192 % pattern)
2190 % pattern)
2193 s = set([repo[tn].rev()])
2191 s = set([repo[tn].rev()])
2194 else:
2192 else:
2195 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2193 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2196 else:
2194 else:
2197 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2195 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2198 return subset & s
2196 return subset & s
2199
2197
2200 @predicate('tagged', safe=True)
2198 @predicate('tagged', safe=True)
2201 def tagged(repo, subset, x):
2199 def tagged(repo, subset, x):
2202 return tag(repo, subset, x)
2200 return tag(repo, subset, x)
2203
2201
2204 @predicate('unstable()', safe=True)
2202 @predicate('unstable()', safe=True)
2205 def unstable(repo, subset, x):
2203 def unstable(repo, subset, x):
2206 """Non-obsolete changesets with obsolete ancestors.
2204 """Non-obsolete changesets with obsolete ancestors.
2207 """
2205 """
2208 # i18n: "unstable" is a keyword
2206 # i18n: "unstable" is a keyword
2209 getargs(x, 0, 0, _("unstable takes no arguments"))
2207 getargs(x, 0, 0, _("unstable takes no arguments"))
2210 unstables = obsmod.getrevs(repo, 'unstable')
2208 unstables = obsmod.getrevs(repo, 'unstable')
2211 return subset & unstables
2209 return subset & unstables
2212
2210
2213
2211
2214 @predicate('user(string)', safe=True)
2212 @predicate('user(string)', safe=True)
2215 def user(repo, subset, x):
2213 def user(repo, subset, x):
2216 """User name contains string. The match is case-insensitive.
2214 """User name contains string. The match is case-insensitive.
2217
2215
2218 If `string` starts with `re:`, the remainder of the string is treated as
2216 If `string` starts with `re:`, the remainder of the string is treated as
2219 a regular expression. To match a user that actually contains `re:`, use
2217 a regular expression. To match a user that actually contains `re:`, use
2220 the prefix `literal:`.
2218 the prefix `literal:`.
2221 """
2219 """
2222 return author(repo, subset, x)
2220 return author(repo, subset, x)
2223
2221
2224 # experimental
2222 # experimental
2225 @predicate('wdir', safe=True)
2223 @predicate('wdir', safe=True)
2226 def wdir(repo, subset, x):
2224 def wdir(repo, subset, x):
2227 # i18n: "wdir" is a keyword
2225 # i18n: "wdir" is a keyword
2228 getargs(x, 0, 0, _("wdir takes no arguments"))
2226 getargs(x, 0, 0, _("wdir takes no arguments"))
2229 if node.wdirrev in subset or isinstance(subset, fullreposet):
2227 if node.wdirrev in subset or isinstance(subset, fullreposet):
2230 return baseset([node.wdirrev])
2228 return baseset([node.wdirrev])
2231 return baseset()
2229 return baseset()
2232
2230
2233 # for internal use
2231 # for internal use
2234 @predicate('_list', safe=True)
2232 @predicate('_list', safe=True)
2235 def _list(repo, subset, x):
2233 def _list(repo, subset, x):
2236 s = getstring(x, "internal error")
2234 s = getstring(x, "internal error")
2237 if not s:
2235 if not s:
2238 return baseset()
2236 return baseset()
2239 # remove duplicates here. it's difficult for caller to deduplicate sets
2237 # remove duplicates here. it's difficult for caller to deduplicate sets
2240 # because different symbols can point to the same rev.
2238 # because different symbols can point to the same rev.
2241 cl = repo.changelog
2239 cl = repo.changelog
2242 ls = []
2240 ls = []
2243 seen = set()
2241 seen = set()
2244 for t in s.split('\0'):
2242 for t in s.split('\0'):
2245 try:
2243 try:
2246 # fast path for integer revision
2244 # fast path for integer revision
2247 r = int(t)
2245 r = int(t)
2248 if str(r) != t or r not in cl:
2246 if str(r) != t or r not in cl:
2249 raise ValueError
2247 raise ValueError
2250 revs = [r]
2248 revs = [r]
2251 except ValueError:
2249 except ValueError:
2252 revs = stringset(repo, subset, t)
2250 revs = stringset(repo, subset, t)
2253
2251
2254 for r in revs:
2252 for r in revs:
2255 if r in seen:
2253 if r in seen:
2256 continue
2254 continue
2257 if (r in subset
2255 if (r in subset
2258 or r == node.nullrev and isinstance(subset, fullreposet)):
2256 or r == node.nullrev and isinstance(subset, fullreposet)):
2259 ls.append(r)
2257 ls.append(r)
2260 seen.add(r)
2258 seen.add(r)
2261 return baseset(ls)
2259 return baseset(ls)
2262
2260
2263 # for internal use
2261 # for internal use
2264 @predicate('_intlist', safe=True)
2262 @predicate('_intlist', safe=True)
2265 def _intlist(repo, subset, x):
2263 def _intlist(repo, subset, x):
2266 s = getstring(x, "internal error")
2264 s = getstring(x, "internal error")
2267 if not s:
2265 if not s:
2268 return baseset()
2266 return baseset()
2269 ls = [int(r) for r in s.split('\0')]
2267 ls = [int(r) for r in s.split('\0')]
2270 s = subset
2268 s = subset
2271 return baseset([r for r in ls if r in s])
2269 return baseset([r for r in ls if r in s])
2272
2270
2273 # for internal use
2271 # for internal use
2274 @predicate('_hexlist', safe=True)
2272 @predicate('_hexlist', safe=True)
2275 def _hexlist(repo, subset, x):
2273 def _hexlist(repo, subset, x):
2276 s = getstring(x, "internal error")
2274 s = getstring(x, "internal error")
2277 if not s:
2275 if not s:
2278 return baseset()
2276 return baseset()
2279 cl = repo.changelog
2277 cl = repo.changelog
2280 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2278 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2281 s = subset
2279 s = subset
2282 return baseset([r for r in ls if r in s])
2280 return baseset([r for r in ls if r in s])
2283
2281
2284 methods = {
2282 methods = {
2285 "range": rangeset,
2283 "range": rangeset,
2286 "dagrange": dagrange,
2284 "dagrange": dagrange,
2287 "string": stringset,
2285 "string": stringset,
2288 "symbol": stringset,
2286 "symbol": stringset,
2289 "and": andset,
2287 "and": andset,
2290 "or": orset,
2288 "or": orset,
2291 "not": notset,
2289 "not": notset,
2292 "difference": differenceset,
2290 "difference": differenceset,
2293 "list": listset,
2291 "list": listset,
2294 "keyvalue": keyvaluepair,
2292 "keyvalue": keyvaluepair,
2295 "func": func,
2293 "func": func,
2296 "ancestor": ancestorspec,
2294 "ancestor": ancestorspec,
2297 "parent": parentspec,
2295 "parent": parentspec,
2298 "parentpost": p1,
2296 "parentpost": p1,
2299 }
2297 }
2300
2298
2301 def _matchonly(revs, bases):
2299 def _matchonly(revs, bases):
2302 """
2300 """
2303 >>> f = lambda *args: _matchonly(*map(parse, args))
2301 >>> f = lambda *args: _matchonly(*map(parse, args))
2304 >>> f('ancestors(A)', 'not ancestors(B)')
2302 >>> f('ancestors(A)', 'not ancestors(B)')
2305 ('list', ('symbol', 'A'), ('symbol', 'B'))
2303 ('list', ('symbol', 'A'), ('symbol', 'B'))
2306 """
2304 """
2307 if (revs is not None
2305 if (revs is not None
2308 and revs[0] == 'func'
2306 and revs[0] == 'func'
2309 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2307 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2310 and bases is not None
2308 and bases is not None
2311 and bases[0] == 'not'
2309 and bases[0] == 'not'
2312 and bases[1][0] == 'func'
2310 and bases[1][0] == 'func'
2313 and getstring(bases[1][1], _('not a symbol')) == 'ancestors'):
2311 and getstring(bases[1][1], _('not a symbol')) == 'ancestors'):
2314 return ('list', revs[2], bases[1][2])
2312 return ('list', revs[2], bases[1][2])
2315
2313
2316 def _optimize(x, small):
2314 def _optimize(x, small):
2317 if x is None:
2315 if x is None:
2318 return 0, x
2316 return 0, x
2319
2317
2320 smallbonus = 1
2318 smallbonus = 1
2321 if small:
2319 if small:
2322 smallbonus = .5
2320 smallbonus = .5
2323
2321
2324 op = x[0]
2322 op = x[0]
2325 if op == 'minus':
2323 if op == 'minus':
2326 return _optimize(('and', x[1], ('not', x[2])), small)
2324 return _optimize(('and', x[1], ('not', x[2])), small)
2327 elif op == 'only':
2325 elif op == 'only':
2328 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2326 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2329 return _optimize(t, small)
2327 return _optimize(t, small)
2330 elif op == 'onlypost':
2328 elif op == 'onlypost':
2331 return _optimize(('func', ('symbol', 'only'), x[1]), small)
2329 return _optimize(('func', ('symbol', 'only'), x[1]), small)
2332 elif op == 'dagrangepre':
2330 elif op == 'dagrangepre':
2333 return _optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2331 return _optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2334 elif op == 'dagrangepost':
2332 elif op == 'dagrangepost':
2335 return _optimize(('func', ('symbol', 'descendants'), x[1]), small)
2333 return _optimize(('func', ('symbol', 'descendants'), x[1]), small)
2336 elif op == 'rangeall':
2334 elif op == 'rangeall':
2337 return _optimize(('range', ('string', '0'), ('string', 'tip')), small)
2335 return _optimize(('range', ('string', '0'), ('string', 'tip')), small)
2338 elif op == 'rangepre':
2336 elif op == 'rangepre':
2339 return _optimize(('range', ('string', '0'), x[1]), small)
2337 return _optimize(('range', ('string', '0'), x[1]), small)
2340 elif op == 'rangepost':
2338 elif op == 'rangepost':
2341 return _optimize(('range', x[1], ('string', 'tip')), small)
2339 return _optimize(('range', x[1], ('string', 'tip')), small)
2342 elif op == 'negate':
2340 elif op == 'negate':
2343 s = getstring(x[1], _("can't negate that"))
2341 s = getstring(x[1], _("can't negate that"))
2344 return _optimize(('string', '-' + s), small)
2342 return _optimize(('string', '-' + s), small)
2345 elif op in 'string symbol negate':
2343 elif op in 'string symbol negate':
2346 return smallbonus, x # single revisions are small
2344 return smallbonus, x # single revisions are small
2347 elif op == 'and':
2345 elif op == 'and':
2348 wa, ta = _optimize(x[1], True)
2346 wa, ta = _optimize(x[1], True)
2349 wb, tb = _optimize(x[2], True)
2347 wb, tb = _optimize(x[2], True)
2350 w = min(wa, wb)
2348 w = min(wa, wb)
2351
2349
2352 # (::x and not ::y)/(not ::y and ::x) have a fast path
2350 # (::x and not ::y)/(not ::y and ::x) have a fast path
2353 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2351 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2354 if tm:
2352 if tm:
2355 return w, ('func', ('symbol', 'only'), tm)
2353 return w, ('func', ('symbol', 'only'), tm)
2356
2354
2357 if tb is not None and tb[0] == 'not':
2355 if tb is not None and tb[0] == 'not':
2358 return wa, ('difference', ta, tb[1])
2356 return wa, ('difference', ta, tb[1])
2359
2357
2360 if wa > wb:
2358 if wa > wb:
2361 return w, (op, tb, ta)
2359 return w, (op, tb, ta)
2362 return w, (op, ta, tb)
2360 return w, (op, ta, tb)
2363 elif op == 'or':
2361 elif op == 'or':
2364 # fast path for machine-generated expression, that is likely to have
2362 # fast path for machine-generated expression, that is likely to have
2365 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2363 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2366 ws, ts, ss = [], [], []
2364 ws, ts, ss = [], [], []
2367 def flushss():
2365 def flushss():
2368 if not ss:
2366 if not ss:
2369 return
2367 return
2370 if len(ss) == 1:
2368 if len(ss) == 1:
2371 w, t = ss[0]
2369 w, t = ss[0]
2372 else:
2370 else:
2373 s = '\0'.join(t[1] for w, t in ss)
2371 s = '\0'.join(t[1] for w, t in ss)
2374 y = ('func', ('symbol', '_list'), ('string', s))
2372 y = ('func', ('symbol', '_list'), ('string', s))
2375 w, t = _optimize(y, False)
2373 w, t = _optimize(y, False)
2376 ws.append(w)
2374 ws.append(w)
2377 ts.append(t)
2375 ts.append(t)
2378 del ss[:]
2376 del ss[:]
2379 for y in x[1:]:
2377 for y in x[1:]:
2380 w, t = _optimize(y, False)
2378 w, t = _optimize(y, False)
2381 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2379 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2382 ss.append((w, t))
2380 ss.append((w, t))
2383 continue
2381 continue
2384 flushss()
2382 flushss()
2385 ws.append(w)
2383 ws.append(w)
2386 ts.append(t)
2384 ts.append(t)
2387 flushss()
2385 flushss()
2388 if len(ts) == 1:
2386 if len(ts) == 1:
2389 return ws[0], ts[0] # 'or' operation is fully optimized out
2387 return ws[0], ts[0] # 'or' operation is fully optimized out
2390 # we can't reorder trees by weight because it would change the order.
2388 # we can't reorder trees by weight because it would change the order.
2391 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2389 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2392 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2390 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2393 return max(ws), (op,) + tuple(ts)
2391 return max(ws), (op,) + tuple(ts)
2394 elif op == 'not':
2392 elif op == 'not':
2395 # Optimize not public() to _notpublic() because we have a fast version
2393 # Optimize not public() to _notpublic() because we have a fast version
2396 if x[1] == ('func', ('symbol', 'public'), None):
2394 if x[1] == ('func', ('symbol', 'public'), None):
2397 newsym = ('func', ('symbol', '_notpublic'), None)
2395 newsym = ('func', ('symbol', '_notpublic'), None)
2398 o = _optimize(newsym, not small)
2396 o = _optimize(newsym, not small)
2399 return o[0], o[1]
2397 return o[0], o[1]
2400 else:
2398 else:
2401 o = _optimize(x[1], not small)
2399 o = _optimize(x[1], not small)
2402 return o[0], (op, o[1])
2400 return o[0], (op, o[1])
2403 elif op == 'parentpost':
2401 elif op == 'parentpost':
2404 o = _optimize(x[1], small)
2402 o = _optimize(x[1], small)
2405 return o[0], (op, o[1])
2403 return o[0], (op, o[1])
2406 elif op == 'group':
2404 elif op == 'group':
2407 return _optimize(x[1], small)
2405 return _optimize(x[1], small)
2408 elif op in 'dagrange range parent ancestorspec':
2406 elif op in 'dagrange range parent ancestorspec':
2409 if op == 'parent':
2407 if op == 'parent':
2410 # x^:y means (x^) : y, not x ^ (:y)
2408 # x^:y means (x^) : y, not x ^ (:y)
2411 post = ('parentpost', x[1])
2409 post = ('parentpost', x[1])
2412 if x[2][0] == 'dagrangepre':
2410 if x[2][0] == 'dagrangepre':
2413 return _optimize(('dagrange', post, x[2][1]), small)
2411 return _optimize(('dagrange', post, x[2][1]), small)
2414 elif x[2][0] == 'rangepre':
2412 elif x[2][0] == 'rangepre':
2415 return _optimize(('range', post, x[2][1]), small)
2413 return _optimize(('range', post, x[2][1]), small)
2416
2414
2417 wa, ta = _optimize(x[1], small)
2415 wa, ta = _optimize(x[1], small)
2418 wb, tb = _optimize(x[2], small)
2416 wb, tb = _optimize(x[2], small)
2419 return wa + wb, (op, ta, tb)
2417 return wa + wb, (op, ta, tb)
2420 elif op == 'list':
2418 elif op == 'list':
2421 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2419 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2422 return sum(ws), (op,) + ts
2420 return sum(ws), (op,) + ts
2423 elif op == 'func':
2421 elif op == 'func':
2424 f = getstring(x[1], _("not a symbol"))
2422 f = getstring(x[1], _("not a symbol"))
2425 wa, ta = _optimize(x[2], small)
2423 wa, ta = _optimize(x[2], small)
2426 if f in ("author branch closed date desc file grep keyword "
2424 if f in ("author branch closed date desc file grep keyword "
2427 "outgoing user"):
2425 "outgoing user"):
2428 w = 10 # slow
2426 w = 10 # slow
2429 elif f in "modifies adds removes":
2427 elif f in "modifies adds removes":
2430 w = 30 # slower
2428 w = 30 # slower
2431 elif f == "contains":
2429 elif f == "contains":
2432 w = 100 # very slow
2430 w = 100 # very slow
2433 elif f == "ancestor":
2431 elif f == "ancestor":
2434 w = 1 * smallbonus
2432 w = 1 * smallbonus
2435 elif f in "reverse limit first _intlist":
2433 elif f in "reverse limit first _intlist":
2436 w = 0
2434 w = 0
2437 elif f in "sort":
2435 elif f in "sort":
2438 w = 10 # assume most sorts look at changelog
2436 w = 10 # assume most sorts look at changelog
2439 else:
2437 else:
2440 w = 1
2438 w = 1
2441 return w + wa, (op, x[1], ta)
2439 return w + wa, (op, x[1], ta)
2442 return 1, x
2440 return 1, x
2443
2441
2444 def optimize(tree):
2442 def optimize(tree):
2445 _weight, newtree = _optimize(tree, small=True)
2443 _weight, newtree = _optimize(tree, small=True)
2446 return newtree
2444 return newtree
2447
2445
2448 # the set of valid characters for the initial letter of symbols in
2446 # the set of valid characters for the initial letter of symbols in
2449 # alias declarations and definitions
2447 # alias declarations and definitions
2450 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2448 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2451 if c.isalnum() or c in '._@$' or ord(c) > 127)
2449 if c.isalnum() or c in '._@$' or ord(c) > 127)
2452
2450
2453 def _parsewith(spec, lookup=None, syminitletters=None):
2451 def _parsewith(spec, lookup=None, syminitletters=None):
2454 """Generate a parse tree of given spec with given tokenizing options
2452 """Generate a parse tree of given spec with given tokenizing options
2455
2453
2456 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2454 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2457 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2455 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2458 >>> _parsewith('$1')
2456 >>> _parsewith('$1')
2459 Traceback (most recent call last):
2457 Traceback (most recent call last):
2460 ...
2458 ...
2461 ParseError: ("syntax error in revset '$1'", 0)
2459 ParseError: ("syntax error in revset '$1'", 0)
2462 >>> _parsewith('foo bar')
2460 >>> _parsewith('foo bar')
2463 Traceback (most recent call last):
2461 Traceback (most recent call last):
2464 ...
2462 ...
2465 ParseError: ('invalid token', 4)
2463 ParseError: ('invalid token', 4)
2466 """
2464 """
2467 p = parser.parser(elements)
2465 p = parser.parser(elements)
2468 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2466 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2469 syminitletters=syminitletters))
2467 syminitletters=syminitletters))
2470 if pos != len(spec):
2468 if pos != len(spec):
2471 raise error.ParseError(_('invalid token'), pos)
2469 raise error.ParseError(_('invalid token'), pos)
2472 return parser.simplifyinfixops(tree, ('list', 'or'))
2470 return parser.simplifyinfixops(tree, ('list', 'or'))
2473
2471
2474 class _aliasrules(parser.basealiasrules):
2472 class _aliasrules(parser.basealiasrules):
2475 """Parsing and expansion rule set of revset aliases"""
2473 """Parsing and expansion rule set of revset aliases"""
2476 _section = _('revset alias')
2474 _section = _('revset alias')
2477
2475
2478 @staticmethod
2476 @staticmethod
2479 def _parse(spec):
2477 def _parse(spec):
2480 """Parse alias declaration/definition ``spec``
2478 """Parse alias declaration/definition ``spec``
2481
2479
2482 This allows symbol names to use also ``$`` as an initial letter
2480 This allows symbol names to use also ``$`` as an initial letter
2483 (for backward compatibility), and callers of this function should
2481 (for backward compatibility), and callers of this function should
2484 examine whether ``$`` is used also for unexpected symbols or not.
2482 examine whether ``$`` is used also for unexpected symbols or not.
2485 """
2483 """
2486 return _parsewith(spec, syminitletters=_aliassyminitletters)
2484 return _parsewith(spec, syminitletters=_aliassyminitletters)
2487
2485
2488 @staticmethod
2486 @staticmethod
2489 def _trygetfunc(tree):
2487 def _trygetfunc(tree):
2490 if tree[0] == 'func' and tree[1][0] == 'symbol':
2488 if tree[0] == 'func' and tree[1][0] == 'symbol':
2491 return tree[1][1], getlist(tree[2])
2489 return tree[1][1], getlist(tree[2])
2492
2490
2493 def expandaliases(ui, tree, showwarning=None):
2491 def expandaliases(ui, tree, showwarning=None):
2494 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2492 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2495 tree = _aliasrules.expand(aliases, tree)
2493 tree = _aliasrules.expand(aliases, tree)
2496 if showwarning:
2494 if showwarning:
2497 # warn about problematic (but not referred) aliases
2495 # warn about problematic (but not referred) aliases
2498 for name, alias in sorted(aliases.iteritems()):
2496 for name, alias in sorted(aliases.iteritems()):
2499 if alias.error and not alias.warned:
2497 if alias.error and not alias.warned:
2500 showwarning(_('warning: %s\n') % (alias.error))
2498 showwarning(_('warning: %s\n') % (alias.error))
2501 alias.warned = True
2499 alias.warned = True
2502 return tree
2500 return tree
2503
2501
2504 def foldconcat(tree):
2502 def foldconcat(tree):
2505 """Fold elements to be concatenated by `##`
2503 """Fold elements to be concatenated by `##`
2506 """
2504 """
2507 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2505 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2508 return tree
2506 return tree
2509 if tree[0] == '_concat':
2507 if tree[0] == '_concat':
2510 pending = [tree]
2508 pending = [tree]
2511 l = []
2509 l = []
2512 while pending:
2510 while pending:
2513 e = pending.pop()
2511 e = pending.pop()
2514 if e[0] == '_concat':
2512 if e[0] == '_concat':
2515 pending.extend(reversed(e[1:]))
2513 pending.extend(reversed(e[1:]))
2516 elif e[0] in ('string', 'symbol'):
2514 elif e[0] in ('string', 'symbol'):
2517 l.append(e[1])
2515 l.append(e[1])
2518 else:
2516 else:
2519 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2517 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2520 raise error.ParseError(msg)
2518 raise error.ParseError(msg)
2521 return ('string', ''.join(l))
2519 return ('string', ''.join(l))
2522 else:
2520 else:
2523 return tuple(foldconcat(t) for t in tree)
2521 return tuple(foldconcat(t) for t in tree)
2524
2522
2525 def parse(spec, lookup=None):
2523 def parse(spec, lookup=None):
2526 return _parsewith(spec, lookup=lookup)
2524 return _parsewith(spec, lookup=lookup)
2527
2525
2528 def posttreebuilthook(tree, repo):
2526 def posttreebuilthook(tree, repo):
2529 # hook for extensions to execute code on the optimized tree
2527 # hook for extensions to execute code on the optimized tree
2530 pass
2528 pass
2531
2529
2532 def match(ui, spec, repo=None):
2530 def match(ui, spec, repo=None):
2533 if not spec:
2531 if not spec:
2534 raise error.ParseError(_("empty query"))
2532 raise error.ParseError(_("empty query"))
2535 lookup = None
2533 lookup = None
2536 if repo:
2534 if repo:
2537 lookup = repo.__contains__
2535 lookup = repo.__contains__
2538 tree = parse(spec, lookup)
2536 tree = parse(spec, lookup)
2539 return _makematcher(ui, tree, repo)
2537 return _makematcher(ui, tree, repo)
2540
2538
2541 def matchany(ui, specs, repo=None):
2539 def matchany(ui, specs, repo=None):
2542 """Create a matcher that will include any revisions matching one of the
2540 """Create a matcher that will include any revisions matching one of the
2543 given specs"""
2541 given specs"""
2544 if not specs:
2542 if not specs:
2545 def mfunc(repo, subset=None):
2543 def mfunc(repo, subset=None):
2546 return baseset()
2544 return baseset()
2547 return mfunc
2545 return mfunc
2548 if not all(specs):
2546 if not all(specs):
2549 raise error.ParseError(_("empty query"))
2547 raise error.ParseError(_("empty query"))
2550 lookup = None
2548 lookup = None
2551 if repo:
2549 if repo:
2552 lookup = repo.__contains__
2550 lookup = repo.__contains__
2553 if len(specs) == 1:
2551 if len(specs) == 1:
2554 tree = parse(specs[0], lookup)
2552 tree = parse(specs[0], lookup)
2555 else:
2553 else:
2556 tree = ('or',) + tuple(parse(s, lookup) for s in specs)
2554 tree = ('or',) + tuple(parse(s, lookup) for s in specs)
2557 return _makematcher(ui, tree, repo)
2555 return _makematcher(ui, tree, repo)
2558
2556
2559 def _makematcher(ui, tree, repo):
2557 def _makematcher(ui, tree, repo):
2560 if ui:
2558 if ui:
2561 tree = expandaliases(ui, tree, showwarning=ui.warn)
2559 tree = expandaliases(ui, tree, showwarning=ui.warn)
2562 tree = foldconcat(tree)
2560 tree = foldconcat(tree)
2563 tree = optimize(tree)
2561 tree = optimize(tree)
2564 posttreebuilthook(tree, repo)
2562 posttreebuilthook(tree, repo)
2565 def mfunc(repo, subset=None):
2563 def mfunc(repo, subset=None):
2566 if subset is None:
2564 if subset is None:
2567 subset = fullreposet(repo)
2565 subset = fullreposet(repo)
2568 if util.safehasattr(subset, 'isascending'):
2566 if util.safehasattr(subset, 'isascending'):
2569 result = getset(repo, subset, tree)
2567 result = getset(repo, subset, tree)
2570 else:
2568 else:
2571 result = getset(repo, baseset(subset), tree)
2569 result = getset(repo, baseset(subset), tree)
2572 return result
2570 return result
2573 return mfunc
2571 return mfunc
2574
2572
2575 def formatspec(expr, *args):
2573 def formatspec(expr, *args):
2576 '''
2574 '''
2577 This is a convenience function for using revsets internally, and
2575 This is a convenience function for using revsets internally, and
2578 escapes arguments appropriately. Aliases are intentionally ignored
2576 escapes arguments appropriately. Aliases are intentionally ignored
2579 so that intended expression behavior isn't accidentally subverted.
2577 so that intended expression behavior isn't accidentally subverted.
2580
2578
2581 Supported arguments:
2579 Supported arguments:
2582
2580
2583 %r = revset expression, parenthesized
2581 %r = revset expression, parenthesized
2584 %d = int(arg), no quoting
2582 %d = int(arg), no quoting
2585 %s = string(arg), escaped and single-quoted
2583 %s = string(arg), escaped and single-quoted
2586 %b = arg.branch(), escaped and single-quoted
2584 %b = arg.branch(), escaped and single-quoted
2587 %n = hex(arg), single-quoted
2585 %n = hex(arg), single-quoted
2588 %% = a literal '%'
2586 %% = a literal '%'
2589
2587
2590 Prefixing the type with 'l' specifies a parenthesized list of that type.
2588 Prefixing the type with 'l' specifies a parenthesized list of that type.
2591
2589
2592 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2590 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2593 '(10 or 11):: and ((this()) or (that()))'
2591 '(10 or 11):: and ((this()) or (that()))'
2594 >>> formatspec('%d:: and not %d::', 10, 20)
2592 >>> formatspec('%d:: and not %d::', 10, 20)
2595 '10:: and not 20::'
2593 '10:: and not 20::'
2596 >>> formatspec('%ld or %ld', [], [1])
2594 >>> formatspec('%ld or %ld', [], [1])
2597 "_list('') or 1"
2595 "_list('') or 1"
2598 >>> formatspec('keyword(%s)', 'foo\\xe9')
2596 >>> formatspec('keyword(%s)', 'foo\\xe9')
2599 "keyword('foo\\\\xe9')"
2597 "keyword('foo\\\\xe9')"
2600 >>> b = lambda: 'default'
2598 >>> b = lambda: 'default'
2601 >>> b.branch = b
2599 >>> b.branch = b
2602 >>> formatspec('branch(%b)', b)
2600 >>> formatspec('branch(%b)', b)
2603 "branch('default')"
2601 "branch('default')"
2604 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2602 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2605 "root(_list('a\\x00b\\x00c\\x00d'))"
2603 "root(_list('a\\x00b\\x00c\\x00d'))"
2606 '''
2604 '''
2607
2605
2608 def quote(s):
2606 def quote(s):
2609 return repr(str(s))
2607 return repr(str(s))
2610
2608
2611 def argtype(c, arg):
2609 def argtype(c, arg):
2612 if c == 'd':
2610 if c == 'd':
2613 return str(int(arg))
2611 return str(int(arg))
2614 elif c == 's':
2612 elif c == 's':
2615 return quote(arg)
2613 return quote(arg)
2616 elif c == 'r':
2614 elif c == 'r':
2617 parse(arg) # make sure syntax errors are confined
2615 parse(arg) # make sure syntax errors are confined
2618 return '(%s)' % arg
2616 return '(%s)' % arg
2619 elif c == 'n':
2617 elif c == 'n':
2620 return quote(node.hex(arg))
2618 return quote(node.hex(arg))
2621 elif c == 'b':
2619 elif c == 'b':
2622 return quote(arg.branch())
2620 return quote(arg.branch())
2623
2621
2624 def listexp(s, t):
2622 def listexp(s, t):
2625 l = len(s)
2623 l = len(s)
2626 if l == 0:
2624 if l == 0:
2627 return "_list('')"
2625 return "_list('')"
2628 elif l == 1:
2626 elif l == 1:
2629 return argtype(t, s[0])
2627 return argtype(t, s[0])
2630 elif t == 'd':
2628 elif t == 'd':
2631 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2629 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2632 elif t == 's':
2630 elif t == 's':
2633 return "_list('%s')" % "\0".join(s)
2631 return "_list('%s')" % "\0".join(s)
2634 elif t == 'n':
2632 elif t == 'n':
2635 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2633 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2636 elif t == 'b':
2634 elif t == 'b':
2637 return "_list('%s')" % "\0".join(a.branch() for a in s)
2635 return "_list('%s')" % "\0".join(a.branch() for a in s)
2638
2636
2639 m = l // 2
2637 m = l // 2
2640 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2638 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2641
2639
2642 ret = ''
2640 ret = ''
2643 pos = 0
2641 pos = 0
2644 arg = 0
2642 arg = 0
2645 while pos < len(expr):
2643 while pos < len(expr):
2646 c = expr[pos]
2644 c = expr[pos]
2647 if c == '%':
2645 if c == '%':
2648 pos += 1
2646 pos += 1
2649 d = expr[pos]
2647 d = expr[pos]
2650 if d == '%':
2648 if d == '%':
2651 ret += d
2649 ret += d
2652 elif d in 'dsnbr':
2650 elif d in 'dsnbr':
2653 ret += argtype(d, args[arg])
2651 ret += argtype(d, args[arg])
2654 arg += 1
2652 arg += 1
2655 elif d == 'l':
2653 elif d == 'l':
2656 # a list of some type
2654 # a list of some type
2657 pos += 1
2655 pos += 1
2658 d = expr[pos]
2656 d = expr[pos]
2659 ret += listexp(list(args[arg]), d)
2657 ret += listexp(list(args[arg]), d)
2660 arg += 1
2658 arg += 1
2661 else:
2659 else:
2662 raise error.Abort(_('unexpected revspec format character %s')
2660 raise error.Abort(_('unexpected revspec format character %s')
2663 % d)
2661 % d)
2664 else:
2662 else:
2665 ret += c
2663 ret += c
2666 pos += 1
2664 pos += 1
2667
2665
2668 return ret
2666 return ret
2669
2667
2670 def prettyformat(tree):
2668 def prettyformat(tree):
2671 return parser.prettyformat(tree, ('string', 'symbol'))
2669 return parser.prettyformat(tree, ('string', 'symbol'))
2672
2670
2673 def depth(tree):
2671 def depth(tree):
2674 if isinstance(tree, tuple):
2672 if isinstance(tree, tuple):
2675 return max(map(depth, tree)) + 1
2673 return max(map(depth, tree)) + 1
2676 else:
2674 else:
2677 return 0
2675 return 0
2678
2676
2679 def funcsused(tree):
2677 def funcsused(tree):
2680 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2678 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2681 return set()
2679 return set()
2682 else:
2680 else:
2683 funcs = set()
2681 funcs = set()
2684 for s in tree[1:]:
2682 for s in tree[1:]:
2685 funcs |= funcsused(s)
2683 funcs |= funcsused(s)
2686 if tree[0] == 'func':
2684 if tree[0] == 'func':
2687 funcs.add(tree[1][1])
2685 funcs.add(tree[1][1])
2688 return funcs
2686 return funcs
2689
2687
2690 def _formatsetrepr(r):
2688 def _formatsetrepr(r):
2691 """Format an optional printable representation of a set
2689 """Format an optional printable representation of a set
2692
2690
2693 ======== =================================
2691 ======== =================================
2694 type(r) example
2692 type(r) example
2695 ======== =================================
2693 ======== =================================
2696 tuple ('<not %r>', other)
2694 tuple ('<not %r>', other)
2697 str '<branch closed>'
2695 str '<branch closed>'
2698 callable lambda: '<branch %r>' % sorted(b)
2696 callable lambda: '<branch %r>' % sorted(b)
2699 object other
2697 object other
2700 ======== =================================
2698 ======== =================================
2701 """
2699 """
2702 if r is None:
2700 if r is None:
2703 return ''
2701 return ''
2704 elif isinstance(r, tuple):
2702 elif isinstance(r, tuple):
2705 return r[0] % r[1:]
2703 return r[0] % r[1:]
2706 elif isinstance(r, str):
2704 elif isinstance(r, str):
2707 return r
2705 return r
2708 elif callable(r):
2706 elif callable(r):
2709 return r()
2707 return r()
2710 else:
2708 else:
2711 return repr(r)
2709 return repr(r)
2712
2710
2713 class abstractsmartset(object):
2711 class abstractsmartset(object):
2714
2712
2715 def __nonzero__(self):
2713 def __nonzero__(self):
2716 """True if the smartset is not empty"""
2714 """True if the smartset is not empty"""
2717 raise NotImplementedError()
2715 raise NotImplementedError()
2718
2716
2719 def __contains__(self, rev):
2717 def __contains__(self, rev):
2720 """provide fast membership testing"""
2718 """provide fast membership testing"""
2721 raise NotImplementedError()
2719 raise NotImplementedError()
2722
2720
2723 def __iter__(self):
2721 def __iter__(self):
2724 """iterate the set in the order it is supposed to be iterated"""
2722 """iterate the set in the order it is supposed to be iterated"""
2725 raise NotImplementedError()
2723 raise NotImplementedError()
2726
2724
2727 # Attributes containing a function to perform a fast iteration in a given
2725 # Attributes containing a function to perform a fast iteration in a given
2728 # direction. A smartset can have none, one, or both defined.
2726 # direction. A smartset can have none, one, or both defined.
2729 #
2727 #
2730 # Default value is None instead of a function returning None to avoid
2728 # Default value is None instead of a function returning None to avoid
2731 # initializing an iterator just for testing if a fast method exists.
2729 # initializing an iterator just for testing if a fast method exists.
2732 fastasc = None
2730 fastasc = None
2733 fastdesc = None
2731 fastdesc = None
2734
2732
2735 def isascending(self):
2733 def isascending(self):
2736 """True if the set will iterate in ascending order"""
2734 """True if the set will iterate in ascending order"""
2737 raise NotImplementedError()
2735 raise NotImplementedError()
2738
2736
2739 def isdescending(self):
2737 def isdescending(self):
2740 """True if the set will iterate in descending order"""
2738 """True if the set will iterate in descending order"""
2741 raise NotImplementedError()
2739 raise NotImplementedError()
2742
2740
2743 def istopo(self):
2741 def istopo(self):
2744 """True if the set will iterate in topographical order"""
2742 """True if the set will iterate in topographical order"""
2745 raise NotImplementedError()
2743 raise NotImplementedError()
2746
2744
2747 @util.cachefunc
2745 @util.cachefunc
2748 def min(self):
2746 def min(self):
2749 """return the minimum element in the set"""
2747 """return the minimum element in the set"""
2750 if self.fastasc is not None:
2748 if self.fastasc is not None:
2751 for r in self.fastasc():
2749 for r in self.fastasc():
2752 return r
2750 return r
2753 raise ValueError('arg is an empty sequence')
2751 raise ValueError('arg is an empty sequence')
2754 return min(self)
2752 return min(self)
2755
2753
2756 @util.cachefunc
2754 @util.cachefunc
2757 def max(self):
2755 def max(self):
2758 """return the maximum element in the set"""
2756 """return the maximum element in the set"""
2759 if self.fastdesc is not None:
2757 if self.fastdesc is not None:
2760 for r in self.fastdesc():
2758 for r in self.fastdesc():
2761 return r
2759 return r
2762 raise ValueError('arg is an empty sequence')
2760 raise ValueError('arg is an empty sequence')
2763 return max(self)
2761 return max(self)
2764
2762
2765 def first(self):
2763 def first(self):
2766 """return the first element in the set (user iteration perspective)
2764 """return the first element in the set (user iteration perspective)
2767
2765
2768 Return None if the set is empty"""
2766 Return None if the set is empty"""
2769 raise NotImplementedError()
2767 raise NotImplementedError()
2770
2768
2771 def last(self):
2769 def last(self):
2772 """return the last element in the set (user iteration perspective)
2770 """return the last element in the set (user iteration perspective)
2773
2771
2774 Return None if the set is empty"""
2772 Return None if the set is empty"""
2775 raise NotImplementedError()
2773 raise NotImplementedError()
2776
2774
2777 def __len__(self):
2775 def __len__(self):
2778 """return the length of the smartsets
2776 """return the length of the smartsets
2779
2777
2780 This can be expensive on smartset that could be lazy otherwise."""
2778 This can be expensive on smartset that could be lazy otherwise."""
2781 raise NotImplementedError()
2779 raise NotImplementedError()
2782
2780
2783 def reverse(self):
2781 def reverse(self):
2784 """reverse the expected iteration order"""
2782 """reverse the expected iteration order"""
2785 raise NotImplementedError()
2783 raise NotImplementedError()
2786
2784
2787 def sort(self, reverse=True):
2785 def sort(self, reverse=True):
2788 """get the set to iterate in an ascending or descending order"""
2786 """get the set to iterate in an ascending or descending order"""
2789 raise NotImplementedError()
2787 raise NotImplementedError()
2790
2788
2791 def __and__(self, other):
2789 def __and__(self, other):
2792 """Returns a new object with the intersection of the two collections.
2790 """Returns a new object with the intersection of the two collections.
2793
2791
2794 This is part of the mandatory API for smartset."""
2792 This is part of the mandatory API for smartset."""
2795 if isinstance(other, fullreposet):
2793 if isinstance(other, fullreposet):
2796 return self
2794 return self
2797 return self.filter(other.__contains__, condrepr=other, cache=False)
2795 return self.filter(other.__contains__, condrepr=other, cache=False)
2798
2796
2799 def __add__(self, other):
2797 def __add__(self, other):
2800 """Returns a new object with the union of the two collections.
2798 """Returns a new object with the union of the two collections.
2801
2799
2802 This is part of the mandatory API for smartset."""
2800 This is part of the mandatory API for smartset."""
2803 return addset(self, other)
2801 return addset(self, other)
2804
2802
2805 def __sub__(self, other):
2803 def __sub__(self, other):
2806 """Returns a new object with the substraction of the two collections.
2804 """Returns a new object with the substraction of the two collections.
2807
2805
2808 This is part of the mandatory API for smartset."""
2806 This is part of the mandatory API for smartset."""
2809 c = other.__contains__
2807 c = other.__contains__
2810 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2808 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2811 cache=False)
2809 cache=False)
2812
2810
2813 def filter(self, condition, condrepr=None, cache=True):
2811 def filter(self, condition, condrepr=None, cache=True):
2814 """Returns this smartset filtered by condition as a new smartset.
2812 """Returns this smartset filtered by condition as a new smartset.
2815
2813
2816 `condition` is a callable which takes a revision number and returns a
2814 `condition` is a callable which takes a revision number and returns a
2817 boolean. Optional `condrepr` provides a printable representation of
2815 boolean. Optional `condrepr` provides a printable representation of
2818 the given `condition`.
2816 the given `condition`.
2819
2817
2820 This is part of the mandatory API for smartset."""
2818 This is part of the mandatory API for smartset."""
2821 # builtin cannot be cached. but do not needs to
2819 # builtin cannot be cached. but do not needs to
2822 if cache and util.safehasattr(condition, 'func_code'):
2820 if cache and util.safehasattr(condition, 'func_code'):
2823 condition = util.cachefunc(condition)
2821 condition = util.cachefunc(condition)
2824 return filteredset(self, condition, condrepr)
2822 return filteredset(self, condition, condrepr)
2825
2823
2826 class baseset(abstractsmartset):
2824 class baseset(abstractsmartset):
2827 """Basic data structure that represents a revset and contains the basic
2825 """Basic data structure that represents a revset and contains the basic
2828 operation that it should be able to perform.
2826 operation that it should be able to perform.
2829
2827
2830 Every method in this class should be implemented by any smartset class.
2828 Every method in this class should be implemented by any smartset class.
2831 """
2829 """
2832 def __init__(self, data=(), datarepr=None, istopo=False):
2830 def __init__(self, data=(), datarepr=None, istopo=False):
2833 """
2831 """
2834 datarepr: a tuple of (format, obj, ...), a function or an object that
2832 datarepr: a tuple of (format, obj, ...), a function or an object that
2835 provides a printable representation of the given data.
2833 provides a printable representation of the given data.
2836 """
2834 """
2837 self._ascending = None
2835 self._ascending = None
2838 self._istopo = istopo
2836 self._istopo = istopo
2839 if not isinstance(data, list):
2837 if not isinstance(data, list):
2840 if isinstance(data, set):
2838 if isinstance(data, set):
2841 self._set = data
2839 self._set = data
2842 # set has no order we pick one for stability purpose
2840 # set has no order we pick one for stability purpose
2843 self._ascending = True
2841 self._ascending = True
2844 data = list(data)
2842 data = list(data)
2845 self._list = data
2843 self._list = data
2846 self._datarepr = datarepr
2844 self._datarepr = datarepr
2847
2845
2848 @util.propertycache
2846 @util.propertycache
2849 def _set(self):
2847 def _set(self):
2850 return set(self._list)
2848 return set(self._list)
2851
2849
2852 @util.propertycache
2850 @util.propertycache
2853 def _asclist(self):
2851 def _asclist(self):
2854 asclist = self._list[:]
2852 asclist = self._list[:]
2855 asclist.sort()
2853 asclist.sort()
2856 return asclist
2854 return asclist
2857
2855
2858 def __iter__(self):
2856 def __iter__(self):
2859 if self._ascending is None:
2857 if self._ascending is None:
2860 return iter(self._list)
2858 return iter(self._list)
2861 elif self._ascending:
2859 elif self._ascending:
2862 return iter(self._asclist)
2860 return iter(self._asclist)
2863 else:
2861 else:
2864 return reversed(self._asclist)
2862 return reversed(self._asclist)
2865
2863
2866 def fastasc(self):
2864 def fastasc(self):
2867 return iter(self._asclist)
2865 return iter(self._asclist)
2868
2866
2869 def fastdesc(self):
2867 def fastdesc(self):
2870 return reversed(self._asclist)
2868 return reversed(self._asclist)
2871
2869
2872 @util.propertycache
2870 @util.propertycache
2873 def __contains__(self):
2871 def __contains__(self):
2874 return self._set.__contains__
2872 return self._set.__contains__
2875
2873
2876 def __nonzero__(self):
2874 def __nonzero__(self):
2877 return bool(self._list)
2875 return bool(self._list)
2878
2876
2879 def sort(self, reverse=False):
2877 def sort(self, reverse=False):
2880 self._ascending = not bool(reverse)
2878 self._ascending = not bool(reverse)
2881 self._istopo = False
2879 self._istopo = False
2882
2880
2883 def reverse(self):
2881 def reverse(self):
2884 if self._ascending is None:
2882 if self._ascending is None:
2885 self._list.reverse()
2883 self._list.reverse()
2886 else:
2884 else:
2887 self._ascending = not self._ascending
2885 self._ascending = not self._ascending
2888 self._istopo = False
2886 self._istopo = False
2889
2887
2890 def __len__(self):
2888 def __len__(self):
2891 return len(self._list)
2889 return len(self._list)
2892
2890
2893 def isascending(self):
2891 def isascending(self):
2894 """Returns True if the collection is ascending order, False if not.
2892 """Returns True if the collection is ascending order, False if not.
2895
2893
2896 This is part of the mandatory API for smartset."""
2894 This is part of the mandatory API for smartset."""
2897 if len(self) <= 1:
2895 if len(self) <= 1:
2898 return True
2896 return True
2899 return self._ascending is not None and self._ascending
2897 return self._ascending is not None and self._ascending
2900
2898
2901 def isdescending(self):
2899 def isdescending(self):
2902 """Returns True if the collection is descending order, False if not.
2900 """Returns True if the collection is descending order, False if not.
2903
2901
2904 This is part of the mandatory API for smartset."""
2902 This is part of the mandatory API for smartset."""
2905 if len(self) <= 1:
2903 if len(self) <= 1:
2906 return True
2904 return True
2907 return self._ascending is not None and not self._ascending
2905 return self._ascending is not None and not self._ascending
2908
2906
2909 def istopo(self):
2907 def istopo(self):
2910 """Is the collection is in topographical order or not.
2908 """Is the collection is in topographical order or not.
2911
2909
2912 This is part of the mandatory API for smartset."""
2910 This is part of the mandatory API for smartset."""
2913 if len(self) <= 1:
2911 if len(self) <= 1:
2914 return True
2912 return True
2915 return self._istopo
2913 return self._istopo
2916
2914
2917 def first(self):
2915 def first(self):
2918 if self:
2916 if self:
2919 if self._ascending is None:
2917 if self._ascending is None:
2920 return self._list[0]
2918 return self._list[0]
2921 elif self._ascending:
2919 elif self._ascending:
2922 return self._asclist[0]
2920 return self._asclist[0]
2923 else:
2921 else:
2924 return self._asclist[-1]
2922 return self._asclist[-1]
2925 return None
2923 return None
2926
2924
2927 def last(self):
2925 def last(self):
2928 if self:
2926 if self:
2929 if self._ascending is None:
2927 if self._ascending is None:
2930 return self._list[-1]
2928 return self._list[-1]
2931 elif self._ascending:
2929 elif self._ascending:
2932 return self._asclist[-1]
2930 return self._asclist[-1]
2933 else:
2931 else:
2934 return self._asclist[0]
2932 return self._asclist[0]
2935 return None
2933 return None
2936
2934
2937 def __repr__(self):
2935 def __repr__(self):
2938 d = {None: '', False: '-', True: '+'}[self._ascending]
2936 d = {None: '', False: '-', True: '+'}[self._ascending]
2939 s = _formatsetrepr(self._datarepr)
2937 s = _formatsetrepr(self._datarepr)
2940 if not s:
2938 if not s:
2941 l = self._list
2939 l = self._list
2942 # if _list has been built from a set, it might have a different
2940 # if _list has been built from a set, it might have a different
2943 # order from one python implementation to another.
2941 # order from one python implementation to another.
2944 # We fallback to the sorted version for a stable output.
2942 # We fallback to the sorted version for a stable output.
2945 if self._ascending is not None:
2943 if self._ascending is not None:
2946 l = self._asclist
2944 l = self._asclist
2947 s = repr(l)
2945 s = repr(l)
2948 return '<%s%s %s>' % (type(self).__name__, d, s)
2946 return '<%s%s %s>' % (type(self).__name__, d, s)
2949
2947
2950 class filteredset(abstractsmartset):
2948 class filteredset(abstractsmartset):
2951 """Duck type for baseset class which iterates lazily over the revisions in
2949 """Duck type for baseset class which iterates lazily over the revisions in
2952 the subset and contains a function which tests for membership in the
2950 the subset and contains a function which tests for membership in the
2953 revset
2951 revset
2954 """
2952 """
2955 def __init__(self, subset, condition=lambda x: True, condrepr=None):
2953 def __init__(self, subset, condition=lambda x: True, condrepr=None):
2956 """
2954 """
2957 condition: a function that decide whether a revision in the subset
2955 condition: a function that decide whether a revision in the subset
2958 belongs to the revset or not.
2956 belongs to the revset or not.
2959 condrepr: a tuple of (format, obj, ...), a function or an object that
2957 condrepr: a tuple of (format, obj, ...), a function or an object that
2960 provides a printable representation of the given condition.
2958 provides a printable representation of the given condition.
2961 """
2959 """
2962 self._subset = subset
2960 self._subset = subset
2963 self._condition = condition
2961 self._condition = condition
2964 self._condrepr = condrepr
2962 self._condrepr = condrepr
2965
2963
2966 def __contains__(self, x):
2964 def __contains__(self, x):
2967 return x in self._subset and self._condition(x)
2965 return x in self._subset and self._condition(x)
2968
2966
2969 def __iter__(self):
2967 def __iter__(self):
2970 return self._iterfilter(self._subset)
2968 return self._iterfilter(self._subset)
2971
2969
2972 def _iterfilter(self, it):
2970 def _iterfilter(self, it):
2973 cond = self._condition
2971 cond = self._condition
2974 for x in it:
2972 for x in it:
2975 if cond(x):
2973 if cond(x):
2976 yield x
2974 yield x
2977
2975
2978 @property
2976 @property
2979 def fastasc(self):
2977 def fastasc(self):
2980 it = self._subset.fastasc
2978 it = self._subset.fastasc
2981 if it is None:
2979 if it is None:
2982 return None
2980 return None
2983 return lambda: self._iterfilter(it())
2981 return lambda: self._iterfilter(it())
2984
2982
2985 @property
2983 @property
2986 def fastdesc(self):
2984 def fastdesc(self):
2987 it = self._subset.fastdesc
2985 it = self._subset.fastdesc
2988 if it is None:
2986 if it is None:
2989 return None
2987 return None
2990 return lambda: self._iterfilter(it())
2988 return lambda: self._iterfilter(it())
2991
2989
2992 def __nonzero__(self):
2990 def __nonzero__(self):
2993 fast = None
2991 fast = None
2994 candidates = [self.fastasc if self.isascending() else None,
2992 candidates = [self.fastasc if self.isascending() else None,
2995 self.fastdesc if self.isdescending() else None,
2993 self.fastdesc if self.isdescending() else None,
2996 self.fastasc,
2994 self.fastasc,
2997 self.fastdesc]
2995 self.fastdesc]
2998 for candidate in candidates:
2996 for candidate in candidates:
2999 if candidate is not None:
2997 if candidate is not None:
3000 fast = candidate
2998 fast = candidate
3001 break
2999 break
3002
3000
3003 if fast is not None:
3001 if fast is not None:
3004 it = fast()
3002 it = fast()
3005 else:
3003 else:
3006 it = self
3004 it = self
3007
3005
3008 for r in it:
3006 for r in it:
3009 return True
3007 return True
3010 return False
3008 return False
3011
3009
3012 def __len__(self):
3010 def __len__(self):
3013 # Basic implementation to be changed in future patches.
3011 # Basic implementation to be changed in future patches.
3014 # until this gets improved, we use generator expression
3012 # until this gets improved, we use generator expression
3015 # here, since list compr is free to call __len__ again
3013 # here, since list compr is free to call __len__ again
3016 # causing infinite recursion
3014 # causing infinite recursion
3017 l = baseset(r for r in self)
3015 l = baseset(r for r in self)
3018 return len(l)
3016 return len(l)
3019
3017
3020 def sort(self, reverse=False):
3018 def sort(self, reverse=False):
3021 self._subset.sort(reverse=reverse)
3019 self._subset.sort(reverse=reverse)
3022
3020
3023 def reverse(self):
3021 def reverse(self):
3024 self._subset.reverse()
3022 self._subset.reverse()
3025
3023
3026 def isascending(self):
3024 def isascending(self):
3027 return self._subset.isascending()
3025 return self._subset.isascending()
3028
3026
3029 def isdescending(self):
3027 def isdescending(self):
3030 return self._subset.isdescending()
3028 return self._subset.isdescending()
3031
3029
3032 def istopo(self):
3030 def istopo(self):
3033 return self._subset.istopo()
3031 return self._subset.istopo()
3034
3032
3035 def first(self):
3033 def first(self):
3036 for x in self:
3034 for x in self:
3037 return x
3035 return x
3038 return None
3036 return None
3039
3037
3040 def last(self):
3038 def last(self):
3041 it = None
3039 it = None
3042 if self.isascending():
3040 if self.isascending():
3043 it = self.fastdesc
3041 it = self.fastdesc
3044 elif self.isdescending():
3042 elif self.isdescending():
3045 it = self.fastasc
3043 it = self.fastasc
3046 if it is not None:
3044 if it is not None:
3047 for x in it():
3045 for x in it():
3048 return x
3046 return x
3049 return None #empty case
3047 return None #empty case
3050 else:
3048 else:
3051 x = None
3049 x = None
3052 for x in self:
3050 for x in self:
3053 pass
3051 pass
3054 return x
3052 return x
3055
3053
3056 def __repr__(self):
3054 def __repr__(self):
3057 xs = [repr(self._subset)]
3055 xs = [repr(self._subset)]
3058 s = _formatsetrepr(self._condrepr)
3056 s = _formatsetrepr(self._condrepr)
3059 if s:
3057 if s:
3060 xs.append(s)
3058 xs.append(s)
3061 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3059 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3062
3060
3063 def _iterordered(ascending, iter1, iter2):
3061 def _iterordered(ascending, iter1, iter2):
3064 """produce an ordered iteration from two iterators with the same order
3062 """produce an ordered iteration from two iterators with the same order
3065
3063
3066 The ascending is used to indicated the iteration direction.
3064 The ascending is used to indicated the iteration direction.
3067 """
3065 """
3068 choice = max
3066 choice = max
3069 if ascending:
3067 if ascending:
3070 choice = min
3068 choice = min
3071
3069
3072 val1 = None
3070 val1 = None
3073 val2 = None
3071 val2 = None
3074 try:
3072 try:
3075 # Consume both iterators in an ordered way until one is empty
3073 # Consume both iterators in an ordered way until one is empty
3076 while True:
3074 while True:
3077 if val1 is None:
3075 if val1 is None:
3078 val1 = next(iter1)
3076 val1 = next(iter1)
3079 if val2 is None:
3077 if val2 is None:
3080 val2 = next(iter2)
3078 val2 = next(iter2)
3081 n = choice(val1, val2)
3079 n = choice(val1, val2)
3082 yield n
3080 yield n
3083 if val1 == n:
3081 if val1 == n:
3084 val1 = None
3082 val1 = None
3085 if val2 == n:
3083 if val2 == n:
3086 val2 = None
3084 val2 = None
3087 except StopIteration:
3085 except StopIteration:
3088 # Flush any remaining values and consume the other one
3086 # Flush any remaining values and consume the other one
3089 it = iter2
3087 it = iter2
3090 if val1 is not None:
3088 if val1 is not None:
3091 yield val1
3089 yield val1
3092 it = iter1
3090 it = iter1
3093 elif val2 is not None:
3091 elif val2 is not None:
3094 # might have been equality and both are empty
3092 # might have been equality and both are empty
3095 yield val2
3093 yield val2
3096 for val in it:
3094 for val in it:
3097 yield val
3095 yield val
3098
3096
3099 class addset(abstractsmartset):
3097 class addset(abstractsmartset):
3100 """Represent the addition of two sets
3098 """Represent the addition of two sets
3101
3099
3102 Wrapper structure for lazily adding two structures without losing much
3100 Wrapper structure for lazily adding two structures without losing much
3103 performance on the __contains__ method
3101 performance on the __contains__ method
3104
3102
3105 If the ascending attribute is set, that means the two structures are
3103 If the ascending attribute is set, that means the two structures are
3106 ordered in either an ascending or descending way. Therefore, we can add
3104 ordered in either an ascending or descending way. Therefore, we can add
3107 them maintaining the order by iterating over both at the same time
3105 them maintaining the order by iterating over both at the same time
3108
3106
3109 >>> xs = baseset([0, 3, 2])
3107 >>> xs = baseset([0, 3, 2])
3110 >>> ys = baseset([5, 2, 4])
3108 >>> ys = baseset([5, 2, 4])
3111
3109
3112 >>> rs = addset(xs, ys)
3110 >>> rs = addset(xs, ys)
3113 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3111 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3114 (True, True, False, True, 0, 4)
3112 (True, True, False, True, 0, 4)
3115 >>> rs = addset(xs, baseset([]))
3113 >>> rs = addset(xs, baseset([]))
3116 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3114 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3117 (True, True, False, 0, 2)
3115 (True, True, False, 0, 2)
3118 >>> rs = addset(baseset([]), baseset([]))
3116 >>> rs = addset(baseset([]), baseset([]))
3119 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3117 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3120 (False, False, None, None)
3118 (False, False, None, None)
3121
3119
3122 iterate unsorted:
3120 iterate unsorted:
3123 >>> rs = addset(xs, ys)
3121 >>> rs = addset(xs, ys)
3124 >>> # (use generator because pypy could call len())
3122 >>> # (use generator because pypy could call len())
3125 >>> list(x for x in rs) # without _genlist
3123 >>> list(x for x in rs) # without _genlist
3126 [0, 3, 2, 5, 4]
3124 [0, 3, 2, 5, 4]
3127 >>> assert not rs._genlist
3125 >>> assert not rs._genlist
3128 >>> len(rs)
3126 >>> len(rs)
3129 5
3127 5
3130 >>> [x for x in rs] # with _genlist
3128 >>> [x for x in rs] # with _genlist
3131 [0, 3, 2, 5, 4]
3129 [0, 3, 2, 5, 4]
3132 >>> assert rs._genlist
3130 >>> assert rs._genlist
3133
3131
3134 iterate ascending:
3132 iterate ascending:
3135 >>> rs = addset(xs, ys, ascending=True)
3133 >>> rs = addset(xs, ys, ascending=True)
3136 >>> # (use generator because pypy could call len())
3134 >>> # (use generator because pypy could call len())
3137 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3135 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3138 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3136 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3139 >>> assert not rs._asclist
3137 >>> assert not rs._asclist
3140 >>> len(rs)
3138 >>> len(rs)
3141 5
3139 5
3142 >>> [x for x in rs], [x for x in rs.fastasc()]
3140 >>> [x for x in rs], [x for x in rs.fastasc()]
3143 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3141 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3144 >>> assert rs._asclist
3142 >>> assert rs._asclist
3145
3143
3146 iterate descending:
3144 iterate descending:
3147 >>> rs = addset(xs, ys, ascending=False)
3145 >>> rs = addset(xs, ys, ascending=False)
3148 >>> # (use generator because pypy could call len())
3146 >>> # (use generator because pypy could call len())
3149 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3147 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3150 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3148 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3151 >>> assert not rs._asclist
3149 >>> assert not rs._asclist
3152 >>> len(rs)
3150 >>> len(rs)
3153 5
3151 5
3154 >>> [x for x in rs], [x for x in rs.fastdesc()]
3152 >>> [x for x in rs], [x for x in rs.fastdesc()]
3155 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3153 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3156 >>> assert rs._asclist
3154 >>> assert rs._asclist
3157
3155
3158 iterate ascending without fastasc:
3156 iterate ascending without fastasc:
3159 >>> rs = addset(xs, generatorset(ys), ascending=True)
3157 >>> rs = addset(xs, generatorset(ys), ascending=True)
3160 >>> assert rs.fastasc is None
3158 >>> assert rs.fastasc is None
3161 >>> [x for x in rs]
3159 >>> [x for x in rs]
3162 [0, 2, 3, 4, 5]
3160 [0, 2, 3, 4, 5]
3163
3161
3164 iterate descending without fastdesc:
3162 iterate descending without fastdesc:
3165 >>> rs = addset(generatorset(xs), ys, ascending=False)
3163 >>> rs = addset(generatorset(xs), ys, ascending=False)
3166 >>> assert rs.fastdesc is None
3164 >>> assert rs.fastdesc is None
3167 >>> [x for x in rs]
3165 >>> [x for x in rs]
3168 [5, 4, 3, 2, 0]
3166 [5, 4, 3, 2, 0]
3169 """
3167 """
3170 def __init__(self, revs1, revs2, ascending=None):
3168 def __init__(self, revs1, revs2, ascending=None):
3171 self._r1 = revs1
3169 self._r1 = revs1
3172 self._r2 = revs2
3170 self._r2 = revs2
3173 self._iter = None
3171 self._iter = None
3174 self._ascending = ascending
3172 self._ascending = ascending
3175 self._genlist = None
3173 self._genlist = None
3176 self._asclist = None
3174 self._asclist = None
3177
3175
3178 def __len__(self):
3176 def __len__(self):
3179 return len(self._list)
3177 return len(self._list)
3180
3178
3181 def __nonzero__(self):
3179 def __nonzero__(self):
3182 return bool(self._r1) or bool(self._r2)
3180 return bool(self._r1) or bool(self._r2)
3183
3181
3184 @util.propertycache
3182 @util.propertycache
3185 def _list(self):
3183 def _list(self):
3186 if not self._genlist:
3184 if not self._genlist:
3187 self._genlist = baseset(iter(self))
3185 self._genlist = baseset(iter(self))
3188 return self._genlist
3186 return self._genlist
3189
3187
3190 def __iter__(self):
3188 def __iter__(self):
3191 """Iterate over both collections without repeating elements
3189 """Iterate over both collections without repeating elements
3192
3190
3193 If the ascending attribute is not set, iterate over the first one and
3191 If the ascending attribute is not set, iterate over the first one and
3194 then over the second one checking for membership on the first one so we
3192 then over the second one checking for membership on the first one so we
3195 dont yield any duplicates.
3193 dont yield any duplicates.
3196
3194
3197 If the ascending attribute is set, iterate over both collections at the
3195 If the ascending attribute is set, iterate over both collections at the
3198 same time, yielding only one value at a time in the given order.
3196 same time, yielding only one value at a time in the given order.
3199 """
3197 """
3200 if self._ascending is None:
3198 if self._ascending is None:
3201 if self._genlist:
3199 if self._genlist:
3202 return iter(self._genlist)
3200 return iter(self._genlist)
3203 def arbitraryordergen():
3201 def arbitraryordergen():
3204 for r in self._r1:
3202 for r in self._r1:
3205 yield r
3203 yield r
3206 inr1 = self._r1.__contains__
3204 inr1 = self._r1.__contains__
3207 for r in self._r2:
3205 for r in self._r2:
3208 if not inr1(r):
3206 if not inr1(r):
3209 yield r
3207 yield r
3210 return arbitraryordergen()
3208 return arbitraryordergen()
3211 # try to use our own fast iterator if it exists
3209 # try to use our own fast iterator if it exists
3212 self._trysetasclist()
3210 self._trysetasclist()
3213 if self._ascending:
3211 if self._ascending:
3214 attr = 'fastasc'
3212 attr = 'fastasc'
3215 else:
3213 else:
3216 attr = 'fastdesc'
3214 attr = 'fastdesc'
3217 it = getattr(self, attr)
3215 it = getattr(self, attr)
3218 if it is not None:
3216 if it is not None:
3219 return it()
3217 return it()
3220 # maybe half of the component supports fast
3218 # maybe half of the component supports fast
3221 # get iterator for _r1
3219 # get iterator for _r1
3222 iter1 = getattr(self._r1, attr)
3220 iter1 = getattr(self._r1, attr)
3223 if iter1 is None:
3221 if iter1 is None:
3224 # let's avoid side effect (not sure it matters)
3222 # let's avoid side effect (not sure it matters)
3225 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3223 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3226 else:
3224 else:
3227 iter1 = iter1()
3225 iter1 = iter1()
3228 # get iterator for _r2
3226 # get iterator for _r2
3229 iter2 = getattr(self._r2, attr)
3227 iter2 = getattr(self._r2, attr)
3230 if iter2 is None:
3228 if iter2 is None:
3231 # let's avoid side effect (not sure it matters)
3229 # let's avoid side effect (not sure it matters)
3232 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3230 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3233 else:
3231 else:
3234 iter2 = iter2()
3232 iter2 = iter2()
3235 return _iterordered(self._ascending, iter1, iter2)
3233 return _iterordered(self._ascending, iter1, iter2)
3236
3234
3237 def _trysetasclist(self):
3235 def _trysetasclist(self):
3238 """populate the _asclist attribute if possible and necessary"""
3236 """populate the _asclist attribute if possible and necessary"""
3239 if self._genlist is not None and self._asclist is None:
3237 if self._genlist is not None and self._asclist is None:
3240 self._asclist = sorted(self._genlist)
3238 self._asclist = sorted(self._genlist)
3241
3239
3242 @property
3240 @property
3243 def fastasc(self):
3241 def fastasc(self):
3244 self._trysetasclist()
3242 self._trysetasclist()
3245 if self._asclist is not None:
3243 if self._asclist is not None:
3246 return self._asclist.__iter__
3244 return self._asclist.__iter__
3247 iter1 = self._r1.fastasc
3245 iter1 = self._r1.fastasc
3248 iter2 = self._r2.fastasc
3246 iter2 = self._r2.fastasc
3249 if None in (iter1, iter2):
3247 if None in (iter1, iter2):
3250 return None
3248 return None
3251 return lambda: _iterordered(True, iter1(), iter2())
3249 return lambda: _iterordered(True, iter1(), iter2())
3252
3250
3253 @property
3251 @property
3254 def fastdesc(self):
3252 def fastdesc(self):
3255 self._trysetasclist()
3253 self._trysetasclist()
3256 if self._asclist is not None:
3254 if self._asclist is not None:
3257 return self._asclist.__reversed__
3255 return self._asclist.__reversed__
3258 iter1 = self._r1.fastdesc
3256 iter1 = self._r1.fastdesc
3259 iter2 = self._r2.fastdesc
3257 iter2 = self._r2.fastdesc
3260 if None in (iter1, iter2):
3258 if None in (iter1, iter2):
3261 return None
3259 return None
3262 return lambda: _iterordered(False, iter1(), iter2())
3260 return lambda: _iterordered(False, iter1(), iter2())
3263
3261
3264 def __contains__(self, x):
3262 def __contains__(self, x):
3265 return x in self._r1 or x in self._r2
3263 return x in self._r1 or x in self._r2
3266
3264
3267 def sort(self, reverse=False):
3265 def sort(self, reverse=False):
3268 """Sort the added set
3266 """Sort the added set
3269
3267
3270 For this we use the cached list with all the generated values and if we
3268 For this we use the cached list with all the generated values and if we
3271 know they are ascending or descending we can sort them in a smart way.
3269 know they are ascending or descending we can sort them in a smart way.
3272 """
3270 """
3273 self._ascending = not reverse
3271 self._ascending = not reverse
3274
3272
3275 def isascending(self):
3273 def isascending(self):
3276 return self._ascending is not None and self._ascending
3274 return self._ascending is not None and self._ascending
3277
3275
3278 def isdescending(self):
3276 def isdescending(self):
3279 return self._ascending is not None and not self._ascending
3277 return self._ascending is not None and not self._ascending
3280
3278
3281 def istopo(self):
3279 def istopo(self):
3282 # not worth the trouble asserting if the two sets combined are still
3280 # not worth the trouble asserting if the two sets combined are still
3283 # in topographical order. Use the sort() predicate to explicitly sort
3281 # in topographical order. Use the sort() predicate to explicitly sort
3284 # again instead.
3282 # again instead.
3285 return False
3283 return False
3286
3284
3287 def reverse(self):
3285 def reverse(self):
3288 if self._ascending is None:
3286 if self._ascending is None:
3289 self._list.reverse()
3287 self._list.reverse()
3290 else:
3288 else:
3291 self._ascending = not self._ascending
3289 self._ascending = not self._ascending
3292
3290
3293 def first(self):
3291 def first(self):
3294 for x in self:
3292 for x in self:
3295 return x
3293 return x
3296 return None
3294 return None
3297
3295
3298 def last(self):
3296 def last(self):
3299 self.reverse()
3297 self.reverse()
3300 val = self.first()
3298 val = self.first()
3301 self.reverse()
3299 self.reverse()
3302 return val
3300 return val
3303
3301
3304 def __repr__(self):
3302 def __repr__(self):
3305 d = {None: '', False: '-', True: '+'}[self._ascending]
3303 d = {None: '', False: '-', True: '+'}[self._ascending]
3306 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3304 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3307
3305
3308 class generatorset(abstractsmartset):
3306 class generatorset(abstractsmartset):
3309 """Wrap a generator for lazy iteration
3307 """Wrap a generator for lazy iteration
3310
3308
3311 Wrapper structure for generators that provides lazy membership and can
3309 Wrapper structure for generators that provides lazy membership and can
3312 be iterated more than once.
3310 be iterated more than once.
3313 When asked for membership it generates values until either it finds the
3311 When asked for membership it generates values until either it finds the
3314 requested one or has gone through all the elements in the generator
3312 requested one or has gone through all the elements in the generator
3315 """
3313 """
3316 def __init__(self, gen, iterasc=None):
3314 def __init__(self, gen, iterasc=None):
3317 """
3315 """
3318 gen: a generator producing the values for the generatorset.
3316 gen: a generator producing the values for the generatorset.
3319 """
3317 """
3320 self._gen = gen
3318 self._gen = gen
3321 self._asclist = None
3319 self._asclist = None
3322 self._cache = {}
3320 self._cache = {}
3323 self._genlist = []
3321 self._genlist = []
3324 self._finished = False
3322 self._finished = False
3325 self._ascending = True
3323 self._ascending = True
3326 if iterasc is not None:
3324 if iterasc is not None:
3327 if iterasc:
3325 if iterasc:
3328 self.fastasc = self._iterator
3326 self.fastasc = self._iterator
3329 self.__contains__ = self._asccontains
3327 self.__contains__ = self._asccontains
3330 else:
3328 else:
3331 self.fastdesc = self._iterator
3329 self.fastdesc = self._iterator
3332 self.__contains__ = self._desccontains
3330 self.__contains__ = self._desccontains
3333
3331
3334 def __nonzero__(self):
3332 def __nonzero__(self):
3335 # Do not use 'for r in self' because it will enforce the iteration
3333 # Do not use 'for r in self' because it will enforce the iteration
3336 # order (default ascending), possibly unrolling a whole descending
3334 # order (default ascending), possibly unrolling a whole descending
3337 # iterator.
3335 # iterator.
3338 if self._genlist:
3336 if self._genlist:
3339 return True
3337 return True
3340 for r in self._consumegen():
3338 for r in self._consumegen():
3341 return True
3339 return True
3342 return False
3340 return False
3343
3341
3344 def __contains__(self, x):
3342 def __contains__(self, x):
3345 if x in self._cache:
3343 if x in self._cache:
3346 return self._cache[x]
3344 return self._cache[x]
3347
3345
3348 # Use new values only, as existing values would be cached.
3346 # Use new values only, as existing values would be cached.
3349 for l in self._consumegen():
3347 for l in self._consumegen():
3350 if l == x:
3348 if l == x:
3351 return True
3349 return True
3352
3350
3353 self._cache[x] = False
3351 self._cache[x] = False
3354 return False
3352 return False
3355
3353
3356 def _asccontains(self, x):
3354 def _asccontains(self, x):
3357 """version of contains optimised for ascending generator"""
3355 """version of contains optimised for ascending generator"""
3358 if x in self._cache:
3356 if x in self._cache:
3359 return self._cache[x]
3357 return self._cache[x]
3360
3358
3361 # Use new values only, as existing values would be cached.
3359 # Use new values only, as existing values would be cached.
3362 for l in self._consumegen():
3360 for l in self._consumegen():
3363 if l == x:
3361 if l == x:
3364 return True
3362 return True
3365 if l > x:
3363 if l > x:
3366 break
3364 break
3367
3365
3368 self._cache[x] = False
3366 self._cache[x] = False
3369 return False
3367 return False
3370
3368
3371 def _desccontains(self, x):
3369 def _desccontains(self, x):
3372 """version of contains optimised for descending generator"""
3370 """version of contains optimised for descending generator"""
3373 if x in self._cache:
3371 if x in self._cache:
3374 return self._cache[x]
3372 return self._cache[x]
3375
3373
3376 # Use new values only, as existing values would be cached.
3374 # Use new values only, as existing values would be cached.
3377 for l in self._consumegen():
3375 for l in self._consumegen():
3378 if l == x:
3376 if l == x:
3379 return True
3377 return True
3380 if l < x:
3378 if l < x:
3381 break
3379 break
3382
3380
3383 self._cache[x] = False
3381 self._cache[x] = False
3384 return False
3382 return False
3385
3383
3386 def __iter__(self):
3384 def __iter__(self):
3387 if self._ascending:
3385 if self._ascending:
3388 it = self.fastasc
3386 it = self.fastasc
3389 else:
3387 else:
3390 it = self.fastdesc
3388 it = self.fastdesc
3391 if it is not None:
3389 if it is not None:
3392 return it()
3390 return it()
3393 # we need to consume the iterator
3391 # we need to consume the iterator
3394 for x in self._consumegen():
3392 for x in self._consumegen():
3395 pass
3393 pass
3396 # recall the same code
3394 # recall the same code
3397 return iter(self)
3395 return iter(self)
3398
3396
3399 def _iterator(self):
3397 def _iterator(self):
3400 if self._finished:
3398 if self._finished:
3401 return iter(self._genlist)
3399 return iter(self._genlist)
3402
3400
3403 # We have to use this complex iteration strategy to allow multiple
3401 # We have to use this complex iteration strategy to allow multiple
3404 # iterations at the same time. We need to be able to catch revision
3402 # iterations at the same time. We need to be able to catch revision
3405 # removed from _consumegen and added to genlist in another instance.
3403 # removed from _consumegen and added to genlist in another instance.
3406 #
3404 #
3407 # Getting rid of it would provide an about 15% speed up on this
3405 # Getting rid of it would provide an about 15% speed up on this
3408 # iteration.
3406 # iteration.
3409 genlist = self._genlist
3407 genlist = self._genlist
3410 nextrev = self._consumegen().next
3408 nextrev = self._consumegen().next
3411 _len = len # cache global lookup
3409 _len = len # cache global lookup
3412 def gen():
3410 def gen():
3413 i = 0
3411 i = 0
3414 while True:
3412 while True:
3415 if i < _len(genlist):
3413 if i < _len(genlist):
3416 yield genlist[i]
3414 yield genlist[i]
3417 else:
3415 else:
3418 yield nextrev()
3416 yield nextrev()
3419 i += 1
3417 i += 1
3420 return gen()
3418 return gen()
3421
3419
3422 def _consumegen(self):
3420 def _consumegen(self):
3423 cache = self._cache
3421 cache = self._cache
3424 genlist = self._genlist.append
3422 genlist = self._genlist.append
3425 for item in self._gen:
3423 for item in self._gen:
3426 cache[item] = True
3424 cache[item] = True
3427 genlist(item)
3425 genlist(item)
3428 yield item
3426 yield item
3429 if not self._finished:
3427 if not self._finished:
3430 self._finished = True
3428 self._finished = True
3431 asc = self._genlist[:]
3429 asc = self._genlist[:]
3432 asc.sort()
3430 asc.sort()
3433 self._asclist = asc
3431 self._asclist = asc
3434 self.fastasc = asc.__iter__
3432 self.fastasc = asc.__iter__
3435 self.fastdesc = asc.__reversed__
3433 self.fastdesc = asc.__reversed__
3436
3434
3437 def __len__(self):
3435 def __len__(self):
3438 for x in self._consumegen():
3436 for x in self._consumegen():
3439 pass
3437 pass
3440 return len(self._genlist)
3438 return len(self._genlist)
3441
3439
3442 def sort(self, reverse=False):
3440 def sort(self, reverse=False):
3443 self._ascending = not reverse
3441 self._ascending = not reverse
3444
3442
3445 def reverse(self):
3443 def reverse(self):
3446 self._ascending = not self._ascending
3444 self._ascending = not self._ascending
3447
3445
3448 def isascending(self):
3446 def isascending(self):
3449 return self._ascending
3447 return self._ascending
3450
3448
3451 def isdescending(self):
3449 def isdescending(self):
3452 return not self._ascending
3450 return not self._ascending
3453
3451
3454 def istopo(self):
3452 def istopo(self):
3455 # not worth the trouble asserting if the two sets combined are still
3453 # not worth the trouble asserting if the two sets combined are still
3456 # in topographical order. Use the sort() predicate to explicitly sort
3454 # in topographical order. Use the sort() predicate to explicitly sort
3457 # again instead.
3455 # again instead.
3458 return False
3456 return False
3459
3457
3460 def first(self):
3458 def first(self):
3461 if self._ascending:
3459 if self._ascending:
3462 it = self.fastasc
3460 it = self.fastasc
3463 else:
3461 else:
3464 it = self.fastdesc
3462 it = self.fastdesc
3465 if it is None:
3463 if it is None:
3466 # we need to consume all and try again
3464 # we need to consume all and try again
3467 for x in self._consumegen():
3465 for x in self._consumegen():
3468 pass
3466 pass
3469 return self.first()
3467 return self.first()
3470 return next(it(), None)
3468 return next(it(), None)
3471
3469
3472 def last(self):
3470 def last(self):
3473 if self._ascending:
3471 if self._ascending:
3474 it = self.fastdesc
3472 it = self.fastdesc
3475 else:
3473 else:
3476 it = self.fastasc
3474 it = self.fastasc
3477 if it is None:
3475 if it is None:
3478 # we need to consume all and try again
3476 # we need to consume all and try again
3479 for x in self._consumegen():
3477 for x in self._consumegen():
3480 pass
3478 pass
3481 return self.first()
3479 return self.first()
3482 return next(it(), None)
3480 return next(it(), None)
3483
3481
3484 def __repr__(self):
3482 def __repr__(self):
3485 d = {False: '-', True: '+'}[self._ascending]
3483 d = {False: '-', True: '+'}[self._ascending]
3486 return '<%s%s>' % (type(self).__name__, d)
3484 return '<%s%s>' % (type(self).__name__, d)
3487
3485
3488 class spanset(abstractsmartset):
3486 class spanset(abstractsmartset):
3489 """Duck type for baseset class which represents a range of revisions and
3487 """Duck type for baseset class which represents a range of revisions and
3490 can work lazily and without having all the range in memory
3488 can work lazily and without having all the range in memory
3491
3489
3492 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3490 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3493 notable points:
3491 notable points:
3494 - when x < y it will be automatically descending,
3492 - when x < y it will be automatically descending,
3495 - revision filtered with this repoview will be skipped.
3493 - revision filtered with this repoview will be skipped.
3496
3494
3497 """
3495 """
3498 def __init__(self, repo, start=0, end=None):
3496 def __init__(self, repo, start=0, end=None):
3499 """
3497 """
3500 start: first revision included the set
3498 start: first revision included the set
3501 (default to 0)
3499 (default to 0)
3502 end: first revision excluded (last+1)
3500 end: first revision excluded (last+1)
3503 (default to len(repo)
3501 (default to len(repo)
3504
3502
3505 Spanset will be descending if `end` < `start`.
3503 Spanset will be descending if `end` < `start`.
3506 """
3504 """
3507 if end is None:
3505 if end is None:
3508 end = len(repo)
3506 end = len(repo)
3509 self._ascending = start <= end
3507 self._ascending = start <= end
3510 if not self._ascending:
3508 if not self._ascending:
3511 start, end = end + 1, start +1
3509 start, end = end + 1, start +1
3512 self._start = start
3510 self._start = start
3513 self._end = end
3511 self._end = end
3514 self._hiddenrevs = repo.changelog.filteredrevs
3512 self._hiddenrevs = repo.changelog.filteredrevs
3515
3513
3516 def sort(self, reverse=False):
3514 def sort(self, reverse=False):
3517 self._ascending = not reverse
3515 self._ascending = not reverse
3518
3516
3519 def reverse(self):
3517 def reverse(self):
3520 self._ascending = not self._ascending
3518 self._ascending = not self._ascending
3521
3519
3522 def istopo(self):
3520 def istopo(self):
3523 # not worth the trouble asserting if the two sets combined are still
3521 # not worth the trouble asserting if the two sets combined are still
3524 # in topographical order. Use the sort() predicate to explicitly sort
3522 # in topographical order. Use the sort() predicate to explicitly sort
3525 # again instead.
3523 # again instead.
3526 return False
3524 return False
3527
3525
3528 def _iterfilter(self, iterrange):
3526 def _iterfilter(self, iterrange):
3529 s = self._hiddenrevs
3527 s = self._hiddenrevs
3530 for r in iterrange:
3528 for r in iterrange:
3531 if r not in s:
3529 if r not in s:
3532 yield r
3530 yield r
3533
3531
3534 def __iter__(self):
3532 def __iter__(self):
3535 if self._ascending:
3533 if self._ascending:
3536 return self.fastasc()
3534 return self.fastasc()
3537 else:
3535 else:
3538 return self.fastdesc()
3536 return self.fastdesc()
3539
3537
3540 def fastasc(self):
3538 def fastasc(self):
3541 iterrange = xrange(self._start, self._end)
3539 iterrange = xrange(self._start, self._end)
3542 if self._hiddenrevs:
3540 if self._hiddenrevs:
3543 return self._iterfilter(iterrange)
3541 return self._iterfilter(iterrange)
3544 return iter(iterrange)
3542 return iter(iterrange)
3545
3543
3546 def fastdesc(self):
3544 def fastdesc(self):
3547 iterrange = xrange(self._end - 1, self._start - 1, -1)
3545 iterrange = xrange(self._end - 1, self._start - 1, -1)
3548 if self._hiddenrevs:
3546 if self._hiddenrevs:
3549 return self._iterfilter(iterrange)
3547 return self._iterfilter(iterrange)
3550 return iter(iterrange)
3548 return iter(iterrange)
3551
3549
3552 def __contains__(self, rev):
3550 def __contains__(self, rev):
3553 hidden = self._hiddenrevs
3551 hidden = self._hiddenrevs
3554 return ((self._start <= rev < self._end)
3552 return ((self._start <= rev < self._end)
3555 and not (hidden and rev in hidden))
3553 and not (hidden and rev in hidden))
3556
3554
3557 def __nonzero__(self):
3555 def __nonzero__(self):
3558 for r in self:
3556 for r in self:
3559 return True
3557 return True
3560 return False
3558 return False
3561
3559
3562 def __len__(self):
3560 def __len__(self):
3563 if not self._hiddenrevs:
3561 if not self._hiddenrevs:
3564 return abs(self._end - self._start)
3562 return abs(self._end - self._start)
3565 else:
3563 else:
3566 count = 0
3564 count = 0
3567 start = self._start
3565 start = self._start
3568 end = self._end
3566 end = self._end
3569 for rev in self._hiddenrevs:
3567 for rev in self._hiddenrevs:
3570 if (end < rev <= start) or (start <= rev < end):
3568 if (end < rev <= start) or (start <= rev < end):
3571 count += 1
3569 count += 1
3572 return abs(self._end - self._start) - count
3570 return abs(self._end - self._start) - count
3573
3571
3574 def isascending(self):
3572 def isascending(self):
3575 return self._ascending
3573 return self._ascending
3576
3574
3577 def isdescending(self):
3575 def isdescending(self):
3578 return not self._ascending
3576 return not self._ascending
3579
3577
3580 def first(self):
3578 def first(self):
3581 if self._ascending:
3579 if self._ascending:
3582 it = self.fastasc
3580 it = self.fastasc
3583 else:
3581 else:
3584 it = self.fastdesc
3582 it = self.fastdesc
3585 for x in it():
3583 for x in it():
3586 return x
3584 return x
3587 return None
3585 return None
3588
3586
3589 def last(self):
3587 def last(self):
3590 if self._ascending:
3588 if self._ascending:
3591 it = self.fastdesc
3589 it = self.fastdesc
3592 else:
3590 else:
3593 it = self.fastasc
3591 it = self.fastasc
3594 for x in it():
3592 for x in it():
3595 return x
3593 return x
3596 return None
3594 return None
3597
3595
3598 def __repr__(self):
3596 def __repr__(self):
3599 d = {False: '-', True: '+'}[self._ascending]
3597 d = {False: '-', True: '+'}[self._ascending]
3600 return '<%s%s %d:%d>' % (type(self).__name__, d,
3598 return '<%s%s %d:%d>' % (type(self).__name__, d,
3601 self._start, self._end - 1)
3599 self._start, self._end - 1)
3602
3600
3603 class fullreposet(spanset):
3601 class fullreposet(spanset):
3604 """a set containing all revisions in the repo
3602 """a set containing all revisions in the repo
3605
3603
3606 This class exists to host special optimization and magic to handle virtual
3604 This class exists to host special optimization and magic to handle virtual
3607 revisions such as "null".
3605 revisions such as "null".
3608 """
3606 """
3609
3607
3610 def __init__(self, repo):
3608 def __init__(self, repo):
3611 super(fullreposet, self).__init__(repo)
3609 super(fullreposet, self).__init__(repo)
3612
3610
3613 def __and__(self, other):
3611 def __and__(self, other):
3614 """As self contains the whole repo, all of the other set should also be
3612 """As self contains the whole repo, all of the other set should also be
3615 in self. Therefore `self & other = other`.
3613 in self. Therefore `self & other = other`.
3616
3614
3617 This boldly assumes the other contains valid revs only.
3615 This boldly assumes the other contains valid revs only.
3618 """
3616 """
3619 # other not a smartset, make is so
3617 # other not a smartset, make is so
3620 if not util.safehasattr(other, 'isascending'):
3618 if not util.safehasattr(other, 'isascending'):
3621 # filter out hidden revision
3619 # filter out hidden revision
3622 # (this boldly assumes all smartset are pure)
3620 # (this boldly assumes all smartset are pure)
3623 #
3621 #
3624 # `other` was used with "&", let's assume this is a set like
3622 # `other` was used with "&", let's assume this is a set like
3625 # object.
3623 # object.
3626 other = baseset(other - self._hiddenrevs)
3624 other = baseset(other - self._hiddenrevs)
3627
3625
3628 # XXX As fullreposet is also used as bootstrap, this is wrong.
3626 # XXX As fullreposet is also used as bootstrap, this is wrong.
3629 #
3627 #
3630 # With a giveme312() revset returning [3,1,2], this makes
3628 # With a giveme312() revset returning [3,1,2], this makes
3631 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3629 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3632 # We cannot just drop it because other usage still need to sort it:
3630 # We cannot just drop it because other usage still need to sort it:
3633 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3631 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3634 #
3632 #
3635 # There is also some faulty revset implementations that rely on it
3633 # There is also some faulty revset implementations that rely on it
3636 # (eg: children as of its state in e8075329c5fb)
3634 # (eg: children as of its state in e8075329c5fb)
3637 #
3635 #
3638 # When we fix the two points above we can move this into the if clause
3636 # When we fix the two points above we can move this into the if clause
3639 other.sort(reverse=self.isdescending())
3637 other.sort(reverse=self.isdescending())
3640 return other
3638 return other
3641
3639
3642 def prettyformatset(revs):
3640 def prettyformatset(revs):
3643 lines = []
3641 lines = []
3644 rs = repr(revs)
3642 rs = repr(revs)
3645 p = 0
3643 p = 0
3646 while p < len(rs):
3644 while p < len(rs):
3647 q = rs.find('<', p + 1)
3645 q = rs.find('<', p + 1)
3648 if q < 0:
3646 if q < 0:
3649 q = len(rs)
3647 q = len(rs)
3650 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3648 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3651 assert l >= 0
3649 assert l >= 0
3652 lines.append((l, rs[p:q].rstrip()))
3650 lines.append((l, rs[p:q].rstrip()))
3653 p = q
3651 p = q
3654 return '\n'.join(' ' * l + s for l, s in lines)
3652 return '\n'.join(' ' * l + s for l, s in lines)
3655
3653
3656 def loadpredicate(ui, extname, registrarobj):
3654 def loadpredicate(ui, extname, registrarobj):
3657 """Load revset predicates from specified registrarobj
3655 """Load revset predicates from specified registrarobj
3658 """
3656 """
3659 for name, func in registrarobj._table.iteritems():
3657 for name, func in registrarobj._table.iteritems():
3660 symbols[name] = func
3658 symbols[name] = func
3661 if func._safe:
3659 if func._safe:
3662 safesymbols.add(name)
3660 safesymbols.add(name)
3663
3661
3664 # load built-in predicates explicitly to setup safesymbols
3662 # load built-in predicates explicitly to setup safesymbols
3665 loadpredicate(None, None, predicate)
3663 loadpredicate(None, None, predicate)
3666
3664
3667 # tell hggettext to extract docstrings from these functions:
3665 # tell hggettext to extract docstrings from these functions:
3668 i18nfunctions = symbols.values()
3666 i18nfunctions = symbols.values()
@@ -1,3037 +1,3036 b''
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 > cmdutil,
40 > cmdutil,
41 > node as nodemod,
41 > node as nodemod,
42 > revset,
42 > revset,
43 > )
43 > )
44 > cmdtable = {}
44 > cmdtable = {}
45 > command = cmdutil.command(cmdtable)
45 > command = cmdutil.command(cmdtable)
46 > @command('debugrevlistspec',
46 > @command('debugrevlistspec',
47 > [('', 'optimize', None, 'print parsed tree after optimizing'),
47 > [('', 'optimize', None, 'print parsed tree after optimizing'),
48 > ('', 'bin', None, 'unhexlify arguments')])
48 > ('', 'bin', None, 'unhexlify arguments')])
49 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
49 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
50 > if opts['bin']:
50 > if opts['bin']:
51 > args = map(nodemod.bin, args)
51 > args = map(nodemod.bin, args)
52 > expr = revset.formatspec(fmt, list(args))
52 > expr = revset.formatspec(fmt, list(args))
53 > if ui.verbose:
53 > if ui.verbose:
54 > tree = revset.parse(expr, lookup=repo.__contains__)
54 > tree = revset.parse(expr, lookup=repo.__contains__)
55 > ui.note(revset.prettyformat(tree), "\n")
55 > ui.note(revset.prettyformat(tree), "\n")
56 > if opts["optimize"]:
56 > if opts["optimize"]:
57 > opttree = revset.optimize(tree)
57 > opttree = revset.optimize(tree)
58 > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n")
58 > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n")
59 > func = revset.match(ui, expr, repo)
59 > func = revset.match(ui, expr, repo)
60 > revs = func(repo)
60 > revs = func(repo)
61 > if ui.verbose:
61 > if ui.verbose:
62 > ui.note("* set:\n", revset.prettyformatset(revs), "\n")
62 > ui.note("* set:\n", revset.prettyformatset(revs), "\n")
63 > for c in revs:
63 > for c in revs:
64 > ui.write("%s\n" % c)
64 > ui.write("%s\n" % c)
65 > EOF
65 > EOF
66 $ cat <<EOF >> $HGRCPATH
66 $ cat <<EOF >> $HGRCPATH
67 > [extensions]
67 > [extensions]
68 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
68 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
69 > EOF
69 > EOF
70 $ trylist() {
70 $ trylist() {
71 > hg debugrevlistspec --debug "$@"
71 > hg debugrevlistspec --debug "$@"
72 > }
72 > }
73
73
74 $ hg init repo
74 $ hg init repo
75 $ cd repo
75 $ cd repo
76
76
77 $ echo a > a
77 $ echo a > a
78 $ hg branch a
78 $ hg branch a
79 marked working directory as branch a
79 marked working directory as branch a
80 (branches are permanent and global, did you want a bookmark?)
80 (branches are permanent and global, did you want a bookmark?)
81 $ hg ci -Aqm0
81 $ hg ci -Aqm0
82
82
83 $ echo b > b
83 $ echo b > b
84 $ hg branch b
84 $ hg branch b
85 marked working directory as branch b
85 marked working directory as branch b
86 $ hg ci -Aqm1
86 $ hg ci -Aqm1
87
87
88 $ rm a
88 $ rm a
89 $ hg branch a-b-c-
89 $ hg branch a-b-c-
90 marked working directory as branch a-b-c-
90 marked working directory as branch a-b-c-
91 $ hg ci -Aqm2 -u Bob
91 $ hg ci -Aqm2 -u Bob
92
92
93 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
93 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
94 2
94 2
95 $ hg log -r "extra('branch')" --template '{rev}\n'
95 $ hg log -r "extra('branch')" --template '{rev}\n'
96 0
96 0
97 1
97 1
98 2
98 2
99 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
99 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
100 0 a
100 0 a
101 2 a-b-c-
101 2 a-b-c-
102
102
103 $ hg co 1
103 $ hg co 1
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 $ hg branch +a+b+c+
105 $ hg branch +a+b+c+
106 marked working directory as branch +a+b+c+
106 marked working directory as branch +a+b+c+
107 $ hg ci -Aqm3
107 $ hg ci -Aqm3
108
108
109 $ hg co 2 # interleave
109 $ hg co 2 # interleave
110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
111 $ echo bb > b
111 $ echo bb > b
112 $ hg branch -- -a-b-c-
112 $ hg branch -- -a-b-c-
113 marked working directory as branch -a-b-c-
113 marked working directory as branch -a-b-c-
114 $ hg ci -Aqm4 -d "May 12 2005"
114 $ hg ci -Aqm4 -d "May 12 2005"
115
115
116 $ hg co 3
116 $ hg co 3
117 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 $ hg branch !a/b/c/
118 $ hg branch !a/b/c/
119 marked working directory as branch !a/b/c/
119 marked working directory as branch !a/b/c/
120 $ hg ci -Aqm"5 bug"
120 $ hg ci -Aqm"5 bug"
121
121
122 $ hg merge 4
122 $ hg merge 4
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 (branch merge, don't forget to commit)
124 (branch merge, don't forget to commit)
125 $ hg branch _a_b_c_
125 $ hg branch _a_b_c_
126 marked working directory as branch _a_b_c_
126 marked working directory as branch _a_b_c_
127 $ hg ci -Aqm"6 issue619"
127 $ hg ci -Aqm"6 issue619"
128
128
129 $ hg branch .a.b.c.
129 $ hg branch .a.b.c.
130 marked working directory as branch .a.b.c.
130 marked working directory as branch .a.b.c.
131 $ hg ci -Aqm7
131 $ hg ci -Aqm7
132
132
133 $ hg branch all
133 $ hg branch all
134 marked working directory as branch all
134 marked working directory as branch all
135
135
136 $ hg co 4
136 $ hg co 4
137 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 $ hg branch Γ©
138 $ hg branch Γ©
139 marked working directory as branch \xc3\xa9 (esc)
139 marked working directory as branch \xc3\xa9 (esc)
140 $ hg ci -Aqm9
140 $ hg ci -Aqm9
141
141
142 $ hg tag -r6 1.0
142 $ hg tag -r6 1.0
143 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
143 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
144
144
145 $ hg clone --quiet -U -r 7 . ../remote1
145 $ hg clone --quiet -U -r 7 . ../remote1
146 $ hg clone --quiet -U -r 8 . ../remote2
146 $ hg clone --quiet -U -r 8 . ../remote2
147 $ echo "[paths]" >> .hg/hgrc
147 $ echo "[paths]" >> .hg/hgrc
148 $ echo "default = ../remote1" >> .hg/hgrc
148 $ echo "default = ../remote1" >> .hg/hgrc
149
149
150 trivial
150 trivial
151
151
152 $ try 0:1
152 $ try 0:1
153 (range
153 (range
154 ('symbol', '0')
154 ('symbol', '0')
155 ('symbol', '1'))
155 ('symbol', '1'))
156 * set:
156 * set:
157 <spanset+ 0:1>
157 <spanset+ 0:1>
158 0
158 0
159 1
159 1
160 $ try --optimize :
160 $ try --optimize :
161 (rangeall
161 (rangeall
162 None)
162 None)
163 * optimized:
163 * optimized:
164 (range
164 (range
165 ('string', '0')
165 ('string', '0')
166 ('string', 'tip'))
166 ('string', 'tip'))
167 * set:
167 * set:
168 <spanset+ 0:9>
168 <spanset+ 0:9>
169 0
169 0
170 1
170 1
171 2
171 2
172 3
172 3
173 4
173 4
174 5
174 5
175 6
175 6
176 7
176 7
177 8
177 8
178 9
178 9
179 $ try 3::6
179 $ try 3::6
180 (dagrange
180 (dagrange
181 ('symbol', '3')
181 ('symbol', '3')
182 ('symbol', '6'))
182 ('symbol', '6'))
183 * set:
183 * set:
184 <baseset+ [3, 5, 6]>
184 <baseset+ [3, 5, 6]>
185 3
185 3
186 5
186 5
187 6
187 6
188 $ try '0|1|2'
188 $ try '0|1|2'
189 (or
189 (or
190 ('symbol', '0')
190 ('symbol', '0')
191 ('symbol', '1')
191 ('symbol', '1')
192 ('symbol', '2'))
192 ('symbol', '2'))
193 * set:
193 * set:
194 <baseset [0, 1, 2]>
194 <baseset [0, 1, 2]>
195 0
195 0
196 1
196 1
197 2
197 2
198
198
199 names that should work without quoting
199 names that should work without quoting
200
200
201 $ try a
201 $ try a
202 ('symbol', 'a')
202 ('symbol', 'a')
203 * set:
203 * set:
204 <baseset [0]>
204 <baseset [0]>
205 0
205 0
206 $ try b-a
206 $ try b-a
207 (minus
207 (minus
208 ('symbol', 'b')
208 ('symbol', 'b')
209 ('symbol', 'a'))
209 ('symbol', 'a'))
210 * set:
210 * set:
211 <filteredset
211 <filteredset
212 <baseset [1]>,
212 <baseset [1]>,
213 <not
213 <not
214 <baseset [0]>>>
214 <baseset [0]>>>
215 1
215 1
216 $ try _a_b_c_
216 $ try _a_b_c_
217 ('symbol', '_a_b_c_')
217 ('symbol', '_a_b_c_')
218 * set:
218 * set:
219 <baseset [6]>
219 <baseset [6]>
220 6
220 6
221 $ try _a_b_c_-a
221 $ try _a_b_c_-a
222 (minus
222 (minus
223 ('symbol', '_a_b_c_')
223 ('symbol', '_a_b_c_')
224 ('symbol', 'a'))
224 ('symbol', 'a'))
225 * set:
225 * set:
226 <filteredset
226 <filteredset
227 <baseset [6]>,
227 <baseset [6]>,
228 <not
228 <not
229 <baseset [0]>>>
229 <baseset [0]>>>
230 6
230 6
231 $ try .a.b.c.
231 $ try .a.b.c.
232 ('symbol', '.a.b.c.')
232 ('symbol', '.a.b.c.')
233 * set:
233 * set:
234 <baseset [7]>
234 <baseset [7]>
235 7
235 7
236 $ try .a.b.c.-a
236 $ try .a.b.c.-a
237 (minus
237 (minus
238 ('symbol', '.a.b.c.')
238 ('symbol', '.a.b.c.')
239 ('symbol', 'a'))
239 ('symbol', 'a'))
240 * set:
240 * set:
241 <filteredset
241 <filteredset
242 <baseset [7]>,
242 <baseset [7]>,
243 <not
243 <not
244 <baseset [0]>>>
244 <baseset [0]>>>
245 7
245 7
246
246
247 names that should be caught by fallback mechanism
247 names that should be caught by fallback mechanism
248
248
249 $ try -- '-a-b-c-'
249 $ try -- '-a-b-c-'
250 ('symbol', '-a-b-c-')
250 ('symbol', '-a-b-c-')
251 * set:
251 * set:
252 <baseset [4]>
252 <baseset [4]>
253 4
253 4
254 $ log -a-b-c-
254 $ log -a-b-c-
255 4
255 4
256 $ try '+a+b+c+'
256 $ try '+a+b+c+'
257 ('symbol', '+a+b+c+')
257 ('symbol', '+a+b+c+')
258 * set:
258 * set:
259 <baseset [3]>
259 <baseset [3]>
260 3
260 3
261 $ try '+a+b+c+:'
261 $ try '+a+b+c+:'
262 (rangepost
262 (rangepost
263 ('symbol', '+a+b+c+'))
263 ('symbol', '+a+b+c+'))
264 * set:
264 * set:
265 <spanset+ 3:9>
265 <spanset+ 3:9>
266 3
266 3
267 4
267 4
268 5
268 5
269 6
269 6
270 7
270 7
271 8
271 8
272 9
272 9
273 $ try ':+a+b+c+'
273 $ try ':+a+b+c+'
274 (rangepre
274 (rangepre
275 ('symbol', '+a+b+c+'))
275 ('symbol', '+a+b+c+'))
276 * set:
276 * set:
277 <spanset+ 0:3>
277 <spanset+ 0:3>
278 0
278 0
279 1
279 1
280 2
280 2
281 3
281 3
282 $ try -- '-a-b-c-:+a+b+c+'
282 $ try -- '-a-b-c-:+a+b+c+'
283 (range
283 (range
284 ('symbol', '-a-b-c-')
284 ('symbol', '-a-b-c-')
285 ('symbol', '+a+b+c+'))
285 ('symbol', '+a+b+c+'))
286 * set:
286 * set:
287 <spanset- 3:4>
287 <spanset- 3:4>
288 4
288 4
289 3
289 3
290 $ log '-a-b-c-:+a+b+c+'
290 $ log '-a-b-c-:+a+b+c+'
291 4
291 4
292 3
292 3
293
293
294 $ try -- -a-b-c--a # complains
294 $ try -- -a-b-c--a # complains
295 (minus
295 (minus
296 (minus
296 (minus
297 (minus
297 (minus
298 (negate
298 (negate
299 ('symbol', 'a'))
299 ('symbol', 'a'))
300 ('symbol', 'b'))
300 ('symbol', 'b'))
301 ('symbol', 'c'))
301 ('symbol', 'c'))
302 (negate
302 (negate
303 ('symbol', 'a')))
303 ('symbol', 'a')))
304 abort: unknown revision '-a'!
304 abort: unknown revision '-a'!
305 [255]
305 [255]
306 $ try Γ©
306 $ try Γ©
307 ('symbol', '\xc3\xa9')
307 ('symbol', '\xc3\xa9')
308 * set:
308 * set:
309 <baseset [9]>
309 <baseset [9]>
310 9
310 9
311
311
312 no quoting needed
312 no quoting needed
313
313
314 $ log ::a-b-c-
314 $ log ::a-b-c-
315 0
315 0
316 1
316 1
317 2
317 2
318
318
319 quoting needed
319 quoting needed
320
320
321 $ try '"-a-b-c-"-a'
321 $ try '"-a-b-c-"-a'
322 (minus
322 (minus
323 ('string', '-a-b-c-')
323 ('string', '-a-b-c-')
324 ('symbol', 'a'))
324 ('symbol', 'a'))
325 * set:
325 * set:
326 <filteredset
326 <filteredset
327 <baseset [4]>,
327 <baseset [4]>,
328 <not
328 <not
329 <baseset [0]>>>
329 <baseset [0]>>>
330 4
330 4
331
331
332 $ log '1 or 2'
332 $ log '1 or 2'
333 1
333 1
334 2
334 2
335 $ log '1|2'
335 $ log '1|2'
336 1
336 1
337 2
337 2
338 $ log '1 and 2'
338 $ log '1 and 2'
339 $ log '1&2'
339 $ log '1&2'
340 $ try '1&2|3' # precedence - and is higher
340 $ try '1&2|3' # precedence - and is higher
341 (or
341 (or
342 (and
342 (and
343 ('symbol', '1')
343 ('symbol', '1')
344 ('symbol', '2'))
344 ('symbol', '2'))
345 ('symbol', '3'))
345 ('symbol', '3'))
346 * set:
346 * set:
347 <addset
347 <addset
348 <baseset []>,
348 <baseset []>,
349 <baseset [3]>>
349 <baseset [3]>>
350 3
350 3
351 $ try '1|2&3'
351 $ try '1|2&3'
352 (or
352 (or
353 ('symbol', '1')
353 ('symbol', '1')
354 (and
354 (and
355 ('symbol', '2')
355 ('symbol', '2')
356 ('symbol', '3')))
356 ('symbol', '3')))
357 * set:
357 * set:
358 <addset
358 <addset
359 <baseset [1]>,
359 <baseset [1]>,
360 <baseset []>>
360 <baseset []>>
361 1
361 1
362 $ try '1&2&3' # associativity
362 $ try '1&2&3' # associativity
363 (and
363 (and
364 (and
364 (and
365 ('symbol', '1')
365 ('symbol', '1')
366 ('symbol', '2'))
366 ('symbol', '2'))
367 ('symbol', '3'))
367 ('symbol', '3'))
368 * set:
368 * set:
369 <baseset []>
369 <baseset []>
370 $ try '1|(2|3)'
370 $ try '1|(2|3)'
371 (or
371 (or
372 ('symbol', '1')
372 ('symbol', '1')
373 (group
373 (group
374 (or
374 (or
375 ('symbol', '2')
375 ('symbol', '2')
376 ('symbol', '3'))))
376 ('symbol', '3'))))
377 * set:
377 * set:
378 <addset
378 <addset
379 <baseset [1]>,
379 <baseset [1]>,
380 <baseset [2, 3]>>
380 <baseset [2, 3]>>
381 1
381 1
382 2
382 2
383 3
383 3
384 $ log '1.0' # tag
384 $ log '1.0' # tag
385 6
385 6
386 $ log 'a' # branch
386 $ log 'a' # branch
387 0
387 0
388 $ log '2785f51ee'
388 $ log '2785f51ee'
389 0
389 0
390 $ log 'date(2005)'
390 $ log 'date(2005)'
391 4
391 4
392 $ log 'date(this is a test)'
392 $ log 'date(this is a test)'
393 hg: parse error at 10: unexpected token: symbol
393 hg: parse error at 10: unexpected token: symbol
394 [255]
394 [255]
395 $ log 'date()'
395 $ log 'date()'
396 hg: parse error: date requires a string
396 hg: parse error: date requires a string
397 [255]
397 [255]
398 $ log 'date'
398 $ log 'date'
399 abort: unknown revision 'date'!
399 abort: unknown revision 'date'!
400 [255]
400 [255]
401 $ log 'date('
401 $ log 'date('
402 hg: parse error at 5: not a prefix: end
402 hg: parse error at 5: not a prefix: end
403 [255]
403 [255]
404 $ log 'date("\xy")'
404 $ log 'date("\xy")'
405 hg: parse error: invalid \x escape
405 hg: parse error: invalid \x escape
406 [255]
406 [255]
407 $ log 'date(tip)'
407 $ log 'date(tip)'
408 abort: invalid date: 'tip'
408 abort: invalid date: 'tip'
409 [255]
409 [255]
410 $ log '0:date'
410 $ log '0:date'
411 abort: unknown revision 'date'!
411 abort: unknown revision 'date'!
412 [255]
412 [255]
413 $ log '::"date"'
413 $ log '::"date"'
414 abort: unknown revision 'date'!
414 abort: unknown revision 'date'!
415 [255]
415 [255]
416 $ hg book date -r 4
416 $ hg book date -r 4
417 $ log '0:date'
417 $ log '0:date'
418 0
418 0
419 1
419 1
420 2
420 2
421 3
421 3
422 4
422 4
423 $ log '::date'
423 $ log '::date'
424 0
424 0
425 1
425 1
426 2
426 2
427 4
427 4
428 $ log '::"date"'
428 $ log '::"date"'
429 0
429 0
430 1
430 1
431 2
431 2
432 4
432 4
433 $ log 'date(2005) and 1::'
433 $ log 'date(2005) and 1::'
434 4
434 4
435 $ hg book -d date
435 $ hg book -d date
436
436
437 keyword arguments
437 keyword arguments
438
438
439 $ log 'extra(branch, value=a)'
439 $ log 'extra(branch, value=a)'
440 0
440 0
441
441
442 $ log 'extra(branch, a, b)'
442 $ log 'extra(branch, a, b)'
443 hg: parse error: extra takes at most 2 arguments
443 hg: parse error: extra takes at most 2 arguments
444 [255]
444 [255]
445 $ log 'extra(a, label=b)'
445 $ log 'extra(a, label=b)'
446 hg: parse error: extra got multiple values for keyword argument 'label'
446 hg: parse error: extra got multiple values for keyword argument 'label'
447 [255]
447 [255]
448 $ log 'extra(label=branch, default)'
448 $ log 'extra(label=branch, default)'
449 hg: parse error: extra got an invalid argument
449 hg: parse error: extra got an invalid argument
450 [255]
450 [255]
451 $ log 'extra(branch, foo+bar=baz)'
451 $ log 'extra(branch, foo+bar=baz)'
452 hg: parse error: extra got an invalid argument
452 hg: parse error: extra got an invalid argument
453 [255]
453 [255]
454 $ log 'extra(unknown=branch)'
454 $ log 'extra(unknown=branch)'
455 hg: parse error: extra got an unexpected keyword argument 'unknown'
455 hg: parse error: extra got an unexpected keyword argument 'unknown'
456 [255]
456 [255]
457
457
458 $ try 'foo=bar|baz'
458 $ try 'foo=bar|baz'
459 (keyvalue
459 (keyvalue
460 ('symbol', 'foo')
460 ('symbol', 'foo')
461 (or
461 (or
462 ('symbol', 'bar')
462 ('symbol', 'bar')
463 ('symbol', 'baz')))
463 ('symbol', 'baz')))
464 hg: parse error: can't use a key-value pair in this context
464 hg: parse error: can't use a key-value pair in this context
465 [255]
465 [255]
466
466
467 Test that symbols only get parsed as functions if there's an opening
467 Test that symbols only get parsed as functions if there's an opening
468 parenthesis.
468 parenthesis.
469
469
470 $ hg book only -r 9
470 $ hg book only -r 9
471 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
471 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
472 8
472 8
473 9
473 9
474
474
475 ancestor can accept 0 or more arguments
475 ancestor can accept 0 or more arguments
476
476
477 $ log 'ancestor()'
477 $ log 'ancestor()'
478 $ log 'ancestor(1)'
478 $ log 'ancestor(1)'
479 1
479 1
480 $ log 'ancestor(4,5)'
480 $ log 'ancestor(4,5)'
481 1
481 1
482 $ log 'ancestor(4,5) and 4'
482 $ log 'ancestor(4,5) and 4'
483 $ log 'ancestor(0,0,1,3)'
483 $ log 'ancestor(0,0,1,3)'
484 0
484 0
485 $ log 'ancestor(3,1,5,3,5,1)'
485 $ log 'ancestor(3,1,5,3,5,1)'
486 1
486 1
487 $ log 'ancestor(0,1,3,5)'
487 $ log 'ancestor(0,1,3,5)'
488 0
488 0
489 $ log 'ancestor(1,2,3,4,5)'
489 $ log 'ancestor(1,2,3,4,5)'
490 1
490 1
491
491
492 test ancestors
492 test ancestors
493
493
494 $ log 'ancestors(5)'
494 $ log 'ancestors(5)'
495 0
495 0
496 1
496 1
497 3
497 3
498 5
498 5
499 $ log 'ancestor(ancestors(5))'
499 $ log 'ancestor(ancestors(5))'
500 0
500 0
501 $ log '::r3232()'
501 $ log '::r3232()'
502 0
502 0
503 1
503 1
504 2
504 2
505 3
505 3
506
506
507 $ log 'author(bob)'
507 $ log 'author(bob)'
508 2
508 2
509 $ log 'author("re:bob|test")'
509 $ log 'author("re:bob|test")'
510 0
510 0
511 1
511 1
512 2
512 2
513 3
513 3
514 4
514 4
515 5
515 5
516 6
516 6
517 7
517 7
518 8
518 8
519 9
519 9
520 $ log 'branch(Γ©)'
520 $ log 'branch(Γ©)'
521 8
521 8
522 9
522 9
523 $ log 'branch(a)'
523 $ log 'branch(a)'
524 0
524 0
525 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
525 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
526 0 a
526 0 a
527 2 a-b-c-
527 2 a-b-c-
528 3 +a+b+c+
528 3 +a+b+c+
529 4 -a-b-c-
529 4 -a-b-c-
530 5 !a/b/c/
530 5 !a/b/c/
531 6 _a_b_c_
531 6 _a_b_c_
532 7 .a.b.c.
532 7 .a.b.c.
533 $ log 'children(ancestor(4,5))'
533 $ log 'children(ancestor(4,5))'
534 2
534 2
535 3
535 3
536 $ log 'closed()'
536 $ log 'closed()'
537 $ log 'contains(a)'
537 $ log 'contains(a)'
538 0
538 0
539 1
539 1
540 3
540 3
541 5
541 5
542 $ log 'contains("../repo/a")'
542 $ log 'contains("../repo/a")'
543 0
543 0
544 1
544 1
545 3
545 3
546 5
546 5
547 $ log 'desc(B)'
547 $ log 'desc(B)'
548 5
548 5
549 $ log 'descendants(2 or 3)'
549 $ log 'descendants(2 or 3)'
550 2
550 2
551 3
551 3
552 4
552 4
553 5
553 5
554 6
554 6
555 7
555 7
556 8
556 8
557 9
557 9
558 $ log 'file("b*")'
558 $ log 'file("b*")'
559 1
559 1
560 4
560 4
561 $ log 'filelog("b")'
561 $ log 'filelog("b")'
562 1
562 1
563 4
563 4
564 $ log 'filelog("../repo/b")'
564 $ log 'filelog("../repo/b")'
565 1
565 1
566 4
566 4
567 $ log 'follow()'
567 $ log 'follow()'
568 0
568 0
569 1
569 1
570 2
570 2
571 4
571 4
572 8
572 8
573 9
573 9
574 $ log 'grep("issue\d+")'
574 $ log 'grep("issue\d+")'
575 6
575 6
576 $ try 'grep("(")' # invalid regular expression
576 $ try 'grep("(")' # invalid regular expression
577 (func
577 (func
578 ('symbol', 'grep')
578 ('symbol', 'grep')
579 ('string', '('))
579 ('string', '('))
580 hg: parse error: invalid match pattern: unbalanced parenthesis
580 hg: parse error: invalid match pattern: unbalanced parenthesis
581 [255]
581 [255]
582 $ try 'grep("\bissue\d+")'
582 $ try 'grep("\bissue\d+")'
583 (func
583 (func
584 ('symbol', 'grep')
584 ('symbol', 'grep')
585 ('string', '\x08issue\\d+'))
585 ('string', '\x08issue\\d+'))
586 * set:
586 * set:
587 <filteredset
587 <filteredset
588 <fullreposet+ 0:9>,
588 <fullreposet+ 0:9>,
589 <grep '\x08issue\\d+'>>
589 <grep '\x08issue\\d+'>>
590 $ try 'grep(r"\bissue\d+")'
590 $ try 'grep(r"\bissue\d+")'
591 (func
591 (func
592 ('symbol', 'grep')
592 ('symbol', 'grep')
593 ('string', '\\bissue\\d+'))
593 ('string', '\\bissue\\d+'))
594 * set:
594 * set:
595 <filteredset
595 <filteredset
596 <fullreposet+ 0:9>,
596 <fullreposet+ 0:9>,
597 <grep '\\bissue\\d+'>>
597 <grep '\\bissue\\d+'>>
598 6
598 6
599 $ try 'grep(r"\")'
599 $ try 'grep(r"\")'
600 hg: parse error at 7: unterminated string
600 hg: parse error at 7: unterminated string
601 [255]
601 [255]
602 $ log 'head()'
602 $ log 'head()'
603 0
603 0
604 1
604 1
605 2
605 2
606 3
606 3
607 4
607 4
608 5
608 5
609 6
609 6
610 7
610 7
611 9
611 9
612 $ log 'heads(6::)'
612 $ log 'heads(6::)'
613 7
613 7
614 $ log 'keyword(issue)'
614 $ log 'keyword(issue)'
615 6
615 6
616 $ log 'keyword("test a")'
616 $ log 'keyword("test a")'
617 $ log 'limit(head(), 1)'
617 $ log 'limit(head(), 1)'
618 0
618 0
619 $ log 'limit(author("re:bob|test"), 3, 5)'
619 $ log 'limit(author("re:bob|test"), 3, 5)'
620 5
620 5
621 6
621 6
622 7
622 7
623 $ log 'limit(author("re:bob|test"), offset=6)'
623 $ log 'limit(author("re:bob|test"), offset=6)'
624 6
624 6
625 $ log 'limit(author("re:bob|test"), offset=10)'
625 $ log 'limit(author("re:bob|test"), offset=10)'
626 $ log 'limit(all(), 1, -1)'
626 $ log 'limit(all(), 1, -1)'
627 hg: parse error: negative offset
627 hg: parse error: negative offset
628 [255]
628 [255]
629 $ log 'matching(6)'
629 $ log 'matching(6)'
630 6
630 6
631 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
631 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
632 6
632 6
633 7
633 7
634
634
635 Testing min and max
635 Testing min and max
636
636
637 max: simple
637 max: simple
638
638
639 $ log 'max(contains(a))'
639 $ log 'max(contains(a))'
640 5
640 5
641
641
642 max: simple on unordered set)
642 max: simple on unordered set)
643
643
644 $ log 'max((4+0+2+5+7) and contains(a))'
644 $ log 'max((4+0+2+5+7) and contains(a))'
645 5
645 5
646
646
647 max: no result
647 max: no result
648
648
649 $ log 'max(contains(stringthatdoesnotappearanywhere))'
649 $ log 'max(contains(stringthatdoesnotappearanywhere))'
650
650
651 max: no result on unordered set
651 max: no result on unordered set
652
652
653 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
653 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
654
654
655 min: simple
655 min: simple
656
656
657 $ log 'min(contains(a))'
657 $ log 'min(contains(a))'
658 0
658 0
659
659
660 min: simple on unordered set
660 min: simple on unordered set
661
661
662 $ log 'min((4+0+2+5+7) and contains(a))'
662 $ log 'min((4+0+2+5+7) and contains(a))'
663 0
663 0
664
664
665 min: empty
665 min: empty
666
666
667 $ log 'min(contains(stringthatdoesnotappearanywhere))'
667 $ log 'min(contains(stringthatdoesnotappearanywhere))'
668
668
669 min: empty on unordered set
669 min: empty on unordered set
670
670
671 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
671 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
672
672
673
673
674 $ log 'merge()'
674 $ log 'merge()'
675 6
675 6
676 $ log 'branchpoint()'
676 $ log 'branchpoint()'
677 1
677 1
678 4
678 4
679 $ log 'modifies(b)'
679 $ log 'modifies(b)'
680 4
680 4
681 $ log 'modifies("path:b")'
681 $ log 'modifies("path:b")'
682 4
682 4
683 $ log 'modifies("*")'
683 $ log 'modifies("*")'
684 4
684 4
685 6
685 6
686 $ log 'modifies("set:modified()")'
686 $ log 'modifies("set:modified()")'
687 4
687 4
688 $ log 'id(5)'
688 $ log 'id(5)'
689 2
689 2
690 $ log 'only(9)'
690 $ log 'only(9)'
691 8
691 8
692 9
692 9
693 $ log 'only(8)'
693 $ log 'only(8)'
694 8
694 8
695 $ log 'only(9, 5)'
695 $ log 'only(9, 5)'
696 2
696 2
697 4
697 4
698 8
698 8
699 9
699 9
700 $ log 'only(7 + 9, 5 + 2)'
700 $ log 'only(7 + 9, 5 + 2)'
701 4
701 4
702 6
702 6
703 7
703 7
704 8
704 8
705 9
705 9
706
706
707 Test empty set input
707 Test empty set input
708 $ log 'only(p2())'
708 $ log 'only(p2())'
709 $ log 'only(p1(), p2())'
709 $ log 'only(p1(), p2())'
710 0
710 0
711 1
711 1
712 2
712 2
713 4
713 4
714 8
714 8
715 9
715 9
716
716
717 Test '%' operator
717 Test '%' operator
718
718
719 $ log '9%'
719 $ log '9%'
720 8
720 8
721 9
721 9
722 $ log '9%5'
722 $ log '9%5'
723 2
723 2
724 4
724 4
725 8
725 8
726 9
726 9
727 $ log '(7 + 9)%(5 + 2)'
727 $ log '(7 + 9)%(5 + 2)'
728 4
728 4
729 6
729 6
730 7
730 7
731 8
731 8
732 9
732 9
733
733
734 Test opreand of '%' is optimized recursively (issue4670)
734 Test opreand of '%' is optimized recursively (issue4670)
735
735
736 $ try --optimize '8:9-8%'
736 $ try --optimize '8:9-8%'
737 (onlypost
737 (onlypost
738 (minus
738 (minus
739 (range
739 (range
740 ('symbol', '8')
740 ('symbol', '8')
741 ('symbol', '9'))
741 ('symbol', '9'))
742 ('symbol', '8')))
742 ('symbol', '8')))
743 * optimized:
743 * optimized:
744 (func
744 (func
745 ('symbol', 'only')
745 ('symbol', 'only')
746 (difference
746 (difference
747 (range
747 (range
748 ('symbol', '8')
748 ('symbol', '8')
749 ('symbol', '9'))
749 ('symbol', '9'))
750 ('symbol', '8')))
750 ('symbol', '8')))
751 * set:
751 * set:
752 <baseset+ [8, 9]>
752 <baseset+ [8, 9]>
753 8
753 8
754 9
754 9
755 $ try --optimize '(9)%(5)'
755 $ try --optimize '(9)%(5)'
756 (only
756 (only
757 (group
757 (group
758 ('symbol', '9'))
758 ('symbol', '9'))
759 (group
759 (group
760 ('symbol', '5')))
760 ('symbol', '5')))
761 * optimized:
761 * optimized:
762 (func
762 (func
763 ('symbol', 'only')
763 ('symbol', 'only')
764 (list
764 (list
765 ('symbol', '9')
765 ('symbol', '9')
766 ('symbol', '5')))
766 ('symbol', '5')))
767 * set:
767 * set:
768 <baseset+ [2, 4, 8, 9]>
768 <baseset+ [2, 4, 8, 9]>
769 2
769 2
770 4
770 4
771 8
771 8
772 9
772 9
773
773
774 Test the order of operations
774 Test the order of operations
775
775
776 $ log '7 + 9%5 + 2'
776 $ log '7 + 9%5 + 2'
777 7
777 7
778 2
778 2
779 4
779 4
780 8
780 8
781 9
781 9
782
782
783 Test explicit numeric revision
783 Test explicit numeric revision
784 $ log 'rev(-2)'
784 $ log 'rev(-2)'
785 $ log 'rev(-1)'
785 $ log 'rev(-1)'
786 -1
786 -1
787 $ log 'rev(0)'
787 $ log 'rev(0)'
788 0
788 0
789 $ log 'rev(9)'
789 $ log 'rev(9)'
790 9
790 9
791 $ log 'rev(10)'
791 $ log 'rev(10)'
792 $ log 'rev(tip)'
792 $ log 'rev(tip)'
793 hg: parse error: rev expects a number
793 hg: parse error: rev expects a number
794 [255]
794 [255]
795
795
796 Test hexadecimal revision
796 Test hexadecimal revision
797 $ log 'id(2)'
797 $ log 'id(2)'
798 abort: 00changelog.i@2: ambiguous identifier!
798 abort: 00changelog.i@2: ambiguous identifier!
799 [255]
799 [255]
800 $ log 'id(23268)'
800 $ log 'id(23268)'
801 4
801 4
802 $ log 'id(2785f51eece)'
802 $ log 'id(2785f51eece)'
803 0
803 0
804 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
804 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
805 8
805 8
806 $ log 'id(d5d0dcbdc4a)'
806 $ log 'id(d5d0dcbdc4a)'
807 $ log 'id(d5d0dcbdc4w)'
807 $ log 'id(d5d0dcbdc4w)'
808 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
808 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
809 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
809 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
810 $ log 'id(1.0)'
810 $ log 'id(1.0)'
811 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
811 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
812
812
813 Test null revision
813 Test null revision
814 $ log '(null)'
814 $ log '(null)'
815 -1
815 -1
816 $ log '(null:0)'
816 $ log '(null:0)'
817 -1
817 -1
818 0
818 0
819 $ log '(0:null)'
819 $ log '(0:null)'
820 0
820 0
821 -1
821 -1
822 $ log 'null::0'
822 $ log 'null::0'
823 -1
823 -1
824 0
824 0
825 $ log 'null:tip - 0:'
825 $ log 'null:tip - 0:'
826 -1
826 -1
827 $ log 'null: and null::' | head -1
827 $ log 'null: and null::' | head -1
828 -1
828 -1
829 $ log 'null: or 0:' | head -2
829 $ log 'null: or 0:' | head -2
830 -1
830 -1
831 0
831 0
832 $ log 'ancestors(null)'
832 $ log 'ancestors(null)'
833 -1
833 -1
834 $ log 'reverse(null:)' | tail -2
834 $ log 'reverse(null:)' | tail -2
835 0
835 0
836 -1
836 -1
837 BROKEN: should be '-1'
837 BROKEN: should be '-1'
838 $ log 'first(null:)'
838 $ log 'first(null:)'
839 BROKEN: should be '-1'
839 BROKEN: should be '-1'
840 $ log 'min(null:)'
840 $ log 'min(null:)'
841 $ log 'tip:null and all()' | tail -2
841 $ log 'tip:null and all()' | tail -2
842 1
842 1
843 0
843 0
844
844
845 Test working-directory revision
845 Test working-directory revision
846 $ hg debugrevspec 'wdir()'
846 $ hg debugrevspec 'wdir()'
847 2147483647
847 2147483647
848 $ hg debugrevspec 'tip or wdir()'
848 $ hg debugrevspec 'tip or wdir()'
849 9
849 9
850 2147483647
850 2147483647
851 $ hg debugrevspec '0:tip and wdir()'
851 $ hg debugrevspec '0:tip and wdir()'
852 $ log '0:wdir()' | tail -3
852 $ log '0:wdir()' | tail -3
853 8
853 8
854 9
854 9
855 2147483647
855 2147483647
856 $ log 'wdir():0' | head -3
856 $ log 'wdir():0' | head -3
857 2147483647
857 2147483647
858 9
858 9
859 8
859 8
860 $ log 'wdir():wdir()'
860 $ log 'wdir():wdir()'
861 2147483647
861 2147483647
862 $ log '(all() + wdir()) & min(. + wdir())'
862 $ log '(all() + wdir()) & min(. + wdir())'
863 9
863 9
864 $ log '(all() + wdir()) & max(. + wdir())'
864 $ log '(all() + wdir()) & max(. + wdir())'
865 2147483647
865 2147483647
866 $ log '(all() + wdir()) & first(wdir() + .)'
866 $ log '(all() + wdir()) & first(wdir() + .)'
867 2147483647
867 2147483647
868 $ log '(all() + wdir()) & last(. + wdir())'
868 $ log '(all() + wdir()) & last(. + wdir())'
869 2147483647
869 2147483647
870
870
871 $ log 'outgoing()'
871 $ log 'outgoing()'
872 8
872 8
873 9
873 9
874 $ log 'outgoing("../remote1")'
874 $ log 'outgoing("../remote1")'
875 8
875 8
876 9
876 9
877 $ log 'outgoing("../remote2")'
877 $ log 'outgoing("../remote2")'
878 3
878 3
879 5
879 5
880 6
880 6
881 7
881 7
882 9
882 9
883 $ log 'p1(merge())'
883 $ log 'p1(merge())'
884 5
884 5
885 $ log 'p2(merge())'
885 $ log 'p2(merge())'
886 4
886 4
887 $ log 'parents(merge())'
887 $ log 'parents(merge())'
888 4
888 4
889 5
889 5
890 $ log 'p1(branchpoint())'
890 $ log 'p1(branchpoint())'
891 0
891 0
892 2
892 2
893 $ log 'p2(branchpoint())'
893 $ log 'p2(branchpoint())'
894 $ log 'parents(branchpoint())'
894 $ log 'parents(branchpoint())'
895 0
895 0
896 2
896 2
897 $ log 'removes(a)'
897 $ log 'removes(a)'
898 2
898 2
899 6
899 6
900 $ log 'roots(all())'
900 $ log 'roots(all())'
901 0
901 0
902 $ log 'reverse(2 or 3 or 4 or 5)'
902 $ log 'reverse(2 or 3 or 4 or 5)'
903 5
903 5
904 4
904 4
905 3
905 3
906 2
906 2
907 $ log 'reverse(all())'
907 $ log 'reverse(all())'
908 9
908 9
909 8
909 8
910 7
910 7
911 6
911 6
912 5
912 5
913 4
913 4
914 3
914 3
915 2
915 2
916 1
916 1
917 0
917 0
918 $ log 'reverse(all()) & filelog(b)'
918 $ log 'reverse(all()) & filelog(b)'
919 4
919 4
920 1
920 1
921 $ log 'rev(5)'
921 $ log 'rev(5)'
922 5
922 5
923 $ log 'sort(limit(reverse(all()), 3))'
923 $ log 'sort(limit(reverse(all()), 3))'
924 7
924 7
925 8
925 8
926 9
926 9
927 $ log 'sort(2 or 3 or 4 or 5, date)'
927 $ log 'sort(2 or 3 or 4 or 5, date)'
928 2
928 2
929 3
929 3
930 5
930 5
931 4
931 4
932 $ log 'tagged()'
932 $ log 'tagged()'
933 6
933 6
934 $ log 'tag()'
934 $ log 'tag()'
935 6
935 6
936 $ log 'tag(1.0)'
936 $ log 'tag(1.0)'
937 6
937 6
938 $ log 'tag(tip)'
938 $ log 'tag(tip)'
939 9
939 9
940
940
941 Test order of revisions in compound expression
941 Test order of revisions in compound expression
942 ----------------------------------------------
942 ----------------------------------------------
943
943
944 The general rule is that only the outermost (= leftmost) predicate can
944 The general rule is that only the outermost (= leftmost) predicate can
945 enforce its ordering requirement. The other predicates should take the
945 enforce its ordering requirement. The other predicates should take the
946 ordering defined by it.
946 ordering defined by it.
947
947
948 'A & B' should follow the order of 'A':
948 'A & B' should follow the order of 'A':
949
949
950 $ log '2:0 & 0::2'
950 $ log '2:0 & 0::2'
951 2
951 2
952 1
952 1
953 0
953 0
954
954
955 'head()' combines sets in wrong order:
955 'head()' combines sets in right order:
956
956
957 $ log '2:0 & head()'
957 $ log '2:0 & head()'
958 0
958 2
959 1
959 1
960 2
960 0
961 BROKEN: should be '2 1 0'
962
961
963 'a + b', which is optimized to '_list(a b)', should take the ordering of
962 'a + b', which is optimized to '_list(a b)', should take the ordering of
964 the left expression:
963 the left expression:
965
964
966 $ try --optimize '2:0 & (0 + 1 + 2)'
965 $ try --optimize '2:0 & (0 + 1 + 2)'
967 (and
966 (and
968 (range
967 (range
969 ('symbol', '2')
968 ('symbol', '2')
970 ('symbol', '0'))
969 ('symbol', '0'))
971 (group
970 (group
972 (or
971 (or
973 ('symbol', '0')
972 ('symbol', '0')
974 ('symbol', '1')
973 ('symbol', '1')
975 ('symbol', '2'))))
974 ('symbol', '2'))))
976 * optimized:
975 * optimized:
977 (and
976 (and
978 (range
977 (range
979 ('symbol', '2')
978 ('symbol', '2')
980 ('symbol', '0'))
979 ('symbol', '0'))
981 (func
980 (func
982 ('symbol', '_list')
981 ('symbol', '_list')
983 ('string', '0\x001\x002')))
982 ('string', '0\x001\x002')))
984 * set:
983 * set:
985 <baseset [0, 1, 2]>
984 <baseset [0, 1, 2]>
986 0
985 0
987 1
986 1
988 2
987 2
989 BROKEN: should be '2 1 0'
988 BROKEN: should be '2 1 0'
990
989
991 'A + B' should take the ordering of the left expression:
990 'A + B' should take the ordering of the left expression:
992
991
993 $ try --optimize '2:0 & (0:1 + 2)'
992 $ try --optimize '2:0 & (0:1 + 2)'
994 (and
993 (and
995 (range
994 (range
996 ('symbol', '2')
995 ('symbol', '2')
997 ('symbol', '0'))
996 ('symbol', '0'))
998 (group
997 (group
999 (or
998 (or
1000 (range
999 (range
1001 ('symbol', '0')
1000 ('symbol', '0')
1002 ('symbol', '1'))
1001 ('symbol', '1'))
1003 ('symbol', '2'))))
1002 ('symbol', '2'))))
1004 * optimized:
1003 * optimized:
1005 (and
1004 (and
1006 (range
1005 (range
1007 ('symbol', '2')
1006 ('symbol', '2')
1008 ('symbol', '0'))
1007 ('symbol', '0'))
1009 (or
1008 (or
1010 (range
1009 (range
1011 ('symbol', '0')
1010 ('symbol', '0')
1012 ('symbol', '1'))
1011 ('symbol', '1'))
1013 ('symbol', '2')))
1012 ('symbol', '2')))
1014 * set:
1013 * set:
1015 <addset
1014 <addset
1016 <filteredset
1015 <filteredset
1017 <spanset+ 0:1>,
1016 <spanset+ 0:1>,
1018 <spanset- 0:2>>,
1017 <spanset- 0:2>>,
1019 <baseset [2]>>
1018 <baseset [2]>>
1020 0
1019 0
1021 1
1020 1
1022 2
1021 2
1023 BROKEN: should be '2 1 0'
1022 BROKEN: should be '2 1 0'
1024
1023
1025 '_intlist(a b)' should behave like 'a + b':
1024 '_intlist(a b)' should behave like 'a + b':
1026
1025
1027 $ trylist --optimize '2:0 & %ld' 0 1 2
1026 $ trylist --optimize '2:0 & %ld' 0 1 2
1028 (and
1027 (and
1029 (range
1028 (range
1030 ('symbol', '2')
1029 ('symbol', '2')
1031 ('symbol', '0'))
1030 ('symbol', '0'))
1032 (func
1031 (func
1033 ('symbol', '_intlist')
1032 ('symbol', '_intlist')
1034 ('string', '0\x001\x002')))
1033 ('string', '0\x001\x002')))
1035 * optimized:
1034 * optimized:
1036 (and
1035 (and
1037 (func
1036 (func
1038 ('symbol', '_intlist')
1037 ('symbol', '_intlist')
1039 ('string', '0\x001\x002'))
1038 ('string', '0\x001\x002'))
1040 (range
1039 (range
1041 ('symbol', '2')
1040 ('symbol', '2')
1042 ('symbol', '0')))
1041 ('symbol', '0')))
1043 * set:
1042 * set:
1044 <filteredset
1043 <filteredset
1045 <spanset- 0:2>,
1044 <spanset- 0:2>,
1046 <baseset [0, 1, 2]>>
1045 <baseset [0, 1, 2]>>
1047 2
1046 2
1048 1
1047 1
1049 0
1048 0
1050
1049
1051 $ trylist --optimize '%ld & 2:0' 0 2 1
1050 $ trylist --optimize '%ld & 2:0' 0 2 1
1052 (and
1051 (and
1053 (func
1052 (func
1054 ('symbol', '_intlist')
1053 ('symbol', '_intlist')
1055 ('string', '0\x002\x001'))
1054 ('string', '0\x002\x001'))
1056 (range
1055 (range
1057 ('symbol', '2')
1056 ('symbol', '2')
1058 ('symbol', '0')))
1057 ('symbol', '0')))
1059 * optimized:
1058 * optimized:
1060 (and
1059 (and
1061 (func
1060 (func
1062 ('symbol', '_intlist')
1061 ('symbol', '_intlist')
1063 ('string', '0\x002\x001'))
1062 ('string', '0\x002\x001'))
1064 (range
1063 (range
1065 ('symbol', '2')
1064 ('symbol', '2')
1066 ('symbol', '0')))
1065 ('symbol', '0')))
1067 * set:
1066 * set:
1068 <filteredset
1067 <filteredset
1069 <spanset- 0:2>,
1068 <spanset- 0:2>,
1070 <baseset [0, 2, 1]>>
1069 <baseset [0, 2, 1]>>
1071 2
1070 2
1072 1
1071 1
1073 0
1072 0
1074 BROKEN: should be '0 2 1'
1073 BROKEN: should be '0 2 1'
1075
1074
1076 '_hexlist(a b)' should behave like 'a + b':
1075 '_hexlist(a b)' should behave like 'a + b':
1077
1076
1078 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1077 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1079 (and
1078 (and
1080 (range
1079 (range
1081 ('symbol', '2')
1080 ('symbol', '2')
1082 ('symbol', '0'))
1081 ('symbol', '0'))
1083 (func
1082 (func
1084 ('symbol', '_hexlist')
1083 ('symbol', '_hexlist')
1085 ('string', '*'))) (glob)
1084 ('string', '*'))) (glob)
1086 * optimized:
1085 * optimized:
1087 (and
1086 (and
1088 (range
1087 (range
1089 ('symbol', '2')
1088 ('symbol', '2')
1090 ('symbol', '0'))
1089 ('symbol', '0'))
1091 (func
1090 (func
1092 ('symbol', '_hexlist')
1091 ('symbol', '_hexlist')
1093 ('string', '*'))) (glob)
1092 ('string', '*'))) (glob)
1094 * set:
1093 * set:
1095 <baseset [0, 1, 2]>
1094 <baseset [0, 1, 2]>
1096 0
1095 0
1097 1
1096 1
1098 2
1097 2
1099 BROKEN: should be '2 1 0'
1098 BROKEN: should be '2 1 0'
1100
1099
1101 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1100 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1102 (and
1101 (and
1103 (func
1102 (func
1104 ('symbol', '_hexlist')
1103 ('symbol', '_hexlist')
1105 ('string', '*')) (glob)
1104 ('string', '*')) (glob)
1106 (range
1105 (range
1107 ('symbol', '2')
1106 ('symbol', '2')
1108 ('symbol', '0')))
1107 ('symbol', '0')))
1109 * optimized:
1108 * optimized:
1110 (and
1109 (and
1111 (range
1110 (range
1112 ('symbol', '2')
1111 ('symbol', '2')
1113 ('symbol', '0'))
1112 ('symbol', '0'))
1114 (func
1113 (func
1115 ('symbol', '_hexlist')
1114 ('symbol', '_hexlist')
1116 ('string', '*'))) (glob)
1115 ('string', '*'))) (glob)
1117 * set:
1116 * set:
1118 <baseset [0, 2, 1]>
1117 <baseset [0, 2, 1]>
1119 0
1118 0
1120 2
1119 2
1121 1
1120 1
1122
1121
1123 'present()' should do nothing other than suppressing an error:
1122 'present()' should do nothing other than suppressing an error:
1124
1123
1125 $ try --optimize '2:0 & present(0 + 1 + 2)'
1124 $ try --optimize '2:0 & present(0 + 1 + 2)'
1126 (and
1125 (and
1127 (range
1126 (range
1128 ('symbol', '2')
1127 ('symbol', '2')
1129 ('symbol', '0'))
1128 ('symbol', '0'))
1130 (func
1129 (func
1131 ('symbol', 'present')
1130 ('symbol', 'present')
1132 (or
1131 (or
1133 ('symbol', '0')
1132 ('symbol', '0')
1134 ('symbol', '1')
1133 ('symbol', '1')
1135 ('symbol', '2'))))
1134 ('symbol', '2'))))
1136 * optimized:
1135 * optimized:
1137 (and
1136 (and
1138 (range
1137 (range
1139 ('symbol', '2')
1138 ('symbol', '2')
1140 ('symbol', '0'))
1139 ('symbol', '0'))
1141 (func
1140 (func
1142 ('symbol', 'present')
1141 ('symbol', 'present')
1143 (func
1142 (func
1144 ('symbol', '_list')
1143 ('symbol', '_list')
1145 ('string', '0\x001\x002'))))
1144 ('string', '0\x001\x002'))))
1146 * set:
1145 * set:
1147 <baseset [0, 1, 2]>
1146 <baseset [0, 1, 2]>
1148 0
1147 0
1149 1
1148 1
1150 2
1149 2
1151 BROKEN: should be '2 1 0'
1150 BROKEN: should be '2 1 0'
1152
1151
1153 'reverse()' should take effect only if it is the outermost expression:
1152 'reverse()' should take effect only if it is the outermost expression:
1154
1153
1155 $ try --optimize '0:2 & reverse(all())'
1154 $ try --optimize '0:2 & reverse(all())'
1156 (and
1155 (and
1157 (range
1156 (range
1158 ('symbol', '0')
1157 ('symbol', '0')
1159 ('symbol', '2'))
1158 ('symbol', '2'))
1160 (func
1159 (func
1161 ('symbol', 'reverse')
1160 ('symbol', 'reverse')
1162 (func
1161 (func
1163 ('symbol', 'all')
1162 ('symbol', 'all')
1164 None)))
1163 None)))
1165 * optimized:
1164 * optimized:
1166 (and
1165 (and
1167 (range
1166 (range
1168 ('symbol', '0')
1167 ('symbol', '0')
1169 ('symbol', '2'))
1168 ('symbol', '2'))
1170 (func
1169 (func
1171 ('symbol', 'reverse')
1170 ('symbol', 'reverse')
1172 (func
1171 (func
1173 ('symbol', 'all')
1172 ('symbol', 'all')
1174 None)))
1173 None)))
1175 * set:
1174 * set:
1176 <filteredset
1175 <filteredset
1177 <spanset- 0:2>,
1176 <spanset- 0:2>,
1178 <spanset+ 0:9>>
1177 <spanset+ 0:9>>
1179 2
1178 2
1180 1
1179 1
1181 0
1180 0
1182 BROKEN: should be '0 1 2'
1181 BROKEN: should be '0 1 2'
1183
1182
1184 'sort()' should take effect only if it is the outermost expression:
1183 'sort()' should take effect only if it is the outermost expression:
1185
1184
1186 $ try --optimize '0:2 & sort(all(), -rev)'
1185 $ try --optimize '0:2 & sort(all(), -rev)'
1187 (and
1186 (and
1188 (range
1187 (range
1189 ('symbol', '0')
1188 ('symbol', '0')
1190 ('symbol', '2'))
1189 ('symbol', '2'))
1191 (func
1190 (func
1192 ('symbol', 'sort')
1191 ('symbol', 'sort')
1193 (list
1192 (list
1194 (func
1193 (func
1195 ('symbol', 'all')
1194 ('symbol', 'all')
1196 None)
1195 None)
1197 (negate
1196 (negate
1198 ('symbol', 'rev')))))
1197 ('symbol', 'rev')))))
1199 * optimized:
1198 * optimized:
1200 (and
1199 (and
1201 (range
1200 (range
1202 ('symbol', '0')
1201 ('symbol', '0')
1203 ('symbol', '2'))
1202 ('symbol', '2'))
1204 (func
1203 (func
1205 ('symbol', 'sort')
1204 ('symbol', 'sort')
1206 (list
1205 (list
1207 (func
1206 (func
1208 ('symbol', 'all')
1207 ('symbol', 'all')
1209 None)
1208 None)
1210 ('string', '-rev'))))
1209 ('string', '-rev'))))
1211 * set:
1210 * set:
1212 <filteredset
1211 <filteredset
1213 <spanset- 0:2>,
1212 <spanset- 0:2>,
1214 <spanset+ 0:9>>
1213 <spanset+ 0:9>>
1215 2
1214 2
1216 1
1215 1
1217 0
1216 0
1218 BROKEN: should be '0 1 2'
1217 BROKEN: should be '0 1 2'
1219
1218
1220 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1219 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1221
1220
1222 $ try --optimize '2:0 & first(1 + 0 + 2)'
1221 $ try --optimize '2:0 & first(1 + 0 + 2)'
1223 (and
1222 (and
1224 (range
1223 (range
1225 ('symbol', '2')
1224 ('symbol', '2')
1226 ('symbol', '0'))
1225 ('symbol', '0'))
1227 (func
1226 (func
1228 ('symbol', 'first')
1227 ('symbol', 'first')
1229 (or
1228 (or
1230 ('symbol', '1')
1229 ('symbol', '1')
1231 ('symbol', '0')
1230 ('symbol', '0')
1232 ('symbol', '2'))))
1231 ('symbol', '2'))))
1233 * optimized:
1232 * optimized:
1234 (and
1233 (and
1235 (range
1234 (range
1236 ('symbol', '2')
1235 ('symbol', '2')
1237 ('symbol', '0'))
1236 ('symbol', '0'))
1238 (func
1237 (func
1239 ('symbol', 'first')
1238 ('symbol', 'first')
1240 (func
1239 (func
1241 ('symbol', '_list')
1240 ('symbol', '_list')
1242 ('string', '1\x000\x002'))))
1241 ('string', '1\x000\x002'))))
1243 * set:
1242 * set:
1244 <baseset
1243 <baseset
1245 <limit n=1, offset=0,
1244 <limit n=1, offset=0,
1246 <spanset- 0:2>,
1245 <spanset- 0:2>,
1247 <baseset [1, 0, 2]>>>
1246 <baseset [1, 0, 2]>>>
1248 1
1247 1
1249
1248
1250 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1249 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1251 (and
1250 (and
1252 (range
1251 (range
1253 ('symbol', '2')
1252 ('symbol', '2')
1254 ('symbol', '0'))
1253 ('symbol', '0'))
1255 (not
1254 (not
1256 (func
1255 (func
1257 ('symbol', 'last')
1256 ('symbol', 'last')
1258 (or
1257 (or
1259 ('symbol', '0')
1258 ('symbol', '0')
1260 ('symbol', '2')
1259 ('symbol', '2')
1261 ('symbol', '1')))))
1260 ('symbol', '1')))))
1262 * optimized:
1261 * optimized:
1263 (difference
1262 (difference
1264 (range
1263 (range
1265 ('symbol', '2')
1264 ('symbol', '2')
1266 ('symbol', '0'))
1265 ('symbol', '0'))
1267 (func
1266 (func
1268 ('symbol', 'last')
1267 ('symbol', 'last')
1269 (func
1268 (func
1270 ('symbol', '_list')
1269 ('symbol', '_list')
1271 ('string', '0\x002\x001'))))
1270 ('string', '0\x002\x001'))))
1272 * set:
1271 * set:
1273 <filteredset
1272 <filteredset
1274 <spanset- 0:2>,
1273 <spanset- 0:2>,
1275 <not
1274 <not
1276 <baseset
1275 <baseset
1277 <last n=1,
1276 <last n=1,
1278 <fullreposet+ 0:9>,
1277 <fullreposet+ 0:9>,
1279 <baseset [1, 2, 0]>>>>>
1278 <baseset [1, 2, 0]>>>>>
1280 2
1279 2
1281 0
1280 0
1282
1281
1283 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1282 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1284
1283
1285 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1284 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1286 (and
1285 (and
1287 (range
1286 (range
1288 ('symbol', '2')
1287 ('symbol', '2')
1289 ('symbol', '0'))
1288 ('symbol', '0'))
1290 (range
1289 (range
1291 (group
1290 (group
1292 (or
1291 (or
1293 ('symbol', '1')
1292 ('symbol', '1')
1294 ('symbol', '0')
1293 ('symbol', '0')
1295 ('symbol', '2')))
1294 ('symbol', '2')))
1296 (group
1295 (group
1297 (or
1296 (or
1298 ('symbol', '0')
1297 ('symbol', '0')
1299 ('symbol', '2')
1298 ('symbol', '2')
1300 ('symbol', '1')))))
1299 ('symbol', '1')))))
1301 * optimized:
1300 * optimized:
1302 (and
1301 (and
1303 (range
1302 (range
1304 ('symbol', '2')
1303 ('symbol', '2')
1305 ('symbol', '0'))
1304 ('symbol', '0'))
1306 (range
1305 (range
1307 (func
1306 (func
1308 ('symbol', '_list')
1307 ('symbol', '_list')
1309 ('string', '1\x000\x002'))
1308 ('string', '1\x000\x002'))
1310 (func
1309 (func
1311 ('symbol', '_list')
1310 ('symbol', '_list')
1312 ('string', '0\x002\x001'))))
1311 ('string', '0\x002\x001'))))
1313 * set:
1312 * set:
1314 <filteredset
1313 <filteredset
1315 <baseset [1]>,
1314 <baseset [1]>,
1316 <spanset- 0:2>>
1315 <spanset- 0:2>>
1317 1
1316 1
1318
1317
1319 'A & B' can be rewritten as 'B & A' by weight, but the ordering rule should
1318 'A & B' can be rewritten as 'B & A' by weight, but the ordering rule should
1320 be determined before the optimization (i.e. 'B' should take the ordering of
1319 be determined before the optimization (i.e. 'B' should take the ordering of
1321 'A'):
1320 'A'):
1322
1321
1323 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1322 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1324 (and
1323 (and
1325 (func
1324 (func
1326 ('symbol', 'contains')
1325 ('symbol', 'contains')
1327 ('string', 'glob:*'))
1326 ('string', 'glob:*'))
1328 (group
1327 (group
1329 (or
1328 (or
1330 ('symbol', '2')
1329 ('symbol', '2')
1331 ('symbol', '0')
1330 ('symbol', '0')
1332 ('symbol', '1'))))
1331 ('symbol', '1'))))
1333 * optimized:
1332 * optimized:
1334 (and
1333 (and
1335 (func
1334 (func
1336 ('symbol', '_list')
1335 ('symbol', '_list')
1337 ('string', '2\x000\x001'))
1336 ('string', '2\x000\x001'))
1338 (func
1337 (func
1339 ('symbol', 'contains')
1338 ('symbol', 'contains')
1340 ('string', 'glob:*')))
1339 ('string', 'glob:*')))
1341 * set:
1340 * set:
1342 <filteredset
1341 <filteredset
1343 <baseset [2, 0, 1]>,
1342 <baseset [2, 0, 1]>,
1344 <contains 'glob:*'>>
1343 <contains 'glob:*'>>
1345 2
1344 2
1346 0
1345 0
1347 1
1346 1
1348 BROKEN: should be '0 1 2'
1347 BROKEN: should be '0 1 2'
1349
1348
1350 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1349 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1351 (and
1350 (and
1352 (func
1351 (func
1353 ('symbol', 'reverse')
1352 ('symbol', 'reverse')
1354 (func
1353 (func
1355 ('symbol', 'contains')
1354 ('symbol', 'contains')
1356 ('string', 'glob:*')))
1355 ('string', 'glob:*')))
1357 (group
1356 (group
1358 (or
1357 (or
1359 ('symbol', '0')
1358 ('symbol', '0')
1360 ('symbol', '2')
1359 ('symbol', '2')
1361 ('symbol', '1'))))
1360 ('symbol', '1'))))
1362 * optimized:
1361 * optimized:
1363 (and
1362 (and
1364 (func
1363 (func
1365 ('symbol', '_list')
1364 ('symbol', '_list')
1366 ('string', '0\x002\x001'))
1365 ('string', '0\x002\x001'))
1367 (func
1366 (func
1368 ('symbol', 'reverse')
1367 ('symbol', 'reverse')
1369 (func
1368 (func
1370 ('symbol', 'contains')
1369 ('symbol', 'contains')
1371 ('string', 'glob:*'))))
1370 ('string', 'glob:*'))))
1372 * set:
1371 * set:
1373 <filteredset
1372 <filteredset
1374 <baseset [1, 2, 0]>,
1373 <baseset [1, 2, 0]>,
1375 <contains 'glob:*'>>
1374 <contains 'glob:*'>>
1376 1
1375 1
1377 2
1376 2
1378 0
1377 0
1379 BROKEN: should be '2 1 0'
1378 BROKEN: should be '2 1 0'
1380
1379
1381 test sort revset
1380 test sort revset
1382 --------------------------------------------
1381 --------------------------------------------
1383
1382
1384 test when adding two unordered revsets
1383 test when adding two unordered revsets
1385
1384
1386 $ log 'sort(keyword(issue) or modifies(b))'
1385 $ log 'sort(keyword(issue) or modifies(b))'
1387 4
1386 4
1388 6
1387 6
1389
1388
1390 test when sorting a reversed collection in the same way it is
1389 test when sorting a reversed collection in the same way it is
1391
1390
1392 $ log 'sort(reverse(all()), -rev)'
1391 $ log 'sort(reverse(all()), -rev)'
1393 9
1392 9
1394 8
1393 8
1395 7
1394 7
1396 6
1395 6
1397 5
1396 5
1398 4
1397 4
1399 3
1398 3
1400 2
1399 2
1401 1
1400 1
1402 0
1401 0
1403
1402
1404 test when sorting a reversed collection
1403 test when sorting a reversed collection
1405
1404
1406 $ log 'sort(reverse(all()), rev)'
1405 $ log 'sort(reverse(all()), rev)'
1407 0
1406 0
1408 1
1407 1
1409 2
1408 2
1410 3
1409 3
1411 4
1410 4
1412 5
1411 5
1413 6
1412 6
1414 7
1413 7
1415 8
1414 8
1416 9
1415 9
1417
1416
1418
1417
1419 test sorting two sorted collections in different orders
1418 test sorting two sorted collections in different orders
1420
1419
1421 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1420 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1422 2
1421 2
1423 6
1422 6
1424 8
1423 8
1425 9
1424 9
1426
1425
1427 test sorting two sorted collections in different orders backwards
1426 test sorting two sorted collections in different orders backwards
1428
1427
1429 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1428 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1430 9
1429 9
1431 8
1430 8
1432 6
1431 6
1433 2
1432 2
1434
1433
1435 test empty sort key which is noop
1434 test empty sort key which is noop
1436
1435
1437 $ log 'sort(0 + 2 + 1, "")'
1436 $ log 'sort(0 + 2 + 1, "")'
1438 0
1437 0
1439 2
1438 2
1440 1
1439 1
1441
1440
1442 test invalid sort keys
1441 test invalid sort keys
1443
1442
1444 $ log 'sort(all(), -invalid)'
1443 $ log 'sort(all(), -invalid)'
1445 hg: parse error: unknown sort key '-invalid'
1444 hg: parse error: unknown sort key '-invalid'
1446 [255]
1445 [255]
1447
1446
1448 $ cd ..
1447 $ cd ..
1449
1448
1450 test sorting by multiple keys including variable-length strings
1449 test sorting by multiple keys including variable-length strings
1451
1450
1452 $ hg init sorting
1451 $ hg init sorting
1453 $ cd sorting
1452 $ cd sorting
1454 $ cat <<EOF >> .hg/hgrc
1453 $ cat <<EOF >> .hg/hgrc
1455 > [ui]
1454 > [ui]
1456 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1455 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1457 > [templatealias]
1456 > [templatealias]
1458 > p5(s) = pad(s, 5)
1457 > p5(s) = pad(s, 5)
1459 > EOF
1458 > EOF
1460 $ hg branch -qf b12
1459 $ hg branch -qf b12
1461 $ hg ci -m m111 -u u112 -d '111 10800'
1460 $ hg ci -m m111 -u u112 -d '111 10800'
1462 $ hg branch -qf b11
1461 $ hg branch -qf b11
1463 $ hg ci -m m12 -u u111 -d '112 7200'
1462 $ hg ci -m m12 -u u111 -d '112 7200'
1464 $ hg branch -qf b111
1463 $ hg branch -qf b111
1465 $ hg ci -m m11 -u u12 -d '111 3600'
1464 $ hg ci -m m11 -u u12 -d '111 3600'
1466 $ hg branch -qf b112
1465 $ hg branch -qf b112
1467 $ hg ci -m m111 -u u11 -d '120 0'
1466 $ hg ci -m m111 -u u11 -d '120 0'
1468 $ hg branch -qf b111
1467 $ hg branch -qf b111
1469 $ hg ci -m m112 -u u111 -d '110 14400'
1468 $ hg ci -m m112 -u u111 -d '110 14400'
1470 created new head
1469 created new head
1471
1470
1472 compare revisions (has fast path):
1471 compare revisions (has fast path):
1473
1472
1474 $ hg log -r 'sort(all(), rev)'
1473 $ hg log -r 'sort(all(), rev)'
1475 0 b12 m111 u112 111 10800
1474 0 b12 m111 u112 111 10800
1476 1 b11 m12 u111 112 7200
1475 1 b11 m12 u111 112 7200
1477 2 b111 m11 u12 111 3600
1476 2 b111 m11 u12 111 3600
1478 3 b112 m111 u11 120 0
1477 3 b112 m111 u11 120 0
1479 4 b111 m112 u111 110 14400
1478 4 b111 m112 u111 110 14400
1480
1479
1481 $ hg log -r 'sort(all(), -rev)'
1480 $ hg log -r 'sort(all(), -rev)'
1482 4 b111 m112 u111 110 14400
1481 4 b111 m112 u111 110 14400
1483 3 b112 m111 u11 120 0
1482 3 b112 m111 u11 120 0
1484 2 b111 m11 u12 111 3600
1483 2 b111 m11 u12 111 3600
1485 1 b11 m12 u111 112 7200
1484 1 b11 m12 u111 112 7200
1486 0 b12 m111 u112 111 10800
1485 0 b12 m111 u112 111 10800
1487
1486
1488 compare variable-length strings (issue5218):
1487 compare variable-length strings (issue5218):
1489
1488
1490 $ hg log -r 'sort(all(), branch)'
1489 $ hg log -r 'sort(all(), branch)'
1491 1 b11 m12 u111 112 7200
1490 1 b11 m12 u111 112 7200
1492 2 b111 m11 u12 111 3600
1491 2 b111 m11 u12 111 3600
1493 4 b111 m112 u111 110 14400
1492 4 b111 m112 u111 110 14400
1494 3 b112 m111 u11 120 0
1493 3 b112 m111 u11 120 0
1495 0 b12 m111 u112 111 10800
1494 0 b12 m111 u112 111 10800
1496
1495
1497 $ hg log -r 'sort(all(), -branch)'
1496 $ hg log -r 'sort(all(), -branch)'
1498 0 b12 m111 u112 111 10800
1497 0 b12 m111 u112 111 10800
1499 3 b112 m111 u11 120 0
1498 3 b112 m111 u11 120 0
1500 2 b111 m11 u12 111 3600
1499 2 b111 m11 u12 111 3600
1501 4 b111 m112 u111 110 14400
1500 4 b111 m112 u111 110 14400
1502 1 b11 m12 u111 112 7200
1501 1 b11 m12 u111 112 7200
1503
1502
1504 $ hg log -r 'sort(all(), desc)'
1503 $ hg log -r 'sort(all(), desc)'
1505 2 b111 m11 u12 111 3600
1504 2 b111 m11 u12 111 3600
1506 0 b12 m111 u112 111 10800
1505 0 b12 m111 u112 111 10800
1507 3 b112 m111 u11 120 0
1506 3 b112 m111 u11 120 0
1508 4 b111 m112 u111 110 14400
1507 4 b111 m112 u111 110 14400
1509 1 b11 m12 u111 112 7200
1508 1 b11 m12 u111 112 7200
1510
1509
1511 $ hg log -r 'sort(all(), -desc)'
1510 $ hg log -r 'sort(all(), -desc)'
1512 1 b11 m12 u111 112 7200
1511 1 b11 m12 u111 112 7200
1513 4 b111 m112 u111 110 14400
1512 4 b111 m112 u111 110 14400
1514 0 b12 m111 u112 111 10800
1513 0 b12 m111 u112 111 10800
1515 3 b112 m111 u11 120 0
1514 3 b112 m111 u11 120 0
1516 2 b111 m11 u12 111 3600
1515 2 b111 m11 u12 111 3600
1517
1516
1518 $ hg log -r 'sort(all(), user)'
1517 $ hg log -r 'sort(all(), user)'
1519 3 b112 m111 u11 120 0
1518 3 b112 m111 u11 120 0
1520 1 b11 m12 u111 112 7200
1519 1 b11 m12 u111 112 7200
1521 4 b111 m112 u111 110 14400
1520 4 b111 m112 u111 110 14400
1522 0 b12 m111 u112 111 10800
1521 0 b12 m111 u112 111 10800
1523 2 b111 m11 u12 111 3600
1522 2 b111 m11 u12 111 3600
1524
1523
1525 $ hg log -r 'sort(all(), -user)'
1524 $ hg log -r 'sort(all(), -user)'
1526 2 b111 m11 u12 111 3600
1525 2 b111 m11 u12 111 3600
1527 0 b12 m111 u112 111 10800
1526 0 b12 m111 u112 111 10800
1528 1 b11 m12 u111 112 7200
1527 1 b11 m12 u111 112 7200
1529 4 b111 m112 u111 110 14400
1528 4 b111 m112 u111 110 14400
1530 3 b112 m111 u11 120 0
1529 3 b112 m111 u11 120 0
1531
1530
1532 compare dates (tz offset should have no effect):
1531 compare dates (tz offset should have no effect):
1533
1532
1534 $ hg log -r 'sort(all(), date)'
1533 $ hg log -r 'sort(all(), date)'
1535 4 b111 m112 u111 110 14400
1534 4 b111 m112 u111 110 14400
1536 0 b12 m111 u112 111 10800
1535 0 b12 m111 u112 111 10800
1537 2 b111 m11 u12 111 3600
1536 2 b111 m11 u12 111 3600
1538 1 b11 m12 u111 112 7200
1537 1 b11 m12 u111 112 7200
1539 3 b112 m111 u11 120 0
1538 3 b112 m111 u11 120 0
1540
1539
1541 $ hg log -r 'sort(all(), -date)'
1540 $ hg log -r 'sort(all(), -date)'
1542 3 b112 m111 u11 120 0
1541 3 b112 m111 u11 120 0
1543 1 b11 m12 u111 112 7200
1542 1 b11 m12 u111 112 7200
1544 0 b12 m111 u112 111 10800
1543 0 b12 m111 u112 111 10800
1545 2 b111 m11 u12 111 3600
1544 2 b111 m11 u12 111 3600
1546 4 b111 m112 u111 110 14400
1545 4 b111 m112 u111 110 14400
1547
1546
1548 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
1547 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
1549 because '-k' reverses the comparison, not the list itself:
1548 because '-k' reverses the comparison, not the list itself:
1550
1549
1551 $ hg log -r 'sort(0 + 2, date)'
1550 $ hg log -r 'sort(0 + 2, date)'
1552 0 b12 m111 u112 111 10800
1551 0 b12 m111 u112 111 10800
1553 2 b111 m11 u12 111 3600
1552 2 b111 m11 u12 111 3600
1554
1553
1555 $ hg log -r 'sort(0 + 2, -date)'
1554 $ hg log -r 'sort(0 + 2, -date)'
1556 0 b12 m111 u112 111 10800
1555 0 b12 m111 u112 111 10800
1557 2 b111 m11 u12 111 3600
1556 2 b111 m11 u12 111 3600
1558
1557
1559 $ hg log -r 'reverse(sort(0 + 2, date))'
1558 $ hg log -r 'reverse(sort(0 + 2, date))'
1560 2 b111 m11 u12 111 3600
1559 2 b111 m11 u12 111 3600
1561 0 b12 m111 u112 111 10800
1560 0 b12 m111 u112 111 10800
1562
1561
1563 sort by multiple keys:
1562 sort by multiple keys:
1564
1563
1565 $ hg log -r 'sort(all(), "branch -rev")'
1564 $ hg log -r 'sort(all(), "branch -rev")'
1566 1 b11 m12 u111 112 7200
1565 1 b11 m12 u111 112 7200
1567 4 b111 m112 u111 110 14400
1566 4 b111 m112 u111 110 14400
1568 2 b111 m11 u12 111 3600
1567 2 b111 m11 u12 111 3600
1569 3 b112 m111 u11 120 0
1568 3 b112 m111 u11 120 0
1570 0 b12 m111 u112 111 10800
1569 0 b12 m111 u112 111 10800
1571
1570
1572 $ hg log -r 'sort(all(), "-desc -date")'
1571 $ hg log -r 'sort(all(), "-desc -date")'
1573 1 b11 m12 u111 112 7200
1572 1 b11 m12 u111 112 7200
1574 4 b111 m112 u111 110 14400
1573 4 b111 m112 u111 110 14400
1575 3 b112 m111 u11 120 0
1574 3 b112 m111 u11 120 0
1576 0 b12 m111 u112 111 10800
1575 0 b12 m111 u112 111 10800
1577 2 b111 m11 u12 111 3600
1576 2 b111 m11 u12 111 3600
1578
1577
1579 $ hg log -r 'sort(all(), "user -branch date rev")'
1578 $ hg log -r 'sort(all(), "user -branch date rev")'
1580 3 b112 m111 u11 120 0
1579 3 b112 m111 u11 120 0
1581 4 b111 m112 u111 110 14400
1580 4 b111 m112 u111 110 14400
1582 1 b11 m12 u111 112 7200
1581 1 b11 m12 u111 112 7200
1583 0 b12 m111 u112 111 10800
1582 0 b12 m111 u112 111 10800
1584 2 b111 m11 u12 111 3600
1583 2 b111 m11 u12 111 3600
1585
1584
1586 toposort prioritises graph branches
1585 toposort prioritises graph branches
1587
1586
1588 $ hg up 2
1587 $ hg up 2
1589 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1588 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1590 $ touch a
1589 $ touch a
1591 $ hg addremove
1590 $ hg addremove
1592 adding a
1591 adding a
1593 $ hg ci -m 't1' -u 'tu' -d '130 0'
1592 $ hg ci -m 't1' -u 'tu' -d '130 0'
1594 created new head
1593 created new head
1595 $ echo 'a' >> a
1594 $ echo 'a' >> a
1596 $ hg ci -m 't2' -u 'tu' -d '130 0'
1595 $ hg ci -m 't2' -u 'tu' -d '130 0'
1597 $ hg book book1
1596 $ hg book book1
1598 $ hg up 4
1597 $ hg up 4
1599 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1598 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1600 (leaving bookmark book1)
1599 (leaving bookmark book1)
1601 $ touch a
1600 $ touch a
1602 $ hg addremove
1601 $ hg addremove
1603 adding a
1602 adding a
1604 $ hg ci -m 't3' -u 'tu' -d '130 0'
1603 $ hg ci -m 't3' -u 'tu' -d '130 0'
1605
1604
1606 $ hg log -r 'sort(all(), topo)'
1605 $ hg log -r 'sort(all(), topo)'
1607 7 b111 t3 tu 130 0
1606 7 b111 t3 tu 130 0
1608 4 b111 m112 u111 110 14400
1607 4 b111 m112 u111 110 14400
1609 3 b112 m111 u11 120 0
1608 3 b112 m111 u11 120 0
1610 6 b111 t2 tu 130 0
1609 6 b111 t2 tu 130 0
1611 5 b111 t1 tu 130 0
1610 5 b111 t1 tu 130 0
1612 2 b111 m11 u12 111 3600
1611 2 b111 m11 u12 111 3600
1613 1 b11 m12 u111 112 7200
1612 1 b11 m12 u111 112 7200
1614 0 b12 m111 u112 111 10800
1613 0 b12 m111 u112 111 10800
1615
1614
1616 $ hg log -r 'sort(all(), -topo)'
1615 $ hg log -r 'sort(all(), -topo)'
1617 0 b12 m111 u112 111 10800
1616 0 b12 m111 u112 111 10800
1618 1 b11 m12 u111 112 7200
1617 1 b11 m12 u111 112 7200
1619 2 b111 m11 u12 111 3600
1618 2 b111 m11 u12 111 3600
1620 5 b111 t1 tu 130 0
1619 5 b111 t1 tu 130 0
1621 6 b111 t2 tu 130 0
1620 6 b111 t2 tu 130 0
1622 3 b112 m111 u11 120 0
1621 3 b112 m111 u11 120 0
1623 4 b111 m112 u111 110 14400
1622 4 b111 m112 u111 110 14400
1624 7 b111 t3 tu 130 0
1623 7 b111 t3 tu 130 0
1625
1624
1626 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
1625 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
1627 6 b111 t2 tu 130 0
1626 6 b111 t2 tu 130 0
1628 5 b111 t1 tu 130 0
1627 5 b111 t1 tu 130 0
1629 7 b111 t3 tu 130 0
1628 7 b111 t3 tu 130 0
1630 4 b111 m112 u111 110 14400
1629 4 b111 m112 u111 110 14400
1631 3 b112 m111 u11 120 0
1630 3 b112 m111 u11 120 0
1632 2 b111 m11 u12 111 3600
1631 2 b111 m11 u12 111 3600
1633 1 b11 m12 u111 112 7200
1632 1 b11 m12 u111 112 7200
1634 0 b12 m111 u112 111 10800
1633 0 b12 m111 u112 111 10800
1635
1634
1636 topographical sorting can't be combined with other sort keys, and you can't
1635 topographical sorting can't be combined with other sort keys, and you can't
1637 use the topo.firstbranch option when topo sort is not active:
1636 use the topo.firstbranch option when topo sort is not active:
1638
1637
1639 $ hg log -r 'sort(all(), "topo user")'
1638 $ hg log -r 'sort(all(), "topo user")'
1640 hg: parse error: topo sort order cannot be combined with other sort keys
1639 hg: parse error: topo sort order cannot be combined with other sort keys
1641 [255]
1640 [255]
1642
1641
1643 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
1642 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
1644 hg: parse error: topo.firstbranch can only be used when using the topo sort key
1643 hg: parse error: topo.firstbranch can only be used when using the topo sort key
1645 [255]
1644 [255]
1646
1645
1647 $ cd ..
1646 $ cd ..
1648 $ cd repo
1647 $ cd repo
1649
1648
1650 test subtracting something from an addset
1649 test subtracting something from an addset
1651
1650
1652 $ log '(outgoing() or removes(a)) - removes(a)'
1651 $ log '(outgoing() or removes(a)) - removes(a)'
1653 8
1652 8
1654 9
1653 9
1655
1654
1656 test intersecting something with an addset
1655 test intersecting something with an addset
1657
1656
1658 $ log 'parents(outgoing() or removes(a))'
1657 $ log 'parents(outgoing() or removes(a))'
1659 1
1658 1
1660 4
1659 4
1661 5
1660 5
1662 8
1661 8
1663
1662
1664 test that `or` operation combines elements in the right order:
1663 test that `or` operation combines elements in the right order:
1665
1664
1666 $ log '3:4 or 2:5'
1665 $ log '3:4 or 2:5'
1667 3
1666 3
1668 4
1667 4
1669 2
1668 2
1670 5
1669 5
1671 $ log '3:4 or 5:2'
1670 $ log '3:4 or 5:2'
1672 3
1671 3
1673 4
1672 4
1674 5
1673 5
1675 2
1674 2
1676 $ log 'sort(3:4 or 2:5)'
1675 $ log 'sort(3:4 or 2:5)'
1677 2
1676 2
1678 3
1677 3
1679 4
1678 4
1680 5
1679 5
1681 $ log 'sort(3:4 or 5:2)'
1680 $ log 'sort(3:4 or 5:2)'
1682 2
1681 2
1683 3
1682 3
1684 4
1683 4
1685 5
1684 5
1686
1685
1687 test that more than one `-r`s are combined in the right order and deduplicated:
1686 test that more than one `-r`s are combined in the right order and deduplicated:
1688
1687
1689 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
1688 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
1690 3
1689 3
1691 4
1690 4
1692 5
1691 5
1693 2
1692 2
1694 0
1693 0
1695 1
1694 1
1696
1695
1697 test that `or` operation skips duplicated revisions from right-hand side
1696 test that `or` operation skips duplicated revisions from right-hand side
1698
1697
1699 $ try 'reverse(1::5) or ancestors(4)'
1698 $ try 'reverse(1::5) or ancestors(4)'
1700 (or
1699 (or
1701 (func
1700 (func
1702 ('symbol', 'reverse')
1701 ('symbol', 'reverse')
1703 (dagrange
1702 (dagrange
1704 ('symbol', '1')
1703 ('symbol', '1')
1705 ('symbol', '5')))
1704 ('symbol', '5')))
1706 (func
1705 (func
1707 ('symbol', 'ancestors')
1706 ('symbol', 'ancestors')
1708 ('symbol', '4')))
1707 ('symbol', '4')))
1709 * set:
1708 * set:
1710 <addset
1709 <addset
1711 <baseset- [1, 3, 5]>,
1710 <baseset- [1, 3, 5]>,
1712 <generatorset+>>
1711 <generatorset+>>
1713 5
1712 5
1714 3
1713 3
1715 1
1714 1
1716 0
1715 0
1717 2
1716 2
1718 4
1717 4
1719 $ try 'sort(ancestors(4) or reverse(1::5))'
1718 $ try 'sort(ancestors(4) or reverse(1::5))'
1720 (func
1719 (func
1721 ('symbol', 'sort')
1720 ('symbol', 'sort')
1722 (or
1721 (or
1723 (func
1722 (func
1724 ('symbol', 'ancestors')
1723 ('symbol', 'ancestors')
1725 ('symbol', '4'))
1724 ('symbol', '4'))
1726 (func
1725 (func
1727 ('symbol', 'reverse')
1726 ('symbol', 'reverse')
1728 (dagrange
1727 (dagrange
1729 ('symbol', '1')
1728 ('symbol', '1')
1730 ('symbol', '5')))))
1729 ('symbol', '5')))))
1731 * set:
1730 * set:
1732 <addset+
1731 <addset+
1733 <generatorset+>,
1732 <generatorset+>,
1734 <baseset- [1, 3, 5]>>
1733 <baseset- [1, 3, 5]>>
1735 0
1734 0
1736 1
1735 1
1737 2
1736 2
1738 3
1737 3
1739 4
1738 4
1740 5
1739 5
1741
1740
1742 test optimization of trivial `or` operation
1741 test optimization of trivial `or` operation
1743
1742
1744 $ try --optimize '0|(1)|"2"|-2|tip|null'
1743 $ try --optimize '0|(1)|"2"|-2|tip|null'
1745 (or
1744 (or
1746 ('symbol', '0')
1745 ('symbol', '0')
1747 (group
1746 (group
1748 ('symbol', '1'))
1747 ('symbol', '1'))
1749 ('string', '2')
1748 ('string', '2')
1750 (negate
1749 (negate
1751 ('symbol', '2'))
1750 ('symbol', '2'))
1752 ('symbol', 'tip')
1751 ('symbol', 'tip')
1753 ('symbol', 'null'))
1752 ('symbol', 'null'))
1754 * optimized:
1753 * optimized:
1755 (func
1754 (func
1756 ('symbol', '_list')
1755 ('symbol', '_list')
1757 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
1756 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
1758 * set:
1757 * set:
1759 <baseset [0, 1, 2, 8, 9, -1]>
1758 <baseset [0, 1, 2, 8, 9, -1]>
1760 0
1759 0
1761 1
1760 1
1762 2
1761 2
1763 8
1762 8
1764 9
1763 9
1765 -1
1764 -1
1766
1765
1767 $ try --optimize '0|1|2:3'
1766 $ try --optimize '0|1|2:3'
1768 (or
1767 (or
1769 ('symbol', '0')
1768 ('symbol', '0')
1770 ('symbol', '1')
1769 ('symbol', '1')
1771 (range
1770 (range
1772 ('symbol', '2')
1771 ('symbol', '2')
1773 ('symbol', '3')))
1772 ('symbol', '3')))
1774 * optimized:
1773 * optimized:
1775 (or
1774 (or
1776 (func
1775 (func
1777 ('symbol', '_list')
1776 ('symbol', '_list')
1778 ('string', '0\x001'))
1777 ('string', '0\x001'))
1779 (range
1778 (range
1780 ('symbol', '2')
1779 ('symbol', '2')
1781 ('symbol', '3')))
1780 ('symbol', '3')))
1782 * set:
1781 * set:
1783 <addset
1782 <addset
1784 <baseset [0, 1]>,
1783 <baseset [0, 1]>,
1785 <spanset+ 2:3>>
1784 <spanset+ 2:3>>
1786 0
1785 0
1787 1
1786 1
1788 2
1787 2
1789 3
1788 3
1790
1789
1791 $ try --optimize '0:1|2|3:4|5|6'
1790 $ try --optimize '0:1|2|3:4|5|6'
1792 (or
1791 (or
1793 (range
1792 (range
1794 ('symbol', '0')
1793 ('symbol', '0')
1795 ('symbol', '1'))
1794 ('symbol', '1'))
1796 ('symbol', '2')
1795 ('symbol', '2')
1797 (range
1796 (range
1798 ('symbol', '3')
1797 ('symbol', '3')
1799 ('symbol', '4'))
1798 ('symbol', '4'))
1800 ('symbol', '5')
1799 ('symbol', '5')
1801 ('symbol', '6'))
1800 ('symbol', '6'))
1802 * optimized:
1801 * optimized:
1803 (or
1802 (or
1804 (range
1803 (range
1805 ('symbol', '0')
1804 ('symbol', '0')
1806 ('symbol', '1'))
1805 ('symbol', '1'))
1807 ('symbol', '2')
1806 ('symbol', '2')
1808 (range
1807 (range
1809 ('symbol', '3')
1808 ('symbol', '3')
1810 ('symbol', '4'))
1809 ('symbol', '4'))
1811 (func
1810 (func
1812 ('symbol', '_list')
1811 ('symbol', '_list')
1813 ('string', '5\x006')))
1812 ('string', '5\x006')))
1814 * set:
1813 * set:
1815 <addset
1814 <addset
1816 <addset
1815 <addset
1817 <spanset+ 0:1>,
1816 <spanset+ 0:1>,
1818 <baseset [2]>>,
1817 <baseset [2]>>,
1819 <addset
1818 <addset
1820 <spanset+ 3:4>,
1819 <spanset+ 3:4>,
1821 <baseset [5, 6]>>>
1820 <baseset [5, 6]>>>
1822 0
1821 0
1823 1
1822 1
1824 2
1823 2
1825 3
1824 3
1826 4
1825 4
1827 5
1826 5
1828 6
1827 6
1829
1828
1830 test that `_list` should be narrowed by provided `subset`
1829 test that `_list` should be narrowed by provided `subset`
1831
1830
1832 $ log '0:2 and (null|1|2|3)'
1831 $ log '0:2 and (null|1|2|3)'
1833 1
1832 1
1834 2
1833 2
1835
1834
1836 test that `_list` should remove duplicates
1835 test that `_list` should remove duplicates
1837
1836
1838 $ log '0|1|2|1|2|-1|tip'
1837 $ log '0|1|2|1|2|-1|tip'
1839 0
1838 0
1840 1
1839 1
1841 2
1840 2
1842 9
1841 9
1843
1842
1844 test unknown revision in `_list`
1843 test unknown revision in `_list`
1845
1844
1846 $ log '0|unknown'
1845 $ log '0|unknown'
1847 abort: unknown revision 'unknown'!
1846 abort: unknown revision 'unknown'!
1848 [255]
1847 [255]
1849
1848
1850 test integer range in `_list`
1849 test integer range in `_list`
1851
1850
1852 $ log '-1|-10'
1851 $ log '-1|-10'
1853 9
1852 9
1854 0
1853 0
1855
1854
1856 $ log '-10|-11'
1855 $ log '-10|-11'
1857 abort: unknown revision '-11'!
1856 abort: unknown revision '-11'!
1858 [255]
1857 [255]
1859
1858
1860 $ log '9|10'
1859 $ log '9|10'
1861 abort: unknown revision '10'!
1860 abort: unknown revision '10'!
1862 [255]
1861 [255]
1863
1862
1864 test '0000' != '0' in `_list`
1863 test '0000' != '0' in `_list`
1865
1864
1866 $ log '0|0000'
1865 $ log '0|0000'
1867 0
1866 0
1868 -1
1867 -1
1869
1868
1870 test ',' in `_list`
1869 test ',' in `_list`
1871 $ log '0,1'
1870 $ log '0,1'
1872 hg: parse error: can't use a list in this context
1871 hg: parse error: can't use a list in this context
1873 (see hg help "revsets.x or y")
1872 (see hg help "revsets.x or y")
1874 [255]
1873 [255]
1875 $ try '0,1,2'
1874 $ try '0,1,2'
1876 (list
1875 (list
1877 ('symbol', '0')
1876 ('symbol', '0')
1878 ('symbol', '1')
1877 ('symbol', '1')
1879 ('symbol', '2'))
1878 ('symbol', '2'))
1880 hg: parse error: can't use a list in this context
1879 hg: parse error: can't use a list in this context
1881 (see hg help "revsets.x or y")
1880 (see hg help "revsets.x or y")
1882 [255]
1881 [255]
1883
1882
1884 test that chained `or` operations make balanced addsets
1883 test that chained `or` operations make balanced addsets
1885
1884
1886 $ try '0:1|1:2|2:3|3:4|4:5'
1885 $ try '0:1|1:2|2:3|3:4|4:5'
1887 (or
1886 (or
1888 (range
1887 (range
1889 ('symbol', '0')
1888 ('symbol', '0')
1890 ('symbol', '1'))
1889 ('symbol', '1'))
1891 (range
1890 (range
1892 ('symbol', '1')
1891 ('symbol', '1')
1893 ('symbol', '2'))
1892 ('symbol', '2'))
1894 (range
1893 (range
1895 ('symbol', '2')
1894 ('symbol', '2')
1896 ('symbol', '3'))
1895 ('symbol', '3'))
1897 (range
1896 (range
1898 ('symbol', '3')
1897 ('symbol', '3')
1899 ('symbol', '4'))
1898 ('symbol', '4'))
1900 (range
1899 (range
1901 ('symbol', '4')
1900 ('symbol', '4')
1902 ('symbol', '5')))
1901 ('symbol', '5')))
1903 * set:
1902 * set:
1904 <addset
1903 <addset
1905 <addset
1904 <addset
1906 <spanset+ 0:1>,
1905 <spanset+ 0:1>,
1907 <spanset+ 1:2>>,
1906 <spanset+ 1:2>>,
1908 <addset
1907 <addset
1909 <spanset+ 2:3>,
1908 <spanset+ 2:3>,
1910 <addset
1909 <addset
1911 <spanset+ 3:4>,
1910 <spanset+ 3:4>,
1912 <spanset+ 4:5>>>>
1911 <spanset+ 4:5>>>>
1913 0
1912 0
1914 1
1913 1
1915 2
1914 2
1916 3
1915 3
1917 4
1916 4
1918 5
1917 5
1919
1918
1920 no crash by empty group "()" while optimizing `or` operations
1919 no crash by empty group "()" while optimizing `or` operations
1921
1920
1922 $ try --optimize '0|()'
1921 $ try --optimize '0|()'
1923 (or
1922 (or
1924 ('symbol', '0')
1923 ('symbol', '0')
1925 (group
1924 (group
1926 None))
1925 None))
1927 * optimized:
1926 * optimized:
1928 (or
1927 (or
1929 ('symbol', '0')
1928 ('symbol', '0')
1930 None)
1929 None)
1931 hg: parse error: missing argument
1930 hg: parse error: missing argument
1932 [255]
1931 [255]
1933
1932
1934 test that chained `or` operations never eat up stack (issue4624)
1933 test that chained `or` operations never eat up stack (issue4624)
1935 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1934 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1936
1935
1937 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
1936 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
1938 0
1937 0
1939 1
1938 1
1940
1939
1941 test that repeated `-r` options never eat up stack (issue4565)
1940 test that repeated `-r` options never eat up stack (issue4565)
1942 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1941 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1943
1942
1944 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1943 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1945 0
1944 0
1946 1
1945 1
1947
1946
1948 check that conversion to only works
1947 check that conversion to only works
1949 $ try --optimize '::3 - ::1'
1948 $ try --optimize '::3 - ::1'
1950 (minus
1949 (minus
1951 (dagrangepre
1950 (dagrangepre
1952 ('symbol', '3'))
1951 ('symbol', '3'))
1953 (dagrangepre
1952 (dagrangepre
1954 ('symbol', '1')))
1953 ('symbol', '1')))
1955 * optimized:
1954 * optimized:
1956 (func
1955 (func
1957 ('symbol', 'only')
1956 ('symbol', 'only')
1958 (list
1957 (list
1959 ('symbol', '3')
1958 ('symbol', '3')
1960 ('symbol', '1')))
1959 ('symbol', '1')))
1961 * set:
1960 * set:
1962 <baseset+ [3]>
1961 <baseset+ [3]>
1963 3
1962 3
1964 $ try --optimize 'ancestors(1) - ancestors(3)'
1963 $ try --optimize 'ancestors(1) - ancestors(3)'
1965 (minus
1964 (minus
1966 (func
1965 (func
1967 ('symbol', 'ancestors')
1966 ('symbol', 'ancestors')
1968 ('symbol', '1'))
1967 ('symbol', '1'))
1969 (func
1968 (func
1970 ('symbol', 'ancestors')
1969 ('symbol', 'ancestors')
1971 ('symbol', '3')))
1970 ('symbol', '3')))
1972 * optimized:
1971 * optimized:
1973 (func
1972 (func
1974 ('symbol', 'only')
1973 ('symbol', 'only')
1975 (list
1974 (list
1976 ('symbol', '1')
1975 ('symbol', '1')
1977 ('symbol', '3')))
1976 ('symbol', '3')))
1978 * set:
1977 * set:
1979 <baseset+ []>
1978 <baseset+ []>
1980 $ try --optimize 'not ::2 and ::6'
1979 $ try --optimize 'not ::2 and ::6'
1981 (and
1980 (and
1982 (not
1981 (not
1983 (dagrangepre
1982 (dagrangepre
1984 ('symbol', '2')))
1983 ('symbol', '2')))
1985 (dagrangepre
1984 (dagrangepre
1986 ('symbol', '6')))
1985 ('symbol', '6')))
1987 * optimized:
1986 * optimized:
1988 (func
1987 (func
1989 ('symbol', 'only')
1988 ('symbol', 'only')
1990 (list
1989 (list
1991 ('symbol', '6')
1990 ('symbol', '6')
1992 ('symbol', '2')))
1991 ('symbol', '2')))
1993 * set:
1992 * set:
1994 <baseset+ [3, 4, 5, 6]>
1993 <baseset+ [3, 4, 5, 6]>
1995 3
1994 3
1996 4
1995 4
1997 5
1996 5
1998 6
1997 6
1999 $ try --optimize 'ancestors(6) and not ancestors(4)'
1998 $ try --optimize 'ancestors(6) and not ancestors(4)'
2000 (and
1999 (and
2001 (func
2000 (func
2002 ('symbol', 'ancestors')
2001 ('symbol', 'ancestors')
2003 ('symbol', '6'))
2002 ('symbol', '6'))
2004 (not
2003 (not
2005 (func
2004 (func
2006 ('symbol', 'ancestors')
2005 ('symbol', 'ancestors')
2007 ('symbol', '4'))))
2006 ('symbol', '4'))))
2008 * optimized:
2007 * optimized:
2009 (func
2008 (func
2010 ('symbol', 'only')
2009 ('symbol', 'only')
2011 (list
2010 (list
2012 ('symbol', '6')
2011 ('symbol', '6')
2013 ('symbol', '4')))
2012 ('symbol', '4')))
2014 * set:
2013 * set:
2015 <baseset+ [3, 5, 6]>
2014 <baseset+ [3, 5, 6]>
2016 3
2015 3
2017 5
2016 5
2018 6
2017 6
2019
2018
2020 no crash by empty group "()" while optimizing to "only()"
2019 no crash by empty group "()" while optimizing to "only()"
2021
2020
2022 $ try --optimize '::1 and ()'
2021 $ try --optimize '::1 and ()'
2023 (and
2022 (and
2024 (dagrangepre
2023 (dagrangepre
2025 ('symbol', '1'))
2024 ('symbol', '1'))
2026 (group
2025 (group
2027 None))
2026 None))
2028 * optimized:
2027 * optimized:
2029 (and
2028 (and
2030 None
2029 None
2031 (func
2030 (func
2032 ('symbol', 'ancestors')
2031 ('symbol', 'ancestors')
2033 ('symbol', '1')))
2032 ('symbol', '1')))
2034 hg: parse error: missing argument
2033 hg: parse error: missing argument
2035 [255]
2034 [255]
2036
2035
2037 we can use patterns when searching for tags
2036 we can use patterns when searching for tags
2038
2037
2039 $ log 'tag("1..*")'
2038 $ log 'tag("1..*")'
2040 abort: tag '1..*' does not exist!
2039 abort: tag '1..*' does not exist!
2041 [255]
2040 [255]
2042 $ log 'tag("re:1..*")'
2041 $ log 'tag("re:1..*")'
2043 6
2042 6
2044 $ log 'tag("re:[0-9].[0-9]")'
2043 $ log 'tag("re:[0-9].[0-9]")'
2045 6
2044 6
2046 $ log 'tag("literal:1.0")'
2045 $ log 'tag("literal:1.0")'
2047 6
2046 6
2048 $ log 'tag("re:0..*")'
2047 $ log 'tag("re:0..*")'
2049
2048
2050 $ log 'tag(unknown)'
2049 $ log 'tag(unknown)'
2051 abort: tag 'unknown' does not exist!
2050 abort: tag 'unknown' does not exist!
2052 [255]
2051 [255]
2053 $ log 'tag("re:unknown")'
2052 $ log 'tag("re:unknown")'
2054 $ log 'present(tag("unknown"))'
2053 $ log 'present(tag("unknown"))'
2055 $ log 'present(tag("re:unknown"))'
2054 $ log 'present(tag("re:unknown"))'
2056 $ log 'branch(unknown)'
2055 $ log 'branch(unknown)'
2057 abort: unknown revision 'unknown'!
2056 abort: unknown revision 'unknown'!
2058 [255]
2057 [255]
2059 $ log 'branch("literal:unknown")'
2058 $ log 'branch("literal:unknown")'
2060 abort: branch 'unknown' does not exist!
2059 abort: branch 'unknown' does not exist!
2061 [255]
2060 [255]
2062 $ log 'branch("re:unknown")'
2061 $ log 'branch("re:unknown")'
2063 $ log 'present(branch("unknown"))'
2062 $ log 'present(branch("unknown"))'
2064 $ log 'present(branch("re:unknown"))'
2063 $ log 'present(branch("re:unknown"))'
2065 $ log 'user(bob)'
2064 $ log 'user(bob)'
2066 2
2065 2
2067
2066
2068 $ log '4::8'
2067 $ log '4::8'
2069 4
2068 4
2070 8
2069 8
2071 $ log '4:8'
2070 $ log '4:8'
2072 4
2071 4
2073 5
2072 5
2074 6
2073 6
2075 7
2074 7
2076 8
2075 8
2077
2076
2078 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2077 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2079 4
2078 4
2080 2
2079 2
2081 5
2080 5
2082
2081
2083 $ log 'not 0 and 0:2'
2082 $ log 'not 0 and 0:2'
2084 1
2083 1
2085 2
2084 2
2086 $ log 'not 1 and 0:2'
2085 $ log 'not 1 and 0:2'
2087 0
2086 0
2088 2
2087 2
2089 $ log 'not 2 and 0:2'
2088 $ log 'not 2 and 0:2'
2090 0
2089 0
2091 1
2090 1
2092 $ log '(1 and 2)::'
2091 $ log '(1 and 2)::'
2093 $ log '(1 and 2):'
2092 $ log '(1 and 2):'
2094 $ log '(1 and 2):3'
2093 $ log '(1 and 2):3'
2095 $ log 'sort(head(), -rev)'
2094 $ log 'sort(head(), -rev)'
2096 9
2095 9
2097 7
2096 7
2098 6
2097 6
2099 5
2098 5
2100 4
2099 4
2101 3
2100 3
2102 2
2101 2
2103 1
2102 1
2104 0
2103 0
2105 $ log '4::8 - 8'
2104 $ log '4::8 - 8'
2106 4
2105 4
2107
2106
2108 matching() should preserve the order of the input set:
2107 matching() should preserve the order of the input set:
2109
2108
2110 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2109 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2111 2
2110 2
2112 3
2111 3
2113 1
2112 1
2114
2113
2115 $ log 'named("unknown")'
2114 $ log 'named("unknown")'
2116 abort: namespace 'unknown' does not exist!
2115 abort: namespace 'unknown' does not exist!
2117 [255]
2116 [255]
2118 $ log 'named("re:unknown")'
2117 $ log 'named("re:unknown")'
2119 abort: no namespace exists that match 'unknown'!
2118 abort: no namespace exists that match 'unknown'!
2120 [255]
2119 [255]
2121 $ log 'present(named("unknown"))'
2120 $ log 'present(named("unknown"))'
2122 $ log 'present(named("re:unknown"))'
2121 $ log 'present(named("re:unknown"))'
2123
2122
2124 $ log 'tag()'
2123 $ log 'tag()'
2125 6
2124 6
2126 $ log 'named("tags")'
2125 $ log 'named("tags")'
2127 6
2126 6
2128
2127
2129 issue2437
2128 issue2437
2130
2129
2131 $ log '3 and p1(5)'
2130 $ log '3 and p1(5)'
2132 3
2131 3
2133 $ log '4 and p2(6)'
2132 $ log '4 and p2(6)'
2134 4
2133 4
2135 $ log '1 and parents(:2)'
2134 $ log '1 and parents(:2)'
2136 1
2135 1
2137 $ log '2 and children(1:)'
2136 $ log '2 and children(1:)'
2138 2
2137 2
2139 $ log 'roots(all()) or roots(all())'
2138 $ log 'roots(all()) or roots(all())'
2140 0
2139 0
2141 $ hg debugrevspec 'roots(all()) or roots(all())'
2140 $ hg debugrevspec 'roots(all()) or roots(all())'
2142 0
2141 0
2143 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2142 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2144 9
2143 9
2145 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2144 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2146 4
2145 4
2147
2146
2148 issue2654: report a parse error if the revset was not completely parsed
2147 issue2654: report a parse error if the revset was not completely parsed
2149
2148
2150 $ log '1 OR 2'
2149 $ log '1 OR 2'
2151 hg: parse error at 2: invalid token
2150 hg: parse error at 2: invalid token
2152 [255]
2151 [255]
2153
2152
2154 or operator should preserve ordering:
2153 or operator should preserve ordering:
2155 $ log 'reverse(2::4) or tip'
2154 $ log 'reverse(2::4) or tip'
2156 4
2155 4
2157 2
2156 2
2158 9
2157 9
2159
2158
2160 parentrevspec
2159 parentrevspec
2161
2160
2162 $ log 'merge()^0'
2161 $ log 'merge()^0'
2163 6
2162 6
2164 $ log 'merge()^'
2163 $ log 'merge()^'
2165 5
2164 5
2166 $ log 'merge()^1'
2165 $ log 'merge()^1'
2167 5
2166 5
2168 $ log 'merge()^2'
2167 $ log 'merge()^2'
2169 4
2168 4
2170 $ log 'merge()^^'
2169 $ log 'merge()^^'
2171 3
2170 3
2172 $ log 'merge()^1^'
2171 $ log 'merge()^1^'
2173 3
2172 3
2174 $ log 'merge()^^^'
2173 $ log 'merge()^^^'
2175 1
2174 1
2176
2175
2177 $ log 'merge()~0'
2176 $ log 'merge()~0'
2178 6
2177 6
2179 $ log 'merge()~1'
2178 $ log 'merge()~1'
2180 5
2179 5
2181 $ log 'merge()~2'
2180 $ log 'merge()~2'
2182 3
2181 3
2183 $ log 'merge()~2^1'
2182 $ log 'merge()~2^1'
2184 1
2183 1
2185 $ log 'merge()~3'
2184 $ log 'merge()~3'
2186 1
2185 1
2187
2186
2188 $ log '(-3:tip)^'
2187 $ log '(-3:tip)^'
2189 4
2188 4
2190 6
2189 6
2191 8
2190 8
2192
2191
2193 $ log 'tip^foo'
2192 $ log 'tip^foo'
2194 hg: parse error: ^ expects a number 0, 1, or 2
2193 hg: parse error: ^ expects a number 0, 1, or 2
2195 [255]
2194 [255]
2196
2195
2197 Bogus function gets suggestions
2196 Bogus function gets suggestions
2198 $ log 'add()'
2197 $ log 'add()'
2199 hg: parse error: unknown identifier: add
2198 hg: parse error: unknown identifier: add
2200 (did you mean adds?)
2199 (did you mean adds?)
2201 [255]
2200 [255]
2202 $ log 'added()'
2201 $ log 'added()'
2203 hg: parse error: unknown identifier: added
2202 hg: parse error: unknown identifier: added
2204 (did you mean adds?)
2203 (did you mean adds?)
2205 [255]
2204 [255]
2206 $ log 'remo()'
2205 $ log 'remo()'
2207 hg: parse error: unknown identifier: remo
2206 hg: parse error: unknown identifier: remo
2208 (did you mean one of remote, removes?)
2207 (did you mean one of remote, removes?)
2209 [255]
2208 [255]
2210 $ log 'babar()'
2209 $ log 'babar()'
2211 hg: parse error: unknown identifier: babar
2210 hg: parse error: unknown identifier: babar
2212 [255]
2211 [255]
2213
2212
2214 Bogus function with a similar internal name doesn't suggest the internal name
2213 Bogus function with a similar internal name doesn't suggest the internal name
2215 $ log 'matches()'
2214 $ log 'matches()'
2216 hg: parse error: unknown identifier: matches
2215 hg: parse error: unknown identifier: matches
2217 (did you mean matching?)
2216 (did you mean matching?)
2218 [255]
2217 [255]
2219
2218
2220 Undocumented functions aren't suggested as similar either
2219 Undocumented functions aren't suggested as similar either
2221 $ log 'wdir2()'
2220 $ log 'wdir2()'
2222 hg: parse error: unknown identifier: wdir2
2221 hg: parse error: unknown identifier: wdir2
2223 [255]
2222 [255]
2224
2223
2225 multiple revspecs
2224 multiple revspecs
2226
2225
2227 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2226 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2228 8
2227 8
2229 9
2228 9
2230 4
2229 4
2231 5
2230 5
2232 6
2231 6
2233 7
2232 7
2234
2233
2235 test usage in revpair (with "+")
2234 test usage in revpair (with "+")
2236
2235
2237 (real pair)
2236 (real pair)
2238
2237
2239 $ hg diff -r 'tip^^' -r 'tip'
2238 $ hg diff -r 'tip^^' -r 'tip'
2240 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2239 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2241 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2242 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2241 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2243 @@ -0,0 +1,1 @@
2242 @@ -0,0 +1,1 @@
2244 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2243 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2245 $ hg diff -r 'tip^^::tip'
2244 $ hg diff -r 'tip^^::tip'
2246 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2245 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2247 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2246 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2248 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2247 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2249 @@ -0,0 +1,1 @@
2248 @@ -0,0 +1,1 @@
2250 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2249 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2251
2250
2252 (single rev)
2251 (single rev)
2253
2252
2254 $ hg diff -r 'tip^' -r 'tip^'
2253 $ hg diff -r 'tip^' -r 'tip^'
2255 $ hg diff -r 'tip^:tip^'
2254 $ hg diff -r 'tip^:tip^'
2256
2255
2257 (single rev that does not looks like a range)
2256 (single rev that does not looks like a range)
2258
2257
2259 $ hg diff -r 'tip^::tip^ or tip^'
2258 $ hg diff -r 'tip^::tip^ or tip^'
2260 diff -r d5d0dcbdc4d9 .hgtags
2259 diff -r d5d0dcbdc4d9 .hgtags
2261 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2260 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2262 +++ b/.hgtags * (glob)
2261 +++ b/.hgtags * (glob)
2263 @@ -0,0 +1,1 @@
2262 @@ -0,0 +1,1 @@
2264 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2263 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2265 $ hg diff -r 'tip^ or tip^'
2264 $ hg diff -r 'tip^ or tip^'
2266 diff -r d5d0dcbdc4d9 .hgtags
2265 diff -r d5d0dcbdc4d9 .hgtags
2267 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2266 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2268 +++ b/.hgtags * (glob)
2267 +++ b/.hgtags * (glob)
2269 @@ -0,0 +1,1 @@
2268 @@ -0,0 +1,1 @@
2270 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2269 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2271
2270
2272 (no rev)
2271 (no rev)
2273
2272
2274 $ hg diff -r 'author("babar") or author("celeste")'
2273 $ hg diff -r 'author("babar") or author("celeste")'
2275 abort: empty revision range
2274 abort: empty revision range
2276 [255]
2275 [255]
2277
2276
2278 aliases:
2277 aliases:
2279
2278
2280 $ echo '[revsetalias]' >> .hg/hgrc
2279 $ echo '[revsetalias]' >> .hg/hgrc
2281 $ echo 'm = merge()' >> .hg/hgrc
2280 $ echo 'm = merge()' >> .hg/hgrc
2282 (revset aliases can override builtin revsets)
2281 (revset aliases can override builtin revsets)
2283 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2282 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2284 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2283 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2285 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2284 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2286 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2285 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2287 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2286 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2288
2287
2289 $ try m
2288 $ try m
2290 ('symbol', 'm')
2289 ('symbol', 'm')
2291 * expanded:
2290 * expanded:
2292 (func
2291 (func
2293 ('symbol', 'merge')
2292 ('symbol', 'merge')
2294 None)
2293 None)
2295 * set:
2294 * set:
2296 <filteredset
2295 <filteredset
2297 <fullreposet+ 0:9>,
2296 <fullreposet+ 0:9>,
2298 <merge>>
2297 <merge>>
2299 6
2298 6
2300
2299
2301 $ HGPLAIN=1
2300 $ HGPLAIN=1
2302 $ export HGPLAIN
2301 $ export HGPLAIN
2303 $ try m
2302 $ try m
2304 ('symbol', 'm')
2303 ('symbol', 'm')
2305 abort: unknown revision 'm'!
2304 abort: unknown revision 'm'!
2306 [255]
2305 [255]
2307
2306
2308 $ HGPLAINEXCEPT=revsetalias
2307 $ HGPLAINEXCEPT=revsetalias
2309 $ export HGPLAINEXCEPT
2308 $ export HGPLAINEXCEPT
2310 $ try m
2309 $ try m
2311 ('symbol', 'm')
2310 ('symbol', 'm')
2312 * expanded:
2311 * expanded:
2313 (func
2312 (func
2314 ('symbol', 'merge')
2313 ('symbol', 'merge')
2315 None)
2314 None)
2316 * set:
2315 * set:
2317 <filteredset
2316 <filteredset
2318 <fullreposet+ 0:9>,
2317 <fullreposet+ 0:9>,
2319 <merge>>
2318 <merge>>
2320 6
2319 6
2321
2320
2322 $ unset HGPLAIN
2321 $ unset HGPLAIN
2323 $ unset HGPLAINEXCEPT
2322 $ unset HGPLAINEXCEPT
2324
2323
2325 $ try 'p2(.)'
2324 $ try 'p2(.)'
2326 (func
2325 (func
2327 ('symbol', 'p2')
2326 ('symbol', 'p2')
2328 ('symbol', '.'))
2327 ('symbol', '.'))
2329 * expanded:
2328 * expanded:
2330 (func
2329 (func
2331 ('symbol', 'p1')
2330 ('symbol', 'p1')
2332 ('symbol', '.'))
2331 ('symbol', '.'))
2333 * set:
2332 * set:
2334 <baseset+ [8]>
2333 <baseset+ [8]>
2335 8
2334 8
2336
2335
2337 $ HGPLAIN=1
2336 $ HGPLAIN=1
2338 $ export HGPLAIN
2337 $ export HGPLAIN
2339 $ try 'p2(.)'
2338 $ try 'p2(.)'
2340 (func
2339 (func
2341 ('symbol', 'p2')
2340 ('symbol', 'p2')
2342 ('symbol', '.'))
2341 ('symbol', '.'))
2343 * set:
2342 * set:
2344 <baseset+ []>
2343 <baseset+ []>
2345
2344
2346 $ HGPLAINEXCEPT=revsetalias
2345 $ HGPLAINEXCEPT=revsetalias
2347 $ export HGPLAINEXCEPT
2346 $ export HGPLAINEXCEPT
2348 $ try 'p2(.)'
2347 $ try 'p2(.)'
2349 (func
2348 (func
2350 ('symbol', 'p2')
2349 ('symbol', 'p2')
2351 ('symbol', '.'))
2350 ('symbol', '.'))
2352 * expanded:
2351 * expanded:
2353 (func
2352 (func
2354 ('symbol', 'p1')
2353 ('symbol', 'p1')
2355 ('symbol', '.'))
2354 ('symbol', '.'))
2356 * set:
2355 * set:
2357 <baseset+ [8]>
2356 <baseset+ [8]>
2358 8
2357 8
2359
2358
2360 $ unset HGPLAIN
2359 $ unset HGPLAIN
2361 $ unset HGPLAINEXCEPT
2360 $ unset HGPLAINEXCEPT
2362
2361
2363 test alias recursion
2362 test alias recursion
2364
2363
2365 $ try sincem
2364 $ try sincem
2366 ('symbol', 'sincem')
2365 ('symbol', 'sincem')
2367 * expanded:
2366 * expanded:
2368 (func
2367 (func
2369 ('symbol', 'descendants')
2368 ('symbol', 'descendants')
2370 (func
2369 (func
2371 ('symbol', 'merge')
2370 ('symbol', 'merge')
2372 None))
2371 None))
2373 * set:
2372 * set:
2374 <addset+
2373 <addset+
2375 <filteredset
2374 <filteredset
2376 <fullreposet+ 0:9>,
2375 <fullreposet+ 0:9>,
2377 <merge>>,
2376 <merge>>,
2378 <generatorset+>>
2377 <generatorset+>>
2379 6
2378 6
2380 7
2379 7
2381
2380
2382 test infinite recursion
2381 test infinite recursion
2383
2382
2384 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2383 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2385 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2384 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2386 $ try recurse1
2385 $ try recurse1
2387 ('symbol', 'recurse1')
2386 ('symbol', 'recurse1')
2388 hg: parse error: infinite expansion of revset alias "recurse1" detected
2387 hg: parse error: infinite expansion of revset alias "recurse1" detected
2389 [255]
2388 [255]
2390
2389
2391 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2390 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2392 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2391 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2393 $ try "level2(level1(1, 2), 3)"
2392 $ try "level2(level1(1, 2), 3)"
2394 (func
2393 (func
2395 ('symbol', 'level2')
2394 ('symbol', 'level2')
2396 (list
2395 (list
2397 (func
2396 (func
2398 ('symbol', 'level1')
2397 ('symbol', 'level1')
2399 (list
2398 (list
2400 ('symbol', '1')
2399 ('symbol', '1')
2401 ('symbol', '2')))
2400 ('symbol', '2')))
2402 ('symbol', '3')))
2401 ('symbol', '3')))
2403 * expanded:
2402 * expanded:
2404 (or
2403 (or
2405 ('symbol', '3')
2404 ('symbol', '3')
2406 (or
2405 (or
2407 ('symbol', '1')
2406 ('symbol', '1')
2408 ('symbol', '2')))
2407 ('symbol', '2')))
2409 * set:
2408 * set:
2410 <addset
2409 <addset
2411 <baseset [3]>,
2410 <baseset [3]>,
2412 <baseset [1, 2]>>
2411 <baseset [1, 2]>>
2413 3
2412 3
2414 1
2413 1
2415 2
2414 2
2416
2415
2417 test nesting and variable passing
2416 test nesting and variable passing
2418
2417
2419 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
2418 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
2420 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
2419 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
2421 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
2420 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
2422 $ try 'nested(2:5)'
2421 $ try 'nested(2:5)'
2423 (func
2422 (func
2424 ('symbol', 'nested')
2423 ('symbol', 'nested')
2425 (range
2424 (range
2426 ('symbol', '2')
2425 ('symbol', '2')
2427 ('symbol', '5')))
2426 ('symbol', '5')))
2428 * expanded:
2427 * expanded:
2429 (func
2428 (func
2430 ('symbol', 'max')
2429 ('symbol', 'max')
2431 (range
2430 (range
2432 ('symbol', '2')
2431 ('symbol', '2')
2433 ('symbol', '5')))
2432 ('symbol', '5')))
2434 * set:
2433 * set:
2435 <baseset
2434 <baseset
2436 <max
2435 <max
2437 <fullreposet+ 0:9>,
2436 <fullreposet+ 0:9>,
2438 <spanset+ 2:5>>>
2437 <spanset+ 2:5>>>
2439 5
2438 5
2440
2439
2441 test chained `or` operations are flattened at parsing phase
2440 test chained `or` operations are flattened at parsing phase
2442
2441
2443 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
2442 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
2444 $ try 'chainedorops(0:1, 1:2, 2:3)'
2443 $ try 'chainedorops(0:1, 1:2, 2:3)'
2445 (func
2444 (func
2446 ('symbol', 'chainedorops')
2445 ('symbol', 'chainedorops')
2447 (list
2446 (list
2448 (range
2447 (range
2449 ('symbol', '0')
2448 ('symbol', '0')
2450 ('symbol', '1'))
2449 ('symbol', '1'))
2451 (range
2450 (range
2452 ('symbol', '1')
2451 ('symbol', '1')
2453 ('symbol', '2'))
2452 ('symbol', '2'))
2454 (range
2453 (range
2455 ('symbol', '2')
2454 ('symbol', '2')
2456 ('symbol', '3'))))
2455 ('symbol', '3'))))
2457 * expanded:
2456 * expanded:
2458 (or
2457 (or
2459 (range
2458 (range
2460 ('symbol', '0')
2459 ('symbol', '0')
2461 ('symbol', '1'))
2460 ('symbol', '1'))
2462 (range
2461 (range
2463 ('symbol', '1')
2462 ('symbol', '1')
2464 ('symbol', '2'))
2463 ('symbol', '2'))
2465 (range
2464 (range
2466 ('symbol', '2')
2465 ('symbol', '2')
2467 ('symbol', '3')))
2466 ('symbol', '3')))
2468 * set:
2467 * set:
2469 <addset
2468 <addset
2470 <spanset+ 0:1>,
2469 <spanset+ 0:1>,
2471 <addset
2470 <addset
2472 <spanset+ 1:2>,
2471 <spanset+ 1:2>,
2473 <spanset+ 2:3>>>
2472 <spanset+ 2:3>>>
2474 0
2473 0
2475 1
2474 1
2476 2
2475 2
2477 3
2476 3
2478
2477
2479 test variable isolation, variable placeholders are rewritten as string
2478 test variable isolation, variable placeholders are rewritten as string
2480 then parsed and matched again as string. Check they do not leak too
2479 then parsed and matched again as string. Check they do not leak too
2481 far away.
2480 far away.
2482
2481
2483 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
2482 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
2484 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
2483 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
2485 $ try 'callinjection(2:5)'
2484 $ try 'callinjection(2:5)'
2486 (func
2485 (func
2487 ('symbol', 'callinjection')
2486 ('symbol', 'callinjection')
2488 (range
2487 (range
2489 ('symbol', '2')
2488 ('symbol', '2')
2490 ('symbol', '5')))
2489 ('symbol', '5')))
2491 * expanded:
2490 * expanded:
2492 (func
2491 (func
2493 ('symbol', 'descendants')
2492 ('symbol', 'descendants')
2494 (func
2493 (func
2495 ('symbol', 'max')
2494 ('symbol', 'max')
2496 ('string', '$1')))
2495 ('string', '$1')))
2497 abort: unknown revision '$1'!
2496 abort: unknown revision '$1'!
2498 [255]
2497 [255]
2499
2498
2500 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
2499 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
2501 but 'all()' should never be substituded to '0()'.
2500 but 'all()' should never be substituded to '0()'.
2502
2501
2503 $ echo 'universe = all()' >> .hg/hgrc
2502 $ echo 'universe = all()' >> .hg/hgrc
2504 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
2503 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
2505 $ try 'shadowall(0)'
2504 $ try 'shadowall(0)'
2506 (func
2505 (func
2507 ('symbol', 'shadowall')
2506 ('symbol', 'shadowall')
2508 ('symbol', '0'))
2507 ('symbol', '0'))
2509 * expanded:
2508 * expanded:
2510 (and
2509 (and
2511 ('symbol', '0')
2510 ('symbol', '0')
2512 (func
2511 (func
2513 ('symbol', 'all')
2512 ('symbol', 'all')
2514 None))
2513 None))
2515 * set:
2514 * set:
2516 <filteredset
2515 <filteredset
2517 <baseset [0]>,
2516 <baseset [0]>,
2518 <spanset+ 0:9>>
2517 <spanset+ 0:9>>
2519 0
2518 0
2520
2519
2521 test unknown reference:
2520 test unknown reference:
2522
2521
2523 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
2522 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
2524 (func
2523 (func
2525 ('symbol', 'unknownref')
2524 ('symbol', 'unknownref')
2526 ('symbol', '0'))
2525 ('symbol', '0'))
2527 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
2526 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
2528 [255]
2527 [255]
2529
2528
2530 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
2529 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
2531 ('symbol', 'tip')
2530 ('symbol', 'tip')
2532 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
2531 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
2533 * set:
2532 * set:
2534 <baseset [9]>
2533 <baseset [9]>
2535 9
2534 9
2536
2535
2537 $ try 'tip'
2536 $ try 'tip'
2538 ('symbol', 'tip')
2537 ('symbol', 'tip')
2539 * set:
2538 * set:
2540 <baseset [9]>
2539 <baseset [9]>
2541 9
2540 9
2542
2541
2543 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
2542 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
2544 ('symbol', 'tip')
2543 ('symbol', 'tip')
2545 warning: bad declaration of revset alias "bad name": at 4: invalid token
2544 warning: bad declaration of revset alias "bad name": at 4: invalid token
2546 * set:
2545 * set:
2547 <baseset [9]>
2546 <baseset [9]>
2548 9
2547 9
2549 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
2548 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
2550 $ try 'strictreplacing("foo", tip)'
2549 $ try 'strictreplacing("foo", tip)'
2551 (func
2550 (func
2552 ('symbol', 'strictreplacing')
2551 ('symbol', 'strictreplacing')
2553 (list
2552 (list
2554 ('string', 'foo')
2553 ('string', 'foo')
2555 ('symbol', 'tip')))
2554 ('symbol', 'tip')))
2556 * expanded:
2555 * expanded:
2557 (or
2556 (or
2558 ('symbol', 'tip')
2557 ('symbol', 'tip')
2559 (func
2558 (func
2560 ('symbol', 'desc')
2559 ('symbol', 'desc')
2561 ('string', '$1')))
2560 ('string', '$1')))
2562 * set:
2561 * set:
2563 <addset
2562 <addset
2564 <baseset [9]>,
2563 <baseset [9]>,
2565 <filteredset
2564 <filteredset
2566 <fullreposet+ 0:9>,
2565 <fullreposet+ 0:9>,
2567 <desc '$1'>>>
2566 <desc '$1'>>>
2568 9
2567 9
2569
2568
2570 $ try 'd(2:5)'
2569 $ try 'd(2:5)'
2571 (func
2570 (func
2572 ('symbol', 'd')
2571 ('symbol', 'd')
2573 (range
2572 (range
2574 ('symbol', '2')
2573 ('symbol', '2')
2575 ('symbol', '5')))
2574 ('symbol', '5')))
2576 * expanded:
2575 * expanded:
2577 (func
2576 (func
2578 ('symbol', 'reverse')
2577 ('symbol', 'reverse')
2579 (func
2578 (func
2580 ('symbol', 'sort')
2579 ('symbol', 'sort')
2581 (list
2580 (list
2582 (range
2581 (range
2583 ('symbol', '2')
2582 ('symbol', '2')
2584 ('symbol', '5'))
2583 ('symbol', '5'))
2585 ('symbol', 'date'))))
2584 ('symbol', 'date'))))
2586 * set:
2585 * set:
2587 <baseset [4, 5, 3, 2]>
2586 <baseset [4, 5, 3, 2]>
2588 4
2587 4
2589 5
2588 5
2590 3
2589 3
2591 2
2590 2
2592 $ try 'rs(2 or 3, date)'
2591 $ try 'rs(2 or 3, date)'
2593 (func
2592 (func
2594 ('symbol', 'rs')
2593 ('symbol', 'rs')
2595 (list
2594 (list
2596 (or
2595 (or
2597 ('symbol', '2')
2596 ('symbol', '2')
2598 ('symbol', '3'))
2597 ('symbol', '3'))
2599 ('symbol', 'date')))
2598 ('symbol', 'date')))
2600 * expanded:
2599 * expanded:
2601 (func
2600 (func
2602 ('symbol', 'reverse')
2601 ('symbol', 'reverse')
2603 (func
2602 (func
2604 ('symbol', 'sort')
2603 ('symbol', 'sort')
2605 (list
2604 (list
2606 (or
2605 (or
2607 ('symbol', '2')
2606 ('symbol', '2')
2608 ('symbol', '3'))
2607 ('symbol', '3'))
2609 ('symbol', 'date'))))
2608 ('symbol', 'date'))))
2610 * set:
2609 * set:
2611 <baseset [3, 2]>
2610 <baseset [3, 2]>
2612 3
2611 3
2613 2
2612 2
2614 $ try 'rs()'
2613 $ try 'rs()'
2615 (func
2614 (func
2616 ('symbol', 'rs')
2615 ('symbol', 'rs')
2617 None)
2616 None)
2618 hg: parse error: invalid number of arguments: 0
2617 hg: parse error: invalid number of arguments: 0
2619 [255]
2618 [255]
2620 $ try 'rs(2)'
2619 $ try 'rs(2)'
2621 (func
2620 (func
2622 ('symbol', 'rs')
2621 ('symbol', 'rs')
2623 ('symbol', '2'))
2622 ('symbol', '2'))
2624 hg: parse error: invalid number of arguments: 1
2623 hg: parse error: invalid number of arguments: 1
2625 [255]
2624 [255]
2626 $ try 'rs(2, data, 7)'
2625 $ try 'rs(2, data, 7)'
2627 (func
2626 (func
2628 ('symbol', 'rs')
2627 ('symbol', 'rs')
2629 (list
2628 (list
2630 ('symbol', '2')
2629 ('symbol', '2')
2631 ('symbol', 'data')
2630 ('symbol', 'data')
2632 ('symbol', '7')))
2631 ('symbol', '7')))
2633 hg: parse error: invalid number of arguments: 3
2632 hg: parse error: invalid number of arguments: 3
2634 [255]
2633 [255]
2635 $ try 'rs4(2 or 3, x, x, date)'
2634 $ try 'rs4(2 or 3, x, x, date)'
2636 (func
2635 (func
2637 ('symbol', 'rs4')
2636 ('symbol', 'rs4')
2638 (list
2637 (list
2639 (or
2638 (or
2640 ('symbol', '2')
2639 ('symbol', '2')
2641 ('symbol', '3'))
2640 ('symbol', '3'))
2642 ('symbol', 'x')
2641 ('symbol', 'x')
2643 ('symbol', 'x')
2642 ('symbol', 'x')
2644 ('symbol', 'date')))
2643 ('symbol', 'date')))
2645 * expanded:
2644 * expanded:
2646 (func
2645 (func
2647 ('symbol', 'reverse')
2646 ('symbol', 'reverse')
2648 (func
2647 (func
2649 ('symbol', 'sort')
2648 ('symbol', 'sort')
2650 (list
2649 (list
2651 (or
2650 (or
2652 ('symbol', '2')
2651 ('symbol', '2')
2653 ('symbol', '3'))
2652 ('symbol', '3'))
2654 ('symbol', 'date'))))
2653 ('symbol', 'date'))))
2655 * set:
2654 * set:
2656 <baseset [3, 2]>
2655 <baseset [3, 2]>
2657 3
2656 3
2658 2
2657 2
2659
2658
2660 issue4553: check that revset aliases override existing hash prefix
2659 issue4553: check that revset aliases override existing hash prefix
2661
2660
2662 $ hg log -qr e
2661 $ hg log -qr e
2663 6:e0cc66ef77e8
2662 6:e0cc66ef77e8
2664
2663
2665 $ hg log -qr e --config revsetalias.e="all()"
2664 $ hg log -qr e --config revsetalias.e="all()"
2666 0:2785f51eece5
2665 0:2785f51eece5
2667 1:d75937da8da0
2666 1:d75937da8da0
2668 2:5ed5505e9f1c
2667 2:5ed5505e9f1c
2669 3:8528aa5637f2
2668 3:8528aa5637f2
2670 4:2326846efdab
2669 4:2326846efdab
2671 5:904fa392b941
2670 5:904fa392b941
2672 6:e0cc66ef77e8
2671 6:e0cc66ef77e8
2673 7:013af1973af4
2672 7:013af1973af4
2674 8:d5d0dcbdc4d9
2673 8:d5d0dcbdc4d9
2675 9:24286f4ae135
2674 9:24286f4ae135
2676
2675
2677 $ hg log -qr e: --config revsetalias.e="0"
2676 $ hg log -qr e: --config revsetalias.e="0"
2678 0:2785f51eece5
2677 0:2785f51eece5
2679 1:d75937da8da0
2678 1:d75937da8da0
2680 2:5ed5505e9f1c
2679 2:5ed5505e9f1c
2681 3:8528aa5637f2
2680 3:8528aa5637f2
2682 4:2326846efdab
2681 4:2326846efdab
2683 5:904fa392b941
2682 5:904fa392b941
2684 6:e0cc66ef77e8
2683 6:e0cc66ef77e8
2685 7:013af1973af4
2684 7:013af1973af4
2686 8:d5d0dcbdc4d9
2685 8:d5d0dcbdc4d9
2687 9:24286f4ae135
2686 9:24286f4ae135
2688
2687
2689 $ hg log -qr :e --config revsetalias.e="9"
2688 $ hg log -qr :e --config revsetalias.e="9"
2690 0:2785f51eece5
2689 0:2785f51eece5
2691 1:d75937da8da0
2690 1:d75937da8da0
2692 2:5ed5505e9f1c
2691 2:5ed5505e9f1c
2693 3:8528aa5637f2
2692 3:8528aa5637f2
2694 4:2326846efdab
2693 4:2326846efdab
2695 5:904fa392b941
2694 5:904fa392b941
2696 6:e0cc66ef77e8
2695 6:e0cc66ef77e8
2697 7:013af1973af4
2696 7:013af1973af4
2698 8:d5d0dcbdc4d9
2697 8:d5d0dcbdc4d9
2699 9:24286f4ae135
2698 9:24286f4ae135
2700
2699
2701 $ hg log -qr e:
2700 $ hg log -qr e:
2702 6:e0cc66ef77e8
2701 6:e0cc66ef77e8
2703 7:013af1973af4
2702 7:013af1973af4
2704 8:d5d0dcbdc4d9
2703 8:d5d0dcbdc4d9
2705 9:24286f4ae135
2704 9:24286f4ae135
2706
2705
2707 $ hg log -qr :e
2706 $ hg log -qr :e
2708 0:2785f51eece5
2707 0:2785f51eece5
2709 1:d75937da8da0
2708 1:d75937da8da0
2710 2:5ed5505e9f1c
2709 2:5ed5505e9f1c
2711 3:8528aa5637f2
2710 3:8528aa5637f2
2712 4:2326846efdab
2711 4:2326846efdab
2713 5:904fa392b941
2712 5:904fa392b941
2714 6:e0cc66ef77e8
2713 6:e0cc66ef77e8
2715
2714
2716 issue2549 - correct optimizations
2715 issue2549 - correct optimizations
2717
2716
2718 $ try 'limit(1 or 2 or 3, 2) and not 2'
2717 $ try 'limit(1 or 2 or 3, 2) and not 2'
2719 (and
2718 (and
2720 (func
2719 (func
2721 ('symbol', 'limit')
2720 ('symbol', 'limit')
2722 (list
2721 (list
2723 (or
2722 (or
2724 ('symbol', '1')
2723 ('symbol', '1')
2725 ('symbol', '2')
2724 ('symbol', '2')
2726 ('symbol', '3'))
2725 ('symbol', '3'))
2727 ('symbol', '2')))
2726 ('symbol', '2')))
2728 (not
2727 (not
2729 ('symbol', '2')))
2728 ('symbol', '2')))
2730 * set:
2729 * set:
2731 <filteredset
2730 <filteredset
2732 <baseset
2731 <baseset
2733 <limit n=2, offset=0,
2732 <limit n=2, offset=0,
2734 <fullreposet+ 0:9>,
2733 <fullreposet+ 0:9>,
2735 <baseset [1, 2, 3]>>>,
2734 <baseset [1, 2, 3]>>>,
2736 <not
2735 <not
2737 <baseset [2]>>>
2736 <baseset [2]>>>
2738 1
2737 1
2739 $ try 'max(1 or 2) and not 2'
2738 $ try 'max(1 or 2) and not 2'
2740 (and
2739 (and
2741 (func
2740 (func
2742 ('symbol', 'max')
2741 ('symbol', 'max')
2743 (or
2742 (or
2744 ('symbol', '1')
2743 ('symbol', '1')
2745 ('symbol', '2')))
2744 ('symbol', '2')))
2746 (not
2745 (not
2747 ('symbol', '2')))
2746 ('symbol', '2')))
2748 * set:
2747 * set:
2749 <filteredset
2748 <filteredset
2750 <baseset
2749 <baseset
2751 <max
2750 <max
2752 <fullreposet+ 0:9>,
2751 <fullreposet+ 0:9>,
2753 <baseset [1, 2]>>>,
2752 <baseset [1, 2]>>>,
2754 <not
2753 <not
2755 <baseset [2]>>>
2754 <baseset [2]>>>
2756 $ try 'min(1 or 2) and not 1'
2755 $ try 'min(1 or 2) and not 1'
2757 (and
2756 (and
2758 (func
2757 (func
2759 ('symbol', 'min')
2758 ('symbol', 'min')
2760 (or
2759 (or
2761 ('symbol', '1')
2760 ('symbol', '1')
2762 ('symbol', '2')))
2761 ('symbol', '2')))
2763 (not
2762 (not
2764 ('symbol', '1')))
2763 ('symbol', '1')))
2765 * set:
2764 * set:
2766 <filteredset
2765 <filteredset
2767 <baseset
2766 <baseset
2768 <min
2767 <min
2769 <fullreposet+ 0:9>,
2768 <fullreposet+ 0:9>,
2770 <baseset [1, 2]>>>,
2769 <baseset [1, 2]>>>,
2771 <not
2770 <not
2772 <baseset [1]>>>
2771 <baseset [1]>>>
2773 $ try 'last(1 or 2, 1) and not 2'
2772 $ try 'last(1 or 2, 1) and not 2'
2774 (and
2773 (and
2775 (func
2774 (func
2776 ('symbol', 'last')
2775 ('symbol', 'last')
2777 (list
2776 (list
2778 (or
2777 (or
2779 ('symbol', '1')
2778 ('symbol', '1')
2780 ('symbol', '2'))
2779 ('symbol', '2'))
2781 ('symbol', '1')))
2780 ('symbol', '1')))
2782 (not
2781 (not
2783 ('symbol', '2')))
2782 ('symbol', '2')))
2784 * set:
2783 * set:
2785 <filteredset
2784 <filteredset
2786 <baseset
2785 <baseset
2787 <last n=1,
2786 <last n=1,
2788 <fullreposet+ 0:9>,
2787 <fullreposet+ 0:9>,
2789 <baseset [2, 1]>>>,
2788 <baseset [2, 1]>>>,
2790 <not
2789 <not
2791 <baseset [2]>>>
2790 <baseset [2]>>>
2792
2791
2793 issue4289 - ordering of built-ins
2792 issue4289 - ordering of built-ins
2794 $ hg log -M -q -r 3:2
2793 $ hg log -M -q -r 3:2
2795 3:8528aa5637f2
2794 3:8528aa5637f2
2796 2:5ed5505e9f1c
2795 2:5ed5505e9f1c
2797
2796
2798 test revsets started with 40-chars hash (issue3669)
2797 test revsets started with 40-chars hash (issue3669)
2799
2798
2800 $ ISSUE3669_TIP=`hg tip --template '{node}'`
2799 $ ISSUE3669_TIP=`hg tip --template '{node}'`
2801 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
2800 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
2802 9
2801 9
2803 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
2802 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
2804 8
2803 8
2805
2804
2806 test or-ed indirect predicates (issue3775)
2805 test or-ed indirect predicates (issue3775)
2807
2806
2808 $ log '6 or 6^1' | sort
2807 $ log '6 or 6^1' | sort
2809 5
2808 5
2810 6
2809 6
2811 $ log '6^1 or 6' | sort
2810 $ log '6^1 or 6' | sort
2812 5
2811 5
2813 6
2812 6
2814 $ log '4 or 4~1' | sort
2813 $ log '4 or 4~1' | sort
2815 2
2814 2
2816 4
2815 4
2817 $ log '4~1 or 4' | sort
2816 $ log '4~1 or 4' | sort
2818 2
2817 2
2819 4
2818 4
2820 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
2819 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
2821 0
2820 0
2822 1
2821 1
2823 2
2822 2
2824 3
2823 3
2825 4
2824 4
2826 5
2825 5
2827 6
2826 6
2828 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
2827 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
2829 0
2828 0
2830 1
2829 1
2831 2
2830 2
2832 3
2831 3
2833 4
2832 4
2834 5
2833 5
2835 6
2834 6
2836
2835
2837 tests for 'remote()' predicate:
2836 tests for 'remote()' predicate:
2838 #. (csets in remote) (id) (remote)
2837 #. (csets in remote) (id) (remote)
2839 1. less than local current branch "default"
2838 1. less than local current branch "default"
2840 2. same with local specified "default"
2839 2. same with local specified "default"
2841 3. more than local specified specified
2840 3. more than local specified specified
2842
2841
2843 $ hg clone --quiet -U . ../remote3
2842 $ hg clone --quiet -U . ../remote3
2844 $ cd ../remote3
2843 $ cd ../remote3
2845 $ hg update -q 7
2844 $ hg update -q 7
2846 $ echo r > r
2845 $ echo r > r
2847 $ hg ci -Aqm 10
2846 $ hg ci -Aqm 10
2848 $ log 'remote()'
2847 $ log 'remote()'
2849 7
2848 7
2850 $ log 'remote("a-b-c-")'
2849 $ log 'remote("a-b-c-")'
2851 2
2850 2
2852 $ cd ../repo
2851 $ cd ../repo
2853 $ log 'remote(".a.b.c.", "../remote3")'
2852 $ log 'remote(".a.b.c.", "../remote3")'
2854
2853
2855 tests for concatenation of strings/symbols by "##"
2854 tests for concatenation of strings/symbols by "##"
2856
2855
2857 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
2856 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
2858 (_concat
2857 (_concat
2859 (_concat
2858 (_concat
2860 (_concat
2859 (_concat
2861 ('symbol', '278')
2860 ('symbol', '278')
2862 ('string', '5f5'))
2861 ('string', '5f5'))
2863 ('symbol', '1ee'))
2862 ('symbol', '1ee'))
2864 ('string', 'ce5'))
2863 ('string', 'ce5'))
2865 * concatenated:
2864 * concatenated:
2866 ('string', '2785f51eece5')
2865 ('string', '2785f51eece5')
2867 * set:
2866 * set:
2868 <baseset [0]>
2867 <baseset [0]>
2869 0
2868 0
2870
2869
2871 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
2870 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
2872 $ try "cat4(278, '5f5', 1ee, 'ce5')"
2871 $ try "cat4(278, '5f5', 1ee, 'ce5')"
2873 (func
2872 (func
2874 ('symbol', 'cat4')
2873 ('symbol', 'cat4')
2875 (list
2874 (list
2876 ('symbol', '278')
2875 ('symbol', '278')
2877 ('string', '5f5')
2876 ('string', '5f5')
2878 ('symbol', '1ee')
2877 ('symbol', '1ee')
2879 ('string', 'ce5')))
2878 ('string', 'ce5')))
2880 * expanded:
2879 * expanded:
2881 (_concat
2880 (_concat
2882 (_concat
2881 (_concat
2883 (_concat
2882 (_concat
2884 ('symbol', '278')
2883 ('symbol', '278')
2885 ('string', '5f5'))
2884 ('string', '5f5'))
2886 ('symbol', '1ee'))
2885 ('symbol', '1ee'))
2887 ('string', 'ce5'))
2886 ('string', 'ce5'))
2888 * concatenated:
2887 * concatenated:
2889 ('string', '2785f51eece5')
2888 ('string', '2785f51eece5')
2890 * set:
2889 * set:
2891 <baseset [0]>
2890 <baseset [0]>
2892 0
2891 0
2893
2892
2894 (check concatenation in alias nesting)
2893 (check concatenation in alias nesting)
2895
2894
2896 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
2895 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
2897 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
2896 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
2898 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
2897 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
2899 0
2898 0
2900
2899
2901 (check operator priority)
2900 (check operator priority)
2902
2901
2903 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
2902 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
2904 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
2903 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
2905 0
2904 0
2906 4
2905 4
2907
2906
2908 $ cd ..
2907 $ cd ..
2909
2908
2910 prepare repository that has "default" branches of multiple roots
2909 prepare repository that has "default" branches of multiple roots
2911
2910
2912 $ hg init namedbranch
2911 $ hg init namedbranch
2913 $ cd namedbranch
2912 $ cd namedbranch
2914
2913
2915 $ echo default0 >> a
2914 $ echo default0 >> a
2916 $ hg ci -Aqm0
2915 $ hg ci -Aqm0
2917 $ echo default1 >> a
2916 $ echo default1 >> a
2918 $ hg ci -m1
2917 $ hg ci -m1
2919
2918
2920 $ hg branch -q stable
2919 $ hg branch -q stable
2921 $ echo stable2 >> a
2920 $ echo stable2 >> a
2922 $ hg ci -m2
2921 $ hg ci -m2
2923 $ echo stable3 >> a
2922 $ echo stable3 >> a
2924 $ hg ci -m3
2923 $ hg ci -m3
2925
2924
2926 $ hg update -q null
2925 $ hg update -q null
2927 $ echo default4 >> a
2926 $ echo default4 >> a
2928 $ hg ci -Aqm4
2927 $ hg ci -Aqm4
2929 $ echo default5 >> a
2928 $ echo default5 >> a
2930 $ hg ci -m5
2929 $ hg ci -m5
2931
2930
2932 "null" revision belongs to "default" branch (issue4683)
2931 "null" revision belongs to "default" branch (issue4683)
2933
2932
2934 $ log 'branch(null)'
2933 $ log 'branch(null)'
2935 0
2934 0
2936 1
2935 1
2937 4
2936 4
2938 5
2937 5
2939
2938
2940 "null" revision belongs to "default" branch, but it shouldn't appear in set
2939 "null" revision belongs to "default" branch, but it shouldn't appear in set
2941 unless explicitly specified (issue4682)
2940 unless explicitly specified (issue4682)
2942
2941
2943 $ log 'children(branch(default))'
2942 $ log 'children(branch(default))'
2944 1
2943 1
2945 2
2944 2
2946 5
2945 5
2947
2946
2948 $ cd ..
2947 $ cd ..
2949
2948
2950 test author/desc/keyword in problematic encoding
2949 test author/desc/keyword in problematic encoding
2951 # unicode: cp932:
2950 # unicode: cp932:
2952 # u30A2 0x83 0x41(= 'A')
2951 # u30A2 0x83 0x41(= 'A')
2953 # u30C2 0x83 0x61(= 'a')
2952 # u30C2 0x83 0x61(= 'a')
2954
2953
2955 $ hg init problematicencoding
2954 $ hg init problematicencoding
2956 $ cd problematicencoding
2955 $ cd problematicencoding
2957
2956
2958 $ python > setup.sh <<EOF
2957 $ python > setup.sh <<EOF
2959 > print u'''
2958 > print u'''
2960 > echo a > text
2959 > echo a > text
2961 > hg add text
2960 > hg add text
2962 > hg --encoding utf-8 commit -u '\u30A2' -m none
2961 > hg --encoding utf-8 commit -u '\u30A2' -m none
2963 > echo b > text
2962 > echo b > text
2964 > hg --encoding utf-8 commit -u '\u30C2' -m none
2963 > hg --encoding utf-8 commit -u '\u30C2' -m none
2965 > echo c > text
2964 > echo c > text
2966 > hg --encoding utf-8 commit -u none -m '\u30A2'
2965 > hg --encoding utf-8 commit -u none -m '\u30A2'
2967 > echo d > text
2966 > echo d > text
2968 > hg --encoding utf-8 commit -u none -m '\u30C2'
2967 > hg --encoding utf-8 commit -u none -m '\u30C2'
2969 > '''.encode('utf-8')
2968 > '''.encode('utf-8')
2970 > EOF
2969 > EOF
2971 $ sh < setup.sh
2970 $ sh < setup.sh
2972
2971
2973 test in problematic encoding
2972 test in problematic encoding
2974 $ python > test.sh <<EOF
2973 $ python > test.sh <<EOF
2975 > print u'''
2974 > print u'''
2976 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2975 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2977 > echo ====
2976 > echo ====
2978 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2977 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2979 > echo ====
2978 > echo ====
2980 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2979 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2981 > echo ====
2980 > echo ====
2982 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2981 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2983 > echo ====
2982 > echo ====
2984 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2983 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2985 > echo ====
2984 > echo ====
2986 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2985 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2987 > '''.encode('cp932')
2986 > '''.encode('cp932')
2988 > EOF
2987 > EOF
2989 $ sh < test.sh
2988 $ sh < test.sh
2990 0
2989 0
2991 ====
2990 ====
2992 1
2991 1
2993 ====
2992 ====
2994 2
2993 2
2995 ====
2994 ====
2996 3
2995 3
2997 ====
2996 ====
2998 0
2997 0
2999 2
2998 2
3000 ====
2999 ====
3001 1
3000 1
3002 3
3001 3
3003
3002
3004 test error message of bad revset
3003 test error message of bad revset
3005 $ hg log -r 'foo\\'
3004 $ hg log -r 'foo\\'
3006 hg: parse error at 3: syntax error in revset 'foo\\'
3005 hg: parse error at 3: syntax error in revset 'foo\\'
3007 [255]
3006 [255]
3008
3007
3009 $ cd ..
3008 $ cd ..
3010
3009
3011 Test that revset predicate of extension isn't loaded at failure of
3010 Test that revset predicate of extension isn't loaded at failure of
3012 loading it
3011 loading it
3013
3012
3014 $ cd repo
3013 $ cd repo
3015
3014
3016 $ cat <<EOF > $TESTTMP/custompredicate.py
3015 $ cat <<EOF > $TESTTMP/custompredicate.py
3017 > from mercurial import error, registrar, revset
3016 > from mercurial import error, registrar, revset
3018 >
3017 >
3019 > revsetpredicate = registrar.revsetpredicate()
3018 > revsetpredicate = registrar.revsetpredicate()
3020 >
3019 >
3021 > @revsetpredicate('custom1()')
3020 > @revsetpredicate('custom1()')
3022 > def custom1(repo, subset, x):
3021 > def custom1(repo, subset, x):
3023 > return revset.baseset([1])
3022 > return revset.baseset([1])
3024 >
3023 >
3025 > raise error.Abort('intentional failure of loading extension')
3024 > raise error.Abort('intentional failure of loading extension')
3026 > EOF
3025 > EOF
3027 $ cat <<EOF > .hg/hgrc
3026 $ cat <<EOF > .hg/hgrc
3028 > [extensions]
3027 > [extensions]
3029 > custompredicate = $TESTTMP/custompredicate.py
3028 > custompredicate = $TESTTMP/custompredicate.py
3030 > EOF
3029 > EOF
3031
3030
3032 $ hg debugrevspec "custom1()"
3031 $ hg debugrevspec "custom1()"
3033 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3032 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3034 hg: parse error: unknown identifier: custom1
3033 hg: parse error: unknown identifier: custom1
3035 [255]
3034 [255]
3036
3035
3037 $ cd ..
3036 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now