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