##// END OF EJS Templates
revset: for x^2, do not take null as a valid p2 revision...
Yuya Nishihara -
r30179:cdef35b3 default
parent child Browse files
Show More
@@ -1,3848 +1,3848
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 import string
12 import string
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 destutil,
16 destutil,
17 encoding,
17 encoding,
18 error,
18 error,
19 hbisect,
19 hbisect,
20 match as matchmod,
20 match as matchmod,
21 node,
21 node,
22 obsolete as obsmod,
22 obsolete as obsmod,
23 parser,
23 parser,
24 pathutil,
24 pathutil,
25 phases,
25 phases,
26 pycompat,
26 pycompat,
27 registrar,
27 registrar,
28 repoview,
28 repoview,
29 util,
29 util,
30 )
30 )
31
31
32 def _revancestors(repo, revs, followfirst):
32 def _revancestors(repo, revs, followfirst):
33 """Like revlog.ancestors(), but supports followfirst."""
33 """Like revlog.ancestors(), but supports followfirst."""
34 if followfirst:
34 if followfirst:
35 cut = 1
35 cut = 1
36 else:
36 else:
37 cut = None
37 cut = None
38 cl = repo.changelog
38 cl = repo.changelog
39
39
40 def iterate():
40 def iterate():
41 revs.sort(reverse=True)
41 revs.sort(reverse=True)
42 irevs = iter(revs)
42 irevs = iter(revs)
43 h = []
43 h = []
44
44
45 inputrev = next(irevs, None)
45 inputrev = next(irevs, None)
46 if inputrev is not None:
46 if inputrev is not None:
47 heapq.heappush(h, -inputrev)
47 heapq.heappush(h, -inputrev)
48
48
49 seen = set()
49 seen = set()
50 while h:
50 while h:
51 current = -heapq.heappop(h)
51 current = -heapq.heappop(h)
52 if current == inputrev:
52 if current == inputrev:
53 inputrev = next(irevs, None)
53 inputrev = next(irevs, None)
54 if inputrev is not None:
54 if inputrev is not None:
55 heapq.heappush(h, -inputrev)
55 heapq.heappush(h, -inputrev)
56 if current not in seen:
56 if current not in seen:
57 seen.add(current)
57 seen.add(current)
58 yield current
58 yield current
59 for parent in cl.parentrevs(current)[:cut]:
59 for parent in cl.parentrevs(current)[:cut]:
60 if parent != node.nullrev:
60 if parent != node.nullrev:
61 heapq.heappush(h, -parent)
61 heapq.heappush(h, -parent)
62
62
63 return generatorset(iterate(), iterasc=False)
63 return generatorset(iterate(), iterasc=False)
64
64
65 def _revdescendants(repo, revs, followfirst):
65 def _revdescendants(repo, revs, followfirst):
66 """Like revlog.descendants() but supports followfirst."""
66 """Like revlog.descendants() but supports followfirst."""
67 if followfirst:
67 if followfirst:
68 cut = 1
68 cut = 1
69 else:
69 else:
70 cut = None
70 cut = None
71
71
72 def iterate():
72 def iterate():
73 cl = repo.changelog
73 cl = repo.changelog
74 # XXX this should be 'parentset.min()' assuming 'parentset' is a
74 # XXX this should be 'parentset.min()' assuming 'parentset' is a
75 # smartset (and if it is not, it should.)
75 # smartset (and if it is not, it should.)
76 first = min(revs)
76 first = min(revs)
77 nullrev = node.nullrev
77 nullrev = node.nullrev
78 if first == nullrev:
78 if first == nullrev:
79 # Are there nodes with a null first parent and a non-null
79 # Are there nodes with a null first parent and a non-null
80 # second one? Maybe. Do we care? Probably not.
80 # second one? Maybe. Do we care? Probably not.
81 for i in cl:
81 for i in cl:
82 yield i
82 yield i
83 else:
83 else:
84 seen = set(revs)
84 seen = set(revs)
85 for i in cl.revs(first + 1):
85 for i in cl.revs(first + 1):
86 for x in cl.parentrevs(i)[:cut]:
86 for x in cl.parentrevs(i)[:cut]:
87 if x != nullrev and x in seen:
87 if x != nullrev and x in seen:
88 seen.add(i)
88 seen.add(i)
89 yield i
89 yield i
90 break
90 break
91
91
92 return generatorset(iterate(), iterasc=True)
92 return generatorset(iterate(), iterasc=True)
93
93
94 def _reachablerootspure(repo, minroot, roots, heads, includepath):
94 def _reachablerootspure(repo, minroot, roots, heads, includepath):
95 """return (heads(::<roots> and ::<heads>))
95 """return (heads(::<roots> and ::<heads>))
96
96
97 If includepath is True, return (<roots>::<heads>)."""
97 If includepath is True, return (<roots>::<heads>)."""
98 if not roots:
98 if not roots:
99 return []
99 return []
100 parentrevs = repo.changelog.parentrevs
100 parentrevs = repo.changelog.parentrevs
101 roots = set(roots)
101 roots = set(roots)
102 visit = list(heads)
102 visit = list(heads)
103 reachable = set()
103 reachable = set()
104 seen = {}
104 seen = {}
105 # prefetch all the things! (because python is slow)
105 # prefetch all the things! (because python is slow)
106 reached = reachable.add
106 reached = reachable.add
107 dovisit = visit.append
107 dovisit = visit.append
108 nextvisit = visit.pop
108 nextvisit = visit.pop
109 # open-code the post-order traversal due to the tiny size of
109 # open-code the post-order traversal due to the tiny size of
110 # sys.getrecursionlimit()
110 # sys.getrecursionlimit()
111 while visit:
111 while visit:
112 rev = nextvisit()
112 rev = nextvisit()
113 if rev in roots:
113 if rev in roots:
114 reached(rev)
114 reached(rev)
115 if not includepath:
115 if not includepath:
116 continue
116 continue
117 parents = parentrevs(rev)
117 parents = parentrevs(rev)
118 seen[rev] = parents
118 seen[rev] = parents
119 for parent in parents:
119 for parent in parents:
120 if parent >= minroot and parent not in seen:
120 if parent >= minroot and parent not in seen:
121 dovisit(parent)
121 dovisit(parent)
122 if not reachable:
122 if not reachable:
123 return baseset()
123 return baseset()
124 if not includepath:
124 if not includepath:
125 return reachable
125 return reachable
126 for rev in sorted(seen):
126 for rev in sorted(seen):
127 for parent in seen[rev]:
127 for parent in seen[rev]:
128 if parent in reachable:
128 if parent in reachable:
129 reached(rev)
129 reached(rev)
130 return reachable
130 return reachable
131
131
132 def reachableroots(repo, roots, heads, includepath=False):
132 def reachableroots(repo, roots, heads, includepath=False):
133 """return (heads(::<roots> and ::<heads>))
133 """return (heads(::<roots> and ::<heads>))
134
134
135 If includepath is True, return (<roots>::<heads>)."""
135 If includepath is True, return (<roots>::<heads>)."""
136 if not roots:
136 if not roots:
137 return baseset()
137 return baseset()
138 minroot = roots.min()
138 minroot = roots.min()
139 roots = list(roots)
139 roots = list(roots)
140 heads = list(heads)
140 heads = list(heads)
141 try:
141 try:
142 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
142 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
143 except AttributeError:
143 except AttributeError:
144 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
144 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
145 revs = baseset(revs)
145 revs = baseset(revs)
146 revs.sort()
146 revs.sort()
147 return revs
147 return revs
148
148
149 elements = {
149 elements = {
150 # token-type: binding-strength, primary, prefix, infix, suffix
150 # token-type: binding-strength, primary, prefix, infix, suffix
151 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
151 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
152 "##": (20, None, None, ("_concat", 20), None),
152 "##": (20, None, None, ("_concat", 20), None),
153 "~": (18, None, None, ("ancestor", 18), None),
153 "~": (18, None, None, ("ancestor", 18), None),
154 "^": (18, None, None, ("parent", 18), "parentpost"),
154 "^": (18, None, None, ("parent", 18), "parentpost"),
155 "-": (5, None, ("negate", 19), ("minus", 5), None),
155 "-": (5, None, ("negate", 19), ("minus", 5), None),
156 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
156 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
157 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
157 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
158 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
158 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
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"),
163 "%": (5, None, None, ("only", 5), "onlypost"),
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(
178 _syminitletters = set(
179 string.ascii_letters +
179 string.ascii_letters +
180 string.digits + pycompat.sysstr('._@')) | set(map(chr, xrange(128, 256)))
180 string.digits + pycompat.sysstr('._@')) | set(map(chr, xrange(128, 256)))
181
181
182 # default set of valid characters for non-initial letters of symbols
182 # default set of valid characters for non-initial letters of symbols
183 _symletters = _syminitletters | set(pycompat.sysstr('-/'))
183 _symletters = _syminitletters | set(pycompat.sysstr('-/'))
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 getsymbol(x):
305 def getsymbol(x):
306 if x and x[0] == 'symbol':
306 if x and x[0] == 'symbol':
307 return x[1]
307 return x[1]
308 raise error.ParseError(_('not a symbol'))
308 raise error.ParseError(_('not a symbol'))
309
309
310 def getstring(x, err):
310 def getstring(x, err):
311 if x and (x[0] == 'string' or x[0] == 'symbol'):
311 if x and (x[0] == 'string' or x[0] == 'symbol'):
312 return x[1]
312 return x[1]
313 raise error.ParseError(err)
313 raise error.ParseError(err)
314
314
315 def getlist(x):
315 def getlist(x):
316 if not x:
316 if not x:
317 return []
317 return []
318 if x[0] == 'list':
318 if x[0] == 'list':
319 return list(x[1:])
319 return list(x[1:])
320 return [x]
320 return [x]
321
321
322 def getargs(x, min, max, err):
322 def getargs(x, min, max, err):
323 l = getlist(x)
323 l = getlist(x)
324 if len(l) < min or (max >= 0 and len(l) > max):
324 if len(l) < min or (max >= 0 and len(l) > max):
325 raise error.ParseError(err)
325 raise error.ParseError(err)
326 return l
326 return l
327
327
328 def getargsdict(x, funcname, keys):
328 def getargsdict(x, funcname, keys):
329 return parser.buildargsdict(getlist(x), funcname, keys.split(),
329 return parser.buildargsdict(getlist(x), funcname, keys.split(),
330 keyvaluenode='keyvalue', keynode='symbol')
330 keyvaluenode='keyvalue', keynode='symbol')
331
331
332 def getset(repo, subset, x):
332 def getset(repo, subset, x):
333 if not x:
333 if not x:
334 raise error.ParseError(_("missing argument"))
334 raise error.ParseError(_("missing argument"))
335 s = methods[x[0]](repo, subset, *x[1:])
335 s = methods[x[0]](repo, subset, *x[1:])
336 if util.safehasattr(s, 'isascending'):
336 if util.safehasattr(s, 'isascending'):
337 return s
337 return s
338 # else case should not happen, because all non-func are internal,
338 # else case should not happen, because all non-func are internal,
339 # ignoring for now.
339 # ignoring for now.
340 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
340 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
341 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
341 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
342 % x[1][1],
342 % x[1][1],
343 '3.9')
343 '3.9')
344 return baseset(s)
344 return baseset(s)
345
345
346 def _getrevsource(repo, r):
346 def _getrevsource(repo, r):
347 extra = repo[r].extra()
347 extra = repo[r].extra()
348 for label in ('source', 'transplant_source', 'rebase_source'):
348 for label in ('source', 'transplant_source', 'rebase_source'):
349 if label in extra:
349 if label in extra:
350 try:
350 try:
351 return repo[extra[label]].rev()
351 return repo[extra[label]].rev()
352 except error.RepoLookupError:
352 except error.RepoLookupError:
353 pass
353 pass
354 return None
354 return None
355
355
356 # operator methods
356 # operator methods
357
357
358 def stringset(repo, subset, x):
358 def stringset(repo, subset, x):
359 x = repo[x].rev()
359 x = repo[x].rev()
360 if (x in subset
360 if (x in subset
361 or x == node.nullrev and isinstance(subset, fullreposet)):
361 or x == node.nullrev and isinstance(subset, fullreposet)):
362 return baseset([x])
362 return baseset([x])
363 return baseset()
363 return baseset()
364
364
365 def rangeset(repo, subset, x, y, order):
365 def rangeset(repo, subset, x, y, order):
366 m = getset(repo, fullreposet(repo), x)
366 m = getset(repo, fullreposet(repo), x)
367 n = getset(repo, fullreposet(repo), y)
367 n = getset(repo, fullreposet(repo), y)
368
368
369 if not m or not n:
369 if not m or not n:
370 return baseset()
370 return baseset()
371 return _makerangeset(repo, subset, m.first(), n.last(), order)
371 return _makerangeset(repo, subset, m.first(), n.last(), order)
372
372
373 def rangepre(repo, subset, y, order):
373 def rangepre(repo, subset, y, order):
374 # ':y' can't be rewritten to '0:y' since '0' may be hidden
374 # ':y' can't be rewritten to '0:y' since '0' may be hidden
375 n = getset(repo, fullreposet(repo), y)
375 n = getset(repo, fullreposet(repo), y)
376 if not n:
376 if not n:
377 return baseset()
377 return baseset()
378 return _makerangeset(repo, subset, 0, n.last(), order)
378 return _makerangeset(repo, subset, 0, n.last(), order)
379
379
380 def _makerangeset(repo, subset, m, n, order):
380 def _makerangeset(repo, subset, m, n, order):
381 if m == n:
381 if m == n:
382 r = baseset([m])
382 r = baseset([m])
383 elif n == node.wdirrev:
383 elif n == node.wdirrev:
384 r = spanset(repo, m, len(repo)) + baseset([n])
384 r = spanset(repo, m, len(repo)) + baseset([n])
385 elif m == node.wdirrev:
385 elif m == node.wdirrev:
386 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
386 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
387 elif m < n:
387 elif m < n:
388 r = spanset(repo, m, n + 1)
388 r = spanset(repo, m, n + 1)
389 else:
389 else:
390 r = spanset(repo, m, n - 1)
390 r = spanset(repo, m, n - 1)
391
391
392 if order == defineorder:
392 if order == defineorder:
393 return r & subset
393 return r & subset
394 else:
394 else:
395 # carrying the sorting over when possible would be more efficient
395 # carrying the sorting over when possible would be more efficient
396 return subset & r
396 return subset & r
397
397
398 def dagrange(repo, subset, x, y, order):
398 def dagrange(repo, subset, x, y, order):
399 r = fullreposet(repo)
399 r = fullreposet(repo)
400 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
400 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
401 includepath=True)
401 includepath=True)
402 return subset & xs
402 return subset & xs
403
403
404 def andset(repo, subset, x, y, order):
404 def andset(repo, subset, x, y, order):
405 return getset(repo, getset(repo, subset, x), y)
405 return getset(repo, getset(repo, subset, x), y)
406
406
407 def differenceset(repo, subset, x, y, order):
407 def differenceset(repo, subset, x, y, order):
408 return getset(repo, subset, x) - getset(repo, subset, y)
408 return getset(repo, subset, x) - getset(repo, subset, y)
409
409
410 def _orsetlist(repo, subset, xs):
410 def _orsetlist(repo, subset, xs):
411 assert xs
411 assert xs
412 if len(xs) == 1:
412 if len(xs) == 1:
413 return getset(repo, subset, xs[0])
413 return getset(repo, subset, xs[0])
414 p = len(xs) // 2
414 p = len(xs) // 2
415 a = _orsetlist(repo, subset, xs[:p])
415 a = _orsetlist(repo, subset, xs[:p])
416 b = _orsetlist(repo, subset, xs[p:])
416 b = _orsetlist(repo, subset, xs[p:])
417 return a + b
417 return a + b
418
418
419 def orset(repo, subset, x, order):
419 def orset(repo, subset, x, order):
420 xs = getlist(x)
420 xs = getlist(x)
421 if order == followorder:
421 if order == followorder:
422 # slow path to take the subset order
422 # slow path to take the subset order
423 return subset & _orsetlist(repo, fullreposet(repo), xs)
423 return subset & _orsetlist(repo, fullreposet(repo), xs)
424 else:
424 else:
425 return _orsetlist(repo, subset, xs)
425 return _orsetlist(repo, subset, xs)
426
426
427 def notset(repo, subset, x, order):
427 def notset(repo, subset, x, order):
428 return subset - getset(repo, subset, x)
428 return subset - getset(repo, subset, x)
429
429
430 def listset(repo, subset, *xs):
430 def listset(repo, subset, *xs):
431 raise error.ParseError(_("can't use a list in this context"),
431 raise error.ParseError(_("can't use a list in this context"),
432 hint=_('see hg help "revsets.x or y"'))
432 hint=_('see hg help "revsets.x or y"'))
433
433
434 def keyvaluepair(repo, subset, k, v):
434 def keyvaluepair(repo, subset, k, v):
435 raise error.ParseError(_("can't use a key-value pair in this context"))
435 raise error.ParseError(_("can't use a key-value pair in this context"))
436
436
437 def func(repo, subset, a, b, order):
437 def func(repo, subset, a, b, order):
438 f = getsymbol(a)
438 f = getsymbol(a)
439 if f in symbols:
439 if f in symbols:
440 fn = symbols[f]
440 fn = symbols[f]
441 if getattr(fn, '_takeorder', False):
441 if getattr(fn, '_takeorder', False):
442 return fn(repo, subset, b, order)
442 return fn(repo, subset, b, order)
443 return fn(repo, subset, b)
443 return fn(repo, subset, b)
444
444
445 keep = lambda fn: getattr(fn, '__doc__', None) is not None
445 keep = lambda fn: getattr(fn, '__doc__', None) is not None
446
446
447 syms = [s for (s, fn) in symbols.items() if keep(fn)]
447 syms = [s for (s, fn) in symbols.items() if keep(fn)]
448 raise error.UnknownIdentifier(f, syms)
448 raise error.UnknownIdentifier(f, syms)
449
449
450 # functions
450 # functions
451
451
452 # symbols are callables like:
452 # symbols are callables like:
453 # fn(repo, subset, x)
453 # fn(repo, subset, x)
454 # with:
454 # with:
455 # repo - current repository instance
455 # repo - current repository instance
456 # subset - of revisions to be examined
456 # subset - of revisions to be examined
457 # x - argument in tree form
457 # x - argument in tree form
458 symbols = {}
458 symbols = {}
459
459
460 # symbols which can't be used for a DoS attack for any given input
460 # symbols which can't be used for a DoS attack for any given input
461 # (e.g. those which accept regexes as plain strings shouldn't be included)
461 # (e.g. those which accept regexes as plain strings shouldn't be included)
462 # functions that just return a lot of changesets (like all) don't count here
462 # functions that just return a lot of changesets (like all) don't count here
463 safesymbols = set()
463 safesymbols = set()
464
464
465 predicate = registrar.revsetpredicate()
465 predicate = registrar.revsetpredicate()
466
466
467 @predicate('_destupdate')
467 @predicate('_destupdate')
468 def _destupdate(repo, subset, x):
468 def _destupdate(repo, subset, x):
469 # experimental revset for update destination
469 # experimental revset for update destination
470 args = getargsdict(x, 'limit', 'clean check')
470 args = getargsdict(x, 'limit', 'clean check')
471 return subset & baseset([destutil.destupdate(repo, **args)[0]])
471 return subset & baseset([destutil.destupdate(repo, **args)[0]])
472
472
473 @predicate('_destmerge')
473 @predicate('_destmerge')
474 def _destmerge(repo, subset, x):
474 def _destmerge(repo, subset, x):
475 # experimental revset for merge destination
475 # experimental revset for merge destination
476 sourceset = None
476 sourceset = None
477 if x is not None:
477 if x is not None:
478 sourceset = getset(repo, fullreposet(repo), x)
478 sourceset = getset(repo, fullreposet(repo), x)
479 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
479 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
480
480
481 @predicate('adds(pattern)', safe=True)
481 @predicate('adds(pattern)', safe=True)
482 def adds(repo, subset, x):
482 def adds(repo, subset, x):
483 """Changesets that add a file matching pattern.
483 """Changesets that add a file matching pattern.
484
484
485 The pattern without explicit kind like ``glob:`` is expected to be
485 The pattern without explicit kind like ``glob:`` is expected to be
486 relative to the current directory and match against a file or a
486 relative to the current directory and match against a file or a
487 directory.
487 directory.
488 """
488 """
489 # i18n: "adds" is a keyword
489 # i18n: "adds" is a keyword
490 pat = getstring(x, _("adds requires a pattern"))
490 pat = getstring(x, _("adds requires a pattern"))
491 return checkstatus(repo, subset, pat, 1)
491 return checkstatus(repo, subset, pat, 1)
492
492
493 @predicate('ancestor(*changeset)', safe=True)
493 @predicate('ancestor(*changeset)', safe=True)
494 def ancestor(repo, subset, x):
494 def ancestor(repo, subset, x):
495 """A greatest common ancestor of the changesets.
495 """A greatest common ancestor of the changesets.
496
496
497 Accepts 0 or more changesets.
497 Accepts 0 or more changesets.
498 Will return empty list when passed no args.
498 Will return empty list when passed no args.
499 Greatest common ancestor of a single changeset is that changeset.
499 Greatest common ancestor of a single changeset is that changeset.
500 """
500 """
501 # i18n: "ancestor" is a keyword
501 # i18n: "ancestor" is a keyword
502 l = getlist(x)
502 l = getlist(x)
503 rl = fullreposet(repo)
503 rl = fullreposet(repo)
504 anc = None
504 anc = None
505
505
506 # (getset(repo, rl, i) for i in l) generates a list of lists
506 # (getset(repo, rl, i) for i in l) generates a list of lists
507 for revs in (getset(repo, rl, i) for i in l):
507 for revs in (getset(repo, rl, i) for i in l):
508 for r in revs:
508 for r in revs:
509 if anc is None:
509 if anc is None:
510 anc = repo[r]
510 anc = repo[r]
511 else:
511 else:
512 anc = anc.ancestor(repo[r])
512 anc = anc.ancestor(repo[r])
513
513
514 if anc is not None and anc.rev() in subset:
514 if anc is not None and anc.rev() in subset:
515 return baseset([anc.rev()])
515 return baseset([anc.rev()])
516 return baseset()
516 return baseset()
517
517
518 def _ancestors(repo, subset, x, followfirst=False):
518 def _ancestors(repo, subset, x, followfirst=False):
519 heads = getset(repo, fullreposet(repo), x)
519 heads = getset(repo, fullreposet(repo), x)
520 if not heads:
520 if not heads:
521 return baseset()
521 return baseset()
522 s = _revancestors(repo, heads, followfirst)
522 s = _revancestors(repo, heads, followfirst)
523 return subset & s
523 return subset & s
524
524
525 @predicate('ancestors(set)', safe=True)
525 @predicate('ancestors(set)', safe=True)
526 def ancestors(repo, subset, x):
526 def ancestors(repo, subset, x):
527 """Changesets that are ancestors of a changeset in set.
527 """Changesets that are ancestors of a changeset in set.
528 """
528 """
529 return _ancestors(repo, subset, x)
529 return _ancestors(repo, subset, x)
530
530
531 @predicate('_firstancestors', safe=True)
531 @predicate('_firstancestors', safe=True)
532 def _firstancestors(repo, subset, x):
532 def _firstancestors(repo, subset, x):
533 # ``_firstancestors(set)``
533 # ``_firstancestors(set)``
534 # Like ``ancestors(set)`` but follows only the first parents.
534 # Like ``ancestors(set)`` but follows only the first parents.
535 return _ancestors(repo, subset, x, followfirst=True)
535 return _ancestors(repo, subset, x, followfirst=True)
536
536
537 def ancestorspec(repo, subset, x, n, order):
537 def ancestorspec(repo, subset, x, n, order):
538 """``set~n``
538 """``set~n``
539 Changesets that are the Nth ancestor (first parents only) of a changeset
539 Changesets that are the Nth ancestor (first parents only) of a changeset
540 in set.
540 in set.
541 """
541 """
542 try:
542 try:
543 n = int(n[1])
543 n = int(n[1])
544 except (TypeError, ValueError):
544 except (TypeError, ValueError):
545 raise error.ParseError(_("~ expects a number"))
545 raise error.ParseError(_("~ expects a number"))
546 ps = set()
546 ps = set()
547 cl = repo.changelog
547 cl = repo.changelog
548 for r in getset(repo, fullreposet(repo), x):
548 for r in getset(repo, fullreposet(repo), x):
549 for i in range(n):
549 for i in range(n):
550 r = cl.parentrevs(r)[0]
550 r = cl.parentrevs(r)[0]
551 ps.add(r)
551 ps.add(r)
552 return subset & ps
552 return subset & ps
553
553
554 @predicate('author(string)', safe=True)
554 @predicate('author(string)', safe=True)
555 def author(repo, subset, x):
555 def author(repo, subset, x):
556 """Alias for ``user(string)``.
556 """Alias for ``user(string)``.
557 """
557 """
558 # i18n: "author" is a keyword
558 # i18n: "author" is a keyword
559 n = encoding.lower(getstring(x, _("author requires a string")))
559 n = encoding.lower(getstring(x, _("author requires a string")))
560 kind, pattern, matcher = _substringmatcher(n)
560 kind, pattern, matcher = _substringmatcher(n)
561 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
561 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
562 condrepr=('<user %r>', n))
562 condrepr=('<user %r>', n))
563
563
564 @predicate('bisect(string)', safe=True)
564 @predicate('bisect(string)', safe=True)
565 def bisect(repo, subset, x):
565 def bisect(repo, subset, x):
566 """Changesets marked in the specified bisect status:
566 """Changesets marked in the specified bisect status:
567
567
568 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
568 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
569 - ``goods``, ``bads`` : csets topologically good/bad
569 - ``goods``, ``bads`` : csets topologically good/bad
570 - ``range`` : csets taking part in the bisection
570 - ``range`` : csets taking part in the bisection
571 - ``pruned`` : csets that are goods, bads or skipped
571 - ``pruned`` : csets that are goods, bads or skipped
572 - ``untested`` : csets whose fate is yet unknown
572 - ``untested`` : csets whose fate is yet unknown
573 - ``ignored`` : csets ignored due to DAG topology
573 - ``ignored`` : csets ignored due to DAG topology
574 - ``current`` : the cset currently being bisected
574 - ``current`` : the cset currently being bisected
575 """
575 """
576 # i18n: "bisect" is a keyword
576 # i18n: "bisect" is a keyword
577 status = getstring(x, _("bisect requires a string")).lower()
577 status = getstring(x, _("bisect requires a string")).lower()
578 state = set(hbisect.get(repo, status))
578 state = set(hbisect.get(repo, status))
579 return subset & state
579 return subset & state
580
580
581 # Backward-compatibility
581 # Backward-compatibility
582 # - no help entry so that we do not advertise it any more
582 # - no help entry so that we do not advertise it any more
583 @predicate('bisected', safe=True)
583 @predicate('bisected', safe=True)
584 def bisected(repo, subset, x):
584 def bisected(repo, subset, x):
585 return bisect(repo, subset, x)
585 return bisect(repo, subset, x)
586
586
587 @predicate('bookmark([name])', safe=True)
587 @predicate('bookmark([name])', safe=True)
588 def bookmark(repo, subset, x):
588 def bookmark(repo, subset, x):
589 """The named bookmark or all bookmarks.
589 """The named bookmark or all bookmarks.
590
590
591 If `name` starts with `re:`, the remainder of the name is treated as
591 If `name` starts with `re:`, the remainder of the name is treated as
592 a regular expression. To match a bookmark that actually starts with `re:`,
592 a regular expression. To match a bookmark that actually starts with `re:`,
593 use the prefix `literal:`.
593 use the prefix `literal:`.
594 """
594 """
595 # i18n: "bookmark" is a keyword
595 # i18n: "bookmark" is a keyword
596 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
596 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
597 if args:
597 if args:
598 bm = getstring(args[0],
598 bm = getstring(args[0],
599 # i18n: "bookmark" is a keyword
599 # i18n: "bookmark" is a keyword
600 _('the argument to bookmark must be a string'))
600 _('the argument to bookmark must be a string'))
601 kind, pattern, matcher = util.stringmatcher(bm)
601 kind, pattern, matcher = util.stringmatcher(bm)
602 bms = set()
602 bms = set()
603 if kind == 'literal':
603 if kind == 'literal':
604 bmrev = repo._bookmarks.get(pattern, None)
604 bmrev = repo._bookmarks.get(pattern, None)
605 if not bmrev:
605 if not bmrev:
606 raise error.RepoLookupError(_("bookmark '%s' does not exist")
606 raise error.RepoLookupError(_("bookmark '%s' does not exist")
607 % pattern)
607 % pattern)
608 bms.add(repo[bmrev].rev())
608 bms.add(repo[bmrev].rev())
609 else:
609 else:
610 matchrevs = set()
610 matchrevs = set()
611 for name, bmrev in repo._bookmarks.iteritems():
611 for name, bmrev in repo._bookmarks.iteritems():
612 if matcher(name):
612 if matcher(name):
613 matchrevs.add(bmrev)
613 matchrevs.add(bmrev)
614 if not matchrevs:
614 if not matchrevs:
615 raise error.RepoLookupError(_("no bookmarks exist"
615 raise error.RepoLookupError(_("no bookmarks exist"
616 " that match '%s'") % pattern)
616 " that match '%s'") % pattern)
617 for bmrev in matchrevs:
617 for bmrev in matchrevs:
618 bms.add(repo[bmrev].rev())
618 bms.add(repo[bmrev].rev())
619 else:
619 else:
620 bms = set([repo[r].rev()
620 bms = set([repo[r].rev()
621 for r in repo._bookmarks.values()])
621 for r in repo._bookmarks.values()])
622 bms -= set([node.nullrev])
622 bms -= set([node.nullrev])
623 return subset & bms
623 return subset & bms
624
624
625 @predicate('branch(string or set)', safe=True)
625 @predicate('branch(string or set)', safe=True)
626 def branch(repo, subset, x):
626 def branch(repo, subset, x):
627 """
627 """
628 All changesets belonging to the given branch or the branches of the given
628 All changesets belonging to the given branch or the branches of the given
629 changesets.
629 changesets.
630
630
631 If `string` starts with `re:`, the remainder of the name is treated as
631 If `string` starts with `re:`, the remainder of the name is treated as
632 a regular expression. To match a branch that actually starts with `re:`,
632 a regular expression. To match a branch that actually starts with `re:`,
633 use the prefix `literal:`.
633 use the prefix `literal:`.
634 """
634 """
635 getbi = repo.revbranchcache().branchinfo
635 getbi = repo.revbranchcache().branchinfo
636
636
637 try:
637 try:
638 b = getstring(x, '')
638 b = getstring(x, '')
639 except error.ParseError:
639 except error.ParseError:
640 # not a string, but another revspec, e.g. tip()
640 # not a string, but another revspec, e.g. tip()
641 pass
641 pass
642 else:
642 else:
643 kind, pattern, matcher = util.stringmatcher(b)
643 kind, pattern, matcher = util.stringmatcher(b)
644 if kind == 'literal':
644 if kind == 'literal':
645 # note: falls through to the revspec case if no branch with
645 # note: falls through to the revspec case if no branch with
646 # this name exists and pattern kind is not specified explicitly
646 # this name exists and pattern kind is not specified explicitly
647 if pattern in repo.branchmap():
647 if pattern in repo.branchmap():
648 return subset.filter(lambda r: matcher(getbi(r)[0]),
648 return subset.filter(lambda r: matcher(getbi(r)[0]),
649 condrepr=('<branch %r>', b))
649 condrepr=('<branch %r>', b))
650 if b.startswith('literal:'):
650 if b.startswith('literal:'):
651 raise error.RepoLookupError(_("branch '%s' does not exist")
651 raise error.RepoLookupError(_("branch '%s' does not exist")
652 % pattern)
652 % pattern)
653 else:
653 else:
654 return subset.filter(lambda r: matcher(getbi(r)[0]),
654 return subset.filter(lambda r: matcher(getbi(r)[0]),
655 condrepr=('<branch %r>', b))
655 condrepr=('<branch %r>', b))
656
656
657 s = getset(repo, fullreposet(repo), x)
657 s = getset(repo, fullreposet(repo), x)
658 b = set()
658 b = set()
659 for r in s:
659 for r in s:
660 b.add(getbi(r)[0])
660 b.add(getbi(r)[0])
661 c = s.__contains__
661 c = s.__contains__
662 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
662 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
663 condrepr=lambda: '<branch %r>' % sorted(b))
663 condrepr=lambda: '<branch %r>' % sorted(b))
664
664
665 @predicate('bumped()', safe=True)
665 @predicate('bumped()', safe=True)
666 def bumped(repo, subset, x):
666 def bumped(repo, subset, x):
667 """Mutable changesets marked as successors of public changesets.
667 """Mutable changesets marked as successors of public changesets.
668
668
669 Only non-public and non-obsolete changesets can be `bumped`.
669 Only non-public and non-obsolete changesets can be `bumped`.
670 """
670 """
671 # i18n: "bumped" is a keyword
671 # i18n: "bumped" is a keyword
672 getargs(x, 0, 0, _("bumped takes no arguments"))
672 getargs(x, 0, 0, _("bumped takes no arguments"))
673 bumped = obsmod.getrevs(repo, 'bumped')
673 bumped = obsmod.getrevs(repo, 'bumped')
674 return subset & bumped
674 return subset & bumped
675
675
676 @predicate('bundle()', safe=True)
676 @predicate('bundle()', safe=True)
677 def bundle(repo, subset, x):
677 def bundle(repo, subset, x):
678 """Changesets in the bundle.
678 """Changesets in the bundle.
679
679
680 Bundle must be specified by the -R option."""
680 Bundle must be specified by the -R option."""
681
681
682 try:
682 try:
683 bundlerevs = repo.changelog.bundlerevs
683 bundlerevs = repo.changelog.bundlerevs
684 except AttributeError:
684 except AttributeError:
685 raise error.Abort(_("no bundle provided - specify with -R"))
685 raise error.Abort(_("no bundle provided - specify with -R"))
686 return subset & bundlerevs
686 return subset & bundlerevs
687
687
688 def checkstatus(repo, subset, pat, field):
688 def checkstatus(repo, subset, pat, field):
689 hasset = matchmod.patkind(pat) == 'set'
689 hasset = matchmod.patkind(pat) == 'set'
690
690
691 mcache = [None]
691 mcache = [None]
692 def matches(x):
692 def matches(x):
693 c = repo[x]
693 c = repo[x]
694 if not mcache[0] or hasset:
694 if not mcache[0] or hasset:
695 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
695 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
696 m = mcache[0]
696 m = mcache[0]
697 fname = None
697 fname = None
698 if not m.anypats() and len(m.files()) == 1:
698 if not m.anypats() and len(m.files()) == 1:
699 fname = m.files()[0]
699 fname = m.files()[0]
700 if fname is not None:
700 if fname is not None:
701 if fname not in c.files():
701 if fname not in c.files():
702 return False
702 return False
703 else:
703 else:
704 for f in c.files():
704 for f in c.files():
705 if m(f):
705 if m(f):
706 break
706 break
707 else:
707 else:
708 return False
708 return False
709 files = repo.status(c.p1().node(), c.node())[field]
709 files = repo.status(c.p1().node(), c.node())[field]
710 if fname is not None:
710 if fname is not None:
711 if fname in files:
711 if fname in files:
712 return True
712 return True
713 else:
713 else:
714 for f in files:
714 for f in files:
715 if m(f):
715 if m(f):
716 return True
716 return True
717
717
718 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
718 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
719
719
720 def _children(repo, subset, parentset):
720 def _children(repo, subset, parentset):
721 if not parentset:
721 if not parentset:
722 return baseset()
722 return baseset()
723 cs = set()
723 cs = set()
724 pr = repo.changelog.parentrevs
724 pr = repo.changelog.parentrevs
725 minrev = parentset.min()
725 minrev = parentset.min()
726 for r in subset:
726 for r in subset:
727 if r <= minrev:
727 if r <= minrev:
728 continue
728 continue
729 for p in pr(r):
729 for p in pr(r):
730 if p in parentset:
730 if p in parentset:
731 cs.add(r)
731 cs.add(r)
732 return baseset(cs)
732 return baseset(cs)
733
733
734 @predicate('children(set)', safe=True)
734 @predicate('children(set)', safe=True)
735 def children(repo, subset, x):
735 def children(repo, subset, x):
736 """Child changesets of changesets in set.
736 """Child changesets of changesets in set.
737 """
737 """
738 s = getset(repo, fullreposet(repo), x)
738 s = getset(repo, fullreposet(repo), x)
739 cs = _children(repo, subset, s)
739 cs = _children(repo, subset, s)
740 return subset & cs
740 return subset & cs
741
741
742 @predicate('closed()', safe=True)
742 @predicate('closed()', safe=True)
743 def closed(repo, subset, x):
743 def closed(repo, subset, x):
744 """Changeset is closed.
744 """Changeset is closed.
745 """
745 """
746 # i18n: "closed" is a keyword
746 # i18n: "closed" is a keyword
747 getargs(x, 0, 0, _("closed takes no arguments"))
747 getargs(x, 0, 0, _("closed takes no arguments"))
748 return subset.filter(lambda r: repo[r].closesbranch(),
748 return subset.filter(lambda r: repo[r].closesbranch(),
749 condrepr='<branch closed>')
749 condrepr='<branch closed>')
750
750
751 @predicate('contains(pattern)')
751 @predicate('contains(pattern)')
752 def contains(repo, subset, x):
752 def contains(repo, subset, x):
753 """The revision's manifest contains a file matching pattern (but might not
753 """The revision's manifest contains a file matching pattern (but might not
754 modify it). See :hg:`help patterns` for information about file patterns.
754 modify it). See :hg:`help patterns` for information about file patterns.
755
755
756 The pattern without explicit kind like ``glob:`` is expected to be
756 The pattern without explicit kind like ``glob:`` is expected to be
757 relative to the current directory and match against a file exactly
757 relative to the current directory and match against a file exactly
758 for efficiency.
758 for efficiency.
759 """
759 """
760 # i18n: "contains" is a keyword
760 # i18n: "contains" is a keyword
761 pat = getstring(x, _("contains requires a pattern"))
761 pat = getstring(x, _("contains requires a pattern"))
762
762
763 def matches(x):
763 def matches(x):
764 if not matchmod.patkind(pat):
764 if not matchmod.patkind(pat):
765 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
765 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
766 if pats in repo[x]:
766 if pats in repo[x]:
767 return True
767 return True
768 else:
768 else:
769 c = repo[x]
769 c = repo[x]
770 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
770 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
771 for f in c.manifest():
771 for f in c.manifest():
772 if m(f):
772 if m(f):
773 return True
773 return True
774 return False
774 return False
775
775
776 return subset.filter(matches, condrepr=('<contains %r>', pat))
776 return subset.filter(matches, condrepr=('<contains %r>', pat))
777
777
778 @predicate('converted([id])', safe=True)
778 @predicate('converted([id])', safe=True)
779 def converted(repo, subset, x):
779 def converted(repo, subset, x):
780 """Changesets converted from the given identifier in the old repository if
780 """Changesets converted from the given identifier in the old repository if
781 present, or all converted changesets if no identifier is specified.
781 present, or all converted changesets if no identifier is specified.
782 """
782 """
783
783
784 # There is exactly no chance of resolving the revision, so do a simple
784 # There is exactly no chance of resolving the revision, so do a simple
785 # string compare and hope for the best
785 # string compare and hope for the best
786
786
787 rev = None
787 rev = None
788 # i18n: "converted" is a keyword
788 # i18n: "converted" is a keyword
789 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
789 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
790 if l:
790 if l:
791 # i18n: "converted" is a keyword
791 # i18n: "converted" is a keyword
792 rev = getstring(l[0], _('converted requires a revision'))
792 rev = getstring(l[0], _('converted requires a revision'))
793
793
794 def _matchvalue(r):
794 def _matchvalue(r):
795 source = repo[r].extra().get('convert_revision', None)
795 source = repo[r].extra().get('convert_revision', None)
796 return source is not None and (rev is None or source.startswith(rev))
796 return source is not None and (rev is None or source.startswith(rev))
797
797
798 return subset.filter(lambda r: _matchvalue(r),
798 return subset.filter(lambda r: _matchvalue(r),
799 condrepr=('<converted %r>', rev))
799 condrepr=('<converted %r>', rev))
800
800
801 @predicate('date(interval)', safe=True)
801 @predicate('date(interval)', safe=True)
802 def date(repo, subset, x):
802 def date(repo, subset, x):
803 """Changesets within the interval, see :hg:`help dates`.
803 """Changesets within the interval, see :hg:`help dates`.
804 """
804 """
805 # i18n: "date" is a keyword
805 # i18n: "date" is a keyword
806 ds = getstring(x, _("date requires a string"))
806 ds = getstring(x, _("date requires a string"))
807 dm = util.matchdate(ds)
807 dm = util.matchdate(ds)
808 return subset.filter(lambda x: dm(repo[x].date()[0]),
808 return subset.filter(lambda x: dm(repo[x].date()[0]),
809 condrepr=('<date %r>', ds))
809 condrepr=('<date %r>', ds))
810
810
811 @predicate('desc(string)', safe=True)
811 @predicate('desc(string)', safe=True)
812 def desc(repo, subset, x):
812 def desc(repo, subset, x):
813 """Search commit message for string. The match is case-insensitive.
813 """Search commit message for string. The match is case-insensitive.
814 """
814 """
815 # i18n: "desc" is a keyword
815 # i18n: "desc" is a keyword
816 ds = encoding.lower(getstring(x, _("desc requires a string")))
816 ds = encoding.lower(getstring(x, _("desc requires a string")))
817
817
818 def matches(x):
818 def matches(x):
819 c = repo[x]
819 c = repo[x]
820 return ds in encoding.lower(c.description())
820 return ds in encoding.lower(c.description())
821
821
822 return subset.filter(matches, condrepr=('<desc %r>', ds))
822 return subset.filter(matches, condrepr=('<desc %r>', ds))
823
823
824 def _descendants(repo, subset, x, followfirst=False):
824 def _descendants(repo, subset, x, followfirst=False):
825 roots = getset(repo, fullreposet(repo), x)
825 roots = getset(repo, fullreposet(repo), x)
826 if not roots:
826 if not roots:
827 return baseset()
827 return baseset()
828 s = _revdescendants(repo, roots, followfirst)
828 s = _revdescendants(repo, roots, followfirst)
829
829
830 # Both sets need to be ascending in order to lazily return the union
830 # Both sets need to be ascending in order to lazily return the union
831 # in the correct order.
831 # in the correct order.
832 base = subset & roots
832 base = subset & roots
833 desc = subset & s
833 desc = subset & s
834 result = base + desc
834 result = base + desc
835 if subset.isascending():
835 if subset.isascending():
836 result.sort()
836 result.sort()
837 elif subset.isdescending():
837 elif subset.isdescending():
838 result.sort(reverse=True)
838 result.sort(reverse=True)
839 else:
839 else:
840 result = subset & result
840 result = subset & result
841 return result
841 return result
842
842
843 @predicate('descendants(set)', safe=True)
843 @predicate('descendants(set)', safe=True)
844 def descendants(repo, subset, x):
844 def descendants(repo, subset, x):
845 """Changesets which are descendants of changesets in set.
845 """Changesets which are descendants of changesets in set.
846 """
846 """
847 return _descendants(repo, subset, x)
847 return _descendants(repo, subset, x)
848
848
849 @predicate('_firstdescendants', safe=True)
849 @predicate('_firstdescendants', safe=True)
850 def _firstdescendants(repo, subset, x):
850 def _firstdescendants(repo, subset, x):
851 # ``_firstdescendants(set)``
851 # ``_firstdescendants(set)``
852 # Like ``descendants(set)`` but follows only the first parents.
852 # Like ``descendants(set)`` but follows only the first parents.
853 return _descendants(repo, subset, x, followfirst=True)
853 return _descendants(repo, subset, x, followfirst=True)
854
854
855 @predicate('destination([set])', safe=True)
855 @predicate('destination([set])', safe=True)
856 def destination(repo, subset, x):
856 def destination(repo, subset, x):
857 """Changesets that were created by a graft, transplant or rebase operation,
857 """Changesets that were created by a graft, transplant or rebase operation,
858 with the given revisions specified as the source. Omitting the optional set
858 with the given revisions specified as the source. Omitting the optional set
859 is the same as passing all().
859 is the same as passing all().
860 """
860 """
861 if x is not None:
861 if x is not None:
862 sources = getset(repo, fullreposet(repo), x)
862 sources = getset(repo, fullreposet(repo), x)
863 else:
863 else:
864 sources = fullreposet(repo)
864 sources = fullreposet(repo)
865
865
866 dests = set()
866 dests = set()
867
867
868 # subset contains all of the possible destinations that can be returned, so
868 # subset contains all of the possible destinations that can be returned, so
869 # iterate over them and see if their source(s) were provided in the arg set.
869 # iterate over them and see if their source(s) were provided in the arg set.
870 # Even if the immediate src of r is not in the arg set, src's source (or
870 # Even if the immediate src of r is not in the arg set, src's source (or
871 # further back) may be. Scanning back further than the immediate src allows
871 # further back) may be. Scanning back further than the immediate src allows
872 # transitive transplants and rebases to yield the same results as transitive
872 # transitive transplants and rebases to yield the same results as transitive
873 # grafts.
873 # grafts.
874 for r in subset:
874 for r in subset:
875 src = _getrevsource(repo, r)
875 src = _getrevsource(repo, r)
876 lineage = None
876 lineage = None
877
877
878 while src is not None:
878 while src is not None:
879 if lineage is None:
879 if lineage is None:
880 lineage = list()
880 lineage = list()
881
881
882 lineage.append(r)
882 lineage.append(r)
883
883
884 # The visited lineage is a match if the current source is in the arg
884 # The visited lineage is a match if the current source is in the arg
885 # set. Since every candidate dest is visited by way of iterating
885 # set. Since every candidate dest is visited by way of iterating
886 # subset, any dests further back in the lineage will be tested by a
886 # subset, any dests further back in the lineage will be tested by a
887 # different iteration over subset. Likewise, if the src was already
887 # different iteration over subset. Likewise, if the src was already
888 # selected, the current lineage can be selected without going back
888 # selected, the current lineage can be selected without going back
889 # further.
889 # further.
890 if src in sources or src in dests:
890 if src in sources or src in dests:
891 dests.update(lineage)
891 dests.update(lineage)
892 break
892 break
893
893
894 r = src
894 r = src
895 src = _getrevsource(repo, r)
895 src = _getrevsource(repo, r)
896
896
897 return subset.filter(dests.__contains__,
897 return subset.filter(dests.__contains__,
898 condrepr=lambda: '<destination %r>' % sorted(dests))
898 condrepr=lambda: '<destination %r>' % sorted(dests))
899
899
900 @predicate('divergent()', safe=True)
900 @predicate('divergent()', safe=True)
901 def divergent(repo, subset, x):
901 def divergent(repo, subset, x):
902 """
902 """
903 Final successors of changesets with an alternative set of final successors.
903 Final successors of changesets with an alternative set of final successors.
904 """
904 """
905 # i18n: "divergent" is a keyword
905 # i18n: "divergent" is a keyword
906 getargs(x, 0, 0, _("divergent takes no arguments"))
906 getargs(x, 0, 0, _("divergent takes no arguments"))
907 divergent = obsmod.getrevs(repo, 'divergent')
907 divergent = obsmod.getrevs(repo, 'divergent')
908 return subset & divergent
908 return subset & divergent
909
909
910 @predicate('extinct()', safe=True)
910 @predicate('extinct()', safe=True)
911 def extinct(repo, subset, x):
911 def extinct(repo, subset, x):
912 """Obsolete changesets with obsolete descendants only.
912 """Obsolete changesets with obsolete descendants only.
913 """
913 """
914 # i18n: "extinct" is a keyword
914 # i18n: "extinct" is a keyword
915 getargs(x, 0, 0, _("extinct takes no arguments"))
915 getargs(x, 0, 0, _("extinct takes no arguments"))
916 extincts = obsmod.getrevs(repo, 'extinct')
916 extincts = obsmod.getrevs(repo, 'extinct')
917 return subset & extincts
917 return subset & extincts
918
918
919 @predicate('extra(label, [value])', safe=True)
919 @predicate('extra(label, [value])', safe=True)
920 def extra(repo, subset, x):
920 def extra(repo, subset, x):
921 """Changesets with the given label in the extra metadata, with the given
921 """Changesets with the given label in the extra metadata, with the given
922 optional value.
922 optional value.
923
923
924 If `value` starts with `re:`, the remainder of the value is treated as
924 If `value` starts with `re:`, the remainder of the value is treated as
925 a regular expression. To match a value that actually starts with `re:`,
925 a regular expression. To match a value that actually starts with `re:`,
926 use the prefix `literal:`.
926 use the prefix `literal:`.
927 """
927 """
928 args = getargsdict(x, 'extra', 'label value')
928 args = getargsdict(x, 'extra', 'label value')
929 if 'label' not in args:
929 if 'label' not in args:
930 # i18n: "extra" is a keyword
930 # i18n: "extra" is a keyword
931 raise error.ParseError(_('extra takes at least 1 argument'))
931 raise error.ParseError(_('extra takes at least 1 argument'))
932 # i18n: "extra" is a keyword
932 # i18n: "extra" is a keyword
933 label = getstring(args['label'], _('first argument to extra must be '
933 label = getstring(args['label'], _('first argument to extra must be '
934 'a string'))
934 'a string'))
935 value = None
935 value = None
936
936
937 if 'value' in args:
937 if 'value' in args:
938 # i18n: "extra" is a keyword
938 # i18n: "extra" is a keyword
939 value = getstring(args['value'], _('second argument to extra must be '
939 value = getstring(args['value'], _('second argument to extra must be '
940 'a string'))
940 'a string'))
941 kind, value, matcher = util.stringmatcher(value)
941 kind, value, matcher = util.stringmatcher(value)
942
942
943 def _matchvalue(r):
943 def _matchvalue(r):
944 extra = repo[r].extra()
944 extra = repo[r].extra()
945 return label in extra and (value is None or matcher(extra[label]))
945 return label in extra and (value is None or matcher(extra[label]))
946
946
947 return subset.filter(lambda r: _matchvalue(r),
947 return subset.filter(lambda r: _matchvalue(r),
948 condrepr=('<extra[%r] %r>', label, value))
948 condrepr=('<extra[%r] %r>', label, value))
949
949
950 @predicate('filelog(pattern)', safe=True)
950 @predicate('filelog(pattern)', safe=True)
951 def filelog(repo, subset, x):
951 def filelog(repo, subset, x):
952 """Changesets connected to the specified filelog.
952 """Changesets connected to the specified filelog.
953
953
954 For performance reasons, visits only revisions mentioned in the file-level
954 For performance reasons, visits only revisions mentioned in the file-level
955 filelog, rather than filtering through all changesets (much faster, but
955 filelog, rather than filtering through all changesets (much faster, but
956 doesn't include deletes or duplicate changes). For a slower, more accurate
956 doesn't include deletes or duplicate changes). For a slower, more accurate
957 result, use ``file()``.
957 result, use ``file()``.
958
958
959 The pattern without explicit kind like ``glob:`` is expected to be
959 The pattern without explicit kind like ``glob:`` is expected to be
960 relative to the current directory and match against a file exactly
960 relative to the current directory and match against a file exactly
961 for efficiency.
961 for efficiency.
962
962
963 If some linkrev points to revisions filtered by the current repoview, we'll
963 If some linkrev points to revisions filtered by the current repoview, we'll
964 work around it to return a non-filtered value.
964 work around it to return a non-filtered value.
965 """
965 """
966
966
967 # i18n: "filelog" is a keyword
967 # i18n: "filelog" is a keyword
968 pat = getstring(x, _("filelog requires a pattern"))
968 pat = getstring(x, _("filelog requires a pattern"))
969 s = set()
969 s = set()
970 cl = repo.changelog
970 cl = repo.changelog
971
971
972 if not matchmod.patkind(pat):
972 if not matchmod.patkind(pat):
973 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
973 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
974 files = [f]
974 files = [f]
975 else:
975 else:
976 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
976 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
977 files = (f for f in repo[None] if m(f))
977 files = (f for f in repo[None] if m(f))
978
978
979 for f in files:
979 for f in files:
980 fl = repo.file(f)
980 fl = repo.file(f)
981 known = {}
981 known = {}
982 scanpos = 0
982 scanpos = 0
983 for fr in list(fl):
983 for fr in list(fl):
984 fn = fl.node(fr)
984 fn = fl.node(fr)
985 if fn in known:
985 if fn in known:
986 s.add(known[fn])
986 s.add(known[fn])
987 continue
987 continue
988
988
989 lr = fl.linkrev(fr)
989 lr = fl.linkrev(fr)
990 if lr in cl:
990 if lr in cl:
991 s.add(lr)
991 s.add(lr)
992 elif scanpos is not None:
992 elif scanpos is not None:
993 # lowest matching changeset is filtered, scan further
993 # lowest matching changeset is filtered, scan further
994 # ahead in changelog
994 # ahead in changelog
995 start = max(lr, scanpos) + 1
995 start = max(lr, scanpos) + 1
996 scanpos = None
996 scanpos = None
997 for r in cl.revs(start):
997 for r in cl.revs(start):
998 # minimize parsing of non-matching entries
998 # minimize parsing of non-matching entries
999 if f in cl.revision(r) and f in cl.readfiles(r):
999 if f in cl.revision(r) and f in cl.readfiles(r):
1000 try:
1000 try:
1001 # try to use manifest delta fastpath
1001 # try to use manifest delta fastpath
1002 n = repo[r].filenode(f)
1002 n = repo[r].filenode(f)
1003 if n not in known:
1003 if n not in known:
1004 if n == fn:
1004 if n == fn:
1005 s.add(r)
1005 s.add(r)
1006 scanpos = r
1006 scanpos = r
1007 break
1007 break
1008 else:
1008 else:
1009 known[n] = r
1009 known[n] = r
1010 except error.ManifestLookupError:
1010 except error.ManifestLookupError:
1011 # deletion in changelog
1011 # deletion in changelog
1012 continue
1012 continue
1013
1013
1014 return subset & s
1014 return subset & s
1015
1015
1016 @predicate('first(set, [n])', safe=True)
1016 @predicate('first(set, [n])', safe=True)
1017 def first(repo, subset, x):
1017 def first(repo, subset, x):
1018 """An alias for limit().
1018 """An alias for limit().
1019 """
1019 """
1020 return limit(repo, subset, x)
1020 return limit(repo, subset, x)
1021
1021
1022 def _follow(repo, subset, x, name, followfirst=False):
1022 def _follow(repo, subset, x, name, followfirst=False):
1023 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1023 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1024 "and an optional revset") % name)
1024 "and an optional revset") % name)
1025 c = repo['.']
1025 c = repo['.']
1026 if l:
1026 if l:
1027 x = getstring(l[0], _("%s expected a pattern") % name)
1027 x = getstring(l[0], _("%s expected a pattern") % name)
1028 rev = None
1028 rev = None
1029 if len(l) >= 2:
1029 if len(l) >= 2:
1030 revs = getset(repo, fullreposet(repo), l[1])
1030 revs = getset(repo, fullreposet(repo), l[1])
1031 if len(revs) != 1:
1031 if len(revs) != 1:
1032 raise error.RepoLookupError(
1032 raise error.RepoLookupError(
1033 _("%s expected one starting revision") % name)
1033 _("%s expected one starting revision") % name)
1034 rev = revs.last()
1034 rev = revs.last()
1035 c = repo[rev]
1035 c = repo[rev]
1036 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1036 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1037 ctx=repo[rev], default='path')
1037 ctx=repo[rev], default='path')
1038
1038
1039 files = c.manifest().walk(matcher)
1039 files = c.manifest().walk(matcher)
1040
1040
1041 s = set()
1041 s = set()
1042 for fname in files:
1042 for fname in files:
1043 fctx = c[fname]
1043 fctx = c[fname]
1044 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1044 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1045 # include the revision responsible for the most recent version
1045 # include the revision responsible for the most recent version
1046 s.add(fctx.introrev())
1046 s.add(fctx.introrev())
1047 else:
1047 else:
1048 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1048 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1049
1049
1050 return subset & s
1050 return subset & s
1051
1051
1052 @predicate('follow([pattern[, startrev]])', safe=True)
1052 @predicate('follow([pattern[, startrev]])', safe=True)
1053 def follow(repo, subset, x):
1053 def follow(repo, subset, x):
1054 """
1054 """
1055 An alias for ``::.`` (ancestors of the working directory's first parent).
1055 An alias for ``::.`` (ancestors of the working directory's first parent).
1056 If pattern is specified, the histories of files matching given
1056 If pattern is specified, the histories of files matching given
1057 pattern in the revision given by startrev are followed, including copies.
1057 pattern in the revision given by startrev are followed, including copies.
1058 """
1058 """
1059 return _follow(repo, subset, x, 'follow')
1059 return _follow(repo, subset, x, 'follow')
1060
1060
1061 @predicate('_followfirst', safe=True)
1061 @predicate('_followfirst', safe=True)
1062 def _followfirst(repo, subset, x):
1062 def _followfirst(repo, subset, x):
1063 # ``followfirst([pattern[, startrev]])``
1063 # ``followfirst([pattern[, startrev]])``
1064 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1064 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1065 # of every revisions or files revisions.
1065 # of every revisions or files revisions.
1066 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1066 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1067
1067
1068 @predicate('all()', safe=True)
1068 @predicate('all()', safe=True)
1069 def getall(repo, subset, x):
1069 def getall(repo, subset, x):
1070 """All changesets, the same as ``0:tip``.
1070 """All changesets, the same as ``0:tip``.
1071 """
1071 """
1072 # i18n: "all" is a keyword
1072 # i18n: "all" is a keyword
1073 getargs(x, 0, 0, _("all takes no arguments"))
1073 getargs(x, 0, 0, _("all takes no arguments"))
1074 return subset & spanset(repo) # drop "null" if any
1074 return subset & spanset(repo) # drop "null" if any
1075
1075
1076 @predicate('grep(regex)')
1076 @predicate('grep(regex)')
1077 def grep(repo, subset, x):
1077 def grep(repo, subset, x):
1078 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1078 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1079 to ensure special escape characters are handled correctly. Unlike
1079 to ensure special escape characters are handled correctly. Unlike
1080 ``keyword(string)``, the match is case-sensitive.
1080 ``keyword(string)``, the match is case-sensitive.
1081 """
1081 """
1082 try:
1082 try:
1083 # i18n: "grep" is a keyword
1083 # i18n: "grep" is a keyword
1084 gr = re.compile(getstring(x, _("grep requires a string")))
1084 gr = re.compile(getstring(x, _("grep requires a string")))
1085 except re.error as e:
1085 except re.error as e:
1086 raise error.ParseError(_('invalid match pattern: %s') % e)
1086 raise error.ParseError(_('invalid match pattern: %s') % e)
1087
1087
1088 def matches(x):
1088 def matches(x):
1089 c = repo[x]
1089 c = repo[x]
1090 for e in c.files() + [c.user(), c.description()]:
1090 for e in c.files() + [c.user(), c.description()]:
1091 if gr.search(e):
1091 if gr.search(e):
1092 return True
1092 return True
1093 return False
1093 return False
1094
1094
1095 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1095 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1096
1096
1097 @predicate('_matchfiles', safe=True)
1097 @predicate('_matchfiles', safe=True)
1098 def _matchfiles(repo, subset, x):
1098 def _matchfiles(repo, subset, x):
1099 # _matchfiles takes a revset list of prefixed arguments:
1099 # _matchfiles takes a revset list of prefixed arguments:
1100 #
1100 #
1101 # [p:foo, i:bar, x:baz]
1101 # [p:foo, i:bar, x:baz]
1102 #
1102 #
1103 # builds a match object from them and filters subset. Allowed
1103 # builds a match object from them and filters subset. Allowed
1104 # prefixes are 'p:' for regular patterns, 'i:' for include
1104 # prefixes are 'p:' for regular patterns, 'i:' for include
1105 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1105 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1106 # a revision identifier, or the empty string to reference the
1106 # a revision identifier, or the empty string to reference the
1107 # working directory, from which the match object is
1107 # working directory, from which the match object is
1108 # initialized. Use 'd:' to set the default matching mode, default
1108 # initialized. Use 'd:' to set the default matching mode, default
1109 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1109 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1110
1110
1111 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1111 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1112 pats, inc, exc = [], [], []
1112 pats, inc, exc = [], [], []
1113 rev, default = None, None
1113 rev, default = None, None
1114 for arg in l:
1114 for arg in l:
1115 s = getstring(arg, "_matchfiles requires string arguments")
1115 s = getstring(arg, "_matchfiles requires string arguments")
1116 prefix, value = s[:2], s[2:]
1116 prefix, value = s[:2], s[2:]
1117 if prefix == 'p:':
1117 if prefix == 'p:':
1118 pats.append(value)
1118 pats.append(value)
1119 elif prefix == 'i:':
1119 elif prefix == 'i:':
1120 inc.append(value)
1120 inc.append(value)
1121 elif prefix == 'x:':
1121 elif prefix == 'x:':
1122 exc.append(value)
1122 exc.append(value)
1123 elif prefix == 'r:':
1123 elif prefix == 'r:':
1124 if rev is not None:
1124 if rev is not None:
1125 raise error.ParseError('_matchfiles expected at most one '
1125 raise error.ParseError('_matchfiles expected at most one '
1126 'revision')
1126 'revision')
1127 if value != '': # empty means working directory; leave rev as None
1127 if value != '': # empty means working directory; leave rev as None
1128 rev = value
1128 rev = value
1129 elif prefix == 'd:':
1129 elif prefix == 'd:':
1130 if default is not None:
1130 if default is not None:
1131 raise error.ParseError('_matchfiles expected at most one '
1131 raise error.ParseError('_matchfiles expected at most one '
1132 'default mode')
1132 'default mode')
1133 default = value
1133 default = value
1134 else:
1134 else:
1135 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1135 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1136 if not default:
1136 if not default:
1137 default = 'glob'
1137 default = 'glob'
1138
1138
1139 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1139 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1140 exclude=exc, ctx=repo[rev], default=default)
1140 exclude=exc, ctx=repo[rev], default=default)
1141
1141
1142 # This directly read the changelog data as creating changectx for all
1142 # This directly read the changelog data as creating changectx for all
1143 # revisions is quite expensive.
1143 # revisions is quite expensive.
1144 getfiles = repo.changelog.readfiles
1144 getfiles = repo.changelog.readfiles
1145 wdirrev = node.wdirrev
1145 wdirrev = node.wdirrev
1146 def matches(x):
1146 def matches(x):
1147 if x == wdirrev:
1147 if x == wdirrev:
1148 files = repo[x].files()
1148 files = repo[x].files()
1149 else:
1149 else:
1150 files = getfiles(x)
1150 files = getfiles(x)
1151 for f in files:
1151 for f in files:
1152 if m(f):
1152 if m(f):
1153 return True
1153 return True
1154 return False
1154 return False
1155
1155
1156 return subset.filter(matches,
1156 return subset.filter(matches,
1157 condrepr=('<matchfiles patterns=%r, include=%r '
1157 condrepr=('<matchfiles patterns=%r, include=%r '
1158 'exclude=%r, default=%r, rev=%r>',
1158 'exclude=%r, default=%r, rev=%r>',
1159 pats, inc, exc, default, rev))
1159 pats, inc, exc, default, rev))
1160
1160
1161 @predicate('file(pattern)', safe=True)
1161 @predicate('file(pattern)', safe=True)
1162 def hasfile(repo, subset, x):
1162 def hasfile(repo, subset, x):
1163 """Changesets affecting files matched by pattern.
1163 """Changesets affecting files matched by pattern.
1164
1164
1165 For a faster but less accurate result, consider using ``filelog()``
1165 For a faster but less accurate result, consider using ``filelog()``
1166 instead.
1166 instead.
1167
1167
1168 This predicate uses ``glob:`` as the default kind of pattern.
1168 This predicate uses ``glob:`` as the default kind of pattern.
1169 """
1169 """
1170 # i18n: "file" is a keyword
1170 # i18n: "file" is a keyword
1171 pat = getstring(x, _("file requires a pattern"))
1171 pat = getstring(x, _("file requires a pattern"))
1172 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1172 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1173
1173
1174 @predicate('head()', safe=True)
1174 @predicate('head()', safe=True)
1175 def head(repo, subset, x):
1175 def head(repo, subset, x):
1176 """Changeset is a named branch head.
1176 """Changeset is a named branch head.
1177 """
1177 """
1178 # i18n: "head" is a keyword
1178 # i18n: "head" is a keyword
1179 getargs(x, 0, 0, _("head takes no arguments"))
1179 getargs(x, 0, 0, _("head takes no arguments"))
1180 hs = set()
1180 hs = set()
1181 cl = repo.changelog
1181 cl = repo.changelog
1182 for ls in repo.branchmap().itervalues():
1182 for ls in repo.branchmap().itervalues():
1183 hs.update(cl.rev(h) for h in ls)
1183 hs.update(cl.rev(h) for h in ls)
1184 return subset & baseset(hs)
1184 return subset & baseset(hs)
1185
1185
1186 @predicate('heads(set)', safe=True)
1186 @predicate('heads(set)', safe=True)
1187 def heads(repo, subset, x):
1187 def heads(repo, subset, x):
1188 """Members of set with no children in set.
1188 """Members of set with no children in set.
1189 """
1189 """
1190 s = getset(repo, subset, x)
1190 s = getset(repo, subset, x)
1191 ps = parents(repo, subset, x)
1191 ps = parents(repo, subset, x)
1192 return s - ps
1192 return s - ps
1193
1193
1194 @predicate('hidden()', safe=True)
1194 @predicate('hidden()', safe=True)
1195 def hidden(repo, subset, x):
1195 def hidden(repo, subset, x):
1196 """Hidden changesets.
1196 """Hidden changesets.
1197 """
1197 """
1198 # i18n: "hidden" is a keyword
1198 # i18n: "hidden" is a keyword
1199 getargs(x, 0, 0, _("hidden takes no arguments"))
1199 getargs(x, 0, 0, _("hidden takes no arguments"))
1200 hiddenrevs = repoview.filterrevs(repo, 'visible')
1200 hiddenrevs = repoview.filterrevs(repo, 'visible')
1201 return subset & hiddenrevs
1201 return subset & hiddenrevs
1202
1202
1203 @predicate('keyword(string)', safe=True)
1203 @predicate('keyword(string)', safe=True)
1204 def keyword(repo, subset, x):
1204 def keyword(repo, subset, x):
1205 """Search commit message, user name, and names of changed files for
1205 """Search commit message, user name, and names of changed files for
1206 string. The match is case-insensitive.
1206 string. The match is case-insensitive.
1207 """
1207 """
1208 # i18n: "keyword" is a keyword
1208 # i18n: "keyword" is a keyword
1209 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1209 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1210
1210
1211 def matches(r):
1211 def matches(r):
1212 c = repo[r]
1212 c = repo[r]
1213 return any(kw in encoding.lower(t)
1213 return any(kw in encoding.lower(t)
1214 for t in c.files() + [c.user(), c.description()])
1214 for t in c.files() + [c.user(), c.description()])
1215
1215
1216 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1216 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1217
1217
1218 @predicate('limit(set[, n[, offset]])', safe=True)
1218 @predicate('limit(set[, n[, offset]])', safe=True)
1219 def limit(repo, subset, x):
1219 def limit(repo, subset, x):
1220 """First n members of set, defaulting to 1, starting from offset.
1220 """First n members of set, defaulting to 1, starting from offset.
1221 """
1221 """
1222 args = getargsdict(x, 'limit', 'set n offset')
1222 args = getargsdict(x, 'limit', 'set n offset')
1223 if 'set' not in args:
1223 if 'set' not in args:
1224 # i18n: "limit" is a keyword
1224 # i18n: "limit" is a keyword
1225 raise error.ParseError(_("limit requires one to three arguments"))
1225 raise error.ParseError(_("limit requires one to three arguments"))
1226 try:
1226 try:
1227 lim, ofs = 1, 0
1227 lim, ofs = 1, 0
1228 if 'n' in args:
1228 if 'n' in args:
1229 # i18n: "limit" is a keyword
1229 # i18n: "limit" is a keyword
1230 lim = int(getstring(args['n'], _("limit requires a number")))
1230 lim = int(getstring(args['n'], _("limit requires a number")))
1231 if 'offset' in args:
1231 if 'offset' in args:
1232 # i18n: "limit" is a keyword
1232 # i18n: "limit" is a keyword
1233 ofs = int(getstring(args['offset'], _("limit requires a number")))
1233 ofs = int(getstring(args['offset'], _("limit requires a number")))
1234 if ofs < 0:
1234 if ofs < 0:
1235 raise error.ParseError(_("negative offset"))
1235 raise error.ParseError(_("negative offset"))
1236 except (TypeError, ValueError):
1236 except (TypeError, ValueError):
1237 # i18n: "limit" is a keyword
1237 # i18n: "limit" is a keyword
1238 raise error.ParseError(_("limit expects a number"))
1238 raise error.ParseError(_("limit expects a number"))
1239 os = getset(repo, fullreposet(repo), args['set'])
1239 os = getset(repo, fullreposet(repo), args['set'])
1240 result = []
1240 result = []
1241 it = iter(os)
1241 it = iter(os)
1242 for x in xrange(ofs):
1242 for x in xrange(ofs):
1243 y = next(it, None)
1243 y = next(it, None)
1244 if y is None:
1244 if y is None:
1245 break
1245 break
1246 for x in xrange(lim):
1246 for x in xrange(lim):
1247 y = next(it, None)
1247 y = next(it, None)
1248 if y is None:
1248 if y is None:
1249 break
1249 break
1250 elif y in subset:
1250 elif y in subset:
1251 result.append(y)
1251 result.append(y)
1252 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1252 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1253 lim, ofs, subset, os))
1253 lim, ofs, subset, os))
1254
1254
1255 @predicate('last(set, [n])', safe=True)
1255 @predicate('last(set, [n])', safe=True)
1256 def last(repo, subset, x):
1256 def last(repo, subset, x):
1257 """Last n members of set, defaulting to 1.
1257 """Last n members of set, defaulting to 1.
1258 """
1258 """
1259 # i18n: "last" is a keyword
1259 # i18n: "last" is a keyword
1260 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1260 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1261 try:
1261 try:
1262 lim = 1
1262 lim = 1
1263 if len(l) == 2:
1263 if len(l) == 2:
1264 # i18n: "last" is a keyword
1264 # i18n: "last" is a keyword
1265 lim = int(getstring(l[1], _("last requires a number")))
1265 lim = int(getstring(l[1], _("last requires a number")))
1266 except (TypeError, ValueError):
1266 except (TypeError, ValueError):
1267 # i18n: "last" is a keyword
1267 # i18n: "last" is a keyword
1268 raise error.ParseError(_("last expects a number"))
1268 raise error.ParseError(_("last expects a number"))
1269 os = getset(repo, fullreposet(repo), l[0])
1269 os = getset(repo, fullreposet(repo), l[0])
1270 os.reverse()
1270 os.reverse()
1271 result = []
1271 result = []
1272 it = iter(os)
1272 it = iter(os)
1273 for x in xrange(lim):
1273 for x in xrange(lim):
1274 y = next(it, None)
1274 y = next(it, None)
1275 if y is None:
1275 if y is None:
1276 break
1276 break
1277 elif y in subset:
1277 elif y in subset:
1278 result.append(y)
1278 result.append(y)
1279 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1279 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1280
1280
1281 @predicate('max(set)', safe=True)
1281 @predicate('max(set)', safe=True)
1282 def maxrev(repo, subset, x):
1282 def maxrev(repo, subset, x):
1283 """Changeset with highest revision number in set.
1283 """Changeset with highest revision number in set.
1284 """
1284 """
1285 os = getset(repo, fullreposet(repo), x)
1285 os = getset(repo, fullreposet(repo), x)
1286 try:
1286 try:
1287 m = os.max()
1287 m = os.max()
1288 if m in subset:
1288 if m in subset:
1289 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1289 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1290 except ValueError:
1290 except ValueError:
1291 # os.max() throws a ValueError when the collection is empty.
1291 # os.max() throws a ValueError when the collection is empty.
1292 # Same as python's max().
1292 # Same as python's max().
1293 pass
1293 pass
1294 return baseset(datarepr=('<max %r, %r>', subset, os))
1294 return baseset(datarepr=('<max %r, %r>', subset, os))
1295
1295
1296 @predicate('merge()', safe=True)
1296 @predicate('merge()', safe=True)
1297 def merge(repo, subset, x):
1297 def merge(repo, subset, x):
1298 """Changeset is a merge changeset.
1298 """Changeset is a merge changeset.
1299 """
1299 """
1300 # i18n: "merge" is a keyword
1300 # i18n: "merge" is a keyword
1301 getargs(x, 0, 0, _("merge takes no arguments"))
1301 getargs(x, 0, 0, _("merge takes no arguments"))
1302 cl = repo.changelog
1302 cl = repo.changelog
1303 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1303 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1304 condrepr='<merge>')
1304 condrepr='<merge>')
1305
1305
1306 @predicate('branchpoint()', safe=True)
1306 @predicate('branchpoint()', safe=True)
1307 def branchpoint(repo, subset, x):
1307 def branchpoint(repo, subset, x):
1308 """Changesets with more than one child.
1308 """Changesets with more than one child.
1309 """
1309 """
1310 # i18n: "branchpoint" is a keyword
1310 # i18n: "branchpoint" is a keyword
1311 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1311 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1312 cl = repo.changelog
1312 cl = repo.changelog
1313 if not subset:
1313 if not subset:
1314 return baseset()
1314 return baseset()
1315 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1315 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1316 # (and if it is not, it should.)
1316 # (and if it is not, it should.)
1317 baserev = min(subset)
1317 baserev = min(subset)
1318 parentscount = [0]*(len(repo) - baserev)
1318 parentscount = [0]*(len(repo) - baserev)
1319 for r in cl.revs(start=baserev + 1):
1319 for r in cl.revs(start=baserev + 1):
1320 for p in cl.parentrevs(r):
1320 for p in cl.parentrevs(r):
1321 if p >= baserev:
1321 if p >= baserev:
1322 parentscount[p - baserev] += 1
1322 parentscount[p - baserev] += 1
1323 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1323 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1324 condrepr='<branchpoint>')
1324 condrepr='<branchpoint>')
1325
1325
1326 @predicate('min(set)', safe=True)
1326 @predicate('min(set)', safe=True)
1327 def minrev(repo, subset, x):
1327 def minrev(repo, subset, x):
1328 """Changeset with lowest revision number in set.
1328 """Changeset with lowest revision number in set.
1329 """
1329 """
1330 os = getset(repo, fullreposet(repo), x)
1330 os = getset(repo, fullreposet(repo), x)
1331 try:
1331 try:
1332 m = os.min()
1332 m = os.min()
1333 if m in subset:
1333 if m in subset:
1334 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1334 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1335 except ValueError:
1335 except ValueError:
1336 # os.min() throws a ValueError when the collection is empty.
1336 # os.min() throws a ValueError when the collection is empty.
1337 # Same as python's min().
1337 # Same as python's min().
1338 pass
1338 pass
1339 return baseset(datarepr=('<min %r, %r>', subset, os))
1339 return baseset(datarepr=('<min %r, %r>', subset, os))
1340
1340
1341 @predicate('modifies(pattern)', safe=True)
1341 @predicate('modifies(pattern)', safe=True)
1342 def modifies(repo, subset, x):
1342 def modifies(repo, subset, x):
1343 """Changesets modifying files matched by pattern.
1343 """Changesets modifying files matched by pattern.
1344
1344
1345 The pattern without explicit kind like ``glob:`` is expected to be
1345 The pattern without explicit kind like ``glob:`` is expected to be
1346 relative to the current directory and match against a file or a
1346 relative to the current directory and match against a file or a
1347 directory.
1347 directory.
1348 """
1348 """
1349 # i18n: "modifies" is a keyword
1349 # i18n: "modifies" is a keyword
1350 pat = getstring(x, _("modifies requires a pattern"))
1350 pat = getstring(x, _("modifies requires a pattern"))
1351 return checkstatus(repo, subset, pat, 0)
1351 return checkstatus(repo, subset, pat, 0)
1352
1352
1353 @predicate('named(namespace)')
1353 @predicate('named(namespace)')
1354 def named(repo, subset, x):
1354 def named(repo, subset, x):
1355 """The changesets in a given namespace.
1355 """The changesets in a given namespace.
1356
1356
1357 If `namespace` starts with `re:`, the remainder of the string is treated as
1357 If `namespace` starts with `re:`, the remainder of the string is treated as
1358 a regular expression. To match a namespace that actually starts with `re:`,
1358 a regular expression. To match a namespace that actually starts with `re:`,
1359 use the prefix `literal:`.
1359 use the prefix `literal:`.
1360 """
1360 """
1361 # i18n: "named" is a keyword
1361 # i18n: "named" is a keyword
1362 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1362 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1363
1363
1364 ns = getstring(args[0],
1364 ns = getstring(args[0],
1365 # i18n: "named" is a keyword
1365 # i18n: "named" is a keyword
1366 _('the argument to named must be a string'))
1366 _('the argument to named must be a string'))
1367 kind, pattern, matcher = util.stringmatcher(ns)
1367 kind, pattern, matcher = util.stringmatcher(ns)
1368 namespaces = set()
1368 namespaces = set()
1369 if kind == 'literal':
1369 if kind == 'literal':
1370 if pattern not in repo.names:
1370 if pattern not in repo.names:
1371 raise error.RepoLookupError(_("namespace '%s' does not exist")
1371 raise error.RepoLookupError(_("namespace '%s' does not exist")
1372 % ns)
1372 % ns)
1373 namespaces.add(repo.names[pattern])
1373 namespaces.add(repo.names[pattern])
1374 else:
1374 else:
1375 for name, ns in repo.names.iteritems():
1375 for name, ns in repo.names.iteritems():
1376 if matcher(name):
1376 if matcher(name):
1377 namespaces.add(ns)
1377 namespaces.add(ns)
1378 if not namespaces:
1378 if not namespaces:
1379 raise error.RepoLookupError(_("no namespace exists"
1379 raise error.RepoLookupError(_("no namespace exists"
1380 " that match '%s'") % pattern)
1380 " that match '%s'") % pattern)
1381
1381
1382 names = set()
1382 names = set()
1383 for ns in namespaces:
1383 for ns in namespaces:
1384 for name in ns.listnames(repo):
1384 for name in ns.listnames(repo):
1385 if name not in ns.deprecated:
1385 if name not in ns.deprecated:
1386 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1386 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1387
1387
1388 names -= set([node.nullrev])
1388 names -= set([node.nullrev])
1389 return subset & names
1389 return subset & names
1390
1390
1391 @predicate('id(string)', safe=True)
1391 @predicate('id(string)', safe=True)
1392 def node_(repo, subset, x):
1392 def node_(repo, subset, x):
1393 """Revision non-ambiguously specified by the given hex string prefix.
1393 """Revision non-ambiguously specified by the given hex string prefix.
1394 """
1394 """
1395 # i18n: "id" is a keyword
1395 # i18n: "id" is a keyword
1396 l = getargs(x, 1, 1, _("id requires one argument"))
1396 l = getargs(x, 1, 1, _("id requires one argument"))
1397 # i18n: "id" is a keyword
1397 # i18n: "id" is a keyword
1398 n = getstring(l[0], _("id requires a string"))
1398 n = getstring(l[0], _("id requires a string"))
1399 if len(n) == 40:
1399 if len(n) == 40:
1400 try:
1400 try:
1401 rn = repo.changelog.rev(node.bin(n))
1401 rn = repo.changelog.rev(node.bin(n))
1402 except (LookupError, TypeError):
1402 except (LookupError, TypeError):
1403 rn = None
1403 rn = None
1404 else:
1404 else:
1405 rn = None
1405 rn = None
1406 pm = repo.changelog._partialmatch(n)
1406 pm = repo.changelog._partialmatch(n)
1407 if pm is not None:
1407 if pm is not None:
1408 rn = repo.changelog.rev(pm)
1408 rn = repo.changelog.rev(pm)
1409
1409
1410 if rn is None:
1410 if rn is None:
1411 return baseset()
1411 return baseset()
1412 result = baseset([rn])
1412 result = baseset([rn])
1413 return result & subset
1413 return result & subset
1414
1414
1415 @predicate('obsolete()', safe=True)
1415 @predicate('obsolete()', safe=True)
1416 def obsolete(repo, subset, x):
1416 def obsolete(repo, subset, x):
1417 """Mutable changeset with a newer version."""
1417 """Mutable changeset with a newer version."""
1418 # i18n: "obsolete" is a keyword
1418 # i18n: "obsolete" is a keyword
1419 getargs(x, 0, 0, _("obsolete takes no arguments"))
1419 getargs(x, 0, 0, _("obsolete takes no arguments"))
1420 obsoletes = obsmod.getrevs(repo, 'obsolete')
1420 obsoletes = obsmod.getrevs(repo, 'obsolete')
1421 return subset & obsoletes
1421 return subset & obsoletes
1422
1422
1423 @predicate('only(set, [set])', safe=True)
1423 @predicate('only(set, [set])', safe=True)
1424 def only(repo, subset, x):
1424 def only(repo, subset, x):
1425 """Changesets that are ancestors of the first set that are not ancestors
1425 """Changesets that are ancestors of the first set that are not ancestors
1426 of any other head in the repo. If a second set is specified, the result
1426 of any other head in the repo. If a second set is specified, the result
1427 is ancestors of the first set that are not ancestors of the second set
1427 is ancestors of the first set that are not ancestors of the second set
1428 (i.e. ::<set1> - ::<set2>).
1428 (i.e. ::<set1> - ::<set2>).
1429 """
1429 """
1430 cl = repo.changelog
1430 cl = repo.changelog
1431 # i18n: "only" is a keyword
1431 # i18n: "only" is a keyword
1432 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1432 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1433 include = getset(repo, fullreposet(repo), args[0])
1433 include = getset(repo, fullreposet(repo), args[0])
1434 if len(args) == 1:
1434 if len(args) == 1:
1435 if not include:
1435 if not include:
1436 return baseset()
1436 return baseset()
1437
1437
1438 descendants = set(_revdescendants(repo, include, False))
1438 descendants = set(_revdescendants(repo, include, False))
1439 exclude = [rev for rev in cl.headrevs()
1439 exclude = [rev for rev in cl.headrevs()
1440 if not rev in descendants and not rev in include]
1440 if not rev in descendants and not rev in include]
1441 else:
1441 else:
1442 exclude = getset(repo, fullreposet(repo), args[1])
1442 exclude = getset(repo, fullreposet(repo), args[1])
1443
1443
1444 results = set(cl.findmissingrevs(common=exclude, heads=include))
1444 results = set(cl.findmissingrevs(common=exclude, heads=include))
1445 # XXX we should turn this into a baseset instead of a set, smartset may do
1445 # XXX we should turn this into a baseset instead of a set, smartset may do
1446 # some optimisations from the fact this is a baseset.
1446 # some optimisations from the fact this is a baseset.
1447 return subset & results
1447 return subset & results
1448
1448
1449 @predicate('origin([set])', safe=True)
1449 @predicate('origin([set])', safe=True)
1450 def origin(repo, subset, x):
1450 def origin(repo, subset, x):
1451 """
1451 """
1452 Changesets that were specified as a source for the grafts, transplants or
1452 Changesets that were specified as a source for the grafts, transplants or
1453 rebases that created the given revisions. Omitting the optional set is the
1453 rebases that created the given revisions. Omitting the optional set is the
1454 same as passing all(). If a changeset created by these operations is itself
1454 same as passing all(). If a changeset created by these operations is itself
1455 specified as a source for one of these operations, only the source changeset
1455 specified as a source for one of these operations, only the source changeset
1456 for the first operation is selected.
1456 for the first operation is selected.
1457 """
1457 """
1458 if x is not None:
1458 if x is not None:
1459 dests = getset(repo, fullreposet(repo), x)
1459 dests = getset(repo, fullreposet(repo), x)
1460 else:
1460 else:
1461 dests = fullreposet(repo)
1461 dests = fullreposet(repo)
1462
1462
1463 def _firstsrc(rev):
1463 def _firstsrc(rev):
1464 src = _getrevsource(repo, rev)
1464 src = _getrevsource(repo, rev)
1465 if src is None:
1465 if src is None:
1466 return None
1466 return None
1467
1467
1468 while True:
1468 while True:
1469 prev = _getrevsource(repo, src)
1469 prev = _getrevsource(repo, src)
1470
1470
1471 if prev is None:
1471 if prev is None:
1472 return src
1472 return src
1473 src = prev
1473 src = prev
1474
1474
1475 o = set([_firstsrc(r) for r in dests])
1475 o = set([_firstsrc(r) for r in dests])
1476 o -= set([None])
1476 o -= set([None])
1477 # XXX we should turn this into a baseset instead of a set, smartset may do
1477 # XXX we should turn this into a baseset instead of a set, smartset may do
1478 # some optimisations from the fact this is a baseset.
1478 # some optimisations from the fact this is a baseset.
1479 return subset & o
1479 return subset & o
1480
1480
1481 @predicate('outgoing([path])', safe=True)
1481 @predicate('outgoing([path])', safe=True)
1482 def outgoing(repo, subset, x):
1482 def outgoing(repo, subset, x):
1483 """Changesets not found in the specified destination repository, or the
1483 """Changesets not found in the specified destination repository, or the
1484 default push location.
1484 default push location.
1485 """
1485 """
1486 # Avoid cycles.
1486 # Avoid cycles.
1487 from . import (
1487 from . import (
1488 discovery,
1488 discovery,
1489 hg,
1489 hg,
1490 )
1490 )
1491 # i18n: "outgoing" is a keyword
1491 # i18n: "outgoing" is a keyword
1492 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1492 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1493 # i18n: "outgoing" is a keyword
1493 # i18n: "outgoing" is a keyword
1494 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1494 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1495 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1495 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1496 dest, branches = hg.parseurl(dest)
1496 dest, branches = hg.parseurl(dest)
1497 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1497 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1498 if revs:
1498 if revs:
1499 revs = [repo.lookup(rev) for rev in revs]
1499 revs = [repo.lookup(rev) for rev in revs]
1500 other = hg.peer(repo, {}, dest)
1500 other = hg.peer(repo, {}, dest)
1501 repo.ui.pushbuffer()
1501 repo.ui.pushbuffer()
1502 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1502 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1503 repo.ui.popbuffer()
1503 repo.ui.popbuffer()
1504 cl = repo.changelog
1504 cl = repo.changelog
1505 o = set([cl.rev(r) for r in outgoing.missing])
1505 o = set([cl.rev(r) for r in outgoing.missing])
1506 return subset & o
1506 return subset & o
1507
1507
1508 @predicate('p1([set])', safe=True)
1508 @predicate('p1([set])', safe=True)
1509 def p1(repo, subset, x):
1509 def p1(repo, subset, x):
1510 """First parent of changesets in set, or the working directory.
1510 """First parent of changesets in set, or the working directory.
1511 """
1511 """
1512 if x is None:
1512 if x is None:
1513 p = repo[x].p1().rev()
1513 p = repo[x].p1().rev()
1514 if p >= 0:
1514 if p >= 0:
1515 return subset & baseset([p])
1515 return subset & baseset([p])
1516 return baseset()
1516 return baseset()
1517
1517
1518 ps = set()
1518 ps = set()
1519 cl = repo.changelog
1519 cl = repo.changelog
1520 for r in getset(repo, fullreposet(repo), x):
1520 for r in getset(repo, fullreposet(repo), x):
1521 ps.add(cl.parentrevs(r)[0])
1521 ps.add(cl.parentrevs(r)[0])
1522 ps -= set([node.nullrev])
1522 ps -= set([node.nullrev])
1523 # XXX we should turn this into a baseset instead of a set, smartset may do
1523 # XXX we should turn this into a baseset instead of a set, smartset may do
1524 # some optimisations from the fact this is a baseset.
1524 # some optimisations from the fact this is a baseset.
1525 return subset & ps
1525 return subset & ps
1526
1526
1527 @predicate('p2([set])', safe=True)
1527 @predicate('p2([set])', safe=True)
1528 def p2(repo, subset, x):
1528 def p2(repo, subset, x):
1529 """Second parent of changesets in set, or the working directory.
1529 """Second parent of changesets in set, or the working directory.
1530 """
1530 """
1531 if x is None:
1531 if x is None:
1532 ps = repo[x].parents()
1532 ps = repo[x].parents()
1533 try:
1533 try:
1534 p = ps[1].rev()
1534 p = ps[1].rev()
1535 if p >= 0:
1535 if p >= 0:
1536 return subset & baseset([p])
1536 return subset & baseset([p])
1537 return baseset()
1537 return baseset()
1538 except IndexError:
1538 except IndexError:
1539 return baseset()
1539 return baseset()
1540
1540
1541 ps = set()
1541 ps = set()
1542 cl = repo.changelog
1542 cl = repo.changelog
1543 for r in getset(repo, fullreposet(repo), x):
1543 for r in getset(repo, fullreposet(repo), x):
1544 ps.add(cl.parentrevs(r)[1])
1544 ps.add(cl.parentrevs(r)[1])
1545 ps -= set([node.nullrev])
1545 ps -= set([node.nullrev])
1546 # XXX we should turn this into a baseset instead of a set, smartset may do
1546 # XXX we should turn this into a baseset instead of a set, smartset may do
1547 # some optimisations from the fact this is a baseset.
1547 # some optimisations from the fact this is a baseset.
1548 return subset & ps
1548 return subset & ps
1549
1549
1550 def parentpost(repo, subset, x, order):
1550 def parentpost(repo, subset, x, order):
1551 return p1(repo, subset, x)
1551 return p1(repo, subset, x)
1552
1552
1553 @predicate('parents([set])', safe=True)
1553 @predicate('parents([set])', safe=True)
1554 def parents(repo, subset, x):
1554 def parents(repo, subset, x):
1555 """
1555 """
1556 The set of all parents for all changesets in set, or the working directory.
1556 The set of all parents for all changesets in set, or the working directory.
1557 """
1557 """
1558 if x is None:
1558 if x is None:
1559 ps = set(p.rev() for p in repo[x].parents())
1559 ps = set(p.rev() for p in repo[x].parents())
1560 else:
1560 else:
1561 ps = set()
1561 ps = set()
1562 cl = repo.changelog
1562 cl = repo.changelog
1563 up = ps.update
1563 up = ps.update
1564 parentrevs = cl.parentrevs
1564 parentrevs = cl.parentrevs
1565 for r in getset(repo, fullreposet(repo), x):
1565 for r in getset(repo, fullreposet(repo), x):
1566 if r == node.wdirrev:
1566 if r == node.wdirrev:
1567 up(p.rev() for p in repo[r].parents())
1567 up(p.rev() for p in repo[r].parents())
1568 else:
1568 else:
1569 up(parentrevs(r))
1569 up(parentrevs(r))
1570 ps -= set([node.nullrev])
1570 ps -= set([node.nullrev])
1571 return subset & ps
1571 return subset & ps
1572
1572
1573 def _phase(repo, subset, target):
1573 def _phase(repo, subset, target):
1574 """helper to select all rev in phase <target>"""
1574 """helper to select all rev in phase <target>"""
1575 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1575 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1576 if repo._phasecache._phasesets:
1576 if repo._phasecache._phasesets:
1577 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1577 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1578 s = baseset(s)
1578 s = baseset(s)
1579 s.sort() # set are non ordered, so we enforce ascending
1579 s.sort() # set are non ordered, so we enforce ascending
1580 return subset & s
1580 return subset & s
1581 else:
1581 else:
1582 phase = repo._phasecache.phase
1582 phase = repo._phasecache.phase
1583 condition = lambda r: phase(repo, r) == target
1583 condition = lambda r: phase(repo, r) == target
1584 return subset.filter(condition, condrepr=('<phase %r>', target),
1584 return subset.filter(condition, condrepr=('<phase %r>', target),
1585 cache=False)
1585 cache=False)
1586
1586
1587 @predicate('draft()', safe=True)
1587 @predicate('draft()', safe=True)
1588 def draft(repo, subset, x):
1588 def draft(repo, subset, x):
1589 """Changeset in draft phase."""
1589 """Changeset in draft phase."""
1590 # i18n: "draft" is a keyword
1590 # i18n: "draft" is a keyword
1591 getargs(x, 0, 0, _("draft takes no arguments"))
1591 getargs(x, 0, 0, _("draft takes no arguments"))
1592 target = phases.draft
1592 target = phases.draft
1593 return _phase(repo, subset, target)
1593 return _phase(repo, subset, target)
1594
1594
1595 @predicate('secret()', safe=True)
1595 @predicate('secret()', safe=True)
1596 def secret(repo, subset, x):
1596 def secret(repo, subset, x):
1597 """Changeset in secret phase."""
1597 """Changeset in secret phase."""
1598 # i18n: "secret" is a keyword
1598 # i18n: "secret" is a keyword
1599 getargs(x, 0, 0, _("secret takes no arguments"))
1599 getargs(x, 0, 0, _("secret takes no arguments"))
1600 target = phases.secret
1600 target = phases.secret
1601 return _phase(repo, subset, target)
1601 return _phase(repo, subset, target)
1602
1602
1603 def parentspec(repo, subset, x, n, order):
1603 def parentspec(repo, subset, x, n, order):
1604 """``set^0``
1604 """``set^0``
1605 The set.
1605 The set.
1606 ``set^1`` (or ``set^``), ``set^2``
1606 ``set^1`` (or ``set^``), ``set^2``
1607 First or second parent, respectively, of all changesets in set.
1607 First or second parent, respectively, of all changesets in set.
1608 """
1608 """
1609 try:
1609 try:
1610 n = int(n[1])
1610 n = int(n[1])
1611 if n not in (0, 1, 2):
1611 if n not in (0, 1, 2):
1612 raise ValueError
1612 raise ValueError
1613 except (TypeError, ValueError):
1613 except (TypeError, ValueError):
1614 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1614 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1615 ps = set()
1615 ps = set()
1616 cl = repo.changelog
1616 cl = repo.changelog
1617 for r in getset(repo, fullreposet(repo), x):
1617 for r in getset(repo, fullreposet(repo), x):
1618 if n == 0:
1618 if n == 0:
1619 ps.add(r)
1619 ps.add(r)
1620 elif n == 1:
1620 elif n == 1:
1621 ps.add(cl.parentrevs(r)[0])
1621 ps.add(cl.parentrevs(r)[0])
1622 elif n == 2:
1622 elif n == 2:
1623 parents = cl.parentrevs(r)
1623 parents = cl.parentrevs(r)
1624 if len(parents) > 1:
1624 if parents[1] != node.nullrev:
1625 ps.add(parents[1])
1625 ps.add(parents[1])
1626 return subset & ps
1626 return subset & ps
1627
1627
1628 @predicate('present(set)', safe=True)
1628 @predicate('present(set)', safe=True)
1629 def present(repo, subset, x):
1629 def present(repo, subset, x):
1630 """An empty set, if any revision in set isn't found; otherwise,
1630 """An empty set, if any revision in set isn't found; otherwise,
1631 all revisions in set.
1631 all revisions in set.
1632
1632
1633 If any of specified revisions is not present in the local repository,
1633 If any of specified revisions is not present in the local repository,
1634 the query is normally aborted. But this predicate allows the query
1634 the query is normally aborted. But this predicate allows the query
1635 to continue even in such cases.
1635 to continue even in such cases.
1636 """
1636 """
1637 try:
1637 try:
1638 return getset(repo, subset, x)
1638 return getset(repo, subset, x)
1639 except error.RepoLookupError:
1639 except error.RepoLookupError:
1640 return baseset()
1640 return baseset()
1641
1641
1642 # for internal use
1642 # for internal use
1643 @predicate('_notpublic', safe=True)
1643 @predicate('_notpublic', safe=True)
1644 def _notpublic(repo, subset, x):
1644 def _notpublic(repo, subset, x):
1645 getargs(x, 0, 0, "_notpublic takes no arguments")
1645 getargs(x, 0, 0, "_notpublic takes no arguments")
1646 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1646 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1647 if repo._phasecache._phasesets:
1647 if repo._phasecache._phasesets:
1648 s = set()
1648 s = set()
1649 for u in repo._phasecache._phasesets[1:]:
1649 for u in repo._phasecache._phasesets[1:]:
1650 s.update(u)
1650 s.update(u)
1651 s = baseset(s - repo.changelog.filteredrevs)
1651 s = baseset(s - repo.changelog.filteredrevs)
1652 s.sort()
1652 s.sort()
1653 return subset & s
1653 return subset & s
1654 else:
1654 else:
1655 phase = repo._phasecache.phase
1655 phase = repo._phasecache.phase
1656 target = phases.public
1656 target = phases.public
1657 condition = lambda r: phase(repo, r) != target
1657 condition = lambda r: phase(repo, r) != target
1658 return subset.filter(condition, condrepr=('<phase %r>', target),
1658 return subset.filter(condition, condrepr=('<phase %r>', target),
1659 cache=False)
1659 cache=False)
1660
1660
1661 @predicate('public()', safe=True)
1661 @predicate('public()', safe=True)
1662 def public(repo, subset, x):
1662 def public(repo, subset, x):
1663 """Changeset in public phase."""
1663 """Changeset in public phase."""
1664 # i18n: "public" is a keyword
1664 # i18n: "public" is a keyword
1665 getargs(x, 0, 0, _("public takes no arguments"))
1665 getargs(x, 0, 0, _("public takes no arguments"))
1666 phase = repo._phasecache.phase
1666 phase = repo._phasecache.phase
1667 target = phases.public
1667 target = phases.public
1668 condition = lambda r: phase(repo, r) == target
1668 condition = lambda r: phase(repo, r) == target
1669 return subset.filter(condition, condrepr=('<phase %r>', target),
1669 return subset.filter(condition, condrepr=('<phase %r>', target),
1670 cache=False)
1670 cache=False)
1671
1671
1672 @predicate('remote([id [,path]])', safe=True)
1672 @predicate('remote([id [,path]])', safe=True)
1673 def remote(repo, subset, x):
1673 def remote(repo, subset, x):
1674 """Local revision that corresponds to the given identifier in a
1674 """Local revision that corresponds to the given identifier in a
1675 remote repository, if present. Here, the '.' identifier is a
1675 remote repository, if present. Here, the '.' identifier is a
1676 synonym for the current local branch.
1676 synonym for the current local branch.
1677 """
1677 """
1678
1678
1679 from . import hg # avoid start-up nasties
1679 from . import hg # avoid start-up nasties
1680 # i18n: "remote" is a keyword
1680 # i18n: "remote" is a keyword
1681 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1681 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1682
1682
1683 q = '.'
1683 q = '.'
1684 if len(l) > 0:
1684 if len(l) > 0:
1685 # i18n: "remote" is a keyword
1685 # i18n: "remote" is a keyword
1686 q = getstring(l[0], _("remote requires a string id"))
1686 q = getstring(l[0], _("remote requires a string id"))
1687 if q == '.':
1687 if q == '.':
1688 q = repo['.'].branch()
1688 q = repo['.'].branch()
1689
1689
1690 dest = ''
1690 dest = ''
1691 if len(l) > 1:
1691 if len(l) > 1:
1692 # i18n: "remote" is a keyword
1692 # i18n: "remote" is a keyword
1693 dest = getstring(l[1], _("remote requires a repository path"))
1693 dest = getstring(l[1], _("remote requires a repository path"))
1694 dest = repo.ui.expandpath(dest or 'default')
1694 dest = repo.ui.expandpath(dest or 'default')
1695 dest, branches = hg.parseurl(dest)
1695 dest, branches = hg.parseurl(dest)
1696 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1696 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1697 if revs:
1697 if revs:
1698 revs = [repo.lookup(rev) for rev in revs]
1698 revs = [repo.lookup(rev) for rev in revs]
1699 other = hg.peer(repo, {}, dest)
1699 other = hg.peer(repo, {}, dest)
1700 n = other.lookup(q)
1700 n = other.lookup(q)
1701 if n in repo:
1701 if n in repo:
1702 r = repo[n].rev()
1702 r = repo[n].rev()
1703 if r in subset:
1703 if r in subset:
1704 return baseset([r])
1704 return baseset([r])
1705 return baseset()
1705 return baseset()
1706
1706
1707 @predicate('removes(pattern)', safe=True)
1707 @predicate('removes(pattern)', safe=True)
1708 def removes(repo, subset, x):
1708 def removes(repo, subset, x):
1709 """Changesets which remove files matching pattern.
1709 """Changesets which remove files matching pattern.
1710
1710
1711 The pattern without explicit kind like ``glob:`` is expected to be
1711 The pattern without explicit kind like ``glob:`` is expected to be
1712 relative to the current directory and match against a file or a
1712 relative to the current directory and match against a file or a
1713 directory.
1713 directory.
1714 """
1714 """
1715 # i18n: "removes" is a keyword
1715 # i18n: "removes" is a keyword
1716 pat = getstring(x, _("removes requires a pattern"))
1716 pat = getstring(x, _("removes requires a pattern"))
1717 return checkstatus(repo, subset, pat, 2)
1717 return checkstatus(repo, subset, pat, 2)
1718
1718
1719 @predicate('rev(number)', safe=True)
1719 @predicate('rev(number)', safe=True)
1720 def rev(repo, subset, x):
1720 def rev(repo, subset, x):
1721 """Revision with the given numeric identifier.
1721 """Revision with the given numeric identifier.
1722 """
1722 """
1723 # i18n: "rev" is a keyword
1723 # i18n: "rev" is a keyword
1724 l = getargs(x, 1, 1, _("rev requires one argument"))
1724 l = getargs(x, 1, 1, _("rev requires one argument"))
1725 try:
1725 try:
1726 # i18n: "rev" is a keyword
1726 # i18n: "rev" is a keyword
1727 l = int(getstring(l[0], _("rev requires a number")))
1727 l = int(getstring(l[0], _("rev requires a number")))
1728 except (TypeError, ValueError):
1728 except (TypeError, ValueError):
1729 # i18n: "rev" is a keyword
1729 # i18n: "rev" is a keyword
1730 raise error.ParseError(_("rev expects a number"))
1730 raise error.ParseError(_("rev expects a number"))
1731 if l not in repo.changelog and l != node.nullrev:
1731 if l not in repo.changelog and l != node.nullrev:
1732 return baseset()
1732 return baseset()
1733 return subset & baseset([l])
1733 return subset & baseset([l])
1734
1734
1735 @predicate('matching(revision [, field])', safe=True)
1735 @predicate('matching(revision [, field])', safe=True)
1736 def matching(repo, subset, x):
1736 def matching(repo, subset, x):
1737 """Changesets in which a given set of fields match the set of fields in the
1737 """Changesets in which a given set of fields match the set of fields in the
1738 selected revision or set.
1738 selected revision or set.
1739
1739
1740 To match more than one field pass the list of fields to match separated
1740 To match more than one field pass the list of fields to match separated
1741 by spaces (e.g. ``author description``).
1741 by spaces (e.g. ``author description``).
1742
1742
1743 Valid fields are most regular revision fields and some special fields.
1743 Valid fields are most regular revision fields and some special fields.
1744
1744
1745 Regular revision fields are ``description``, ``author``, ``branch``,
1745 Regular revision fields are ``description``, ``author``, ``branch``,
1746 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1746 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1747 and ``diff``.
1747 and ``diff``.
1748 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1748 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1749 contents of the revision. Two revisions matching their ``diff`` will
1749 contents of the revision. Two revisions matching their ``diff`` will
1750 also match their ``files``.
1750 also match their ``files``.
1751
1751
1752 Special fields are ``summary`` and ``metadata``:
1752 Special fields are ``summary`` and ``metadata``:
1753 ``summary`` matches the first line of the description.
1753 ``summary`` matches the first line of the description.
1754 ``metadata`` is equivalent to matching ``description user date``
1754 ``metadata`` is equivalent to matching ``description user date``
1755 (i.e. it matches the main metadata fields).
1755 (i.e. it matches the main metadata fields).
1756
1756
1757 ``metadata`` is the default field which is used when no fields are
1757 ``metadata`` is the default field which is used when no fields are
1758 specified. You can match more than one field at a time.
1758 specified. You can match more than one field at a time.
1759 """
1759 """
1760 # i18n: "matching" is a keyword
1760 # i18n: "matching" is a keyword
1761 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1761 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1762
1762
1763 revs = getset(repo, fullreposet(repo), l[0])
1763 revs = getset(repo, fullreposet(repo), l[0])
1764
1764
1765 fieldlist = ['metadata']
1765 fieldlist = ['metadata']
1766 if len(l) > 1:
1766 if len(l) > 1:
1767 fieldlist = getstring(l[1],
1767 fieldlist = getstring(l[1],
1768 # i18n: "matching" is a keyword
1768 # i18n: "matching" is a keyword
1769 _("matching requires a string "
1769 _("matching requires a string "
1770 "as its second argument")).split()
1770 "as its second argument")).split()
1771
1771
1772 # Make sure that there are no repeated fields,
1772 # Make sure that there are no repeated fields,
1773 # expand the 'special' 'metadata' field type
1773 # expand the 'special' 'metadata' field type
1774 # and check the 'files' whenever we check the 'diff'
1774 # and check the 'files' whenever we check the 'diff'
1775 fields = []
1775 fields = []
1776 for field in fieldlist:
1776 for field in fieldlist:
1777 if field == 'metadata':
1777 if field == 'metadata':
1778 fields += ['user', 'description', 'date']
1778 fields += ['user', 'description', 'date']
1779 elif field == 'diff':
1779 elif field == 'diff':
1780 # a revision matching the diff must also match the files
1780 # a revision matching the diff must also match the files
1781 # since matching the diff is very costly, make sure to
1781 # since matching the diff is very costly, make sure to
1782 # also match the files first
1782 # also match the files first
1783 fields += ['files', 'diff']
1783 fields += ['files', 'diff']
1784 else:
1784 else:
1785 if field == 'author':
1785 if field == 'author':
1786 field = 'user'
1786 field = 'user'
1787 fields.append(field)
1787 fields.append(field)
1788 fields = set(fields)
1788 fields = set(fields)
1789 if 'summary' in fields and 'description' in fields:
1789 if 'summary' in fields and 'description' in fields:
1790 # If a revision matches its description it also matches its summary
1790 # If a revision matches its description it also matches its summary
1791 fields.discard('summary')
1791 fields.discard('summary')
1792
1792
1793 # We may want to match more than one field
1793 # We may want to match more than one field
1794 # Not all fields take the same amount of time to be matched
1794 # Not all fields take the same amount of time to be matched
1795 # Sort the selected fields in order of increasing matching cost
1795 # Sort the selected fields in order of increasing matching cost
1796 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1796 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1797 'files', 'description', 'substate', 'diff']
1797 'files', 'description', 'substate', 'diff']
1798 def fieldkeyfunc(f):
1798 def fieldkeyfunc(f):
1799 try:
1799 try:
1800 return fieldorder.index(f)
1800 return fieldorder.index(f)
1801 except ValueError:
1801 except ValueError:
1802 # assume an unknown field is very costly
1802 # assume an unknown field is very costly
1803 return len(fieldorder)
1803 return len(fieldorder)
1804 fields = list(fields)
1804 fields = list(fields)
1805 fields.sort(key=fieldkeyfunc)
1805 fields.sort(key=fieldkeyfunc)
1806
1806
1807 # Each field will be matched with its own "getfield" function
1807 # Each field will be matched with its own "getfield" function
1808 # which will be added to the getfieldfuncs array of functions
1808 # which will be added to the getfieldfuncs array of functions
1809 getfieldfuncs = []
1809 getfieldfuncs = []
1810 _funcs = {
1810 _funcs = {
1811 'user': lambda r: repo[r].user(),
1811 'user': lambda r: repo[r].user(),
1812 'branch': lambda r: repo[r].branch(),
1812 'branch': lambda r: repo[r].branch(),
1813 'date': lambda r: repo[r].date(),
1813 'date': lambda r: repo[r].date(),
1814 'description': lambda r: repo[r].description(),
1814 'description': lambda r: repo[r].description(),
1815 'files': lambda r: repo[r].files(),
1815 'files': lambda r: repo[r].files(),
1816 'parents': lambda r: repo[r].parents(),
1816 'parents': lambda r: repo[r].parents(),
1817 'phase': lambda r: repo[r].phase(),
1817 'phase': lambda r: repo[r].phase(),
1818 'substate': lambda r: repo[r].substate,
1818 'substate': lambda r: repo[r].substate,
1819 'summary': lambda r: repo[r].description().splitlines()[0],
1819 'summary': lambda r: repo[r].description().splitlines()[0],
1820 'diff': lambda r: list(repo[r].diff(git=True),)
1820 'diff': lambda r: list(repo[r].diff(git=True),)
1821 }
1821 }
1822 for info in fields:
1822 for info in fields:
1823 getfield = _funcs.get(info, None)
1823 getfield = _funcs.get(info, None)
1824 if getfield is None:
1824 if getfield is None:
1825 raise error.ParseError(
1825 raise error.ParseError(
1826 # i18n: "matching" is a keyword
1826 # i18n: "matching" is a keyword
1827 _("unexpected field name passed to matching: %s") % info)
1827 _("unexpected field name passed to matching: %s") % info)
1828 getfieldfuncs.append(getfield)
1828 getfieldfuncs.append(getfield)
1829 # convert the getfield array of functions into a "getinfo" function
1829 # convert the getfield array of functions into a "getinfo" function
1830 # which returns an array of field values (or a single value if there
1830 # which returns an array of field values (or a single value if there
1831 # is only one field to match)
1831 # is only one field to match)
1832 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1832 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1833
1833
1834 def matches(x):
1834 def matches(x):
1835 for rev in revs:
1835 for rev in revs:
1836 target = getinfo(rev)
1836 target = getinfo(rev)
1837 match = True
1837 match = True
1838 for n, f in enumerate(getfieldfuncs):
1838 for n, f in enumerate(getfieldfuncs):
1839 if target[n] != f(x):
1839 if target[n] != f(x):
1840 match = False
1840 match = False
1841 if match:
1841 if match:
1842 return True
1842 return True
1843 return False
1843 return False
1844
1844
1845 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1845 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1846
1846
1847 @predicate('reverse(set)', safe=True, takeorder=True)
1847 @predicate('reverse(set)', safe=True, takeorder=True)
1848 def reverse(repo, subset, x, order):
1848 def reverse(repo, subset, x, order):
1849 """Reverse order of set.
1849 """Reverse order of set.
1850 """
1850 """
1851 l = getset(repo, subset, x)
1851 l = getset(repo, subset, x)
1852 if order == defineorder:
1852 if order == defineorder:
1853 l.reverse()
1853 l.reverse()
1854 return l
1854 return l
1855
1855
1856 @predicate('roots(set)', safe=True)
1856 @predicate('roots(set)', safe=True)
1857 def roots(repo, subset, x):
1857 def roots(repo, subset, x):
1858 """Changesets in set with no parent changeset in set.
1858 """Changesets in set with no parent changeset in set.
1859 """
1859 """
1860 s = getset(repo, fullreposet(repo), x)
1860 s = getset(repo, fullreposet(repo), x)
1861 parents = repo.changelog.parentrevs
1861 parents = repo.changelog.parentrevs
1862 def filter(r):
1862 def filter(r):
1863 for p in parents(r):
1863 for p in parents(r):
1864 if 0 <= p and p in s:
1864 if 0 <= p and p in s:
1865 return False
1865 return False
1866 return True
1866 return True
1867 return subset & s.filter(filter, condrepr='<roots>')
1867 return subset & s.filter(filter, condrepr='<roots>')
1868
1868
1869 _sortkeyfuncs = {
1869 _sortkeyfuncs = {
1870 'rev': lambda c: c.rev(),
1870 'rev': lambda c: c.rev(),
1871 'branch': lambda c: c.branch(),
1871 'branch': lambda c: c.branch(),
1872 'desc': lambda c: c.description(),
1872 'desc': lambda c: c.description(),
1873 'user': lambda c: c.user(),
1873 'user': lambda c: c.user(),
1874 'author': lambda c: c.user(),
1874 'author': lambda c: c.user(),
1875 'date': lambda c: c.date()[0],
1875 'date': lambda c: c.date()[0],
1876 }
1876 }
1877
1877
1878 def _getsortargs(x):
1878 def _getsortargs(x):
1879 """Parse sort options into (set, [(key, reverse)], opts)"""
1879 """Parse sort options into (set, [(key, reverse)], opts)"""
1880 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1880 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1881 if 'set' not in args:
1881 if 'set' not in args:
1882 # i18n: "sort" is a keyword
1882 # i18n: "sort" is a keyword
1883 raise error.ParseError(_('sort requires one or two arguments'))
1883 raise error.ParseError(_('sort requires one or two arguments'))
1884 keys = "rev"
1884 keys = "rev"
1885 if 'keys' in args:
1885 if 'keys' in args:
1886 # i18n: "sort" is a keyword
1886 # i18n: "sort" is a keyword
1887 keys = getstring(args['keys'], _("sort spec must be a string"))
1887 keys = getstring(args['keys'], _("sort spec must be a string"))
1888
1888
1889 keyflags = []
1889 keyflags = []
1890 for k in keys.split():
1890 for k in keys.split():
1891 fk = k
1891 fk = k
1892 reverse = (k[0] == '-')
1892 reverse = (k[0] == '-')
1893 if reverse:
1893 if reverse:
1894 k = k[1:]
1894 k = k[1:]
1895 if k not in _sortkeyfuncs and k != 'topo':
1895 if k not in _sortkeyfuncs and k != 'topo':
1896 raise error.ParseError(_("unknown sort key %r") % fk)
1896 raise error.ParseError(_("unknown sort key %r") % fk)
1897 keyflags.append((k, reverse))
1897 keyflags.append((k, reverse))
1898
1898
1899 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1899 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1900 # i18n: "topo" is a keyword
1900 # i18n: "topo" is a keyword
1901 raise error.ParseError(_('topo sort order cannot be combined '
1901 raise error.ParseError(_('topo sort order cannot be combined '
1902 'with other sort keys'))
1902 'with other sort keys'))
1903
1903
1904 opts = {}
1904 opts = {}
1905 if 'topo.firstbranch' in args:
1905 if 'topo.firstbranch' in args:
1906 if any(k == 'topo' for k, reverse in keyflags):
1906 if any(k == 'topo' for k, reverse in keyflags):
1907 opts['topo.firstbranch'] = args['topo.firstbranch']
1907 opts['topo.firstbranch'] = args['topo.firstbranch']
1908 else:
1908 else:
1909 # i18n: "topo" and "topo.firstbranch" are keywords
1909 # i18n: "topo" and "topo.firstbranch" are keywords
1910 raise error.ParseError(_('topo.firstbranch can only be used '
1910 raise error.ParseError(_('topo.firstbranch can only be used '
1911 'when using the topo sort key'))
1911 'when using the topo sort key'))
1912
1912
1913 return args['set'], keyflags, opts
1913 return args['set'], keyflags, opts
1914
1914
1915 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1915 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1916 def sort(repo, subset, x, order):
1916 def sort(repo, subset, x, order):
1917 """Sort set by keys. The default sort order is ascending, specify a key
1917 """Sort set by keys. The default sort order is ascending, specify a key
1918 as ``-key`` to sort in descending order.
1918 as ``-key`` to sort in descending order.
1919
1919
1920 The keys can be:
1920 The keys can be:
1921
1921
1922 - ``rev`` for the revision number,
1922 - ``rev`` for the revision number,
1923 - ``branch`` for the branch name,
1923 - ``branch`` for the branch name,
1924 - ``desc`` for the commit message (description),
1924 - ``desc`` for the commit message (description),
1925 - ``user`` for user name (``author`` can be used as an alias),
1925 - ``user`` for user name (``author`` can be used as an alias),
1926 - ``date`` for the commit date
1926 - ``date`` for the commit date
1927 - ``topo`` for a reverse topographical sort
1927 - ``topo`` for a reverse topographical sort
1928
1928
1929 The ``topo`` sort order cannot be combined with other sort keys. This sort
1929 The ``topo`` sort order cannot be combined with other sort keys. This sort
1930 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1930 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1931 specifies what topographical branches to prioritize in the sort.
1931 specifies what topographical branches to prioritize in the sort.
1932
1932
1933 """
1933 """
1934 s, keyflags, opts = _getsortargs(x)
1934 s, keyflags, opts = _getsortargs(x)
1935 revs = getset(repo, subset, s)
1935 revs = getset(repo, subset, s)
1936
1936
1937 if not keyflags or order != defineorder:
1937 if not keyflags or order != defineorder:
1938 return revs
1938 return revs
1939 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1939 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1940 revs.sort(reverse=keyflags[0][1])
1940 revs.sort(reverse=keyflags[0][1])
1941 return revs
1941 return revs
1942 elif keyflags[0][0] == "topo":
1942 elif keyflags[0][0] == "topo":
1943 firstbranch = ()
1943 firstbranch = ()
1944 if 'topo.firstbranch' in opts:
1944 if 'topo.firstbranch' in opts:
1945 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1945 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1946 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1946 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1947 istopo=True)
1947 istopo=True)
1948 if keyflags[0][1]:
1948 if keyflags[0][1]:
1949 revs.reverse()
1949 revs.reverse()
1950 return revs
1950 return revs
1951
1951
1952 # sort() is guaranteed to be stable
1952 # sort() is guaranteed to be stable
1953 ctxs = [repo[r] for r in revs]
1953 ctxs = [repo[r] for r in revs]
1954 for k, reverse in reversed(keyflags):
1954 for k, reverse in reversed(keyflags):
1955 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1955 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1956 return baseset([c.rev() for c in ctxs])
1956 return baseset([c.rev() for c in ctxs])
1957
1957
1958 def _toposort(revs, parentsfunc, firstbranch=()):
1958 def _toposort(revs, parentsfunc, firstbranch=()):
1959 """Yield revisions from heads to roots one (topo) branch at a time.
1959 """Yield revisions from heads to roots one (topo) branch at a time.
1960
1960
1961 This function aims to be used by a graph generator that wishes to minimize
1961 This function aims to be used by a graph generator that wishes to minimize
1962 the number of parallel branches and their interleaving.
1962 the number of parallel branches and their interleaving.
1963
1963
1964 Example iteration order (numbers show the "true" order in a changelog):
1964 Example iteration order (numbers show the "true" order in a changelog):
1965
1965
1966 o 4
1966 o 4
1967 |
1967 |
1968 o 1
1968 o 1
1969 |
1969 |
1970 | o 3
1970 | o 3
1971 | |
1971 | |
1972 | o 2
1972 | o 2
1973 |/
1973 |/
1974 o 0
1974 o 0
1975
1975
1976 Note that the ancestors of merges are understood by the current
1976 Note that the ancestors of merges are understood by the current
1977 algorithm to be on the same branch. This means no reordering will
1977 algorithm to be on the same branch. This means no reordering will
1978 occur behind a merge.
1978 occur behind a merge.
1979 """
1979 """
1980
1980
1981 ### Quick summary of the algorithm
1981 ### Quick summary of the algorithm
1982 #
1982 #
1983 # This function is based around a "retention" principle. We keep revisions
1983 # This function is based around a "retention" principle. We keep revisions
1984 # in memory until we are ready to emit a whole branch that immediately
1984 # in memory until we are ready to emit a whole branch that immediately
1985 # "merges" into an existing one. This reduces the number of parallel
1985 # "merges" into an existing one. This reduces the number of parallel
1986 # branches with interleaved revisions.
1986 # branches with interleaved revisions.
1987 #
1987 #
1988 # During iteration revs are split into two groups:
1988 # During iteration revs are split into two groups:
1989 # A) revision already emitted
1989 # A) revision already emitted
1990 # B) revision in "retention". They are stored as different subgroups.
1990 # B) revision in "retention". They are stored as different subgroups.
1991 #
1991 #
1992 # for each REV, we do the following logic:
1992 # for each REV, we do the following logic:
1993 #
1993 #
1994 # 1) if REV is a parent of (A), we will emit it. If there is a
1994 # 1) if REV is a parent of (A), we will emit it. If there is a
1995 # retention group ((B) above) that is blocked on REV being
1995 # retention group ((B) above) that is blocked on REV being
1996 # available, we emit all the revisions out of that retention
1996 # available, we emit all the revisions out of that retention
1997 # group first.
1997 # group first.
1998 #
1998 #
1999 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1999 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
2000 # available, if such subgroup exist, we add REV to it and the subgroup is
2000 # available, if such subgroup exist, we add REV to it and the subgroup is
2001 # now awaiting for REV.parents() to be available.
2001 # now awaiting for REV.parents() to be available.
2002 #
2002 #
2003 # 3) finally if no such group existed in (B), we create a new subgroup.
2003 # 3) finally if no such group existed in (B), we create a new subgroup.
2004 #
2004 #
2005 #
2005 #
2006 # To bootstrap the algorithm, we emit the tipmost revision (which
2006 # To bootstrap the algorithm, we emit the tipmost revision (which
2007 # puts it in group (A) from above).
2007 # puts it in group (A) from above).
2008
2008
2009 revs.sort(reverse=True)
2009 revs.sort(reverse=True)
2010
2010
2011 # Set of parents of revision that have been emitted. They can be considered
2011 # Set of parents of revision that have been emitted. They can be considered
2012 # unblocked as the graph generator is already aware of them so there is no
2012 # unblocked as the graph generator is already aware of them so there is no
2013 # need to delay the revisions that reference them.
2013 # need to delay the revisions that reference them.
2014 #
2014 #
2015 # If someone wants to prioritize a branch over the others, pre-filling this
2015 # If someone wants to prioritize a branch over the others, pre-filling this
2016 # set will force all other branches to wait until this branch is ready to be
2016 # set will force all other branches to wait until this branch is ready to be
2017 # emitted.
2017 # emitted.
2018 unblocked = set(firstbranch)
2018 unblocked = set(firstbranch)
2019
2019
2020 # list of groups waiting to be displayed, each group is defined by:
2020 # list of groups waiting to be displayed, each group is defined by:
2021 #
2021 #
2022 # (revs: lists of revs waiting to be displayed,
2022 # (revs: lists of revs waiting to be displayed,
2023 # blocked: set of that cannot be displayed before those in 'revs')
2023 # blocked: set of that cannot be displayed before those in 'revs')
2024 #
2024 #
2025 # The second value ('blocked') correspond to parents of any revision in the
2025 # The second value ('blocked') correspond to parents of any revision in the
2026 # group ('revs') that is not itself contained in the group. The main idea
2026 # group ('revs') that is not itself contained in the group. The main idea
2027 # of this algorithm is to delay as much as possible the emission of any
2027 # of this algorithm is to delay as much as possible the emission of any
2028 # revision. This means waiting for the moment we are about to display
2028 # revision. This means waiting for the moment we are about to display
2029 # these parents to display the revs in a group.
2029 # these parents to display the revs in a group.
2030 #
2030 #
2031 # This first implementation is smart until it encounters a merge: it will
2031 # This first implementation is smart until it encounters a merge: it will
2032 # emit revs as soon as any parent is about to be emitted and can grow an
2032 # emit revs as soon as any parent is about to be emitted and can grow an
2033 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2033 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2034 # retains new branches but gives up on any special ordering for ancestors
2034 # retains new branches but gives up on any special ordering for ancestors
2035 # of merges. The implementation can be improved to handle this better.
2035 # of merges. The implementation can be improved to handle this better.
2036 #
2036 #
2037 # The first subgroup is special. It corresponds to all the revision that
2037 # The first subgroup is special. It corresponds to all the revision that
2038 # were already emitted. The 'revs' lists is expected to be empty and the
2038 # were already emitted. The 'revs' lists is expected to be empty and the
2039 # 'blocked' set contains the parents revisions of already emitted revision.
2039 # 'blocked' set contains the parents revisions of already emitted revision.
2040 #
2040 #
2041 # You could pre-seed the <parents> set of groups[0] to a specific
2041 # You could pre-seed the <parents> set of groups[0] to a specific
2042 # changesets to select what the first emitted branch should be.
2042 # changesets to select what the first emitted branch should be.
2043 groups = [([], unblocked)]
2043 groups = [([], unblocked)]
2044 pendingheap = []
2044 pendingheap = []
2045 pendingset = set()
2045 pendingset = set()
2046
2046
2047 heapq.heapify(pendingheap)
2047 heapq.heapify(pendingheap)
2048 heappop = heapq.heappop
2048 heappop = heapq.heappop
2049 heappush = heapq.heappush
2049 heappush = heapq.heappush
2050 for currentrev in revs:
2050 for currentrev in revs:
2051 # Heap works with smallest element, we want highest so we invert
2051 # Heap works with smallest element, we want highest so we invert
2052 if currentrev not in pendingset:
2052 if currentrev not in pendingset:
2053 heappush(pendingheap, -currentrev)
2053 heappush(pendingheap, -currentrev)
2054 pendingset.add(currentrev)
2054 pendingset.add(currentrev)
2055 # iterates on pending rev until after the current rev have been
2055 # iterates on pending rev until after the current rev have been
2056 # processed.
2056 # processed.
2057 rev = None
2057 rev = None
2058 while rev != currentrev:
2058 while rev != currentrev:
2059 rev = -heappop(pendingheap)
2059 rev = -heappop(pendingheap)
2060 pendingset.remove(rev)
2060 pendingset.remove(rev)
2061
2061
2062 # Seek for a subgroup blocked, waiting for the current revision.
2062 # Seek for a subgroup blocked, waiting for the current revision.
2063 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2063 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2064
2064
2065 if matching:
2065 if matching:
2066 # The main idea is to gather together all sets that are blocked
2066 # The main idea is to gather together all sets that are blocked
2067 # on the same revision.
2067 # on the same revision.
2068 #
2068 #
2069 # Groups are merged when a common blocking ancestor is
2069 # Groups are merged when a common blocking ancestor is
2070 # observed. For example, given two groups:
2070 # observed. For example, given two groups:
2071 #
2071 #
2072 # revs [5, 4] waiting for 1
2072 # revs [5, 4] waiting for 1
2073 # revs [3, 2] waiting for 1
2073 # revs [3, 2] waiting for 1
2074 #
2074 #
2075 # These two groups will be merged when we process
2075 # These two groups will be merged when we process
2076 # 1. In theory, we could have merged the groups when
2076 # 1. In theory, we could have merged the groups when
2077 # we added 2 to the group it is now in (we could have
2077 # we added 2 to the group it is now in (we could have
2078 # noticed the groups were both blocked on 1 then), but
2078 # noticed the groups were both blocked on 1 then), but
2079 # the way it works now makes the algorithm simpler.
2079 # the way it works now makes the algorithm simpler.
2080 #
2080 #
2081 # We also always keep the oldest subgroup first. We can
2081 # We also always keep the oldest subgroup first. We can
2082 # probably improve the behavior by having the longest set
2082 # probably improve the behavior by having the longest set
2083 # first. That way, graph algorithms could minimise the length
2083 # first. That way, graph algorithms could minimise the length
2084 # of parallel lines their drawing. This is currently not done.
2084 # of parallel lines their drawing. This is currently not done.
2085 targetidx = matching.pop(0)
2085 targetidx = matching.pop(0)
2086 trevs, tparents = groups[targetidx]
2086 trevs, tparents = groups[targetidx]
2087 for i in matching:
2087 for i in matching:
2088 gr = groups[i]
2088 gr = groups[i]
2089 trevs.extend(gr[0])
2089 trevs.extend(gr[0])
2090 tparents |= gr[1]
2090 tparents |= gr[1]
2091 # delete all merged subgroups (except the one we kept)
2091 # delete all merged subgroups (except the one we kept)
2092 # (starting from the last subgroup for performance and
2092 # (starting from the last subgroup for performance and
2093 # sanity reasons)
2093 # sanity reasons)
2094 for i in reversed(matching):
2094 for i in reversed(matching):
2095 del groups[i]
2095 del groups[i]
2096 else:
2096 else:
2097 # This is a new head. We create a new subgroup for it.
2097 # This is a new head. We create a new subgroup for it.
2098 targetidx = len(groups)
2098 targetidx = len(groups)
2099 groups.append(([], set([rev])))
2099 groups.append(([], set([rev])))
2100
2100
2101 gr = groups[targetidx]
2101 gr = groups[targetidx]
2102
2102
2103 # We now add the current nodes to this subgroups. This is done
2103 # We now add the current nodes to this subgroups. This is done
2104 # after the subgroup merging because all elements from a subgroup
2104 # after the subgroup merging because all elements from a subgroup
2105 # that relied on this rev must precede it.
2105 # that relied on this rev must precede it.
2106 #
2106 #
2107 # we also update the <parents> set to include the parents of the
2107 # we also update the <parents> set to include the parents of the
2108 # new nodes.
2108 # new nodes.
2109 if rev == currentrev: # only display stuff in rev
2109 if rev == currentrev: # only display stuff in rev
2110 gr[0].append(rev)
2110 gr[0].append(rev)
2111 gr[1].remove(rev)
2111 gr[1].remove(rev)
2112 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2112 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2113 gr[1].update(parents)
2113 gr[1].update(parents)
2114 for p in parents:
2114 for p in parents:
2115 if p not in pendingset:
2115 if p not in pendingset:
2116 pendingset.add(p)
2116 pendingset.add(p)
2117 heappush(pendingheap, -p)
2117 heappush(pendingheap, -p)
2118
2118
2119 # Look for a subgroup to display
2119 # Look for a subgroup to display
2120 #
2120 #
2121 # When unblocked is empty (if clause), we were not waiting for any
2121 # When unblocked is empty (if clause), we were not waiting for any
2122 # revisions during the first iteration (if no priority was given) or
2122 # revisions during the first iteration (if no priority was given) or
2123 # if we emitted a whole disconnected set of the graph (reached a
2123 # if we emitted a whole disconnected set of the graph (reached a
2124 # root). In that case we arbitrarily take the oldest known
2124 # root). In that case we arbitrarily take the oldest known
2125 # subgroup. The heuristic could probably be better.
2125 # subgroup. The heuristic could probably be better.
2126 #
2126 #
2127 # Otherwise (elif clause) if the subgroup is blocked on
2127 # Otherwise (elif clause) if the subgroup is blocked on
2128 # a revision we just emitted, we can safely emit it as
2128 # a revision we just emitted, we can safely emit it as
2129 # well.
2129 # well.
2130 if not unblocked:
2130 if not unblocked:
2131 if len(groups) > 1: # display other subset
2131 if len(groups) > 1: # display other subset
2132 targetidx = 1
2132 targetidx = 1
2133 gr = groups[1]
2133 gr = groups[1]
2134 elif not gr[1] & unblocked:
2134 elif not gr[1] & unblocked:
2135 gr = None
2135 gr = None
2136
2136
2137 if gr is not None:
2137 if gr is not None:
2138 # update the set of awaited revisions with the one from the
2138 # update the set of awaited revisions with the one from the
2139 # subgroup
2139 # subgroup
2140 unblocked |= gr[1]
2140 unblocked |= gr[1]
2141 # output all revisions in the subgroup
2141 # output all revisions in the subgroup
2142 for r in gr[0]:
2142 for r in gr[0]:
2143 yield r
2143 yield r
2144 # delete the subgroup that you just output
2144 # delete the subgroup that you just output
2145 # unless it is groups[0] in which case you just empty it.
2145 # unless it is groups[0] in which case you just empty it.
2146 if targetidx:
2146 if targetidx:
2147 del groups[targetidx]
2147 del groups[targetidx]
2148 else:
2148 else:
2149 gr[0][:] = []
2149 gr[0][:] = []
2150 # Check if we have some subgroup waiting for revisions we are not going to
2150 # Check if we have some subgroup waiting for revisions we are not going to
2151 # iterate over
2151 # iterate over
2152 for g in groups:
2152 for g in groups:
2153 for r in g[0]:
2153 for r in g[0]:
2154 yield r
2154 yield r
2155
2155
2156 @predicate('subrepo([pattern])')
2156 @predicate('subrepo([pattern])')
2157 def subrepo(repo, subset, x):
2157 def subrepo(repo, subset, x):
2158 """Changesets that add, modify or remove the given subrepo. If no subrepo
2158 """Changesets that add, modify or remove the given subrepo. If no subrepo
2159 pattern is named, any subrepo changes are returned.
2159 pattern is named, any subrepo changes are returned.
2160 """
2160 """
2161 # i18n: "subrepo" is a keyword
2161 # i18n: "subrepo" is a keyword
2162 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2162 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2163 pat = None
2163 pat = None
2164 if len(args) != 0:
2164 if len(args) != 0:
2165 pat = getstring(args[0], _("subrepo requires a pattern"))
2165 pat = getstring(args[0], _("subrepo requires a pattern"))
2166
2166
2167 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2167 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2168
2168
2169 def submatches(names):
2169 def submatches(names):
2170 k, p, m = util.stringmatcher(pat)
2170 k, p, m = util.stringmatcher(pat)
2171 for name in names:
2171 for name in names:
2172 if m(name):
2172 if m(name):
2173 yield name
2173 yield name
2174
2174
2175 def matches(x):
2175 def matches(x):
2176 c = repo[x]
2176 c = repo[x]
2177 s = repo.status(c.p1().node(), c.node(), match=m)
2177 s = repo.status(c.p1().node(), c.node(), match=m)
2178
2178
2179 if pat is None:
2179 if pat is None:
2180 return s.added or s.modified or s.removed
2180 return s.added or s.modified or s.removed
2181
2181
2182 if s.added:
2182 if s.added:
2183 return any(submatches(c.substate.keys()))
2183 return any(submatches(c.substate.keys()))
2184
2184
2185 if s.modified:
2185 if s.modified:
2186 subs = set(c.p1().substate.keys())
2186 subs = set(c.p1().substate.keys())
2187 subs.update(c.substate.keys())
2187 subs.update(c.substate.keys())
2188
2188
2189 for path in submatches(subs):
2189 for path in submatches(subs):
2190 if c.p1().substate.get(path) != c.substate.get(path):
2190 if c.p1().substate.get(path) != c.substate.get(path):
2191 return True
2191 return True
2192
2192
2193 if s.removed:
2193 if s.removed:
2194 return any(submatches(c.p1().substate.keys()))
2194 return any(submatches(c.p1().substate.keys()))
2195
2195
2196 return False
2196 return False
2197
2197
2198 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2198 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2199
2199
2200 def _substringmatcher(pattern):
2200 def _substringmatcher(pattern):
2201 kind, pattern, matcher = util.stringmatcher(pattern)
2201 kind, pattern, matcher = util.stringmatcher(pattern)
2202 if kind == 'literal':
2202 if kind == 'literal':
2203 matcher = lambda s: pattern in s
2203 matcher = lambda s: pattern in s
2204 return kind, pattern, matcher
2204 return kind, pattern, matcher
2205
2205
2206 @predicate('tag([name])', safe=True)
2206 @predicate('tag([name])', safe=True)
2207 def tag(repo, subset, x):
2207 def tag(repo, subset, x):
2208 """The specified tag by name, or all tagged revisions if no name is given.
2208 """The specified tag by name, or all tagged revisions if no name is given.
2209
2209
2210 If `name` starts with `re:`, the remainder of the name is treated as
2210 If `name` starts with `re:`, the remainder of the name is treated as
2211 a regular expression. To match a tag that actually starts with `re:`,
2211 a regular expression. To match a tag that actually starts with `re:`,
2212 use the prefix `literal:`.
2212 use the prefix `literal:`.
2213 """
2213 """
2214 # i18n: "tag" is a keyword
2214 # i18n: "tag" is a keyword
2215 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2215 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2216 cl = repo.changelog
2216 cl = repo.changelog
2217 if args:
2217 if args:
2218 pattern = getstring(args[0],
2218 pattern = getstring(args[0],
2219 # i18n: "tag" is a keyword
2219 # i18n: "tag" is a keyword
2220 _('the argument to tag must be a string'))
2220 _('the argument to tag must be a string'))
2221 kind, pattern, matcher = util.stringmatcher(pattern)
2221 kind, pattern, matcher = util.stringmatcher(pattern)
2222 if kind == 'literal':
2222 if kind == 'literal':
2223 # avoid resolving all tags
2223 # avoid resolving all tags
2224 tn = repo._tagscache.tags.get(pattern, None)
2224 tn = repo._tagscache.tags.get(pattern, None)
2225 if tn is None:
2225 if tn is None:
2226 raise error.RepoLookupError(_("tag '%s' does not exist")
2226 raise error.RepoLookupError(_("tag '%s' does not exist")
2227 % pattern)
2227 % pattern)
2228 s = set([repo[tn].rev()])
2228 s = set([repo[tn].rev()])
2229 else:
2229 else:
2230 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2230 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2231 else:
2231 else:
2232 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2232 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2233 return subset & s
2233 return subset & s
2234
2234
2235 @predicate('tagged', safe=True)
2235 @predicate('tagged', safe=True)
2236 def tagged(repo, subset, x):
2236 def tagged(repo, subset, x):
2237 return tag(repo, subset, x)
2237 return tag(repo, subset, x)
2238
2238
2239 @predicate('unstable()', safe=True)
2239 @predicate('unstable()', safe=True)
2240 def unstable(repo, subset, x):
2240 def unstable(repo, subset, x):
2241 """Non-obsolete changesets with obsolete ancestors.
2241 """Non-obsolete changesets with obsolete ancestors.
2242 """
2242 """
2243 # i18n: "unstable" is a keyword
2243 # i18n: "unstable" is a keyword
2244 getargs(x, 0, 0, _("unstable takes no arguments"))
2244 getargs(x, 0, 0, _("unstable takes no arguments"))
2245 unstables = obsmod.getrevs(repo, 'unstable')
2245 unstables = obsmod.getrevs(repo, 'unstable')
2246 return subset & unstables
2246 return subset & unstables
2247
2247
2248
2248
2249 @predicate('user(string)', safe=True)
2249 @predicate('user(string)', safe=True)
2250 def user(repo, subset, x):
2250 def user(repo, subset, x):
2251 """User name contains string. The match is case-insensitive.
2251 """User name contains string. The match is case-insensitive.
2252
2252
2253 If `string` starts with `re:`, the remainder of the string is treated as
2253 If `string` starts with `re:`, the remainder of the string is treated as
2254 a regular expression. To match a user that actually contains `re:`, use
2254 a regular expression. To match a user that actually contains `re:`, use
2255 the prefix `literal:`.
2255 the prefix `literal:`.
2256 """
2256 """
2257 return author(repo, subset, x)
2257 return author(repo, subset, x)
2258
2258
2259 # experimental
2259 # experimental
2260 @predicate('wdir', safe=True)
2260 @predicate('wdir', safe=True)
2261 def wdir(repo, subset, x):
2261 def wdir(repo, subset, x):
2262 # i18n: "wdir" is a keyword
2262 # i18n: "wdir" is a keyword
2263 getargs(x, 0, 0, _("wdir takes no arguments"))
2263 getargs(x, 0, 0, _("wdir takes no arguments"))
2264 if node.wdirrev in subset or isinstance(subset, fullreposet):
2264 if node.wdirrev in subset or isinstance(subset, fullreposet):
2265 return baseset([node.wdirrev])
2265 return baseset([node.wdirrev])
2266 return baseset()
2266 return baseset()
2267
2267
2268 def _orderedlist(repo, subset, x):
2268 def _orderedlist(repo, subset, x):
2269 s = getstring(x, "internal error")
2269 s = getstring(x, "internal error")
2270 if not s:
2270 if not s:
2271 return baseset()
2271 return baseset()
2272 # remove duplicates here. it's difficult for caller to deduplicate sets
2272 # remove duplicates here. it's difficult for caller to deduplicate sets
2273 # because different symbols can point to the same rev.
2273 # because different symbols can point to the same rev.
2274 cl = repo.changelog
2274 cl = repo.changelog
2275 ls = []
2275 ls = []
2276 seen = set()
2276 seen = set()
2277 for t in s.split('\0'):
2277 for t in s.split('\0'):
2278 try:
2278 try:
2279 # fast path for integer revision
2279 # fast path for integer revision
2280 r = int(t)
2280 r = int(t)
2281 if str(r) != t or r not in cl:
2281 if str(r) != t or r not in cl:
2282 raise ValueError
2282 raise ValueError
2283 revs = [r]
2283 revs = [r]
2284 except ValueError:
2284 except ValueError:
2285 revs = stringset(repo, subset, t)
2285 revs = stringset(repo, subset, t)
2286
2286
2287 for r in revs:
2287 for r in revs:
2288 if r in seen:
2288 if r in seen:
2289 continue
2289 continue
2290 if (r in subset
2290 if (r in subset
2291 or r == node.nullrev and isinstance(subset, fullreposet)):
2291 or r == node.nullrev and isinstance(subset, fullreposet)):
2292 ls.append(r)
2292 ls.append(r)
2293 seen.add(r)
2293 seen.add(r)
2294 return baseset(ls)
2294 return baseset(ls)
2295
2295
2296 # for internal use
2296 # for internal use
2297 @predicate('_list', safe=True, takeorder=True)
2297 @predicate('_list', safe=True, takeorder=True)
2298 def _list(repo, subset, x, order):
2298 def _list(repo, subset, x, order):
2299 if order == followorder:
2299 if order == followorder:
2300 # slow path to take the subset order
2300 # slow path to take the subset order
2301 return subset & _orderedlist(repo, fullreposet(repo), x)
2301 return subset & _orderedlist(repo, fullreposet(repo), x)
2302 else:
2302 else:
2303 return _orderedlist(repo, subset, x)
2303 return _orderedlist(repo, subset, x)
2304
2304
2305 def _orderedintlist(repo, subset, x):
2305 def _orderedintlist(repo, subset, x):
2306 s = getstring(x, "internal error")
2306 s = getstring(x, "internal error")
2307 if not s:
2307 if not s:
2308 return baseset()
2308 return baseset()
2309 ls = [int(r) for r in s.split('\0')]
2309 ls = [int(r) for r in s.split('\0')]
2310 s = subset
2310 s = subset
2311 return baseset([r for r in ls if r in s])
2311 return baseset([r for r in ls if r in s])
2312
2312
2313 # for internal use
2313 # for internal use
2314 @predicate('_intlist', safe=True, takeorder=True)
2314 @predicate('_intlist', safe=True, takeorder=True)
2315 def _intlist(repo, subset, x, order):
2315 def _intlist(repo, subset, x, order):
2316 if order == followorder:
2316 if order == followorder:
2317 # slow path to take the subset order
2317 # slow path to take the subset order
2318 return subset & _orderedintlist(repo, fullreposet(repo), x)
2318 return subset & _orderedintlist(repo, fullreposet(repo), x)
2319 else:
2319 else:
2320 return _orderedintlist(repo, subset, x)
2320 return _orderedintlist(repo, subset, x)
2321
2321
2322 def _orderedhexlist(repo, subset, x):
2322 def _orderedhexlist(repo, subset, x):
2323 s = getstring(x, "internal error")
2323 s = getstring(x, "internal error")
2324 if not s:
2324 if not s:
2325 return baseset()
2325 return baseset()
2326 cl = repo.changelog
2326 cl = repo.changelog
2327 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2327 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2328 s = subset
2328 s = subset
2329 return baseset([r for r in ls if r in s])
2329 return baseset([r for r in ls if r in s])
2330
2330
2331 # for internal use
2331 # for internal use
2332 @predicate('_hexlist', safe=True, takeorder=True)
2332 @predicate('_hexlist', safe=True, takeorder=True)
2333 def _hexlist(repo, subset, x, order):
2333 def _hexlist(repo, subset, x, order):
2334 if order == followorder:
2334 if order == followorder:
2335 # slow path to take the subset order
2335 # slow path to take the subset order
2336 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2336 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2337 else:
2337 else:
2338 return _orderedhexlist(repo, subset, x)
2338 return _orderedhexlist(repo, subset, x)
2339
2339
2340 methods = {
2340 methods = {
2341 "range": rangeset,
2341 "range": rangeset,
2342 "rangepre": rangepre,
2342 "rangepre": rangepre,
2343 "dagrange": dagrange,
2343 "dagrange": dagrange,
2344 "string": stringset,
2344 "string": stringset,
2345 "symbol": stringset,
2345 "symbol": stringset,
2346 "and": andset,
2346 "and": andset,
2347 "or": orset,
2347 "or": orset,
2348 "not": notset,
2348 "not": notset,
2349 "difference": differenceset,
2349 "difference": differenceset,
2350 "list": listset,
2350 "list": listset,
2351 "keyvalue": keyvaluepair,
2351 "keyvalue": keyvaluepair,
2352 "func": func,
2352 "func": func,
2353 "ancestor": ancestorspec,
2353 "ancestor": ancestorspec,
2354 "parent": parentspec,
2354 "parent": parentspec,
2355 "parentpost": parentpost,
2355 "parentpost": parentpost,
2356 }
2356 }
2357
2357
2358 # Constants for ordering requirement, used in _analyze():
2358 # Constants for ordering requirement, used in _analyze():
2359 #
2359 #
2360 # If 'define', any nested functions and operations can change the ordering of
2360 # If 'define', any nested functions and operations can change the ordering of
2361 # the entries in the set. If 'follow', any nested functions and operations
2361 # the entries in the set. If 'follow', any nested functions and operations
2362 # should take the ordering specified by the first operand to the '&' operator.
2362 # should take the ordering specified by the first operand to the '&' operator.
2363 #
2363 #
2364 # For instance,
2364 # For instance,
2365 #
2365 #
2366 # X & (Y | Z)
2366 # X & (Y | Z)
2367 # ^ ^^^^^^^
2367 # ^ ^^^^^^^
2368 # | follow
2368 # | follow
2369 # define
2369 # define
2370 #
2370 #
2371 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2371 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2372 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2372 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2373 #
2373 #
2374 # 'any' means the order doesn't matter. For instance,
2374 # 'any' means the order doesn't matter. For instance,
2375 #
2375 #
2376 # X & !Y
2376 # X & !Y
2377 # ^
2377 # ^
2378 # any
2378 # any
2379 #
2379 #
2380 # 'y()' can either enforce its ordering requirement or take the ordering
2380 # 'y()' can either enforce its ordering requirement or take the ordering
2381 # specified by 'x()' because 'not()' doesn't care the order.
2381 # specified by 'x()' because 'not()' doesn't care the order.
2382 #
2382 #
2383 # Transition of ordering requirement:
2383 # Transition of ordering requirement:
2384 #
2384 #
2385 # 1. starts with 'define'
2385 # 1. starts with 'define'
2386 # 2. shifts to 'follow' by 'x & y'
2386 # 2. shifts to 'follow' by 'x & y'
2387 # 3. changes back to 'define' on function call 'f(x)' or function-like
2387 # 3. changes back to 'define' on function call 'f(x)' or function-like
2388 # operation 'x (f) y' because 'f' may have its own ordering requirement
2388 # operation 'x (f) y' because 'f' may have its own ordering requirement
2389 # for 'x' and 'y' (e.g. 'first(x)')
2389 # for 'x' and 'y' (e.g. 'first(x)')
2390 #
2390 #
2391 anyorder = 'any' # don't care the order
2391 anyorder = 'any' # don't care the order
2392 defineorder = 'define' # should define the order
2392 defineorder = 'define' # should define the order
2393 followorder = 'follow' # must follow the current order
2393 followorder = 'follow' # must follow the current order
2394
2394
2395 # transition table for 'x & y', from the current expression 'x' to 'y'
2395 # transition table for 'x & y', from the current expression 'x' to 'y'
2396 _tofolloworder = {
2396 _tofolloworder = {
2397 anyorder: anyorder,
2397 anyorder: anyorder,
2398 defineorder: followorder,
2398 defineorder: followorder,
2399 followorder: followorder,
2399 followorder: followorder,
2400 }
2400 }
2401
2401
2402 def _matchonly(revs, bases):
2402 def _matchonly(revs, bases):
2403 """
2403 """
2404 >>> f = lambda *args: _matchonly(*map(parse, args))
2404 >>> f = lambda *args: _matchonly(*map(parse, args))
2405 >>> f('ancestors(A)', 'not ancestors(B)')
2405 >>> f('ancestors(A)', 'not ancestors(B)')
2406 ('list', ('symbol', 'A'), ('symbol', 'B'))
2406 ('list', ('symbol', 'A'), ('symbol', 'B'))
2407 """
2407 """
2408 if (revs is not None
2408 if (revs is not None
2409 and revs[0] == 'func'
2409 and revs[0] == 'func'
2410 and getsymbol(revs[1]) == 'ancestors'
2410 and getsymbol(revs[1]) == 'ancestors'
2411 and bases is not None
2411 and bases is not None
2412 and bases[0] == 'not'
2412 and bases[0] == 'not'
2413 and bases[1][0] == 'func'
2413 and bases[1][0] == 'func'
2414 and getsymbol(bases[1][1]) == 'ancestors'):
2414 and getsymbol(bases[1][1]) == 'ancestors'):
2415 return ('list', revs[2], bases[1][2])
2415 return ('list', revs[2], bases[1][2])
2416
2416
2417 def _fixops(x):
2417 def _fixops(x):
2418 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2418 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2419 handled well by our simple top-down parser"""
2419 handled well by our simple top-down parser"""
2420 if not isinstance(x, tuple):
2420 if not isinstance(x, tuple):
2421 return x
2421 return x
2422
2422
2423 op = x[0]
2423 op = x[0]
2424 if op == 'parent':
2424 if op == 'parent':
2425 # x^:y means (x^) : y, not x ^ (:y)
2425 # x^:y means (x^) : y, not x ^ (:y)
2426 # x^: means (x^) :, not x ^ (:)
2426 # x^: means (x^) :, not x ^ (:)
2427 post = ('parentpost', x[1])
2427 post = ('parentpost', x[1])
2428 if x[2][0] == 'dagrangepre':
2428 if x[2][0] == 'dagrangepre':
2429 return _fixops(('dagrange', post, x[2][1]))
2429 return _fixops(('dagrange', post, x[2][1]))
2430 elif x[2][0] == 'rangepre':
2430 elif x[2][0] == 'rangepre':
2431 return _fixops(('range', post, x[2][1]))
2431 return _fixops(('range', post, x[2][1]))
2432 elif x[2][0] == 'rangeall':
2432 elif x[2][0] == 'rangeall':
2433 return _fixops(('rangepost', post))
2433 return _fixops(('rangepost', post))
2434 elif op == 'or':
2434 elif op == 'or':
2435 # make number of arguments deterministic:
2435 # make number of arguments deterministic:
2436 # x + y + z -> (or x y z) -> (or (list x y z))
2436 # x + y + z -> (or x y z) -> (or (list x y z))
2437 return (op, _fixops(('list',) + x[1:]))
2437 return (op, _fixops(('list',) + x[1:]))
2438
2438
2439 return (op,) + tuple(_fixops(y) for y in x[1:])
2439 return (op,) + tuple(_fixops(y) for y in x[1:])
2440
2440
2441 def _analyze(x, order):
2441 def _analyze(x, order):
2442 if x is None:
2442 if x is None:
2443 return x
2443 return x
2444
2444
2445 op = x[0]
2445 op = x[0]
2446 if op == 'minus':
2446 if op == 'minus':
2447 return _analyze(('and', x[1], ('not', x[2])), order)
2447 return _analyze(('and', x[1], ('not', x[2])), order)
2448 elif op == 'only':
2448 elif op == 'only':
2449 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2449 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2450 return _analyze(t, order)
2450 return _analyze(t, order)
2451 elif op == 'onlypost':
2451 elif op == 'onlypost':
2452 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2452 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2453 elif op == 'dagrangepre':
2453 elif op == 'dagrangepre':
2454 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2454 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2455 elif op == 'dagrangepost':
2455 elif op == 'dagrangepost':
2456 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2456 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2457 elif op == 'rangeall':
2457 elif op == 'rangeall':
2458 return _analyze(('rangepre', ('string', 'tip')), order)
2458 return _analyze(('rangepre', ('string', 'tip')), order)
2459 elif op == 'rangepost':
2459 elif op == 'rangepost':
2460 return _analyze(('range', x[1], ('string', 'tip')), order)
2460 return _analyze(('range', x[1], ('string', 'tip')), order)
2461 elif op == 'negate':
2461 elif op == 'negate':
2462 s = getstring(x[1], _("can't negate that"))
2462 s = getstring(x[1], _("can't negate that"))
2463 return _analyze(('string', '-' + s), order)
2463 return _analyze(('string', '-' + s), order)
2464 elif op in ('string', 'symbol'):
2464 elif op in ('string', 'symbol'):
2465 return x
2465 return x
2466 elif op == 'and':
2466 elif op == 'and':
2467 ta = _analyze(x[1], order)
2467 ta = _analyze(x[1], order)
2468 tb = _analyze(x[2], _tofolloworder[order])
2468 tb = _analyze(x[2], _tofolloworder[order])
2469 return (op, ta, tb, order)
2469 return (op, ta, tb, order)
2470 elif op == 'or':
2470 elif op == 'or':
2471 return (op, _analyze(x[1], order), order)
2471 return (op, _analyze(x[1], order), order)
2472 elif op == 'not':
2472 elif op == 'not':
2473 return (op, _analyze(x[1], anyorder), order)
2473 return (op, _analyze(x[1], anyorder), order)
2474 elif op in ('rangepre', 'parentpost'):
2474 elif op in ('rangepre', 'parentpost'):
2475 return (op, _analyze(x[1], defineorder), order)
2475 return (op, _analyze(x[1], defineorder), order)
2476 elif op == 'group':
2476 elif op == 'group':
2477 return _analyze(x[1], order)
2477 return _analyze(x[1], order)
2478 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2478 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2479 ta = _analyze(x[1], defineorder)
2479 ta = _analyze(x[1], defineorder)
2480 tb = _analyze(x[2], defineorder)
2480 tb = _analyze(x[2], defineorder)
2481 return (op, ta, tb, order)
2481 return (op, ta, tb, order)
2482 elif op == 'list':
2482 elif op == 'list':
2483 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2483 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2484 elif op == 'keyvalue':
2484 elif op == 'keyvalue':
2485 return (op, x[1], _analyze(x[2], order))
2485 return (op, x[1], _analyze(x[2], order))
2486 elif op == 'func':
2486 elif op == 'func':
2487 f = getsymbol(x[1])
2487 f = getsymbol(x[1])
2488 d = defineorder
2488 d = defineorder
2489 if f == 'present':
2489 if f == 'present':
2490 # 'present(set)' is known to return the argument set with no
2490 # 'present(set)' is known to return the argument set with no
2491 # modification, so forward the current order to its argument
2491 # modification, so forward the current order to its argument
2492 d = order
2492 d = order
2493 return (op, x[1], _analyze(x[2], d), order)
2493 return (op, x[1], _analyze(x[2], d), order)
2494 raise ValueError('invalid operator %r' % op)
2494 raise ValueError('invalid operator %r' % op)
2495
2495
2496 def analyze(x, order=defineorder):
2496 def analyze(x, order=defineorder):
2497 """Transform raw parsed tree to evaluatable tree which can be fed to
2497 """Transform raw parsed tree to evaluatable tree which can be fed to
2498 optimize() or getset()
2498 optimize() or getset()
2499
2499
2500 All pseudo operations should be mapped to real operations or functions
2500 All pseudo operations should be mapped to real operations or functions
2501 defined in methods or symbols table respectively.
2501 defined in methods or symbols table respectively.
2502
2502
2503 'order' specifies how the current expression 'x' is ordered (see the
2503 'order' specifies how the current expression 'x' is ordered (see the
2504 constants defined above.)
2504 constants defined above.)
2505 """
2505 """
2506 return _analyze(x, order)
2506 return _analyze(x, order)
2507
2507
2508 def _optimize(x, small):
2508 def _optimize(x, small):
2509 if x is None:
2509 if x is None:
2510 return 0, x
2510 return 0, x
2511
2511
2512 smallbonus = 1
2512 smallbonus = 1
2513 if small:
2513 if small:
2514 smallbonus = .5
2514 smallbonus = .5
2515
2515
2516 op = x[0]
2516 op = x[0]
2517 if op in ('string', 'symbol'):
2517 if op in ('string', 'symbol'):
2518 return smallbonus, x # single revisions are small
2518 return smallbonus, x # single revisions are small
2519 elif op == 'and':
2519 elif op == 'and':
2520 wa, ta = _optimize(x[1], True)
2520 wa, ta = _optimize(x[1], True)
2521 wb, tb = _optimize(x[2], True)
2521 wb, tb = _optimize(x[2], True)
2522 order = x[3]
2522 order = x[3]
2523 w = min(wa, wb)
2523 w = min(wa, wb)
2524
2524
2525 # (::x and not ::y)/(not ::y and ::x) have a fast path
2525 # (::x and not ::y)/(not ::y and ::x) have a fast path
2526 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2526 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2527 if tm:
2527 if tm:
2528 return w, ('func', ('symbol', 'only'), tm, order)
2528 return w, ('func', ('symbol', 'only'), tm, order)
2529
2529
2530 if tb is not None and tb[0] == 'not':
2530 if tb is not None and tb[0] == 'not':
2531 return wa, ('difference', ta, tb[1], order)
2531 return wa, ('difference', ta, tb[1], order)
2532
2532
2533 if wa > wb:
2533 if wa > wb:
2534 return w, (op, tb, ta, order)
2534 return w, (op, tb, ta, order)
2535 return w, (op, ta, tb, order)
2535 return w, (op, ta, tb, order)
2536 elif op == 'or':
2536 elif op == 'or':
2537 # fast path for machine-generated expression, that is likely to have
2537 # fast path for machine-generated expression, that is likely to have
2538 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2538 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2539 order = x[2]
2539 order = x[2]
2540 ws, ts, ss = [], [], []
2540 ws, ts, ss = [], [], []
2541 def flushss():
2541 def flushss():
2542 if not ss:
2542 if not ss:
2543 return
2543 return
2544 if len(ss) == 1:
2544 if len(ss) == 1:
2545 w, t = ss[0]
2545 w, t = ss[0]
2546 else:
2546 else:
2547 s = '\0'.join(t[1] for w, t in ss)
2547 s = '\0'.join(t[1] for w, t in ss)
2548 y = ('func', ('symbol', '_list'), ('string', s), order)
2548 y = ('func', ('symbol', '_list'), ('string', s), order)
2549 w, t = _optimize(y, False)
2549 w, t = _optimize(y, False)
2550 ws.append(w)
2550 ws.append(w)
2551 ts.append(t)
2551 ts.append(t)
2552 del ss[:]
2552 del ss[:]
2553 for y in getlist(x[1]):
2553 for y in getlist(x[1]):
2554 w, t = _optimize(y, False)
2554 w, t = _optimize(y, False)
2555 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2555 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2556 ss.append((w, t))
2556 ss.append((w, t))
2557 continue
2557 continue
2558 flushss()
2558 flushss()
2559 ws.append(w)
2559 ws.append(w)
2560 ts.append(t)
2560 ts.append(t)
2561 flushss()
2561 flushss()
2562 if len(ts) == 1:
2562 if len(ts) == 1:
2563 return ws[0], ts[0] # 'or' operation is fully optimized out
2563 return ws[0], ts[0] # 'or' operation is fully optimized out
2564 # we can't reorder trees by weight because it would change the order.
2564 # we can't reorder trees by weight because it would change the order.
2565 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2565 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2566 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2566 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2567 return max(ws), (op, ('list',) + tuple(ts), order)
2567 return max(ws), (op, ('list',) + tuple(ts), order)
2568 elif op == 'not':
2568 elif op == 'not':
2569 # Optimize not public() to _notpublic() because we have a fast version
2569 # Optimize not public() to _notpublic() because we have a fast version
2570 if x[1][:3] == ('func', ('symbol', 'public'), None):
2570 if x[1][:3] == ('func', ('symbol', 'public'), None):
2571 order = x[1][3]
2571 order = x[1][3]
2572 newsym = ('func', ('symbol', '_notpublic'), None, order)
2572 newsym = ('func', ('symbol', '_notpublic'), None, order)
2573 o = _optimize(newsym, not small)
2573 o = _optimize(newsym, not small)
2574 return o[0], o[1]
2574 return o[0], o[1]
2575 else:
2575 else:
2576 o = _optimize(x[1], not small)
2576 o = _optimize(x[1], not small)
2577 order = x[2]
2577 order = x[2]
2578 return o[0], (op, o[1], order)
2578 return o[0], (op, o[1], order)
2579 elif op in ('rangepre', 'parentpost'):
2579 elif op in ('rangepre', 'parentpost'):
2580 o = _optimize(x[1], small)
2580 o = _optimize(x[1], small)
2581 order = x[2]
2581 order = x[2]
2582 return o[0], (op, o[1], order)
2582 return o[0], (op, o[1], order)
2583 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2583 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2584 wa, ta = _optimize(x[1], small)
2584 wa, ta = _optimize(x[1], small)
2585 wb, tb = _optimize(x[2], small)
2585 wb, tb = _optimize(x[2], small)
2586 order = x[3]
2586 order = x[3]
2587 return wa + wb, (op, ta, tb, order)
2587 return wa + wb, (op, ta, tb, order)
2588 elif op == 'list':
2588 elif op == 'list':
2589 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2589 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2590 return sum(ws), (op,) + ts
2590 return sum(ws), (op,) + ts
2591 elif op == 'keyvalue':
2591 elif op == 'keyvalue':
2592 w, t = _optimize(x[2], small)
2592 w, t = _optimize(x[2], small)
2593 return w, (op, x[1], t)
2593 return w, (op, x[1], t)
2594 elif op == 'func':
2594 elif op == 'func':
2595 f = getsymbol(x[1])
2595 f = getsymbol(x[1])
2596 wa, ta = _optimize(x[2], small)
2596 wa, ta = _optimize(x[2], small)
2597 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2597 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2598 'keyword', 'outgoing', 'user'):
2598 'keyword', 'outgoing', 'user'):
2599 w = 10 # slow
2599 w = 10 # slow
2600 elif f in ('modifies', 'adds', 'removes'):
2600 elif f in ('modifies', 'adds', 'removes'):
2601 w = 30 # slower
2601 w = 30 # slower
2602 elif f == "contains":
2602 elif f == "contains":
2603 w = 100 # very slow
2603 w = 100 # very slow
2604 elif f == "ancestor":
2604 elif f == "ancestor":
2605 w = 1 * smallbonus
2605 w = 1 * smallbonus
2606 elif f in ('reverse', 'limit', 'first', '_intlist'):
2606 elif f in ('reverse', 'limit', 'first', '_intlist'):
2607 w = 0
2607 w = 0
2608 elif f == "sort":
2608 elif f == "sort":
2609 w = 10 # assume most sorts look at changelog
2609 w = 10 # assume most sorts look at changelog
2610 else:
2610 else:
2611 w = 1
2611 w = 1
2612 order = x[3]
2612 order = x[3]
2613 return w + wa, (op, x[1], ta, order)
2613 return w + wa, (op, x[1], ta, order)
2614 raise ValueError('invalid operator %r' % op)
2614 raise ValueError('invalid operator %r' % op)
2615
2615
2616 def optimize(tree):
2616 def optimize(tree):
2617 """Optimize evaluatable tree
2617 """Optimize evaluatable tree
2618
2618
2619 All pseudo operations should be transformed beforehand.
2619 All pseudo operations should be transformed beforehand.
2620 """
2620 """
2621 _weight, newtree = _optimize(tree, small=True)
2621 _weight, newtree = _optimize(tree, small=True)
2622 return newtree
2622 return newtree
2623
2623
2624 # the set of valid characters for the initial letter of symbols in
2624 # the set of valid characters for the initial letter of symbols in
2625 # alias declarations and definitions
2625 # alias declarations and definitions
2626 _aliassyminitletters = _syminitletters | set(pycompat.sysstr('$'))
2626 _aliassyminitletters = _syminitletters | set(pycompat.sysstr('$'))
2627
2627
2628 def _parsewith(spec, lookup=None, syminitletters=None):
2628 def _parsewith(spec, lookup=None, syminitletters=None):
2629 """Generate a parse tree of given spec with given tokenizing options
2629 """Generate a parse tree of given spec with given tokenizing options
2630
2630
2631 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2631 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2632 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2632 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2633 >>> _parsewith('$1')
2633 >>> _parsewith('$1')
2634 Traceback (most recent call last):
2634 Traceback (most recent call last):
2635 ...
2635 ...
2636 ParseError: ("syntax error in revset '$1'", 0)
2636 ParseError: ("syntax error in revset '$1'", 0)
2637 >>> _parsewith('foo bar')
2637 >>> _parsewith('foo bar')
2638 Traceback (most recent call last):
2638 Traceback (most recent call last):
2639 ...
2639 ...
2640 ParseError: ('invalid token', 4)
2640 ParseError: ('invalid token', 4)
2641 """
2641 """
2642 p = parser.parser(elements)
2642 p = parser.parser(elements)
2643 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2643 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2644 syminitletters=syminitletters))
2644 syminitletters=syminitletters))
2645 if pos != len(spec):
2645 if pos != len(spec):
2646 raise error.ParseError(_('invalid token'), pos)
2646 raise error.ParseError(_('invalid token'), pos)
2647 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2647 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2648
2648
2649 class _aliasrules(parser.basealiasrules):
2649 class _aliasrules(parser.basealiasrules):
2650 """Parsing and expansion rule set of revset aliases"""
2650 """Parsing and expansion rule set of revset aliases"""
2651 _section = _('revset alias')
2651 _section = _('revset alias')
2652
2652
2653 @staticmethod
2653 @staticmethod
2654 def _parse(spec):
2654 def _parse(spec):
2655 """Parse alias declaration/definition ``spec``
2655 """Parse alias declaration/definition ``spec``
2656
2656
2657 This allows symbol names to use also ``$`` as an initial letter
2657 This allows symbol names to use also ``$`` as an initial letter
2658 (for backward compatibility), and callers of this function should
2658 (for backward compatibility), and callers of this function should
2659 examine whether ``$`` is used also for unexpected symbols or not.
2659 examine whether ``$`` is used also for unexpected symbols or not.
2660 """
2660 """
2661 return _parsewith(spec, syminitletters=_aliassyminitletters)
2661 return _parsewith(spec, syminitletters=_aliassyminitletters)
2662
2662
2663 @staticmethod
2663 @staticmethod
2664 def _trygetfunc(tree):
2664 def _trygetfunc(tree):
2665 if tree[0] == 'func' and tree[1][0] == 'symbol':
2665 if tree[0] == 'func' and tree[1][0] == 'symbol':
2666 return tree[1][1], getlist(tree[2])
2666 return tree[1][1], getlist(tree[2])
2667
2667
2668 def expandaliases(ui, tree):
2668 def expandaliases(ui, tree):
2669 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2669 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2670 tree = _aliasrules.expand(aliases, tree)
2670 tree = _aliasrules.expand(aliases, tree)
2671 # warn about problematic (but not referred) aliases
2671 # warn about problematic (but not referred) aliases
2672 for name, alias in sorted(aliases.iteritems()):
2672 for name, alias in sorted(aliases.iteritems()):
2673 if alias.error and not alias.warned:
2673 if alias.error and not alias.warned:
2674 ui.warn(_('warning: %s\n') % (alias.error))
2674 ui.warn(_('warning: %s\n') % (alias.error))
2675 alias.warned = True
2675 alias.warned = True
2676 return tree
2676 return tree
2677
2677
2678 def foldconcat(tree):
2678 def foldconcat(tree):
2679 """Fold elements to be concatenated by `##`
2679 """Fold elements to be concatenated by `##`
2680 """
2680 """
2681 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2681 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2682 return tree
2682 return tree
2683 if tree[0] == '_concat':
2683 if tree[0] == '_concat':
2684 pending = [tree]
2684 pending = [tree]
2685 l = []
2685 l = []
2686 while pending:
2686 while pending:
2687 e = pending.pop()
2687 e = pending.pop()
2688 if e[0] == '_concat':
2688 if e[0] == '_concat':
2689 pending.extend(reversed(e[1:]))
2689 pending.extend(reversed(e[1:]))
2690 elif e[0] in ('string', 'symbol'):
2690 elif e[0] in ('string', 'symbol'):
2691 l.append(e[1])
2691 l.append(e[1])
2692 else:
2692 else:
2693 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2693 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2694 raise error.ParseError(msg)
2694 raise error.ParseError(msg)
2695 return ('string', ''.join(l))
2695 return ('string', ''.join(l))
2696 else:
2696 else:
2697 return tuple(foldconcat(t) for t in tree)
2697 return tuple(foldconcat(t) for t in tree)
2698
2698
2699 def parse(spec, lookup=None):
2699 def parse(spec, lookup=None):
2700 return _parsewith(spec, lookup=lookup)
2700 return _parsewith(spec, lookup=lookup)
2701
2701
2702 def posttreebuilthook(tree, repo):
2702 def posttreebuilthook(tree, repo):
2703 # hook for extensions to execute code on the optimized tree
2703 # hook for extensions to execute code on the optimized tree
2704 pass
2704 pass
2705
2705
2706 def match(ui, spec, repo=None, order=defineorder):
2706 def match(ui, spec, repo=None, order=defineorder):
2707 """Create a matcher for a single revision spec
2707 """Create a matcher for a single revision spec
2708
2708
2709 If order=followorder, a matcher takes the ordering specified by the input
2709 If order=followorder, a matcher takes the ordering specified by the input
2710 set.
2710 set.
2711 """
2711 """
2712 return matchany(ui, [spec], repo=repo, order=order)
2712 return matchany(ui, [spec], repo=repo, order=order)
2713
2713
2714 def matchany(ui, specs, repo=None, order=defineorder):
2714 def matchany(ui, specs, repo=None, order=defineorder):
2715 """Create a matcher that will include any revisions matching one of the
2715 """Create a matcher that will include any revisions matching one of the
2716 given specs
2716 given specs
2717
2717
2718 If order=followorder, a matcher takes the ordering specified by the input
2718 If order=followorder, a matcher takes the ordering specified by the input
2719 set.
2719 set.
2720 """
2720 """
2721 if not specs:
2721 if not specs:
2722 def mfunc(repo, subset=None):
2722 def mfunc(repo, subset=None):
2723 return baseset()
2723 return baseset()
2724 return mfunc
2724 return mfunc
2725 if not all(specs):
2725 if not all(specs):
2726 raise error.ParseError(_("empty query"))
2726 raise error.ParseError(_("empty query"))
2727 lookup = None
2727 lookup = None
2728 if repo:
2728 if repo:
2729 lookup = repo.__contains__
2729 lookup = repo.__contains__
2730 if len(specs) == 1:
2730 if len(specs) == 1:
2731 tree = parse(specs[0], lookup)
2731 tree = parse(specs[0], lookup)
2732 else:
2732 else:
2733 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2733 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2734
2734
2735 if ui:
2735 if ui:
2736 tree = expandaliases(ui, tree)
2736 tree = expandaliases(ui, tree)
2737 tree = foldconcat(tree)
2737 tree = foldconcat(tree)
2738 tree = analyze(tree, order)
2738 tree = analyze(tree, order)
2739 tree = optimize(tree)
2739 tree = optimize(tree)
2740 posttreebuilthook(tree, repo)
2740 posttreebuilthook(tree, repo)
2741 return makematcher(tree)
2741 return makematcher(tree)
2742
2742
2743 def makematcher(tree):
2743 def makematcher(tree):
2744 """Create a matcher from an evaluatable tree"""
2744 """Create a matcher from an evaluatable tree"""
2745 def mfunc(repo, subset=None):
2745 def mfunc(repo, subset=None):
2746 if subset is None:
2746 if subset is None:
2747 subset = fullreposet(repo)
2747 subset = fullreposet(repo)
2748 if util.safehasattr(subset, 'isascending'):
2748 if util.safehasattr(subset, 'isascending'):
2749 result = getset(repo, subset, tree)
2749 result = getset(repo, subset, tree)
2750 else:
2750 else:
2751 result = getset(repo, baseset(subset), tree)
2751 result = getset(repo, baseset(subset), tree)
2752 return result
2752 return result
2753 return mfunc
2753 return mfunc
2754
2754
2755 def formatspec(expr, *args):
2755 def formatspec(expr, *args):
2756 '''
2756 '''
2757 This is a convenience function for using revsets internally, and
2757 This is a convenience function for using revsets internally, and
2758 escapes arguments appropriately. Aliases are intentionally ignored
2758 escapes arguments appropriately. Aliases are intentionally ignored
2759 so that intended expression behavior isn't accidentally subverted.
2759 so that intended expression behavior isn't accidentally subverted.
2760
2760
2761 Supported arguments:
2761 Supported arguments:
2762
2762
2763 %r = revset expression, parenthesized
2763 %r = revset expression, parenthesized
2764 %d = int(arg), no quoting
2764 %d = int(arg), no quoting
2765 %s = string(arg), escaped and single-quoted
2765 %s = string(arg), escaped and single-quoted
2766 %b = arg.branch(), escaped and single-quoted
2766 %b = arg.branch(), escaped and single-quoted
2767 %n = hex(arg), single-quoted
2767 %n = hex(arg), single-quoted
2768 %% = a literal '%'
2768 %% = a literal '%'
2769
2769
2770 Prefixing the type with 'l' specifies a parenthesized list of that type.
2770 Prefixing the type with 'l' specifies a parenthesized list of that type.
2771
2771
2772 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2772 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2773 '(10 or 11):: and ((this()) or (that()))'
2773 '(10 or 11):: and ((this()) or (that()))'
2774 >>> formatspec('%d:: and not %d::', 10, 20)
2774 >>> formatspec('%d:: and not %d::', 10, 20)
2775 '10:: and not 20::'
2775 '10:: and not 20::'
2776 >>> formatspec('%ld or %ld', [], [1])
2776 >>> formatspec('%ld or %ld', [], [1])
2777 "_list('') or 1"
2777 "_list('') or 1"
2778 >>> formatspec('keyword(%s)', 'foo\\xe9')
2778 >>> formatspec('keyword(%s)', 'foo\\xe9')
2779 "keyword('foo\\\\xe9')"
2779 "keyword('foo\\\\xe9')"
2780 >>> b = lambda: 'default'
2780 >>> b = lambda: 'default'
2781 >>> b.branch = b
2781 >>> b.branch = b
2782 >>> formatspec('branch(%b)', b)
2782 >>> formatspec('branch(%b)', b)
2783 "branch('default')"
2783 "branch('default')"
2784 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2784 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2785 "root(_list('a\\x00b\\x00c\\x00d'))"
2785 "root(_list('a\\x00b\\x00c\\x00d'))"
2786 '''
2786 '''
2787
2787
2788 def quote(s):
2788 def quote(s):
2789 return repr(str(s))
2789 return repr(str(s))
2790
2790
2791 def argtype(c, arg):
2791 def argtype(c, arg):
2792 if c == 'd':
2792 if c == 'd':
2793 return str(int(arg))
2793 return str(int(arg))
2794 elif c == 's':
2794 elif c == 's':
2795 return quote(arg)
2795 return quote(arg)
2796 elif c == 'r':
2796 elif c == 'r':
2797 parse(arg) # make sure syntax errors are confined
2797 parse(arg) # make sure syntax errors are confined
2798 return '(%s)' % arg
2798 return '(%s)' % arg
2799 elif c == 'n':
2799 elif c == 'n':
2800 return quote(node.hex(arg))
2800 return quote(node.hex(arg))
2801 elif c == 'b':
2801 elif c == 'b':
2802 return quote(arg.branch())
2802 return quote(arg.branch())
2803
2803
2804 def listexp(s, t):
2804 def listexp(s, t):
2805 l = len(s)
2805 l = len(s)
2806 if l == 0:
2806 if l == 0:
2807 return "_list('')"
2807 return "_list('')"
2808 elif l == 1:
2808 elif l == 1:
2809 return argtype(t, s[0])
2809 return argtype(t, s[0])
2810 elif t == 'd':
2810 elif t == 'd':
2811 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2811 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2812 elif t == 's':
2812 elif t == 's':
2813 return "_list('%s')" % "\0".join(s)
2813 return "_list('%s')" % "\0".join(s)
2814 elif t == 'n':
2814 elif t == 'n':
2815 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2815 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2816 elif t == 'b':
2816 elif t == 'b':
2817 return "_list('%s')" % "\0".join(a.branch() for a in s)
2817 return "_list('%s')" % "\0".join(a.branch() for a in s)
2818
2818
2819 m = l // 2
2819 m = l // 2
2820 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2820 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2821
2821
2822 ret = ''
2822 ret = ''
2823 pos = 0
2823 pos = 0
2824 arg = 0
2824 arg = 0
2825 while pos < len(expr):
2825 while pos < len(expr):
2826 c = expr[pos]
2826 c = expr[pos]
2827 if c == '%':
2827 if c == '%':
2828 pos += 1
2828 pos += 1
2829 d = expr[pos]
2829 d = expr[pos]
2830 if d == '%':
2830 if d == '%':
2831 ret += d
2831 ret += d
2832 elif d in 'dsnbr':
2832 elif d in 'dsnbr':
2833 ret += argtype(d, args[arg])
2833 ret += argtype(d, args[arg])
2834 arg += 1
2834 arg += 1
2835 elif d == 'l':
2835 elif d == 'l':
2836 # a list of some type
2836 # a list of some type
2837 pos += 1
2837 pos += 1
2838 d = expr[pos]
2838 d = expr[pos]
2839 ret += listexp(list(args[arg]), d)
2839 ret += listexp(list(args[arg]), d)
2840 arg += 1
2840 arg += 1
2841 else:
2841 else:
2842 raise error.Abort(_('unexpected revspec format character %s')
2842 raise error.Abort(_('unexpected revspec format character %s')
2843 % d)
2843 % d)
2844 else:
2844 else:
2845 ret += c
2845 ret += c
2846 pos += 1
2846 pos += 1
2847
2847
2848 return ret
2848 return ret
2849
2849
2850 def prettyformat(tree):
2850 def prettyformat(tree):
2851 return parser.prettyformat(tree, ('string', 'symbol'))
2851 return parser.prettyformat(tree, ('string', 'symbol'))
2852
2852
2853 def depth(tree):
2853 def depth(tree):
2854 if isinstance(tree, tuple):
2854 if isinstance(tree, tuple):
2855 return max(map(depth, tree)) + 1
2855 return max(map(depth, tree)) + 1
2856 else:
2856 else:
2857 return 0
2857 return 0
2858
2858
2859 def funcsused(tree):
2859 def funcsused(tree):
2860 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2860 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2861 return set()
2861 return set()
2862 else:
2862 else:
2863 funcs = set()
2863 funcs = set()
2864 for s in tree[1:]:
2864 for s in tree[1:]:
2865 funcs |= funcsused(s)
2865 funcs |= funcsused(s)
2866 if tree[0] == 'func':
2866 if tree[0] == 'func':
2867 funcs.add(tree[1][1])
2867 funcs.add(tree[1][1])
2868 return funcs
2868 return funcs
2869
2869
2870 def _formatsetrepr(r):
2870 def _formatsetrepr(r):
2871 """Format an optional printable representation of a set
2871 """Format an optional printable representation of a set
2872
2872
2873 ======== =================================
2873 ======== =================================
2874 type(r) example
2874 type(r) example
2875 ======== =================================
2875 ======== =================================
2876 tuple ('<not %r>', other)
2876 tuple ('<not %r>', other)
2877 str '<branch closed>'
2877 str '<branch closed>'
2878 callable lambda: '<branch %r>' % sorted(b)
2878 callable lambda: '<branch %r>' % sorted(b)
2879 object other
2879 object other
2880 ======== =================================
2880 ======== =================================
2881 """
2881 """
2882 if r is None:
2882 if r is None:
2883 return ''
2883 return ''
2884 elif isinstance(r, tuple):
2884 elif isinstance(r, tuple):
2885 return r[0] % r[1:]
2885 return r[0] % r[1:]
2886 elif isinstance(r, str):
2886 elif isinstance(r, str):
2887 return r
2887 return r
2888 elif callable(r):
2888 elif callable(r):
2889 return r()
2889 return r()
2890 else:
2890 else:
2891 return repr(r)
2891 return repr(r)
2892
2892
2893 class abstractsmartset(object):
2893 class abstractsmartset(object):
2894
2894
2895 def __nonzero__(self):
2895 def __nonzero__(self):
2896 """True if the smartset is not empty"""
2896 """True if the smartset is not empty"""
2897 raise NotImplementedError()
2897 raise NotImplementedError()
2898
2898
2899 def __contains__(self, rev):
2899 def __contains__(self, rev):
2900 """provide fast membership testing"""
2900 """provide fast membership testing"""
2901 raise NotImplementedError()
2901 raise NotImplementedError()
2902
2902
2903 def __iter__(self):
2903 def __iter__(self):
2904 """iterate the set in the order it is supposed to be iterated"""
2904 """iterate the set in the order it is supposed to be iterated"""
2905 raise NotImplementedError()
2905 raise NotImplementedError()
2906
2906
2907 # Attributes containing a function to perform a fast iteration in a given
2907 # Attributes containing a function to perform a fast iteration in a given
2908 # direction. A smartset can have none, one, or both defined.
2908 # direction. A smartset can have none, one, or both defined.
2909 #
2909 #
2910 # Default value is None instead of a function returning None to avoid
2910 # Default value is None instead of a function returning None to avoid
2911 # initializing an iterator just for testing if a fast method exists.
2911 # initializing an iterator just for testing if a fast method exists.
2912 fastasc = None
2912 fastasc = None
2913 fastdesc = None
2913 fastdesc = None
2914
2914
2915 def isascending(self):
2915 def isascending(self):
2916 """True if the set will iterate in ascending order"""
2916 """True if the set will iterate in ascending order"""
2917 raise NotImplementedError()
2917 raise NotImplementedError()
2918
2918
2919 def isdescending(self):
2919 def isdescending(self):
2920 """True if the set will iterate in descending order"""
2920 """True if the set will iterate in descending order"""
2921 raise NotImplementedError()
2921 raise NotImplementedError()
2922
2922
2923 def istopo(self):
2923 def istopo(self):
2924 """True if the set will iterate in topographical order"""
2924 """True if the set will iterate in topographical order"""
2925 raise NotImplementedError()
2925 raise NotImplementedError()
2926
2926
2927 @util.cachefunc
2927 @util.cachefunc
2928 def min(self):
2928 def min(self):
2929 """return the minimum element in the set"""
2929 """return the minimum element in the set"""
2930 if self.fastasc is not None:
2930 if self.fastasc is not None:
2931 for r in self.fastasc():
2931 for r in self.fastasc():
2932 return r
2932 return r
2933 raise ValueError('arg is an empty sequence')
2933 raise ValueError('arg is an empty sequence')
2934 return min(self)
2934 return min(self)
2935
2935
2936 @util.cachefunc
2936 @util.cachefunc
2937 def max(self):
2937 def max(self):
2938 """return the maximum element in the set"""
2938 """return the maximum element in the set"""
2939 if self.fastdesc is not None:
2939 if self.fastdesc is not None:
2940 for r in self.fastdesc():
2940 for r in self.fastdesc():
2941 return r
2941 return r
2942 raise ValueError('arg is an empty sequence')
2942 raise ValueError('arg is an empty sequence')
2943 return max(self)
2943 return max(self)
2944
2944
2945 def first(self):
2945 def first(self):
2946 """return the first element in the set (user iteration perspective)
2946 """return the first element in the set (user iteration perspective)
2947
2947
2948 Return None if the set is empty"""
2948 Return None if the set is empty"""
2949 raise NotImplementedError()
2949 raise NotImplementedError()
2950
2950
2951 def last(self):
2951 def last(self):
2952 """return the last element in the set (user iteration perspective)
2952 """return the last element in the set (user iteration perspective)
2953
2953
2954 Return None if the set is empty"""
2954 Return None if the set is empty"""
2955 raise NotImplementedError()
2955 raise NotImplementedError()
2956
2956
2957 def __len__(self):
2957 def __len__(self):
2958 """return the length of the smartsets
2958 """return the length of the smartsets
2959
2959
2960 This can be expensive on smartset that could be lazy otherwise."""
2960 This can be expensive on smartset that could be lazy otherwise."""
2961 raise NotImplementedError()
2961 raise NotImplementedError()
2962
2962
2963 def reverse(self):
2963 def reverse(self):
2964 """reverse the expected iteration order"""
2964 """reverse the expected iteration order"""
2965 raise NotImplementedError()
2965 raise NotImplementedError()
2966
2966
2967 def sort(self, reverse=True):
2967 def sort(self, reverse=True):
2968 """get the set to iterate in an ascending or descending order"""
2968 """get the set to iterate in an ascending or descending order"""
2969 raise NotImplementedError()
2969 raise NotImplementedError()
2970
2970
2971 def __and__(self, other):
2971 def __and__(self, other):
2972 """Returns a new object with the intersection of the two collections.
2972 """Returns a new object with the intersection of the two collections.
2973
2973
2974 This is part of the mandatory API for smartset."""
2974 This is part of the mandatory API for smartset."""
2975 if isinstance(other, fullreposet):
2975 if isinstance(other, fullreposet):
2976 return self
2976 return self
2977 return self.filter(other.__contains__, condrepr=other, cache=False)
2977 return self.filter(other.__contains__, condrepr=other, cache=False)
2978
2978
2979 def __add__(self, other):
2979 def __add__(self, other):
2980 """Returns a new object with the union of the two collections.
2980 """Returns a new object with the union of the two collections.
2981
2981
2982 This is part of the mandatory API for smartset."""
2982 This is part of the mandatory API for smartset."""
2983 return addset(self, other)
2983 return addset(self, other)
2984
2984
2985 def __sub__(self, other):
2985 def __sub__(self, other):
2986 """Returns a new object with the substraction of the two collections.
2986 """Returns a new object with the substraction of the two collections.
2987
2987
2988 This is part of the mandatory API for smartset."""
2988 This is part of the mandatory API for smartset."""
2989 c = other.__contains__
2989 c = other.__contains__
2990 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2990 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2991 cache=False)
2991 cache=False)
2992
2992
2993 def filter(self, condition, condrepr=None, cache=True):
2993 def filter(self, condition, condrepr=None, cache=True):
2994 """Returns this smartset filtered by condition as a new smartset.
2994 """Returns this smartset filtered by condition as a new smartset.
2995
2995
2996 `condition` is a callable which takes a revision number and returns a
2996 `condition` is a callable which takes a revision number and returns a
2997 boolean. Optional `condrepr` provides a printable representation of
2997 boolean. Optional `condrepr` provides a printable representation of
2998 the given `condition`.
2998 the given `condition`.
2999
2999
3000 This is part of the mandatory API for smartset."""
3000 This is part of the mandatory API for smartset."""
3001 # builtin cannot be cached. but do not needs to
3001 # builtin cannot be cached. but do not needs to
3002 if cache and util.safehasattr(condition, 'func_code'):
3002 if cache and util.safehasattr(condition, 'func_code'):
3003 condition = util.cachefunc(condition)
3003 condition = util.cachefunc(condition)
3004 return filteredset(self, condition, condrepr)
3004 return filteredset(self, condition, condrepr)
3005
3005
3006 class baseset(abstractsmartset):
3006 class baseset(abstractsmartset):
3007 """Basic data structure that represents a revset and contains the basic
3007 """Basic data structure that represents a revset and contains the basic
3008 operation that it should be able to perform.
3008 operation that it should be able to perform.
3009
3009
3010 Every method in this class should be implemented by any smartset class.
3010 Every method in this class should be implemented by any smartset class.
3011 """
3011 """
3012 def __init__(self, data=(), datarepr=None, istopo=False):
3012 def __init__(self, data=(), datarepr=None, istopo=False):
3013 """
3013 """
3014 datarepr: a tuple of (format, obj, ...), a function or an object that
3014 datarepr: a tuple of (format, obj, ...), a function or an object that
3015 provides a printable representation of the given data.
3015 provides a printable representation of the given data.
3016 """
3016 """
3017 self._ascending = None
3017 self._ascending = None
3018 self._istopo = istopo
3018 self._istopo = istopo
3019 if not isinstance(data, list):
3019 if not isinstance(data, list):
3020 if isinstance(data, set):
3020 if isinstance(data, set):
3021 self._set = data
3021 self._set = data
3022 # set has no order we pick one for stability purpose
3022 # set has no order we pick one for stability purpose
3023 self._ascending = True
3023 self._ascending = True
3024 data = list(data)
3024 data = list(data)
3025 self._list = data
3025 self._list = data
3026 self._datarepr = datarepr
3026 self._datarepr = datarepr
3027
3027
3028 @util.propertycache
3028 @util.propertycache
3029 def _set(self):
3029 def _set(self):
3030 return set(self._list)
3030 return set(self._list)
3031
3031
3032 @util.propertycache
3032 @util.propertycache
3033 def _asclist(self):
3033 def _asclist(self):
3034 asclist = self._list[:]
3034 asclist = self._list[:]
3035 asclist.sort()
3035 asclist.sort()
3036 return asclist
3036 return asclist
3037
3037
3038 def __iter__(self):
3038 def __iter__(self):
3039 if self._ascending is None:
3039 if self._ascending is None:
3040 return iter(self._list)
3040 return iter(self._list)
3041 elif self._ascending:
3041 elif self._ascending:
3042 return iter(self._asclist)
3042 return iter(self._asclist)
3043 else:
3043 else:
3044 return reversed(self._asclist)
3044 return reversed(self._asclist)
3045
3045
3046 def fastasc(self):
3046 def fastasc(self):
3047 return iter(self._asclist)
3047 return iter(self._asclist)
3048
3048
3049 def fastdesc(self):
3049 def fastdesc(self):
3050 return reversed(self._asclist)
3050 return reversed(self._asclist)
3051
3051
3052 @util.propertycache
3052 @util.propertycache
3053 def __contains__(self):
3053 def __contains__(self):
3054 return self._set.__contains__
3054 return self._set.__contains__
3055
3055
3056 def __nonzero__(self):
3056 def __nonzero__(self):
3057 return bool(self._list)
3057 return bool(self._list)
3058
3058
3059 def sort(self, reverse=False):
3059 def sort(self, reverse=False):
3060 self._ascending = not bool(reverse)
3060 self._ascending = not bool(reverse)
3061 self._istopo = False
3061 self._istopo = False
3062
3062
3063 def reverse(self):
3063 def reverse(self):
3064 if self._ascending is None:
3064 if self._ascending is None:
3065 self._list.reverse()
3065 self._list.reverse()
3066 else:
3066 else:
3067 self._ascending = not self._ascending
3067 self._ascending = not self._ascending
3068 self._istopo = False
3068 self._istopo = False
3069
3069
3070 def __len__(self):
3070 def __len__(self):
3071 return len(self._list)
3071 return len(self._list)
3072
3072
3073 def isascending(self):
3073 def isascending(self):
3074 """Returns True if the collection is ascending order, False if not.
3074 """Returns True if the collection is ascending order, False if not.
3075
3075
3076 This is part of the mandatory API for smartset."""
3076 This is part of the mandatory API for smartset."""
3077 if len(self) <= 1:
3077 if len(self) <= 1:
3078 return True
3078 return True
3079 return self._ascending is not None and self._ascending
3079 return self._ascending is not None and self._ascending
3080
3080
3081 def isdescending(self):
3081 def isdescending(self):
3082 """Returns True if the collection is descending order, False if not.
3082 """Returns True if the collection is descending order, False if not.
3083
3083
3084 This is part of the mandatory API for smartset."""
3084 This is part of the mandatory API for smartset."""
3085 if len(self) <= 1:
3085 if len(self) <= 1:
3086 return True
3086 return True
3087 return self._ascending is not None and not self._ascending
3087 return self._ascending is not None and not self._ascending
3088
3088
3089 def istopo(self):
3089 def istopo(self):
3090 """Is the collection is in topographical order or not.
3090 """Is the collection is in topographical order or not.
3091
3091
3092 This is part of the mandatory API for smartset."""
3092 This is part of the mandatory API for smartset."""
3093 if len(self) <= 1:
3093 if len(self) <= 1:
3094 return True
3094 return True
3095 return self._istopo
3095 return self._istopo
3096
3096
3097 def first(self):
3097 def first(self):
3098 if self:
3098 if self:
3099 if self._ascending is None:
3099 if self._ascending is None:
3100 return self._list[0]
3100 return self._list[0]
3101 elif self._ascending:
3101 elif self._ascending:
3102 return self._asclist[0]
3102 return self._asclist[0]
3103 else:
3103 else:
3104 return self._asclist[-1]
3104 return self._asclist[-1]
3105 return None
3105 return None
3106
3106
3107 def last(self):
3107 def last(self):
3108 if self:
3108 if self:
3109 if self._ascending is None:
3109 if self._ascending is None:
3110 return self._list[-1]
3110 return self._list[-1]
3111 elif self._ascending:
3111 elif self._ascending:
3112 return self._asclist[-1]
3112 return self._asclist[-1]
3113 else:
3113 else:
3114 return self._asclist[0]
3114 return self._asclist[0]
3115 return None
3115 return None
3116
3116
3117 def __repr__(self):
3117 def __repr__(self):
3118 d = {None: '', False: '-', True: '+'}[self._ascending]
3118 d = {None: '', False: '-', True: '+'}[self._ascending]
3119 s = _formatsetrepr(self._datarepr)
3119 s = _formatsetrepr(self._datarepr)
3120 if not s:
3120 if not s:
3121 l = self._list
3121 l = self._list
3122 # if _list has been built from a set, it might have a different
3122 # if _list has been built from a set, it might have a different
3123 # order from one python implementation to another.
3123 # order from one python implementation to another.
3124 # We fallback to the sorted version for a stable output.
3124 # We fallback to the sorted version for a stable output.
3125 if self._ascending is not None:
3125 if self._ascending is not None:
3126 l = self._asclist
3126 l = self._asclist
3127 s = repr(l)
3127 s = repr(l)
3128 return '<%s%s %s>' % (type(self).__name__, d, s)
3128 return '<%s%s %s>' % (type(self).__name__, d, s)
3129
3129
3130 class filteredset(abstractsmartset):
3130 class filteredset(abstractsmartset):
3131 """Duck type for baseset class which iterates lazily over the revisions in
3131 """Duck type for baseset class which iterates lazily over the revisions in
3132 the subset and contains a function which tests for membership in the
3132 the subset and contains a function which tests for membership in the
3133 revset
3133 revset
3134 """
3134 """
3135 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3135 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3136 """
3136 """
3137 condition: a function that decide whether a revision in the subset
3137 condition: a function that decide whether a revision in the subset
3138 belongs to the revset or not.
3138 belongs to the revset or not.
3139 condrepr: a tuple of (format, obj, ...), a function or an object that
3139 condrepr: a tuple of (format, obj, ...), a function or an object that
3140 provides a printable representation of the given condition.
3140 provides a printable representation of the given condition.
3141 """
3141 """
3142 self._subset = subset
3142 self._subset = subset
3143 self._condition = condition
3143 self._condition = condition
3144 self._condrepr = condrepr
3144 self._condrepr = condrepr
3145
3145
3146 def __contains__(self, x):
3146 def __contains__(self, x):
3147 return x in self._subset and self._condition(x)
3147 return x in self._subset and self._condition(x)
3148
3148
3149 def __iter__(self):
3149 def __iter__(self):
3150 return self._iterfilter(self._subset)
3150 return self._iterfilter(self._subset)
3151
3151
3152 def _iterfilter(self, it):
3152 def _iterfilter(self, it):
3153 cond = self._condition
3153 cond = self._condition
3154 for x in it:
3154 for x in it:
3155 if cond(x):
3155 if cond(x):
3156 yield x
3156 yield x
3157
3157
3158 @property
3158 @property
3159 def fastasc(self):
3159 def fastasc(self):
3160 it = self._subset.fastasc
3160 it = self._subset.fastasc
3161 if it is None:
3161 if it is None:
3162 return None
3162 return None
3163 return lambda: self._iterfilter(it())
3163 return lambda: self._iterfilter(it())
3164
3164
3165 @property
3165 @property
3166 def fastdesc(self):
3166 def fastdesc(self):
3167 it = self._subset.fastdesc
3167 it = self._subset.fastdesc
3168 if it is None:
3168 if it is None:
3169 return None
3169 return None
3170 return lambda: self._iterfilter(it())
3170 return lambda: self._iterfilter(it())
3171
3171
3172 def __nonzero__(self):
3172 def __nonzero__(self):
3173 fast = None
3173 fast = None
3174 candidates = [self.fastasc if self.isascending() else None,
3174 candidates = [self.fastasc if self.isascending() else None,
3175 self.fastdesc if self.isdescending() else None,
3175 self.fastdesc if self.isdescending() else None,
3176 self.fastasc,
3176 self.fastasc,
3177 self.fastdesc]
3177 self.fastdesc]
3178 for candidate in candidates:
3178 for candidate in candidates:
3179 if candidate is not None:
3179 if candidate is not None:
3180 fast = candidate
3180 fast = candidate
3181 break
3181 break
3182
3182
3183 if fast is not None:
3183 if fast is not None:
3184 it = fast()
3184 it = fast()
3185 else:
3185 else:
3186 it = self
3186 it = self
3187
3187
3188 for r in it:
3188 for r in it:
3189 return True
3189 return True
3190 return False
3190 return False
3191
3191
3192 def __len__(self):
3192 def __len__(self):
3193 # Basic implementation to be changed in future patches.
3193 # Basic implementation to be changed in future patches.
3194 # until this gets improved, we use generator expression
3194 # until this gets improved, we use generator expression
3195 # here, since list compr is free to call __len__ again
3195 # here, since list compr is free to call __len__ again
3196 # causing infinite recursion
3196 # causing infinite recursion
3197 l = baseset(r for r in self)
3197 l = baseset(r for r in self)
3198 return len(l)
3198 return len(l)
3199
3199
3200 def sort(self, reverse=False):
3200 def sort(self, reverse=False):
3201 self._subset.sort(reverse=reverse)
3201 self._subset.sort(reverse=reverse)
3202
3202
3203 def reverse(self):
3203 def reverse(self):
3204 self._subset.reverse()
3204 self._subset.reverse()
3205
3205
3206 def isascending(self):
3206 def isascending(self):
3207 return self._subset.isascending()
3207 return self._subset.isascending()
3208
3208
3209 def isdescending(self):
3209 def isdescending(self):
3210 return self._subset.isdescending()
3210 return self._subset.isdescending()
3211
3211
3212 def istopo(self):
3212 def istopo(self):
3213 return self._subset.istopo()
3213 return self._subset.istopo()
3214
3214
3215 def first(self):
3215 def first(self):
3216 for x in self:
3216 for x in self:
3217 return x
3217 return x
3218 return None
3218 return None
3219
3219
3220 def last(self):
3220 def last(self):
3221 it = None
3221 it = None
3222 if self.isascending():
3222 if self.isascending():
3223 it = self.fastdesc
3223 it = self.fastdesc
3224 elif self.isdescending():
3224 elif self.isdescending():
3225 it = self.fastasc
3225 it = self.fastasc
3226 if it is not None:
3226 if it is not None:
3227 for x in it():
3227 for x in it():
3228 return x
3228 return x
3229 return None #empty case
3229 return None #empty case
3230 else:
3230 else:
3231 x = None
3231 x = None
3232 for x in self:
3232 for x in self:
3233 pass
3233 pass
3234 return x
3234 return x
3235
3235
3236 def __repr__(self):
3236 def __repr__(self):
3237 xs = [repr(self._subset)]
3237 xs = [repr(self._subset)]
3238 s = _formatsetrepr(self._condrepr)
3238 s = _formatsetrepr(self._condrepr)
3239 if s:
3239 if s:
3240 xs.append(s)
3240 xs.append(s)
3241 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3241 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3242
3242
3243 def _iterordered(ascending, iter1, iter2):
3243 def _iterordered(ascending, iter1, iter2):
3244 """produce an ordered iteration from two iterators with the same order
3244 """produce an ordered iteration from two iterators with the same order
3245
3245
3246 The ascending is used to indicated the iteration direction.
3246 The ascending is used to indicated the iteration direction.
3247 """
3247 """
3248 choice = max
3248 choice = max
3249 if ascending:
3249 if ascending:
3250 choice = min
3250 choice = min
3251
3251
3252 val1 = None
3252 val1 = None
3253 val2 = None
3253 val2 = None
3254 try:
3254 try:
3255 # Consume both iterators in an ordered way until one is empty
3255 # Consume both iterators in an ordered way until one is empty
3256 while True:
3256 while True:
3257 if val1 is None:
3257 if val1 is None:
3258 val1 = next(iter1)
3258 val1 = next(iter1)
3259 if val2 is None:
3259 if val2 is None:
3260 val2 = next(iter2)
3260 val2 = next(iter2)
3261 n = choice(val1, val2)
3261 n = choice(val1, val2)
3262 yield n
3262 yield n
3263 if val1 == n:
3263 if val1 == n:
3264 val1 = None
3264 val1 = None
3265 if val2 == n:
3265 if val2 == n:
3266 val2 = None
3266 val2 = None
3267 except StopIteration:
3267 except StopIteration:
3268 # Flush any remaining values and consume the other one
3268 # Flush any remaining values and consume the other one
3269 it = iter2
3269 it = iter2
3270 if val1 is not None:
3270 if val1 is not None:
3271 yield val1
3271 yield val1
3272 it = iter1
3272 it = iter1
3273 elif val2 is not None:
3273 elif val2 is not None:
3274 # might have been equality and both are empty
3274 # might have been equality and both are empty
3275 yield val2
3275 yield val2
3276 for val in it:
3276 for val in it:
3277 yield val
3277 yield val
3278
3278
3279 class addset(abstractsmartset):
3279 class addset(abstractsmartset):
3280 """Represent the addition of two sets
3280 """Represent the addition of two sets
3281
3281
3282 Wrapper structure for lazily adding two structures without losing much
3282 Wrapper structure for lazily adding two structures without losing much
3283 performance on the __contains__ method
3283 performance on the __contains__ method
3284
3284
3285 If the ascending attribute is set, that means the two structures are
3285 If the ascending attribute is set, that means the two structures are
3286 ordered in either an ascending or descending way. Therefore, we can add
3286 ordered in either an ascending or descending way. Therefore, we can add
3287 them maintaining the order by iterating over both at the same time
3287 them maintaining the order by iterating over both at the same time
3288
3288
3289 >>> xs = baseset([0, 3, 2])
3289 >>> xs = baseset([0, 3, 2])
3290 >>> ys = baseset([5, 2, 4])
3290 >>> ys = baseset([5, 2, 4])
3291
3291
3292 >>> rs = addset(xs, ys)
3292 >>> rs = addset(xs, ys)
3293 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3293 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3294 (True, True, False, True, 0, 4)
3294 (True, True, False, True, 0, 4)
3295 >>> rs = addset(xs, baseset([]))
3295 >>> rs = addset(xs, baseset([]))
3296 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3296 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3297 (True, True, False, 0, 2)
3297 (True, True, False, 0, 2)
3298 >>> rs = addset(baseset([]), baseset([]))
3298 >>> rs = addset(baseset([]), baseset([]))
3299 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3299 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3300 (False, False, None, None)
3300 (False, False, None, None)
3301
3301
3302 iterate unsorted:
3302 iterate unsorted:
3303 >>> rs = addset(xs, ys)
3303 >>> rs = addset(xs, ys)
3304 >>> # (use generator because pypy could call len())
3304 >>> # (use generator because pypy could call len())
3305 >>> list(x for x in rs) # without _genlist
3305 >>> list(x for x in rs) # without _genlist
3306 [0, 3, 2, 5, 4]
3306 [0, 3, 2, 5, 4]
3307 >>> assert not rs._genlist
3307 >>> assert not rs._genlist
3308 >>> len(rs)
3308 >>> len(rs)
3309 5
3309 5
3310 >>> [x for x in rs] # with _genlist
3310 >>> [x for x in rs] # with _genlist
3311 [0, 3, 2, 5, 4]
3311 [0, 3, 2, 5, 4]
3312 >>> assert rs._genlist
3312 >>> assert rs._genlist
3313
3313
3314 iterate ascending:
3314 iterate ascending:
3315 >>> rs = addset(xs, ys, ascending=True)
3315 >>> rs = addset(xs, ys, ascending=True)
3316 >>> # (use generator because pypy could call len())
3316 >>> # (use generator because pypy could call len())
3317 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3317 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3318 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3318 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3319 >>> assert not rs._asclist
3319 >>> assert not rs._asclist
3320 >>> len(rs)
3320 >>> len(rs)
3321 5
3321 5
3322 >>> [x for x in rs], [x for x in rs.fastasc()]
3322 >>> [x for x in rs], [x for x in rs.fastasc()]
3323 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3323 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3324 >>> assert rs._asclist
3324 >>> assert rs._asclist
3325
3325
3326 iterate descending:
3326 iterate descending:
3327 >>> rs = addset(xs, ys, ascending=False)
3327 >>> rs = addset(xs, ys, ascending=False)
3328 >>> # (use generator because pypy could call len())
3328 >>> # (use generator because pypy could call len())
3329 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3329 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3330 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3330 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3331 >>> assert not rs._asclist
3331 >>> assert not rs._asclist
3332 >>> len(rs)
3332 >>> len(rs)
3333 5
3333 5
3334 >>> [x for x in rs], [x for x in rs.fastdesc()]
3334 >>> [x for x in rs], [x for x in rs.fastdesc()]
3335 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3335 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3336 >>> assert rs._asclist
3336 >>> assert rs._asclist
3337
3337
3338 iterate ascending without fastasc:
3338 iterate ascending without fastasc:
3339 >>> rs = addset(xs, generatorset(ys), ascending=True)
3339 >>> rs = addset(xs, generatorset(ys), ascending=True)
3340 >>> assert rs.fastasc is None
3340 >>> assert rs.fastasc is None
3341 >>> [x for x in rs]
3341 >>> [x for x in rs]
3342 [0, 2, 3, 4, 5]
3342 [0, 2, 3, 4, 5]
3343
3343
3344 iterate descending without fastdesc:
3344 iterate descending without fastdesc:
3345 >>> rs = addset(generatorset(xs), ys, ascending=False)
3345 >>> rs = addset(generatorset(xs), ys, ascending=False)
3346 >>> assert rs.fastdesc is None
3346 >>> assert rs.fastdesc is None
3347 >>> [x for x in rs]
3347 >>> [x for x in rs]
3348 [5, 4, 3, 2, 0]
3348 [5, 4, 3, 2, 0]
3349 """
3349 """
3350 def __init__(self, revs1, revs2, ascending=None):
3350 def __init__(self, revs1, revs2, ascending=None):
3351 self._r1 = revs1
3351 self._r1 = revs1
3352 self._r2 = revs2
3352 self._r2 = revs2
3353 self._iter = None
3353 self._iter = None
3354 self._ascending = ascending
3354 self._ascending = ascending
3355 self._genlist = None
3355 self._genlist = None
3356 self._asclist = None
3356 self._asclist = None
3357
3357
3358 def __len__(self):
3358 def __len__(self):
3359 return len(self._list)
3359 return len(self._list)
3360
3360
3361 def __nonzero__(self):
3361 def __nonzero__(self):
3362 return bool(self._r1) or bool(self._r2)
3362 return bool(self._r1) or bool(self._r2)
3363
3363
3364 @util.propertycache
3364 @util.propertycache
3365 def _list(self):
3365 def _list(self):
3366 if not self._genlist:
3366 if not self._genlist:
3367 self._genlist = baseset(iter(self))
3367 self._genlist = baseset(iter(self))
3368 return self._genlist
3368 return self._genlist
3369
3369
3370 def __iter__(self):
3370 def __iter__(self):
3371 """Iterate over both collections without repeating elements
3371 """Iterate over both collections without repeating elements
3372
3372
3373 If the ascending attribute is not set, iterate over the first one and
3373 If the ascending attribute is not set, iterate over the first one and
3374 then over the second one checking for membership on the first one so we
3374 then over the second one checking for membership on the first one so we
3375 dont yield any duplicates.
3375 dont yield any duplicates.
3376
3376
3377 If the ascending attribute is set, iterate over both collections at the
3377 If the ascending attribute is set, iterate over both collections at the
3378 same time, yielding only one value at a time in the given order.
3378 same time, yielding only one value at a time in the given order.
3379 """
3379 """
3380 if self._ascending is None:
3380 if self._ascending is None:
3381 if self._genlist:
3381 if self._genlist:
3382 return iter(self._genlist)
3382 return iter(self._genlist)
3383 def arbitraryordergen():
3383 def arbitraryordergen():
3384 for r in self._r1:
3384 for r in self._r1:
3385 yield r
3385 yield r
3386 inr1 = self._r1.__contains__
3386 inr1 = self._r1.__contains__
3387 for r in self._r2:
3387 for r in self._r2:
3388 if not inr1(r):
3388 if not inr1(r):
3389 yield r
3389 yield r
3390 return arbitraryordergen()
3390 return arbitraryordergen()
3391 # try to use our own fast iterator if it exists
3391 # try to use our own fast iterator if it exists
3392 self._trysetasclist()
3392 self._trysetasclist()
3393 if self._ascending:
3393 if self._ascending:
3394 attr = 'fastasc'
3394 attr = 'fastasc'
3395 else:
3395 else:
3396 attr = 'fastdesc'
3396 attr = 'fastdesc'
3397 it = getattr(self, attr)
3397 it = getattr(self, attr)
3398 if it is not None:
3398 if it is not None:
3399 return it()
3399 return it()
3400 # maybe half of the component supports fast
3400 # maybe half of the component supports fast
3401 # get iterator for _r1
3401 # get iterator for _r1
3402 iter1 = getattr(self._r1, attr)
3402 iter1 = getattr(self._r1, attr)
3403 if iter1 is None:
3403 if iter1 is None:
3404 # let's avoid side effect (not sure it matters)
3404 # let's avoid side effect (not sure it matters)
3405 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3405 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3406 else:
3406 else:
3407 iter1 = iter1()
3407 iter1 = iter1()
3408 # get iterator for _r2
3408 # get iterator for _r2
3409 iter2 = getattr(self._r2, attr)
3409 iter2 = getattr(self._r2, attr)
3410 if iter2 is None:
3410 if iter2 is None:
3411 # let's avoid side effect (not sure it matters)
3411 # let's avoid side effect (not sure it matters)
3412 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3412 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3413 else:
3413 else:
3414 iter2 = iter2()
3414 iter2 = iter2()
3415 return _iterordered(self._ascending, iter1, iter2)
3415 return _iterordered(self._ascending, iter1, iter2)
3416
3416
3417 def _trysetasclist(self):
3417 def _trysetasclist(self):
3418 """populate the _asclist attribute if possible and necessary"""
3418 """populate the _asclist attribute if possible and necessary"""
3419 if self._genlist is not None and self._asclist is None:
3419 if self._genlist is not None and self._asclist is None:
3420 self._asclist = sorted(self._genlist)
3420 self._asclist = sorted(self._genlist)
3421
3421
3422 @property
3422 @property
3423 def fastasc(self):
3423 def fastasc(self):
3424 self._trysetasclist()
3424 self._trysetasclist()
3425 if self._asclist is not None:
3425 if self._asclist is not None:
3426 return self._asclist.__iter__
3426 return self._asclist.__iter__
3427 iter1 = self._r1.fastasc
3427 iter1 = self._r1.fastasc
3428 iter2 = self._r2.fastasc
3428 iter2 = self._r2.fastasc
3429 if None in (iter1, iter2):
3429 if None in (iter1, iter2):
3430 return None
3430 return None
3431 return lambda: _iterordered(True, iter1(), iter2())
3431 return lambda: _iterordered(True, iter1(), iter2())
3432
3432
3433 @property
3433 @property
3434 def fastdesc(self):
3434 def fastdesc(self):
3435 self._trysetasclist()
3435 self._trysetasclist()
3436 if self._asclist is not None:
3436 if self._asclist is not None:
3437 return self._asclist.__reversed__
3437 return self._asclist.__reversed__
3438 iter1 = self._r1.fastdesc
3438 iter1 = self._r1.fastdesc
3439 iter2 = self._r2.fastdesc
3439 iter2 = self._r2.fastdesc
3440 if None in (iter1, iter2):
3440 if None in (iter1, iter2):
3441 return None
3441 return None
3442 return lambda: _iterordered(False, iter1(), iter2())
3442 return lambda: _iterordered(False, iter1(), iter2())
3443
3443
3444 def __contains__(self, x):
3444 def __contains__(self, x):
3445 return x in self._r1 or x in self._r2
3445 return x in self._r1 or x in self._r2
3446
3446
3447 def sort(self, reverse=False):
3447 def sort(self, reverse=False):
3448 """Sort the added set
3448 """Sort the added set
3449
3449
3450 For this we use the cached list with all the generated values and if we
3450 For this we use the cached list with all the generated values and if we
3451 know they are ascending or descending we can sort them in a smart way.
3451 know they are ascending or descending we can sort them in a smart way.
3452 """
3452 """
3453 self._ascending = not reverse
3453 self._ascending = not reverse
3454
3454
3455 def isascending(self):
3455 def isascending(self):
3456 return self._ascending is not None and self._ascending
3456 return self._ascending is not None and self._ascending
3457
3457
3458 def isdescending(self):
3458 def isdescending(self):
3459 return self._ascending is not None and not self._ascending
3459 return self._ascending is not None and not self._ascending
3460
3460
3461 def istopo(self):
3461 def istopo(self):
3462 # not worth the trouble asserting if the two sets combined are still
3462 # not worth the trouble asserting if the two sets combined are still
3463 # in topographical order. Use the sort() predicate to explicitly sort
3463 # in topographical order. Use the sort() predicate to explicitly sort
3464 # again instead.
3464 # again instead.
3465 return False
3465 return False
3466
3466
3467 def reverse(self):
3467 def reverse(self):
3468 if self._ascending is None:
3468 if self._ascending is None:
3469 self._list.reverse()
3469 self._list.reverse()
3470 else:
3470 else:
3471 self._ascending = not self._ascending
3471 self._ascending = not self._ascending
3472
3472
3473 def first(self):
3473 def first(self):
3474 for x in self:
3474 for x in self:
3475 return x
3475 return x
3476 return None
3476 return None
3477
3477
3478 def last(self):
3478 def last(self):
3479 self.reverse()
3479 self.reverse()
3480 val = self.first()
3480 val = self.first()
3481 self.reverse()
3481 self.reverse()
3482 return val
3482 return val
3483
3483
3484 def __repr__(self):
3484 def __repr__(self):
3485 d = {None: '', False: '-', True: '+'}[self._ascending]
3485 d = {None: '', False: '-', True: '+'}[self._ascending]
3486 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3486 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3487
3487
3488 class generatorset(abstractsmartset):
3488 class generatorset(abstractsmartset):
3489 """Wrap a generator for lazy iteration
3489 """Wrap a generator for lazy iteration
3490
3490
3491 Wrapper structure for generators that provides lazy membership and can
3491 Wrapper structure for generators that provides lazy membership and can
3492 be iterated more than once.
3492 be iterated more than once.
3493 When asked for membership it generates values until either it finds the
3493 When asked for membership it generates values until either it finds the
3494 requested one or has gone through all the elements in the generator
3494 requested one or has gone through all the elements in the generator
3495 """
3495 """
3496 def __init__(self, gen, iterasc=None):
3496 def __init__(self, gen, iterasc=None):
3497 """
3497 """
3498 gen: a generator producing the values for the generatorset.
3498 gen: a generator producing the values for the generatorset.
3499 """
3499 """
3500 self._gen = gen
3500 self._gen = gen
3501 self._asclist = None
3501 self._asclist = None
3502 self._cache = {}
3502 self._cache = {}
3503 self._genlist = []
3503 self._genlist = []
3504 self._finished = False
3504 self._finished = False
3505 self._ascending = True
3505 self._ascending = True
3506 if iterasc is not None:
3506 if iterasc is not None:
3507 if iterasc:
3507 if iterasc:
3508 self.fastasc = self._iterator
3508 self.fastasc = self._iterator
3509 self.__contains__ = self._asccontains
3509 self.__contains__ = self._asccontains
3510 else:
3510 else:
3511 self.fastdesc = self._iterator
3511 self.fastdesc = self._iterator
3512 self.__contains__ = self._desccontains
3512 self.__contains__ = self._desccontains
3513
3513
3514 def __nonzero__(self):
3514 def __nonzero__(self):
3515 # Do not use 'for r in self' because it will enforce the iteration
3515 # Do not use 'for r in self' because it will enforce the iteration
3516 # order (default ascending), possibly unrolling a whole descending
3516 # order (default ascending), possibly unrolling a whole descending
3517 # iterator.
3517 # iterator.
3518 if self._genlist:
3518 if self._genlist:
3519 return True
3519 return True
3520 for r in self._consumegen():
3520 for r in self._consumegen():
3521 return True
3521 return True
3522 return False
3522 return False
3523
3523
3524 def __contains__(self, x):
3524 def __contains__(self, x):
3525 if x in self._cache:
3525 if x in self._cache:
3526 return self._cache[x]
3526 return self._cache[x]
3527
3527
3528 # Use new values only, as existing values would be cached.
3528 # Use new values only, as existing values would be cached.
3529 for l in self._consumegen():
3529 for l in self._consumegen():
3530 if l == x:
3530 if l == x:
3531 return True
3531 return True
3532
3532
3533 self._cache[x] = False
3533 self._cache[x] = False
3534 return False
3534 return False
3535
3535
3536 def _asccontains(self, x):
3536 def _asccontains(self, x):
3537 """version of contains optimised for ascending generator"""
3537 """version of contains optimised for ascending generator"""
3538 if x in self._cache:
3538 if x in self._cache:
3539 return self._cache[x]
3539 return self._cache[x]
3540
3540
3541 # Use new values only, as existing values would be cached.
3541 # Use new values only, as existing values would be cached.
3542 for l in self._consumegen():
3542 for l in self._consumegen():
3543 if l == x:
3543 if l == x:
3544 return True
3544 return True
3545 if l > x:
3545 if l > x:
3546 break
3546 break
3547
3547
3548 self._cache[x] = False
3548 self._cache[x] = False
3549 return False
3549 return False
3550
3550
3551 def _desccontains(self, x):
3551 def _desccontains(self, x):
3552 """version of contains optimised for descending generator"""
3552 """version of contains optimised for descending generator"""
3553 if x in self._cache:
3553 if x in self._cache:
3554 return self._cache[x]
3554 return self._cache[x]
3555
3555
3556 # Use new values only, as existing values would be cached.
3556 # Use new values only, as existing values would be cached.
3557 for l in self._consumegen():
3557 for l in self._consumegen():
3558 if l == x:
3558 if l == x:
3559 return True
3559 return True
3560 if l < x:
3560 if l < x:
3561 break
3561 break
3562
3562
3563 self._cache[x] = False
3563 self._cache[x] = False
3564 return False
3564 return False
3565
3565
3566 def __iter__(self):
3566 def __iter__(self):
3567 if self._ascending:
3567 if self._ascending:
3568 it = self.fastasc
3568 it = self.fastasc
3569 else:
3569 else:
3570 it = self.fastdesc
3570 it = self.fastdesc
3571 if it is not None:
3571 if it is not None:
3572 return it()
3572 return it()
3573 # we need to consume the iterator
3573 # we need to consume the iterator
3574 for x in self._consumegen():
3574 for x in self._consumegen():
3575 pass
3575 pass
3576 # recall the same code
3576 # recall the same code
3577 return iter(self)
3577 return iter(self)
3578
3578
3579 def _iterator(self):
3579 def _iterator(self):
3580 if self._finished:
3580 if self._finished:
3581 return iter(self._genlist)
3581 return iter(self._genlist)
3582
3582
3583 # We have to use this complex iteration strategy to allow multiple
3583 # We have to use this complex iteration strategy to allow multiple
3584 # iterations at the same time. We need to be able to catch revision
3584 # iterations at the same time. We need to be able to catch revision
3585 # removed from _consumegen and added to genlist in another instance.
3585 # removed from _consumegen and added to genlist in another instance.
3586 #
3586 #
3587 # Getting rid of it would provide an about 15% speed up on this
3587 # Getting rid of it would provide an about 15% speed up on this
3588 # iteration.
3588 # iteration.
3589 genlist = self._genlist
3589 genlist = self._genlist
3590 nextrev = self._consumegen().next
3590 nextrev = self._consumegen().next
3591 _len = len # cache global lookup
3591 _len = len # cache global lookup
3592 def gen():
3592 def gen():
3593 i = 0
3593 i = 0
3594 while True:
3594 while True:
3595 if i < _len(genlist):
3595 if i < _len(genlist):
3596 yield genlist[i]
3596 yield genlist[i]
3597 else:
3597 else:
3598 yield nextrev()
3598 yield nextrev()
3599 i += 1
3599 i += 1
3600 return gen()
3600 return gen()
3601
3601
3602 def _consumegen(self):
3602 def _consumegen(self):
3603 cache = self._cache
3603 cache = self._cache
3604 genlist = self._genlist.append
3604 genlist = self._genlist.append
3605 for item in self._gen:
3605 for item in self._gen:
3606 cache[item] = True
3606 cache[item] = True
3607 genlist(item)
3607 genlist(item)
3608 yield item
3608 yield item
3609 if not self._finished:
3609 if not self._finished:
3610 self._finished = True
3610 self._finished = True
3611 asc = self._genlist[:]
3611 asc = self._genlist[:]
3612 asc.sort()
3612 asc.sort()
3613 self._asclist = asc
3613 self._asclist = asc
3614 self.fastasc = asc.__iter__
3614 self.fastasc = asc.__iter__
3615 self.fastdesc = asc.__reversed__
3615 self.fastdesc = asc.__reversed__
3616
3616
3617 def __len__(self):
3617 def __len__(self):
3618 for x in self._consumegen():
3618 for x in self._consumegen():
3619 pass
3619 pass
3620 return len(self._genlist)
3620 return len(self._genlist)
3621
3621
3622 def sort(self, reverse=False):
3622 def sort(self, reverse=False):
3623 self._ascending = not reverse
3623 self._ascending = not reverse
3624
3624
3625 def reverse(self):
3625 def reverse(self):
3626 self._ascending = not self._ascending
3626 self._ascending = not self._ascending
3627
3627
3628 def isascending(self):
3628 def isascending(self):
3629 return self._ascending
3629 return self._ascending
3630
3630
3631 def isdescending(self):
3631 def isdescending(self):
3632 return not self._ascending
3632 return not self._ascending
3633
3633
3634 def istopo(self):
3634 def istopo(self):
3635 # not worth the trouble asserting if the two sets combined are still
3635 # not worth the trouble asserting if the two sets combined are still
3636 # in topographical order. Use the sort() predicate to explicitly sort
3636 # in topographical order. Use the sort() predicate to explicitly sort
3637 # again instead.
3637 # again instead.
3638 return False
3638 return False
3639
3639
3640 def first(self):
3640 def first(self):
3641 if self._ascending:
3641 if self._ascending:
3642 it = self.fastasc
3642 it = self.fastasc
3643 else:
3643 else:
3644 it = self.fastdesc
3644 it = self.fastdesc
3645 if it is None:
3645 if it is None:
3646 # we need to consume all and try again
3646 # we need to consume all and try again
3647 for x in self._consumegen():
3647 for x in self._consumegen():
3648 pass
3648 pass
3649 return self.first()
3649 return self.first()
3650 return next(it(), None)
3650 return next(it(), None)
3651
3651
3652 def last(self):
3652 def last(self):
3653 if self._ascending:
3653 if self._ascending:
3654 it = self.fastdesc
3654 it = self.fastdesc
3655 else:
3655 else:
3656 it = self.fastasc
3656 it = self.fastasc
3657 if it is None:
3657 if it is None:
3658 # we need to consume all and try again
3658 # we need to consume all and try again
3659 for x in self._consumegen():
3659 for x in self._consumegen():
3660 pass
3660 pass
3661 return self.first()
3661 return self.first()
3662 return next(it(), None)
3662 return next(it(), None)
3663
3663
3664 def __repr__(self):
3664 def __repr__(self):
3665 d = {False: '-', True: '+'}[self._ascending]
3665 d = {False: '-', True: '+'}[self._ascending]
3666 return '<%s%s>' % (type(self).__name__, d)
3666 return '<%s%s>' % (type(self).__name__, d)
3667
3667
3668 class spanset(abstractsmartset):
3668 class spanset(abstractsmartset):
3669 """Duck type for baseset class which represents a range of revisions and
3669 """Duck type for baseset class which represents a range of revisions and
3670 can work lazily and without having all the range in memory
3670 can work lazily and without having all the range in memory
3671
3671
3672 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3672 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3673 notable points:
3673 notable points:
3674 - when x < y it will be automatically descending,
3674 - when x < y it will be automatically descending,
3675 - revision filtered with this repoview will be skipped.
3675 - revision filtered with this repoview will be skipped.
3676
3676
3677 """
3677 """
3678 def __init__(self, repo, start=0, end=None):
3678 def __init__(self, repo, start=0, end=None):
3679 """
3679 """
3680 start: first revision included the set
3680 start: first revision included the set
3681 (default to 0)
3681 (default to 0)
3682 end: first revision excluded (last+1)
3682 end: first revision excluded (last+1)
3683 (default to len(repo)
3683 (default to len(repo)
3684
3684
3685 Spanset will be descending if `end` < `start`.
3685 Spanset will be descending if `end` < `start`.
3686 """
3686 """
3687 if end is None:
3687 if end is None:
3688 end = len(repo)
3688 end = len(repo)
3689 self._ascending = start <= end
3689 self._ascending = start <= end
3690 if not self._ascending:
3690 if not self._ascending:
3691 start, end = end + 1, start +1
3691 start, end = end + 1, start +1
3692 self._start = start
3692 self._start = start
3693 self._end = end
3693 self._end = end
3694 self._hiddenrevs = repo.changelog.filteredrevs
3694 self._hiddenrevs = repo.changelog.filteredrevs
3695
3695
3696 def sort(self, reverse=False):
3696 def sort(self, reverse=False):
3697 self._ascending = not reverse
3697 self._ascending = not reverse
3698
3698
3699 def reverse(self):
3699 def reverse(self):
3700 self._ascending = not self._ascending
3700 self._ascending = not self._ascending
3701
3701
3702 def istopo(self):
3702 def istopo(self):
3703 # not worth the trouble asserting if the two sets combined are still
3703 # not worth the trouble asserting if the two sets combined are still
3704 # in topographical order. Use the sort() predicate to explicitly sort
3704 # in topographical order. Use the sort() predicate to explicitly sort
3705 # again instead.
3705 # again instead.
3706 return False
3706 return False
3707
3707
3708 def _iterfilter(self, iterrange):
3708 def _iterfilter(self, iterrange):
3709 s = self._hiddenrevs
3709 s = self._hiddenrevs
3710 for r in iterrange:
3710 for r in iterrange:
3711 if r not in s:
3711 if r not in s:
3712 yield r
3712 yield r
3713
3713
3714 def __iter__(self):
3714 def __iter__(self):
3715 if self._ascending:
3715 if self._ascending:
3716 return self.fastasc()
3716 return self.fastasc()
3717 else:
3717 else:
3718 return self.fastdesc()
3718 return self.fastdesc()
3719
3719
3720 def fastasc(self):
3720 def fastasc(self):
3721 iterrange = xrange(self._start, self._end)
3721 iterrange = xrange(self._start, self._end)
3722 if self._hiddenrevs:
3722 if self._hiddenrevs:
3723 return self._iterfilter(iterrange)
3723 return self._iterfilter(iterrange)
3724 return iter(iterrange)
3724 return iter(iterrange)
3725
3725
3726 def fastdesc(self):
3726 def fastdesc(self):
3727 iterrange = xrange(self._end - 1, self._start - 1, -1)
3727 iterrange = xrange(self._end - 1, self._start - 1, -1)
3728 if self._hiddenrevs:
3728 if self._hiddenrevs:
3729 return self._iterfilter(iterrange)
3729 return self._iterfilter(iterrange)
3730 return iter(iterrange)
3730 return iter(iterrange)
3731
3731
3732 def __contains__(self, rev):
3732 def __contains__(self, rev):
3733 hidden = self._hiddenrevs
3733 hidden = self._hiddenrevs
3734 return ((self._start <= rev < self._end)
3734 return ((self._start <= rev < self._end)
3735 and not (hidden and rev in hidden))
3735 and not (hidden and rev in hidden))
3736
3736
3737 def __nonzero__(self):
3737 def __nonzero__(self):
3738 for r in self:
3738 for r in self:
3739 return True
3739 return True
3740 return False
3740 return False
3741
3741
3742 def __len__(self):
3742 def __len__(self):
3743 if not self._hiddenrevs:
3743 if not self._hiddenrevs:
3744 return abs(self._end - self._start)
3744 return abs(self._end - self._start)
3745 else:
3745 else:
3746 count = 0
3746 count = 0
3747 start = self._start
3747 start = self._start
3748 end = self._end
3748 end = self._end
3749 for rev in self._hiddenrevs:
3749 for rev in self._hiddenrevs:
3750 if (end < rev <= start) or (start <= rev < end):
3750 if (end < rev <= start) or (start <= rev < end):
3751 count += 1
3751 count += 1
3752 return abs(self._end - self._start) - count
3752 return abs(self._end - self._start) - count
3753
3753
3754 def isascending(self):
3754 def isascending(self):
3755 return self._ascending
3755 return self._ascending
3756
3756
3757 def isdescending(self):
3757 def isdescending(self):
3758 return not self._ascending
3758 return not self._ascending
3759
3759
3760 def first(self):
3760 def first(self):
3761 if self._ascending:
3761 if self._ascending:
3762 it = self.fastasc
3762 it = self.fastasc
3763 else:
3763 else:
3764 it = self.fastdesc
3764 it = self.fastdesc
3765 for x in it():
3765 for x in it():
3766 return x
3766 return x
3767 return None
3767 return None
3768
3768
3769 def last(self):
3769 def last(self):
3770 if self._ascending:
3770 if self._ascending:
3771 it = self.fastdesc
3771 it = self.fastdesc
3772 else:
3772 else:
3773 it = self.fastasc
3773 it = self.fastasc
3774 for x in it():
3774 for x in it():
3775 return x
3775 return x
3776 return None
3776 return None
3777
3777
3778 def __repr__(self):
3778 def __repr__(self):
3779 d = {False: '-', True: '+'}[self._ascending]
3779 d = {False: '-', True: '+'}[self._ascending]
3780 return '<%s%s %d:%d>' % (type(self).__name__, d,
3780 return '<%s%s %d:%d>' % (type(self).__name__, d,
3781 self._start, self._end - 1)
3781 self._start, self._end - 1)
3782
3782
3783 class fullreposet(spanset):
3783 class fullreposet(spanset):
3784 """a set containing all revisions in the repo
3784 """a set containing all revisions in the repo
3785
3785
3786 This class exists to host special optimization and magic to handle virtual
3786 This class exists to host special optimization and magic to handle virtual
3787 revisions such as "null".
3787 revisions such as "null".
3788 """
3788 """
3789
3789
3790 def __init__(self, repo):
3790 def __init__(self, repo):
3791 super(fullreposet, self).__init__(repo)
3791 super(fullreposet, self).__init__(repo)
3792
3792
3793 def __and__(self, other):
3793 def __and__(self, other):
3794 """As self contains the whole repo, all of the other set should also be
3794 """As self contains the whole repo, all of the other set should also be
3795 in self. Therefore `self & other = other`.
3795 in self. Therefore `self & other = other`.
3796
3796
3797 This boldly assumes the other contains valid revs only.
3797 This boldly assumes the other contains valid revs only.
3798 """
3798 """
3799 # other not a smartset, make is so
3799 # other not a smartset, make is so
3800 if not util.safehasattr(other, 'isascending'):
3800 if not util.safehasattr(other, 'isascending'):
3801 # filter out hidden revision
3801 # filter out hidden revision
3802 # (this boldly assumes all smartset are pure)
3802 # (this boldly assumes all smartset are pure)
3803 #
3803 #
3804 # `other` was used with "&", let's assume this is a set like
3804 # `other` was used with "&", let's assume this is a set like
3805 # object.
3805 # object.
3806 other = baseset(other - self._hiddenrevs)
3806 other = baseset(other - self._hiddenrevs)
3807
3807
3808 # XXX As fullreposet is also used as bootstrap, this is wrong.
3808 # XXX As fullreposet is also used as bootstrap, this is wrong.
3809 #
3809 #
3810 # With a giveme312() revset returning [3,1,2], this makes
3810 # With a giveme312() revset returning [3,1,2], this makes
3811 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3811 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3812 # We cannot just drop it because other usage still need to sort it:
3812 # We cannot just drop it because other usage still need to sort it:
3813 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3813 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3814 #
3814 #
3815 # There is also some faulty revset implementations that rely on it
3815 # There is also some faulty revset implementations that rely on it
3816 # (eg: children as of its state in e8075329c5fb)
3816 # (eg: children as of its state in e8075329c5fb)
3817 #
3817 #
3818 # When we fix the two points above we can move this into the if clause
3818 # When we fix the two points above we can move this into the if clause
3819 other.sort(reverse=self.isdescending())
3819 other.sort(reverse=self.isdescending())
3820 return other
3820 return other
3821
3821
3822 def prettyformatset(revs):
3822 def prettyformatset(revs):
3823 lines = []
3823 lines = []
3824 rs = repr(revs)
3824 rs = repr(revs)
3825 p = 0
3825 p = 0
3826 while p < len(rs):
3826 while p < len(rs):
3827 q = rs.find('<', p + 1)
3827 q = rs.find('<', p + 1)
3828 if q < 0:
3828 if q < 0:
3829 q = len(rs)
3829 q = len(rs)
3830 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3830 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3831 assert l >= 0
3831 assert l >= 0
3832 lines.append((l, rs[p:q].rstrip()))
3832 lines.append((l, rs[p:q].rstrip()))
3833 p = q
3833 p = q
3834 return '\n'.join(' ' * l + s for l, s in lines)
3834 return '\n'.join(' ' * l + s for l, s in lines)
3835
3835
3836 def loadpredicate(ui, extname, registrarobj):
3836 def loadpredicate(ui, extname, registrarobj):
3837 """Load revset predicates from specified registrarobj
3837 """Load revset predicates from specified registrarobj
3838 """
3838 """
3839 for name, func in registrarobj._table.iteritems():
3839 for name, func in registrarobj._table.iteritems():
3840 symbols[name] = func
3840 symbols[name] = func
3841 if func._safe:
3841 if func._safe:
3842 safesymbols.add(name)
3842 safesymbols.add(name)
3843
3843
3844 # load built-in predicates explicitly to setup safesymbols
3844 # load built-in predicates explicitly to setup safesymbols
3845 loadpredicate(None, None, predicate)
3845 loadpredicate(None, None, predicate)
3846
3846
3847 # tell hggettext to extract docstrings from these functions:
3847 # tell hggettext to extract docstrings from these functions:
3848 i18nfunctions = symbols.values()
3848 i18nfunctions = symbols.values()
@@ -1,3632 +1,3633
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols['r3232'] = r3232
19 > mercurial.revset.symbols['r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > testrevset=$TESTTMP/testrevset.py
23 > testrevset=$TESTTMP/testrevset.py
24 > EOF
24 > EOF
25
25
26 $ try() {
26 $ try() {
27 > hg debugrevspec --debug "$@"
27 > hg debugrevspec --debug "$@"
28 > }
28 > }
29
29
30 $ log() {
30 $ log() {
31 > hg log --template '{rev}\n' -r "$1"
31 > hg log --template '{rev}\n' -r "$1"
32 > }
32 > }
33
33
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 these predicates use '\0' as a separator:
35 these predicates use '\0' as a separator:
36
36
37 $ cat <<EOF > debugrevlistspec.py
37 $ cat <<EOF > debugrevlistspec.py
38 > from __future__ import absolute_import
38 > from __future__ import absolute_import
39 > from mercurial import (
39 > from mercurial import (
40 > cmdutil,
40 > cmdutil,
41 > node as nodemod,
41 > node as nodemod,
42 > revset,
42 > revset,
43 > )
43 > )
44 > cmdtable = {}
44 > cmdtable = {}
45 > command = cmdutil.command(cmdtable)
45 > command = cmdutil.command(cmdtable)
46 > @command('debugrevlistspec',
46 > @command('debugrevlistspec',
47 > [('', 'optimize', None, 'print parsed tree after optimizing'),
47 > [('', 'optimize', None, 'print parsed tree after optimizing'),
48 > ('', 'bin', None, 'unhexlify arguments')])
48 > ('', 'bin', None, 'unhexlify arguments')])
49 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
49 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
50 > if opts['bin']:
50 > if opts['bin']:
51 > args = map(nodemod.bin, args)
51 > args = map(nodemod.bin, args)
52 > expr = revset.formatspec(fmt, list(args))
52 > expr = revset.formatspec(fmt, list(args))
53 > if ui.verbose:
53 > if ui.verbose:
54 > tree = revset.parse(expr, lookup=repo.__contains__)
54 > tree = revset.parse(expr, lookup=repo.__contains__)
55 > ui.note(revset.prettyformat(tree), "\n")
55 > ui.note(revset.prettyformat(tree), "\n")
56 > if opts["optimize"]:
56 > if opts["optimize"]:
57 > opttree = revset.optimize(revset.analyze(tree))
57 > opttree = revset.optimize(revset.analyze(tree))
58 > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n")
58 > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n")
59 > func = revset.match(ui, expr, repo)
59 > func = revset.match(ui, expr, repo)
60 > revs = func(repo)
60 > revs = func(repo)
61 > if ui.verbose:
61 > if ui.verbose:
62 > ui.note("* set:\n", revset.prettyformatset(revs), "\n")
62 > ui.note("* set:\n", revset.prettyformatset(revs), "\n")
63 > for c in revs:
63 > for c in revs:
64 > ui.write("%s\n" % c)
64 > ui.write("%s\n" % c)
65 > EOF
65 > EOF
66 $ cat <<EOF >> $HGRCPATH
66 $ cat <<EOF >> $HGRCPATH
67 > [extensions]
67 > [extensions]
68 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
68 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
69 > EOF
69 > EOF
70 $ trylist() {
70 $ trylist() {
71 > hg debugrevlistspec --debug "$@"
71 > hg debugrevlistspec --debug "$@"
72 > }
72 > }
73
73
74 $ hg init repo
74 $ hg init repo
75 $ cd repo
75 $ cd repo
76
76
77 $ echo a > a
77 $ echo a > a
78 $ hg branch a
78 $ hg branch a
79 marked working directory as branch a
79 marked working directory as branch a
80 (branches are permanent and global, did you want a bookmark?)
80 (branches are permanent and global, did you want a bookmark?)
81 $ hg ci -Aqm0
81 $ hg ci -Aqm0
82
82
83 $ echo b > b
83 $ echo b > b
84 $ hg branch b
84 $ hg branch b
85 marked working directory as branch b
85 marked working directory as branch b
86 $ hg ci -Aqm1
86 $ hg ci -Aqm1
87
87
88 $ rm a
88 $ rm a
89 $ hg branch a-b-c-
89 $ hg branch a-b-c-
90 marked working directory as branch a-b-c-
90 marked working directory as branch a-b-c-
91 $ hg ci -Aqm2 -u Bob
91 $ hg ci -Aqm2 -u Bob
92
92
93 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
93 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
94 2
94 2
95 $ hg log -r "extra('branch')" --template '{rev}\n'
95 $ hg log -r "extra('branch')" --template '{rev}\n'
96 0
96 0
97 1
97 1
98 2
98 2
99 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
99 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
100 0 a
100 0 a
101 2 a-b-c-
101 2 a-b-c-
102
102
103 $ hg co 1
103 $ hg co 1
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 $ hg branch +a+b+c+
105 $ hg branch +a+b+c+
106 marked working directory as branch +a+b+c+
106 marked working directory as branch +a+b+c+
107 $ hg ci -Aqm3
107 $ hg ci -Aqm3
108
108
109 $ hg co 2 # interleave
109 $ hg co 2 # interleave
110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
111 $ echo bb > b
111 $ echo bb > b
112 $ hg branch -- -a-b-c-
112 $ hg branch -- -a-b-c-
113 marked working directory as branch -a-b-c-
113 marked working directory as branch -a-b-c-
114 $ hg ci -Aqm4 -d "May 12 2005"
114 $ hg ci -Aqm4 -d "May 12 2005"
115
115
116 $ hg co 3
116 $ hg co 3
117 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 $ hg branch !a/b/c/
118 $ hg branch !a/b/c/
119 marked working directory as branch !a/b/c/
119 marked working directory as branch !a/b/c/
120 $ hg ci -Aqm"5 bug"
120 $ hg ci -Aqm"5 bug"
121
121
122 $ hg merge 4
122 $ hg merge 4
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 (branch merge, don't forget to commit)
124 (branch merge, don't forget to commit)
125 $ hg branch _a_b_c_
125 $ hg branch _a_b_c_
126 marked working directory as branch _a_b_c_
126 marked working directory as branch _a_b_c_
127 $ hg ci -Aqm"6 issue619"
127 $ hg ci -Aqm"6 issue619"
128
128
129 $ hg branch .a.b.c.
129 $ hg branch .a.b.c.
130 marked working directory as branch .a.b.c.
130 marked working directory as branch .a.b.c.
131 $ hg ci -Aqm7
131 $ hg ci -Aqm7
132
132
133 $ hg branch all
133 $ hg branch all
134 marked working directory as branch all
134 marked working directory as branch all
135
135
136 $ hg co 4
136 $ hg co 4
137 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 $ hg branch Γ©
138 $ hg branch Γ©
139 marked working directory as branch \xc3\xa9 (esc)
139 marked working directory as branch \xc3\xa9 (esc)
140 $ hg ci -Aqm9
140 $ hg ci -Aqm9
141
141
142 $ hg tag -r6 1.0
142 $ hg tag -r6 1.0
143 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
143 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
144
144
145 $ hg clone --quiet -U -r 7 . ../remote1
145 $ hg clone --quiet -U -r 7 . ../remote1
146 $ hg clone --quiet -U -r 8 . ../remote2
146 $ hg clone --quiet -U -r 8 . ../remote2
147 $ echo "[paths]" >> .hg/hgrc
147 $ echo "[paths]" >> .hg/hgrc
148 $ echo "default = ../remote1" >> .hg/hgrc
148 $ echo "default = ../remote1" >> .hg/hgrc
149
149
150 trivial
150 trivial
151
151
152 $ try 0:1
152 $ try 0:1
153 (range
153 (range
154 ('symbol', '0')
154 ('symbol', '0')
155 ('symbol', '1'))
155 ('symbol', '1'))
156 * set:
156 * set:
157 <spanset+ 0:1>
157 <spanset+ 0:1>
158 0
158 0
159 1
159 1
160 $ try --optimize :
160 $ try --optimize :
161 (rangeall
161 (rangeall
162 None)
162 None)
163 * optimized:
163 * optimized:
164 (rangepre
164 (rangepre
165 ('string', 'tip')
165 ('string', 'tip')
166 define)
166 define)
167 * set:
167 * set:
168 <spanset+ 0:9>
168 <spanset+ 0:9>
169 0
169 0
170 1
170 1
171 2
171 2
172 3
172 3
173 4
173 4
174 5
174 5
175 6
175 6
176 7
176 7
177 8
177 8
178 9
178 9
179 $ try 3::6
179 $ try 3::6
180 (dagrange
180 (dagrange
181 ('symbol', '3')
181 ('symbol', '3')
182 ('symbol', '6'))
182 ('symbol', '6'))
183 * set:
183 * set:
184 <baseset+ [3, 5, 6]>
184 <baseset+ [3, 5, 6]>
185 3
185 3
186 5
186 5
187 6
187 6
188 $ try '0|1|2'
188 $ try '0|1|2'
189 (or
189 (or
190 (list
190 (list
191 ('symbol', '0')
191 ('symbol', '0')
192 ('symbol', '1')
192 ('symbol', '1')
193 ('symbol', '2')))
193 ('symbol', '2')))
194 * set:
194 * set:
195 <baseset [0, 1, 2]>
195 <baseset [0, 1, 2]>
196 0
196 0
197 1
197 1
198 2
198 2
199
199
200 names that should work without quoting
200 names that should work without quoting
201
201
202 $ try a
202 $ try a
203 ('symbol', 'a')
203 ('symbol', 'a')
204 * set:
204 * set:
205 <baseset [0]>
205 <baseset [0]>
206 0
206 0
207 $ try b-a
207 $ try b-a
208 (minus
208 (minus
209 ('symbol', 'b')
209 ('symbol', 'b')
210 ('symbol', 'a'))
210 ('symbol', 'a'))
211 * set:
211 * set:
212 <filteredset
212 <filteredset
213 <baseset [1]>,
213 <baseset [1]>,
214 <not
214 <not
215 <baseset [0]>>>
215 <baseset [0]>>>
216 1
216 1
217 $ try _a_b_c_
217 $ try _a_b_c_
218 ('symbol', '_a_b_c_')
218 ('symbol', '_a_b_c_')
219 * set:
219 * set:
220 <baseset [6]>
220 <baseset [6]>
221 6
221 6
222 $ try _a_b_c_-a
222 $ try _a_b_c_-a
223 (minus
223 (minus
224 ('symbol', '_a_b_c_')
224 ('symbol', '_a_b_c_')
225 ('symbol', 'a'))
225 ('symbol', 'a'))
226 * set:
226 * set:
227 <filteredset
227 <filteredset
228 <baseset [6]>,
228 <baseset [6]>,
229 <not
229 <not
230 <baseset [0]>>>
230 <baseset [0]>>>
231 6
231 6
232 $ try .a.b.c.
232 $ try .a.b.c.
233 ('symbol', '.a.b.c.')
233 ('symbol', '.a.b.c.')
234 * set:
234 * set:
235 <baseset [7]>
235 <baseset [7]>
236 7
236 7
237 $ try .a.b.c.-a
237 $ try .a.b.c.-a
238 (minus
238 (minus
239 ('symbol', '.a.b.c.')
239 ('symbol', '.a.b.c.')
240 ('symbol', 'a'))
240 ('symbol', 'a'))
241 * set:
241 * set:
242 <filteredset
242 <filteredset
243 <baseset [7]>,
243 <baseset [7]>,
244 <not
244 <not
245 <baseset [0]>>>
245 <baseset [0]>>>
246 7
246 7
247
247
248 names that should be caught by fallback mechanism
248 names that should be caught by fallback mechanism
249
249
250 $ try -- '-a-b-c-'
250 $ try -- '-a-b-c-'
251 ('symbol', '-a-b-c-')
251 ('symbol', '-a-b-c-')
252 * set:
252 * set:
253 <baseset [4]>
253 <baseset [4]>
254 4
254 4
255 $ log -a-b-c-
255 $ log -a-b-c-
256 4
256 4
257 $ try '+a+b+c+'
257 $ try '+a+b+c+'
258 ('symbol', '+a+b+c+')
258 ('symbol', '+a+b+c+')
259 * set:
259 * set:
260 <baseset [3]>
260 <baseset [3]>
261 3
261 3
262 $ try '+a+b+c+:'
262 $ try '+a+b+c+:'
263 (rangepost
263 (rangepost
264 ('symbol', '+a+b+c+'))
264 ('symbol', '+a+b+c+'))
265 * set:
265 * set:
266 <spanset+ 3:9>
266 <spanset+ 3:9>
267 3
267 3
268 4
268 4
269 5
269 5
270 6
270 6
271 7
271 7
272 8
272 8
273 9
273 9
274 $ try ':+a+b+c+'
274 $ try ':+a+b+c+'
275 (rangepre
275 (rangepre
276 ('symbol', '+a+b+c+'))
276 ('symbol', '+a+b+c+'))
277 * set:
277 * set:
278 <spanset+ 0:3>
278 <spanset+ 0:3>
279 0
279 0
280 1
280 1
281 2
281 2
282 3
282 3
283 $ try -- '-a-b-c-:+a+b+c+'
283 $ try -- '-a-b-c-:+a+b+c+'
284 (range
284 (range
285 ('symbol', '-a-b-c-')
285 ('symbol', '-a-b-c-')
286 ('symbol', '+a+b+c+'))
286 ('symbol', '+a+b+c+'))
287 * set:
287 * set:
288 <spanset- 3:4>
288 <spanset- 3:4>
289 4
289 4
290 3
290 3
291 $ log '-a-b-c-:+a+b+c+'
291 $ log '-a-b-c-:+a+b+c+'
292 4
292 4
293 3
293 3
294
294
295 $ try -- -a-b-c--a # complains
295 $ try -- -a-b-c--a # complains
296 (minus
296 (minus
297 (minus
297 (minus
298 (minus
298 (minus
299 (negate
299 (negate
300 ('symbol', 'a'))
300 ('symbol', 'a'))
301 ('symbol', 'b'))
301 ('symbol', 'b'))
302 ('symbol', 'c'))
302 ('symbol', 'c'))
303 (negate
303 (negate
304 ('symbol', 'a')))
304 ('symbol', 'a')))
305 abort: unknown revision '-a'!
305 abort: unknown revision '-a'!
306 [255]
306 [255]
307 $ try Γ©
307 $ try Γ©
308 ('symbol', '\xc3\xa9')
308 ('symbol', '\xc3\xa9')
309 * set:
309 * set:
310 <baseset [9]>
310 <baseset [9]>
311 9
311 9
312
312
313 no quoting needed
313 no quoting needed
314
314
315 $ log ::a-b-c-
315 $ log ::a-b-c-
316 0
316 0
317 1
317 1
318 2
318 2
319
319
320 quoting needed
320 quoting needed
321
321
322 $ try '"-a-b-c-"-a'
322 $ try '"-a-b-c-"-a'
323 (minus
323 (minus
324 ('string', '-a-b-c-')
324 ('string', '-a-b-c-')
325 ('symbol', 'a'))
325 ('symbol', 'a'))
326 * set:
326 * set:
327 <filteredset
327 <filteredset
328 <baseset [4]>,
328 <baseset [4]>,
329 <not
329 <not
330 <baseset [0]>>>
330 <baseset [0]>>>
331 4
331 4
332
332
333 $ log '1 or 2'
333 $ log '1 or 2'
334 1
334 1
335 2
335 2
336 $ log '1|2'
336 $ log '1|2'
337 1
337 1
338 2
338 2
339 $ log '1 and 2'
339 $ log '1 and 2'
340 $ log '1&2'
340 $ log '1&2'
341 $ try '1&2|3' # precedence - and is higher
341 $ try '1&2|3' # precedence - and is higher
342 (or
342 (or
343 (list
343 (list
344 (and
344 (and
345 ('symbol', '1')
345 ('symbol', '1')
346 ('symbol', '2'))
346 ('symbol', '2'))
347 ('symbol', '3')))
347 ('symbol', '3')))
348 * set:
348 * set:
349 <addset
349 <addset
350 <baseset []>,
350 <baseset []>,
351 <baseset [3]>>
351 <baseset [3]>>
352 3
352 3
353 $ try '1|2&3'
353 $ try '1|2&3'
354 (or
354 (or
355 (list
355 (list
356 ('symbol', '1')
356 ('symbol', '1')
357 (and
357 (and
358 ('symbol', '2')
358 ('symbol', '2')
359 ('symbol', '3'))))
359 ('symbol', '3'))))
360 * set:
360 * set:
361 <addset
361 <addset
362 <baseset [1]>,
362 <baseset [1]>,
363 <baseset []>>
363 <baseset []>>
364 1
364 1
365 $ try '1&2&3' # associativity
365 $ try '1&2&3' # associativity
366 (and
366 (and
367 (and
367 (and
368 ('symbol', '1')
368 ('symbol', '1')
369 ('symbol', '2'))
369 ('symbol', '2'))
370 ('symbol', '3'))
370 ('symbol', '3'))
371 * set:
371 * set:
372 <baseset []>
372 <baseset []>
373 $ try '1|(2|3)'
373 $ try '1|(2|3)'
374 (or
374 (or
375 (list
375 (list
376 ('symbol', '1')
376 ('symbol', '1')
377 (group
377 (group
378 (or
378 (or
379 (list
379 (list
380 ('symbol', '2')
380 ('symbol', '2')
381 ('symbol', '3'))))))
381 ('symbol', '3'))))))
382 * set:
382 * set:
383 <addset
383 <addset
384 <baseset [1]>,
384 <baseset [1]>,
385 <baseset [2, 3]>>
385 <baseset [2, 3]>>
386 1
386 1
387 2
387 2
388 3
388 3
389 $ log '1.0' # tag
389 $ log '1.0' # tag
390 6
390 6
391 $ log 'a' # branch
391 $ log 'a' # branch
392 0
392 0
393 $ log '2785f51ee'
393 $ log '2785f51ee'
394 0
394 0
395 $ log 'date(2005)'
395 $ log 'date(2005)'
396 4
396 4
397 $ log 'date(this is a test)'
397 $ log 'date(this is a test)'
398 hg: parse error at 10: unexpected token: symbol
398 hg: parse error at 10: unexpected token: symbol
399 [255]
399 [255]
400 $ log 'date()'
400 $ log 'date()'
401 hg: parse error: date requires a string
401 hg: parse error: date requires a string
402 [255]
402 [255]
403 $ log 'date'
403 $ log 'date'
404 abort: unknown revision 'date'!
404 abort: unknown revision 'date'!
405 [255]
405 [255]
406 $ log 'date('
406 $ log 'date('
407 hg: parse error at 5: not a prefix: end
407 hg: parse error at 5: not a prefix: end
408 [255]
408 [255]
409 $ log 'date("\xy")'
409 $ log 'date("\xy")'
410 hg: parse error: invalid \x escape
410 hg: parse error: invalid \x escape
411 [255]
411 [255]
412 $ log 'date(tip)'
412 $ log 'date(tip)'
413 abort: invalid date: 'tip'
413 abort: invalid date: 'tip'
414 [255]
414 [255]
415 $ log '0:date'
415 $ log '0:date'
416 abort: unknown revision 'date'!
416 abort: unknown revision 'date'!
417 [255]
417 [255]
418 $ log '::"date"'
418 $ log '::"date"'
419 abort: unknown revision 'date'!
419 abort: unknown revision 'date'!
420 [255]
420 [255]
421 $ hg book date -r 4
421 $ hg book date -r 4
422 $ log '0:date'
422 $ log '0:date'
423 0
423 0
424 1
424 1
425 2
425 2
426 3
426 3
427 4
427 4
428 $ log '::date'
428 $ log '::date'
429 0
429 0
430 1
430 1
431 2
431 2
432 4
432 4
433 $ log '::"date"'
433 $ log '::"date"'
434 0
434 0
435 1
435 1
436 2
436 2
437 4
437 4
438 $ log 'date(2005) and 1::'
438 $ log 'date(2005) and 1::'
439 4
439 4
440 $ hg book -d date
440 $ hg book -d date
441
441
442 function name should be a symbol
442 function name should be a symbol
443
443
444 $ log '"date"(2005)'
444 $ log '"date"(2005)'
445 hg: parse error: not a symbol
445 hg: parse error: not a symbol
446 [255]
446 [255]
447
447
448 keyword arguments
448 keyword arguments
449
449
450 $ log 'extra(branch, value=a)'
450 $ log 'extra(branch, value=a)'
451 0
451 0
452
452
453 $ log 'extra(branch, a, b)'
453 $ log 'extra(branch, a, b)'
454 hg: parse error: extra takes at most 2 arguments
454 hg: parse error: extra takes at most 2 arguments
455 [255]
455 [255]
456 $ log 'extra(a, label=b)'
456 $ log 'extra(a, label=b)'
457 hg: parse error: extra got multiple values for keyword argument 'label'
457 hg: parse error: extra got multiple values for keyword argument 'label'
458 [255]
458 [255]
459 $ log 'extra(label=branch, default)'
459 $ log 'extra(label=branch, default)'
460 hg: parse error: extra got an invalid argument
460 hg: parse error: extra got an invalid argument
461 [255]
461 [255]
462 $ log 'extra(branch, foo+bar=baz)'
462 $ log 'extra(branch, foo+bar=baz)'
463 hg: parse error: extra got an invalid argument
463 hg: parse error: extra got an invalid argument
464 [255]
464 [255]
465 $ log 'extra(unknown=branch)'
465 $ log 'extra(unknown=branch)'
466 hg: parse error: extra got an unexpected keyword argument 'unknown'
466 hg: parse error: extra got an unexpected keyword argument 'unknown'
467 [255]
467 [255]
468
468
469 $ try 'foo=bar|baz'
469 $ try 'foo=bar|baz'
470 (keyvalue
470 (keyvalue
471 ('symbol', 'foo')
471 ('symbol', 'foo')
472 (or
472 (or
473 (list
473 (list
474 ('symbol', 'bar')
474 ('symbol', 'bar')
475 ('symbol', 'baz'))))
475 ('symbol', 'baz'))))
476 hg: parse error: can't use a key-value pair in this context
476 hg: parse error: can't use a key-value pair in this context
477 [255]
477 [255]
478
478
479 right-hand side should be optimized recursively
479 right-hand side should be optimized recursively
480
480
481 $ try --optimize 'foo=(not public())'
481 $ try --optimize 'foo=(not public())'
482 (keyvalue
482 (keyvalue
483 ('symbol', 'foo')
483 ('symbol', 'foo')
484 (group
484 (group
485 (not
485 (not
486 (func
486 (func
487 ('symbol', 'public')
487 ('symbol', 'public')
488 None))))
488 None))))
489 * optimized:
489 * optimized:
490 (keyvalue
490 (keyvalue
491 ('symbol', 'foo')
491 ('symbol', 'foo')
492 (func
492 (func
493 ('symbol', '_notpublic')
493 ('symbol', '_notpublic')
494 None
494 None
495 any))
495 any))
496 hg: parse error: can't use a key-value pair in this context
496 hg: parse error: can't use a key-value pair in this context
497 [255]
497 [255]
498
498
499 parsed tree at stages:
499 parsed tree at stages:
500
500
501 $ hg debugrevspec -p all '()'
501 $ hg debugrevspec -p all '()'
502 * parsed:
502 * parsed:
503 (group
503 (group
504 None)
504 None)
505 * expanded:
505 * expanded:
506 (group
506 (group
507 None)
507 None)
508 * concatenated:
508 * concatenated:
509 (group
509 (group
510 None)
510 None)
511 * analyzed:
511 * analyzed:
512 None
512 None
513 * optimized:
513 * optimized:
514 None
514 None
515 hg: parse error: missing argument
515 hg: parse error: missing argument
516 [255]
516 [255]
517
517
518 $ hg debugrevspec --no-optimized -p all '()'
518 $ hg debugrevspec --no-optimized -p all '()'
519 * parsed:
519 * parsed:
520 (group
520 (group
521 None)
521 None)
522 * expanded:
522 * expanded:
523 (group
523 (group
524 None)
524 None)
525 * concatenated:
525 * concatenated:
526 (group
526 (group
527 None)
527 None)
528 * analyzed:
528 * analyzed:
529 None
529 None
530 hg: parse error: missing argument
530 hg: parse error: missing argument
531 [255]
531 [255]
532
532
533 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
533 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
534 * parsed:
534 * parsed:
535 (minus
535 (minus
536 (group
536 (group
537 (or
537 (or
538 (list
538 (list
539 ('symbol', '0')
539 ('symbol', '0')
540 ('symbol', '1'))))
540 ('symbol', '1'))))
541 ('symbol', '1'))
541 ('symbol', '1'))
542 * analyzed:
542 * analyzed:
543 (and
543 (and
544 (or
544 (or
545 (list
545 (list
546 ('symbol', '0')
546 ('symbol', '0')
547 ('symbol', '1'))
547 ('symbol', '1'))
548 define)
548 define)
549 (not
549 (not
550 ('symbol', '1')
550 ('symbol', '1')
551 follow)
551 follow)
552 define)
552 define)
553 * optimized:
553 * optimized:
554 (difference
554 (difference
555 (func
555 (func
556 ('symbol', '_list')
556 ('symbol', '_list')
557 ('string', '0\x001')
557 ('string', '0\x001')
558 define)
558 define)
559 ('symbol', '1')
559 ('symbol', '1')
560 define)
560 define)
561 0
561 0
562
562
563 $ hg debugrevspec -p unknown '0'
563 $ hg debugrevspec -p unknown '0'
564 abort: invalid stage name: unknown
564 abort: invalid stage name: unknown
565 [255]
565 [255]
566
566
567 $ hg debugrevspec -p all --optimize '0'
567 $ hg debugrevspec -p all --optimize '0'
568 abort: cannot use --optimize with --show-stage
568 abort: cannot use --optimize with --show-stage
569 [255]
569 [255]
570
570
571 verify optimized tree:
571 verify optimized tree:
572
572
573 $ hg debugrevspec --verify '0|1'
573 $ hg debugrevspec --verify '0|1'
574
574
575 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
575 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
576 * analyzed:
576 * analyzed:
577 (and
577 (and
578 (func
578 (func
579 ('symbol', 'r3232')
579 ('symbol', 'r3232')
580 None
580 None
581 define)
581 define)
582 ('symbol', '2')
582 ('symbol', '2')
583 define)
583 define)
584 * optimized:
584 * optimized:
585 (and
585 (and
586 ('symbol', '2')
586 ('symbol', '2')
587 (func
587 (func
588 ('symbol', 'r3232')
588 ('symbol', 'r3232')
589 None
589 None
590 define)
590 define)
591 define)
591 define)
592 * analyzed set:
592 * analyzed set:
593 <baseset [2]>
593 <baseset [2]>
594 * optimized set:
594 * optimized set:
595 <baseset [2, 2]>
595 <baseset [2, 2]>
596 --- analyzed
596 --- analyzed
597 +++ optimized
597 +++ optimized
598 2
598 2
599 +2
599 +2
600 [1]
600 [1]
601
601
602 $ hg debugrevspec --no-optimized --verify-optimized '0'
602 $ hg debugrevspec --no-optimized --verify-optimized '0'
603 abort: cannot use --verify-optimized with --no-optimized
603 abort: cannot use --verify-optimized with --no-optimized
604 [255]
604 [255]
605
605
606 Test that symbols only get parsed as functions if there's an opening
606 Test that symbols only get parsed as functions if there's an opening
607 parenthesis.
607 parenthesis.
608
608
609 $ hg book only -r 9
609 $ hg book only -r 9
610 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
610 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
611 8
611 8
612 9
612 9
613
613
614 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
614 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
615 may be hidden (issue5385)
615 may be hidden (issue5385)
616
616
617 $ try -p parsed -p analyzed ':'
617 $ try -p parsed -p analyzed ':'
618 * parsed:
618 * parsed:
619 (rangeall
619 (rangeall
620 None)
620 None)
621 * analyzed:
621 * analyzed:
622 (rangepre
622 (rangepre
623 ('string', 'tip')
623 ('string', 'tip')
624 define)
624 define)
625 * set:
625 * set:
626 <spanset+ 0:9>
626 <spanset+ 0:9>
627 0
627 0
628 1
628 1
629 2
629 2
630 3
630 3
631 4
631 4
632 5
632 5
633 6
633 6
634 7
634 7
635 8
635 8
636 9
636 9
637 $ try -p analyzed ':1'
637 $ try -p analyzed ':1'
638 * analyzed:
638 * analyzed:
639 (rangepre
639 (rangepre
640 ('symbol', '1')
640 ('symbol', '1')
641 define)
641 define)
642 * set:
642 * set:
643 <spanset+ 0:1>
643 <spanset+ 0:1>
644 0
644 0
645 1
645 1
646 $ try -p analyzed ':(1|2)'
646 $ try -p analyzed ':(1|2)'
647 * analyzed:
647 * analyzed:
648 (rangepre
648 (rangepre
649 (or
649 (or
650 (list
650 (list
651 ('symbol', '1')
651 ('symbol', '1')
652 ('symbol', '2'))
652 ('symbol', '2'))
653 define)
653 define)
654 define)
654 define)
655 * set:
655 * set:
656 <spanset+ 0:2>
656 <spanset+ 0:2>
657 0
657 0
658 1
658 1
659 2
659 2
660 $ try -p analyzed ':(1&2)'
660 $ try -p analyzed ':(1&2)'
661 * analyzed:
661 * analyzed:
662 (rangepre
662 (rangepre
663 (and
663 (and
664 ('symbol', '1')
664 ('symbol', '1')
665 ('symbol', '2')
665 ('symbol', '2')
666 define)
666 define)
667 define)
667 define)
668 * set:
668 * set:
669 <baseset []>
669 <baseset []>
670
670
671 infix/suffix resolution of ^ operator (issue2884):
671 infix/suffix resolution of ^ operator (issue2884):
672
672
673 x^:y means (x^):y
673 x^:y means (x^):y
674
674
675 $ try '1^:2'
675 $ try '1^:2'
676 (range
676 (range
677 (parentpost
677 (parentpost
678 ('symbol', '1'))
678 ('symbol', '1'))
679 ('symbol', '2'))
679 ('symbol', '2'))
680 * set:
680 * set:
681 <spanset+ 0:2>
681 <spanset+ 0:2>
682 0
682 0
683 1
683 1
684 2
684 2
685
685
686 $ try '1^::2'
686 $ try '1^::2'
687 (dagrange
687 (dagrange
688 (parentpost
688 (parentpost
689 ('symbol', '1'))
689 ('symbol', '1'))
690 ('symbol', '2'))
690 ('symbol', '2'))
691 * set:
691 * set:
692 <baseset+ [0, 1, 2]>
692 <baseset+ [0, 1, 2]>
693 0
693 0
694 1
694 1
695 2
695 2
696
696
697 $ try '9^:'
697 $ try '9^:'
698 (rangepost
698 (rangepost
699 (parentpost
699 (parentpost
700 ('symbol', '9')))
700 ('symbol', '9')))
701 * set:
701 * set:
702 <spanset+ 8:9>
702 <spanset+ 8:9>
703 8
703 8
704 9
704 9
705
705
706 x^:y should be resolved before omitting group operators
706 x^:y should be resolved before omitting group operators
707
707
708 $ try '1^(:2)'
708 $ try '1^(:2)'
709 (parent
709 (parent
710 ('symbol', '1')
710 ('symbol', '1')
711 (group
711 (group
712 (rangepre
712 (rangepre
713 ('symbol', '2'))))
713 ('symbol', '2'))))
714 hg: parse error: ^ expects a number 0, 1, or 2
714 hg: parse error: ^ expects a number 0, 1, or 2
715 [255]
715 [255]
716
716
717 x^:y should be resolved recursively
717 x^:y should be resolved recursively
718
718
719 $ try 'sort(1^:2)'
719 $ try 'sort(1^:2)'
720 (func
720 (func
721 ('symbol', 'sort')
721 ('symbol', 'sort')
722 (range
722 (range
723 (parentpost
723 (parentpost
724 ('symbol', '1'))
724 ('symbol', '1'))
725 ('symbol', '2')))
725 ('symbol', '2')))
726 * set:
726 * set:
727 <spanset+ 0:2>
727 <spanset+ 0:2>
728 0
728 0
729 1
729 1
730 2
730 2
731
731
732 $ try '(3^:4)^:2'
732 $ try '(3^:4)^:2'
733 (range
733 (range
734 (parentpost
734 (parentpost
735 (group
735 (group
736 (range
736 (range
737 (parentpost
737 (parentpost
738 ('symbol', '3'))
738 ('symbol', '3'))
739 ('symbol', '4'))))
739 ('symbol', '4'))))
740 ('symbol', '2'))
740 ('symbol', '2'))
741 * set:
741 * set:
742 <spanset+ 0:2>
742 <spanset+ 0:2>
743 0
743 0
744 1
744 1
745 2
745 2
746
746
747 $ try '(3^::4)^::2'
747 $ try '(3^::4)^::2'
748 (dagrange
748 (dagrange
749 (parentpost
749 (parentpost
750 (group
750 (group
751 (dagrange
751 (dagrange
752 (parentpost
752 (parentpost
753 ('symbol', '3'))
753 ('symbol', '3'))
754 ('symbol', '4'))))
754 ('symbol', '4'))))
755 ('symbol', '2'))
755 ('symbol', '2'))
756 * set:
756 * set:
757 <baseset+ [0, 1, 2]>
757 <baseset+ [0, 1, 2]>
758 0
758 0
759 1
759 1
760 2
760 2
761
761
762 $ try '(9^:)^:'
762 $ try '(9^:)^:'
763 (rangepost
763 (rangepost
764 (parentpost
764 (parentpost
765 (group
765 (group
766 (rangepost
766 (rangepost
767 (parentpost
767 (parentpost
768 ('symbol', '9'))))))
768 ('symbol', '9'))))))
769 * set:
769 * set:
770 <spanset+ 4:9>
770 <spanset+ 4:9>
771 4
771 4
772 5
772 5
773 6
773 6
774 7
774 7
775 8
775 8
776 9
776 9
777
777
778 x^ in alias should also be resolved
778 x^ in alias should also be resolved
779
779
780 $ try 'A' --config 'revsetalias.A=1^:2'
780 $ try 'A' --config 'revsetalias.A=1^:2'
781 ('symbol', 'A')
781 ('symbol', 'A')
782 * expanded:
782 * expanded:
783 (range
783 (range
784 (parentpost
784 (parentpost
785 ('symbol', '1'))
785 ('symbol', '1'))
786 ('symbol', '2'))
786 ('symbol', '2'))
787 * set:
787 * set:
788 <spanset+ 0:2>
788 <spanset+ 0:2>
789 0
789 0
790 1
790 1
791 2
791 2
792
792
793 $ try 'A:2' --config 'revsetalias.A=1^'
793 $ try 'A:2' --config 'revsetalias.A=1^'
794 (range
794 (range
795 ('symbol', 'A')
795 ('symbol', 'A')
796 ('symbol', '2'))
796 ('symbol', '2'))
797 * expanded:
797 * expanded:
798 (range
798 (range
799 (parentpost
799 (parentpost
800 ('symbol', '1'))
800 ('symbol', '1'))
801 ('symbol', '2'))
801 ('symbol', '2'))
802 * set:
802 * set:
803 <spanset+ 0:2>
803 <spanset+ 0:2>
804 0
804 0
805 1
805 1
806 2
806 2
807
807
808 but not beyond the boundary of alias expansion, because the resolution should
808 but not beyond the boundary of alias expansion, because the resolution should
809 be made at the parsing stage
809 be made at the parsing stage
810
810
811 $ try '1^A' --config 'revsetalias.A=:2'
811 $ try '1^A' --config 'revsetalias.A=:2'
812 (parent
812 (parent
813 ('symbol', '1')
813 ('symbol', '1')
814 ('symbol', 'A'))
814 ('symbol', 'A'))
815 * expanded:
815 * expanded:
816 (parent
816 (parent
817 ('symbol', '1')
817 ('symbol', '1')
818 (rangepre
818 (rangepre
819 ('symbol', '2')))
819 ('symbol', '2')))
820 hg: parse error: ^ expects a number 0, 1, or 2
820 hg: parse error: ^ expects a number 0, 1, or 2
821 [255]
821 [255]
822
822
823 ancestor can accept 0 or more arguments
823 ancestor can accept 0 or more arguments
824
824
825 $ log 'ancestor()'
825 $ log 'ancestor()'
826 $ log 'ancestor(1)'
826 $ log 'ancestor(1)'
827 1
827 1
828 $ log 'ancestor(4,5)'
828 $ log 'ancestor(4,5)'
829 1
829 1
830 $ log 'ancestor(4,5) and 4'
830 $ log 'ancestor(4,5) and 4'
831 $ log 'ancestor(0,0,1,3)'
831 $ log 'ancestor(0,0,1,3)'
832 0
832 0
833 $ log 'ancestor(3,1,5,3,5,1)'
833 $ log 'ancestor(3,1,5,3,5,1)'
834 1
834 1
835 $ log 'ancestor(0,1,3,5)'
835 $ log 'ancestor(0,1,3,5)'
836 0
836 0
837 $ log 'ancestor(1,2,3,4,5)'
837 $ log 'ancestor(1,2,3,4,5)'
838 1
838 1
839
839
840 test ancestors
840 test ancestors
841
841
842 $ log 'ancestors(5)'
842 $ log 'ancestors(5)'
843 0
843 0
844 1
844 1
845 3
845 3
846 5
846 5
847 $ log 'ancestor(ancestors(5))'
847 $ log 'ancestor(ancestors(5))'
848 0
848 0
849 $ log '::r3232()'
849 $ log '::r3232()'
850 0
850 0
851 1
851 1
852 2
852 2
853 3
853 3
854
854
855 $ log 'author(bob)'
855 $ log 'author(bob)'
856 2
856 2
857 $ log 'author("re:bob|test")'
857 $ log 'author("re:bob|test")'
858 0
858 0
859 1
859 1
860 2
860 2
861 3
861 3
862 4
862 4
863 5
863 5
864 6
864 6
865 7
865 7
866 8
866 8
867 9
867 9
868 $ log 'branch(Γ©)'
868 $ log 'branch(Γ©)'
869 8
869 8
870 9
870 9
871 $ log 'branch(a)'
871 $ log 'branch(a)'
872 0
872 0
873 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
873 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
874 0 a
874 0 a
875 2 a-b-c-
875 2 a-b-c-
876 3 +a+b+c+
876 3 +a+b+c+
877 4 -a-b-c-
877 4 -a-b-c-
878 5 !a/b/c/
878 5 !a/b/c/
879 6 _a_b_c_
879 6 _a_b_c_
880 7 .a.b.c.
880 7 .a.b.c.
881 $ log 'children(ancestor(4,5))'
881 $ log 'children(ancestor(4,5))'
882 2
882 2
883 3
883 3
884 $ log 'closed()'
884 $ log 'closed()'
885 $ log 'contains(a)'
885 $ log 'contains(a)'
886 0
886 0
887 1
887 1
888 3
888 3
889 5
889 5
890 $ log 'contains("../repo/a")'
890 $ log 'contains("../repo/a")'
891 0
891 0
892 1
892 1
893 3
893 3
894 5
894 5
895 $ log 'desc(B)'
895 $ log 'desc(B)'
896 5
896 5
897 $ log 'descendants(2 or 3)'
897 $ log 'descendants(2 or 3)'
898 2
898 2
899 3
899 3
900 4
900 4
901 5
901 5
902 6
902 6
903 7
903 7
904 8
904 8
905 9
905 9
906 $ log 'file("b*")'
906 $ log 'file("b*")'
907 1
907 1
908 4
908 4
909 $ log 'filelog("b")'
909 $ log 'filelog("b")'
910 1
910 1
911 4
911 4
912 $ log 'filelog("../repo/b")'
912 $ log 'filelog("../repo/b")'
913 1
913 1
914 4
914 4
915 $ log 'follow()'
915 $ log 'follow()'
916 0
916 0
917 1
917 1
918 2
918 2
919 4
919 4
920 8
920 8
921 9
921 9
922 $ log 'grep("issue\d+")'
922 $ log 'grep("issue\d+")'
923 6
923 6
924 $ try 'grep("(")' # invalid regular expression
924 $ try 'grep("(")' # invalid regular expression
925 (func
925 (func
926 ('symbol', 'grep')
926 ('symbol', 'grep')
927 ('string', '('))
927 ('string', '('))
928 hg: parse error: invalid match pattern: unbalanced parenthesis
928 hg: parse error: invalid match pattern: unbalanced parenthesis
929 [255]
929 [255]
930 $ try 'grep("\bissue\d+")'
930 $ try 'grep("\bissue\d+")'
931 (func
931 (func
932 ('symbol', 'grep')
932 ('symbol', 'grep')
933 ('string', '\x08issue\\d+'))
933 ('string', '\x08issue\\d+'))
934 * set:
934 * set:
935 <filteredset
935 <filteredset
936 <fullreposet+ 0:9>,
936 <fullreposet+ 0:9>,
937 <grep '\x08issue\\d+'>>
937 <grep '\x08issue\\d+'>>
938 $ try 'grep(r"\bissue\d+")'
938 $ try 'grep(r"\bissue\d+")'
939 (func
939 (func
940 ('symbol', 'grep')
940 ('symbol', 'grep')
941 ('string', '\\bissue\\d+'))
941 ('string', '\\bissue\\d+'))
942 * set:
942 * set:
943 <filteredset
943 <filteredset
944 <fullreposet+ 0:9>,
944 <fullreposet+ 0:9>,
945 <grep '\\bissue\\d+'>>
945 <grep '\\bissue\\d+'>>
946 6
946 6
947 $ try 'grep(r"\")'
947 $ try 'grep(r"\")'
948 hg: parse error at 7: unterminated string
948 hg: parse error at 7: unterminated string
949 [255]
949 [255]
950 $ log 'head()'
950 $ log 'head()'
951 0
951 0
952 1
952 1
953 2
953 2
954 3
954 3
955 4
955 4
956 5
956 5
957 6
957 6
958 7
958 7
959 9
959 9
960 $ log 'heads(6::)'
960 $ log 'heads(6::)'
961 7
961 7
962 $ log 'keyword(issue)'
962 $ log 'keyword(issue)'
963 6
963 6
964 $ log 'keyword("test a")'
964 $ log 'keyword("test a")'
965 $ log 'limit(head(), 1)'
965 $ log 'limit(head(), 1)'
966 0
966 0
967 $ log 'limit(author("re:bob|test"), 3, 5)'
967 $ log 'limit(author("re:bob|test"), 3, 5)'
968 5
968 5
969 6
969 6
970 7
970 7
971 $ log 'limit(author("re:bob|test"), offset=6)'
971 $ log 'limit(author("re:bob|test"), offset=6)'
972 6
972 6
973 $ log 'limit(author("re:bob|test"), offset=10)'
973 $ log 'limit(author("re:bob|test"), offset=10)'
974 $ log 'limit(all(), 1, -1)'
974 $ log 'limit(all(), 1, -1)'
975 hg: parse error: negative offset
975 hg: parse error: negative offset
976 [255]
976 [255]
977 $ log 'matching(6)'
977 $ log 'matching(6)'
978 6
978 6
979 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
979 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
980 6
980 6
981 7
981 7
982
982
983 Testing min and max
983 Testing min and max
984
984
985 max: simple
985 max: simple
986
986
987 $ log 'max(contains(a))'
987 $ log 'max(contains(a))'
988 5
988 5
989
989
990 max: simple on unordered set)
990 max: simple on unordered set)
991
991
992 $ log 'max((4+0+2+5+7) and contains(a))'
992 $ log 'max((4+0+2+5+7) and contains(a))'
993 5
993 5
994
994
995 max: no result
995 max: no result
996
996
997 $ log 'max(contains(stringthatdoesnotappearanywhere))'
997 $ log 'max(contains(stringthatdoesnotappearanywhere))'
998
998
999 max: no result on unordered set
999 max: no result on unordered set
1000
1000
1001 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1001 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1002
1002
1003 min: simple
1003 min: simple
1004
1004
1005 $ log 'min(contains(a))'
1005 $ log 'min(contains(a))'
1006 0
1006 0
1007
1007
1008 min: simple on unordered set
1008 min: simple on unordered set
1009
1009
1010 $ log 'min((4+0+2+5+7) and contains(a))'
1010 $ log 'min((4+0+2+5+7) and contains(a))'
1011 0
1011 0
1012
1012
1013 min: empty
1013 min: empty
1014
1014
1015 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1015 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1016
1016
1017 min: empty on unordered set
1017 min: empty on unordered set
1018
1018
1019 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1019 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1020
1020
1021
1021
1022 $ log 'merge()'
1022 $ log 'merge()'
1023 6
1023 6
1024 $ log 'branchpoint()'
1024 $ log 'branchpoint()'
1025 1
1025 1
1026 4
1026 4
1027 $ log 'modifies(b)'
1027 $ log 'modifies(b)'
1028 4
1028 4
1029 $ log 'modifies("path:b")'
1029 $ log 'modifies("path:b")'
1030 4
1030 4
1031 $ log 'modifies("*")'
1031 $ log 'modifies("*")'
1032 4
1032 4
1033 6
1033 6
1034 $ log 'modifies("set:modified()")'
1034 $ log 'modifies("set:modified()")'
1035 4
1035 4
1036 $ log 'id(5)'
1036 $ log 'id(5)'
1037 2
1037 2
1038 $ log 'only(9)'
1038 $ log 'only(9)'
1039 8
1039 8
1040 9
1040 9
1041 $ log 'only(8)'
1041 $ log 'only(8)'
1042 8
1042 8
1043 $ log 'only(9, 5)'
1043 $ log 'only(9, 5)'
1044 2
1044 2
1045 4
1045 4
1046 8
1046 8
1047 9
1047 9
1048 $ log 'only(7 + 9, 5 + 2)'
1048 $ log 'only(7 + 9, 5 + 2)'
1049 4
1049 4
1050 6
1050 6
1051 7
1051 7
1052 8
1052 8
1053 9
1053 9
1054
1054
1055 Test empty set input
1055 Test empty set input
1056 $ log 'only(p2())'
1056 $ log 'only(p2())'
1057 $ log 'only(p1(), p2())'
1057 $ log 'only(p1(), p2())'
1058 0
1058 0
1059 1
1059 1
1060 2
1060 2
1061 4
1061 4
1062 8
1062 8
1063 9
1063 9
1064
1064
1065 Test '%' operator
1065 Test '%' operator
1066
1066
1067 $ log '9%'
1067 $ log '9%'
1068 8
1068 8
1069 9
1069 9
1070 $ log '9%5'
1070 $ log '9%5'
1071 2
1071 2
1072 4
1072 4
1073 8
1073 8
1074 9
1074 9
1075 $ log '(7 + 9)%(5 + 2)'
1075 $ log '(7 + 9)%(5 + 2)'
1076 4
1076 4
1077 6
1077 6
1078 7
1078 7
1079 8
1079 8
1080 9
1080 9
1081
1081
1082 Test opreand of '%' is optimized recursively (issue4670)
1082 Test opreand of '%' is optimized recursively (issue4670)
1083
1083
1084 $ try --optimize '8:9-8%'
1084 $ try --optimize '8:9-8%'
1085 (onlypost
1085 (onlypost
1086 (minus
1086 (minus
1087 (range
1087 (range
1088 ('symbol', '8')
1088 ('symbol', '8')
1089 ('symbol', '9'))
1089 ('symbol', '9'))
1090 ('symbol', '8')))
1090 ('symbol', '8')))
1091 * optimized:
1091 * optimized:
1092 (func
1092 (func
1093 ('symbol', 'only')
1093 ('symbol', 'only')
1094 (difference
1094 (difference
1095 (range
1095 (range
1096 ('symbol', '8')
1096 ('symbol', '8')
1097 ('symbol', '9')
1097 ('symbol', '9')
1098 define)
1098 define)
1099 ('symbol', '8')
1099 ('symbol', '8')
1100 define)
1100 define)
1101 define)
1101 define)
1102 * set:
1102 * set:
1103 <baseset+ [8, 9]>
1103 <baseset+ [8, 9]>
1104 8
1104 8
1105 9
1105 9
1106 $ try --optimize '(9)%(5)'
1106 $ try --optimize '(9)%(5)'
1107 (only
1107 (only
1108 (group
1108 (group
1109 ('symbol', '9'))
1109 ('symbol', '9'))
1110 (group
1110 (group
1111 ('symbol', '5')))
1111 ('symbol', '5')))
1112 * optimized:
1112 * optimized:
1113 (func
1113 (func
1114 ('symbol', 'only')
1114 ('symbol', 'only')
1115 (list
1115 (list
1116 ('symbol', '9')
1116 ('symbol', '9')
1117 ('symbol', '5'))
1117 ('symbol', '5'))
1118 define)
1118 define)
1119 * set:
1119 * set:
1120 <baseset+ [2, 4, 8, 9]>
1120 <baseset+ [2, 4, 8, 9]>
1121 2
1121 2
1122 4
1122 4
1123 8
1123 8
1124 9
1124 9
1125
1125
1126 Test the order of operations
1126 Test the order of operations
1127
1127
1128 $ log '7 + 9%5 + 2'
1128 $ log '7 + 9%5 + 2'
1129 7
1129 7
1130 2
1130 2
1131 4
1131 4
1132 8
1132 8
1133 9
1133 9
1134
1134
1135 Test explicit numeric revision
1135 Test explicit numeric revision
1136 $ log 'rev(-2)'
1136 $ log 'rev(-2)'
1137 $ log 'rev(-1)'
1137 $ log 'rev(-1)'
1138 -1
1138 -1
1139 $ log 'rev(0)'
1139 $ log 'rev(0)'
1140 0
1140 0
1141 $ log 'rev(9)'
1141 $ log 'rev(9)'
1142 9
1142 9
1143 $ log 'rev(10)'
1143 $ log 'rev(10)'
1144 $ log 'rev(tip)'
1144 $ log 'rev(tip)'
1145 hg: parse error: rev expects a number
1145 hg: parse error: rev expects a number
1146 [255]
1146 [255]
1147
1147
1148 Test hexadecimal revision
1148 Test hexadecimal revision
1149 $ log 'id(2)'
1149 $ log 'id(2)'
1150 abort: 00changelog.i@2: ambiguous identifier!
1150 abort: 00changelog.i@2: ambiguous identifier!
1151 [255]
1151 [255]
1152 $ log 'id(23268)'
1152 $ log 'id(23268)'
1153 4
1153 4
1154 $ log 'id(2785f51eece)'
1154 $ log 'id(2785f51eece)'
1155 0
1155 0
1156 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1156 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1157 8
1157 8
1158 $ log 'id(d5d0dcbdc4a)'
1158 $ log 'id(d5d0dcbdc4a)'
1159 $ log 'id(d5d0dcbdc4w)'
1159 $ log 'id(d5d0dcbdc4w)'
1160 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1160 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1161 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1161 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1162 $ log 'id(1.0)'
1162 $ log 'id(1.0)'
1163 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1163 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1164
1164
1165 Test null revision
1165 Test null revision
1166 $ log '(null)'
1166 $ log '(null)'
1167 -1
1167 -1
1168 $ log '(null:0)'
1168 $ log '(null:0)'
1169 -1
1169 -1
1170 0
1170 0
1171 $ log '(0:null)'
1171 $ log '(0:null)'
1172 0
1172 0
1173 -1
1173 -1
1174 $ log 'null::0'
1174 $ log 'null::0'
1175 -1
1175 -1
1176 0
1176 0
1177 $ log 'null:tip - 0:'
1177 $ log 'null:tip - 0:'
1178 -1
1178 -1
1179 $ log 'null: and null::' | head -1
1179 $ log 'null: and null::' | head -1
1180 -1
1180 -1
1181 $ log 'null: or 0:' | head -2
1181 $ log 'null: or 0:' | head -2
1182 -1
1182 -1
1183 0
1183 0
1184 $ log 'ancestors(null)'
1184 $ log 'ancestors(null)'
1185 -1
1185 -1
1186 $ log 'reverse(null:)' | tail -2
1186 $ log 'reverse(null:)' | tail -2
1187 0
1187 0
1188 -1
1188 -1
1189 BROKEN: should be '-1'
1189 BROKEN: should be '-1'
1190 $ log 'first(null:)'
1190 $ log 'first(null:)'
1191 BROKEN: should be '-1'
1191 BROKEN: should be '-1'
1192 $ log 'min(null:)'
1192 $ log 'min(null:)'
1193 $ log 'tip:null and all()' | tail -2
1193 $ log 'tip:null and all()' | tail -2
1194 1
1194 1
1195 0
1195 0
1196
1196
1197 Test working-directory revision
1197 Test working-directory revision
1198 $ hg debugrevspec 'wdir()'
1198 $ hg debugrevspec 'wdir()'
1199 2147483647
1199 2147483647
1200 $ hg debugrevspec 'tip or wdir()'
1200 $ hg debugrevspec 'tip or wdir()'
1201 9
1201 9
1202 2147483647
1202 2147483647
1203 $ hg debugrevspec '0:tip and wdir()'
1203 $ hg debugrevspec '0:tip and wdir()'
1204 $ log '0:wdir()' | tail -3
1204 $ log '0:wdir()' | tail -3
1205 8
1205 8
1206 9
1206 9
1207 2147483647
1207 2147483647
1208 $ log 'wdir():0' | head -3
1208 $ log 'wdir():0' | head -3
1209 2147483647
1209 2147483647
1210 9
1210 9
1211 8
1211 8
1212 $ log 'wdir():wdir()'
1212 $ log 'wdir():wdir()'
1213 2147483647
1213 2147483647
1214 $ log '(all() + wdir()) & min(. + wdir())'
1214 $ log '(all() + wdir()) & min(. + wdir())'
1215 9
1215 9
1216 $ log '(all() + wdir()) & max(. + wdir())'
1216 $ log '(all() + wdir()) & max(. + wdir())'
1217 2147483647
1217 2147483647
1218 $ log '(all() + wdir()) & first(wdir() + .)'
1218 $ log '(all() + wdir()) & first(wdir() + .)'
1219 2147483647
1219 2147483647
1220 $ log '(all() + wdir()) & last(. + wdir())'
1220 $ log '(all() + wdir()) & last(. + wdir())'
1221 2147483647
1221 2147483647
1222
1222
1223 $ log 'outgoing()'
1223 $ log 'outgoing()'
1224 8
1224 8
1225 9
1225 9
1226 $ log 'outgoing("../remote1")'
1226 $ log 'outgoing("../remote1")'
1227 8
1227 8
1228 9
1228 9
1229 $ log 'outgoing("../remote2")'
1229 $ log 'outgoing("../remote2")'
1230 3
1230 3
1231 5
1231 5
1232 6
1232 6
1233 7
1233 7
1234 9
1234 9
1235 $ log 'p1(merge())'
1235 $ log 'p1(merge())'
1236 5
1236 5
1237 $ log 'p2(merge())'
1237 $ log 'p2(merge())'
1238 4
1238 4
1239 $ log 'parents(merge())'
1239 $ log 'parents(merge())'
1240 4
1240 4
1241 5
1241 5
1242 $ log 'p1(branchpoint())'
1242 $ log 'p1(branchpoint())'
1243 0
1243 0
1244 2
1244 2
1245 $ log 'p2(branchpoint())'
1245 $ log 'p2(branchpoint())'
1246 $ log 'parents(branchpoint())'
1246 $ log 'parents(branchpoint())'
1247 0
1247 0
1248 2
1248 2
1249 $ log 'removes(a)'
1249 $ log 'removes(a)'
1250 2
1250 2
1251 6
1251 6
1252 $ log 'roots(all())'
1252 $ log 'roots(all())'
1253 0
1253 0
1254 $ log 'reverse(2 or 3 or 4 or 5)'
1254 $ log 'reverse(2 or 3 or 4 or 5)'
1255 5
1255 5
1256 4
1256 4
1257 3
1257 3
1258 2
1258 2
1259 $ log 'reverse(all())'
1259 $ log 'reverse(all())'
1260 9
1260 9
1261 8
1261 8
1262 7
1262 7
1263 6
1263 6
1264 5
1264 5
1265 4
1265 4
1266 3
1266 3
1267 2
1267 2
1268 1
1268 1
1269 0
1269 0
1270 $ log 'reverse(all()) & filelog(b)'
1270 $ log 'reverse(all()) & filelog(b)'
1271 4
1271 4
1272 1
1272 1
1273 $ log 'rev(5)'
1273 $ log 'rev(5)'
1274 5
1274 5
1275 $ log 'sort(limit(reverse(all()), 3))'
1275 $ log 'sort(limit(reverse(all()), 3))'
1276 7
1276 7
1277 8
1277 8
1278 9
1278 9
1279 $ log 'sort(2 or 3 or 4 or 5, date)'
1279 $ log 'sort(2 or 3 or 4 or 5, date)'
1280 2
1280 2
1281 3
1281 3
1282 5
1282 5
1283 4
1283 4
1284 $ log 'tagged()'
1284 $ log 'tagged()'
1285 6
1285 6
1286 $ log 'tag()'
1286 $ log 'tag()'
1287 6
1287 6
1288 $ log 'tag(1.0)'
1288 $ log 'tag(1.0)'
1289 6
1289 6
1290 $ log 'tag(tip)'
1290 $ log 'tag(tip)'
1291 9
1291 9
1292
1292
1293 Test order of revisions in compound expression
1293 Test order of revisions in compound expression
1294 ----------------------------------------------
1294 ----------------------------------------------
1295
1295
1296 The general rule is that only the outermost (= leftmost) predicate can
1296 The general rule is that only the outermost (= leftmost) predicate can
1297 enforce its ordering requirement. The other predicates should take the
1297 enforce its ordering requirement. The other predicates should take the
1298 ordering defined by it.
1298 ordering defined by it.
1299
1299
1300 'A & B' should follow the order of 'A':
1300 'A & B' should follow the order of 'A':
1301
1301
1302 $ log '2:0 & 0::2'
1302 $ log '2:0 & 0::2'
1303 2
1303 2
1304 1
1304 1
1305 0
1305 0
1306
1306
1307 'head()' combines sets in right order:
1307 'head()' combines sets in right order:
1308
1308
1309 $ log '2:0 & head()'
1309 $ log '2:0 & head()'
1310 2
1310 2
1311 1
1311 1
1312 0
1312 0
1313
1313
1314 'x:y' takes ordering parameter into account:
1314 'x:y' takes ordering parameter into account:
1315
1315
1316 $ try -p optimized '3:0 & 0:3 & not 2:1'
1316 $ try -p optimized '3:0 & 0:3 & not 2:1'
1317 * optimized:
1317 * optimized:
1318 (difference
1318 (difference
1319 (and
1319 (and
1320 (range
1320 (range
1321 ('symbol', '3')
1321 ('symbol', '3')
1322 ('symbol', '0')
1322 ('symbol', '0')
1323 define)
1323 define)
1324 (range
1324 (range
1325 ('symbol', '0')
1325 ('symbol', '0')
1326 ('symbol', '3')
1326 ('symbol', '3')
1327 follow)
1327 follow)
1328 define)
1328 define)
1329 (range
1329 (range
1330 ('symbol', '2')
1330 ('symbol', '2')
1331 ('symbol', '1')
1331 ('symbol', '1')
1332 any)
1332 any)
1333 define)
1333 define)
1334 * set:
1334 * set:
1335 <filteredset
1335 <filteredset
1336 <filteredset
1336 <filteredset
1337 <spanset- 0:3>,
1337 <spanset- 0:3>,
1338 <spanset+ 0:3>>,
1338 <spanset+ 0:3>>,
1339 <not
1339 <not
1340 <spanset+ 1:2>>>
1340 <spanset+ 1:2>>>
1341 3
1341 3
1342 0
1342 0
1343
1343
1344 'a + b', which is optimized to '_list(a b)', should take the ordering of
1344 'a + b', which is optimized to '_list(a b)', should take the ordering of
1345 the left expression:
1345 the left expression:
1346
1346
1347 $ try --optimize '2:0 & (0 + 1 + 2)'
1347 $ try --optimize '2:0 & (0 + 1 + 2)'
1348 (and
1348 (and
1349 (range
1349 (range
1350 ('symbol', '2')
1350 ('symbol', '2')
1351 ('symbol', '0'))
1351 ('symbol', '0'))
1352 (group
1352 (group
1353 (or
1353 (or
1354 (list
1354 (list
1355 ('symbol', '0')
1355 ('symbol', '0')
1356 ('symbol', '1')
1356 ('symbol', '1')
1357 ('symbol', '2')))))
1357 ('symbol', '2')))))
1358 * optimized:
1358 * optimized:
1359 (and
1359 (and
1360 (range
1360 (range
1361 ('symbol', '2')
1361 ('symbol', '2')
1362 ('symbol', '0')
1362 ('symbol', '0')
1363 define)
1363 define)
1364 (func
1364 (func
1365 ('symbol', '_list')
1365 ('symbol', '_list')
1366 ('string', '0\x001\x002')
1366 ('string', '0\x001\x002')
1367 follow)
1367 follow)
1368 define)
1368 define)
1369 * set:
1369 * set:
1370 <filteredset
1370 <filteredset
1371 <spanset- 0:2>,
1371 <spanset- 0:2>,
1372 <baseset [0, 1, 2]>>
1372 <baseset [0, 1, 2]>>
1373 2
1373 2
1374 1
1374 1
1375 0
1375 0
1376
1376
1377 'A + B' should take the ordering of the left expression:
1377 'A + B' should take the ordering of the left expression:
1378
1378
1379 $ try --optimize '2:0 & (0:1 + 2)'
1379 $ try --optimize '2:0 & (0:1 + 2)'
1380 (and
1380 (and
1381 (range
1381 (range
1382 ('symbol', '2')
1382 ('symbol', '2')
1383 ('symbol', '0'))
1383 ('symbol', '0'))
1384 (group
1384 (group
1385 (or
1385 (or
1386 (list
1386 (list
1387 (range
1387 (range
1388 ('symbol', '0')
1388 ('symbol', '0')
1389 ('symbol', '1'))
1389 ('symbol', '1'))
1390 ('symbol', '2')))))
1390 ('symbol', '2')))))
1391 * optimized:
1391 * optimized:
1392 (and
1392 (and
1393 (range
1393 (range
1394 ('symbol', '2')
1394 ('symbol', '2')
1395 ('symbol', '0')
1395 ('symbol', '0')
1396 define)
1396 define)
1397 (or
1397 (or
1398 (list
1398 (list
1399 (range
1399 (range
1400 ('symbol', '0')
1400 ('symbol', '0')
1401 ('symbol', '1')
1401 ('symbol', '1')
1402 follow)
1402 follow)
1403 ('symbol', '2'))
1403 ('symbol', '2'))
1404 follow)
1404 follow)
1405 define)
1405 define)
1406 * set:
1406 * set:
1407 <filteredset
1407 <filteredset
1408 <spanset- 0:2>,
1408 <spanset- 0:2>,
1409 <addset
1409 <addset
1410 <spanset+ 0:1>,
1410 <spanset+ 0:1>,
1411 <baseset [2]>>>
1411 <baseset [2]>>>
1412 2
1412 2
1413 1
1413 1
1414 0
1414 0
1415
1415
1416 '_intlist(a b)' should behave like 'a + b':
1416 '_intlist(a b)' should behave like 'a + b':
1417
1417
1418 $ trylist --optimize '2:0 & %ld' 0 1 2
1418 $ trylist --optimize '2:0 & %ld' 0 1 2
1419 (and
1419 (and
1420 (range
1420 (range
1421 ('symbol', '2')
1421 ('symbol', '2')
1422 ('symbol', '0'))
1422 ('symbol', '0'))
1423 (func
1423 (func
1424 ('symbol', '_intlist')
1424 ('symbol', '_intlist')
1425 ('string', '0\x001\x002')))
1425 ('string', '0\x001\x002')))
1426 * optimized:
1426 * optimized:
1427 (and
1427 (and
1428 (func
1428 (func
1429 ('symbol', '_intlist')
1429 ('symbol', '_intlist')
1430 ('string', '0\x001\x002')
1430 ('string', '0\x001\x002')
1431 follow)
1431 follow)
1432 (range
1432 (range
1433 ('symbol', '2')
1433 ('symbol', '2')
1434 ('symbol', '0')
1434 ('symbol', '0')
1435 define)
1435 define)
1436 define)
1436 define)
1437 * set:
1437 * set:
1438 <filteredset
1438 <filteredset
1439 <spanset- 0:2>,
1439 <spanset- 0:2>,
1440 <baseset+ [0, 1, 2]>>
1440 <baseset+ [0, 1, 2]>>
1441 2
1441 2
1442 1
1442 1
1443 0
1443 0
1444
1444
1445 $ trylist --optimize '%ld & 2:0' 0 2 1
1445 $ trylist --optimize '%ld & 2:0' 0 2 1
1446 (and
1446 (and
1447 (func
1447 (func
1448 ('symbol', '_intlist')
1448 ('symbol', '_intlist')
1449 ('string', '0\x002\x001'))
1449 ('string', '0\x002\x001'))
1450 (range
1450 (range
1451 ('symbol', '2')
1451 ('symbol', '2')
1452 ('symbol', '0')))
1452 ('symbol', '0')))
1453 * optimized:
1453 * optimized:
1454 (and
1454 (and
1455 (func
1455 (func
1456 ('symbol', '_intlist')
1456 ('symbol', '_intlist')
1457 ('string', '0\x002\x001')
1457 ('string', '0\x002\x001')
1458 define)
1458 define)
1459 (range
1459 (range
1460 ('symbol', '2')
1460 ('symbol', '2')
1461 ('symbol', '0')
1461 ('symbol', '0')
1462 follow)
1462 follow)
1463 define)
1463 define)
1464 * set:
1464 * set:
1465 <filteredset
1465 <filteredset
1466 <baseset [0, 2, 1]>,
1466 <baseset [0, 2, 1]>,
1467 <spanset- 0:2>>
1467 <spanset- 0:2>>
1468 0
1468 0
1469 2
1469 2
1470 1
1470 1
1471
1471
1472 '_hexlist(a b)' should behave like 'a + b':
1472 '_hexlist(a b)' should behave like 'a + b':
1473
1473
1474 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1474 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1475 (and
1475 (and
1476 (range
1476 (range
1477 ('symbol', '2')
1477 ('symbol', '2')
1478 ('symbol', '0'))
1478 ('symbol', '0'))
1479 (func
1479 (func
1480 ('symbol', '_hexlist')
1480 ('symbol', '_hexlist')
1481 ('string', '*'))) (glob)
1481 ('string', '*'))) (glob)
1482 * optimized:
1482 * optimized:
1483 (and
1483 (and
1484 (range
1484 (range
1485 ('symbol', '2')
1485 ('symbol', '2')
1486 ('symbol', '0')
1486 ('symbol', '0')
1487 define)
1487 define)
1488 (func
1488 (func
1489 ('symbol', '_hexlist')
1489 ('symbol', '_hexlist')
1490 ('string', '*') (glob)
1490 ('string', '*') (glob)
1491 follow)
1491 follow)
1492 define)
1492 define)
1493 * set:
1493 * set:
1494 <filteredset
1494 <filteredset
1495 <spanset- 0:2>,
1495 <spanset- 0:2>,
1496 <baseset [0, 1, 2]>>
1496 <baseset [0, 1, 2]>>
1497 2
1497 2
1498 1
1498 1
1499 0
1499 0
1500
1500
1501 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1501 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1502 (and
1502 (and
1503 (func
1503 (func
1504 ('symbol', '_hexlist')
1504 ('symbol', '_hexlist')
1505 ('string', '*')) (glob)
1505 ('string', '*')) (glob)
1506 (range
1506 (range
1507 ('symbol', '2')
1507 ('symbol', '2')
1508 ('symbol', '0')))
1508 ('symbol', '0')))
1509 * optimized:
1509 * optimized:
1510 (and
1510 (and
1511 (range
1511 (range
1512 ('symbol', '2')
1512 ('symbol', '2')
1513 ('symbol', '0')
1513 ('symbol', '0')
1514 follow)
1514 follow)
1515 (func
1515 (func
1516 ('symbol', '_hexlist')
1516 ('symbol', '_hexlist')
1517 ('string', '*') (glob)
1517 ('string', '*') (glob)
1518 define)
1518 define)
1519 define)
1519 define)
1520 * set:
1520 * set:
1521 <baseset [0, 2, 1]>
1521 <baseset [0, 2, 1]>
1522 0
1522 0
1523 2
1523 2
1524 1
1524 1
1525
1525
1526 '_list' should not go through the slow follow-order path if order doesn't
1526 '_list' should not go through the slow follow-order path if order doesn't
1527 matter:
1527 matter:
1528
1528
1529 $ try -p optimized '2:0 & not (0 + 1)'
1529 $ try -p optimized '2:0 & not (0 + 1)'
1530 * optimized:
1530 * optimized:
1531 (difference
1531 (difference
1532 (range
1532 (range
1533 ('symbol', '2')
1533 ('symbol', '2')
1534 ('symbol', '0')
1534 ('symbol', '0')
1535 define)
1535 define)
1536 (func
1536 (func
1537 ('symbol', '_list')
1537 ('symbol', '_list')
1538 ('string', '0\x001')
1538 ('string', '0\x001')
1539 any)
1539 any)
1540 define)
1540 define)
1541 * set:
1541 * set:
1542 <filteredset
1542 <filteredset
1543 <spanset- 0:2>,
1543 <spanset- 0:2>,
1544 <not
1544 <not
1545 <baseset [0, 1]>>>
1545 <baseset [0, 1]>>>
1546 2
1546 2
1547
1547
1548 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1548 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1549 * optimized:
1549 * optimized:
1550 (difference
1550 (difference
1551 (range
1551 (range
1552 ('symbol', '2')
1552 ('symbol', '2')
1553 ('symbol', '0')
1553 ('symbol', '0')
1554 define)
1554 define)
1555 (and
1555 (and
1556 (range
1556 (range
1557 ('symbol', '0')
1557 ('symbol', '0')
1558 ('symbol', '2')
1558 ('symbol', '2')
1559 any)
1559 any)
1560 (func
1560 (func
1561 ('symbol', '_list')
1561 ('symbol', '_list')
1562 ('string', '0\x001')
1562 ('string', '0\x001')
1563 any)
1563 any)
1564 any)
1564 any)
1565 define)
1565 define)
1566 * set:
1566 * set:
1567 <filteredset
1567 <filteredset
1568 <spanset- 0:2>,
1568 <spanset- 0:2>,
1569 <not
1569 <not
1570 <baseset [0, 1]>>>
1570 <baseset [0, 1]>>>
1571 2
1571 2
1572
1572
1573 because 'present()' does nothing other than suppressing an error, the
1573 because 'present()' does nothing other than suppressing an error, the
1574 ordering requirement should be forwarded to the nested expression
1574 ordering requirement should be forwarded to the nested expression
1575
1575
1576 $ try -p optimized 'present(2 + 0 + 1)'
1576 $ try -p optimized 'present(2 + 0 + 1)'
1577 * optimized:
1577 * optimized:
1578 (func
1578 (func
1579 ('symbol', 'present')
1579 ('symbol', 'present')
1580 (func
1580 (func
1581 ('symbol', '_list')
1581 ('symbol', '_list')
1582 ('string', '2\x000\x001')
1582 ('string', '2\x000\x001')
1583 define)
1583 define)
1584 define)
1584 define)
1585 * set:
1585 * set:
1586 <baseset [2, 0, 1]>
1586 <baseset [2, 0, 1]>
1587 2
1587 2
1588 0
1588 0
1589 1
1589 1
1590
1590
1591 $ try --optimize '2:0 & present(0 + 1 + 2)'
1591 $ try --optimize '2:0 & present(0 + 1 + 2)'
1592 (and
1592 (and
1593 (range
1593 (range
1594 ('symbol', '2')
1594 ('symbol', '2')
1595 ('symbol', '0'))
1595 ('symbol', '0'))
1596 (func
1596 (func
1597 ('symbol', 'present')
1597 ('symbol', 'present')
1598 (or
1598 (or
1599 (list
1599 (list
1600 ('symbol', '0')
1600 ('symbol', '0')
1601 ('symbol', '1')
1601 ('symbol', '1')
1602 ('symbol', '2')))))
1602 ('symbol', '2')))))
1603 * optimized:
1603 * optimized:
1604 (and
1604 (and
1605 (range
1605 (range
1606 ('symbol', '2')
1606 ('symbol', '2')
1607 ('symbol', '0')
1607 ('symbol', '0')
1608 define)
1608 define)
1609 (func
1609 (func
1610 ('symbol', 'present')
1610 ('symbol', 'present')
1611 (func
1611 (func
1612 ('symbol', '_list')
1612 ('symbol', '_list')
1613 ('string', '0\x001\x002')
1613 ('string', '0\x001\x002')
1614 follow)
1614 follow)
1615 follow)
1615 follow)
1616 define)
1616 define)
1617 * set:
1617 * set:
1618 <filteredset
1618 <filteredset
1619 <spanset- 0:2>,
1619 <spanset- 0:2>,
1620 <baseset [0, 1, 2]>>
1620 <baseset [0, 1, 2]>>
1621 2
1621 2
1622 1
1622 1
1623 0
1623 0
1624
1624
1625 'reverse()' should take effect only if it is the outermost expression:
1625 'reverse()' should take effect only if it is the outermost expression:
1626
1626
1627 $ try --optimize '0:2 & reverse(all())'
1627 $ try --optimize '0:2 & reverse(all())'
1628 (and
1628 (and
1629 (range
1629 (range
1630 ('symbol', '0')
1630 ('symbol', '0')
1631 ('symbol', '2'))
1631 ('symbol', '2'))
1632 (func
1632 (func
1633 ('symbol', 'reverse')
1633 ('symbol', 'reverse')
1634 (func
1634 (func
1635 ('symbol', 'all')
1635 ('symbol', 'all')
1636 None)))
1636 None)))
1637 * optimized:
1637 * optimized:
1638 (and
1638 (and
1639 (range
1639 (range
1640 ('symbol', '0')
1640 ('symbol', '0')
1641 ('symbol', '2')
1641 ('symbol', '2')
1642 define)
1642 define)
1643 (func
1643 (func
1644 ('symbol', 'reverse')
1644 ('symbol', 'reverse')
1645 (func
1645 (func
1646 ('symbol', 'all')
1646 ('symbol', 'all')
1647 None
1647 None
1648 define)
1648 define)
1649 follow)
1649 follow)
1650 define)
1650 define)
1651 * set:
1651 * set:
1652 <filteredset
1652 <filteredset
1653 <spanset+ 0:2>,
1653 <spanset+ 0:2>,
1654 <spanset+ 0:9>>
1654 <spanset+ 0:9>>
1655 0
1655 0
1656 1
1656 1
1657 2
1657 2
1658
1658
1659 'sort()' should take effect only if it is the outermost expression:
1659 'sort()' should take effect only if it is the outermost expression:
1660
1660
1661 $ try --optimize '0:2 & sort(all(), -rev)'
1661 $ try --optimize '0:2 & sort(all(), -rev)'
1662 (and
1662 (and
1663 (range
1663 (range
1664 ('symbol', '0')
1664 ('symbol', '0')
1665 ('symbol', '2'))
1665 ('symbol', '2'))
1666 (func
1666 (func
1667 ('symbol', 'sort')
1667 ('symbol', 'sort')
1668 (list
1668 (list
1669 (func
1669 (func
1670 ('symbol', 'all')
1670 ('symbol', 'all')
1671 None)
1671 None)
1672 (negate
1672 (negate
1673 ('symbol', 'rev')))))
1673 ('symbol', 'rev')))))
1674 * optimized:
1674 * optimized:
1675 (and
1675 (and
1676 (range
1676 (range
1677 ('symbol', '0')
1677 ('symbol', '0')
1678 ('symbol', '2')
1678 ('symbol', '2')
1679 define)
1679 define)
1680 (func
1680 (func
1681 ('symbol', 'sort')
1681 ('symbol', 'sort')
1682 (list
1682 (list
1683 (func
1683 (func
1684 ('symbol', 'all')
1684 ('symbol', 'all')
1685 None
1685 None
1686 define)
1686 define)
1687 ('string', '-rev'))
1687 ('string', '-rev'))
1688 follow)
1688 follow)
1689 define)
1689 define)
1690 * set:
1690 * set:
1691 <filteredset
1691 <filteredset
1692 <spanset+ 0:2>,
1692 <spanset+ 0:2>,
1693 <spanset+ 0:9>>
1693 <spanset+ 0:9>>
1694 0
1694 0
1695 1
1695 1
1696 2
1696 2
1697
1697
1698 invalid argument passed to noop sort():
1698 invalid argument passed to noop sort():
1699
1699
1700 $ log '0:2 & sort()'
1700 $ log '0:2 & sort()'
1701 hg: parse error: sort requires one or two arguments
1701 hg: parse error: sort requires one or two arguments
1702 [255]
1702 [255]
1703 $ log '0:2 & sort(all(), -invalid)'
1703 $ log '0:2 & sort(all(), -invalid)'
1704 hg: parse error: unknown sort key '-invalid'
1704 hg: parse error: unknown sort key '-invalid'
1705 [255]
1705 [255]
1706
1706
1707 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1707 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1708
1708
1709 $ try --optimize '2:0 & first(1 + 0 + 2)'
1709 $ try --optimize '2:0 & first(1 + 0 + 2)'
1710 (and
1710 (and
1711 (range
1711 (range
1712 ('symbol', '2')
1712 ('symbol', '2')
1713 ('symbol', '0'))
1713 ('symbol', '0'))
1714 (func
1714 (func
1715 ('symbol', 'first')
1715 ('symbol', 'first')
1716 (or
1716 (or
1717 (list
1717 (list
1718 ('symbol', '1')
1718 ('symbol', '1')
1719 ('symbol', '0')
1719 ('symbol', '0')
1720 ('symbol', '2')))))
1720 ('symbol', '2')))))
1721 * optimized:
1721 * optimized:
1722 (and
1722 (and
1723 (range
1723 (range
1724 ('symbol', '2')
1724 ('symbol', '2')
1725 ('symbol', '0')
1725 ('symbol', '0')
1726 define)
1726 define)
1727 (func
1727 (func
1728 ('symbol', 'first')
1728 ('symbol', 'first')
1729 (func
1729 (func
1730 ('symbol', '_list')
1730 ('symbol', '_list')
1731 ('string', '1\x000\x002')
1731 ('string', '1\x000\x002')
1732 define)
1732 define)
1733 follow)
1733 follow)
1734 define)
1734 define)
1735 * set:
1735 * set:
1736 <baseset
1736 <baseset
1737 <limit n=1, offset=0,
1737 <limit n=1, offset=0,
1738 <spanset- 0:2>,
1738 <spanset- 0:2>,
1739 <baseset [1, 0, 2]>>>
1739 <baseset [1, 0, 2]>>>
1740 1
1740 1
1741
1741
1742 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1742 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1743 (and
1743 (and
1744 (range
1744 (range
1745 ('symbol', '2')
1745 ('symbol', '2')
1746 ('symbol', '0'))
1746 ('symbol', '0'))
1747 (not
1747 (not
1748 (func
1748 (func
1749 ('symbol', 'last')
1749 ('symbol', 'last')
1750 (or
1750 (or
1751 (list
1751 (list
1752 ('symbol', '0')
1752 ('symbol', '0')
1753 ('symbol', '2')
1753 ('symbol', '2')
1754 ('symbol', '1'))))))
1754 ('symbol', '1'))))))
1755 * optimized:
1755 * optimized:
1756 (difference
1756 (difference
1757 (range
1757 (range
1758 ('symbol', '2')
1758 ('symbol', '2')
1759 ('symbol', '0')
1759 ('symbol', '0')
1760 define)
1760 define)
1761 (func
1761 (func
1762 ('symbol', 'last')
1762 ('symbol', 'last')
1763 (func
1763 (func
1764 ('symbol', '_list')
1764 ('symbol', '_list')
1765 ('string', '0\x002\x001')
1765 ('string', '0\x002\x001')
1766 define)
1766 define)
1767 any)
1767 any)
1768 define)
1768 define)
1769 * set:
1769 * set:
1770 <filteredset
1770 <filteredset
1771 <spanset- 0:2>,
1771 <spanset- 0:2>,
1772 <not
1772 <not
1773 <baseset
1773 <baseset
1774 <last n=1,
1774 <last n=1,
1775 <fullreposet+ 0:9>,
1775 <fullreposet+ 0:9>,
1776 <baseset [1, 2, 0]>>>>>
1776 <baseset [1, 2, 0]>>>>>
1777 2
1777 2
1778 0
1778 0
1779
1779
1780 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1780 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1781
1781
1782 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1782 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1783 (and
1783 (and
1784 (range
1784 (range
1785 ('symbol', '2')
1785 ('symbol', '2')
1786 ('symbol', '0'))
1786 ('symbol', '0'))
1787 (range
1787 (range
1788 (group
1788 (group
1789 (or
1789 (or
1790 (list
1790 (list
1791 ('symbol', '1')
1791 ('symbol', '1')
1792 ('symbol', '0')
1792 ('symbol', '0')
1793 ('symbol', '2'))))
1793 ('symbol', '2'))))
1794 (group
1794 (group
1795 (or
1795 (or
1796 (list
1796 (list
1797 ('symbol', '0')
1797 ('symbol', '0')
1798 ('symbol', '2')
1798 ('symbol', '2')
1799 ('symbol', '1'))))))
1799 ('symbol', '1'))))))
1800 * optimized:
1800 * optimized:
1801 (and
1801 (and
1802 (range
1802 (range
1803 ('symbol', '2')
1803 ('symbol', '2')
1804 ('symbol', '0')
1804 ('symbol', '0')
1805 define)
1805 define)
1806 (range
1806 (range
1807 (func
1807 (func
1808 ('symbol', '_list')
1808 ('symbol', '_list')
1809 ('string', '1\x000\x002')
1809 ('string', '1\x000\x002')
1810 define)
1810 define)
1811 (func
1811 (func
1812 ('symbol', '_list')
1812 ('symbol', '_list')
1813 ('string', '0\x002\x001')
1813 ('string', '0\x002\x001')
1814 define)
1814 define)
1815 follow)
1815 follow)
1816 define)
1816 define)
1817 * set:
1817 * set:
1818 <filteredset
1818 <filteredset
1819 <spanset- 0:2>,
1819 <spanset- 0:2>,
1820 <baseset [1]>>
1820 <baseset [1]>>
1821 1
1821 1
1822
1822
1823 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1823 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1824 the ordering rule is determined before the rewrite; in this example,
1824 the ordering rule is determined before the rewrite; in this example,
1825 'B' follows the order of the initial set, which is the same order as 'A'
1825 'B' follows the order of the initial set, which is the same order as 'A'
1826 since 'A' also follows the order:
1826 since 'A' also follows the order:
1827
1827
1828 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1828 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1829 (and
1829 (and
1830 (func
1830 (func
1831 ('symbol', 'contains')
1831 ('symbol', 'contains')
1832 ('string', 'glob:*'))
1832 ('string', 'glob:*'))
1833 (group
1833 (group
1834 (or
1834 (or
1835 (list
1835 (list
1836 ('symbol', '2')
1836 ('symbol', '2')
1837 ('symbol', '0')
1837 ('symbol', '0')
1838 ('symbol', '1')))))
1838 ('symbol', '1')))))
1839 * optimized:
1839 * optimized:
1840 (and
1840 (and
1841 (func
1841 (func
1842 ('symbol', '_list')
1842 ('symbol', '_list')
1843 ('string', '2\x000\x001')
1843 ('string', '2\x000\x001')
1844 follow)
1844 follow)
1845 (func
1845 (func
1846 ('symbol', 'contains')
1846 ('symbol', 'contains')
1847 ('string', 'glob:*')
1847 ('string', 'glob:*')
1848 define)
1848 define)
1849 define)
1849 define)
1850 * set:
1850 * set:
1851 <filteredset
1851 <filteredset
1852 <baseset+ [0, 1, 2]>,
1852 <baseset+ [0, 1, 2]>,
1853 <contains 'glob:*'>>
1853 <contains 'glob:*'>>
1854 0
1854 0
1855 1
1855 1
1856 2
1856 2
1857
1857
1858 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1858 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1859 the order appropriately:
1859 the order appropriately:
1860
1860
1861 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1861 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1862 (and
1862 (and
1863 (func
1863 (func
1864 ('symbol', 'reverse')
1864 ('symbol', 'reverse')
1865 (func
1865 (func
1866 ('symbol', 'contains')
1866 ('symbol', 'contains')
1867 ('string', 'glob:*')))
1867 ('string', 'glob:*')))
1868 (group
1868 (group
1869 (or
1869 (or
1870 (list
1870 (list
1871 ('symbol', '0')
1871 ('symbol', '0')
1872 ('symbol', '2')
1872 ('symbol', '2')
1873 ('symbol', '1')))))
1873 ('symbol', '1')))))
1874 * optimized:
1874 * optimized:
1875 (and
1875 (and
1876 (func
1876 (func
1877 ('symbol', '_list')
1877 ('symbol', '_list')
1878 ('string', '0\x002\x001')
1878 ('string', '0\x002\x001')
1879 follow)
1879 follow)
1880 (func
1880 (func
1881 ('symbol', 'reverse')
1881 ('symbol', 'reverse')
1882 (func
1882 (func
1883 ('symbol', 'contains')
1883 ('symbol', 'contains')
1884 ('string', 'glob:*')
1884 ('string', 'glob:*')
1885 define)
1885 define)
1886 define)
1886 define)
1887 define)
1887 define)
1888 * set:
1888 * set:
1889 <filteredset
1889 <filteredset
1890 <baseset- [0, 1, 2]>,
1890 <baseset- [0, 1, 2]>,
1891 <contains 'glob:*'>>
1891 <contains 'glob:*'>>
1892 2
1892 2
1893 1
1893 1
1894 0
1894 0
1895
1895
1896 test sort revset
1896 test sort revset
1897 --------------------------------------------
1897 --------------------------------------------
1898
1898
1899 test when adding two unordered revsets
1899 test when adding two unordered revsets
1900
1900
1901 $ log 'sort(keyword(issue) or modifies(b))'
1901 $ log 'sort(keyword(issue) or modifies(b))'
1902 4
1902 4
1903 6
1903 6
1904
1904
1905 test when sorting a reversed collection in the same way it is
1905 test when sorting a reversed collection in the same way it is
1906
1906
1907 $ log 'sort(reverse(all()), -rev)'
1907 $ log 'sort(reverse(all()), -rev)'
1908 9
1908 9
1909 8
1909 8
1910 7
1910 7
1911 6
1911 6
1912 5
1912 5
1913 4
1913 4
1914 3
1914 3
1915 2
1915 2
1916 1
1916 1
1917 0
1917 0
1918
1918
1919 test when sorting a reversed collection
1919 test when sorting a reversed collection
1920
1920
1921 $ log 'sort(reverse(all()), rev)'
1921 $ log 'sort(reverse(all()), rev)'
1922 0
1922 0
1923 1
1923 1
1924 2
1924 2
1925 3
1925 3
1926 4
1926 4
1927 5
1927 5
1928 6
1928 6
1929 7
1929 7
1930 8
1930 8
1931 9
1931 9
1932
1932
1933
1933
1934 test sorting two sorted collections in different orders
1934 test sorting two sorted collections in different orders
1935
1935
1936 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1936 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1937 2
1937 2
1938 6
1938 6
1939 8
1939 8
1940 9
1940 9
1941
1941
1942 test sorting two sorted collections in different orders backwards
1942 test sorting two sorted collections in different orders backwards
1943
1943
1944 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1944 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1945 9
1945 9
1946 8
1946 8
1947 6
1947 6
1948 2
1948 2
1949
1949
1950 test empty sort key which is noop
1950 test empty sort key which is noop
1951
1951
1952 $ log 'sort(0 + 2 + 1, "")'
1952 $ log 'sort(0 + 2 + 1, "")'
1953 0
1953 0
1954 2
1954 2
1955 1
1955 1
1956
1956
1957 test invalid sort keys
1957 test invalid sort keys
1958
1958
1959 $ log 'sort(all(), -invalid)'
1959 $ log 'sort(all(), -invalid)'
1960 hg: parse error: unknown sort key '-invalid'
1960 hg: parse error: unknown sort key '-invalid'
1961 [255]
1961 [255]
1962
1962
1963 $ cd ..
1963 $ cd ..
1964
1964
1965 test sorting by multiple keys including variable-length strings
1965 test sorting by multiple keys including variable-length strings
1966
1966
1967 $ hg init sorting
1967 $ hg init sorting
1968 $ cd sorting
1968 $ cd sorting
1969 $ cat <<EOF >> .hg/hgrc
1969 $ cat <<EOF >> .hg/hgrc
1970 > [ui]
1970 > [ui]
1971 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1971 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1972 > [templatealias]
1972 > [templatealias]
1973 > p5(s) = pad(s, 5)
1973 > p5(s) = pad(s, 5)
1974 > EOF
1974 > EOF
1975 $ hg branch -qf b12
1975 $ hg branch -qf b12
1976 $ hg ci -m m111 -u u112 -d '111 10800'
1976 $ hg ci -m m111 -u u112 -d '111 10800'
1977 $ hg branch -qf b11
1977 $ hg branch -qf b11
1978 $ hg ci -m m12 -u u111 -d '112 7200'
1978 $ hg ci -m m12 -u u111 -d '112 7200'
1979 $ hg branch -qf b111
1979 $ hg branch -qf b111
1980 $ hg ci -m m11 -u u12 -d '111 3600'
1980 $ hg ci -m m11 -u u12 -d '111 3600'
1981 $ hg branch -qf b112
1981 $ hg branch -qf b112
1982 $ hg ci -m m111 -u u11 -d '120 0'
1982 $ hg ci -m m111 -u u11 -d '120 0'
1983 $ hg branch -qf b111
1983 $ hg branch -qf b111
1984 $ hg ci -m m112 -u u111 -d '110 14400'
1984 $ hg ci -m m112 -u u111 -d '110 14400'
1985 created new head
1985 created new head
1986
1986
1987 compare revisions (has fast path):
1987 compare revisions (has fast path):
1988
1988
1989 $ hg log -r 'sort(all(), rev)'
1989 $ hg log -r 'sort(all(), rev)'
1990 0 b12 m111 u112 111 10800
1990 0 b12 m111 u112 111 10800
1991 1 b11 m12 u111 112 7200
1991 1 b11 m12 u111 112 7200
1992 2 b111 m11 u12 111 3600
1992 2 b111 m11 u12 111 3600
1993 3 b112 m111 u11 120 0
1993 3 b112 m111 u11 120 0
1994 4 b111 m112 u111 110 14400
1994 4 b111 m112 u111 110 14400
1995
1995
1996 $ hg log -r 'sort(all(), -rev)'
1996 $ hg log -r 'sort(all(), -rev)'
1997 4 b111 m112 u111 110 14400
1997 4 b111 m112 u111 110 14400
1998 3 b112 m111 u11 120 0
1998 3 b112 m111 u11 120 0
1999 2 b111 m11 u12 111 3600
1999 2 b111 m11 u12 111 3600
2000 1 b11 m12 u111 112 7200
2000 1 b11 m12 u111 112 7200
2001 0 b12 m111 u112 111 10800
2001 0 b12 m111 u112 111 10800
2002
2002
2003 compare variable-length strings (issue5218):
2003 compare variable-length strings (issue5218):
2004
2004
2005 $ hg log -r 'sort(all(), branch)'
2005 $ hg log -r 'sort(all(), branch)'
2006 1 b11 m12 u111 112 7200
2006 1 b11 m12 u111 112 7200
2007 2 b111 m11 u12 111 3600
2007 2 b111 m11 u12 111 3600
2008 4 b111 m112 u111 110 14400
2008 4 b111 m112 u111 110 14400
2009 3 b112 m111 u11 120 0
2009 3 b112 m111 u11 120 0
2010 0 b12 m111 u112 111 10800
2010 0 b12 m111 u112 111 10800
2011
2011
2012 $ hg log -r 'sort(all(), -branch)'
2012 $ hg log -r 'sort(all(), -branch)'
2013 0 b12 m111 u112 111 10800
2013 0 b12 m111 u112 111 10800
2014 3 b112 m111 u11 120 0
2014 3 b112 m111 u11 120 0
2015 2 b111 m11 u12 111 3600
2015 2 b111 m11 u12 111 3600
2016 4 b111 m112 u111 110 14400
2016 4 b111 m112 u111 110 14400
2017 1 b11 m12 u111 112 7200
2017 1 b11 m12 u111 112 7200
2018
2018
2019 $ hg log -r 'sort(all(), desc)'
2019 $ hg log -r 'sort(all(), desc)'
2020 2 b111 m11 u12 111 3600
2020 2 b111 m11 u12 111 3600
2021 0 b12 m111 u112 111 10800
2021 0 b12 m111 u112 111 10800
2022 3 b112 m111 u11 120 0
2022 3 b112 m111 u11 120 0
2023 4 b111 m112 u111 110 14400
2023 4 b111 m112 u111 110 14400
2024 1 b11 m12 u111 112 7200
2024 1 b11 m12 u111 112 7200
2025
2025
2026 $ hg log -r 'sort(all(), -desc)'
2026 $ hg log -r 'sort(all(), -desc)'
2027 1 b11 m12 u111 112 7200
2027 1 b11 m12 u111 112 7200
2028 4 b111 m112 u111 110 14400
2028 4 b111 m112 u111 110 14400
2029 0 b12 m111 u112 111 10800
2029 0 b12 m111 u112 111 10800
2030 3 b112 m111 u11 120 0
2030 3 b112 m111 u11 120 0
2031 2 b111 m11 u12 111 3600
2031 2 b111 m11 u12 111 3600
2032
2032
2033 $ hg log -r 'sort(all(), user)'
2033 $ hg log -r 'sort(all(), user)'
2034 3 b112 m111 u11 120 0
2034 3 b112 m111 u11 120 0
2035 1 b11 m12 u111 112 7200
2035 1 b11 m12 u111 112 7200
2036 4 b111 m112 u111 110 14400
2036 4 b111 m112 u111 110 14400
2037 0 b12 m111 u112 111 10800
2037 0 b12 m111 u112 111 10800
2038 2 b111 m11 u12 111 3600
2038 2 b111 m11 u12 111 3600
2039
2039
2040 $ hg log -r 'sort(all(), -user)'
2040 $ hg log -r 'sort(all(), -user)'
2041 2 b111 m11 u12 111 3600
2041 2 b111 m11 u12 111 3600
2042 0 b12 m111 u112 111 10800
2042 0 b12 m111 u112 111 10800
2043 1 b11 m12 u111 112 7200
2043 1 b11 m12 u111 112 7200
2044 4 b111 m112 u111 110 14400
2044 4 b111 m112 u111 110 14400
2045 3 b112 m111 u11 120 0
2045 3 b112 m111 u11 120 0
2046
2046
2047 compare dates (tz offset should have no effect):
2047 compare dates (tz offset should have no effect):
2048
2048
2049 $ hg log -r 'sort(all(), date)'
2049 $ hg log -r 'sort(all(), date)'
2050 4 b111 m112 u111 110 14400
2050 4 b111 m112 u111 110 14400
2051 0 b12 m111 u112 111 10800
2051 0 b12 m111 u112 111 10800
2052 2 b111 m11 u12 111 3600
2052 2 b111 m11 u12 111 3600
2053 1 b11 m12 u111 112 7200
2053 1 b11 m12 u111 112 7200
2054 3 b112 m111 u11 120 0
2054 3 b112 m111 u11 120 0
2055
2055
2056 $ hg log -r 'sort(all(), -date)'
2056 $ hg log -r 'sort(all(), -date)'
2057 3 b112 m111 u11 120 0
2057 3 b112 m111 u11 120 0
2058 1 b11 m12 u111 112 7200
2058 1 b11 m12 u111 112 7200
2059 0 b12 m111 u112 111 10800
2059 0 b12 m111 u112 111 10800
2060 2 b111 m11 u12 111 3600
2060 2 b111 m11 u12 111 3600
2061 4 b111 m112 u111 110 14400
2061 4 b111 m112 u111 110 14400
2062
2062
2063 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2063 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2064 because '-k' reverses the comparison, not the list itself:
2064 because '-k' reverses the comparison, not the list itself:
2065
2065
2066 $ hg log -r 'sort(0 + 2, date)'
2066 $ hg log -r 'sort(0 + 2, date)'
2067 0 b12 m111 u112 111 10800
2067 0 b12 m111 u112 111 10800
2068 2 b111 m11 u12 111 3600
2068 2 b111 m11 u12 111 3600
2069
2069
2070 $ hg log -r 'sort(0 + 2, -date)'
2070 $ hg log -r 'sort(0 + 2, -date)'
2071 0 b12 m111 u112 111 10800
2071 0 b12 m111 u112 111 10800
2072 2 b111 m11 u12 111 3600
2072 2 b111 m11 u12 111 3600
2073
2073
2074 $ hg log -r 'reverse(sort(0 + 2, date))'
2074 $ hg log -r 'reverse(sort(0 + 2, date))'
2075 2 b111 m11 u12 111 3600
2075 2 b111 m11 u12 111 3600
2076 0 b12 m111 u112 111 10800
2076 0 b12 m111 u112 111 10800
2077
2077
2078 sort by multiple keys:
2078 sort by multiple keys:
2079
2079
2080 $ hg log -r 'sort(all(), "branch -rev")'
2080 $ hg log -r 'sort(all(), "branch -rev")'
2081 1 b11 m12 u111 112 7200
2081 1 b11 m12 u111 112 7200
2082 4 b111 m112 u111 110 14400
2082 4 b111 m112 u111 110 14400
2083 2 b111 m11 u12 111 3600
2083 2 b111 m11 u12 111 3600
2084 3 b112 m111 u11 120 0
2084 3 b112 m111 u11 120 0
2085 0 b12 m111 u112 111 10800
2085 0 b12 m111 u112 111 10800
2086
2086
2087 $ hg log -r 'sort(all(), "-desc -date")'
2087 $ hg log -r 'sort(all(), "-desc -date")'
2088 1 b11 m12 u111 112 7200
2088 1 b11 m12 u111 112 7200
2089 4 b111 m112 u111 110 14400
2089 4 b111 m112 u111 110 14400
2090 3 b112 m111 u11 120 0
2090 3 b112 m111 u11 120 0
2091 0 b12 m111 u112 111 10800
2091 0 b12 m111 u112 111 10800
2092 2 b111 m11 u12 111 3600
2092 2 b111 m11 u12 111 3600
2093
2093
2094 $ hg log -r 'sort(all(), "user -branch date rev")'
2094 $ hg log -r 'sort(all(), "user -branch date rev")'
2095 3 b112 m111 u11 120 0
2095 3 b112 m111 u11 120 0
2096 4 b111 m112 u111 110 14400
2096 4 b111 m112 u111 110 14400
2097 1 b11 m12 u111 112 7200
2097 1 b11 m12 u111 112 7200
2098 0 b12 m111 u112 111 10800
2098 0 b12 m111 u112 111 10800
2099 2 b111 m11 u12 111 3600
2099 2 b111 m11 u12 111 3600
2100
2100
2101 toposort prioritises graph branches
2101 toposort prioritises graph branches
2102
2102
2103 $ hg up 2
2103 $ hg up 2
2104 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2104 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2105 $ touch a
2105 $ touch a
2106 $ hg addremove
2106 $ hg addremove
2107 adding a
2107 adding a
2108 $ hg ci -m 't1' -u 'tu' -d '130 0'
2108 $ hg ci -m 't1' -u 'tu' -d '130 0'
2109 created new head
2109 created new head
2110 $ echo 'a' >> a
2110 $ echo 'a' >> a
2111 $ hg ci -m 't2' -u 'tu' -d '130 0'
2111 $ hg ci -m 't2' -u 'tu' -d '130 0'
2112 $ hg book book1
2112 $ hg book book1
2113 $ hg up 4
2113 $ hg up 4
2114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2115 (leaving bookmark book1)
2115 (leaving bookmark book1)
2116 $ touch a
2116 $ touch a
2117 $ hg addremove
2117 $ hg addremove
2118 adding a
2118 adding a
2119 $ hg ci -m 't3' -u 'tu' -d '130 0'
2119 $ hg ci -m 't3' -u 'tu' -d '130 0'
2120
2120
2121 $ hg log -r 'sort(all(), topo)'
2121 $ hg log -r 'sort(all(), topo)'
2122 7 b111 t3 tu 130 0
2122 7 b111 t3 tu 130 0
2123 4 b111 m112 u111 110 14400
2123 4 b111 m112 u111 110 14400
2124 3 b112 m111 u11 120 0
2124 3 b112 m111 u11 120 0
2125 6 b111 t2 tu 130 0
2125 6 b111 t2 tu 130 0
2126 5 b111 t1 tu 130 0
2126 5 b111 t1 tu 130 0
2127 2 b111 m11 u12 111 3600
2127 2 b111 m11 u12 111 3600
2128 1 b11 m12 u111 112 7200
2128 1 b11 m12 u111 112 7200
2129 0 b12 m111 u112 111 10800
2129 0 b12 m111 u112 111 10800
2130
2130
2131 $ hg log -r 'sort(all(), -topo)'
2131 $ hg log -r 'sort(all(), -topo)'
2132 0 b12 m111 u112 111 10800
2132 0 b12 m111 u112 111 10800
2133 1 b11 m12 u111 112 7200
2133 1 b11 m12 u111 112 7200
2134 2 b111 m11 u12 111 3600
2134 2 b111 m11 u12 111 3600
2135 5 b111 t1 tu 130 0
2135 5 b111 t1 tu 130 0
2136 6 b111 t2 tu 130 0
2136 6 b111 t2 tu 130 0
2137 3 b112 m111 u11 120 0
2137 3 b112 m111 u11 120 0
2138 4 b111 m112 u111 110 14400
2138 4 b111 m112 u111 110 14400
2139 7 b111 t3 tu 130 0
2139 7 b111 t3 tu 130 0
2140
2140
2141 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2141 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2142 6 b111 t2 tu 130 0
2142 6 b111 t2 tu 130 0
2143 5 b111 t1 tu 130 0
2143 5 b111 t1 tu 130 0
2144 7 b111 t3 tu 130 0
2144 7 b111 t3 tu 130 0
2145 4 b111 m112 u111 110 14400
2145 4 b111 m112 u111 110 14400
2146 3 b112 m111 u11 120 0
2146 3 b112 m111 u11 120 0
2147 2 b111 m11 u12 111 3600
2147 2 b111 m11 u12 111 3600
2148 1 b11 m12 u111 112 7200
2148 1 b11 m12 u111 112 7200
2149 0 b12 m111 u112 111 10800
2149 0 b12 m111 u112 111 10800
2150
2150
2151 topographical sorting can't be combined with other sort keys, and you can't
2151 topographical sorting can't be combined with other sort keys, and you can't
2152 use the topo.firstbranch option when topo sort is not active:
2152 use the topo.firstbranch option when topo sort is not active:
2153
2153
2154 $ hg log -r 'sort(all(), "topo user")'
2154 $ hg log -r 'sort(all(), "topo user")'
2155 hg: parse error: topo sort order cannot be combined with other sort keys
2155 hg: parse error: topo sort order cannot be combined with other sort keys
2156 [255]
2156 [255]
2157
2157
2158 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2158 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2159 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2159 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2160 [255]
2160 [255]
2161
2161
2162 topo.firstbranch should accept any kind of expressions:
2162 topo.firstbranch should accept any kind of expressions:
2163
2163
2164 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2164 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2165 0 b12 m111 u112 111 10800
2165 0 b12 m111 u112 111 10800
2166
2166
2167 $ cd ..
2167 $ cd ..
2168 $ cd repo
2168 $ cd repo
2169
2169
2170 test subtracting something from an addset
2170 test subtracting something from an addset
2171
2171
2172 $ log '(outgoing() or removes(a)) - removes(a)'
2172 $ log '(outgoing() or removes(a)) - removes(a)'
2173 8
2173 8
2174 9
2174 9
2175
2175
2176 test intersecting something with an addset
2176 test intersecting something with an addset
2177
2177
2178 $ log 'parents(outgoing() or removes(a))'
2178 $ log 'parents(outgoing() or removes(a))'
2179 1
2179 1
2180 4
2180 4
2181 5
2181 5
2182 8
2182 8
2183
2183
2184 test that `or` operation combines elements in the right order:
2184 test that `or` operation combines elements in the right order:
2185
2185
2186 $ log '3:4 or 2:5'
2186 $ log '3:4 or 2:5'
2187 3
2187 3
2188 4
2188 4
2189 2
2189 2
2190 5
2190 5
2191 $ log '3:4 or 5:2'
2191 $ log '3:4 or 5:2'
2192 3
2192 3
2193 4
2193 4
2194 5
2194 5
2195 2
2195 2
2196 $ log 'sort(3:4 or 2:5)'
2196 $ log 'sort(3:4 or 2:5)'
2197 2
2197 2
2198 3
2198 3
2199 4
2199 4
2200 5
2200 5
2201 $ log 'sort(3:4 or 5:2)'
2201 $ log 'sort(3:4 or 5:2)'
2202 2
2202 2
2203 3
2203 3
2204 4
2204 4
2205 5
2205 5
2206
2206
2207 test that more than one `-r`s are combined in the right order and deduplicated:
2207 test that more than one `-r`s are combined in the right order and deduplicated:
2208
2208
2209 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2209 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2210 3
2210 3
2211 4
2211 4
2212 5
2212 5
2213 2
2213 2
2214 0
2214 0
2215 1
2215 1
2216
2216
2217 test that `or` operation skips duplicated revisions from right-hand side
2217 test that `or` operation skips duplicated revisions from right-hand side
2218
2218
2219 $ try 'reverse(1::5) or ancestors(4)'
2219 $ try 'reverse(1::5) or ancestors(4)'
2220 (or
2220 (or
2221 (list
2221 (list
2222 (func
2222 (func
2223 ('symbol', 'reverse')
2223 ('symbol', 'reverse')
2224 (dagrange
2224 (dagrange
2225 ('symbol', '1')
2225 ('symbol', '1')
2226 ('symbol', '5')))
2226 ('symbol', '5')))
2227 (func
2227 (func
2228 ('symbol', 'ancestors')
2228 ('symbol', 'ancestors')
2229 ('symbol', '4'))))
2229 ('symbol', '4'))))
2230 * set:
2230 * set:
2231 <addset
2231 <addset
2232 <baseset- [1, 3, 5]>,
2232 <baseset- [1, 3, 5]>,
2233 <generatorset+>>
2233 <generatorset+>>
2234 5
2234 5
2235 3
2235 3
2236 1
2236 1
2237 0
2237 0
2238 2
2238 2
2239 4
2239 4
2240 $ try 'sort(ancestors(4) or reverse(1::5))'
2240 $ try 'sort(ancestors(4) or reverse(1::5))'
2241 (func
2241 (func
2242 ('symbol', 'sort')
2242 ('symbol', 'sort')
2243 (or
2243 (or
2244 (list
2244 (list
2245 (func
2245 (func
2246 ('symbol', 'ancestors')
2246 ('symbol', 'ancestors')
2247 ('symbol', '4'))
2247 ('symbol', '4'))
2248 (func
2248 (func
2249 ('symbol', 'reverse')
2249 ('symbol', 'reverse')
2250 (dagrange
2250 (dagrange
2251 ('symbol', '1')
2251 ('symbol', '1')
2252 ('symbol', '5'))))))
2252 ('symbol', '5'))))))
2253 * set:
2253 * set:
2254 <addset+
2254 <addset+
2255 <generatorset+>,
2255 <generatorset+>,
2256 <baseset- [1, 3, 5]>>
2256 <baseset- [1, 3, 5]>>
2257 0
2257 0
2258 1
2258 1
2259 2
2259 2
2260 3
2260 3
2261 4
2261 4
2262 5
2262 5
2263
2263
2264 test optimization of trivial `or` operation
2264 test optimization of trivial `or` operation
2265
2265
2266 $ try --optimize '0|(1)|"2"|-2|tip|null'
2266 $ try --optimize '0|(1)|"2"|-2|tip|null'
2267 (or
2267 (or
2268 (list
2268 (list
2269 ('symbol', '0')
2269 ('symbol', '0')
2270 (group
2270 (group
2271 ('symbol', '1'))
2271 ('symbol', '1'))
2272 ('string', '2')
2272 ('string', '2')
2273 (negate
2273 (negate
2274 ('symbol', '2'))
2274 ('symbol', '2'))
2275 ('symbol', 'tip')
2275 ('symbol', 'tip')
2276 ('symbol', 'null')))
2276 ('symbol', 'null')))
2277 * optimized:
2277 * optimized:
2278 (func
2278 (func
2279 ('symbol', '_list')
2279 ('symbol', '_list')
2280 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2280 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2281 define)
2281 define)
2282 * set:
2282 * set:
2283 <baseset [0, 1, 2, 8, 9, -1]>
2283 <baseset [0, 1, 2, 8, 9, -1]>
2284 0
2284 0
2285 1
2285 1
2286 2
2286 2
2287 8
2287 8
2288 9
2288 9
2289 -1
2289 -1
2290
2290
2291 $ try --optimize '0|1|2:3'
2291 $ try --optimize '0|1|2:3'
2292 (or
2292 (or
2293 (list
2293 (list
2294 ('symbol', '0')
2294 ('symbol', '0')
2295 ('symbol', '1')
2295 ('symbol', '1')
2296 (range
2296 (range
2297 ('symbol', '2')
2297 ('symbol', '2')
2298 ('symbol', '3'))))
2298 ('symbol', '3'))))
2299 * optimized:
2299 * optimized:
2300 (or
2300 (or
2301 (list
2301 (list
2302 (func
2302 (func
2303 ('symbol', '_list')
2303 ('symbol', '_list')
2304 ('string', '0\x001')
2304 ('string', '0\x001')
2305 define)
2305 define)
2306 (range
2306 (range
2307 ('symbol', '2')
2307 ('symbol', '2')
2308 ('symbol', '3')
2308 ('symbol', '3')
2309 define))
2309 define))
2310 define)
2310 define)
2311 * set:
2311 * set:
2312 <addset
2312 <addset
2313 <baseset [0, 1]>,
2313 <baseset [0, 1]>,
2314 <spanset+ 2:3>>
2314 <spanset+ 2:3>>
2315 0
2315 0
2316 1
2316 1
2317 2
2317 2
2318 3
2318 3
2319
2319
2320 $ try --optimize '0:1|2|3:4|5|6'
2320 $ try --optimize '0:1|2|3:4|5|6'
2321 (or
2321 (or
2322 (list
2322 (list
2323 (range
2323 (range
2324 ('symbol', '0')
2324 ('symbol', '0')
2325 ('symbol', '1'))
2325 ('symbol', '1'))
2326 ('symbol', '2')
2326 ('symbol', '2')
2327 (range
2327 (range
2328 ('symbol', '3')
2328 ('symbol', '3')
2329 ('symbol', '4'))
2329 ('symbol', '4'))
2330 ('symbol', '5')
2330 ('symbol', '5')
2331 ('symbol', '6')))
2331 ('symbol', '6')))
2332 * optimized:
2332 * optimized:
2333 (or
2333 (or
2334 (list
2334 (list
2335 (range
2335 (range
2336 ('symbol', '0')
2336 ('symbol', '0')
2337 ('symbol', '1')
2337 ('symbol', '1')
2338 define)
2338 define)
2339 ('symbol', '2')
2339 ('symbol', '2')
2340 (range
2340 (range
2341 ('symbol', '3')
2341 ('symbol', '3')
2342 ('symbol', '4')
2342 ('symbol', '4')
2343 define)
2343 define)
2344 (func
2344 (func
2345 ('symbol', '_list')
2345 ('symbol', '_list')
2346 ('string', '5\x006')
2346 ('string', '5\x006')
2347 define))
2347 define))
2348 define)
2348 define)
2349 * set:
2349 * set:
2350 <addset
2350 <addset
2351 <addset
2351 <addset
2352 <spanset+ 0:1>,
2352 <spanset+ 0:1>,
2353 <baseset [2]>>,
2353 <baseset [2]>>,
2354 <addset
2354 <addset
2355 <spanset+ 3:4>,
2355 <spanset+ 3:4>,
2356 <baseset [5, 6]>>>
2356 <baseset [5, 6]>>>
2357 0
2357 0
2358 1
2358 1
2359 2
2359 2
2360 3
2360 3
2361 4
2361 4
2362 5
2362 5
2363 6
2363 6
2364
2364
2365 unoptimized `or` looks like this
2365 unoptimized `or` looks like this
2366
2366
2367 $ try --no-optimized -p analyzed '0|1|2|3|4'
2367 $ try --no-optimized -p analyzed '0|1|2|3|4'
2368 * analyzed:
2368 * analyzed:
2369 (or
2369 (or
2370 (list
2370 (list
2371 ('symbol', '0')
2371 ('symbol', '0')
2372 ('symbol', '1')
2372 ('symbol', '1')
2373 ('symbol', '2')
2373 ('symbol', '2')
2374 ('symbol', '3')
2374 ('symbol', '3')
2375 ('symbol', '4'))
2375 ('symbol', '4'))
2376 define)
2376 define)
2377 * set:
2377 * set:
2378 <addset
2378 <addset
2379 <addset
2379 <addset
2380 <baseset [0]>,
2380 <baseset [0]>,
2381 <baseset [1]>>,
2381 <baseset [1]>>,
2382 <addset
2382 <addset
2383 <baseset [2]>,
2383 <baseset [2]>,
2384 <addset
2384 <addset
2385 <baseset [3]>,
2385 <baseset [3]>,
2386 <baseset [4]>>>>
2386 <baseset [4]>>>>
2387 0
2387 0
2388 1
2388 1
2389 2
2389 2
2390 3
2390 3
2391 4
2391 4
2392
2392
2393 test that `_list` should be narrowed by provided `subset`
2393 test that `_list` should be narrowed by provided `subset`
2394
2394
2395 $ log '0:2 and (null|1|2|3)'
2395 $ log '0:2 and (null|1|2|3)'
2396 1
2396 1
2397 2
2397 2
2398
2398
2399 test that `_list` should remove duplicates
2399 test that `_list` should remove duplicates
2400
2400
2401 $ log '0|1|2|1|2|-1|tip'
2401 $ log '0|1|2|1|2|-1|tip'
2402 0
2402 0
2403 1
2403 1
2404 2
2404 2
2405 9
2405 9
2406
2406
2407 test unknown revision in `_list`
2407 test unknown revision in `_list`
2408
2408
2409 $ log '0|unknown'
2409 $ log '0|unknown'
2410 abort: unknown revision 'unknown'!
2410 abort: unknown revision 'unknown'!
2411 [255]
2411 [255]
2412
2412
2413 test integer range in `_list`
2413 test integer range in `_list`
2414
2414
2415 $ log '-1|-10'
2415 $ log '-1|-10'
2416 9
2416 9
2417 0
2417 0
2418
2418
2419 $ log '-10|-11'
2419 $ log '-10|-11'
2420 abort: unknown revision '-11'!
2420 abort: unknown revision '-11'!
2421 [255]
2421 [255]
2422
2422
2423 $ log '9|10'
2423 $ log '9|10'
2424 abort: unknown revision '10'!
2424 abort: unknown revision '10'!
2425 [255]
2425 [255]
2426
2426
2427 test '0000' != '0' in `_list`
2427 test '0000' != '0' in `_list`
2428
2428
2429 $ log '0|0000'
2429 $ log '0|0000'
2430 0
2430 0
2431 -1
2431 -1
2432
2432
2433 test ',' in `_list`
2433 test ',' in `_list`
2434 $ log '0,1'
2434 $ log '0,1'
2435 hg: parse error: can't use a list in this context
2435 hg: parse error: can't use a list in this context
2436 (see hg help "revsets.x or y")
2436 (see hg help "revsets.x or y")
2437 [255]
2437 [255]
2438 $ try '0,1,2'
2438 $ try '0,1,2'
2439 (list
2439 (list
2440 ('symbol', '0')
2440 ('symbol', '0')
2441 ('symbol', '1')
2441 ('symbol', '1')
2442 ('symbol', '2'))
2442 ('symbol', '2'))
2443 hg: parse error: can't use a list in this context
2443 hg: parse error: can't use a list in this context
2444 (see hg help "revsets.x or y")
2444 (see hg help "revsets.x or y")
2445 [255]
2445 [255]
2446
2446
2447 test that chained `or` operations make balanced addsets
2447 test that chained `or` operations make balanced addsets
2448
2448
2449 $ try '0:1|1:2|2:3|3:4|4:5'
2449 $ try '0:1|1:2|2:3|3:4|4:5'
2450 (or
2450 (or
2451 (list
2451 (list
2452 (range
2452 (range
2453 ('symbol', '0')
2453 ('symbol', '0')
2454 ('symbol', '1'))
2454 ('symbol', '1'))
2455 (range
2455 (range
2456 ('symbol', '1')
2456 ('symbol', '1')
2457 ('symbol', '2'))
2457 ('symbol', '2'))
2458 (range
2458 (range
2459 ('symbol', '2')
2459 ('symbol', '2')
2460 ('symbol', '3'))
2460 ('symbol', '3'))
2461 (range
2461 (range
2462 ('symbol', '3')
2462 ('symbol', '3')
2463 ('symbol', '4'))
2463 ('symbol', '4'))
2464 (range
2464 (range
2465 ('symbol', '4')
2465 ('symbol', '4')
2466 ('symbol', '5'))))
2466 ('symbol', '5'))))
2467 * set:
2467 * set:
2468 <addset
2468 <addset
2469 <addset
2469 <addset
2470 <spanset+ 0:1>,
2470 <spanset+ 0:1>,
2471 <spanset+ 1:2>>,
2471 <spanset+ 1:2>>,
2472 <addset
2472 <addset
2473 <spanset+ 2:3>,
2473 <spanset+ 2:3>,
2474 <addset
2474 <addset
2475 <spanset+ 3:4>,
2475 <spanset+ 3:4>,
2476 <spanset+ 4:5>>>>
2476 <spanset+ 4:5>>>>
2477 0
2477 0
2478 1
2478 1
2479 2
2479 2
2480 3
2480 3
2481 4
2481 4
2482 5
2482 5
2483
2483
2484 no crash by empty group "()" while optimizing `or` operations
2484 no crash by empty group "()" while optimizing `or` operations
2485
2485
2486 $ try --optimize '0|()'
2486 $ try --optimize '0|()'
2487 (or
2487 (or
2488 (list
2488 (list
2489 ('symbol', '0')
2489 ('symbol', '0')
2490 (group
2490 (group
2491 None)))
2491 None)))
2492 * optimized:
2492 * optimized:
2493 (or
2493 (or
2494 (list
2494 (list
2495 ('symbol', '0')
2495 ('symbol', '0')
2496 None)
2496 None)
2497 define)
2497 define)
2498 hg: parse error: missing argument
2498 hg: parse error: missing argument
2499 [255]
2499 [255]
2500
2500
2501 test that chained `or` operations never eat up stack (issue4624)
2501 test that chained `or` operations never eat up stack (issue4624)
2502 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2502 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2503
2503
2504 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2504 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2505 0
2505 0
2506 1
2506 1
2507
2507
2508 test that repeated `-r` options never eat up stack (issue4565)
2508 test that repeated `-r` options never eat up stack (issue4565)
2509 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2509 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2510
2510
2511 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2511 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2512 0
2512 0
2513 1
2513 1
2514
2514
2515 check that conversion to only works
2515 check that conversion to only works
2516 $ try --optimize '::3 - ::1'
2516 $ try --optimize '::3 - ::1'
2517 (minus
2517 (minus
2518 (dagrangepre
2518 (dagrangepre
2519 ('symbol', '3'))
2519 ('symbol', '3'))
2520 (dagrangepre
2520 (dagrangepre
2521 ('symbol', '1')))
2521 ('symbol', '1')))
2522 * optimized:
2522 * optimized:
2523 (func
2523 (func
2524 ('symbol', 'only')
2524 ('symbol', 'only')
2525 (list
2525 (list
2526 ('symbol', '3')
2526 ('symbol', '3')
2527 ('symbol', '1'))
2527 ('symbol', '1'))
2528 define)
2528 define)
2529 * set:
2529 * set:
2530 <baseset+ [3]>
2530 <baseset+ [3]>
2531 3
2531 3
2532 $ try --optimize 'ancestors(1) - ancestors(3)'
2532 $ try --optimize 'ancestors(1) - ancestors(3)'
2533 (minus
2533 (minus
2534 (func
2534 (func
2535 ('symbol', 'ancestors')
2535 ('symbol', 'ancestors')
2536 ('symbol', '1'))
2536 ('symbol', '1'))
2537 (func
2537 (func
2538 ('symbol', 'ancestors')
2538 ('symbol', 'ancestors')
2539 ('symbol', '3')))
2539 ('symbol', '3')))
2540 * optimized:
2540 * optimized:
2541 (func
2541 (func
2542 ('symbol', 'only')
2542 ('symbol', 'only')
2543 (list
2543 (list
2544 ('symbol', '1')
2544 ('symbol', '1')
2545 ('symbol', '3'))
2545 ('symbol', '3'))
2546 define)
2546 define)
2547 * set:
2547 * set:
2548 <baseset+ []>
2548 <baseset+ []>
2549 $ try --optimize 'not ::2 and ::6'
2549 $ try --optimize 'not ::2 and ::6'
2550 (and
2550 (and
2551 (not
2551 (not
2552 (dagrangepre
2552 (dagrangepre
2553 ('symbol', '2')))
2553 ('symbol', '2')))
2554 (dagrangepre
2554 (dagrangepre
2555 ('symbol', '6')))
2555 ('symbol', '6')))
2556 * optimized:
2556 * optimized:
2557 (func
2557 (func
2558 ('symbol', 'only')
2558 ('symbol', 'only')
2559 (list
2559 (list
2560 ('symbol', '6')
2560 ('symbol', '6')
2561 ('symbol', '2'))
2561 ('symbol', '2'))
2562 define)
2562 define)
2563 * set:
2563 * set:
2564 <baseset+ [3, 4, 5, 6]>
2564 <baseset+ [3, 4, 5, 6]>
2565 3
2565 3
2566 4
2566 4
2567 5
2567 5
2568 6
2568 6
2569 $ try --optimize 'ancestors(6) and not ancestors(4)'
2569 $ try --optimize 'ancestors(6) and not ancestors(4)'
2570 (and
2570 (and
2571 (func
2571 (func
2572 ('symbol', 'ancestors')
2572 ('symbol', 'ancestors')
2573 ('symbol', '6'))
2573 ('symbol', '6'))
2574 (not
2574 (not
2575 (func
2575 (func
2576 ('symbol', 'ancestors')
2576 ('symbol', 'ancestors')
2577 ('symbol', '4'))))
2577 ('symbol', '4'))))
2578 * optimized:
2578 * optimized:
2579 (func
2579 (func
2580 ('symbol', 'only')
2580 ('symbol', 'only')
2581 (list
2581 (list
2582 ('symbol', '6')
2582 ('symbol', '6')
2583 ('symbol', '4'))
2583 ('symbol', '4'))
2584 define)
2584 define)
2585 * set:
2585 * set:
2586 <baseset+ [3, 5, 6]>
2586 <baseset+ [3, 5, 6]>
2587 3
2587 3
2588 5
2588 5
2589 6
2589 6
2590
2590
2591 no crash by empty group "()" while optimizing to "only()"
2591 no crash by empty group "()" while optimizing to "only()"
2592
2592
2593 $ try --optimize '::1 and ()'
2593 $ try --optimize '::1 and ()'
2594 (and
2594 (and
2595 (dagrangepre
2595 (dagrangepre
2596 ('symbol', '1'))
2596 ('symbol', '1'))
2597 (group
2597 (group
2598 None))
2598 None))
2599 * optimized:
2599 * optimized:
2600 (and
2600 (and
2601 None
2601 None
2602 (func
2602 (func
2603 ('symbol', 'ancestors')
2603 ('symbol', 'ancestors')
2604 ('symbol', '1')
2604 ('symbol', '1')
2605 define)
2605 define)
2606 define)
2606 define)
2607 hg: parse error: missing argument
2607 hg: parse error: missing argument
2608 [255]
2608 [255]
2609
2609
2610 invalid function call should not be optimized to only()
2610 invalid function call should not be optimized to only()
2611
2611
2612 $ log '"ancestors"(6) and not ancestors(4)'
2612 $ log '"ancestors"(6) and not ancestors(4)'
2613 hg: parse error: not a symbol
2613 hg: parse error: not a symbol
2614 [255]
2614 [255]
2615
2615
2616 $ log 'ancestors(6) and not "ancestors"(4)'
2616 $ log 'ancestors(6) and not "ancestors"(4)'
2617 hg: parse error: not a symbol
2617 hg: parse error: not a symbol
2618 [255]
2618 [255]
2619
2619
2620 we can use patterns when searching for tags
2620 we can use patterns when searching for tags
2621
2621
2622 $ log 'tag("1..*")'
2622 $ log 'tag("1..*")'
2623 abort: tag '1..*' does not exist!
2623 abort: tag '1..*' does not exist!
2624 [255]
2624 [255]
2625 $ log 'tag("re:1..*")'
2625 $ log 'tag("re:1..*")'
2626 6
2626 6
2627 $ log 'tag("re:[0-9].[0-9]")'
2627 $ log 'tag("re:[0-9].[0-9]")'
2628 6
2628 6
2629 $ log 'tag("literal:1.0")'
2629 $ log 'tag("literal:1.0")'
2630 6
2630 6
2631 $ log 'tag("re:0..*")'
2631 $ log 'tag("re:0..*")'
2632
2632
2633 $ log 'tag(unknown)'
2633 $ log 'tag(unknown)'
2634 abort: tag 'unknown' does not exist!
2634 abort: tag 'unknown' does not exist!
2635 [255]
2635 [255]
2636 $ log 'tag("re:unknown")'
2636 $ log 'tag("re:unknown")'
2637 $ log 'present(tag("unknown"))'
2637 $ log 'present(tag("unknown"))'
2638 $ log 'present(tag("re:unknown"))'
2638 $ log 'present(tag("re:unknown"))'
2639 $ log 'branch(unknown)'
2639 $ log 'branch(unknown)'
2640 abort: unknown revision 'unknown'!
2640 abort: unknown revision 'unknown'!
2641 [255]
2641 [255]
2642 $ log 'branch("literal:unknown")'
2642 $ log 'branch("literal:unknown")'
2643 abort: branch 'unknown' does not exist!
2643 abort: branch 'unknown' does not exist!
2644 [255]
2644 [255]
2645 $ log 'branch("re:unknown")'
2645 $ log 'branch("re:unknown")'
2646 $ log 'present(branch("unknown"))'
2646 $ log 'present(branch("unknown"))'
2647 $ log 'present(branch("re:unknown"))'
2647 $ log 'present(branch("re:unknown"))'
2648 $ log 'user(bob)'
2648 $ log 'user(bob)'
2649 2
2649 2
2650
2650
2651 $ log '4::8'
2651 $ log '4::8'
2652 4
2652 4
2653 8
2653 8
2654 $ log '4:8'
2654 $ log '4:8'
2655 4
2655 4
2656 5
2656 5
2657 6
2657 6
2658 7
2658 7
2659 8
2659 8
2660
2660
2661 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2661 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2662 4
2662 4
2663 2
2663 2
2664 5
2664 5
2665
2665
2666 $ log 'not 0 and 0:2'
2666 $ log 'not 0 and 0:2'
2667 1
2667 1
2668 2
2668 2
2669 $ log 'not 1 and 0:2'
2669 $ log 'not 1 and 0:2'
2670 0
2670 0
2671 2
2671 2
2672 $ log 'not 2 and 0:2'
2672 $ log 'not 2 and 0:2'
2673 0
2673 0
2674 1
2674 1
2675 $ log '(1 and 2)::'
2675 $ log '(1 and 2)::'
2676 $ log '(1 and 2):'
2676 $ log '(1 and 2):'
2677 $ log '(1 and 2):3'
2677 $ log '(1 and 2):3'
2678 $ log 'sort(head(), -rev)'
2678 $ log 'sort(head(), -rev)'
2679 9
2679 9
2680 7
2680 7
2681 6
2681 6
2682 5
2682 5
2683 4
2683 4
2684 3
2684 3
2685 2
2685 2
2686 1
2686 1
2687 0
2687 0
2688 $ log '4::8 - 8'
2688 $ log '4::8 - 8'
2689 4
2689 4
2690
2690
2691 matching() should preserve the order of the input set:
2691 matching() should preserve the order of the input set:
2692
2692
2693 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2693 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2694 2
2694 2
2695 3
2695 3
2696 1
2696 1
2697
2697
2698 $ log 'named("unknown")'
2698 $ log 'named("unknown")'
2699 abort: namespace 'unknown' does not exist!
2699 abort: namespace 'unknown' does not exist!
2700 [255]
2700 [255]
2701 $ log 'named("re:unknown")'
2701 $ log 'named("re:unknown")'
2702 abort: no namespace exists that match 'unknown'!
2702 abort: no namespace exists that match 'unknown'!
2703 [255]
2703 [255]
2704 $ log 'present(named("unknown"))'
2704 $ log 'present(named("unknown"))'
2705 $ log 'present(named("re:unknown"))'
2705 $ log 'present(named("re:unknown"))'
2706
2706
2707 $ log 'tag()'
2707 $ log 'tag()'
2708 6
2708 6
2709 $ log 'named("tags")'
2709 $ log 'named("tags")'
2710 6
2710 6
2711
2711
2712 issue2437
2712 issue2437
2713
2713
2714 $ log '3 and p1(5)'
2714 $ log '3 and p1(5)'
2715 3
2715 3
2716 $ log '4 and p2(6)'
2716 $ log '4 and p2(6)'
2717 4
2717 4
2718 $ log '1 and parents(:2)'
2718 $ log '1 and parents(:2)'
2719 1
2719 1
2720 $ log '2 and children(1:)'
2720 $ log '2 and children(1:)'
2721 2
2721 2
2722 $ log 'roots(all()) or roots(all())'
2722 $ log 'roots(all()) or roots(all())'
2723 0
2723 0
2724 $ hg debugrevspec 'roots(all()) or roots(all())'
2724 $ hg debugrevspec 'roots(all()) or roots(all())'
2725 0
2725 0
2726 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2726 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2727 9
2727 9
2728 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2728 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2729 4
2729 4
2730
2730
2731 issue2654: report a parse error if the revset was not completely parsed
2731 issue2654: report a parse error if the revset was not completely parsed
2732
2732
2733 $ log '1 OR 2'
2733 $ log '1 OR 2'
2734 hg: parse error at 2: invalid token
2734 hg: parse error at 2: invalid token
2735 [255]
2735 [255]
2736
2736
2737 or operator should preserve ordering:
2737 or operator should preserve ordering:
2738 $ log 'reverse(2::4) or tip'
2738 $ log 'reverse(2::4) or tip'
2739 4
2739 4
2740 2
2740 2
2741 9
2741 9
2742
2742
2743 parentrevspec
2743 parentrevspec
2744
2744
2745 $ log 'merge()^0'
2745 $ log 'merge()^0'
2746 6
2746 6
2747 $ log 'merge()^'
2747 $ log 'merge()^'
2748 5
2748 5
2749 $ log 'merge()^1'
2749 $ log 'merge()^1'
2750 5
2750 5
2751 $ log 'merge()^2'
2751 $ log 'merge()^2'
2752 4
2752 4
2753 $ log '(not merge())^2'
2753 $ log 'merge()^^'
2754 $ log 'merge()^^'
2754 3
2755 3
2755 $ log 'merge()^1^'
2756 $ log 'merge()^1^'
2756 3
2757 3
2757 $ log 'merge()^^^'
2758 $ log 'merge()^^^'
2758 1
2759 1
2759
2760
2760 $ log 'merge()~0'
2761 $ log 'merge()~0'
2761 6
2762 6
2762 $ log 'merge()~1'
2763 $ log 'merge()~1'
2763 5
2764 5
2764 $ log 'merge()~2'
2765 $ log 'merge()~2'
2765 3
2766 3
2766 $ log 'merge()~2^1'
2767 $ log 'merge()~2^1'
2767 1
2768 1
2768 $ log 'merge()~3'
2769 $ log 'merge()~3'
2769 1
2770 1
2770
2771
2771 $ log '(-3:tip)^'
2772 $ log '(-3:tip)^'
2772 4
2773 4
2773 6
2774 6
2774 8
2775 8
2775
2776
2776 $ log 'tip^foo'
2777 $ log 'tip^foo'
2777 hg: parse error: ^ expects a number 0, 1, or 2
2778 hg: parse error: ^ expects a number 0, 1, or 2
2778 [255]
2779 [255]
2779
2780
2780 Bogus function gets suggestions
2781 Bogus function gets suggestions
2781 $ log 'add()'
2782 $ log 'add()'
2782 hg: parse error: unknown identifier: add
2783 hg: parse error: unknown identifier: add
2783 (did you mean adds?)
2784 (did you mean adds?)
2784 [255]
2785 [255]
2785 $ log 'added()'
2786 $ log 'added()'
2786 hg: parse error: unknown identifier: added
2787 hg: parse error: unknown identifier: added
2787 (did you mean adds?)
2788 (did you mean adds?)
2788 [255]
2789 [255]
2789 $ log 'remo()'
2790 $ log 'remo()'
2790 hg: parse error: unknown identifier: remo
2791 hg: parse error: unknown identifier: remo
2791 (did you mean one of remote, removes?)
2792 (did you mean one of remote, removes?)
2792 [255]
2793 [255]
2793 $ log 'babar()'
2794 $ log 'babar()'
2794 hg: parse error: unknown identifier: babar
2795 hg: parse error: unknown identifier: babar
2795 [255]
2796 [255]
2796
2797
2797 Bogus function with a similar internal name doesn't suggest the internal name
2798 Bogus function with a similar internal name doesn't suggest the internal name
2798 $ log 'matches()'
2799 $ log 'matches()'
2799 hg: parse error: unknown identifier: matches
2800 hg: parse error: unknown identifier: matches
2800 (did you mean matching?)
2801 (did you mean matching?)
2801 [255]
2802 [255]
2802
2803
2803 Undocumented functions aren't suggested as similar either
2804 Undocumented functions aren't suggested as similar either
2804 $ log 'wdir2()'
2805 $ log 'wdir2()'
2805 hg: parse error: unknown identifier: wdir2
2806 hg: parse error: unknown identifier: wdir2
2806 [255]
2807 [255]
2807
2808
2808 multiple revspecs
2809 multiple revspecs
2809
2810
2810 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2811 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2811 8
2812 8
2812 9
2813 9
2813 4
2814 4
2814 5
2815 5
2815 6
2816 6
2816 7
2817 7
2817
2818
2818 test usage in revpair (with "+")
2819 test usage in revpair (with "+")
2819
2820
2820 (real pair)
2821 (real pair)
2821
2822
2822 $ hg diff -r 'tip^^' -r 'tip'
2823 $ hg diff -r 'tip^^' -r 'tip'
2823 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2824 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2824 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2825 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2825 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2826 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2826 @@ -0,0 +1,1 @@
2827 @@ -0,0 +1,1 @@
2827 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2828 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2828 $ hg diff -r 'tip^^::tip'
2829 $ hg diff -r 'tip^^::tip'
2829 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2830 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2830 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2831 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2831 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2832 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2832 @@ -0,0 +1,1 @@
2833 @@ -0,0 +1,1 @@
2833 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2834 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2834
2835
2835 (single rev)
2836 (single rev)
2836
2837
2837 $ hg diff -r 'tip^' -r 'tip^'
2838 $ hg diff -r 'tip^' -r 'tip^'
2838 $ hg diff -r 'tip^:tip^'
2839 $ hg diff -r 'tip^:tip^'
2839
2840
2840 (single rev that does not looks like a range)
2841 (single rev that does not looks like a range)
2841
2842
2842 $ hg diff -r 'tip^::tip^ or tip^'
2843 $ hg diff -r 'tip^::tip^ or tip^'
2843 diff -r d5d0dcbdc4d9 .hgtags
2844 diff -r d5d0dcbdc4d9 .hgtags
2844 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2845 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2845 +++ b/.hgtags * (glob)
2846 +++ b/.hgtags * (glob)
2846 @@ -0,0 +1,1 @@
2847 @@ -0,0 +1,1 @@
2847 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2848 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2848 $ hg diff -r 'tip^ or tip^'
2849 $ hg diff -r 'tip^ or tip^'
2849 diff -r d5d0dcbdc4d9 .hgtags
2850 diff -r d5d0dcbdc4d9 .hgtags
2850 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2851 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2851 +++ b/.hgtags * (glob)
2852 +++ b/.hgtags * (glob)
2852 @@ -0,0 +1,1 @@
2853 @@ -0,0 +1,1 @@
2853 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2854 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2854
2855
2855 (no rev)
2856 (no rev)
2856
2857
2857 $ hg diff -r 'author("babar") or author("celeste")'
2858 $ hg diff -r 'author("babar") or author("celeste")'
2858 abort: empty revision range
2859 abort: empty revision range
2859 [255]
2860 [255]
2860
2861
2861 aliases:
2862 aliases:
2862
2863
2863 $ echo '[revsetalias]' >> .hg/hgrc
2864 $ echo '[revsetalias]' >> .hg/hgrc
2864 $ echo 'm = merge()' >> .hg/hgrc
2865 $ echo 'm = merge()' >> .hg/hgrc
2865 (revset aliases can override builtin revsets)
2866 (revset aliases can override builtin revsets)
2866 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2867 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2867 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2868 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2868 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2869 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2869 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2870 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2870 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2871 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2871
2872
2872 $ try m
2873 $ try m
2873 ('symbol', 'm')
2874 ('symbol', 'm')
2874 * expanded:
2875 * expanded:
2875 (func
2876 (func
2876 ('symbol', 'merge')
2877 ('symbol', 'merge')
2877 None)
2878 None)
2878 * set:
2879 * set:
2879 <filteredset
2880 <filteredset
2880 <fullreposet+ 0:9>,
2881 <fullreposet+ 0:9>,
2881 <merge>>
2882 <merge>>
2882 6
2883 6
2883
2884
2884 $ HGPLAIN=1
2885 $ HGPLAIN=1
2885 $ export HGPLAIN
2886 $ export HGPLAIN
2886 $ try m
2887 $ try m
2887 ('symbol', 'm')
2888 ('symbol', 'm')
2888 abort: unknown revision 'm'!
2889 abort: unknown revision 'm'!
2889 [255]
2890 [255]
2890
2891
2891 $ HGPLAINEXCEPT=revsetalias
2892 $ HGPLAINEXCEPT=revsetalias
2892 $ export HGPLAINEXCEPT
2893 $ export HGPLAINEXCEPT
2893 $ try m
2894 $ try m
2894 ('symbol', 'm')
2895 ('symbol', 'm')
2895 * expanded:
2896 * expanded:
2896 (func
2897 (func
2897 ('symbol', 'merge')
2898 ('symbol', 'merge')
2898 None)
2899 None)
2899 * set:
2900 * set:
2900 <filteredset
2901 <filteredset
2901 <fullreposet+ 0:9>,
2902 <fullreposet+ 0:9>,
2902 <merge>>
2903 <merge>>
2903 6
2904 6
2904
2905
2905 $ unset HGPLAIN
2906 $ unset HGPLAIN
2906 $ unset HGPLAINEXCEPT
2907 $ unset HGPLAINEXCEPT
2907
2908
2908 $ try 'p2(.)'
2909 $ try 'p2(.)'
2909 (func
2910 (func
2910 ('symbol', 'p2')
2911 ('symbol', 'p2')
2911 ('symbol', '.'))
2912 ('symbol', '.'))
2912 * expanded:
2913 * expanded:
2913 (func
2914 (func
2914 ('symbol', 'p1')
2915 ('symbol', 'p1')
2915 ('symbol', '.'))
2916 ('symbol', '.'))
2916 * set:
2917 * set:
2917 <baseset+ [8]>
2918 <baseset+ [8]>
2918 8
2919 8
2919
2920
2920 $ HGPLAIN=1
2921 $ HGPLAIN=1
2921 $ export HGPLAIN
2922 $ export HGPLAIN
2922 $ try 'p2(.)'
2923 $ try 'p2(.)'
2923 (func
2924 (func
2924 ('symbol', 'p2')
2925 ('symbol', 'p2')
2925 ('symbol', '.'))
2926 ('symbol', '.'))
2926 * set:
2927 * set:
2927 <baseset+ []>
2928 <baseset+ []>
2928
2929
2929 $ HGPLAINEXCEPT=revsetalias
2930 $ HGPLAINEXCEPT=revsetalias
2930 $ export HGPLAINEXCEPT
2931 $ export HGPLAINEXCEPT
2931 $ try 'p2(.)'
2932 $ try 'p2(.)'
2932 (func
2933 (func
2933 ('symbol', 'p2')
2934 ('symbol', 'p2')
2934 ('symbol', '.'))
2935 ('symbol', '.'))
2935 * expanded:
2936 * expanded:
2936 (func
2937 (func
2937 ('symbol', 'p1')
2938 ('symbol', 'p1')
2938 ('symbol', '.'))
2939 ('symbol', '.'))
2939 * set:
2940 * set:
2940 <baseset+ [8]>
2941 <baseset+ [8]>
2941 8
2942 8
2942
2943
2943 $ unset HGPLAIN
2944 $ unset HGPLAIN
2944 $ unset HGPLAINEXCEPT
2945 $ unset HGPLAINEXCEPT
2945
2946
2946 test alias recursion
2947 test alias recursion
2947
2948
2948 $ try sincem
2949 $ try sincem
2949 ('symbol', 'sincem')
2950 ('symbol', 'sincem')
2950 * expanded:
2951 * expanded:
2951 (func
2952 (func
2952 ('symbol', 'descendants')
2953 ('symbol', 'descendants')
2953 (func
2954 (func
2954 ('symbol', 'merge')
2955 ('symbol', 'merge')
2955 None))
2956 None))
2956 * set:
2957 * set:
2957 <addset+
2958 <addset+
2958 <filteredset
2959 <filteredset
2959 <fullreposet+ 0:9>,
2960 <fullreposet+ 0:9>,
2960 <merge>>,
2961 <merge>>,
2961 <generatorset+>>
2962 <generatorset+>>
2962 6
2963 6
2963 7
2964 7
2964
2965
2965 test infinite recursion
2966 test infinite recursion
2966
2967
2967 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2968 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2968 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2969 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2969 $ try recurse1
2970 $ try recurse1
2970 ('symbol', 'recurse1')
2971 ('symbol', 'recurse1')
2971 hg: parse error: infinite expansion of revset alias "recurse1" detected
2972 hg: parse error: infinite expansion of revset alias "recurse1" detected
2972 [255]
2973 [255]
2973
2974
2974 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2975 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2975 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2976 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2976 $ try "level2(level1(1, 2), 3)"
2977 $ try "level2(level1(1, 2), 3)"
2977 (func
2978 (func
2978 ('symbol', 'level2')
2979 ('symbol', 'level2')
2979 (list
2980 (list
2980 (func
2981 (func
2981 ('symbol', 'level1')
2982 ('symbol', 'level1')
2982 (list
2983 (list
2983 ('symbol', '1')
2984 ('symbol', '1')
2984 ('symbol', '2')))
2985 ('symbol', '2')))
2985 ('symbol', '3')))
2986 ('symbol', '3')))
2986 * expanded:
2987 * expanded:
2987 (or
2988 (or
2988 (list
2989 (list
2989 ('symbol', '3')
2990 ('symbol', '3')
2990 (or
2991 (or
2991 (list
2992 (list
2992 ('symbol', '1')
2993 ('symbol', '1')
2993 ('symbol', '2')))))
2994 ('symbol', '2')))))
2994 * set:
2995 * set:
2995 <addset
2996 <addset
2996 <baseset [3]>,
2997 <baseset [3]>,
2997 <baseset [1, 2]>>
2998 <baseset [1, 2]>>
2998 3
2999 3
2999 1
3000 1
3000 2
3001 2
3001
3002
3002 test nesting and variable passing
3003 test nesting and variable passing
3003
3004
3004 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3005 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3005 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3006 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3006 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3007 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3007 $ try 'nested(2:5)'
3008 $ try 'nested(2:5)'
3008 (func
3009 (func
3009 ('symbol', 'nested')
3010 ('symbol', 'nested')
3010 (range
3011 (range
3011 ('symbol', '2')
3012 ('symbol', '2')
3012 ('symbol', '5')))
3013 ('symbol', '5')))
3013 * expanded:
3014 * expanded:
3014 (func
3015 (func
3015 ('symbol', 'max')
3016 ('symbol', 'max')
3016 (range
3017 (range
3017 ('symbol', '2')
3018 ('symbol', '2')
3018 ('symbol', '5')))
3019 ('symbol', '5')))
3019 * set:
3020 * set:
3020 <baseset
3021 <baseset
3021 <max
3022 <max
3022 <fullreposet+ 0:9>,
3023 <fullreposet+ 0:9>,
3023 <spanset+ 2:5>>>
3024 <spanset+ 2:5>>>
3024 5
3025 5
3025
3026
3026 test chained `or` operations are flattened at parsing phase
3027 test chained `or` operations are flattened at parsing phase
3027
3028
3028 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3029 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3029 $ try 'chainedorops(0:1, 1:2, 2:3)'
3030 $ try 'chainedorops(0:1, 1:2, 2:3)'
3030 (func
3031 (func
3031 ('symbol', 'chainedorops')
3032 ('symbol', 'chainedorops')
3032 (list
3033 (list
3033 (range
3034 (range
3034 ('symbol', '0')
3035 ('symbol', '0')
3035 ('symbol', '1'))
3036 ('symbol', '1'))
3036 (range
3037 (range
3037 ('symbol', '1')
3038 ('symbol', '1')
3038 ('symbol', '2'))
3039 ('symbol', '2'))
3039 (range
3040 (range
3040 ('symbol', '2')
3041 ('symbol', '2')
3041 ('symbol', '3'))))
3042 ('symbol', '3'))))
3042 * expanded:
3043 * expanded:
3043 (or
3044 (or
3044 (list
3045 (list
3045 (range
3046 (range
3046 ('symbol', '0')
3047 ('symbol', '0')
3047 ('symbol', '1'))
3048 ('symbol', '1'))
3048 (range
3049 (range
3049 ('symbol', '1')
3050 ('symbol', '1')
3050 ('symbol', '2'))
3051 ('symbol', '2'))
3051 (range
3052 (range
3052 ('symbol', '2')
3053 ('symbol', '2')
3053 ('symbol', '3'))))
3054 ('symbol', '3'))))
3054 * set:
3055 * set:
3055 <addset
3056 <addset
3056 <spanset+ 0:1>,
3057 <spanset+ 0:1>,
3057 <addset
3058 <addset
3058 <spanset+ 1:2>,
3059 <spanset+ 1:2>,
3059 <spanset+ 2:3>>>
3060 <spanset+ 2:3>>>
3060 0
3061 0
3061 1
3062 1
3062 2
3063 2
3063 3
3064 3
3064
3065
3065 test variable isolation, variable placeholders are rewritten as string
3066 test variable isolation, variable placeholders are rewritten as string
3066 then parsed and matched again as string. Check they do not leak too
3067 then parsed and matched again as string. Check they do not leak too
3067 far away.
3068 far away.
3068
3069
3069 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3070 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3070 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3071 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3071 $ try 'callinjection(2:5)'
3072 $ try 'callinjection(2:5)'
3072 (func
3073 (func
3073 ('symbol', 'callinjection')
3074 ('symbol', 'callinjection')
3074 (range
3075 (range
3075 ('symbol', '2')
3076 ('symbol', '2')
3076 ('symbol', '5')))
3077 ('symbol', '5')))
3077 * expanded:
3078 * expanded:
3078 (func
3079 (func
3079 ('symbol', 'descendants')
3080 ('symbol', 'descendants')
3080 (func
3081 (func
3081 ('symbol', 'max')
3082 ('symbol', 'max')
3082 ('string', '$1')))
3083 ('string', '$1')))
3083 abort: unknown revision '$1'!
3084 abort: unknown revision '$1'!
3084 [255]
3085 [255]
3085
3086
3086 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3087 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3087 but 'all()' should never be substituded to '0()'.
3088 but 'all()' should never be substituded to '0()'.
3088
3089
3089 $ echo 'universe = all()' >> .hg/hgrc
3090 $ echo 'universe = all()' >> .hg/hgrc
3090 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3091 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3091 $ try 'shadowall(0)'
3092 $ try 'shadowall(0)'
3092 (func
3093 (func
3093 ('symbol', 'shadowall')
3094 ('symbol', 'shadowall')
3094 ('symbol', '0'))
3095 ('symbol', '0'))
3095 * expanded:
3096 * expanded:
3096 (and
3097 (and
3097 ('symbol', '0')
3098 ('symbol', '0')
3098 (func
3099 (func
3099 ('symbol', 'all')
3100 ('symbol', 'all')
3100 None))
3101 None))
3101 * set:
3102 * set:
3102 <filteredset
3103 <filteredset
3103 <baseset [0]>,
3104 <baseset [0]>,
3104 <spanset+ 0:9>>
3105 <spanset+ 0:9>>
3105 0
3106 0
3106
3107
3107 test unknown reference:
3108 test unknown reference:
3108
3109
3109 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3110 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3110 (func
3111 (func
3111 ('symbol', 'unknownref')
3112 ('symbol', 'unknownref')
3112 ('symbol', '0'))
3113 ('symbol', '0'))
3113 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3114 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3114 [255]
3115 [255]
3115
3116
3116 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3117 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3117 ('symbol', 'tip')
3118 ('symbol', 'tip')
3118 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3119 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3119 * set:
3120 * set:
3120 <baseset [9]>
3121 <baseset [9]>
3121 9
3122 9
3122
3123
3123 $ try 'tip'
3124 $ try 'tip'
3124 ('symbol', 'tip')
3125 ('symbol', 'tip')
3125 * set:
3126 * set:
3126 <baseset [9]>
3127 <baseset [9]>
3127 9
3128 9
3128
3129
3129 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3130 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3130 ('symbol', 'tip')
3131 ('symbol', 'tip')
3131 warning: bad declaration of revset alias "bad name": at 4: invalid token
3132 warning: bad declaration of revset alias "bad name": at 4: invalid token
3132 * set:
3133 * set:
3133 <baseset [9]>
3134 <baseset [9]>
3134 9
3135 9
3135 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3136 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3136 $ try 'strictreplacing("foo", tip)'
3137 $ try 'strictreplacing("foo", tip)'
3137 (func
3138 (func
3138 ('symbol', 'strictreplacing')
3139 ('symbol', 'strictreplacing')
3139 (list
3140 (list
3140 ('string', 'foo')
3141 ('string', 'foo')
3141 ('symbol', 'tip')))
3142 ('symbol', 'tip')))
3142 * expanded:
3143 * expanded:
3143 (or
3144 (or
3144 (list
3145 (list
3145 ('symbol', 'tip')
3146 ('symbol', 'tip')
3146 (func
3147 (func
3147 ('symbol', 'desc')
3148 ('symbol', 'desc')
3148 ('string', '$1'))))
3149 ('string', '$1'))))
3149 * set:
3150 * set:
3150 <addset
3151 <addset
3151 <baseset [9]>,
3152 <baseset [9]>,
3152 <filteredset
3153 <filteredset
3153 <fullreposet+ 0:9>,
3154 <fullreposet+ 0:9>,
3154 <desc '$1'>>>
3155 <desc '$1'>>>
3155 9
3156 9
3156
3157
3157 $ try 'd(2:5)'
3158 $ try 'd(2:5)'
3158 (func
3159 (func
3159 ('symbol', 'd')
3160 ('symbol', 'd')
3160 (range
3161 (range
3161 ('symbol', '2')
3162 ('symbol', '2')
3162 ('symbol', '5')))
3163 ('symbol', '5')))
3163 * expanded:
3164 * expanded:
3164 (func
3165 (func
3165 ('symbol', 'reverse')
3166 ('symbol', 'reverse')
3166 (func
3167 (func
3167 ('symbol', 'sort')
3168 ('symbol', 'sort')
3168 (list
3169 (list
3169 (range
3170 (range
3170 ('symbol', '2')
3171 ('symbol', '2')
3171 ('symbol', '5'))
3172 ('symbol', '5'))
3172 ('symbol', 'date'))))
3173 ('symbol', 'date'))))
3173 * set:
3174 * set:
3174 <baseset [4, 5, 3, 2]>
3175 <baseset [4, 5, 3, 2]>
3175 4
3176 4
3176 5
3177 5
3177 3
3178 3
3178 2
3179 2
3179 $ try 'rs(2 or 3, date)'
3180 $ try 'rs(2 or 3, date)'
3180 (func
3181 (func
3181 ('symbol', 'rs')
3182 ('symbol', 'rs')
3182 (list
3183 (list
3183 (or
3184 (or
3184 (list
3185 (list
3185 ('symbol', '2')
3186 ('symbol', '2')
3186 ('symbol', '3')))
3187 ('symbol', '3')))
3187 ('symbol', 'date')))
3188 ('symbol', 'date')))
3188 * expanded:
3189 * expanded:
3189 (func
3190 (func
3190 ('symbol', 'reverse')
3191 ('symbol', 'reverse')
3191 (func
3192 (func
3192 ('symbol', 'sort')
3193 ('symbol', 'sort')
3193 (list
3194 (list
3194 (or
3195 (or
3195 (list
3196 (list
3196 ('symbol', '2')
3197 ('symbol', '2')
3197 ('symbol', '3')))
3198 ('symbol', '3')))
3198 ('symbol', 'date'))))
3199 ('symbol', 'date'))))
3199 * set:
3200 * set:
3200 <baseset [3, 2]>
3201 <baseset [3, 2]>
3201 3
3202 3
3202 2
3203 2
3203 $ try 'rs()'
3204 $ try 'rs()'
3204 (func
3205 (func
3205 ('symbol', 'rs')
3206 ('symbol', 'rs')
3206 None)
3207 None)
3207 hg: parse error: invalid number of arguments: 0
3208 hg: parse error: invalid number of arguments: 0
3208 [255]
3209 [255]
3209 $ try 'rs(2)'
3210 $ try 'rs(2)'
3210 (func
3211 (func
3211 ('symbol', 'rs')
3212 ('symbol', 'rs')
3212 ('symbol', '2'))
3213 ('symbol', '2'))
3213 hg: parse error: invalid number of arguments: 1
3214 hg: parse error: invalid number of arguments: 1
3214 [255]
3215 [255]
3215 $ try 'rs(2, data, 7)'
3216 $ try 'rs(2, data, 7)'
3216 (func
3217 (func
3217 ('symbol', 'rs')
3218 ('symbol', 'rs')
3218 (list
3219 (list
3219 ('symbol', '2')
3220 ('symbol', '2')
3220 ('symbol', 'data')
3221 ('symbol', 'data')
3221 ('symbol', '7')))
3222 ('symbol', '7')))
3222 hg: parse error: invalid number of arguments: 3
3223 hg: parse error: invalid number of arguments: 3
3223 [255]
3224 [255]
3224 $ try 'rs4(2 or 3, x, x, date)'
3225 $ try 'rs4(2 or 3, x, x, date)'
3225 (func
3226 (func
3226 ('symbol', 'rs4')
3227 ('symbol', 'rs4')
3227 (list
3228 (list
3228 (or
3229 (or
3229 (list
3230 (list
3230 ('symbol', '2')
3231 ('symbol', '2')
3231 ('symbol', '3')))
3232 ('symbol', '3')))
3232 ('symbol', 'x')
3233 ('symbol', 'x')
3233 ('symbol', 'x')
3234 ('symbol', 'x')
3234 ('symbol', 'date')))
3235 ('symbol', 'date')))
3235 * expanded:
3236 * expanded:
3236 (func
3237 (func
3237 ('symbol', 'reverse')
3238 ('symbol', 'reverse')
3238 (func
3239 (func
3239 ('symbol', 'sort')
3240 ('symbol', 'sort')
3240 (list
3241 (list
3241 (or
3242 (or
3242 (list
3243 (list
3243 ('symbol', '2')
3244 ('symbol', '2')
3244 ('symbol', '3')))
3245 ('symbol', '3')))
3245 ('symbol', 'date'))))
3246 ('symbol', 'date'))))
3246 * set:
3247 * set:
3247 <baseset [3, 2]>
3248 <baseset [3, 2]>
3248 3
3249 3
3249 2
3250 2
3250
3251
3251 issue4553: check that revset aliases override existing hash prefix
3252 issue4553: check that revset aliases override existing hash prefix
3252
3253
3253 $ hg log -qr e
3254 $ hg log -qr e
3254 6:e0cc66ef77e8
3255 6:e0cc66ef77e8
3255
3256
3256 $ hg log -qr e --config revsetalias.e="all()"
3257 $ hg log -qr e --config revsetalias.e="all()"
3257 0:2785f51eece5
3258 0:2785f51eece5
3258 1:d75937da8da0
3259 1:d75937da8da0
3259 2:5ed5505e9f1c
3260 2:5ed5505e9f1c
3260 3:8528aa5637f2
3261 3:8528aa5637f2
3261 4:2326846efdab
3262 4:2326846efdab
3262 5:904fa392b941
3263 5:904fa392b941
3263 6:e0cc66ef77e8
3264 6:e0cc66ef77e8
3264 7:013af1973af4
3265 7:013af1973af4
3265 8:d5d0dcbdc4d9
3266 8:d5d0dcbdc4d9
3266 9:24286f4ae135
3267 9:24286f4ae135
3267
3268
3268 $ hg log -qr e: --config revsetalias.e="0"
3269 $ hg log -qr e: --config revsetalias.e="0"
3269 0:2785f51eece5
3270 0:2785f51eece5
3270 1:d75937da8da0
3271 1:d75937da8da0
3271 2:5ed5505e9f1c
3272 2:5ed5505e9f1c
3272 3:8528aa5637f2
3273 3:8528aa5637f2
3273 4:2326846efdab
3274 4:2326846efdab
3274 5:904fa392b941
3275 5:904fa392b941
3275 6:e0cc66ef77e8
3276 6:e0cc66ef77e8
3276 7:013af1973af4
3277 7:013af1973af4
3277 8:d5d0dcbdc4d9
3278 8:d5d0dcbdc4d9
3278 9:24286f4ae135
3279 9:24286f4ae135
3279
3280
3280 $ hg log -qr :e --config revsetalias.e="9"
3281 $ hg log -qr :e --config revsetalias.e="9"
3281 0:2785f51eece5
3282 0:2785f51eece5
3282 1:d75937da8da0
3283 1:d75937da8da0
3283 2:5ed5505e9f1c
3284 2:5ed5505e9f1c
3284 3:8528aa5637f2
3285 3:8528aa5637f2
3285 4:2326846efdab
3286 4:2326846efdab
3286 5:904fa392b941
3287 5:904fa392b941
3287 6:e0cc66ef77e8
3288 6:e0cc66ef77e8
3288 7:013af1973af4
3289 7:013af1973af4
3289 8:d5d0dcbdc4d9
3290 8:d5d0dcbdc4d9
3290 9:24286f4ae135
3291 9:24286f4ae135
3291
3292
3292 $ hg log -qr e:
3293 $ hg log -qr e:
3293 6:e0cc66ef77e8
3294 6:e0cc66ef77e8
3294 7:013af1973af4
3295 7:013af1973af4
3295 8:d5d0dcbdc4d9
3296 8:d5d0dcbdc4d9
3296 9:24286f4ae135
3297 9:24286f4ae135
3297
3298
3298 $ hg log -qr :e
3299 $ hg log -qr :e
3299 0:2785f51eece5
3300 0:2785f51eece5
3300 1:d75937da8da0
3301 1:d75937da8da0
3301 2:5ed5505e9f1c
3302 2:5ed5505e9f1c
3302 3:8528aa5637f2
3303 3:8528aa5637f2
3303 4:2326846efdab
3304 4:2326846efdab
3304 5:904fa392b941
3305 5:904fa392b941
3305 6:e0cc66ef77e8
3306 6:e0cc66ef77e8
3306
3307
3307 issue2549 - correct optimizations
3308 issue2549 - correct optimizations
3308
3309
3309 $ try 'limit(1 or 2 or 3, 2) and not 2'
3310 $ try 'limit(1 or 2 or 3, 2) and not 2'
3310 (and
3311 (and
3311 (func
3312 (func
3312 ('symbol', 'limit')
3313 ('symbol', 'limit')
3313 (list
3314 (list
3314 (or
3315 (or
3315 (list
3316 (list
3316 ('symbol', '1')
3317 ('symbol', '1')
3317 ('symbol', '2')
3318 ('symbol', '2')
3318 ('symbol', '3')))
3319 ('symbol', '3')))
3319 ('symbol', '2')))
3320 ('symbol', '2')))
3320 (not
3321 (not
3321 ('symbol', '2')))
3322 ('symbol', '2')))
3322 * set:
3323 * set:
3323 <filteredset
3324 <filteredset
3324 <baseset
3325 <baseset
3325 <limit n=2, offset=0,
3326 <limit n=2, offset=0,
3326 <fullreposet+ 0:9>,
3327 <fullreposet+ 0:9>,
3327 <baseset [1, 2, 3]>>>,
3328 <baseset [1, 2, 3]>>>,
3328 <not
3329 <not
3329 <baseset [2]>>>
3330 <baseset [2]>>>
3330 1
3331 1
3331 $ try 'max(1 or 2) and not 2'
3332 $ try 'max(1 or 2) and not 2'
3332 (and
3333 (and
3333 (func
3334 (func
3334 ('symbol', 'max')
3335 ('symbol', 'max')
3335 (or
3336 (or
3336 (list
3337 (list
3337 ('symbol', '1')
3338 ('symbol', '1')
3338 ('symbol', '2'))))
3339 ('symbol', '2'))))
3339 (not
3340 (not
3340 ('symbol', '2')))
3341 ('symbol', '2')))
3341 * set:
3342 * set:
3342 <filteredset
3343 <filteredset
3343 <baseset
3344 <baseset
3344 <max
3345 <max
3345 <fullreposet+ 0:9>,
3346 <fullreposet+ 0:9>,
3346 <baseset [1, 2]>>>,
3347 <baseset [1, 2]>>>,
3347 <not
3348 <not
3348 <baseset [2]>>>
3349 <baseset [2]>>>
3349 $ try 'min(1 or 2) and not 1'
3350 $ try 'min(1 or 2) and not 1'
3350 (and
3351 (and
3351 (func
3352 (func
3352 ('symbol', 'min')
3353 ('symbol', 'min')
3353 (or
3354 (or
3354 (list
3355 (list
3355 ('symbol', '1')
3356 ('symbol', '1')
3356 ('symbol', '2'))))
3357 ('symbol', '2'))))
3357 (not
3358 (not
3358 ('symbol', '1')))
3359 ('symbol', '1')))
3359 * set:
3360 * set:
3360 <filteredset
3361 <filteredset
3361 <baseset
3362 <baseset
3362 <min
3363 <min
3363 <fullreposet+ 0:9>,
3364 <fullreposet+ 0:9>,
3364 <baseset [1, 2]>>>,
3365 <baseset [1, 2]>>>,
3365 <not
3366 <not
3366 <baseset [1]>>>
3367 <baseset [1]>>>
3367 $ try 'last(1 or 2, 1) and not 2'
3368 $ try 'last(1 or 2, 1) and not 2'
3368 (and
3369 (and
3369 (func
3370 (func
3370 ('symbol', 'last')
3371 ('symbol', 'last')
3371 (list
3372 (list
3372 (or
3373 (or
3373 (list
3374 (list
3374 ('symbol', '1')
3375 ('symbol', '1')
3375 ('symbol', '2')))
3376 ('symbol', '2')))
3376 ('symbol', '1')))
3377 ('symbol', '1')))
3377 (not
3378 (not
3378 ('symbol', '2')))
3379 ('symbol', '2')))
3379 * set:
3380 * set:
3380 <filteredset
3381 <filteredset
3381 <baseset
3382 <baseset
3382 <last n=1,
3383 <last n=1,
3383 <fullreposet+ 0:9>,
3384 <fullreposet+ 0:9>,
3384 <baseset [2, 1]>>>,
3385 <baseset [2, 1]>>>,
3385 <not
3386 <not
3386 <baseset [2]>>>
3387 <baseset [2]>>>
3387
3388
3388 issue4289 - ordering of built-ins
3389 issue4289 - ordering of built-ins
3389 $ hg log -M -q -r 3:2
3390 $ hg log -M -q -r 3:2
3390 3:8528aa5637f2
3391 3:8528aa5637f2
3391 2:5ed5505e9f1c
3392 2:5ed5505e9f1c
3392
3393
3393 test revsets started with 40-chars hash (issue3669)
3394 test revsets started with 40-chars hash (issue3669)
3394
3395
3395 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3396 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3396 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3397 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3397 9
3398 9
3398 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3399 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3399 8
3400 8
3400
3401
3401 test or-ed indirect predicates (issue3775)
3402 test or-ed indirect predicates (issue3775)
3402
3403
3403 $ log '6 or 6^1' | sort
3404 $ log '6 or 6^1' | sort
3404 5
3405 5
3405 6
3406 6
3406 $ log '6^1 or 6' | sort
3407 $ log '6^1 or 6' | sort
3407 5
3408 5
3408 6
3409 6
3409 $ log '4 or 4~1' | sort
3410 $ log '4 or 4~1' | sort
3410 2
3411 2
3411 4
3412 4
3412 $ log '4~1 or 4' | sort
3413 $ log '4~1 or 4' | sort
3413 2
3414 2
3414 4
3415 4
3415 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3416 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3416 0
3417 0
3417 1
3418 1
3418 2
3419 2
3419 3
3420 3
3420 4
3421 4
3421 5
3422 5
3422 6
3423 6
3423 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3424 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3424 0
3425 0
3425 1
3426 1
3426 2
3427 2
3427 3
3428 3
3428 4
3429 4
3429 5
3430 5
3430 6
3431 6
3431
3432
3432 tests for 'remote()' predicate:
3433 tests for 'remote()' predicate:
3433 #. (csets in remote) (id) (remote)
3434 #. (csets in remote) (id) (remote)
3434 1. less than local current branch "default"
3435 1. less than local current branch "default"
3435 2. same with local specified "default"
3436 2. same with local specified "default"
3436 3. more than local specified specified
3437 3. more than local specified specified
3437
3438
3438 $ hg clone --quiet -U . ../remote3
3439 $ hg clone --quiet -U . ../remote3
3439 $ cd ../remote3
3440 $ cd ../remote3
3440 $ hg update -q 7
3441 $ hg update -q 7
3441 $ echo r > r
3442 $ echo r > r
3442 $ hg ci -Aqm 10
3443 $ hg ci -Aqm 10
3443 $ log 'remote()'
3444 $ log 'remote()'
3444 7
3445 7
3445 $ log 'remote("a-b-c-")'
3446 $ log 'remote("a-b-c-")'
3446 2
3447 2
3447 $ cd ../repo
3448 $ cd ../repo
3448 $ log 'remote(".a.b.c.", "../remote3")'
3449 $ log 'remote(".a.b.c.", "../remote3")'
3449
3450
3450 tests for concatenation of strings/symbols by "##"
3451 tests for concatenation of strings/symbols by "##"
3451
3452
3452 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3453 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3453 (_concat
3454 (_concat
3454 (_concat
3455 (_concat
3455 (_concat
3456 (_concat
3456 ('symbol', '278')
3457 ('symbol', '278')
3457 ('string', '5f5'))
3458 ('string', '5f5'))
3458 ('symbol', '1ee'))
3459 ('symbol', '1ee'))
3459 ('string', 'ce5'))
3460 ('string', 'ce5'))
3460 * concatenated:
3461 * concatenated:
3461 ('string', '2785f51eece5')
3462 ('string', '2785f51eece5')
3462 * set:
3463 * set:
3463 <baseset [0]>
3464 <baseset [0]>
3464 0
3465 0
3465
3466
3466 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3467 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3467 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3468 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3468 (func
3469 (func
3469 ('symbol', 'cat4')
3470 ('symbol', 'cat4')
3470 (list
3471 (list
3471 ('symbol', '278')
3472 ('symbol', '278')
3472 ('string', '5f5')
3473 ('string', '5f5')
3473 ('symbol', '1ee')
3474 ('symbol', '1ee')
3474 ('string', 'ce5')))
3475 ('string', 'ce5')))
3475 * expanded:
3476 * expanded:
3476 (_concat
3477 (_concat
3477 (_concat
3478 (_concat
3478 (_concat
3479 (_concat
3479 ('symbol', '278')
3480 ('symbol', '278')
3480 ('string', '5f5'))
3481 ('string', '5f5'))
3481 ('symbol', '1ee'))
3482 ('symbol', '1ee'))
3482 ('string', 'ce5'))
3483 ('string', 'ce5'))
3483 * concatenated:
3484 * concatenated:
3484 ('string', '2785f51eece5')
3485 ('string', '2785f51eece5')
3485 * set:
3486 * set:
3486 <baseset [0]>
3487 <baseset [0]>
3487 0
3488 0
3488
3489
3489 (check concatenation in alias nesting)
3490 (check concatenation in alias nesting)
3490
3491
3491 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3492 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3492 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3493 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3493 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3494 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3494 0
3495 0
3495
3496
3496 (check operator priority)
3497 (check operator priority)
3497
3498
3498 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3499 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3499 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3500 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3500 0
3501 0
3501 4
3502 4
3502
3503
3503 $ cd ..
3504 $ cd ..
3504
3505
3505 prepare repository that has "default" branches of multiple roots
3506 prepare repository that has "default" branches of multiple roots
3506
3507
3507 $ hg init namedbranch
3508 $ hg init namedbranch
3508 $ cd namedbranch
3509 $ cd namedbranch
3509
3510
3510 $ echo default0 >> a
3511 $ echo default0 >> a
3511 $ hg ci -Aqm0
3512 $ hg ci -Aqm0
3512 $ echo default1 >> a
3513 $ echo default1 >> a
3513 $ hg ci -m1
3514 $ hg ci -m1
3514
3515
3515 $ hg branch -q stable
3516 $ hg branch -q stable
3516 $ echo stable2 >> a
3517 $ echo stable2 >> a
3517 $ hg ci -m2
3518 $ hg ci -m2
3518 $ echo stable3 >> a
3519 $ echo stable3 >> a
3519 $ hg ci -m3
3520 $ hg ci -m3
3520
3521
3521 $ hg update -q null
3522 $ hg update -q null
3522 $ echo default4 >> a
3523 $ echo default4 >> a
3523 $ hg ci -Aqm4
3524 $ hg ci -Aqm4
3524 $ echo default5 >> a
3525 $ echo default5 >> a
3525 $ hg ci -m5
3526 $ hg ci -m5
3526
3527
3527 "null" revision belongs to "default" branch (issue4683)
3528 "null" revision belongs to "default" branch (issue4683)
3528
3529
3529 $ log 'branch(null)'
3530 $ log 'branch(null)'
3530 0
3531 0
3531 1
3532 1
3532 4
3533 4
3533 5
3534 5
3534
3535
3535 "null" revision belongs to "default" branch, but it shouldn't appear in set
3536 "null" revision belongs to "default" branch, but it shouldn't appear in set
3536 unless explicitly specified (issue4682)
3537 unless explicitly specified (issue4682)
3537
3538
3538 $ log 'children(branch(default))'
3539 $ log 'children(branch(default))'
3539 1
3540 1
3540 2
3541 2
3541 5
3542 5
3542
3543
3543 $ cd ..
3544 $ cd ..
3544
3545
3545 test author/desc/keyword in problematic encoding
3546 test author/desc/keyword in problematic encoding
3546 # unicode: cp932:
3547 # unicode: cp932:
3547 # u30A2 0x83 0x41(= 'A')
3548 # u30A2 0x83 0x41(= 'A')
3548 # u30C2 0x83 0x61(= 'a')
3549 # u30C2 0x83 0x61(= 'a')
3549
3550
3550 $ hg init problematicencoding
3551 $ hg init problematicencoding
3551 $ cd problematicencoding
3552 $ cd problematicencoding
3552
3553
3553 $ python > setup.sh <<EOF
3554 $ python > setup.sh <<EOF
3554 > print u'''
3555 > print u'''
3555 > echo a > text
3556 > echo a > text
3556 > hg add text
3557 > hg add text
3557 > hg --encoding utf-8 commit -u '\u30A2' -m none
3558 > hg --encoding utf-8 commit -u '\u30A2' -m none
3558 > echo b > text
3559 > echo b > text
3559 > hg --encoding utf-8 commit -u '\u30C2' -m none
3560 > hg --encoding utf-8 commit -u '\u30C2' -m none
3560 > echo c > text
3561 > echo c > text
3561 > hg --encoding utf-8 commit -u none -m '\u30A2'
3562 > hg --encoding utf-8 commit -u none -m '\u30A2'
3562 > echo d > text
3563 > echo d > text
3563 > hg --encoding utf-8 commit -u none -m '\u30C2'
3564 > hg --encoding utf-8 commit -u none -m '\u30C2'
3564 > '''.encode('utf-8')
3565 > '''.encode('utf-8')
3565 > EOF
3566 > EOF
3566 $ sh < setup.sh
3567 $ sh < setup.sh
3567
3568
3568 test in problematic encoding
3569 test in problematic encoding
3569 $ python > test.sh <<EOF
3570 $ python > test.sh <<EOF
3570 > print u'''
3571 > print u'''
3571 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3572 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3572 > echo ====
3573 > echo ====
3573 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3574 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3574 > echo ====
3575 > echo ====
3575 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3576 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3576 > echo ====
3577 > echo ====
3577 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3578 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3578 > echo ====
3579 > echo ====
3579 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3580 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3580 > echo ====
3581 > echo ====
3581 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3582 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3582 > '''.encode('cp932')
3583 > '''.encode('cp932')
3583 > EOF
3584 > EOF
3584 $ sh < test.sh
3585 $ sh < test.sh
3585 0
3586 0
3586 ====
3587 ====
3587 1
3588 1
3588 ====
3589 ====
3589 2
3590 2
3590 ====
3591 ====
3591 3
3592 3
3592 ====
3593 ====
3593 0
3594 0
3594 2
3595 2
3595 ====
3596 ====
3596 1
3597 1
3597 3
3598 3
3598
3599
3599 test error message of bad revset
3600 test error message of bad revset
3600 $ hg log -r 'foo\\'
3601 $ hg log -r 'foo\\'
3601 hg: parse error at 3: syntax error in revset 'foo\\'
3602 hg: parse error at 3: syntax error in revset 'foo\\'
3602 [255]
3603 [255]
3603
3604
3604 $ cd ..
3605 $ cd ..
3605
3606
3606 Test that revset predicate of extension isn't loaded at failure of
3607 Test that revset predicate of extension isn't loaded at failure of
3607 loading it
3608 loading it
3608
3609
3609 $ cd repo
3610 $ cd repo
3610
3611
3611 $ cat <<EOF > $TESTTMP/custompredicate.py
3612 $ cat <<EOF > $TESTTMP/custompredicate.py
3612 > from mercurial import error, registrar, revset
3613 > from mercurial import error, registrar, revset
3613 >
3614 >
3614 > revsetpredicate = registrar.revsetpredicate()
3615 > revsetpredicate = registrar.revsetpredicate()
3615 >
3616 >
3616 > @revsetpredicate('custom1()')
3617 > @revsetpredicate('custom1()')
3617 > def custom1(repo, subset, x):
3618 > def custom1(repo, subset, x):
3618 > return revset.baseset([1])
3619 > return revset.baseset([1])
3619 >
3620 >
3620 > raise error.Abort('intentional failure of loading extension')
3621 > raise error.Abort('intentional failure of loading extension')
3621 > EOF
3622 > EOF
3622 $ cat <<EOF > .hg/hgrc
3623 $ cat <<EOF > .hg/hgrc
3623 > [extensions]
3624 > [extensions]
3624 > custompredicate = $TESTTMP/custompredicate.py
3625 > custompredicate = $TESTTMP/custompredicate.py
3625 > EOF
3626 > EOF
3626
3627
3627 $ hg debugrevspec "custom1()"
3628 $ hg debugrevspec "custom1()"
3628 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3629 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3629 hg: parse error: unknown identifier: custom1
3630 hg: parse error: unknown identifier: custom1
3630 [255]
3631 [255]
3631
3632
3632 $ cd ..
3633 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now