##// END OF EJS Templates
revset: fix order of nested 'range' expression (BC)...
Yuya Nishihara -
r29944:5f56a3b9 default
parent child Browse files
Show More
@@ -1,3830 +1,3830 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11 import re
11 import re
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 destutil,
15 destutil,
16 encoding,
16 encoding,
17 error,
17 error,
18 hbisect,
18 hbisect,
19 match as matchmod,
19 match as matchmod,
20 node,
20 node,
21 obsolete as obsmod,
21 obsolete as obsmod,
22 parser,
22 parser,
23 pathutil,
23 pathutil,
24 phases,
24 phases,
25 registrar,
25 registrar,
26 repoview,
26 repoview,
27 util,
27 util,
28 )
28 )
29
29
30 def _revancestors(repo, revs, followfirst):
30 def _revancestors(repo, revs, followfirst):
31 """Like revlog.ancestors(), but supports followfirst."""
31 """Like revlog.ancestors(), but supports followfirst."""
32 if followfirst:
32 if followfirst:
33 cut = 1
33 cut = 1
34 else:
34 else:
35 cut = None
35 cut = None
36 cl = repo.changelog
36 cl = repo.changelog
37
37
38 def iterate():
38 def iterate():
39 revs.sort(reverse=True)
39 revs.sort(reverse=True)
40 irevs = iter(revs)
40 irevs = iter(revs)
41 h = []
41 h = []
42
42
43 inputrev = next(irevs, None)
43 inputrev = next(irevs, None)
44 if inputrev is not None:
44 if inputrev is not None:
45 heapq.heappush(h, -inputrev)
45 heapq.heappush(h, -inputrev)
46
46
47 seen = set()
47 seen = set()
48 while h:
48 while h:
49 current = -heapq.heappop(h)
49 current = -heapq.heappop(h)
50 if current == inputrev:
50 if current == inputrev:
51 inputrev = next(irevs, None)
51 inputrev = next(irevs, None)
52 if inputrev is not None:
52 if inputrev is not None:
53 heapq.heappush(h, -inputrev)
53 heapq.heappush(h, -inputrev)
54 if current not in seen:
54 if current not in seen:
55 seen.add(current)
55 seen.add(current)
56 yield current
56 yield current
57 for parent in cl.parentrevs(current)[:cut]:
57 for parent in cl.parentrevs(current)[:cut]:
58 if parent != node.nullrev:
58 if parent != node.nullrev:
59 heapq.heappush(h, -parent)
59 heapq.heappush(h, -parent)
60
60
61 return generatorset(iterate(), iterasc=False)
61 return generatorset(iterate(), iterasc=False)
62
62
63 def _revdescendants(repo, revs, followfirst):
63 def _revdescendants(repo, revs, followfirst):
64 """Like revlog.descendants() but supports followfirst."""
64 """Like revlog.descendants() but supports followfirst."""
65 if followfirst:
65 if followfirst:
66 cut = 1
66 cut = 1
67 else:
67 else:
68 cut = None
68 cut = None
69
69
70 def iterate():
70 def iterate():
71 cl = repo.changelog
71 cl = repo.changelog
72 # XXX this should be 'parentset.min()' assuming 'parentset' is a
72 # XXX this should be 'parentset.min()' assuming 'parentset' is a
73 # smartset (and if it is not, it should.)
73 # smartset (and if it is not, it should.)
74 first = min(revs)
74 first = min(revs)
75 nullrev = node.nullrev
75 nullrev = node.nullrev
76 if first == nullrev:
76 if first == nullrev:
77 # Are there nodes with a null first parent and a non-null
77 # Are there nodes with a null first parent and a non-null
78 # second one? Maybe. Do we care? Probably not.
78 # second one? Maybe. Do we care? Probably not.
79 for i in cl:
79 for i in cl:
80 yield i
80 yield i
81 else:
81 else:
82 seen = set(revs)
82 seen = set(revs)
83 for i in cl.revs(first + 1):
83 for i in cl.revs(first + 1):
84 for x in cl.parentrevs(i)[:cut]:
84 for x in cl.parentrevs(i)[:cut]:
85 if x != nullrev and x in seen:
85 if x != nullrev and x in seen:
86 seen.add(i)
86 seen.add(i)
87 yield i
87 yield i
88 break
88 break
89
89
90 return generatorset(iterate(), iterasc=True)
90 return generatorset(iterate(), iterasc=True)
91
91
92 def _reachablerootspure(repo, minroot, roots, heads, includepath):
92 def _reachablerootspure(repo, minroot, roots, heads, includepath):
93 """return (heads(::<roots> and ::<heads>))
93 """return (heads(::<roots> and ::<heads>))
94
94
95 If includepath is True, return (<roots>::<heads>)."""
95 If includepath is True, return (<roots>::<heads>)."""
96 if not roots:
96 if not roots:
97 return []
97 return []
98 parentrevs = repo.changelog.parentrevs
98 parentrevs = repo.changelog.parentrevs
99 roots = set(roots)
99 roots = set(roots)
100 visit = list(heads)
100 visit = list(heads)
101 reachable = set()
101 reachable = set()
102 seen = {}
102 seen = {}
103 # prefetch all the things! (because python is slow)
103 # prefetch all the things! (because python is slow)
104 reached = reachable.add
104 reached = reachable.add
105 dovisit = visit.append
105 dovisit = visit.append
106 nextvisit = visit.pop
106 nextvisit = visit.pop
107 # open-code the post-order traversal due to the tiny size of
107 # open-code the post-order traversal due to the tiny size of
108 # sys.getrecursionlimit()
108 # sys.getrecursionlimit()
109 while visit:
109 while visit:
110 rev = nextvisit()
110 rev = nextvisit()
111 if rev in roots:
111 if rev in roots:
112 reached(rev)
112 reached(rev)
113 if not includepath:
113 if not includepath:
114 continue
114 continue
115 parents = parentrevs(rev)
115 parents = parentrevs(rev)
116 seen[rev] = parents
116 seen[rev] = parents
117 for parent in parents:
117 for parent in parents:
118 if parent >= minroot and parent not in seen:
118 if parent >= minroot and parent not in seen:
119 dovisit(parent)
119 dovisit(parent)
120 if not reachable:
120 if not reachable:
121 return baseset()
121 return baseset()
122 if not includepath:
122 if not includepath:
123 return reachable
123 return reachable
124 for rev in sorted(seen):
124 for rev in sorted(seen):
125 for parent in seen[rev]:
125 for parent in seen[rev]:
126 if parent in reachable:
126 if parent in reachable:
127 reached(rev)
127 reached(rev)
128 return reachable
128 return reachable
129
129
130 def reachableroots(repo, roots, heads, includepath=False):
130 def reachableroots(repo, roots, heads, includepath=False):
131 """return (heads(::<roots> and ::<heads>))
131 """return (heads(::<roots> and ::<heads>))
132
132
133 If includepath is True, return (<roots>::<heads>)."""
133 If includepath is True, return (<roots>::<heads>)."""
134 if not roots:
134 if not roots:
135 return baseset()
135 return baseset()
136 minroot = roots.min()
136 minroot = roots.min()
137 roots = list(roots)
137 roots = list(roots)
138 heads = list(heads)
138 heads = list(heads)
139 try:
139 try:
140 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
140 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
141 except AttributeError:
141 except AttributeError:
142 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
142 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
143 revs = baseset(revs)
143 revs = baseset(revs)
144 revs.sort()
144 revs.sort()
145 return revs
145 return revs
146
146
147 elements = {
147 elements = {
148 # token-type: binding-strength, primary, prefix, infix, suffix
148 # token-type: binding-strength, primary, prefix, infix, suffix
149 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
149 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
150 "##": (20, None, None, ("_concat", 20), None),
150 "##": (20, None, None, ("_concat", 20), None),
151 "~": (18, None, None, ("ancestor", 18), None),
151 "~": (18, None, None, ("ancestor", 18), None),
152 "^": (18, None, None, ("parent", 18), "parentpost"),
152 "^": (18, None, None, ("parent", 18), "parentpost"),
153 "-": (5, None, ("negate", 19), ("minus", 5), None),
153 "-": (5, None, ("negate", 19), ("minus", 5), None),
154 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
154 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
155 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
155 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
156 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
156 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
157 "not": (10, None, ("not", 10), None, None),
157 "not": (10, None, ("not", 10), None, None),
158 "!": (10, None, ("not", 10), None, None),
158 "!": (10, None, ("not", 10), None, None),
159 "and": (5, None, None, ("and", 5), None),
159 "and": (5, None, None, ("and", 5), None),
160 "&": (5, None, None, ("and", 5), None),
160 "&": (5, None, None, ("and", 5), None),
161 "%": (5, None, None, ("only", 5), "onlypost"),
161 "%": (5, None, None, ("only", 5), "onlypost"),
162 "or": (4, None, None, ("or", 4), None),
162 "or": (4, None, None, ("or", 4), None),
163 "|": (4, None, None, ("or", 4), None),
163 "|": (4, None, None, ("or", 4), None),
164 "+": (4, None, None, ("or", 4), None),
164 "+": (4, None, None, ("or", 4), None),
165 "=": (3, None, None, ("keyvalue", 3), None),
165 "=": (3, None, None, ("keyvalue", 3), None),
166 ",": (2, None, None, ("list", 2), None),
166 ",": (2, None, None, ("list", 2), None),
167 ")": (0, None, None, None, None),
167 ")": (0, None, None, None, None),
168 "symbol": (0, "symbol", None, None, None),
168 "symbol": (0, "symbol", None, None, None),
169 "string": (0, "string", None, None, None),
169 "string": (0, "string", None, None, None),
170 "end": (0, None, None, None, None),
170 "end": (0, None, None, None, None),
171 }
171 }
172
172
173 keywords = set(['and', 'or', 'not'])
173 keywords = set(['and', 'or', 'not'])
174
174
175 # default set of valid characters for the initial letter of symbols
175 # default set of valid characters for the initial letter of symbols
176 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
176 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
177 if c.isalnum() or c in '._@' or ord(c) > 127)
177 if c.isalnum() or c in '._@' or ord(c) > 127)
178
178
179 # default set of valid characters for non-initial letters of symbols
179 # default set of valid characters for non-initial letters of symbols
180 _symletters = set(c for c in [chr(i) for i in xrange(256)]
180 _symletters = set(c for c in [chr(i) for i in xrange(256)]
181 if c.isalnum() or c in '-._/@' or ord(c) > 127)
181 if c.isalnum() or c in '-._/@' or ord(c) > 127)
182
182
183 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
183 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
184 '''
184 '''
185 Parse a revset statement into a stream of tokens
185 Parse a revset statement into a stream of tokens
186
186
187 ``syminitletters`` is the set of valid characters for the initial
187 ``syminitletters`` is the set of valid characters for the initial
188 letter of symbols.
188 letter of symbols.
189
189
190 By default, character ``c`` is recognized as valid for initial
190 By default, character ``c`` is recognized as valid for initial
191 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
191 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
192
192
193 ``symletters`` is the set of valid characters for non-initial
193 ``symletters`` is the set of valid characters for non-initial
194 letters of symbols.
194 letters of symbols.
195
195
196 By default, character ``c`` is recognized as valid for non-initial
196 By default, character ``c`` is recognized as valid for non-initial
197 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
197 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
198
198
199 Check that @ is a valid unquoted token character (issue3686):
199 Check that @ is a valid unquoted token character (issue3686):
200 >>> list(tokenize("@::"))
200 >>> list(tokenize("@::"))
201 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
201 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
202
202
203 '''
203 '''
204 if syminitletters is None:
204 if syminitletters is None:
205 syminitletters = _syminitletters
205 syminitletters = _syminitletters
206 if symletters is None:
206 if symletters is None:
207 symletters = _symletters
207 symletters = _symletters
208
208
209 if program and lookup:
209 if program and lookup:
210 # attempt to parse old-style ranges first to deal with
210 # attempt to parse old-style ranges first to deal with
211 # things like old-tag which contain query metacharacters
211 # things like old-tag which contain query metacharacters
212 parts = program.split(':', 1)
212 parts = program.split(':', 1)
213 if all(lookup(sym) for sym in parts if sym):
213 if all(lookup(sym) for sym in parts if sym):
214 if parts[0]:
214 if parts[0]:
215 yield ('symbol', parts[0], 0)
215 yield ('symbol', parts[0], 0)
216 if len(parts) > 1:
216 if len(parts) > 1:
217 s = len(parts[0])
217 s = len(parts[0])
218 yield (':', None, s)
218 yield (':', None, s)
219 if parts[1]:
219 if parts[1]:
220 yield ('symbol', parts[1], s + 1)
220 yield ('symbol', parts[1], s + 1)
221 yield ('end', None, len(program))
221 yield ('end', None, len(program))
222 return
222 return
223
223
224 pos, l = 0, len(program)
224 pos, l = 0, len(program)
225 while pos < l:
225 while pos < l:
226 c = program[pos]
226 c = program[pos]
227 if c.isspace(): # skip inter-token whitespace
227 if c.isspace(): # skip inter-token whitespace
228 pass
228 pass
229 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
229 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
230 yield ('::', None, pos)
230 yield ('::', None, pos)
231 pos += 1 # skip ahead
231 pos += 1 # skip ahead
232 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
232 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
233 yield ('..', None, pos)
233 yield ('..', None, pos)
234 pos += 1 # skip ahead
234 pos += 1 # skip ahead
235 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
235 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
236 yield ('##', None, pos)
236 yield ('##', None, pos)
237 pos += 1 # skip ahead
237 pos += 1 # skip ahead
238 elif c in "():=,-|&+!~^%": # handle simple operators
238 elif c in "():=,-|&+!~^%": # handle simple operators
239 yield (c, None, pos)
239 yield (c, None, pos)
240 elif (c in '"\'' or c == 'r' and
240 elif (c in '"\'' or c == 'r' and
241 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
241 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
242 if c == 'r':
242 if c == 'r':
243 pos += 1
243 pos += 1
244 c = program[pos]
244 c = program[pos]
245 decode = lambda x: x
245 decode = lambda x: x
246 else:
246 else:
247 decode = parser.unescapestr
247 decode = parser.unescapestr
248 pos += 1
248 pos += 1
249 s = pos
249 s = pos
250 while pos < l: # find closing quote
250 while pos < l: # find closing quote
251 d = program[pos]
251 d = program[pos]
252 if d == '\\': # skip over escaped characters
252 if d == '\\': # skip over escaped characters
253 pos += 2
253 pos += 2
254 continue
254 continue
255 if d == c:
255 if d == c:
256 yield ('string', decode(program[s:pos]), s)
256 yield ('string', decode(program[s:pos]), s)
257 break
257 break
258 pos += 1
258 pos += 1
259 else:
259 else:
260 raise error.ParseError(_("unterminated string"), s)
260 raise error.ParseError(_("unterminated string"), s)
261 # gather up a symbol/keyword
261 # gather up a symbol/keyword
262 elif c in syminitletters:
262 elif c in syminitletters:
263 s = pos
263 s = pos
264 pos += 1
264 pos += 1
265 while pos < l: # find end of symbol
265 while pos < l: # find end of symbol
266 d = program[pos]
266 d = program[pos]
267 if d not in symletters:
267 if d not in symletters:
268 break
268 break
269 if d == '.' and program[pos - 1] == '.': # special case for ..
269 if d == '.' and program[pos - 1] == '.': # special case for ..
270 pos -= 1
270 pos -= 1
271 break
271 break
272 pos += 1
272 pos += 1
273 sym = program[s:pos]
273 sym = program[s:pos]
274 if sym in keywords: # operator keywords
274 if sym in keywords: # operator keywords
275 yield (sym, None, s)
275 yield (sym, None, s)
276 elif '-' in sym:
276 elif '-' in sym:
277 # some jerk gave us foo-bar-baz, try to check if it's a symbol
277 # some jerk gave us foo-bar-baz, try to check if it's a symbol
278 if lookup and lookup(sym):
278 if lookup and lookup(sym):
279 # looks like a real symbol
279 # looks like a real symbol
280 yield ('symbol', sym, s)
280 yield ('symbol', sym, s)
281 else:
281 else:
282 # looks like an expression
282 # looks like an expression
283 parts = sym.split('-')
283 parts = sym.split('-')
284 for p in parts[:-1]:
284 for p in parts[:-1]:
285 if p: # possible consecutive -
285 if p: # possible consecutive -
286 yield ('symbol', p, s)
286 yield ('symbol', p, s)
287 s += len(p)
287 s += len(p)
288 yield ('-', None, pos)
288 yield ('-', None, pos)
289 s += 1
289 s += 1
290 if parts[-1]: # possible trailing -
290 if parts[-1]: # possible trailing -
291 yield ('symbol', parts[-1], s)
291 yield ('symbol', parts[-1], s)
292 else:
292 else:
293 yield ('symbol', sym, s)
293 yield ('symbol', sym, s)
294 pos -= 1
294 pos -= 1
295 else:
295 else:
296 raise error.ParseError(_("syntax error in revset '%s'") %
296 raise error.ParseError(_("syntax error in revset '%s'") %
297 program, pos)
297 program, pos)
298 pos += 1
298 pos += 1
299 yield ('end', None, pos)
299 yield ('end', None, pos)
300
300
301 # helpers
301 # helpers
302
302
303 def getsymbol(x):
303 def getsymbol(x):
304 if x and x[0] == 'symbol':
304 if x and x[0] == 'symbol':
305 return x[1]
305 return x[1]
306 raise error.ParseError(_('not a symbol'))
306 raise error.ParseError(_('not a symbol'))
307
307
308 def getstring(x, err):
308 def getstring(x, err):
309 if x and (x[0] == 'string' or x[0] == 'symbol'):
309 if x and (x[0] == 'string' or x[0] == 'symbol'):
310 return x[1]
310 return x[1]
311 raise error.ParseError(err)
311 raise error.ParseError(err)
312
312
313 def getlist(x):
313 def getlist(x):
314 if not x:
314 if not x:
315 return []
315 return []
316 if x[0] == 'list':
316 if x[0] == 'list':
317 return list(x[1:])
317 return list(x[1:])
318 return [x]
318 return [x]
319
319
320 def getargs(x, min, max, err):
320 def getargs(x, min, max, err):
321 l = getlist(x)
321 l = getlist(x)
322 if len(l) < min or (max >= 0 and len(l) > max):
322 if len(l) < min or (max >= 0 and len(l) > max):
323 raise error.ParseError(err)
323 raise error.ParseError(err)
324 return l
324 return l
325
325
326 def getargsdict(x, funcname, keys):
326 def getargsdict(x, funcname, keys):
327 return parser.buildargsdict(getlist(x), funcname, keys.split(),
327 return parser.buildargsdict(getlist(x), funcname, keys.split(),
328 keyvaluenode='keyvalue', keynode='symbol')
328 keyvaluenode='keyvalue', keynode='symbol')
329
329
330 def getset(repo, subset, x):
330 def getset(repo, subset, x):
331 if not x:
331 if not x:
332 raise error.ParseError(_("missing argument"))
332 raise error.ParseError(_("missing argument"))
333 s = methods[x[0]](repo, subset, *x[1:])
333 s = methods[x[0]](repo, subset, *x[1:])
334 if util.safehasattr(s, 'isascending'):
334 if util.safehasattr(s, 'isascending'):
335 return s
335 return s
336 # else case should not happen, because all non-func are internal,
336 # else case should not happen, because all non-func are internal,
337 # ignoring for now.
337 # ignoring for now.
338 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
338 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
339 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
339 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
340 % x[1][1],
340 % x[1][1],
341 '3.9')
341 '3.9')
342 return baseset(s)
342 return baseset(s)
343
343
344 def _getrevsource(repo, r):
344 def _getrevsource(repo, r):
345 extra = repo[r].extra()
345 extra = repo[r].extra()
346 for label in ('source', 'transplant_source', 'rebase_source'):
346 for label in ('source', 'transplant_source', 'rebase_source'):
347 if label in extra:
347 if label in extra:
348 try:
348 try:
349 return repo[extra[label]].rev()
349 return repo[extra[label]].rev()
350 except error.RepoLookupError:
350 except error.RepoLookupError:
351 pass
351 pass
352 return None
352 return None
353
353
354 # operator methods
354 # operator methods
355
355
356 def stringset(repo, subset, x):
356 def stringset(repo, subset, x):
357 x = repo[x].rev()
357 x = repo[x].rev()
358 if (x in subset
358 if (x in subset
359 or x == node.nullrev and isinstance(subset, fullreposet)):
359 or x == node.nullrev and isinstance(subset, fullreposet)):
360 return baseset([x])
360 return baseset([x])
361 return baseset()
361 return baseset()
362
362
363 def rangeset(repo, subset, x, y, order):
363 def rangeset(repo, subset, x, y, order):
364 m = getset(repo, fullreposet(repo), x)
364 m = getset(repo, fullreposet(repo), x)
365 n = getset(repo, fullreposet(repo), y)
365 n = getset(repo, fullreposet(repo), y)
366
366
367 if not m or not n:
367 if not m or not n:
368 return baseset()
368 return baseset()
369 m, n = m.first(), n.last()
369 m, n = m.first(), n.last()
370
370
371 if m == n:
371 if m == n:
372 r = baseset([m])
372 r = baseset([m])
373 elif n == node.wdirrev:
373 elif n == node.wdirrev:
374 r = spanset(repo, m, len(repo)) + baseset([n])
374 r = spanset(repo, m, len(repo)) + baseset([n])
375 elif m == node.wdirrev:
375 elif m == node.wdirrev:
376 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
376 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
377 elif m < n:
377 elif m < n:
378 r = spanset(repo, m, n + 1)
378 r = spanset(repo, m, n + 1)
379 else:
379 else:
380 r = spanset(repo, m, n - 1)
380 r = spanset(repo, m, n - 1)
381 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
381
382 # necessary to ensure we preserve the order in subset.
382 if order == defineorder:
383 #
383 return r & subset
384 # This has performance implication, carrying the sorting over when possible
384 else:
385 # would be more efficient.
385 # carrying the sorting over when possible would be more efficient
386 return r & subset
386 return subset & r
387
387
388 def dagrange(repo, subset, x, y, order):
388 def dagrange(repo, subset, x, y, order):
389 r = fullreposet(repo)
389 r = fullreposet(repo)
390 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
390 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
391 includepath=True)
391 includepath=True)
392 return subset & xs
392 return subset & xs
393
393
394 def andset(repo, subset, x, y, order):
394 def andset(repo, subset, x, y, order):
395 return getset(repo, getset(repo, subset, x), y)
395 return getset(repo, getset(repo, subset, x), y)
396
396
397 def differenceset(repo, subset, x, y, order):
397 def differenceset(repo, subset, x, y, order):
398 return getset(repo, subset, x) - getset(repo, subset, y)
398 return getset(repo, subset, x) - getset(repo, subset, y)
399
399
400 def _orsetlist(repo, subset, xs):
400 def _orsetlist(repo, subset, xs):
401 assert xs
401 assert xs
402 if len(xs) == 1:
402 if len(xs) == 1:
403 return getset(repo, subset, xs[0])
403 return getset(repo, subset, xs[0])
404 p = len(xs) // 2
404 p = len(xs) // 2
405 a = _orsetlist(repo, subset, xs[:p])
405 a = _orsetlist(repo, subset, xs[:p])
406 b = _orsetlist(repo, subset, xs[p:])
406 b = _orsetlist(repo, subset, xs[p:])
407 return a + b
407 return a + b
408
408
409 def orset(repo, subset, x, order):
409 def orset(repo, subset, x, order):
410 xs = getlist(x)
410 xs = getlist(x)
411 if order == followorder:
411 if order == followorder:
412 # slow path to take the subset order
412 # slow path to take the subset order
413 return subset & _orsetlist(repo, fullreposet(repo), xs)
413 return subset & _orsetlist(repo, fullreposet(repo), xs)
414 else:
414 else:
415 return _orsetlist(repo, subset, xs)
415 return _orsetlist(repo, subset, xs)
416
416
417 def notset(repo, subset, x, order):
417 def notset(repo, subset, x, order):
418 return subset - getset(repo, subset, x)
418 return subset - getset(repo, subset, x)
419
419
420 def listset(repo, subset, *xs):
420 def listset(repo, subset, *xs):
421 raise error.ParseError(_("can't use a list in this context"),
421 raise error.ParseError(_("can't use a list in this context"),
422 hint=_('see hg help "revsets.x or y"'))
422 hint=_('see hg help "revsets.x or y"'))
423
423
424 def keyvaluepair(repo, subset, k, v):
424 def keyvaluepair(repo, subset, k, v):
425 raise error.ParseError(_("can't use a key-value pair in this context"))
425 raise error.ParseError(_("can't use a key-value pair in this context"))
426
426
427 def func(repo, subset, a, b, order):
427 def func(repo, subset, a, b, order):
428 f = getsymbol(a)
428 f = getsymbol(a)
429 if f in symbols:
429 if f in symbols:
430 fn = symbols[f]
430 fn = symbols[f]
431 if getattr(fn, '_takeorder', False):
431 if getattr(fn, '_takeorder', False):
432 return fn(repo, subset, b, order)
432 return fn(repo, subset, b, order)
433 return fn(repo, subset, b)
433 return fn(repo, subset, b)
434
434
435 keep = lambda fn: getattr(fn, '__doc__', None) is not None
435 keep = lambda fn: getattr(fn, '__doc__', None) is not None
436
436
437 syms = [s for (s, fn) in symbols.items() if keep(fn)]
437 syms = [s for (s, fn) in symbols.items() if keep(fn)]
438 raise error.UnknownIdentifier(f, syms)
438 raise error.UnknownIdentifier(f, syms)
439
439
440 # functions
440 # functions
441
441
442 # symbols are callables like:
442 # symbols are callables like:
443 # fn(repo, subset, x)
443 # fn(repo, subset, x)
444 # with:
444 # with:
445 # repo - current repository instance
445 # repo - current repository instance
446 # subset - of revisions to be examined
446 # subset - of revisions to be examined
447 # x - argument in tree form
447 # x - argument in tree form
448 symbols = {}
448 symbols = {}
449
449
450 # symbols which can't be used for a DoS attack for any given input
450 # symbols which can't be used for a DoS attack for any given input
451 # (e.g. those which accept regexes as plain strings shouldn't be included)
451 # (e.g. those which accept regexes as plain strings shouldn't be included)
452 # functions that just return a lot of changesets (like all) don't count here
452 # functions that just return a lot of changesets (like all) don't count here
453 safesymbols = set()
453 safesymbols = set()
454
454
455 predicate = registrar.revsetpredicate()
455 predicate = registrar.revsetpredicate()
456
456
457 @predicate('_destupdate')
457 @predicate('_destupdate')
458 def _destupdate(repo, subset, x):
458 def _destupdate(repo, subset, x):
459 # experimental revset for update destination
459 # experimental revset for update destination
460 args = getargsdict(x, 'limit', 'clean check')
460 args = getargsdict(x, 'limit', 'clean check')
461 return subset & baseset([destutil.destupdate(repo, **args)[0]])
461 return subset & baseset([destutil.destupdate(repo, **args)[0]])
462
462
463 @predicate('_destmerge')
463 @predicate('_destmerge')
464 def _destmerge(repo, subset, x):
464 def _destmerge(repo, subset, x):
465 # experimental revset for merge destination
465 # experimental revset for merge destination
466 sourceset = None
466 sourceset = None
467 if x is not None:
467 if x is not None:
468 sourceset = getset(repo, fullreposet(repo), x)
468 sourceset = getset(repo, fullreposet(repo), x)
469 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
469 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
470
470
471 @predicate('adds(pattern)', safe=True)
471 @predicate('adds(pattern)', safe=True)
472 def adds(repo, subset, x):
472 def adds(repo, subset, x):
473 """Changesets that add a file matching pattern.
473 """Changesets that add a file matching pattern.
474
474
475 The pattern without explicit kind like ``glob:`` is expected to be
475 The pattern without explicit kind like ``glob:`` is expected to be
476 relative to the current directory and match against a file or a
476 relative to the current directory and match against a file or a
477 directory.
477 directory.
478 """
478 """
479 # i18n: "adds" is a keyword
479 # i18n: "adds" is a keyword
480 pat = getstring(x, _("adds requires a pattern"))
480 pat = getstring(x, _("adds requires a pattern"))
481 return checkstatus(repo, subset, pat, 1)
481 return checkstatus(repo, subset, pat, 1)
482
482
483 @predicate('ancestor(*changeset)', safe=True)
483 @predicate('ancestor(*changeset)', safe=True)
484 def ancestor(repo, subset, x):
484 def ancestor(repo, subset, x):
485 """A greatest common ancestor of the changesets.
485 """A greatest common ancestor of the changesets.
486
486
487 Accepts 0 or more changesets.
487 Accepts 0 or more changesets.
488 Will return empty list when passed no args.
488 Will return empty list when passed no args.
489 Greatest common ancestor of a single changeset is that changeset.
489 Greatest common ancestor of a single changeset is that changeset.
490 """
490 """
491 # i18n: "ancestor" is a keyword
491 # i18n: "ancestor" is a keyword
492 l = getlist(x)
492 l = getlist(x)
493 rl = fullreposet(repo)
493 rl = fullreposet(repo)
494 anc = None
494 anc = None
495
495
496 # (getset(repo, rl, i) for i in l) generates a list of lists
496 # (getset(repo, rl, i) for i in l) generates a list of lists
497 for revs in (getset(repo, rl, i) for i in l):
497 for revs in (getset(repo, rl, i) for i in l):
498 for r in revs:
498 for r in revs:
499 if anc is None:
499 if anc is None:
500 anc = repo[r]
500 anc = repo[r]
501 else:
501 else:
502 anc = anc.ancestor(repo[r])
502 anc = anc.ancestor(repo[r])
503
503
504 if anc is not None and anc.rev() in subset:
504 if anc is not None and anc.rev() in subset:
505 return baseset([anc.rev()])
505 return baseset([anc.rev()])
506 return baseset()
506 return baseset()
507
507
508 def _ancestors(repo, subset, x, followfirst=False):
508 def _ancestors(repo, subset, x, followfirst=False):
509 heads = getset(repo, fullreposet(repo), x)
509 heads = getset(repo, fullreposet(repo), x)
510 if not heads:
510 if not heads:
511 return baseset()
511 return baseset()
512 s = _revancestors(repo, heads, followfirst)
512 s = _revancestors(repo, heads, followfirst)
513 return subset & s
513 return subset & s
514
514
515 @predicate('ancestors(set)', safe=True)
515 @predicate('ancestors(set)', safe=True)
516 def ancestors(repo, subset, x):
516 def ancestors(repo, subset, x):
517 """Changesets that are ancestors of a changeset in set.
517 """Changesets that are ancestors of a changeset in set.
518 """
518 """
519 return _ancestors(repo, subset, x)
519 return _ancestors(repo, subset, x)
520
520
521 @predicate('_firstancestors', safe=True)
521 @predicate('_firstancestors', safe=True)
522 def _firstancestors(repo, subset, x):
522 def _firstancestors(repo, subset, x):
523 # ``_firstancestors(set)``
523 # ``_firstancestors(set)``
524 # Like ``ancestors(set)`` but follows only the first parents.
524 # Like ``ancestors(set)`` but follows only the first parents.
525 return _ancestors(repo, subset, x, followfirst=True)
525 return _ancestors(repo, subset, x, followfirst=True)
526
526
527 def ancestorspec(repo, subset, x, n, order):
527 def ancestorspec(repo, subset, x, n, order):
528 """``set~n``
528 """``set~n``
529 Changesets that are the Nth ancestor (first parents only) of a changeset
529 Changesets that are the Nth ancestor (first parents only) of a changeset
530 in set.
530 in set.
531 """
531 """
532 try:
532 try:
533 n = int(n[1])
533 n = int(n[1])
534 except (TypeError, ValueError):
534 except (TypeError, ValueError):
535 raise error.ParseError(_("~ expects a number"))
535 raise error.ParseError(_("~ expects a number"))
536 ps = set()
536 ps = set()
537 cl = repo.changelog
537 cl = repo.changelog
538 for r in getset(repo, fullreposet(repo), x):
538 for r in getset(repo, fullreposet(repo), x):
539 for i in range(n):
539 for i in range(n):
540 r = cl.parentrevs(r)[0]
540 r = cl.parentrevs(r)[0]
541 ps.add(r)
541 ps.add(r)
542 return subset & ps
542 return subset & ps
543
543
544 @predicate('author(string)', safe=True)
544 @predicate('author(string)', safe=True)
545 def author(repo, subset, x):
545 def author(repo, subset, x):
546 """Alias for ``user(string)``.
546 """Alias for ``user(string)``.
547 """
547 """
548 # i18n: "author" is a keyword
548 # i18n: "author" is a keyword
549 n = encoding.lower(getstring(x, _("author requires a string")))
549 n = encoding.lower(getstring(x, _("author requires a string")))
550 kind, pattern, matcher = _substringmatcher(n)
550 kind, pattern, matcher = _substringmatcher(n)
551 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
551 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
552 condrepr=('<user %r>', n))
552 condrepr=('<user %r>', n))
553
553
554 @predicate('bisect(string)', safe=True)
554 @predicate('bisect(string)', safe=True)
555 def bisect(repo, subset, x):
555 def bisect(repo, subset, x):
556 """Changesets marked in the specified bisect status:
556 """Changesets marked in the specified bisect status:
557
557
558 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
558 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
559 - ``goods``, ``bads`` : csets topologically good/bad
559 - ``goods``, ``bads`` : csets topologically good/bad
560 - ``range`` : csets taking part in the bisection
560 - ``range`` : csets taking part in the bisection
561 - ``pruned`` : csets that are goods, bads or skipped
561 - ``pruned`` : csets that are goods, bads or skipped
562 - ``untested`` : csets whose fate is yet unknown
562 - ``untested`` : csets whose fate is yet unknown
563 - ``ignored`` : csets ignored due to DAG topology
563 - ``ignored`` : csets ignored due to DAG topology
564 - ``current`` : the cset currently being bisected
564 - ``current`` : the cset currently being bisected
565 """
565 """
566 # i18n: "bisect" is a keyword
566 # i18n: "bisect" is a keyword
567 status = getstring(x, _("bisect requires a string")).lower()
567 status = getstring(x, _("bisect requires a string")).lower()
568 state = set(hbisect.get(repo, status))
568 state = set(hbisect.get(repo, status))
569 return subset & state
569 return subset & state
570
570
571 # Backward-compatibility
571 # Backward-compatibility
572 # - no help entry so that we do not advertise it any more
572 # - no help entry so that we do not advertise it any more
573 @predicate('bisected', safe=True)
573 @predicate('bisected', safe=True)
574 def bisected(repo, subset, x):
574 def bisected(repo, subset, x):
575 return bisect(repo, subset, x)
575 return bisect(repo, subset, x)
576
576
577 @predicate('bookmark([name])', safe=True)
577 @predicate('bookmark([name])', safe=True)
578 def bookmark(repo, subset, x):
578 def bookmark(repo, subset, x):
579 """The named bookmark or all bookmarks.
579 """The named bookmark or all bookmarks.
580
580
581 If `name` starts with `re:`, the remainder of the name is treated as
581 If `name` starts with `re:`, the remainder of the name is treated as
582 a regular expression. To match a bookmark that actually starts with `re:`,
582 a regular expression. To match a bookmark that actually starts with `re:`,
583 use the prefix `literal:`.
583 use the prefix `literal:`.
584 """
584 """
585 # i18n: "bookmark" is a keyword
585 # i18n: "bookmark" is a keyword
586 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
586 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
587 if args:
587 if args:
588 bm = getstring(args[0],
588 bm = getstring(args[0],
589 # i18n: "bookmark" is a keyword
589 # i18n: "bookmark" is a keyword
590 _('the argument to bookmark must be a string'))
590 _('the argument to bookmark must be a string'))
591 kind, pattern, matcher = util.stringmatcher(bm)
591 kind, pattern, matcher = util.stringmatcher(bm)
592 bms = set()
592 bms = set()
593 if kind == 'literal':
593 if kind == 'literal':
594 bmrev = repo._bookmarks.get(pattern, None)
594 bmrev = repo._bookmarks.get(pattern, None)
595 if not bmrev:
595 if not bmrev:
596 raise error.RepoLookupError(_("bookmark '%s' does not exist")
596 raise error.RepoLookupError(_("bookmark '%s' does not exist")
597 % pattern)
597 % pattern)
598 bms.add(repo[bmrev].rev())
598 bms.add(repo[bmrev].rev())
599 else:
599 else:
600 matchrevs = set()
600 matchrevs = set()
601 for name, bmrev in repo._bookmarks.iteritems():
601 for name, bmrev in repo._bookmarks.iteritems():
602 if matcher(name):
602 if matcher(name):
603 matchrevs.add(bmrev)
603 matchrevs.add(bmrev)
604 if not matchrevs:
604 if not matchrevs:
605 raise error.RepoLookupError(_("no bookmarks exist"
605 raise error.RepoLookupError(_("no bookmarks exist"
606 " that match '%s'") % pattern)
606 " that match '%s'") % pattern)
607 for bmrev in matchrevs:
607 for bmrev in matchrevs:
608 bms.add(repo[bmrev].rev())
608 bms.add(repo[bmrev].rev())
609 else:
609 else:
610 bms = set([repo[r].rev()
610 bms = set([repo[r].rev()
611 for r in repo._bookmarks.values()])
611 for r in repo._bookmarks.values()])
612 bms -= set([node.nullrev])
612 bms -= set([node.nullrev])
613 return subset & bms
613 return subset & bms
614
614
615 @predicate('branch(string or set)', safe=True)
615 @predicate('branch(string or set)', safe=True)
616 def branch(repo, subset, x):
616 def branch(repo, subset, x):
617 """
617 """
618 All changesets belonging to the given branch or the branches of the given
618 All changesets belonging to the given branch or the branches of the given
619 changesets.
619 changesets.
620
620
621 If `string` starts with `re:`, the remainder of the name is treated as
621 If `string` starts with `re:`, the remainder of the name is treated as
622 a regular expression. To match a branch that actually starts with `re:`,
622 a regular expression. To match a branch that actually starts with `re:`,
623 use the prefix `literal:`.
623 use the prefix `literal:`.
624 """
624 """
625 getbi = repo.revbranchcache().branchinfo
625 getbi = repo.revbranchcache().branchinfo
626
626
627 try:
627 try:
628 b = getstring(x, '')
628 b = getstring(x, '')
629 except error.ParseError:
629 except error.ParseError:
630 # not a string, but another revspec, e.g. tip()
630 # not a string, but another revspec, e.g. tip()
631 pass
631 pass
632 else:
632 else:
633 kind, pattern, matcher = util.stringmatcher(b)
633 kind, pattern, matcher = util.stringmatcher(b)
634 if kind == 'literal':
634 if kind == 'literal':
635 # note: falls through to the revspec case if no branch with
635 # note: falls through to the revspec case if no branch with
636 # this name exists and pattern kind is not specified explicitly
636 # this name exists and pattern kind is not specified explicitly
637 if pattern in repo.branchmap():
637 if pattern in repo.branchmap():
638 return subset.filter(lambda r: matcher(getbi(r)[0]),
638 return subset.filter(lambda r: matcher(getbi(r)[0]),
639 condrepr=('<branch %r>', b))
639 condrepr=('<branch %r>', b))
640 if b.startswith('literal:'):
640 if b.startswith('literal:'):
641 raise error.RepoLookupError(_("branch '%s' does not exist")
641 raise error.RepoLookupError(_("branch '%s' does not exist")
642 % pattern)
642 % pattern)
643 else:
643 else:
644 return subset.filter(lambda r: matcher(getbi(r)[0]),
644 return subset.filter(lambda r: matcher(getbi(r)[0]),
645 condrepr=('<branch %r>', b))
645 condrepr=('<branch %r>', b))
646
646
647 s = getset(repo, fullreposet(repo), x)
647 s = getset(repo, fullreposet(repo), x)
648 b = set()
648 b = set()
649 for r in s:
649 for r in s:
650 b.add(getbi(r)[0])
650 b.add(getbi(r)[0])
651 c = s.__contains__
651 c = s.__contains__
652 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
652 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
653 condrepr=lambda: '<branch %r>' % sorted(b))
653 condrepr=lambda: '<branch %r>' % sorted(b))
654
654
655 @predicate('bumped()', safe=True)
655 @predicate('bumped()', safe=True)
656 def bumped(repo, subset, x):
656 def bumped(repo, subset, x):
657 """Mutable changesets marked as successors of public changesets.
657 """Mutable changesets marked as successors of public changesets.
658
658
659 Only non-public and non-obsolete changesets can be `bumped`.
659 Only non-public and non-obsolete changesets can be `bumped`.
660 """
660 """
661 # i18n: "bumped" is a keyword
661 # i18n: "bumped" is a keyword
662 getargs(x, 0, 0, _("bumped takes no arguments"))
662 getargs(x, 0, 0, _("bumped takes no arguments"))
663 bumped = obsmod.getrevs(repo, 'bumped')
663 bumped = obsmod.getrevs(repo, 'bumped')
664 return subset & bumped
664 return subset & bumped
665
665
666 @predicate('bundle()', safe=True)
666 @predicate('bundle()', safe=True)
667 def bundle(repo, subset, x):
667 def bundle(repo, subset, x):
668 """Changesets in the bundle.
668 """Changesets in the bundle.
669
669
670 Bundle must be specified by the -R option."""
670 Bundle must be specified by the -R option."""
671
671
672 try:
672 try:
673 bundlerevs = repo.changelog.bundlerevs
673 bundlerevs = repo.changelog.bundlerevs
674 except AttributeError:
674 except AttributeError:
675 raise error.Abort(_("no bundle provided - specify with -R"))
675 raise error.Abort(_("no bundle provided - specify with -R"))
676 return subset & bundlerevs
676 return subset & bundlerevs
677
677
678 def checkstatus(repo, subset, pat, field):
678 def checkstatus(repo, subset, pat, field):
679 hasset = matchmod.patkind(pat) == 'set'
679 hasset = matchmod.patkind(pat) == 'set'
680
680
681 mcache = [None]
681 mcache = [None]
682 def matches(x):
682 def matches(x):
683 c = repo[x]
683 c = repo[x]
684 if not mcache[0] or hasset:
684 if not mcache[0] or hasset:
685 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
685 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
686 m = mcache[0]
686 m = mcache[0]
687 fname = None
687 fname = None
688 if not m.anypats() and len(m.files()) == 1:
688 if not m.anypats() and len(m.files()) == 1:
689 fname = m.files()[0]
689 fname = m.files()[0]
690 if fname is not None:
690 if fname is not None:
691 if fname not in c.files():
691 if fname not in c.files():
692 return False
692 return False
693 else:
693 else:
694 for f in c.files():
694 for f in c.files():
695 if m(f):
695 if m(f):
696 break
696 break
697 else:
697 else:
698 return False
698 return False
699 files = repo.status(c.p1().node(), c.node())[field]
699 files = repo.status(c.p1().node(), c.node())[field]
700 if fname is not None:
700 if fname is not None:
701 if fname in files:
701 if fname in files:
702 return True
702 return True
703 else:
703 else:
704 for f in files:
704 for f in files:
705 if m(f):
705 if m(f):
706 return True
706 return True
707
707
708 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
708 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
709
709
710 def _children(repo, subset, parentset):
710 def _children(repo, subset, parentset):
711 if not parentset:
711 if not parentset:
712 return baseset()
712 return baseset()
713 cs = set()
713 cs = set()
714 pr = repo.changelog.parentrevs
714 pr = repo.changelog.parentrevs
715 minrev = parentset.min()
715 minrev = parentset.min()
716 for r in subset:
716 for r in subset:
717 if r <= minrev:
717 if r <= minrev:
718 continue
718 continue
719 for p in pr(r):
719 for p in pr(r):
720 if p in parentset:
720 if p in parentset:
721 cs.add(r)
721 cs.add(r)
722 return baseset(cs)
722 return baseset(cs)
723
723
724 @predicate('children(set)', safe=True)
724 @predicate('children(set)', safe=True)
725 def children(repo, subset, x):
725 def children(repo, subset, x):
726 """Child changesets of changesets in set.
726 """Child changesets of changesets in set.
727 """
727 """
728 s = getset(repo, fullreposet(repo), x)
728 s = getset(repo, fullreposet(repo), x)
729 cs = _children(repo, subset, s)
729 cs = _children(repo, subset, s)
730 return subset & cs
730 return subset & cs
731
731
732 @predicate('closed()', safe=True)
732 @predicate('closed()', safe=True)
733 def closed(repo, subset, x):
733 def closed(repo, subset, x):
734 """Changeset is closed.
734 """Changeset is closed.
735 """
735 """
736 # i18n: "closed" is a keyword
736 # i18n: "closed" is a keyword
737 getargs(x, 0, 0, _("closed takes no arguments"))
737 getargs(x, 0, 0, _("closed takes no arguments"))
738 return subset.filter(lambda r: repo[r].closesbranch(),
738 return subset.filter(lambda r: repo[r].closesbranch(),
739 condrepr='<branch closed>')
739 condrepr='<branch closed>')
740
740
741 @predicate('contains(pattern)')
741 @predicate('contains(pattern)')
742 def contains(repo, subset, x):
742 def contains(repo, subset, x):
743 """The revision's manifest contains a file matching pattern (but might not
743 """The revision's manifest contains a file matching pattern (but might not
744 modify it). See :hg:`help patterns` for information about file patterns.
744 modify it). See :hg:`help patterns` for information about file patterns.
745
745
746 The pattern without explicit kind like ``glob:`` is expected to be
746 The pattern without explicit kind like ``glob:`` is expected to be
747 relative to the current directory and match against a file exactly
747 relative to the current directory and match against a file exactly
748 for efficiency.
748 for efficiency.
749 """
749 """
750 # i18n: "contains" is a keyword
750 # i18n: "contains" is a keyword
751 pat = getstring(x, _("contains requires a pattern"))
751 pat = getstring(x, _("contains requires a pattern"))
752
752
753 def matches(x):
753 def matches(x):
754 if not matchmod.patkind(pat):
754 if not matchmod.patkind(pat):
755 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
755 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
756 if pats in repo[x]:
756 if pats in repo[x]:
757 return True
757 return True
758 else:
758 else:
759 c = repo[x]
759 c = repo[x]
760 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
760 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
761 for f in c.manifest():
761 for f in c.manifest():
762 if m(f):
762 if m(f):
763 return True
763 return True
764 return False
764 return False
765
765
766 return subset.filter(matches, condrepr=('<contains %r>', pat))
766 return subset.filter(matches, condrepr=('<contains %r>', pat))
767
767
768 @predicate('converted([id])', safe=True)
768 @predicate('converted([id])', safe=True)
769 def converted(repo, subset, x):
769 def converted(repo, subset, x):
770 """Changesets converted from the given identifier in the old repository if
770 """Changesets converted from the given identifier in the old repository if
771 present, or all converted changesets if no identifier is specified.
771 present, or all converted changesets if no identifier is specified.
772 """
772 """
773
773
774 # There is exactly no chance of resolving the revision, so do a simple
774 # There is exactly no chance of resolving the revision, so do a simple
775 # string compare and hope for the best
775 # string compare and hope for the best
776
776
777 rev = None
777 rev = None
778 # i18n: "converted" is a keyword
778 # i18n: "converted" is a keyword
779 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
779 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
780 if l:
780 if l:
781 # i18n: "converted" is a keyword
781 # i18n: "converted" is a keyword
782 rev = getstring(l[0], _('converted requires a revision'))
782 rev = getstring(l[0], _('converted requires a revision'))
783
783
784 def _matchvalue(r):
784 def _matchvalue(r):
785 source = repo[r].extra().get('convert_revision', None)
785 source = repo[r].extra().get('convert_revision', None)
786 return source is not None and (rev is None or source.startswith(rev))
786 return source is not None and (rev is None or source.startswith(rev))
787
787
788 return subset.filter(lambda r: _matchvalue(r),
788 return subset.filter(lambda r: _matchvalue(r),
789 condrepr=('<converted %r>', rev))
789 condrepr=('<converted %r>', rev))
790
790
791 @predicate('date(interval)', safe=True)
791 @predicate('date(interval)', safe=True)
792 def date(repo, subset, x):
792 def date(repo, subset, x):
793 """Changesets within the interval, see :hg:`help dates`.
793 """Changesets within the interval, see :hg:`help dates`.
794 """
794 """
795 # i18n: "date" is a keyword
795 # i18n: "date" is a keyword
796 ds = getstring(x, _("date requires a string"))
796 ds = getstring(x, _("date requires a string"))
797 dm = util.matchdate(ds)
797 dm = util.matchdate(ds)
798 return subset.filter(lambda x: dm(repo[x].date()[0]),
798 return subset.filter(lambda x: dm(repo[x].date()[0]),
799 condrepr=('<date %r>', ds))
799 condrepr=('<date %r>', ds))
800
800
801 @predicate('desc(string)', safe=True)
801 @predicate('desc(string)', safe=True)
802 def desc(repo, subset, x):
802 def desc(repo, subset, x):
803 """Search commit message for string. The match is case-insensitive.
803 """Search commit message for string. The match is case-insensitive.
804 """
804 """
805 # i18n: "desc" is a keyword
805 # i18n: "desc" is a keyword
806 ds = encoding.lower(getstring(x, _("desc requires a string")))
806 ds = encoding.lower(getstring(x, _("desc requires a string")))
807
807
808 def matches(x):
808 def matches(x):
809 c = repo[x]
809 c = repo[x]
810 return ds in encoding.lower(c.description())
810 return ds in encoding.lower(c.description())
811
811
812 return subset.filter(matches, condrepr=('<desc %r>', ds))
812 return subset.filter(matches, condrepr=('<desc %r>', ds))
813
813
814 def _descendants(repo, subset, x, followfirst=False):
814 def _descendants(repo, subset, x, followfirst=False):
815 roots = getset(repo, fullreposet(repo), x)
815 roots = getset(repo, fullreposet(repo), x)
816 if not roots:
816 if not roots:
817 return baseset()
817 return baseset()
818 s = _revdescendants(repo, roots, followfirst)
818 s = _revdescendants(repo, roots, followfirst)
819
819
820 # Both sets need to be ascending in order to lazily return the union
820 # Both sets need to be ascending in order to lazily return the union
821 # in the correct order.
821 # in the correct order.
822 base = subset & roots
822 base = subset & roots
823 desc = subset & s
823 desc = subset & s
824 result = base + desc
824 result = base + desc
825 if subset.isascending():
825 if subset.isascending():
826 result.sort()
826 result.sort()
827 elif subset.isdescending():
827 elif subset.isdescending():
828 result.sort(reverse=True)
828 result.sort(reverse=True)
829 else:
829 else:
830 result = subset & result
830 result = subset & result
831 return result
831 return result
832
832
833 @predicate('descendants(set)', safe=True)
833 @predicate('descendants(set)', safe=True)
834 def descendants(repo, subset, x):
834 def descendants(repo, subset, x):
835 """Changesets which are descendants of changesets in set.
835 """Changesets which are descendants of changesets in set.
836 """
836 """
837 return _descendants(repo, subset, x)
837 return _descendants(repo, subset, x)
838
838
839 @predicate('_firstdescendants', safe=True)
839 @predicate('_firstdescendants', safe=True)
840 def _firstdescendants(repo, subset, x):
840 def _firstdescendants(repo, subset, x):
841 # ``_firstdescendants(set)``
841 # ``_firstdescendants(set)``
842 # Like ``descendants(set)`` but follows only the first parents.
842 # Like ``descendants(set)`` but follows only the first parents.
843 return _descendants(repo, subset, x, followfirst=True)
843 return _descendants(repo, subset, x, followfirst=True)
844
844
845 @predicate('destination([set])', safe=True)
845 @predicate('destination([set])', safe=True)
846 def destination(repo, subset, x):
846 def destination(repo, subset, x):
847 """Changesets that were created by a graft, transplant or rebase operation,
847 """Changesets that were created by a graft, transplant or rebase operation,
848 with the given revisions specified as the source. Omitting the optional set
848 with the given revisions specified as the source. Omitting the optional set
849 is the same as passing all().
849 is the same as passing all().
850 """
850 """
851 if x is not None:
851 if x is not None:
852 sources = getset(repo, fullreposet(repo), x)
852 sources = getset(repo, fullreposet(repo), x)
853 else:
853 else:
854 sources = fullreposet(repo)
854 sources = fullreposet(repo)
855
855
856 dests = set()
856 dests = set()
857
857
858 # subset contains all of the possible destinations that can be returned, so
858 # subset contains all of the possible destinations that can be returned, so
859 # iterate over them and see if their source(s) were provided in the arg set.
859 # iterate over them and see if their source(s) were provided in the arg set.
860 # Even if the immediate src of r is not in the arg set, src's source (or
860 # Even if the immediate src of r is not in the arg set, src's source (or
861 # further back) may be. Scanning back further than the immediate src allows
861 # further back) may be. Scanning back further than the immediate src allows
862 # transitive transplants and rebases to yield the same results as transitive
862 # transitive transplants and rebases to yield the same results as transitive
863 # grafts.
863 # grafts.
864 for r in subset:
864 for r in subset:
865 src = _getrevsource(repo, r)
865 src = _getrevsource(repo, r)
866 lineage = None
866 lineage = None
867
867
868 while src is not None:
868 while src is not None:
869 if lineage is None:
869 if lineage is None:
870 lineage = list()
870 lineage = list()
871
871
872 lineage.append(r)
872 lineage.append(r)
873
873
874 # The visited lineage is a match if the current source is in the arg
874 # The visited lineage is a match if the current source is in the arg
875 # set. Since every candidate dest is visited by way of iterating
875 # set. Since every candidate dest is visited by way of iterating
876 # subset, any dests further back in the lineage will be tested by a
876 # subset, any dests further back in the lineage will be tested by a
877 # different iteration over subset. Likewise, if the src was already
877 # different iteration over subset. Likewise, if the src was already
878 # selected, the current lineage can be selected without going back
878 # selected, the current lineage can be selected without going back
879 # further.
879 # further.
880 if src in sources or src in dests:
880 if src in sources or src in dests:
881 dests.update(lineage)
881 dests.update(lineage)
882 break
882 break
883
883
884 r = src
884 r = src
885 src = _getrevsource(repo, r)
885 src = _getrevsource(repo, r)
886
886
887 return subset.filter(dests.__contains__,
887 return subset.filter(dests.__contains__,
888 condrepr=lambda: '<destination %r>' % sorted(dests))
888 condrepr=lambda: '<destination %r>' % sorted(dests))
889
889
890 @predicate('divergent()', safe=True)
890 @predicate('divergent()', safe=True)
891 def divergent(repo, subset, x):
891 def divergent(repo, subset, x):
892 """
892 """
893 Final successors of changesets with an alternative set of final successors.
893 Final successors of changesets with an alternative set of final successors.
894 """
894 """
895 # i18n: "divergent" is a keyword
895 # i18n: "divergent" is a keyword
896 getargs(x, 0, 0, _("divergent takes no arguments"))
896 getargs(x, 0, 0, _("divergent takes no arguments"))
897 divergent = obsmod.getrevs(repo, 'divergent')
897 divergent = obsmod.getrevs(repo, 'divergent')
898 return subset & divergent
898 return subset & divergent
899
899
900 @predicate('extinct()', safe=True)
900 @predicate('extinct()', safe=True)
901 def extinct(repo, subset, x):
901 def extinct(repo, subset, x):
902 """Obsolete changesets with obsolete descendants only.
902 """Obsolete changesets with obsolete descendants only.
903 """
903 """
904 # i18n: "extinct" is a keyword
904 # i18n: "extinct" is a keyword
905 getargs(x, 0, 0, _("extinct takes no arguments"))
905 getargs(x, 0, 0, _("extinct takes no arguments"))
906 extincts = obsmod.getrevs(repo, 'extinct')
906 extincts = obsmod.getrevs(repo, 'extinct')
907 return subset & extincts
907 return subset & extincts
908
908
909 @predicate('extra(label, [value])', safe=True)
909 @predicate('extra(label, [value])', safe=True)
910 def extra(repo, subset, x):
910 def extra(repo, subset, x):
911 """Changesets with the given label in the extra metadata, with the given
911 """Changesets with the given label in the extra metadata, with the given
912 optional value.
912 optional value.
913
913
914 If `value` starts with `re:`, the remainder of the value is treated as
914 If `value` starts with `re:`, the remainder of the value is treated as
915 a regular expression. To match a value that actually starts with `re:`,
915 a regular expression. To match a value that actually starts with `re:`,
916 use the prefix `literal:`.
916 use the prefix `literal:`.
917 """
917 """
918 args = getargsdict(x, 'extra', 'label value')
918 args = getargsdict(x, 'extra', 'label value')
919 if 'label' not in args:
919 if 'label' not in args:
920 # i18n: "extra" is a keyword
920 # i18n: "extra" is a keyword
921 raise error.ParseError(_('extra takes at least 1 argument'))
921 raise error.ParseError(_('extra takes at least 1 argument'))
922 # i18n: "extra" is a keyword
922 # i18n: "extra" is a keyword
923 label = getstring(args['label'], _('first argument to extra must be '
923 label = getstring(args['label'], _('first argument to extra must be '
924 'a string'))
924 'a string'))
925 value = None
925 value = None
926
926
927 if 'value' in args:
927 if 'value' in args:
928 # i18n: "extra" is a keyword
928 # i18n: "extra" is a keyword
929 value = getstring(args['value'], _('second argument to extra must be '
929 value = getstring(args['value'], _('second argument to extra must be '
930 'a string'))
930 'a string'))
931 kind, value, matcher = util.stringmatcher(value)
931 kind, value, matcher = util.stringmatcher(value)
932
932
933 def _matchvalue(r):
933 def _matchvalue(r):
934 extra = repo[r].extra()
934 extra = repo[r].extra()
935 return label in extra and (value is None or matcher(extra[label]))
935 return label in extra and (value is None or matcher(extra[label]))
936
936
937 return subset.filter(lambda r: _matchvalue(r),
937 return subset.filter(lambda r: _matchvalue(r),
938 condrepr=('<extra[%r] %r>', label, value))
938 condrepr=('<extra[%r] %r>', label, value))
939
939
940 @predicate('filelog(pattern)', safe=True)
940 @predicate('filelog(pattern)', safe=True)
941 def filelog(repo, subset, x):
941 def filelog(repo, subset, x):
942 """Changesets connected to the specified filelog.
942 """Changesets connected to the specified filelog.
943
943
944 For performance reasons, visits only revisions mentioned in the file-level
944 For performance reasons, visits only revisions mentioned in the file-level
945 filelog, rather than filtering through all changesets (much faster, but
945 filelog, rather than filtering through all changesets (much faster, but
946 doesn't include deletes or duplicate changes). For a slower, more accurate
946 doesn't include deletes or duplicate changes). For a slower, more accurate
947 result, use ``file()``.
947 result, use ``file()``.
948
948
949 The pattern without explicit kind like ``glob:`` is expected to be
949 The pattern without explicit kind like ``glob:`` is expected to be
950 relative to the current directory and match against a file exactly
950 relative to the current directory and match against a file exactly
951 for efficiency.
951 for efficiency.
952
952
953 If some linkrev points to revisions filtered by the current repoview, we'll
953 If some linkrev points to revisions filtered by the current repoview, we'll
954 work around it to return a non-filtered value.
954 work around it to return a non-filtered value.
955 """
955 """
956
956
957 # i18n: "filelog" is a keyword
957 # i18n: "filelog" is a keyword
958 pat = getstring(x, _("filelog requires a pattern"))
958 pat = getstring(x, _("filelog requires a pattern"))
959 s = set()
959 s = set()
960 cl = repo.changelog
960 cl = repo.changelog
961
961
962 if not matchmod.patkind(pat):
962 if not matchmod.patkind(pat):
963 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
963 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
964 files = [f]
964 files = [f]
965 else:
965 else:
966 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
966 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
967 files = (f for f in repo[None] if m(f))
967 files = (f for f in repo[None] if m(f))
968
968
969 for f in files:
969 for f in files:
970 fl = repo.file(f)
970 fl = repo.file(f)
971 known = {}
971 known = {}
972 scanpos = 0
972 scanpos = 0
973 for fr in list(fl):
973 for fr in list(fl):
974 fn = fl.node(fr)
974 fn = fl.node(fr)
975 if fn in known:
975 if fn in known:
976 s.add(known[fn])
976 s.add(known[fn])
977 continue
977 continue
978
978
979 lr = fl.linkrev(fr)
979 lr = fl.linkrev(fr)
980 if lr in cl:
980 if lr in cl:
981 s.add(lr)
981 s.add(lr)
982 elif scanpos is not None:
982 elif scanpos is not None:
983 # lowest matching changeset is filtered, scan further
983 # lowest matching changeset is filtered, scan further
984 # ahead in changelog
984 # ahead in changelog
985 start = max(lr, scanpos) + 1
985 start = max(lr, scanpos) + 1
986 scanpos = None
986 scanpos = None
987 for r in cl.revs(start):
987 for r in cl.revs(start):
988 # minimize parsing of non-matching entries
988 # minimize parsing of non-matching entries
989 if f in cl.revision(r) and f in cl.readfiles(r):
989 if f in cl.revision(r) and f in cl.readfiles(r):
990 try:
990 try:
991 # try to use manifest delta fastpath
991 # try to use manifest delta fastpath
992 n = repo[r].filenode(f)
992 n = repo[r].filenode(f)
993 if n not in known:
993 if n not in known:
994 if n == fn:
994 if n == fn:
995 s.add(r)
995 s.add(r)
996 scanpos = r
996 scanpos = r
997 break
997 break
998 else:
998 else:
999 known[n] = r
999 known[n] = r
1000 except error.ManifestLookupError:
1000 except error.ManifestLookupError:
1001 # deletion in changelog
1001 # deletion in changelog
1002 continue
1002 continue
1003
1003
1004 return subset & s
1004 return subset & s
1005
1005
1006 @predicate('first(set, [n])', safe=True)
1006 @predicate('first(set, [n])', safe=True)
1007 def first(repo, subset, x):
1007 def first(repo, subset, x):
1008 """An alias for limit().
1008 """An alias for limit().
1009 """
1009 """
1010 return limit(repo, subset, x)
1010 return limit(repo, subset, x)
1011
1011
1012 def _follow(repo, subset, x, name, followfirst=False):
1012 def _follow(repo, subset, x, name, followfirst=False):
1013 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1013 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1014 "and an optional revset") % name)
1014 "and an optional revset") % name)
1015 c = repo['.']
1015 c = repo['.']
1016 if l:
1016 if l:
1017 x = getstring(l[0], _("%s expected a pattern") % name)
1017 x = getstring(l[0], _("%s expected a pattern") % name)
1018 rev = None
1018 rev = None
1019 if len(l) >= 2:
1019 if len(l) >= 2:
1020 rev = getset(repo, fullreposet(repo), l[1]).last()
1020 rev = getset(repo, fullreposet(repo), l[1]).last()
1021 if rev is None:
1021 if rev is None:
1022 raise error.RepoLookupError(
1022 raise error.RepoLookupError(
1023 _("%s: starting revision set cannot be empty") % name)
1023 _("%s: starting revision set cannot be empty") % name)
1024 c = repo[rev]
1024 c = repo[rev]
1025 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1025 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1026 ctx=repo[rev], default='path')
1026 ctx=repo[rev], default='path')
1027
1027
1028 files = c.manifest().walk(matcher)
1028 files = c.manifest().walk(matcher)
1029
1029
1030 s = set()
1030 s = set()
1031 for fname in files:
1031 for fname in files:
1032 fctx = c[fname]
1032 fctx = c[fname]
1033 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1033 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1034 # include the revision responsible for the most recent version
1034 # include the revision responsible for the most recent version
1035 s.add(fctx.introrev())
1035 s.add(fctx.introrev())
1036 else:
1036 else:
1037 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1037 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1038
1038
1039 return subset & s
1039 return subset & s
1040
1040
1041 @predicate('follow([pattern[, startrev]])', safe=True)
1041 @predicate('follow([pattern[, startrev]])', safe=True)
1042 def follow(repo, subset, x):
1042 def follow(repo, subset, x):
1043 """
1043 """
1044 An alias for ``::.`` (ancestors of the working directory's first parent).
1044 An alias for ``::.`` (ancestors of the working directory's first parent).
1045 If pattern is specified, the histories of files matching given
1045 If pattern is specified, the histories of files matching given
1046 pattern in the revision given by startrev are followed, including copies.
1046 pattern in the revision given by startrev are followed, including copies.
1047 """
1047 """
1048 return _follow(repo, subset, x, 'follow')
1048 return _follow(repo, subset, x, 'follow')
1049
1049
1050 @predicate('_followfirst', safe=True)
1050 @predicate('_followfirst', safe=True)
1051 def _followfirst(repo, subset, x):
1051 def _followfirst(repo, subset, x):
1052 # ``followfirst([pattern[, startrev]])``
1052 # ``followfirst([pattern[, startrev]])``
1053 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1053 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1054 # of every revisions or files revisions.
1054 # of every revisions or files revisions.
1055 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1055 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1056
1056
1057 @predicate('all()', safe=True)
1057 @predicate('all()', safe=True)
1058 def getall(repo, subset, x):
1058 def getall(repo, subset, x):
1059 """All changesets, the same as ``0:tip``.
1059 """All changesets, the same as ``0:tip``.
1060 """
1060 """
1061 # i18n: "all" is a keyword
1061 # i18n: "all" is a keyword
1062 getargs(x, 0, 0, _("all takes no arguments"))
1062 getargs(x, 0, 0, _("all takes no arguments"))
1063 return subset & spanset(repo) # drop "null" if any
1063 return subset & spanset(repo) # drop "null" if any
1064
1064
1065 @predicate('grep(regex)')
1065 @predicate('grep(regex)')
1066 def grep(repo, subset, x):
1066 def grep(repo, subset, x):
1067 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1067 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1068 to ensure special escape characters are handled correctly. Unlike
1068 to ensure special escape characters are handled correctly. Unlike
1069 ``keyword(string)``, the match is case-sensitive.
1069 ``keyword(string)``, the match is case-sensitive.
1070 """
1070 """
1071 try:
1071 try:
1072 # i18n: "grep" is a keyword
1072 # i18n: "grep" is a keyword
1073 gr = re.compile(getstring(x, _("grep requires a string")))
1073 gr = re.compile(getstring(x, _("grep requires a string")))
1074 except re.error as e:
1074 except re.error as e:
1075 raise error.ParseError(_('invalid match pattern: %s') % e)
1075 raise error.ParseError(_('invalid match pattern: %s') % e)
1076
1076
1077 def matches(x):
1077 def matches(x):
1078 c = repo[x]
1078 c = repo[x]
1079 for e in c.files() + [c.user(), c.description()]:
1079 for e in c.files() + [c.user(), c.description()]:
1080 if gr.search(e):
1080 if gr.search(e):
1081 return True
1081 return True
1082 return False
1082 return False
1083
1083
1084 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1084 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1085
1085
1086 @predicate('_matchfiles', safe=True)
1086 @predicate('_matchfiles', safe=True)
1087 def _matchfiles(repo, subset, x):
1087 def _matchfiles(repo, subset, x):
1088 # _matchfiles takes a revset list of prefixed arguments:
1088 # _matchfiles takes a revset list of prefixed arguments:
1089 #
1089 #
1090 # [p:foo, i:bar, x:baz]
1090 # [p:foo, i:bar, x:baz]
1091 #
1091 #
1092 # builds a match object from them and filters subset. Allowed
1092 # builds a match object from them and filters subset. Allowed
1093 # prefixes are 'p:' for regular patterns, 'i:' for include
1093 # prefixes are 'p:' for regular patterns, 'i:' for include
1094 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1094 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1095 # a revision identifier, or the empty string to reference the
1095 # a revision identifier, or the empty string to reference the
1096 # working directory, from which the match object is
1096 # working directory, from which the match object is
1097 # initialized. Use 'd:' to set the default matching mode, default
1097 # initialized. Use 'd:' to set the default matching mode, default
1098 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1098 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1099
1099
1100 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1100 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1101 pats, inc, exc = [], [], []
1101 pats, inc, exc = [], [], []
1102 rev, default = None, None
1102 rev, default = None, None
1103 for arg in l:
1103 for arg in l:
1104 s = getstring(arg, "_matchfiles requires string arguments")
1104 s = getstring(arg, "_matchfiles requires string arguments")
1105 prefix, value = s[:2], s[2:]
1105 prefix, value = s[:2], s[2:]
1106 if prefix == 'p:':
1106 if prefix == 'p:':
1107 pats.append(value)
1107 pats.append(value)
1108 elif prefix == 'i:':
1108 elif prefix == 'i:':
1109 inc.append(value)
1109 inc.append(value)
1110 elif prefix == 'x:':
1110 elif prefix == 'x:':
1111 exc.append(value)
1111 exc.append(value)
1112 elif prefix == 'r:':
1112 elif prefix == 'r:':
1113 if rev is not None:
1113 if rev is not None:
1114 raise error.ParseError('_matchfiles expected at most one '
1114 raise error.ParseError('_matchfiles expected at most one '
1115 'revision')
1115 'revision')
1116 if value != '': # empty means working directory; leave rev as None
1116 if value != '': # empty means working directory; leave rev as None
1117 rev = value
1117 rev = value
1118 elif prefix == 'd:':
1118 elif prefix == 'd:':
1119 if default is not None:
1119 if default is not None:
1120 raise error.ParseError('_matchfiles expected at most one '
1120 raise error.ParseError('_matchfiles expected at most one '
1121 'default mode')
1121 'default mode')
1122 default = value
1122 default = value
1123 else:
1123 else:
1124 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1124 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1125 if not default:
1125 if not default:
1126 default = 'glob'
1126 default = 'glob'
1127
1127
1128 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1128 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1129 exclude=exc, ctx=repo[rev], default=default)
1129 exclude=exc, ctx=repo[rev], default=default)
1130
1130
1131 # This directly read the changelog data as creating changectx for all
1131 # This directly read the changelog data as creating changectx for all
1132 # revisions is quite expensive.
1132 # revisions is quite expensive.
1133 getfiles = repo.changelog.readfiles
1133 getfiles = repo.changelog.readfiles
1134 wdirrev = node.wdirrev
1134 wdirrev = node.wdirrev
1135 def matches(x):
1135 def matches(x):
1136 if x == wdirrev:
1136 if x == wdirrev:
1137 files = repo[x].files()
1137 files = repo[x].files()
1138 else:
1138 else:
1139 files = getfiles(x)
1139 files = getfiles(x)
1140 for f in files:
1140 for f in files:
1141 if m(f):
1141 if m(f):
1142 return True
1142 return True
1143 return False
1143 return False
1144
1144
1145 return subset.filter(matches,
1145 return subset.filter(matches,
1146 condrepr=('<matchfiles patterns=%r, include=%r '
1146 condrepr=('<matchfiles patterns=%r, include=%r '
1147 'exclude=%r, default=%r, rev=%r>',
1147 'exclude=%r, default=%r, rev=%r>',
1148 pats, inc, exc, default, rev))
1148 pats, inc, exc, default, rev))
1149
1149
1150 @predicate('file(pattern)', safe=True)
1150 @predicate('file(pattern)', safe=True)
1151 def hasfile(repo, subset, x):
1151 def hasfile(repo, subset, x):
1152 """Changesets affecting files matched by pattern.
1152 """Changesets affecting files matched by pattern.
1153
1153
1154 For a faster but less accurate result, consider using ``filelog()``
1154 For a faster but less accurate result, consider using ``filelog()``
1155 instead.
1155 instead.
1156
1156
1157 This predicate uses ``glob:`` as the default kind of pattern.
1157 This predicate uses ``glob:`` as the default kind of pattern.
1158 """
1158 """
1159 # i18n: "file" is a keyword
1159 # i18n: "file" is a keyword
1160 pat = getstring(x, _("file requires a pattern"))
1160 pat = getstring(x, _("file requires a pattern"))
1161 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1161 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1162
1162
1163 @predicate('head()', safe=True)
1163 @predicate('head()', safe=True)
1164 def head(repo, subset, x):
1164 def head(repo, subset, x):
1165 """Changeset is a named branch head.
1165 """Changeset is a named branch head.
1166 """
1166 """
1167 # i18n: "head" is a keyword
1167 # i18n: "head" is a keyword
1168 getargs(x, 0, 0, _("head takes no arguments"))
1168 getargs(x, 0, 0, _("head takes no arguments"))
1169 hs = set()
1169 hs = set()
1170 cl = repo.changelog
1170 cl = repo.changelog
1171 for ls in repo.branchmap().itervalues():
1171 for ls in repo.branchmap().itervalues():
1172 hs.update(cl.rev(h) for h in ls)
1172 hs.update(cl.rev(h) for h in ls)
1173 return subset & baseset(hs)
1173 return subset & baseset(hs)
1174
1174
1175 @predicate('heads(set)', safe=True)
1175 @predicate('heads(set)', safe=True)
1176 def heads(repo, subset, x):
1176 def heads(repo, subset, x):
1177 """Members of set with no children in set.
1177 """Members of set with no children in set.
1178 """
1178 """
1179 s = getset(repo, subset, x)
1179 s = getset(repo, subset, x)
1180 ps = parents(repo, subset, x)
1180 ps = parents(repo, subset, x)
1181 return s - ps
1181 return s - ps
1182
1182
1183 @predicate('hidden()', safe=True)
1183 @predicate('hidden()', safe=True)
1184 def hidden(repo, subset, x):
1184 def hidden(repo, subset, x):
1185 """Hidden changesets.
1185 """Hidden changesets.
1186 """
1186 """
1187 # i18n: "hidden" is a keyword
1187 # i18n: "hidden" is a keyword
1188 getargs(x, 0, 0, _("hidden takes no arguments"))
1188 getargs(x, 0, 0, _("hidden takes no arguments"))
1189 hiddenrevs = repoview.filterrevs(repo, 'visible')
1189 hiddenrevs = repoview.filterrevs(repo, 'visible')
1190 return subset & hiddenrevs
1190 return subset & hiddenrevs
1191
1191
1192 @predicate('keyword(string)', safe=True)
1192 @predicate('keyword(string)', safe=True)
1193 def keyword(repo, subset, x):
1193 def keyword(repo, subset, x):
1194 """Search commit message, user name, and names of changed files for
1194 """Search commit message, user name, and names of changed files for
1195 string. The match is case-insensitive.
1195 string. The match is case-insensitive.
1196 """
1196 """
1197 # i18n: "keyword" is a keyword
1197 # i18n: "keyword" is a keyword
1198 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1198 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1199
1199
1200 def matches(r):
1200 def matches(r):
1201 c = repo[r]
1201 c = repo[r]
1202 return any(kw in encoding.lower(t)
1202 return any(kw in encoding.lower(t)
1203 for t in c.files() + [c.user(), c.description()])
1203 for t in c.files() + [c.user(), c.description()])
1204
1204
1205 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1205 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1206
1206
1207 @predicate('limit(set[, n[, offset]])', safe=True)
1207 @predicate('limit(set[, n[, offset]])', safe=True)
1208 def limit(repo, subset, x):
1208 def limit(repo, subset, x):
1209 """First n members of set, defaulting to 1, starting from offset.
1209 """First n members of set, defaulting to 1, starting from offset.
1210 """
1210 """
1211 args = getargsdict(x, 'limit', 'set n offset')
1211 args = getargsdict(x, 'limit', 'set n offset')
1212 if 'set' not in args:
1212 if 'set' not in args:
1213 # i18n: "limit" is a keyword
1213 # i18n: "limit" is a keyword
1214 raise error.ParseError(_("limit requires one to three arguments"))
1214 raise error.ParseError(_("limit requires one to three arguments"))
1215 try:
1215 try:
1216 lim, ofs = 1, 0
1216 lim, ofs = 1, 0
1217 if 'n' in args:
1217 if 'n' in args:
1218 # i18n: "limit" is a keyword
1218 # i18n: "limit" is a keyword
1219 lim = int(getstring(args['n'], _("limit requires a number")))
1219 lim = int(getstring(args['n'], _("limit requires a number")))
1220 if 'offset' in args:
1220 if 'offset' in args:
1221 # i18n: "limit" is a keyword
1221 # i18n: "limit" is a keyword
1222 ofs = int(getstring(args['offset'], _("limit requires a number")))
1222 ofs = int(getstring(args['offset'], _("limit requires a number")))
1223 if ofs < 0:
1223 if ofs < 0:
1224 raise error.ParseError(_("negative offset"))
1224 raise error.ParseError(_("negative offset"))
1225 except (TypeError, ValueError):
1225 except (TypeError, ValueError):
1226 # i18n: "limit" is a keyword
1226 # i18n: "limit" is a keyword
1227 raise error.ParseError(_("limit expects a number"))
1227 raise error.ParseError(_("limit expects a number"))
1228 os = getset(repo, fullreposet(repo), args['set'])
1228 os = getset(repo, fullreposet(repo), args['set'])
1229 result = []
1229 result = []
1230 it = iter(os)
1230 it = iter(os)
1231 for x in xrange(ofs):
1231 for x in xrange(ofs):
1232 y = next(it, None)
1232 y = next(it, None)
1233 if y is None:
1233 if y is None:
1234 break
1234 break
1235 for x in xrange(lim):
1235 for x in xrange(lim):
1236 y = next(it, None)
1236 y = next(it, None)
1237 if y is None:
1237 if y is None:
1238 break
1238 break
1239 elif y in subset:
1239 elif y in subset:
1240 result.append(y)
1240 result.append(y)
1241 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1241 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1242 lim, ofs, subset, os))
1242 lim, ofs, subset, os))
1243
1243
1244 @predicate('last(set, [n])', safe=True)
1244 @predicate('last(set, [n])', safe=True)
1245 def last(repo, subset, x):
1245 def last(repo, subset, x):
1246 """Last n members of set, defaulting to 1.
1246 """Last n members of set, defaulting to 1.
1247 """
1247 """
1248 # i18n: "last" is a keyword
1248 # i18n: "last" is a keyword
1249 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1249 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1250 try:
1250 try:
1251 lim = 1
1251 lim = 1
1252 if len(l) == 2:
1252 if len(l) == 2:
1253 # i18n: "last" is a keyword
1253 # i18n: "last" is a keyword
1254 lim = int(getstring(l[1], _("last requires a number")))
1254 lim = int(getstring(l[1], _("last requires a number")))
1255 except (TypeError, ValueError):
1255 except (TypeError, ValueError):
1256 # i18n: "last" is a keyword
1256 # i18n: "last" is a keyword
1257 raise error.ParseError(_("last expects a number"))
1257 raise error.ParseError(_("last expects a number"))
1258 os = getset(repo, fullreposet(repo), l[0])
1258 os = getset(repo, fullreposet(repo), l[0])
1259 os.reverse()
1259 os.reverse()
1260 result = []
1260 result = []
1261 it = iter(os)
1261 it = iter(os)
1262 for x in xrange(lim):
1262 for x in xrange(lim):
1263 y = next(it, None)
1263 y = next(it, None)
1264 if y is None:
1264 if y is None:
1265 break
1265 break
1266 elif y in subset:
1266 elif y in subset:
1267 result.append(y)
1267 result.append(y)
1268 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1268 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1269
1269
1270 @predicate('max(set)', safe=True)
1270 @predicate('max(set)', safe=True)
1271 def maxrev(repo, subset, x):
1271 def maxrev(repo, subset, x):
1272 """Changeset with highest revision number in set.
1272 """Changeset with highest revision number in set.
1273 """
1273 """
1274 os = getset(repo, fullreposet(repo), x)
1274 os = getset(repo, fullreposet(repo), x)
1275 try:
1275 try:
1276 m = os.max()
1276 m = os.max()
1277 if m in subset:
1277 if m in subset:
1278 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1278 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1279 except ValueError:
1279 except ValueError:
1280 # os.max() throws a ValueError when the collection is empty.
1280 # os.max() throws a ValueError when the collection is empty.
1281 # Same as python's max().
1281 # Same as python's max().
1282 pass
1282 pass
1283 return baseset(datarepr=('<max %r, %r>', subset, os))
1283 return baseset(datarepr=('<max %r, %r>', subset, os))
1284
1284
1285 @predicate('merge()', safe=True)
1285 @predicate('merge()', safe=True)
1286 def merge(repo, subset, x):
1286 def merge(repo, subset, x):
1287 """Changeset is a merge changeset.
1287 """Changeset is a merge changeset.
1288 """
1288 """
1289 # i18n: "merge" is a keyword
1289 # i18n: "merge" is a keyword
1290 getargs(x, 0, 0, _("merge takes no arguments"))
1290 getargs(x, 0, 0, _("merge takes no arguments"))
1291 cl = repo.changelog
1291 cl = repo.changelog
1292 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1292 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1293 condrepr='<merge>')
1293 condrepr='<merge>')
1294
1294
1295 @predicate('branchpoint()', safe=True)
1295 @predicate('branchpoint()', safe=True)
1296 def branchpoint(repo, subset, x):
1296 def branchpoint(repo, subset, x):
1297 """Changesets with more than one child.
1297 """Changesets with more than one child.
1298 """
1298 """
1299 # i18n: "branchpoint" is a keyword
1299 # i18n: "branchpoint" is a keyword
1300 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1300 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1301 cl = repo.changelog
1301 cl = repo.changelog
1302 if not subset:
1302 if not subset:
1303 return baseset()
1303 return baseset()
1304 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1304 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1305 # (and if it is not, it should.)
1305 # (and if it is not, it should.)
1306 baserev = min(subset)
1306 baserev = min(subset)
1307 parentscount = [0]*(len(repo) - baserev)
1307 parentscount = [0]*(len(repo) - baserev)
1308 for r in cl.revs(start=baserev + 1):
1308 for r in cl.revs(start=baserev + 1):
1309 for p in cl.parentrevs(r):
1309 for p in cl.parentrevs(r):
1310 if p >= baserev:
1310 if p >= baserev:
1311 parentscount[p - baserev] += 1
1311 parentscount[p - baserev] += 1
1312 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1312 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1313 condrepr='<branchpoint>')
1313 condrepr='<branchpoint>')
1314
1314
1315 @predicate('min(set)', safe=True)
1315 @predicate('min(set)', safe=True)
1316 def minrev(repo, subset, x):
1316 def minrev(repo, subset, x):
1317 """Changeset with lowest revision number in set.
1317 """Changeset with lowest revision number in set.
1318 """
1318 """
1319 os = getset(repo, fullreposet(repo), x)
1319 os = getset(repo, fullreposet(repo), x)
1320 try:
1320 try:
1321 m = os.min()
1321 m = os.min()
1322 if m in subset:
1322 if m in subset:
1323 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1323 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1324 except ValueError:
1324 except ValueError:
1325 # os.min() throws a ValueError when the collection is empty.
1325 # os.min() throws a ValueError when the collection is empty.
1326 # Same as python's min().
1326 # Same as python's min().
1327 pass
1327 pass
1328 return baseset(datarepr=('<min %r, %r>', subset, os))
1328 return baseset(datarepr=('<min %r, %r>', subset, os))
1329
1329
1330 @predicate('modifies(pattern)', safe=True)
1330 @predicate('modifies(pattern)', safe=True)
1331 def modifies(repo, subset, x):
1331 def modifies(repo, subset, x):
1332 """Changesets modifying files matched by pattern.
1332 """Changesets modifying files matched by pattern.
1333
1333
1334 The pattern without explicit kind like ``glob:`` is expected to be
1334 The pattern without explicit kind like ``glob:`` is expected to be
1335 relative to the current directory and match against a file or a
1335 relative to the current directory and match against a file or a
1336 directory.
1336 directory.
1337 """
1337 """
1338 # i18n: "modifies" is a keyword
1338 # i18n: "modifies" is a keyword
1339 pat = getstring(x, _("modifies requires a pattern"))
1339 pat = getstring(x, _("modifies requires a pattern"))
1340 return checkstatus(repo, subset, pat, 0)
1340 return checkstatus(repo, subset, pat, 0)
1341
1341
1342 @predicate('named(namespace)')
1342 @predicate('named(namespace)')
1343 def named(repo, subset, x):
1343 def named(repo, subset, x):
1344 """The changesets in a given namespace.
1344 """The changesets in a given namespace.
1345
1345
1346 If `namespace` starts with `re:`, the remainder of the string is treated as
1346 If `namespace` starts with `re:`, the remainder of the string is treated as
1347 a regular expression. To match a namespace that actually starts with `re:`,
1347 a regular expression. To match a namespace that actually starts with `re:`,
1348 use the prefix `literal:`.
1348 use the prefix `literal:`.
1349 """
1349 """
1350 # i18n: "named" is a keyword
1350 # i18n: "named" is a keyword
1351 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1351 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1352
1352
1353 ns = getstring(args[0],
1353 ns = getstring(args[0],
1354 # i18n: "named" is a keyword
1354 # i18n: "named" is a keyword
1355 _('the argument to named must be a string'))
1355 _('the argument to named must be a string'))
1356 kind, pattern, matcher = util.stringmatcher(ns)
1356 kind, pattern, matcher = util.stringmatcher(ns)
1357 namespaces = set()
1357 namespaces = set()
1358 if kind == 'literal':
1358 if kind == 'literal':
1359 if pattern not in repo.names:
1359 if pattern not in repo.names:
1360 raise error.RepoLookupError(_("namespace '%s' does not exist")
1360 raise error.RepoLookupError(_("namespace '%s' does not exist")
1361 % ns)
1361 % ns)
1362 namespaces.add(repo.names[pattern])
1362 namespaces.add(repo.names[pattern])
1363 else:
1363 else:
1364 for name, ns in repo.names.iteritems():
1364 for name, ns in repo.names.iteritems():
1365 if matcher(name):
1365 if matcher(name):
1366 namespaces.add(ns)
1366 namespaces.add(ns)
1367 if not namespaces:
1367 if not namespaces:
1368 raise error.RepoLookupError(_("no namespace exists"
1368 raise error.RepoLookupError(_("no namespace exists"
1369 " that match '%s'") % pattern)
1369 " that match '%s'") % pattern)
1370
1370
1371 names = set()
1371 names = set()
1372 for ns in namespaces:
1372 for ns in namespaces:
1373 for name in ns.listnames(repo):
1373 for name in ns.listnames(repo):
1374 if name not in ns.deprecated:
1374 if name not in ns.deprecated:
1375 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1375 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1376
1376
1377 names -= set([node.nullrev])
1377 names -= set([node.nullrev])
1378 return subset & names
1378 return subset & names
1379
1379
1380 @predicate('id(string)', safe=True)
1380 @predicate('id(string)', safe=True)
1381 def node_(repo, subset, x):
1381 def node_(repo, subset, x):
1382 """Revision non-ambiguously specified by the given hex string prefix.
1382 """Revision non-ambiguously specified by the given hex string prefix.
1383 """
1383 """
1384 # i18n: "id" is a keyword
1384 # i18n: "id" is a keyword
1385 l = getargs(x, 1, 1, _("id requires one argument"))
1385 l = getargs(x, 1, 1, _("id requires one argument"))
1386 # i18n: "id" is a keyword
1386 # i18n: "id" is a keyword
1387 n = getstring(l[0], _("id requires a string"))
1387 n = getstring(l[0], _("id requires a string"))
1388 if len(n) == 40:
1388 if len(n) == 40:
1389 try:
1389 try:
1390 rn = repo.changelog.rev(node.bin(n))
1390 rn = repo.changelog.rev(node.bin(n))
1391 except (LookupError, TypeError):
1391 except (LookupError, TypeError):
1392 rn = None
1392 rn = None
1393 else:
1393 else:
1394 rn = None
1394 rn = None
1395 pm = repo.changelog._partialmatch(n)
1395 pm = repo.changelog._partialmatch(n)
1396 if pm is not None:
1396 if pm is not None:
1397 rn = repo.changelog.rev(pm)
1397 rn = repo.changelog.rev(pm)
1398
1398
1399 if rn is None:
1399 if rn is None:
1400 return baseset()
1400 return baseset()
1401 result = baseset([rn])
1401 result = baseset([rn])
1402 return result & subset
1402 return result & subset
1403
1403
1404 @predicate('obsolete()', safe=True)
1404 @predicate('obsolete()', safe=True)
1405 def obsolete(repo, subset, x):
1405 def obsolete(repo, subset, x):
1406 """Mutable changeset with a newer version."""
1406 """Mutable changeset with a newer version."""
1407 # i18n: "obsolete" is a keyword
1407 # i18n: "obsolete" is a keyword
1408 getargs(x, 0, 0, _("obsolete takes no arguments"))
1408 getargs(x, 0, 0, _("obsolete takes no arguments"))
1409 obsoletes = obsmod.getrevs(repo, 'obsolete')
1409 obsoletes = obsmod.getrevs(repo, 'obsolete')
1410 return subset & obsoletes
1410 return subset & obsoletes
1411
1411
1412 @predicate('only(set, [set])', safe=True)
1412 @predicate('only(set, [set])', safe=True)
1413 def only(repo, subset, x):
1413 def only(repo, subset, x):
1414 """Changesets that are ancestors of the first set that are not ancestors
1414 """Changesets that are ancestors of the first set that are not ancestors
1415 of any other head in the repo. If a second set is specified, the result
1415 of any other head in the repo. If a second set is specified, the result
1416 is ancestors of the first set that are not ancestors of the second set
1416 is ancestors of the first set that are not ancestors of the second set
1417 (i.e. ::<set1> - ::<set2>).
1417 (i.e. ::<set1> - ::<set2>).
1418 """
1418 """
1419 cl = repo.changelog
1419 cl = repo.changelog
1420 # i18n: "only" is a keyword
1420 # i18n: "only" is a keyword
1421 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1421 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1422 include = getset(repo, fullreposet(repo), args[0])
1422 include = getset(repo, fullreposet(repo), args[0])
1423 if len(args) == 1:
1423 if len(args) == 1:
1424 if not include:
1424 if not include:
1425 return baseset()
1425 return baseset()
1426
1426
1427 descendants = set(_revdescendants(repo, include, False))
1427 descendants = set(_revdescendants(repo, include, False))
1428 exclude = [rev for rev in cl.headrevs()
1428 exclude = [rev for rev in cl.headrevs()
1429 if not rev in descendants and not rev in include]
1429 if not rev in descendants and not rev in include]
1430 else:
1430 else:
1431 exclude = getset(repo, fullreposet(repo), args[1])
1431 exclude = getset(repo, fullreposet(repo), args[1])
1432
1432
1433 results = set(cl.findmissingrevs(common=exclude, heads=include))
1433 results = set(cl.findmissingrevs(common=exclude, heads=include))
1434 # XXX we should turn this into a baseset instead of a set, smartset may do
1434 # XXX we should turn this into a baseset instead of a set, smartset may do
1435 # some optimisations from the fact this is a baseset.
1435 # some optimisations from the fact this is a baseset.
1436 return subset & results
1436 return subset & results
1437
1437
1438 @predicate('origin([set])', safe=True)
1438 @predicate('origin([set])', safe=True)
1439 def origin(repo, subset, x):
1439 def origin(repo, subset, x):
1440 """
1440 """
1441 Changesets that were specified as a source for the grafts, transplants or
1441 Changesets that were specified as a source for the grafts, transplants or
1442 rebases that created the given revisions. Omitting the optional set is the
1442 rebases that created the given revisions. Omitting the optional set is the
1443 same as passing all(). If a changeset created by these operations is itself
1443 same as passing all(). If a changeset created by these operations is itself
1444 specified as a source for one of these operations, only the source changeset
1444 specified as a source for one of these operations, only the source changeset
1445 for the first operation is selected.
1445 for the first operation is selected.
1446 """
1446 """
1447 if x is not None:
1447 if x is not None:
1448 dests = getset(repo, fullreposet(repo), x)
1448 dests = getset(repo, fullreposet(repo), x)
1449 else:
1449 else:
1450 dests = fullreposet(repo)
1450 dests = fullreposet(repo)
1451
1451
1452 def _firstsrc(rev):
1452 def _firstsrc(rev):
1453 src = _getrevsource(repo, rev)
1453 src = _getrevsource(repo, rev)
1454 if src is None:
1454 if src is None:
1455 return None
1455 return None
1456
1456
1457 while True:
1457 while True:
1458 prev = _getrevsource(repo, src)
1458 prev = _getrevsource(repo, src)
1459
1459
1460 if prev is None:
1460 if prev is None:
1461 return src
1461 return src
1462 src = prev
1462 src = prev
1463
1463
1464 o = set([_firstsrc(r) for r in dests])
1464 o = set([_firstsrc(r) for r in dests])
1465 o -= set([None])
1465 o -= set([None])
1466 # XXX we should turn this into a baseset instead of a set, smartset may do
1466 # XXX we should turn this into a baseset instead of a set, smartset may do
1467 # some optimisations from the fact this is a baseset.
1467 # some optimisations from the fact this is a baseset.
1468 return subset & o
1468 return subset & o
1469
1469
1470 @predicate('outgoing([path])', safe=True)
1470 @predicate('outgoing([path])', safe=True)
1471 def outgoing(repo, subset, x):
1471 def outgoing(repo, subset, x):
1472 """Changesets not found in the specified destination repository, or the
1472 """Changesets not found in the specified destination repository, or the
1473 default push location.
1473 default push location.
1474 """
1474 """
1475 # Avoid cycles.
1475 # Avoid cycles.
1476 from . import (
1476 from . import (
1477 discovery,
1477 discovery,
1478 hg,
1478 hg,
1479 )
1479 )
1480 # i18n: "outgoing" is a keyword
1480 # i18n: "outgoing" is a keyword
1481 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1481 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1482 # i18n: "outgoing" is a keyword
1482 # i18n: "outgoing" is a keyword
1483 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1483 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1484 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1484 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1485 dest, branches = hg.parseurl(dest)
1485 dest, branches = hg.parseurl(dest)
1486 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1486 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1487 if revs:
1487 if revs:
1488 revs = [repo.lookup(rev) for rev in revs]
1488 revs = [repo.lookup(rev) for rev in revs]
1489 other = hg.peer(repo, {}, dest)
1489 other = hg.peer(repo, {}, dest)
1490 repo.ui.pushbuffer()
1490 repo.ui.pushbuffer()
1491 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1491 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1492 repo.ui.popbuffer()
1492 repo.ui.popbuffer()
1493 cl = repo.changelog
1493 cl = repo.changelog
1494 o = set([cl.rev(r) for r in outgoing.missing])
1494 o = set([cl.rev(r) for r in outgoing.missing])
1495 return subset & o
1495 return subset & o
1496
1496
1497 @predicate('p1([set])', safe=True)
1497 @predicate('p1([set])', safe=True)
1498 def p1(repo, subset, x):
1498 def p1(repo, subset, x):
1499 """First parent of changesets in set, or the working directory.
1499 """First parent of changesets in set, or the working directory.
1500 """
1500 """
1501 if x is None:
1501 if x is None:
1502 p = repo[x].p1().rev()
1502 p = repo[x].p1().rev()
1503 if p >= 0:
1503 if p >= 0:
1504 return subset & baseset([p])
1504 return subset & baseset([p])
1505 return baseset()
1505 return baseset()
1506
1506
1507 ps = set()
1507 ps = set()
1508 cl = repo.changelog
1508 cl = repo.changelog
1509 for r in getset(repo, fullreposet(repo), x):
1509 for r in getset(repo, fullreposet(repo), x):
1510 ps.add(cl.parentrevs(r)[0])
1510 ps.add(cl.parentrevs(r)[0])
1511 ps -= set([node.nullrev])
1511 ps -= set([node.nullrev])
1512 # XXX we should turn this into a baseset instead of a set, smartset may do
1512 # XXX we should turn this into a baseset instead of a set, smartset may do
1513 # some optimisations from the fact this is a baseset.
1513 # some optimisations from the fact this is a baseset.
1514 return subset & ps
1514 return subset & ps
1515
1515
1516 @predicate('p2([set])', safe=True)
1516 @predicate('p2([set])', safe=True)
1517 def p2(repo, subset, x):
1517 def p2(repo, subset, x):
1518 """Second parent of changesets in set, or the working directory.
1518 """Second parent of changesets in set, or the working directory.
1519 """
1519 """
1520 if x is None:
1520 if x is None:
1521 ps = repo[x].parents()
1521 ps = repo[x].parents()
1522 try:
1522 try:
1523 p = ps[1].rev()
1523 p = ps[1].rev()
1524 if p >= 0:
1524 if p >= 0:
1525 return subset & baseset([p])
1525 return subset & baseset([p])
1526 return baseset()
1526 return baseset()
1527 except IndexError:
1527 except IndexError:
1528 return baseset()
1528 return baseset()
1529
1529
1530 ps = set()
1530 ps = set()
1531 cl = repo.changelog
1531 cl = repo.changelog
1532 for r in getset(repo, fullreposet(repo), x):
1532 for r in getset(repo, fullreposet(repo), x):
1533 ps.add(cl.parentrevs(r)[1])
1533 ps.add(cl.parentrevs(r)[1])
1534 ps -= set([node.nullrev])
1534 ps -= set([node.nullrev])
1535 # XXX we should turn this into a baseset instead of a set, smartset may do
1535 # XXX we should turn this into a baseset instead of a set, smartset may do
1536 # some optimisations from the fact this is a baseset.
1536 # some optimisations from the fact this is a baseset.
1537 return subset & ps
1537 return subset & ps
1538
1538
1539 def parentpost(repo, subset, x, order):
1539 def parentpost(repo, subset, x, order):
1540 return p1(repo, subset, x)
1540 return p1(repo, subset, x)
1541
1541
1542 @predicate('parents([set])', safe=True)
1542 @predicate('parents([set])', safe=True)
1543 def parents(repo, subset, x):
1543 def parents(repo, subset, x):
1544 """
1544 """
1545 The set of all parents for all changesets in set, or the working directory.
1545 The set of all parents for all changesets in set, or the working directory.
1546 """
1546 """
1547 if x is None:
1547 if x is None:
1548 ps = set(p.rev() for p in repo[x].parents())
1548 ps = set(p.rev() for p in repo[x].parents())
1549 else:
1549 else:
1550 ps = set()
1550 ps = set()
1551 cl = repo.changelog
1551 cl = repo.changelog
1552 up = ps.update
1552 up = ps.update
1553 parentrevs = cl.parentrevs
1553 parentrevs = cl.parentrevs
1554 for r in getset(repo, fullreposet(repo), x):
1554 for r in getset(repo, fullreposet(repo), x):
1555 if r == node.wdirrev:
1555 if r == node.wdirrev:
1556 up(p.rev() for p in repo[r].parents())
1556 up(p.rev() for p in repo[r].parents())
1557 else:
1557 else:
1558 up(parentrevs(r))
1558 up(parentrevs(r))
1559 ps -= set([node.nullrev])
1559 ps -= set([node.nullrev])
1560 return subset & ps
1560 return subset & ps
1561
1561
1562 def _phase(repo, subset, target):
1562 def _phase(repo, subset, target):
1563 """helper to select all rev in phase <target>"""
1563 """helper to select all rev in phase <target>"""
1564 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1564 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1565 if repo._phasecache._phasesets:
1565 if repo._phasecache._phasesets:
1566 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1566 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1567 s = baseset(s)
1567 s = baseset(s)
1568 s.sort() # set are non ordered, so we enforce ascending
1568 s.sort() # set are non ordered, so we enforce ascending
1569 return subset & s
1569 return subset & s
1570 else:
1570 else:
1571 phase = repo._phasecache.phase
1571 phase = repo._phasecache.phase
1572 condition = lambda r: phase(repo, r) == target
1572 condition = lambda r: phase(repo, r) == target
1573 return subset.filter(condition, condrepr=('<phase %r>', target),
1573 return subset.filter(condition, condrepr=('<phase %r>', target),
1574 cache=False)
1574 cache=False)
1575
1575
1576 @predicate('draft()', safe=True)
1576 @predicate('draft()', safe=True)
1577 def draft(repo, subset, x):
1577 def draft(repo, subset, x):
1578 """Changeset in draft phase."""
1578 """Changeset in draft phase."""
1579 # i18n: "draft" is a keyword
1579 # i18n: "draft" is a keyword
1580 getargs(x, 0, 0, _("draft takes no arguments"))
1580 getargs(x, 0, 0, _("draft takes no arguments"))
1581 target = phases.draft
1581 target = phases.draft
1582 return _phase(repo, subset, target)
1582 return _phase(repo, subset, target)
1583
1583
1584 @predicate('secret()', safe=True)
1584 @predicate('secret()', safe=True)
1585 def secret(repo, subset, x):
1585 def secret(repo, subset, x):
1586 """Changeset in secret phase."""
1586 """Changeset in secret phase."""
1587 # i18n: "secret" is a keyword
1587 # i18n: "secret" is a keyword
1588 getargs(x, 0, 0, _("secret takes no arguments"))
1588 getargs(x, 0, 0, _("secret takes no arguments"))
1589 target = phases.secret
1589 target = phases.secret
1590 return _phase(repo, subset, target)
1590 return _phase(repo, subset, target)
1591
1591
1592 def parentspec(repo, subset, x, n, order):
1592 def parentspec(repo, subset, x, n, order):
1593 """``set^0``
1593 """``set^0``
1594 The set.
1594 The set.
1595 ``set^1`` (or ``set^``), ``set^2``
1595 ``set^1`` (or ``set^``), ``set^2``
1596 First or second parent, respectively, of all changesets in set.
1596 First or second parent, respectively, of all changesets in set.
1597 """
1597 """
1598 try:
1598 try:
1599 n = int(n[1])
1599 n = int(n[1])
1600 if n not in (0, 1, 2):
1600 if n not in (0, 1, 2):
1601 raise ValueError
1601 raise ValueError
1602 except (TypeError, ValueError):
1602 except (TypeError, ValueError):
1603 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1603 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1604 ps = set()
1604 ps = set()
1605 cl = repo.changelog
1605 cl = repo.changelog
1606 for r in getset(repo, fullreposet(repo), x):
1606 for r in getset(repo, fullreposet(repo), x):
1607 if n == 0:
1607 if n == 0:
1608 ps.add(r)
1608 ps.add(r)
1609 elif n == 1:
1609 elif n == 1:
1610 ps.add(cl.parentrevs(r)[0])
1610 ps.add(cl.parentrevs(r)[0])
1611 elif n == 2:
1611 elif n == 2:
1612 parents = cl.parentrevs(r)
1612 parents = cl.parentrevs(r)
1613 if len(parents) > 1:
1613 if len(parents) > 1:
1614 ps.add(parents[1])
1614 ps.add(parents[1])
1615 return subset & ps
1615 return subset & ps
1616
1616
1617 @predicate('present(set)', safe=True)
1617 @predicate('present(set)', safe=True)
1618 def present(repo, subset, x):
1618 def present(repo, subset, x):
1619 """An empty set, if any revision in set isn't found; otherwise,
1619 """An empty set, if any revision in set isn't found; otherwise,
1620 all revisions in set.
1620 all revisions in set.
1621
1621
1622 If any of specified revisions is not present in the local repository,
1622 If any of specified revisions is not present in the local repository,
1623 the query is normally aborted. But this predicate allows the query
1623 the query is normally aborted. But this predicate allows the query
1624 to continue even in such cases.
1624 to continue even in such cases.
1625 """
1625 """
1626 try:
1626 try:
1627 return getset(repo, subset, x)
1627 return getset(repo, subset, x)
1628 except error.RepoLookupError:
1628 except error.RepoLookupError:
1629 return baseset()
1629 return baseset()
1630
1630
1631 # for internal use
1631 # for internal use
1632 @predicate('_notpublic', safe=True)
1632 @predicate('_notpublic', safe=True)
1633 def _notpublic(repo, subset, x):
1633 def _notpublic(repo, subset, x):
1634 getargs(x, 0, 0, "_notpublic takes no arguments")
1634 getargs(x, 0, 0, "_notpublic takes no arguments")
1635 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1635 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1636 if repo._phasecache._phasesets:
1636 if repo._phasecache._phasesets:
1637 s = set()
1637 s = set()
1638 for u in repo._phasecache._phasesets[1:]:
1638 for u in repo._phasecache._phasesets[1:]:
1639 s.update(u)
1639 s.update(u)
1640 s = baseset(s - repo.changelog.filteredrevs)
1640 s = baseset(s - repo.changelog.filteredrevs)
1641 s.sort()
1641 s.sort()
1642 return subset & s
1642 return subset & s
1643 else:
1643 else:
1644 phase = repo._phasecache.phase
1644 phase = repo._phasecache.phase
1645 target = phases.public
1645 target = phases.public
1646 condition = lambda r: phase(repo, r) != target
1646 condition = lambda r: phase(repo, r) != target
1647 return subset.filter(condition, condrepr=('<phase %r>', target),
1647 return subset.filter(condition, condrepr=('<phase %r>', target),
1648 cache=False)
1648 cache=False)
1649
1649
1650 @predicate('public()', safe=True)
1650 @predicate('public()', safe=True)
1651 def public(repo, subset, x):
1651 def public(repo, subset, x):
1652 """Changeset in public phase."""
1652 """Changeset in public phase."""
1653 # i18n: "public" is a keyword
1653 # i18n: "public" is a keyword
1654 getargs(x, 0, 0, _("public takes no arguments"))
1654 getargs(x, 0, 0, _("public takes no arguments"))
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('remote([id [,path]])', safe=True)
1661 @predicate('remote([id [,path]])', safe=True)
1662 def remote(repo, subset, x):
1662 def remote(repo, subset, x):
1663 """Local revision that corresponds to the given identifier in a
1663 """Local revision that corresponds to the given identifier in a
1664 remote repository, if present. Here, the '.' identifier is a
1664 remote repository, if present. Here, the '.' identifier is a
1665 synonym for the current local branch.
1665 synonym for the current local branch.
1666 """
1666 """
1667
1667
1668 from . import hg # avoid start-up nasties
1668 from . import hg # avoid start-up nasties
1669 # i18n: "remote" is a keyword
1669 # i18n: "remote" is a keyword
1670 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1670 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1671
1671
1672 q = '.'
1672 q = '.'
1673 if len(l) > 0:
1673 if len(l) > 0:
1674 # i18n: "remote" is a keyword
1674 # i18n: "remote" is a keyword
1675 q = getstring(l[0], _("remote requires a string id"))
1675 q = getstring(l[0], _("remote requires a string id"))
1676 if q == '.':
1676 if q == '.':
1677 q = repo['.'].branch()
1677 q = repo['.'].branch()
1678
1678
1679 dest = ''
1679 dest = ''
1680 if len(l) > 1:
1680 if len(l) > 1:
1681 # i18n: "remote" is a keyword
1681 # i18n: "remote" is a keyword
1682 dest = getstring(l[1], _("remote requires a repository path"))
1682 dest = getstring(l[1], _("remote requires a repository path"))
1683 dest = repo.ui.expandpath(dest or 'default')
1683 dest = repo.ui.expandpath(dest or 'default')
1684 dest, branches = hg.parseurl(dest)
1684 dest, branches = hg.parseurl(dest)
1685 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1685 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1686 if revs:
1686 if revs:
1687 revs = [repo.lookup(rev) for rev in revs]
1687 revs = [repo.lookup(rev) for rev in revs]
1688 other = hg.peer(repo, {}, dest)
1688 other = hg.peer(repo, {}, dest)
1689 n = other.lookup(q)
1689 n = other.lookup(q)
1690 if n in repo:
1690 if n in repo:
1691 r = repo[n].rev()
1691 r = repo[n].rev()
1692 if r in subset:
1692 if r in subset:
1693 return baseset([r])
1693 return baseset([r])
1694 return baseset()
1694 return baseset()
1695
1695
1696 @predicate('removes(pattern)', safe=True)
1696 @predicate('removes(pattern)', safe=True)
1697 def removes(repo, subset, x):
1697 def removes(repo, subset, x):
1698 """Changesets which remove files matching pattern.
1698 """Changesets which remove files matching pattern.
1699
1699
1700 The pattern without explicit kind like ``glob:`` is expected to be
1700 The pattern without explicit kind like ``glob:`` is expected to be
1701 relative to the current directory and match against a file or a
1701 relative to the current directory and match against a file or a
1702 directory.
1702 directory.
1703 """
1703 """
1704 # i18n: "removes" is a keyword
1704 # i18n: "removes" is a keyword
1705 pat = getstring(x, _("removes requires a pattern"))
1705 pat = getstring(x, _("removes requires a pattern"))
1706 return checkstatus(repo, subset, pat, 2)
1706 return checkstatus(repo, subset, pat, 2)
1707
1707
1708 @predicate('rev(number)', safe=True)
1708 @predicate('rev(number)', safe=True)
1709 def rev(repo, subset, x):
1709 def rev(repo, subset, x):
1710 """Revision with the given numeric identifier.
1710 """Revision with the given numeric identifier.
1711 """
1711 """
1712 # i18n: "rev" is a keyword
1712 # i18n: "rev" is a keyword
1713 l = getargs(x, 1, 1, _("rev requires one argument"))
1713 l = getargs(x, 1, 1, _("rev requires one argument"))
1714 try:
1714 try:
1715 # i18n: "rev" is a keyword
1715 # i18n: "rev" is a keyword
1716 l = int(getstring(l[0], _("rev requires a number")))
1716 l = int(getstring(l[0], _("rev requires a number")))
1717 except (TypeError, ValueError):
1717 except (TypeError, ValueError):
1718 # i18n: "rev" is a keyword
1718 # i18n: "rev" is a keyword
1719 raise error.ParseError(_("rev expects a number"))
1719 raise error.ParseError(_("rev expects a number"))
1720 if l not in repo.changelog and l != node.nullrev:
1720 if l not in repo.changelog and l != node.nullrev:
1721 return baseset()
1721 return baseset()
1722 return subset & baseset([l])
1722 return subset & baseset([l])
1723
1723
1724 @predicate('matching(revision [, field])', safe=True)
1724 @predicate('matching(revision [, field])', safe=True)
1725 def matching(repo, subset, x):
1725 def matching(repo, subset, x):
1726 """Changesets in which a given set of fields match the set of fields in the
1726 """Changesets in which a given set of fields match the set of fields in the
1727 selected revision or set.
1727 selected revision or set.
1728
1728
1729 To match more than one field pass the list of fields to match separated
1729 To match more than one field pass the list of fields to match separated
1730 by spaces (e.g. ``author description``).
1730 by spaces (e.g. ``author description``).
1731
1731
1732 Valid fields are most regular revision fields and some special fields.
1732 Valid fields are most regular revision fields and some special fields.
1733
1733
1734 Regular revision fields are ``description``, ``author``, ``branch``,
1734 Regular revision fields are ``description``, ``author``, ``branch``,
1735 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1735 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1736 and ``diff``.
1736 and ``diff``.
1737 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1737 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1738 contents of the revision. Two revisions matching their ``diff`` will
1738 contents of the revision. Two revisions matching their ``diff`` will
1739 also match their ``files``.
1739 also match their ``files``.
1740
1740
1741 Special fields are ``summary`` and ``metadata``:
1741 Special fields are ``summary`` and ``metadata``:
1742 ``summary`` matches the first line of the description.
1742 ``summary`` matches the first line of the description.
1743 ``metadata`` is equivalent to matching ``description user date``
1743 ``metadata`` is equivalent to matching ``description user date``
1744 (i.e. it matches the main metadata fields).
1744 (i.e. it matches the main metadata fields).
1745
1745
1746 ``metadata`` is the default field which is used when no fields are
1746 ``metadata`` is the default field which is used when no fields are
1747 specified. You can match more than one field at a time.
1747 specified. You can match more than one field at a time.
1748 """
1748 """
1749 # i18n: "matching" is a keyword
1749 # i18n: "matching" is a keyword
1750 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1750 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1751
1751
1752 revs = getset(repo, fullreposet(repo), l[0])
1752 revs = getset(repo, fullreposet(repo), l[0])
1753
1753
1754 fieldlist = ['metadata']
1754 fieldlist = ['metadata']
1755 if len(l) > 1:
1755 if len(l) > 1:
1756 fieldlist = getstring(l[1],
1756 fieldlist = getstring(l[1],
1757 # i18n: "matching" is a keyword
1757 # i18n: "matching" is a keyword
1758 _("matching requires a string "
1758 _("matching requires a string "
1759 "as its second argument")).split()
1759 "as its second argument")).split()
1760
1760
1761 # Make sure that there are no repeated fields,
1761 # Make sure that there are no repeated fields,
1762 # expand the 'special' 'metadata' field type
1762 # expand the 'special' 'metadata' field type
1763 # and check the 'files' whenever we check the 'diff'
1763 # and check the 'files' whenever we check the 'diff'
1764 fields = []
1764 fields = []
1765 for field in fieldlist:
1765 for field in fieldlist:
1766 if field == 'metadata':
1766 if field == 'metadata':
1767 fields += ['user', 'description', 'date']
1767 fields += ['user', 'description', 'date']
1768 elif field == 'diff':
1768 elif field == 'diff':
1769 # a revision matching the diff must also match the files
1769 # a revision matching the diff must also match the files
1770 # since matching the diff is very costly, make sure to
1770 # since matching the diff is very costly, make sure to
1771 # also match the files first
1771 # also match the files first
1772 fields += ['files', 'diff']
1772 fields += ['files', 'diff']
1773 else:
1773 else:
1774 if field == 'author':
1774 if field == 'author':
1775 field = 'user'
1775 field = 'user'
1776 fields.append(field)
1776 fields.append(field)
1777 fields = set(fields)
1777 fields = set(fields)
1778 if 'summary' in fields and 'description' in fields:
1778 if 'summary' in fields and 'description' in fields:
1779 # If a revision matches its description it also matches its summary
1779 # If a revision matches its description it also matches its summary
1780 fields.discard('summary')
1780 fields.discard('summary')
1781
1781
1782 # We may want to match more than one field
1782 # We may want to match more than one field
1783 # Not all fields take the same amount of time to be matched
1783 # Not all fields take the same amount of time to be matched
1784 # Sort the selected fields in order of increasing matching cost
1784 # Sort the selected fields in order of increasing matching cost
1785 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1785 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1786 'files', 'description', 'substate', 'diff']
1786 'files', 'description', 'substate', 'diff']
1787 def fieldkeyfunc(f):
1787 def fieldkeyfunc(f):
1788 try:
1788 try:
1789 return fieldorder.index(f)
1789 return fieldorder.index(f)
1790 except ValueError:
1790 except ValueError:
1791 # assume an unknown field is very costly
1791 # assume an unknown field is very costly
1792 return len(fieldorder)
1792 return len(fieldorder)
1793 fields = list(fields)
1793 fields = list(fields)
1794 fields.sort(key=fieldkeyfunc)
1794 fields.sort(key=fieldkeyfunc)
1795
1795
1796 # Each field will be matched with its own "getfield" function
1796 # Each field will be matched with its own "getfield" function
1797 # which will be added to the getfieldfuncs array of functions
1797 # which will be added to the getfieldfuncs array of functions
1798 getfieldfuncs = []
1798 getfieldfuncs = []
1799 _funcs = {
1799 _funcs = {
1800 'user': lambda r: repo[r].user(),
1800 'user': lambda r: repo[r].user(),
1801 'branch': lambda r: repo[r].branch(),
1801 'branch': lambda r: repo[r].branch(),
1802 'date': lambda r: repo[r].date(),
1802 'date': lambda r: repo[r].date(),
1803 'description': lambda r: repo[r].description(),
1803 'description': lambda r: repo[r].description(),
1804 'files': lambda r: repo[r].files(),
1804 'files': lambda r: repo[r].files(),
1805 'parents': lambda r: repo[r].parents(),
1805 'parents': lambda r: repo[r].parents(),
1806 'phase': lambda r: repo[r].phase(),
1806 'phase': lambda r: repo[r].phase(),
1807 'substate': lambda r: repo[r].substate,
1807 'substate': lambda r: repo[r].substate,
1808 'summary': lambda r: repo[r].description().splitlines()[0],
1808 'summary': lambda r: repo[r].description().splitlines()[0],
1809 'diff': lambda r: list(repo[r].diff(git=True),)
1809 'diff': lambda r: list(repo[r].diff(git=True),)
1810 }
1810 }
1811 for info in fields:
1811 for info in fields:
1812 getfield = _funcs.get(info, None)
1812 getfield = _funcs.get(info, None)
1813 if getfield is None:
1813 if getfield is None:
1814 raise error.ParseError(
1814 raise error.ParseError(
1815 # i18n: "matching" is a keyword
1815 # i18n: "matching" is a keyword
1816 _("unexpected field name passed to matching: %s") % info)
1816 _("unexpected field name passed to matching: %s") % info)
1817 getfieldfuncs.append(getfield)
1817 getfieldfuncs.append(getfield)
1818 # convert the getfield array of functions into a "getinfo" function
1818 # convert the getfield array of functions into a "getinfo" function
1819 # which returns an array of field values (or a single value if there
1819 # which returns an array of field values (or a single value if there
1820 # is only one field to match)
1820 # is only one field to match)
1821 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1821 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1822
1822
1823 def matches(x):
1823 def matches(x):
1824 for rev in revs:
1824 for rev in revs:
1825 target = getinfo(rev)
1825 target = getinfo(rev)
1826 match = True
1826 match = True
1827 for n, f in enumerate(getfieldfuncs):
1827 for n, f in enumerate(getfieldfuncs):
1828 if target[n] != f(x):
1828 if target[n] != f(x):
1829 match = False
1829 match = False
1830 if match:
1830 if match:
1831 return True
1831 return True
1832 return False
1832 return False
1833
1833
1834 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1834 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1835
1835
1836 @predicate('reverse(set)', safe=True)
1836 @predicate('reverse(set)', safe=True)
1837 def reverse(repo, subset, x):
1837 def reverse(repo, subset, x):
1838 """Reverse order of set.
1838 """Reverse order of set.
1839 """
1839 """
1840 l = getset(repo, subset, x)
1840 l = getset(repo, subset, x)
1841 l.reverse()
1841 l.reverse()
1842 return l
1842 return l
1843
1843
1844 @predicate('roots(set)', safe=True)
1844 @predicate('roots(set)', safe=True)
1845 def roots(repo, subset, x):
1845 def roots(repo, subset, x):
1846 """Changesets in set with no parent changeset in set.
1846 """Changesets in set with no parent changeset in set.
1847 """
1847 """
1848 s = getset(repo, fullreposet(repo), x)
1848 s = getset(repo, fullreposet(repo), x)
1849 parents = repo.changelog.parentrevs
1849 parents = repo.changelog.parentrevs
1850 def filter(r):
1850 def filter(r):
1851 for p in parents(r):
1851 for p in parents(r):
1852 if 0 <= p and p in s:
1852 if 0 <= p and p in s:
1853 return False
1853 return False
1854 return True
1854 return True
1855 return subset & s.filter(filter, condrepr='<roots>')
1855 return subset & s.filter(filter, condrepr='<roots>')
1856
1856
1857 _sortkeyfuncs = {
1857 _sortkeyfuncs = {
1858 'rev': lambda c: c.rev(),
1858 'rev': lambda c: c.rev(),
1859 'branch': lambda c: c.branch(),
1859 'branch': lambda c: c.branch(),
1860 'desc': lambda c: c.description(),
1860 'desc': lambda c: c.description(),
1861 'user': lambda c: c.user(),
1861 'user': lambda c: c.user(),
1862 'author': lambda c: c.user(),
1862 'author': lambda c: c.user(),
1863 'date': lambda c: c.date()[0],
1863 'date': lambda c: c.date()[0],
1864 }
1864 }
1865
1865
1866 def _getsortargs(x):
1866 def _getsortargs(x):
1867 """Parse sort options into (set, [(key, reverse)], opts)"""
1867 """Parse sort options into (set, [(key, reverse)], opts)"""
1868 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1868 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1869 if 'set' not in args:
1869 if 'set' not in args:
1870 # i18n: "sort" is a keyword
1870 # i18n: "sort" is a keyword
1871 raise error.ParseError(_('sort requires one or two arguments'))
1871 raise error.ParseError(_('sort requires one or two arguments'))
1872 keys = "rev"
1872 keys = "rev"
1873 if 'keys' in args:
1873 if 'keys' in args:
1874 # i18n: "sort" is a keyword
1874 # i18n: "sort" is a keyword
1875 keys = getstring(args['keys'], _("sort spec must be a string"))
1875 keys = getstring(args['keys'], _("sort spec must be a string"))
1876
1876
1877 keyflags = []
1877 keyflags = []
1878 for k in keys.split():
1878 for k in keys.split():
1879 fk = k
1879 fk = k
1880 reverse = (k[0] == '-')
1880 reverse = (k[0] == '-')
1881 if reverse:
1881 if reverse:
1882 k = k[1:]
1882 k = k[1:]
1883 if k not in _sortkeyfuncs and k != 'topo':
1883 if k not in _sortkeyfuncs and k != 'topo':
1884 raise error.ParseError(_("unknown sort key %r") % fk)
1884 raise error.ParseError(_("unknown sort key %r") % fk)
1885 keyflags.append((k, reverse))
1885 keyflags.append((k, reverse))
1886
1886
1887 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1887 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1888 # i18n: "topo" is a keyword
1888 # i18n: "topo" is a keyword
1889 raise error.ParseError(_('topo sort order cannot be combined '
1889 raise error.ParseError(_('topo sort order cannot be combined '
1890 'with other sort keys'))
1890 'with other sort keys'))
1891
1891
1892 opts = {}
1892 opts = {}
1893 if 'topo.firstbranch' in args:
1893 if 'topo.firstbranch' in args:
1894 if any(k == 'topo' for k, reverse in keyflags):
1894 if any(k == 'topo' for k, reverse in keyflags):
1895 opts['topo.firstbranch'] = args['topo.firstbranch']
1895 opts['topo.firstbranch'] = args['topo.firstbranch']
1896 else:
1896 else:
1897 # i18n: "topo" and "topo.firstbranch" are keywords
1897 # i18n: "topo" and "topo.firstbranch" are keywords
1898 raise error.ParseError(_('topo.firstbranch can only be used '
1898 raise error.ParseError(_('topo.firstbranch can only be used '
1899 'when using the topo sort key'))
1899 'when using the topo sort key'))
1900
1900
1901 return args['set'], keyflags, opts
1901 return args['set'], keyflags, opts
1902
1902
1903 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1903 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1904 def sort(repo, subset, x):
1904 def sort(repo, subset, x):
1905 """Sort set by keys. The default sort order is ascending, specify a key
1905 """Sort set by keys. The default sort order is ascending, specify a key
1906 as ``-key`` to sort in descending order.
1906 as ``-key`` to sort in descending order.
1907
1907
1908 The keys can be:
1908 The keys can be:
1909
1909
1910 - ``rev`` for the revision number,
1910 - ``rev`` for the revision number,
1911 - ``branch`` for the branch name,
1911 - ``branch`` for the branch name,
1912 - ``desc`` for the commit message (description),
1912 - ``desc`` for the commit message (description),
1913 - ``user`` for user name (``author`` can be used as an alias),
1913 - ``user`` for user name (``author`` can be used as an alias),
1914 - ``date`` for the commit date
1914 - ``date`` for the commit date
1915 - ``topo`` for a reverse topographical sort
1915 - ``topo`` for a reverse topographical sort
1916
1916
1917 The ``topo`` sort order cannot be combined with other sort keys. This sort
1917 The ``topo`` sort order cannot be combined with other sort keys. This sort
1918 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1918 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1919 specifies what topographical branches to prioritize in the sort.
1919 specifies what topographical branches to prioritize in the sort.
1920
1920
1921 """
1921 """
1922 s, keyflags, opts = _getsortargs(x)
1922 s, keyflags, opts = _getsortargs(x)
1923 revs = getset(repo, subset, s)
1923 revs = getset(repo, subset, s)
1924
1924
1925 if not keyflags:
1925 if not keyflags:
1926 return revs
1926 return revs
1927 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1927 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1928 revs.sort(reverse=keyflags[0][1])
1928 revs.sort(reverse=keyflags[0][1])
1929 return revs
1929 return revs
1930 elif keyflags[0][0] == "topo":
1930 elif keyflags[0][0] == "topo":
1931 firstbranch = ()
1931 firstbranch = ()
1932 if 'topo.firstbranch' in opts:
1932 if 'topo.firstbranch' in opts:
1933 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1933 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1934 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1934 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1935 istopo=True)
1935 istopo=True)
1936 if keyflags[0][1]:
1936 if keyflags[0][1]:
1937 revs.reverse()
1937 revs.reverse()
1938 return revs
1938 return revs
1939
1939
1940 # sort() is guaranteed to be stable
1940 # sort() is guaranteed to be stable
1941 ctxs = [repo[r] for r in revs]
1941 ctxs = [repo[r] for r in revs]
1942 for k, reverse in reversed(keyflags):
1942 for k, reverse in reversed(keyflags):
1943 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1943 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1944 return baseset([c.rev() for c in ctxs])
1944 return baseset([c.rev() for c in ctxs])
1945
1945
1946 def _toposort(revs, parentsfunc, firstbranch=()):
1946 def _toposort(revs, parentsfunc, firstbranch=()):
1947 """Yield revisions from heads to roots one (topo) branch at a time.
1947 """Yield revisions from heads to roots one (topo) branch at a time.
1948
1948
1949 This function aims to be used by a graph generator that wishes to minimize
1949 This function aims to be used by a graph generator that wishes to minimize
1950 the number of parallel branches and their interleaving.
1950 the number of parallel branches and their interleaving.
1951
1951
1952 Example iteration order (numbers show the "true" order in a changelog):
1952 Example iteration order (numbers show the "true" order in a changelog):
1953
1953
1954 o 4
1954 o 4
1955 |
1955 |
1956 o 1
1956 o 1
1957 |
1957 |
1958 | o 3
1958 | o 3
1959 | |
1959 | |
1960 | o 2
1960 | o 2
1961 |/
1961 |/
1962 o 0
1962 o 0
1963
1963
1964 Note that the ancestors of merges are understood by the current
1964 Note that the ancestors of merges are understood by the current
1965 algorithm to be on the same branch. This means no reordering will
1965 algorithm to be on the same branch. This means no reordering will
1966 occur behind a merge.
1966 occur behind a merge.
1967 """
1967 """
1968
1968
1969 ### Quick summary of the algorithm
1969 ### Quick summary of the algorithm
1970 #
1970 #
1971 # This function is based around a "retention" principle. We keep revisions
1971 # This function is based around a "retention" principle. We keep revisions
1972 # in memory until we are ready to emit a whole branch that immediately
1972 # in memory until we are ready to emit a whole branch that immediately
1973 # "merges" into an existing one. This reduces the number of parallel
1973 # "merges" into an existing one. This reduces the number of parallel
1974 # branches with interleaved revisions.
1974 # branches with interleaved revisions.
1975 #
1975 #
1976 # During iteration revs are split into two groups:
1976 # During iteration revs are split into two groups:
1977 # A) revision already emitted
1977 # A) revision already emitted
1978 # B) revision in "retention". They are stored as different subgroups.
1978 # B) revision in "retention". They are stored as different subgroups.
1979 #
1979 #
1980 # for each REV, we do the following logic:
1980 # for each REV, we do the following logic:
1981 #
1981 #
1982 # 1) if REV is a parent of (A), we will emit it. If there is a
1982 # 1) if REV is a parent of (A), we will emit it. If there is a
1983 # retention group ((B) above) that is blocked on REV being
1983 # retention group ((B) above) that is blocked on REV being
1984 # available, we emit all the revisions out of that retention
1984 # available, we emit all the revisions out of that retention
1985 # group first.
1985 # group first.
1986 #
1986 #
1987 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1987 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1988 # available, if such subgroup exist, we add REV to it and the subgroup is
1988 # available, if such subgroup exist, we add REV to it and the subgroup is
1989 # now awaiting for REV.parents() to be available.
1989 # now awaiting for REV.parents() to be available.
1990 #
1990 #
1991 # 3) finally if no such group existed in (B), we create a new subgroup.
1991 # 3) finally if no such group existed in (B), we create a new subgroup.
1992 #
1992 #
1993 #
1993 #
1994 # To bootstrap the algorithm, we emit the tipmost revision (which
1994 # To bootstrap the algorithm, we emit the tipmost revision (which
1995 # puts it in group (A) from above).
1995 # puts it in group (A) from above).
1996
1996
1997 revs.sort(reverse=True)
1997 revs.sort(reverse=True)
1998
1998
1999 # Set of parents of revision that have been emitted. They can be considered
1999 # Set of parents of revision that have been emitted. They can be considered
2000 # unblocked as the graph generator is already aware of them so there is no
2000 # unblocked as the graph generator is already aware of them so there is no
2001 # need to delay the revisions that reference them.
2001 # need to delay the revisions that reference them.
2002 #
2002 #
2003 # If someone wants to prioritize a branch over the others, pre-filling this
2003 # If someone wants to prioritize a branch over the others, pre-filling this
2004 # set will force all other branches to wait until this branch is ready to be
2004 # set will force all other branches to wait until this branch is ready to be
2005 # emitted.
2005 # emitted.
2006 unblocked = set(firstbranch)
2006 unblocked = set(firstbranch)
2007
2007
2008 # list of groups waiting to be displayed, each group is defined by:
2008 # list of groups waiting to be displayed, each group is defined by:
2009 #
2009 #
2010 # (revs: lists of revs waiting to be displayed,
2010 # (revs: lists of revs waiting to be displayed,
2011 # blocked: set of that cannot be displayed before those in 'revs')
2011 # blocked: set of that cannot be displayed before those in 'revs')
2012 #
2012 #
2013 # The second value ('blocked') correspond to parents of any revision in the
2013 # The second value ('blocked') correspond to parents of any revision in the
2014 # group ('revs') that is not itself contained in the group. The main idea
2014 # group ('revs') that is not itself contained in the group. The main idea
2015 # of this algorithm is to delay as much as possible the emission of any
2015 # of this algorithm is to delay as much as possible the emission of any
2016 # revision. This means waiting for the moment we are about to display
2016 # revision. This means waiting for the moment we are about to display
2017 # these parents to display the revs in a group.
2017 # these parents to display the revs in a group.
2018 #
2018 #
2019 # This first implementation is smart until it encounters a merge: it will
2019 # This first implementation is smart until it encounters a merge: it will
2020 # emit revs as soon as any parent is about to be emitted and can grow an
2020 # emit revs as soon as any parent is about to be emitted and can grow an
2021 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2021 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2022 # retains new branches but gives up on any special ordering for ancestors
2022 # retains new branches but gives up on any special ordering for ancestors
2023 # of merges. The implementation can be improved to handle this better.
2023 # of merges. The implementation can be improved to handle this better.
2024 #
2024 #
2025 # The first subgroup is special. It corresponds to all the revision that
2025 # The first subgroup is special. It corresponds to all the revision that
2026 # were already emitted. The 'revs' lists is expected to be empty and the
2026 # were already emitted. The 'revs' lists is expected to be empty and the
2027 # 'blocked' set contains the parents revisions of already emitted revision.
2027 # 'blocked' set contains the parents revisions of already emitted revision.
2028 #
2028 #
2029 # You could pre-seed the <parents> set of groups[0] to a specific
2029 # You could pre-seed the <parents> set of groups[0] to a specific
2030 # changesets to select what the first emitted branch should be.
2030 # changesets to select what the first emitted branch should be.
2031 groups = [([], unblocked)]
2031 groups = [([], unblocked)]
2032 pendingheap = []
2032 pendingheap = []
2033 pendingset = set()
2033 pendingset = set()
2034
2034
2035 heapq.heapify(pendingheap)
2035 heapq.heapify(pendingheap)
2036 heappop = heapq.heappop
2036 heappop = heapq.heappop
2037 heappush = heapq.heappush
2037 heappush = heapq.heappush
2038 for currentrev in revs:
2038 for currentrev in revs:
2039 # Heap works with smallest element, we want highest so we invert
2039 # Heap works with smallest element, we want highest so we invert
2040 if currentrev not in pendingset:
2040 if currentrev not in pendingset:
2041 heappush(pendingheap, -currentrev)
2041 heappush(pendingheap, -currentrev)
2042 pendingset.add(currentrev)
2042 pendingset.add(currentrev)
2043 # iterates on pending rev until after the current rev have been
2043 # iterates on pending rev until after the current rev have been
2044 # processed.
2044 # processed.
2045 rev = None
2045 rev = None
2046 while rev != currentrev:
2046 while rev != currentrev:
2047 rev = -heappop(pendingheap)
2047 rev = -heappop(pendingheap)
2048 pendingset.remove(rev)
2048 pendingset.remove(rev)
2049
2049
2050 # Seek for a subgroup blocked, waiting for the current revision.
2050 # Seek for a subgroup blocked, waiting for the current revision.
2051 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2051 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2052
2052
2053 if matching:
2053 if matching:
2054 # The main idea is to gather together all sets that are blocked
2054 # The main idea is to gather together all sets that are blocked
2055 # on the same revision.
2055 # on the same revision.
2056 #
2056 #
2057 # Groups are merged when a common blocking ancestor is
2057 # Groups are merged when a common blocking ancestor is
2058 # observed. For example, given two groups:
2058 # observed. For example, given two groups:
2059 #
2059 #
2060 # revs [5, 4] waiting for 1
2060 # revs [5, 4] waiting for 1
2061 # revs [3, 2] waiting for 1
2061 # revs [3, 2] waiting for 1
2062 #
2062 #
2063 # These two groups will be merged when we process
2063 # These two groups will be merged when we process
2064 # 1. In theory, we could have merged the groups when
2064 # 1. In theory, we could have merged the groups when
2065 # we added 2 to the group it is now in (we could have
2065 # we added 2 to the group it is now in (we could have
2066 # noticed the groups were both blocked on 1 then), but
2066 # noticed the groups were both blocked on 1 then), but
2067 # the way it works now makes the algorithm simpler.
2067 # the way it works now makes the algorithm simpler.
2068 #
2068 #
2069 # We also always keep the oldest subgroup first. We can
2069 # We also always keep the oldest subgroup first. We can
2070 # probably improve the behavior by having the longest set
2070 # probably improve the behavior by having the longest set
2071 # first. That way, graph algorithms could minimise the length
2071 # first. That way, graph algorithms could minimise the length
2072 # of parallel lines their drawing. This is currently not done.
2072 # of parallel lines their drawing. This is currently not done.
2073 targetidx = matching.pop(0)
2073 targetidx = matching.pop(0)
2074 trevs, tparents = groups[targetidx]
2074 trevs, tparents = groups[targetidx]
2075 for i in matching:
2075 for i in matching:
2076 gr = groups[i]
2076 gr = groups[i]
2077 trevs.extend(gr[0])
2077 trevs.extend(gr[0])
2078 tparents |= gr[1]
2078 tparents |= gr[1]
2079 # delete all merged subgroups (except the one we kept)
2079 # delete all merged subgroups (except the one we kept)
2080 # (starting from the last subgroup for performance and
2080 # (starting from the last subgroup for performance and
2081 # sanity reasons)
2081 # sanity reasons)
2082 for i in reversed(matching):
2082 for i in reversed(matching):
2083 del groups[i]
2083 del groups[i]
2084 else:
2084 else:
2085 # This is a new head. We create a new subgroup for it.
2085 # This is a new head. We create a new subgroup for it.
2086 targetidx = len(groups)
2086 targetidx = len(groups)
2087 groups.append(([], set([rev])))
2087 groups.append(([], set([rev])))
2088
2088
2089 gr = groups[targetidx]
2089 gr = groups[targetidx]
2090
2090
2091 # We now add the current nodes to this subgroups. This is done
2091 # We now add the current nodes to this subgroups. This is done
2092 # after the subgroup merging because all elements from a subgroup
2092 # after the subgroup merging because all elements from a subgroup
2093 # that relied on this rev must precede it.
2093 # that relied on this rev must precede it.
2094 #
2094 #
2095 # we also update the <parents> set to include the parents of the
2095 # we also update the <parents> set to include the parents of the
2096 # new nodes.
2096 # new nodes.
2097 if rev == currentrev: # only display stuff in rev
2097 if rev == currentrev: # only display stuff in rev
2098 gr[0].append(rev)
2098 gr[0].append(rev)
2099 gr[1].remove(rev)
2099 gr[1].remove(rev)
2100 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2100 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2101 gr[1].update(parents)
2101 gr[1].update(parents)
2102 for p in parents:
2102 for p in parents:
2103 if p not in pendingset:
2103 if p not in pendingset:
2104 pendingset.add(p)
2104 pendingset.add(p)
2105 heappush(pendingheap, -p)
2105 heappush(pendingheap, -p)
2106
2106
2107 # Look for a subgroup to display
2107 # Look for a subgroup to display
2108 #
2108 #
2109 # When unblocked is empty (if clause), we were not waiting for any
2109 # When unblocked is empty (if clause), we were not waiting for any
2110 # revisions during the first iteration (if no priority was given) or
2110 # revisions during the first iteration (if no priority was given) or
2111 # if we emitted a whole disconnected set of the graph (reached a
2111 # if we emitted a whole disconnected set of the graph (reached a
2112 # root). In that case we arbitrarily take the oldest known
2112 # root). In that case we arbitrarily take the oldest known
2113 # subgroup. The heuristic could probably be better.
2113 # subgroup. The heuristic could probably be better.
2114 #
2114 #
2115 # Otherwise (elif clause) if the subgroup is blocked on
2115 # Otherwise (elif clause) if the subgroup is blocked on
2116 # a revision we just emitted, we can safely emit it as
2116 # a revision we just emitted, we can safely emit it as
2117 # well.
2117 # well.
2118 if not unblocked:
2118 if not unblocked:
2119 if len(groups) > 1: # display other subset
2119 if len(groups) > 1: # display other subset
2120 targetidx = 1
2120 targetidx = 1
2121 gr = groups[1]
2121 gr = groups[1]
2122 elif not gr[1] & unblocked:
2122 elif not gr[1] & unblocked:
2123 gr = None
2123 gr = None
2124
2124
2125 if gr is not None:
2125 if gr is not None:
2126 # update the set of awaited revisions with the one from the
2126 # update the set of awaited revisions with the one from the
2127 # subgroup
2127 # subgroup
2128 unblocked |= gr[1]
2128 unblocked |= gr[1]
2129 # output all revisions in the subgroup
2129 # output all revisions in the subgroup
2130 for r in gr[0]:
2130 for r in gr[0]:
2131 yield r
2131 yield r
2132 # delete the subgroup that you just output
2132 # delete the subgroup that you just output
2133 # unless it is groups[0] in which case you just empty it.
2133 # unless it is groups[0] in which case you just empty it.
2134 if targetidx:
2134 if targetidx:
2135 del groups[targetidx]
2135 del groups[targetidx]
2136 else:
2136 else:
2137 gr[0][:] = []
2137 gr[0][:] = []
2138 # Check if we have some subgroup waiting for revisions we are not going to
2138 # Check if we have some subgroup waiting for revisions we are not going to
2139 # iterate over
2139 # iterate over
2140 for g in groups:
2140 for g in groups:
2141 for r in g[0]:
2141 for r in g[0]:
2142 yield r
2142 yield r
2143
2143
2144 @predicate('subrepo([pattern])')
2144 @predicate('subrepo([pattern])')
2145 def subrepo(repo, subset, x):
2145 def subrepo(repo, subset, x):
2146 """Changesets that add, modify or remove the given subrepo. If no subrepo
2146 """Changesets that add, modify or remove the given subrepo. If no subrepo
2147 pattern is named, any subrepo changes are returned.
2147 pattern is named, any subrepo changes are returned.
2148 """
2148 """
2149 # i18n: "subrepo" is a keyword
2149 # i18n: "subrepo" is a keyword
2150 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2150 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2151 pat = None
2151 pat = None
2152 if len(args) != 0:
2152 if len(args) != 0:
2153 pat = getstring(args[0], _("subrepo requires a pattern"))
2153 pat = getstring(args[0], _("subrepo requires a pattern"))
2154
2154
2155 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2155 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2156
2156
2157 def submatches(names):
2157 def submatches(names):
2158 k, p, m = util.stringmatcher(pat)
2158 k, p, m = util.stringmatcher(pat)
2159 for name in names:
2159 for name in names:
2160 if m(name):
2160 if m(name):
2161 yield name
2161 yield name
2162
2162
2163 def matches(x):
2163 def matches(x):
2164 c = repo[x]
2164 c = repo[x]
2165 s = repo.status(c.p1().node(), c.node(), match=m)
2165 s = repo.status(c.p1().node(), c.node(), match=m)
2166
2166
2167 if pat is None:
2167 if pat is None:
2168 return s.added or s.modified or s.removed
2168 return s.added or s.modified or s.removed
2169
2169
2170 if s.added:
2170 if s.added:
2171 return any(submatches(c.substate.keys()))
2171 return any(submatches(c.substate.keys()))
2172
2172
2173 if s.modified:
2173 if s.modified:
2174 subs = set(c.p1().substate.keys())
2174 subs = set(c.p1().substate.keys())
2175 subs.update(c.substate.keys())
2175 subs.update(c.substate.keys())
2176
2176
2177 for path in submatches(subs):
2177 for path in submatches(subs):
2178 if c.p1().substate.get(path) != c.substate.get(path):
2178 if c.p1().substate.get(path) != c.substate.get(path):
2179 return True
2179 return True
2180
2180
2181 if s.removed:
2181 if s.removed:
2182 return any(submatches(c.p1().substate.keys()))
2182 return any(submatches(c.p1().substate.keys()))
2183
2183
2184 return False
2184 return False
2185
2185
2186 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2186 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2187
2187
2188 def _substringmatcher(pattern):
2188 def _substringmatcher(pattern):
2189 kind, pattern, matcher = util.stringmatcher(pattern)
2189 kind, pattern, matcher = util.stringmatcher(pattern)
2190 if kind == 'literal':
2190 if kind == 'literal':
2191 matcher = lambda s: pattern in s
2191 matcher = lambda s: pattern in s
2192 return kind, pattern, matcher
2192 return kind, pattern, matcher
2193
2193
2194 @predicate('tag([name])', safe=True)
2194 @predicate('tag([name])', safe=True)
2195 def tag(repo, subset, x):
2195 def tag(repo, subset, x):
2196 """The specified tag by name, or all tagged revisions if no name is given.
2196 """The specified tag by name, or all tagged revisions if no name is given.
2197
2197
2198 If `name` starts with `re:`, the remainder of the name is treated as
2198 If `name` starts with `re:`, the remainder of the name is treated as
2199 a regular expression. To match a tag that actually starts with `re:`,
2199 a regular expression. To match a tag that actually starts with `re:`,
2200 use the prefix `literal:`.
2200 use the prefix `literal:`.
2201 """
2201 """
2202 # i18n: "tag" is a keyword
2202 # i18n: "tag" is a keyword
2203 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2203 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2204 cl = repo.changelog
2204 cl = repo.changelog
2205 if args:
2205 if args:
2206 pattern = getstring(args[0],
2206 pattern = getstring(args[0],
2207 # i18n: "tag" is a keyword
2207 # i18n: "tag" is a keyword
2208 _('the argument to tag must be a string'))
2208 _('the argument to tag must be a string'))
2209 kind, pattern, matcher = util.stringmatcher(pattern)
2209 kind, pattern, matcher = util.stringmatcher(pattern)
2210 if kind == 'literal':
2210 if kind == 'literal':
2211 # avoid resolving all tags
2211 # avoid resolving all tags
2212 tn = repo._tagscache.tags.get(pattern, None)
2212 tn = repo._tagscache.tags.get(pattern, None)
2213 if tn is None:
2213 if tn is None:
2214 raise error.RepoLookupError(_("tag '%s' does not exist")
2214 raise error.RepoLookupError(_("tag '%s' does not exist")
2215 % pattern)
2215 % pattern)
2216 s = set([repo[tn].rev()])
2216 s = set([repo[tn].rev()])
2217 else:
2217 else:
2218 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2218 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2219 else:
2219 else:
2220 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2220 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2221 return subset & s
2221 return subset & s
2222
2222
2223 @predicate('tagged', safe=True)
2223 @predicate('tagged', safe=True)
2224 def tagged(repo, subset, x):
2224 def tagged(repo, subset, x):
2225 return tag(repo, subset, x)
2225 return tag(repo, subset, x)
2226
2226
2227 @predicate('unstable()', safe=True)
2227 @predicate('unstable()', safe=True)
2228 def unstable(repo, subset, x):
2228 def unstable(repo, subset, x):
2229 """Non-obsolete changesets with obsolete ancestors.
2229 """Non-obsolete changesets with obsolete ancestors.
2230 """
2230 """
2231 # i18n: "unstable" is a keyword
2231 # i18n: "unstable" is a keyword
2232 getargs(x, 0, 0, _("unstable takes no arguments"))
2232 getargs(x, 0, 0, _("unstable takes no arguments"))
2233 unstables = obsmod.getrevs(repo, 'unstable')
2233 unstables = obsmod.getrevs(repo, 'unstable')
2234 return subset & unstables
2234 return subset & unstables
2235
2235
2236
2236
2237 @predicate('user(string)', safe=True)
2237 @predicate('user(string)', safe=True)
2238 def user(repo, subset, x):
2238 def user(repo, subset, x):
2239 """User name contains string. The match is case-insensitive.
2239 """User name contains string. The match is case-insensitive.
2240
2240
2241 If `string` starts with `re:`, the remainder of the string is treated as
2241 If `string` starts with `re:`, the remainder of the string is treated as
2242 a regular expression. To match a user that actually contains `re:`, use
2242 a regular expression. To match a user that actually contains `re:`, use
2243 the prefix `literal:`.
2243 the prefix `literal:`.
2244 """
2244 """
2245 return author(repo, subset, x)
2245 return author(repo, subset, x)
2246
2246
2247 # experimental
2247 # experimental
2248 @predicate('wdir', safe=True)
2248 @predicate('wdir', safe=True)
2249 def wdir(repo, subset, x):
2249 def wdir(repo, subset, x):
2250 # i18n: "wdir" is a keyword
2250 # i18n: "wdir" is a keyword
2251 getargs(x, 0, 0, _("wdir takes no arguments"))
2251 getargs(x, 0, 0, _("wdir takes no arguments"))
2252 if node.wdirrev in subset or isinstance(subset, fullreposet):
2252 if node.wdirrev in subset or isinstance(subset, fullreposet):
2253 return baseset([node.wdirrev])
2253 return baseset([node.wdirrev])
2254 return baseset()
2254 return baseset()
2255
2255
2256 def _orderedlist(repo, subset, x):
2256 def _orderedlist(repo, subset, x):
2257 s = getstring(x, "internal error")
2257 s = getstring(x, "internal error")
2258 if not s:
2258 if not s:
2259 return baseset()
2259 return baseset()
2260 # remove duplicates here. it's difficult for caller to deduplicate sets
2260 # remove duplicates here. it's difficult for caller to deduplicate sets
2261 # because different symbols can point to the same rev.
2261 # because different symbols can point to the same rev.
2262 cl = repo.changelog
2262 cl = repo.changelog
2263 ls = []
2263 ls = []
2264 seen = set()
2264 seen = set()
2265 for t in s.split('\0'):
2265 for t in s.split('\0'):
2266 try:
2266 try:
2267 # fast path for integer revision
2267 # fast path for integer revision
2268 r = int(t)
2268 r = int(t)
2269 if str(r) != t or r not in cl:
2269 if str(r) != t or r not in cl:
2270 raise ValueError
2270 raise ValueError
2271 revs = [r]
2271 revs = [r]
2272 except ValueError:
2272 except ValueError:
2273 revs = stringset(repo, subset, t)
2273 revs = stringset(repo, subset, t)
2274
2274
2275 for r in revs:
2275 for r in revs:
2276 if r in seen:
2276 if r in seen:
2277 continue
2277 continue
2278 if (r in subset
2278 if (r in subset
2279 or r == node.nullrev and isinstance(subset, fullreposet)):
2279 or r == node.nullrev and isinstance(subset, fullreposet)):
2280 ls.append(r)
2280 ls.append(r)
2281 seen.add(r)
2281 seen.add(r)
2282 return baseset(ls)
2282 return baseset(ls)
2283
2283
2284 # for internal use
2284 # for internal use
2285 @predicate('_list', safe=True, takeorder=True)
2285 @predicate('_list', safe=True, takeorder=True)
2286 def _list(repo, subset, x, order):
2286 def _list(repo, subset, x, order):
2287 if order == followorder:
2287 if order == followorder:
2288 # slow path to take the subset order
2288 # slow path to take the subset order
2289 return subset & _orderedlist(repo, fullreposet(repo), x)
2289 return subset & _orderedlist(repo, fullreposet(repo), x)
2290 else:
2290 else:
2291 return _orderedlist(repo, subset, x)
2291 return _orderedlist(repo, subset, x)
2292
2292
2293 def _orderedintlist(repo, subset, x):
2293 def _orderedintlist(repo, subset, x):
2294 s = getstring(x, "internal error")
2294 s = getstring(x, "internal error")
2295 if not s:
2295 if not s:
2296 return baseset()
2296 return baseset()
2297 ls = [int(r) for r in s.split('\0')]
2297 ls = [int(r) for r in s.split('\0')]
2298 s = subset
2298 s = subset
2299 return baseset([r for r in ls if r in s])
2299 return baseset([r for r in ls if r in s])
2300
2300
2301 # for internal use
2301 # for internal use
2302 @predicate('_intlist', safe=True, takeorder=True)
2302 @predicate('_intlist', safe=True, takeorder=True)
2303 def _intlist(repo, subset, x, order):
2303 def _intlist(repo, subset, x, order):
2304 if order == followorder:
2304 if order == followorder:
2305 # slow path to take the subset order
2305 # slow path to take the subset order
2306 return subset & _orderedintlist(repo, fullreposet(repo), x)
2306 return subset & _orderedintlist(repo, fullreposet(repo), x)
2307 else:
2307 else:
2308 return _orderedintlist(repo, subset, x)
2308 return _orderedintlist(repo, subset, x)
2309
2309
2310 def _orderedhexlist(repo, subset, x):
2310 def _orderedhexlist(repo, subset, x):
2311 s = getstring(x, "internal error")
2311 s = getstring(x, "internal error")
2312 if not s:
2312 if not s:
2313 return baseset()
2313 return baseset()
2314 cl = repo.changelog
2314 cl = repo.changelog
2315 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2315 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2316 s = subset
2316 s = subset
2317 return baseset([r for r in ls if r in s])
2317 return baseset([r for r in ls if r in s])
2318
2318
2319 # for internal use
2319 # for internal use
2320 @predicate('_hexlist', safe=True, takeorder=True)
2320 @predicate('_hexlist', safe=True, takeorder=True)
2321 def _hexlist(repo, subset, x, order):
2321 def _hexlist(repo, subset, x, order):
2322 if order == followorder:
2322 if order == followorder:
2323 # slow path to take the subset order
2323 # slow path to take the subset order
2324 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2324 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2325 else:
2325 else:
2326 return _orderedhexlist(repo, subset, x)
2326 return _orderedhexlist(repo, subset, x)
2327
2327
2328 methods = {
2328 methods = {
2329 "range": rangeset,
2329 "range": rangeset,
2330 "dagrange": dagrange,
2330 "dagrange": dagrange,
2331 "string": stringset,
2331 "string": stringset,
2332 "symbol": stringset,
2332 "symbol": stringset,
2333 "and": andset,
2333 "and": andset,
2334 "or": orset,
2334 "or": orset,
2335 "not": notset,
2335 "not": notset,
2336 "difference": differenceset,
2336 "difference": differenceset,
2337 "list": listset,
2337 "list": listset,
2338 "keyvalue": keyvaluepair,
2338 "keyvalue": keyvaluepair,
2339 "func": func,
2339 "func": func,
2340 "ancestor": ancestorspec,
2340 "ancestor": ancestorspec,
2341 "parent": parentspec,
2341 "parent": parentspec,
2342 "parentpost": parentpost,
2342 "parentpost": parentpost,
2343 }
2343 }
2344
2344
2345 # Constants for ordering requirement, used in _analyze():
2345 # Constants for ordering requirement, used in _analyze():
2346 #
2346 #
2347 # If 'define', any nested functions and operations can change the ordering of
2347 # If 'define', any nested functions and operations can change the ordering of
2348 # the entries in the set. If 'follow', any nested functions and operations
2348 # the entries in the set. If 'follow', any nested functions and operations
2349 # should take the ordering specified by the first operand to the '&' operator.
2349 # should take the ordering specified by the first operand to the '&' operator.
2350 #
2350 #
2351 # For instance,
2351 # For instance,
2352 #
2352 #
2353 # X & (Y | Z)
2353 # X & (Y | Z)
2354 # ^ ^^^^^^^
2354 # ^ ^^^^^^^
2355 # | follow
2355 # | follow
2356 # define
2356 # define
2357 #
2357 #
2358 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2358 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2359 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2359 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2360 #
2360 #
2361 # 'any' means the order doesn't matter. For instance,
2361 # 'any' means the order doesn't matter. For instance,
2362 #
2362 #
2363 # X & !Y
2363 # X & !Y
2364 # ^
2364 # ^
2365 # any
2365 # any
2366 #
2366 #
2367 # 'y()' can either enforce its ordering requirement or take the ordering
2367 # 'y()' can either enforce its ordering requirement or take the ordering
2368 # specified by 'x()' because 'not()' doesn't care the order.
2368 # specified by 'x()' because 'not()' doesn't care the order.
2369 #
2369 #
2370 # Transition of ordering requirement:
2370 # Transition of ordering requirement:
2371 #
2371 #
2372 # 1. starts with 'define'
2372 # 1. starts with 'define'
2373 # 2. shifts to 'follow' by 'x & y'
2373 # 2. shifts to 'follow' by 'x & y'
2374 # 3. changes back to 'define' on function call 'f(x)' or function-like
2374 # 3. changes back to 'define' on function call 'f(x)' or function-like
2375 # operation 'x (f) y' because 'f' may have its own ordering requirement
2375 # operation 'x (f) y' because 'f' may have its own ordering requirement
2376 # for 'x' and 'y' (e.g. 'first(x)')
2376 # for 'x' and 'y' (e.g. 'first(x)')
2377 #
2377 #
2378 anyorder = 'any' # don't care the order
2378 anyorder = 'any' # don't care the order
2379 defineorder = 'define' # should define the order
2379 defineorder = 'define' # should define the order
2380 followorder = 'follow' # must follow the current order
2380 followorder = 'follow' # must follow the current order
2381
2381
2382 # transition table for 'x & y', from the current expression 'x' to 'y'
2382 # transition table for 'x & y', from the current expression 'x' to 'y'
2383 _tofolloworder = {
2383 _tofolloworder = {
2384 anyorder: anyorder,
2384 anyorder: anyorder,
2385 defineorder: followorder,
2385 defineorder: followorder,
2386 followorder: followorder,
2386 followorder: followorder,
2387 }
2387 }
2388
2388
2389 def _matchonly(revs, bases):
2389 def _matchonly(revs, bases):
2390 """
2390 """
2391 >>> f = lambda *args: _matchonly(*map(parse, args))
2391 >>> f = lambda *args: _matchonly(*map(parse, args))
2392 >>> f('ancestors(A)', 'not ancestors(B)')
2392 >>> f('ancestors(A)', 'not ancestors(B)')
2393 ('list', ('symbol', 'A'), ('symbol', 'B'))
2393 ('list', ('symbol', 'A'), ('symbol', 'B'))
2394 """
2394 """
2395 if (revs is not None
2395 if (revs is not None
2396 and revs[0] == 'func'
2396 and revs[0] == 'func'
2397 and getsymbol(revs[1]) == 'ancestors'
2397 and getsymbol(revs[1]) == 'ancestors'
2398 and bases is not None
2398 and bases is not None
2399 and bases[0] == 'not'
2399 and bases[0] == 'not'
2400 and bases[1][0] == 'func'
2400 and bases[1][0] == 'func'
2401 and getsymbol(bases[1][1]) == 'ancestors'):
2401 and getsymbol(bases[1][1]) == 'ancestors'):
2402 return ('list', revs[2], bases[1][2])
2402 return ('list', revs[2], bases[1][2])
2403
2403
2404 def _fixops(x):
2404 def _fixops(x):
2405 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2405 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2406 handled well by our simple top-down parser"""
2406 handled well by our simple top-down parser"""
2407 if not isinstance(x, tuple):
2407 if not isinstance(x, tuple):
2408 return x
2408 return x
2409
2409
2410 op = x[0]
2410 op = x[0]
2411 if op == 'parent':
2411 if op == 'parent':
2412 # x^:y means (x^) : y, not x ^ (:y)
2412 # x^:y means (x^) : y, not x ^ (:y)
2413 # x^: means (x^) :, not x ^ (:)
2413 # x^: means (x^) :, not x ^ (:)
2414 post = ('parentpost', x[1])
2414 post = ('parentpost', x[1])
2415 if x[2][0] == 'dagrangepre':
2415 if x[2][0] == 'dagrangepre':
2416 return _fixops(('dagrange', post, x[2][1]))
2416 return _fixops(('dagrange', post, x[2][1]))
2417 elif x[2][0] == 'rangepre':
2417 elif x[2][0] == 'rangepre':
2418 return _fixops(('range', post, x[2][1]))
2418 return _fixops(('range', post, x[2][1]))
2419 elif x[2][0] == 'rangeall':
2419 elif x[2][0] == 'rangeall':
2420 return _fixops(('rangepost', post))
2420 return _fixops(('rangepost', post))
2421 elif op == 'or':
2421 elif op == 'or':
2422 # make number of arguments deterministic:
2422 # make number of arguments deterministic:
2423 # x + y + z -> (or x y z) -> (or (list x y z))
2423 # x + y + z -> (or x y z) -> (or (list x y z))
2424 return (op, _fixops(('list',) + x[1:]))
2424 return (op, _fixops(('list',) + x[1:]))
2425
2425
2426 return (op,) + tuple(_fixops(y) for y in x[1:])
2426 return (op,) + tuple(_fixops(y) for y in x[1:])
2427
2427
2428 def _analyze(x, order):
2428 def _analyze(x, order):
2429 if x is None:
2429 if x is None:
2430 return x
2430 return x
2431
2431
2432 op = x[0]
2432 op = x[0]
2433 if op == 'minus':
2433 if op == 'minus':
2434 return _analyze(('and', x[1], ('not', x[2])), order)
2434 return _analyze(('and', x[1], ('not', x[2])), order)
2435 elif op == 'only':
2435 elif op == 'only':
2436 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2436 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2437 return _analyze(t, order)
2437 return _analyze(t, order)
2438 elif op == 'onlypost':
2438 elif op == 'onlypost':
2439 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2439 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2440 elif op == 'dagrangepre':
2440 elif op == 'dagrangepre':
2441 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2441 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2442 elif op == 'dagrangepost':
2442 elif op == 'dagrangepost':
2443 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2443 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2444 elif op == 'rangeall':
2444 elif op == 'rangeall':
2445 return _analyze(('range', ('string', '0'), ('string', 'tip')), order)
2445 return _analyze(('range', ('string', '0'), ('string', 'tip')), order)
2446 elif op == 'rangepre':
2446 elif op == 'rangepre':
2447 return _analyze(('range', ('string', '0'), x[1]), order)
2447 return _analyze(('range', ('string', '0'), x[1]), order)
2448 elif op == 'rangepost':
2448 elif op == 'rangepost':
2449 return _analyze(('range', x[1], ('string', 'tip')), order)
2449 return _analyze(('range', x[1], ('string', 'tip')), order)
2450 elif op == 'negate':
2450 elif op == 'negate':
2451 s = getstring(x[1], _("can't negate that"))
2451 s = getstring(x[1], _("can't negate that"))
2452 return _analyze(('string', '-' + s), order)
2452 return _analyze(('string', '-' + s), order)
2453 elif op in ('string', 'symbol'):
2453 elif op in ('string', 'symbol'):
2454 return x
2454 return x
2455 elif op == 'and':
2455 elif op == 'and':
2456 ta = _analyze(x[1], order)
2456 ta = _analyze(x[1], order)
2457 tb = _analyze(x[2], _tofolloworder[order])
2457 tb = _analyze(x[2], _tofolloworder[order])
2458 return (op, ta, tb, order)
2458 return (op, ta, tb, order)
2459 elif op == 'or':
2459 elif op == 'or':
2460 return (op, _analyze(x[1], order), order)
2460 return (op, _analyze(x[1], order), order)
2461 elif op == 'not':
2461 elif op == 'not':
2462 return (op, _analyze(x[1], anyorder), order)
2462 return (op, _analyze(x[1], anyorder), order)
2463 elif op == 'parentpost':
2463 elif op == 'parentpost':
2464 return (op, _analyze(x[1], defineorder), order)
2464 return (op, _analyze(x[1], defineorder), order)
2465 elif op == 'group':
2465 elif op == 'group':
2466 return _analyze(x[1], order)
2466 return _analyze(x[1], order)
2467 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2467 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2468 ta = _analyze(x[1], defineorder)
2468 ta = _analyze(x[1], defineorder)
2469 tb = _analyze(x[2], defineorder)
2469 tb = _analyze(x[2], defineorder)
2470 return (op, ta, tb, order)
2470 return (op, ta, tb, order)
2471 elif op == 'list':
2471 elif op == 'list':
2472 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2472 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2473 elif op == 'keyvalue':
2473 elif op == 'keyvalue':
2474 return (op, x[1], _analyze(x[2], order))
2474 return (op, x[1], _analyze(x[2], order))
2475 elif op == 'func':
2475 elif op == 'func':
2476 f = getsymbol(x[1])
2476 f = getsymbol(x[1])
2477 d = defineorder
2477 d = defineorder
2478 if f == 'present':
2478 if f == 'present':
2479 # 'present(set)' is known to return the argument set with no
2479 # 'present(set)' is known to return the argument set with no
2480 # modification, so forward the current order to its argument
2480 # modification, so forward the current order to its argument
2481 d = order
2481 d = order
2482 return (op, x[1], _analyze(x[2], d), order)
2482 return (op, x[1], _analyze(x[2], d), order)
2483 raise ValueError('invalid operator %r' % op)
2483 raise ValueError('invalid operator %r' % op)
2484
2484
2485 def analyze(x, order=defineorder):
2485 def analyze(x, order=defineorder):
2486 """Transform raw parsed tree to evaluatable tree which can be fed to
2486 """Transform raw parsed tree to evaluatable tree which can be fed to
2487 optimize() or getset()
2487 optimize() or getset()
2488
2488
2489 All pseudo operations should be mapped to real operations or functions
2489 All pseudo operations should be mapped to real operations or functions
2490 defined in methods or symbols table respectively.
2490 defined in methods or symbols table respectively.
2491
2491
2492 'order' specifies how the current expression 'x' is ordered (see the
2492 'order' specifies how the current expression 'x' is ordered (see the
2493 constants defined above.)
2493 constants defined above.)
2494 """
2494 """
2495 return _analyze(x, order)
2495 return _analyze(x, order)
2496
2496
2497 def _optimize(x, small):
2497 def _optimize(x, small):
2498 if x is None:
2498 if x is None:
2499 return 0, x
2499 return 0, x
2500
2500
2501 smallbonus = 1
2501 smallbonus = 1
2502 if small:
2502 if small:
2503 smallbonus = .5
2503 smallbonus = .5
2504
2504
2505 op = x[0]
2505 op = x[0]
2506 if op in ('string', 'symbol'):
2506 if op in ('string', 'symbol'):
2507 return smallbonus, x # single revisions are small
2507 return smallbonus, x # single revisions are small
2508 elif op == 'and':
2508 elif op == 'and':
2509 wa, ta = _optimize(x[1], True)
2509 wa, ta = _optimize(x[1], True)
2510 wb, tb = _optimize(x[2], True)
2510 wb, tb = _optimize(x[2], True)
2511 order = x[3]
2511 order = x[3]
2512 w = min(wa, wb)
2512 w = min(wa, wb)
2513
2513
2514 # (::x and not ::y)/(not ::y and ::x) have a fast path
2514 # (::x and not ::y)/(not ::y and ::x) have a fast path
2515 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2515 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2516 if tm:
2516 if tm:
2517 return w, ('func', ('symbol', 'only'), tm, order)
2517 return w, ('func', ('symbol', 'only'), tm, order)
2518
2518
2519 if tb is not None and tb[0] == 'not':
2519 if tb is not None and tb[0] == 'not':
2520 return wa, ('difference', ta, tb[1], order)
2520 return wa, ('difference', ta, tb[1], order)
2521
2521
2522 if wa > wb:
2522 if wa > wb:
2523 return w, (op, tb, ta, order)
2523 return w, (op, tb, ta, order)
2524 return w, (op, ta, tb, order)
2524 return w, (op, ta, tb, order)
2525 elif op == 'or':
2525 elif op == 'or':
2526 # fast path for machine-generated expression, that is likely to have
2526 # fast path for machine-generated expression, that is likely to have
2527 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2527 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2528 order = x[2]
2528 order = x[2]
2529 ws, ts, ss = [], [], []
2529 ws, ts, ss = [], [], []
2530 def flushss():
2530 def flushss():
2531 if not ss:
2531 if not ss:
2532 return
2532 return
2533 if len(ss) == 1:
2533 if len(ss) == 1:
2534 w, t = ss[0]
2534 w, t = ss[0]
2535 else:
2535 else:
2536 s = '\0'.join(t[1] for w, t in ss)
2536 s = '\0'.join(t[1] for w, t in ss)
2537 y = ('func', ('symbol', '_list'), ('string', s), order)
2537 y = ('func', ('symbol', '_list'), ('string', s), order)
2538 w, t = _optimize(y, False)
2538 w, t = _optimize(y, False)
2539 ws.append(w)
2539 ws.append(w)
2540 ts.append(t)
2540 ts.append(t)
2541 del ss[:]
2541 del ss[:]
2542 for y in getlist(x[1]):
2542 for y in getlist(x[1]):
2543 w, t = _optimize(y, False)
2543 w, t = _optimize(y, False)
2544 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2544 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2545 ss.append((w, t))
2545 ss.append((w, t))
2546 continue
2546 continue
2547 flushss()
2547 flushss()
2548 ws.append(w)
2548 ws.append(w)
2549 ts.append(t)
2549 ts.append(t)
2550 flushss()
2550 flushss()
2551 if len(ts) == 1:
2551 if len(ts) == 1:
2552 return ws[0], ts[0] # 'or' operation is fully optimized out
2552 return ws[0], ts[0] # 'or' operation is fully optimized out
2553 # we can't reorder trees by weight because it would change the order.
2553 # we can't reorder trees by weight because it would change the order.
2554 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2554 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2555 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2555 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2556 return max(ws), (op, ('list',) + tuple(ts), order)
2556 return max(ws), (op, ('list',) + tuple(ts), order)
2557 elif op == 'not':
2557 elif op == 'not':
2558 # Optimize not public() to _notpublic() because we have a fast version
2558 # Optimize not public() to _notpublic() because we have a fast version
2559 if x[1][:3] == ('func', ('symbol', 'public'), None):
2559 if x[1][:3] == ('func', ('symbol', 'public'), None):
2560 order = x[1][3]
2560 order = x[1][3]
2561 newsym = ('func', ('symbol', '_notpublic'), None, order)
2561 newsym = ('func', ('symbol', '_notpublic'), None, order)
2562 o = _optimize(newsym, not small)
2562 o = _optimize(newsym, not small)
2563 return o[0], o[1]
2563 return o[0], o[1]
2564 else:
2564 else:
2565 o = _optimize(x[1], not small)
2565 o = _optimize(x[1], not small)
2566 order = x[2]
2566 order = x[2]
2567 return o[0], (op, o[1], order)
2567 return o[0], (op, o[1], order)
2568 elif op == 'parentpost':
2568 elif op == 'parentpost':
2569 o = _optimize(x[1], small)
2569 o = _optimize(x[1], small)
2570 order = x[2]
2570 order = x[2]
2571 return o[0], (op, o[1], order)
2571 return o[0], (op, o[1], order)
2572 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2572 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2573 wa, ta = _optimize(x[1], small)
2573 wa, ta = _optimize(x[1], small)
2574 wb, tb = _optimize(x[2], small)
2574 wb, tb = _optimize(x[2], small)
2575 order = x[3]
2575 order = x[3]
2576 return wa + wb, (op, ta, tb, order)
2576 return wa + wb, (op, ta, tb, order)
2577 elif op == 'list':
2577 elif op == 'list':
2578 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2578 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2579 return sum(ws), (op,) + ts
2579 return sum(ws), (op,) + ts
2580 elif op == 'keyvalue':
2580 elif op == 'keyvalue':
2581 w, t = _optimize(x[2], small)
2581 w, t = _optimize(x[2], small)
2582 return w, (op, x[1], t)
2582 return w, (op, x[1], t)
2583 elif op == 'func':
2583 elif op == 'func':
2584 f = getsymbol(x[1])
2584 f = getsymbol(x[1])
2585 wa, ta = _optimize(x[2], small)
2585 wa, ta = _optimize(x[2], small)
2586 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2586 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2587 'keyword', 'outgoing', 'user'):
2587 'keyword', 'outgoing', 'user'):
2588 w = 10 # slow
2588 w = 10 # slow
2589 elif f in ('modifies', 'adds', 'removes'):
2589 elif f in ('modifies', 'adds', 'removes'):
2590 w = 30 # slower
2590 w = 30 # slower
2591 elif f == "contains":
2591 elif f == "contains":
2592 w = 100 # very slow
2592 w = 100 # very slow
2593 elif f == "ancestor":
2593 elif f == "ancestor":
2594 w = 1 * smallbonus
2594 w = 1 * smallbonus
2595 elif f in ('reverse', 'limit', 'first', '_intlist'):
2595 elif f in ('reverse', 'limit', 'first', '_intlist'):
2596 w = 0
2596 w = 0
2597 elif f == "sort":
2597 elif f == "sort":
2598 w = 10 # assume most sorts look at changelog
2598 w = 10 # assume most sorts look at changelog
2599 else:
2599 else:
2600 w = 1
2600 w = 1
2601 order = x[3]
2601 order = x[3]
2602 return w + wa, (op, x[1], ta, order)
2602 return w + wa, (op, x[1], ta, order)
2603 raise ValueError('invalid operator %r' % op)
2603 raise ValueError('invalid operator %r' % op)
2604
2604
2605 def optimize(tree):
2605 def optimize(tree):
2606 """Optimize evaluatable tree
2606 """Optimize evaluatable tree
2607
2607
2608 All pseudo operations should be transformed beforehand.
2608 All pseudo operations should be transformed beforehand.
2609 """
2609 """
2610 _weight, newtree = _optimize(tree, small=True)
2610 _weight, newtree = _optimize(tree, small=True)
2611 return newtree
2611 return newtree
2612
2612
2613 # the set of valid characters for the initial letter of symbols in
2613 # the set of valid characters for the initial letter of symbols in
2614 # alias declarations and definitions
2614 # alias declarations and definitions
2615 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2615 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2616 if c.isalnum() or c in '._@$' or ord(c) > 127)
2616 if c.isalnum() or c in '._@$' or ord(c) > 127)
2617
2617
2618 def _parsewith(spec, lookup=None, syminitletters=None):
2618 def _parsewith(spec, lookup=None, syminitletters=None):
2619 """Generate a parse tree of given spec with given tokenizing options
2619 """Generate a parse tree of given spec with given tokenizing options
2620
2620
2621 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2621 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2622 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2622 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2623 >>> _parsewith('$1')
2623 >>> _parsewith('$1')
2624 Traceback (most recent call last):
2624 Traceback (most recent call last):
2625 ...
2625 ...
2626 ParseError: ("syntax error in revset '$1'", 0)
2626 ParseError: ("syntax error in revset '$1'", 0)
2627 >>> _parsewith('foo bar')
2627 >>> _parsewith('foo bar')
2628 Traceback (most recent call last):
2628 Traceback (most recent call last):
2629 ...
2629 ...
2630 ParseError: ('invalid token', 4)
2630 ParseError: ('invalid token', 4)
2631 """
2631 """
2632 p = parser.parser(elements)
2632 p = parser.parser(elements)
2633 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2633 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2634 syminitletters=syminitletters))
2634 syminitletters=syminitletters))
2635 if pos != len(spec):
2635 if pos != len(spec):
2636 raise error.ParseError(_('invalid token'), pos)
2636 raise error.ParseError(_('invalid token'), pos)
2637 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2637 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2638
2638
2639 class _aliasrules(parser.basealiasrules):
2639 class _aliasrules(parser.basealiasrules):
2640 """Parsing and expansion rule set of revset aliases"""
2640 """Parsing and expansion rule set of revset aliases"""
2641 _section = _('revset alias')
2641 _section = _('revset alias')
2642
2642
2643 @staticmethod
2643 @staticmethod
2644 def _parse(spec):
2644 def _parse(spec):
2645 """Parse alias declaration/definition ``spec``
2645 """Parse alias declaration/definition ``spec``
2646
2646
2647 This allows symbol names to use also ``$`` as an initial letter
2647 This allows symbol names to use also ``$`` as an initial letter
2648 (for backward compatibility), and callers of this function should
2648 (for backward compatibility), and callers of this function should
2649 examine whether ``$`` is used also for unexpected symbols or not.
2649 examine whether ``$`` is used also for unexpected symbols or not.
2650 """
2650 """
2651 return _parsewith(spec, syminitletters=_aliassyminitletters)
2651 return _parsewith(spec, syminitletters=_aliassyminitletters)
2652
2652
2653 @staticmethod
2653 @staticmethod
2654 def _trygetfunc(tree):
2654 def _trygetfunc(tree):
2655 if tree[0] == 'func' and tree[1][0] == 'symbol':
2655 if tree[0] == 'func' and tree[1][0] == 'symbol':
2656 return tree[1][1], getlist(tree[2])
2656 return tree[1][1], getlist(tree[2])
2657
2657
2658 def expandaliases(ui, tree):
2658 def expandaliases(ui, tree):
2659 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2659 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2660 tree = _aliasrules.expand(aliases, tree)
2660 tree = _aliasrules.expand(aliases, tree)
2661 # warn about problematic (but not referred) aliases
2661 # warn about problematic (but not referred) aliases
2662 for name, alias in sorted(aliases.iteritems()):
2662 for name, alias in sorted(aliases.iteritems()):
2663 if alias.error and not alias.warned:
2663 if alias.error and not alias.warned:
2664 ui.warn(_('warning: %s\n') % (alias.error))
2664 ui.warn(_('warning: %s\n') % (alias.error))
2665 alias.warned = True
2665 alias.warned = True
2666 return tree
2666 return tree
2667
2667
2668 def foldconcat(tree):
2668 def foldconcat(tree):
2669 """Fold elements to be concatenated by `##`
2669 """Fold elements to be concatenated by `##`
2670 """
2670 """
2671 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2671 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2672 return tree
2672 return tree
2673 if tree[0] == '_concat':
2673 if tree[0] == '_concat':
2674 pending = [tree]
2674 pending = [tree]
2675 l = []
2675 l = []
2676 while pending:
2676 while pending:
2677 e = pending.pop()
2677 e = pending.pop()
2678 if e[0] == '_concat':
2678 if e[0] == '_concat':
2679 pending.extend(reversed(e[1:]))
2679 pending.extend(reversed(e[1:]))
2680 elif e[0] in ('string', 'symbol'):
2680 elif e[0] in ('string', 'symbol'):
2681 l.append(e[1])
2681 l.append(e[1])
2682 else:
2682 else:
2683 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2683 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2684 raise error.ParseError(msg)
2684 raise error.ParseError(msg)
2685 return ('string', ''.join(l))
2685 return ('string', ''.join(l))
2686 else:
2686 else:
2687 return tuple(foldconcat(t) for t in tree)
2687 return tuple(foldconcat(t) for t in tree)
2688
2688
2689 def parse(spec, lookup=None):
2689 def parse(spec, lookup=None):
2690 return _parsewith(spec, lookup=lookup)
2690 return _parsewith(spec, lookup=lookup)
2691
2691
2692 def posttreebuilthook(tree, repo):
2692 def posttreebuilthook(tree, repo):
2693 # hook for extensions to execute code on the optimized tree
2693 # hook for extensions to execute code on the optimized tree
2694 pass
2694 pass
2695
2695
2696 def match(ui, spec, repo=None):
2696 def match(ui, spec, repo=None):
2697 """Create a matcher for a single revision spec."""
2697 """Create a matcher for a single revision spec."""
2698 return matchany(ui, [spec], repo=repo)
2698 return matchany(ui, [spec], repo=repo)
2699
2699
2700 def matchany(ui, specs, repo=None):
2700 def matchany(ui, specs, repo=None):
2701 """Create a matcher that will include any revisions matching one of the
2701 """Create a matcher that will include any revisions matching one of the
2702 given specs"""
2702 given specs"""
2703 if not specs:
2703 if not specs:
2704 def mfunc(repo, subset=None):
2704 def mfunc(repo, subset=None):
2705 return baseset()
2705 return baseset()
2706 return mfunc
2706 return mfunc
2707 if not all(specs):
2707 if not all(specs):
2708 raise error.ParseError(_("empty query"))
2708 raise error.ParseError(_("empty query"))
2709 lookup = None
2709 lookup = None
2710 if repo:
2710 if repo:
2711 lookup = repo.__contains__
2711 lookup = repo.__contains__
2712 if len(specs) == 1:
2712 if len(specs) == 1:
2713 tree = parse(specs[0], lookup)
2713 tree = parse(specs[0], lookup)
2714 else:
2714 else:
2715 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2715 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2716
2716
2717 if ui:
2717 if ui:
2718 tree = expandaliases(ui, tree)
2718 tree = expandaliases(ui, tree)
2719 tree = foldconcat(tree)
2719 tree = foldconcat(tree)
2720 tree = analyze(tree)
2720 tree = analyze(tree)
2721 tree = optimize(tree)
2721 tree = optimize(tree)
2722 posttreebuilthook(tree, repo)
2722 posttreebuilthook(tree, repo)
2723 return makematcher(tree)
2723 return makematcher(tree)
2724
2724
2725 def makematcher(tree):
2725 def makematcher(tree):
2726 """Create a matcher from an evaluatable tree"""
2726 """Create a matcher from an evaluatable tree"""
2727 def mfunc(repo, subset=None):
2727 def mfunc(repo, subset=None):
2728 if subset is None:
2728 if subset is None:
2729 subset = fullreposet(repo)
2729 subset = fullreposet(repo)
2730 if util.safehasattr(subset, 'isascending'):
2730 if util.safehasattr(subset, 'isascending'):
2731 result = getset(repo, subset, tree)
2731 result = getset(repo, subset, tree)
2732 else:
2732 else:
2733 result = getset(repo, baseset(subset), tree)
2733 result = getset(repo, baseset(subset), tree)
2734 return result
2734 return result
2735 return mfunc
2735 return mfunc
2736
2736
2737 def formatspec(expr, *args):
2737 def formatspec(expr, *args):
2738 '''
2738 '''
2739 This is a convenience function for using revsets internally, and
2739 This is a convenience function for using revsets internally, and
2740 escapes arguments appropriately. Aliases are intentionally ignored
2740 escapes arguments appropriately. Aliases are intentionally ignored
2741 so that intended expression behavior isn't accidentally subverted.
2741 so that intended expression behavior isn't accidentally subverted.
2742
2742
2743 Supported arguments:
2743 Supported arguments:
2744
2744
2745 %r = revset expression, parenthesized
2745 %r = revset expression, parenthesized
2746 %d = int(arg), no quoting
2746 %d = int(arg), no quoting
2747 %s = string(arg), escaped and single-quoted
2747 %s = string(arg), escaped and single-quoted
2748 %b = arg.branch(), escaped and single-quoted
2748 %b = arg.branch(), escaped and single-quoted
2749 %n = hex(arg), single-quoted
2749 %n = hex(arg), single-quoted
2750 %% = a literal '%'
2750 %% = a literal '%'
2751
2751
2752 Prefixing the type with 'l' specifies a parenthesized list of that type.
2752 Prefixing the type with 'l' specifies a parenthesized list of that type.
2753
2753
2754 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2754 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2755 '(10 or 11):: and ((this()) or (that()))'
2755 '(10 or 11):: and ((this()) or (that()))'
2756 >>> formatspec('%d:: and not %d::', 10, 20)
2756 >>> formatspec('%d:: and not %d::', 10, 20)
2757 '10:: and not 20::'
2757 '10:: and not 20::'
2758 >>> formatspec('%ld or %ld', [], [1])
2758 >>> formatspec('%ld or %ld', [], [1])
2759 "_list('') or 1"
2759 "_list('') or 1"
2760 >>> formatspec('keyword(%s)', 'foo\\xe9')
2760 >>> formatspec('keyword(%s)', 'foo\\xe9')
2761 "keyword('foo\\\\xe9')"
2761 "keyword('foo\\\\xe9')"
2762 >>> b = lambda: 'default'
2762 >>> b = lambda: 'default'
2763 >>> b.branch = b
2763 >>> b.branch = b
2764 >>> formatspec('branch(%b)', b)
2764 >>> formatspec('branch(%b)', b)
2765 "branch('default')"
2765 "branch('default')"
2766 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2766 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2767 "root(_list('a\\x00b\\x00c\\x00d'))"
2767 "root(_list('a\\x00b\\x00c\\x00d'))"
2768 '''
2768 '''
2769
2769
2770 def quote(s):
2770 def quote(s):
2771 return repr(str(s))
2771 return repr(str(s))
2772
2772
2773 def argtype(c, arg):
2773 def argtype(c, arg):
2774 if c == 'd':
2774 if c == 'd':
2775 return str(int(arg))
2775 return str(int(arg))
2776 elif c == 's':
2776 elif c == 's':
2777 return quote(arg)
2777 return quote(arg)
2778 elif c == 'r':
2778 elif c == 'r':
2779 parse(arg) # make sure syntax errors are confined
2779 parse(arg) # make sure syntax errors are confined
2780 return '(%s)' % arg
2780 return '(%s)' % arg
2781 elif c == 'n':
2781 elif c == 'n':
2782 return quote(node.hex(arg))
2782 return quote(node.hex(arg))
2783 elif c == 'b':
2783 elif c == 'b':
2784 return quote(arg.branch())
2784 return quote(arg.branch())
2785
2785
2786 def listexp(s, t):
2786 def listexp(s, t):
2787 l = len(s)
2787 l = len(s)
2788 if l == 0:
2788 if l == 0:
2789 return "_list('')"
2789 return "_list('')"
2790 elif l == 1:
2790 elif l == 1:
2791 return argtype(t, s[0])
2791 return argtype(t, s[0])
2792 elif t == 'd':
2792 elif t == 'd':
2793 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2793 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2794 elif t == 's':
2794 elif t == 's':
2795 return "_list('%s')" % "\0".join(s)
2795 return "_list('%s')" % "\0".join(s)
2796 elif t == 'n':
2796 elif t == 'n':
2797 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2797 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2798 elif t == 'b':
2798 elif t == 'b':
2799 return "_list('%s')" % "\0".join(a.branch() for a in s)
2799 return "_list('%s')" % "\0".join(a.branch() for a in s)
2800
2800
2801 m = l // 2
2801 m = l // 2
2802 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2802 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2803
2803
2804 ret = ''
2804 ret = ''
2805 pos = 0
2805 pos = 0
2806 arg = 0
2806 arg = 0
2807 while pos < len(expr):
2807 while pos < len(expr):
2808 c = expr[pos]
2808 c = expr[pos]
2809 if c == '%':
2809 if c == '%':
2810 pos += 1
2810 pos += 1
2811 d = expr[pos]
2811 d = expr[pos]
2812 if d == '%':
2812 if d == '%':
2813 ret += d
2813 ret += d
2814 elif d in 'dsnbr':
2814 elif d in 'dsnbr':
2815 ret += argtype(d, args[arg])
2815 ret += argtype(d, args[arg])
2816 arg += 1
2816 arg += 1
2817 elif d == 'l':
2817 elif d == 'l':
2818 # a list of some type
2818 # a list of some type
2819 pos += 1
2819 pos += 1
2820 d = expr[pos]
2820 d = expr[pos]
2821 ret += listexp(list(args[arg]), d)
2821 ret += listexp(list(args[arg]), d)
2822 arg += 1
2822 arg += 1
2823 else:
2823 else:
2824 raise error.Abort(_('unexpected revspec format character %s')
2824 raise error.Abort(_('unexpected revspec format character %s')
2825 % d)
2825 % d)
2826 else:
2826 else:
2827 ret += c
2827 ret += c
2828 pos += 1
2828 pos += 1
2829
2829
2830 return ret
2830 return ret
2831
2831
2832 def prettyformat(tree):
2832 def prettyformat(tree):
2833 return parser.prettyformat(tree, ('string', 'symbol'))
2833 return parser.prettyformat(tree, ('string', 'symbol'))
2834
2834
2835 def depth(tree):
2835 def depth(tree):
2836 if isinstance(tree, tuple):
2836 if isinstance(tree, tuple):
2837 return max(map(depth, tree)) + 1
2837 return max(map(depth, tree)) + 1
2838 else:
2838 else:
2839 return 0
2839 return 0
2840
2840
2841 def funcsused(tree):
2841 def funcsused(tree):
2842 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2842 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2843 return set()
2843 return set()
2844 else:
2844 else:
2845 funcs = set()
2845 funcs = set()
2846 for s in tree[1:]:
2846 for s in tree[1:]:
2847 funcs |= funcsused(s)
2847 funcs |= funcsused(s)
2848 if tree[0] == 'func':
2848 if tree[0] == 'func':
2849 funcs.add(tree[1][1])
2849 funcs.add(tree[1][1])
2850 return funcs
2850 return funcs
2851
2851
2852 def _formatsetrepr(r):
2852 def _formatsetrepr(r):
2853 """Format an optional printable representation of a set
2853 """Format an optional printable representation of a set
2854
2854
2855 ======== =================================
2855 ======== =================================
2856 type(r) example
2856 type(r) example
2857 ======== =================================
2857 ======== =================================
2858 tuple ('<not %r>', other)
2858 tuple ('<not %r>', other)
2859 str '<branch closed>'
2859 str '<branch closed>'
2860 callable lambda: '<branch %r>' % sorted(b)
2860 callable lambda: '<branch %r>' % sorted(b)
2861 object other
2861 object other
2862 ======== =================================
2862 ======== =================================
2863 """
2863 """
2864 if r is None:
2864 if r is None:
2865 return ''
2865 return ''
2866 elif isinstance(r, tuple):
2866 elif isinstance(r, tuple):
2867 return r[0] % r[1:]
2867 return r[0] % r[1:]
2868 elif isinstance(r, str):
2868 elif isinstance(r, str):
2869 return r
2869 return r
2870 elif callable(r):
2870 elif callable(r):
2871 return r()
2871 return r()
2872 else:
2872 else:
2873 return repr(r)
2873 return repr(r)
2874
2874
2875 class abstractsmartset(object):
2875 class abstractsmartset(object):
2876
2876
2877 def __nonzero__(self):
2877 def __nonzero__(self):
2878 """True if the smartset is not empty"""
2878 """True if the smartset is not empty"""
2879 raise NotImplementedError()
2879 raise NotImplementedError()
2880
2880
2881 def __contains__(self, rev):
2881 def __contains__(self, rev):
2882 """provide fast membership testing"""
2882 """provide fast membership testing"""
2883 raise NotImplementedError()
2883 raise NotImplementedError()
2884
2884
2885 def __iter__(self):
2885 def __iter__(self):
2886 """iterate the set in the order it is supposed to be iterated"""
2886 """iterate the set in the order it is supposed to be iterated"""
2887 raise NotImplementedError()
2887 raise NotImplementedError()
2888
2888
2889 # Attributes containing a function to perform a fast iteration in a given
2889 # Attributes containing a function to perform a fast iteration in a given
2890 # direction. A smartset can have none, one, or both defined.
2890 # direction. A smartset can have none, one, or both defined.
2891 #
2891 #
2892 # Default value is None instead of a function returning None to avoid
2892 # Default value is None instead of a function returning None to avoid
2893 # initializing an iterator just for testing if a fast method exists.
2893 # initializing an iterator just for testing if a fast method exists.
2894 fastasc = None
2894 fastasc = None
2895 fastdesc = None
2895 fastdesc = None
2896
2896
2897 def isascending(self):
2897 def isascending(self):
2898 """True if the set will iterate in ascending order"""
2898 """True if the set will iterate in ascending order"""
2899 raise NotImplementedError()
2899 raise NotImplementedError()
2900
2900
2901 def isdescending(self):
2901 def isdescending(self):
2902 """True if the set will iterate in descending order"""
2902 """True if the set will iterate in descending order"""
2903 raise NotImplementedError()
2903 raise NotImplementedError()
2904
2904
2905 def istopo(self):
2905 def istopo(self):
2906 """True if the set will iterate in topographical order"""
2906 """True if the set will iterate in topographical order"""
2907 raise NotImplementedError()
2907 raise NotImplementedError()
2908
2908
2909 @util.cachefunc
2909 @util.cachefunc
2910 def min(self):
2910 def min(self):
2911 """return the minimum element in the set"""
2911 """return the minimum element in the set"""
2912 if self.fastasc is not None:
2912 if self.fastasc is not None:
2913 for r in self.fastasc():
2913 for r in self.fastasc():
2914 return r
2914 return r
2915 raise ValueError('arg is an empty sequence')
2915 raise ValueError('arg is an empty sequence')
2916 return min(self)
2916 return min(self)
2917
2917
2918 @util.cachefunc
2918 @util.cachefunc
2919 def max(self):
2919 def max(self):
2920 """return the maximum element in the set"""
2920 """return the maximum element in the set"""
2921 if self.fastdesc is not None:
2921 if self.fastdesc is not None:
2922 for r in self.fastdesc():
2922 for r in self.fastdesc():
2923 return r
2923 return r
2924 raise ValueError('arg is an empty sequence')
2924 raise ValueError('arg is an empty sequence')
2925 return max(self)
2925 return max(self)
2926
2926
2927 def first(self):
2927 def first(self):
2928 """return the first element in the set (user iteration perspective)
2928 """return the first element in the set (user iteration perspective)
2929
2929
2930 Return None if the set is empty"""
2930 Return None if the set is empty"""
2931 raise NotImplementedError()
2931 raise NotImplementedError()
2932
2932
2933 def last(self):
2933 def last(self):
2934 """return the last element in the set (user iteration perspective)
2934 """return the last element in the set (user iteration perspective)
2935
2935
2936 Return None if the set is empty"""
2936 Return None if the set is empty"""
2937 raise NotImplementedError()
2937 raise NotImplementedError()
2938
2938
2939 def __len__(self):
2939 def __len__(self):
2940 """return the length of the smartsets
2940 """return the length of the smartsets
2941
2941
2942 This can be expensive on smartset that could be lazy otherwise."""
2942 This can be expensive on smartset that could be lazy otherwise."""
2943 raise NotImplementedError()
2943 raise NotImplementedError()
2944
2944
2945 def reverse(self):
2945 def reverse(self):
2946 """reverse the expected iteration order"""
2946 """reverse the expected iteration order"""
2947 raise NotImplementedError()
2947 raise NotImplementedError()
2948
2948
2949 def sort(self, reverse=True):
2949 def sort(self, reverse=True):
2950 """get the set to iterate in an ascending or descending order"""
2950 """get the set to iterate in an ascending or descending order"""
2951 raise NotImplementedError()
2951 raise NotImplementedError()
2952
2952
2953 def __and__(self, other):
2953 def __and__(self, other):
2954 """Returns a new object with the intersection of the two collections.
2954 """Returns a new object with the intersection of the two collections.
2955
2955
2956 This is part of the mandatory API for smartset."""
2956 This is part of the mandatory API for smartset."""
2957 if isinstance(other, fullreposet):
2957 if isinstance(other, fullreposet):
2958 return self
2958 return self
2959 return self.filter(other.__contains__, condrepr=other, cache=False)
2959 return self.filter(other.__contains__, condrepr=other, cache=False)
2960
2960
2961 def __add__(self, other):
2961 def __add__(self, other):
2962 """Returns a new object with the union of the two collections.
2962 """Returns a new object with the union of the two collections.
2963
2963
2964 This is part of the mandatory API for smartset."""
2964 This is part of the mandatory API for smartset."""
2965 return addset(self, other)
2965 return addset(self, other)
2966
2966
2967 def __sub__(self, other):
2967 def __sub__(self, other):
2968 """Returns a new object with the substraction of the two collections.
2968 """Returns a new object with the substraction of the two collections.
2969
2969
2970 This is part of the mandatory API for smartset."""
2970 This is part of the mandatory API for smartset."""
2971 c = other.__contains__
2971 c = other.__contains__
2972 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2972 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2973 cache=False)
2973 cache=False)
2974
2974
2975 def filter(self, condition, condrepr=None, cache=True):
2975 def filter(self, condition, condrepr=None, cache=True):
2976 """Returns this smartset filtered by condition as a new smartset.
2976 """Returns this smartset filtered by condition as a new smartset.
2977
2977
2978 `condition` is a callable which takes a revision number and returns a
2978 `condition` is a callable which takes a revision number and returns a
2979 boolean. Optional `condrepr` provides a printable representation of
2979 boolean. Optional `condrepr` provides a printable representation of
2980 the given `condition`.
2980 the given `condition`.
2981
2981
2982 This is part of the mandatory API for smartset."""
2982 This is part of the mandatory API for smartset."""
2983 # builtin cannot be cached. but do not needs to
2983 # builtin cannot be cached. but do not needs to
2984 if cache and util.safehasattr(condition, 'func_code'):
2984 if cache and util.safehasattr(condition, 'func_code'):
2985 condition = util.cachefunc(condition)
2985 condition = util.cachefunc(condition)
2986 return filteredset(self, condition, condrepr)
2986 return filteredset(self, condition, condrepr)
2987
2987
2988 class baseset(abstractsmartset):
2988 class baseset(abstractsmartset):
2989 """Basic data structure that represents a revset and contains the basic
2989 """Basic data structure that represents a revset and contains the basic
2990 operation that it should be able to perform.
2990 operation that it should be able to perform.
2991
2991
2992 Every method in this class should be implemented by any smartset class.
2992 Every method in this class should be implemented by any smartset class.
2993 """
2993 """
2994 def __init__(self, data=(), datarepr=None, istopo=False):
2994 def __init__(self, data=(), datarepr=None, istopo=False):
2995 """
2995 """
2996 datarepr: a tuple of (format, obj, ...), a function or an object that
2996 datarepr: a tuple of (format, obj, ...), a function or an object that
2997 provides a printable representation of the given data.
2997 provides a printable representation of the given data.
2998 """
2998 """
2999 self._ascending = None
2999 self._ascending = None
3000 self._istopo = istopo
3000 self._istopo = istopo
3001 if not isinstance(data, list):
3001 if not isinstance(data, list):
3002 if isinstance(data, set):
3002 if isinstance(data, set):
3003 self._set = data
3003 self._set = data
3004 # set has no order we pick one for stability purpose
3004 # set has no order we pick one for stability purpose
3005 self._ascending = True
3005 self._ascending = True
3006 data = list(data)
3006 data = list(data)
3007 self._list = data
3007 self._list = data
3008 self._datarepr = datarepr
3008 self._datarepr = datarepr
3009
3009
3010 @util.propertycache
3010 @util.propertycache
3011 def _set(self):
3011 def _set(self):
3012 return set(self._list)
3012 return set(self._list)
3013
3013
3014 @util.propertycache
3014 @util.propertycache
3015 def _asclist(self):
3015 def _asclist(self):
3016 asclist = self._list[:]
3016 asclist = self._list[:]
3017 asclist.sort()
3017 asclist.sort()
3018 return asclist
3018 return asclist
3019
3019
3020 def __iter__(self):
3020 def __iter__(self):
3021 if self._ascending is None:
3021 if self._ascending is None:
3022 return iter(self._list)
3022 return iter(self._list)
3023 elif self._ascending:
3023 elif self._ascending:
3024 return iter(self._asclist)
3024 return iter(self._asclist)
3025 else:
3025 else:
3026 return reversed(self._asclist)
3026 return reversed(self._asclist)
3027
3027
3028 def fastasc(self):
3028 def fastasc(self):
3029 return iter(self._asclist)
3029 return iter(self._asclist)
3030
3030
3031 def fastdesc(self):
3031 def fastdesc(self):
3032 return reversed(self._asclist)
3032 return reversed(self._asclist)
3033
3033
3034 @util.propertycache
3034 @util.propertycache
3035 def __contains__(self):
3035 def __contains__(self):
3036 return self._set.__contains__
3036 return self._set.__contains__
3037
3037
3038 def __nonzero__(self):
3038 def __nonzero__(self):
3039 return bool(self._list)
3039 return bool(self._list)
3040
3040
3041 def sort(self, reverse=False):
3041 def sort(self, reverse=False):
3042 self._ascending = not bool(reverse)
3042 self._ascending = not bool(reverse)
3043 self._istopo = False
3043 self._istopo = False
3044
3044
3045 def reverse(self):
3045 def reverse(self):
3046 if self._ascending is None:
3046 if self._ascending is None:
3047 self._list.reverse()
3047 self._list.reverse()
3048 else:
3048 else:
3049 self._ascending = not self._ascending
3049 self._ascending = not self._ascending
3050 self._istopo = False
3050 self._istopo = False
3051
3051
3052 def __len__(self):
3052 def __len__(self):
3053 return len(self._list)
3053 return len(self._list)
3054
3054
3055 def isascending(self):
3055 def isascending(self):
3056 """Returns True if the collection is ascending order, False if not.
3056 """Returns True if the collection is ascending order, False if not.
3057
3057
3058 This is part of the mandatory API for smartset."""
3058 This is part of the mandatory API for smartset."""
3059 if len(self) <= 1:
3059 if len(self) <= 1:
3060 return True
3060 return True
3061 return self._ascending is not None and self._ascending
3061 return self._ascending is not None and self._ascending
3062
3062
3063 def isdescending(self):
3063 def isdescending(self):
3064 """Returns True if the collection is descending order, False if not.
3064 """Returns True if the collection is descending order, False if not.
3065
3065
3066 This is part of the mandatory API for smartset."""
3066 This is part of the mandatory API for smartset."""
3067 if len(self) <= 1:
3067 if len(self) <= 1:
3068 return True
3068 return True
3069 return self._ascending is not None and not self._ascending
3069 return self._ascending is not None and not self._ascending
3070
3070
3071 def istopo(self):
3071 def istopo(self):
3072 """Is the collection is in topographical order or not.
3072 """Is the collection is in topographical order or not.
3073
3073
3074 This is part of the mandatory API for smartset."""
3074 This is part of the mandatory API for smartset."""
3075 if len(self) <= 1:
3075 if len(self) <= 1:
3076 return True
3076 return True
3077 return self._istopo
3077 return self._istopo
3078
3078
3079 def first(self):
3079 def first(self):
3080 if self:
3080 if self:
3081 if self._ascending is None:
3081 if self._ascending is None:
3082 return self._list[0]
3082 return self._list[0]
3083 elif self._ascending:
3083 elif self._ascending:
3084 return self._asclist[0]
3084 return self._asclist[0]
3085 else:
3085 else:
3086 return self._asclist[-1]
3086 return self._asclist[-1]
3087 return None
3087 return None
3088
3088
3089 def last(self):
3089 def last(self):
3090 if self:
3090 if self:
3091 if self._ascending is None:
3091 if self._ascending is None:
3092 return self._list[-1]
3092 return self._list[-1]
3093 elif self._ascending:
3093 elif self._ascending:
3094 return self._asclist[-1]
3094 return self._asclist[-1]
3095 else:
3095 else:
3096 return self._asclist[0]
3096 return self._asclist[0]
3097 return None
3097 return None
3098
3098
3099 def __repr__(self):
3099 def __repr__(self):
3100 d = {None: '', False: '-', True: '+'}[self._ascending]
3100 d = {None: '', False: '-', True: '+'}[self._ascending]
3101 s = _formatsetrepr(self._datarepr)
3101 s = _formatsetrepr(self._datarepr)
3102 if not s:
3102 if not s:
3103 l = self._list
3103 l = self._list
3104 # if _list has been built from a set, it might have a different
3104 # if _list has been built from a set, it might have a different
3105 # order from one python implementation to another.
3105 # order from one python implementation to another.
3106 # We fallback to the sorted version for a stable output.
3106 # We fallback to the sorted version for a stable output.
3107 if self._ascending is not None:
3107 if self._ascending is not None:
3108 l = self._asclist
3108 l = self._asclist
3109 s = repr(l)
3109 s = repr(l)
3110 return '<%s%s %s>' % (type(self).__name__, d, s)
3110 return '<%s%s %s>' % (type(self).__name__, d, s)
3111
3111
3112 class filteredset(abstractsmartset):
3112 class filteredset(abstractsmartset):
3113 """Duck type for baseset class which iterates lazily over the revisions in
3113 """Duck type for baseset class which iterates lazily over the revisions in
3114 the subset and contains a function which tests for membership in the
3114 the subset and contains a function which tests for membership in the
3115 revset
3115 revset
3116 """
3116 """
3117 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3117 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3118 """
3118 """
3119 condition: a function that decide whether a revision in the subset
3119 condition: a function that decide whether a revision in the subset
3120 belongs to the revset or not.
3120 belongs to the revset or not.
3121 condrepr: a tuple of (format, obj, ...), a function or an object that
3121 condrepr: a tuple of (format, obj, ...), a function or an object that
3122 provides a printable representation of the given condition.
3122 provides a printable representation of the given condition.
3123 """
3123 """
3124 self._subset = subset
3124 self._subset = subset
3125 self._condition = condition
3125 self._condition = condition
3126 self._condrepr = condrepr
3126 self._condrepr = condrepr
3127
3127
3128 def __contains__(self, x):
3128 def __contains__(self, x):
3129 return x in self._subset and self._condition(x)
3129 return x in self._subset and self._condition(x)
3130
3130
3131 def __iter__(self):
3131 def __iter__(self):
3132 return self._iterfilter(self._subset)
3132 return self._iterfilter(self._subset)
3133
3133
3134 def _iterfilter(self, it):
3134 def _iterfilter(self, it):
3135 cond = self._condition
3135 cond = self._condition
3136 for x in it:
3136 for x in it:
3137 if cond(x):
3137 if cond(x):
3138 yield x
3138 yield x
3139
3139
3140 @property
3140 @property
3141 def fastasc(self):
3141 def fastasc(self):
3142 it = self._subset.fastasc
3142 it = self._subset.fastasc
3143 if it is None:
3143 if it is None:
3144 return None
3144 return None
3145 return lambda: self._iterfilter(it())
3145 return lambda: self._iterfilter(it())
3146
3146
3147 @property
3147 @property
3148 def fastdesc(self):
3148 def fastdesc(self):
3149 it = self._subset.fastdesc
3149 it = self._subset.fastdesc
3150 if it is None:
3150 if it is None:
3151 return None
3151 return None
3152 return lambda: self._iterfilter(it())
3152 return lambda: self._iterfilter(it())
3153
3153
3154 def __nonzero__(self):
3154 def __nonzero__(self):
3155 fast = None
3155 fast = None
3156 candidates = [self.fastasc if self.isascending() else None,
3156 candidates = [self.fastasc if self.isascending() else None,
3157 self.fastdesc if self.isdescending() else None,
3157 self.fastdesc if self.isdescending() else None,
3158 self.fastasc,
3158 self.fastasc,
3159 self.fastdesc]
3159 self.fastdesc]
3160 for candidate in candidates:
3160 for candidate in candidates:
3161 if candidate is not None:
3161 if candidate is not None:
3162 fast = candidate
3162 fast = candidate
3163 break
3163 break
3164
3164
3165 if fast is not None:
3165 if fast is not None:
3166 it = fast()
3166 it = fast()
3167 else:
3167 else:
3168 it = self
3168 it = self
3169
3169
3170 for r in it:
3170 for r in it:
3171 return True
3171 return True
3172 return False
3172 return False
3173
3173
3174 def __len__(self):
3174 def __len__(self):
3175 # Basic implementation to be changed in future patches.
3175 # Basic implementation to be changed in future patches.
3176 # until this gets improved, we use generator expression
3176 # until this gets improved, we use generator expression
3177 # here, since list compr is free to call __len__ again
3177 # here, since list compr is free to call __len__ again
3178 # causing infinite recursion
3178 # causing infinite recursion
3179 l = baseset(r for r in self)
3179 l = baseset(r for r in self)
3180 return len(l)
3180 return len(l)
3181
3181
3182 def sort(self, reverse=False):
3182 def sort(self, reverse=False):
3183 self._subset.sort(reverse=reverse)
3183 self._subset.sort(reverse=reverse)
3184
3184
3185 def reverse(self):
3185 def reverse(self):
3186 self._subset.reverse()
3186 self._subset.reverse()
3187
3187
3188 def isascending(self):
3188 def isascending(self):
3189 return self._subset.isascending()
3189 return self._subset.isascending()
3190
3190
3191 def isdescending(self):
3191 def isdescending(self):
3192 return self._subset.isdescending()
3192 return self._subset.isdescending()
3193
3193
3194 def istopo(self):
3194 def istopo(self):
3195 return self._subset.istopo()
3195 return self._subset.istopo()
3196
3196
3197 def first(self):
3197 def first(self):
3198 for x in self:
3198 for x in self:
3199 return x
3199 return x
3200 return None
3200 return None
3201
3201
3202 def last(self):
3202 def last(self):
3203 it = None
3203 it = None
3204 if self.isascending():
3204 if self.isascending():
3205 it = self.fastdesc
3205 it = self.fastdesc
3206 elif self.isdescending():
3206 elif self.isdescending():
3207 it = self.fastasc
3207 it = self.fastasc
3208 if it is not None:
3208 if it is not None:
3209 for x in it():
3209 for x in it():
3210 return x
3210 return x
3211 return None #empty case
3211 return None #empty case
3212 else:
3212 else:
3213 x = None
3213 x = None
3214 for x in self:
3214 for x in self:
3215 pass
3215 pass
3216 return x
3216 return x
3217
3217
3218 def __repr__(self):
3218 def __repr__(self):
3219 xs = [repr(self._subset)]
3219 xs = [repr(self._subset)]
3220 s = _formatsetrepr(self._condrepr)
3220 s = _formatsetrepr(self._condrepr)
3221 if s:
3221 if s:
3222 xs.append(s)
3222 xs.append(s)
3223 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3223 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3224
3224
3225 def _iterordered(ascending, iter1, iter2):
3225 def _iterordered(ascending, iter1, iter2):
3226 """produce an ordered iteration from two iterators with the same order
3226 """produce an ordered iteration from two iterators with the same order
3227
3227
3228 The ascending is used to indicated the iteration direction.
3228 The ascending is used to indicated the iteration direction.
3229 """
3229 """
3230 choice = max
3230 choice = max
3231 if ascending:
3231 if ascending:
3232 choice = min
3232 choice = min
3233
3233
3234 val1 = None
3234 val1 = None
3235 val2 = None
3235 val2 = None
3236 try:
3236 try:
3237 # Consume both iterators in an ordered way until one is empty
3237 # Consume both iterators in an ordered way until one is empty
3238 while True:
3238 while True:
3239 if val1 is None:
3239 if val1 is None:
3240 val1 = next(iter1)
3240 val1 = next(iter1)
3241 if val2 is None:
3241 if val2 is None:
3242 val2 = next(iter2)
3242 val2 = next(iter2)
3243 n = choice(val1, val2)
3243 n = choice(val1, val2)
3244 yield n
3244 yield n
3245 if val1 == n:
3245 if val1 == n:
3246 val1 = None
3246 val1 = None
3247 if val2 == n:
3247 if val2 == n:
3248 val2 = None
3248 val2 = None
3249 except StopIteration:
3249 except StopIteration:
3250 # Flush any remaining values and consume the other one
3250 # Flush any remaining values and consume the other one
3251 it = iter2
3251 it = iter2
3252 if val1 is not None:
3252 if val1 is not None:
3253 yield val1
3253 yield val1
3254 it = iter1
3254 it = iter1
3255 elif val2 is not None:
3255 elif val2 is not None:
3256 # might have been equality and both are empty
3256 # might have been equality and both are empty
3257 yield val2
3257 yield val2
3258 for val in it:
3258 for val in it:
3259 yield val
3259 yield val
3260
3260
3261 class addset(abstractsmartset):
3261 class addset(abstractsmartset):
3262 """Represent the addition of two sets
3262 """Represent the addition of two sets
3263
3263
3264 Wrapper structure for lazily adding two structures without losing much
3264 Wrapper structure for lazily adding two structures without losing much
3265 performance on the __contains__ method
3265 performance on the __contains__ method
3266
3266
3267 If the ascending attribute is set, that means the two structures are
3267 If the ascending attribute is set, that means the two structures are
3268 ordered in either an ascending or descending way. Therefore, we can add
3268 ordered in either an ascending or descending way. Therefore, we can add
3269 them maintaining the order by iterating over both at the same time
3269 them maintaining the order by iterating over both at the same time
3270
3270
3271 >>> xs = baseset([0, 3, 2])
3271 >>> xs = baseset([0, 3, 2])
3272 >>> ys = baseset([5, 2, 4])
3272 >>> ys = baseset([5, 2, 4])
3273
3273
3274 >>> rs = addset(xs, ys)
3274 >>> rs = addset(xs, ys)
3275 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3275 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3276 (True, True, False, True, 0, 4)
3276 (True, True, False, True, 0, 4)
3277 >>> rs = addset(xs, baseset([]))
3277 >>> rs = addset(xs, baseset([]))
3278 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3278 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3279 (True, True, False, 0, 2)
3279 (True, True, False, 0, 2)
3280 >>> rs = addset(baseset([]), baseset([]))
3280 >>> rs = addset(baseset([]), baseset([]))
3281 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3281 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3282 (False, False, None, None)
3282 (False, False, None, None)
3283
3283
3284 iterate unsorted:
3284 iterate unsorted:
3285 >>> rs = addset(xs, ys)
3285 >>> rs = addset(xs, ys)
3286 >>> # (use generator because pypy could call len())
3286 >>> # (use generator because pypy could call len())
3287 >>> list(x for x in rs) # without _genlist
3287 >>> list(x for x in rs) # without _genlist
3288 [0, 3, 2, 5, 4]
3288 [0, 3, 2, 5, 4]
3289 >>> assert not rs._genlist
3289 >>> assert not rs._genlist
3290 >>> len(rs)
3290 >>> len(rs)
3291 5
3291 5
3292 >>> [x for x in rs] # with _genlist
3292 >>> [x for x in rs] # with _genlist
3293 [0, 3, 2, 5, 4]
3293 [0, 3, 2, 5, 4]
3294 >>> assert rs._genlist
3294 >>> assert rs._genlist
3295
3295
3296 iterate ascending:
3296 iterate ascending:
3297 >>> rs = addset(xs, ys, ascending=True)
3297 >>> rs = addset(xs, ys, ascending=True)
3298 >>> # (use generator because pypy could call len())
3298 >>> # (use generator because pypy could call len())
3299 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3299 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3300 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3300 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3301 >>> assert not rs._asclist
3301 >>> assert not rs._asclist
3302 >>> len(rs)
3302 >>> len(rs)
3303 5
3303 5
3304 >>> [x for x in rs], [x for x in rs.fastasc()]
3304 >>> [x for x in rs], [x for x in rs.fastasc()]
3305 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3305 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3306 >>> assert rs._asclist
3306 >>> assert rs._asclist
3307
3307
3308 iterate descending:
3308 iterate descending:
3309 >>> rs = addset(xs, ys, ascending=False)
3309 >>> rs = addset(xs, ys, ascending=False)
3310 >>> # (use generator because pypy could call len())
3310 >>> # (use generator because pypy could call len())
3311 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3311 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3312 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3312 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3313 >>> assert not rs._asclist
3313 >>> assert not rs._asclist
3314 >>> len(rs)
3314 >>> len(rs)
3315 5
3315 5
3316 >>> [x for x in rs], [x for x in rs.fastdesc()]
3316 >>> [x for x in rs], [x for x in rs.fastdesc()]
3317 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3317 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3318 >>> assert rs._asclist
3318 >>> assert rs._asclist
3319
3319
3320 iterate ascending without fastasc:
3320 iterate ascending without fastasc:
3321 >>> rs = addset(xs, generatorset(ys), ascending=True)
3321 >>> rs = addset(xs, generatorset(ys), ascending=True)
3322 >>> assert rs.fastasc is None
3322 >>> assert rs.fastasc is None
3323 >>> [x for x in rs]
3323 >>> [x for x in rs]
3324 [0, 2, 3, 4, 5]
3324 [0, 2, 3, 4, 5]
3325
3325
3326 iterate descending without fastdesc:
3326 iterate descending without fastdesc:
3327 >>> rs = addset(generatorset(xs), ys, ascending=False)
3327 >>> rs = addset(generatorset(xs), ys, ascending=False)
3328 >>> assert rs.fastdesc is None
3328 >>> assert rs.fastdesc is None
3329 >>> [x for x in rs]
3329 >>> [x for x in rs]
3330 [5, 4, 3, 2, 0]
3330 [5, 4, 3, 2, 0]
3331 """
3331 """
3332 def __init__(self, revs1, revs2, ascending=None):
3332 def __init__(self, revs1, revs2, ascending=None):
3333 self._r1 = revs1
3333 self._r1 = revs1
3334 self._r2 = revs2
3334 self._r2 = revs2
3335 self._iter = None
3335 self._iter = None
3336 self._ascending = ascending
3336 self._ascending = ascending
3337 self._genlist = None
3337 self._genlist = None
3338 self._asclist = None
3338 self._asclist = None
3339
3339
3340 def __len__(self):
3340 def __len__(self):
3341 return len(self._list)
3341 return len(self._list)
3342
3342
3343 def __nonzero__(self):
3343 def __nonzero__(self):
3344 return bool(self._r1) or bool(self._r2)
3344 return bool(self._r1) or bool(self._r2)
3345
3345
3346 @util.propertycache
3346 @util.propertycache
3347 def _list(self):
3347 def _list(self):
3348 if not self._genlist:
3348 if not self._genlist:
3349 self._genlist = baseset(iter(self))
3349 self._genlist = baseset(iter(self))
3350 return self._genlist
3350 return self._genlist
3351
3351
3352 def __iter__(self):
3352 def __iter__(self):
3353 """Iterate over both collections without repeating elements
3353 """Iterate over both collections without repeating elements
3354
3354
3355 If the ascending attribute is not set, iterate over the first one and
3355 If the ascending attribute is not set, iterate over the first one and
3356 then over the second one checking for membership on the first one so we
3356 then over the second one checking for membership on the first one so we
3357 dont yield any duplicates.
3357 dont yield any duplicates.
3358
3358
3359 If the ascending attribute is set, iterate over both collections at the
3359 If the ascending attribute is set, iterate over both collections at the
3360 same time, yielding only one value at a time in the given order.
3360 same time, yielding only one value at a time in the given order.
3361 """
3361 """
3362 if self._ascending is None:
3362 if self._ascending is None:
3363 if self._genlist:
3363 if self._genlist:
3364 return iter(self._genlist)
3364 return iter(self._genlist)
3365 def arbitraryordergen():
3365 def arbitraryordergen():
3366 for r in self._r1:
3366 for r in self._r1:
3367 yield r
3367 yield r
3368 inr1 = self._r1.__contains__
3368 inr1 = self._r1.__contains__
3369 for r in self._r2:
3369 for r in self._r2:
3370 if not inr1(r):
3370 if not inr1(r):
3371 yield r
3371 yield r
3372 return arbitraryordergen()
3372 return arbitraryordergen()
3373 # try to use our own fast iterator if it exists
3373 # try to use our own fast iterator if it exists
3374 self._trysetasclist()
3374 self._trysetasclist()
3375 if self._ascending:
3375 if self._ascending:
3376 attr = 'fastasc'
3376 attr = 'fastasc'
3377 else:
3377 else:
3378 attr = 'fastdesc'
3378 attr = 'fastdesc'
3379 it = getattr(self, attr)
3379 it = getattr(self, attr)
3380 if it is not None:
3380 if it is not None:
3381 return it()
3381 return it()
3382 # maybe half of the component supports fast
3382 # maybe half of the component supports fast
3383 # get iterator for _r1
3383 # get iterator for _r1
3384 iter1 = getattr(self._r1, attr)
3384 iter1 = getattr(self._r1, attr)
3385 if iter1 is None:
3385 if iter1 is None:
3386 # let's avoid side effect (not sure it matters)
3386 # let's avoid side effect (not sure it matters)
3387 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3387 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3388 else:
3388 else:
3389 iter1 = iter1()
3389 iter1 = iter1()
3390 # get iterator for _r2
3390 # get iterator for _r2
3391 iter2 = getattr(self._r2, attr)
3391 iter2 = getattr(self._r2, attr)
3392 if iter2 is None:
3392 if iter2 is None:
3393 # let's avoid side effect (not sure it matters)
3393 # let's avoid side effect (not sure it matters)
3394 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3394 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3395 else:
3395 else:
3396 iter2 = iter2()
3396 iter2 = iter2()
3397 return _iterordered(self._ascending, iter1, iter2)
3397 return _iterordered(self._ascending, iter1, iter2)
3398
3398
3399 def _trysetasclist(self):
3399 def _trysetasclist(self):
3400 """populate the _asclist attribute if possible and necessary"""
3400 """populate the _asclist attribute if possible and necessary"""
3401 if self._genlist is not None and self._asclist is None:
3401 if self._genlist is not None and self._asclist is None:
3402 self._asclist = sorted(self._genlist)
3402 self._asclist = sorted(self._genlist)
3403
3403
3404 @property
3404 @property
3405 def fastasc(self):
3405 def fastasc(self):
3406 self._trysetasclist()
3406 self._trysetasclist()
3407 if self._asclist is not None:
3407 if self._asclist is not None:
3408 return self._asclist.__iter__
3408 return self._asclist.__iter__
3409 iter1 = self._r1.fastasc
3409 iter1 = self._r1.fastasc
3410 iter2 = self._r2.fastasc
3410 iter2 = self._r2.fastasc
3411 if None in (iter1, iter2):
3411 if None in (iter1, iter2):
3412 return None
3412 return None
3413 return lambda: _iterordered(True, iter1(), iter2())
3413 return lambda: _iterordered(True, iter1(), iter2())
3414
3414
3415 @property
3415 @property
3416 def fastdesc(self):
3416 def fastdesc(self):
3417 self._trysetasclist()
3417 self._trysetasclist()
3418 if self._asclist is not None:
3418 if self._asclist is not None:
3419 return self._asclist.__reversed__
3419 return self._asclist.__reversed__
3420 iter1 = self._r1.fastdesc
3420 iter1 = self._r1.fastdesc
3421 iter2 = self._r2.fastdesc
3421 iter2 = self._r2.fastdesc
3422 if None in (iter1, iter2):
3422 if None in (iter1, iter2):
3423 return None
3423 return None
3424 return lambda: _iterordered(False, iter1(), iter2())
3424 return lambda: _iterordered(False, iter1(), iter2())
3425
3425
3426 def __contains__(self, x):
3426 def __contains__(self, x):
3427 return x in self._r1 or x in self._r2
3427 return x in self._r1 or x in self._r2
3428
3428
3429 def sort(self, reverse=False):
3429 def sort(self, reverse=False):
3430 """Sort the added set
3430 """Sort the added set
3431
3431
3432 For this we use the cached list with all the generated values and if we
3432 For this we use the cached list with all the generated values and if we
3433 know they are ascending or descending we can sort them in a smart way.
3433 know they are ascending or descending we can sort them in a smart way.
3434 """
3434 """
3435 self._ascending = not reverse
3435 self._ascending = not reverse
3436
3436
3437 def isascending(self):
3437 def isascending(self):
3438 return self._ascending is not None and self._ascending
3438 return self._ascending is not None and self._ascending
3439
3439
3440 def isdescending(self):
3440 def isdescending(self):
3441 return self._ascending is not None and not self._ascending
3441 return self._ascending is not None and not self._ascending
3442
3442
3443 def istopo(self):
3443 def istopo(self):
3444 # not worth the trouble asserting if the two sets combined are still
3444 # not worth the trouble asserting if the two sets combined are still
3445 # in topographical order. Use the sort() predicate to explicitly sort
3445 # in topographical order. Use the sort() predicate to explicitly sort
3446 # again instead.
3446 # again instead.
3447 return False
3447 return False
3448
3448
3449 def reverse(self):
3449 def reverse(self):
3450 if self._ascending is None:
3450 if self._ascending is None:
3451 self._list.reverse()
3451 self._list.reverse()
3452 else:
3452 else:
3453 self._ascending = not self._ascending
3453 self._ascending = not self._ascending
3454
3454
3455 def first(self):
3455 def first(self):
3456 for x in self:
3456 for x in self:
3457 return x
3457 return x
3458 return None
3458 return None
3459
3459
3460 def last(self):
3460 def last(self):
3461 self.reverse()
3461 self.reverse()
3462 val = self.first()
3462 val = self.first()
3463 self.reverse()
3463 self.reverse()
3464 return val
3464 return val
3465
3465
3466 def __repr__(self):
3466 def __repr__(self):
3467 d = {None: '', False: '-', True: '+'}[self._ascending]
3467 d = {None: '', False: '-', True: '+'}[self._ascending]
3468 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3468 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3469
3469
3470 class generatorset(abstractsmartset):
3470 class generatorset(abstractsmartset):
3471 """Wrap a generator for lazy iteration
3471 """Wrap a generator for lazy iteration
3472
3472
3473 Wrapper structure for generators that provides lazy membership and can
3473 Wrapper structure for generators that provides lazy membership and can
3474 be iterated more than once.
3474 be iterated more than once.
3475 When asked for membership it generates values until either it finds the
3475 When asked for membership it generates values until either it finds the
3476 requested one or has gone through all the elements in the generator
3476 requested one or has gone through all the elements in the generator
3477 """
3477 """
3478 def __init__(self, gen, iterasc=None):
3478 def __init__(self, gen, iterasc=None):
3479 """
3479 """
3480 gen: a generator producing the values for the generatorset.
3480 gen: a generator producing the values for the generatorset.
3481 """
3481 """
3482 self._gen = gen
3482 self._gen = gen
3483 self._asclist = None
3483 self._asclist = None
3484 self._cache = {}
3484 self._cache = {}
3485 self._genlist = []
3485 self._genlist = []
3486 self._finished = False
3486 self._finished = False
3487 self._ascending = True
3487 self._ascending = True
3488 if iterasc is not None:
3488 if iterasc is not None:
3489 if iterasc:
3489 if iterasc:
3490 self.fastasc = self._iterator
3490 self.fastasc = self._iterator
3491 self.__contains__ = self._asccontains
3491 self.__contains__ = self._asccontains
3492 else:
3492 else:
3493 self.fastdesc = self._iterator
3493 self.fastdesc = self._iterator
3494 self.__contains__ = self._desccontains
3494 self.__contains__ = self._desccontains
3495
3495
3496 def __nonzero__(self):
3496 def __nonzero__(self):
3497 # Do not use 'for r in self' because it will enforce the iteration
3497 # Do not use 'for r in self' because it will enforce the iteration
3498 # order (default ascending), possibly unrolling a whole descending
3498 # order (default ascending), possibly unrolling a whole descending
3499 # iterator.
3499 # iterator.
3500 if self._genlist:
3500 if self._genlist:
3501 return True
3501 return True
3502 for r in self._consumegen():
3502 for r in self._consumegen():
3503 return True
3503 return True
3504 return False
3504 return False
3505
3505
3506 def __contains__(self, x):
3506 def __contains__(self, x):
3507 if x in self._cache:
3507 if x in self._cache:
3508 return self._cache[x]
3508 return self._cache[x]
3509
3509
3510 # Use new values only, as existing values would be cached.
3510 # Use new values only, as existing values would be cached.
3511 for l in self._consumegen():
3511 for l in self._consumegen():
3512 if l == x:
3512 if l == x:
3513 return True
3513 return True
3514
3514
3515 self._cache[x] = False
3515 self._cache[x] = False
3516 return False
3516 return False
3517
3517
3518 def _asccontains(self, x):
3518 def _asccontains(self, x):
3519 """version of contains optimised for ascending generator"""
3519 """version of contains optimised for ascending generator"""
3520 if x in self._cache:
3520 if x in self._cache:
3521 return self._cache[x]
3521 return self._cache[x]
3522
3522
3523 # Use new values only, as existing values would be cached.
3523 # Use new values only, as existing values would be cached.
3524 for l in self._consumegen():
3524 for l in self._consumegen():
3525 if l == x:
3525 if l == x:
3526 return True
3526 return True
3527 if l > x:
3527 if l > x:
3528 break
3528 break
3529
3529
3530 self._cache[x] = False
3530 self._cache[x] = False
3531 return False
3531 return False
3532
3532
3533 def _desccontains(self, x):
3533 def _desccontains(self, x):
3534 """version of contains optimised for descending generator"""
3534 """version of contains optimised for descending generator"""
3535 if x in self._cache:
3535 if x in self._cache:
3536 return self._cache[x]
3536 return self._cache[x]
3537
3537
3538 # Use new values only, as existing values would be cached.
3538 # Use new values only, as existing values would be cached.
3539 for l in self._consumegen():
3539 for l in self._consumegen():
3540 if l == x:
3540 if l == x:
3541 return True
3541 return True
3542 if l < x:
3542 if l < x:
3543 break
3543 break
3544
3544
3545 self._cache[x] = False
3545 self._cache[x] = False
3546 return False
3546 return False
3547
3547
3548 def __iter__(self):
3548 def __iter__(self):
3549 if self._ascending:
3549 if self._ascending:
3550 it = self.fastasc
3550 it = self.fastasc
3551 else:
3551 else:
3552 it = self.fastdesc
3552 it = self.fastdesc
3553 if it is not None:
3553 if it is not None:
3554 return it()
3554 return it()
3555 # we need to consume the iterator
3555 # we need to consume the iterator
3556 for x in self._consumegen():
3556 for x in self._consumegen():
3557 pass
3557 pass
3558 # recall the same code
3558 # recall the same code
3559 return iter(self)
3559 return iter(self)
3560
3560
3561 def _iterator(self):
3561 def _iterator(self):
3562 if self._finished:
3562 if self._finished:
3563 return iter(self._genlist)
3563 return iter(self._genlist)
3564
3564
3565 # We have to use this complex iteration strategy to allow multiple
3565 # We have to use this complex iteration strategy to allow multiple
3566 # iterations at the same time. We need to be able to catch revision
3566 # iterations at the same time. We need to be able to catch revision
3567 # removed from _consumegen and added to genlist in another instance.
3567 # removed from _consumegen and added to genlist in another instance.
3568 #
3568 #
3569 # Getting rid of it would provide an about 15% speed up on this
3569 # Getting rid of it would provide an about 15% speed up on this
3570 # iteration.
3570 # iteration.
3571 genlist = self._genlist
3571 genlist = self._genlist
3572 nextrev = self._consumegen().next
3572 nextrev = self._consumegen().next
3573 _len = len # cache global lookup
3573 _len = len # cache global lookup
3574 def gen():
3574 def gen():
3575 i = 0
3575 i = 0
3576 while True:
3576 while True:
3577 if i < _len(genlist):
3577 if i < _len(genlist):
3578 yield genlist[i]
3578 yield genlist[i]
3579 else:
3579 else:
3580 yield nextrev()
3580 yield nextrev()
3581 i += 1
3581 i += 1
3582 return gen()
3582 return gen()
3583
3583
3584 def _consumegen(self):
3584 def _consumegen(self):
3585 cache = self._cache
3585 cache = self._cache
3586 genlist = self._genlist.append
3586 genlist = self._genlist.append
3587 for item in self._gen:
3587 for item in self._gen:
3588 cache[item] = True
3588 cache[item] = True
3589 genlist(item)
3589 genlist(item)
3590 yield item
3590 yield item
3591 if not self._finished:
3591 if not self._finished:
3592 self._finished = True
3592 self._finished = True
3593 asc = self._genlist[:]
3593 asc = self._genlist[:]
3594 asc.sort()
3594 asc.sort()
3595 self._asclist = asc
3595 self._asclist = asc
3596 self.fastasc = asc.__iter__
3596 self.fastasc = asc.__iter__
3597 self.fastdesc = asc.__reversed__
3597 self.fastdesc = asc.__reversed__
3598
3598
3599 def __len__(self):
3599 def __len__(self):
3600 for x in self._consumegen():
3600 for x in self._consumegen():
3601 pass
3601 pass
3602 return len(self._genlist)
3602 return len(self._genlist)
3603
3603
3604 def sort(self, reverse=False):
3604 def sort(self, reverse=False):
3605 self._ascending = not reverse
3605 self._ascending = not reverse
3606
3606
3607 def reverse(self):
3607 def reverse(self):
3608 self._ascending = not self._ascending
3608 self._ascending = not self._ascending
3609
3609
3610 def isascending(self):
3610 def isascending(self):
3611 return self._ascending
3611 return self._ascending
3612
3612
3613 def isdescending(self):
3613 def isdescending(self):
3614 return not self._ascending
3614 return not self._ascending
3615
3615
3616 def istopo(self):
3616 def istopo(self):
3617 # not worth the trouble asserting if the two sets combined are still
3617 # not worth the trouble asserting if the two sets combined are still
3618 # in topographical order. Use the sort() predicate to explicitly sort
3618 # in topographical order. Use the sort() predicate to explicitly sort
3619 # again instead.
3619 # again instead.
3620 return False
3620 return False
3621
3621
3622 def first(self):
3622 def first(self):
3623 if self._ascending:
3623 if self._ascending:
3624 it = self.fastasc
3624 it = self.fastasc
3625 else:
3625 else:
3626 it = self.fastdesc
3626 it = self.fastdesc
3627 if it is None:
3627 if it is None:
3628 # we need to consume all and try again
3628 # we need to consume all and try again
3629 for x in self._consumegen():
3629 for x in self._consumegen():
3630 pass
3630 pass
3631 return self.first()
3631 return self.first()
3632 return next(it(), None)
3632 return next(it(), None)
3633
3633
3634 def last(self):
3634 def last(self):
3635 if self._ascending:
3635 if self._ascending:
3636 it = self.fastdesc
3636 it = self.fastdesc
3637 else:
3637 else:
3638 it = self.fastasc
3638 it = self.fastasc
3639 if it is None:
3639 if it is None:
3640 # we need to consume all and try again
3640 # we need to consume all and try again
3641 for x in self._consumegen():
3641 for x in self._consumegen():
3642 pass
3642 pass
3643 return self.first()
3643 return self.first()
3644 return next(it(), None)
3644 return next(it(), None)
3645
3645
3646 def __repr__(self):
3646 def __repr__(self):
3647 d = {False: '-', True: '+'}[self._ascending]
3647 d = {False: '-', True: '+'}[self._ascending]
3648 return '<%s%s>' % (type(self).__name__, d)
3648 return '<%s%s>' % (type(self).__name__, d)
3649
3649
3650 class spanset(abstractsmartset):
3650 class spanset(abstractsmartset):
3651 """Duck type for baseset class which represents a range of revisions and
3651 """Duck type for baseset class which represents a range of revisions and
3652 can work lazily and without having all the range in memory
3652 can work lazily and without having all the range in memory
3653
3653
3654 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3654 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3655 notable points:
3655 notable points:
3656 - when x < y it will be automatically descending,
3656 - when x < y it will be automatically descending,
3657 - revision filtered with this repoview will be skipped.
3657 - revision filtered with this repoview will be skipped.
3658
3658
3659 """
3659 """
3660 def __init__(self, repo, start=0, end=None):
3660 def __init__(self, repo, start=0, end=None):
3661 """
3661 """
3662 start: first revision included the set
3662 start: first revision included the set
3663 (default to 0)
3663 (default to 0)
3664 end: first revision excluded (last+1)
3664 end: first revision excluded (last+1)
3665 (default to len(repo)
3665 (default to len(repo)
3666
3666
3667 Spanset will be descending if `end` < `start`.
3667 Spanset will be descending if `end` < `start`.
3668 """
3668 """
3669 if end is None:
3669 if end is None:
3670 end = len(repo)
3670 end = len(repo)
3671 self._ascending = start <= end
3671 self._ascending = start <= end
3672 if not self._ascending:
3672 if not self._ascending:
3673 start, end = end + 1, start +1
3673 start, end = end + 1, start +1
3674 self._start = start
3674 self._start = start
3675 self._end = end
3675 self._end = end
3676 self._hiddenrevs = repo.changelog.filteredrevs
3676 self._hiddenrevs = repo.changelog.filteredrevs
3677
3677
3678 def sort(self, reverse=False):
3678 def sort(self, reverse=False):
3679 self._ascending = not reverse
3679 self._ascending = not reverse
3680
3680
3681 def reverse(self):
3681 def reverse(self):
3682 self._ascending = not self._ascending
3682 self._ascending = not self._ascending
3683
3683
3684 def istopo(self):
3684 def istopo(self):
3685 # not worth the trouble asserting if the two sets combined are still
3685 # not worth the trouble asserting if the two sets combined are still
3686 # in topographical order. Use the sort() predicate to explicitly sort
3686 # in topographical order. Use the sort() predicate to explicitly sort
3687 # again instead.
3687 # again instead.
3688 return False
3688 return False
3689
3689
3690 def _iterfilter(self, iterrange):
3690 def _iterfilter(self, iterrange):
3691 s = self._hiddenrevs
3691 s = self._hiddenrevs
3692 for r in iterrange:
3692 for r in iterrange:
3693 if r not in s:
3693 if r not in s:
3694 yield r
3694 yield r
3695
3695
3696 def __iter__(self):
3696 def __iter__(self):
3697 if self._ascending:
3697 if self._ascending:
3698 return self.fastasc()
3698 return self.fastasc()
3699 else:
3699 else:
3700 return self.fastdesc()
3700 return self.fastdesc()
3701
3701
3702 def fastasc(self):
3702 def fastasc(self):
3703 iterrange = xrange(self._start, self._end)
3703 iterrange = xrange(self._start, self._end)
3704 if self._hiddenrevs:
3704 if self._hiddenrevs:
3705 return self._iterfilter(iterrange)
3705 return self._iterfilter(iterrange)
3706 return iter(iterrange)
3706 return iter(iterrange)
3707
3707
3708 def fastdesc(self):
3708 def fastdesc(self):
3709 iterrange = xrange(self._end - 1, self._start - 1, -1)
3709 iterrange = xrange(self._end - 1, self._start - 1, -1)
3710 if self._hiddenrevs:
3710 if self._hiddenrevs:
3711 return self._iterfilter(iterrange)
3711 return self._iterfilter(iterrange)
3712 return iter(iterrange)
3712 return iter(iterrange)
3713
3713
3714 def __contains__(self, rev):
3714 def __contains__(self, rev):
3715 hidden = self._hiddenrevs
3715 hidden = self._hiddenrevs
3716 return ((self._start <= rev < self._end)
3716 return ((self._start <= rev < self._end)
3717 and not (hidden and rev in hidden))
3717 and not (hidden and rev in hidden))
3718
3718
3719 def __nonzero__(self):
3719 def __nonzero__(self):
3720 for r in self:
3720 for r in self:
3721 return True
3721 return True
3722 return False
3722 return False
3723
3723
3724 def __len__(self):
3724 def __len__(self):
3725 if not self._hiddenrevs:
3725 if not self._hiddenrevs:
3726 return abs(self._end - self._start)
3726 return abs(self._end - self._start)
3727 else:
3727 else:
3728 count = 0
3728 count = 0
3729 start = self._start
3729 start = self._start
3730 end = self._end
3730 end = self._end
3731 for rev in self._hiddenrevs:
3731 for rev in self._hiddenrevs:
3732 if (end < rev <= start) or (start <= rev < end):
3732 if (end < rev <= start) or (start <= rev < end):
3733 count += 1
3733 count += 1
3734 return abs(self._end - self._start) - count
3734 return abs(self._end - self._start) - count
3735
3735
3736 def isascending(self):
3736 def isascending(self):
3737 return self._ascending
3737 return self._ascending
3738
3738
3739 def isdescending(self):
3739 def isdescending(self):
3740 return not self._ascending
3740 return not self._ascending
3741
3741
3742 def first(self):
3742 def first(self):
3743 if self._ascending:
3743 if self._ascending:
3744 it = self.fastasc
3744 it = self.fastasc
3745 else:
3745 else:
3746 it = self.fastdesc
3746 it = self.fastdesc
3747 for x in it():
3747 for x in it():
3748 return x
3748 return x
3749 return None
3749 return None
3750
3750
3751 def last(self):
3751 def last(self):
3752 if self._ascending:
3752 if self._ascending:
3753 it = self.fastdesc
3753 it = self.fastdesc
3754 else:
3754 else:
3755 it = self.fastasc
3755 it = self.fastasc
3756 for x in it():
3756 for x in it():
3757 return x
3757 return x
3758 return None
3758 return None
3759
3759
3760 def __repr__(self):
3760 def __repr__(self):
3761 d = {False: '-', True: '+'}[self._ascending]
3761 d = {False: '-', True: '+'}[self._ascending]
3762 return '<%s%s %d:%d>' % (type(self).__name__, d,
3762 return '<%s%s %d:%d>' % (type(self).__name__, d,
3763 self._start, self._end - 1)
3763 self._start, self._end - 1)
3764
3764
3765 class fullreposet(spanset):
3765 class fullreposet(spanset):
3766 """a set containing all revisions in the repo
3766 """a set containing all revisions in the repo
3767
3767
3768 This class exists to host special optimization and magic to handle virtual
3768 This class exists to host special optimization and magic to handle virtual
3769 revisions such as "null".
3769 revisions such as "null".
3770 """
3770 """
3771
3771
3772 def __init__(self, repo):
3772 def __init__(self, repo):
3773 super(fullreposet, self).__init__(repo)
3773 super(fullreposet, self).__init__(repo)
3774
3774
3775 def __and__(self, other):
3775 def __and__(self, other):
3776 """As self contains the whole repo, all of the other set should also be
3776 """As self contains the whole repo, all of the other set should also be
3777 in self. Therefore `self & other = other`.
3777 in self. Therefore `self & other = other`.
3778
3778
3779 This boldly assumes the other contains valid revs only.
3779 This boldly assumes the other contains valid revs only.
3780 """
3780 """
3781 # other not a smartset, make is so
3781 # other not a smartset, make is so
3782 if not util.safehasattr(other, 'isascending'):
3782 if not util.safehasattr(other, 'isascending'):
3783 # filter out hidden revision
3783 # filter out hidden revision
3784 # (this boldly assumes all smartset are pure)
3784 # (this boldly assumes all smartset are pure)
3785 #
3785 #
3786 # `other` was used with "&", let's assume this is a set like
3786 # `other` was used with "&", let's assume this is a set like
3787 # object.
3787 # object.
3788 other = baseset(other - self._hiddenrevs)
3788 other = baseset(other - self._hiddenrevs)
3789
3789
3790 # XXX As fullreposet is also used as bootstrap, this is wrong.
3790 # XXX As fullreposet is also used as bootstrap, this is wrong.
3791 #
3791 #
3792 # With a giveme312() revset returning [3,1,2], this makes
3792 # With a giveme312() revset returning [3,1,2], this makes
3793 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3793 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3794 # We cannot just drop it because other usage still need to sort it:
3794 # We cannot just drop it because other usage still need to sort it:
3795 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3795 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3796 #
3796 #
3797 # There is also some faulty revset implementations that rely on it
3797 # There is also some faulty revset implementations that rely on it
3798 # (eg: children as of its state in e8075329c5fb)
3798 # (eg: children as of its state in e8075329c5fb)
3799 #
3799 #
3800 # When we fix the two points above we can move this into the if clause
3800 # When we fix the two points above we can move this into the if clause
3801 other.sort(reverse=self.isdescending())
3801 other.sort(reverse=self.isdescending())
3802 return other
3802 return other
3803
3803
3804 def prettyformatset(revs):
3804 def prettyformatset(revs):
3805 lines = []
3805 lines = []
3806 rs = repr(revs)
3806 rs = repr(revs)
3807 p = 0
3807 p = 0
3808 while p < len(rs):
3808 while p < len(rs):
3809 q = rs.find('<', p + 1)
3809 q = rs.find('<', p + 1)
3810 if q < 0:
3810 if q < 0:
3811 q = len(rs)
3811 q = len(rs)
3812 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3812 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3813 assert l >= 0
3813 assert l >= 0
3814 lines.append((l, rs[p:q].rstrip()))
3814 lines.append((l, rs[p:q].rstrip()))
3815 p = q
3815 p = q
3816 return '\n'.join(' ' * l + s for l, s in lines)
3816 return '\n'.join(' ' * l + s for l, s in lines)
3817
3817
3818 def loadpredicate(ui, extname, registrarobj):
3818 def loadpredicate(ui, extname, registrarobj):
3819 """Load revset predicates from specified registrarobj
3819 """Load revset predicates from specified registrarobj
3820 """
3820 """
3821 for name, func in registrarobj._table.iteritems():
3821 for name, func in registrarobj._table.iteritems():
3822 symbols[name] = func
3822 symbols[name] = func
3823 if func._safe:
3823 if func._safe:
3824 safesymbols.add(name)
3824 safesymbols.add(name)
3825
3825
3826 # load built-in predicates explicitly to setup safesymbols
3826 # load built-in predicates explicitly to setup safesymbols
3827 loadpredicate(None, None, predicate)
3827 loadpredicate(None, None, predicate)
3828
3828
3829 # tell hggettext to extract docstrings from these functions:
3829 # tell hggettext to extract docstrings from these functions:
3830 i18nfunctions = symbols.values()
3830 i18nfunctions = symbols.values()
@@ -1,3540 +1,3569 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols['r3232'] = r3232
19 > mercurial.revset.symbols['r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > testrevset=$TESTTMP/testrevset.py
23 > testrevset=$TESTTMP/testrevset.py
24 > EOF
24 > EOF
25
25
26 $ try() {
26 $ try() {
27 > hg debugrevspec --debug "$@"
27 > hg debugrevspec --debug "$@"
28 > }
28 > }
29
29
30 $ log() {
30 $ log() {
31 > hg log --template '{rev}\n' -r "$1"
31 > hg log --template '{rev}\n' -r "$1"
32 > }
32 > }
33
33
34 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 (range
164 (range
165 ('string', '0')
165 ('string', '0')
166 ('string', 'tip')
166 ('string', 'tip')
167 define)
167 define)
168 * set:
168 * set:
169 <spanset+ 0:9>
169 <spanset+ 0:9>
170 0
170 0
171 1
171 1
172 2
172 2
173 3
173 3
174 4
174 4
175 5
175 5
176 6
176 6
177 7
177 7
178 8
178 8
179 9
179 9
180 $ try 3::6
180 $ try 3::6
181 (dagrange
181 (dagrange
182 ('symbol', '3')
182 ('symbol', '3')
183 ('symbol', '6'))
183 ('symbol', '6'))
184 * set:
184 * set:
185 <baseset+ [3, 5, 6]>
185 <baseset+ [3, 5, 6]>
186 3
186 3
187 5
187 5
188 6
188 6
189 $ try '0|1|2'
189 $ try '0|1|2'
190 (or
190 (or
191 (list
191 (list
192 ('symbol', '0')
192 ('symbol', '0')
193 ('symbol', '1')
193 ('symbol', '1')
194 ('symbol', '2')))
194 ('symbol', '2')))
195 * set:
195 * set:
196 <baseset [0, 1, 2]>
196 <baseset [0, 1, 2]>
197 0
197 0
198 1
198 1
199 2
199 2
200
200
201 names that should work without quoting
201 names that should work without quoting
202
202
203 $ try a
203 $ try a
204 ('symbol', 'a')
204 ('symbol', 'a')
205 * set:
205 * set:
206 <baseset [0]>
206 <baseset [0]>
207 0
207 0
208 $ try b-a
208 $ try b-a
209 (minus
209 (minus
210 ('symbol', 'b')
210 ('symbol', 'b')
211 ('symbol', 'a'))
211 ('symbol', 'a'))
212 * set:
212 * set:
213 <filteredset
213 <filteredset
214 <baseset [1]>,
214 <baseset [1]>,
215 <not
215 <not
216 <baseset [0]>>>
216 <baseset [0]>>>
217 1
217 1
218 $ try _a_b_c_
218 $ try _a_b_c_
219 ('symbol', '_a_b_c_')
219 ('symbol', '_a_b_c_')
220 * set:
220 * set:
221 <baseset [6]>
221 <baseset [6]>
222 6
222 6
223 $ try _a_b_c_-a
223 $ try _a_b_c_-a
224 (minus
224 (minus
225 ('symbol', '_a_b_c_')
225 ('symbol', '_a_b_c_')
226 ('symbol', 'a'))
226 ('symbol', 'a'))
227 * set:
227 * set:
228 <filteredset
228 <filteredset
229 <baseset [6]>,
229 <baseset [6]>,
230 <not
230 <not
231 <baseset [0]>>>
231 <baseset [0]>>>
232 6
232 6
233 $ try .a.b.c.
233 $ try .a.b.c.
234 ('symbol', '.a.b.c.')
234 ('symbol', '.a.b.c.')
235 * set:
235 * set:
236 <baseset [7]>
236 <baseset [7]>
237 7
237 7
238 $ try .a.b.c.-a
238 $ try .a.b.c.-a
239 (minus
239 (minus
240 ('symbol', '.a.b.c.')
240 ('symbol', '.a.b.c.')
241 ('symbol', 'a'))
241 ('symbol', 'a'))
242 * set:
242 * set:
243 <filteredset
243 <filteredset
244 <baseset [7]>,
244 <baseset [7]>,
245 <not
245 <not
246 <baseset [0]>>>
246 <baseset [0]>>>
247 7
247 7
248
248
249 names that should be caught by fallback mechanism
249 names that should be caught by fallback mechanism
250
250
251 $ try -- '-a-b-c-'
251 $ try -- '-a-b-c-'
252 ('symbol', '-a-b-c-')
252 ('symbol', '-a-b-c-')
253 * set:
253 * set:
254 <baseset [4]>
254 <baseset [4]>
255 4
255 4
256 $ log -a-b-c-
256 $ log -a-b-c-
257 4
257 4
258 $ try '+a+b+c+'
258 $ try '+a+b+c+'
259 ('symbol', '+a+b+c+')
259 ('symbol', '+a+b+c+')
260 * set:
260 * set:
261 <baseset [3]>
261 <baseset [3]>
262 3
262 3
263 $ try '+a+b+c+:'
263 $ try '+a+b+c+:'
264 (rangepost
264 (rangepost
265 ('symbol', '+a+b+c+'))
265 ('symbol', '+a+b+c+'))
266 * set:
266 * set:
267 <spanset+ 3:9>
267 <spanset+ 3:9>
268 3
268 3
269 4
269 4
270 5
270 5
271 6
271 6
272 7
272 7
273 8
273 8
274 9
274 9
275 $ try ':+a+b+c+'
275 $ try ':+a+b+c+'
276 (rangepre
276 (rangepre
277 ('symbol', '+a+b+c+'))
277 ('symbol', '+a+b+c+'))
278 * set:
278 * set:
279 <spanset+ 0:3>
279 <spanset+ 0:3>
280 0
280 0
281 1
281 1
282 2
282 2
283 3
283 3
284 $ try -- '-a-b-c-:+a+b+c+'
284 $ try -- '-a-b-c-:+a+b+c+'
285 (range
285 (range
286 ('symbol', '-a-b-c-')
286 ('symbol', '-a-b-c-')
287 ('symbol', '+a+b+c+'))
287 ('symbol', '+a+b+c+'))
288 * set:
288 * set:
289 <spanset- 3:4>
289 <spanset- 3:4>
290 4
290 4
291 3
291 3
292 $ log '-a-b-c-:+a+b+c+'
292 $ log '-a-b-c-:+a+b+c+'
293 4
293 4
294 3
294 3
295
295
296 $ try -- -a-b-c--a # complains
296 $ try -- -a-b-c--a # complains
297 (minus
297 (minus
298 (minus
298 (minus
299 (minus
299 (minus
300 (negate
300 (negate
301 ('symbol', 'a'))
301 ('symbol', 'a'))
302 ('symbol', 'b'))
302 ('symbol', 'b'))
303 ('symbol', 'c'))
303 ('symbol', 'c'))
304 (negate
304 (negate
305 ('symbol', 'a')))
305 ('symbol', 'a')))
306 abort: unknown revision '-a'!
306 abort: unknown revision '-a'!
307 [255]
307 [255]
308 $ try Γ©
308 $ try Γ©
309 ('symbol', '\xc3\xa9')
309 ('symbol', '\xc3\xa9')
310 * set:
310 * set:
311 <baseset [9]>
311 <baseset [9]>
312 9
312 9
313
313
314 no quoting needed
314 no quoting needed
315
315
316 $ log ::a-b-c-
316 $ log ::a-b-c-
317 0
317 0
318 1
318 1
319 2
319 2
320
320
321 quoting needed
321 quoting needed
322
322
323 $ try '"-a-b-c-"-a'
323 $ try '"-a-b-c-"-a'
324 (minus
324 (minus
325 ('string', '-a-b-c-')
325 ('string', '-a-b-c-')
326 ('symbol', 'a'))
326 ('symbol', 'a'))
327 * set:
327 * set:
328 <filteredset
328 <filteredset
329 <baseset [4]>,
329 <baseset [4]>,
330 <not
330 <not
331 <baseset [0]>>>
331 <baseset [0]>>>
332 4
332 4
333
333
334 $ log '1 or 2'
334 $ log '1 or 2'
335 1
335 1
336 2
336 2
337 $ log '1|2'
337 $ log '1|2'
338 1
338 1
339 2
339 2
340 $ log '1 and 2'
340 $ log '1 and 2'
341 $ log '1&2'
341 $ log '1&2'
342 $ try '1&2|3' # precedence - and is higher
342 $ try '1&2|3' # precedence - and is higher
343 (or
343 (or
344 (list
344 (list
345 (and
345 (and
346 ('symbol', '1')
346 ('symbol', '1')
347 ('symbol', '2'))
347 ('symbol', '2'))
348 ('symbol', '3')))
348 ('symbol', '3')))
349 * set:
349 * set:
350 <addset
350 <addset
351 <baseset []>,
351 <baseset []>,
352 <baseset [3]>>
352 <baseset [3]>>
353 3
353 3
354 $ try '1|2&3'
354 $ try '1|2&3'
355 (or
355 (or
356 (list
356 (list
357 ('symbol', '1')
357 ('symbol', '1')
358 (and
358 (and
359 ('symbol', '2')
359 ('symbol', '2')
360 ('symbol', '3'))))
360 ('symbol', '3'))))
361 * set:
361 * set:
362 <addset
362 <addset
363 <baseset [1]>,
363 <baseset [1]>,
364 <baseset []>>
364 <baseset []>>
365 1
365 1
366 $ try '1&2&3' # associativity
366 $ try '1&2&3' # associativity
367 (and
367 (and
368 (and
368 (and
369 ('symbol', '1')
369 ('symbol', '1')
370 ('symbol', '2'))
370 ('symbol', '2'))
371 ('symbol', '3'))
371 ('symbol', '3'))
372 * set:
372 * set:
373 <baseset []>
373 <baseset []>
374 $ try '1|(2|3)'
374 $ try '1|(2|3)'
375 (or
375 (or
376 (list
376 (list
377 ('symbol', '1')
377 ('symbol', '1')
378 (group
378 (group
379 (or
379 (or
380 (list
380 (list
381 ('symbol', '2')
381 ('symbol', '2')
382 ('symbol', '3'))))))
382 ('symbol', '3'))))))
383 * set:
383 * set:
384 <addset
384 <addset
385 <baseset [1]>,
385 <baseset [1]>,
386 <baseset [2, 3]>>
386 <baseset [2, 3]>>
387 1
387 1
388 2
388 2
389 3
389 3
390 $ log '1.0' # tag
390 $ log '1.0' # tag
391 6
391 6
392 $ log 'a' # branch
392 $ log 'a' # branch
393 0
393 0
394 $ log '2785f51ee'
394 $ log '2785f51ee'
395 0
395 0
396 $ log 'date(2005)'
396 $ log 'date(2005)'
397 4
397 4
398 $ log 'date(this is a test)'
398 $ log 'date(this is a test)'
399 hg: parse error at 10: unexpected token: symbol
399 hg: parse error at 10: unexpected token: symbol
400 [255]
400 [255]
401 $ log 'date()'
401 $ log 'date()'
402 hg: parse error: date requires a string
402 hg: parse error: date requires a string
403 [255]
403 [255]
404 $ log 'date'
404 $ log 'date'
405 abort: unknown revision 'date'!
405 abort: unknown revision 'date'!
406 [255]
406 [255]
407 $ log 'date('
407 $ log 'date('
408 hg: parse error at 5: not a prefix: end
408 hg: parse error at 5: not a prefix: end
409 [255]
409 [255]
410 $ log 'date("\xy")'
410 $ log 'date("\xy")'
411 hg: parse error: invalid \x escape
411 hg: parse error: invalid \x escape
412 [255]
412 [255]
413 $ log 'date(tip)'
413 $ log 'date(tip)'
414 abort: invalid date: 'tip'
414 abort: invalid date: 'tip'
415 [255]
415 [255]
416 $ log '0:date'
416 $ log '0:date'
417 abort: unknown revision 'date'!
417 abort: unknown revision 'date'!
418 [255]
418 [255]
419 $ log '::"date"'
419 $ log '::"date"'
420 abort: unknown revision 'date'!
420 abort: unknown revision 'date'!
421 [255]
421 [255]
422 $ hg book date -r 4
422 $ hg book date -r 4
423 $ log '0:date'
423 $ log '0:date'
424 0
424 0
425 1
425 1
426 2
426 2
427 3
427 3
428 4
428 4
429 $ log '::date'
429 $ log '::date'
430 0
430 0
431 1
431 1
432 2
432 2
433 4
433 4
434 $ log '::"date"'
434 $ log '::"date"'
435 0
435 0
436 1
436 1
437 2
437 2
438 4
438 4
439 $ log 'date(2005) and 1::'
439 $ log 'date(2005) and 1::'
440 4
440 4
441 $ hg book -d date
441 $ hg book -d date
442
442
443 function name should be a symbol
443 function name should be a symbol
444
444
445 $ log '"date"(2005)'
445 $ log '"date"(2005)'
446 hg: parse error: not a symbol
446 hg: parse error: not a symbol
447 [255]
447 [255]
448
448
449 keyword arguments
449 keyword arguments
450
450
451 $ log 'extra(branch, value=a)'
451 $ log 'extra(branch, value=a)'
452 0
452 0
453
453
454 $ log 'extra(branch, a, b)'
454 $ log 'extra(branch, a, b)'
455 hg: parse error: extra takes at most 2 arguments
455 hg: parse error: extra takes at most 2 arguments
456 [255]
456 [255]
457 $ log 'extra(a, label=b)'
457 $ log 'extra(a, label=b)'
458 hg: parse error: extra got multiple values for keyword argument 'label'
458 hg: parse error: extra got multiple values for keyword argument 'label'
459 [255]
459 [255]
460 $ log 'extra(label=branch, default)'
460 $ log 'extra(label=branch, default)'
461 hg: parse error: extra got an invalid argument
461 hg: parse error: extra got an invalid argument
462 [255]
462 [255]
463 $ log 'extra(branch, foo+bar=baz)'
463 $ log 'extra(branch, foo+bar=baz)'
464 hg: parse error: extra got an invalid argument
464 hg: parse error: extra got an invalid argument
465 [255]
465 [255]
466 $ log 'extra(unknown=branch)'
466 $ log 'extra(unknown=branch)'
467 hg: parse error: extra got an unexpected keyword argument 'unknown'
467 hg: parse error: extra got an unexpected keyword argument 'unknown'
468 [255]
468 [255]
469
469
470 $ try 'foo=bar|baz'
470 $ try 'foo=bar|baz'
471 (keyvalue
471 (keyvalue
472 ('symbol', 'foo')
472 ('symbol', 'foo')
473 (or
473 (or
474 (list
474 (list
475 ('symbol', 'bar')
475 ('symbol', 'bar')
476 ('symbol', 'baz'))))
476 ('symbol', 'baz'))))
477 hg: parse error: can't use a key-value pair in this context
477 hg: parse error: can't use a key-value pair in this context
478 [255]
478 [255]
479
479
480 right-hand side should be optimized recursively
480 right-hand side should be optimized recursively
481
481
482 $ try --optimize 'foo=(not public())'
482 $ try --optimize 'foo=(not public())'
483 (keyvalue
483 (keyvalue
484 ('symbol', 'foo')
484 ('symbol', 'foo')
485 (group
485 (group
486 (not
486 (not
487 (func
487 (func
488 ('symbol', 'public')
488 ('symbol', 'public')
489 None))))
489 None))))
490 * optimized:
490 * optimized:
491 (keyvalue
491 (keyvalue
492 ('symbol', 'foo')
492 ('symbol', 'foo')
493 (func
493 (func
494 ('symbol', '_notpublic')
494 ('symbol', '_notpublic')
495 None
495 None
496 any))
496 any))
497 hg: parse error: can't use a key-value pair in this context
497 hg: parse error: can't use a key-value pair in this context
498 [255]
498 [255]
499
499
500 parsed tree at stages:
500 parsed tree at stages:
501
501
502 $ hg debugrevspec -p all '()'
502 $ hg debugrevspec -p all '()'
503 * parsed:
503 * parsed:
504 (group
504 (group
505 None)
505 None)
506 * expanded:
506 * expanded:
507 (group
507 (group
508 None)
508 None)
509 * concatenated:
509 * concatenated:
510 (group
510 (group
511 None)
511 None)
512 * analyzed:
512 * analyzed:
513 None
513 None
514 * optimized:
514 * optimized:
515 None
515 None
516 hg: parse error: missing argument
516 hg: parse error: missing argument
517 [255]
517 [255]
518
518
519 $ hg debugrevspec --no-optimized -p all '()'
519 $ hg debugrevspec --no-optimized -p all '()'
520 * parsed:
520 * parsed:
521 (group
521 (group
522 None)
522 None)
523 * expanded:
523 * expanded:
524 (group
524 (group
525 None)
525 None)
526 * concatenated:
526 * concatenated:
527 (group
527 (group
528 None)
528 None)
529 * analyzed:
529 * analyzed:
530 None
530 None
531 hg: parse error: missing argument
531 hg: parse error: missing argument
532 [255]
532 [255]
533
533
534 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
534 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
535 * parsed:
535 * parsed:
536 (minus
536 (minus
537 (group
537 (group
538 (or
538 (or
539 (list
539 (list
540 ('symbol', '0')
540 ('symbol', '0')
541 ('symbol', '1'))))
541 ('symbol', '1'))))
542 ('symbol', '1'))
542 ('symbol', '1'))
543 * analyzed:
543 * analyzed:
544 (and
544 (and
545 (or
545 (or
546 (list
546 (list
547 ('symbol', '0')
547 ('symbol', '0')
548 ('symbol', '1'))
548 ('symbol', '1'))
549 define)
549 define)
550 (not
550 (not
551 ('symbol', '1')
551 ('symbol', '1')
552 follow)
552 follow)
553 define)
553 define)
554 * optimized:
554 * optimized:
555 (difference
555 (difference
556 (func
556 (func
557 ('symbol', '_list')
557 ('symbol', '_list')
558 ('string', '0\x001')
558 ('string', '0\x001')
559 define)
559 define)
560 ('symbol', '1')
560 ('symbol', '1')
561 define)
561 define)
562 0
562 0
563
563
564 $ hg debugrevspec -p unknown '0'
564 $ hg debugrevspec -p unknown '0'
565 abort: invalid stage name: unknown
565 abort: invalid stage name: unknown
566 [255]
566 [255]
567
567
568 $ hg debugrevspec -p all --optimize '0'
568 $ hg debugrevspec -p all --optimize '0'
569 abort: cannot use --optimize with --show-stage
569 abort: cannot use --optimize with --show-stage
570 [255]
570 [255]
571
571
572 verify optimized tree:
572 verify optimized tree:
573
573
574 $ hg debugrevspec --verify '0|1'
574 $ hg debugrevspec --verify '0|1'
575
575
576 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
576 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
577 * analyzed:
577 * analyzed:
578 (and
578 (and
579 (func
579 (func
580 ('symbol', 'r3232')
580 ('symbol', 'r3232')
581 None
581 None
582 define)
582 define)
583 ('symbol', '2')
583 ('symbol', '2')
584 define)
584 define)
585 * optimized:
585 * optimized:
586 (and
586 (and
587 ('symbol', '2')
587 ('symbol', '2')
588 (func
588 (func
589 ('symbol', 'r3232')
589 ('symbol', 'r3232')
590 None
590 None
591 define)
591 define)
592 define)
592 define)
593 * analyzed set:
593 * analyzed set:
594 <baseset [2]>
594 <baseset [2]>
595 * optimized set:
595 * optimized set:
596 <baseset [2, 2]>
596 <baseset [2, 2]>
597 --- analyzed
597 --- analyzed
598 +++ optimized
598 +++ optimized
599 2
599 2
600 +2
600 +2
601 [1]
601 [1]
602
602
603 $ hg debugrevspec --no-optimized --verify-optimized '0'
603 $ hg debugrevspec --no-optimized --verify-optimized '0'
604 abort: cannot use --verify-optimized with --no-optimized
604 abort: cannot use --verify-optimized with --no-optimized
605 [255]
605 [255]
606
606
607 Test that symbols only get parsed as functions if there's an opening
607 Test that symbols only get parsed as functions if there's an opening
608 parenthesis.
608 parenthesis.
609
609
610 $ hg book only -r 9
610 $ hg book only -r 9
611 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
611 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
612 8
612 8
613 9
613 9
614
614
615 infix/suffix resolution of ^ operator (issue2884):
615 infix/suffix resolution of ^ operator (issue2884):
616
616
617 x^:y means (x^):y
617 x^:y means (x^):y
618
618
619 $ try '1^:2'
619 $ try '1^:2'
620 (range
620 (range
621 (parentpost
621 (parentpost
622 ('symbol', '1'))
622 ('symbol', '1'))
623 ('symbol', '2'))
623 ('symbol', '2'))
624 * set:
624 * set:
625 <spanset+ 0:2>
625 <spanset+ 0:2>
626 0
626 0
627 1
627 1
628 2
628 2
629
629
630 $ try '1^::2'
630 $ try '1^::2'
631 (dagrange
631 (dagrange
632 (parentpost
632 (parentpost
633 ('symbol', '1'))
633 ('symbol', '1'))
634 ('symbol', '2'))
634 ('symbol', '2'))
635 * set:
635 * set:
636 <baseset+ [0, 1, 2]>
636 <baseset+ [0, 1, 2]>
637 0
637 0
638 1
638 1
639 2
639 2
640
640
641 $ try '9^:'
641 $ try '9^:'
642 (rangepost
642 (rangepost
643 (parentpost
643 (parentpost
644 ('symbol', '9')))
644 ('symbol', '9')))
645 * set:
645 * set:
646 <spanset+ 8:9>
646 <spanset+ 8:9>
647 8
647 8
648 9
648 9
649
649
650 x^:y should be resolved before omitting group operators
650 x^:y should be resolved before omitting group operators
651
651
652 $ try '1^(:2)'
652 $ try '1^(:2)'
653 (parent
653 (parent
654 ('symbol', '1')
654 ('symbol', '1')
655 (group
655 (group
656 (rangepre
656 (rangepre
657 ('symbol', '2'))))
657 ('symbol', '2'))))
658 hg: parse error: ^ expects a number 0, 1, or 2
658 hg: parse error: ^ expects a number 0, 1, or 2
659 [255]
659 [255]
660
660
661 x^:y should be resolved recursively
661 x^:y should be resolved recursively
662
662
663 $ try 'sort(1^:2)'
663 $ try 'sort(1^:2)'
664 (func
664 (func
665 ('symbol', 'sort')
665 ('symbol', 'sort')
666 (range
666 (range
667 (parentpost
667 (parentpost
668 ('symbol', '1'))
668 ('symbol', '1'))
669 ('symbol', '2')))
669 ('symbol', '2')))
670 * set:
670 * set:
671 <spanset+ 0:2>
671 <spanset+ 0:2>
672 0
672 0
673 1
673 1
674 2
674 2
675
675
676 $ try '(3^:4)^:2'
676 $ try '(3^:4)^:2'
677 (range
677 (range
678 (parentpost
678 (parentpost
679 (group
679 (group
680 (range
680 (range
681 (parentpost
681 (parentpost
682 ('symbol', '3'))
682 ('symbol', '3'))
683 ('symbol', '4'))))
683 ('symbol', '4'))))
684 ('symbol', '2'))
684 ('symbol', '2'))
685 * set:
685 * set:
686 <spanset+ 0:2>
686 <spanset+ 0:2>
687 0
687 0
688 1
688 1
689 2
689 2
690
690
691 $ try '(3^::4)^::2'
691 $ try '(3^::4)^::2'
692 (dagrange
692 (dagrange
693 (parentpost
693 (parentpost
694 (group
694 (group
695 (dagrange
695 (dagrange
696 (parentpost
696 (parentpost
697 ('symbol', '3'))
697 ('symbol', '3'))
698 ('symbol', '4'))))
698 ('symbol', '4'))))
699 ('symbol', '2'))
699 ('symbol', '2'))
700 * set:
700 * set:
701 <baseset+ [0, 1, 2]>
701 <baseset+ [0, 1, 2]>
702 0
702 0
703 1
703 1
704 2
704 2
705
705
706 $ try '(9^:)^:'
706 $ try '(9^:)^:'
707 (rangepost
707 (rangepost
708 (parentpost
708 (parentpost
709 (group
709 (group
710 (rangepost
710 (rangepost
711 (parentpost
711 (parentpost
712 ('symbol', '9'))))))
712 ('symbol', '9'))))))
713 * set:
713 * set:
714 <spanset+ 4:9>
714 <spanset+ 4:9>
715 4
715 4
716 5
716 5
717 6
717 6
718 7
718 7
719 8
719 8
720 9
720 9
721
721
722 x^ in alias should also be resolved
722 x^ in alias should also be resolved
723
723
724 $ try 'A' --config 'revsetalias.A=1^:2'
724 $ try 'A' --config 'revsetalias.A=1^:2'
725 ('symbol', 'A')
725 ('symbol', 'A')
726 * expanded:
726 * expanded:
727 (range
727 (range
728 (parentpost
728 (parentpost
729 ('symbol', '1'))
729 ('symbol', '1'))
730 ('symbol', '2'))
730 ('symbol', '2'))
731 * set:
731 * set:
732 <spanset+ 0:2>
732 <spanset+ 0:2>
733 0
733 0
734 1
734 1
735 2
735 2
736
736
737 $ try 'A:2' --config 'revsetalias.A=1^'
737 $ try 'A:2' --config 'revsetalias.A=1^'
738 (range
738 (range
739 ('symbol', 'A')
739 ('symbol', 'A')
740 ('symbol', '2'))
740 ('symbol', '2'))
741 * expanded:
741 * expanded:
742 (range
742 (range
743 (parentpost
743 (parentpost
744 ('symbol', '1'))
744 ('symbol', '1'))
745 ('symbol', '2'))
745 ('symbol', '2'))
746 * set:
746 * set:
747 <spanset+ 0:2>
747 <spanset+ 0:2>
748 0
748 0
749 1
749 1
750 2
750 2
751
751
752 but not beyond the boundary of alias expansion, because the resolution should
752 but not beyond the boundary of alias expansion, because the resolution should
753 be made at the parsing stage
753 be made at the parsing stage
754
754
755 $ try '1^A' --config 'revsetalias.A=:2'
755 $ try '1^A' --config 'revsetalias.A=:2'
756 (parent
756 (parent
757 ('symbol', '1')
757 ('symbol', '1')
758 ('symbol', 'A'))
758 ('symbol', 'A'))
759 * expanded:
759 * expanded:
760 (parent
760 (parent
761 ('symbol', '1')
761 ('symbol', '1')
762 (rangepre
762 (rangepre
763 ('symbol', '2')))
763 ('symbol', '2')))
764 hg: parse error: ^ expects a number 0, 1, or 2
764 hg: parse error: ^ expects a number 0, 1, or 2
765 [255]
765 [255]
766
766
767 ancestor can accept 0 or more arguments
767 ancestor can accept 0 or more arguments
768
768
769 $ log 'ancestor()'
769 $ log 'ancestor()'
770 $ log 'ancestor(1)'
770 $ log 'ancestor(1)'
771 1
771 1
772 $ log 'ancestor(4,5)'
772 $ log 'ancestor(4,5)'
773 1
773 1
774 $ log 'ancestor(4,5) and 4'
774 $ log 'ancestor(4,5) and 4'
775 $ log 'ancestor(0,0,1,3)'
775 $ log 'ancestor(0,0,1,3)'
776 0
776 0
777 $ log 'ancestor(3,1,5,3,5,1)'
777 $ log 'ancestor(3,1,5,3,5,1)'
778 1
778 1
779 $ log 'ancestor(0,1,3,5)'
779 $ log 'ancestor(0,1,3,5)'
780 0
780 0
781 $ log 'ancestor(1,2,3,4,5)'
781 $ log 'ancestor(1,2,3,4,5)'
782 1
782 1
783
783
784 test ancestors
784 test ancestors
785
785
786 $ log 'ancestors(5)'
786 $ log 'ancestors(5)'
787 0
787 0
788 1
788 1
789 3
789 3
790 5
790 5
791 $ log 'ancestor(ancestors(5))'
791 $ log 'ancestor(ancestors(5))'
792 0
792 0
793 $ log '::r3232()'
793 $ log '::r3232()'
794 0
794 0
795 1
795 1
796 2
796 2
797 3
797 3
798
798
799 $ log 'author(bob)'
799 $ log 'author(bob)'
800 2
800 2
801 $ log 'author("re:bob|test")'
801 $ log 'author("re:bob|test")'
802 0
802 0
803 1
803 1
804 2
804 2
805 3
805 3
806 4
806 4
807 5
807 5
808 6
808 6
809 7
809 7
810 8
810 8
811 9
811 9
812 $ log 'branch(Γ©)'
812 $ log 'branch(Γ©)'
813 8
813 8
814 9
814 9
815 $ log 'branch(a)'
815 $ log 'branch(a)'
816 0
816 0
817 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
817 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
818 0 a
818 0 a
819 2 a-b-c-
819 2 a-b-c-
820 3 +a+b+c+
820 3 +a+b+c+
821 4 -a-b-c-
821 4 -a-b-c-
822 5 !a/b/c/
822 5 !a/b/c/
823 6 _a_b_c_
823 6 _a_b_c_
824 7 .a.b.c.
824 7 .a.b.c.
825 $ log 'children(ancestor(4,5))'
825 $ log 'children(ancestor(4,5))'
826 2
826 2
827 3
827 3
828 $ log 'closed()'
828 $ log 'closed()'
829 $ log 'contains(a)'
829 $ log 'contains(a)'
830 0
830 0
831 1
831 1
832 3
832 3
833 5
833 5
834 $ log 'contains("../repo/a")'
834 $ log 'contains("../repo/a")'
835 0
835 0
836 1
836 1
837 3
837 3
838 5
838 5
839 $ log 'desc(B)'
839 $ log 'desc(B)'
840 5
840 5
841 $ log 'descendants(2 or 3)'
841 $ log 'descendants(2 or 3)'
842 2
842 2
843 3
843 3
844 4
844 4
845 5
845 5
846 6
846 6
847 7
847 7
848 8
848 8
849 9
849 9
850 $ log 'file("b*")'
850 $ log 'file("b*")'
851 1
851 1
852 4
852 4
853 $ log 'filelog("b")'
853 $ log 'filelog("b")'
854 1
854 1
855 4
855 4
856 $ log 'filelog("../repo/b")'
856 $ log 'filelog("../repo/b")'
857 1
857 1
858 4
858 4
859 $ log 'follow()'
859 $ log 'follow()'
860 0
860 0
861 1
861 1
862 2
862 2
863 4
863 4
864 8
864 8
865 9
865 9
866 $ log 'grep("issue\d+")'
866 $ log 'grep("issue\d+")'
867 6
867 6
868 $ try 'grep("(")' # invalid regular expression
868 $ try 'grep("(")' # invalid regular expression
869 (func
869 (func
870 ('symbol', 'grep')
870 ('symbol', 'grep')
871 ('string', '('))
871 ('string', '('))
872 hg: parse error: invalid match pattern: unbalanced parenthesis
872 hg: parse error: invalid match pattern: unbalanced parenthesis
873 [255]
873 [255]
874 $ try 'grep("\bissue\d+")'
874 $ try 'grep("\bissue\d+")'
875 (func
875 (func
876 ('symbol', 'grep')
876 ('symbol', 'grep')
877 ('string', '\x08issue\\d+'))
877 ('string', '\x08issue\\d+'))
878 * set:
878 * set:
879 <filteredset
879 <filteredset
880 <fullreposet+ 0:9>,
880 <fullreposet+ 0:9>,
881 <grep '\x08issue\\d+'>>
881 <grep '\x08issue\\d+'>>
882 $ try 'grep(r"\bissue\d+")'
882 $ try 'grep(r"\bissue\d+")'
883 (func
883 (func
884 ('symbol', 'grep')
884 ('symbol', 'grep')
885 ('string', '\\bissue\\d+'))
885 ('string', '\\bissue\\d+'))
886 * set:
886 * set:
887 <filteredset
887 <filteredset
888 <fullreposet+ 0:9>,
888 <fullreposet+ 0:9>,
889 <grep '\\bissue\\d+'>>
889 <grep '\\bissue\\d+'>>
890 6
890 6
891 $ try 'grep(r"\")'
891 $ try 'grep(r"\")'
892 hg: parse error at 7: unterminated string
892 hg: parse error at 7: unterminated string
893 [255]
893 [255]
894 $ log 'head()'
894 $ log 'head()'
895 0
895 0
896 1
896 1
897 2
897 2
898 3
898 3
899 4
899 4
900 5
900 5
901 6
901 6
902 7
902 7
903 9
903 9
904 $ log 'heads(6::)'
904 $ log 'heads(6::)'
905 7
905 7
906 $ log 'keyword(issue)'
906 $ log 'keyword(issue)'
907 6
907 6
908 $ log 'keyword("test a")'
908 $ log 'keyword("test a")'
909 $ log 'limit(head(), 1)'
909 $ log 'limit(head(), 1)'
910 0
910 0
911 $ log 'limit(author("re:bob|test"), 3, 5)'
911 $ log 'limit(author("re:bob|test"), 3, 5)'
912 5
912 5
913 6
913 6
914 7
914 7
915 $ log 'limit(author("re:bob|test"), offset=6)'
915 $ log 'limit(author("re:bob|test"), offset=6)'
916 6
916 6
917 $ log 'limit(author("re:bob|test"), offset=10)'
917 $ log 'limit(author("re:bob|test"), offset=10)'
918 $ log 'limit(all(), 1, -1)'
918 $ log 'limit(all(), 1, -1)'
919 hg: parse error: negative offset
919 hg: parse error: negative offset
920 [255]
920 [255]
921 $ log 'matching(6)'
921 $ log 'matching(6)'
922 6
922 6
923 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
923 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
924 6
924 6
925 7
925 7
926
926
927 Testing min and max
927 Testing min and max
928
928
929 max: simple
929 max: simple
930
930
931 $ log 'max(contains(a))'
931 $ log 'max(contains(a))'
932 5
932 5
933
933
934 max: simple on unordered set)
934 max: simple on unordered set)
935
935
936 $ log 'max((4+0+2+5+7) and contains(a))'
936 $ log 'max((4+0+2+5+7) and contains(a))'
937 5
937 5
938
938
939 max: no result
939 max: no result
940
940
941 $ log 'max(contains(stringthatdoesnotappearanywhere))'
941 $ log 'max(contains(stringthatdoesnotappearanywhere))'
942
942
943 max: no result on unordered set
943 max: no result on unordered set
944
944
945 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
945 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
946
946
947 min: simple
947 min: simple
948
948
949 $ log 'min(contains(a))'
949 $ log 'min(contains(a))'
950 0
950 0
951
951
952 min: simple on unordered set
952 min: simple on unordered set
953
953
954 $ log 'min((4+0+2+5+7) and contains(a))'
954 $ log 'min((4+0+2+5+7) and contains(a))'
955 0
955 0
956
956
957 min: empty
957 min: empty
958
958
959 $ log 'min(contains(stringthatdoesnotappearanywhere))'
959 $ log 'min(contains(stringthatdoesnotappearanywhere))'
960
960
961 min: empty on unordered set
961 min: empty on unordered set
962
962
963 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
963 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
964
964
965
965
966 $ log 'merge()'
966 $ log 'merge()'
967 6
967 6
968 $ log 'branchpoint()'
968 $ log 'branchpoint()'
969 1
969 1
970 4
970 4
971 $ log 'modifies(b)'
971 $ log 'modifies(b)'
972 4
972 4
973 $ log 'modifies("path:b")'
973 $ log 'modifies("path:b")'
974 4
974 4
975 $ log 'modifies("*")'
975 $ log 'modifies("*")'
976 4
976 4
977 6
977 6
978 $ log 'modifies("set:modified()")'
978 $ log 'modifies("set:modified()")'
979 4
979 4
980 $ log 'id(5)'
980 $ log 'id(5)'
981 2
981 2
982 $ log 'only(9)'
982 $ log 'only(9)'
983 8
983 8
984 9
984 9
985 $ log 'only(8)'
985 $ log 'only(8)'
986 8
986 8
987 $ log 'only(9, 5)'
987 $ log 'only(9, 5)'
988 2
988 2
989 4
989 4
990 8
990 8
991 9
991 9
992 $ log 'only(7 + 9, 5 + 2)'
992 $ log 'only(7 + 9, 5 + 2)'
993 4
993 4
994 6
994 6
995 7
995 7
996 8
996 8
997 9
997 9
998
998
999 Test empty set input
999 Test empty set input
1000 $ log 'only(p2())'
1000 $ log 'only(p2())'
1001 $ log 'only(p1(), p2())'
1001 $ log 'only(p1(), p2())'
1002 0
1002 0
1003 1
1003 1
1004 2
1004 2
1005 4
1005 4
1006 8
1006 8
1007 9
1007 9
1008
1008
1009 Test '%' operator
1009 Test '%' operator
1010
1010
1011 $ log '9%'
1011 $ log '9%'
1012 8
1012 8
1013 9
1013 9
1014 $ log '9%5'
1014 $ log '9%5'
1015 2
1015 2
1016 4
1016 4
1017 8
1017 8
1018 9
1018 9
1019 $ log '(7 + 9)%(5 + 2)'
1019 $ log '(7 + 9)%(5 + 2)'
1020 4
1020 4
1021 6
1021 6
1022 7
1022 7
1023 8
1023 8
1024 9
1024 9
1025
1025
1026 Test opreand of '%' is optimized recursively (issue4670)
1026 Test opreand of '%' is optimized recursively (issue4670)
1027
1027
1028 $ try --optimize '8:9-8%'
1028 $ try --optimize '8:9-8%'
1029 (onlypost
1029 (onlypost
1030 (minus
1030 (minus
1031 (range
1031 (range
1032 ('symbol', '8')
1032 ('symbol', '8')
1033 ('symbol', '9'))
1033 ('symbol', '9'))
1034 ('symbol', '8')))
1034 ('symbol', '8')))
1035 * optimized:
1035 * optimized:
1036 (func
1036 (func
1037 ('symbol', 'only')
1037 ('symbol', 'only')
1038 (difference
1038 (difference
1039 (range
1039 (range
1040 ('symbol', '8')
1040 ('symbol', '8')
1041 ('symbol', '9')
1041 ('symbol', '9')
1042 define)
1042 define)
1043 ('symbol', '8')
1043 ('symbol', '8')
1044 define)
1044 define)
1045 define)
1045 define)
1046 * set:
1046 * set:
1047 <baseset+ [8, 9]>
1047 <baseset+ [8, 9]>
1048 8
1048 8
1049 9
1049 9
1050 $ try --optimize '(9)%(5)'
1050 $ try --optimize '(9)%(5)'
1051 (only
1051 (only
1052 (group
1052 (group
1053 ('symbol', '9'))
1053 ('symbol', '9'))
1054 (group
1054 (group
1055 ('symbol', '5')))
1055 ('symbol', '5')))
1056 * optimized:
1056 * optimized:
1057 (func
1057 (func
1058 ('symbol', 'only')
1058 ('symbol', 'only')
1059 (list
1059 (list
1060 ('symbol', '9')
1060 ('symbol', '9')
1061 ('symbol', '5'))
1061 ('symbol', '5'))
1062 define)
1062 define)
1063 * set:
1063 * set:
1064 <baseset+ [2, 4, 8, 9]>
1064 <baseset+ [2, 4, 8, 9]>
1065 2
1065 2
1066 4
1066 4
1067 8
1067 8
1068 9
1068 9
1069
1069
1070 Test the order of operations
1070 Test the order of operations
1071
1071
1072 $ log '7 + 9%5 + 2'
1072 $ log '7 + 9%5 + 2'
1073 7
1073 7
1074 2
1074 2
1075 4
1075 4
1076 8
1076 8
1077 9
1077 9
1078
1078
1079 Test explicit numeric revision
1079 Test explicit numeric revision
1080 $ log 'rev(-2)'
1080 $ log 'rev(-2)'
1081 $ log 'rev(-1)'
1081 $ log 'rev(-1)'
1082 -1
1082 -1
1083 $ log 'rev(0)'
1083 $ log 'rev(0)'
1084 0
1084 0
1085 $ log 'rev(9)'
1085 $ log 'rev(9)'
1086 9
1086 9
1087 $ log 'rev(10)'
1087 $ log 'rev(10)'
1088 $ log 'rev(tip)'
1088 $ log 'rev(tip)'
1089 hg: parse error: rev expects a number
1089 hg: parse error: rev expects a number
1090 [255]
1090 [255]
1091
1091
1092 Test hexadecimal revision
1092 Test hexadecimal revision
1093 $ log 'id(2)'
1093 $ log 'id(2)'
1094 abort: 00changelog.i@2: ambiguous identifier!
1094 abort: 00changelog.i@2: ambiguous identifier!
1095 [255]
1095 [255]
1096 $ log 'id(23268)'
1096 $ log 'id(23268)'
1097 4
1097 4
1098 $ log 'id(2785f51eece)'
1098 $ log 'id(2785f51eece)'
1099 0
1099 0
1100 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1100 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1101 8
1101 8
1102 $ log 'id(d5d0dcbdc4a)'
1102 $ log 'id(d5d0dcbdc4a)'
1103 $ log 'id(d5d0dcbdc4w)'
1103 $ log 'id(d5d0dcbdc4w)'
1104 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1104 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1105 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1105 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1106 $ log 'id(1.0)'
1106 $ log 'id(1.0)'
1107 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1107 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1108
1108
1109 Test null revision
1109 Test null revision
1110 $ log '(null)'
1110 $ log '(null)'
1111 -1
1111 -1
1112 $ log '(null:0)'
1112 $ log '(null:0)'
1113 -1
1113 -1
1114 0
1114 0
1115 $ log '(0:null)'
1115 $ log '(0:null)'
1116 0
1116 0
1117 -1
1117 -1
1118 $ log 'null::0'
1118 $ log 'null::0'
1119 -1
1119 -1
1120 0
1120 0
1121 $ log 'null:tip - 0:'
1121 $ log 'null:tip - 0:'
1122 -1
1122 -1
1123 $ log 'null: and null::' | head -1
1123 $ log 'null: and null::' | head -1
1124 -1
1124 -1
1125 $ log 'null: or 0:' | head -2
1125 $ log 'null: or 0:' | head -2
1126 -1
1126 -1
1127 0
1127 0
1128 $ log 'ancestors(null)'
1128 $ log 'ancestors(null)'
1129 -1
1129 -1
1130 $ log 'reverse(null:)' | tail -2
1130 $ log 'reverse(null:)' | tail -2
1131 0
1131 0
1132 -1
1132 -1
1133 BROKEN: should be '-1'
1133 BROKEN: should be '-1'
1134 $ log 'first(null:)'
1134 $ log 'first(null:)'
1135 BROKEN: should be '-1'
1135 BROKEN: should be '-1'
1136 $ log 'min(null:)'
1136 $ log 'min(null:)'
1137 $ log 'tip:null and all()' | tail -2
1137 $ log 'tip:null and all()' | tail -2
1138 1
1138 1
1139 0
1139 0
1140
1140
1141 Test working-directory revision
1141 Test working-directory revision
1142 $ hg debugrevspec 'wdir()'
1142 $ hg debugrevspec 'wdir()'
1143 2147483647
1143 2147483647
1144 $ hg debugrevspec 'tip or wdir()'
1144 $ hg debugrevspec 'tip or wdir()'
1145 9
1145 9
1146 2147483647
1146 2147483647
1147 $ hg debugrevspec '0:tip and wdir()'
1147 $ hg debugrevspec '0:tip and wdir()'
1148 $ log '0:wdir()' | tail -3
1148 $ log '0:wdir()' | tail -3
1149 8
1149 8
1150 9
1150 9
1151 2147483647
1151 2147483647
1152 $ log 'wdir():0' | head -3
1152 $ log 'wdir():0' | head -3
1153 2147483647
1153 2147483647
1154 9
1154 9
1155 8
1155 8
1156 $ log 'wdir():wdir()'
1156 $ log 'wdir():wdir()'
1157 2147483647
1157 2147483647
1158 $ log '(all() + wdir()) & min(. + wdir())'
1158 $ log '(all() + wdir()) & min(. + wdir())'
1159 9
1159 9
1160 $ log '(all() + wdir()) & max(. + wdir())'
1160 $ log '(all() + wdir()) & max(. + wdir())'
1161 2147483647
1161 2147483647
1162 $ log '(all() + wdir()) & first(wdir() + .)'
1162 $ log '(all() + wdir()) & first(wdir() + .)'
1163 2147483647
1163 2147483647
1164 $ log '(all() + wdir()) & last(. + wdir())'
1164 $ log '(all() + wdir()) & last(. + wdir())'
1165 2147483647
1165 2147483647
1166
1166
1167 $ log 'outgoing()'
1167 $ log 'outgoing()'
1168 8
1168 8
1169 9
1169 9
1170 $ log 'outgoing("../remote1")'
1170 $ log 'outgoing("../remote1")'
1171 8
1171 8
1172 9
1172 9
1173 $ log 'outgoing("../remote2")'
1173 $ log 'outgoing("../remote2")'
1174 3
1174 3
1175 5
1175 5
1176 6
1176 6
1177 7
1177 7
1178 9
1178 9
1179 $ log 'p1(merge())'
1179 $ log 'p1(merge())'
1180 5
1180 5
1181 $ log 'p2(merge())'
1181 $ log 'p2(merge())'
1182 4
1182 4
1183 $ log 'parents(merge())'
1183 $ log 'parents(merge())'
1184 4
1184 4
1185 5
1185 5
1186 $ log 'p1(branchpoint())'
1186 $ log 'p1(branchpoint())'
1187 0
1187 0
1188 2
1188 2
1189 $ log 'p2(branchpoint())'
1189 $ log 'p2(branchpoint())'
1190 $ log 'parents(branchpoint())'
1190 $ log 'parents(branchpoint())'
1191 0
1191 0
1192 2
1192 2
1193 $ log 'removes(a)'
1193 $ log 'removes(a)'
1194 2
1194 2
1195 6
1195 6
1196 $ log 'roots(all())'
1196 $ log 'roots(all())'
1197 0
1197 0
1198 $ log 'reverse(2 or 3 or 4 or 5)'
1198 $ log 'reverse(2 or 3 or 4 or 5)'
1199 5
1199 5
1200 4
1200 4
1201 3
1201 3
1202 2
1202 2
1203 $ log 'reverse(all())'
1203 $ log 'reverse(all())'
1204 9
1204 9
1205 8
1205 8
1206 7
1206 7
1207 6
1207 6
1208 5
1208 5
1209 4
1209 4
1210 3
1210 3
1211 2
1211 2
1212 1
1212 1
1213 0
1213 0
1214 $ log 'reverse(all()) & filelog(b)'
1214 $ log 'reverse(all()) & filelog(b)'
1215 4
1215 4
1216 1
1216 1
1217 $ log 'rev(5)'
1217 $ log 'rev(5)'
1218 5
1218 5
1219 $ log 'sort(limit(reverse(all()), 3))'
1219 $ log 'sort(limit(reverse(all()), 3))'
1220 7
1220 7
1221 8
1221 8
1222 9
1222 9
1223 $ log 'sort(2 or 3 or 4 or 5, date)'
1223 $ log 'sort(2 or 3 or 4 or 5, date)'
1224 2
1224 2
1225 3
1225 3
1226 5
1226 5
1227 4
1227 4
1228 $ log 'tagged()'
1228 $ log 'tagged()'
1229 6
1229 6
1230 $ log 'tag()'
1230 $ log 'tag()'
1231 6
1231 6
1232 $ log 'tag(1.0)'
1232 $ log 'tag(1.0)'
1233 6
1233 6
1234 $ log 'tag(tip)'
1234 $ log 'tag(tip)'
1235 9
1235 9
1236
1236
1237 Test order of revisions in compound expression
1237 Test order of revisions in compound expression
1238 ----------------------------------------------
1238 ----------------------------------------------
1239
1239
1240 The general rule is that only the outermost (= leftmost) predicate can
1240 The general rule is that only the outermost (= leftmost) predicate can
1241 enforce its ordering requirement. The other predicates should take the
1241 enforce its ordering requirement. The other predicates should take the
1242 ordering defined by it.
1242 ordering defined by it.
1243
1243
1244 'A & B' should follow the order of 'A':
1244 'A & B' should follow the order of 'A':
1245
1245
1246 $ log '2:0 & 0::2'
1246 $ log '2:0 & 0::2'
1247 2
1247 2
1248 1
1248 1
1249 0
1249 0
1250
1250
1251 'head()' combines sets in right order:
1251 'head()' combines sets in right order:
1252
1252
1253 $ log '2:0 & head()'
1253 $ log '2:0 & head()'
1254 2
1254 2
1255 1
1255 1
1256 0
1256 0
1257
1257
1258 'x:y' takes ordering parameter into account:
1259
1260 $ try -p optimized '3:0 & 0:3 & not 2:1'
1261 * optimized:
1262 (difference
1263 (and
1264 (range
1265 ('symbol', '3')
1266 ('symbol', '0')
1267 define)
1268 (range
1269 ('symbol', '0')
1270 ('symbol', '3')
1271 follow)
1272 define)
1273 (range
1274 ('symbol', '2')
1275 ('symbol', '1')
1276 any)
1277 define)
1278 * set:
1279 <filteredset
1280 <filteredset
1281 <spanset- 0:3>,
1282 <spanset+ 0:3>>,
1283 <not
1284 <spanset+ 1:2>>>
1285 3
1286 0
1287
1258 'a + b', which is optimized to '_list(a b)', should take the ordering of
1288 'a + b', which is optimized to '_list(a b)', should take the ordering of
1259 the left expression:
1289 the left expression:
1260
1290
1261 $ try --optimize '2:0 & (0 + 1 + 2)'
1291 $ try --optimize '2:0 & (0 + 1 + 2)'
1262 (and
1292 (and
1263 (range
1293 (range
1264 ('symbol', '2')
1294 ('symbol', '2')
1265 ('symbol', '0'))
1295 ('symbol', '0'))
1266 (group
1296 (group
1267 (or
1297 (or
1268 (list
1298 (list
1269 ('symbol', '0')
1299 ('symbol', '0')
1270 ('symbol', '1')
1300 ('symbol', '1')
1271 ('symbol', '2')))))
1301 ('symbol', '2')))))
1272 * optimized:
1302 * optimized:
1273 (and
1303 (and
1274 (range
1304 (range
1275 ('symbol', '2')
1305 ('symbol', '2')
1276 ('symbol', '0')
1306 ('symbol', '0')
1277 define)
1307 define)
1278 (func
1308 (func
1279 ('symbol', '_list')
1309 ('symbol', '_list')
1280 ('string', '0\x001\x002')
1310 ('string', '0\x001\x002')
1281 follow)
1311 follow)
1282 define)
1312 define)
1283 * set:
1313 * set:
1284 <filteredset
1314 <filteredset
1285 <spanset- 0:2>,
1315 <spanset- 0:2>,
1286 <baseset [0, 1, 2]>>
1316 <baseset [0, 1, 2]>>
1287 2
1317 2
1288 1
1318 1
1289 0
1319 0
1290
1320
1291 'A + B' should take the ordering of the left expression:
1321 'A + B' should take the ordering of the left expression:
1292
1322
1293 $ try --optimize '2:0 & (0:1 + 2)'
1323 $ try --optimize '2:0 & (0:1 + 2)'
1294 (and
1324 (and
1295 (range
1325 (range
1296 ('symbol', '2')
1326 ('symbol', '2')
1297 ('symbol', '0'))
1327 ('symbol', '0'))
1298 (group
1328 (group
1299 (or
1329 (or
1300 (list
1330 (list
1301 (range
1331 (range
1302 ('symbol', '0')
1332 ('symbol', '0')
1303 ('symbol', '1'))
1333 ('symbol', '1'))
1304 ('symbol', '2')))))
1334 ('symbol', '2')))))
1305 * optimized:
1335 * optimized:
1306 (and
1336 (and
1307 (range
1337 (range
1308 ('symbol', '2')
1338 ('symbol', '2')
1309 ('symbol', '0')
1339 ('symbol', '0')
1310 define)
1340 define)
1311 (or
1341 (or
1312 (list
1342 (list
1313 (range
1343 (range
1314 ('symbol', '0')
1344 ('symbol', '0')
1315 ('symbol', '1')
1345 ('symbol', '1')
1316 follow)
1346 follow)
1317 ('symbol', '2'))
1347 ('symbol', '2'))
1318 follow)
1348 follow)
1319 define)
1349 define)
1320 * set:
1350 * set:
1321 <filteredset
1351 <filteredset
1322 <spanset- 0:2>,
1352 <spanset- 0:2>,
1323 <addset
1353 <addset
1324 <spanset+ 0:1>,
1354 <spanset+ 0:1>,
1325 <baseset [2]>>>
1355 <baseset [2]>>>
1326 2
1356 2
1327 1
1357 1
1328 0
1358 0
1329
1359
1330 '_intlist(a b)' should behave like 'a + b':
1360 '_intlist(a b)' should behave like 'a + b':
1331
1361
1332 $ trylist --optimize '2:0 & %ld' 0 1 2
1362 $ trylist --optimize '2:0 & %ld' 0 1 2
1333 (and
1363 (and
1334 (range
1364 (range
1335 ('symbol', '2')
1365 ('symbol', '2')
1336 ('symbol', '0'))
1366 ('symbol', '0'))
1337 (func
1367 (func
1338 ('symbol', '_intlist')
1368 ('symbol', '_intlist')
1339 ('string', '0\x001\x002')))
1369 ('string', '0\x001\x002')))
1340 * optimized:
1370 * optimized:
1341 (and
1371 (and
1342 (func
1372 (func
1343 ('symbol', '_intlist')
1373 ('symbol', '_intlist')
1344 ('string', '0\x001\x002')
1374 ('string', '0\x001\x002')
1345 follow)
1375 follow)
1346 (range
1376 (range
1347 ('symbol', '2')
1377 ('symbol', '2')
1348 ('symbol', '0')
1378 ('symbol', '0')
1349 define)
1379 define)
1350 define)
1380 define)
1351 * set:
1381 * set:
1352 <filteredset
1382 <filteredset
1353 <spanset- 0:2>,
1383 <spanset- 0:2>,
1354 <baseset+ [0, 1, 2]>>
1384 <baseset+ [0, 1, 2]>>
1355 2
1385 2
1356 1
1386 1
1357 0
1387 0
1358
1388
1359 $ trylist --optimize '%ld & 2:0' 0 2 1
1389 $ trylist --optimize '%ld & 2:0' 0 2 1
1360 (and
1390 (and
1361 (func
1391 (func
1362 ('symbol', '_intlist')
1392 ('symbol', '_intlist')
1363 ('string', '0\x002\x001'))
1393 ('string', '0\x002\x001'))
1364 (range
1394 (range
1365 ('symbol', '2')
1395 ('symbol', '2')
1366 ('symbol', '0')))
1396 ('symbol', '0')))
1367 * optimized:
1397 * optimized:
1368 (and
1398 (and
1369 (func
1399 (func
1370 ('symbol', '_intlist')
1400 ('symbol', '_intlist')
1371 ('string', '0\x002\x001')
1401 ('string', '0\x002\x001')
1372 define)
1402 define)
1373 (range
1403 (range
1374 ('symbol', '2')
1404 ('symbol', '2')
1375 ('symbol', '0')
1405 ('symbol', '0')
1376 follow)
1406 follow)
1377 define)
1407 define)
1378 * set:
1408 * set:
1379 <filteredset
1409 <filteredset
1380 <spanset- 0:2>,
1410 <baseset [0, 2, 1]>,
1381 <baseset [0, 2, 1]>>
1411 <spanset- 0:2>>
1412 0
1382 2
1413 2
1383 1
1414 1
1384 0
1385 BROKEN: should be '0 2 1'
1386
1415
1387 '_hexlist(a b)' should behave like 'a + b':
1416 '_hexlist(a b)' should behave like 'a + b':
1388
1417
1389 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1418 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1390 (and
1419 (and
1391 (range
1420 (range
1392 ('symbol', '2')
1421 ('symbol', '2')
1393 ('symbol', '0'))
1422 ('symbol', '0'))
1394 (func
1423 (func
1395 ('symbol', '_hexlist')
1424 ('symbol', '_hexlist')
1396 ('string', '*'))) (glob)
1425 ('string', '*'))) (glob)
1397 * optimized:
1426 * optimized:
1398 (and
1427 (and
1399 (range
1428 (range
1400 ('symbol', '2')
1429 ('symbol', '2')
1401 ('symbol', '0')
1430 ('symbol', '0')
1402 define)
1431 define)
1403 (func
1432 (func
1404 ('symbol', '_hexlist')
1433 ('symbol', '_hexlist')
1405 ('string', '*') (glob)
1434 ('string', '*') (glob)
1406 follow)
1435 follow)
1407 define)
1436 define)
1408 * set:
1437 * set:
1409 <filteredset
1438 <filteredset
1410 <spanset- 0:2>,
1439 <spanset- 0:2>,
1411 <baseset [0, 1, 2]>>
1440 <baseset [0, 1, 2]>>
1412 2
1441 2
1413 1
1442 1
1414 0
1443 0
1415
1444
1416 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1445 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1417 (and
1446 (and
1418 (func
1447 (func
1419 ('symbol', '_hexlist')
1448 ('symbol', '_hexlist')
1420 ('string', '*')) (glob)
1449 ('string', '*')) (glob)
1421 (range
1450 (range
1422 ('symbol', '2')
1451 ('symbol', '2')
1423 ('symbol', '0')))
1452 ('symbol', '0')))
1424 * optimized:
1453 * optimized:
1425 (and
1454 (and
1426 (range
1455 (range
1427 ('symbol', '2')
1456 ('symbol', '2')
1428 ('symbol', '0')
1457 ('symbol', '0')
1429 follow)
1458 follow)
1430 (func
1459 (func
1431 ('symbol', '_hexlist')
1460 ('symbol', '_hexlist')
1432 ('string', '*') (glob)
1461 ('string', '*') (glob)
1433 define)
1462 define)
1434 define)
1463 define)
1435 * set:
1464 * set:
1436 <baseset [0, 2, 1]>
1465 <baseset [0, 2, 1]>
1437 0
1466 0
1438 2
1467 2
1439 1
1468 1
1440
1469
1441 '_list' should not go through the slow follow-order path if order doesn't
1470 '_list' should not go through the slow follow-order path if order doesn't
1442 matter:
1471 matter:
1443
1472
1444 $ try -p optimized '2:0 & not (0 + 1)'
1473 $ try -p optimized '2:0 & not (0 + 1)'
1445 * optimized:
1474 * optimized:
1446 (difference
1475 (difference
1447 (range
1476 (range
1448 ('symbol', '2')
1477 ('symbol', '2')
1449 ('symbol', '0')
1478 ('symbol', '0')
1450 define)
1479 define)
1451 (func
1480 (func
1452 ('symbol', '_list')
1481 ('symbol', '_list')
1453 ('string', '0\x001')
1482 ('string', '0\x001')
1454 any)
1483 any)
1455 define)
1484 define)
1456 * set:
1485 * set:
1457 <filteredset
1486 <filteredset
1458 <spanset- 0:2>,
1487 <spanset- 0:2>,
1459 <not
1488 <not
1460 <baseset [0, 1]>>>
1489 <baseset [0, 1]>>>
1461 2
1490 2
1462
1491
1463 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1492 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1464 * optimized:
1493 * optimized:
1465 (difference
1494 (difference
1466 (range
1495 (range
1467 ('symbol', '2')
1496 ('symbol', '2')
1468 ('symbol', '0')
1497 ('symbol', '0')
1469 define)
1498 define)
1470 (and
1499 (and
1471 (range
1500 (range
1472 ('symbol', '0')
1501 ('symbol', '0')
1473 ('symbol', '2')
1502 ('symbol', '2')
1474 any)
1503 any)
1475 (func
1504 (func
1476 ('symbol', '_list')
1505 ('symbol', '_list')
1477 ('string', '0\x001')
1506 ('string', '0\x001')
1478 any)
1507 any)
1479 any)
1508 any)
1480 define)
1509 define)
1481 * set:
1510 * set:
1482 <filteredset
1511 <filteredset
1483 <spanset- 0:2>,
1512 <spanset- 0:2>,
1484 <not
1513 <not
1485 <baseset [0, 1]>>>
1514 <baseset [0, 1]>>>
1486 2
1515 2
1487
1516
1488 because 'present()' does nothing other than suppressing an error, the
1517 because 'present()' does nothing other than suppressing an error, the
1489 ordering requirement should be forwarded to the nested expression
1518 ordering requirement should be forwarded to the nested expression
1490
1519
1491 $ try -p optimized 'present(2 + 0 + 1)'
1520 $ try -p optimized 'present(2 + 0 + 1)'
1492 * optimized:
1521 * optimized:
1493 (func
1522 (func
1494 ('symbol', 'present')
1523 ('symbol', 'present')
1495 (func
1524 (func
1496 ('symbol', '_list')
1525 ('symbol', '_list')
1497 ('string', '2\x000\x001')
1526 ('string', '2\x000\x001')
1498 define)
1527 define)
1499 define)
1528 define)
1500 * set:
1529 * set:
1501 <baseset [2, 0, 1]>
1530 <baseset [2, 0, 1]>
1502 2
1531 2
1503 0
1532 0
1504 1
1533 1
1505
1534
1506 $ try --optimize '2:0 & present(0 + 1 + 2)'
1535 $ try --optimize '2:0 & present(0 + 1 + 2)'
1507 (and
1536 (and
1508 (range
1537 (range
1509 ('symbol', '2')
1538 ('symbol', '2')
1510 ('symbol', '0'))
1539 ('symbol', '0'))
1511 (func
1540 (func
1512 ('symbol', 'present')
1541 ('symbol', 'present')
1513 (or
1542 (or
1514 (list
1543 (list
1515 ('symbol', '0')
1544 ('symbol', '0')
1516 ('symbol', '1')
1545 ('symbol', '1')
1517 ('symbol', '2')))))
1546 ('symbol', '2')))))
1518 * optimized:
1547 * optimized:
1519 (and
1548 (and
1520 (range
1549 (range
1521 ('symbol', '2')
1550 ('symbol', '2')
1522 ('symbol', '0')
1551 ('symbol', '0')
1523 define)
1552 define)
1524 (func
1553 (func
1525 ('symbol', 'present')
1554 ('symbol', 'present')
1526 (func
1555 (func
1527 ('symbol', '_list')
1556 ('symbol', '_list')
1528 ('string', '0\x001\x002')
1557 ('string', '0\x001\x002')
1529 follow)
1558 follow)
1530 follow)
1559 follow)
1531 define)
1560 define)
1532 * set:
1561 * set:
1533 <filteredset
1562 <filteredset
1534 <spanset- 0:2>,
1563 <spanset- 0:2>,
1535 <baseset [0, 1, 2]>>
1564 <baseset [0, 1, 2]>>
1536 2
1565 2
1537 1
1566 1
1538 0
1567 0
1539
1568
1540 'reverse()' should take effect only if it is the outermost expression:
1569 'reverse()' should take effect only if it is the outermost expression:
1541
1570
1542 $ try --optimize '0:2 & reverse(all())'
1571 $ try --optimize '0:2 & reverse(all())'
1543 (and
1572 (and
1544 (range
1573 (range
1545 ('symbol', '0')
1574 ('symbol', '0')
1546 ('symbol', '2'))
1575 ('symbol', '2'))
1547 (func
1576 (func
1548 ('symbol', 'reverse')
1577 ('symbol', 'reverse')
1549 (func
1578 (func
1550 ('symbol', 'all')
1579 ('symbol', 'all')
1551 None)))
1580 None)))
1552 * optimized:
1581 * optimized:
1553 (and
1582 (and
1554 (range
1583 (range
1555 ('symbol', '0')
1584 ('symbol', '0')
1556 ('symbol', '2')
1585 ('symbol', '2')
1557 define)
1586 define)
1558 (func
1587 (func
1559 ('symbol', 'reverse')
1588 ('symbol', 'reverse')
1560 (func
1589 (func
1561 ('symbol', 'all')
1590 ('symbol', 'all')
1562 None
1591 None
1563 define)
1592 define)
1564 follow)
1593 follow)
1565 define)
1594 define)
1566 * set:
1595 * set:
1567 <filteredset
1596 <filteredset
1568 <spanset- 0:2>,
1597 <spanset- 0:2>,
1569 <spanset+ 0:9>>
1598 <spanset+ 0:9>>
1570 2
1599 2
1571 1
1600 1
1572 0
1601 0
1573 BROKEN: should be '0 1 2'
1602 BROKEN: should be '0 1 2'
1574
1603
1575 'sort()' should take effect only if it is the outermost expression:
1604 'sort()' should take effect only if it is the outermost expression:
1576
1605
1577 $ try --optimize '0:2 & sort(all(), -rev)'
1606 $ try --optimize '0:2 & sort(all(), -rev)'
1578 (and
1607 (and
1579 (range
1608 (range
1580 ('symbol', '0')
1609 ('symbol', '0')
1581 ('symbol', '2'))
1610 ('symbol', '2'))
1582 (func
1611 (func
1583 ('symbol', 'sort')
1612 ('symbol', 'sort')
1584 (list
1613 (list
1585 (func
1614 (func
1586 ('symbol', 'all')
1615 ('symbol', 'all')
1587 None)
1616 None)
1588 (negate
1617 (negate
1589 ('symbol', 'rev')))))
1618 ('symbol', 'rev')))))
1590 * optimized:
1619 * optimized:
1591 (and
1620 (and
1592 (range
1621 (range
1593 ('symbol', '0')
1622 ('symbol', '0')
1594 ('symbol', '2')
1623 ('symbol', '2')
1595 define)
1624 define)
1596 (func
1625 (func
1597 ('symbol', 'sort')
1626 ('symbol', 'sort')
1598 (list
1627 (list
1599 (func
1628 (func
1600 ('symbol', 'all')
1629 ('symbol', 'all')
1601 None
1630 None
1602 define)
1631 define)
1603 ('string', '-rev'))
1632 ('string', '-rev'))
1604 follow)
1633 follow)
1605 define)
1634 define)
1606 * set:
1635 * set:
1607 <filteredset
1636 <filteredset
1608 <spanset- 0:2>,
1637 <spanset- 0:2>,
1609 <spanset+ 0:9>>
1638 <spanset+ 0:9>>
1610 2
1639 2
1611 1
1640 1
1612 0
1641 0
1613 BROKEN: should be '0 1 2'
1642 BROKEN: should be '0 1 2'
1614
1643
1615 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1644 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1616
1645
1617 $ try --optimize '2:0 & first(1 + 0 + 2)'
1646 $ try --optimize '2:0 & first(1 + 0 + 2)'
1618 (and
1647 (and
1619 (range
1648 (range
1620 ('symbol', '2')
1649 ('symbol', '2')
1621 ('symbol', '0'))
1650 ('symbol', '0'))
1622 (func
1651 (func
1623 ('symbol', 'first')
1652 ('symbol', 'first')
1624 (or
1653 (or
1625 (list
1654 (list
1626 ('symbol', '1')
1655 ('symbol', '1')
1627 ('symbol', '0')
1656 ('symbol', '0')
1628 ('symbol', '2')))))
1657 ('symbol', '2')))))
1629 * optimized:
1658 * optimized:
1630 (and
1659 (and
1631 (range
1660 (range
1632 ('symbol', '2')
1661 ('symbol', '2')
1633 ('symbol', '0')
1662 ('symbol', '0')
1634 define)
1663 define)
1635 (func
1664 (func
1636 ('symbol', 'first')
1665 ('symbol', 'first')
1637 (func
1666 (func
1638 ('symbol', '_list')
1667 ('symbol', '_list')
1639 ('string', '1\x000\x002')
1668 ('string', '1\x000\x002')
1640 define)
1669 define)
1641 follow)
1670 follow)
1642 define)
1671 define)
1643 * set:
1672 * set:
1644 <baseset
1673 <baseset
1645 <limit n=1, offset=0,
1674 <limit n=1, offset=0,
1646 <spanset- 0:2>,
1675 <spanset- 0:2>,
1647 <baseset [1, 0, 2]>>>
1676 <baseset [1, 0, 2]>>>
1648 1
1677 1
1649
1678
1650 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1679 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1651 (and
1680 (and
1652 (range
1681 (range
1653 ('symbol', '2')
1682 ('symbol', '2')
1654 ('symbol', '0'))
1683 ('symbol', '0'))
1655 (not
1684 (not
1656 (func
1685 (func
1657 ('symbol', 'last')
1686 ('symbol', 'last')
1658 (or
1687 (or
1659 (list
1688 (list
1660 ('symbol', '0')
1689 ('symbol', '0')
1661 ('symbol', '2')
1690 ('symbol', '2')
1662 ('symbol', '1'))))))
1691 ('symbol', '1'))))))
1663 * optimized:
1692 * optimized:
1664 (difference
1693 (difference
1665 (range
1694 (range
1666 ('symbol', '2')
1695 ('symbol', '2')
1667 ('symbol', '0')
1696 ('symbol', '0')
1668 define)
1697 define)
1669 (func
1698 (func
1670 ('symbol', 'last')
1699 ('symbol', 'last')
1671 (func
1700 (func
1672 ('symbol', '_list')
1701 ('symbol', '_list')
1673 ('string', '0\x002\x001')
1702 ('string', '0\x002\x001')
1674 define)
1703 define)
1675 any)
1704 any)
1676 define)
1705 define)
1677 * set:
1706 * set:
1678 <filteredset
1707 <filteredset
1679 <spanset- 0:2>,
1708 <spanset- 0:2>,
1680 <not
1709 <not
1681 <baseset
1710 <baseset
1682 <last n=1,
1711 <last n=1,
1683 <fullreposet+ 0:9>,
1712 <fullreposet+ 0:9>,
1684 <baseset [1, 2, 0]>>>>>
1713 <baseset [1, 2, 0]>>>>>
1685 2
1714 2
1686 0
1715 0
1687
1716
1688 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1717 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1689
1718
1690 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1719 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1691 (and
1720 (and
1692 (range
1721 (range
1693 ('symbol', '2')
1722 ('symbol', '2')
1694 ('symbol', '0'))
1723 ('symbol', '0'))
1695 (range
1724 (range
1696 (group
1725 (group
1697 (or
1726 (or
1698 (list
1727 (list
1699 ('symbol', '1')
1728 ('symbol', '1')
1700 ('symbol', '0')
1729 ('symbol', '0')
1701 ('symbol', '2'))))
1730 ('symbol', '2'))))
1702 (group
1731 (group
1703 (or
1732 (or
1704 (list
1733 (list
1705 ('symbol', '0')
1734 ('symbol', '0')
1706 ('symbol', '2')
1735 ('symbol', '2')
1707 ('symbol', '1'))))))
1736 ('symbol', '1'))))))
1708 * optimized:
1737 * optimized:
1709 (and
1738 (and
1710 (range
1739 (range
1711 ('symbol', '2')
1740 ('symbol', '2')
1712 ('symbol', '0')
1741 ('symbol', '0')
1713 define)
1742 define)
1714 (range
1743 (range
1715 (func
1744 (func
1716 ('symbol', '_list')
1745 ('symbol', '_list')
1717 ('string', '1\x000\x002')
1746 ('string', '1\x000\x002')
1718 define)
1747 define)
1719 (func
1748 (func
1720 ('symbol', '_list')
1749 ('symbol', '_list')
1721 ('string', '0\x002\x001')
1750 ('string', '0\x002\x001')
1722 define)
1751 define)
1723 follow)
1752 follow)
1724 define)
1753 define)
1725 * set:
1754 * set:
1726 <filteredset
1755 <filteredset
1727 <baseset [1]>,
1756 <spanset- 0:2>,
1728 <spanset- 0:2>>
1757 <baseset [1]>>
1729 1
1758 1
1730
1759
1731 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1760 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1732 the ordering rule is determined before the rewrite; in this example,
1761 the ordering rule is determined before the rewrite; in this example,
1733 'B' follows the order of the initial set, which is the same order as 'A'
1762 'B' follows the order of the initial set, which is the same order as 'A'
1734 since 'A' also follows the order:
1763 since 'A' also follows the order:
1735
1764
1736 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1765 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1737 (and
1766 (and
1738 (func
1767 (func
1739 ('symbol', 'contains')
1768 ('symbol', 'contains')
1740 ('string', 'glob:*'))
1769 ('string', 'glob:*'))
1741 (group
1770 (group
1742 (or
1771 (or
1743 (list
1772 (list
1744 ('symbol', '2')
1773 ('symbol', '2')
1745 ('symbol', '0')
1774 ('symbol', '0')
1746 ('symbol', '1')))))
1775 ('symbol', '1')))))
1747 * optimized:
1776 * optimized:
1748 (and
1777 (and
1749 (func
1778 (func
1750 ('symbol', '_list')
1779 ('symbol', '_list')
1751 ('string', '2\x000\x001')
1780 ('string', '2\x000\x001')
1752 follow)
1781 follow)
1753 (func
1782 (func
1754 ('symbol', 'contains')
1783 ('symbol', 'contains')
1755 ('string', 'glob:*')
1784 ('string', 'glob:*')
1756 define)
1785 define)
1757 define)
1786 define)
1758 * set:
1787 * set:
1759 <filteredset
1788 <filteredset
1760 <baseset+ [0, 1, 2]>,
1789 <baseset+ [0, 1, 2]>,
1761 <contains 'glob:*'>>
1790 <contains 'glob:*'>>
1762 0
1791 0
1763 1
1792 1
1764 2
1793 2
1765
1794
1766 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1795 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1767 the order appropriately:
1796 the order appropriately:
1768
1797
1769 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1798 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1770 (and
1799 (and
1771 (func
1800 (func
1772 ('symbol', 'reverse')
1801 ('symbol', 'reverse')
1773 (func
1802 (func
1774 ('symbol', 'contains')
1803 ('symbol', 'contains')
1775 ('string', 'glob:*')))
1804 ('string', 'glob:*')))
1776 (group
1805 (group
1777 (or
1806 (or
1778 (list
1807 (list
1779 ('symbol', '0')
1808 ('symbol', '0')
1780 ('symbol', '2')
1809 ('symbol', '2')
1781 ('symbol', '1')))))
1810 ('symbol', '1')))))
1782 * optimized:
1811 * optimized:
1783 (and
1812 (and
1784 (func
1813 (func
1785 ('symbol', '_list')
1814 ('symbol', '_list')
1786 ('string', '0\x002\x001')
1815 ('string', '0\x002\x001')
1787 follow)
1816 follow)
1788 (func
1817 (func
1789 ('symbol', 'reverse')
1818 ('symbol', 'reverse')
1790 (func
1819 (func
1791 ('symbol', 'contains')
1820 ('symbol', 'contains')
1792 ('string', 'glob:*')
1821 ('string', 'glob:*')
1793 define)
1822 define)
1794 define)
1823 define)
1795 define)
1824 define)
1796 * set:
1825 * set:
1797 <filteredset
1826 <filteredset
1798 <baseset- [0, 1, 2]>,
1827 <baseset- [0, 1, 2]>,
1799 <contains 'glob:*'>>
1828 <contains 'glob:*'>>
1800 2
1829 2
1801 1
1830 1
1802 0
1831 0
1803
1832
1804 test sort revset
1833 test sort revset
1805 --------------------------------------------
1834 --------------------------------------------
1806
1835
1807 test when adding two unordered revsets
1836 test when adding two unordered revsets
1808
1837
1809 $ log 'sort(keyword(issue) or modifies(b))'
1838 $ log 'sort(keyword(issue) or modifies(b))'
1810 4
1839 4
1811 6
1840 6
1812
1841
1813 test when sorting a reversed collection in the same way it is
1842 test when sorting a reversed collection in the same way it is
1814
1843
1815 $ log 'sort(reverse(all()), -rev)'
1844 $ log 'sort(reverse(all()), -rev)'
1816 9
1845 9
1817 8
1846 8
1818 7
1847 7
1819 6
1848 6
1820 5
1849 5
1821 4
1850 4
1822 3
1851 3
1823 2
1852 2
1824 1
1853 1
1825 0
1854 0
1826
1855
1827 test when sorting a reversed collection
1856 test when sorting a reversed collection
1828
1857
1829 $ log 'sort(reverse(all()), rev)'
1858 $ log 'sort(reverse(all()), rev)'
1830 0
1859 0
1831 1
1860 1
1832 2
1861 2
1833 3
1862 3
1834 4
1863 4
1835 5
1864 5
1836 6
1865 6
1837 7
1866 7
1838 8
1867 8
1839 9
1868 9
1840
1869
1841
1870
1842 test sorting two sorted collections in different orders
1871 test sorting two sorted collections in different orders
1843
1872
1844 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1873 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1845 2
1874 2
1846 6
1875 6
1847 8
1876 8
1848 9
1877 9
1849
1878
1850 test sorting two sorted collections in different orders backwards
1879 test sorting two sorted collections in different orders backwards
1851
1880
1852 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1881 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1853 9
1882 9
1854 8
1883 8
1855 6
1884 6
1856 2
1885 2
1857
1886
1858 test empty sort key which is noop
1887 test empty sort key which is noop
1859
1888
1860 $ log 'sort(0 + 2 + 1, "")'
1889 $ log 'sort(0 + 2 + 1, "")'
1861 0
1890 0
1862 2
1891 2
1863 1
1892 1
1864
1893
1865 test invalid sort keys
1894 test invalid sort keys
1866
1895
1867 $ log 'sort(all(), -invalid)'
1896 $ log 'sort(all(), -invalid)'
1868 hg: parse error: unknown sort key '-invalid'
1897 hg: parse error: unknown sort key '-invalid'
1869 [255]
1898 [255]
1870
1899
1871 $ cd ..
1900 $ cd ..
1872
1901
1873 test sorting by multiple keys including variable-length strings
1902 test sorting by multiple keys including variable-length strings
1874
1903
1875 $ hg init sorting
1904 $ hg init sorting
1876 $ cd sorting
1905 $ cd sorting
1877 $ cat <<EOF >> .hg/hgrc
1906 $ cat <<EOF >> .hg/hgrc
1878 > [ui]
1907 > [ui]
1879 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1908 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1880 > [templatealias]
1909 > [templatealias]
1881 > p5(s) = pad(s, 5)
1910 > p5(s) = pad(s, 5)
1882 > EOF
1911 > EOF
1883 $ hg branch -qf b12
1912 $ hg branch -qf b12
1884 $ hg ci -m m111 -u u112 -d '111 10800'
1913 $ hg ci -m m111 -u u112 -d '111 10800'
1885 $ hg branch -qf b11
1914 $ hg branch -qf b11
1886 $ hg ci -m m12 -u u111 -d '112 7200'
1915 $ hg ci -m m12 -u u111 -d '112 7200'
1887 $ hg branch -qf b111
1916 $ hg branch -qf b111
1888 $ hg ci -m m11 -u u12 -d '111 3600'
1917 $ hg ci -m m11 -u u12 -d '111 3600'
1889 $ hg branch -qf b112
1918 $ hg branch -qf b112
1890 $ hg ci -m m111 -u u11 -d '120 0'
1919 $ hg ci -m m111 -u u11 -d '120 0'
1891 $ hg branch -qf b111
1920 $ hg branch -qf b111
1892 $ hg ci -m m112 -u u111 -d '110 14400'
1921 $ hg ci -m m112 -u u111 -d '110 14400'
1893 created new head
1922 created new head
1894
1923
1895 compare revisions (has fast path):
1924 compare revisions (has fast path):
1896
1925
1897 $ hg log -r 'sort(all(), rev)'
1926 $ hg log -r 'sort(all(), rev)'
1898 0 b12 m111 u112 111 10800
1927 0 b12 m111 u112 111 10800
1899 1 b11 m12 u111 112 7200
1928 1 b11 m12 u111 112 7200
1900 2 b111 m11 u12 111 3600
1929 2 b111 m11 u12 111 3600
1901 3 b112 m111 u11 120 0
1930 3 b112 m111 u11 120 0
1902 4 b111 m112 u111 110 14400
1931 4 b111 m112 u111 110 14400
1903
1932
1904 $ hg log -r 'sort(all(), -rev)'
1933 $ hg log -r 'sort(all(), -rev)'
1905 4 b111 m112 u111 110 14400
1934 4 b111 m112 u111 110 14400
1906 3 b112 m111 u11 120 0
1935 3 b112 m111 u11 120 0
1907 2 b111 m11 u12 111 3600
1936 2 b111 m11 u12 111 3600
1908 1 b11 m12 u111 112 7200
1937 1 b11 m12 u111 112 7200
1909 0 b12 m111 u112 111 10800
1938 0 b12 m111 u112 111 10800
1910
1939
1911 compare variable-length strings (issue5218):
1940 compare variable-length strings (issue5218):
1912
1941
1913 $ hg log -r 'sort(all(), branch)'
1942 $ hg log -r 'sort(all(), branch)'
1914 1 b11 m12 u111 112 7200
1943 1 b11 m12 u111 112 7200
1915 2 b111 m11 u12 111 3600
1944 2 b111 m11 u12 111 3600
1916 4 b111 m112 u111 110 14400
1945 4 b111 m112 u111 110 14400
1917 3 b112 m111 u11 120 0
1946 3 b112 m111 u11 120 0
1918 0 b12 m111 u112 111 10800
1947 0 b12 m111 u112 111 10800
1919
1948
1920 $ hg log -r 'sort(all(), -branch)'
1949 $ hg log -r 'sort(all(), -branch)'
1921 0 b12 m111 u112 111 10800
1950 0 b12 m111 u112 111 10800
1922 3 b112 m111 u11 120 0
1951 3 b112 m111 u11 120 0
1923 2 b111 m11 u12 111 3600
1952 2 b111 m11 u12 111 3600
1924 4 b111 m112 u111 110 14400
1953 4 b111 m112 u111 110 14400
1925 1 b11 m12 u111 112 7200
1954 1 b11 m12 u111 112 7200
1926
1955
1927 $ hg log -r 'sort(all(), desc)'
1956 $ hg log -r 'sort(all(), desc)'
1928 2 b111 m11 u12 111 3600
1957 2 b111 m11 u12 111 3600
1929 0 b12 m111 u112 111 10800
1958 0 b12 m111 u112 111 10800
1930 3 b112 m111 u11 120 0
1959 3 b112 m111 u11 120 0
1931 4 b111 m112 u111 110 14400
1960 4 b111 m112 u111 110 14400
1932 1 b11 m12 u111 112 7200
1961 1 b11 m12 u111 112 7200
1933
1962
1934 $ hg log -r 'sort(all(), -desc)'
1963 $ hg log -r 'sort(all(), -desc)'
1935 1 b11 m12 u111 112 7200
1964 1 b11 m12 u111 112 7200
1936 4 b111 m112 u111 110 14400
1965 4 b111 m112 u111 110 14400
1937 0 b12 m111 u112 111 10800
1966 0 b12 m111 u112 111 10800
1938 3 b112 m111 u11 120 0
1967 3 b112 m111 u11 120 0
1939 2 b111 m11 u12 111 3600
1968 2 b111 m11 u12 111 3600
1940
1969
1941 $ hg log -r 'sort(all(), user)'
1970 $ hg log -r 'sort(all(), user)'
1942 3 b112 m111 u11 120 0
1971 3 b112 m111 u11 120 0
1943 1 b11 m12 u111 112 7200
1972 1 b11 m12 u111 112 7200
1944 4 b111 m112 u111 110 14400
1973 4 b111 m112 u111 110 14400
1945 0 b12 m111 u112 111 10800
1974 0 b12 m111 u112 111 10800
1946 2 b111 m11 u12 111 3600
1975 2 b111 m11 u12 111 3600
1947
1976
1948 $ hg log -r 'sort(all(), -user)'
1977 $ hg log -r 'sort(all(), -user)'
1949 2 b111 m11 u12 111 3600
1978 2 b111 m11 u12 111 3600
1950 0 b12 m111 u112 111 10800
1979 0 b12 m111 u112 111 10800
1951 1 b11 m12 u111 112 7200
1980 1 b11 m12 u111 112 7200
1952 4 b111 m112 u111 110 14400
1981 4 b111 m112 u111 110 14400
1953 3 b112 m111 u11 120 0
1982 3 b112 m111 u11 120 0
1954
1983
1955 compare dates (tz offset should have no effect):
1984 compare dates (tz offset should have no effect):
1956
1985
1957 $ hg log -r 'sort(all(), date)'
1986 $ hg log -r 'sort(all(), date)'
1958 4 b111 m112 u111 110 14400
1987 4 b111 m112 u111 110 14400
1959 0 b12 m111 u112 111 10800
1988 0 b12 m111 u112 111 10800
1960 2 b111 m11 u12 111 3600
1989 2 b111 m11 u12 111 3600
1961 1 b11 m12 u111 112 7200
1990 1 b11 m12 u111 112 7200
1962 3 b112 m111 u11 120 0
1991 3 b112 m111 u11 120 0
1963
1992
1964 $ hg log -r 'sort(all(), -date)'
1993 $ hg log -r 'sort(all(), -date)'
1965 3 b112 m111 u11 120 0
1994 3 b112 m111 u11 120 0
1966 1 b11 m12 u111 112 7200
1995 1 b11 m12 u111 112 7200
1967 0 b12 m111 u112 111 10800
1996 0 b12 m111 u112 111 10800
1968 2 b111 m11 u12 111 3600
1997 2 b111 m11 u12 111 3600
1969 4 b111 m112 u111 110 14400
1998 4 b111 m112 u111 110 14400
1970
1999
1971 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2000 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
1972 because '-k' reverses the comparison, not the list itself:
2001 because '-k' reverses the comparison, not the list itself:
1973
2002
1974 $ hg log -r 'sort(0 + 2, date)'
2003 $ hg log -r 'sort(0 + 2, date)'
1975 0 b12 m111 u112 111 10800
2004 0 b12 m111 u112 111 10800
1976 2 b111 m11 u12 111 3600
2005 2 b111 m11 u12 111 3600
1977
2006
1978 $ hg log -r 'sort(0 + 2, -date)'
2007 $ hg log -r 'sort(0 + 2, -date)'
1979 0 b12 m111 u112 111 10800
2008 0 b12 m111 u112 111 10800
1980 2 b111 m11 u12 111 3600
2009 2 b111 m11 u12 111 3600
1981
2010
1982 $ hg log -r 'reverse(sort(0 + 2, date))'
2011 $ hg log -r 'reverse(sort(0 + 2, date))'
1983 2 b111 m11 u12 111 3600
2012 2 b111 m11 u12 111 3600
1984 0 b12 m111 u112 111 10800
2013 0 b12 m111 u112 111 10800
1985
2014
1986 sort by multiple keys:
2015 sort by multiple keys:
1987
2016
1988 $ hg log -r 'sort(all(), "branch -rev")'
2017 $ hg log -r 'sort(all(), "branch -rev")'
1989 1 b11 m12 u111 112 7200
2018 1 b11 m12 u111 112 7200
1990 4 b111 m112 u111 110 14400
2019 4 b111 m112 u111 110 14400
1991 2 b111 m11 u12 111 3600
2020 2 b111 m11 u12 111 3600
1992 3 b112 m111 u11 120 0
2021 3 b112 m111 u11 120 0
1993 0 b12 m111 u112 111 10800
2022 0 b12 m111 u112 111 10800
1994
2023
1995 $ hg log -r 'sort(all(), "-desc -date")'
2024 $ hg log -r 'sort(all(), "-desc -date")'
1996 1 b11 m12 u111 112 7200
2025 1 b11 m12 u111 112 7200
1997 4 b111 m112 u111 110 14400
2026 4 b111 m112 u111 110 14400
1998 3 b112 m111 u11 120 0
2027 3 b112 m111 u11 120 0
1999 0 b12 m111 u112 111 10800
2028 0 b12 m111 u112 111 10800
2000 2 b111 m11 u12 111 3600
2029 2 b111 m11 u12 111 3600
2001
2030
2002 $ hg log -r 'sort(all(), "user -branch date rev")'
2031 $ hg log -r 'sort(all(), "user -branch date rev")'
2003 3 b112 m111 u11 120 0
2032 3 b112 m111 u11 120 0
2004 4 b111 m112 u111 110 14400
2033 4 b111 m112 u111 110 14400
2005 1 b11 m12 u111 112 7200
2034 1 b11 m12 u111 112 7200
2006 0 b12 m111 u112 111 10800
2035 0 b12 m111 u112 111 10800
2007 2 b111 m11 u12 111 3600
2036 2 b111 m11 u12 111 3600
2008
2037
2009 toposort prioritises graph branches
2038 toposort prioritises graph branches
2010
2039
2011 $ hg up 2
2040 $ hg up 2
2012 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2041 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2013 $ touch a
2042 $ touch a
2014 $ hg addremove
2043 $ hg addremove
2015 adding a
2044 adding a
2016 $ hg ci -m 't1' -u 'tu' -d '130 0'
2045 $ hg ci -m 't1' -u 'tu' -d '130 0'
2017 created new head
2046 created new head
2018 $ echo 'a' >> a
2047 $ echo 'a' >> a
2019 $ hg ci -m 't2' -u 'tu' -d '130 0'
2048 $ hg ci -m 't2' -u 'tu' -d '130 0'
2020 $ hg book book1
2049 $ hg book book1
2021 $ hg up 4
2050 $ hg up 4
2022 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2051 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2023 (leaving bookmark book1)
2052 (leaving bookmark book1)
2024 $ touch a
2053 $ touch a
2025 $ hg addremove
2054 $ hg addremove
2026 adding a
2055 adding a
2027 $ hg ci -m 't3' -u 'tu' -d '130 0'
2056 $ hg ci -m 't3' -u 'tu' -d '130 0'
2028
2057
2029 $ hg log -r 'sort(all(), topo)'
2058 $ hg log -r 'sort(all(), topo)'
2030 7 b111 t3 tu 130 0
2059 7 b111 t3 tu 130 0
2031 4 b111 m112 u111 110 14400
2060 4 b111 m112 u111 110 14400
2032 3 b112 m111 u11 120 0
2061 3 b112 m111 u11 120 0
2033 6 b111 t2 tu 130 0
2062 6 b111 t2 tu 130 0
2034 5 b111 t1 tu 130 0
2063 5 b111 t1 tu 130 0
2035 2 b111 m11 u12 111 3600
2064 2 b111 m11 u12 111 3600
2036 1 b11 m12 u111 112 7200
2065 1 b11 m12 u111 112 7200
2037 0 b12 m111 u112 111 10800
2066 0 b12 m111 u112 111 10800
2038
2067
2039 $ hg log -r 'sort(all(), -topo)'
2068 $ hg log -r 'sort(all(), -topo)'
2040 0 b12 m111 u112 111 10800
2069 0 b12 m111 u112 111 10800
2041 1 b11 m12 u111 112 7200
2070 1 b11 m12 u111 112 7200
2042 2 b111 m11 u12 111 3600
2071 2 b111 m11 u12 111 3600
2043 5 b111 t1 tu 130 0
2072 5 b111 t1 tu 130 0
2044 6 b111 t2 tu 130 0
2073 6 b111 t2 tu 130 0
2045 3 b112 m111 u11 120 0
2074 3 b112 m111 u11 120 0
2046 4 b111 m112 u111 110 14400
2075 4 b111 m112 u111 110 14400
2047 7 b111 t3 tu 130 0
2076 7 b111 t3 tu 130 0
2048
2077
2049 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2078 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2050 6 b111 t2 tu 130 0
2079 6 b111 t2 tu 130 0
2051 5 b111 t1 tu 130 0
2080 5 b111 t1 tu 130 0
2052 7 b111 t3 tu 130 0
2081 7 b111 t3 tu 130 0
2053 4 b111 m112 u111 110 14400
2082 4 b111 m112 u111 110 14400
2054 3 b112 m111 u11 120 0
2083 3 b112 m111 u11 120 0
2055 2 b111 m11 u12 111 3600
2084 2 b111 m11 u12 111 3600
2056 1 b11 m12 u111 112 7200
2085 1 b11 m12 u111 112 7200
2057 0 b12 m111 u112 111 10800
2086 0 b12 m111 u112 111 10800
2058
2087
2059 topographical sorting can't be combined with other sort keys, and you can't
2088 topographical sorting can't be combined with other sort keys, and you can't
2060 use the topo.firstbranch option when topo sort is not active:
2089 use the topo.firstbranch option when topo sort is not active:
2061
2090
2062 $ hg log -r 'sort(all(), "topo user")'
2091 $ hg log -r 'sort(all(), "topo user")'
2063 hg: parse error: topo sort order cannot be combined with other sort keys
2092 hg: parse error: topo sort order cannot be combined with other sort keys
2064 [255]
2093 [255]
2065
2094
2066 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2095 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2067 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2096 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2068 [255]
2097 [255]
2069
2098
2070 topo.firstbranch should accept any kind of expressions:
2099 topo.firstbranch should accept any kind of expressions:
2071
2100
2072 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2101 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2073 0 b12 m111 u112 111 10800
2102 0 b12 m111 u112 111 10800
2074
2103
2075 $ cd ..
2104 $ cd ..
2076 $ cd repo
2105 $ cd repo
2077
2106
2078 test subtracting something from an addset
2107 test subtracting something from an addset
2079
2108
2080 $ log '(outgoing() or removes(a)) - removes(a)'
2109 $ log '(outgoing() or removes(a)) - removes(a)'
2081 8
2110 8
2082 9
2111 9
2083
2112
2084 test intersecting something with an addset
2113 test intersecting something with an addset
2085
2114
2086 $ log 'parents(outgoing() or removes(a))'
2115 $ log 'parents(outgoing() or removes(a))'
2087 1
2116 1
2088 4
2117 4
2089 5
2118 5
2090 8
2119 8
2091
2120
2092 test that `or` operation combines elements in the right order:
2121 test that `or` operation combines elements in the right order:
2093
2122
2094 $ log '3:4 or 2:5'
2123 $ log '3:4 or 2:5'
2095 3
2124 3
2096 4
2125 4
2097 2
2126 2
2098 5
2127 5
2099 $ log '3:4 or 5:2'
2128 $ log '3:4 or 5:2'
2100 3
2129 3
2101 4
2130 4
2102 5
2131 5
2103 2
2132 2
2104 $ log 'sort(3:4 or 2:5)'
2133 $ log 'sort(3:4 or 2:5)'
2105 2
2134 2
2106 3
2135 3
2107 4
2136 4
2108 5
2137 5
2109 $ log 'sort(3:4 or 5:2)'
2138 $ log 'sort(3:4 or 5:2)'
2110 2
2139 2
2111 3
2140 3
2112 4
2141 4
2113 5
2142 5
2114
2143
2115 test that more than one `-r`s are combined in the right order and deduplicated:
2144 test that more than one `-r`s are combined in the right order and deduplicated:
2116
2145
2117 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2146 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2118 3
2147 3
2119 4
2148 4
2120 5
2149 5
2121 2
2150 2
2122 0
2151 0
2123 1
2152 1
2124
2153
2125 test that `or` operation skips duplicated revisions from right-hand side
2154 test that `or` operation skips duplicated revisions from right-hand side
2126
2155
2127 $ try 'reverse(1::5) or ancestors(4)'
2156 $ try 'reverse(1::5) or ancestors(4)'
2128 (or
2157 (or
2129 (list
2158 (list
2130 (func
2159 (func
2131 ('symbol', 'reverse')
2160 ('symbol', 'reverse')
2132 (dagrange
2161 (dagrange
2133 ('symbol', '1')
2162 ('symbol', '1')
2134 ('symbol', '5')))
2163 ('symbol', '5')))
2135 (func
2164 (func
2136 ('symbol', 'ancestors')
2165 ('symbol', 'ancestors')
2137 ('symbol', '4'))))
2166 ('symbol', '4'))))
2138 * set:
2167 * set:
2139 <addset
2168 <addset
2140 <baseset- [1, 3, 5]>,
2169 <baseset- [1, 3, 5]>,
2141 <generatorset+>>
2170 <generatorset+>>
2142 5
2171 5
2143 3
2172 3
2144 1
2173 1
2145 0
2174 0
2146 2
2175 2
2147 4
2176 4
2148 $ try 'sort(ancestors(4) or reverse(1::5))'
2177 $ try 'sort(ancestors(4) or reverse(1::5))'
2149 (func
2178 (func
2150 ('symbol', 'sort')
2179 ('symbol', 'sort')
2151 (or
2180 (or
2152 (list
2181 (list
2153 (func
2182 (func
2154 ('symbol', 'ancestors')
2183 ('symbol', 'ancestors')
2155 ('symbol', '4'))
2184 ('symbol', '4'))
2156 (func
2185 (func
2157 ('symbol', 'reverse')
2186 ('symbol', 'reverse')
2158 (dagrange
2187 (dagrange
2159 ('symbol', '1')
2188 ('symbol', '1')
2160 ('symbol', '5'))))))
2189 ('symbol', '5'))))))
2161 * set:
2190 * set:
2162 <addset+
2191 <addset+
2163 <generatorset+>,
2192 <generatorset+>,
2164 <baseset- [1, 3, 5]>>
2193 <baseset- [1, 3, 5]>>
2165 0
2194 0
2166 1
2195 1
2167 2
2196 2
2168 3
2197 3
2169 4
2198 4
2170 5
2199 5
2171
2200
2172 test optimization of trivial `or` operation
2201 test optimization of trivial `or` operation
2173
2202
2174 $ try --optimize '0|(1)|"2"|-2|tip|null'
2203 $ try --optimize '0|(1)|"2"|-2|tip|null'
2175 (or
2204 (or
2176 (list
2205 (list
2177 ('symbol', '0')
2206 ('symbol', '0')
2178 (group
2207 (group
2179 ('symbol', '1'))
2208 ('symbol', '1'))
2180 ('string', '2')
2209 ('string', '2')
2181 (negate
2210 (negate
2182 ('symbol', '2'))
2211 ('symbol', '2'))
2183 ('symbol', 'tip')
2212 ('symbol', 'tip')
2184 ('symbol', 'null')))
2213 ('symbol', 'null')))
2185 * optimized:
2214 * optimized:
2186 (func
2215 (func
2187 ('symbol', '_list')
2216 ('symbol', '_list')
2188 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2217 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2189 define)
2218 define)
2190 * set:
2219 * set:
2191 <baseset [0, 1, 2, 8, 9, -1]>
2220 <baseset [0, 1, 2, 8, 9, -1]>
2192 0
2221 0
2193 1
2222 1
2194 2
2223 2
2195 8
2224 8
2196 9
2225 9
2197 -1
2226 -1
2198
2227
2199 $ try --optimize '0|1|2:3'
2228 $ try --optimize '0|1|2:3'
2200 (or
2229 (or
2201 (list
2230 (list
2202 ('symbol', '0')
2231 ('symbol', '0')
2203 ('symbol', '1')
2232 ('symbol', '1')
2204 (range
2233 (range
2205 ('symbol', '2')
2234 ('symbol', '2')
2206 ('symbol', '3'))))
2235 ('symbol', '3'))))
2207 * optimized:
2236 * optimized:
2208 (or
2237 (or
2209 (list
2238 (list
2210 (func
2239 (func
2211 ('symbol', '_list')
2240 ('symbol', '_list')
2212 ('string', '0\x001')
2241 ('string', '0\x001')
2213 define)
2242 define)
2214 (range
2243 (range
2215 ('symbol', '2')
2244 ('symbol', '2')
2216 ('symbol', '3')
2245 ('symbol', '3')
2217 define))
2246 define))
2218 define)
2247 define)
2219 * set:
2248 * set:
2220 <addset
2249 <addset
2221 <baseset [0, 1]>,
2250 <baseset [0, 1]>,
2222 <spanset+ 2:3>>
2251 <spanset+ 2:3>>
2223 0
2252 0
2224 1
2253 1
2225 2
2254 2
2226 3
2255 3
2227
2256
2228 $ try --optimize '0:1|2|3:4|5|6'
2257 $ try --optimize '0:1|2|3:4|5|6'
2229 (or
2258 (or
2230 (list
2259 (list
2231 (range
2260 (range
2232 ('symbol', '0')
2261 ('symbol', '0')
2233 ('symbol', '1'))
2262 ('symbol', '1'))
2234 ('symbol', '2')
2263 ('symbol', '2')
2235 (range
2264 (range
2236 ('symbol', '3')
2265 ('symbol', '3')
2237 ('symbol', '4'))
2266 ('symbol', '4'))
2238 ('symbol', '5')
2267 ('symbol', '5')
2239 ('symbol', '6')))
2268 ('symbol', '6')))
2240 * optimized:
2269 * optimized:
2241 (or
2270 (or
2242 (list
2271 (list
2243 (range
2272 (range
2244 ('symbol', '0')
2273 ('symbol', '0')
2245 ('symbol', '1')
2274 ('symbol', '1')
2246 define)
2275 define)
2247 ('symbol', '2')
2276 ('symbol', '2')
2248 (range
2277 (range
2249 ('symbol', '3')
2278 ('symbol', '3')
2250 ('symbol', '4')
2279 ('symbol', '4')
2251 define)
2280 define)
2252 (func
2281 (func
2253 ('symbol', '_list')
2282 ('symbol', '_list')
2254 ('string', '5\x006')
2283 ('string', '5\x006')
2255 define))
2284 define))
2256 define)
2285 define)
2257 * set:
2286 * set:
2258 <addset
2287 <addset
2259 <addset
2288 <addset
2260 <spanset+ 0:1>,
2289 <spanset+ 0:1>,
2261 <baseset [2]>>,
2290 <baseset [2]>>,
2262 <addset
2291 <addset
2263 <spanset+ 3:4>,
2292 <spanset+ 3:4>,
2264 <baseset [5, 6]>>>
2293 <baseset [5, 6]>>>
2265 0
2294 0
2266 1
2295 1
2267 2
2296 2
2268 3
2297 3
2269 4
2298 4
2270 5
2299 5
2271 6
2300 6
2272
2301
2273 unoptimized `or` looks like this
2302 unoptimized `or` looks like this
2274
2303
2275 $ try --no-optimized -p analyzed '0|1|2|3|4'
2304 $ try --no-optimized -p analyzed '0|1|2|3|4'
2276 * analyzed:
2305 * analyzed:
2277 (or
2306 (or
2278 (list
2307 (list
2279 ('symbol', '0')
2308 ('symbol', '0')
2280 ('symbol', '1')
2309 ('symbol', '1')
2281 ('symbol', '2')
2310 ('symbol', '2')
2282 ('symbol', '3')
2311 ('symbol', '3')
2283 ('symbol', '4'))
2312 ('symbol', '4'))
2284 define)
2313 define)
2285 * set:
2314 * set:
2286 <addset
2315 <addset
2287 <addset
2316 <addset
2288 <baseset [0]>,
2317 <baseset [0]>,
2289 <baseset [1]>>,
2318 <baseset [1]>>,
2290 <addset
2319 <addset
2291 <baseset [2]>,
2320 <baseset [2]>,
2292 <addset
2321 <addset
2293 <baseset [3]>,
2322 <baseset [3]>,
2294 <baseset [4]>>>>
2323 <baseset [4]>>>>
2295 0
2324 0
2296 1
2325 1
2297 2
2326 2
2298 3
2327 3
2299 4
2328 4
2300
2329
2301 test that `_list` should be narrowed by provided `subset`
2330 test that `_list` should be narrowed by provided `subset`
2302
2331
2303 $ log '0:2 and (null|1|2|3)'
2332 $ log '0:2 and (null|1|2|3)'
2304 1
2333 1
2305 2
2334 2
2306
2335
2307 test that `_list` should remove duplicates
2336 test that `_list` should remove duplicates
2308
2337
2309 $ log '0|1|2|1|2|-1|tip'
2338 $ log '0|1|2|1|2|-1|tip'
2310 0
2339 0
2311 1
2340 1
2312 2
2341 2
2313 9
2342 9
2314
2343
2315 test unknown revision in `_list`
2344 test unknown revision in `_list`
2316
2345
2317 $ log '0|unknown'
2346 $ log '0|unknown'
2318 abort: unknown revision 'unknown'!
2347 abort: unknown revision 'unknown'!
2319 [255]
2348 [255]
2320
2349
2321 test integer range in `_list`
2350 test integer range in `_list`
2322
2351
2323 $ log '-1|-10'
2352 $ log '-1|-10'
2324 9
2353 9
2325 0
2354 0
2326
2355
2327 $ log '-10|-11'
2356 $ log '-10|-11'
2328 abort: unknown revision '-11'!
2357 abort: unknown revision '-11'!
2329 [255]
2358 [255]
2330
2359
2331 $ log '9|10'
2360 $ log '9|10'
2332 abort: unknown revision '10'!
2361 abort: unknown revision '10'!
2333 [255]
2362 [255]
2334
2363
2335 test '0000' != '0' in `_list`
2364 test '0000' != '0' in `_list`
2336
2365
2337 $ log '0|0000'
2366 $ log '0|0000'
2338 0
2367 0
2339 -1
2368 -1
2340
2369
2341 test ',' in `_list`
2370 test ',' in `_list`
2342 $ log '0,1'
2371 $ log '0,1'
2343 hg: parse error: can't use a list in this context
2372 hg: parse error: can't use a list in this context
2344 (see hg help "revsets.x or y")
2373 (see hg help "revsets.x or y")
2345 [255]
2374 [255]
2346 $ try '0,1,2'
2375 $ try '0,1,2'
2347 (list
2376 (list
2348 ('symbol', '0')
2377 ('symbol', '0')
2349 ('symbol', '1')
2378 ('symbol', '1')
2350 ('symbol', '2'))
2379 ('symbol', '2'))
2351 hg: parse error: can't use a list in this context
2380 hg: parse error: can't use a list in this context
2352 (see hg help "revsets.x or y")
2381 (see hg help "revsets.x or y")
2353 [255]
2382 [255]
2354
2383
2355 test that chained `or` operations make balanced addsets
2384 test that chained `or` operations make balanced addsets
2356
2385
2357 $ try '0:1|1:2|2:3|3:4|4:5'
2386 $ try '0:1|1:2|2:3|3:4|4:5'
2358 (or
2387 (or
2359 (list
2388 (list
2360 (range
2389 (range
2361 ('symbol', '0')
2390 ('symbol', '0')
2362 ('symbol', '1'))
2391 ('symbol', '1'))
2363 (range
2392 (range
2364 ('symbol', '1')
2393 ('symbol', '1')
2365 ('symbol', '2'))
2394 ('symbol', '2'))
2366 (range
2395 (range
2367 ('symbol', '2')
2396 ('symbol', '2')
2368 ('symbol', '3'))
2397 ('symbol', '3'))
2369 (range
2398 (range
2370 ('symbol', '3')
2399 ('symbol', '3')
2371 ('symbol', '4'))
2400 ('symbol', '4'))
2372 (range
2401 (range
2373 ('symbol', '4')
2402 ('symbol', '4')
2374 ('symbol', '5'))))
2403 ('symbol', '5'))))
2375 * set:
2404 * set:
2376 <addset
2405 <addset
2377 <addset
2406 <addset
2378 <spanset+ 0:1>,
2407 <spanset+ 0:1>,
2379 <spanset+ 1:2>>,
2408 <spanset+ 1:2>>,
2380 <addset
2409 <addset
2381 <spanset+ 2:3>,
2410 <spanset+ 2:3>,
2382 <addset
2411 <addset
2383 <spanset+ 3:4>,
2412 <spanset+ 3:4>,
2384 <spanset+ 4:5>>>>
2413 <spanset+ 4:5>>>>
2385 0
2414 0
2386 1
2415 1
2387 2
2416 2
2388 3
2417 3
2389 4
2418 4
2390 5
2419 5
2391
2420
2392 no crash by empty group "()" while optimizing `or` operations
2421 no crash by empty group "()" while optimizing `or` operations
2393
2422
2394 $ try --optimize '0|()'
2423 $ try --optimize '0|()'
2395 (or
2424 (or
2396 (list
2425 (list
2397 ('symbol', '0')
2426 ('symbol', '0')
2398 (group
2427 (group
2399 None)))
2428 None)))
2400 * optimized:
2429 * optimized:
2401 (or
2430 (or
2402 (list
2431 (list
2403 ('symbol', '0')
2432 ('symbol', '0')
2404 None)
2433 None)
2405 define)
2434 define)
2406 hg: parse error: missing argument
2435 hg: parse error: missing argument
2407 [255]
2436 [255]
2408
2437
2409 test that chained `or` operations never eat up stack (issue4624)
2438 test that chained `or` operations never eat up stack (issue4624)
2410 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2439 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2411
2440
2412 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2441 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2413 0
2442 0
2414 1
2443 1
2415
2444
2416 test that repeated `-r` options never eat up stack (issue4565)
2445 test that repeated `-r` options never eat up stack (issue4565)
2417 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2446 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2418
2447
2419 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2448 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2420 0
2449 0
2421 1
2450 1
2422
2451
2423 check that conversion to only works
2452 check that conversion to only works
2424 $ try --optimize '::3 - ::1'
2453 $ try --optimize '::3 - ::1'
2425 (minus
2454 (minus
2426 (dagrangepre
2455 (dagrangepre
2427 ('symbol', '3'))
2456 ('symbol', '3'))
2428 (dagrangepre
2457 (dagrangepre
2429 ('symbol', '1')))
2458 ('symbol', '1')))
2430 * optimized:
2459 * optimized:
2431 (func
2460 (func
2432 ('symbol', 'only')
2461 ('symbol', 'only')
2433 (list
2462 (list
2434 ('symbol', '3')
2463 ('symbol', '3')
2435 ('symbol', '1'))
2464 ('symbol', '1'))
2436 define)
2465 define)
2437 * set:
2466 * set:
2438 <baseset+ [3]>
2467 <baseset+ [3]>
2439 3
2468 3
2440 $ try --optimize 'ancestors(1) - ancestors(3)'
2469 $ try --optimize 'ancestors(1) - ancestors(3)'
2441 (minus
2470 (minus
2442 (func
2471 (func
2443 ('symbol', 'ancestors')
2472 ('symbol', 'ancestors')
2444 ('symbol', '1'))
2473 ('symbol', '1'))
2445 (func
2474 (func
2446 ('symbol', 'ancestors')
2475 ('symbol', 'ancestors')
2447 ('symbol', '3')))
2476 ('symbol', '3')))
2448 * optimized:
2477 * optimized:
2449 (func
2478 (func
2450 ('symbol', 'only')
2479 ('symbol', 'only')
2451 (list
2480 (list
2452 ('symbol', '1')
2481 ('symbol', '1')
2453 ('symbol', '3'))
2482 ('symbol', '3'))
2454 define)
2483 define)
2455 * set:
2484 * set:
2456 <baseset+ []>
2485 <baseset+ []>
2457 $ try --optimize 'not ::2 and ::6'
2486 $ try --optimize 'not ::2 and ::6'
2458 (and
2487 (and
2459 (not
2488 (not
2460 (dagrangepre
2489 (dagrangepre
2461 ('symbol', '2')))
2490 ('symbol', '2')))
2462 (dagrangepre
2491 (dagrangepre
2463 ('symbol', '6')))
2492 ('symbol', '6')))
2464 * optimized:
2493 * optimized:
2465 (func
2494 (func
2466 ('symbol', 'only')
2495 ('symbol', 'only')
2467 (list
2496 (list
2468 ('symbol', '6')
2497 ('symbol', '6')
2469 ('symbol', '2'))
2498 ('symbol', '2'))
2470 define)
2499 define)
2471 * set:
2500 * set:
2472 <baseset+ [3, 4, 5, 6]>
2501 <baseset+ [3, 4, 5, 6]>
2473 3
2502 3
2474 4
2503 4
2475 5
2504 5
2476 6
2505 6
2477 $ try --optimize 'ancestors(6) and not ancestors(4)'
2506 $ try --optimize 'ancestors(6) and not ancestors(4)'
2478 (and
2507 (and
2479 (func
2508 (func
2480 ('symbol', 'ancestors')
2509 ('symbol', 'ancestors')
2481 ('symbol', '6'))
2510 ('symbol', '6'))
2482 (not
2511 (not
2483 (func
2512 (func
2484 ('symbol', 'ancestors')
2513 ('symbol', 'ancestors')
2485 ('symbol', '4'))))
2514 ('symbol', '4'))))
2486 * optimized:
2515 * optimized:
2487 (func
2516 (func
2488 ('symbol', 'only')
2517 ('symbol', 'only')
2489 (list
2518 (list
2490 ('symbol', '6')
2519 ('symbol', '6')
2491 ('symbol', '4'))
2520 ('symbol', '4'))
2492 define)
2521 define)
2493 * set:
2522 * set:
2494 <baseset+ [3, 5, 6]>
2523 <baseset+ [3, 5, 6]>
2495 3
2524 3
2496 5
2525 5
2497 6
2526 6
2498
2527
2499 no crash by empty group "()" while optimizing to "only()"
2528 no crash by empty group "()" while optimizing to "only()"
2500
2529
2501 $ try --optimize '::1 and ()'
2530 $ try --optimize '::1 and ()'
2502 (and
2531 (and
2503 (dagrangepre
2532 (dagrangepre
2504 ('symbol', '1'))
2533 ('symbol', '1'))
2505 (group
2534 (group
2506 None))
2535 None))
2507 * optimized:
2536 * optimized:
2508 (and
2537 (and
2509 None
2538 None
2510 (func
2539 (func
2511 ('symbol', 'ancestors')
2540 ('symbol', 'ancestors')
2512 ('symbol', '1')
2541 ('symbol', '1')
2513 define)
2542 define)
2514 define)
2543 define)
2515 hg: parse error: missing argument
2544 hg: parse error: missing argument
2516 [255]
2545 [255]
2517
2546
2518 invalid function call should not be optimized to only()
2547 invalid function call should not be optimized to only()
2519
2548
2520 $ log '"ancestors"(6) and not ancestors(4)'
2549 $ log '"ancestors"(6) and not ancestors(4)'
2521 hg: parse error: not a symbol
2550 hg: parse error: not a symbol
2522 [255]
2551 [255]
2523
2552
2524 $ log 'ancestors(6) and not "ancestors"(4)'
2553 $ log 'ancestors(6) and not "ancestors"(4)'
2525 hg: parse error: not a symbol
2554 hg: parse error: not a symbol
2526 [255]
2555 [255]
2527
2556
2528 we can use patterns when searching for tags
2557 we can use patterns when searching for tags
2529
2558
2530 $ log 'tag("1..*")'
2559 $ log 'tag("1..*")'
2531 abort: tag '1..*' does not exist!
2560 abort: tag '1..*' does not exist!
2532 [255]
2561 [255]
2533 $ log 'tag("re:1..*")'
2562 $ log 'tag("re:1..*")'
2534 6
2563 6
2535 $ log 'tag("re:[0-9].[0-9]")'
2564 $ log 'tag("re:[0-9].[0-9]")'
2536 6
2565 6
2537 $ log 'tag("literal:1.0")'
2566 $ log 'tag("literal:1.0")'
2538 6
2567 6
2539 $ log 'tag("re:0..*")'
2568 $ log 'tag("re:0..*")'
2540
2569
2541 $ log 'tag(unknown)'
2570 $ log 'tag(unknown)'
2542 abort: tag 'unknown' does not exist!
2571 abort: tag 'unknown' does not exist!
2543 [255]
2572 [255]
2544 $ log 'tag("re:unknown")'
2573 $ log 'tag("re:unknown")'
2545 $ log 'present(tag("unknown"))'
2574 $ log 'present(tag("unknown"))'
2546 $ log 'present(tag("re:unknown"))'
2575 $ log 'present(tag("re:unknown"))'
2547 $ log 'branch(unknown)'
2576 $ log 'branch(unknown)'
2548 abort: unknown revision 'unknown'!
2577 abort: unknown revision 'unknown'!
2549 [255]
2578 [255]
2550 $ log 'branch("literal:unknown")'
2579 $ log 'branch("literal:unknown")'
2551 abort: branch 'unknown' does not exist!
2580 abort: branch 'unknown' does not exist!
2552 [255]
2581 [255]
2553 $ log 'branch("re:unknown")'
2582 $ log 'branch("re:unknown")'
2554 $ log 'present(branch("unknown"))'
2583 $ log 'present(branch("unknown"))'
2555 $ log 'present(branch("re:unknown"))'
2584 $ log 'present(branch("re:unknown"))'
2556 $ log 'user(bob)'
2585 $ log 'user(bob)'
2557 2
2586 2
2558
2587
2559 $ log '4::8'
2588 $ log '4::8'
2560 4
2589 4
2561 8
2590 8
2562 $ log '4:8'
2591 $ log '4:8'
2563 4
2592 4
2564 5
2593 5
2565 6
2594 6
2566 7
2595 7
2567 8
2596 8
2568
2597
2569 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2598 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2570 4
2599 4
2571 2
2600 2
2572 5
2601 5
2573
2602
2574 $ log 'not 0 and 0:2'
2603 $ log 'not 0 and 0:2'
2575 1
2604 1
2576 2
2605 2
2577 $ log 'not 1 and 0:2'
2606 $ log 'not 1 and 0:2'
2578 0
2607 0
2579 2
2608 2
2580 $ log 'not 2 and 0:2'
2609 $ log 'not 2 and 0:2'
2581 0
2610 0
2582 1
2611 1
2583 $ log '(1 and 2)::'
2612 $ log '(1 and 2)::'
2584 $ log '(1 and 2):'
2613 $ log '(1 and 2):'
2585 $ log '(1 and 2):3'
2614 $ log '(1 and 2):3'
2586 $ log 'sort(head(), -rev)'
2615 $ log 'sort(head(), -rev)'
2587 9
2616 9
2588 7
2617 7
2589 6
2618 6
2590 5
2619 5
2591 4
2620 4
2592 3
2621 3
2593 2
2622 2
2594 1
2623 1
2595 0
2624 0
2596 $ log '4::8 - 8'
2625 $ log '4::8 - 8'
2597 4
2626 4
2598
2627
2599 matching() should preserve the order of the input set:
2628 matching() should preserve the order of the input set:
2600
2629
2601 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2630 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2602 2
2631 2
2603 3
2632 3
2604 1
2633 1
2605
2634
2606 $ log 'named("unknown")'
2635 $ log 'named("unknown")'
2607 abort: namespace 'unknown' does not exist!
2636 abort: namespace 'unknown' does not exist!
2608 [255]
2637 [255]
2609 $ log 'named("re:unknown")'
2638 $ log 'named("re:unknown")'
2610 abort: no namespace exists that match 'unknown'!
2639 abort: no namespace exists that match 'unknown'!
2611 [255]
2640 [255]
2612 $ log 'present(named("unknown"))'
2641 $ log 'present(named("unknown"))'
2613 $ log 'present(named("re:unknown"))'
2642 $ log 'present(named("re:unknown"))'
2614
2643
2615 $ log 'tag()'
2644 $ log 'tag()'
2616 6
2645 6
2617 $ log 'named("tags")'
2646 $ log 'named("tags")'
2618 6
2647 6
2619
2648
2620 issue2437
2649 issue2437
2621
2650
2622 $ log '3 and p1(5)'
2651 $ log '3 and p1(5)'
2623 3
2652 3
2624 $ log '4 and p2(6)'
2653 $ log '4 and p2(6)'
2625 4
2654 4
2626 $ log '1 and parents(:2)'
2655 $ log '1 and parents(:2)'
2627 1
2656 1
2628 $ log '2 and children(1:)'
2657 $ log '2 and children(1:)'
2629 2
2658 2
2630 $ log 'roots(all()) or roots(all())'
2659 $ log 'roots(all()) or roots(all())'
2631 0
2660 0
2632 $ hg debugrevspec 'roots(all()) or roots(all())'
2661 $ hg debugrevspec 'roots(all()) or roots(all())'
2633 0
2662 0
2634 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2663 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2635 9
2664 9
2636 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2665 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2637 4
2666 4
2638
2667
2639 issue2654: report a parse error if the revset was not completely parsed
2668 issue2654: report a parse error if the revset was not completely parsed
2640
2669
2641 $ log '1 OR 2'
2670 $ log '1 OR 2'
2642 hg: parse error at 2: invalid token
2671 hg: parse error at 2: invalid token
2643 [255]
2672 [255]
2644
2673
2645 or operator should preserve ordering:
2674 or operator should preserve ordering:
2646 $ log 'reverse(2::4) or tip'
2675 $ log 'reverse(2::4) or tip'
2647 4
2676 4
2648 2
2677 2
2649 9
2678 9
2650
2679
2651 parentrevspec
2680 parentrevspec
2652
2681
2653 $ log 'merge()^0'
2682 $ log 'merge()^0'
2654 6
2683 6
2655 $ log 'merge()^'
2684 $ log 'merge()^'
2656 5
2685 5
2657 $ log 'merge()^1'
2686 $ log 'merge()^1'
2658 5
2687 5
2659 $ log 'merge()^2'
2688 $ log 'merge()^2'
2660 4
2689 4
2661 $ log 'merge()^^'
2690 $ log 'merge()^^'
2662 3
2691 3
2663 $ log 'merge()^1^'
2692 $ log 'merge()^1^'
2664 3
2693 3
2665 $ log 'merge()^^^'
2694 $ log 'merge()^^^'
2666 1
2695 1
2667
2696
2668 $ log 'merge()~0'
2697 $ log 'merge()~0'
2669 6
2698 6
2670 $ log 'merge()~1'
2699 $ log 'merge()~1'
2671 5
2700 5
2672 $ log 'merge()~2'
2701 $ log 'merge()~2'
2673 3
2702 3
2674 $ log 'merge()~2^1'
2703 $ log 'merge()~2^1'
2675 1
2704 1
2676 $ log 'merge()~3'
2705 $ log 'merge()~3'
2677 1
2706 1
2678
2707
2679 $ log '(-3:tip)^'
2708 $ log '(-3:tip)^'
2680 4
2709 4
2681 6
2710 6
2682 8
2711 8
2683
2712
2684 $ log 'tip^foo'
2713 $ log 'tip^foo'
2685 hg: parse error: ^ expects a number 0, 1, or 2
2714 hg: parse error: ^ expects a number 0, 1, or 2
2686 [255]
2715 [255]
2687
2716
2688 Bogus function gets suggestions
2717 Bogus function gets suggestions
2689 $ log 'add()'
2718 $ log 'add()'
2690 hg: parse error: unknown identifier: add
2719 hg: parse error: unknown identifier: add
2691 (did you mean adds?)
2720 (did you mean adds?)
2692 [255]
2721 [255]
2693 $ log 'added()'
2722 $ log 'added()'
2694 hg: parse error: unknown identifier: added
2723 hg: parse error: unknown identifier: added
2695 (did you mean adds?)
2724 (did you mean adds?)
2696 [255]
2725 [255]
2697 $ log 'remo()'
2726 $ log 'remo()'
2698 hg: parse error: unknown identifier: remo
2727 hg: parse error: unknown identifier: remo
2699 (did you mean one of remote, removes?)
2728 (did you mean one of remote, removes?)
2700 [255]
2729 [255]
2701 $ log 'babar()'
2730 $ log 'babar()'
2702 hg: parse error: unknown identifier: babar
2731 hg: parse error: unknown identifier: babar
2703 [255]
2732 [255]
2704
2733
2705 Bogus function with a similar internal name doesn't suggest the internal name
2734 Bogus function with a similar internal name doesn't suggest the internal name
2706 $ log 'matches()'
2735 $ log 'matches()'
2707 hg: parse error: unknown identifier: matches
2736 hg: parse error: unknown identifier: matches
2708 (did you mean matching?)
2737 (did you mean matching?)
2709 [255]
2738 [255]
2710
2739
2711 Undocumented functions aren't suggested as similar either
2740 Undocumented functions aren't suggested as similar either
2712 $ log 'wdir2()'
2741 $ log 'wdir2()'
2713 hg: parse error: unknown identifier: wdir2
2742 hg: parse error: unknown identifier: wdir2
2714 [255]
2743 [255]
2715
2744
2716 multiple revspecs
2745 multiple revspecs
2717
2746
2718 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2747 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2719 8
2748 8
2720 9
2749 9
2721 4
2750 4
2722 5
2751 5
2723 6
2752 6
2724 7
2753 7
2725
2754
2726 test usage in revpair (with "+")
2755 test usage in revpair (with "+")
2727
2756
2728 (real pair)
2757 (real pair)
2729
2758
2730 $ hg diff -r 'tip^^' -r 'tip'
2759 $ hg diff -r 'tip^^' -r 'tip'
2731 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2760 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2732 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2761 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2733 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2762 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2734 @@ -0,0 +1,1 @@
2763 @@ -0,0 +1,1 @@
2735 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2764 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2736 $ hg diff -r 'tip^^::tip'
2765 $ hg diff -r 'tip^^::tip'
2737 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2766 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2738 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2767 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2739 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2768 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2740 @@ -0,0 +1,1 @@
2769 @@ -0,0 +1,1 @@
2741 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2770 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2742
2771
2743 (single rev)
2772 (single rev)
2744
2773
2745 $ hg diff -r 'tip^' -r 'tip^'
2774 $ hg diff -r 'tip^' -r 'tip^'
2746 $ hg diff -r 'tip^:tip^'
2775 $ hg diff -r 'tip^:tip^'
2747
2776
2748 (single rev that does not looks like a range)
2777 (single rev that does not looks like a range)
2749
2778
2750 $ hg diff -r 'tip^::tip^ or tip^'
2779 $ hg diff -r 'tip^::tip^ or tip^'
2751 diff -r d5d0dcbdc4d9 .hgtags
2780 diff -r d5d0dcbdc4d9 .hgtags
2752 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2781 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2753 +++ b/.hgtags * (glob)
2782 +++ b/.hgtags * (glob)
2754 @@ -0,0 +1,1 @@
2783 @@ -0,0 +1,1 @@
2755 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2784 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2756 $ hg diff -r 'tip^ or tip^'
2785 $ hg diff -r 'tip^ or tip^'
2757 diff -r d5d0dcbdc4d9 .hgtags
2786 diff -r d5d0dcbdc4d9 .hgtags
2758 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2787 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2759 +++ b/.hgtags * (glob)
2788 +++ b/.hgtags * (glob)
2760 @@ -0,0 +1,1 @@
2789 @@ -0,0 +1,1 @@
2761 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2790 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2762
2791
2763 (no rev)
2792 (no rev)
2764
2793
2765 $ hg diff -r 'author("babar") or author("celeste")'
2794 $ hg diff -r 'author("babar") or author("celeste")'
2766 abort: empty revision range
2795 abort: empty revision range
2767 [255]
2796 [255]
2768
2797
2769 aliases:
2798 aliases:
2770
2799
2771 $ echo '[revsetalias]' >> .hg/hgrc
2800 $ echo '[revsetalias]' >> .hg/hgrc
2772 $ echo 'm = merge()' >> .hg/hgrc
2801 $ echo 'm = merge()' >> .hg/hgrc
2773 (revset aliases can override builtin revsets)
2802 (revset aliases can override builtin revsets)
2774 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2803 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2775 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2804 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2776 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2805 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2777 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2806 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2778 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2807 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2779
2808
2780 $ try m
2809 $ try m
2781 ('symbol', 'm')
2810 ('symbol', 'm')
2782 * expanded:
2811 * expanded:
2783 (func
2812 (func
2784 ('symbol', 'merge')
2813 ('symbol', 'merge')
2785 None)
2814 None)
2786 * set:
2815 * set:
2787 <filteredset
2816 <filteredset
2788 <fullreposet+ 0:9>,
2817 <fullreposet+ 0:9>,
2789 <merge>>
2818 <merge>>
2790 6
2819 6
2791
2820
2792 $ HGPLAIN=1
2821 $ HGPLAIN=1
2793 $ export HGPLAIN
2822 $ export HGPLAIN
2794 $ try m
2823 $ try m
2795 ('symbol', 'm')
2824 ('symbol', 'm')
2796 abort: unknown revision 'm'!
2825 abort: unknown revision 'm'!
2797 [255]
2826 [255]
2798
2827
2799 $ HGPLAINEXCEPT=revsetalias
2828 $ HGPLAINEXCEPT=revsetalias
2800 $ export HGPLAINEXCEPT
2829 $ export HGPLAINEXCEPT
2801 $ try m
2830 $ try m
2802 ('symbol', 'm')
2831 ('symbol', 'm')
2803 * expanded:
2832 * expanded:
2804 (func
2833 (func
2805 ('symbol', 'merge')
2834 ('symbol', 'merge')
2806 None)
2835 None)
2807 * set:
2836 * set:
2808 <filteredset
2837 <filteredset
2809 <fullreposet+ 0:9>,
2838 <fullreposet+ 0:9>,
2810 <merge>>
2839 <merge>>
2811 6
2840 6
2812
2841
2813 $ unset HGPLAIN
2842 $ unset HGPLAIN
2814 $ unset HGPLAINEXCEPT
2843 $ unset HGPLAINEXCEPT
2815
2844
2816 $ try 'p2(.)'
2845 $ try 'p2(.)'
2817 (func
2846 (func
2818 ('symbol', 'p2')
2847 ('symbol', 'p2')
2819 ('symbol', '.'))
2848 ('symbol', '.'))
2820 * expanded:
2849 * expanded:
2821 (func
2850 (func
2822 ('symbol', 'p1')
2851 ('symbol', 'p1')
2823 ('symbol', '.'))
2852 ('symbol', '.'))
2824 * set:
2853 * set:
2825 <baseset+ [8]>
2854 <baseset+ [8]>
2826 8
2855 8
2827
2856
2828 $ HGPLAIN=1
2857 $ HGPLAIN=1
2829 $ export HGPLAIN
2858 $ export HGPLAIN
2830 $ try 'p2(.)'
2859 $ try 'p2(.)'
2831 (func
2860 (func
2832 ('symbol', 'p2')
2861 ('symbol', 'p2')
2833 ('symbol', '.'))
2862 ('symbol', '.'))
2834 * set:
2863 * set:
2835 <baseset+ []>
2864 <baseset+ []>
2836
2865
2837 $ HGPLAINEXCEPT=revsetalias
2866 $ HGPLAINEXCEPT=revsetalias
2838 $ export HGPLAINEXCEPT
2867 $ export HGPLAINEXCEPT
2839 $ try 'p2(.)'
2868 $ try 'p2(.)'
2840 (func
2869 (func
2841 ('symbol', 'p2')
2870 ('symbol', 'p2')
2842 ('symbol', '.'))
2871 ('symbol', '.'))
2843 * expanded:
2872 * expanded:
2844 (func
2873 (func
2845 ('symbol', 'p1')
2874 ('symbol', 'p1')
2846 ('symbol', '.'))
2875 ('symbol', '.'))
2847 * set:
2876 * set:
2848 <baseset+ [8]>
2877 <baseset+ [8]>
2849 8
2878 8
2850
2879
2851 $ unset HGPLAIN
2880 $ unset HGPLAIN
2852 $ unset HGPLAINEXCEPT
2881 $ unset HGPLAINEXCEPT
2853
2882
2854 test alias recursion
2883 test alias recursion
2855
2884
2856 $ try sincem
2885 $ try sincem
2857 ('symbol', 'sincem')
2886 ('symbol', 'sincem')
2858 * expanded:
2887 * expanded:
2859 (func
2888 (func
2860 ('symbol', 'descendants')
2889 ('symbol', 'descendants')
2861 (func
2890 (func
2862 ('symbol', 'merge')
2891 ('symbol', 'merge')
2863 None))
2892 None))
2864 * set:
2893 * set:
2865 <addset+
2894 <addset+
2866 <filteredset
2895 <filteredset
2867 <fullreposet+ 0:9>,
2896 <fullreposet+ 0:9>,
2868 <merge>>,
2897 <merge>>,
2869 <generatorset+>>
2898 <generatorset+>>
2870 6
2899 6
2871 7
2900 7
2872
2901
2873 test infinite recursion
2902 test infinite recursion
2874
2903
2875 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2904 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2876 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2905 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2877 $ try recurse1
2906 $ try recurse1
2878 ('symbol', 'recurse1')
2907 ('symbol', 'recurse1')
2879 hg: parse error: infinite expansion of revset alias "recurse1" detected
2908 hg: parse error: infinite expansion of revset alias "recurse1" detected
2880 [255]
2909 [255]
2881
2910
2882 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2911 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2883 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2912 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2884 $ try "level2(level1(1, 2), 3)"
2913 $ try "level2(level1(1, 2), 3)"
2885 (func
2914 (func
2886 ('symbol', 'level2')
2915 ('symbol', 'level2')
2887 (list
2916 (list
2888 (func
2917 (func
2889 ('symbol', 'level1')
2918 ('symbol', 'level1')
2890 (list
2919 (list
2891 ('symbol', '1')
2920 ('symbol', '1')
2892 ('symbol', '2')))
2921 ('symbol', '2')))
2893 ('symbol', '3')))
2922 ('symbol', '3')))
2894 * expanded:
2923 * expanded:
2895 (or
2924 (or
2896 (list
2925 (list
2897 ('symbol', '3')
2926 ('symbol', '3')
2898 (or
2927 (or
2899 (list
2928 (list
2900 ('symbol', '1')
2929 ('symbol', '1')
2901 ('symbol', '2')))))
2930 ('symbol', '2')))))
2902 * set:
2931 * set:
2903 <addset
2932 <addset
2904 <baseset [3]>,
2933 <baseset [3]>,
2905 <baseset [1, 2]>>
2934 <baseset [1, 2]>>
2906 3
2935 3
2907 1
2936 1
2908 2
2937 2
2909
2938
2910 test nesting and variable passing
2939 test nesting and variable passing
2911
2940
2912 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
2941 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
2913 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
2942 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
2914 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
2943 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
2915 $ try 'nested(2:5)'
2944 $ try 'nested(2:5)'
2916 (func
2945 (func
2917 ('symbol', 'nested')
2946 ('symbol', 'nested')
2918 (range
2947 (range
2919 ('symbol', '2')
2948 ('symbol', '2')
2920 ('symbol', '5')))
2949 ('symbol', '5')))
2921 * expanded:
2950 * expanded:
2922 (func
2951 (func
2923 ('symbol', 'max')
2952 ('symbol', 'max')
2924 (range
2953 (range
2925 ('symbol', '2')
2954 ('symbol', '2')
2926 ('symbol', '5')))
2955 ('symbol', '5')))
2927 * set:
2956 * set:
2928 <baseset
2957 <baseset
2929 <max
2958 <max
2930 <fullreposet+ 0:9>,
2959 <fullreposet+ 0:9>,
2931 <spanset+ 2:5>>>
2960 <spanset+ 2:5>>>
2932 5
2961 5
2933
2962
2934 test chained `or` operations are flattened at parsing phase
2963 test chained `or` operations are flattened at parsing phase
2935
2964
2936 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
2965 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
2937 $ try 'chainedorops(0:1, 1:2, 2:3)'
2966 $ try 'chainedorops(0:1, 1:2, 2:3)'
2938 (func
2967 (func
2939 ('symbol', 'chainedorops')
2968 ('symbol', 'chainedorops')
2940 (list
2969 (list
2941 (range
2970 (range
2942 ('symbol', '0')
2971 ('symbol', '0')
2943 ('symbol', '1'))
2972 ('symbol', '1'))
2944 (range
2973 (range
2945 ('symbol', '1')
2974 ('symbol', '1')
2946 ('symbol', '2'))
2975 ('symbol', '2'))
2947 (range
2976 (range
2948 ('symbol', '2')
2977 ('symbol', '2')
2949 ('symbol', '3'))))
2978 ('symbol', '3'))))
2950 * expanded:
2979 * expanded:
2951 (or
2980 (or
2952 (list
2981 (list
2953 (range
2982 (range
2954 ('symbol', '0')
2983 ('symbol', '0')
2955 ('symbol', '1'))
2984 ('symbol', '1'))
2956 (range
2985 (range
2957 ('symbol', '1')
2986 ('symbol', '1')
2958 ('symbol', '2'))
2987 ('symbol', '2'))
2959 (range
2988 (range
2960 ('symbol', '2')
2989 ('symbol', '2')
2961 ('symbol', '3'))))
2990 ('symbol', '3'))))
2962 * set:
2991 * set:
2963 <addset
2992 <addset
2964 <spanset+ 0:1>,
2993 <spanset+ 0:1>,
2965 <addset
2994 <addset
2966 <spanset+ 1:2>,
2995 <spanset+ 1:2>,
2967 <spanset+ 2:3>>>
2996 <spanset+ 2:3>>>
2968 0
2997 0
2969 1
2998 1
2970 2
2999 2
2971 3
3000 3
2972
3001
2973 test variable isolation, variable placeholders are rewritten as string
3002 test variable isolation, variable placeholders are rewritten as string
2974 then parsed and matched again as string. Check they do not leak too
3003 then parsed and matched again as string. Check they do not leak too
2975 far away.
3004 far away.
2976
3005
2977 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3006 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
2978 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3007 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
2979 $ try 'callinjection(2:5)'
3008 $ try 'callinjection(2:5)'
2980 (func
3009 (func
2981 ('symbol', 'callinjection')
3010 ('symbol', 'callinjection')
2982 (range
3011 (range
2983 ('symbol', '2')
3012 ('symbol', '2')
2984 ('symbol', '5')))
3013 ('symbol', '5')))
2985 * expanded:
3014 * expanded:
2986 (func
3015 (func
2987 ('symbol', 'descendants')
3016 ('symbol', 'descendants')
2988 (func
3017 (func
2989 ('symbol', 'max')
3018 ('symbol', 'max')
2990 ('string', '$1')))
3019 ('string', '$1')))
2991 abort: unknown revision '$1'!
3020 abort: unknown revision '$1'!
2992 [255]
3021 [255]
2993
3022
2994 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3023 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
2995 but 'all()' should never be substituded to '0()'.
3024 but 'all()' should never be substituded to '0()'.
2996
3025
2997 $ echo 'universe = all()' >> .hg/hgrc
3026 $ echo 'universe = all()' >> .hg/hgrc
2998 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3027 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
2999 $ try 'shadowall(0)'
3028 $ try 'shadowall(0)'
3000 (func
3029 (func
3001 ('symbol', 'shadowall')
3030 ('symbol', 'shadowall')
3002 ('symbol', '0'))
3031 ('symbol', '0'))
3003 * expanded:
3032 * expanded:
3004 (and
3033 (and
3005 ('symbol', '0')
3034 ('symbol', '0')
3006 (func
3035 (func
3007 ('symbol', 'all')
3036 ('symbol', 'all')
3008 None))
3037 None))
3009 * set:
3038 * set:
3010 <filteredset
3039 <filteredset
3011 <baseset [0]>,
3040 <baseset [0]>,
3012 <spanset+ 0:9>>
3041 <spanset+ 0:9>>
3013 0
3042 0
3014
3043
3015 test unknown reference:
3044 test unknown reference:
3016
3045
3017 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3046 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3018 (func
3047 (func
3019 ('symbol', 'unknownref')
3048 ('symbol', 'unknownref')
3020 ('symbol', '0'))
3049 ('symbol', '0'))
3021 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3050 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3022 [255]
3051 [255]
3023
3052
3024 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3053 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3025 ('symbol', 'tip')
3054 ('symbol', 'tip')
3026 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3055 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3027 * set:
3056 * set:
3028 <baseset [9]>
3057 <baseset [9]>
3029 9
3058 9
3030
3059
3031 $ try 'tip'
3060 $ try 'tip'
3032 ('symbol', 'tip')
3061 ('symbol', 'tip')
3033 * set:
3062 * set:
3034 <baseset [9]>
3063 <baseset [9]>
3035 9
3064 9
3036
3065
3037 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3066 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3038 ('symbol', 'tip')
3067 ('symbol', 'tip')
3039 warning: bad declaration of revset alias "bad name": at 4: invalid token
3068 warning: bad declaration of revset alias "bad name": at 4: invalid token
3040 * set:
3069 * set:
3041 <baseset [9]>
3070 <baseset [9]>
3042 9
3071 9
3043 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3072 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3044 $ try 'strictreplacing("foo", tip)'
3073 $ try 'strictreplacing("foo", tip)'
3045 (func
3074 (func
3046 ('symbol', 'strictreplacing')
3075 ('symbol', 'strictreplacing')
3047 (list
3076 (list
3048 ('string', 'foo')
3077 ('string', 'foo')
3049 ('symbol', 'tip')))
3078 ('symbol', 'tip')))
3050 * expanded:
3079 * expanded:
3051 (or
3080 (or
3052 (list
3081 (list
3053 ('symbol', 'tip')
3082 ('symbol', 'tip')
3054 (func
3083 (func
3055 ('symbol', 'desc')
3084 ('symbol', 'desc')
3056 ('string', '$1'))))
3085 ('string', '$1'))))
3057 * set:
3086 * set:
3058 <addset
3087 <addset
3059 <baseset [9]>,
3088 <baseset [9]>,
3060 <filteredset
3089 <filteredset
3061 <fullreposet+ 0:9>,
3090 <fullreposet+ 0:9>,
3062 <desc '$1'>>>
3091 <desc '$1'>>>
3063 9
3092 9
3064
3093
3065 $ try 'd(2:5)'
3094 $ try 'd(2:5)'
3066 (func
3095 (func
3067 ('symbol', 'd')
3096 ('symbol', 'd')
3068 (range
3097 (range
3069 ('symbol', '2')
3098 ('symbol', '2')
3070 ('symbol', '5')))
3099 ('symbol', '5')))
3071 * expanded:
3100 * expanded:
3072 (func
3101 (func
3073 ('symbol', 'reverse')
3102 ('symbol', 'reverse')
3074 (func
3103 (func
3075 ('symbol', 'sort')
3104 ('symbol', 'sort')
3076 (list
3105 (list
3077 (range
3106 (range
3078 ('symbol', '2')
3107 ('symbol', '2')
3079 ('symbol', '5'))
3108 ('symbol', '5'))
3080 ('symbol', 'date'))))
3109 ('symbol', 'date'))))
3081 * set:
3110 * set:
3082 <baseset [4, 5, 3, 2]>
3111 <baseset [4, 5, 3, 2]>
3083 4
3112 4
3084 5
3113 5
3085 3
3114 3
3086 2
3115 2
3087 $ try 'rs(2 or 3, date)'
3116 $ try 'rs(2 or 3, date)'
3088 (func
3117 (func
3089 ('symbol', 'rs')
3118 ('symbol', 'rs')
3090 (list
3119 (list
3091 (or
3120 (or
3092 (list
3121 (list
3093 ('symbol', '2')
3122 ('symbol', '2')
3094 ('symbol', '3')))
3123 ('symbol', '3')))
3095 ('symbol', 'date')))
3124 ('symbol', 'date')))
3096 * expanded:
3125 * expanded:
3097 (func
3126 (func
3098 ('symbol', 'reverse')
3127 ('symbol', 'reverse')
3099 (func
3128 (func
3100 ('symbol', 'sort')
3129 ('symbol', 'sort')
3101 (list
3130 (list
3102 (or
3131 (or
3103 (list
3132 (list
3104 ('symbol', '2')
3133 ('symbol', '2')
3105 ('symbol', '3')))
3134 ('symbol', '3')))
3106 ('symbol', 'date'))))
3135 ('symbol', 'date'))))
3107 * set:
3136 * set:
3108 <baseset [3, 2]>
3137 <baseset [3, 2]>
3109 3
3138 3
3110 2
3139 2
3111 $ try 'rs()'
3140 $ try 'rs()'
3112 (func
3141 (func
3113 ('symbol', 'rs')
3142 ('symbol', 'rs')
3114 None)
3143 None)
3115 hg: parse error: invalid number of arguments: 0
3144 hg: parse error: invalid number of arguments: 0
3116 [255]
3145 [255]
3117 $ try 'rs(2)'
3146 $ try 'rs(2)'
3118 (func
3147 (func
3119 ('symbol', 'rs')
3148 ('symbol', 'rs')
3120 ('symbol', '2'))
3149 ('symbol', '2'))
3121 hg: parse error: invalid number of arguments: 1
3150 hg: parse error: invalid number of arguments: 1
3122 [255]
3151 [255]
3123 $ try 'rs(2, data, 7)'
3152 $ try 'rs(2, data, 7)'
3124 (func
3153 (func
3125 ('symbol', 'rs')
3154 ('symbol', 'rs')
3126 (list
3155 (list
3127 ('symbol', '2')
3156 ('symbol', '2')
3128 ('symbol', 'data')
3157 ('symbol', 'data')
3129 ('symbol', '7')))
3158 ('symbol', '7')))
3130 hg: parse error: invalid number of arguments: 3
3159 hg: parse error: invalid number of arguments: 3
3131 [255]
3160 [255]
3132 $ try 'rs4(2 or 3, x, x, date)'
3161 $ try 'rs4(2 or 3, x, x, date)'
3133 (func
3162 (func
3134 ('symbol', 'rs4')
3163 ('symbol', 'rs4')
3135 (list
3164 (list
3136 (or
3165 (or
3137 (list
3166 (list
3138 ('symbol', '2')
3167 ('symbol', '2')
3139 ('symbol', '3')))
3168 ('symbol', '3')))
3140 ('symbol', 'x')
3169 ('symbol', 'x')
3141 ('symbol', 'x')
3170 ('symbol', 'x')
3142 ('symbol', 'date')))
3171 ('symbol', 'date')))
3143 * expanded:
3172 * expanded:
3144 (func
3173 (func
3145 ('symbol', 'reverse')
3174 ('symbol', 'reverse')
3146 (func
3175 (func
3147 ('symbol', 'sort')
3176 ('symbol', 'sort')
3148 (list
3177 (list
3149 (or
3178 (or
3150 (list
3179 (list
3151 ('symbol', '2')
3180 ('symbol', '2')
3152 ('symbol', '3')))
3181 ('symbol', '3')))
3153 ('symbol', 'date'))))
3182 ('symbol', 'date'))))
3154 * set:
3183 * set:
3155 <baseset [3, 2]>
3184 <baseset [3, 2]>
3156 3
3185 3
3157 2
3186 2
3158
3187
3159 issue4553: check that revset aliases override existing hash prefix
3188 issue4553: check that revset aliases override existing hash prefix
3160
3189
3161 $ hg log -qr e
3190 $ hg log -qr e
3162 6:e0cc66ef77e8
3191 6:e0cc66ef77e8
3163
3192
3164 $ hg log -qr e --config revsetalias.e="all()"
3193 $ hg log -qr e --config revsetalias.e="all()"
3165 0:2785f51eece5
3194 0:2785f51eece5
3166 1:d75937da8da0
3195 1:d75937da8da0
3167 2:5ed5505e9f1c
3196 2:5ed5505e9f1c
3168 3:8528aa5637f2
3197 3:8528aa5637f2
3169 4:2326846efdab
3198 4:2326846efdab
3170 5:904fa392b941
3199 5:904fa392b941
3171 6:e0cc66ef77e8
3200 6:e0cc66ef77e8
3172 7:013af1973af4
3201 7:013af1973af4
3173 8:d5d0dcbdc4d9
3202 8:d5d0dcbdc4d9
3174 9:24286f4ae135
3203 9:24286f4ae135
3175
3204
3176 $ hg log -qr e: --config revsetalias.e="0"
3205 $ hg log -qr e: --config revsetalias.e="0"
3177 0:2785f51eece5
3206 0:2785f51eece5
3178 1:d75937da8da0
3207 1:d75937da8da0
3179 2:5ed5505e9f1c
3208 2:5ed5505e9f1c
3180 3:8528aa5637f2
3209 3:8528aa5637f2
3181 4:2326846efdab
3210 4:2326846efdab
3182 5:904fa392b941
3211 5:904fa392b941
3183 6:e0cc66ef77e8
3212 6:e0cc66ef77e8
3184 7:013af1973af4
3213 7:013af1973af4
3185 8:d5d0dcbdc4d9
3214 8:d5d0dcbdc4d9
3186 9:24286f4ae135
3215 9:24286f4ae135
3187
3216
3188 $ hg log -qr :e --config revsetalias.e="9"
3217 $ hg log -qr :e --config revsetalias.e="9"
3189 0:2785f51eece5
3218 0:2785f51eece5
3190 1:d75937da8da0
3219 1:d75937da8da0
3191 2:5ed5505e9f1c
3220 2:5ed5505e9f1c
3192 3:8528aa5637f2
3221 3:8528aa5637f2
3193 4:2326846efdab
3222 4:2326846efdab
3194 5:904fa392b941
3223 5:904fa392b941
3195 6:e0cc66ef77e8
3224 6:e0cc66ef77e8
3196 7:013af1973af4
3225 7:013af1973af4
3197 8:d5d0dcbdc4d9
3226 8:d5d0dcbdc4d9
3198 9:24286f4ae135
3227 9:24286f4ae135
3199
3228
3200 $ hg log -qr e:
3229 $ hg log -qr e:
3201 6:e0cc66ef77e8
3230 6:e0cc66ef77e8
3202 7:013af1973af4
3231 7:013af1973af4
3203 8:d5d0dcbdc4d9
3232 8:d5d0dcbdc4d9
3204 9:24286f4ae135
3233 9:24286f4ae135
3205
3234
3206 $ hg log -qr :e
3235 $ hg log -qr :e
3207 0:2785f51eece5
3236 0:2785f51eece5
3208 1:d75937da8da0
3237 1:d75937da8da0
3209 2:5ed5505e9f1c
3238 2:5ed5505e9f1c
3210 3:8528aa5637f2
3239 3:8528aa5637f2
3211 4:2326846efdab
3240 4:2326846efdab
3212 5:904fa392b941
3241 5:904fa392b941
3213 6:e0cc66ef77e8
3242 6:e0cc66ef77e8
3214
3243
3215 issue2549 - correct optimizations
3244 issue2549 - correct optimizations
3216
3245
3217 $ try 'limit(1 or 2 or 3, 2) and not 2'
3246 $ try 'limit(1 or 2 or 3, 2) and not 2'
3218 (and
3247 (and
3219 (func
3248 (func
3220 ('symbol', 'limit')
3249 ('symbol', 'limit')
3221 (list
3250 (list
3222 (or
3251 (or
3223 (list
3252 (list
3224 ('symbol', '1')
3253 ('symbol', '1')
3225 ('symbol', '2')
3254 ('symbol', '2')
3226 ('symbol', '3')))
3255 ('symbol', '3')))
3227 ('symbol', '2')))
3256 ('symbol', '2')))
3228 (not
3257 (not
3229 ('symbol', '2')))
3258 ('symbol', '2')))
3230 * set:
3259 * set:
3231 <filteredset
3260 <filteredset
3232 <baseset
3261 <baseset
3233 <limit n=2, offset=0,
3262 <limit n=2, offset=0,
3234 <fullreposet+ 0:9>,
3263 <fullreposet+ 0:9>,
3235 <baseset [1, 2, 3]>>>,
3264 <baseset [1, 2, 3]>>>,
3236 <not
3265 <not
3237 <baseset [2]>>>
3266 <baseset [2]>>>
3238 1
3267 1
3239 $ try 'max(1 or 2) and not 2'
3268 $ try 'max(1 or 2) and not 2'
3240 (and
3269 (and
3241 (func
3270 (func
3242 ('symbol', 'max')
3271 ('symbol', 'max')
3243 (or
3272 (or
3244 (list
3273 (list
3245 ('symbol', '1')
3274 ('symbol', '1')
3246 ('symbol', '2'))))
3275 ('symbol', '2'))))
3247 (not
3276 (not
3248 ('symbol', '2')))
3277 ('symbol', '2')))
3249 * set:
3278 * set:
3250 <filteredset
3279 <filteredset
3251 <baseset
3280 <baseset
3252 <max
3281 <max
3253 <fullreposet+ 0:9>,
3282 <fullreposet+ 0:9>,
3254 <baseset [1, 2]>>>,
3283 <baseset [1, 2]>>>,
3255 <not
3284 <not
3256 <baseset [2]>>>
3285 <baseset [2]>>>
3257 $ try 'min(1 or 2) and not 1'
3286 $ try 'min(1 or 2) and not 1'
3258 (and
3287 (and
3259 (func
3288 (func
3260 ('symbol', 'min')
3289 ('symbol', 'min')
3261 (or
3290 (or
3262 (list
3291 (list
3263 ('symbol', '1')
3292 ('symbol', '1')
3264 ('symbol', '2'))))
3293 ('symbol', '2'))))
3265 (not
3294 (not
3266 ('symbol', '1')))
3295 ('symbol', '1')))
3267 * set:
3296 * set:
3268 <filteredset
3297 <filteredset
3269 <baseset
3298 <baseset
3270 <min
3299 <min
3271 <fullreposet+ 0:9>,
3300 <fullreposet+ 0:9>,
3272 <baseset [1, 2]>>>,
3301 <baseset [1, 2]>>>,
3273 <not
3302 <not
3274 <baseset [1]>>>
3303 <baseset [1]>>>
3275 $ try 'last(1 or 2, 1) and not 2'
3304 $ try 'last(1 or 2, 1) and not 2'
3276 (and
3305 (and
3277 (func
3306 (func
3278 ('symbol', 'last')
3307 ('symbol', 'last')
3279 (list
3308 (list
3280 (or
3309 (or
3281 (list
3310 (list
3282 ('symbol', '1')
3311 ('symbol', '1')
3283 ('symbol', '2')))
3312 ('symbol', '2')))
3284 ('symbol', '1')))
3313 ('symbol', '1')))
3285 (not
3314 (not
3286 ('symbol', '2')))
3315 ('symbol', '2')))
3287 * set:
3316 * set:
3288 <filteredset
3317 <filteredset
3289 <baseset
3318 <baseset
3290 <last n=1,
3319 <last n=1,
3291 <fullreposet+ 0:9>,
3320 <fullreposet+ 0:9>,
3292 <baseset [2, 1]>>>,
3321 <baseset [2, 1]>>>,
3293 <not
3322 <not
3294 <baseset [2]>>>
3323 <baseset [2]>>>
3295
3324
3296 issue4289 - ordering of built-ins
3325 issue4289 - ordering of built-ins
3297 $ hg log -M -q -r 3:2
3326 $ hg log -M -q -r 3:2
3298 3:8528aa5637f2
3327 3:8528aa5637f2
3299 2:5ed5505e9f1c
3328 2:5ed5505e9f1c
3300
3329
3301 test revsets started with 40-chars hash (issue3669)
3330 test revsets started with 40-chars hash (issue3669)
3302
3331
3303 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3332 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3304 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3333 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3305 9
3334 9
3306 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3335 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3307 8
3336 8
3308
3337
3309 test or-ed indirect predicates (issue3775)
3338 test or-ed indirect predicates (issue3775)
3310
3339
3311 $ log '6 or 6^1' | sort
3340 $ log '6 or 6^1' | sort
3312 5
3341 5
3313 6
3342 6
3314 $ log '6^1 or 6' | sort
3343 $ log '6^1 or 6' | sort
3315 5
3344 5
3316 6
3345 6
3317 $ log '4 or 4~1' | sort
3346 $ log '4 or 4~1' | sort
3318 2
3347 2
3319 4
3348 4
3320 $ log '4~1 or 4' | sort
3349 $ log '4~1 or 4' | sort
3321 2
3350 2
3322 4
3351 4
3323 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3352 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3324 0
3353 0
3325 1
3354 1
3326 2
3355 2
3327 3
3356 3
3328 4
3357 4
3329 5
3358 5
3330 6
3359 6
3331 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3360 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3332 0
3361 0
3333 1
3362 1
3334 2
3363 2
3335 3
3364 3
3336 4
3365 4
3337 5
3366 5
3338 6
3367 6
3339
3368
3340 tests for 'remote()' predicate:
3369 tests for 'remote()' predicate:
3341 #. (csets in remote) (id) (remote)
3370 #. (csets in remote) (id) (remote)
3342 1. less than local current branch "default"
3371 1. less than local current branch "default"
3343 2. same with local specified "default"
3372 2. same with local specified "default"
3344 3. more than local specified specified
3373 3. more than local specified specified
3345
3374
3346 $ hg clone --quiet -U . ../remote3
3375 $ hg clone --quiet -U . ../remote3
3347 $ cd ../remote3
3376 $ cd ../remote3
3348 $ hg update -q 7
3377 $ hg update -q 7
3349 $ echo r > r
3378 $ echo r > r
3350 $ hg ci -Aqm 10
3379 $ hg ci -Aqm 10
3351 $ log 'remote()'
3380 $ log 'remote()'
3352 7
3381 7
3353 $ log 'remote("a-b-c-")'
3382 $ log 'remote("a-b-c-")'
3354 2
3383 2
3355 $ cd ../repo
3384 $ cd ../repo
3356 $ log 'remote(".a.b.c.", "../remote3")'
3385 $ log 'remote(".a.b.c.", "../remote3")'
3357
3386
3358 tests for concatenation of strings/symbols by "##"
3387 tests for concatenation of strings/symbols by "##"
3359
3388
3360 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3389 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3361 (_concat
3390 (_concat
3362 (_concat
3391 (_concat
3363 (_concat
3392 (_concat
3364 ('symbol', '278')
3393 ('symbol', '278')
3365 ('string', '5f5'))
3394 ('string', '5f5'))
3366 ('symbol', '1ee'))
3395 ('symbol', '1ee'))
3367 ('string', 'ce5'))
3396 ('string', 'ce5'))
3368 * concatenated:
3397 * concatenated:
3369 ('string', '2785f51eece5')
3398 ('string', '2785f51eece5')
3370 * set:
3399 * set:
3371 <baseset [0]>
3400 <baseset [0]>
3372 0
3401 0
3373
3402
3374 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3403 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3375 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3404 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3376 (func
3405 (func
3377 ('symbol', 'cat4')
3406 ('symbol', 'cat4')
3378 (list
3407 (list
3379 ('symbol', '278')
3408 ('symbol', '278')
3380 ('string', '5f5')
3409 ('string', '5f5')
3381 ('symbol', '1ee')
3410 ('symbol', '1ee')
3382 ('string', 'ce5')))
3411 ('string', 'ce5')))
3383 * expanded:
3412 * expanded:
3384 (_concat
3413 (_concat
3385 (_concat
3414 (_concat
3386 (_concat
3415 (_concat
3387 ('symbol', '278')
3416 ('symbol', '278')
3388 ('string', '5f5'))
3417 ('string', '5f5'))
3389 ('symbol', '1ee'))
3418 ('symbol', '1ee'))
3390 ('string', 'ce5'))
3419 ('string', 'ce5'))
3391 * concatenated:
3420 * concatenated:
3392 ('string', '2785f51eece5')
3421 ('string', '2785f51eece5')
3393 * set:
3422 * set:
3394 <baseset [0]>
3423 <baseset [0]>
3395 0
3424 0
3396
3425
3397 (check concatenation in alias nesting)
3426 (check concatenation in alias nesting)
3398
3427
3399 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3428 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3400 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3429 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3401 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3430 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3402 0
3431 0
3403
3432
3404 (check operator priority)
3433 (check operator priority)
3405
3434
3406 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3435 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3407 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3436 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3408 0
3437 0
3409 4
3438 4
3410
3439
3411 $ cd ..
3440 $ cd ..
3412
3441
3413 prepare repository that has "default" branches of multiple roots
3442 prepare repository that has "default" branches of multiple roots
3414
3443
3415 $ hg init namedbranch
3444 $ hg init namedbranch
3416 $ cd namedbranch
3445 $ cd namedbranch
3417
3446
3418 $ echo default0 >> a
3447 $ echo default0 >> a
3419 $ hg ci -Aqm0
3448 $ hg ci -Aqm0
3420 $ echo default1 >> a
3449 $ echo default1 >> a
3421 $ hg ci -m1
3450 $ hg ci -m1
3422
3451
3423 $ hg branch -q stable
3452 $ hg branch -q stable
3424 $ echo stable2 >> a
3453 $ echo stable2 >> a
3425 $ hg ci -m2
3454 $ hg ci -m2
3426 $ echo stable3 >> a
3455 $ echo stable3 >> a
3427 $ hg ci -m3
3456 $ hg ci -m3
3428
3457
3429 $ hg update -q null
3458 $ hg update -q null
3430 $ echo default4 >> a
3459 $ echo default4 >> a
3431 $ hg ci -Aqm4
3460 $ hg ci -Aqm4
3432 $ echo default5 >> a
3461 $ echo default5 >> a
3433 $ hg ci -m5
3462 $ hg ci -m5
3434
3463
3435 "null" revision belongs to "default" branch (issue4683)
3464 "null" revision belongs to "default" branch (issue4683)
3436
3465
3437 $ log 'branch(null)'
3466 $ log 'branch(null)'
3438 0
3467 0
3439 1
3468 1
3440 4
3469 4
3441 5
3470 5
3442
3471
3443 "null" revision belongs to "default" branch, but it shouldn't appear in set
3472 "null" revision belongs to "default" branch, but it shouldn't appear in set
3444 unless explicitly specified (issue4682)
3473 unless explicitly specified (issue4682)
3445
3474
3446 $ log 'children(branch(default))'
3475 $ log 'children(branch(default))'
3447 1
3476 1
3448 2
3477 2
3449 5
3478 5
3450
3479
3451 $ cd ..
3480 $ cd ..
3452
3481
3453 test author/desc/keyword in problematic encoding
3482 test author/desc/keyword in problematic encoding
3454 # unicode: cp932:
3483 # unicode: cp932:
3455 # u30A2 0x83 0x41(= 'A')
3484 # u30A2 0x83 0x41(= 'A')
3456 # u30C2 0x83 0x61(= 'a')
3485 # u30C2 0x83 0x61(= 'a')
3457
3486
3458 $ hg init problematicencoding
3487 $ hg init problematicencoding
3459 $ cd problematicencoding
3488 $ cd problematicencoding
3460
3489
3461 $ python > setup.sh <<EOF
3490 $ python > setup.sh <<EOF
3462 > print u'''
3491 > print u'''
3463 > echo a > text
3492 > echo a > text
3464 > hg add text
3493 > hg add text
3465 > hg --encoding utf-8 commit -u '\u30A2' -m none
3494 > hg --encoding utf-8 commit -u '\u30A2' -m none
3466 > echo b > text
3495 > echo b > text
3467 > hg --encoding utf-8 commit -u '\u30C2' -m none
3496 > hg --encoding utf-8 commit -u '\u30C2' -m none
3468 > echo c > text
3497 > echo c > text
3469 > hg --encoding utf-8 commit -u none -m '\u30A2'
3498 > hg --encoding utf-8 commit -u none -m '\u30A2'
3470 > echo d > text
3499 > echo d > text
3471 > hg --encoding utf-8 commit -u none -m '\u30C2'
3500 > hg --encoding utf-8 commit -u none -m '\u30C2'
3472 > '''.encode('utf-8')
3501 > '''.encode('utf-8')
3473 > EOF
3502 > EOF
3474 $ sh < setup.sh
3503 $ sh < setup.sh
3475
3504
3476 test in problematic encoding
3505 test in problematic encoding
3477 $ python > test.sh <<EOF
3506 $ python > test.sh <<EOF
3478 > print u'''
3507 > print u'''
3479 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3508 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3480 > echo ====
3509 > echo ====
3481 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3510 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3482 > echo ====
3511 > echo ====
3483 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3512 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3484 > echo ====
3513 > echo ====
3485 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3514 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3486 > echo ====
3515 > echo ====
3487 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3516 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3488 > echo ====
3517 > echo ====
3489 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3518 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3490 > '''.encode('cp932')
3519 > '''.encode('cp932')
3491 > EOF
3520 > EOF
3492 $ sh < test.sh
3521 $ sh < test.sh
3493 0
3522 0
3494 ====
3523 ====
3495 1
3524 1
3496 ====
3525 ====
3497 2
3526 2
3498 ====
3527 ====
3499 3
3528 3
3500 ====
3529 ====
3501 0
3530 0
3502 2
3531 2
3503 ====
3532 ====
3504 1
3533 1
3505 3
3534 3
3506
3535
3507 test error message of bad revset
3536 test error message of bad revset
3508 $ hg log -r 'foo\\'
3537 $ hg log -r 'foo\\'
3509 hg: parse error at 3: syntax error in revset 'foo\\'
3538 hg: parse error at 3: syntax error in revset 'foo\\'
3510 [255]
3539 [255]
3511
3540
3512 $ cd ..
3541 $ cd ..
3513
3542
3514 Test that revset predicate of extension isn't loaded at failure of
3543 Test that revset predicate of extension isn't loaded at failure of
3515 loading it
3544 loading it
3516
3545
3517 $ cd repo
3546 $ cd repo
3518
3547
3519 $ cat <<EOF > $TESTTMP/custompredicate.py
3548 $ cat <<EOF > $TESTTMP/custompredicate.py
3520 > from mercurial import error, registrar, revset
3549 > from mercurial import error, registrar, revset
3521 >
3550 >
3522 > revsetpredicate = registrar.revsetpredicate()
3551 > revsetpredicate = registrar.revsetpredicate()
3523 >
3552 >
3524 > @revsetpredicate('custom1()')
3553 > @revsetpredicate('custom1()')
3525 > def custom1(repo, subset, x):
3554 > def custom1(repo, subset, x):
3526 > return revset.baseset([1])
3555 > return revset.baseset([1])
3527 >
3556 >
3528 > raise error.Abort('intentional failure of loading extension')
3557 > raise error.Abort('intentional failure of loading extension')
3529 > EOF
3558 > EOF
3530 $ cat <<EOF > .hg/hgrc
3559 $ cat <<EOF > .hg/hgrc
3531 > [extensions]
3560 > [extensions]
3532 > custompredicate = $TESTTMP/custompredicate.py
3561 > custompredicate = $TESTTMP/custompredicate.py
3533 > EOF
3562 > EOF
3534
3563
3535 $ hg debugrevspec "custom1()"
3564 $ hg debugrevspec "custom1()"
3536 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3565 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3537 hg: parse error: unknown identifier: custom1
3566 hg: parse error: unknown identifier: custom1
3538 [255]
3567 [255]
3539
3568
3540 $ cd ..
3569 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now