##// END OF EJS Templates
revset: add inspection data to all filter() calls...
Yuya Nishihara -
r28424:534f968d default
parent child Browse files
Show More
@@ -1,3646 +1,3663
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 def parseerrordetail(inst):
303 def parseerrordetail(inst):
304 """Compose error message from specified ParseError object
304 """Compose error message from specified ParseError object
305 """
305 """
306 if len(inst.args) > 1:
306 if len(inst.args) > 1:
307 return _('at %s: %s') % (inst.args[1], inst.args[0])
307 return _('at %s: %s') % (inst.args[1], inst.args[0])
308 else:
308 else:
309 return inst.args[0]
309 return inst.args[0]
310
310
311 # helpers
311 # helpers
312
312
313 def getstring(x, err):
313 def getstring(x, err):
314 if x and (x[0] == 'string' or x[0] == 'symbol'):
314 if x and (x[0] == 'string' or x[0] == 'symbol'):
315 return x[1]
315 return x[1]
316 raise error.ParseError(err)
316 raise error.ParseError(err)
317
317
318 def getlist(x):
318 def getlist(x):
319 if not x:
319 if not x:
320 return []
320 return []
321 if x[0] == 'list':
321 if x[0] == 'list':
322 return list(x[1:])
322 return list(x[1:])
323 return [x]
323 return [x]
324
324
325 def getargs(x, min, max, err):
325 def getargs(x, min, max, err):
326 l = getlist(x)
326 l = getlist(x)
327 if len(l) < min or (max >= 0 and len(l) > max):
327 if len(l) < min or (max >= 0 and len(l) > max):
328 raise error.ParseError(err)
328 raise error.ParseError(err)
329 return l
329 return l
330
330
331 def getargsdict(x, funcname, keys):
331 def getargsdict(x, funcname, keys):
332 return parser.buildargsdict(getlist(x), funcname, keys.split(),
332 return parser.buildargsdict(getlist(x), funcname, keys.split(),
333 keyvaluenode='keyvalue', keynode='symbol')
333 keyvaluenode='keyvalue', keynode='symbol')
334
334
335 def isvalidsymbol(tree):
335 def isvalidsymbol(tree):
336 """Examine whether specified ``tree`` is valid ``symbol`` or not
336 """Examine whether specified ``tree`` is valid ``symbol`` or not
337 """
337 """
338 return tree[0] == 'symbol' and len(tree) > 1
338 return tree[0] == 'symbol' and len(tree) > 1
339
339
340 def getsymbol(tree):
340 def getsymbol(tree):
341 """Get symbol name from valid ``symbol`` in ``tree``
341 """Get symbol name from valid ``symbol`` in ``tree``
342
342
343 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
343 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
344 """
344 """
345 return tree[1]
345 return tree[1]
346
346
347 def isvalidfunc(tree):
347 def isvalidfunc(tree):
348 """Examine whether specified ``tree`` is valid ``func`` or not
348 """Examine whether specified ``tree`` is valid ``func`` or not
349 """
349 """
350 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
350 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
351
351
352 def getfuncname(tree):
352 def getfuncname(tree):
353 """Get function name from valid ``func`` in ``tree``
353 """Get function name from valid ``func`` in ``tree``
354
354
355 This assumes that ``tree`` is already examined by ``isvalidfunc``.
355 This assumes that ``tree`` is already examined by ``isvalidfunc``.
356 """
356 """
357 return getsymbol(tree[1])
357 return getsymbol(tree[1])
358
358
359 def getfuncargs(tree):
359 def getfuncargs(tree):
360 """Get list of function arguments from valid ``func`` in ``tree``
360 """Get list of function arguments from valid ``func`` in ``tree``
361
361
362 This assumes that ``tree`` is already examined by ``isvalidfunc``.
362 This assumes that ``tree`` is already examined by ``isvalidfunc``.
363 """
363 """
364 if len(tree) > 2:
364 if len(tree) > 2:
365 return getlist(tree[2])
365 return getlist(tree[2])
366 else:
366 else:
367 return []
367 return []
368
368
369 def getset(repo, subset, x):
369 def getset(repo, subset, x):
370 if not x:
370 if not x:
371 raise error.ParseError(_("missing argument"))
371 raise error.ParseError(_("missing argument"))
372 s = methods[x[0]](repo, subset, *x[1:])
372 s = methods[x[0]](repo, subset, *x[1:])
373 if util.safehasattr(s, 'isascending'):
373 if util.safehasattr(s, 'isascending'):
374 return s
374 return s
375 if (repo.ui.configbool('devel', 'all-warnings')
375 if (repo.ui.configbool('devel', 'all-warnings')
376 or repo.ui.configbool('devel', 'old-revset')):
376 or repo.ui.configbool('devel', 'old-revset')):
377 # else case should not happen, because all non-func are internal,
377 # else case should not happen, because all non-func are internal,
378 # ignoring for now.
378 # ignoring for now.
379 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
379 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
380 repo.ui.develwarn('revset "%s" use list instead of smartset, '
380 repo.ui.develwarn('revset "%s" use list instead of smartset, '
381 '(upgrade your code)' % x[1][1])
381 '(upgrade your code)' % x[1][1])
382 return baseset(s)
382 return baseset(s)
383
383
384 def _getrevsource(repo, r):
384 def _getrevsource(repo, r):
385 extra = repo[r].extra()
385 extra = repo[r].extra()
386 for label in ('source', 'transplant_source', 'rebase_source'):
386 for label in ('source', 'transplant_source', 'rebase_source'):
387 if label in extra:
387 if label in extra:
388 try:
388 try:
389 return repo[extra[label]].rev()
389 return repo[extra[label]].rev()
390 except error.RepoLookupError:
390 except error.RepoLookupError:
391 pass
391 pass
392 return None
392 return None
393
393
394 # operator methods
394 # operator methods
395
395
396 def stringset(repo, subset, x):
396 def stringset(repo, subset, x):
397 x = repo[x].rev()
397 x = repo[x].rev()
398 if (x in subset
398 if (x in subset
399 or x == node.nullrev and isinstance(subset, fullreposet)):
399 or x == node.nullrev and isinstance(subset, fullreposet)):
400 return baseset([x])
400 return baseset([x])
401 return baseset()
401 return baseset()
402
402
403 def rangeset(repo, subset, x, y):
403 def rangeset(repo, subset, x, y):
404 m = getset(repo, fullreposet(repo), x)
404 m = getset(repo, fullreposet(repo), x)
405 n = getset(repo, fullreposet(repo), y)
405 n = getset(repo, fullreposet(repo), y)
406
406
407 if not m or not n:
407 if not m or not n:
408 return baseset()
408 return baseset()
409 m, n = m.first(), n.last()
409 m, n = m.first(), n.last()
410
410
411 if m == n:
411 if m == n:
412 r = baseset([m])
412 r = baseset([m])
413 elif n == node.wdirrev:
413 elif n == node.wdirrev:
414 r = spanset(repo, m, len(repo)) + baseset([n])
414 r = spanset(repo, m, len(repo)) + baseset([n])
415 elif m == node.wdirrev:
415 elif m == node.wdirrev:
416 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
416 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
417 elif m < n:
417 elif m < n:
418 r = spanset(repo, m, n + 1)
418 r = spanset(repo, m, n + 1)
419 else:
419 else:
420 r = spanset(repo, m, n - 1)
420 r = spanset(repo, m, n - 1)
421 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
421 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
422 # necessary to ensure we preserve the order in subset.
422 # necessary to ensure we preserve the order in subset.
423 #
423 #
424 # This has performance implication, carrying the sorting over when possible
424 # This has performance implication, carrying the sorting over when possible
425 # would be more efficient.
425 # would be more efficient.
426 return r & subset
426 return r & subset
427
427
428 def dagrange(repo, subset, x, y):
428 def dagrange(repo, subset, x, y):
429 r = fullreposet(repo)
429 r = fullreposet(repo)
430 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
430 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
431 includepath=True)
431 includepath=True)
432 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
432 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
433 # necessary to ensure we preserve the order in subset.
433 # necessary to ensure we preserve the order in subset.
434 return xs & subset
434 return xs & subset
435
435
436 def andset(repo, subset, x, y):
436 def andset(repo, subset, x, y):
437 return getset(repo, getset(repo, subset, x), y)
437 return getset(repo, getset(repo, subset, x), y)
438
438
439 def differenceset(repo, subset, x, y):
439 def differenceset(repo, subset, x, y):
440 return getset(repo, subset, x) - getset(repo, subset, y)
440 return getset(repo, subset, x) - getset(repo, subset, y)
441
441
442 def orset(repo, subset, *xs):
442 def orset(repo, subset, *xs):
443 assert xs
443 assert xs
444 if len(xs) == 1:
444 if len(xs) == 1:
445 return getset(repo, subset, xs[0])
445 return getset(repo, subset, xs[0])
446 p = len(xs) // 2
446 p = len(xs) // 2
447 a = orset(repo, subset, *xs[:p])
447 a = orset(repo, subset, *xs[:p])
448 b = orset(repo, subset, *xs[p:])
448 b = orset(repo, subset, *xs[p:])
449 return a + b
449 return a + b
450
450
451 def notset(repo, subset, x):
451 def notset(repo, subset, x):
452 return subset - getset(repo, subset, x)
452 return subset - getset(repo, subset, x)
453
453
454 def listset(repo, subset, *xs):
454 def listset(repo, subset, *xs):
455 raise error.ParseError(_("can't use a list in this context"),
455 raise error.ParseError(_("can't use a list in this context"),
456 hint=_('see hg help "revsets.x or y"'))
456 hint=_('see hg help "revsets.x or y"'))
457
457
458 def keyvaluepair(repo, subset, k, v):
458 def keyvaluepair(repo, subset, k, v):
459 raise error.ParseError(_("can't use a key-value pair in this context"))
459 raise error.ParseError(_("can't use a key-value pair in this context"))
460
460
461 def func(repo, subset, a, b):
461 def func(repo, subset, a, b):
462 if a[0] == 'symbol' and a[1] in symbols:
462 if a[0] == 'symbol' and a[1] in symbols:
463 return symbols[a[1]](repo, subset, b)
463 return symbols[a[1]](repo, subset, b)
464
464
465 keep = lambda fn: getattr(fn, '__doc__', None) is not None
465 keep = lambda fn: getattr(fn, '__doc__', None) is not None
466
466
467 syms = [s for (s, fn) in symbols.items() if keep(fn)]
467 syms = [s for (s, fn) in symbols.items() if keep(fn)]
468 raise error.UnknownIdentifier(a[1], syms)
468 raise error.UnknownIdentifier(a[1], syms)
469
469
470 # functions
470 # functions
471
471
472 # symbols are callables like:
472 # symbols are callables like:
473 # fn(repo, subset, x)
473 # fn(repo, subset, x)
474 # with:
474 # with:
475 # repo - current repository instance
475 # repo - current repository instance
476 # subset - of revisions to be examined
476 # subset - of revisions to be examined
477 # x - argument in tree form
477 # x - argument in tree form
478 symbols = {}
478 symbols = {}
479
479
480 # symbols which can't be used for a DoS attack for any given input
480 # symbols which can't be used for a DoS attack for any given input
481 # (e.g. those which accept regexes as plain strings shouldn't be included)
481 # (e.g. those which accept regexes as plain strings shouldn't be included)
482 # functions that just return a lot of changesets (like all) don't count here
482 # functions that just return a lot of changesets (like all) don't count here
483 safesymbols = set()
483 safesymbols = set()
484
484
485 predicate = registrar.revsetpredicate()
485 predicate = registrar.revsetpredicate()
486
486
487 class extpredicate(registrar.delayregistrar):
487 class extpredicate(registrar.delayregistrar):
488 """Decorator to register revset predicate in extensions
488 """Decorator to register revset predicate in extensions
489
489
490 Usage::
490 Usage::
491
491
492 revsetpredicate = revset.extpredicate()
492 revsetpredicate = revset.extpredicate()
493
493
494 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
494 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
495 def mypredicatefunc(repo, subset, x):
495 def mypredicatefunc(repo, subset, x):
496 '''Explanation of this revset predicate ....
496 '''Explanation of this revset predicate ....
497 '''
497 '''
498 pass
498 pass
499
499
500 def uisetup(ui):
500 def uisetup(ui):
501 revsetpredicate.setup()
501 revsetpredicate.setup()
502
502
503 'revsetpredicate' instance above can be used to decorate multiple
503 'revsetpredicate' instance above can be used to decorate multiple
504 functions, and 'setup()' on it registers all such functions at
504 functions, and 'setup()' on it registers all such functions at
505 once.
505 once.
506 """
506 """
507 registrar = predicate
507 registrar = predicate
508
508
509 @predicate('_destupdate')
509 @predicate('_destupdate')
510 def _destupdate(repo, subset, x):
510 def _destupdate(repo, subset, x):
511 # experimental revset for update destination
511 # experimental revset for update destination
512 args = getargsdict(x, 'limit', 'clean check')
512 args = getargsdict(x, 'limit', 'clean check')
513 return subset & baseset([destutil.destupdate(repo, **args)[0]])
513 return subset & baseset([destutil.destupdate(repo, **args)[0]])
514
514
515 @predicate('_destmerge')
515 @predicate('_destmerge')
516 def _destmerge(repo, subset, x):
516 def _destmerge(repo, subset, x):
517 # experimental revset for merge destination
517 # experimental revset for merge destination
518 sourceset = None
518 sourceset = None
519 if x is not None:
519 if x is not None:
520 sourceset = getset(repo, fullreposet(repo), x)
520 sourceset = getset(repo, fullreposet(repo), x)
521 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
521 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
522
522
523 @predicate('adds(pattern)', safe=True)
523 @predicate('adds(pattern)', safe=True)
524 def adds(repo, subset, x):
524 def adds(repo, subset, x):
525 """Changesets that add a file matching pattern.
525 """Changesets that add a file matching pattern.
526
526
527 The pattern without explicit kind like ``glob:`` is expected to be
527 The pattern without explicit kind like ``glob:`` is expected to be
528 relative to the current directory and match against a file or a
528 relative to the current directory and match against a file or a
529 directory.
529 directory.
530 """
530 """
531 # i18n: "adds" is a keyword
531 # i18n: "adds" is a keyword
532 pat = getstring(x, _("adds requires a pattern"))
532 pat = getstring(x, _("adds requires a pattern"))
533 return checkstatus(repo, subset, pat, 1)
533 return checkstatus(repo, subset, pat, 1)
534
534
535 @predicate('ancestor(*changeset)', safe=True)
535 @predicate('ancestor(*changeset)', safe=True)
536 def ancestor(repo, subset, x):
536 def ancestor(repo, subset, x):
537 """A greatest common ancestor of the changesets.
537 """A greatest common ancestor of the changesets.
538
538
539 Accepts 0 or more changesets.
539 Accepts 0 or more changesets.
540 Will return empty list when passed no args.
540 Will return empty list when passed no args.
541 Greatest common ancestor of a single changeset is that changeset.
541 Greatest common ancestor of a single changeset is that changeset.
542 """
542 """
543 # i18n: "ancestor" is a keyword
543 # i18n: "ancestor" is a keyword
544 l = getlist(x)
544 l = getlist(x)
545 rl = fullreposet(repo)
545 rl = fullreposet(repo)
546 anc = None
546 anc = None
547
547
548 # (getset(repo, rl, i) for i in l) generates a list of lists
548 # (getset(repo, rl, i) for i in l) generates a list of lists
549 for revs in (getset(repo, rl, i) for i in l):
549 for revs in (getset(repo, rl, i) for i in l):
550 for r in revs:
550 for r in revs:
551 if anc is None:
551 if anc is None:
552 anc = repo[r]
552 anc = repo[r]
553 else:
553 else:
554 anc = anc.ancestor(repo[r])
554 anc = anc.ancestor(repo[r])
555
555
556 if anc is not None and anc.rev() in subset:
556 if anc is not None and anc.rev() in subset:
557 return baseset([anc.rev()])
557 return baseset([anc.rev()])
558 return baseset()
558 return baseset()
559
559
560 def _ancestors(repo, subset, x, followfirst=False):
560 def _ancestors(repo, subset, x, followfirst=False):
561 heads = getset(repo, fullreposet(repo), x)
561 heads = getset(repo, fullreposet(repo), x)
562 if not heads:
562 if not heads:
563 return baseset()
563 return baseset()
564 s = _revancestors(repo, heads, followfirst)
564 s = _revancestors(repo, heads, followfirst)
565 return subset & s
565 return subset & s
566
566
567 @predicate('ancestors(set)', safe=True)
567 @predicate('ancestors(set)', safe=True)
568 def ancestors(repo, subset, x):
568 def ancestors(repo, subset, x):
569 """Changesets that are ancestors of a changeset in set.
569 """Changesets that are ancestors of a changeset in set.
570 """
570 """
571 return _ancestors(repo, subset, x)
571 return _ancestors(repo, subset, x)
572
572
573 @predicate('_firstancestors', safe=True)
573 @predicate('_firstancestors', safe=True)
574 def _firstancestors(repo, subset, x):
574 def _firstancestors(repo, subset, x):
575 # ``_firstancestors(set)``
575 # ``_firstancestors(set)``
576 # Like ``ancestors(set)`` but follows only the first parents.
576 # Like ``ancestors(set)`` but follows only the first parents.
577 return _ancestors(repo, subset, x, followfirst=True)
577 return _ancestors(repo, subset, x, followfirst=True)
578
578
579 def ancestorspec(repo, subset, x, n):
579 def ancestorspec(repo, subset, x, n):
580 """``set~n``
580 """``set~n``
581 Changesets that are the Nth ancestor (first parents only) of a changeset
581 Changesets that are the Nth ancestor (first parents only) of a changeset
582 in set.
582 in set.
583 """
583 """
584 try:
584 try:
585 n = int(n[1])
585 n = int(n[1])
586 except (TypeError, ValueError):
586 except (TypeError, ValueError):
587 raise error.ParseError(_("~ expects a number"))
587 raise error.ParseError(_("~ expects a number"))
588 ps = set()
588 ps = set()
589 cl = repo.changelog
589 cl = repo.changelog
590 for r in getset(repo, fullreposet(repo), x):
590 for r in getset(repo, fullreposet(repo), x):
591 for i in range(n):
591 for i in range(n):
592 r = cl.parentrevs(r)[0]
592 r = cl.parentrevs(r)[0]
593 ps.add(r)
593 ps.add(r)
594 return subset & ps
594 return subset & ps
595
595
596 @predicate('author(string)', safe=True)
596 @predicate('author(string)', safe=True)
597 def author(repo, subset, x):
597 def author(repo, subset, x):
598 """Alias for ``user(string)``.
598 """Alias for ``user(string)``.
599 """
599 """
600 # i18n: "author" is a keyword
600 # i18n: "author" is a keyword
601 n = encoding.lower(getstring(x, _("author requires a string")))
601 n = encoding.lower(getstring(x, _("author requires a string")))
602 kind, pattern, matcher = _substringmatcher(n)
602 kind, pattern, matcher = _substringmatcher(n)
603 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
603 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
604 condrepr=('<user %r>', n))
604
605
605 @predicate('bisect(string)', safe=True)
606 @predicate('bisect(string)', safe=True)
606 def bisect(repo, subset, x):
607 def bisect(repo, subset, x):
607 """Changesets marked in the specified bisect status:
608 """Changesets marked in the specified bisect status:
608
609
609 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
610 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
610 - ``goods``, ``bads`` : csets topologically good/bad
611 - ``goods``, ``bads`` : csets topologically good/bad
611 - ``range`` : csets taking part in the bisection
612 - ``range`` : csets taking part in the bisection
612 - ``pruned`` : csets that are goods, bads or skipped
613 - ``pruned`` : csets that are goods, bads or skipped
613 - ``untested`` : csets whose fate is yet unknown
614 - ``untested`` : csets whose fate is yet unknown
614 - ``ignored`` : csets ignored due to DAG topology
615 - ``ignored`` : csets ignored due to DAG topology
615 - ``current`` : the cset currently being bisected
616 - ``current`` : the cset currently being bisected
616 """
617 """
617 # i18n: "bisect" is a keyword
618 # i18n: "bisect" is a keyword
618 status = getstring(x, _("bisect requires a string")).lower()
619 status = getstring(x, _("bisect requires a string")).lower()
619 state = set(hbisect.get(repo, status))
620 state = set(hbisect.get(repo, status))
620 return subset & state
621 return subset & state
621
622
622 # Backward-compatibility
623 # Backward-compatibility
623 # - no help entry so that we do not advertise it any more
624 # - no help entry so that we do not advertise it any more
624 @predicate('bisected', safe=True)
625 @predicate('bisected', safe=True)
625 def bisected(repo, subset, x):
626 def bisected(repo, subset, x):
626 return bisect(repo, subset, x)
627 return bisect(repo, subset, x)
627
628
628 @predicate('bookmark([name])', safe=True)
629 @predicate('bookmark([name])', safe=True)
629 def bookmark(repo, subset, x):
630 def bookmark(repo, subset, x):
630 """The named bookmark or all bookmarks.
631 """The named bookmark or all bookmarks.
631
632
632 If `name` starts with `re:`, the remainder of the name is treated as
633 If `name` starts with `re:`, the remainder of the name is treated as
633 a regular expression. To match a bookmark that actually starts with `re:`,
634 a regular expression. To match a bookmark that actually starts with `re:`,
634 use the prefix `literal:`.
635 use the prefix `literal:`.
635 """
636 """
636 # i18n: "bookmark" is a keyword
637 # i18n: "bookmark" is a keyword
637 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
638 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
638 if args:
639 if args:
639 bm = getstring(args[0],
640 bm = getstring(args[0],
640 # i18n: "bookmark" is a keyword
641 # i18n: "bookmark" is a keyword
641 _('the argument to bookmark must be a string'))
642 _('the argument to bookmark must be a string'))
642 kind, pattern, matcher = util.stringmatcher(bm)
643 kind, pattern, matcher = util.stringmatcher(bm)
643 bms = set()
644 bms = set()
644 if kind == 'literal':
645 if kind == 'literal':
645 bmrev = repo._bookmarks.get(pattern, None)
646 bmrev = repo._bookmarks.get(pattern, None)
646 if not bmrev:
647 if not bmrev:
647 raise error.RepoLookupError(_("bookmark '%s' does not exist")
648 raise error.RepoLookupError(_("bookmark '%s' does not exist")
648 % pattern)
649 % pattern)
649 bms.add(repo[bmrev].rev())
650 bms.add(repo[bmrev].rev())
650 else:
651 else:
651 matchrevs = set()
652 matchrevs = set()
652 for name, bmrev in repo._bookmarks.iteritems():
653 for name, bmrev in repo._bookmarks.iteritems():
653 if matcher(name):
654 if matcher(name):
654 matchrevs.add(bmrev)
655 matchrevs.add(bmrev)
655 if not matchrevs:
656 if not matchrevs:
656 raise error.RepoLookupError(_("no bookmarks exist"
657 raise error.RepoLookupError(_("no bookmarks exist"
657 " that match '%s'") % pattern)
658 " that match '%s'") % pattern)
658 for bmrev in matchrevs:
659 for bmrev in matchrevs:
659 bms.add(repo[bmrev].rev())
660 bms.add(repo[bmrev].rev())
660 else:
661 else:
661 bms = set([repo[r].rev()
662 bms = set([repo[r].rev()
662 for r in repo._bookmarks.values()])
663 for r in repo._bookmarks.values()])
663 bms -= set([node.nullrev])
664 bms -= set([node.nullrev])
664 return subset & bms
665 return subset & bms
665
666
666 @predicate('branch(string or set)', safe=True)
667 @predicate('branch(string or set)', safe=True)
667 def branch(repo, subset, x):
668 def branch(repo, subset, x):
668 """
669 """
669 All changesets belonging to the given branch or the branches of the given
670 All changesets belonging to the given branch or the branches of the given
670 changesets.
671 changesets.
671
672
672 If `string` starts with `re:`, the remainder of the name is treated as
673 If `string` starts with `re:`, the remainder of the name is treated as
673 a regular expression. To match a branch that actually starts with `re:`,
674 a regular expression. To match a branch that actually starts with `re:`,
674 use the prefix `literal:`.
675 use the prefix `literal:`.
675 """
676 """
676 getbi = repo.revbranchcache().branchinfo
677 getbi = repo.revbranchcache().branchinfo
677
678
678 try:
679 try:
679 b = getstring(x, '')
680 b = getstring(x, '')
680 except error.ParseError:
681 except error.ParseError:
681 # not a string, but another revspec, e.g. tip()
682 # not a string, but another revspec, e.g. tip()
682 pass
683 pass
683 else:
684 else:
684 kind, pattern, matcher = util.stringmatcher(b)
685 kind, pattern, matcher = util.stringmatcher(b)
685 if kind == 'literal':
686 if kind == 'literal':
686 # note: falls through to the revspec case if no branch with
687 # note: falls through to the revspec case if no branch with
687 # this name exists and pattern kind is not specified explicitly
688 # this name exists and pattern kind is not specified explicitly
688 if pattern in repo.branchmap():
689 if pattern in repo.branchmap():
689 return subset.filter(lambda r: matcher(getbi(r)[0]))
690 return subset.filter(lambda r: matcher(getbi(r)[0]),
691 condrepr=('<branch %r>', b))
690 if b.startswith('literal:'):
692 if b.startswith('literal:'):
691 raise error.RepoLookupError(_("branch '%s' does not exist")
693 raise error.RepoLookupError(_("branch '%s' does not exist")
692 % pattern)
694 % pattern)
693 else:
695 else:
694 return subset.filter(lambda r: matcher(getbi(r)[0]))
696 return subset.filter(lambda r: matcher(getbi(r)[0]),
697 condrepr=('<branch %r>', b))
695
698
696 s = getset(repo, fullreposet(repo), x)
699 s = getset(repo, fullreposet(repo), x)
697 b = set()
700 b = set()
698 for r in s:
701 for r in s:
699 b.add(getbi(r)[0])
702 b.add(getbi(r)[0])
700 c = s.__contains__
703 c = s.__contains__
701 return subset.filter(lambda r: c(r) or getbi(r)[0] in b)
704 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
705 condrepr=lambda: '<branch %r>' % sorted(b))
702
706
703 @predicate('bumped()', safe=True)
707 @predicate('bumped()', safe=True)
704 def bumped(repo, subset, x):
708 def bumped(repo, subset, x):
705 """Mutable changesets marked as successors of public changesets.
709 """Mutable changesets marked as successors of public changesets.
706
710
707 Only non-public and non-obsolete changesets can be `bumped`.
711 Only non-public and non-obsolete changesets can be `bumped`.
708 """
712 """
709 # i18n: "bumped" is a keyword
713 # i18n: "bumped" is a keyword
710 getargs(x, 0, 0, _("bumped takes no arguments"))
714 getargs(x, 0, 0, _("bumped takes no arguments"))
711 bumped = obsmod.getrevs(repo, 'bumped')
715 bumped = obsmod.getrevs(repo, 'bumped')
712 return subset & bumped
716 return subset & bumped
713
717
714 @predicate('bundle()', safe=True)
718 @predicate('bundle()', safe=True)
715 def bundle(repo, subset, x):
719 def bundle(repo, subset, x):
716 """Changesets in the bundle.
720 """Changesets in the bundle.
717
721
718 Bundle must be specified by the -R option."""
722 Bundle must be specified by the -R option."""
719
723
720 try:
724 try:
721 bundlerevs = repo.changelog.bundlerevs
725 bundlerevs = repo.changelog.bundlerevs
722 except AttributeError:
726 except AttributeError:
723 raise error.Abort(_("no bundle provided - specify with -R"))
727 raise error.Abort(_("no bundle provided - specify with -R"))
724 return subset & bundlerevs
728 return subset & bundlerevs
725
729
726 def checkstatus(repo, subset, pat, field):
730 def checkstatus(repo, subset, pat, field):
727 hasset = matchmod.patkind(pat) == 'set'
731 hasset = matchmod.patkind(pat) == 'set'
728
732
729 mcache = [None]
733 mcache = [None]
730 def matches(x):
734 def matches(x):
731 c = repo[x]
735 c = repo[x]
732 if not mcache[0] or hasset:
736 if not mcache[0] or hasset:
733 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
737 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
734 m = mcache[0]
738 m = mcache[0]
735 fname = None
739 fname = None
736 if not m.anypats() and len(m.files()) == 1:
740 if not m.anypats() and len(m.files()) == 1:
737 fname = m.files()[0]
741 fname = m.files()[0]
738 if fname is not None:
742 if fname is not None:
739 if fname not in c.files():
743 if fname not in c.files():
740 return False
744 return False
741 else:
745 else:
742 for f in c.files():
746 for f in c.files():
743 if m(f):
747 if m(f):
744 break
748 break
745 else:
749 else:
746 return False
750 return False
747 files = repo.status(c.p1().node(), c.node())[field]
751 files = repo.status(c.p1().node(), c.node())[field]
748 if fname is not None:
752 if fname is not None:
749 if fname in files:
753 if fname in files:
750 return True
754 return True
751 else:
755 else:
752 for f in files:
756 for f in files:
753 if m(f):
757 if m(f):
754 return True
758 return True
755
759
756 return subset.filter(matches)
760 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
757
761
758 def _children(repo, narrow, parentset):
762 def _children(repo, narrow, parentset):
759 if not parentset:
763 if not parentset:
760 return baseset()
764 return baseset()
761 cs = set()
765 cs = set()
762 pr = repo.changelog.parentrevs
766 pr = repo.changelog.parentrevs
763 minrev = parentset.min()
767 minrev = parentset.min()
764 for r in narrow:
768 for r in narrow:
765 if r <= minrev:
769 if r <= minrev:
766 continue
770 continue
767 for p in pr(r):
771 for p in pr(r):
768 if p in parentset:
772 if p in parentset:
769 cs.add(r)
773 cs.add(r)
770 # XXX using a set to feed the baseset is wrong. Sets are not ordered.
774 # XXX using a set to feed the baseset is wrong. Sets are not ordered.
771 # This does not break because of other fullreposet misbehavior.
775 # This does not break because of other fullreposet misbehavior.
772 return baseset(cs)
776 return baseset(cs)
773
777
774 @predicate('children(set)', safe=True)
778 @predicate('children(set)', safe=True)
775 def children(repo, subset, x):
779 def children(repo, subset, x):
776 """Child changesets of changesets in set.
780 """Child changesets of changesets in set.
777 """
781 """
778 s = getset(repo, fullreposet(repo), x)
782 s = getset(repo, fullreposet(repo), x)
779 cs = _children(repo, subset, s)
783 cs = _children(repo, subset, s)
780 return subset & cs
784 return subset & cs
781
785
782 @predicate('closed()', safe=True)
786 @predicate('closed()', safe=True)
783 def closed(repo, subset, x):
787 def closed(repo, subset, x):
784 """Changeset is closed.
788 """Changeset is closed.
785 """
789 """
786 # i18n: "closed" is a keyword
790 # i18n: "closed" is a keyword
787 getargs(x, 0, 0, _("closed takes no arguments"))
791 getargs(x, 0, 0, _("closed takes no arguments"))
788 return subset.filter(lambda r: repo[r].closesbranch())
792 return subset.filter(lambda r: repo[r].closesbranch(),
793 condrepr='<branch closed>')
789
794
790 @predicate('contains(pattern)')
795 @predicate('contains(pattern)')
791 def contains(repo, subset, x):
796 def contains(repo, subset, x):
792 """The revision's manifest contains a file matching pattern (but might not
797 """The revision's manifest contains a file matching pattern (but might not
793 modify it). See :hg:`help patterns` for information about file patterns.
798 modify it). See :hg:`help patterns` for information about file patterns.
794
799
795 The pattern without explicit kind like ``glob:`` is expected to be
800 The pattern without explicit kind like ``glob:`` is expected to be
796 relative to the current directory and match against a file exactly
801 relative to the current directory and match against a file exactly
797 for efficiency.
802 for efficiency.
798 """
803 """
799 # i18n: "contains" is a keyword
804 # i18n: "contains" is a keyword
800 pat = getstring(x, _("contains requires a pattern"))
805 pat = getstring(x, _("contains requires a pattern"))
801
806
802 def matches(x):
807 def matches(x):
803 if not matchmod.patkind(pat):
808 if not matchmod.patkind(pat):
804 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
809 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
805 if pats in repo[x]:
810 if pats in repo[x]:
806 return True
811 return True
807 else:
812 else:
808 c = repo[x]
813 c = repo[x]
809 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
814 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
810 for f in c.manifest():
815 for f in c.manifest():
811 if m(f):
816 if m(f):
812 return True
817 return True
813 return False
818 return False
814
819
815 return subset.filter(matches)
820 return subset.filter(matches, condrepr=('<contains %r>', pat))
816
821
817 @predicate('converted([id])', safe=True)
822 @predicate('converted([id])', safe=True)
818 def converted(repo, subset, x):
823 def converted(repo, subset, x):
819 """Changesets converted from the given identifier in the old repository if
824 """Changesets converted from the given identifier in the old repository if
820 present, or all converted changesets if no identifier is specified.
825 present, or all converted changesets if no identifier is specified.
821 """
826 """
822
827
823 # There is exactly no chance of resolving the revision, so do a simple
828 # There is exactly no chance of resolving the revision, so do a simple
824 # string compare and hope for the best
829 # string compare and hope for the best
825
830
826 rev = None
831 rev = None
827 # i18n: "converted" is a keyword
832 # i18n: "converted" is a keyword
828 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
833 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
829 if l:
834 if l:
830 # i18n: "converted" is a keyword
835 # i18n: "converted" is a keyword
831 rev = getstring(l[0], _('converted requires a revision'))
836 rev = getstring(l[0], _('converted requires a revision'))
832
837
833 def _matchvalue(r):
838 def _matchvalue(r):
834 source = repo[r].extra().get('convert_revision', None)
839 source = repo[r].extra().get('convert_revision', None)
835 return source is not None and (rev is None or source.startswith(rev))
840 return source is not None and (rev is None or source.startswith(rev))
836
841
837 return subset.filter(lambda r: _matchvalue(r))
842 return subset.filter(lambda r: _matchvalue(r),
843 condrepr=('<converted %r>', rev))
838
844
839 @predicate('date(interval)', safe=True)
845 @predicate('date(interval)', safe=True)
840 def date(repo, subset, x):
846 def date(repo, subset, x):
841 """Changesets within the interval, see :hg:`help dates`.
847 """Changesets within the interval, see :hg:`help dates`.
842 """
848 """
843 # i18n: "date" is a keyword
849 # i18n: "date" is a keyword
844 ds = getstring(x, _("date requires a string"))
850 ds = getstring(x, _("date requires a string"))
845 dm = util.matchdate(ds)
851 dm = util.matchdate(ds)
846 return subset.filter(lambda x: dm(repo[x].date()[0]))
852 return subset.filter(lambda x: dm(repo[x].date()[0]),
853 condrepr=('<date %r>', ds))
847
854
848 @predicate('desc(string)', safe=True)
855 @predicate('desc(string)', safe=True)
849 def desc(repo, subset, x):
856 def desc(repo, subset, x):
850 """Search commit message for string. The match is case-insensitive.
857 """Search commit message for string. The match is case-insensitive.
851 """
858 """
852 # i18n: "desc" is a keyword
859 # i18n: "desc" is a keyword
853 ds = encoding.lower(getstring(x, _("desc requires a string")))
860 ds = encoding.lower(getstring(x, _("desc requires a string")))
854
861
855 def matches(x):
862 def matches(x):
856 c = repo[x]
863 c = repo[x]
857 return ds in encoding.lower(c.description())
864 return ds in encoding.lower(c.description())
858
865
859 return subset.filter(matches)
866 return subset.filter(matches, condrepr=('<desc %r>', ds))
860
867
861 def _descendants(repo, subset, x, followfirst=False):
868 def _descendants(repo, subset, x, followfirst=False):
862 roots = getset(repo, fullreposet(repo), x)
869 roots = getset(repo, fullreposet(repo), x)
863 if not roots:
870 if not roots:
864 return baseset()
871 return baseset()
865 s = _revdescendants(repo, roots, followfirst)
872 s = _revdescendants(repo, roots, followfirst)
866
873
867 # Both sets need to be ascending in order to lazily return the union
874 # Both sets need to be ascending in order to lazily return the union
868 # in the correct order.
875 # in the correct order.
869 base = subset & roots
876 base = subset & roots
870 desc = subset & s
877 desc = subset & s
871 result = base + desc
878 result = base + desc
872 if subset.isascending():
879 if subset.isascending():
873 result.sort()
880 result.sort()
874 elif subset.isdescending():
881 elif subset.isdescending():
875 result.sort(reverse=True)
882 result.sort(reverse=True)
876 else:
883 else:
877 result = subset & result
884 result = subset & result
878 return result
885 return result
879
886
880 @predicate('descendants(set)', safe=True)
887 @predicate('descendants(set)', safe=True)
881 def descendants(repo, subset, x):
888 def descendants(repo, subset, x):
882 """Changesets which are descendants of changesets in set.
889 """Changesets which are descendants of changesets in set.
883 """
890 """
884 return _descendants(repo, subset, x)
891 return _descendants(repo, subset, x)
885
892
886 @predicate('_firstdescendants', safe=True)
893 @predicate('_firstdescendants', safe=True)
887 def _firstdescendants(repo, subset, x):
894 def _firstdescendants(repo, subset, x):
888 # ``_firstdescendants(set)``
895 # ``_firstdescendants(set)``
889 # Like ``descendants(set)`` but follows only the first parents.
896 # Like ``descendants(set)`` but follows only the first parents.
890 return _descendants(repo, subset, x, followfirst=True)
897 return _descendants(repo, subset, x, followfirst=True)
891
898
892 @predicate('destination([set])', safe=True)
899 @predicate('destination([set])', safe=True)
893 def destination(repo, subset, x):
900 def destination(repo, subset, x):
894 """Changesets that were created by a graft, transplant or rebase operation,
901 """Changesets that were created by a graft, transplant or rebase operation,
895 with the given revisions specified as the source. Omitting the optional set
902 with the given revisions specified as the source. Omitting the optional set
896 is the same as passing all().
903 is the same as passing all().
897 """
904 """
898 if x is not None:
905 if x is not None:
899 sources = getset(repo, fullreposet(repo), x)
906 sources = getset(repo, fullreposet(repo), x)
900 else:
907 else:
901 sources = fullreposet(repo)
908 sources = fullreposet(repo)
902
909
903 dests = set()
910 dests = set()
904
911
905 # subset contains all of the possible destinations that can be returned, so
912 # subset contains all of the possible destinations that can be returned, so
906 # iterate over them and see if their source(s) were provided in the arg set.
913 # iterate over them and see if their source(s) were provided in the arg set.
907 # Even if the immediate src of r is not in the arg set, src's source (or
914 # Even if the immediate src of r is not in the arg set, src's source (or
908 # further back) may be. Scanning back further than the immediate src allows
915 # further back) may be. Scanning back further than the immediate src allows
909 # transitive transplants and rebases to yield the same results as transitive
916 # transitive transplants and rebases to yield the same results as transitive
910 # grafts.
917 # grafts.
911 for r in subset:
918 for r in subset:
912 src = _getrevsource(repo, r)
919 src = _getrevsource(repo, r)
913 lineage = None
920 lineage = None
914
921
915 while src is not None:
922 while src is not None:
916 if lineage is None:
923 if lineage is None:
917 lineage = list()
924 lineage = list()
918
925
919 lineage.append(r)
926 lineage.append(r)
920
927
921 # The visited lineage is a match if the current source is in the arg
928 # The visited lineage is a match if the current source is in the arg
922 # set. Since every candidate dest is visited by way of iterating
929 # set. Since every candidate dest is visited by way of iterating
923 # subset, any dests further back in the lineage will be tested by a
930 # subset, any dests further back in the lineage will be tested by a
924 # different iteration over subset. Likewise, if the src was already
931 # different iteration over subset. Likewise, if the src was already
925 # selected, the current lineage can be selected without going back
932 # selected, the current lineage can be selected without going back
926 # further.
933 # further.
927 if src in sources or src in dests:
934 if src in sources or src in dests:
928 dests.update(lineage)
935 dests.update(lineage)
929 break
936 break
930
937
931 r = src
938 r = src
932 src = _getrevsource(repo, r)
939 src = _getrevsource(repo, r)
933
940
934 return subset.filter(dests.__contains__)
941 return subset.filter(dests.__contains__,
942 condrepr=lambda: '<destination %r>' % sorted(dests))
935
943
936 @predicate('divergent()', safe=True)
944 @predicate('divergent()', safe=True)
937 def divergent(repo, subset, x):
945 def divergent(repo, subset, x):
938 """
946 """
939 Final successors of changesets with an alternative set of final successors.
947 Final successors of changesets with an alternative set of final successors.
940 """
948 """
941 # i18n: "divergent" is a keyword
949 # i18n: "divergent" is a keyword
942 getargs(x, 0, 0, _("divergent takes no arguments"))
950 getargs(x, 0, 0, _("divergent takes no arguments"))
943 divergent = obsmod.getrevs(repo, 'divergent')
951 divergent = obsmod.getrevs(repo, 'divergent')
944 return subset & divergent
952 return subset & divergent
945
953
946 @predicate('extinct()', safe=True)
954 @predicate('extinct()', safe=True)
947 def extinct(repo, subset, x):
955 def extinct(repo, subset, x):
948 """Obsolete changesets with obsolete descendants only.
956 """Obsolete changesets with obsolete descendants only.
949 """
957 """
950 # i18n: "extinct" is a keyword
958 # i18n: "extinct" is a keyword
951 getargs(x, 0, 0, _("extinct takes no arguments"))
959 getargs(x, 0, 0, _("extinct takes no arguments"))
952 extincts = obsmod.getrevs(repo, 'extinct')
960 extincts = obsmod.getrevs(repo, 'extinct')
953 return subset & extincts
961 return subset & extincts
954
962
955 @predicate('extra(label, [value])', safe=True)
963 @predicate('extra(label, [value])', safe=True)
956 def extra(repo, subset, x):
964 def extra(repo, subset, x):
957 """Changesets with the given label in the extra metadata, with the given
965 """Changesets with the given label in the extra metadata, with the given
958 optional value.
966 optional value.
959
967
960 If `value` starts with `re:`, the remainder of the value is treated as
968 If `value` starts with `re:`, the remainder of the value is treated as
961 a regular expression. To match a value that actually starts with `re:`,
969 a regular expression. To match a value that actually starts with `re:`,
962 use the prefix `literal:`.
970 use the prefix `literal:`.
963 """
971 """
964 args = getargsdict(x, 'extra', 'label value')
972 args = getargsdict(x, 'extra', 'label value')
965 if 'label' not in args:
973 if 'label' not in args:
966 # i18n: "extra" is a keyword
974 # i18n: "extra" is a keyword
967 raise error.ParseError(_('extra takes at least 1 argument'))
975 raise error.ParseError(_('extra takes at least 1 argument'))
968 # i18n: "extra" is a keyword
976 # i18n: "extra" is a keyword
969 label = getstring(args['label'], _('first argument to extra must be '
977 label = getstring(args['label'], _('first argument to extra must be '
970 'a string'))
978 'a string'))
971 value = None
979 value = None
972
980
973 if 'value' in args:
981 if 'value' in args:
974 # i18n: "extra" is a keyword
982 # i18n: "extra" is a keyword
975 value = getstring(args['value'], _('second argument to extra must be '
983 value = getstring(args['value'], _('second argument to extra must be '
976 'a string'))
984 'a string'))
977 kind, value, matcher = util.stringmatcher(value)
985 kind, value, matcher = util.stringmatcher(value)
978
986
979 def _matchvalue(r):
987 def _matchvalue(r):
980 extra = repo[r].extra()
988 extra = repo[r].extra()
981 return label in extra and (value is None or matcher(extra[label]))
989 return label in extra and (value is None or matcher(extra[label]))
982
990
983 return subset.filter(lambda r: _matchvalue(r))
991 return subset.filter(lambda r: _matchvalue(r),
992 condrepr=('<extra[%r] %r>', label, value))
984
993
985 @predicate('filelog(pattern)', safe=True)
994 @predicate('filelog(pattern)', safe=True)
986 def filelog(repo, subset, x):
995 def filelog(repo, subset, x):
987 """Changesets connected to the specified filelog.
996 """Changesets connected to the specified filelog.
988
997
989 For performance reasons, visits only revisions mentioned in the file-level
998 For performance reasons, visits only revisions mentioned in the file-level
990 filelog, rather than filtering through all changesets (much faster, but
999 filelog, rather than filtering through all changesets (much faster, but
991 doesn't include deletes or duplicate changes). For a slower, more accurate
1000 doesn't include deletes or duplicate changes). For a slower, more accurate
992 result, use ``file()``.
1001 result, use ``file()``.
993
1002
994 The pattern without explicit kind like ``glob:`` is expected to be
1003 The pattern without explicit kind like ``glob:`` is expected to be
995 relative to the current directory and match against a file exactly
1004 relative to the current directory and match against a file exactly
996 for efficiency.
1005 for efficiency.
997
1006
998 If some linkrev points to revisions filtered by the current repoview, we'll
1007 If some linkrev points to revisions filtered by the current repoview, we'll
999 work around it to return a non-filtered value.
1008 work around it to return a non-filtered value.
1000 """
1009 """
1001
1010
1002 # i18n: "filelog" is a keyword
1011 # i18n: "filelog" is a keyword
1003 pat = getstring(x, _("filelog requires a pattern"))
1012 pat = getstring(x, _("filelog requires a pattern"))
1004 s = set()
1013 s = set()
1005 cl = repo.changelog
1014 cl = repo.changelog
1006
1015
1007 if not matchmod.patkind(pat):
1016 if not matchmod.patkind(pat):
1008 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
1017 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
1009 files = [f]
1018 files = [f]
1010 else:
1019 else:
1011 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
1020 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
1012 files = (f for f in repo[None] if m(f))
1021 files = (f for f in repo[None] if m(f))
1013
1022
1014 for f in files:
1023 for f in files:
1015 fl = repo.file(f)
1024 fl = repo.file(f)
1016 known = {}
1025 known = {}
1017 scanpos = 0
1026 scanpos = 0
1018 for fr in list(fl):
1027 for fr in list(fl):
1019 fn = fl.node(fr)
1028 fn = fl.node(fr)
1020 if fn in known:
1029 if fn in known:
1021 s.add(known[fn])
1030 s.add(known[fn])
1022 continue
1031 continue
1023
1032
1024 lr = fl.linkrev(fr)
1033 lr = fl.linkrev(fr)
1025 if lr in cl:
1034 if lr in cl:
1026 s.add(lr)
1035 s.add(lr)
1027 elif scanpos is not None:
1036 elif scanpos is not None:
1028 # lowest matching changeset is filtered, scan further
1037 # lowest matching changeset is filtered, scan further
1029 # ahead in changelog
1038 # ahead in changelog
1030 start = max(lr, scanpos) + 1
1039 start = max(lr, scanpos) + 1
1031 scanpos = None
1040 scanpos = None
1032 for r in cl.revs(start):
1041 for r in cl.revs(start):
1033 # minimize parsing of non-matching entries
1042 # minimize parsing of non-matching entries
1034 if f in cl.revision(r) and f in cl.readfiles(r):
1043 if f in cl.revision(r) and f in cl.readfiles(r):
1035 try:
1044 try:
1036 # try to use manifest delta fastpath
1045 # try to use manifest delta fastpath
1037 n = repo[r].filenode(f)
1046 n = repo[r].filenode(f)
1038 if n not in known:
1047 if n not in known:
1039 if n == fn:
1048 if n == fn:
1040 s.add(r)
1049 s.add(r)
1041 scanpos = r
1050 scanpos = r
1042 break
1051 break
1043 else:
1052 else:
1044 known[n] = r
1053 known[n] = r
1045 except error.ManifestLookupError:
1054 except error.ManifestLookupError:
1046 # deletion in changelog
1055 # deletion in changelog
1047 continue
1056 continue
1048
1057
1049 return subset & s
1058 return subset & s
1050
1059
1051 @predicate('first(set, [n])', safe=True)
1060 @predicate('first(set, [n])', safe=True)
1052 def first(repo, subset, x):
1061 def first(repo, subset, x):
1053 """An alias for limit().
1062 """An alias for limit().
1054 """
1063 """
1055 return limit(repo, subset, x)
1064 return limit(repo, subset, x)
1056
1065
1057 def _follow(repo, subset, x, name, followfirst=False):
1066 def _follow(repo, subset, x, name, followfirst=False):
1058 l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name)
1067 l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name)
1059 c = repo['.']
1068 c = repo['.']
1060 if l:
1069 if l:
1061 x = getstring(l[0], _("%s expected a pattern") % name)
1070 x = getstring(l[0], _("%s expected a pattern") % name)
1062 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1071 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1063 ctx=repo[None], default='path')
1072 ctx=repo[None], default='path')
1064
1073
1065 files = c.manifest().walk(matcher)
1074 files = c.manifest().walk(matcher)
1066
1075
1067 s = set()
1076 s = set()
1068 for fname in files:
1077 for fname in files:
1069 fctx = c[fname]
1078 fctx = c[fname]
1070 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1079 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1071 # include the revision responsible for the most recent version
1080 # include the revision responsible for the most recent version
1072 s.add(fctx.introrev())
1081 s.add(fctx.introrev())
1073 else:
1082 else:
1074 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1083 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1075
1084
1076 return subset & s
1085 return subset & s
1077
1086
1078 @predicate('follow([pattern])', safe=True)
1087 @predicate('follow([pattern])', safe=True)
1079 def follow(repo, subset, x):
1088 def follow(repo, subset, x):
1080 """
1089 """
1081 An alias for ``::.`` (ancestors of the working directory's first parent).
1090 An alias for ``::.`` (ancestors of the working directory's first parent).
1082 If pattern is specified, the histories of files matching given
1091 If pattern is specified, the histories of files matching given
1083 pattern is followed, including copies.
1092 pattern is followed, including copies.
1084 """
1093 """
1085 return _follow(repo, subset, x, 'follow')
1094 return _follow(repo, subset, x, 'follow')
1086
1095
1087 @predicate('_followfirst', safe=True)
1096 @predicate('_followfirst', safe=True)
1088 def _followfirst(repo, subset, x):
1097 def _followfirst(repo, subset, x):
1089 # ``followfirst([pattern])``
1098 # ``followfirst([pattern])``
1090 # Like ``follow([pattern])`` but follows only the first parent of
1099 # Like ``follow([pattern])`` but follows only the first parent of
1091 # every revisions or files revisions.
1100 # every revisions or files revisions.
1092 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1101 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1093
1102
1094 @predicate('all()', safe=True)
1103 @predicate('all()', safe=True)
1095 def getall(repo, subset, x):
1104 def getall(repo, subset, x):
1096 """All changesets, the same as ``0:tip``.
1105 """All changesets, the same as ``0:tip``.
1097 """
1106 """
1098 # i18n: "all" is a keyword
1107 # i18n: "all" is a keyword
1099 getargs(x, 0, 0, _("all takes no arguments"))
1108 getargs(x, 0, 0, _("all takes no arguments"))
1100 return subset & spanset(repo) # drop "null" if any
1109 return subset & spanset(repo) # drop "null" if any
1101
1110
1102 @predicate('grep(regex)')
1111 @predicate('grep(regex)')
1103 def grep(repo, subset, x):
1112 def grep(repo, subset, x):
1104 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1113 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1105 to ensure special escape characters are handled correctly. Unlike
1114 to ensure special escape characters are handled correctly. Unlike
1106 ``keyword(string)``, the match is case-sensitive.
1115 ``keyword(string)``, the match is case-sensitive.
1107 """
1116 """
1108 try:
1117 try:
1109 # i18n: "grep" is a keyword
1118 # i18n: "grep" is a keyword
1110 gr = re.compile(getstring(x, _("grep requires a string")))
1119 gr = re.compile(getstring(x, _("grep requires a string")))
1111 except re.error as e:
1120 except re.error as e:
1112 raise error.ParseError(_('invalid match pattern: %s') % e)
1121 raise error.ParseError(_('invalid match pattern: %s') % e)
1113
1122
1114 def matches(x):
1123 def matches(x):
1115 c = repo[x]
1124 c = repo[x]
1116 for e in c.files() + [c.user(), c.description()]:
1125 for e in c.files() + [c.user(), c.description()]:
1117 if gr.search(e):
1126 if gr.search(e):
1118 return True
1127 return True
1119 return False
1128 return False
1120
1129
1121 return subset.filter(matches)
1130 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1122
1131
1123 @predicate('_matchfiles', safe=True)
1132 @predicate('_matchfiles', safe=True)
1124 def _matchfiles(repo, subset, x):
1133 def _matchfiles(repo, subset, x):
1125 # _matchfiles takes a revset list of prefixed arguments:
1134 # _matchfiles takes a revset list of prefixed arguments:
1126 #
1135 #
1127 # [p:foo, i:bar, x:baz]
1136 # [p:foo, i:bar, x:baz]
1128 #
1137 #
1129 # builds a match object from them and filters subset. Allowed
1138 # builds a match object from them and filters subset. Allowed
1130 # prefixes are 'p:' for regular patterns, 'i:' for include
1139 # prefixes are 'p:' for regular patterns, 'i:' for include
1131 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1140 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1132 # a revision identifier, or the empty string to reference the
1141 # a revision identifier, or the empty string to reference the
1133 # working directory, from which the match object is
1142 # working directory, from which the match object is
1134 # initialized. Use 'd:' to set the default matching mode, default
1143 # initialized. Use 'd:' to set the default matching mode, default
1135 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1144 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1136
1145
1137 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1146 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1138 pats, inc, exc = [], [], []
1147 pats, inc, exc = [], [], []
1139 rev, default = None, None
1148 rev, default = None, None
1140 for arg in l:
1149 for arg in l:
1141 s = getstring(arg, "_matchfiles requires string arguments")
1150 s = getstring(arg, "_matchfiles requires string arguments")
1142 prefix, value = s[:2], s[2:]
1151 prefix, value = s[:2], s[2:]
1143 if prefix == 'p:':
1152 if prefix == 'p:':
1144 pats.append(value)
1153 pats.append(value)
1145 elif prefix == 'i:':
1154 elif prefix == 'i:':
1146 inc.append(value)
1155 inc.append(value)
1147 elif prefix == 'x:':
1156 elif prefix == 'x:':
1148 exc.append(value)
1157 exc.append(value)
1149 elif prefix == 'r:':
1158 elif prefix == 'r:':
1150 if rev is not None:
1159 if rev is not None:
1151 raise error.ParseError('_matchfiles expected at most one '
1160 raise error.ParseError('_matchfiles expected at most one '
1152 'revision')
1161 'revision')
1153 if value != '': # empty means working directory; leave rev as None
1162 if value != '': # empty means working directory; leave rev as None
1154 rev = value
1163 rev = value
1155 elif prefix == 'd:':
1164 elif prefix == 'd:':
1156 if default is not None:
1165 if default is not None:
1157 raise error.ParseError('_matchfiles expected at most one '
1166 raise error.ParseError('_matchfiles expected at most one '
1158 'default mode')
1167 'default mode')
1159 default = value
1168 default = value
1160 else:
1169 else:
1161 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1170 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1162 if not default:
1171 if not default:
1163 default = 'glob'
1172 default = 'glob'
1164
1173
1165 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1174 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1166 exclude=exc, ctx=repo[rev], default=default)
1175 exclude=exc, ctx=repo[rev], default=default)
1167
1176
1168 # This directly read the changelog data as creating changectx for all
1177 # This directly read the changelog data as creating changectx for all
1169 # revisions is quite expensive.
1178 # revisions is quite expensive.
1170 getfiles = repo.changelog.readfiles
1179 getfiles = repo.changelog.readfiles
1171 wdirrev = node.wdirrev
1180 wdirrev = node.wdirrev
1172 def matches(x):
1181 def matches(x):
1173 if x == wdirrev:
1182 if x == wdirrev:
1174 files = repo[x].files()
1183 files = repo[x].files()
1175 else:
1184 else:
1176 files = getfiles(x)
1185 files = getfiles(x)
1177 for f in files:
1186 for f in files:
1178 if m(f):
1187 if m(f):
1179 return True
1188 return True
1180 return False
1189 return False
1181
1190
1182 return subset.filter(matches)
1191 return subset.filter(matches,
1192 condrepr=('<matchfiles patterns=%r, include=%r '
1193 'exclude=%r, default=%r, rev=%r>',
1194 pats, inc, exc, default, rev))
1183
1195
1184 @predicate('file(pattern)', safe=True)
1196 @predicate('file(pattern)', safe=True)
1185 def hasfile(repo, subset, x):
1197 def hasfile(repo, subset, x):
1186 """Changesets affecting files matched by pattern.
1198 """Changesets affecting files matched by pattern.
1187
1199
1188 For a faster but less accurate result, consider using ``filelog()``
1200 For a faster but less accurate result, consider using ``filelog()``
1189 instead.
1201 instead.
1190
1202
1191 This predicate uses ``glob:`` as the default kind of pattern.
1203 This predicate uses ``glob:`` as the default kind of pattern.
1192 """
1204 """
1193 # i18n: "file" is a keyword
1205 # i18n: "file" is a keyword
1194 pat = getstring(x, _("file requires a pattern"))
1206 pat = getstring(x, _("file requires a pattern"))
1195 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1207 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1196
1208
1197 @predicate('head()', safe=True)
1209 @predicate('head()', safe=True)
1198 def head(repo, subset, x):
1210 def head(repo, subset, x):
1199 """Changeset is a named branch head.
1211 """Changeset is a named branch head.
1200 """
1212 """
1201 # i18n: "head" is a keyword
1213 # i18n: "head" is a keyword
1202 getargs(x, 0, 0, _("head takes no arguments"))
1214 getargs(x, 0, 0, _("head takes no arguments"))
1203 hs = set()
1215 hs = set()
1204 cl = repo.changelog
1216 cl = repo.changelog
1205 for b, ls in repo.branchmap().iteritems():
1217 for b, ls in repo.branchmap().iteritems():
1206 hs.update(cl.rev(h) for h in ls)
1218 hs.update(cl.rev(h) for h in ls)
1207 # XXX using a set to feed the baseset is wrong. Sets are not ordered.
1219 # XXX using a set to feed the baseset is wrong. Sets are not ordered.
1208 # This does not break because of other fullreposet misbehavior.
1220 # This does not break because of other fullreposet misbehavior.
1209 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
1221 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
1210 # necessary to ensure we preserve the order in subset.
1222 # necessary to ensure we preserve the order in subset.
1211 return baseset(hs) & subset
1223 return baseset(hs) & subset
1212
1224
1213 @predicate('heads(set)', safe=True)
1225 @predicate('heads(set)', safe=True)
1214 def heads(repo, subset, x):
1226 def heads(repo, subset, x):
1215 """Members of set with no children in set.
1227 """Members of set with no children in set.
1216 """
1228 """
1217 s = getset(repo, subset, x)
1229 s = getset(repo, subset, x)
1218 ps = parents(repo, subset, x)
1230 ps = parents(repo, subset, x)
1219 return s - ps
1231 return s - ps
1220
1232
1221 @predicate('hidden()', safe=True)
1233 @predicate('hidden()', safe=True)
1222 def hidden(repo, subset, x):
1234 def hidden(repo, subset, x):
1223 """Hidden changesets.
1235 """Hidden changesets.
1224 """
1236 """
1225 # i18n: "hidden" is a keyword
1237 # i18n: "hidden" is a keyword
1226 getargs(x, 0, 0, _("hidden takes no arguments"))
1238 getargs(x, 0, 0, _("hidden takes no arguments"))
1227 hiddenrevs = repoview.filterrevs(repo, 'visible')
1239 hiddenrevs = repoview.filterrevs(repo, 'visible')
1228 return subset & hiddenrevs
1240 return subset & hiddenrevs
1229
1241
1230 @predicate('keyword(string)', safe=True)
1242 @predicate('keyword(string)', safe=True)
1231 def keyword(repo, subset, x):
1243 def keyword(repo, subset, x):
1232 """Search commit message, user name, and names of changed files for
1244 """Search commit message, user name, and names of changed files for
1233 string. The match is case-insensitive.
1245 string. The match is case-insensitive.
1234 """
1246 """
1235 # i18n: "keyword" is a keyword
1247 # i18n: "keyword" is a keyword
1236 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1248 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1237
1249
1238 def matches(r):
1250 def matches(r):
1239 c = repo[r]
1251 c = repo[r]
1240 return any(kw in encoding.lower(t)
1252 return any(kw in encoding.lower(t)
1241 for t in c.files() + [c.user(), c.description()])
1253 for t in c.files() + [c.user(), c.description()])
1242
1254
1243 return subset.filter(matches)
1255 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1244
1256
1245 @predicate('limit(set[, n[, offset]])', safe=True)
1257 @predicate('limit(set[, n[, offset]])', safe=True)
1246 def limit(repo, subset, x):
1258 def limit(repo, subset, x):
1247 """First n members of set, defaulting to 1, starting from offset.
1259 """First n members of set, defaulting to 1, starting from offset.
1248 """
1260 """
1249 args = getargsdict(x, 'limit', 'set n offset')
1261 args = getargsdict(x, 'limit', 'set n offset')
1250 if 'set' not in args:
1262 if 'set' not in args:
1251 # i18n: "limit" is a keyword
1263 # i18n: "limit" is a keyword
1252 raise error.ParseError(_("limit requires one to three arguments"))
1264 raise error.ParseError(_("limit requires one to three arguments"))
1253 try:
1265 try:
1254 lim, ofs = 1, 0
1266 lim, ofs = 1, 0
1255 if 'n' in args:
1267 if 'n' in args:
1256 # i18n: "limit" is a keyword
1268 # i18n: "limit" is a keyword
1257 lim = int(getstring(args['n'], _("limit requires a number")))
1269 lim = int(getstring(args['n'], _("limit requires a number")))
1258 if 'offset' in args:
1270 if 'offset' in args:
1259 # i18n: "limit" is a keyword
1271 # i18n: "limit" is a keyword
1260 ofs = int(getstring(args['offset'], _("limit requires a number")))
1272 ofs = int(getstring(args['offset'], _("limit requires a number")))
1261 if ofs < 0:
1273 if ofs < 0:
1262 raise error.ParseError(_("negative offset"))
1274 raise error.ParseError(_("negative offset"))
1263 except (TypeError, ValueError):
1275 except (TypeError, ValueError):
1264 # i18n: "limit" is a keyword
1276 # i18n: "limit" is a keyword
1265 raise error.ParseError(_("limit expects a number"))
1277 raise error.ParseError(_("limit expects a number"))
1266 os = getset(repo, fullreposet(repo), args['set'])
1278 os = getset(repo, fullreposet(repo), args['set'])
1267 result = []
1279 result = []
1268 it = iter(os)
1280 it = iter(os)
1269 for x in xrange(ofs):
1281 for x in xrange(ofs):
1270 y = next(it, None)
1282 y = next(it, None)
1271 if y is None:
1283 if y is None:
1272 break
1284 break
1273 for x in xrange(lim):
1285 for x in xrange(lim):
1274 y = next(it, None)
1286 y = next(it, None)
1275 if y is None:
1287 if y is None:
1276 break
1288 break
1277 elif y in subset:
1289 elif y in subset:
1278 result.append(y)
1290 result.append(y)
1279 return baseset(result)
1291 return baseset(result)
1280
1292
1281 @predicate('last(set, [n])', safe=True)
1293 @predicate('last(set, [n])', safe=True)
1282 def last(repo, subset, x):
1294 def last(repo, subset, x):
1283 """Last n members of set, defaulting to 1.
1295 """Last n members of set, defaulting to 1.
1284 """
1296 """
1285 # i18n: "last" is a keyword
1297 # i18n: "last" is a keyword
1286 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1298 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1287 try:
1299 try:
1288 lim = 1
1300 lim = 1
1289 if len(l) == 2:
1301 if len(l) == 2:
1290 # i18n: "last" is a keyword
1302 # i18n: "last" is a keyword
1291 lim = int(getstring(l[1], _("last requires a number")))
1303 lim = int(getstring(l[1], _("last requires a number")))
1292 except (TypeError, ValueError):
1304 except (TypeError, ValueError):
1293 # i18n: "last" is a keyword
1305 # i18n: "last" is a keyword
1294 raise error.ParseError(_("last expects a number"))
1306 raise error.ParseError(_("last expects a number"))
1295 os = getset(repo, fullreposet(repo), l[0])
1307 os = getset(repo, fullreposet(repo), l[0])
1296 os.reverse()
1308 os.reverse()
1297 result = []
1309 result = []
1298 it = iter(os)
1310 it = iter(os)
1299 for x in xrange(lim):
1311 for x in xrange(lim):
1300 y = next(it, None)
1312 y = next(it, None)
1301 if y is None:
1313 if y is None:
1302 break
1314 break
1303 elif y in subset:
1315 elif y in subset:
1304 result.append(y)
1316 result.append(y)
1305 return baseset(result)
1317 return baseset(result)
1306
1318
1307 @predicate('max(set)', safe=True)
1319 @predicate('max(set)', safe=True)
1308 def maxrev(repo, subset, x):
1320 def maxrev(repo, subset, x):
1309 """Changeset with highest revision number in set.
1321 """Changeset with highest revision number in set.
1310 """
1322 """
1311 os = getset(repo, fullreposet(repo), x)
1323 os = getset(repo, fullreposet(repo), x)
1312 try:
1324 try:
1313 m = os.max()
1325 m = os.max()
1314 if m in subset:
1326 if m in subset:
1315 return baseset([m])
1327 return baseset([m])
1316 except ValueError:
1328 except ValueError:
1317 # os.max() throws a ValueError when the collection is empty.
1329 # os.max() throws a ValueError when the collection is empty.
1318 # Same as python's max().
1330 # Same as python's max().
1319 pass
1331 pass
1320 return baseset()
1332 return baseset()
1321
1333
1322 @predicate('merge()', safe=True)
1334 @predicate('merge()', safe=True)
1323 def merge(repo, subset, x):
1335 def merge(repo, subset, x):
1324 """Changeset is a merge changeset.
1336 """Changeset is a merge changeset.
1325 """
1337 """
1326 # i18n: "merge" is a keyword
1338 # i18n: "merge" is a keyword
1327 getargs(x, 0, 0, _("merge takes no arguments"))
1339 getargs(x, 0, 0, _("merge takes no arguments"))
1328 cl = repo.changelog
1340 cl = repo.changelog
1329 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1341 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1342 condrepr='<merge>')
1330
1343
1331 @predicate('branchpoint()', safe=True)
1344 @predicate('branchpoint()', safe=True)
1332 def branchpoint(repo, subset, x):
1345 def branchpoint(repo, subset, x):
1333 """Changesets with more than one child.
1346 """Changesets with more than one child.
1334 """
1347 """
1335 # i18n: "branchpoint" is a keyword
1348 # i18n: "branchpoint" is a keyword
1336 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1349 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1337 cl = repo.changelog
1350 cl = repo.changelog
1338 if not subset:
1351 if not subset:
1339 return baseset()
1352 return baseset()
1340 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1353 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1341 # (and if it is not, it should.)
1354 # (and if it is not, it should.)
1342 baserev = min(subset)
1355 baserev = min(subset)
1343 parentscount = [0]*(len(repo) - baserev)
1356 parentscount = [0]*(len(repo) - baserev)
1344 for r in cl.revs(start=baserev + 1):
1357 for r in cl.revs(start=baserev + 1):
1345 for p in cl.parentrevs(r):
1358 for p in cl.parentrevs(r):
1346 if p >= baserev:
1359 if p >= baserev:
1347 parentscount[p - baserev] += 1
1360 parentscount[p - baserev] += 1
1348 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1361 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1362 condrepr='<branchpoint>')
1349
1363
1350 @predicate('min(set)', safe=True)
1364 @predicate('min(set)', safe=True)
1351 def minrev(repo, subset, x):
1365 def minrev(repo, subset, x):
1352 """Changeset with lowest revision number in set.
1366 """Changeset with lowest revision number in set.
1353 """
1367 """
1354 os = getset(repo, fullreposet(repo), x)
1368 os = getset(repo, fullreposet(repo), x)
1355 try:
1369 try:
1356 m = os.min()
1370 m = os.min()
1357 if m in subset:
1371 if m in subset:
1358 return baseset([m])
1372 return baseset([m])
1359 except ValueError:
1373 except ValueError:
1360 # os.min() throws a ValueError when the collection is empty.
1374 # os.min() throws a ValueError when the collection is empty.
1361 # Same as python's min().
1375 # Same as python's min().
1362 pass
1376 pass
1363 return baseset()
1377 return baseset()
1364
1378
1365 @predicate('modifies(pattern)', safe=True)
1379 @predicate('modifies(pattern)', safe=True)
1366 def modifies(repo, subset, x):
1380 def modifies(repo, subset, x):
1367 """Changesets modifying files matched by pattern.
1381 """Changesets modifying files matched by pattern.
1368
1382
1369 The pattern without explicit kind like ``glob:`` is expected to be
1383 The pattern without explicit kind like ``glob:`` is expected to be
1370 relative to the current directory and match against a file or a
1384 relative to the current directory and match against a file or a
1371 directory.
1385 directory.
1372 """
1386 """
1373 # i18n: "modifies" is a keyword
1387 # i18n: "modifies" is a keyword
1374 pat = getstring(x, _("modifies requires a pattern"))
1388 pat = getstring(x, _("modifies requires a pattern"))
1375 return checkstatus(repo, subset, pat, 0)
1389 return checkstatus(repo, subset, pat, 0)
1376
1390
1377 @predicate('named(namespace)')
1391 @predicate('named(namespace)')
1378 def named(repo, subset, x):
1392 def named(repo, subset, x):
1379 """The changesets in a given namespace.
1393 """The changesets in a given namespace.
1380
1394
1381 If `namespace` starts with `re:`, the remainder of the string is treated as
1395 If `namespace` starts with `re:`, the remainder of the string is treated as
1382 a regular expression. To match a namespace that actually starts with `re:`,
1396 a regular expression. To match a namespace that actually starts with `re:`,
1383 use the prefix `literal:`.
1397 use the prefix `literal:`.
1384 """
1398 """
1385 # i18n: "named" is a keyword
1399 # i18n: "named" is a keyword
1386 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1400 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1387
1401
1388 ns = getstring(args[0],
1402 ns = getstring(args[0],
1389 # i18n: "named" is a keyword
1403 # i18n: "named" is a keyword
1390 _('the argument to named must be a string'))
1404 _('the argument to named must be a string'))
1391 kind, pattern, matcher = util.stringmatcher(ns)
1405 kind, pattern, matcher = util.stringmatcher(ns)
1392 namespaces = set()
1406 namespaces = set()
1393 if kind == 'literal':
1407 if kind == 'literal':
1394 if pattern not in repo.names:
1408 if pattern not in repo.names:
1395 raise error.RepoLookupError(_("namespace '%s' does not exist")
1409 raise error.RepoLookupError(_("namespace '%s' does not exist")
1396 % ns)
1410 % ns)
1397 namespaces.add(repo.names[pattern])
1411 namespaces.add(repo.names[pattern])
1398 else:
1412 else:
1399 for name, ns in repo.names.iteritems():
1413 for name, ns in repo.names.iteritems():
1400 if matcher(name):
1414 if matcher(name):
1401 namespaces.add(ns)
1415 namespaces.add(ns)
1402 if not namespaces:
1416 if not namespaces:
1403 raise error.RepoLookupError(_("no namespace exists"
1417 raise error.RepoLookupError(_("no namespace exists"
1404 " that match '%s'") % pattern)
1418 " that match '%s'") % pattern)
1405
1419
1406 names = set()
1420 names = set()
1407 for ns in namespaces:
1421 for ns in namespaces:
1408 for name in ns.listnames(repo):
1422 for name in ns.listnames(repo):
1409 if name not in ns.deprecated:
1423 if name not in ns.deprecated:
1410 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1424 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1411
1425
1412 names -= set([node.nullrev])
1426 names -= set([node.nullrev])
1413 return subset & names
1427 return subset & names
1414
1428
1415 @predicate('id(string)', safe=True)
1429 @predicate('id(string)', safe=True)
1416 def node_(repo, subset, x):
1430 def node_(repo, subset, x):
1417 """Revision non-ambiguously specified by the given hex string prefix.
1431 """Revision non-ambiguously specified by the given hex string prefix.
1418 """
1432 """
1419 # i18n: "id" is a keyword
1433 # i18n: "id" is a keyword
1420 l = getargs(x, 1, 1, _("id requires one argument"))
1434 l = getargs(x, 1, 1, _("id requires one argument"))
1421 # i18n: "id" is a keyword
1435 # i18n: "id" is a keyword
1422 n = getstring(l[0], _("id requires a string"))
1436 n = getstring(l[0], _("id requires a string"))
1423 if len(n) == 40:
1437 if len(n) == 40:
1424 try:
1438 try:
1425 rn = repo.changelog.rev(node.bin(n))
1439 rn = repo.changelog.rev(node.bin(n))
1426 except (LookupError, TypeError):
1440 except (LookupError, TypeError):
1427 rn = None
1441 rn = None
1428 else:
1442 else:
1429 rn = None
1443 rn = None
1430 pm = repo.changelog._partialmatch(n)
1444 pm = repo.changelog._partialmatch(n)
1431 if pm is not None:
1445 if pm is not None:
1432 rn = repo.changelog.rev(pm)
1446 rn = repo.changelog.rev(pm)
1433
1447
1434 if rn is None:
1448 if rn is None:
1435 return baseset()
1449 return baseset()
1436 result = baseset([rn])
1450 result = baseset([rn])
1437 return result & subset
1451 return result & subset
1438
1452
1439 @predicate('obsolete()', safe=True)
1453 @predicate('obsolete()', safe=True)
1440 def obsolete(repo, subset, x):
1454 def obsolete(repo, subset, x):
1441 """Mutable changeset with a newer version."""
1455 """Mutable changeset with a newer version."""
1442 # i18n: "obsolete" is a keyword
1456 # i18n: "obsolete" is a keyword
1443 getargs(x, 0, 0, _("obsolete takes no arguments"))
1457 getargs(x, 0, 0, _("obsolete takes no arguments"))
1444 obsoletes = obsmod.getrevs(repo, 'obsolete')
1458 obsoletes = obsmod.getrevs(repo, 'obsolete')
1445 return subset & obsoletes
1459 return subset & obsoletes
1446
1460
1447 @predicate('only(set, [set])', safe=True)
1461 @predicate('only(set, [set])', safe=True)
1448 def only(repo, subset, x):
1462 def only(repo, subset, x):
1449 """Changesets that are ancestors of the first set that are not ancestors
1463 """Changesets that are ancestors of the first set that are not ancestors
1450 of any other head in the repo. If a second set is specified, the result
1464 of any other head in the repo. If a second set is specified, the result
1451 is ancestors of the first set that are not ancestors of the second set
1465 is ancestors of the first set that are not ancestors of the second set
1452 (i.e. ::<set1> - ::<set2>).
1466 (i.e. ::<set1> - ::<set2>).
1453 """
1467 """
1454 cl = repo.changelog
1468 cl = repo.changelog
1455 # i18n: "only" is a keyword
1469 # i18n: "only" is a keyword
1456 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1470 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1457 include = getset(repo, fullreposet(repo), args[0])
1471 include = getset(repo, fullreposet(repo), args[0])
1458 if len(args) == 1:
1472 if len(args) == 1:
1459 if not include:
1473 if not include:
1460 return baseset()
1474 return baseset()
1461
1475
1462 descendants = set(_revdescendants(repo, include, False))
1476 descendants = set(_revdescendants(repo, include, False))
1463 exclude = [rev for rev in cl.headrevs()
1477 exclude = [rev for rev in cl.headrevs()
1464 if not rev in descendants and not rev in include]
1478 if not rev in descendants and not rev in include]
1465 else:
1479 else:
1466 exclude = getset(repo, fullreposet(repo), args[1])
1480 exclude = getset(repo, fullreposet(repo), args[1])
1467
1481
1468 results = set(cl.findmissingrevs(common=exclude, heads=include))
1482 results = set(cl.findmissingrevs(common=exclude, heads=include))
1469 # XXX we should turn this into a baseset instead of a set, smartset may do
1483 # XXX we should turn this into a baseset instead of a set, smartset may do
1470 # some optimisations from the fact this is a baseset.
1484 # some optimisations from the fact this is a baseset.
1471 return subset & results
1485 return subset & results
1472
1486
1473 @predicate('origin([set])', safe=True)
1487 @predicate('origin([set])', safe=True)
1474 def origin(repo, subset, x):
1488 def origin(repo, subset, x):
1475 """
1489 """
1476 Changesets that were specified as a source for the grafts, transplants or
1490 Changesets that were specified as a source for the grafts, transplants or
1477 rebases that created the given revisions. Omitting the optional set is the
1491 rebases that created the given revisions. Omitting the optional set is the
1478 same as passing all(). If a changeset created by these operations is itself
1492 same as passing all(). If a changeset created by these operations is itself
1479 specified as a source for one of these operations, only the source changeset
1493 specified as a source for one of these operations, only the source changeset
1480 for the first operation is selected.
1494 for the first operation is selected.
1481 """
1495 """
1482 if x is not None:
1496 if x is not None:
1483 dests = getset(repo, fullreposet(repo), x)
1497 dests = getset(repo, fullreposet(repo), x)
1484 else:
1498 else:
1485 dests = fullreposet(repo)
1499 dests = fullreposet(repo)
1486
1500
1487 def _firstsrc(rev):
1501 def _firstsrc(rev):
1488 src = _getrevsource(repo, rev)
1502 src = _getrevsource(repo, rev)
1489 if src is None:
1503 if src is None:
1490 return None
1504 return None
1491
1505
1492 while True:
1506 while True:
1493 prev = _getrevsource(repo, src)
1507 prev = _getrevsource(repo, src)
1494
1508
1495 if prev is None:
1509 if prev is None:
1496 return src
1510 return src
1497 src = prev
1511 src = prev
1498
1512
1499 o = set([_firstsrc(r) for r in dests])
1513 o = set([_firstsrc(r) for r in dests])
1500 o -= set([None])
1514 o -= set([None])
1501 # XXX we should turn this into a baseset instead of a set, smartset may do
1515 # XXX we should turn this into a baseset instead of a set, smartset may do
1502 # some optimisations from the fact this is a baseset.
1516 # some optimisations from the fact this is a baseset.
1503 return subset & o
1517 return subset & o
1504
1518
1505 @predicate('outgoing([path])', safe=True)
1519 @predicate('outgoing([path])', safe=True)
1506 def outgoing(repo, subset, x):
1520 def outgoing(repo, subset, x):
1507 """Changesets not found in the specified destination repository, or the
1521 """Changesets not found in the specified destination repository, or the
1508 default push location.
1522 default push location.
1509 """
1523 """
1510 # Avoid cycles.
1524 # Avoid cycles.
1511 from . import (
1525 from . import (
1512 discovery,
1526 discovery,
1513 hg,
1527 hg,
1514 )
1528 )
1515 # i18n: "outgoing" is a keyword
1529 # i18n: "outgoing" is a keyword
1516 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1530 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1517 # i18n: "outgoing" is a keyword
1531 # i18n: "outgoing" is a keyword
1518 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1532 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1519 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1533 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1520 dest, branches = hg.parseurl(dest)
1534 dest, branches = hg.parseurl(dest)
1521 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1535 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1522 if revs:
1536 if revs:
1523 revs = [repo.lookup(rev) for rev in revs]
1537 revs = [repo.lookup(rev) for rev in revs]
1524 other = hg.peer(repo, {}, dest)
1538 other = hg.peer(repo, {}, dest)
1525 repo.ui.pushbuffer()
1539 repo.ui.pushbuffer()
1526 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1540 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1527 repo.ui.popbuffer()
1541 repo.ui.popbuffer()
1528 cl = repo.changelog
1542 cl = repo.changelog
1529 o = set([cl.rev(r) for r in outgoing.missing])
1543 o = set([cl.rev(r) for r in outgoing.missing])
1530 return subset & o
1544 return subset & o
1531
1545
1532 @predicate('p1([set])', safe=True)
1546 @predicate('p1([set])', safe=True)
1533 def p1(repo, subset, x):
1547 def p1(repo, subset, x):
1534 """First parent of changesets in set, or the working directory.
1548 """First parent of changesets in set, or the working directory.
1535 """
1549 """
1536 if x is None:
1550 if x is None:
1537 p = repo[x].p1().rev()
1551 p = repo[x].p1().rev()
1538 if p >= 0:
1552 if p >= 0:
1539 return subset & baseset([p])
1553 return subset & baseset([p])
1540 return baseset()
1554 return baseset()
1541
1555
1542 ps = set()
1556 ps = set()
1543 cl = repo.changelog
1557 cl = repo.changelog
1544 for r in getset(repo, fullreposet(repo), x):
1558 for r in getset(repo, fullreposet(repo), x):
1545 ps.add(cl.parentrevs(r)[0])
1559 ps.add(cl.parentrevs(r)[0])
1546 ps -= set([node.nullrev])
1560 ps -= set([node.nullrev])
1547 # XXX we should turn this into a baseset instead of a set, smartset may do
1561 # XXX we should turn this into a baseset instead of a set, smartset may do
1548 # some optimisations from the fact this is a baseset.
1562 # some optimisations from the fact this is a baseset.
1549 return subset & ps
1563 return subset & ps
1550
1564
1551 @predicate('p2([set])', safe=True)
1565 @predicate('p2([set])', safe=True)
1552 def p2(repo, subset, x):
1566 def p2(repo, subset, x):
1553 """Second parent of changesets in set, or the working directory.
1567 """Second parent of changesets in set, or the working directory.
1554 """
1568 """
1555 if x is None:
1569 if x is None:
1556 ps = repo[x].parents()
1570 ps = repo[x].parents()
1557 try:
1571 try:
1558 p = ps[1].rev()
1572 p = ps[1].rev()
1559 if p >= 0:
1573 if p >= 0:
1560 return subset & baseset([p])
1574 return subset & baseset([p])
1561 return baseset()
1575 return baseset()
1562 except IndexError:
1576 except IndexError:
1563 return baseset()
1577 return baseset()
1564
1578
1565 ps = set()
1579 ps = set()
1566 cl = repo.changelog
1580 cl = repo.changelog
1567 for r in getset(repo, fullreposet(repo), x):
1581 for r in getset(repo, fullreposet(repo), x):
1568 ps.add(cl.parentrevs(r)[1])
1582 ps.add(cl.parentrevs(r)[1])
1569 ps -= set([node.nullrev])
1583 ps -= set([node.nullrev])
1570 # XXX we should turn this into a baseset instead of a set, smartset may do
1584 # XXX we should turn this into a baseset instead of a set, smartset may do
1571 # some optimisations from the fact this is a baseset.
1585 # some optimisations from the fact this is a baseset.
1572 return subset & ps
1586 return subset & ps
1573
1587
1574 @predicate('parents([set])', safe=True)
1588 @predicate('parents([set])', safe=True)
1575 def parents(repo, subset, x):
1589 def parents(repo, subset, x):
1576 """
1590 """
1577 The set of all parents for all changesets in set, or the working directory.
1591 The set of all parents for all changesets in set, or the working directory.
1578 """
1592 """
1579 if x is None:
1593 if x is None:
1580 ps = set(p.rev() for p in repo[x].parents())
1594 ps = set(p.rev() for p in repo[x].parents())
1581 else:
1595 else:
1582 ps = set()
1596 ps = set()
1583 cl = repo.changelog
1597 cl = repo.changelog
1584 up = ps.update
1598 up = ps.update
1585 parentrevs = cl.parentrevs
1599 parentrevs = cl.parentrevs
1586 for r in getset(repo, fullreposet(repo), x):
1600 for r in getset(repo, fullreposet(repo), x):
1587 if r == node.wdirrev:
1601 if r == node.wdirrev:
1588 up(p.rev() for p in repo[r].parents())
1602 up(p.rev() for p in repo[r].parents())
1589 else:
1603 else:
1590 up(parentrevs(r))
1604 up(parentrevs(r))
1591 ps -= set([node.nullrev])
1605 ps -= set([node.nullrev])
1592 return subset & ps
1606 return subset & ps
1593
1607
1594 def _phase(repo, subset, target):
1608 def _phase(repo, subset, target):
1595 """helper to select all rev in phase <target>"""
1609 """helper to select all rev in phase <target>"""
1596 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1610 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1597 if repo._phasecache._phasesets:
1611 if repo._phasecache._phasesets:
1598 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1612 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1599 s = baseset(s)
1613 s = baseset(s)
1600 s.sort() # set are non ordered, so we enforce ascending
1614 s.sort() # set are non ordered, so we enforce ascending
1601 return subset & s
1615 return subset & s
1602 else:
1616 else:
1603 phase = repo._phasecache.phase
1617 phase = repo._phasecache.phase
1604 condition = lambda r: phase(repo, r) == target
1618 condition = lambda r: phase(repo, r) == target
1605 return subset.filter(condition, cache=False)
1619 return subset.filter(condition, condrepr=('<phase %r>', target),
1620 cache=False)
1606
1621
1607 @predicate('draft()', safe=True)
1622 @predicate('draft()', safe=True)
1608 def draft(repo, subset, x):
1623 def draft(repo, subset, x):
1609 """Changeset in draft phase."""
1624 """Changeset in draft phase."""
1610 # i18n: "draft" is a keyword
1625 # i18n: "draft" is a keyword
1611 getargs(x, 0, 0, _("draft takes no arguments"))
1626 getargs(x, 0, 0, _("draft takes no arguments"))
1612 target = phases.draft
1627 target = phases.draft
1613 return _phase(repo, subset, target)
1628 return _phase(repo, subset, target)
1614
1629
1615 @predicate('secret()', safe=True)
1630 @predicate('secret()', safe=True)
1616 def secret(repo, subset, x):
1631 def secret(repo, subset, x):
1617 """Changeset in secret phase."""
1632 """Changeset in secret phase."""
1618 # i18n: "secret" is a keyword
1633 # i18n: "secret" is a keyword
1619 getargs(x, 0, 0, _("secret takes no arguments"))
1634 getargs(x, 0, 0, _("secret takes no arguments"))
1620 target = phases.secret
1635 target = phases.secret
1621 return _phase(repo, subset, target)
1636 return _phase(repo, subset, target)
1622
1637
1623 def parentspec(repo, subset, x, n):
1638 def parentspec(repo, subset, x, n):
1624 """``set^0``
1639 """``set^0``
1625 The set.
1640 The set.
1626 ``set^1`` (or ``set^``), ``set^2``
1641 ``set^1`` (or ``set^``), ``set^2``
1627 First or second parent, respectively, of all changesets in set.
1642 First or second parent, respectively, of all changesets in set.
1628 """
1643 """
1629 try:
1644 try:
1630 n = int(n[1])
1645 n = int(n[1])
1631 if n not in (0, 1, 2):
1646 if n not in (0, 1, 2):
1632 raise ValueError
1647 raise ValueError
1633 except (TypeError, ValueError):
1648 except (TypeError, ValueError):
1634 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1649 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1635 ps = set()
1650 ps = set()
1636 cl = repo.changelog
1651 cl = repo.changelog
1637 for r in getset(repo, fullreposet(repo), x):
1652 for r in getset(repo, fullreposet(repo), x):
1638 if n == 0:
1653 if n == 0:
1639 ps.add(r)
1654 ps.add(r)
1640 elif n == 1:
1655 elif n == 1:
1641 ps.add(cl.parentrevs(r)[0])
1656 ps.add(cl.parentrevs(r)[0])
1642 elif n == 2:
1657 elif n == 2:
1643 parents = cl.parentrevs(r)
1658 parents = cl.parentrevs(r)
1644 if len(parents) > 1:
1659 if len(parents) > 1:
1645 ps.add(parents[1])
1660 ps.add(parents[1])
1646 return subset & ps
1661 return subset & ps
1647
1662
1648 @predicate('present(set)', safe=True)
1663 @predicate('present(set)', safe=True)
1649 def present(repo, subset, x):
1664 def present(repo, subset, x):
1650 """An empty set, if any revision in set isn't found; otherwise,
1665 """An empty set, if any revision in set isn't found; otherwise,
1651 all revisions in set.
1666 all revisions in set.
1652
1667
1653 If any of specified revisions is not present in the local repository,
1668 If any of specified revisions is not present in the local repository,
1654 the query is normally aborted. But this predicate allows the query
1669 the query is normally aborted. But this predicate allows the query
1655 to continue even in such cases.
1670 to continue even in such cases.
1656 """
1671 """
1657 try:
1672 try:
1658 return getset(repo, subset, x)
1673 return getset(repo, subset, x)
1659 except error.RepoLookupError:
1674 except error.RepoLookupError:
1660 return baseset()
1675 return baseset()
1661
1676
1662 # for internal use
1677 # for internal use
1663 @predicate('_notpublic', safe=True)
1678 @predicate('_notpublic', safe=True)
1664 def _notpublic(repo, subset, x):
1679 def _notpublic(repo, subset, x):
1665 getargs(x, 0, 0, "_notpublic takes no arguments")
1680 getargs(x, 0, 0, "_notpublic takes no arguments")
1666 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1681 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1667 if repo._phasecache._phasesets:
1682 if repo._phasecache._phasesets:
1668 s = set()
1683 s = set()
1669 for u in repo._phasecache._phasesets[1:]:
1684 for u in repo._phasecache._phasesets[1:]:
1670 s.update(u)
1685 s.update(u)
1671 s = baseset(s - repo.changelog.filteredrevs)
1686 s = baseset(s - repo.changelog.filteredrevs)
1672 s.sort()
1687 s.sort()
1673 return subset & s
1688 return subset & s
1674 else:
1689 else:
1675 phase = repo._phasecache.phase
1690 phase = repo._phasecache.phase
1676 target = phases.public
1691 target = phases.public
1677 condition = lambda r: phase(repo, r) != target
1692 condition = lambda r: phase(repo, r) != target
1678 return subset.filter(condition, cache=False)
1693 return subset.filter(condition, condrepr=('<phase %r>', target),
1694 cache=False)
1679
1695
1680 @predicate('public()', safe=True)
1696 @predicate('public()', safe=True)
1681 def public(repo, subset, x):
1697 def public(repo, subset, x):
1682 """Changeset in public phase."""
1698 """Changeset in public phase."""
1683 # i18n: "public" is a keyword
1699 # i18n: "public" is a keyword
1684 getargs(x, 0, 0, _("public takes no arguments"))
1700 getargs(x, 0, 0, _("public takes no arguments"))
1685 phase = repo._phasecache.phase
1701 phase = repo._phasecache.phase
1686 target = phases.public
1702 target = phases.public
1687 condition = lambda r: phase(repo, r) == target
1703 condition = lambda r: phase(repo, r) == target
1688 return subset.filter(condition, cache=False)
1704 return subset.filter(condition, condrepr=('<phase %r>', target),
1705 cache=False)
1689
1706
1690 @predicate('remote([id [,path]])', safe=True)
1707 @predicate('remote([id [,path]])', safe=True)
1691 def remote(repo, subset, x):
1708 def remote(repo, subset, x):
1692 """Local revision that corresponds to the given identifier in a
1709 """Local revision that corresponds to the given identifier in a
1693 remote repository, if present. Here, the '.' identifier is a
1710 remote repository, if present. Here, the '.' identifier is a
1694 synonym for the current local branch.
1711 synonym for the current local branch.
1695 """
1712 """
1696
1713
1697 from . import hg # avoid start-up nasties
1714 from . import hg # avoid start-up nasties
1698 # i18n: "remote" is a keyword
1715 # i18n: "remote" is a keyword
1699 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1716 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1700
1717
1701 q = '.'
1718 q = '.'
1702 if len(l) > 0:
1719 if len(l) > 0:
1703 # i18n: "remote" is a keyword
1720 # i18n: "remote" is a keyword
1704 q = getstring(l[0], _("remote requires a string id"))
1721 q = getstring(l[0], _("remote requires a string id"))
1705 if q == '.':
1722 if q == '.':
1706 q = repo['.'].branch()
1723 q = repo['.'].branch()
1707
1724
1708 dest = ''
1725 dest = ''
1709 if len(l) > 1:
1726 if len(l) > 1:
1710 # i18n: "remote" is a keyword
1727 # i18n: "remote" is a keyword
1711 dest = getstring(l[1], _("remote requires a repository path"))
1728 dest = getstring(l[1], _("remote requires a repository path"))
1712 dest = repo.ui.expandpath(dest or 'default')
1729 dest = repo.ui.expandpath(dest or 'default')
1713 dest, branches = hg.parseurl(dest)
1730 dest, branches = hg.parseurl(dest)
1714 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1731 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1715 if revs:
1732 if revs:
1716 revs = [repo.lookup(rev) for rev in revs]
1733 revs = [repo.lookup(rev) for rev in revs]
1717 other = hg.peer(repo, {}, dest)
1734 other = hg.peer(repo, {}, dest)
1718 n = other.lookup(q)
1735 n = other.lookup(q)
1719 if n in repo:
1736 if n in repo:
1720 r = repo[n].rev()
1737 r = repo[n].rev()
1721 if r in subset:
1738 if r in subset:
1722 return baseset([r])
1739 return baseset([r])
1723 return baseset()
1740 return baseset()
1724
1741
1725 @predicate('removes(pattern)', safe=True)
1742 @predicate('removes(pattern)', safe=True)
1726 def removes(repo, subset, x):
1743 def removes(repo, subset, x):
1727 """Changesets which remove files matching pattern.
1744 """Changesets which remove files matching pattern.
1728
1745
1729 The pattern without explicit kind like ``glob:`` is expected to be
1746 The pattern without explicit kind like ``glob:`` is expected to be
1730 relative to the current directory and match against a file or a
1747 relative to the current directory and match against a file or a
1731 directory.
1748 directory.
1732 """
1749 """
1733 # i18n: "removes" is a keyword
1750 # i18n: "removes" is a keyword
1734 pat = getstring(x, _("removes requires a pattern"))
1751 pat = getstring(x, _("removes requires a pattern"))
1735 return checkstatus(repo, subset, pat, 2)
1752 return checkstatus(repo, subset, pat, 2)
1736
1753
1737 @predicate('rev(number)', safe=True)
1754 @predicate('rev(number)', safe=True)
1738 def rev(repo, subset, x):
1755 def rev(repo, subset, x):
1739 """Revision with the given numeric identifier.
1756 """Revision with the given numeric identifier.
1740 """
1757 """
1741 # i18n: "rev" is a keyword
1758 # i18n: "rev" is a keyword
1742 l = getargs(x, 1, 1, _("rev requires one argument"))
1759 l = getargs(x, 1, 1, _("rev requires one argument"))
1743 try:
1760 try:
1744 # i18n: "rev" is a keyword
1761 # i18n: "rev" is a keyword
1745 l = int(getstring(l[0], _("rev requires a number")))
1762 l = int(getstring(l[0], _("rev requires a number")))
1746 except (TypeError, ValueError):
1763 except (TypeError, ValueError):
1747 # i18n: "rev" is a keyword
1764 # i18n: "rev" is a keyword
1748 raise error.ParseError(_("rev expects a number"))
1765 raise error.ParseError(_("rev expects a number"))
1749 if l not in repo.changelog and l != node.nullrev:
1766 if l not in repo.changelog and l != node.nullrev:
1750 return baseset()
1767 return baseset()
1751 return subset & baseset([l])
1768 return subset & baseset([l])
1752
1769
1753 @predicate('matching(revision [, field])', safe=True)
1770 @predicate('matching(revision [, field])', safe=True)
1754 def matching(repo, subset, x):
1771 def matching(repo, subset, x):
1755 """Changesets in which a given set of fields match the set of fields in the
1772 """Changesets in which a given set of fields match the set of fields in the
1756 selected revision or set.
1773 selected revision or set.
1757
1774
1758 To match more than one field pass the list of fields to match separated
1775 To match more than one field pass the list of fields to match separated
1759 by spaces (e.g. ``author description``).
1776 by spaces (e.g. ``author description``).
1760
1777
1761 Valid fields are most regular revision fields and some special fields.
1778 Valid fields are most regular revision fields and some special fields.
1762
1779
1763 Regular revision fields are ``description``, ``author``, ``branch``,
1780 Regular revision fields are ``description``, ``author``, ``branch``,
1764 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1781 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1765 and ``diff``.
1782 and ``diff``.
1766 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1783 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1767 contents of the revision. Two revisions matching their ``diff`` will
1784 contents of the revision. Two revisions matching their ``diff`` will
1768 also match their ``files``.
1785 also match their ``files``.
1769
1786
1770 Special fields are ``summary`` and ``metadata``:
1787 Special fields are ``summary`` and ``metadata``:
1771 ``summary`` matches the first line of the description.
1788 ``summary`` matches the first line of the description.
1772 ``metadata`` is equivalent to matching ``description user date``
1789 ``metadata`` is equivalent to matching ``description user date``
1773 (i.e. it matches the main metadata fields).
1790 (i.e. it matches the main metadata fields).
1774
1791
1775 ``metadata`` is the default field which is used when no fields are
1792 ``metadata`` is the default field which is used when no fields are
1776 specified. You can match more than one field at a time.
1793 specified. You can match more than one field at a time.
1777 """
1794 """
1778 # i18n: "matching" is a keyword
1795 # i18n: "matching" is a keyword
1779 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1796 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1780
1797
1781 revs = getset(repo, fullreposet(repo), l[0])
1798 revs = getset(repo, fullreposet(repo), l[0])
1782
1799
1783 fieldlist = ['metadata']
1800 fieldlist = ['metadata']
1784 if len(l) > 1:
1801 if len(l) > 1:
1785 fieldlist = getstring(l[1],
1802 fieldlist = getstring(l[1],
1786 # i18n: "matching" is a keyword
1803 # i18n: "matching" is a keyword
1787 _("matching requires a string "
1804 _("matching requires a string "
1788 "as its second argument")).split()
1805 "as its second argument")).split()
1789
1806
1790 # Make sure that there are no repeated fields,
1807 # Make sure that there are no repeated fields,
1791 # expand the 'special' 'metadata' field type
1808 # expand the 'special' 'metadata' field type
1792 # and check the 'files' whenever we check the 'diff'
1809 # and check the 'files' whenever we check the 'diff'
1793 fields = []
1810 fields = []
1794 for field in fieldlist:
1811 for field in fieldlist:
1795 if field == 'metadata':
1812 if field == 'metadata':
1796 fields += ['user', 'description', 'date']
1813 fields += ['user', 'description', 'date']
1797 elif field == 'diff':
1814 elif field == 'diff':
1798 # a revision matching the diff must also match the files
1815 # a revision matching the diff must also match the files
1799 # since matching the diff is very costly, make sure to
1816 # since matching the diff is very costly, make sure to
1800 # also match the files first
1817 # also match the files first
1801 fields += ['files', 'diff']
1818 fields += ['files', 'diff']
1802 else:
1819 else:
1803 if field == 'author':
1820 if field == 'author':
1804 field = 'user'
1821 field = 'user'
1805 fields.append(field)
1822 fields.append(field)
1806 fields = set(fields)
1823 fields = set(fields)
1807 if 'summary' in fields and 'description' in fields:
1824 if 'summary' in fields and 'description' in fields:
1808 # If a revision matches its description it also matches its summary
1825 # If a revision matches its description it also matches its summary
1809 fields.discard('summary')
1826 fields.discard('summary')
1810
1827
1811 # We may want to match more than one field
1828 # We may want to match more than one field
1812 # Not all fields take the same amount of time to be matched
1829 # Not all fields take the same amount of time to be matched
1813 # Sort the selected fields in order of increasing matching cost
1830 # Sort the selected fields in order of increasing matching cost
1814 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1831 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1815 'files', 'description', 'substate', 'diff']
1832 'files', 'description', 'substate', 'diff']
1816 def fieldkeyfunc(f):
1833 def fieldkeyfunc(f):
1817 try:
1834 try:
1818 return fieldorder.index(f)
1835 return fieldorder.index(f)
1819 except ValueError:
1836 except ValueError:
1820 # assume an unknown field is very costly
1837 # assume an unknown field is very costly
1821 return len(fieldorder)
1838 return len(fieldorder)
1822 fields = list(fields)
1839 fields = list(fields)
1823 fields.sort(key=fieldkeyfunc)
1840 fields.sort(key=fieldkeyfunc)
1824
1841
1825 # Each field will be matched with its own "getfield" function
1842 # Each field will be matched with its own "getfield" function
1826 # which will be added to the getfieldfuncs array of functions
1843 # which will be added to the getfieldfuncs array of functions
1827 getfieldfuncs = []
1844 getfieldfuncs = []
1828 _funcs = {
1845 _funcs = {
1829 'user': lambda r: repo[r].user(),
1846 'user': lambda r: repo[r].user(),
1830 'branch': lambda r: repo[r].branch(),
1847 'branch': lambda r: repo[r].branch(),
1831 'date': lambda r: repo[r].date(),
1848 'date': lambda r: repo[r].date(),
1832 'description': lambda r: repo[r].description(),
1849 'description': lambda r: repo[r].description(),
1833 'files': lambda r: repo[r].files(),
1850 'files': lambda r: repo[r].files(),
1834 'parents': lambda r: repo[r].parents(),
1851 'parents': lambda r: repo[r].parents(),
1835 'phase': lambda r: repo[r].phase(),
1852 'phase': lambda r: repo[r].phase(),
1836 'substate': lambda r: repo[r].substate,
1853 'substate': lambda r: repo[r].substate,
1837 'summary': lambda r: repo[r].description().splitlines()[0],
1854 'summary': lambda r: repo[r].description().splitlines()[0],
1838 'diff': lambda r: list(repo[r].diff(git=True),)
1855 'diff': lambda r: list(repo[r].diff(git=True),)
1839 }
1856 }
1840 for info in fields:
1857 for info in fields:
1841 getfield = _funcs.get(info, None)
1858 getfield = _funcs.get(info, None)
1842 if getfield is None:
1859 if getfield is None:
1843 raise error.ParseError(
1860 raise error.ParseError(
1844 # i18n: "matching" is a keyword
1861 # i18n: "matching" is a keyword
1845 _("unexpected field name passed to matching: %s") % info)
1862 _("unexpected field name passed to matching: %s") % info)
1846 getfieldfuncs.append(getfield)
1863 getfieldfuncs.append(getfield)
1847 # convert the getfield array of functions into a "getinfo" function
1864 # convert the getfield array of functions into a "getinfo" function
1848 # which returns an array of field values (or a single value if there
1865 # which returns an array of field values (or a single value if there
1849 # is only one field to match)
1866 # is only one field to match)
1850 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1867 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1851
1868
1852 def matches(x):
1869 def matches(x):
1853 for rev in revs:
1870 for rev in revs:
1854 target = getinfo(rev)
1871 target = getinfo(rev)
1855 match = True
1872 match = True
1856 for n, f in enumerate(getfieldfuncs):
1873 for n, f in enumerate(getfieldfuncs):
1857 if target[n] != f(x):
1874 if target[n] != f(x):
1858 match = False
1875 match = False
1859 if match:
1876 if match:
1860 return True
1877 return True
1861 return False
1878 return False
1862
1879
1863 return subset.filter(matches)
1880 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1864
1881
1865 @predicate('reverse(set)', safe=True)
1882 @predicate('reverse(set)', safe=True)
1866 def reverse(repo, subset, x):
1883 def reverse(repo, subset, x):
1867 """Reverse order of set.
1884 """Reverse order of set.
1868 """
1885 """
1869 l = getset(repo, subset, x)
1886 l = getset(repo, subset, x)
1870 l.reverse()
1887 l.reverse()
1871 return l
1888 return l
1872
1889
1873 @predicate('roots(set)', safe=True)
1890 @predicate('roots(set)', safe=True)
1874 def roots(repo, subset, x):
1891 def roots(repo, subset, x):
1875 """Changesets in set with no parent changeset in set.
1892 """Changesets in set with no parent changeset in set.
1876 """
1893 """
1877 s = getset(repo, fullreposet(repo), x)
1894 s = getset(repo, fullreposet(repo), x)
1878 parents = repo.changelog.parentrevs
1895 parents = repo.changelog.parentrevs
1879 def filter(r):
1896 def filter(r):
1880 for p in parents(r):
1897 for p in parents(r):
1881 if 0 <= p and p in s:
1898 if 0 <= p and p in s:
1882 return False
1899 return False
1883 return True
1900 return True
1884 return subset & s.filter(filter)
1901 return subset & s.filter(filter, condrepr='<roots>')
1885
1902
1886 @predicate('sort(set[, [-]key...])', safe=True)
1903 @predicate('sort(set[, [-]key...])', safe=True)
1887 def sort(repo, subset, x):
1904 def sort(repo, subset, x):
1888 """Sort set by keys. The default sort order is ascending, specify a key
1905 """Sort set by keys. The default sort order is ascending, specify a key
1889 as ``-key`` to sort in descending order.
1906 as ``-key`` to sort in descending order.
1890
1907
1891 The keys can be:
1908 The keys can be:
1892
1909
1893 - ``rev`` for the revision number,
1910 - ``rev`` for the revision number,
1894 - ``branch`` for the branch name,
1911 - ``branch`` for the branch name,
1895 - ``desc`` for the commit message (description),
1912 - ``desc`` for the commit message (description),
1896 - ``user`` for user name (``author`` can be used as an alias),
1913 - ``user`` for user name (``author`` can be used as an alias),
1897 - ``date`` for the commit date
1914 - ``date`` for the commit date
1898 """
1915 """
1899 # i18n: "sort" is a keyword
1916 # i18n: "sort" is a keyword
1900 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1917 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1901 keys = "rev"
1918 keys = "rev"
1902 if len(l) == 2:
1919 if len(l) == 2:
1903 # i18n: "sort" is a keyword
1920 # i18n: "sort" is a keyword
1904 keys = getstring(l[1], _("sort spec must be a string"))
1921 keys = getstring(l[1], _("sort spec must be a string"))
1905
1922
1906 s = l[0]
1923 s = l[0]
1907 keys = keys.split()
1924 keys = keys.split()
1908 l = []
1925 l = []
1909 def invert(s):
1926 def invert(s):
1910 return "".join(chr(255 - ord(c)) for c in s)
1927 return "".join(chr(255 - ord(c)) for c in s)
1911 revs = getset(repo, subset, s)
1928 revs = getset(repo, subset, s)
1912 if keys == ["rev"]:
1929 if keys == ["rev"]:
1913 revs.sort()
1930 revs.sort()
1914 return revs
1931 return revs
1915 elif keys == ["-rev"]:
1932 elif keys == ["-rev"]:
1916 revs.sort(reverse=True)
1933 revs.sort(reverse=True)
1917 return revs
1934 return revs
1918 for r in revs:
1935 for r in revs:
1919 c = repo[r]
1936 c = repo[r]
1920 e = []
1937 e = []
1921 for k in keys:
1938 for k in keys:
1922 if k == 'rev':
1939 if k == 'rev':
1923 e.append(r)
1940 e.append(r)
1924 elif k == '-rev':
1941 elif k == '-rev':
1925 e.append(-r)
1942 e.append(-r)
1926 elif k == 'branch':
1943 elif k == 'branch':
1927 e.append(c.branch())
1944 e.append(c.branch())
1928 elif k == '-branch':
1945 elif k == '-branch':
1929 e.append(invert(c.branch()))
1946 e.append(invert(c.branch()))
1930 elif k == 'desc':
1947 elif k == 'desc':
1931 e.append(c.description())
1948 e.append(c.description())
1932 elif k == '-desc':
1949 elif k == '-desc':
1933 e.append(invert(c.description()))
1950 e.append(invert(c.description()))
1934 elif k in 'user author':
1951 elif k in 'user author':
1935 e.append(c.user())
1952 e.append(c.user())
1936 elif k in '-user -author':
1953 elif k in '-user -author':
1937 e.append(invert(c.user()))
1954 e.append(invert(c.user()))
1938 elif k == 'date':
1955 elif k == 'date':
1939 e.append(c.date()[0])
1956 e.append(c.date()[0])
1940 elif k == '-date':
1957 elif k == '-date':
1941 e.append(-c.date()[0])
1958 e.append(-c.date()[0])
1942 else:
1959 else:
1943 raise error.ParseError(_("unknown sort key %r") % k)
1960 raise error.ParseError(_("unknown sort key %r") % k)
1944 e.append(r)
1961 e.append(r)
1945 l.append(e)
1962 l.append(e)
1946 l.sort()
1963 l.sort()
1947 return baseset([e[-1] for e in l])
1964 return baseset([e[-1] for e in l])
1948
1965
1949 @predicate('subrepo([pattern])')
1966 @predicate('subrepo([pattern])')
1950 def subrepo(repo, subset, x):
1967 def subrepo(repo, subset, x):
1951 """Changesets that add, modify or remove the given subrepo. If no subrepo
1968 """Changesets that add, modify or remove the given subrepo. If no subrepo
1952 pattern is named, any subrepo changes are returned.
1969 pattern is named, any subrepo changes are returned.
1953 """
1970 """
1954 # i18n: "subrepo" is a keyword
1971 # i18n: "subrepo" is a keyword
1955 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1972 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1956 pat = None
1973 pat = None
1957 if len(args) != 0:
1974 if len(args) != 0:
1958 pat = getstring(args[0], _("subrepo requires a pattern"))
1975 pat = getstring(args[0], _("subrepo requires a pattern"))
1959
1976
1960 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1977 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1961
1978
1962 def submatches(names):
1979 def submatches(names):
1963 k, p, m = util.stringmatcher(pat)
1980 k, p, m = util.stringmatcher(pat)
1964 for name in names:
1981 for name in names:
1965 if m(name):
1982 if m(name):
1966 yield name
1983 yield name
1967
1984
1968 def matches(x):
1985 def matches(x):
1969 c = repo[x]
1986 c = repo[x]
1970 s = repo.status(c.p1().node(), c.node(), match=m)
1987 s = repo.status(c.p1().node(), c.node(), match=m)
1971
1988
1972 if pat is None:
1989 if pat is None:
1973 return s.added or s.modified or s.removed
1990 return s.added or s.modified or s.removed
1974
1991
1975 if s.added:
1992 if s.added:
1976 return any(submatches(c.substate.keys()))
1993 return any(submatches(c.substate.keys()))
1977
1994
1978 if s.modified:
1995 if s.modified:
1979 subs = set(c.p1().substate.keys())
1996 subs = set(c.p1().substate.keys())
1980 subs.update(c.substate.keys())
1997 subs.update(c.substate.keys())
1981
1998
1982 for path in submatches(subs):
1999 for path in submatches(subs):
1983 if c.p1().substate.get(path) != c.substate.get(path):
2000 if c.p1().substate.get(path) != c.substate.get(path):
1984 return True
2001 return True
1985
2002
1986 if s.removed:
2003 if s.removed:
1987 return any(submatches(c.p1().substate.keys()))
2004 return any(submatches(c.p1().substate.keys()))
1988
2005
1989 return False
2006 return False
1990
2007
1991 return subset.filter(matches)
2008 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1992
2009
1993 def _substringmatcher(pattern):
2010 def _substringmatcher(pattern):
1994 kind, pattern, matcher = util.stringmatcher(pattern)
2011 kind, pattern, matcher = util.stringmatcher(pattern)
1995 if kind == 'literal':
2012 if kind == 'literal':
1996 matcher = lambda s: pattern in s
2013 matcher = lambda s: pattern in s
1997 return kind, pattern, matcher
2014 return kind, pattern, matcher
1998
2015
1999 @predicate('tag([name])', safe=True)
2016 @predicate('tag([name])', safe=True)
2000 def tag(repo, subset, x):
2017 def tag(repo, subset, x):
2001 """The specified tag by name, or all tagged revisions if no name is given.
2018 """The specified tag by name, or all tagged revisions if no name is given.
2002
2019
2003 If `name` starts with `re:`, the remainder of the name is treated as
2020 If `name` starts with `re:`, the remainder of the name is treated as
2004 a regular expression. To match a tag that actually starts with `re:`,
2021 a regular expression. To match a tag that actually starts with `re:`,
2005 use the prefix `literal:`.
2022 use the prefix `literal:`.
2006 """
2023 """
2007 # i18n: "tag" is a keyword
2024 # i18n: "tag" is a keyword
2008 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2025 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2009 cl = repo.changelog
2026 cl = repo.changelog
2010 if args:
2027 if args:
2011 pattern = getstring(args[0],
2028 pattern = getstring(args[0],
2012 # i18n: "tag" is a keyword
2029 # i18n: "tag" is a keyword
2013 _('the argument to tag must be a string'))
2030 _('the argument to tag must be a string'))
2014 kind, pattern, matcher = util.stringmatcher(pattern)
2031 kind, pattern, matcher = util.stringmatcher(pattern)
2015 if kind == 'literal':
2032 if kind == 'literal':
2016 # avoid resolving all tags
2033 # avoid resolving all tags
2017 tn = repo._tagscache.tags.get(pattern, None)
2034 tn = repo._tagscache.tags.get(pattern, None)
2018 if tn is None:
2035 if tn is None:
2019 raise error.RepoLookupError(_("tag '%s' does not exist")
2036 raise error.RepoLookupError(_("tag '%s' does not exist")
2020 % pattern)
2037 % pattern)
2021 s = set([repo[tn].rev()])
2038 s = set([repo[tn].rev()])
2022 else:
2039 else:
2023 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2040 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2024 else:
2041 else:
2025 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2042 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2026 return subset & s
2043 return subset & s
2027
2044
2028 @predicate('tagged', safe=True)
2045 @predicate('tagged', safe=True)
2029 def tagged(repo, subset, x):
2046 def tagged(repo, subset, x):
2030 return tag(repo, subset, x)
2047 return tag(repo, subset, x)
2031
2048
2032 @predicate('unstable()', safe=True)
2049 @predicate('unstable()', safe=True)
2033 def unstable(repo, subset, x):
2050 def unstable(repo, subset, x):
2034 """Non-obsolete changesets with obsolete ancestors.
2051 """Non-obsolete changesets with obsolete ancestors.
2035 """
2052 """
2036 # i18n: "unstable" is a keyword
2053 # i18n: "unstable" is a keyword
2037 getargs(x, 0, 0, _("unstable takes no arguments"))
2054 getargs(x, 0, 0, _("unstable takes no arguments"))
2038 unstables = obsmod.getrevs(repo, 'unstable')
2055 unstables = obsmod.getrevs(repo, 'unstable')
2039 return subset & unstables
2056 return subset & unstables
2040
2057
2041
2058
2042 @predicate('user(string)', safe=True)
2059 @predicate('user(string)', safe=True)
2043 def user(repo, subset, x):
2060 def user(repo, subset, x):
2044 """User name contains string. The match is case-insensitive.
2061 """User name contains string. The match is case-insensitive.
2045
2062
2046 If `string` starts with `re:`, the remainder of the string is treated as
2063 If `string` starts with `re:`, the remainder of the string is treated as
2047 a regular expression. To match a user that actually contains `re:`, use
2064 a regular expression. To match a user that actually contains `re:`, use
2048 the prefix `literal:`.
2065 the prefix `literal:`.
2049 """
2066 """
2050 return author(repo, subset, x)
2067 return author(repo, subset, x)
2051
2068
2052 # experimental
2069 # experimental
2053 @predicate('wdir', safe=True)
2070 @predicate('wdir', safe=True)
2054 def wdir(repo, subset, x):
2071 def wdir(repo, subset, x):
2055 # i18n: "wdir" is a keyword
2072 # i18n: "wdir" is a keyword
2056 getargs(x, 0, 0, _("wdir takes no arguments"))
2073 getargs(x, 0, 0, _("wdir takes no arguments"))
2057 if node.wdirrev in subset or isinstance(subset, fullreposet):
2074 if node.wdirrev in subset or isinstance(subset, fullreposet):
2058 return baseset([node.wdirrev])
2075 return baseset([node.wdirrev])
2059 return baseset()
2076 return baseset()
2060
2077
2061 # for internal use
2078 # for internal use
2062 @predicate('_list', safe=True)
2079 @predicate('_list', safe=True)
2063 def _list(repo, subset, x):
2080 def _list(repo, subset, x):
2064 s = getstring(x, "internal error")
2081 s = getstring(x, "internal error")
2065 if not s:
2082 if not s:
2066 return baseset()
2083 return baseset()
2067 # remove duplicates here. it's difficult for caller to deduplicate sets
2084 # remove duplicates here. it's difficult for caller to deduplicate sets
2068 # because different symbols can point to the same rev.
2085 # because different symbols can point to the same rev.
2069 cl = repo.changelog
2086 cl = repo.changelog
2070 ls = []
2087 ls = []
2071 seen = set()
2088 seen = set()
2072 for t in s.split('\0'):
2089 for t in s.split('\0'):
2073 try:
2090 try:
2074 # fast path for integer revision
2091 # fast path for integer revision
2075 r = int(t)
2092 r = int(t)
2076 if str(r) != t or r not in cl:
2093 if str(r) != t or r not in cl:
2077 raise ValueError
2094 raise ValueError
2078 revs = [r]
2095 revs = [r]
2079 except ValueError:
2096 except ValueError:
2080 revs = stringset(repo, subset, t)
2097 revs = stringset(repo, subset, t)
2081
2098
2082 for r in revs:
2099 for r in revs:
2083 if r in seen:
2100 if r in seen:
2084 continue
2101 continue
2085 if (r in subset
2102 if (r in subset
2086 or r == node.nullrev and isinstance(subset, fullreposet)):
2103 or r == node.nullrev and isinstance(subset, fullreposet)):
2087 ls.append(r)
2104 ls.append(r)
2088 seen.add(r)
2105 seen.add(r)
2089 return baseset(ls)
2106 return baseset(ls)
2090
2107
2091 # for internal use
2108 # for internal use
2092 @predicate('_intlist', safe=True)
2109 @predicate('_intlist', safe=True)
2093 def _intlist(repo, subset, x):
2110 def _intlist(repo, subset, x):
2094 s = getstring(x, "internal error")
2111 s = getstring(x, "internal error")
2095 if not s:
2112 if not s:
2096 return baseset()
2113 return baseset()
2097 ls = [int(r) for r in s.split('\0')]
2114 ls = [int(r) for r in s.split('\0')]
2098 s = subset
2115 s = subset
2099 return baseset([r for r in ls if r in s])
2116 return baseset([r for r in ls if r in s])
2100
2117
2101 # for internal use
2118 # for internal use
2102 @predicate('_hexlist', safe=True)
2119 @predicate('_hexlist', safe=True)
2103 def _hexlist(repo, subset, x):
2120 def _hexlist(repo, subset, x):
2104 s = getstring(x, "internal error")
2121 s = getstring(x, "internal error")
2105 if not s:
2122 if not s:
2106 return baseset()
2123 return baseset()
2107 cl = repo.changelog
2124 cl = repo.changelog
2108 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2125 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2109 s = subset
2126 s = subset
2110 return baseset([r for r in ls if r in s])
2127 return baseset([r for r in ls if r in s])
2111
2128
2112 methods = {
2129 methods = {
2113 "range": rangeset,
2130 "range": rangeset,
2114 "dagrange": dagrange,
2131 "dagrange": dagrange,
2115 "string": stringset,
2132 "string": stringset,
2116 "symbol": stringset,
2133 "symbol": stringset,
2117 "and": andset,
2134 "and": andset,
2118 "or": orset,
2135 "or": orset,
2119 "not": notset,
2136 "not": notset,
2120 "difference": differenceset,
2137 "difference": differenceset,
2121 "list": listset,
2138 "list": listset,
2122 "keyvalue": keyvaluepair,
2139 "keyvalue": keyvaluepair,
2123 "func": func,
2140 "func": func,
2124 "ancestor": ancestorspec,
2141 "ancestor": ancestorspec,
2125 "parent": parentspec,
2142 "parent": parentspec,
2126 "parentpost": p1,
2143 "parentpost": p1,
2127 }
2144 }
2128
2145
2129 def optimize(x, small):
2146 def optimize(x, small):
2130 if x is None:
2147 if x is None:
2131 return 0, x
2148 return 0, x
2132
2149
2133 smallbonus = 1
2150 smallbonus = 1
2134 if small:
2151 if small:
2135 smallbonus = .5
2152 smallbonus = .5
2136
2153
2137 op = x[0]
2154 op = x[0]
2138 if op == 'minus':
2155 if op == 'minus':
2139 return optimize(('and', x[1], ('not', x[2])), small)
2156 return optimize(('and', x[1], ('not', x[2])), small)
2140 elif op == 'only':
2157 elif op == 'only':
2141 return optimize(('func', ('symbol', 'only'),
2158 return optimize(('func', ('symbol', 'only'),
2142 ('list', x[1], x[2])), small)
2159 ('list', x[1], x[2])), small)
2143 elif op == 'onlypost':
2160 elif op == 'onlypost':
2144 return optimize(('func', ('symbol', 'only'), x[1]), small)
2161 return optimize(('func', ('symbol', 'only'), x[1]), small)
2145 elif op == 'dagrangepre':
2162 elif op == 'dagrangepre':
2146 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2163 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2147 elif op == 'dagrangepost':
2164 elif op == 'dagrangepost':
2148 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2165 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2149 elif op == 'rangeall':
2166 elif op == 'rangeall':
2150 return optimize(('range', ('string', '0'), ('string', 'tip')), small)
2167 return optimize(('range', ('string', '0'), ('string', 'tip')), small)
2151 elif op == 'rangepre':
2168 elif op == 'rangepre':
2152 return optimize(('range', ('string', '0'), x[1]), small)
2169 return optimize(('range', ('string', '0'), x[1]), small)
2153 elif op == 'rangepost':
2170 elif op == 'rangepost':
2154 return optimize(('range', x[1], ('string', 'tip')), small)
2171 return optimize(('range', x[1], ('string', 'tip')), small)
2155 elif op == 'negate':
2172 elif op == 'negate':
2156 return optimize(('string',
2173 return optimize(('string',
2157 '-' + getstring(x[1], _("can't negate that"))), small)
2174 '-' + getstring(x[1], _("can't negate that"))), small)
2158 elif op in 'string symbol negate':
2175 elif op in 'string symbol negate':
2159 return smallbonus, x # single revisions are small
2176 return smallbonus, x # single revisions are small
2160 elif op == 'and':
2177 elif op == 'and':
2161 wa, ta = optimize(x[1], True)
2178 wa, ta = optimize(x[1], True)
2162 wb, tb = optimize(x[2], True)
2179 wb, tb = optimize(x[2], True)
2163
2180
2164 # (::x and not ::y)/(not ::y and ::x) have a fast path
2181 # (::x and not ::y)/(not ::y and ::x) have a fast path
2165 def isonly(revs, bases):
2182 def isonly(revs, bases):
2166 return (
2183 return (
2167 revs is not None
2184 revs is not None
2168 and revs[0] == 'func'
2185 and revs[0] == 'func'
2169 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2186 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2170 and bases is not None
2187 and bases is not None
2171 and bases[0] == 'not'
2188 and bases[0] == 'not'
2172 and bases[1][0] == 'func'
2189 and bases[1][0] == 'func'
2173 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2190 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2174
2191
2175 w = min(wa, wb)
2192 w = min(wa, wb)
2176 if isonly(ta, tb):
2193 if isonly(ta, tb):
2177 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2194 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2178 if isonly(tb, ta):
2195 if isonly(tb, ta):
2179 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2196 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2180
2197
2181 if tb is not None and tb[0] == 'not':
2198 if tb is not None and tb[0] == 'not':
2182 return wa, ('difference', ta, tb[1])
2199 return wa, ('difference', ta, tb[1])
2183
2200
2184 if wa > wb:
2201 if wa > wb:
2185 return w, (op, tb, ta)
2202 return w, (op, tb, ta)
2186 return w, (op, ta, tb)
2203 return w, (op, ta, tb)
2187 elif op == 'or':
2204 elif op == 'or':
2188 # fast path for machine-generated expression, that is likely to have
2205 # fast path for machine-generated expression, that is likely to have
2189 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2206 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2190 ws, ts, ss = [], [], []
2207 ws, ts, ss = [], [], []
2191 def flushss():
2208 def flushss():
2192 if not ss:
2209 if not ss:
2193 return
2210 return
2194 if len(ss) == 1:
2211 if len(ss) == 1:
2195 w, t = ss[0]
2212 w, t = ss[0]
2196 else:
2213 else:
2197 s = '\0'.join(t[1] for w, t in ss)
2214 s = '\0'.join(t[1] for w, t in ss)
2198 y = ('func', ('symbol', '_list'), ('string', s))
2215 y = ('func', ('symbol', '_list'), ('string', s))
2199 w, t = optimize(y, False)
2216 w, t = optimize(y, False)
2200 ws.append(w)
2217 ws.append(w)
2201 ts.append(t)
2218 ts.append(t)
2202 del ss[:]
2219 del ss[:]
2203 for y in x[1:]:
2220 for y in x[1:]:
2204 w, t = optimize(y, False)
2221 w, t = optimize(y, False)
2205 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2222 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2206 ss.append((w, t))
2223 ss.append((w, t))
2207 continue
2224 continue
2208 flushss()
2225 flushss()
2209 ws.append(w)
2226 ws.append(w)
2210 ts.append(t)
2227 ts.append(t)
2211 flushss()
2228 flushss()
2212 if len(ts) == 1:
2229 if len(ts) == 1:
2213 return ws[0], ts[0] # 'or' operation is fully optimized out
2230 return ws[0], ts[0] # 'or' operation is fully optimized out
2214 # we can't reorder trees by weight because it would change the order.
2231 # we can't reorder trees by weight because it would change the order.
2215 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2232 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2216 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2233 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2217 return max(ws), (op,) + tuple(ts)
2234 return max(ws), (op,) + tuple(ts)
2218 elif op == 'not':
2235 elif op == 'not':
2219 # Optimize not public() to _notpublic() because we have a fast version
2236 # Optimize not public() to _notpublic() because we have a fast version
2220 if x[1] == ('func', ('symbol', 'public'), None):
2237 if x[1] == ('func', ('symbol', 'public'), None):
2221 newsym = ('func', ('symbol', '_notpublic'), None)
2238 newsym = ('func', ('symbol', '_notpublic'), None)
2222 o = optimize(newsym, not small)
2239 o = optimize(newsym, not small)
2223 return o[0], o[1]
2240 return o[0], o[1]
2224 else:
2241 else:
2225 o = optimize(x[1], not small)
2242 o = optimize(x[1], not small)
2226 return o[0], (op, o[1])
2243 return o[0], (op, o[1])
2227 elif op == 'parentpost':
2244 elif op == 'parentpost':
2228 o = optimize(x[1], small)
2245 o = optimize(x[1], small)
2229 return o[0], (op, o[1])
2246 return o[0], (op, o[1])
2230 elif op == 'group':
2247 elif op == 'group':
2231 return optimize(x[1], small)
2248 return optimize(x[1], small)
2232 elif op in 'dagrange range parent ancestorspec':
2249 elif op in 'dagrange range parent ancestorspec':
2233 if op == 'parent':
2250 if op == 'parent':
2234 # x^:y means (x^) : y, not x ^ (:y)
2251 # x^:y means (x^) : y, not x ^ (:y)
2235 post = ('parentpost', x[1])
2252 post = ('parentpost', x[1])
2236 if x[2][0] == 'dagrangepre':
2253 if x[2][0] == 'dagrangepre':
2237 return optimize(('dagrange', post, x[2][1]), small)
2254 return optimize(('dagrange', post, x[2][1]), small)
2238 elif x[2][0] == 'rangepre':
2255 elif x[2][0] == 'rangepre':
2239 return optimize(('range', post, x[2][1]), small)
2256 return optimize(('range', post, x[2][1]), small)
2240
2257
2241 wa, ta = optimize(x[1], small)
2258 wa, ta = optimize(x[1], small)
2242 wb, tb = optimize(x[2], small)
2259 wb, tb = optimize(x[2], small)
2243 return wa + wb, (op, ta, tb)
2260 return wa + wb, (op, ta, tb)
2244 elif op == 'list':
2261 elif op == 'list':
2245 ws, ts = zip(*(optimize(y, small) for y in x[1:]))
2262 ws, ts = zip(*(optimize(y, small) for y in x[1:]))
2246 return sum(ws), (op,) + ts
2263 return sum(ws), (op,) + ts
2247 elif op == 'func':
2264 elif op == 'func':
2248 f = getstring(x[1], _("not a symbol"))
2265 f = getstring(x[1], _("not a symbol"))
2249 wa, ta = optimize(x[2], small)
2266 wa, ta = optimize(x[2], small)
2250 if f in ("author branch closed date desc file grep keyword "
2267 if f in ("author branch closed date desc file grep keyword "
2251 "outgoing user"):
2268 "outgoing user"):
2252 w = 10 # slow
2269 w = 10 # slow
2253 elif f in "modifies adds removes":
2270 elif f in "modifies adds removes":
2254 w = 30 # slower
2271 w = 30 # slower
2255 elif f == "contains":
2272 elif f == "contains":
2256 w = 100 # very slow
2273 w = 100 # very slow
2257 elif f == "ancestor":
2274 elif f == "ancestor":
2258 w = 1 * smallbonus
2275 w = 1 * smallbonus
2259 elif f in "reverse limit first _intlist":
2276 elif f in "reverse limit first _intlist":
2260 w = 0
2277 w = 0
2261 elif f in "sort":
2278 elif f in "sort":
2262 w = 10 # assume most sorts look at changelog
2279 w = 10 # assume most sorts look at changelog
2263 else:
2280 else:
2264 w = 1
2281 w = 1
2265 return w + wa, (op, x[1], ta)
2282 return w + wa, (op, x[1], ta)
2266 return 1, x
2283 return 1, x
2267
2284
2268 _aliasarg = ('func', ('symbol', '_aliasarg'))
2285 _aliasarg = ('func', ('symbol', '_aliasarg'))
2269 def _getaliasarg(tree):
2286 def _getaliasarg(tree):
2270 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2287 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2271 return X, None otherwise.
2288 return X, None otherwise.
2272 """
2289 """
2273 if (len(tree) == 3 and tree[:2] == _aliasarg
2290 if (len(tree) == 3 and tree[:2] == _aliasarg
2274 and tree[2][0] == 'string'):
2291 and tree[2][0] == 'string'):
2275 return tree[2][1]
2292 return tree[2][1]
2276 return None
2293 return None
2277
2294
2278 def _checkaliasarg(tree, known=None):
2295 def _checkaliasarg(tree, known=None):
2279 """Check tree contains no _aliasarg construct or only ones which
2296 """Check tree contains no _aliasarg construct or only ones which
2280 value is in known. Used to avoid alias placeholders injection.
2297 value is in known. Used to avoid alias placeholders injection.
2281 """
2298 """
2282 if isinstance(tree, tuple):
2299 if isinstance(tree, tuple):
2283 arg = _getaliasarg(tree)
2300 arg = _getaliasarg(tree)
2284 if arg is not None and (not known or arg not in known):
2301 if arg is not None and (not known or arg not in known):
2285 raise error.UnknownIdentifier('_aliasarg', [])
2302 raise error.UnknownIdentifier('_aliasarg', [])
2286 for t in tree:
2303 for t in tree:
2287 _checkaliasarg(t, known)
2304 _checkaliasarg(t, known)
2288
2305
2289 # the set of valid characters for the initial letter of symbols in
2306 # the set of valid characters for the initial letter of symbols in
2290 # alias declarations and definitions
2307 # alias declarations and definitions
2291 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2308 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2292 if c.isalnum() or c in '._@$' or ord(c) > 127)
2309 if c.isalnum() or c in '._@$' or ord(c) > 127)
2293
2310
2294 def _tokenizealias(program, lookup=None):
2311 def _tokenizealias(program, lookup=None):
2295 """Parse alias declaration/definition into a stream of tokens
2312 """Parse alias declaration/definition into a stream of tokens
2296
2313
2297 This allows symbol names to use also ``$`` as an initial letter
2314 This allows symbol names to use also ``$`` as an initial letter
2298 (for backward compatibility), and callers of this function should
2315 (for backward compatibility), and callers of this function should
2299 examine whether ``$`` is used also for unexpected symbols or not.
2316 examine whether ``$`` is used also for unexpected symbols or not.
2300 """
2317 """
2301 return tokenize(program, lookup=lookup,
2318 return tokenize(program, lookup=lookup,
2302 syminitletters=_aliassyminitletters)
2319 syminitletters=_aliassyminitletters)
2303
2320
2304 def _parsealiasdecl(decl):
2321 def _parsealiasdecl(decl):
2305 """Parse alias declaration ``decl``
2322 """Parse alias declaration ``decl``
2306
2323
2307 This returns ``(name, tree, args, errorstr)`` tuple:
2324 This returns ``(name, tree, args, errorstr)`` tuple:
2308
2325
2309 - ``name``: of declared alias (may be ``decl`` itself at error)
2326 - ``name``: of declared alias (may be ``decl`` itself at error)
2310 - ``tree``: parse result (or ``None`` at error)
2327 - ``tree``: parse result (or ``None`` at error)
2311 - ``args``: list of alias argument names (or None for symbol declaration)
2328 - ``args``: list of alias argument names (or None for symbol declaration)
2312 - ``errorstr``: detail about detected error (or None)
2329 - ``errorstr``: detail about detected error (or None)
2313
2330
2314 >>> _parsealiasdecl('foo')
2331 >>> _parsealiasdecl('foo')
2315 ('foo', ('symbol', 'foo'), None, None)
2332 ('foo', ('symbol', 'foo'), None, None)
2316 >>> _parsealiasdecl('$foo')
2333 >>> _parsealiasdecl('$foo')
2317 ('$foo', None, None, "'$' not for alias arguments")
2334 ('$foo', None, None, "'$' not for alias arguments")
2318 >>> _parsealiasdecl('foo::bar')
2335 >>> _parsealiasdecl('foo::bar')
2319 ('foo::bar', None, None, 'invalid format')
2336 ('foo::bar', None, None, 'invalid format')
2320 >>> _parsealiasdecl('foo bar')
2337 >>> _parsealiasdecl('foo bar')
2321 ('foo bar', None, None, 'at 4: invalid token')
2338 ('foo bar', None, None, 'at 4: invalid token')
2322 >>> _parsealiasdecl('foo()')
2339 >>> _parsealiasdecl('foo()')
2323 ('foo', ('func', ('symbol', 'foo')), [], None)
2340 ('foo', ('func', ('symbol', 'foo')), [], None)
2324 >>> _parsealiasdecl('$foo()')
2341 >>> _parsealiasdecl('$foo()')
2325 ('$foo()', None, None, "'$' not for alias arguments")
2342 ('$foo()', None, None, "'$' not for alias arguments")
2326 >>> _parsealiasdecl('foo($1, $2)')
2343 >>> _parsealiasdecl('foo($1, $2)')
2327 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2344 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2328 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2345 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2329 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2346 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2330 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2347 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2331 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2348 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2332 >>> _parsealiasdecl('foo(bar($1, $2))')
2349 >>> _parsealiasdecl('foo(bar($1, $2))')
2333 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2350 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2334 >>> _parsealiasdecl('foo("string")')
2351 >>> _parsealiasdecl('foo("string")')
2335 ('foo("string")', None, None, 'invalid argument list')
2352 ('foo("string")', None, None, 'invalid argument list')
2336 >>> _parsealiasdecl('foo($1, $2')
2353 >>> _parsealiasdecl('foo($1, $2')
2337 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2354 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2338 >>> _parsealiasdecl('foo("string')
2355 >>> _parsealiasdecl('foo("string')
2339 ('foo("string', None, None, 'at 5: unterminated string')
2356 ('foo("string', None, None, 'at 5: unterminated string')
2340 >>> _parsealiasdecl('foo($1, $2, $1)')
2357 >>> _parsealiasdecl('foo($1, $2, $1)')
2341 ('foo', None, None, 'argument names collide with each other')
2358 ('foo', None, None, 'argument names collide with each other')
2342 """
2359 """
2343 p = parser.parser(elements)
2360 p = parser.parser(elements)
2344 try:
2361 try:
2345 tree, pos = p.parse(_tokenizealias(decl))
2362 tree, pos = p.parse(_tokenizealias(decl))
2346 if (pos != len(decl)):
2363 if (pos != len(decl)):
2347 raise error.ParseError(_('invalid token'), pos)
2364 raise error.ParseError(_('invalid token'), pos)
2348 tree = parser.simplifyinfixops(tree, ('list',))
2365 tree = parser.simplifyinfixops(tree, ('list',))
2349
2366
2350 if isvalidsymbol(tree):
2367 if isvalidsymbol(tree):
2351 # "name = ...." style
2368 # "name = ...." style
2352 name = getsymbol(tree)
2369 name = getsymbol(tree)
2353 if name.startswith('$'):
2370 if name.startswith('$'):
2354 return (decl, None, None, _("'$' not for alias arguments"))
2371 return (decl, None, None, _("'$' not for alias arguments"))
2355 return (name, ('symbol', name), None, None)
2372 return (name, ('symbol', name), None, None)
2356
2373
2357 if isvalidfunc(tree):
2374 if isvalidfunc(tree):
2358 # "name(arg, ....) = ...." style
2375 # "name(arg, ....) = ...." style
2359 name = getfuncname(tree)
2376 name = getfuncname(tree)
2360 if name.startswith('$'):
2377 if name.startswith('$'):
2361 return (decl, None, None, _("'$' not for alias arguments"))
2378 return (decl, None, None, _("'$' not for alias arguments"))
2362 args = []
2379 args = []
2363 for arg in getfuncargs(tree):
2380 for arg in getfuncargs(tree):
2364 if not isvalidsymbol(arg):
2381 if not isvalidsymbol(arg):
2365 return (decl, None, None, _("invalid argument list"))
2382 return (decl, None, None, _("invalid argument list"))
2366 args.append(getsymbol(arg))
2383 args.append(getsymbol(arg))
2367 if len(args) != len(set(args)):
2384 if len(args) != len(set(args)):
2368 return (name, None, None,
2385 return (name, None, None,
2369 _("argument names collide with each other"))
2386 _("argument names collide with each other"))
2370 return (name, ('func', ('symbol', name)), args, None)
2387 return (name, ('func', ('symbol', name)), args, None)
2371
2388
2372 return (decl, None, None, _("invalid format"))
2389 return (decl, None, None, _("invalid format"))
2373 except error.ParseError as inst:
2390 except error.ParseError as inst:
2374 return (decl, None, None, parseerrordetail(inst))
2391 return (decl, None, None, parseerrordetail(inst))
2375
2392
2376 def _parsealiasdefn(defn, args):
2393 def _parsealiasdefn(defn, args):
2377 """Parse alias definition ``defn``
2394 """Parse alias definition ``defn``
2378
2395
2379 This function also replaces alias argument references in the
2396 This function also replaces alias argument references in the
2380 specified definition by ``_aliasarg(ARGNAME)``.
2397 specified definition by ``_aliasarg(ARGNAME)``.
2381
2398
2382 ``args`` is a list of alias argument names, or None if the alias
2399 ``args`` is a list of alias argument names, or None if the alias
2383 is declared as a symbol.
2400 is declared as a symbol.
2384
2401
2385 This returns "tree" as parsing result.
2402 This returns "tree" as parsing result.
2386
2403
2387 >>> args = ['$1', '$2', 'foo']
2404 >>> args = ['$1', '$2', 'foo']
2388 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2405 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2389 (or
2406 (or
2390 (func
2407 (func
2391 ('symbol', '_aliasarg')
2408 ('symbol', '_aliasarg')
2392 ('string', '$1'))
2409 ('string', '$1'))
2393 (func
2410 (func
2394 ('symbol', '_aliasarg')
2411 ('symbol', '_aliasarg')
2395 ('string', 'foo')))
2412 ('string', 'foo')))
2396 >>> try:
2413 >>> try:
2397 ... _parsealiasdefn('$1 or $bar', args)
2414 ... _parsealiasdefn('$1 or $bar', args)
2398 ... except error.ParseError, inst:
2415 ... except error.ParseError, inst:
2399 ... print parseerrordetail(inst)
2416 ... print parseerrordetail(inst)
2400 at 6: '$' not for alias arguments
2417 at 6: '$' not for alias arguments
2401 >>> args = ['$1', '$10', 'foo']
2418 >>> args = ['$1', '$10', 'foo']
2402 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2419 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2403 (or
2420 (or
2404 (func
2421 (func
2405 ('symbol', '_aliasarg')
2422 ('symbol', '_aliasarg')
2406 ('string', '$10'))
2423 ('string', '$10'))
2407 ('symbol', 'foobar'))
2424 ('symbol', 'foobar'))
2408 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2425 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2409 (or
2426 (or
2410 ('string', '$1')
2427 ('string', '$1')
2411 ('string', 'foo'))
2428 ('string', 'foo'))
2412 """
2429 """
2413 def tokenizedefn(program, lookup=None):
2430 def tokenizedefn(program, lookup=None):
2414 if args:
2431 if args:
2415 argset = set(args)
2432 argset = set(args)
2416 else:
2433 else:
2417 argset = set()
2434 argset = set()
2418
2435
2419 for t, value, pos in _tokenizealias(program, lookup=lookup):
2436 for t, value, pos in _tokenizealias(program, lookup=lookup):
2420 if t == 'symbol':
2437 if t == 'symbol':
2421 if value in argset:
2438 if value in argset:
2422 # emulate tokenization of "_aliasarg('ARGNAME')":
2439 # emulate tokenization of "_aliasarg('ARGNAME')":
2423 # "_aliasarg()" is an unknown symbol only used separate
2440 # "_aliasarg()" is an unknown symbol only used separate
2424 # alias argument placeholders from regular strings.
2441 # alias argument placeholders from regular strings.
2425 yield ('symbol', '_aliasarg', pos)
2442 yield ('symbol', '_aliasarg', pos)
2426 yield ('(', None, pos)
2443 yield ('(', None, pos)
2427 yield ('string', value, pos)
2444 yield ('string', value, pos)
2428 yield (')', None, pos)
2445 yield (')', None, pos)
2429 continue
2446 continue
2430 elif value.startswith('$'):
2447 elif value.startswith('$'):
2431 raise error.ParseError(_("'$' not for alias arguments"),
2448 raise error.ParseError(_("'$' not for alias arguments"),
2432 pos)
2449 pos)
2433 yield (t, value, pos)
2450 yield (t, value, pos)
2434
2451
2435 p = parser.parser(elements)
2452 p = parser.parser(elements)
2436 tree, pos = p.parse(tokenizedefn(defn))
2453 tree, pos = p.parse(tokenizedefn(defn))
2437 if pos != len(defn):
2454 if pos != len(defn):
2438 raise error.ParseError(_('invalid token'), pos)
2455 raise error.ParseError(_('invalid token'), pos)
2439 return parser.simplifyinfixops(tree, ('list', 'or'))
2456 return parser.simplifyinfixops(tree, ('list', 'or'))
2440
2457
2441 class revsetalias(object):
2458 class revsetalias(object):
2442 # whether own `error` information is already shown or not.
2459 # whether own `error` information is already shown or not.
2443 # this avoids showing same warning multiple times at each `findaliases`.
2460 # this avoids showing same warning multiple times at each `findaliases`.
2444 warned = False
2461 warned = False
2445
2462
2446 def __init__(self, name, value):
2463 def __init__(self, name, value):
2447 '''Aliases like:
2464 '''Aliases like:
2448
2465
2449 h = heads(default)
2466 h = heads(default)
2450 b($1) = ancestors($1) - ancestors(default)
2467 b($1) = ancestors($1) - ancestors(default)
2451 '''
2468 '''
2452 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2469 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2453 if self.error:
2470 if self.error:
2454 self.error = _('failed to parse the declaration of revset alias'
2471 self.error = _('failed to parse the declaration of revset alias'
2455 ' "%s": %s') % (self.name, self.error)
2472 ' "%s": %s') % (self.name, self.error)
2456 return
2473 return
2457
2474
2458 try:
2475 try:
2459 self.replacement = _parsealiasdefn(value, self.args)
2476 self.replacement = _parsealiasdefn(value, self.args)
2460 # Check for placeholder injection
2477 # Check for placeholder injection
2461 _checkaliasarg(self.replacement, self.args)
2478 _checkaliasarg(self.replacement, self.args)
2462 except error.ParseError as inst:
2479 except error.ParseError as inst:
2463 self.error = _('failed to parse the definition of revset alias'
2480 self.error = _('failed to parse the definition of revset alias'
2464 ' "%s": %s') % (self.name, parseerrordetail(inst))
2481 ' "%s": %s') % (self.name, parseerrordetail(inst))
2465
2482
2466 def _getalias(aliases, tree):
2483 def _getalias(aliases, tree):
2467 """If tree looks like an unexpanded alias, return it. Return None
2484 """If tree looks like an unexpanded alias, return it. Return None
2468 otherwise.
2485 otherwise.
2469 """
2486 """
2470 if isinstance(tree, tuple) and tree:
2487 if isinstance(tree, tuple) and tree:
2471 if tree[0] == 'symbol' and len(tree) == 2:
2488 if tree[0] == 'symbol' and len(tree) == 2:
2472 name = tree[1]
2489 name = tree[1]
2473 alias = aliases.get(name)
2490 alias = aliases.get(name)
2474 if alias and alias.args is None and alias.tree == tree:
2491 if alias and alias.args is None and alias.tree == tree:
2475 return alias
2492 return alias
2476 if tree[0] == 'func' and len(tree) > 1:
2493 if tree[0] == 'func' and len(tree) > 1:
2477 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2494 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2478 name = tree[1][1]
2495 name = tree[1][1]
2479 alias = aliases.get(name)
2496 alias = aliases.get(name)
2480 if alias and alias.args is not None and alias.tree == tree[:2]:
2497 if alias and alias.args is not None and alias.tree == tree[:2]:
2481 return alias
2498 return alias
2482 return None
2499 return None
2483
2500
2484 def _expandargs(tree, args):
2501 def _expandargs(tree, args):
2485 """Replace _aliasarg instances with the substitution value of the
2502 """Replace _aliasarg instances with the substitution value of the
2486 same name in args, recursively.
2503 same name in args, recursively.
2487 """
2504 """
2488 if not tree or not isinstance(tree, tuple):
2505 if not tree or not isinstance(tree, tuple):
2489 return tree
2506 return tree
2490 arg = _getaliasarg(tree)
2507 arg = _getaliasarg(tree)
2491 if arg is not None:
2508 if arg is not None:
2492 return args[arg]
2509 return args[arg]
2493 return tuple(_expandargs(t, args) for t in tree)
2510 return tuple(_expandargs(t, args) for t in tree)
2494
2511
2495 def _expandaliases(aliases, tree, expanding, cache):
2512 def _expandaliases(aliases, tree, expanding, cache):
2496 """Expand aliases in tree, recursively.
2513 """Expand aliases in tree, recursively.
2497
2514
2498 'aliases' is a dictionary mapping user defined aliases to
2515 'aliases' is a dictionary mapping user defined aliases to
2499 revsetalias objects.
2516 revsetalias objects.
2500 """
2517 """
2501 if not isinstance(tree, tuple):
2518 if not isinstance(tree, tuple):
2502 # Do not expand raw strings
2519 # Do not expand raw strings
2503 return tree
2520 return tree
2504 alias = _getalias(aliases, tree)
2521 alias = _getalias(aliases, tree)
2505 if alias is not None:
2522 if alias is not None:
2506 if alias.error:
2523 if alias.error:
2507 raise error.Abort(alias.error)
2524 raise error.Abort(alias.error)
2508 if alias in expanding:
2525 if alias in expanding:
2509 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2526 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2510 'detected') % alias.name)
2527 'detected') % alias.name)
2511 expanding.append(alias)
2528 expanding.append(alias)
2512 if alias.name not in cache:
2529 if alias.name not in cache:
2513 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2530 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2514 expanding, cache)
2531 expanding, cache)
2515 result = cache[alias.name]
2532 result = cache[alias.name]
2516 expanding.pop()
2533 expanding.pop()
2517 if alias.args is not None:
2534 if alias.args is not None:
2518 l = getlist(tree[2])
2535 l = getlist(tree[2])
2519 if len(l) != len(alias.args):
2536 if len(l) != len(alias.args):
2520 raise error.ParseError(
2537 raise error.ParseError(
2521 _('invalid number of arguments: %d') % len(l))
2538 _('invalid number of arguments: %d') % len(l))
2522 l = [_expandaliases(aliases, a, [], cache) for a in l]
2539 l = [_expandaliases(aliases, a, [], cache) for a in l]
2523 result = _expandargs(result, dict(zip(alias.args, l)))
2540 result = _expandargs(result, dict(zip(alias.args, l)))
2524 else:
2541 else:
2525 result = tuple(_expandaliases(aliases, t, expanding, cache)
2542 result = tuple(_expandaliases(aliases, t, expanding, cache)
2526 for t in tree)
2543 for t in tree)
2527 return result
2544 return result
2528
2545
2529 def findaliases(ui, tree, showwarning=None):
2546 def findaliases(ui, tree, showwarning=None):
2530 _checkaliasarg(tree)
2547 _checkaliasarg(tree)
2531 aliases = {}
2548 aliases = {}
2532 for k, v in ui.configitems('revsetalias'):
2549 for k, v in ui.configitems('revsetalias'):
2533 alias = revsetalias(k, v)
2550 alias = revsetalias(k, v)
2534 aliases[alias.name] = alias
2551 aliases[alias.name] = alias
2535 tree = _expandaliases(aliases, tree, [], {})
2552 tree = _expandaliases(aliases, tree, [], {})
2536 if showwarning:
2553 if showwarning:
2537 # warn about problematic (but not referred) aliases
2554 # warn about problematic (but not referred) aliases
2538 for name, alias in sorted(aliases.iteritems()):
2555 for name, alias in sorted(aliases.iteritems()):
2539 if alias.error and not alias.warned:
2556 if alias.error and not alias.warned:
2540 showwarning(_('warning: %s\n') % (alias.error))
2557 showwarning(_('warning: %s\n') % (alias.error))
2541 alias.warned = True
2558 alias.warned = True
2542 return tree
2559 return tree
2543
2560
2544 def foldconcat(tree):
2561 def foldconcat(tree):
2545 """Fold elements to be concatenated by `##`
2562 """Fold elements to be concatenated by `##`
2546 """
2563 """
2547 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2564 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2548 return tree
2565 return tree
2549 if tree[0] == '_concat':
2566 if tree[0] == '_concat':
2550 pending = [tree]
2567 pending = [tree]
2551 l = []
2568 l = []
2552 while pending:
2569 while pending:
2553 e = pending.pop()
2570 e = pending.pop()
2554 if e[0] == '_concat':
2571 if e[0] == '_concat':
2555 pending.extend(reversed(e[1:]))
2572 pending.extend(reversed(e[1:]))
2556 elif e[0] in ('string', 'symbol'):
2573 elif e[0] in ('string', 'symbol'):
2557 l.append(e[1])
2574 l.append(e[1])
2558 else:
2575 else:
2559 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2576 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2560 raise error.ParseError(msg)
2577 raise error.ParseError(msg)
2561 return ('string', ''.join(l))
2578 return ('string', ''.join(l))
2562 else:
2579 else:
2563 return tuple(foldconcat(t) for t in tree)
2580 return tuple(foldconcat(t) for t in tree)
2564
2581
2565 def parse(spec, lookup=None):
2582 def parse(spec, lookup=None):
2566 p = parser.parser(elements)
2583 p = parser.parser(elements)
2567 tree, pos = p.parse(tokenize(spec, lookup=lookup))
2584 tree, pos = p.parse(tokenize(spec, lookup=lookup))
2568 if pos != len(spec):
2585 if pos != len(spec):
2569 raise error.ParseError(_("invalid token"), pos)
2586 raise error.ParseError(_("invalid token"), pos)
2570 return parser.simplifyinfixops(tree, ('list', 'or'))
2587 return parser.simplifyinfixops(tree, ('list', 'or'))
2571
2588
2572 def posttreebuilthook(tree, repo):
2589 def posttreebuilthook(tree, repo):
2573 # hook for extensions to execute code on the optimized tree
2590 # hook for extensions to execute code on the optimized tree
2574 pass
2591 pass
2575
2592
2576 def match(ui, spec, repo=None):
2593 def match(ui, spec, repo=None):
2577 if not spec:
2594 if not spec:
2578 raise error.ParseError(_("empty query"))
2595 raise error.ParseError(_("empty query"))
2579 lookup = None
2596 lookup = None
2580 if repo:
2597 if repo:
2581 lookup = repo.__contains__
2598 lookup = repo.__contains__
2582 tree = parse(spec, lookup)
2599 tree = parse(spec, lookup)
2583 return _makematcher(ui, tree, repo)
2600 return _makematcher(ui, tree, repo)
2584
2601
2585 def matchany(ui, specs, repo=None):
2602 def matchany(ui, specs, repo=None):
2586 """Create a matcher that will include any revisions matching one of the
2603 """Create a matcher that will include any revisions matching one of the
2587 given specs"""
2604 given specs"""
2588 if not specs:
2605 if not specs:
2589 def mfunc(repo, subset=None):
2606 def mfunc(repo, subset=None):
2590 return baseset()
2607 return baseset()
2591 return mfunc
2608 return mfunc
2592 if not all(specs):
2609 if not all(specs):
2593 raise error.ParseError(_("empty query"))
2610 raise error.ParseError(_("empty query"))
2594 lookup = None
2611 lookup = None
2595 if repo:
2612 if repo:
2596 lookup = repo.__contains__
2613 lookup = repo.__contains__
2597 if len(specs) == 1:
2614 if len(specs) == 1:
2598 tree = parse(specs[0], lookup)
2615 tree = parse(specs[0], lookup)
2599 else:
2616 else:
2600 tree = ('or',) + tuple(parse(s, lookup) for s in specs)
2617 tree = ('or',) + tuple(parse(s, lookup) for s in specs)
2601 return _makematcher(ui, tree, repo)
2618 return _makematcher(ui, tree, repo)
2602
2619
2603 def _makematcher(ui, tree, repo):
2620 def _makematcher(ui, tree, repo):
2604 if ui:
2621 if ui:
2605 tree = findaliases(ui, tree, showwarning=ui.warn)
2622 tree = findaliases(ui, tree, showwarning=ui.warn)
2606 tree = foldconcat(tree)
2623 tree = foldconcat(tree)
2607 weight, tree = optimize(tree, True)
2624 weight, tree = optimize(tree, True)
2608 posttreebuilthook(tree, repo)
2625 posttreebuilthook(tree, repo)
2609 def mfunc(repo, subset=None):
2626 def mfunc(repo, subset=None):
2610 if subset is None:
2627 if subset is None:
2611 subset = fullreposet(repo)
2628 subset = fullreposet(repo)
2612 if util.safehasattr(subset, 'isascending'):
2629 if util.safehasattr(subset, 'isascending'):
2613 result = getset(repo, subset, tree)
2630 result = getset(repo, subset, tree)
2614 else:
2631 else:
2615 result = getset(repo, baseset(subset), tree)
2632 result = getset(repo, baseset(subset), tree)
2616 return result
2633 return result
2617 return mfunc
2634 return mfunc
2618
2635
2619 def formatspec(expr, *args):
2636 def formatspec(expr, *args):
2620 '''
2637 '''
2621 This is a convenience function for using revsets internally, and
2638 This is a convenience function for using revsets internally, and
2622 escapes arguments appropriately. Aliases are intentionally ignored
2639 escapes arguments appropriately. Aliases are intentionally ignored
2623 so that intended expression behavior isn't accidentally subverted.
2640 so that intended expression behavior isn't accidentally subverted.
2624
2641
2625 Supported arguments:
2642 Supported arguments:
2626
2643
2627 %r = revset expression, parenthesized
2644 %r = revset expression, parenthesized
2628 %d = int(arg), no quoting
2645 %d = int(arg), no quoting
2629 %s = string(arg), escaped and single-quoted
2646 %s = string(arg), escaped and single-quoted
2630 %b = arg.branch(), escaped and single-quoted
2647 %b = arg.branch(), escaped and single-quoted
2631 %n = hex(arg), single-quoted
2648 %n = hex(arg), single-quoted
2632 %% = a literal '%'
2649 %% = a literal '%'
2633
2650
2634 Prefixing the type with 'l' specifies a parenthesized list of that type.
2651 Prefixing the type with 'l' specifies a parenthesized list of that type.
2635
2652
2636 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2653 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2637 '(10 or 11):: and ((this()) or (that()))'
2654 '(10 or 11):: and ((this()) or (that()))'
2638 >>> formatspec('%d:: and not %d::', 10, 20)
2655 >>> formatspec('%d:: and not %d::', 10, 20)
2639 '10:: and not 20::'
2656 '10:: and not 20::'
2640 >>> formatspec('%ld or %ld', [], [1])
2657 >>> formatspec('%ld or %ld', [], [1])
2641 "_list('') or 1"
2658 "_list('') or 1"
2642 >>> formatspec('keyword(%s)', 'foo\\xe9')
2659 >>> formatspec('keyword(%s)', 'foo\\xe9')
2643 "keyword('foo\\\\xe9')"
2660 "keyword('foo\\\\xe9')"
2644 >>> b = lambda: 'default'
2661 >>> b = lambda: 'default'
2645 >>> b.branch = b
2662 >>> b.branch = b
2646 >>> formatspec('branch(%b)', b)
2663 >>> formatspec('branch(%b)', b)
2647 "branch('default')"
2664 "branch('default')"
2648 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2665 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2649 "root(_list('a\\x00b\\x00c\\x00d'))"
2666 "root(_list('a\\x00b\\x00c\\x00d'))"
2650 '''
2667 '''
2651
2668
2652 def quote(s):
2669 def quote(s):
2653 return repr(str(s))
2670 return repr(str(s))
2654
2671
2655 def argtype(c, arg):
2672 def argtype(c, arg):
2656 if c == 'd':
2673 if c == 'd':
2657 return str(int(arg))
2674 return str(int(arg))
2658 elif c == 's':
2675 elif c == 's':
2659 return quote(arg)
2676 return quote(arg)
2660 elif c == 'r':
2677 elif c == 'r':
2661 parse(arg) # make sure syntax errors are confined
2678 parse(arg) # make sure syntax errors are confined
2662 return '(%s)' % arg
2679 return '(%s)' % arg
2663 elif c == 'n':
2680 elif c == 'n':
2664 return quote(node.hex(arg))
2681 return quote(node.hex(arg))
2665 elif c == 'b':
2682 elif c == 'b':
2666 return quote(arg.branch())
2683 return quote(arg.branch())
2667
2684
2668 def listexp(s, t):
2685 def listexp(s, t):
2669 l = len(s)
2686 l = len(s)
2670 if l == 0:
2687 if l == 0:
2671 return "_list('')"
2688 return "_list('')"
2672 elif l == 1:
2689 elif l == 1:
2673 return argtype(t, s[0])
2690 return argtype(t, s[0])
2674 elif t == 'd':
2691 elif t == 'd':
2675 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2692 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2676 elif t == 's':
2693 elif t == 's':
2677 return "_list('%s')" % "\0".join(s)
2694 return "_list('%s')" % "\0".join(s)
2678 elif t == 'n':
2695 elif t == 'n':
2679 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2696 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2680 elif t == 'b':
2697 elif t == 'b':
2681 return "_list('%s')" % "\0".join(a.branch() for a in s)
2698 return "_list('%s')" % "\0".join(a.branch() for a in s)
2682
2699
2683 m = l // 2
2700 m = l // 2
2684 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2701 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2685
2702
2686 ret = ''
2703 ret = ''
2687 pos = 0
2704 pos = 0
2688 arg = 0
2705 arg = 0
2689 while pos < len(expr):
2706 while pos < len(expr):
2690 c = expr[pos]
2707 c = expr[pos]
2691 if c == '%':
2708 if c == '%':
2692 pos += 1
2709 pos += 1
2693 d = expr[pos]
2710 d = expr[pos]
2694 if d == '%':
2711 if d == '%':
2695 ret += d
2712 ret += d
2696 elif d in 'dsnbr':
2713 elif d in 'dsnbr':
2697 ret += argtype(d, args[arg])
2714 ret += argtype(d, args[arg])
2698 arg += 1
2715 arg += 1
2699 elif d == 'l':
2716 elif d == 'l':
2700 # a list of some type
2717 # a list of some type
2701 pos += 1
2718 pos += 1
2702 d = expr[pos]
2719 d = expr[pos]
2703 ret += listexp(list(args[arg]), d)
2720 ret += listexp(list(args[arg]), d)
2704 arg += 1
2721 arg += 1
2705 else:
2722 else:
2706 raise error.Abort('unexpected revspec format character %s' % d)
2723 raise error.Abort('unexpected revspec format character %s' % d)
2707 else:
2724 else:
2708 ret += c
2725 ret += c
2709 pos += 1
2726 pos += 1
2710
2727
2711 return ret
2728 return ret
2712
2729
2713 def prettyformat(tree):
2730 def prettyformat(tree):
2714 return parser.prettyformat(tree, ('string', 'symbol'))
2731 return parser.prettyformat(tree, ('string', 'symbol'))
2715
2732
2716 def depth(tree):
2733 def depth(tree):
2717 if isinstance(tree, tuple):
2734 if isinstance(tree, tuple):
2718 return max(map(depth, tree)) + 1
2735 return max(map(depth, tree)) + 1
2719 else:
2736 else:
2720 return 0
2737 return 0
2721
2738
2722 def funcsused(tree):
2739 def funcsused(tree):
2723 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2740 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2724 return set()
2741 return set()
2725 else:
2742 else:
2726 funcs = set()
2743 funcs = set()
2727 for s in tree[1:]:
2744 for s in tree[1:]:
2728 funcs |= funcsused(s)
2745 funcs |= funcsused(s)
2729 if tree[0] == 'func':
2746 if tree[0] == 'func':
2730 funcs.add(tree[1][1])
2747 funcs.add(tree[1][1])
2731 return funcs
2748 return funcs
2732
2749
2733 def _formatsetrepr(r):
2750 def _formatsetrepr(r):
2734 """Format an optional printable representation of a set
2751 """Format an optional printable representation of a set
2735
2752
2736 ======== =================================
2753 ======== =================================
2737 type(r) example
2754 type(r) example
2738 ======== =================================
2755 ======== =================================
2739 tuple ('<not %r>', other)
2756 tuple ('<not %r>', other)
2740 str '<branch closed>'
2757 str '<branch closed>'
2741 callable lambda: '<branch %r>' % sorted(b)
2758 callable lambda: '<branch %r>' % sorted(b)
2742 object other
2759 object other
2743 ======== =================================
2760 ======== =================================
2744 """
2761 """
2745 if r is None:
2762 if r is None:
2746 return ''
2763 return ''
2747 elif isinstance(r, tuple):
2764 elif isinstance(r, tuple):
2748 return r[0] % r[1:]
2765 return r[0] % r[1:]
2749 elif isinstance(r, str):
2766 elif isinstance(r, str):
2750 return r
2767 return r
2751 elif callable(r):
2768 elif callable(r):
2752 return r()
2769 return r()
2753 else:
2770 else:
2754 return repr(r)
2771 return repr(r)
2755
2772
2756 class abstractsmartset(object):
2773 class abstractsmartset(object):
2757
2774
2758 def __nonzero__(self):
2775 def __nonzero__(self):
2759 """True if the smartset is not empty"""
2776 """True if the smartset is not empty"""
2760 raise NotImplementedError()
2777 raise NotImplementedError()
2761
2778
2762 def __contains__(self, rev):
2779 def __contains__(self, rev):
2763 """provide fast membership testing"""
2780 """provide fast membership testing"""
2764 raise NotImplementedError()
2781 raise NotImplementedError()
2765
2782
2766 def __iter__(self):
2783 def __iter__(self):
2767 """iterate the set in the order it is supposed to be iterated"""
2784 """iterate the set in the order it is supposed to be iterated"""
2768 raise NotImplementedError()
2785 raise NotImplementedError()
2769
2786
2770 # Attributes containing a function to perform a fast iteration in a given
2787 # Attributes containing a function to perform a fast iteration in a given
2771 # direction. A smartset can have none, one, or both defined.
2788 # direction. A smartset can have none, one, or both defined.
2772 #
2789 #
2773 # Default value is None instead of a function returning None to avoid
2790 # Default value is None instead of a function returning None to avoid
2774 # initializing an iterator just for testing if a fast method exists.
2791 # initializing an iterator just for testing if a fast method exists.
2775 fastasc = None
2792 fastasc = None
2776 fastdesc = None
2793 fastdesc = None
2777
2794
2778 def isascending(self):
2795 def isascending(self):
2779 """True if the set will iterate in ascending order"""
2796 """True if the set will iterate in ascending order"""
2780 raise NotImplementedError()
2797 raise NotImplementedError()
2781
2798
2782 def isdescending(self):
2799 def isdescending(self):
2783 """True if the set will iterate in descending order"""
2800 """True if the set will iterate in descending order"""
2784 raise NotImplementedError()
2801 raise NotImplementedError()
2785
2802
2786 @util.cachefunc
2803 @util.cachefunc
2787 def min(self):
2804 def min(self):
2788 """return the minimum element in the set"""
2805 """return the minimum element in the set"""
2789 if self.fastasc is not None:
2806 if self.fastasc is not None:
2790 for r in self.fastasc():
2807 for r in self.fastasc():
2791 return r
2808 return r
2792 raise ValueError('arg is an empty sequence')
2809 raise ValueError('arg is an empty sequence')
2793 return min(self)
2810 return min(self)
2794
2811
2795 @util.cachefunc
2812 @util.cachefunc
2796 def max(self):
2813 def max(self):
2797 """return the maximum element in the set"""
2814 """return the maximum element in the set"""
2798 if self.fastdesc is not None:
2815 if self.fastdesc is not None:
2799 for r in self.fastdesc():
2816 for r in self.fastdesc():
2800 return r
2817 return r
2801 raise ValueError('arg is an empty sequence')
2818 raise ValueError('arg is an empty sequence')
2802 return max(self)
2819 return max(self)
2803
2820
2804 def first(self):
2821 def first(self):
2805 """return the first element in the set (user iteration perspective)
2822 """return the first element in the set (user iteration perspective)
2806
2823
2807 Return None if the set is empty"""
2824 Return None if the set is empty"""
2808 raise NotImplementedError()
2825 raise NotImplementedError()
2809
2826
2810 def last(self):
2827 def last(self):
2811 """return the last element in the set (user iteration perspective)
2828 """return the last element in the set (user iteration perspective)
2812
2829
2813 Return None if the set is empty"""
2830 Return None if the set is empty"""
2814 raise NotImplementedError()
2831 raise NotImplementedError()
2815
2832
2816 def __len__(self):
2833 def __len__(self):
2817 """return the length of the smartsets
2834 """return the length of the smartsets
2818
2835
2819 This can be expensive on smartset that could be lazy otherwise."""
2836 This can be expensive on smartset that could be lazy otherwise."""
2820 raise NotImplementedError()
2837 raise NotImplementedError()
2821
2838
2822 def reverse(self):
2839 def reverse(self):
2823 """reverse the expected iteration order"""
2840 """reverse the expected iteration order"""
2824 raise NotImplementedError()
2841 raise NotImplementedError()
2825
2842
2826 def sort(self, reverse=True):
2843 def sort(self, reverse=True):
2827 """get the set to iterate in an ascending or descending order"""
2844 """get the set to iterate in an ascending or descending order"""
2828 raise NotImplementedError()
2845 raise NotImplementedError()
2829
2846
2830 def __and__(self, other):
2847 def __and__(self, other):
2831 """Returns a new object with the intersection of the two collections.
2848 """Returns a new object with the intersection of the two collections.
2832
2849
2833 This is part of the mandatory API for smartset."""
2850 This is part of the mandatory API for smartset."""
2834 if isinstance(other, fullreposet):
2851 if isinstance(other, fullreposet):
2835 return self
2852 return self
2836 return self.filter(other.__contains__, condrepr=other, cache=False)
2853 return self.filter(other.__contains__, condrepr=other, cache=False)
2837
2854
2838 def __add__(self, other):
2855 def __add__(self, other):
2839 """Returns a new object with the union of the two collections.
2856 """Returns a new object with the union of the two collections.
2840
2857
2841 This is part of the mandatory API for smartset."""
2858 This is part of the mandatory API for smartset."""
2842 return addset(self, other)
2859 return addset(self, other)
2843
2860
2844 def __sub__(self, other):
2861 def __sub__(self, other):
2845 """Returns a new object with the substraction of the two collections.
2862 """Returns a new object with the substraction of the two collections.
2846
2863
2847 This is part of the mandatory API for smartset."""
2864 This is part of the mandatory API for smartset."""
2848 c = other.__contains__
2865 c = other.__contains__
2849 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2866 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2850 cache=False)
2867 cache=False)
2851
2868
2852 def filter(self, condition, condrepr=None, cache=True):
2869 def filter(self, condition, condrepr=None, cache=True):
2853 """Returns this smartset filtered by condition as a new smartset.
2870 """Returns this smartset filtered by condition as a new smartset.
2854
2871
2855 `condition` is a callable which takes a revision number and returns a
2872 `condition` is a callable which takes a revision number and returns a
2856 boolean. Optional `condrepr` provides a printable representation of
2873 boolean. Optional `condrepr` provides a printable representation of
2857 the given `condition`.
2874 the given `condition`.
2858
2875
2859 This is part of the mandatory API for smartset."""
2876 This is part of the mandatory API for smartset."""
2860 # builtin cannot be cached. but do not needs to
2877 # builtin cannot be cached. but do not needs to
2861 if cache and util.safehasattr(condition, 'func_code'):
2878 if cache and util.safehasattr(condition, 'func_code'):
2862 condition = util.cachefunc(condition)
2879 condition = util.cachefunc(condition)
2863 return filteredset(self, condition, condrepr)
2880 return filteredset(self, condition, condrepr)
2864
2881
2865 class baseset(abstractsmartset):
2882 class baseset(abstractsmartset):
2866 """Basic data structure that represents a revset and contains the basic
2883 """Basic data structure that represents a revset and contains the basic
2867 operation that it should be able to perform.
2884 operation that it should be able to perform.
2868
2885
2869 Every method in this class should be implemented by any smartset class.
2886 Every method in this class should be implemented by any smartset class.
2870 """
2887 """
2871 def __init__(self, data=()):
2888 def __init__(self, data=()):
2872 if not isinstance(data, list):
2889 if not isinstance(data, list):
2873 if isinstance(data, set):
2890 if isinstance(data, set):
2874 self._set = data
2891 self._set = data
2875 data = list(data)
2892 data = list(data)
2876 self._list = data
2893 self._list = data
2877 self._ascending = None
2894 self._ascending = None
2878
2895
2879 @util.propertycache
2896 @util.propertycache
2880 def _set(self):
2897 def _set(self):
2881 return set(self._list)
2898 return set(self._list)
2882
2899
2883 @util.propertycache
2900 @util.propertycache
2884 def _asclist(self):
2901 def _asclist(self):
2885 asclist = self._list[:]
2902 asclist = self._list[:]
2886 asclist.sort()
2903 asclist.sort()
2887 return asclist
2904 return asclist
2888
2905
2889 def __iter__(self):
2906 def __iter__(self):
2890 if self._ascending is None:
2907 if self._ascending is None:
2891 return iter(self._list)
2908 return iter(self._list)
2892 elif self._ascending:
2909 elif self._ascending:
2893 return iter(self._asclist)
2910 return iter(self._asclist)
2894 else:
2911 else:
2895 return reversed(self._asclist)
2912 return reversed(self._asclist)
2896
2913
2897 def fastasc(self):
2914 def fastasc(self):
2898 return iter(self._asclist)
2915 return iter(self._asclist)
2899
2916
2900 def fastdesc(self):
2917 def fastdesc(self):
2901 return reversed(self._asclist)
2918 return reversed(self._asclist)
2902
2919
2903 @util.propertycache
2920 @util.propertycache
2904 def __contains__(self):
2921 def __contains__(self):
2905 return self._set.__contains__
2922 return self._set.__contains__
2906
2923
2907 def __nonzero__(self):
2924 def __nonzero__(self):
2908 return bool(self._list)
2925 return bool(self._list)
2909
2926
2910 def sort(self, reverse=False):
2927 def sort(self, reverse=False):
2911 self._ascending = not bool(reverse)
2928 self._ascending = not bool(reverse)
2912
2929
2913 def reverse(self):
2930 def reverse(self):
2914 if self._ascending is None:
2931 if self._ascending is None:
2915 self._list.reverse()
2932 self._list.reverse()
2916 else:
2933 else:
2917 self._ascending = not self._ascending
2934 self._ascending = not self._ascending
2918
2935
2919 def __len__(self):
2936 def __len__(self):
2920 return len(self._list)
2937 return len(self._list)
2921
2938
2922 def isascending(self):
2939 def isascending(self):
2923 """Returns True if the collection is ascending order, False if not.
2940 """Returns True if the collection is ascending order, False if not.
2924
2941
2925 This is part of the mandatory API for smartset."""
2942 This is part of the mandatory API for smartset."""
2926 if len(self) <= 1:
2943 if len(self) <= 1:
2927 return True
2944 return True
2928 return self._ascending is not None and self._ascending
2945 return self._ascending is not None and self._ascending
2929
2946
2930 def isdescending(self):
2947 def isdescending(self):
2931 """Returns True if the collection is descending order, False if not.
2948 """Returns True if the collection is descending order, False if not.
2932
2949
2933 This is part of the mandatory API for smartset."""
2950 This is part of the mandatory API for smartset."""
2934 if len(self) <= 1:
2951 if len(self) <= 1:
2935 return True
2952 return True
2936 return self._ascending is not None and not self._ascending
2953 return self._ascending is not None and not self._ascending
2937
2954
2938 def first(self):
2955 def first(self):
2939 if self:
2956 if self:
2940 if self._ascending is None:
2957 if self._ascending is None:
2941 return self._list[0]
2958 return self._list[0]
2942 elif self._ascending:
2959 elif self._ascending:
2943 return self._asclist[0]
2960 return self._asclist[0]
2944 else:
2961 else:
2945 return self._asclist[-1]
2962 return self._asclist[-1]
2946 return None
2963 return None
2947
2964
2948 def last(self):
2965 def last(self):
2949 if self:
2966 if self:
2950 if self._ascending is None:
2967 if self._ascending is None:
2951 return self._list[-1]
2968 return self._list[-1]
2952 elif self._ascending:
2969 elif self._ascending:
2953 return self._asclist[-1]
2970 return self._asclist[-1]
2954 else:
2971 else:
2955 return self._asclist[0]
2972 return self._asclist[0]
2956 return None
2973 return None
2957
2974
2958 def __repr__(self):
2975 def __repr__(self):
2959 d = {None: '', False: '-', True: '+'}[self._ascending]
2976 d = {None: '', False: '-', True: '+'}[self._ascending]
2960 return '<%s%s %r>' % (type(self).__name__, d, self._list)
2977 return '<%s%s %r>' % (type(self).__name__, d, self._list)
2961
2978
2962 class filteredset(abstractsmartset):
2979 class filteredset(abstractsmartset):
2963 """Duck type for baseset class which iterates lazily over the revisions in
2980 """Duck type for baseset class which iterates lazily over the revisions in
2964 the subset and contains a function which tests for membership in the
2981 the subset and contains a function which tests for membership in the
2965 revset
2982 revset
2966 """
2983 """
2967 def __init__(self, subset, condition=lambda x: True, condrepr=None):
2984 def __init__(self, subset, condition=lambda x: True, condrepr=None):
2968 """
2985 """
2969 condition: a function that decide whether a revision in the subset
2986 condition: a function that decide whether a revision in the subset
2970 belongs to the revset or not.
2987 belongs to the revset or not.
2971 condrepr: a tuple of (format, obj, ...), a function or an object that
2988 condrepr: a tuple of (format, obj, ...), a function or an object that
2972 provides a printable representation of the given condition.
2989 provides a printable representation of the given condition.
2973 """
2990 """
2974 self._subset = subset
2991 self._subset = subset
2975 self._condition = condition
2992 self._condition = condition
2976 self._condrepr = condrepr
2993 self._condrepr = condrepr
2977
2994
2978 def __contains__(self, x):
2995 def __contains__(self, x):
2979 return x in self._subset and self._condition(x)
2996 return x in self._subset and self._condition(x)
2980
2997
2981 def __iter__(self):
2998 def __iter__(self):
2982 return self._iterfilter(self._subset)
2999 return self._iterfilter(self._subset)
2983
3000
2984 def _iterfilter(self, it):
3001 def _iterfilter(self, it):
2985 cond = self._condition
3002 cond = self._condition
2986 for x in it:
3003 for x in it:
2987 if cond(x):
3004 if cond(x):
2988 yield x
3005 yield x
2989
3006
2990 @property
3007 @property
2991 def fastasc(self):
3008 def fastasc(self):
2992 it = self._subset.fastasc
3009 it = self._subset.fastasc
2993 if it is None:
3010 if it is None:
2994 return None
3011 return None
2995 return lambda: self._iterfilter(it())
3012 return lambda: self._iterfilter(it())
2996
3013
2997 @property
3014 @property
2998 def fastdesc(self):
3015 def fastdesc(self):
2999 it = self._subset.fastdesc
3016 it = self._subset.fastdesc
3000 if it is None:
3017 if it is None:
3001 return None
3018 return None
3002 return lambda: self._iterfilter(it())
3019 return lambda: self._iterfilter(it())
3003
3020
3004 def __nonzero__(self):
3021 def __nonzero__(self):
3005 fast = self.fastasc
3022 fast = self.fastasc
3006 if fast is None:
3023 if fast is None:
3007 fast = self.fastdesc
3024 fast = self.fastdesc
3008 if fast is not None:
3025 if fast is not None:
3009 it = fast()
3026 it = fast()
3010 else:
3027 else:
3011 it = self
3028 it = self
3012
3029
3013 for r in it:
3030 for r in it:
3014 return True
3031 return True
3015 return False
3032 return False
3016
3033
3017 def __len__(self):
3034 def __len__(self):
3018 # Basic implementation to be changed in future patches.
3035 # Basic implementation to be changed in future patches.
3019 l = baseset([r for r in self])
3036 l = baseset([r for r in self])
3020 return len(l)
3037 return len(l)
3021
3038
3022 def sort(self, reverse=False):
3039 def sort(self, reverse=False):
3023 self._subset.sort(reverse=reverse)
3040 self._subset.sort(reverse=reverse)
3024
3041
3025 def reverse(self):
3042 def reverse(self):
3026 self._subset.reverse()
3043 self._subset.reverse()
3027
3044
3028 def isascending(self):
3045 def isascending(self):
3029 return self._subset.isascending()
3046 return self._subset.isascending()
3030
3047
3031 def isdescending(self):
3048 def isdescending(self):
3032 return self._subset.isdescending()
3049 return self._subset.isdescending()
3033
3050
3034 def first(self):
3051 def first(self):
3035 for x in self:
3052 for x in self:
3036 return x
3053 return x
3037 return None
3054 return None
3038
3055
3039 def last(self):
3056 def last(self):
3040 it = None
3057 it = None
3041 if self.isascending():
3058 if self.isascending():
3042 it = self.fastdesc
3059 it = self.fastdesc
3043 elif self.isdescending():
3060 elif self.isdescending():
3044 it = self.fastasc
3061 it = self.fastasc
3045 if it is not None:
3062 if it is not None:
3046 for x in it():
3063 for x in it():
3047 return x
3064 return x
3048 return None #empty case
3065 return None #empty case
3049 else:
3066 else:
3050 x = None
3067 x = None
3051 for x in self:
3068 for x in self:
3052 pass
3069 pass
3053 return x
3070 return x
3054
3071
3055 def __repr__(self):
3072 def __repr__(self):
3056 xs = [repr(self._subset)]
3073 xs = [repr(self._subset)]
3057 s = _formatsetrepr(self._condrepr)
3074 s = _formatsetrepr(self._condrepr)
3058 if s:
3075 if s:
3059 xs.append(s)
3076 xs.append(s)
3060 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3077 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3061
3078
3062 def _iterordered(ascending, iter1, iter2):
3079 def _iterordered(ascending, iter1, iter2):
3063 """produce an ordered iteration from two iterators with the same order
3080 """produce an ordered iteration from two iterators with the same order
3064
3081
3065 The ascending is used to indicated the iteration direction.
3082 The ascending is used to indicated the iteration direction.
3066 """
3083 """
3067 choice = max
3084 choice = max
3068 if ascending:
3085 if ascending:
3069 choice = min
3086 choice = min
3070
3087
3071 val1 = None
3088 val1 = None
3072 val2 = None
3089 val2 = None
3073 try:
3090 try:
3074 # Consume both iterators in an ordered way until one is empty
3091 # Consume both iterators in an ordered way until one is empty
3075 while True:
3092 while True:
3076 if val1 is None:
3093 if val1 is None:
3077 val1 = iter1.next()
3094 val1 = iter1.next()
3078 if val2 is None:
3095 if val2 is None:
3079 val2 = iter2.next()
3096 val2 = iter2.next()
3080 next = choice(val1, val2)
3097 next = choice(val1, val2)
3081 yield next
3098 yield next
3082 if val1 == next:
3099 if val1 == next:
3083 val1 = None
3100 val1 = None
3084 if val2 == next:
3101 if val2 == next:
3085 val2 = None
3102 val2 = None
3086 except StopIteration:
3103 except StopIteration:
3087 # Flush any remaining values and consume the other one
3104 # Flush any remaining values and consume the other one
3088 it = iter2
3105 it = iter2
3089 if val1 is not None:
3106 if val1 is not None:
3090 yield val1
3107 yield val1
3091 it = iter1
3108 it = iter1
3092 elif val2 is not None:
3109 elif val2 is not None:
3093 # might have been equality and both are empty
3110 # might have been equality and both are empty
3094 yield val2
3111 yield val2
3095 for val in it:
3112 for val in it:
3096 yield val
3113 yield val
3097
3114
3098 class addset(abstractsmartset):
3115 class addset(abstractsmartset):
3099 """Represent the addition of two sets
3116 """Represent the addition of two sets
3100
3117
3101 Wrapper structure for lazily adding two structures without losing much
3118 Wrapper structure for lazily adding two structures without losing much
3102 performance on the __contains__ method
3119 performance on the __contains__ method
3103
3120
3104 If the ascending attribute is set, that means the two structures are
3121 If the ascending attribute is set, that means the two structures are
3105 ordered in either an ascending or descending way. Therefore, we can add
3122 ordered in either an ascending or descending way. Therefore, we can add
3106 them maintaining the order by iterating over both at the same time
3123 them maintaining the order by iterating over both at the same time
3107
3124
3108 >>> xs = baseset([0, 3, 2])
3125 >>> xs = baseset([0, 3, 2])
3109 >>> ys = baseset([5, 2, 4])
3126 >>> ys = baseset([5, 2, 4])
3110
3127
3111 >>> rs = addset(xs, ys)
3128 >>> rs = addset(xs, ys)
3112 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3129 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3113 (True, True, False, True, 0, 4)
3130 (True, True, False, True, 0, 4)
3114 >>> rs = addset(xs, baseset([]))
3131 >>> rs = addset(xs, baseset([]))
3115 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3132 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3116 (True, True, False, 0, 2)
3133 (True, True, False, 0, 2)
3117 >>> rs = addset(baseset([]), baseset([]))
3134 >>> rs = addset(baseset([]), baseset([]))
3118 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3135 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3119 (False, False, None, None)
3136 (False, False, None, None)
3120
3137
3121 iterate unsorted:
3138 iterate unsorted:
3122 >>> rs = addset(xs, ys)
3139 >>> rs = addset(xs, ys)
3123 >>> [x for x in rs] # without _genlist
3140 >>> [x for x in rs] # without _genlist
3124 [0, 3, 2, 5, 4]
3141 [0, 3, 2, 5, 4]
3125 >>> assert not rs._genlist
3142 >>> assert not rs._genlist
3126 >>> len(rs)
3143 >>> len(rs)
3127 5
3144 5
3128 >>> [x for x in rs] # with _genlist
3145 >>> [x for x in rs] # with _genlist
3129 [0, 3, 2, 5, 4]
3146 [0, 3, 2, 5, 4]
3130 >>> assert rs._genlist
3147 >>> assert rs._genlist
3131
3148
3132 iterate ascending:
3149 iterate ascending:
3133 >>> rs = addset(xs, ys, ascending=True)
3150 >>> rs = addset(xs, ys, ascending=True)
3134 >>> [x for x in rs], [x for x in rs.fastasc()] # without _asclist
3151 >>> [x for x in rs], [x for x in rs.fastasc()] # without _asclist
3135 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3152 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3136 >>> assert not rs._asclist
3153 >>> assert not rs._asclist
3137 >>> len(rs)
3154 >>> len(rs)
3138 5
3155 5
3139 >>> [x for x in rs], [x for x in rs.fastasc()]
3156 >>> [x for x in rs], [x for x in rs.fastasc()]
3140 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3157 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3141 >>> assert rs._asclist
3158 >>> assert rs._asclist
3142
3159
3143 iterate descending:
3160 iterate descending:
3144 >>> rs = addset(xs, ys, ascending=False)
3161 >>> rs = addset(xs, ys, ascending=False)
3145 >>> [x for x in rs], [x for x in rs.fastdesc()] # without _asclist
3162 >>> [x for x in rs], [x for x in rs.fastdesc()] # without _asclist
3146 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3163 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3147 >>> assert not rs._asclist
3164 >>> assert not rs._asclist
3148 >>> len(rs)
3165 >>> len(rs)
3149 5
3166 5
3150 >>> [x for x in rs], [x for x in rs.fastdesc()]
3167 >>> [x for x in rs], [x for x in rs.fastdesc()]
3151 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3168 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3152 >>> assert rs._asclist
3169 >>> assert rs._asclist
3153
3170
3154 iterate ascending without fastasc:
3171 iterate ascending without fastasc:
3155 >>> rs = addset(xs, generatorset(ys), ascending=True)
3172 >>> rs = addset(xs, generatorset(ys), ascending=True)
3156 >>> assert rs.fastasc is None
3173 >>> assert rs.fastasc is None
3157 >>> [x for x in rs]
3174 >>> [x for x in rs]
3158 [0, 2, 3, 4, 5]
3175 [0, 2, 3, 4, 5]
3159
3176
3160 iterate descending without fastdesc:
3177 iterate descending without fastdesc:
3161 >>> rs = addset(generatorset(xs), ys, ascending=False)
3178 >>> rs = addset(generatorset(xs), ys, ascending=False)
3162 >>> assert rs.fastdesc is None
3179 >>> assert rs.fastdesc is None
3163 >>> [x for x in rs]
3180 >>> [x for x in rs]
3164 [5, 4, 3, 2, 0]
3181 [5, 4, 3, 2, 0]
3165 """
3182 """
3166 def __init__(self, revs1, revs2, ascending=None):
3183 def __init__(self, revs1, revs2, ascending=None):
3167 self._r1 = revs1
3184 self._r1 = revs1
3168 self._r2 = revs2
3185 self._r2 = revs2
3169 self._iter = None
3186 self._iter = None
3170 self._ascending = ascending
3187 self._ascending = ascending
3171 self._genlist = None
3188 self._genlist = None
3172 self._asclist = None
3189 self._asclist = None
3173
3190
3174 def __len__(self):
3191 def __len__(self):
3175 return len(self._list)
3192 return len(self._list)
3176
3193
3177 def __nonzero__(self):
3194 def __nonzero__(self):
3178 return bool(self._r1) or bool(self._r2)
3195 return bool(self._r1) or bool(self._r2)
3179
3196
3180 @util.propertycache
3197 @util.propertycache
3181 def _list(self):
3198 def _list(self):
3182 if not self._genlist:
3199 if not self._genlist:
3183 self._genlist = baseset(iter(self))
3200 self._genlist = baseset(iter(self))
3184 return self._genlist
3201 return self._genlist
3185
3202
3186 def __iter__(self):
3203 def __iter__(self):
3187 """Iterate over both collections without repeating elements
3204 """Iterate over both collections without repeating elements
3188
3205
3189 If the ascending attribute is not set, iterate over the first one and
3206 If the ascending attribute is not set, iterate over the first one and
3190 then over the second one checking for membership on the first one so we
3207 then over the second one checking for membership on the first one so we
3191 dont yield any duplicates.
3208 dont yield any duplicates.
3192
3209
3193 If the ascending attribute is set, iterate over both collections at the
3210 If the ascending attribute is set, iterate over both collections at the
3194 same time, yielding only one value at a time in the given order.
3211 same time, yielding only one value at a time in the given order.
3195 """
3212 """
3196 if self._ascending is None:
3213 if self._ascending is None:
3197 if self._genlist:
3214 if self._genlist:
3198 return iter(self._genlist)
3215 return iter(self._genlist)
3199 def arbitraryordergen():
3216 def arbitraryordergen():
3200 for r in self._r1:
3217 for r in self._r1:
3201 yield r
3218 yield r
3202 inr1 = self._r1.__contains__
3219 inr1 = self._r1.__contains__
3203 for r in self._r2:
3220 for r in self._r2:
3204 if not inr1(r):
3221 if not inr1(r):
3205 yield r
3222 yield r
3206 return arbitraryordergen()
3223 return arbitraryordergen()
3207 # try to use our own fast iterator if it exists
3224 # try to use our own fast iterator if it exists
3208 self._trysetasclist()
3225 self._trysetasclist()
3209 if self._ascending:
3226 if self._ascending:
3210 attr = 'fastasc'
3227 attr = 'fastasc'
3211 else:
3228 else:
3212 attr = 'fastdesc'
3229 attr = 'fastdesc'
3213 it = getattr(self, attr)
3230 it = getattr(self, attr)
3214 if it is not None:
3231 if it is not None:
3215 return it()
3232 return it()
3216 # maybe half of the component supports fast
3233 # maybe half of the component supports fast
3217 # get iterator for _r1
3234 # get iterator for _r1
3218 iter1 = getattr(self._r1, attr)
3235 iter1 = getattr(self._r1, attr)
3219 if iter1 is None:
3236 if iter1 is None:
3220 # let's avoid side effect (not sure it matters)
3237 # let's avoid side effect (not sure it matters)
3221 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3238 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3222 else:
3239 else:
3223 iter1 = iter1()
3240 iter1 = iter1()
3224 # get iterator for _r2
3241 # get iterator for _r2
3225 iter2 = getattr(self._r2, attr)
3242 iter2 = getattr(self._r2, attr)
3226 if iter2 is None:
3243 if iter2 is None:
3227 # let's avoid side effect (not sure it matters)
3244 # let's avoid side effect (not sure it matters)
3228 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3245 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3229 else:
3246 else:
3230 iter2 = iter2()
3247 iter2 = iter2()
3231 return _iterordered(self._ascending, iter1, iter2)
3248 return _iterordered(self._ascending, iter1, iter2)
3232
3249
3233 def _trysetasclist(self):
3250 def _trysetasclist(self):
3234 """populate the _asclist attribute if possible and necessary"""
3251 """populate the _asclist attribute if possible and necessary"""
3235 if self._genlist is not None and self._asclist is None:
3252 if self._genlist is not None and self._asclist is None:
3236 self._asclist = sorted(self._genlist)
3253 self._asclist = sorted(self._genlist)
3237
3254
3238 @property
3255 @property
3239 def fastasc(self):
3256 def fastasc(self):
3240 self._trysetasclist()
3257 self._trysetasclist()
3241 if self._asclist is not None:
3258 if self._asclist is not None:
3242 return self._asclist.__iter__
3259 return self._asclist.__iter__
3243 iter1 = self._r1.fastasc
3260 iter1 = self._r1.fastasc
3244 iter2 = self._r2.fastasc
3261 iter2 = self._r2.fastasc
3245 if None in (iter1, iter2):
3262 if None in (iter1, iter2):
3246 return None
3263 return None
3247 return lambda: _iterordered(True, iter1(), iter2())
3264 return lambda: _iterordered(True, iter1(), iter2())
3248
3265
3249 @property
3266 @property
3250 def fastdesc(self):
3267 def fastdesc(self):
3251 self._trysetasclist()
3268 self._trysetasclist()
3252 if self._asclist is not None:
3269 if self._asclist is not None:
3253 return self._asclist.__reversed__
3270 return self._asclist.__reversed__
3254 iter1 = self._r1.fastdesc
3271 iter1 = self._r1.fastdesc
3255 iter2 = self._r2.fastdesc
3272 iter2 = self._r2.fastdesc
3256 if None in (iter1, iter2):
3273 if None in (iter1, iter2):
3257 return None
3274 return None
3258 return lambda: _iterordered(False, iter1(), iter2())
3275 return lambda: _iterordered(False, iter1(), iter2())
3259
3276
3260 def __contains__(self, x):
3277 def __contains__(self, x):
3261 return x in self._r1 or x in self._r2
3278 return x in self._r1 or x in self._r2
3262
3279
3263 def sort(self, reverse=False):
3280 def sort(self, reverse=False):
3264 """Sort the added set
3281 """Sort the added set
3265
3282
3266 For this we use the cached list with all the generated values and if we
3283 For this we use the cached list with all the generated values and if we
3267 know they are ascending or descending we can sort them in a smart way.
3284 know they are ascending or descending we can sort them in a smart way.
3268 """
3285 """
3269 self._ascending = not reverse
3286 self._ascending = not reverse
3270
3287
3271 def isascending(self):
3288 def isascending(self):
3272 return self._ascending is not None and self._ascending
3289 return self._ascending is not None and self._ascending
3273
3290
3274 def isdescending(self):
3291 def isdescending(self):
3275 return self._ascending is not None and not self._ascending
3292 return self._ascending is not None and not self._ascending
3276
3293
3277 def reverse(self):
3294 def reverse(self):
3278 if self._ascending is None:
3295 if self._ascending is None:
3279 self._list.reverse()
3296 self._list.reverse()
3280 else:
3297 else:
3281 self._ascending = not self._ascending
3298 self._ascending = not self._ascending
3282
3299
3283 def first(self):
3300 def first(self):
3284 for x in self:
3301 for x in self:
3285 return x
3302 return x
3286 return None
3303 return None
3287
3304
3288 def last(self):
3305 def last(self):
3289 self.reverse()
3306 self.reverse()
3290 val = self.first()
3307 val = self.first()
3291 self.reverse()
3308 self.reverse()
3292 return val
3309 return val
3293
3310
3294 def __repr__(self):
3311 def __repr__(self):
3295 d = {None: '', False: '-', True: '+'}[self._ascending]
3312 d = {None: '', False: '-', True: '+'}[self._ascending]
3296 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3313 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3297
3314
3298 class generatorset(abstractsmartset):
3315 class generatorset(abstractsmartset):
3299 """Wrap a generator for lazy iteration
3316 """Wrap a generator for lazy iteration
3300
3317
3301 Wrapper structure for generators that provides lazy membership and can
3318 Wrapper structure for generators that provides lazy membership and can
3302 be iterated more than once.
3319 be iterated more than once.
3303 When asked for membership it generates values until either it finds the
3320 When asked for membership it generates values until either it finds the
3304 requested one or has gone through all the elements in the generator
3321 requested one or has gone through all the elements in the generator
3305 """
3322 """
3306 def __init__(self, gen, iterasc=None):
3323 def __init__(self, gen, iterasc=None):
3307 """
3324 """
3308 gen: a generator producing the values for the generatorset.
3325 gen: a generator producing the values for the generatorset.
3309 """
3326 """
3310 self._gen = gen
3327 self._gen = gen
3311 self._asclist = None
3328 self._asclist = None
3312 self._cache = {}
3329 self._cache = {}
3313 self._genlist = []
3330 self._genlist = []
3314 self._finished = False
3331 self._finished = False
3315 self._ascending = True
3332 self._ascending = True
3316 if iterasc is not None:
3333 if iterasc is not None:
3317 if iterasc:
3334 if iterasc:
3318 self.fastasc = self._iterator
3335 self.fastasc = self._iterator
3319 self.__contains__ = self._asccontains
3336 self.__contains__ = self._asccontains
3320 else:
3337 else:
3321 self.fastdesc = self._iterator
3338 self.fastdesc = self._iterator
3322 self.__contains__ = self._desccontains
3339 self.__contains__ = self._desccontains
3323
3340
3324 def __nonzero__(self):
3341 def __nonzero__(self):
3325 # Do not use 'for r in self' because it will enforce the iteration
3342 # Do not use 'for r in self' because it will enforce the iteration
3326 # order (default ascending), possibly unrolling a whole descending
3343 # order (default ascending), possibly unrolling a whole descending
3327 # iterator.
3344 # iterator.
3328 if self._genlist:
3345 if self._genlist:
3329 return True
3346 return True
3330 for r in self._consumegen():
3347 for r in self._consumegen():
3331 return True
3348 return True
3332 return False
3349 return False
3333
3350
3334 def __contains__(self, x):
3351 def __contains__(self, x):
3335 if x in self._cache:
3352 if x in self._cache:
3336 return self._cache[x]
3353 return self._cache[x]
3337
3354
3338 # Use new values only, as existing values would be cached.
3355 # Use new values only, as existing values would be cached.
3339 for l in self._consumegen():
3356 for l in self._consumegen():
3340 if l == x:
3357 if l == x:
3341 return True
3358 return True
3342
3359
3343 self._cache[x] = False
3360 self._cache[x] = False
3344 return False
3361 return False
3345
3362
3346 def _asccontains(self, x):
3363 def _asccontains(self, x):
3347 """version of contains optimised for ascending generator"""
3364 """version of contains optimised for ascending generator"""
3348 if x in self._cache:
3365 if x in self._cache:
3349 return self._cache[x]
3366 return self._cache[x]
3350
3367
3351 # Use new values only, as existing values would be cached.
3368 # Use new values only, as existing values would be cached.
3352 for l in self._consumegen():
3369 for l in self._consumegen():
3353 if l == x:
3370 if l == x:
3354 return True
3371 return True
3355 if l > x:
3372 if l > x:
3356 break
3373 break
3357
3374
3358 self._cache[x] = False
3375 self._cache[x] = False
3359 return False
3376 return False
3360
3377
3361 def _desccontains(self, x):
3378 def _desccontains(self, x):
3362 """version of contains optimised for descending generator"""
3379 """version of contains optimised for descending generator"""
3363 if x in self._cache:
3380 if x in self._cache:
3364 return self._cache[x]
3381 return self._cache[x]
3365
3382
3366 # Use new values only, as existing values would be cached.
3383 # Use new values only, as existing values would be cached.
3367 for l in self._consumegen():
3384 for l in self._consumegen():
3368 if l == x:
3385 if l == x:
3369 return True
3386 return True
3370 if l < x:
3387 if l < x:
3371 break
3388 break
3372
3389
3373 self._cache[x] = False
3390 self._cache[x] = False
3374 return False
3391 return False
3375
3392
3376 def __iter__(self):
3393 def __iter__(self):
3377 if self._ascending:
3394 if self._ascending:
3378 it = self.fastasc
3395 it = self.fastasc
3379 else:
3396 else:
3380 it = self.fastdesc
3397 it = self.fastdesc
3381 if it is not None:
3398 if it is not None:
3382 return it()
3399 return it()
3383 # we need to consume the iterator
3400 # we need to consume the iterator
3384 for x in self._consumegen():
3401 for x in self._consumegen():
3385 pass
3402 pass
3386 # recall the same code
3403 # recall the same code
3387 return iter(self)
3404 return iter(self)
3388
3405
3389 def _iterator(self):
3406 def _iterator(self):
3390 if self._finished:
3407 if self._finished:
3391 return iter(self._genlist)
3408 return iter(self._genlist)
3392
3409
3393 # We have to use this complex iteration strategy to allow multiple
3410 # We have to use this complex iteration strategy to allow multiple
3394 # iterations at the same time. We need to be able to catch revision
3411 # iterations at the same time. We need to be able to catch revision
3395 # removed from _consumegen and added to genlist in another instance.
3412 # removed from _consumegen and added to genlist in another instance.
3396 #
3413 #
3397 # Getting rid of it would provide an about 15% speed up on this
3414 # Getting rid of it would provide an about 15% speed up on this
3398 # iteration.
3415 # iteration.
3399 genlist = self._genlist
3416 genlist = self._genlist
3400 nextrev = self._consumegen().next
3417 nextrev = self._consumegen().next
3401 _len = len # cache global lookup
3418 _len = len # cache global lookup
3402 def gen():
3419 def gen():
3403 i = 0
3420 i = 0
3404 while True:
3421 while True:
3405 if i < _len(genlist):
3422 if i < _len(genlist):
3406 yield genlist[i]
3423 yield genlist[i]
3407 else:
3424 else:
3408 yield nextrev()
3425 yield nextrev()
3409 i += 1
3426 i += 1
3410 return gen()
3427 return gen()
3411
3428
3412 def _consumegen(self):
3429 def _consumegen(self):
3413 cache = self._cache
3430 cache = self._cache
3414 genlist = self._genlist.append
3431 genlist = self._genlist.append
3415 for item in self._gen:
3432 for item in self._gen:
3416 cache[item] = True
3433 cache[item] = True
3417 genlist(item)
3434 genlist(item)
3418 yield item
3435 yield item
3419 if not self._finished:
3436 if not self._finished:
3420 self._finished = True
3437 self._finished = True
3421 asc = self._genlist[:]
3438 asc = self._genlist[:]
3422 asc.sort()
3439 asc.sort()
3423 self._asclist = asc
3440 self._asclist = asc
3424 self.fastasc = asc.__iter__
3441 self.fastasc = asc.__iter__
3425 self.fastdesc = asc.__reversed__
3442 self.fastdesc = asc.__reversed__
3426
3443
3427 def __len__(self):
3444 def __len__(self):
3428 for x in self._consumegen():
3445 for x in self._consumegen():
3429 pass
3446 pass
3430 return len(self._genlist)
3447 return len(self._genlist)
3431
3448
3432 def sort(self, reverse=False):
3449 def sort(self, reverse=False):
3433 self._ascending = not reverse
3450 self._ascending = not reverse
3434
3451
3435 def reverse(self):
3452 def reverse(self):
3436 self._ascending = not self._ascending
3453 self._ascending = not self._ascending
3437
3454
3438 def isascending(self):
3455 def isascending(self):
3439 return self._ascending
3456 return self._ascending
3440
3457
3441 def isdescending(self):
3458 def isdescending(self):
3442 return not self._ascending
3459 return not self._ascending
3443
3460
3444 def first(self):
3461 def first(self):
3445 if self._ascending:
3462 if self._ascending:
3446 it = self.fastasc
3463 it = self.fastasc
3447 else:
3464 else:
3448 it = self.fastdesc
3465 it = self.fastdesc
3449 if it is None:
3466 if it is None:
3450 # we need to consume all and try again
3467 # we need to consume all and try again
3451 for x in self._consumegen():
3468 for x in self._consumegen():
3452 pass
3469 pass
3453 return self.first()
3470 return self.first()
3454 return next(it(), None)
3471 return next(it(), None)
3455
3472
3456 def last(self):
3473 def last(self):
3457 if self._ascending:
3474 if self._ascending:
3458 it = self.fastdesc
3475 it = self.fastdesc
3459 else:
3476 else:
3460 it = self.fastasc
3477 it = self.fastasc
3461 if it is None:
3478 if it is None:
3462 # we need to consume all and try again
3479 # we need to consume all and try again
3463 for x in self._consumegen():
3480 for x in self._consumegen():
3464 pass
3481 pass
3465 return self.first()
3482 return self.first()
3466 return next(it(), None)
3483 return next(it(), None)
3467
3484
3468 def __repr__(self):
3485 def __repr__(self):
3469 d = {False: '-', True: '+'}[self._ascending]
3486 d = {False: '-', True: '+'}[self._ascending]
3470 return '<%s%s>' % (type(self).__name__, d)
3487 return '<%s%s>' % (type(self).__name__, d)
3471
3488
3472 class spanset(abstractsmartset):
3489 class spanset(abstractsmartset):
3473 """Duck type for baseset class which represents a range of revisions and
3490 """Duck type for baseset class which represents a range of revisions and
3474 can work lazily and without having all the range in memory
3491 can work lazily and without having all the range in memory
3475
3492
3476 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3493 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3477 notable points:
3494 notable points:
3478 - when x < y it will be automatically descending,
3495 - when x < y it will be automatically descending,
3479 - revision filtered with this repoview will be skipped.
3496 - revision filtered with this repoview will be skipped.
3480
3497
3481 """
3498 """
3482 def __init__(self, repo, start=0, end=None):
3499 def __init__(self, repo, start=0, end=None):
3483 """
3500 """
3484 start: first revision included the set
3501 start: first revision included the set
3485 (default to 0)
3502 (default to 0)
3486 end: first revision excluded (last+1)
3503 end: first revision excluded (last+1)
3487 (default to len(repo)
3504 (default to len(repo)
3488
3505
3489 Spanset will be descending if `end` < `start`.
3506 Spanset will be descending if `end` < `start`.
3490 """
3507 """
3491 if end is None:
3508 if end is None:
3492 end = len(repo)
3509 end = len(repo)
3493 self._ascending = start <= end
3510 self._ascending = start <= end
3494 if not self._ascending:
3511 if not self._ascending:
3495 start, end = end + 1, start +1
3512 start, end = end + 1, start +1
3496 self._start = start
3513 self._start = start
3497 self._end = end
3514 self._end = end
3498 self._hiddenrevs = repo.changelog.filteredrevs
3515 self._hiddenrevs = repo.changelog.filteredrevs
3499
3516
3500 def sort(self, reverse=False):
3517 def sort(self, reverse=False):
3501 self._ascending = not reverse
3518 self._ascending = not reverse
3502
3519
3503 def reverse(self):
3520 def reverse(self):
3504 self._ascending = not self._ascending
3521 self._ascending = not self._ascending
3505
3522
3506 def _iterfilter(self, iterrange):
3523 def _iterfilter(self, iterrange):
3507 s = self._hiddenrevs
3524 s = self._hiddenrevs
3508 for r in iterrange:
3525 for r in iterrange:
3509 if r not in s:
3526 if r not in s:
3510 yield r
3527 yield r
3511
3528
3512 def __iter__(self):
3529 def __iter__(self):
3513 if self._ascending:
3530 if self._ascending:
3514 return self.fastasc()
3531 return self.fastasc()
3515 else:
3532 else:
3516 return self.fastdesc()
3533 return self.fastdesc()
3517
3534
3518 def fastasc(self):
3535 def fastasc(self):
3519 iterrange = xrange(self._start, self._end)
3536 iterrange = xrange(self._start, self._end)
3520 if self._hiddenrevs:
3537 if self._hiddenrevs:
3521 return self._iterfilter(iterrange)
3538 return self._iterfilter(iterrange)
3522 return iter(iterrange)
3539 return iter(iterrange)
3523
3540
3524 def fastdesc(self):
3541 def fastdesc(self):
3525 iterrange = xrange(self._end - 1, self._start - 1, -1)
3542 iterrange = xrange(self._end - 1, self._start - 1, -1)
3526 if self._hiddenrevs:
3543 if self._hiddenrevs:
3527 return self._iterfilter(iterrange)
3544 return self._iterfilter(iterrange)
3528 return iter(iterrange)
3545 return iter(iterrange)
3529
3546
3530 def __contains__(self, rev):
3547 def __contains__(self, rev):
3531 hidden = self._hiddenrevs
3548 hidden = self._hiddenrevs
3532 return ((self._start <= rev < self._end)
3549 return ((self._start <= rev < self._end)
3533 and not (hidden and rev in hidden))
3550 and not (hidden and rev in hidden))
3534
3551
3535 def __nonzero__(self):
3552 def __nonzero__(self):
3536 for r in self:
3553 for r in self:
3537 return True
3554 return True
3538 return False
3555 return False
3539
3556
3540 def __len__(self):
3557 def __len__(self):
3541 if not self._hiddenrevs:
3558 if not self._hiddenrevs:
3542 return abs(self._end - self._start)
3559 return abs(self._end - self._start)
3543 else:
3560 else:
3544 count = 0
3561 count = 0
3545 start = self._start
3562 start = self._start
3546 end = self._end
3563 end = self._end
3547 for rev in self._hiddenrevs:
3564 for rev in self._hiddenrevs:
3548 if (end < rev <= start) or (start <= rev < end):
3565 if (end < rev <= start) or (start <= rev < end):
3549 count += 1
3566 count += 1
3550 return abs(self._end - self._start) - count
3567 return abs(self._end - self._start) - count
3551
3568
3552 def isascending(self):
3569 def isascending(self):
3553 return self._ascending
3570 return self._ascending
3554
3571
3555 def isdescending(self):
3572 def isdescending(self):
3556 return not self._ascending
3573 return not self._ascending
3557
3574
3558 def first(self):
3575 def first(self):
3559 if self._ascending:
3576 if self._ascending:
3560 it = self.fastasc
3577 it = self.fastasc
3561 else:
3578 else:
3562 it = self.fastdesc
3579 it = self.fastdesc
3563 for x in it():
3580 for x in it():
3564 return x
3581 return x
3565 return None
3582 return None
3566
3583
3567 def last(self):
3584 def last(self):
3568 if self._ascending:
3585 if self._ascending:
3569 it = self.fastdesc
3586 it = self.fastdesc
3570 else:
3587 else:
3571 it = self.fastasc
3588 it = self.fastasc
3572 for x in it():
3589 for x in it():
3573 return x
3590 return x
3574 return None
3591 return None
3575
3592
3576 def __repr__(self):
3593 def __repr__(self):
3577 d = {False: '-', True: '+'}[self._ascending]
3594 d = {False: '-', True: '+'}[self._ascending]
3578 return '<%s%s %d:%d>' % (type(self).__name__, d,
3595 return '<%s%s %d:%d>' % (type(self).__name__, d,
3579 self._start, self._end - 1)
3596 self._start, self._end - 1)
3580
3597
3581 class fullreposet(spanset):
3598 class fullreposet(spanset):
3582 """a set containing all revisions in the repo
3599 """a set containing all revisions in the repo
3583
3600
3584 This class exists to host special optimization and magic to handle virtual
3601 This class exists to host special optimization and magic to handle virtual
3585 revisions such as "null".
3602 revisions such as "null".
3586 """
3603 """
3587
3604
3588 def __init__(self, repo):
3605 def __init__(self, repo):
3589 super(fullreposet, self).__init__(repo)
3606 super(fullreposet, self).__init__(repo)
3590
3607
3591 def __and__(self, other):
3608 def __and__(self, other):
3592 """As self contains the whole repo, all of the other set should also be
3609 """As self contains the whole repo, all of the other set should also be
3593 in self. Therefore `self & other = other`.
3610 in self. Therefore `self & other = other`.
3594
3611
3595 This boldly assumes the other contains valid revs only.
3612 This boldly assumes the other contains valid revs only.
3596 """
3613 """
3597 # other not a smartset, make is so
3614 # other not a smartset, make is so
3598 if not util.safehasattr(other, 'isascending'):
3615 if not util.safehasattr(other, 'isascending'):
3599 # filter out hidden revision
3616 # filter out hidden revision
3600 # (this boldly assumes all smartset are pure)
3617 # (this boldly assumes all smartset are pure)
3601 #
3618 #
3602 # `other` was used with "&", let's assume this is a set like
3619 # `other` was used with "&", let's assume this is a set like
3603 # object.
3620 # object.
3604 other = baseset(other - self._hiddenrevs)
3621 other = baseset(other - self._hiddenrevs)
3605
3622
3606 # XXX As fullreposet is also used as bootstrap, this is wrong.
3623 # XXX As fullreposet is also used as bootstrap, this is wrong.
3607 #
3624 #
3608 # With a giveme312() revset returning [3,1,2], this makes
3625 # With a giveme312() revset returning [3,1,2], this makes
3609 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3626 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3610 # We cannot just drop it because other usage still need to sort it:
3627 # We cannot just drop it because other usage still need to sort it:
3611 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3628 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3612 #
3629 #
3613 # There is also some faulty revset implementations that rely on it
3630 # There is also some faulty revset implementations that rely on it
3614 # (eg: children as of its state in e8075329c5fb)
3631 # (eg: children as of its state in e8075329c5fb)
3615 #
3632 #
3616 # When we fix the two points above we can move this into the if clause
3633 # When we fix the two points above we can move this into the if clause
3617 other.sort(reverse=self.isdescending())
3634 other.sort(reverse=self.isdescending())
3618 return other
3635 return other
3619
3636
3620 def prettyformatset(revs):
3637 def prettyformatset(revs):
3621 lines = []
3638 lines = []
3622 rs = repr(revs)
3639 rs = repr(revs)
3623 p = 0
3640 p = 0
3624 while p < len(rs):
3641 while p < len(rs):
3625 q = rs.find('<', p + 1)
3642 q = rs.find('<', p + 1)
3626 if q < 0:
3643 if q < 0:
3627 q = len(rs)
3644 q = len(rs)
3628 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3645 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3629 assert l >= 0
3646 assert l >= 0
3630 lines.append((l, rs[p:q].rstrip()))
3647 lines.append((l, rs[p:q].rstrip()))
3631 p = q
3648 p = q
3632 return '\n'.join(' ' * l + s for l, s in lines)
3649 return '\n'.join(' ' * l + s for l, s in lines)
3633
3650
3634 def loadpredicate(ui, extname, registrarobj):
3651 def loadpredicate(ui, extname, registrarobj):
3635 """Load revset predicates from specified registrarobj
3652 """Load revset predicates from specified registrarobj
3636 """
3653 """
3637 for name, func in registrarobj._table.iteritems():
3654 for name, func in registrarobj._table.iteritems():
3638 symbols[name] = func
3655 symbols[name] = func
3639 if func._safe:
3656 if func._safe:
3640 safesymbols.add(name)
3657 safesymbols.add(name)
3641
3658
3642 # load built-in predicates explicitly to setup safesymbols
3659 # load built-in predicates explicitly to setup safesymbols
3643 loadpredicate(None, None, predicate)
3660 loadpredicate(None, None, predicate)
3644
3661
3645 # tell hggettext to extract docstrings from these functions:
3662 # tell hggettext to extract docstrings from these functions:
3646 i18nfunctions = symbols.values()
3663 i18nfunctions = symbols.values()
@@ -1,2228 +1,2234
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 $ hg init repo
34 $ hg init repo
35 $ cd repo
35 $ cd repo
36
36
37 $ echo a > a
37 $ echo a > a
38 $ hg branch a
38 $ hg branch a
39 marked working directory as branch a
39 marked working directory as branch a
40 (branches are permanent and global, did you want a bookmark?)
40 (branches are permanent and global, did you want a bookmark?)
41 $ hg ci -Aqm0
41 $ hg ci -Aqm0
42
42
43 $ echo b > b
43 $ echo b > b
44 $ hg branch b
44 $ hg branch b
45 marked working directory as branch b
45 marked working directory as branch b
46 $ hg ci -Aqm1
46 $ hg ci -Aqm1
47
47
48 $ rm a
48 $ rm a
49 $ hg branch a-b-c-
49 $ hg branch a-b-c-
50 marked working directory as branch a-b-c-
50 marked working directory as branch a-b-c-
51 $ hg ci -Aqm2 -u Bob
51 $ hg ci -Aqm2 -u Bob
52
52
53 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
53 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
54 2
54 2
55 $ hg log -r "extra('branch')" --template '{rev}\n'
55 $ hg log -r "extra('branch')" --template '{rev}\n'
56 0
56 0
57 1
57 1
58 2
58 2
59 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
59 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
60 0 a
60 0 a
61 2 a-b-c-
61 2 a-b-c-
62
62
63 $ hg co 1
63 $ hg co 1
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 $ hg branch +a+b+c+
65 $ hg branch +a+b+c+
66 marked working directory as branch +a+b+c+
66 marked working directory as branch +a+b+c+
67 $ hg ci -Aqm3
67 $ hg ci -Aqm3
68
68
69 $ hg co 2 # interleave
69 $ hg co 2 # interleave
70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
71 $ echo bb > b
71 $ echo bb > b
72 $ hg branch -- -a-b-c-
72 $ hg branch -- -a-b-c-
73 marked working directory as branch -a-b-c-
73 marked working directory as branch -a-b-c-
74 $ hg ci -Aqm4 -d "May 12 2005"
74 $ hg ci -Aqm4 -d "May 12 2005"
75
75
76 $ hg co 3
76 $ hg co 3
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 $ hg branch !a/b/c/
78 $ hg branch !a/b/c/
79 marked working directory as branch !a/b/c/
79 marked working directory as branch !a/b/c/
80 $ hg ci -Aqm"5 bug"
80 $ hg ci -Aqm"5 bug"
81
81
82 $ hg merge 4
82 $ hg merge 4
83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
84 (branch merge, don't forget to commit)
84 (branch merge, don't forget to commit)
85 $ hg branch _a_b_c_
85 $ hg branch _a_b_c_
86 marked working directory as branch _a_b_c_
86 marked working directory as branch _a_b_c_
87 $ hg ci -Aqm"6 issue619"
87 $ hg ci -Aqm"6 issue619"
88
88
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 -Aqm7
91 $ hg ci -Aqm7
92
92
93 $ hg branch all
93 $ hg branch all
94 marked working directory as branch all
94 marked working directory as branch all
95
95
96 $ hg co 4
96 $ hg co 4
97 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 $ hg branch Γ©
98 $ hg branch Γ©
99 marked working directory as branch \xc3\xa9 (esc)
99 marked working directory as branch \xc3\xa9 (esc)
100 $ hg ci -Aqm9
100 $ hg ci -Aqm9
101
101
102 $ hg tag -r6 1.0
102 $ hg tag -r6 1.0
103 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
103 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
104
104
105 $ hg clone --quiet -U -r 7 . ../remote1
105 $ hg clone --quiet -U -r 7 . ../remote1
106 $ hg clone --quiet -U -r 8 . ../remote2
106 $ hg clone --quiet -U -r 8 . ../remote2
107 $ echo "[paths]" >> .hg/hgrc
107 $ echo "[paths]" >> .hg/hgrc
108 $ echo "default = ../remote1" >> .hg/hgrc
108 $ echo "default = ../remote1" >> .hg/hgrc
109
109
110 trivial
110 trivial
111
111
112 $ try 0:1
112 $ try 0:1
113 (range
113 (range
114 ('symbol', '0')
114 ('symbol', '0')
115 ('symbol', '1'))
115 ('symbol', '1'))
116 * set:
116 * set:
117 <spanset+ 0:1>
117 <spanset+ 0:1>
118 0
118 0
119 1
119 1
120 $ try --optimize :
120 $ try --optimize :
121 (rangeall
121 (rangeall
122 None)
122 None)
123 * optimized:
123 * optimized:
124 (range
124 (range
125 ('string', '0')
125 ('string', '0')
126 ('string', 'tip'))
126 ('string', 'tip'))
127 * set:
127 * set:
128 <spanset+ 0:9>
128 <spanset+ 0:9>
129 0
129 0
130 1
130 1
131 2
131 2
132 3
132 3
133 4
133 4
134 5
134 5
135 6
135 6
136 7
136 7
137 8
137 8
138 9
138 9
139 $ try 3::6
139 $ try 3::6
140 (dagrange
140 (dagrange
141 ('symbol', '3')
141 ('symbol', '3')
142 ('symbol', '6'))
142 ('symbol', '6'))
143 * set:
143 * set:
144 <baseset+ [3, 5, 6]>
144 <baseset+ [3, 5, 6]>
145 3
145 3
146 5
146 5
147 6
147 6
148 $ try '0|1|2'
148 $ try '0|1|2'
149 (or
149 (or
150 ('symbol', '0')
150 ('symbol', '0')
151 ('symbol', '1')
151 ('symbol', '1')
152 ('symbol', '2'))
152 ('symbol', '2'))
153 * set:
153 * set:
154 <baseset [0, 1, 2]>
154 <baseset [0, 1, 2]>
155 0
155 0
156 1
156 1
157 2
157 2
158
158
159 names that should work without quoting
159 names that should work without quoting
160
160
161 $ try a
161 $ try a
162 ('symbol', 'a')
162 ('symbol', 'a')
163 * set:
163 * set:
164 <baseset [0]>
164 <baseset [0]>
165 0
165 0
166 $ try b-a
166 $ try b-a
167 (minus
167 (minus
168 ('symbol', 'b')
168 ('symbol', 'b')
169 ('symbol', 'a'))
169 ('symbol', 'a'))
170 * set:
170 * set:
171 <filteredset
171 <filteredset
172 <baseset [1]>,
172 <baseset [1]>,
173 <not
173 <not
174 <baseset [0]>>>
174 <baseset [0]>>>
175 1
175 1
176 $ try _a_b_c_
176 $ try _a_b_c_
177 ('symbol', '_a_b_c_')
177 ('symbol', '_a_b_c_')
178 * set:
178 * set:
179 <baseset [6]>
179 <baseset [6]>
180 6
180 6
181 $ try _a_b_c_-a
181 $ try _a_b_c_-a
182 (minus
182 (minus
183 ('symbol', '_a_b_c_')
183 ('symbol', '_a_b_c_')
184 ('symbol', 'a'))
184 ('symbol', 'a'))
185 * set:
185 * set:
186 <filteredset
186 <filteredset
187 <baseset [6]>,
187 <baseset [6]>,
188 <not
188 <not
189 <baseset [0]>>>
189 <baseset [0]>>>
190 6
190 6
191 $ try .a.b.c.
191 $ try .a.b.c.
192 ('symbol', '.a.b.c.')
192 ('symbol', '.a.b.c.')
193 * set:
193 * set:
194 <baseset [7]>
194 <baseset [7]>
195 7
195 7
196 $ try .a.b.c.-a
196 $ try .a.b.c.-a
197 (minus
197 (minus
198 ('symbol', '.a.b.c.')
198 ('symbol', '.a.b.c.')
199 ('symbol', 'a'))
199 ('symbol', 'a'))
200 * set:
200 * set:
201 <filteredset
201 <filteredset
202 <baseset [7]>,
202 <baseset [7]>,
203 <not
203 <not
204 <baseset [0]>>>
204 <baseset [0]>>>
205 7
205 7
206
206
207 names that should be caught by fallback mechanism
207 names that should be caught by fallback mechanism
208
208
209 $ try -- '-a-b-c-'
209 $ try -- '-a-b-c-'
210 ('symbol', '-a-b-c-')
210 ('symbol', '-a-b-c-')
211 * set:
211 * set:
212 <baseset [4]>
212 <baseset [4]>
213 4
213 4
214 $ log -a-b-c-
214 $ log -a-b-c-
215 4
215 4
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 [3]>
219 <baseset [3]>
220 3
220 3
221 $ try '+a+b+c+:'
221 $ try '+a+b+c+:'
222 (rangepost
222 (rangepost
223 ('symbol', '+a+b+c+'))
223 ('symbol', '+a+b+c+'))
224 * set:
224 * set:
225 <spanset+ 3:9>
225 <spanset+ 3:9>
226 3
226 3
227 4
227 4
228 5
228 5
229 6
229 6
230 7
230 7
231 8
231 8
232 9
232 9
233 $ try ':+a+b+c+'
233 $ try ':+a+b+c+'
234 (rangepre
234 (rangepre
235 ('symbol', '+a+b+c+'))
235 ('symbol', '+a+b+c+'))
236 * set:
236 * set:
237 <spanset+ 0:3>
237 <spanset+ 0:3>
238 0
238 0
239 1
239 1
240 2
240 2
241 3
241 3
242 $ try -- '-a-b-c-:+a+b+c+'
242 $ try -- '-a-b-c-:+a+b+c+'
243 (range
243 (range
244 ('symbol', '-a-b-c-')
244 ('symbol', '-a-b-c-')
245 ('symbol', '+a+b+c+'))
245 ('symbol', '+a+b+c+'))
246 * set:
246 * set:
247 <spanset- 3:4>
247 <spanset- 3:4>
248 4
248 4
249 3
249 3
250 $ log '-a-b-c-:+a+b+c+'
250 $ log '-a-b-c-:+a+b+c+'
251 4
251 4
252 3
252 3
253
253
254 $ try -- -a-b-c--a # complains
254 $ try -- -a-b-c--a # complains
255 (minus
255 (minus
256 (minus
256 (minus
257 (minus
257 (minus
258 (negate
258 (negate
259 ('symbol', 'a'))
259 ('symbol', 'a'))
260 ('symbol', 'b'))
260 ('symbol', 'b'))
261 ('symbol', 'c'))
261 ('symbol', 'c'))
262 (negate
262 (negate
263 ('symbol', 'a')))
263 ('symbol', 'a')))
264 abort: unknown revision '-a'!
264 abort: unknown revision '-a'!
265 [255]
265 [255]
266 $ try Γ©
266 $ try Γ©
267 ('symbol', '\xc3\xa9')
267 ('symbol', '\xc3\xa9')
268 * set:
268 * set:
269 <baseset [9]>
269 <baseset [9]>
270 9
270 9
271
271
272 no quoting needed
272 no quoting needed
273
273
274 $ log ::a-b-c-
274 $ log ::a-b-c-
275 0
275 0
276 1
276 1
277 2
277 2
278
278
279 quoting needed
279 quoting needed
280
280
281 $ try '"-a-b-c-"-a'
281 $ try '"-a-b-c-"-a'
282 (minus
282 (minus
283 ('string', '-a-b-c-')
283 ('string', '-a-b-c-')
284 ('symbol', 'a'))
284 ('symbol', 'a'))
285 * set:
285 * set:
286 <filteredset
286 <filteredset
287 <baseset [4]>,
287 <baseset [4]>,
288 <not
288 <not
289 <baseset [0]>>>
289 <baseset [0]>>>
290 4
290 4
291
291
292 $ log '1 or 2'
292 $ log '1 or 2'
293 1
293 1
294 2
294 2
295 $ log '1|2'
295 $ log '1|2'
296 1
296 1
297 2
297 2
298 $ log '1 and 2'
298 $ log '1 and 2'
299 $ log '1&2'
299 $ log '1&2'
300 $ try '1&2|3' # precedence - and is higher
300 $ try '1&2|3' # precedence - and is higher
301 (or
301 (or
302 (and
302 (and
303 ('symbol', '1')
303 ('symbol', '1')
304 ('symbol', '2'))
304 ('symbol', '2'))
305 ('symbol', '3'))
305 ('symbol', '3'))
306 * set:
306 * set:
307 <addset
307 <addset
308 <baseset []>,
308 <baseset []>,
309 <baseset [3]>>
309 <baseset [3]>>
310 3
310 3
311 $ try '1|2&3'
311 $ try '1|2&3'
312 (or
312 (or
313 ('symbol', '1')
313 ('symbol', '1')
314 (and
314 (and
315 ('symbol', '2')
315 ('symbol', '2')
316 ('symbol', '3')))
316 ('symbol', '3')))
317 * set:
317 * set:
318 <addset
318 <addset
319 <baseset [1]>,
319 <baseset [1]>,
320 <baseset []>>
320 <baseset []>>
321 1
321 1
322 $ try '1&2&3' # associativity
322 $ try '1&2&3' # associativity
323 (and
323 (and
324 (and
324 (and
325 ('symbol', '1')
325 ('symbol', '1')
326 ('symbol', '2'))
326 ('symbol', '2'))
327 ('symbol', '3'))
327 ('symbol', '3'))
328 * set:
328 * set:
329 <baseset []>
329 <baseset []>
330 $ try '1|(2|3)'
330 $ try '1|(2|3)'
331 (or
331 (or
332 ('symbol', '1')
332 ('symbol', '1')
333 (group
333 (group
334 (or
334 (or
335 ('symbol', '2')
335 ('symbol', '2')
336 ('symbol', '3'))))
336 ('symbol', '3'))))
337 * set:
337 * set:
338 <addset
338 <addset
339 <baseset [1]>,
339 <baseset [1]>,
340 <baseset [2, 3]>>
340 <baseset [2, 3]>>
341 1
341 1
342 2
342 2
343 3
343 3
344 $ log '1.0' # tag
344 $ log '1.0' # tag
345 6
345 6
346 $ log 'a' # branch
346 $ log 'a' # branch
347 0
347 0
348 $ log '2785f51ee'
348 $ log '2785f51ee'
349 0
349 0
350 $ log 'date(2005)'
350 $ log 'date(2005)'
351 4
351 4
352 $ log 'date(this is a test)'
352 $ log 'date(this is a test)'
353 hg: parse error at 10: unexpected token: symbol
353 hg: parse error at 10: unexpected token: symbol
354 [255]
354 [255]
355 $ log 'date()'
355 $ log 'date()'
356 hg: parse error: date requires a string
356 hg: parse error: date requires a string
357 [255]
357 [255]
358 $ log 'date'
358 $ log 'date'
359 abort: unknown revision 'date'!
359 abort: unknown revision 'date'!
360 [255]
360 [255]
361 $ log 'date('
361 $ log 'date('
362 hg: parse error at 5: not a prefix: end
362 hg: parse error at 5: not a prefix: end
363 [255]
363 [255]
364 $ log 'date("\xy")'
364 $ log 'date("\xy")'
365 hg: parse error: invalid \x escape
365 hg: parse error: invalid \x escape
366 [255]
366 [255]
367 $ log 'date(tip)'
367 $ log 'date(tip)'
368 abort: invalid date: 'tip'
368 abort: invalid date: 'tip'
369 [255]
369 [255]
370 $ log '0:date'
370 $ log '0:date'
371 abort: unknown revision 'date'!
371 abort: unknown revision 'date'!
372 [255]
372 [255]
373 $ log '::"date"'
373 $ log '::"date"'
374 abort: unknown revision 'date'!
374 abort: unknown revision 'date'!
375 [255]
375 [255]
376 $ hg book date -r 4
376 $ hg book date -r 4
377 $ log '0:date'
377 $ log '0:date'
378 0
378 0
379 1
379 1
380 2
380 2
381 3
381 3
382 4
382 4
383 $ log '::date'
383 $ log '::date'
384 0
384 0
385 1
385 1
386 2
386 2
387 4
387 4
388 $ log '::"date"'
388 $ log '::"date"'
389 0
389 0
390 1
390 1
391 2
391 2
392 4
392 4
393 $ log 'date(2005) and 1::'
393 $ log 'date(2005) and 1::'
394 4
394 4
395 $ hg book -d date
395 $ hg book -d date
396
396
397 keyword arguments
397 keyword arguments
398
398
399 $ log 'extra(branch, value=a)'
399 $ log 'extra(branch, value=a)'
400 0
400 0
401
401
402 $ log 'extra(branch, a, b)'
402 $ log 'extra(branch, a, b)'
403 hg: parse error: extra takes at most 2 arguments
403 hg: parse error: extra takes at most 2 arguments
404 [255]
404 [255]
405 $ log 'extra(a, label=b)'
405 $ log 'extra(a, label=b)'
406 hg: parse error: extra got multiple values for keyword argument 'label'
406 hg: parse error: extra got multiple values for keyword argument 'label'
407 [255]
407 [255]
408 $ log 'extra(label=branch, default)'
408 $ log 'extra(label=branch, default)'
409 hg: parse error: extra got an invalid argument
409 hg: parse error: extra got an invalid argument
410 [255]
410 [255]
411 $ log 'extra(branch, foo+bar=baz)'
411 $ log 'extra(branch, foo+bar=baz)'
412 hg: parse error: extra got an invalid argument
412 hg: parse error: extra got an invalid argument
413 [255]
413 [255]
414 $ log 'extra(unknown=branch)'
414 $ log 'extra(unknown=branch)'
415 hg: parse error: extra got an unexpected keyword argument 'unknown'
415 hg: parse error: extra got an unexpected keyword argument 'unknown'
416 [255]
416 [255]
417
417
418 $ try 'foo=bar|baz'
418 $ try 'foo=bar|baz'
419 (keyvalue
419 (keyvalue
420 ('symbol', 'foo')
420 ('symbol', 'foo')
421 (or
421 (or
422 ('symbol', 'bar')
422 ('symbol', 'bar')
423 ('symbol', 'baz')))
423 ('symbol', 'baz')))
424 hg: parse error: can't use a key-value pair in this context
424 hg: parse error: can't use a key-value pair in this context
425 [255]
425 [255]
426
426
427 Test that symbols only get parsed as functions if there's an opening
427 Test that symbols only get parsed as functions if there's an opening
428 parenthesis.
428 parenthesis.
429
429
430 $ hg book only -r 9
430 $ hg book only -r 9
431 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
431 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
432 8
432 8
433 9
433 9
434
434
435 ancestor can accept 0 or more arguments
435 ancestor can accept 0 or more arguments
436
436
437 $ log 'ancestor()'
437 $ log 'ancestor()'
438 $ log 'ancestor(1)'
438 $ log 'ancestor(1)'
439 1
439 1
440 $ log 'ancestor(4,5)'
440 $ log 'ancestor(4,5)'
441 1
441 1
442 $ log 'ancestor(4,5) and 4'
442 $ log 'ancestor(4,5) and 4'
443 $ log 'ancestor(0,0,1,3)'
443 $ log 'ancestor(0,0,1,3)'
444 0
444 0
445 $ log 'ancestor(3,1,5,3,5,1)'
445 $ log 'ancestor(3,1,5,3,5,1)'
446 1
446 1
447 $ log 'ancestor(0,1,3,5)'
447 $ log 'ancestor(0,1,3,5)'
448 0
448 0
449 $ log 'ancestor(1,2,3,4,5)'
449 $ log 'ancestor(1,2,3,4,5)'
450 1
450 1
451
451
452 test ancestors
452 test ancestors
453
453
454 $ log 'ancestors(5)'
454 $ log 'ancestors(5)'
455 0
455 0
456 1
456 1
457 3
457 3
458 5
458 5
459 $ log 'ancestor(ancestors(5))'
459 $ log 'ancestor(ancestors(5))'
460 0
460 0
461 $ log '::r3232()'
461 $ log '::r3232()'
462 0
462 0
463 1
463 1
464 2
464 2
465 3
465 3
466
466
467 $ log 'author(bob)'
467 $ log 'author(bob)'
468 2
468 2
469 $ log 'author("re:bob|test")'
469 $ log 'author("re:bob|test")'
470 0
470 0
471 1
471 1
472 2
472 2
473 3
473 3
474 4
474 4
475 5
475 5
476 6
476 6
477 7
477 7
478 8
478 8
479 9
479 9
480 $ log 'branch(Γ©)'
480 $ log 'branch(Γ©)'
481 8
481 8
482 9
482 9
483 $ log 'branch(a)'
483 $ log 'branch(a)'
484 0
484 0
485 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
485 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
486 0 a
486 0 a
487 2 a-b-c-
487 2 a-b-c-
488 3 +a+b+c+
488 3 +a+b+c+
489 4 -a-b-c-
489 4 -a-b-c-
490 5 !a/b/c/
490 5 !a/b/c/
491 6 _a_b_c_
491 6 _a_b_c_
492 7 .a.b.c.
492 7 .a.b.c.
493 $ log 'children(ancestor(4,5))'
493 $ log 'children(ancestor(4,5))'
494 2
494 2
495 3
495 3
496 $ log 'closed()'
496 $ log 'closed()'
497 $ log 'contains(a)'
497 $ log 'contains(a)'
498 0
498 0
499 1
499 1
500 3
500 3
501 5
501 5
502 $ log 'contains("../repo/a")'
502 $ log 'contains("../repo/a")'
503 0
503 0
504 1
504 1
505 3
505 3
506 5
506 5
507 $ log 'desc(B)'
507 $ log 'desc(B)'
508 5
508 5
509 $ log 'descendants(2 or 3)'
509 $ log 'descendants(2 or 3)'
510 2
510 2
511 3
511 3
512 4
512 4
513 5
513 5
514 6
514 6
515 7
515 7
516 8
516 8
517 9
517 9
518 $ log 'file("b*")'
518 $ log 'file("b*")'
519 1
519 1
520 4
520 4
521 $ log 'filelog("b")'
521 $ log 'filelog("b")'
522 1
522 1
523 4
523 4
524 $ log 'filelog("../repo/b")'
524 $ log 'filelog("../repo/b")'
525 1
525 1
526 4
526 4
527 $ log 'follow()'
527 $ log 'follow()'
528 0
528 0
529 1
529 1
530 2
530 2
531 4
531 4
532 8
532 8
533 9
533 9
534 $ log 'grep("issue\d+")'
534 $ log 'grep("issue\d+")'
535 6
535 6
536 $ try 'grep("(")' # invalid regular expression
536 $ try 'grep("(")' # invalid regular expression
537 (func
537 (func
538 ('symbol', 'grep')
538 ('symbol', 'grep')
539 ('string', '('))
539 ('string', '('))
540 hg: parse error: invalid match pattern: unbalanced parenthesis
540 hg: parse error: invalid match pattern: unbalanced parenthesis
541 [255]
541 [255]
542 $ try 'grep("\bissue\d+")'
542 $ try 'grep("\bissue\d+")'
543 (func
543 (func
544 ('symbol', 'grep')
544 ('symbol', 'grep')
545 ('string', '\x08issue\\d+'))
545 ('string', '\x08issue\\d+'))
546 * set:
546 * set:
547 <filteredset
547 <filteredset
548 <fullreposet+ 0:9>>
548 <fullreposet+ 0:9>,
549 <grep '\x08issue\\d+'>>
549 $ try 'grep(r"\bissue\d+")'
550 $ try 'grep(r"\bissue\d+")'
550 (func
551 (func
551 ('symbol', 'grep')
552 ('symbol', 'grep')
552 ('string', '\\bissue\\d+'))
553 ('string', '\\bissue\\d+'))
553 * set:
554 * set:
554 <filteredset
555 <filteredset
555 <fullreposet+ 0:9>>
556 <fullreposet+ 0:9>,
557 <grep '\\bissue\\d+'>>
556 6
558 6
557 $ try 'grep(r"\")'
559 $ try 'grep(r"\")'
558 hg: parse error at 7: unterminated string
560 hg: parse error at 7: unterminated string
559 [255]
561 [255]
560 $ log 'head()'
562 $ log 'head()'
561 0
563 0
562 1
564 1
563 2
565 2
564 3
566 3
565 4
567 4
566 5
568 5
567 6
569 6
568 7
570 7
569 9
571 9
570 $ log 'heads(6::)'
572 $ log 'heads(6::)'
571 7
573 7
572 $ log 'keyword(issue)'
574 $ log 'keyword(issue)'
573 6
575 6
574 $ log 'keyword("test a")'
576 $ log 'keyword("test a")'
575 $ log 'limit(head(), 1)'
577 $ log 'limit(head(), 1)'
576 0
578 0
577 $ log 'limit(author("re:bob|test"), 3, 5)'
579 $ log 'limit(author("re:bob|test"), 3, 5)'
578 5
580 5
579 6
581 6
580 7
582 7
581 $ log 'limit(author("re:bob|test"), offset=6)'
583 $ log 'limit(author("re:bob|test"), offset=6)'
582 6
584 6
583 $ log 'limit(author("re:bob|test"), offset=10)'
585 $ log 'limit(author("re:bob|test"), offset=10)'
584 $ log 'limit(all(), 1, -1)'
586 $ log 'limit(all(), 1, -1)'
585 hg: parse error: negative offset
587 hg: parse error: negative offset
586 [255]
588 [255]
587 $ log 'matching(6)'
589 $ log 'matching(6)'
588 6
590 6
589 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
591 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
590 6
592 6
591 7
593 7
592
594
593 Testing min and max
595 Testing min and max
594
596
595 max: simple
597 max: simple
596
598
597 $ log 'max(contains(a))'
599 $ log 'max(contains(a))'
598 5
600 5
599
601
600 max: simple on unordered set)
602 max: simple on unordered set)
601
603
602 $ log 'max((4+0+2+5+7) and contains(a))'
604 $ log 'max((4+0+2+5+7) and contains(a))'
603 5
605 5
604
606
605 max: no result
607 max: no result
606
608
607 $ log 'max(contains(stringthatdoesnotappearanywhere))'
609 $ log 'max(contains(stringthatdoesnotappearanywhere))'
608
610
609 max: no result on unordered set
611 max: no result on unordered set
610
612
611 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
613 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
612
614
613 min: simple
615 min: simple
614
616
615 $ log 'min(contains(a))'
617 $ log 'min(contains(a))'
616 0
618 0
617
619
618 min: simple on unordered set
620 min: simple on unordered set
619
621
620 $ log 'min((4+0+2+5+7) and contains(a))'
622 $ log 'min((4+0+2+5+7) and contains(a))'
621 0
623 0
622
624
623 min: empty
625 min: empty
624
626
625 $ log 'min(contains(stringthatdoesnotappearanywhere))'
627 $ log 'min(contains(stringthatdoesnotappearanywhere))'
626
628
627 min: empty on unordered set
629 min: empty on unordered set
628
630
629 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
631 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
630
632
631
633
632 $ log 'merge()'
634 $ log 'merge()'
633 6
635 6
634 $ log 'branchpoint()'
636 $ log 'branchpoint()'
635 1
637 1
636 4
638 4
637 $ log 'modifies(b)'
639 $ log 'modifies(b)'
638 4
640 4
639 $ log 'modifies("path:b")'
641 $ log 'modifies("path:b")'
640 4
642 4
641 $ log 'modifies("*")'
643 $ log 'modifies("*")'
642 4
644 4
643 6
645 6
644 $ log 'modifies("set:modified()")'
646 $ log 'modifies("set:modified()")'
645 4
647 4
646 $ log 'id(5)'
648 $ log 'id(5)'
647 2
649 2
648 $ log 'only(9)'
650 $ log 'only(9)'
649 8
651 8
650 9
652 9
651 $ log 'only(8)'
653 $ log 'only(8)'
652 8
654 8
653 $ log 'only(9, 5)'
655 $ log 'only(9, 5)'
654 2
656 2
655 4
657 4
656 8
658 8
657 9
659 9
658 $ log 'only(7 + 9, 5 + 2)'
660 $ log 'only(7 + 9, 5 + 2)'
659 4
661 4
660 6
662 6
661 7
663 7
662 8
664 8
663 9
665 9
664
666
665 Test empty set input
667 Test empty set input
666 $ log 'only(p2())'
668 $ log 'only(p2())'
667 $ log 'only(p1(), p2())'
669 $ log 'only(p1(), p2())'
668 0
670 0
669 1
671 1
670 2
672 2
671 4
673 4
672 8
674 8
673 9
675 9
674
676
675 Test '%' operator
677 Test '%' operator
676
678
677 $ log '9%'
679 $ log '9%'
678 8
680 8
679 9
681 9
680 $ log '9%5'
682 $ log '9%5'
681 2
683 2
682 4
684 4
683 8
685 8
684 9
686 9
685 $ log '(7 + 9)%(5 + 2)'
687 $ log '(7 + 9)%(5 + 2)'
686 4
688 4
687 6
689 6
688 7
690 7
689 8
691 8
690 9
692 9
691
693
692 Test opreand of '%' is optimized recursively (issue4670)
694 Test opreand of '%' is optimized recursively (issue4670)
693
695
694 $ try --optimize '8:9-8%'
696 $ try --optimize '8:9-8%'
695 (onlypost
697 (onlypost
696 (minus
698 (minus
697 (range
699 (range
698 ('symbol', '8')
700 ('symbol', '8')
699 ('symbol', '9'))
701 ('symbol', '9'))
700 ('symbol', '8')))
702 ('symbol', '8')))
701 * optimized:
703 * optimized:
702 (func
704 (func
703 ('symbol', 'only')
705 ('symbol', 'only')
704 (difference
706 (difference
705 (range
707 (range
706 ('symbol', '8')
708 ('symbol', '8')
707 ('symbol', '9'))
709 ('symbol', '9'))
708 ('symbol', '8')))
710 ('symbol', '8')))
709 * set:
711 * set:
710 <baseset+ [8, 9]>
712 <baseset+ [8, 9]>
711 8
713 8
712 9
714 9
713 $ try --optimize '(9)%(5)'
715 $ try --optimize '(9)%(5)'
714 (only
716 (only
715 (group
717 (group
716 ('symbol', '9'))
718 ('symbol', '9'))
717 (group
719 (group
718 ('symbol', '5')))
720 ('symbol', '5')))
719 * optimized:
721 * optimized:
720 (func
722 (func
721 ('symbol', 'only')
723 ('symbol', 'only')
722 (list
724 (list
723 ('symbol', '9')
725 ('symbol', '9')
724 ('symbol', '5')))
726 ('symbol', '5')))
725 * set:
727 * set:
726 <baseset+ [8, 9, 2, 4]>
728 <baseset+ [8, 9, 2, 4]>
727 2
729 2
728 4
730 4
729 8
731 8
730 9
732 9
731
733
732 Test the order of operations
734 Test the order of operations
733
735
734 $ log '7 + 9%5 + 2'
736 $ log '7 + 9%5 + 2'
735 7
737 7
736 2
738 2
737 4
739 4
738 8
740 8
739 9
741 9
740
742
741 Test explicit numeric revision
743 Test explicit numeric revision
742 $ log 'rev(-2)'
744 $ log 'rev(-2)'
743 $ log 'rev(-1)'
745 $ log 'rev(-1)'
744 -1
746 -1
745 $ log 'rev(0)'
747 $ log 'rev(0)'
746 0
748 0
747 $ log 'rev(9)'
749 $ log 'rev(9)'
748 9
750 9
749 $ log 'rev(10)'
751 $ log 'rev(10)'
750 $ log 'rev(tip)'
752 $ log 'rev(tip)'
751 hg: parse error: rev expects a number
753 hg: parse error: rev expects a number
752 [255]
754 [255]
753
755
754 Test hexadecimal revision
756 Test hexadecimal revision
755 $ log 'id(2)'
757 $ log 'id(2)'
756 abort: 00changelog.i@2: ambiguous identifier!
758 abort: 00changelog.i@2: ambiguous identifier!
757 [255]
759 [255]
758 $ log 'id(23268)'
760 $ log 'id(23268)'
759 4
761 4
760 $ log 'id(2785f51eece)'
762 $ log 'id(2785f51eece)'
761 0
763 0
762 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
764 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
763 8
765 8
764 $ log 'id(d5d0dcbdc4a)'
766 $ log 'id(d5d0dcbdc4a)'
765 $ log 'id(d5d0dcbdc4w)'
767 $ log 'id(d5d0dcbdc4w)'
766 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
768 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
767 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
769 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
768 $ log 'id(1.0)'
770 $ log 'id(1.0)'
769 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
771 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
770
772
771 Test null revision
773 Test null revision
772 $ log '(null)'
774 $ log '(null)'
773 -1
775 -1
774 $ log '(null:0)'
776 $ log '(null:0)'
775 -1
777 -1
776 0
778 0
777 $ log '(0:null)'
779 $ log '(0:null)'
778 0
780 0
779 -1
781 -1
780 $ log 'null::0'
782 $ log 'null::0'
781 -1
783 -1
782 0
784 0
783 $ log 'null:tip - 0:'
785 $ log 'null:tip - 0:'
784 -1
786 -1
785 $ log 'null: and null::' | head -1
787 $ log 'null: and null::' | head -1
786 -1
788 -1
787 $ log 'null: or 0:' | head -2
789 $ log 'null: or 0:' | head -2
788 -1
790 -1
789 0
791 0
790 $ log 'ancestors(null)'
792 $ log 'ancestors(null)'
791 -1
793 -1
792 $ log 'reverse(null:)' | tail -2
794 $ log 'reverse(null:)' | tail -2
793 0
795 0
794 -1
796 -1
795 BROKEN: should be '-1'
797 BROKEN: should be '-1'
796 $ log 'first(null:)'
798 $ log 'first(null:)'
797 BROKEN: should be '-1'
799 BROKEN: should be '-1'
798 $ log 'min(null:)'
800 $ log 'min(null:)'
799 $ log 'tip:null and all()' | tail -2
801 $ log 'tip:null and all()' | tail -2
800 1
802 1
801 0
803 0
802
804
803 Test working-directory revision
805 Test working-directory revision
804 $ hg debugrevspec 'wdir()'
806 $ hg debugrevspec 'wdir()'
805 2147483647
807 2147483647
806 $ hg debugrevspec 'tip or wdir()'
808 $ hg debugrevspec 'tip or wdir()'
807 9
809 9
808 2147483647
810 2147483647
809 $ hg debugrevspec '0:tip and wdir()'
811 $ hg debugrevspec '0:tip and wdir()'
810 $ log '0:wdir()' | tail -3
812 $ log '0:wdir()' | tail -3
811 8
813 8
812 9
814 9
813 2147483647
815 2147483647
814 $ log 'wdir():0' | head -3
816 $ log 'wdir():0' | head -3
815 2147483647
817 2147483647
816 9
818 9
817 8
819 8
818 $ log 'wdir():wdir()'
820 $ log 'wdir():wdir()'
819 2147483647
821 2147483647
820 $ log '(all() + wdir()) & min(. + wdir())'
822 $ log '(all() + wdir()) & min(. + wdir())'
821 9
823 9
822 $ log '(all() + wdir()) & max(. + wdir())'
824 $ log '(all() + wdir()) & max(. + wdir())'
823 2147483647
825 2147483647
824 $ log '(all() + wdir()) & first(wdir() + .)'
826 $ log '(all() + wdir()) & first(wdir() + .)'
825 2147483647
827 2147483647
826 $ log '(all() + wdir()) & last(. + wdir())'
828 $ log '(all() + wdir()) & last(. + wdir())'
827 2147483647
829 2147483647
828
830
829 $ log 'outgoing()'
831 $ log 'outgoing()'
830 8
832 8
831 9
833 9
832 $ log 'outgoing("../remote1")'
834 $ log 'outgoing("../remote1")'
833 8
835 8
834 9
836 9
835 $ log 'outgoing("../remote2")'
837 $ log 'outgoing("../remote2")'
836 3
838 3
837 5
839 5
838 6
840 6
839 7
841 7
840 9
842 9
841 $ log 'p1(merge())'
843 $ log 'p1(merge())'
842 5
844 5
843 $ log 'p2(merge())'
845 $ log 'p2(merge())'
844 4
846 4
845 $ log 'parents(merge())'
847 $ log 'parents(merge())'
846 4
848 4
847 5
849 5
848 $ log 'p1(branchpoint())'
850 $ log 'p1(branchpoint())'
849 0
851 0
850 2
852 2
851 $ log 'p2(branchpoint())'
853 $ log 'p2(branchpoint())'
852 $ log 'parents(branchpoint())'
854 $ log 'parents(branchpoint())'
853 0
855 0
854 2
856 2
855 $ log 'removes(a)'
857 $ log 'removes(a)'
856 2
858 2
857 6
859 6
858 $ log 'roots(all())'
860 $ log 'roots(all())'
859 0
861 0
860 $ log 'reverse(2 or 3 or 4 or 5)'
862 $ log 'reverse(2 or 3 or 4 or 5)'
861 5
863 5
862 4
864 4
863 3
865 3
864 2
866 2
865 $ log 'reverse(all())'
867 $ log 'reverse(all())'
866 9
868 9
867 8
869 8
868 7
870 7
869 6
871 6
870 5
872 5
871 4
873 4
872 3
874 3
873 2
875 2
874 1
876 1
875 0
877 0
876 $ log 'reverse(all()) & filelog(b)'
878 $ log 'reverse(all()) & filelog(b)'
877 4
879 4
878 1
880 1
879 $ log 'rev(5)'
881 $ log 'rev(5)'
880 5
882 5
881 $ log 'sort(limit(reverse(all()), 3))'
883 $ log 'sort(limit(reverse(all()), 3))'
882 7
884 7
883 8
885 8
884 9
886 9
885 $ log 'sort(2 or 3 or 4 or 5, date)'
887 $ log 'sort(2 or 3 or 4 or 5, date)'
886 2
888 2
887 3
889 3
888 5
890 5
889 4
891 4
890 $ log 'tagged()'
892 $ log 'tagged()'
891 6
893 6
892 $ log 'tag()'
894 $ log 'tag()'
893 6
895 6
894 $ log 'tag(1.0)'
896 $ log 'tag(1.0)'
895 6
897 6
896 $ log 'tag(tip)'
898 $ log 'tag(tip)'
897 9
899 9
898
900
899 test sort revset
901 test sort revset
900 --------------------------------------------
902 --------------------------------------------
901
903
902 test when adding two unordered revsets
904 test when adding two unordered revsets
903
905
904 $ log 'sort(keyword(issue) or modifies(b))'
906 $ log 'sort(keyword(issue) or modifies(b))'
905 4
907 4
906 6
908 6
907
909
908 test when sorting a reversed collection in the same way it is
910 test when sorting a reversed collection in the same way it is
909
911
910 $ log 'sort(reverse(all()), -rev)'
912 $ log 'sort(reverse(all()), -rev)'
911 9
913 9
912 8
914 8
913 7
915 7
914 6
916 6
915 5
917 5
916 4
918 4
917 3
919 3
918 2
920 2
919 1
921 1
920 0
922 0
921
923
922 test when sorting a reversed collection
924 test when sorting a reversed collection
923
925
924 $ log 'sort(reverse(all()), rev)'
926 $ log 'sort(reverse(all()), rev)'
925 0
927 0
926 1
928 1
927 2
929 2
928 3
930 3
929 4
931 4
930 5
932 5
931 6
933 6
932 7
934 7
933 8
935 8
934 9
936 9
935
937
936
938
937 test sorting two sorted collections in different orders
939 test sorting two sorted collections in different orders
938
940
939 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
941 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
940 2
942 2
941 6
943 6
942 8
944 8
943 9
945 9
944
946
945 test sorting two sorted collections in different orders backwards
947 test sorting two sorted collections in different orders backwards
946
948
947 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
949 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
948 9
950 9
949 8
951 8
950 6
952 6
951 2
953 2
952
954
953 test subtracting something from an addset
955 test subtracting something from an addset
954
956
955 $ log '(outgoing() or removes(a)) - removes(a)'
957 $ log '(outgoing() or removes(a)) - removes(a)'
956 8
958 8
957 9
959 9
958
960
959 test intersecting something with an addset
961 test intersecting something with an addset
960
962
961 $ log 'parents(outgoing() or removes(a))'
963 $ log 'parents(outgoing() or removes(a))'
962 1
964 1
963 4
965 4
964 5
966 5
965 8
967 8
966
968
967 test that `or` operation combines elements in the right order:
969 test that `or` operation combines elements in the right order:
968
970
969 $ log '3:4 or 2:5'
971 $ log '3:4 or 2:5'
970 3
972 3
971 4
973 4
972 2
974 2
973 5
975 5
974 $ log '3:4 or 5:2'
976 $ log '3:4 or 5:2'
975 3
977 3
976 4
978 4
977 5
979 5
978 2
980 2
979 $ log 'sort(3:4 or 2:5)'
981 $ log 'sort(3:4 or 2:5)'
980 2
982 2
981 3
983 3
982 4
984 4
983 5
985 5
984 $ log 'sort(3:4 or 5:2)'
986 $ log 'sort(3:4 or 5:2)'
985 2
987 2
986 3
988 3
987 4
989 4
988 5
990 5
989
991
990 test that more than one `-r`s are combined in the right order and deduplicated:
992 test that more than one `-r`s are combined in the right order and deduplicated:
991
993
992 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
994 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
993 3
995 3
994 4
996 4
995 5
997 5
996 2
998 2
997 0
999 0
998 1
1000 1
999
1001
1000 test that `or` operation skips duplicated revisions from right-hand side
1002 test that `or` operation skips duplicated revisions from right-hand side
1001
1003
1002 $ try 'reverse(1::5) or ancestors(4)'
1004 $ try 'reverse(1::5) or ancestors(4)'
1003 (or
1005 (or
1004 (func
1006 (func
1005 ('symbol', 'reverse')
1007 ('symbol', 'reverse')
1006 (dagrange
1008 (dagrange
1007 ('symbol', '1')
1009 ('symbol', '1')
1008 ('symbol', '5')))
1010 ('symbol', '5')))
1009 (func
1011 (func
1010 ('symbol', 'ancestors')
1012 ('symbol', 'ancestors')
1011 ('symbol', '4')))
1013 ('symbol', '4')))
1012 * set:
1014 * set:
1013 <addset
1015 <addset
1014 <baseset- [1, 3, 5]>,
1016 <baseset- [1, 3, 5]>,
1015 <generatorset+>>
1017 <generatorset+>>
1016 5
1018 5
1017 3
1019 3
1018 1
1020 1
1019 0
1021 0
1020 2
1022 2
1021 4
1023 4
1022 $ try 'sort(ancestors(4) or reverse(1::5))'
1024 $ try 'sort(ancestors(4) or reverse(1::5))'
1023 (func
1025 (func
1024 ('symbol', 'sort')
1026 ('symbol', 'sort')
1025 (or
1027 (or
1026 (func
1028 (func
1027 ('symbol', 'ancestors')
1029 ('symbol', 'ancestors')
1028 ('symbol', '4'))
1030 ('symbol', '4'))
1029 (func
1031 (func
1030 ('symbol', 'reverse')
1032 ('symbol', 'reverse')
1031 (dagrange
1033 (dagrange
1032 ('symbol', '1')
1034 ('symbol', '1')
1033 ('symbol', '5')))))
1035 ('symbol', '5')))))
1034 * set:
1036 * set:
1035 <addset+
1037 <addset+
1036 <generatorset+>,
1038 <generatorset+>,
1037 <baseset- [1, 3, 5]>>
1039 <baseset- [1, 3, 5]>>
1038 0
1040 0
1039 1
1041 1
1040 2
1042 2
1041 3
1043 3
1042 4
1044 4
1043 5
1045 5
1044
1046
1045 test optimization of trivial `or` operation
1047 test optimization of trivial `or` operation
1046
1048
1047 $ try --optimize '0|(1)|"2"|-2|tip|null'
1049 $ try --optimize '0|(1)|"2"|-2|tip|null'
1048 (or
1050 (or
1049 ('symbol', '0')
1051 ('symbol', '0')
1050 (group
1052 (group
1051 ('symbol', '1'))
1053 ('symbol', '1'))
1052 ('string', '2')
1054 ('string', '2')
1053 (negate
1055 (negate
1054 ('symbol', '2'))
1056 ('symbol', '2'))
1055 ('symbol', 'tip')
1057 ('symbol', 'tip')
1056 ('symbol', 'null'))
1058 ('symbol', 'null'))
1057 * optimized:
1059 * optimized:
1058 (func
1060 (func
1059 ('symbol', '_list')
1061 ('symbol', '_list')
1060 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
1062 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
1061 * set:
1063 * set:
1062 <baseset [0, 1, 2, 8, 9, -1]>
1064 <baseset [0, 1, 2, 8, 9, -1]>
1063 0
1065 0
1064 1
1066 1
1065 2
1067 2
1066 8
1068 8
1067 9
1069 9
1068 -1
1070 -1
1069
1071
1070 $ try --optimize '0|1|2:3'
1072 $ try --optimize '0|1|2:3'
1071 (or
1073 (or
1072 ('symbol', '0')
1074 ('symbol', '0')
1073 ('symbol', '1')
1075 ('symbol', '1')
1074 (range
1076 (range
1075 ('symbol', '2')
1077 ('symbol', '2')
1076 ('symbol', '3')))
1078 ('symbol', '3')))
1077 * optimized:
1079 * optimized:
1078 (or
1080 (or
1079 (func
1081 (func
1080 ('symbol', '_list')
1082 ('symbol', '_list')
1081 ('string', '0\x001'))
1083 ('string', '0\x001'))
1082 (range
1084 (range
1083 ('symbol', '2')
1085 ('symbol', '2')
1084 ('symbol', '3')))
1086 ('symbol', '3')))
1085 * set:
1087 * set:
1086 <addset
1088 <addset
1087 <baseset [0, 1]>,
1089 <baseset [0, 1]>,
1088 <spanset+ 2:3>>
1090 <spanset+ 2:3>>
1089 0
1091 0
1090 1
1092 1
1091 2
1093 2
1092 3
1094 3
1093
1095
1094 $ try --optimize '0:1|2|3:4|5|6'
1096 $ try --optimize '0:1|2|3:4|5|6'
1095 (or
1097 (or
1096 (range
1098 (range
1097 ('symbol', '0')
1099 ('symbol', '0')
1098 ('symbol', '1'))
1100 ('symbol', '1'))
1099 ('symbol', '2')
1101 ('symbol', '2')
1100 (range
1102 (range
1101 ('symbol', '3')
1103 ('symbol', '3')
1102 ('symbol', '4'))
1104 ('symbol', '4'))
1103 ('symbol', '5')
1105 ('symbol', '5')
1104 ('symbol', '6'))
1106 ('symbol', '6'))
1105 * optimized:
1107 * optimized:
1106 (or
1108 (or
1107 (range
1109 (range
1108 ('symbol', '0')
1110 ('symbol', '0')
1109 ('symbol', '1'))
1111 ('symbol', '1'))
1110 ('symbol', '2')
1112 ('symbol', '2')
1111 (range
1113 (range
1112 ('symbol', '3')
1114 ('symbol', '3')
1113 ('symbol', '4'))
1115 ('symbol', '4'))
1114 (func
1116 (func
1115 ('symbol', '_list')
1117 ('symbol', '_list')
1116 ('string', '5\x006')))
1118 ('string', '5\x006')))
1117 * set:
1119 * set:
1118 <addset
1120 <addset
1119 <addset
1121 <addset
1120 <spanset+ 0:1>,
1122 <spanset+ 0:1>,
1121 <baseset [2]>>,
1123 <baseset [2]>>,
1122 <addset
1124 <addset
1123 <spanset+ 3:4>,
1125 <spanset+ 3:4>,
1124 <baseset [5, 6]>>>
1126 <baseset [5, 6]>>>
1125 0
1127 0
1126 1
1128 1
1127 2
1129 2
1128 3
1130 3
1129 4
1131 4
1130 5
1132 5
1131 6
1133 6
1132
1134
1133 test that `_list` should be narrowed by provided `subset`
1135 test that `_list` should be narrowed by provided `subset`
1134
1136
1135 $ log '0:2 and (null|1|2|3)'
1137 $ log '0:2 and (null|1|2|3)'
1136 1
1138 1
1137 2
1139 2
1138
1140
1139 test that `_list` should remove duplicates
1141 test that `_list` should remove duplicates
1140
1142
1141 $ log '0|1|2|1|2|-1|tip'
1143 $ log '0|1|2|1|2|-1|tip'
1142 0
1144 0
1143 1
1145 1
1144 2
1146 2
1145 9
1147 9
1146
1148
1147 test unknown revision in `_list`
1149 test unknown revision in `_list`
1148
1150
1149 $ log '0|unknown'
1151 $ log '0|unknown'
1150 abort: unknown revision 'unknown'!
1152 abort: unknown revision 'unknown'!
1151 [255]
1153 [255]
1152
1154
1153 test integer range in `_list`
1155 test integer range in `_list`
1154
1156
1155 $ log '-1|-10'
1157 $ log '-1|-10'
1156 9
1158 9
1157 0
1159 0
1158
1160
1159 $ log '-10|-11'
1161 $ log '-10|-11'
1160 abort: unknown revision '-11'!
1162 abort: unknown revision '-11'!
1161 [255]
1163 [255]
1162
1164
1163 $ log '9|10'
1165 $ log '9|10'
1164 abort: unknown revision '10'!
1166 abort: unknown revision '10'!
1165 [255]
1167 [255]
1166
1168
1167 test '0000' != '0' in `_list`
1169 test '0000' != '0' in `_list`
1168
1170
1169 $ log '0|0000'
1171 $ log '0|0000'
1170 0
1172 0
1171 -1
1173 -1
1172
1174
1173 test ',' in `_list`
1175 test ',' in `_list`
1174 $ log '0,1'
1176 $ log '0,1'
1175 hg: parse error: can't use a list in this context
1177 hg: parse error: can't use a list in this context
1176 (see hg help "revsets.x or y")
1178 (see hg help "revsets.x or y")
1177 [255]
1179 [255]
1178 $ try '0,1,2'
1180 $ try '0,1,2'
1179 (list
1181 (list
1180 ('symbol', '0')
1182 ('symbol', '0')
1181 ('symbol', '1')
1183 ('symbol', '1')
1182 ('symbol', '2'))
1184 ('symbol', '2'))
1183 hg: parse error: can't use a list in this context
1185 hg: parse error: can't use a list in this context
1184 (see hg help "revsets.x or y")
1186 (see hg help "revsets.x or y")
1185 [255]
1187 [255]
1186
1188
1187 test that chained `or` operations make balanced addsets
1189 test that chained `or` operations make balanced addsets
1188
1190
1189 $ try '0:1|1:2|2:3|3:4|4:5'
1191 $ try '0:1|1:2|2:3|3:4|4:5'
1190 (or
1192 (or
1191 (range
1193 (range
1192 ('symbol', '0')
1194 ('symbol', '0')
1193 ('symbol', '1'))
1195 ('symbol', '1'))
1194 (range
1196 (range
1195 ('symbol', '1')
1197 ('symbol', '1')
1196 ('symbol', '2'))
1198 ('symbol', '2'))
1197 (range
1199 (range
1198 ('symbol', '2')
1200 ('symbol', '2')
1199 ('symbol', '3'))
1201 ('symbol', '3'))
1200 (range
1202 (range
1201 ('symbol', '3')
1203 ('symbol', '3')
1202 ('symbol', '4'))
1204 ('symbol', '4'))
1203 (range
1205 (range
1204 ('symbol', '4')
1206 ('symbol', '4')
1205 ('symbol', '5')))
1207 ('symbol', '5')))
1206 * set:
1208 * set:
1207 <addset
1209 <addset
1208 <addset
1210 <addset
1209 <spanset+ 0:1>,
1211 <spanset+ 0:1>,
1210 <spanset+ 1:2>>,
1212 <spanset+ 1:2>>,
1211 <addset
1213 <addset
1212 <spanset+ 2:3>,
1214 <spanset+ 2:3>,
1213 <addset
1215 <addset
1214 <spanset+ 3:4>,
1216 <spanset+ 3:4>,
1215 <spanset+ 4:5>>>>
1217 <spanset+ 4:5>>>>
1216 0
1218 0
1217 1
1219 1
1218 2
1220 2
1219 3
1221 3
1220 4
1222 4
1221 5
1223 5
1222
1224
1223 no crash by empty group "()" while optimizing `or` operations
1225 no crash by empty group "()" while optimizing `or` operations
1224
1226
1225 $ try --optimize '0|()'
1227 $ try --optimize '0|()'
1226 (or
1228 (or
1227 ('symbol', '0')
1229 ('symbol', '0')
1228 (group
1230 (group
1229 None))
1231 None))
1230 * optimized:
1232 * optimized:
1231 (or
1233 (or
1232 ('symbol', '0')
1234 ('symbol', '0')
1233 None)
1235 None)
1234 hg: parse error: missing argument
1236 hg: parse error: missing argument
1235 [255]
1237 [255]
1236
1238
1237 test that chained `or` operations never eat up stack (issue4624)
1239 test that chained `or` operations never eat up stack (issue4624)
1238 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1240 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1239
1241
1240 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
1242 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
1241 0
1243 0
1242 1
1244 1
1243
1245
1244 test that repeated `-r` options never eat up stack (issue4565)
1246 test that repeated `-r` options never eat up stack (issue4565)
1245 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1247 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1246
1248
1247 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1249 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1248 0
1250 0
1249 1
1251 1
1250
1252
1251 check that conversion to only works
1253 check that conversion to only works
1252 $ try --optimize '::3 - ::1'
1254 $ try --optimize '::3 - ::1'
1253 (minus
1255 (minus
1254 (dagrangepre
1256 (dagrangepre
1255 ('symbol', '3'))
1257 ('symbol', '3'))
1256 (dagrangepre
1258 (dagrangepre
1257 ('symbol', '1')))
1259 ('symbol', '1')))
1258 * optimized:
1260 * optimized:
1259 (func
1261 (func
1260 ('symbol', 'only')
1262 ('symbol', 'only')
1261 (list
1263 (list
1262 ('symbol', '3')
1264 ('symbol', '3')
1263 ('symbol', '1')))
1265 ('symbol', '1')))
1264 * set:
1266 * set:
1265 <baseset+ [3]>
1267 <baseset+ [3]>
1266 3
1268 3
1267 $ try --optimize 'ancestors(1) - ancestors(3)'
1269 $ try --optimize 'ancestors(1) - ancestors(3)'
1268 (minus
1270 (minus
1269 (func
1271 (func
1270 ('symbol', 'ancestors')
1272 ('symbol', 'ancestors')
1271 ('symbol', '1'))
1273 ('symbol', '1'))
1272 (func
1274 (func
1273 ('symbol', 'ancestors')
1275 ('symbol', 'ancestors')
1274 ('symbol', '3')))
1276 ('symbol', '3')))
1275 * optimized:
1277 * optimized:
1276 (func
1278 (func
1277 ('symbol', 'only')
1279 ('symbol', 'only')
1278 (list
1280 (list
1279 ('symbol', '1')
1281 ('symbol', '1')
1280 ('symbol', '3')))
1282 ('symbol', '3')))
1281 * set:
1283 * set:
1282 <baseset+ []>
1284 <baseset+ []>
1283 $ try --optimize 'not ::2 and ::6'
1285 $ try --optimize 'not ::2 and ::6'
1284 (and
1286 (and
1285 (not
1287 (not
1286 (dagrangepre
1288 (dagrangepre
1287 ('symbol', '2')))
1289 ('symbol', '2')))
1288 (dagrangepre
1290 (dagrangepre
1289 ('symbol', '6')))
1291 ('symbol', '6')))
1290 * optimized:
1292 * optimized:
1291 (func
1293 (func
1292 ('symbol', 'only')
1294 ('symbol', 'only')
1293 (list
1295 (list
1294 ('symbol', '6')
1296 ('symbol', '6')
1295 ('symbol', '2')))
1297 ('symbol', '2')))
1296 * set:
1298 * set:
1297 <baseset+ [3, 4, 5, 6]>
1299 <baseset+ [3, 4, 5, 6]>
1298 3
1300 3
1299 4
1301 4
1300 5
1302 5
1301 6
1303 6
1302 $ try --optimize 'ancestors(6) and not ancestors(4)'
1304 $ try --optimize 'ancestors(6) and not ancestors(4)'
1303 (and
1305 (and
1304 (func
1306 (func
1305 ('symbol', 'ancestors')
1307 ('symbol', 'ancestors')
1306 ('symbol', '6'))
1308 ('symbol', '6'))
1307 (not
1309 (not
1308 (func
1310 (func
1309 ('symbol', 'ancestors')
1311 ('symbol', 'ancestors')
1310 ('symbol', '4'))))
1312 ('symbol', '4'))))
1311 * optimized:
1313 * optimized:
1312 (func
1314 (func
1313 ('symbol', 'only')
1315 ('symbol', 'only')
1314 (list
1316 (list
1315 ('symbol', '6')
1317 ('symbol', '6')
1316 ('symbol', '4')))
1318 ('symbol', '4')))
1317 * set:
1319 * set:
1318 <baseset+ [3, 5, 6]>
1320 <baseset+ [3, 5, 6]>
1319 3
1321 3
1320 5
1322 5
1321 6
1323 6
1322
1324
1323 no crash by empty group "()" while optimizing to "only()"
1325 no crash by empty group "()" while optimizing to "only()"
1324
1326
1325 $ try --optimize '::1 and ()'
1327 $ try --optimize '::1 and ()'
1326 (and
1328 (and
1327 (dagrangepre
1329 (dagrangepre
1328 ('symbol', '1'))
1330 ('symbol', '1'))
1329 (group
1331 (group
1330 None))
1332 None))
1331 * optimized:
1333 * optimized:
1332 (and
1334 (and
1333 None
1335 None
1334 (func
1336 (func
1335 ('symbol', 'ancestors')
1337 ('symbol', 'ancestors')
1336 ('symbol', '1')))
1338 ('symbol', '1')))
1337 hg: parse error: missing argument
1339 hg: parse error: missing argument
1338 [255]
1340 [255]
1339
1341
1340 we can use patterns when searching for tags
1342 we can use patterns when searching for tags
1341
1343
1342 $ log 'tag("1..*")'
1344 $ log 'tag("1..*")'
1343 abort: tag '1..*' does not exist!
1345 abort: tag '1..*' does not exist!
1344 [255]
1346 [255]
1345 $ log 'tag("re:1..*")'
1347 $ log 'tag("re:1..*")'
1346 6
1348 6
1347 $ log 'tag("re:[0-9].[0-9]")'
1349 $ log 'tag("re:[0-9].[0-9]")'
1348 6
1350 6
1349 $ log 'tag("literal:1.0")'
1351 $ log 'tag("literal:1.0")'
1350 6
1352 6
1351 $ log 'tag("re:0..*")'
1353 $ log 'tag("re:0..*")'
1352
1354
1353 $ log 'tag(unknown)'
1355 $ log 'tag(unknown)'
1354 abort: tag 'unknown' does not exist!
1356 abort: tag 'unknown' does not exist!
1355 [255]
1357 [255]
1356 $ log 'tag("re:unknown")'
1358 $ log 'tag("re:unknown")'
1357 $ log 'present(tag("unknown"))'
1359 $ log 'present(tag("unknown"))'
1358 $ log 'present(tag("re:unknown"))'
1360 $ log 'present(tag("re:unknown"))'
1359 $ log 'branch(unknown)'
1361 $ log 'branch(unknown)'
1360 abort: unknown revision 'unknown'!
1362 abort: unknown revision 'unknown'!
1361 [255]
1363 [255]
1362 $ log 'branch("literal:unknown")'
1364 $ log 'branch("literal:unknown")'
1363 abort: branch 'unknown' does not exist!
1365 abort: branch 'unknown' does not exist!
1364 [255]
1366 [255]
1365 $ log 'branch("re:unknown")'
1367 $ log 'branch("re:unknown")'
1366 $ log 'present(branch("unknown"))'
1368 $ log 'present(branch("unknown"))'
1367 $ log 'present(branch("re:unknown"))'
1369 $ log 'present(branch("re:unknown"))'
1368 $ log 'user(bob)'
1370 $ log 'user(bob)'
1369 2
1371 2
1370
1372
1371 $ log '4::8'
1373 $ log '4::8'
1372 4
1374 4
1373 8
1375 8
1374 $ log '4:8'
1376 $ log '4:8'
1375 4
1377 4
1376 5
1378 5
1377 6
1379 6
1378 7
1380 7
1379 8
1381 8
1380
1382
1381 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
1383 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
1382 4
1384 4
1383 2
1385 2
1384 5
1386 5
1385
1387
1386 $ log 'not 0 and 0:2'
1388 $ log 'not 0 and 0:2'
1387 1
1389 1
1388 2
1390 2
1389 $ log 'not 1 and 0:2'
1391 $ log 'not 1 and 0:2'
1390 0
1392 0
1391 2
1393 2
1392 $ log 'not 2 and 0:2'
1394 $ log 'not 2 and 0:2'
1393 0
1395 0
1394 1
1396 1
1395 $ log '(1 and 2)::'
1397 $ log '(1 and 2)::'
1396 $ log '(1 and 2):'
1398 $ log '(1 and 2):'
1397 $ log '(1 and 2):3'
1399 $ log '(1 and 2):3'
1398 $ log 'sort(head(), -rev)'
1400 $ log 'sort(head(), -rev)'
1399 9
1401 9
1400 7
1402 7
1401 6
1403 6
1402 5
1404 5
1403 4
1405 4
1404 3
1406 3
1405 2
1407 2
1406 1
1408 1
1407 0
1409 0
1408 $ log '4::8 - 8'
1410 $ log '4::8 - 8'
1409 4
1411 4
1410 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
1412 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
1411 2
1413 2
1412 3
1414 3
1413 1
1415 1
1414
1416
1415 $ log 'named("unknown")'
1417 $ log 'named("unknown")'
1416 abort: namespace 'unknown' does not exist!
1418 abort: namespace 'unknown' does not exist!
1417 [255]
1419 [255]
1418 $ log 'named("re:unknown")'
1420 $ log 'named("re:unknown")'
1419 abort: no namespace exists that match 'unknown'!
1421 abort: no namespace exists that match 'unknown'!
1420 [255]
1422 [255]
1421 $ log 'present(named("unknown"))'
1423 $ log 'present(named("unknown"))'
1422 $ log 'present(named("re:unknown"))'
1424 $ log 'present(named("re:unknown"))'
1423
1425
1424 $ log 'tag()'
1426 $ log 'tag()'
1425 6
1427 6
1426 $ log 'named("tags")'
1428 $ log 'named("tags")'
1427 6
1429 6
1428
1430
1429 issue2437
1431 issue2437
1430
1432
1431 $ log '3 and p1(5)'
1433 $ log '3 and p1(5)'
1432 3
1434 3
1433 $ log '4 and p2(6)'
1435 $ log '4 and p2(6)'
1434 4
1436 4
1435 $ log '1 and parents(:2)'
1437 $ log '1 and parents(:2)'
1436 1
1438 1
1437 $ log '2 and children(1:)'
1439 $ log '2 and children(1:)'
1438 2
1440 2
1439 $ log 'roots(all()) or roots(all())'
1441 $ log 'roots(all()) or roots(all())'
1440 0
1442 0
1441 $ hg debugrevspec 'roots(all()) or roots(all())'
1443 $ hg debugrevspec 'roots(all()) or roots(all())'
1442 0
1444 0
1443 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
1445 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
1444 9
1446 9
1445 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
1447 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
1446 4
1448 4
1447
1449
1448 issue2654: report a parse error if the revset was not completely parsed
1450 issue2654: report a parse error if the revset was not completely parsed
1449
1451
1450 $ log '1 OR 2'
1452 $ log '1 OR 2'
1451 hg: parse error at 2: invalid token
1453 hg: parse error at 2: invalid token
1452 [255]
1454 [255]
1453
1455
1454 or operator should preserve ordering:
1456 or operator should preserve ordering:
1455 $ log 'reverse(2::4) or tip'
1457 $ log 'reverse(2::4) or tip'
1456 4
1458 4
1457 2
1459 2
1458 9
1460 9
1459
1461
1460 parentrevspec
1462 parentrevspec
1461
1463
1462 $ log 'merge()^0'
1464 $ log 'merge()^0'
1463 6
1465 6
1464 $ log 'merge()^'
1466 $ log 'merge()^'
1465 5
1467 5
1466 $ log 'merge()^1'
1468 $ log 'merge()^1'
1467 5
1469 5
1468 $ log 'merge()^2'
1470 $ log 'merge()^2'
1469 4
1471 4
1470 $ log 'merge()^^'
1472 $ log 'merge()^^'
1471 3
1473 3
1472 $ log 'merge()^1^'
1474 $ log 'merge()^1^'
1473 3
1475 3
1474 $ log 'merge()^^^'
1476 $ log 'merge()^^^'
1475 1
1477 1
1476
1478
1477 $ log 'merge()~0'
1479 $ log 'merge()~0'
1478 6
1480 6
1479 $ log 'merge()~1'
1481 $ log 'merge()~1'
1480 5
1482 5
1481 $ log 'merge()~2'
1483 $ log 'merge()~2'
1482 3
1484 3
1483 $ log 'merge()~2^1'
1485 $ log 'merge()~2^1'
1484 1
1486 1
1485 $ log 'merge()~3'
1487 $ log 'merge()~3'
1486 1
1488 1
1487
1489
1488 $ log '(-3:tip)^'
1490 $ log '(-3:tip)^'
1489 4
1491 4
1490 6
1492 6
1491 8
1493 8
1492
1494
1493 $ log 'tip^foo'
1495 $ log 'tip^foo'
1494 hg: parse error: ^ expects a number 0, 1, or 2
1496 hg: parse error: ^ expects a number 0, 1, or 2
1495 [255]
1497 [255]
1496
1498
1497 Bogus function gets suggestions
1499 Bogus function gets suggestions
1498 $ log 'add()'
1500 $ log 'add()'
1499 hg: parse error: unknown identifier: add
1501 hg: parse error: unknown identifier: add
1500 (did you mean adds?)
1502 (did you mean adds?)
1501 [255]
1503 [255]
1502 $ log 'added()'
1504 $ log 'added()'
1503 hg: parse error: unknown identifier: added
1505 hg: parse error: unknown identifier: added
1504 (did you mean adds?)
1506 (did you mean adds?)
1505 [255]
1507 [255]
1506 $ log 'remo()'
1508 $ log 'remo()'
1507 hg: parse error: unknown identifier: remo
1509 hg: parse error: unknown identifier: remo
1508 (did you mean one of remote, removes?)
1510 (did you mean one of remote, removes?)
1509 [255]
1511 [255]
1510 $ log 'babar()'
1512 $ log 'babar()'
1511 hg: parse error: unknown identifier: babar
1513 hg: parse error: unknown identifier: babar
1512 [255]
1514 [255]
1513
1515
1514 Bogus function with a similar internal name doesn't suggest the internal name
1516 Bogus function with a similar internal name doesn't suggest the internal name
1515 $ log 'matches()'
1517 $ log 'matches()'
1516 hg: parse error: unknown identifier: matches
1518 hg: parse error: unknown identifier: matches
1517 (did you mean matching?)
1519 (did you mean matching?)
1518 [255]
1520 [255]
1519
1521
1520 Undocumented functions aren't suggested as similar either
1522 Undocumented functions aren't suggested as similar either
1521 $ log 'wdir2()'
1523 $ log 'wdir2()'
1522 hg: parse error: unknown identifier: wdir2
1524 hg: parse error: unknown identifier: wdir2
1523 [255]
1525 [255]
1524
1526
1525 multiple revspecs
1527 multiple revspecs
1526
1528
1527 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
1529 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
1528 8
1530 8
1529 9
1531 9
1530 4
1532 4
1531 5
1533 5
1532 6
1534 6
1533 7
1535 7
1534
1536
1535 test usage in revpair (with "+")
1537 test usage in revpair (with "+")
1536
1538
1537 (real pair)
1539 (real pair)
1538
1540
1539 $ hg diff -r 'tip^^' -r 'tip'
1541 $ hg diff -r 'tip^^' -r 'tip'
1540 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1542 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1541 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1543 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1542 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1544 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1543 @@ -0,0 +1,1 @@
1545 @@ -0,0 +1,1 @@
1544 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1546 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1545 $ hg diff -r 'tip^^::tip'
1547 $ hg diff -r 'tip^^::tip'
1546 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1548 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1547 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1549 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1548 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1550 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1549 @@ -0,0 +1,1 @@
1551 @@ -0,0 +1,1 @@
1550 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1552 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1551
1553
1552 (single rev)
1554 (single rev)
1553
1555
1554 $ hg diff -r 'tip^' -r 'tip^'
1556 $ hg diff -r 'tip^' -r 'tip^'
1555 $ hg diff -r 'tip^:tip^'
1557 $ hg diff -r 'tip^:tip^'
1556
1558
1557 (single rev that does not looks like a range)
1559 (single rev that does not looks like a range)
1558
1560
1559 $ hg diff -r 'tip^::tip^ or tip^'
1561 $ hg diff -r 'tip^::tip^ or tip^'
1560 diff -r d5d0dcbdc4d9 .hgtags
1562 diff -r d5d0dcbdc4d9 .hgtags
1561 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1563 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1562 +++ b/.hgtags * (glob)
1564 +++ b/.hgtags * (glob)
1563 @@ -0,0 +1,1 @@
1565 @@ -0,0 +1,1 @@
1564 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1566 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1565 $ hg diff -r 'tip^ or tip^'
1567 $ hg diff -r 'tip^ or tip^'
1566 diff -r d5d0dcbdc4d9 .hgtags
1568 diff -r d5d0dcbdc4d9 .hgtags
1567 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1569 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1568 +++ b/.hgtags * (glob)
1570 +++ b/.hgtags * (glob)
1569 @@ -0,0 +1,1 @@
1571 @@ -0,0 +1,1 @@
1570 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1572 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1571
1573
1572 (no rev)
1574 (no rev)
1573
1575
1574 $ hg diff -r 'author("babar") or author("celeste")'
1576 $ hg diff -r 'author("babar") or author("celeste")'
1575 abort: empty revision range
1577 abort: empty revision range
1576 [255]
1578 [255]
1577
1579
1578 aliases:
1580 aliases:
1579
1581
1580 $ echo '[revsetalias]' >> .hg/hgrc
1582 $ echo '[revsetalias]' >> .hg/hgrc
1581 $ echo 'm = merge()' >> .hg/hgrc
1583 $ echo 'm = merge()' >> .hg/hgrc
1582 (revset aliases can override builtin revsets)
1584 (revset aliases can override builtin revsets)
1583 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
1585 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
1584 $ echo 'sincem = descendants(m)' >> .hg/hgrc
1586 $ echo 'sincem = descendants(m)' >> .hg/hgrc
1585 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
1587 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
1586 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1588 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1587 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1589 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1588
1590
1589 $ try m
1591 $ try m
1590 ('symbol', 'm')
1592 ('symbol', 'm')
1591 (func
1593 (func
1592 ('symbol', 'merge')
1594 ('symbol', 'merge')
1593 None)
1595 None)
1594 * set:
1596 * set:
1595 <filteredset
1597 <filteredset
1596 <fullreposet+ 0:9>>
1598 <fullreposet+ 0:9>,
1599 <merge>>
1597 6
1600 6
1598
1601
1599 $ HGPLAIN=1
1602 $ HGPLAIN=1
1600 $ export HGPLAIN
1603 $ export HGPLAIN
1601 $ try m
1604 $ try m
1602 ('symbol', 'm')
1605 ('symbol', 'm')
1603 abort: unknown revision 'm'!
1606 abort: unknown revision 'm'!
1604 [255]
1607 [255]
1605
1608
1606 $ HGPLAINEXCEPT=revsetalias
1609 $ HGPLAINEXCEPT=revsetalias
1607 $ export HGPLAINEXCEPT
1610 $ export HGPLAINEXCEPT
1608 $ try m
1611 $ try m
1609 ('symbol', 'm')
1612 ('symbol', 'm')
1610 (func
1613 (func
1611 ('symbol', 'merge')
1614 ('symbol', 'merge')
1612 None)
1615 None)
1613 * set:
1616 * set:
1614 <filteredset
1617 <filteredset
1615 <fullreposet+ 0:9>>
1618 <fullreposet+ 0:9>,
1619 <merge>>
1616 6
1620 6
1617
1621
1618 $ unset HGPLAIN
1622 $ unset HGPLAIN
1619 $ unset HGPLAINEXCEPT
1623 $ unset HGPLAINEXCEPT
1620
1624
1621 $ try 'p2(.)'
1625 $ try 'p2(.)'
1622 (func
1626 (func
1623 ('symbol', 'p2')
1627 ('symbol', 'p2')
1624 ('symbol', '.'))
1628 ('symbol', '.'))
1625 (func
1629 (func
1626 ('symbol', 'p1')
1630 ('symbol', 'p1')
1627 ('symbol', '.'))
1631 ('symbol', '.'))
1628 * set:
1632 * set:
1629 <baseset+ [8]>
1633 <baseset+ [8]>
1630 8
1634 8
1631
1635
1632 $ HGPLAIN=1
1636 $ HGPLAIN=1
1633 $ export HGPLAIN
1637 $ export HGPLAIN
1634 $ try 'p2(.)'
1638 $ try 'p2(.)'
1635 (func
1639 (func
1636 ('symbol', 'p2')
1640 ('symbol', 'p2')
1637 ('symbol', '.'))
1641 ('symbol', '.'))
1638 * set:
1642 * set:
1639 <baseset+ []>
1643 <baseset+ []>
1640
1644
1641 $ HGPLAINEXCEPT=revsetalias
1645 $ HGPLAINEXCEPT=revsetalias
1642 $ export HGPLAINEXCEPT
1646 $ export HGPLAINEXCEPT
1643 $ try 'p2(.)'
1647 $ try 'p2(.)'
1644 (func
1648 (func
1645 ('symbol', 'p2')
1649 ('symbol', 'p2')
1646 ('symbol', '.'))
1650 ('symbol', '.'))
1647 (func
1651 (func
1648 ('symbol', 'p1')
1652 ('symbol', 'p1')
1649 ('symbol', '.'))
1653 ('symbol', '.'))
1650 * set:
1654 * set:
1651 <baseset+ [8]>
1655 <baseset+ [8]>
1652 8
1656 8
1653
1657
1654 $ unset HGPLAIN
1658 $ unset HGPLAIN
1655 $ unset HGPLAINEXCEPT
1659 $ unset HGPLAINEXCEPT
1656
1660
1657 test alias recursion
1661 test alias recursion
1658
1662
1659 $ try sincem
1663 $ try sincem
1660 ('symbol', 'sincem')
1664 ('symbol', 'sincem')
1661 (func
1665 (func
1662 ('symbol', 'descendants')
1666 ('symbol', 'descendants')
1663 (func
1667 (func
1664 ('symbol', 'merge')
1668 ('symbol', 'merge')
1665 None))
1669 None))
1666 * set:
1670 * set:
1667 <addset+
1671 <addset+
1668 <filteredset
1672 <filteredset
1669 <fullreposet+ 0:9>>,
1673 <fullreposet+ 0:9>,
1674 <merge>>,
1670 <generatorset+>>
1675 <generatorset+>>
1671 6
1676 6
1672 7
1677 7
1673
1678
1674 test infinite recursion
1679 test infinite recursion
1675
1680
1676 $ echo 'recurse1 = recurse2' >> .hg/hgrc
1681 $ echo 'recurse1 = recurse2' >> .hg/hgrc
1677 $ echo 'recurse2 = recurse1' >> .hg/hgrc
1682 $ echo 'recurse2 = recurse1' >> .hg/hgrc
1678 $ try recurse1
1683 $ try recurse1
1679 ('symbol', 'recurse1')
1684 ('symbol', 'recurse1')
1680 hg: parse error: infinite expansion of revset alias "recurse1" detected
1685 hg: parse error: infinite expansion of revset alias "recurse1" detected
1681 [255]
1686 [255]
1682
1687
1683 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
1688 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
1684 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
1689 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
1685 $ try "level2(level1(1, 2), 3)"
1690 $ try "level2(level1(1, 2), 3)"
1686 (func
1691 (func
1687 ('symbol', 'level2')
1692 ('symbol', 'level2')
1688 (list
1693 (list
1689 (func
1694 (func
1690 ('symbol', 'level1')
1695 ('symbol', 'level1')
1691 (list
1696 (list
1692 ('symbol', '1')
1697 ('symbol', '1')
1693 ('symbol', '2')))
1698 ('symbol', '2')))
1694 ('symbol', '3')))
1699 ('symbol', '3')))
1695 (or
1700 (or
1696 ('symbol', '3')
1701 ('symbol', '3')
1697 (or
1702 (or
1698 ('symbol', '1')
1703 ('symbol', '1')
1699 ('symbol', '2')))
1704 ('symbol', '2')))
1700 * set:
1705 * set:
1701 <addset
1706 <addset
1702 <baseset [3]>,
1707 <baseset [3]>,
1703 <baseset [1, 2]>>
1708 <baseset [1, 2]>>
1704 3
1709 3
1705 1
1710 1
1706 2
1711 2
1707
1712
1708 test nesting and variable passing
1713 test nesting and variable passing
1709
1714
1710 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1715 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1711 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1716 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1712 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1717 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1713 $ try 'nested(2:5)'
1718 $ try 'nested(2:5)'
1714 (func
1719 (func
1715 ('symbol', 'nested')
1720 ('symbol', 'nested')
1716 (range
1721 (range
1717 ('symbol', '2')
1722 ('symbol', '2')
1718 ('symbol', '5')))
1723 ('symbol', '5')))
1719 (func
1724 (func
1720 ('symbol', 'max')
1725 ('symbol', 'max')
1721 (range
1726 (range
1722 ('symbol', '2')
1727 ('symbol', '2')
1723 ('symbol', '5')))
1728 ('symbol', '5')))
1724 * set:
1729 * set:
1725 <baseset [5]>
1730 <baseset [5]>
1726 5
1731 5
1727
1732
1728 test chained `or` operations are flattened at parsing phase
1733 test chained `or` operations are flattened at parsing phase
1729
1734
1730 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1735 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1731 $ try 'chainedorops(0:1, 1:2, 2:3)'
1736 $ try 'chainedorops(0:1, 1:2, 2:3)'
1732 (func
1737 (func
1733 ('symbol', 'chainedorops')
1738 ('symbol', 'chainedorops')
1734 (list
1739 (list
1735 (range
1740 (range
1736 ('symbol', '0')
1741 ('symbol', '0')
1737 ('symbol', '1'))
1742 ('symbol', '1'))
1738 (range
1743 (range
1739 ('symbol', '1')
1744 ('symbol', '1')
1740 ('symbol', '2'))
1745 ('symbol', '2'))
1741 (range
1746 (range
1742 ('symbol', '2')
1747 ('symbol', '2')
1743 ('symbol', '3'))))
1748 ('symbol', '3'))))
1744 (or
1749 (or
1745 (range
1750 (range
1746 ('symbol', '0')
1751 ('symbol', '0')
1747 ('symbol', '1'))
1752 ('symbol', '1'))
1748 (range
1753 (range
1749 ('symbol', '1')
1754 ('symbol', '1')
1750 ('symbol', '2'))
1755 ('symbol', '2'))
1751 (range
1756 (range
1752 ('symbol', '2')
1757 ('symbol', '2')
1753 ('symbol', '3')))
1758 ('symbol', '3')))
1754 * set:
1759 * set:
1755 <addset
1760 <addset
1756 <spanset+ 0:1>,
1761 <spanset+ 0:1>,
1757 <addset
1762 <addset
1758 <spanset+ 1:2>,
1763 <spanset+ 1:2>,
1759 <spanset+ 2:3>>>
1764 <spanset+ 2:3>>>
1760 0
1765 0
1761 1
1766 1
1762 2
1767 2
1763 3
1768 3
1764
1769
1765 test variable isolation, variable placeholders are rewritten as string
1770 test variable isolation, variable placeholders are rewritten as string
1766 then parsed and matched again as string. Check they do not leak too
1771 then parsed and matched again as string. Check they do not leak too
1767 far away.
1772 far away.
1768
1773
1769 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1774 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1770 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1775 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1771 $ try 'callinjection(2:5)'
1776 $ try 'callinjection(2:5)'
1772 (func
1777 (func
1773 ('symbol', 'callinjection')
1778 ('symbol', 'callinjection')
1774 (range
1779 (range
1775 ('symbol', '2')
1780 ('symbol', '2')
1776 ('symbol', '5')))
1781 ('symbol', '5')))
1777 (func
1782 (func
1778 ('symbol', 'descendants')
1783 ('symbol', 'descendants')
1779 (func
1784 (func
1780 ('symbol', 'max')
1785 ('symbol', 'max')
1781 ('string', '$1')))
1786 ('string', '$1')))
1782 abort: unknown revision '$1'!
1787 abort: unknown revision '$1'!
1783 [255]
1788 [255]
1784
1789
1785 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1790 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1786 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1791 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1787 $ try 'callinjection2(2:5)'
1792 $ try 'callinjection2(2:5)'
1788 (func
1793 (func
1789 ('symbol', 'callinjection2')
1794 ('symbol', 'callinjection2')
1790 (range
1795 (range
1791 ('symbol', '2')
1796 ('symbol', '2')
1792 ('symbol', '5')))
1797 ('symbol', '5')))
1793 abort: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1798 abort: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1794 [255]
1799 [255]
1795 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1800 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1796 ('symbol', 'tip')
1801 ('symbol', 'tip')
1797 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1802 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1798 warning: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1803 warning: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1799 * set:
1804 * set:
1800 <baseset [9]>
1805 <baseset [9]>
1801 9
1806 9
1802 >>> data = file('.hg/hgrc', 'rb').read()
1807 >>> data = file('.hg/hgrc', 'rb').read()
1803 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1808 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1804
1809
1805 $ try 'tip'
1810 $ try 'tip'
1806 ('symbol', 'tip')
1811 ('symbol', 'tip')
1807 * set:
1812 * set:
1808 <baseset [9]>
1813 <baseset [9]>
1809 9
1814 9
1810
1815
1811 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1816 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1812 ('symbol', 'tip')
1817 ('symbol', 'tip')
1813 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1818 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1814 * set:
1819 * set:
1815 <baseset [9]>
1820 <baseset [9]>
1816 9
1821 9
1817 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1822 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1818 $ try 'strictreplacing("foo", tip)'
1823 $ try 'strictreplacing("foo", tip)'
1819 (func
1824 (func
1820 ('symbol', 'strictreplacing')
1825 ('symbol', 'strictreplacing')
1821 (list
1826 (list
1822 ('string', 'foo')
1827 ('string', 'foo')
1823 ('symbol', 'tip')))
1828 ('symbol', 'tip')))
1824 (or
1829 (or
1825 ('symbol', 'tip')
1830 ('symbol', 'tip')
1826 (func
1831 (func
1827 ('symbol', 'desc')
1832 ('symbol', 'desc')
1828 ('string', '$1')))
1833 ('string', '$1')))
1829 * set:
1834 * set:
1830 <addset
1835 <addset
1831 <baseset [9]>,
1836 <baseset [9]>,
1832 <filteredset
1837 <filteredset
1833 <fullreposet+ 0:9>>>
1838 <fullreposet+ 0:9>,
1839 <desc '$1'>>>
1834 9
1840 9
1835
1841
1836 $ try 'd(2:5)'
1842 $ try 'd(2:5)'
1837 (func
1843 (func
1838 ('symbol', 'd')
1844 ('symbol', 'd')
1839 (range
1845 (range
1840 ('symbol', '2')
1846 ('symbol', '2')
1841 ('symbol', '5')))
1847 ('symbol', '5')))
1842 (func
1848 (func
1843 ('symbol', 'reverse')
1849 ('symbol', 'reverse')
1844 (func
1850 (func
1845 ('symbol', 'sort')
1851 ('symbol', 'sort')
1846 (list
1852 (list
1847 (range
1853 (range
1848 ('symbol', '2')
1854 ('symbol', '2')
1849 ('symbol', '5'))
1855 ('symbol', '5'))
1850 ('symbol', 'date'))))
1856 ('symbol', 'date'))))
1851 * set:
1857 * set:
1852 <baseset [4, 5, 3, 2]>
1858 <baseset [4, 5, 3, 2]>
1853 4
1859 4
1854 5
1860 5
1855 3
1861 3
1856 2
1862 2
1857 $ try 'rs(2 or 3, date)'
1863 $ try 'rs(2 or 3, date)'
1858 (func
1864 (func
1859 ('symbol', 'rs')
1865 ('symbol', 'rs')
1860 (list
1866 (list
1861 (or
1867 (or
1862 ('symbol', '2')
1868 ('symbol', '2')
1863 ('symbol', '3'))
1869 ('symbol', '3'))
1864 ('symbol', 'date')))
1870 ('symbol', 'date')))
1865 (func
1871 (func
1866 ('symbol', 'reverse')
1872 ('symbol', 'reverse')
1867 (func
1873 (func
1868 ('symbol', 'sort')
1874 ('symbol', 'sort')
1869 (list
1875 (list
1870 (or
1876 (or
1871 ('symbol', '2')
1877 ('symbol', '2')
1872 ('symbol', '3'))
1878 ('symbol', '3'))
1873 ('symbol', 'date'))))
1879 ('symbol', 'date'))))
1874 * set:
1880 * set:
1875 <baseset [3, 2]>
1881 <baseset [3, 2]>
1876 3
1882 3
1877 2
1883 2
1878 $ try 'rs()'
1884 $ try 'rs()'
1879 (func
1885 (func
1880 ('symbol', 'rs')
1886 ('symbol', 'rs')
1881 None)
1887 None)
1882 hg: parse error: invalid number of arguments: 0
1888 hg: parse error: invalid number of arguments: 0
1883 [255]
1889 [255]
1884 $ try 'rs(2)'
1890 $ try 'rs(2)'
1885 (func
1891 (func
1886 ('symbol', 'rs')
1892 ('symbol', 'rs')
1887 ('symbol', '2'))
1893 ('symbol', '2'))
1888 hg: parse error: invalid number of arguments: 1
1894 hg: parse error: invalid number of arguments: 1
1889 [255]
1895 [255]
1890 $ try 'rs(2, data, 7)'
1896 $ try 'rs(2, data, 7)'
1891 (func
1897 (func
1892 ('symbol', 'rs')
1898 ('symbol', 'rs')
1893 (list
1899 (list
1894 ('symbol', '2')
1900 ('symbol', '2')
1895 ('symbol', 'data')
1901 ('symbol', 'data')
1896 ('symbol', '7')))
1902 ('symbol', '7')))
1897 hg: parse error: invalid number of arguments: 3
1903 hg: parse error: invalid number of arguments: 3
1898 [255]
1904 [255]
1899 $ try 'rs4(2 or 3, x, x, date)'
1905 $ try 'rs4(2 or 3, x, x, date)'
1900 (func
1906 (func
1901 ('symbol', 'rs4')
1907 ('symbol', 'rs4')
1902 (list
1908 (list
1903 (or
1909 (or
1904 ('symbol', '2')
1910 ('symbol', '2')
1905 ('symbol', '3'))
1911 ('symbol', '3'))
1906 ('symbol', 'x')
1912 ('symbol', 'x')
1907 ('symbol', 'x')
1913 ('symbol', 'x')
1908 ('symbol', 'date')))
1914 ('symbol', 'date')))
1909 (func
1915 (func
1910 ('symbol', 'reverse')
1916 ('symbol', 'reverse')
1911 (func
1917 (func
1912 ('symbol', 'sort')
1918 ('symbol', 'sort')
1913 (list
1919 (list
1914 (or
1920 (or
1915 ('symbol', '2')
1921 ('symbol', '2')
1916 ('symbol', '3'))
1922 ('symbol', '3'))
1917 ('symbol', 'date'))))
1923 ('symbol', 'date'))))
1918 * set:
1924 * set:
1919 <baseset [3, 2]>
1925 <baseset [3, 2]>
1920 3
1926 3
1921 2
1927 2
1922
1928
1923 issue4553: check that revset aliases override existing hash prefix
1929 issue4553: check that revset aliases override existing hash prefix
1924
1930
1925 $ hg log -qr e
1931 $ hg log -qr e
1926 6:e0cc66ef77e8
1932 6:e0cc66ef77e8
1927
1933
1928 $ hg log -qr e --config revsetalias.e="all()"
1934 $ hg log -qr e --config revsetalias.e="all()"
1929 0:2785f51eece5
1935 0:2785f51eece5
1930 1:d75937da8da0
1936 1:d75937da8da0
1931 2:5ed5505e9f1c
1937 2:5ed5505e9f1c
1932 3:8528aa5637f2
1938 3:8528aa5637f2
1933 4:2326846efdab
1939 4:2326846efdab
1934 5:904fa392b941
1940 5:904fa392b941
1935 6:e0cc66ef77e8
1941 6:e0cc66ef77e8
1936 7:013af1973af4
1942 7:013af1973af4
1937 8:d5d0dcbdc4d9
1943 8:d5d0dcbdc4d9
1938 9:24286f4ae135
1944 9:24286f4ae135
1939
1945
1940 $ hg log -qr e: --config revsetalias.e="0"
1946 $ hg log -qr e: --config revsetalias.e="0"
1941 0:2785f51eece5
1947 0:2785f51eece5
1942 1:d75937da8da0
1948 1:d75937da8da0
1943 2:5ed5505e9f1c
1949 2:5ed5505e9f1c
1944 3:8528aa5637f2
1950 3:8528aa5637f2
1945 4:2326846efdab
1951 4:2326846efdab
1946 5:904fa392b941
1952 5:904fa392b941
1947 6:e0cc66ef77e8
1953 6:e0cc66ef77e8
1948 7:013af1973af4
1954 7:013af1973af4
1949 8:d5d0dcbdc4d9
1955 8:d5d0dcbdc4d9
1950 9:24286f4ae135
1956 9:24286f4ae135
1951
1957
1952 $ hg log -qr :e --config revsetalias.e="9"
1958 $ hg log -qr :e --config revsetalias.e="9"
1953 0:2785f51eece5
1959 0:2785f51eece5
1954 1:d75937da8da0
1960 1:d75937da8da0
1955 2:5ed5505e9f1c
1961 2:5ed5505e9f1c
1956 3:8528aa5637f2
1962 3:8528aa5637f2
1957 4:2326846efdab
1963 4:2326846efdab
1958 5:904fa392b941
1964 5:904fa392b941
1959 6:e0cc66ef77e8
1965 6:e0cc66ef77e8
1960 7:013af1973af4
1966 7:013af1973af4
1961 8:d5d0dcbdc4d9
1967 8:d5d0dcbdc4d9
1962 9:24286f4ae135
1968 9:24286f4ae135
1963
1969
1964 $ hg log -qr e:
1970 $ hg log -qr e:
1965 6:e0cc66ef77e8
1971 6:e0cc66ef77e8
1966 7:013af1973af4
1972 7:013af1973af4
1967 8:d5d0dcbdc4d9
1973 8:d5d0dcbdc4d9
1968 9:24286f4ae135
1974 9:24286f4ae135
1969
1975
1970 $ hg log -qr :e
1976 $ hg log -qr :e
1971 0:2785f51eece5
1977 0:2785f51eece5
1972 1:d75937da8da0
1978 1:d75937da8da0
1973 2:5ed5505e9f1c
1979 2:5ed5505e9f1c
1974 3:8528aa5637f2
1980 3:8528aa5637f2
1975 4:2326846efdab
1981 4:2326846efdab
1976 5:904fa392b941
1982 5:904fa392b941
1977 6:e0cc66ef77e8
1983 6:e0cc66ef77e8
1978
1984
1979 issue2549 - correct optimizations
1985 issue2549 - correct optimizations
1980
1986
1981 $ log 'limit(1 or 2 or 3, 2) and not 2'
1987 $ log 'limit(1 or 2 or 3, 2) and not 2'
1982 1
1988 1
1983 $ log 'max(1 or 2) and not 2'
1989 $ log 'max(1 or 2) and not 2'
1984 $ log 'min(1 or 2) and not 1'
1990 $ log 'min(1 or 2) and not 1'
1985 $ log 'last(1 or 2, 1) and not 2'
1991 $ log 'last(1 or 2, 1) and not 2'
1986
1992
1987 issue4289 - ordering of built-ins
1993 issue4289 - ordering of built-ins
1988 $ hg log -M -q -r 3:2
1994 $ hg log -M -q -r 3:2
1989 3:8528aa5637f2
1995 3:8528aa5637f2
1990 2:5ed5505e9f1c
1996 2:5ed5505e9f1c
1991
1997
1992 test revsets started with 40-chars hash (issue3669)
1998 test revsets started with 40-chars hash (issue3669)
1993
1999
1994 $ ISSUE3669_TIP=`hg tip --template '{node}'`
2000 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1995 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
2001 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1996 9
2002 9
1997 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
2003 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1998 8
2004 8
1999
2005
2000 test or-ed indirect predicates (issue3775)
2006 test or-ed indirect predicates (issue3775)
2001
2007
2002 $ log '6 or 6^1' | sort
2008 $ log '6 or 6^1' | sort
2003 5
2009 5
2004 6
2010 6
2005 $ log '6^1 or 6' | sort
2011 $ log '6^1 or 6' | sort
2006 5
2012 5
2007 6
2013 6
2008 $ log '4 or 4~1' | sort
2014 $ log '4 or 4~1' | sort
2009 2
2015 2
2010 4
2016 4
2011 $ log '4~1 or 4' | sort
2017 $ log '4~1 or 4' | sort
2012 2
2018 2
2013 4
2019 4
2014 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
2020 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
2015 0
2021 0
2016 1
2022 1
2017 2
2023 2
2018 3
2024 3
2019 4
2025 4
2020 5
2026 5
2021 6
2027 6
2022 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
2028 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
2023 0
2029 0
2024 1
2030 1
2025 2
2031 2
2026 3
2032 3
2027 4
2033 4
2028 5
2034 5
2029 6
2035 6
2030
2036
2031 tests for 'remote()' predicate:
2037 tests for 'remote()' predicate:
2032 #. (csets in remote) (id) (remote)
2038 #. (csets in remote) (id) (remote)
2033 1. less than local current branch "default"
2039 1. less than local current branch "default"
2034 2. same with local specified "default"
2040 2. same with local specified "default"
2035 3. more than local specified specified
2041 3. more than local specified specified
2036
2042
2037 $ hg clone --quiet -U . ../remote3
2043 $ hg clone --quiet -U . ../remote3
2038 $ cd ../remote3
2044 $ cd ../remote3
2039 $ hg update -q 7
2045 $ hg update -q 7
2040 $ echo r > r
2046 $ echo r > r
2041 $ hg ci -Aqm 10
2047 $ hg ci -Aqm 10
2042 $ log 'remote()'
2048 $ log 'remote()'
2043 7
2049 7
2044 $ log 'remote("a-b-c-")'
2050 $ log 'remote("a-b-c-")'
2045 2
2051 2
2046 $ cd ../repo
2052 $ cd ../repo
2047 $ log 'remote(".a.b.c.", "../remote3")'
2053 $ log 'remote(".a.b.c.", "../remote3")'
2048
2054
2049 tests for concatenation of strings/symbols by "##"
2055 tests for concatenation of strings/symbols by "##"
2050
2056
2051 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
2057 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
2052 (_concat
2058 (_concat
2053 (_concat
2059 (_concat
2054 (_concat
2060 (_concat
2055 ('symbol', '278')
2061 ('symbol', '278')
2056 ('string', '5f5'))
2062 ('string', '5f5'))
2057 ('symbol', '1ee'))
2063 ('symbol', '1ee'))
2058 ('string', 'ce5'))
2064 ('string', 'ce5'))
2059 ('string', '2785f51eece5')
2065 ('string', '2785f51eece5')
2060 * set:
2066 * set:
2061 <baseset [0]>
2067 <baseset [0]>
2062 0
2068 0
2063
2069
2064 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
2070 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
2065 $ try "cat4(278, '5f5', 1ee, 'ce5')"
2071 $ try "cat4(278, '5f5', 1ee, 'ce5')"
2066 (func
2072 (func
2067 ('symbol', 'cat4')
2073 ('symbol', 'cat4')
2068 (list
2074 (list
2069 ('symbol', '278')
2075 ('symbol', '278')
2070 ('string', '5f5')
2076 ('string', '5f5')
2071 ('symbol', '1ee')
2077 ('symbol', '1ee')
2072 ('string', 'ce5')))
2078 ('string', 'ce5')))
2073 (_concat
2079 (_concat
2074 (_concat
2080 (_concat
2075 (_concat
2081 (_concat
2076 ('symbol', '278')
2082 ('symbol', '278')
2077 ('string', '5f5'))
2083 ('string', '5f5'))
2078 ('symbol', '1ee'))
2084 ('symbol', '1ee'))
2079 ('string', 'ce5'))
2085 ('string', 'ce5'))
2080 ('string', '2785f51eece5')
2086 ('string', '2785f51eece5')
2081 * set:
2087 * set:
2082 <baseset [0]>
2088 <baseset [0]>
2083 0
2089 0
2084
2090
2085 (check concatenation in alias nesting)
2091 (check concatenation in alias nesting)
2086
2092
2087 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
2093 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
2088 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
2094 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
2089 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
2095 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
2090 0
2096 0
2091
2097
2092 (check operator priority)
2098 (check operator priority)
2093
2099
2094 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
2100 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
2095 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
2101 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
2096 0
2102 0
2097 4
2103 4
2098
2104
2099 $ cd ..
2105 $ cd ..
2100
2106
2101 prepare repository that has "default" branches of multiple roots
2107 prepare repository that has "default" branches of multiple roots
2102
2108
2103 $ hg init namedbranch
2109 $ hg init namedbranch
2104 $ cd namedbranch
2110 $ cd namedbranch
2105
2111
2106 $ echo default0 >> a
2112 $ echo default0 >> a
2107 $ hg ci -Aqm0
2113 $ hg ci -Aqm0
2108 $ echo default1 >> a
2114 $ echo default1 >> a
2109 $ hg ci -m1
2115 $ hg ci -m1
2110
2116
2111 $ hg branch -q stable
2117 $ hg branch -q stable
2112 $ echo stable2 >> a
2118 $ echo stable2 >> a
2113 $ hg ci -m2
2119 $ hg ci -m2
2114 $ echo stable3 >> a
2120 $ echo stable3 >> a
2115 $ hg ci -m3
2121 $ hg ci -m3
2116
2122
2117 $ hg update -q null
2123 $ hg update -q null
2118 $ echo default4 >> a
2124 $ echo default4 >> a
2119 $ hg ci -Aqm4
2125 $ hg ci -Aqm4
2120 $ echo default5 >> a
2126 $ echo default5 >> a
2121 $ hg ci -m5
2127 $ hg ci -m5
2122
2128
2123 "null" revision belongs to "default" branch (issue4683)
2129 "null" revision belongs to "default" branch (issue4683)
2124
2130
2125 $ log 'branch(null)'
2131 $ log 'branch(null)'
2126 0
2132 0
2127 1
2133 1
2128 4
2134 4
2129 5
2135 5
2130
2136
2131 "null" revision belongs to "default" branch, but it shouldn't appear in set
2137 "null" revision belongs to "default" branch, but it shouldn't appear in set
2132 unless explicitly specified (issue4682)
2138 unless explicitly specified (issue4682)
2133
2139
2134 $ log 'children(branch(default))'
2140 $ log 'children(branch(default))'
2135 1
2141 1
2136 2
2142 2
2137 5
2143 5
2138
2144
2139 $ cd ..
2145 $ cd ..
2140
2146
2141 test author/desc/keyword in problematic encoding
2147 test author/desc/keyword in problematic encoding
2142 # unicode: cp932:
2148 # unicode: cp932:
2143 # u30A2 0x83 0x41(= 'A')
2149 # u30A2 0x83 0x41(= 'A')
2144 # u30C2 0x83 0x61(= 'a')
2150 # u30C2 0x83 0x61(= 'a')
2145
2151
2146 $ hg init problematicencoding
2152 $ hg init problematicencoding
2147 $ cd problematicencoding
2153 $ cd problematicencoding
2148
2154
2149 $ python > setup.sh <<EOF
2155 $ python > setup.sh <<EOF
2150 > print u'''
2156 > print u'''
2151 > echo a > text
2157 > echo a > text
2152 > hg add text
2158 > hg add text
2153 > hg --encoding utf-8 commit -u '\u30A2' -m none
2159 > hg --encoding utf-8 commit -u '\u30A2' -m none
2154 > echo b > text
2160 > echo b > text
2155 > hg --encoding utf-8 commit -u '\u30C2' -m none
2161 > hg --encoding utf-8 commit -u '\u30C2' -m none
2156 > echo c > text
2162 > echo c > text
2157 > hg --encoding utf-8 commit -u none -m '\u30A2'
2163 > hg --encoding utf-8 commit -u none -m '\u30A2'
2158 > echo d > text
2164 > echo d > text
2159 > hg --encoding utf-8 commit -u none -m '\u30C2'
2165 > hg --encoding utf-8 commit -u none -m '\u30C2'
2160 > '''.encode('utf-8')
2166 > '''.encode('utf-8')
2161 > EOF
2167 > EOF
2162 $ sh < setup.sh
2168 $ sh < setup.sh
2163
2169
2164 test in problematic encoding
2170 test in problematic encoding
2165 $ python > test.sh <<EOF
2171 $ python > test.sh <<EOF
2166 > print u'''
2172 > print u'''
2167 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2173 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2168 > echo ====
2174 > echo ====
2169 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2175 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2170 > echo ====
2176 > echo ====
2171 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2177 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2172 > echo ====
2178 > echo ====
2173 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2179 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2174 > echo ====
2180 > echo ====
2175 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2181 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2176 > echo ====
2182 > echo ====
2177 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2183 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2178 > '''.encode('cp932')
2184 > '''.encode('cp932')
2179 > EOF
2185 > EOF
2180 $ sh < test.sh
2186 $ sh < test.sh
2181 0
2187 0
2182 ====
2188 ====
2183 1
2189 1
2184 ====
2190 ====
2185 2
2191 2
2186 ====
2192 ====
2187 3
2193 3
2188 ====
2194 ====
2189 0
2195 0
2190 2
2196 2
2191 ====
2197 ====
2192 1
2198 1
2193 3
2199 3
2194
2200
2195 test error message of bad revset
2201 test error message of bad revset
2196 $ hg log -r 'foo\\'
2202 $ hg log -r 'foo\\'
2197 hg: parse error at 3: syntax error in revset 'foo\\'
2203 hg: parse error at 3: syntax error in revset 'foo\\'
2198 [255]
2204 [255]
2199
2205
2200 $ cd ..
2206 $ cd ..
2201
2207
2202 Test that revset predicate of extension isn't loaded at failure of
2208 Test that revset predicate of extension isn't loaded at failure of
2203 loading it
2209 loading it
2204
2210
2205 $ cd repo
2211 $ cd repo
2206
2212
2207 $ cat <<EOF > $TESTTMP/custompredicate.py
2213 $ cat <<EOF > $TESTTMP/custompredicate.py
2208 > from mercurial import error, registrar, revset
2214 > from mercurial import error, registrar, revset
2209 >
2215 >
2210 > revsetpredicate = registrar.revsetpredicate()
2216 > revsetpredicate = registrar.revsetpredicate()
2211 >
2217 >
2212 > @revsetpredicate('custom1()')
2218 > @revsetpredicate('custom1()')
2213 > def custom1(repo, subset, x):
2219 > def custom1(repo, subset, x):
2214 > return revset.baseset([1])
2220 > return revset.baseset([1])
2215 >
2221 >
2216 > raise error.Abort('intentional failure of loading extension')
2222 > raise error.Abort('intentional failure of loading extension')
2217 > EOF
2223 > EOF
2218 $ cat <<EOF > .hg/hgrc
2224 $ cat <<EOF > .hg/hgrc
2219 > [extensions]
2225 > [extensions]
2220 > custompredicate = $TESTTMP/custompredicate.py
2226 > custompredicate = $TESTTMP/custompredicate.py
2221 > EOF
2227 > EOF
2222
2228
2223 $ hg debugrevspec "custom1()"
2229 $ hg debugrevspec "custom1()"
2224 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
2230 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
2225 hg: parse error: unknown identifier: custom1
2231 hg: parse error: unknown identifier: custom1
2226 [255]
2232 [255]
2227
2233
2228 $ cd ..
2234 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now