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