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