##// END OF EJS Templates
revset: make sort() noop depending on ordering requirement (BC)...
Yuya Nishihara -
r29946:285a8c3e default
parent child Browse files
Show More
@@ -1,3831 +1,3831
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
381
382 if order == defineorder:
382 if order == defineorder:
383 return r & subset
383 return r & subset
384 else:
384 else:
385 # carrying the sorting over when possible would be more efficient
385 # carrying the sorting over when possible would be more efficient
386 return subset & r
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, takeorder=True)
1836 @predicate('reverse(set)', safe=True, takeorder=True)
1837 def reverse(repo, subset, x, order):
1837 def reverse(repo, subset, x, order):
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 if order == defineorder:
1841 if order == defineorder:
1842 l.reverse()
1842 l.reverse()
1843 return l
1843 return l
1844
1844
1845 @predicate('roots(set)', safe=True)
1845 @predicate('roots(set)', safe=True)
1846 def roots(repo, subset, x):
1846 def roots(repo, subset, x):
1847 """Changesets in set with no parent changeset in set.
1847 """Changesets in set with no parent changeset in set.
1848 """
1848 """
1849 s = getset(repo, fullreposet(repo), x)
1849 s = getset(repo, fullreposet(repo), x)
1850 parents = repo.changelog.parentrevs
1850 parents = repo.changelog.parentrevs
1851 def filter(r):
1851 def filter(r):
1852 for p in parents(r):
1852 for p in parents(r):
1853 if 0 <= p and p in s:
1853 if 0 <= p and p in s:
1854 return False
1854 return False
1855 return True
1855 return True
1856 return subset & s.filter(filter, condrepr='<roots>')
1856 return subset & s.filter(filter, condrepr='<roots>')
1857
1857
1858 _sortkeyfuncs = {
1858 _sortkeyfuncs = {
1859 'rev': lambda c: c.rev(),
1859 'rev': lambda c: c.rev(),
1860 'branch': lambda c: c.branch(),
1860 'branch': lambda c: c.branch(),
1861 'desc': lambda c: c.description(),
1861 'desc': lambda c: c.description(),
1862 'user': lambda c: c.user(),
1862 'user': lambda c: c.user(),
1863 'author': lambda c: c.user(),
1863 'author': lambda c: c.user(),
1864 'date': lambda c: c.date()[0],
1864 'date': lambda c: c.date()[0],
1865 }
1865 }
1866
1866
1867 def _getsortargs(x):
1867 def _getsortargs(x):
1868 """Parse sort options into (set, [(key, reverse)], opts)"""
1868 """Parse sort options into (set, [(key, reverse)], opts)"""
1869 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1869 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1870 if 'set' not in args:
1870 if 'set' not in args:
1871 # i18n: "sort" is a keyword
1871 # i18n: "sort" is a keyword
1872 raise error.ParseError(_('sort requires one or two arguments'))
1872 raise error.ParseError(_('sort requires one or two arguments'))
1873 keys = "rev"
1873 keys = "rev"
1874 if 'keys' in args:
1874 if 'keys' in args:
1875 # i18n: "sort" is a keyword
1875 # i18n: "sort" is a keyword
1876 keys = getstring(args['keys'], _("sort spec must be a string"))
1876 keys = getstring(args['keys'], _("sort spec must be a string"))
1877
1877
1878 keyflags = []
1878 keyflags = []
1879 for k in keys.split():
1879 for k in keys.split():
1880 fk = k
1880 fk = k
1881 reverse = (k[0] == '-')
1881 reverse = (k[0] == '-')
1882 if reverse:
1882 if reverse:
1883 k = k[1:]
1883 k = k[1:]
1884 if k not in _sortkeyfuncs and k != 'topo':
1884 if k not in _sortkeyfuncs and k != 'topo':
1885 raise error.ParseError(_("unknown sort key %r") % fk)
1885 raise error.ParseError(_("unknown sort key %r") % fk)
1886 keyflags.append((k, reverse))
1886 keyflags.append((k, reverse))
1887
1887
1888 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1888 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1889 # i18n: "topo" is a keyword
1889 # i18n: "topo" is a keyword
1890 raise error.ParseError(_('topo sort order cannot be combined '
1890 raise error.ParseError(_('topo sort order cannot be combined '
1891 'with other sort keys'))
1891 'with other sort keys'))
1892
1892
1893 opts = {}
1893 opts = {}
1894 if 'topo.firstbranch' in args:
1894 if 'topo.firstbranch' in args:
1895 if any(k == 'topo' for k, reverse in keyflags):
1895 if any(k == 'topo' for k, reverse in keyflags):
1896 opts['topo.firstbranch'] = args['topo.firstbranch']
1896 opts['topo.firstbranch'] = args['topo.firstbranch']
1897 else:
1897 else:
1898 # i18n: "topo" and "topo.firstbranch" are keywords
1898 # i18n: "topo" and "topo.firstbranch" are keywords
1899 raise error.ParseError(_('topo.firstbranch can only be used '
1899 raise error.ParseError(_('topo.firstbranch can only be used '
1900 'when using the topo sort key'))
1900 'when using the topo sort key'))
1901
1901
1902 return args['set'], keyflags, opts
1902 return args['set'], keyflags, opts
1903
1903
1904 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1904 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1905 def sort(repo, subset, x):
1905 def sort(repo, subset, x, order):
1906 """Sort set by keys. The default sort order is ascending, specify a key
1906 """Sort set by keys. The default sort order is ascending, specify a key
1907 as ``-key`` to sort in descending order.
1907 as ``-key`` to sort in descending order.
1908
1908
1909 The keys can be:
1909 The keys can be:
1910
1910
1911 - ``rev`` for the revision number,
1911 - ``rev`` for the revision number,
1912 - ``branch`` for the branch name,
1912 - ``branch`` for the branch name,
1913 - ``desc`` for the commit message (description),
1913 - ``desc`` for the commit message (description),
1914 - ``user`` for user name (``author`` can be used as an alias),
1914 - ``user`` for user name (``author`` can be used as an alias),
1915 - ``date`` for the commit date
1915 - ``date`` for the commit date
1916 - ``topo`` for a reverse topographical sort
1916 - ``topo`` for a reverse topographical sort
1917
1917
1918 The ``topo`` sort order cannot be combined with other sort keys. This sort
1918 The ``topo`` sort order cannot be combined with other sort keys. This sort
1919 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1919 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1920 specifies what topographical branches to prioritize in the sort.
1920 specifies what topographical branches to prioritize in the sort.
1921
1921
1922 """
1922 """
1923 s, keyflags, opts = _getsortargs(x)
1923 s, keyflags, opts = _getsortargs(x)
1924 revs = getset(repo, subset, s)
1924 revs = getset(repo, subset, s)
1925
1925
1926 if not keyflags:
1926 if not keyflags or order != defineorder:
1927 return revs
1927 return revs
1928 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1928 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1929 revs.sort(reverse=keyflags[0][1])
1929 revs.sort(reverse=keyflags[0][1])
1930 return revs
1930 return revs
1931 elif keyflags[0][0] == "topo":
1931 elif keyflags[0][0] == "topo":
1932 firstbranch = ()
1932 firstbranch = ()
1933 if 'topo.firstbranch' in opts:
1933 if 'topo.firstbranch' in opts:
1934 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1934 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1935 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1935 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1936 istopo=True)
1936 istopo=True)
1937 if keyflags[0][1]:
1937 if keyflags[0][1]:
1938 revs.reverse()
1938 revs.reverse()
1939 return revs
1939 return revs
1940
1940
1941 # sort() is guaranteed to be stable
1941 # sort() is guaranteed to be stable
1942 ctxs = [repo[r] for r in revs]
1942 ctxs = [repo[r] for r in revs]
1943 for k, reverse in reversed(keyflags):
1943 for k, reverse in reversed(keyflags):
1944 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1944 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1945 return baseset([c.rev() for c in ctxs])
1945 return baseset([c.rev() for c in ctxs])
1946
1946
1947 def _toposort(revs, parentsfunc, firstbranch=()):
1947 def _toposort(revs, parentsfunc, firstbranch=()):
1948 """Yield revisions from heads to roots one (topo) branch at a time.
1948 """Yield revisions from heads to roots one (topo) branch at a time.
1949
1949
1950 This function aims to be used by a graph generator that wishes to minimize
1950 This function aims to be used by a graph generator that wishes to minimize
1951 the number of parallel branches and their interleaving.
1951 the number of parallel branches and their interleaving.
1952
1952
1953 Example iteration order (numbers show the "true" order in a changelog):
1953 Example iteration order (numbers show the "true" order in a changelog):
1954
1954
1955 o 4
1955 o 4
1956 |
1956 |
1957 o 1
1957 o 1
1958 |
1958 |
1959 | o 3
1959 | o 3
1960 | |
1960 | |
1961 | o 2
1961 | o 2
1962 |/
1962 |/
1963 o 0
1963 o 0
1964
1964
1965 Note that the ancestors of merges are understood by the current
1965 Note that the ancestors of merges are understood by the current
1966 algorithm to be on the same branch. This means no reordering will
1966 algorithm to be on the same branch. This means no reordering will
1967 occur behind a merge.
1967 occur behind a merge.
1968 """
1968 """
1969
1969
1970 ### Quick summary of the algorithm
1970 ### Quick summary of the algorithm
1971 #
1971 #
1972 # This function is based around a "retention" principle. We keep revisions
1972 # This function is based around a "retention" principle. We keep revisions
1973 # in memory until we are ready to emit a whole branch that immediately
1973 # in memory until we are ready to emit a whole branch that immediately
1974 # "merges" into an existing one. This reduces the number of parallel
1974 # "merges" into an existing one. This reduces the number of parallel
1975 # branches with interleaved revisions.
1975 # branches with interleaved revisions.
1976 #
1976 #
1977 # During iteration revs are split into two groups:
1977 # During iteration revs are split into two groups:
1978 # A) revision already emitted
1978 # A) revision already emitted
1979 # B) revision in "retention". They are stored as different subgroups.
1979 # B) revision in "retention". They are stored as different subgroups.
1980 #
1980 #
1981 # for each REV, we do the following logic:
1981 # for each REV, we do the following logic:
1982 #
1982 #
1983 # 1) if REV is a parent of (A), we will emit it. If there is a
1983 # 1) if REV is a parent of (A), we will emit it. If there is a
1984 # retention group ((B) above) that is blocked on REV being
1984 # retention group ((B) above) that is blocked on REV being
1985 # available, we emit all the revisions out of that retention
1985 # available, we emit all the revisions out of that retention
1986 # group first.
1986 # group first.
1987 #
1987 #
1988 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1988 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1989 # available, if such subgroup exist, we add REV to it and the subgroup is
1989 # available, if such subgroup exist, we add REV to it and the subgroup is
1990 # now awaiting for REV.parents() to be available.
1990 # now awaiting for REV.parents() to be available.
1991 #
1991 #
1992 # 3) finally if no such group existed in (B), we create a new subgroup.
1992 # 3) finally if no such group existed in (B), we create a new subgroup.
1993 #
1993 #
1994 #
1994 #
1995 # To bootstrap the algorithm, we emit the tipmost revision (which
1995 # To bootstrap the algorithm, we emit the tipmost revision (which
1996 # puts it in group (A) from above).
1996 # puts it in group (A) from above).
1997
1997
1998 revs.sort(reverse=True)
1998 revs.sort(reverse=True)
1999
1999
2000 # Set of parents of revision that have been emitted. They can be considered
2000 # Set of parents of revision that have been emitted. They can be considered
2001 # unblocked as the graph generator is already aware of them so there is no
2001 # unblocked as the graph generator is already aware of them so there is no
2002 # need to delay the revisions that reference them.
2002 # need to delay the revisions that reference them.
2003 #
2003 #
2004 # If someone wants to prioritize a branch over the others, pre-filling this
2004 # If someone wants to prioritize a branch over the others, pre-filling this
2005 # set will force all other branches to wait until this branch is ready to be
2005 # set will force all other branches to wait until this branch is ready to be
2006 # emitted.
2006 # emitted.
2007 unblocked = set(firstbranch)
2007 unblocked = set(firstbranch)
2008
2008
2009 # list of groups waiting to be displayed, each group is defined by:
2009 # list of groups waiting to be displayed, each group is defined by:
2010 #
2010 #
2011 # (revs: lists of revs waiting to be displayed,
2011 # (revs: lists of revs waiting to be displayed,
2012 # blocked: set of that cannot be displayed before those in 'revs')
2012 # blocked: set of that cannot be displayed before those in 'revs')
2013 #
2013 #
2014 # The second value ('blocked') correspond to parents of any revision in the
2014 # The second value ('blocked') correspond to parents of any revision in the
2015 # group ('revs') that is not itself contained in the group. The main idea
2015 # group ('revs') that is not itself contained in the group. The main idea
2016 # of this algorithm is to delay as much as possible the emission of any
2016 # of this algorithm is to delay as much as possible the emission of any
2017 # revision. This means waiting for the moment we are about to display
2017 # revision. This means waiting for the moment we are about to display
2018 # these parents to display the revs in a group.
2018 # these parents to display the revs in a group.
2019 #
2019 #
2020 # This first implementation is smart until it encounters a merge: it will
2020 # This first implementation is smart until it encounters a merge: it will
2021 # emit revs as soon as any parent is about to be emitted and can grow an
2021 # emit revs as soon as any parent is about to be emitted and can grow an
2022 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2022 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2023 # retains new branches but gives up on any special ordering for ancestors
2023 # retains new branches but gives up on any special ordering for ancestors
2024 # of merges. The implementation can be improved to handle this better.
2024 # of merges. The implementation can be improved to handle this better.
2025 #
2025 #
2026 # The first subgroup is special. It corresponds to all the revision that
2026 # The first subgroup is special. It corresponds to all the revision that
2027 # were already emitted. The 'revs' lists is expected to be empty and the
2027 # were already emitted. The 'revs' lists is expected to be empty and the
2028 # 'blocked' set contains the parents revisions of already emitted revision.
2028 # 'blocked' set contains the parents revisions of already emitted revision.
2029 #
2029 #
2030 # You could pre-seed the <parents> set of groups[0] to a specific
2030 # You could pre-seed the <parents> set of groups[0] to a specific
2031 # changesets to select what the first emitted branch should be.
2031 # changesets to select what the first emitted branch should be.
2032 groups = [([], unblocked)]
2032 groups = [([], unblocked)]
2033 pendingheap = []
2033 pendingheap = []
2034 pendingset = set()
2034 pendingset = set()
2035
2035
2036 heapq.heapify(pendingheap)
2036 heapq.heapify(pendingheap)
2037 heappop = heapq.heappop
2037 heappop = heapq.heappop
2038 heappush = heapq.heappush
2038 heappush = heapq.heappush
2039 for currentrev in revs:
2039 for currentrev in revs:
2040 # Heap works with smallest element, we want highest so we invert
2040 # Heap works with smallest element, we want highest so we invert
2041 if currentrev not in pendingset:
2041 if currentrev not in pendingset:
2042 heappush(pendingheap, -currentrev)
2042 heappush(pendingheap, -currentrev)
2043 pendingset.add(currentrev)
2043 pendingset.add(currentrev)
2044 # iterates on pending rev until after the current rev have been
2044 # iterates on pending rev until after the current rev have been
2045 # processed.
2045 # processed.
2046 rev = None
2046 rev = None
2047 while rev != currentrev:
2047 while rev != currentrev:
2048 rev = -heappop(pendingheap)
2048 rev = -heappop(pendingheap)
2049 pendingset.remove(rev)
2049 pendingset.remove(rev)
2050
2050
2051 # Seek for a subgroup blocked, waiting for the current revision.
2051 # Seek for a subgroup blocked, waiting for the current revision.
2052 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2052 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2053
2053
2054 if matching:
2054 if matching:
2055 # The main idea is to gather together all sets that are blocked
2055 # The main idea is to gather together all sets that are blocked
2056 # on the same revision.
2056 # on the same revision.
2057 #
2057 #
2058 # Groups are merged when a common blocking ancestor is
2058 # Groups are merged when a common blocking ancestor is
2059 # observed. For example, given two groups:
2059 # observed. For example, given two groups:
2060 #
2060 #
2061 # revs [5, 4] waiting for 1
2061 # revs [5, 4] waiting for 1
2062 # revs [3, 2] waiting for 1
2062 # revs [3, 2] waiting for 1
2063 #
2063 #
2064 # These two groups will be merged when we process
2064 # These two groups will be merged when we process
2065 # 1. In theory, we could have merged the groups when
2065 # 1. In theory, we could have merged the groups when
2066 # we added 2 to the group it is now in (we could have
2066 # we added 2 to the group it is now in (we could have
2067 # noticed the groups were both blocked on 1 then), but
2067 # noticed the groups were both blocked on 1 then), but
2068 # the way it works now makes the algorithm simpler.
2068 # the way it works now makes the algorithm simpler.
2069 #
2069 #
2070 # We also always keep the oldest subgroup first. We can
2070 # We also always keep the oldest subgroup first. We can
2071 # probably improve the behavior by having the longest set
2071 # probably improve the behavior by having the longest set
2072 # first. That way, graph algorithms could minimise the length
2072 # first. That way, graph algorithms could minimise the length
2073 # of parallel lines their drawing. This is currently not done.
2073 # of parallel lines their drawing. This is currently not done.
2074 targetidx = matching.pop(0)
2074 targetidx = matching.pop(0)
2075 trevs, tparents = groups[targetidx]
2075 trevs, tparents = groups[targetidx]
2076 for i in matching:
2076 for i in matching:
2077 gr = groups[i]
2077 gr = groups[i]
2078 trevs.extend(gr[0])
2078 trevs.extend(gr[0])
2079 tparents |= gr[1]
2079 tparents |= gr[1]
2080 # delete all merged subgroups (except the one we kept)
2080 # delete all merged subgroups (except the one we kept)
2081 # (starting from the last subgroup for performance and
2081 # (starting from the last subgroup for performance and
2082 # sanity reasons)
2082 # sanity reasons)
2083 for i in reversed(matching):
2083 for i in reversed(matching):
2084 del groups[i]
2084 del groups[i]
2085 else:
2085 else:
2086 # This is a new head. We create a new subgroup for it.
2086 # This is a new head. We create a new subgroup for it.
2087 targetidx = len(groups)
2087 targetidx = len(groups)
2088 groups.append(([], set([rev])))
2088 groups.append(([], set([rev])))
2089
2089
2090 gr = groups[targetidx]
2090 gr = groups[targetidx]
2091
2091
2092 # We now add the current nodes to this subgroups. This is done
2092 # We now add the current nodes to this subgroups. This is done
2093 # after the subgroup merging because all elements from a subgroup
2093 # after the subgroup merging because all elements from a subgroup
2094 # that relied on this rev must precede it.
2094 # that relied on this rev must precede it.
2095 #
2095 #
2096 # we also update the <parents> set to include the parents of the
2096 # we also update the <parents> set to include the parents of the
2097 # new nodes.
2097 # new nodes.
2098 if rev == currentrev: # only display stuff in rev
2098 if rev == currentrev: # only display stuff in rev
2099 gr[0].append(rev)
2099 gr[0].append(rev)
2100 gr[1].remove(rev)
2100 gr[1].remove(rev)
2101 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2101 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2102 gr[1].update(parents)
2102 gr[1].update(parents)
2103 for p in parents:
2103 for p in parents:
2104 if p not in pendingset:
2104 if p not in pendingset:
2105 pendingset.add(p)
2105 pendingset.add(p)
2106 heappush(pendingheap, -p)
2106 heappush(pendingheap, -p)
2107
2107
2108 # Look for a subgroup to display
2108 # Look for a subgroup to display
2109 #
2109 #
2110 # When unblocked is empty (if clause), we were not waiting for any
2110 # When unblocked is empty (if clause), we were not waiting for any
2111 # revisions during the first iteration (if no priority was given) or
2111 # revisions during the first iteration (if no priority was given) or
2112 # if we emitted a whole disconnected set of the graph (reached a
2112 # if we emitted a whole disconnected set of the graph (reached a
2113 # root). In that case we arbitrarily take the oldest known
2113 # root). In that case we arbitrarily take the oldest known
2114 # subgroup. The heuristic could probably be better.
2114 # subgroup. The heuristic could probably be better.
2115 #
2115 #
2116 # Otherwise (elif clause) if the subgroup is blocked on
2116 # Otherwise (elif clause) if the subgroup is blocked on
2117 # a revision we just emitted, we can safely emit it as
2117 # a revision we just emitted, we can safely emit it as
2118 # well.
2118 # well.
2119 if not unblocked:
2119 if not unblocked:
2120 if len(groups) > 1: # display other subset
2120 if len(groups) > 1: # display other subset
2121 targetidx = 1
2121 targetidx = 1
2122 gr = groups[1]
2122 gr = groups[1]
2123 elif not gr[1] & unblocked:
2123 elif not gr[1] & unblocked:
2124 gr = None
2124 gr = None
2125
2125
2126 if gr is not None:
2126 if gr is not None:
2127 # update the set of awaited revisions with the one from the
2127 # update the set of awaited revisions with the one from the
2128 # subgroup
2128 # subgroup
2129 unblocked |= gr[1]
2129 unblocked |= gr[1]
2130 # output all revisions in the subgroup
2130 # output all revisions in the subgroup
2131 for r in gr[0]:
2131 for r in gr[0]:
2132 yield r
2132 yield r
2133 # delete the subgroup that you just output
2133 # delete the subgroup that you just output
2134 # unless it is groups[0] in which case you just empty it.
2134 # unless it is groups[0] in which case you just empty it.
2135 if targetidx:
2135 if targetidx:
2136 del groups[targetidx]
2136 del groups[targetidx]
2137 else:
2137 else:
2138 gr[0][:] = []
2138 gr[0][:] = []
2139 # Check if we have some subgroup waiting for revisions we are not going to
2139 # Check if we have some subgroup waiting for revisions we are not going to
2140 # iterate over
2140 # iterate over
2141 for g in groups:
2141 for g in groups:
2142 for r in g[0]:
2142 for r in g[0]:
2143 yield r
2143 yield r
2144
2144
2145 @predicate('subrepo([pattern])')
2145 @predicate('subrepo([pattern])')
2146 def subrepo(repo, subset, x):
2146 def subrepo(repo, subset, x):
2147 """Changesets that add, modify or remove the given subrepo. If no subrepo
2147 """Changesets that add, modify or remove the given subrepo. If no subrepo
2148 pattern is named, any subrepo changes are returned.
2148 pattern is named, any subrepo changes are returned.
2149 """
2149 """
2150 # i18n: "subrepo" is a keyword
2150 # i18n: "subrepo" is a keyword
2151 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2151 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2152 pat = None
2152 pat = None
2153 if len(args) != 0:
2153 if len(args) != 0:
2154 pat = getstring(args[0], _("subrepo requires a pattern"))
2154 pat = getstring(args[0], _("subrepo requires a pattern"))
2155
2155
2156 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2156 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2157
2157
2158 def submatches(names):
2158 def submatches(names):
2159 k, p, m = util.stringmatcher(pat)
2159 k, p, m = util.stringmatcher(pat)
2160 for name in names:
2160 for name in names:
2161 if m(name):
2161 if m(name):
2162 yield name
2162 yield name
2163
2163
2164 def matches(x):
2164 def matches(x):
2165 c = repo[x]
2165 c = repo[x]
2166 s = repo.status(c.p1().node(), c.node(), match=m)
2166 s = repo.status(c.p1().node(), c.node(), match=m)
2167
2167
2168 if pat is None:
2168 if pat is None:
2169 return s.added or s.modified or s.removed
2169 return s.added or s.modified or s.removed
2170
2170
2171 if s.added:
2171 if s.added:
2172 return any(submatches(c.substate.keys()))
2172 return any(submatches(c.substate.keys()))
2173
2173
2174 if s.modified:
2174 if s.modified:
2175 subs = set(c.p1().substate.keys())
2175 subs = set(c.p1().substate.keys())
2176 subs.update(c.substate.keys())
2176 subs.update(c.substate.keys())
2177
2177
2178 for path in submatches(subs):
2178 for path in submatches(subs):
2179 if c.p1().substate.get(path) != c.substate.get(path):
2179 if c.p1().substate.get(path) != c.substate.get(path):
2180 return True
2180 return True
2181
2181
2182 if s.removed:
2182 if s.removed:
2183 return any(submatches(c.p1().substate.keys()))
2183 return any(submatches(c.p1().substate.keys()))
2184
2184
2185 return False
2185 return False
2186
2186
2187 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2187 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2188
2188
2189 def _substringmatcher(pattern):
2189 def _substringmatcher(pattern):
2190 kind, pattern, matcher = util.stringmatcher(pattern)
2190 kind, pattern, matcher = util.stringmatcher(pattern)
2191 if kind == 'literal':
2191 if kind == 'literal':
2192 matcher = lambda s: pattern in s
2192 matcher = lambda s: pattern in s
2193 return kind, pattern, matcher
2193 return kind, pattern, matcher
2194
2194
2195 @predicate('tag([name])', safe=True)
2195 @predicate('tag([name])', safe=True)
2196 def tag(repo, subset, x):
2196 def tag(repo, subset, x):
2197 """The specified tag by name, or all tagged revisions if no name is given.
2197 """The specified tag by name, or all tagged revisions if no name is given.
2198
2198
2199 If `name` starts with `re:`, the remainder of the name is treated as
2199 If `name` starts with `re:`, the remainder of the name is treated as
2200 a regular expression. To match a tag that actually starts with `re:`,
2200 a regular expression. To match a tag that actually starts with `re:`,
2201 use the prefix `literal:`.
2201 use the prefix `literal:`.
2202 """
2202 """
2203 # i18n: "tag" is a keyword
2203 # i18n: "tag" is a keyword
2204 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2204 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2205 cl = repo.changelog
2205 cl = repo.changelog
2206 if args:
2206 if args:
2207 pattern = getstring(args[0],
2207 pattern = getstring(args[0],
2208 # i18n: "tag" is a keyword
2208 # i18n: "tag" is a keyword
2209 _('the argument to tag must be a string'))
2209 _('the argument to tag must be a string'))
2210 kind, pattern, matcher = util.stringmatcher(pattern)
2210 kind, pattern, matcher = util.stringmatcher(pattern)
2211 if kind == 'literal':
2211 if kind == 'literal':
2212 # avoid resolving all tags
2212 # avoid resolving all tags
2213 tn = repo._tagscache.tags.get(pattern, None)
2213 tn = repo._tagscache.tags.get(pattern, None)
2214 if tn is None:
2214 if tn is None:
2215 raise error.RepoLookupError(_("tag '%s' does not exist")
2215 raise error.RepoLookupError(_("tag '%s' does not exist")
2216 % pattern)
2216 % pattern)
2217 s = set([repo[tn].rev()])
2217 s = set([repo[tn].rev()])
2218 else:
2218 else:
2219 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2219 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2220 else:
2220 else:
2221 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2221 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2222 return subset & s
2222 return subset & s
2223
2223
2224 @predicate('tagged', safe=True)
2224 @predicate('tagged', safe=True)
2225 def tagged(repo, subset, x):
2225 def tagged(repo, subset, x):
2226 return tag(repo, subset, x)
2226 return tag(repo, subset, x)
2227
2227
2228 @predicate('unstable()', safe=True)
2228 @predicate('unstable()', safe=True)
2229 def unstable(repo, subset, x):
2229 def unstable(repo, subset, x):
2230 """Non-obsolete changesets with obsolete ancestors.
2230 """Non-obsolete changesets with obsolete ancestors.
2231 """
2231 """
2232 # i18n: "unstable" is a keyword
2232 # i18n: "unstable" is a keyword
2233 getargs(x, 0, 0, _("unstable takes no arguments"))
2233 getargs(x, 0, 0, _("unstable takes no arguments"))
2234 unstables = obsmod.getrevs(repo, 'unstable')
2234 unstables = obsmod.getrevs(repo, 'unstable')
2235 return subset & unstables
2235 return subset & unstables
2236
2236
2237
2237
2238 @predicate('user(string)', safe=True)
2238 @predicate('user(string)', safe=True)
2239 def user(repo, subset, x):
2239 def user(repo, subset, x):
2240 """User name contains string. The match is case-insensitive.
2240 """User name contains string. The match is case-insensitive.
2241
2241
2242 If `string` starts with `re:`, the remainder of the string is treated as
2242 If `string` starts with `re:`, the remainder of the string is treated as
2243 a regular expression. To match a user that actually contains `re:`, use
2243 a regular expression. To match a user that actually contains `re:`, use
2244 the prefix `literal:`.
2244 the prefix `literal:`.
2245 """
2245 """
2246 return author(repo, subset, x)
2246 return author(repo, subset, x)
2247
2247
2248 # experimental
2248 # experimental
2249 @predicate('wdir', safe=True)
2249 @predicate('wdir', safe=True)
2250 def wdir(repo, subset, x):
2250 def wdir(repo, subset, x):
2251 # i18n: "wdir" is a keyword
2251 # i18n: "wdir" is a keyword
2252 getargs(x, 0, 0, _("wdir takes no arguments"))
2252 getargs(x, 0, 0, _("wdir takes no arguments"))
2253 if node.wdirrev in subset or isinstance(subset, fullreposet):
2253 if node.wdirrev in subset or isinstance(subset, fullreposet):
2254 return baseset([node.wdirrev])
2254 return baseset([node.wdirrev])
2255 return baseset()
2255 return baseset()
2256
2256
2257 def _orderedlist(repo, subset, x):
2257 def _orderedlist(repo, subset, x):
2258 s = getstring(x, "internal error")
2258 s = getstring(x, "internal error")
2259 if not s:
2259 if not s:
2260 return baseset()
2260 return baseset()
2261 # remove duplicates here. it's difficult for caller to deduplicate sets
2261 # remove duplicates here. it's difficult for caller to deduplicate sets
2262 # because different symbols can point to the same rev.
2262 # because different symbols can point to the same rev.
2263 cl = repo.changelog
2263 cl = repo.changelog
2264 ls = []
2264 ls = []
2265 seen = set()
2265 seen = set()
2266 for t in s.split('\0'):
2266 for t in s.split('\0'):
2267 try:
2267 try:
2268 # fast path for integer revision
2268 # fast path for integer revision
2269 r = int(t)
2269 r = int(t)
2270 if str(r) != t or r not in cl:
2270 if str(r) != t or r not in cl:
2271 raise ValueError
2271 raise ValueError
2272 revs = [r]
2272 revs = [r]
2273 except ValueError:
2273 except ValueError:
2274 revs = stringset(repo, subset, t)
2274 revs = stringset(repo, subset, t)
2275
2275
2276 for r in revs:
2276 for r in revs:
2277 if r in seen:
2277 if r in seen:
2278 continue
2278 continue
2279 if (r in subset
2279 if (r in subset
2280 or r == node.nullrev and isinstance(subset, fullreposet)):
2280 or r == node.nullrev and isinstance(subset, fullreposet)):
2281 ls.append(r)
2281 ls.append(r)
2282 seen.add(r)
2282 seen.add(r)
2283 return baseset(ls)
2283 return baseset(ls)
2284
2284
2285 # for internal use
2285 # for internal use
2286 @predicate('_list', safe=True, takeorder=True)
2286 @predicate('_list', safe=True, takeorder=True)
2287 def _list(repo, subset, x, order):
2287 def _list(repo, subset, x, order):
2288 if order == followorder:
2288 if order == followorder:
2289 # slow path to take the subset order
2289 # slow path to take the subset order
2290 return subset & _orderedlist(repo, fullreposet(repo), x)
2290 return subset & _orderedlist(repo, fullreposet(repo), x)
2291 else:
2291 else:
2292 return _orderedlist(repo, subset, x)
2292 return _orderedlist(repo, subset, x)
2293
2293
2294 def _orderedintlist(repo, subset, x):
2294 def _orderedintlist(repo, subset, x):
2295 s = getstring(x, "internal error")
2295 s = getstring(x, "internal error")
2296 if not s:
2296 if not s:
2297 return baseset()
2297 return baseset()
2298 ls = [int(r) for r in s.split('\0')]
2298 ls = [int(r) for r in s.split('\0')]
2299 s = subset
2299 s = subset
2300 return baseset([r for r in ls if r in s])
2300 return baseset([r for r in ls if r in s])
2301
2301
2302 # for internal use
2302 # for internal use
2303 @predicate('_intlist', safe=True, takeorder=True)
2303 @predicate('_intlist', safe=True, takeorder=True)
2304 def _intlist(repo, subset, x, order):
2304 def _intlist(repo, subset, x, order):
2305 if order == followorder:
2305 if order == followorder:
2306 # slow path to take the subset order
2306 # slow path to take the subset order
2307 return subset & _orderedintlist(repo, fullreposet(repo), x)
2307 return subset & _orderedintlist(repo, fullreposet(repo), x)
2308 else:
2308 else:
2309 return _orderedintlist(repo, subset, x)
2309 return _orderedintlist(repo, subset, x)
2310
2310
2311 def _orderedhexlist(repo, subset, x):
2311 def _orderedhexlist(repo, subset, x):
2312 s = getstring(x, "internal error")
2312 s = getstring(x, "internal error")
2313 if not s:
2313 if not s:
2314 return baseset()
2314 return baseset()
2315 cl = repo.changelog
2315 cl = repo.changelog
2316 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2316 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2317 s = subset
2317 s = subset
2318 return baseset([r for r in ls if r in s])
2318 return baseset([r for r in ls if r in s])
2319
2319
2320 # for internal use
2320 # for internal use
2321 @predicate('_hexlist', safe=True, takeorder=True)
2321 @predicate('_hexlist', safe=True, takeorder=True)
2322 def _hexlist(repo, subset, x, order):
2322 def _hexlist(repo, subset, x, order):
2323 if order == followorder:
2323 if order == followorder:
2324 # slow path to take the subset order
2324 # slow path to take the subset order
2325 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2325 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2326 else:
2326 else:
2327 return _orderedhexlist(repo, subset, x)
2327 return _orderedhexlist(repo, subset, x)
2328
2328
2329 methods = {
2329 methods = {
2330 "range": rangeset,
2330 "range": rangeset,
2331 "dagrange": dagrange,
2331 "dagrange": dagrange,
2332 "string": stringset,
2332 "string": stringset,
2333 "symbol": stringset,
2333 "symbol": stringset,
2334 "and": andset,
2334 "and": andset,
2335 "or": orset,
2335 "or": orset,
2336 "not": notset,
2336 "not": notset,
2337 "difference": differenceset,
2337 "difference": differenceset,
2338 "list": listset,
2338 "list": listset,
2339 "keyvalue": keyvaluepair,
2339 "keyvalue": keyvaluepair,
2340 "func": func,
2340 "func": func,
2341 "ancestor": ancestorspec,
2341 "ancestor": ancestorspec,
2342 "parent": parentspec,
2342 "parent": parentspec,
2343 "parentpost": parentpost,
2343 "parentpost": parentpost,
2344 }
2344 }
2345
2345
2346 # Constants for ordering requirement, used in _analyze():
2346 # Constants for ordering requirement, used in _analyze():
2347 #
2347 #
2348 # If 'define', any nested functions and operations can change the ordering of
2348 # If 'define', any nested functions and operations can change the ordering of
2349 # the entries in the set. If 'follow', any nested functions and operations
2349 # the entries in the set. If 'follow', any nested functions and operations
2350 # should take the ordering specified by the first operand to the '&' operator.
2350 # should take the ordering specified by the first operand to the '&' operator.
2351 #
2351 #
2352 # For instance,
2352 # For instance,
2353 #
2353 #
2354 # X & (Y | Z)
2354 # X & (Y | Z)
2355 # ^ ^^^^^^^
2355 # ^ ^^^^^^^
2356 # | follow
2356 # | follow
2357 # define
2357 # define
2358 #
2358 #
2359 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2359 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2360 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2360 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2361 #
2361 #
2362 # 'any' means the order doesn't matter. For instance,
2362 # 'any' means the order doesn't matter. For instance,
2363 #
2363 #
2364 # X & !Y
2364 # X & !Y
2365 # ^
2365 # ^
2366 # any
2366 # any
2367 #
2367 #
2368 # 'y()' can either enforce its ordering requirement or take the ordering
2368 # 'y()' can either enforce its ordering requirement or take the ordering
2369 # specified by 'x()' because 'not()' doesn't care the order.
2369 # specified by 'x()' because 'not()' doesn't care the order.
2370 #
2370 #
2371 # Transition of ordering requirement:
2371 # Transition of ordering requirement:
2372 #
2372 #
2373 # 1. starts with 'define'
2373 # 1. starts with 'define'
2374 # 2. shifts to 'follow' by 'x & y'
2374 # 2. shifts to 'follow' by 'x & y'
2375 # 3. changes back to 'define' on function call 'f(x)' or function-like
2375 # 3. changes back to 'define' on function call 'f(x)' or function-like
2376 # operation 'x (f) y' because 'f' may have its own ordering requirement
2376 # operation 'x (f) y' because 'f' may have its own ordering requirement
2377 # for 'x' and 'y' (e.g. 'first(x)')
2377 # for 'x' and 'y' (e.g. 'first(x)')
2378 #
2378 #
2379 anyorder = 'any' # don't care the order
2379 anyorder = 'any' # don't care the order
2380 defineorder = 'define' # should define the order
2380 defineorder = 'define' # should define the order
2381 followorder = 'follow' # must follow the current order
2381 followorder = 'follow' # must follow the current order
2382
2382
2383 # transition table for 'x & y', from the current expression 'x' to 'y'
2383 # transition table for 'x & y', from the current expression 'x' to 'y'
2384 _tofolloworder = {
2384 _tofolloworder = {
2385 anyorder: anyorder,
2385 anyorder: anyorder,
2386 defineorder: followorder,
2386 defineorder: followorder,
2387 followorder: followorder,
2387 followorder: followorder,
2388 }
2388 }
2389
2389
2390 def _matchonly(revs, bases):
2390 def _matchonly(revs, bases):
2391 """
2391 """
2392 >>> f = lambda *args: _matchonly(*map(parse, args))
2392 >>> f = lambda *args: _matchonly(*map(parse, args))
2393 >>> f('ancestors(A)', 'not ancestors(B)')
2393 >>> f('ancestors(A)', 'not ancestors(B)')
2394 ('list', ('symbol', 'A'), ('symbol', 'B'))
2394 ('list', ('symbol', 'A'), ('symbol', 'B'))
2395 """
2395 """
2396 if (revs is not None
2396 if (revs is not None
2397 and revs[0] == 'func'
2397 and revs[0] == 'func'
2398 and getsymbol(revs[1]) == 'ancestors'
2398 and getsymbol(revs[1]) == 'ancestors'
2399 and bases is not None
2399 and bases is not None
2400 and bases[0] == 'not'
2400 and bases[0] == 'not'
2401 and bases[1][0] == 'func'
2401 and bases[1][0] == 'func'
2402 and getsymbol(bases[1][1]) == 'ancestors'):
2402 and getsymbol(bases[1][1]) == 'ancestors'):
2403 return ('list', revs[2], bases[1][2])
2403 return ('list', revs[2], bases[1][2])
2404
2404
2405 def _fixops(x):
2405 def _fixops(x):
2406 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2406 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2407 handled well by our simple top-down parser"""
2407 handled well by our simple top-down parser"""
2408 if not isinstance(x, tuple):
2408 if not isinstance(x, tuple):
2409 return x
2409 return x
2410
2410
2411 op = x[0]
2411 op = x[0]
2412 if op == 'parent':
2412 if op == 'parent':
2413 # x^:y means (x^) : y, not x ^ (:y)
2413 # x^:y means (x^) : y, not x ^ (:y)
2414 # x^: means (x^) :, not x ^ (:)
2414 # x^: means (x^) :, not x ^ (:)
2415 post = ('parentpost', x[1])
2415 post = ('parentpost', x[1])
2416 if x[2][0] == 'dagrangepre':
2416 if x[2][0] == 'dagrangepre':
2417 return _fixops(('dagrange', post, x[2][1]))
2417 return _fixops(('dagrange', post, x[2][1]))
2418 elif x[2][0] == 'rangepre':
2418 elif x[2][0] == 'rangepre':
2419 return _fixops(('range', post, x[2][1]))
2419 return _fixops(('range', post, x[2][1]))
2420 elif x[2][0] == 'rangeall':
2420 elif x[2][0] == 'rangeall':
2421 return _fixops(('rangepost', post))
2421 return _fixops(('rangepost', post))
2422 elif op == 'or':
2422 elif op == 'or':
2423 # make number of arguments deterministic:
2423 # make number of arguments deterministic:
2424 # x + y + z -> (or x y z) -> (or (list x y z))
2424 # x + y + z -> (or x y z) -> (or (list x y z))
2425 return (op, _fixops(('list',) + x[1:]))
2425 return (op, _fixops(('list',) + x[1:]))
2426
2426
2427 return (op,) + tuple(_fixops(y) for y in x[1:])
2427 return (op,) + tuple(_fixops(y) for y in x[1:])
2428
2428
2429 def _analyze(x, order):
2429 def _analyze(x, order):
2430 if x is None:
2430 if x is None:
2431 return x
2431 return x
2432
2432
2433 op = x[0]
2433 op = x[0]
2434 if op == 'minus':
2434 if op == 'minus':
2435 return _analyze(('and', x[1], ('not', x[2])), order)
2435 return _analyze(('and', x[1], ('not', x[2])), order)
2436 elif op == 'only':
2436 elif op == 'only':
2437 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2437 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2438 return _analyze(t, order)
2438 return _analyze(t, order)
2439 elif op == 'onlypost':
2439 elif op == 'onlypost':
2440 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2440 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2441 elif op == 'dagrangepre':
2441 elif op == 'dagrangepre':
2442 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2442 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2443 elif op == 'dagrangepost':
2443 elif op == 'dagrangepost':
2444 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2444 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2445 elif op == 'rangeall':
2445 elif op == 'rangeall':
2446 return _analyze(('range', ('string', '0'), ('string', 'tip')), order)
2446 return _analyze(('range', ('string', '0'), ('string', 'tip')), order)
2447 elif op == 'rangepre':
2447 elif op == 'rangepre':
2448 return _analyze(('range', ('string', '0'), x[1]), order)
2448 return _analyze(('range', ('string', '0'), x[1]), order)
2449 elif op == 'rangepost':
2449 elif op == 'rangepost':
2450 return _analyze(('range', x[1], ('string', 'tip')), order)
2450 return _analyze(('range', x[1], ('string', 'tip')), order)
2451 elif op == 'negate':
2451 elif op == 'negate':
2452 s = getstring(x[1], _("can't negate that"))
2452 s = getstring(x[1], _("can't negate that"))
2453 return _analyze(('string', '-' + s), order)
2453 return _analyze(('string', '-' + s), order)
2454 elif op in ('string', 'symbol'):
2454 elif op in ('string', 'symbol'):
2455 return x
2455 return x
2456 elif op == 'and':
2456 elif op == 'and':
2457 ta = _analyze(x[1], order)
2457 ta = _analyze(x[1], order)
2458 tb = _analyze(x[2], _tofolloworder[order])
2458 tb = _analyze(x[2], _tofolloworder[order])
2459 return (op, ta, tb, order)
2459 return (op, ta, tb, order)
2460 elif op == 'or':
2460 elif op == 'or':
2461 return (op, _analyze(x[1], order), order)
2461 return (op, _analyze(x[1], order), order)
2462 elif op == 'not':
2462 elif op == 'not':
2463 return (op, _analyze(x[1], anyorder), order)
2463 return (op, _analyze(x[1], anyorder), order)
2464 elif op == 'parentpost':
2464 elif op == 'parentpost':
2465 return (op, _analyze(x[1], defineorder), order)
2465 return (op, _analyze(x[1], defineorder), order)
2466 elif op == 'group':
2466 elif op == 'group':
2467 return _analyze(x[1], order)
2467 return _analyze(x[1], order)
2468 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2468 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2469 ta = _analyze(x[1], defineorder)
2469 ta = _analyze(x[1], defineorder)
2470 tb = _analyze(x[2], defineorder)
2470 tb = _analyze(x[2], defineorder)
2471 return (op, ta, tb, order)
2471 return (op, ta, tb, order)
2472 elif op == 'list':
2472 elif op == 'list':
2473 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2473 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2474 elif op == 'keyvalue':
2474 elif op == 'keyvalue':
2475 return (op, x[1], _analyze(x[2], order))
2475 return (op, x[1], _analyze(x[2], order))
2476 elif op == 'func':
2476 elif op == 'func':
2477 f = getsymbol(x[1])
2477 f = getsymbol(x[1])
2478 d = defineorder
2478 d = defineorder
2479 if f == 'present':
2479 if f == 'present':
2480 # 'present(set)' is known to return the argument set with no
2480 # 'present(set)' is known to return the argument set with no
2481 # modification, so forward the current order to its argument
2481 # modification, so forward the current order to its argument
2482 d = order
2482 d = order
2483 return (op, x[1], _analyze(x[2], d), order)
2483 return (op, x[1], _analyze(x[2], d), order)
2484 raise ValueError('invalid operator %r' % op)
2484 raise ValueError('invalid operator %r' % op)
2485
2485
2486 def analyze(x, order=defineorder):
2486 def analyze(x, order=defineorder):
2487 """Transform raw parsed tree to evaluatable tree which can be fed to
2487 """Transform raw parsed tree to evaluatable tree which can be fed to
2488 optimize() or getset()
2488 optimize() or getset()
2489
2489
2490 All pseudo operations should be mapped to real operations or functions
2490 All pseudo operations should be mapped to real operations or functions
2491 defined in methods or symbols table respectively.
2491 defined in methods or symbols table respectively.
2492
2492
2493 'order' specifies how the current expression 'x' is ordered (see the
2493 'order' specifies how the current expression 'x' is ordered (see the
2494 constants defined above.)
2494 constants defined above.)
2495 """
2495 """
2496 return _analyze(x, order)
2496 return _analyze(x, order)
2497
2497
2498 def _optimize(x, small):
2498 def _optimize(x, small):
2499 if x is None:
2499 if x is None:
2500 return 0, x
2500 return 0, x
2501
2501
2502 smallbonus = 1
2502 smallbonus = 1
2503 if small:
2503 if small:
2504 smallbonus = .5
2504 smallbonus = .5
2505
2505
2506 op = x[0]
2506 op = x[0]
2507 if op in ('string', 'symbol'):
2507 if op in ('string', 'symbol'):
2508 return smallbonus, x # single revisions are small
2508 return smallbonus, x # single revisions are small
2509 elif op == 'and':
2509 elif op == 'and':
2510 wa, ta = _optimize(x[1], True)
2510 wa, ta = _optimize(x[1], True)
2511 wb, tb = _optimize(x[2], True)
2511 wb, tb = _optimize(x[2], True)
2512 order = x[3]
2512 order = x[3]
2513 w = min(wa, wb)
2513 w = min(wa, wb)
2514
2514
2515 # (::x and not ::y)/(not ::y and ::x) have a fast path
2515 # (::x and not ::y)/(not ::y and ::x) have a fast path
2516 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2516 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2517 if tm:
2517 if tm:
2518 return w, ('func', ('symbol', 'only'), tm, order)
2518 return w, ('func', ('symbol', 'only'), tm, order)
2519
2519
2520 if tb is not None and tb[0] == 'not':
2520 if tb is not None and tb[0] == 'not':
2521 return wa, ('difference', ta, tb[1], order)
2521 return wa, ('difference', ta, tb[1], order)
2522
2522
2523 if wa > wb:
2523 if wa > wb:
2524 return w, (op, tb, ta, order)
2524 return w, (op, tb, ta, order)
2525 return w, (op, ta, tb, order)
2525 return w, (op, ta, tb, order)
2526 elif op == 'or':
2526 elif op == 'or':
2527 # fast path for machine-generated expression, that is likely to have
2527 # fast path for machine-generated expression, that is likely to have
2528 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2528 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2529 order = x[2]
2529 order = x[2]
2530 ws, ts, ss = [], [], []
2530 ws, ts, ss = [], [], []
2531 def flushss():
2531 def flushss():
2532 if not ss:
2532 if not ss:
2533 return
2533 return
2534 if len(ss) == 1:
2534 if len(ss) == 1:
2535 w, t = ss[0]
2535 w, t = ss[0]
2536 else:
2536 else:
2537 s = '\0'.join(t[1] for w, t in ss)
2537 s = '\0'.join(t[1] for w, t in ss)
2538 y = ('func', ('symbol', '_list'), ('string', s), order)
2538 y = ('func', ('symbol', '_list'), ('string', s), order)
2539 w, t = _optimize(y, False)
2539 w, t = _optimize(y, False)
2540 ws.append(w)
2540 ws.append(w)
2541 ts.append(t)
2541 ts.append(t)
2542 del ss[:]
2542 del ss[:]
2543 for y in getlist(x[1]):
2543 for y in getlist(x[1]):
2544 w, t = _optimize(y, False)
2544 w, t = _optimize(y, False)
2545 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2545 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2546 ss.append((w, t))
2546 ss.append((w, t))
2547 continue
2547 continue
2548 flushss()
2548 flushss()
2549 ws.append(w)
2549 ws.append(w)
2550 ts.append(t)
2550 ts.append(t)
2551 flushss()
2551 flushss()
2552 if len(ts) == 1:
2552 if len(ts) == 1:
2553 return ws[0], ts[0] # 'or' operation is fully optimized out
2553 return ws[0], ts[0] # 'or' operation is fully optimized out
2554 # we can't reorder trees by weight because it would change the order.
2554 # we can't reorder trees by weight because it would change the order.
2555 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2555 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2556 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2556 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2557 return max(ws), (op, ('list',) + tuple(ts), order)
2557 return max(ws), (op, ('list',) + tuple(ts), order)
2558 elif op == 'not':
2558 elif op == 'not':
2559 # Optimize not public() to _notpublic() because we have a fast version
2559 # Optimize not public() to _notpublic() because we have a fast version
2560 if x[1][:3] == ('func', ('symbol', 'public'), None):
2560 if x[1][:3] == ('func', ('symbol', 'public'), None):
2561 order = x[1][3]
2561 order = x[1][3]
2562 newsym = ('func', ('symbol', '_notpublic'), None, order)
2562 newsym = ('func', ('symbol', '_notpublic'), None, order)
2563 o = _optimize(newsym, not small)
2563 o = _optimize(newsym, not small)
2564 return o[0], o[1]
2564 return o[0], o[1]
2565 else:
2565 else:
2566 o = _optimize(x[1], not small)
2566 o = _optimize(x[1], not small)
2567 order = x[2]
2567 order = x[2]
2568 return o[0], (op, o[1], order)
2568 return o[0], (op, o[1], order)
2569 elif op == 'parentpost':
2569 elif op == 'parentpost':
2570 o = _optimize(x[1], small)
2570 o = _optimize(x[1], small)
2571 order = x[2]
2571 order = x[2]
2572 return o[0], (op, o[1], order)
2572 return o[0], (op, o[1], order)
2573 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2573 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2574 wa, ta = _optimize(x[1], small)
2574 wa, ta = _optimize(x[1], small)
2575 wb, tb = _optimize(x[2], small)
2575 wb, tb = _optimize(x[2], small)
2576 order = x[3]
2576 order = x[3]
2577 return wa + wb, (op, ta, tb, order)
2577 return wa + wb, (op, ta, tb, order)
2578 elif op == 'list':
2578 elif op == 'list':
2579 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2579 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2580 return sum(ws), (op,) + ts
2580 return sum(ws), (op,) + ts
2581 elif op == 'keyvalue':
2581 elif op == 'keyvalue':
2582 w, t = _optimize(x[2], small)
2582 w, t = _optimize(x[2], small)
2583 return w, (op, x[1], t)
2583 return w, (op, x[1], t)
2584 elif op == 'func':
2584 elif op == 'func':
2585 f = getsymbol(x[1])
2585 f = getsymbol(x[1])
2586 wa, ta = _optimize(x[2], small)
2586 wa, ta = _optimize(x[2], small)
2587 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2587 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2588 'keyword', 'outgoing', 'user'):
2588 'keyword', 'outgoing', 'user'):
2589 w = 10 # slow
2589 w = 10 # slow
2590 elif f in ('modifies', 'adds', 'removes'):
2590 elif f in ('modifies', 'adds', 'removes'):
2591 w = 30 # slower
2591 w = 30 # slower
2592 elif f == "contains":
2592 elif f == "contains":
2593 w = 100 # very slow
2593 w = 100 # very slow
2594 elif f == "ancestor":
2594 elif f == "ancestor":
2595 w = 1 * smallbonus
2595 w = 1 * smallbonus
2596 elif f in ('reverse', 'limit', 'first', '_intlist'):
2596 elif f in ('reverse', 'limit', 'first', '_intlist'):
2597 w = 0
2597 w = 0
2598 elif f == "sort":
2598 elif f == "sort":
2599 w = 10 # assume most sorts look at changelog
2599 w = 10 # assume most sorts look at changelog
2600 else:
2600 else:
2601 w = 1
2601 w = 1
2602 order = x[3]
2602 order = x[3]
2603 return w + wa, (op, x[1], ta, order)
2603 return w + wa, (op, x[1], ta, order)
2604 raise ValueError('invalid operator %r' % op)
2604 raise ValueError('invalid operator %r' % op)
2605
2605
2606 def optimize(tree):
2606 def optimize(tree):
2607 """Optimize evaluatable tree
2607 """Optimize evaluatable tree
2608
2608
2609 All pseudo operations should be transformed beforehand.
2609 All pseudo operations should be transformed beforehand.
2610 """
2610 """
2611 _weight, newtree = _optimize(tree, small=True)
2611 _weight, newtree = _optimize(tree, small=True)
2612 return newtree
2612 return newtree
2613
2613
2614 # the set of valid characters for the initial letter of symbols in
2614 # the set of valid characters for the initial letter of symbols in
2615 # alias declarations and definitions
2615 # alias declarations and definitions
2616 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2616 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2617 if c.isalnum() or c in '._@$' or ord(c) > 127)
2617 if c.isalnum() or c in '._@$' or ord(c) > 127)
2618
2618
2619 def _parsewith(spec, lookup=None, syminitletters=None):
2619 def _parsewith(spec, lookup=None, syminitletters=None):
2620 """Generate a parse tree of given spec with given tokenizing options
2620 """Generate a parse tree of given spec with given tokenizing options
2621
2621
2622 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2622 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2623 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2623 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2624 >>> _parsewith('$1')
2624 >>> _parsewith('$1')
2625 Traceback (most recent call last):
2625 Traceback (most recent call last):
2626 ...
2626 ...
2627 ParseError: ("syntax error in revset '$1'", 0)
2627 ParseError: ("syntax error in revset '$1'", 0)
2628 >>> _parsewith('foo bar')
2628 >>> _parsewith('foo bar')
2629 Traceback (most recent call last):
2629 Traceback (most recent call last):
2630 ...
2630 ...
2631 ParseError: ('invalid token', 4)
2631 ParseError: ('invalid token', 4)
2632 """
2632 """
2633 p = parser.parser(elements)
2633 p = parser.parser(elements)
2634 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2634 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2635 syminitletters=syminitletters))
2635 syminitletters=syminitletters))
2636 if pos != len(spec):
2636 if pos != len(spec):
2637 raise error.ParseError(_('invalid token'), pos)
2637 raise error.ParseError(_('invalid token'), pos)
2638 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2638 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2639
2639
2640 class _aliasrules(parser.basealiasrules):
2640 class _aliasrules(parser.basealiasrules):
2641 """Parsing and expansion rule set of revset aliases"""
2641 """Parsing and expansion rule set of revset aliases"""
2642 _section = _('revset alias')
2642 _section = _('revset alias')
2643
2643
2644 @staticmethod
2644 @staticmethod
2645 def _parse(spec):
2645 def _parse(spec):
2646 """Parse alias declaration/definition ``spec``
2646 """Parse alias declaration/definition ``spec``
2647
2647
2648 This allows symbol names to use also ``$`` as an initial letter
2648 This allows symbol names to use also ``$`` as an initial letter
2649 (for backward compatibility), and callers of this function should
2649 (for backward compatibility), and callers of this function should
2650 examine whether ``$`` is used also for unexpected symbols or not.
2650 examine whether ``$`` is used also for unexpected symbols or not.
2651 """
2651 """
2652 return _parsewith(spec, syminitletters=_aliassyminitletters)
2652 return _parsewith(spec, syminitletters=_aliassyminitletters)
2653
2653
2654 @staticmethod
2654 @staticmethod
2655 def _trygetfunc(tree):
2655 def _trygetfunc(tree):
2656 if tree[0] == 'func' and tree[1][0] == 'symbol':
2656 if tree[0] == 'func' and tree[1][0] == 'symbol':
2657 return tree[1][1], getlist(tree[2])
2657 return tree[1][1], getlist(tree[2])
2658
2658
2659 def expandaliases(ui, tree):
2659 def expandaliases(ui, tree):
2660 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2660 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2661 tree = _aliasrules.expand(aliases, tree)
2661 tree = _aliasrules.expand(aliases, tree)
2662 # warn about problematic (but not referred) aliases
2662 # warn about problematic (but not referred) aliases
2663 for name, alias in sorted(aliases.iteritems()):
2663 for name, alias in sorted(aliases.iteritems()):
2664 if alias.error and not alias.warned:
2664 if alias.error and not alias.warned:
2665 ui.warn(_('warning: %s\n') % (alias.error))
2665 ui.warn(_('warning: %s\n') % (alias.error))
2666 alias.warned = True
2666 alias.warned = True
2667 return tree
2667 return tree
2668
2668
2669 def foldconcat(tree):
2669 def foldconcat(tree):
2670 """Fold elements to be concatenated by `##`
2670 """Fold elements to be concatenated by `##`
2671 """
2671 """
2672 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2672 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2673 return tree
2673 return tree
2674 if tree[0] == '_concat':
2674 if tree[0] == '_concat':
2675 pending = [tree]
2675 pending = [tree]
2676 l = []
2676 l = []
2677 while pending:
2677 while pending:
2678 e = pending.pop()
2678 e = pending.pop()
2679 if e[0] == '_concat':
2679 if e[0] == '_concat':
2680 pending.extend(reversed(e[1:]))
2680 pending.extend(reversed(e[1:]))
2681 elif e[0] in ('string', 'symbol'):
2681 elif e[0] in ('string', 'symbol'):
2682 l.append(e[1])
2682 l.append(e[1])
2683 else:
2683 else:
2684 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2684 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2685 raise error.ParseError(msg)
2685 raise error.ParseError(msg)
2686 return ('string', ''.join(l))
2686 return ('string', ''.join(l))
2687 else:
2687 else:
2688 return tuple(foldconcat(t) for t in tree)
2688 return tuple(foldconcat(t) for t in tree)
2689
2689
2690 def parse(spec, lookup=None):
2690 def parse(spec, lookup=None):
2691 return _parsewith(spec, lookup=lookup)
2691 return _parsewith(spec, lookup=lookup)
2692
2692
2693 def posttreebuilthook(tree, repo):
2693 def posttreebuilthook(tree, repo):
2694 # hook for extensions to execute code on the optimized tree
2694 # hook for extensions to execute code on the optimized tree
2695 pass
2695 pass
2696
2696
2697 def match(ui, spec, repo=None):
2697 def match(ui, spec, repo=None):
2698 """Create a matcher for a single revision spec."""
2698 """Create a matcher for a single revision spec."""
2699 return matchany(ui, [spec], repo=repo)
2699 return matchany(ui, [spec], repo=repo)
2700
2700
2701 def matchany(ui, specs, repo=None):
2701 def matchany(ui, specs, repo=None):
2702 """Create a matcher that will include any revisions matching one of the
2702 """Create a matcher that will include any revisions matching one of the
2703 given specs"""
2703 given specs"""
2704 if not specs:
2704 if not specs:
2705 def mfunc(repo, subset=None):
2705 def mfunc(repo, subset=None):
2706 return baseset()
2706 return baseset()
2707 return mfunc
2707 return mfunc
2708 if not all(specs):
2708 if not all(specs):
2709 raise error.ParseError(_("empty query"))
2709 raise error.ParseError(_("empty query"))
2710 lookup = None
2710 lookup = None
2711 if repo:
2711 if repo:
2712 lookup = repo.__contains__
2712 lookup = repo.__contains__
2713 if len(specs) == 1:
2713 if len(specs) == 1:
2714 tree = parse(specs[0], lookup)
2714 tree = parse(specs[0], lookup)
2715 else:
2715 else:
2716 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2716 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2717
2717
2718 if ui:
2718 if ui:
2719 tree = expandaliases(ui, tree)
2719 tree = expandaliases(ui, tree)
2720 tree = foldconcat(tree)
2720 tree = foldconcat(tree)
2721 tree = analyze(tree)
2721 tree = analyze(tree)
2722 tree = optimize(tree)
2722 tree = optimize(tree)
2723 posttreebuilthook(tree, repo)
2723 posttreebuilthook(tree, repo)
2724 return makematcher(tree)
2724 return makematcher(tree)
2725
2725
2726 def makematcher(tree):
2726 def makematcher(tree):
2727 """Create a matcher from an evaluatable tree"""
2727 """Create a matcher from an evaluatable tree"""
2728 def mfunc(repo, subset=None):
2728 def mfunc(repo, subset=None):
2729 if subset is None:
2729 if subset is None:
2730 subset = fullreposet(repo)
2730 subset = fullreposet(repo)
2731 if util.safehasattr(subset, 'isascending'):
2731 if util.safehasattr(subset, 'isascending'):
2732 result = getset(repo, subset, tree)
2732 result = getset(repo, subset, tree)
2733 else:
2733 else:
2734 result = getset(repo, baseset(subset), tree)
2734 result = getset(repo, baseset(subset), tree)
2735 return result
2735 return result
2736 return mfunc
2736 return mfunc
2737
2737
2738 def formatspec(expr, *args):
2738 def formatspec(expr, *args):
2739 '''
2739 '''
2740 This is a convenience function for using revsets internally, and
2740 This is a convenience function for using revsets internally, and
2741 escapes arguments appropriately. Aliases are intentionally ignored
2741 escapes arguments appropriately. Aliases are intentionally ignored
2742 so that intended expression behavior isn't accidentally subverted.
2742 so that intended expression behavior isn't accidentally subverted.
2743
2743
2744 Supported arguments:
2744 Supported arguments:
2745
2745
2746 %r = revset expression, parenthesized
2746 %r = revset expression, parenthesized
2747 %d = int(arg), no quoting
2747 %d = int(arg), no quoting
2748 %s = string(arg), escaped and single-quoted
2748 %s = string(arg), escaped and single-quoted
2749 %b = arg.branch(), escaped and single-quoted
2749 %b = arg.branch(), escaped and single-quoted
2750 %n = hex(arg), single-quoted
2750 %n = hex(arg), single-quoted
2751 %% = a literal '%'
2751 %% = a literal '%'
2752
2752
2753 Prefixing the type with 'l' specifies a parenthesized list of that type.
2753 Prefixing the type with 'l' specifies a parenthesized list of that type.
2754
2754
2755 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2755 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2756 '(10 or 11):: and ((this()) or (that()))'
2756 '(10 or 11):: and ((this()) or (that()))'
2757 >>> formatspec('%d:: and not %d::', 10, 20)
2757 >>> formatspec('%d:: and not %d::', 10, 20)
2758 '10:: and not 20::'
2758 '10:: and not 20::'
2759 >>> formatspec('%ld or %ld', [], [1])
2759 >>> formatspec('%ld or %ld', [], [1])
2760 "_list('') or 1"
2760 "_list('') or 1"
2761 >>> formatspec('keyword(%s)', 'foo\\xe9')
2761 >>> formatspec('keyword(%s)', 'foo\\xe9')
2762 "keyword('foo\\\\xe9')"
2762 "keyword('foo\\\\xe9')"
2763 >>> b = lambda: 'default'
2763 >>> b = lambda: 'default'
2764 >>> b.branch = b
2764 >>> b.branch = b
2765 >>> formatspec('branch(%b)', b)
2765 >>> formatspec('branch(%b)', b)
2766 "branch('default')"
2766 "branch('default')"
2767 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2767 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2768 "root(_list('a\\x00b\\x00c\\x00d'))"
2768 "root(_list('a\\x00b\\x00c\\x00d'))"
2769 '''
2769 '''
2770
2770
2771 def quote(s):
2771 def quote(s):
2772 return repr(str(s))
2772 return repr(str(s))
2773
2773
2774 def argtype(c, arg):
2774 def argtype(c, arg):
2775 if c == 'd':
2775 if c == 'd':
2776 return str(int(arg))
2776 return str(int(arg))
2777 elif c == 's':
2777 elif c == 's':
2778 return quote(arg)
2778 return quote(arg)
2779 elif c == 'r':
2779 elif c == 'r':
2780 parse(arg) # make sure syntax errors are confined
2780 parse(arg) # make sure syntax errors are confined
2781 return '(%s)' % arg
2781 return '(%s)' % arg
2782 elif c == 'n':
2782 elif c == 'n':
2783 return quote(node.hex(arg))
2783 return quote(node.hex(arg))
2784 elif c == 'b':
2784 elif c == 'b':
2785 return quote(arg.branch())
2785 return quote(arg.branch())
2786
2786
2787 def listexp(s, t):
2787 def listexp(s, t):
2788 l = len(s)
2788 l = len(s)
2789 if l == 0:
2789 if l == 0:
2790 return "_list('')"
2790 return "_list('')"
2791 elif l == 1:
2791 elif l == 1:
2792 return argtype(t, s[0])
2792 return argtype(t, s[0])
2793 elif t == 'd':
2793 elif t == 'd':
2794 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2794 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2795 elif t == 's':
2795 elif t == 's':
2796 return "_list('%s')" % "\0".join(s)
2796 return "_list('%s')" % "\0".join(s)
2797 elif t == 'n':
2797 elif t == 'n':
2798 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2798 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2799 elif t == 'b':
2799 elif t == 'b':
2800 return "_list('%s')" % "\0".join(a.branch() for a in s)
2800 return "_list('%s')" % "\0".join(a.branch() for a in s)
2801
2801
2802 m = l // 2
2802 m = l // 2
2803 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2803 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2804
2804
2805 ret = ''
2805 ret = ''
2806 pos = 0
2806 pos = 0
2807 arg = 0
2807 arg = 0
2808 while pos < len(expr):
2808 while pos < len(expr):
2809 c = expr[pos]
2809 c = expr[pos]
2810 if c == '%':
2810 if c == '%':
2811 pos += 1
2811 pos += 1
2812 d = expr[pos]
2812 d = expr[pos]
2813 if d == '%':
2813 if d == '%':
2814 ret += d
2814 ret += d
2815 elif d in 'dsnbr':
2815 elif d in 'dsnbr':
2816 ret += argtype(d, args[arg])
2816 ret += argtype(d, args[arg])
2817 arg += 1
2817 arg += 1
2818 elif d == 'l':
2818 elif d == 'l':
2819 # a list of some type
2819 # a list of some type
2820 pos += 1
2820 pos += 1
2821 d = expr[pos]
2821 d = expr[pos]
2822 ret += listexp(list(args[arg]), d)
2822 ret += listexp(list(args[arg]), d)
2823 arg += 1
2823 arg += 1
2824 else:
2824 else:
2825 raise error.Abort(_('unexpected revspec format character %s')
2825 raise error.Abort(_('unexpected revspec format character %s')
2826 % d)
2826 % d)
2827 else:
2827 else:
2828 ret += c
2828 ret += c
2829 pos += 1
2829 pos += 1
2830
2830
2831 return ret
2831 return ret
2832
2832
2833 def prettyformat(tree):
2833 def prettyformat(tree):
2834 return parser.prettyformat(tree, ('string', 'symbol'))
2834 return parser.prettyformat(tree, ('string', 'symbol'))
2835
2835
2836 def depth(tree):
2836 def depth(tree):
2837 if isinstance(tree, tuple):
2837 if isinstance(tree, tuple):
2838 return max(map(depth, tree)) + 1
2838 return max(map(depth, tree)) + 1
2839 else:
2839 else:
2840 return 0
2840 return 0
2841
2841
2842 def funcsused(tree):
2842 def funcsused(tree):
2843 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2843 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2844 return set()
2844 return set()
2845 else:
2845 else:
2846 funcs = set()
2846 funcs = set()
2847 for s in tree[1:]:
2847 for s in tree[1:]:
2848 funcs |= funcsused(s)
2848 funcs |= funcsused(s)
2849 if tree[0] == 'func':
2849 if tree[0] == 'func':
2850 funcs.add(tree[1][1])
2850 funcs.add(tree[1][1])
2851 return funcs
2851 return funcs
2852
2852
2853 def _formatsetrepr(r):
2853 def _formatsetrepr(r):
2854 """Format an optional printable representation of a set
2854 """Format an optional printable representation of a set
2855
2855
2856 ======== =================================
2856 ======== =================================
2857 type(r) example
2857 type(r) example
2858 ======== =================================
2858 ======== =================================
2859 tuple ('<not %r>', other)
2859 tuple ('<not %r>', other)
2860 str '<branch closed>'
2860 str '<branch closed>'
2861 callable lambda: '<branch %r>' % sorted(b)
2861 callable lambda: '<branch %r>' % sorted(b)
2862 object other
2862 object other
2863 ======== =================================
2863 ======== =================================
2864 """
2864 """
2865 if r is None:
2865 if r is None:
2866 return ''
2866 return ''
2867 elif isinstance(r, tuple):
2867 elif isinstance(r, tuple):
2868 return r[0] % r[1:]
2868 return r[0] % r[1:]
2869 elif isinstance(r, str):
2869 elif isinstance(r, str):
2870 return r
2870 return r
2871 elif callable(r):
2871 elif callable(r):
2872 return r()
2872 return r()
2873 else:
2873 else:
2874 return repr(r)
2874 return repr(r)
2875
2875
2876 class abstractsmartset(object):
2876 class abstractsmartset(object):
2877
2877
2878 def __nonzero__(self):
2878 def __nonzero__(self):
2879 """True if the smartset is not empty"""
2879 """True if the smartset is not empty"""
2880 raise NotImplementedError()
2880 raise NotImplementedError()
2881
2881
2882 def __contains__(self, rev):
2882 def __contains__(self, rev):
2883 """provide fast membership testing"""
2883 """provide fast membership testing"""
2884 raise NotImplementedError()
2884 raise NotImplementedError()
2885
2885
2886 def __iter__(self):
2886 def __iter__(self):
2887 """iterate the set in the order it is supposed to be iterated"""
2887 """iterate the set in the order it is supposed to be iterated"""
2888 raise NotImplementedError()
2888 raise NotImplementedError()
2889
2889
2890 # Attributes containing a function to perform a fast iteration in a given
2890 # Attributes containing a function to perform a fast iteration in a given
2891 # direction. A smartset can have none, one, or both defined.
2891 # direction. A smartset can have none, one, or both defined.
2892 #
2892 #
2893 # Default value is None instead of a function returning None to avoid
2893 # Default value is None instead of a function returning None to avoid
2894 # initializing an iterator just for testing if a fast method exists.
2894 # initializing an iterator just for testing if a fast method exists.
2895 fastasc = None
2895 fastasc = None
2896 fastdesc = None
2896 fastdesc = None
2897
2897
2898 def isascending(self):
2898 def isascending(self):
2899 """True if the set will iterate in ascending order"""
2899 """True if the set will iterate in ascending order"""
2900 raise NotImplementedError()
2900 raise NotImplementedError()
2901
2901
2902 def isdescending(self):
2902 def isdescending(self):
2903 """True if the set will iterate in descending order"""
2903 """True if the set will iterate in descending order"""
2904 raise NotImplementedError()
2904 raise NotImplementedError()
2905
2905
2906 def istopo(self):
2906 def istopo(self):
2907 """True if the set will iterate in topographical order"""
2907 """True if the set will iterate in topographical order"""
2908 raise NotImplementedError()
2908 raise NotImplementedError()
2909
2909
2910 @util.cachefunc
2910 @util.cachefunc
2911 def min(self):
2911 def min(self):
2912 """return the minimum element in the set"""
2912 """return the minimum element in the set"""
2913 if self.fastasc is not None:
2913 if self.fastasc is not None:
2914 for r in self.fastasc():
2914 for r in self.fastasc():
2915 return r
2915 return r
2916 raise ValueError('arg is an empty sequence')
2916 raise ValueError('arg is an empty sequence')
2917 return min(self)
2917 return min(self)
2918
2918
2919 @util.cachefunc
2919 @util.cachefunc
2920 def max(self):
2920 def max(self):
2921 """return the maximum element in the set"""
2921 """return the maximum element in the set"""
2922 if self.fastdesc is not None:
2922 if self.fastdesc is not None:
2923 for r in self.fastdesc():
2923 for r in self.fastdesc():
2924 return r
2924 return r
2925 raise ValueError('arg is an empty sequence')
2925 raise ValueError('arg is an empty sequence')
2926 return max(self)
2926 return max(self)
2927
2927
2928 def first(self):
2928 def first(self):
2929 """return the first element in the set (user iteration perspective)
2929 """return the first element in the set (user iteration perspective)
2930
2930
2931 Return None if the set is empty"""
2931 Return None if the set is empty"""
2932 raise NotImplementedError()
2932 raise NotImplementedError()
2933
2933
2934 def last(self):
2934 def last(self):
2935 """return the last element in the set (user iteration perspective)
2935 """return the last element in the set (user iteration perspective)
2936
2936
2937 Return None if the set is empty"""
2937 Return None if the set is empty"""
2938 raise NotImplementedError()
2938 raise NotImplementedError()
2939
2939
2940 def __len__(self):
2940 def __len__(self):
2941 """return the length of the smartsets
2941 """return the length of the smartsets
2942
2942
2943 This can be expensive on smartset that could be lazy otherwise."""
2943 This can be expensive on smartset that could be lazy otherwise."""
2944 raise NotImplementedError()
2944 raise NotImplementedError()
2945
2945
2946 def reverse(self):
2946 def reverse(self):
2947 """reverse the expected iteration order"""
2947 """reverse the expected iteration order"""
2948 raise NotImplementedError()
2948 raise NotImplementedError()
2949
2949
2950 def sort(self, reverse=True):
2950 def sort(self, reverse=True):
2951 """get the set to iterate in an ascending or descending order"""
2951 """get the set to iterate in an ascending or descending order"""
2952 raise NotImplementedError()
2952 raise NotImplementedError()
2953
2953
2954 def __and__(self, other):
2954 def __and__(self, other):
2955 """Returns a new object with the intersection of the two collections.
2955 """Returns a new object with the intersection of the two collections.
2956
2956
2957 This is part of the mandatory API for smartset."""
2957 This is part of the mandatory API for smartset."""
2958 if isinstance(other, fullreposet):
2958 if isinstance(other, fullreposet):
2959 return self
2959 return self
2960 return self.filter(other.__contains__, condrepr=other, cache=False)
2960 return self.filter(other.__contains__, condrepr=other, cache=False)
2961
2961
2962 def __add__(self, other):
2962 def __add__(self, other):
2963 """Returns a new object with the union of the two collections.
2963 """Returns a new object with the union of the two collections.
2964
2964
2965 This is part of the mandatory API for smartset."""
2965 This is part of the mandatory API for smartset."""
2966 return addset(self, other)
2966 return addset(self, other)
2967
2967
2968 def __sub__(self, other):
2968 def __sub__(self, other):
2969 """Returns a new object with the substraction of the two collections.
2969 """Returns a new object with the substraction of the two collections.
2970
2970
2971 This is part of the mandatory API for smartset."""
2971 This is part of the mandatory API for smartset."""
2972 c = other.__contains__
2972 c = other.__contains__
2973 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2973 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2974 cache=False)
2974 cache=False)
2975
2975
2976 def filter(self, condition, condrepr=None, cache=True):
2976 def filter(self, condition, condrepr=None, cache=True):
2977 """Returns this smartset filtered by condition as a new smartset.
2977 """Returns this smartset filtered by condition as a new smartset.
2978
2978
2979 `condition` is a callable which takes a revision number and returns a
2979 `condition` is a callable which takes a revision number and returns a
2980 boolean. Optional `condrepr` provides a printable representation of
2980 boolean. Optional `condrepr` provides a printable representation of
2981 the given `condition`.
2981 the given `condition`.
2982
2982
2983 This is part of the mandatory API for smartset."""
2983 This is part of the mandatory API for smartset."""
2984 # builtin cannot be cached. but do not needs to
2984 # builtin cannot be cached. but do not needs to
2985 if cache and util.safehasattr(condition, 'func_code'):
2985 if cache and util.safehasattr(condition, 'func_code'):
2986 condition = util.cachefunc(condition)
2986 condition = util.cachefunc(condition)
2987 return filteredset(self, condition, condrepr)
2987 return filteredset(self, condition, condrepr)
2988
2988
2989 class baseset(abstractsmartset):
2989 class baseset(abstractsmartset):
2990 """Basic data structure that represents a revset and contains the basic
2990 """Basic data structure that represents a revset and contains the basic
2991 operation that it should be able to perform.
2991 operation that it should be able to perform.
2992
2992
2993 Every method in this class should be implemented by any smartset class.
2993 Every method in this class should be implemented by any smartset class.
2994 """
2994 """
2995 def __init__(self, data=(), datarepr=None, istopo=False):
2995 def __init__(self, data=(), datarepr=None, istopo=False):
2996 """
2996 """
2997 datarepr: a tuple of (format, obj, ...), a function or an object that
2997 datarepr: a tuple of (format, obj, ...), a function or an object that
2998 provides a printable representation of the given data.
2998 provides a printable representation of the given data.
2999 """
2999 """
3000 self._ascending = None
3000 self._ascending = None
3001 self._istopo = istopo
3001 self._istopo = istopo
3002 if not isinstance(data, list):
3002 if not isinstance(data, list):
3003 if isinstance(data, set):
3003 if isinstance(data, set):
3004 self._set = data
3004 self._set = data
3005 # set has no order we pick one for stability purpose
3005 # set has no order we pick one for stability purpose
3006 self._ascending = True
3006 self._ascending = True
3007 data = list(data)
3007 data = list(data)
3008 self._list = data
3008 self._list = data
3009 self._datarepr = datarepr
3009 self._datarepr = datarepr
3010
3010
3011 @util.propertycache
3011 @util.propertycache
3012 def _set(self):
3012 def _set(self):
3013 return set(self._list)
3013 return set(self._list)
3014
3014
3015 @util.propertycache
3015 @util.propertycache
3016 def _asclist(self):
3016 def _asclist(self):
3017 asclist = self._list[:]
3017 asclist = self._list[:]
3018 asclist.sort()
3018 asclist.sort()
3019 return asclist
3019 return asclist
3020
3020
3021 def __iter__(self):
3021 def __iter__(self):
3022 if self._ascending is None:
3022 if self._ascending is None:
3023 return iter(self._list)
3023 return iter(self._list)
3024 elif self._ascending:
3024 elif self._ascending:
3025 return iter(self._asclist)
3025 return iter(self._asclist)
3026 else:
3026 else:
3027 return reversed(self._asclist)
3027 return reversed(self._asclist)
3028
3028
3029 def fastasc(self):
3029 def fastasc(self):
3030 return iter(self._asclist)
3030 return iter(self._asclist)
3031
3031
3032 def fastdesc(self):
3032 def fastdesc(self):
3033 return reversed(self._asclist)
3033 return reversed(self._asclist)
3034
3034
3035 @util.propertycache
3035 @util.propertycache
3036 def __contains__(self):
3036 def __contains__(self):
3037 return self._set.__contains__
3037 return self._set.__contains__
3038
3038
3039 def __nonzero__(self):
3039 def __nonzero__(self):
3040 return bool(self._list)
3040 return bool(self._list)
3041
3041
3042 def sort(self, reverse=False):
3042 def sort(self, reverse=False):
3043 self._ascending = not bool(reverse)
3043 self._ascending = not bool(reverse)
3044 self._istopo = False
3044 self._istopo = False
3045
3045
3046 def reverse(self):
3046 def reverse(self):
3047 if self._ascending is None:
3047 if self._ascending is None:
3048 self._list.reverse()
3048 self._list.reverse()
3049 else:
3049 else:
3050 self._ascending = not self._ascending
3050 self._ascending = not self._ascending
3051 self._istopo = False
3051 self._istopo = False
3052
3052
3053 def __len__(self):
3053 def __len__(self):
3054 return len(self._list)
3054 return len(self._list)
3055
3055
3056 def isascending(self):
3056 def isascending(self):
3057 """Returns True if the collection is ascending order, False if not.
3057 """Returns True if the collection is ascending order, False if not.
3058
3058
3059 This is part of the mandatory API for smartset."""
3059 This is part of the mandatory API for smartset."""
3060 if len(self) <= 1:
3060 if len(self) <= 1:
3061 return True
3061 return True
3062 return self._ascending is not None and self._ascending
3062 return self._ascending is not None and self._ascending
3063
3063
3064 def isdescending(self):
3064 def isdescending(self):
3065 """Returns True if the collection is descending order, False if not.
3065 """Returns True if the collection is descending order, False if not.
3066
3066
3067 This is part of the mandatory API for smartset."""
3067 This is part of the mandatory API for smartset."""
3068 if len(self) <= 1:
3068 if len(self) <= 1:
3069 return True
3069 return True
3070 return self._ascending is not None and not self._ascending
3070 return self._ascending is not None and not self._ascending
3071
3071
3072 def istopo(self):
3072 def istopo(self):
3073 """Is the collection is in topographical order or not.
3073 """Is the collection is in topographical order or not.
3074
3074
3075 This is part of the mandatory API for smartset."""
3075 This is part of the mandatory API for smartset."""
3076 if len(self) <= 1:
3076 if len(self) <= 1:
3077 return True
3077 return True
3078 return self._istopo
3078 return self._istopo
3079
3079
3080 def first(self):
3080 def first(self):
3081 if self:
3081 if self:
3082 if self._ascending is None:
3082 if self._ascending is None:
3083 return self._list[0]
3083 return self._list[0]
3084 elif self._ascending:
3084 elif self._ascending:
3085 return self._asclist[0]
3085 return self._asclist[0]
3086 else:
3086 else:
3087 return self._asclist[-1]
3087 return self._asclist[-1]
3088 return None
3088 return None
3089
3089
3090 def last(self):
3090 def last(self):
3091 if self:
3091 if self:
3092 if self._ascending is None:
3092 if self._ascending is None:
3093 return self._list[-1]
3093 return self._list[-1]
3094 elif self._ascending:
3094 elif self._ascending:
3095 return self._asclist[-1]
3095 return self._asclist[-1]
3096 else:
3096 else:
3097 return self._asclist[0]
3097 return self._asclist[0]
3098 return None
3098 return None
3099
3099
3100 def __repr__(self):
3100 def __repr__(self):
3101 d = {None: '', False: '-', True: '+'}[self._ascending]
3101 d = {None: '', False: '-', True: '+'}[self._ascending]
3102 s = _formatsetrepr(self._datarepr)
3102 s = _formatsetrepr(self._datarepr)
3103 if not s:
3103 if not s:
3104 l = self._list
3104 l = self._list
3105 # if _list has been built from a set, it might have a different
3105 # if _list has been built from a set, it might have a different
3106 # order from one python implementation to another.
3106 # order from one python implementation to another.
3107 # We fallback to the sorted version for a stable output.
3107 # We fallback to the sorted version for a stable output.
3108 if self._ascending is not None:
3108 if self._ascending is not None:
3109 l = self._asclist
3109 l = self._asclist
3110 s = repr(l)
3110 s = repr(l)
3111 return '<%s%s %s>' % (type(self).__name__, d, s)
3111 return '<%s%s %s>' % (type(self).__name__, d, s)
3112
3112
3113 class filteredset(abstractsmartset):
3113 class filteredset(abstractsmartset):
3114 """Duck type for baseset class which iterates lazily over the revisions in
3114 """Duck type for baseset class which iterates lazily over the revisions in
3115 the subset and contains a function which tests for membership in the
3115 the subset and contains a function which tests for membership in the
3116 revset
3116 revset
3117 """
3117 """
3118 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3118 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3119 """
3119 """
3120 condition: a function that decide whether a revision in the subset
3120 condition: a function that decide whether a revision in the subset
3121 belongs to the revset or not.
3121 belongs to the revset or not.
3122 condrepr: a tuple of (format, obj, ...), a function or an object that
3122 condrepr: a tuple of (format, obj, ...), a function or an object that
3123 provides a printable representation of the given condition.
3123 provides a printable representation of the given condition.
3124 """
3124 """
3125 self._subset = subset
3125 self._subset = subset
3126 self._condition = condition
3126 self._condition = condition
3127 self._condrepr = condrepr
3127 self._condrepr = condrepr
3128
3128
3129 def __contains__(self, x):
3129 def __contains__(self, x):
3130 return x in self._subset and self._condition(x)
3130 return x in self._subset and self._condition(x)
3131
3131
3132 def __iter__(self):
3132 def __iter__(self):
3133 return self._iterfilter(self._subset)
3133 return self._iterfilter(self._subset)
3134
3134
3135 def _iterfilter(self, it):
3135 def _iterfilter(self, it):
3136 cond = self._condition
3136 cond = self._condition
3137 for x in it:
3137 for x in it:
3138 if cond(x):
3138 if cond(x):
3139 yield x
3139 yield x
3140
3140
3141 @property
3141 @property
3142 def fastasc(self):
3142 def fastasc(self):
3143 it = self._subset.fastasc
3143 it = self._subset.fastasc
3144 if it is None:
3144 if it is None:
3145 return None
3145 return None
3146 return lambda: self._iterfilter(it())
3146 return lambda: self._iterfilter(it())
3147
3147
3148 @property
3148 @property
3149 def fastdesc(self):
3149 def fastdesc(self):
3150 it = self._subset.fastdesc
3150 it = self._subset.fastdesc
3151 if it is None:
3151 if it is None:
3152 return None
3152 return None
3153 return lambda: self._iterfilter(it())
3153 return lambda: self._iterfilter(it())
3154
3154
3155 def __nonzero__(self):
3155 def __nonzero__(self):
3156 fast = None
3156 fast = None
3157 candidates = [self.fastasc if self.isascending() else None,
3157 candidates = [self.fastasc if self.isascending() else None,
3158 self.fastdesc if self.isdescending() else None,
3158 self.fastdesc if self.isdescending() else None,
3159 self.fastasc,
3159 self.fastasc,
3160 self.fastdesc]
3160 self.fastdesc]
3161 for candidate in candidates:
3161 for candidate in candidates:
3162 if candidate is not None:
3162 if candidate is not None:
3163 fast = candidate
3163 fast = candidate
3164 break
3164 break
3165
3165
3166 if fast is not None:
3166 if fast is not None:
3167 it = fast()
3167 it = fast()
3168 else:
3168 else:
3169 it = self
3169 it = self
3170
3170
3171 for r in it:
3171 for r in it:
3172 return True
3172 return True
3173 return False
3173 return False
3174
3174
3175 def __len__(self):
3175 def __len__(self):
3176 # Basic implementation to be changed in future patches.
3176 # Basic implementation to be changed in future patches.
3177 # until this gets improved, we use generator expression
3177 # until this gets improved, we use generator expression
3178 # here, since list compr is free to call __len__ again
3178 # here, since list compr is free to call __len__ again
3179 # causing infinite recursion
3179 # causing infinite recursion
3180 l = baseset(r for r in self)
3180 l = baseset(r for r in self)
3181 return len(l)
3181 return len(l)
3182
3182
3183 def sort(self, reverse=False):
3183 def sort(self, reverse=False):
3184 self._subset.sort(reverse=reverse)
3184 self._subset.sort(reverse=reverse)
3185
3185
3186 def reverse(self):
3186 def reverse(self):
3187 self._subset.reverse()
3187 self._subset.reverse()
3188
3188
3189 def isascending(self):
3189 def isascending(self):
3190 return self._subset.isascending()
3190 return self._subset.isascending()
3191
3191
3192 def isdescending(self):
3192 def isdescending(self):
3193 return self._subset.isdescending()
3193 return self._subset.isdescending()
3194
3194
3195 def istopo(self):
3195 def istopo(self):
3196 return self._subset.istopo()
3196 return self._subset.istopo()
3197
3197
3198 def first(self):
3198 def first(self):
3199 for x in self:
3199 for x in self:
3200 return x
3200 return x
3201 return None
3201 return None
3202
3202
3203 def last(self):
3203 def last(self):
3204 it = None
3204 it = None
3205 if self.isascending():
3205 if self.isascending():
3206 it = self.fastdesc
3206 it = self.fastdesc
3207 elif self.isdescending():
3207 elif self.isdescending():
3208 it = self.fastasc
3208 it = self.fastasc
3209 if it is not None:
3209 if it is not None:
3210 for x in it():
3210 for x in it():
3211 return x
3211 return x
3212 return None #empty case
3212 return None #empty case
3213 else:
3213 else:
3214 x = None
3214 x = None
3215 for x in self:
3215 for x in self:
3216 pass
3216 pass
3217 return x
3217 return x
3218
3218
3219 def __repr__(self):
3219 def __repr__(self):
3220 xs = [repr(self._subset)]
3220 xs = [repr(self._subset)]
3221 s = _formatsetrepr(self._condrepr)
3221 s = _formatsetrepr(self._condrepr)
3222 if s:
3222 if s:
3223 xs.append(s)
3223 xs.append(s)
3224 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3224 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3225
3225
3226 def _iterordered(ascending, iter1, iter2):
3226 def _iterordered(ascending, iter1, iter2):
3227 """produce an ordered iteration from two iterators with the same order
3227 """produce an ordered iteration from two iterators with the same order
3228
3228
3229 The ascending is used to indicated the iteration direction.
3229 The ascending is used to indicated the iteration direction.
3230 """
3230 """
3231 choice = max
3231 choice = max
3232 if ascending:
3232 if ascending:
3233 choice = min
3233 choice = min
3234
3234
3235 val1 = None
3235 val1 = None
3236 val2 = None
3236 val2 = None
3237 try:
3237 try:
3238 # Consume both iterators in an ordered way until one is empty
3238 # Consume both iterators in an ordered way until one is empty
3239 while True:
3239 while True:
3240 if val1 is None:
3240 if val1 is None:
3241 val1 = next(iter1)
3241 val1 = next(iter1)
3242 if val2 is None:
3242 if val2 is None:
3243 val2 = next(iter2)
3243 val2 = next(iter2)
3244 n = choice(val1, val2)
3244 n = choice(val1, val2)
3245 yield n
3245 yield n
3246 if val1 == n:
3246 if val1 == n:
3247 val1 = None
3247 val1 = None
3248 if val2 == n:
3248 if val2 == n:
3249 val2 = None
3249 val2 = None
3250 except StopIteration:
3250 except StopIteration:
3251 # Flush any remaining values and consume the other one
3251 # Flush any remaining values and consume the other one
3252 it = iter2
3252 it = iter2
3253 if val1 is not None:
3253 if val1 is not None:
3254 yield val1
3254 yield val1
3255 it = iter1
3255 it = iter1
3256 elif val2 is not None:
3256 elif val2 is not None:
3257 # might have been equality and both are empty
3257 # might have been equality and both are empty
3258 yield val2
3258 yield val2
3259 for val in it:
3259 for val in it:
3260 yield val
3260 yield val
3261
3261
3262 class addset(abstractsmartset):
3262 class addset(abstractsmartset):
3263 """Represent the addition of two sets
3263 """Represent the addition of two sets
3264
3264
3265 Wrapper structure for lazily adding two structures without losing much
3265 Wrapper structure for lazily adding two structures without losing much
3266 performance on the __contains__ method
3266 performance on the __contains__ method
3267
3267
3268 If the ascending attribute is set, that means the two structures are
3268 If the ascending attribute is set, that means the two structures are
3269 ordered in either an ascending or descending way. Therefore, we can add
3269 ordered in either an ascending or descending way. Therefore, we can add
3270 them maintaining the order by iterating over both at the same time
3270 them maintaining the order by iterating over both at the same time
3271
3271
3272 >>> xs = baseset([0, 3, 2])
3272 >>> xs = baseset([0, 3, 2])
3273 >>> ys = baseset([5, 2, 4])
3273 >>> ys = baseset([5, 2, 4])
3274
3274
3275 >>> rs = addset(xs, ys)
3275 >>> rs = addset(xs, ys)
3276 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3276 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3277 (True, True, False, True, 0, 4)
3277 (True, True, False, True, 0, 4)
3278 >>> rs = addset(xs, baseset([]))
3278 >>> rs = addset(xs, baseset([]))
3279 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3279 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3280 (True, True, False, 0, 2)
3280 (True, True, False, 0, 2)
3281 >>> rs = addset(baseset([]), baseset([]))
3281 >>> rs = addset(baseset([]), baseset([]))
3282 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3282 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3283 (False, False, None, None)
3283 (False, False, None, None)
3284
3284
3285 iterate unsorted:
3285 iterate unsorted:
3286 >>> rs = addset(xs, ys)
3286 >>> rs = addset(xs, ys)
3287 >>> # (use generator because pypy could call len())
3287 >>> # (use generator because pypy could call len())
3288 >>> list(x for x in rs) # without _genlist
3288 >>> list(x for x in rs) # without _genlist
3289 [0, 3, 2, 5, 4]
3289 [0, 3, 2, 5, 4]
3290 >>> assert not rs._genlist
3290 >>> assert not rs._genlist
3291 >>> len(rs)
3291 >>> len(rs)
3292 5
3292 5
3293 >>> [x for x in rs] # with _genlist
3293 >>> [x for x in rs] # with _genlist
3294 [0, 3, 2, 5, 4]
3294 [0, 3, 2, 5, 4]
3295 >>> assert rs._genlist
3295 >>> assert rs._genlist
3296
3296
3297 iterate ascending:
3297 iterate ascending:
3298 >>> rs = addset(xs, ys, ascending=True)
3298 >>> rs = addset(xs, ys, ascending=True)
3299 >>> # (use generator because pypy could call len())
3299 >>> # (use generator because pypy could call len())
3300 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3300 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3301 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3301 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3302 >>> assert not rs._asclist
3302 >>> assert not rs._asclist
3303 >>> len(rs)
3303 >>> len(rs)
3304 5
3304 5
3305 >>> [x for x in rs], [x for x in rs.fastasc()]
3305 >>> [x for x in rs], [x for x in rs.fastasc()]
3306 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3306 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3307 >>> assert rs._asclist
3307 >>> assert rs._asclist
3308
3308
3309 iterate descending:
3309 iterate descending:
3310 >>> rs = addset(xs, ys, ascending=False)
3310 >>> rs = addset(xs, ys, ascending=False)
3311 >>> # (use generator because pypy could call len())
3311 >>> # (use generator because pypy could call len())
3312 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3312 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3313 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3313 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3314 >>> assert not rs._asclist
3314 >>> assert not rs._asclist
3315 >>> len(rs)
3315 >>> len(rs)
3316 5
3316 5
3317 >>> [x for x in rs], [x for x in rs.fastdesc()]
3317 >>> [x for x in rs], [x for x in rs.fastdesc()]
3318 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3318 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3319 >>> assert rs._asclist
3319 >>> assert rs._asclist
3320
3320
3321 iterate ascending without fastasc:
3321 iterate ascending without fastasc:
3322 >>> rs = addset(xs, generatorset(ys), ascending=True)
3322 >>> rs = addset(xs, generatorset(ys), ascending=True)
3323 >>> assert rs.fastasc is None
3323 >>> assert rs.fastasc is None
3324 >>> [x for x in rs]
3324 >>> [x for x in rs]
3325 [0, 2, 3, 4, 5]
3325 [0, 2, 3, 4, 5]
3326
3326
3327 iterate descending without fastdesc:
3327 iterate descending without fastdesc:
3328 >>> rs = addset(generatorset(xs), ys, ascending=False)
3328 >>> rs = addset(generatorset(xs), ys, ascending=False)
3329 >>> assert rs.fastdesc is None
3329 >>> assert rs.fastdesc is None
3330 >>> [x for x in rs]
3330 >>> [x for x in rs]
3331 [5, 4, 3, 2, 0]
3331 [5, 4, 3, 2, 0]
3332 """
3332 """
3333 def __init__(self, revs1, revs2, ascending=None):
3333 def __init__(self, revs1, revs2, ascending=None):
3334 self._r1 = revs1
3334 self._r1 = revs1
3335 self._r2 = revs2
3335 self._r2 = revs2
3336 self._iter = None
3336 self._iter = None
3337 self._ascending = ascending
3337 self._ascending = ascending
3338 self._genlist = None
3338 self._genlist = None
3339 self._asclist = None
3339 self._asclist = None
3340
3340
3341 def __len__(self):
3341 def __len__(self):
3342 return len(self._list)
3342 return len(self._list)
3343
3343
3344 def __nonzero__(self):
3344 def __nonzero__(self):
3345 return bool(self._r1) or bool(self._r2)
3345 return bool(self._r1) or bool(self._r2)
3346
3346
3347 @util.propertycache
3347 @util.propertycache
3348 def _list(self):
3348 def _list(self):
3349 if not self._genlist:
3349 if not self._genlist:
3350 self._genlist = baseset(iter(self))
3350 self._genlist = baseset(iter(self))
3351 return self._genlist
3351 return self._genlist
3352
3352
3353 def __iter__(self):
3353 def __iter__(self):
3354 """Iterate over both collections without repeating elements
3354 """Iterate over both collections without repeating elements
3355
3355
3356 If the ascending attribute is not set, iterate over the first one and
3356 If the ascending attribute is not set, iterate over the first one and
3357 then over the second one checking for membership on the first one so we
3357 then over the second one checking for membership on the first one so we
3358 dont yield any duplicates.
3358 dont yield any duplicates.
3359
3359
3360 If the ascending attribute is set, iterate over both collections at the
3360 If the ascending attribute is set, iterate over both collections at the
3361 same time, yielding only one value at a time in the given order.
3361 same time, yielding only one value at a time in the given order.
3362 """
3362 """
3363 if self._ascending is None:
3363 if self._ascending is None:
3364 if self._genlist:
3364 if self._genlist:
3365 return iter(self._genlist)
3365 return iter(self._genlist)
3366 def arbitraryordergen():
3366 def arbitraryordergen():
3367 for r in self._r1:
3367 for r in self._r1:
3368 yield r
3368 yield r
3369 inr1 = self._r1.__contains__
3369 inr1 = self._r1.__contains__
3370 for r in self._r2:
3370 for r in self._r2:
3371 if not inr1(r):
3371 if not inr1(r):
3372 yield r
3372 yield r
3373 return arbitraryordergen()
3373 return arbitraryordergen()
3374 # try to use our own fast iterator if it exists
3374 # try to use our own fast iterator if it exists
3375 self._trysetasclist()
3375 self._trysetasclist()
3376 if self._ascending:
3376 if self._ascending:
3377 attr = 'fastasc'
3377 attr = 'fastasc'
3378 else:
3378 else:
3379 attr = 'fastdesc'
3379 attr = 'fastdesc'
3380 it = getattr(self, attr)
3380 it = getattr(self, attr)
3381 if it is not None:
3381 if it is not None:
3382 return it()
3382 return it()
3383 # maybe half of the component supports fast
3383 # maybe half of the component supports fast
3384 # get iterator for _r1
3384 # get iterator for _r1
3385 iter1 = getattr(self._r1, attr)
3385 iter1 = getattr(self._r1, attr)
3386 if iter1 is None:
3386 if iter1 is None:
3387 # let's avoid side effect (not sure it matters)
3387 # let's avoid side effect (not sure it matters)
3388 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3388 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3389 else:
3389 else:
3390 iter1 = iter1()
3390 iter1 = iter1()
3391 # get iterator for _r2
3391 # get iterator for _r2
3392 iter2 = getattr(self._r2, attr)
3392 iter2 = getattr(self._r2, attr)
3393 if iter2 is None:
3393 if iter2 is None:
3394 # let's avoid side effect (not sure it matters)
3394 # let's avoid side effect (not sure it matters)
3395 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3395 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3396 else:
3396 else:
3397 iter2 = iter2()
3397 iter2 = iter2()
3398 return _iterordered(self._ascending, iter1, iter2)
3398 return _iterordered(self._ascending, iter1, iter2)
3399
3399
3400 def _trysetasclist(self):
3400 def _trysetasclist(self):
3401 """populate the _asclist attribute if possible and necessary"""
3401 """populate the _asclist attribute if possible and necessary"""
3402 if self._genlist is not None and self._asclist is None:
3402 if self._genlist is not None and self._asclist is None:
3403 self._asclist = sorted(self._genlist)
3403 self._asclist = sorted(self._genlist)
3404
3404
3405 @property
3405 @property
3406 def fastasc(self):
3406 def fastasc(self):
3407 self._trysetasclist()
3407 self._trysetasclist()
3408 if self._asclist is not None:
3408 if self._asclist is not None:
3409 return self._asclist.__iter__
3409 return self._asclist.__iter__
3410 iter1 = self._r1.fastasc
3410 iter1 = self._r1.fastasc
3411 iter2 = self._r2.fastasc
3411 iter2 = self._r2.fastasc
3412 if None in (iter1, iter2):
3412 if None in (iter1, iter2):
3413 return None
3413 return None
3414 return lambda: _iterordered(True, iter1(), iter2())
3414 return lambda: _iterordered(True, iter1(), iter2())
3415
3415
3416 @property
3416 @property
3417 def fastdesc(self):
3417 def fastdesc(self):
3418 self._trysetasclist()
3418 self._trysetasclist()
3419 if self._asclist is not None:
3419 if self._asclist is not None:
3420 return self._asclist.__reversed__
3420 return self._asclist.__reversed__
3421 iter1 = self._r1.fastdesc
3421 iter1 = self._r1.fastdesc
3422 iter2 = self._r2.fastdesc
3422 iter2 = self._r2.fastdesc
3423 if None in (iter1, iter2):
3423 if None in (iter1, iter2):
3424 return None
3424 return None
3425 return lambda: _iterordered(False, iter1(), iter2())
3425 return lambda: _iterordered(False, iter1(), iter2())
3426
3426
3427 def __contains__(self, x):
3427 def __contains__(self, x):
3428 return x in self._r1 or x in self._r2
3428 return x in self._r1 or x in self._r2
3429
3429
3430 def sort(self, reverse=False):
3430 def sort(self, reverse=False):
3431 """Sort the added set
3431 """Sort the added set
3432
3432
3433 For this we use the cached list with all the generated values and if we
3433 For this we use the cached list with all the generated values and if we
3434 know they are ascending or descending we can sort them in a smart way.
3434 know they are ascending or descending we can sort them in a smart way.
3435 """
3435 """
3436 self._ascending = not reverse
3436 self._ascending = not reverse
3437
3437
3438 def isascending(self):
3438 def isascending(self):
3439 return self._ascending is not None and self._ascending
3439 return self._ascending is not None and self._ascending
3440
3440
3441 def isdescending(self):
3441 def isdescending(self):
3442 return self._ascending is not None and not self._ascending
3442 return self._ascending is not None and not self._ascending
3443
3443
3444 def istopo(self):
3444 def istopo(self):
3445 # not worth the trouble asserting if the two sets combined are still
3445 # not worth the trouble asserting if the two sets combined are still
3446 # in topographical order. Use the sort() predicate to explicitly sort
3446 # in topographical order. Use the sort() predicate to explicitly sort
3447 # again instead.
3447 # again instead.
3448 return False
3448 return False
3449
3449
3450 def reverse(self):
3450 def reverse(self):
3451 if self._ascending is None:
3451 if self._ascending is None:
3452 self._list.reverse()
3452 self._list.reverse()
3453 else:
3453 else:
3454 self._ascending = not self._ascending
3454 self._ascending = not self._ascending
3455
3455
3456 def first(self):
3456 def first(self):
3457 for x in self:
3457 for x in self:
3458 return x
3458 return x
3459 return None
3459 return None
3460
3460
3461 def last(self):
3461 def last(self):
3462 self.reverse()
3462 self.reverse()
3463 val = self.first()
3463 val = self.first()
3464 self.reverse()
3464 self.reverse()
3465 return val
3465 return val
3466
3466
3467 def __repr__(self):
3467 def __repr__(self):
3468 d = {None: '', False: '-', True: '+'}[self._ascending]
3468 d = {None: '', False: '-', True: '+'}[self._ascending]
3469 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3469 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3470
3470
3471 class generatorset(abstractsmartset):
3471 class generatorset(abstractsmartset):
3472 """Wrap a generator for lazy iteration
3472 """Wrap a generator for lazy iteration
3473
3473
3474 Wrapper structure for generators that provides lazy membership and can
3474 Wrapper structure for generators that provides lazy membership and can
3475 be iterated more than once.
3475 be iterated more than once.
3476 When asked for membership it generates values until either it finds the
3476 When asked for membership it generates values until either it finds the
3477 requested one or has gone through all the elements in the generator
3477 requested one or has gone through all the elements in the generator
3478 """
3478 """
3479 def __init__(self, gen, iterasc=None):
3479 def __init__(self, gen, iterasc=None):
3480 """
3480 """
3481 gen: a generator producing the values for the generatorset.
3481 gen: a generator producing the values for the generatorset.
3482 """
3482 """
3483 self._gen = gen
3483 self._gen = gen
3484 self._asclist = None
3484 self._asclist = None
3485 self._cache = {}
3485 self._cache = {}
3486 self._genlist = []
3486 self._genlist = []
3487 self._finished = False
3487 self._finished = False
3488 self._ascending = True
3488 self._ascending = True
3489 if iterasc is not None:
3489 if iterasc is not None:
3490 if iterasc:
3490 if iterasc:
3491 self.fastasc = self._iterator
3491 self.fastasc = self._iterator
3492 self.__contains__ = self._asccontains
3492 self.__contains__ = self._asccontains
3493 else:
3493 else:
3494 self.fastdesc = self._iterator
3494 self.fastdesc = self._iterator
3495 self.__contains__ = self._desccontains
3495 self.__contains__ = self._desccontains
3496
3496
3497 def __nonzero__(self):
3497 def __nonzero__(self):
3498 # Do not use 'for r in self' because it will enforce the iteration
3498 # Do not use 'for r in self' because it will enforce the iteration
3499 # order (default ascending), possibly unrolling a whole descending
3499 # order (default ascending), possibly unrolling a whole descending
3500 # iterator.
3500 # iterator.
3501 if self._genlist:
3501 if self._genlist:
3502 return True
3502 return True
3503 for r in self._consumegen():
3503 for r in self._consumegen():
3504 return True
3504 return True
3505 return False
3505 return False
3506
3506
3507 def __contains__(self, x):
3507 def __contains__(self, x):
3508 if x in self._cache:
3508 if x in self._cache:
3509 return self._cache[x]
3509 return self._cache[x]
3510
3510
3511 # Use new values only, as existing values would be cached.
3511 # Use new values only, as existing values would be cached.
3512 for l in self._consumegen():
3512 for l in self._consumegen():
3513 if l == x:
3513 if l == x:
3514 return True
3514 return True
3515
3515
3516 self._cache[x] = False
3516 self._cache[x] = False
3517 return False
3517 return False
3518
3518
3519 def _asccontains(self, x):
3519 def _asccontains(self, x):
3520 """version of contains optimised for ascending generator"""
3520 """version of contains optimised for ascending generator"""
3521 if x in self._cache:
3521 if x in self._cache:
3522 return self._cache[x]
3522 return self._cache[x]
3523
3523
3524 # Use new values only, as existing values would be cached.
3524 # Use new values only, as existing values would be cached.
3525 for l in self._consumegen():
3525 for l in self._consumegen():
3526 if l == x:
3526 if l == x:
3527 return True
3527 return True
3528 if l > x:
3528 if l > x:
3529 break
3529 break
3530
3530
3531 self._cache[x] = False
3531 self._cache[x] = False
3532 return False
3532 return False
3533
3533
3534 def _desccontains(self, x):
3534 def _desccontains(self, x):
3535 """version of contains optimised for descending generator"""
3535 """version of contains optimised for descending generator"""
3536 if x in self._cache:
3536 if x in self._cache:
3537 return self._cache[x]
3537 return self._cache[x]
3538
3538
3539 # Use new values only, as existing values would be cached.
3539 # Use new values only, as existing values would be cached.
3540 for l in self._consumegen():
3540 for l in self._consumegen():
3541 if l == x:
3541 if l == x:
3542 return True
3542 return True
3543 if l < x:
3543 if l < x:
3544 break
3544 break
3545
3545
3546 self._cache[x] = False
3546 self._cache[x] = False
3547 return False
3547 return False
3548
3548
3549 def __iter__(self):
3549 def __iter__(self):
3550 if self._ascending:
3550 if self._ascending:
3551 it = self.fastasc
3551 it = self.fastasc
3552 else:
3552 else:
3553 it = self.fastdesc
3553 it = self.fastdesc
3554 if it is not None:
3554 if it is not None:
3555 return it()
3555 return it()
3556 # we need to consume the iterator
3556 # we need to consume the iterator
3557 for x in self._consumegen():
3557 for x in self._consumegen():
3558 pass
3558 pass
3559 # recall the same code
3559 # recall the same code
3560 return iter(self)
3560 return iter(self)
3561
3561
3562 def _iterator(self):
3562 def _iterator(self):
3563 if self._finished:
3563 if self._finished:
3564 return iter(self._genlist)
3564 return iter(self._genlist)
3565
3565
3566 # We have to use this complex iteration strategy to allow multiple
3566 # We have to use this complex iteration strategy to allow multiple
3567 # iterations at the same time. We need to be able to catch revision
3567 # iterations at the same time. We need to be able to catch revision
3568 # removed from _consumegen and added to genlist in another instance.
3568 # removed from _consumegen and added to genlist in another instance.
3569 #
3569 #
3570 # Getting rid of it would provide an about 15% speed up on this
3570 # Getting rid of it would provide an about 15% speed up on this
3571 # iteration.
3571 # iteration.
3572 genlist = self._genlist
3572 genlist = self._genlist
3573 nextrev = self._consumegen().next
3573 nextrev = self._consumegen().next
3574 _len = len # cache global lookup
3574 _len = len # cache global lookup
3575 def gen():
3575 def gen():
3576 i = 0
3576 i = 0
3577 while True:
3577 while True:
3578 if i < _len(genlist):
3578 if i < _len(genlist):
3579 yield genlist[i]
3579 yield genlist[i]
3580 else:
3580 else:
3581 yield nextrev()
3581 yield nextrev()
3582 i += 1
3582 i += 1
3583 return gen()
3583 return gen()
3584
3584
3585 def _consumegen(self):
3585 def _consumegen(self):
3586 cache = self._cache
3586 cache = self._cache
3587 genlist = self._genlist.append
3587 genlist = self._genlist.append
3588 for item in self._gen:
3588 for item in self._gen:
3589 cache[item] = True
3589 cache[item] = True
3590 genlist(item)
3590 genlist(item)
3591 yield item
3591 yield item
3592 if not self._finished:
3592 if not self._finished:
3593 self._finished = True
3593 self._finished = True
3594 asc = self._genlist[:]
3594 asc = self._genlist[:]
3595 asc.sort()
3595 asc.sort()
3596 self._asclist = asc
3596 self._asclist = asc
3597 self.fastasc = asc.__iter__
3597 self.fastasc = asc.__iter__
3598 self.fastdesc = asc.__reversed__
3598 self.fastdesc = asc.__reversed__
3599
3599
3600 def __len__(self):
3600 def __len__(self):
3601 for x in self._consumegen():
3601 for x in self._consumegen():
3602 pass
3602 pass
3603 return len(self._genlist)
3603 return len(self._genlist)
3604
3604
3605 def sort(self, reverse=False):
3605 def sort(self, reverse=False):
3606 self._ascending = not reverse
3606 self._ascending = not reverse
3607
3607
3608 def reverse(self):
3608 def reverse(self):
3609 self._ascending = not self._ascending
3609 self._ascending = not self._ascending
3610
3610
3611 def isascending(self):
3611 def isascending(self):
3612 return self._ascending
3612 return self._ascending
3613
3613
3614 def isdescending(self):
3614 def isdescending(self):
3615 return not self._ascending
3615 return not self._ascending
3616
3616
3617 def istopo(self):
3617 def istopo(self):
3618 # not worth the trouble asserting if the two sets combined are still
3618 # not worth the trouble asserting if the two sets combined are still
3619 # in topographical order. Use the sort() predicate to explicitly sort
3619 # in topographical order. Use the sort() predicate to explicitly sort
3620 # again instead.
3620 # again instead.
3621 return False
3621 return False
3622
3622
3623 def first(self):
3623 def first(self):
3624 if self._ascending:
3624 if self._ascending:
3625 it = self.fastasc
3625 it = self.fastasc
3626 else:
3626 else:
3627 it = self.fastdesc
3627 it = self.fastdesc
3628 if it is None:
3628 if it is None:
3629 # we need to consume all and try again
3629 # we need to consume all and try again
3630 for x in self._consumegen():
3630 for x in self._consumegen():
3631 pass
3631 pass
3632 return self.first()
3632 return self.first()
3633 return next(it(), None)
3633 return next(it(), None)
3634
3634
3635 def last(self):
3635 def last(self):
3636 if self._ascending:
3636 if self._ascending:
3637 it = self.fastdesc
3637 it = self.fastdesc
3638 else:
3638 else:
3639 it = self.fastasc
3639 it = self.fastasc
3640 if it is None:
3640 if it is None:
3641 # we need to consume all and try again
3641 # we need to consume all and try again
3642 for x in self._consumegen():
3642 for x in self._consumegen():
3643 pass
3643 pass
3644 return self.first()
3644 return self.first()
3645 return next(it(), None)
3645 return next(it(), None)
3646
3646
3647 def __repr__(self):
3647 def __repr__(self):
3648 d = {False: '-', True: '+'}[self._ascending]
3648 d = {False: '-', True: '+'}[self._ascending]
3649 return '<%s%s>' % (type(self).__name__, d)
3649 return '<%s%s>' % (type(self).__name__, d)
3650
3650
3651 class spanset(abstractsmartset):
3651 class spanset(abstractsmartset):
3652 """Duck type for baseset class which represents a range of revisions and
3652 """Duck type for baseset class which represents a range of revisions and
3653 can work lazily and without having all the range in memory
3653 can work lazily and without having all the range in memory
3654
3654
3655 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3655 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3656 notable points:
3656 notable points:
3657 - when x < y it will be automatically descending,
3657 - when x < y it will be automatically descending,
3658 - revision filtered with this repoview will be skipped.
3658 - revision filtered with this repoview will be skipped.
3659
3659
3660 """
3660 """
3661 def __init__(self, repo, start=0, end=None):
3661 def __init__(self, repo, start=0, end=None):
3662 """
3662 """
3663 start: first revision included the set
3663 start: first revision included the set
3664 (default to 0)
3664 (default to 0)
3665 end: first revision excluded (last+1)
3665 end: first revision excluded (last+1)
3666 (default to len(repo)
3666 (default to len(repo)
3667
3667
3668 Spanset will be descending if `end` < `start`.
3668 Spanset will be descending if `end` < `start`.
3669 """
3669 """
3670 if end is None:
3670 if end is None:
3671 end = len(repo)
3671 end = len(repo)
3672 self._ascending = start <= end
3672 self._ascending = start <= end
3673 if not self._ascending:
3673 if not self._ascending:
3674 start, end = end + 1, start +1
3674 start, end = end + 1, start +1
3675 self._start = start
3675 self._start = start
3676 self._end = end
3676 self._end = end
3677 self._hiddenrevs = repo.changelog.filteredrevs
3677 self._hiddenrevs = repo.changelog.filteredrevs
3678
3678
3679 def sort(self, reverse=False):
3679 def sort(self, reverse=False):
3680 self._ascending = not reverse
3680 self._ascending = not reverse
3681
3681
3682 def reverse(self):
3682 def reverse(self):
3683 self._ascending = not self._ascending
3683 self._ascending = not self._ascending
3684
3684
3685 def istopo(self):
3685 def istopo(self):
3686 # not worth the trouble asserting if the two sets combined are still
3686 # not worth the trouble asserting if the two sets combined are still
3687 # in topographical order. Use the sort() predicate to explicitly sort
3687 # in topographical order. Use the sort() predicate to explicitly sort
3688 # again instead.
3688 # again instead.
3689 return False
3689 return False
3690
3690
3691 def _iterfilter(self, iterrange):
3691 def _iterfilter(self, iterrange):
3692 s = self._hiddenrevs
3692 s = self._hiddenrevs
3693 for r in iterrange:
3693 for r in iterrange:
3694 if r not in s:
3694 if r not in s:
3695 yield r
3695 yield r
3696
3696
3697 def __iter__(self):
3697 def __iter__(self):
3698 if self._ascending:
3698 if self._ascending:
3699 return self.fastasc()
3699 return self.fastasc()
3700 else:
3700 else:
3701 return self.fastdesc()
3701 return self.fastdesc()
3702
3702
3703 def fastasc(self):
3703 def fastasc(self):
3704 iterrange = xrange(self._start, self._end)
3704 iterrange = xrange(self._start, self._end)
3705 if self._hiddenrevs:
3705 if self._hiddenrevs:
3706 return self._iterfilter(iterrange)
3706 return self._iterfilter(iterrange)
3707 return iter(iterrange)
3707 return iter(iterrange)
3708
3708
3709 def fastdesc(self):
3709 def fastdesc(self):
3710 iterrange = xrange(self._end - 1, self._start - 1, -1)
3710 iterrange = xrange(self._end - 1, self._start - 1, -1)
3711 if self._hiddenrevs:
3711 if self._hiddenrevs:
3712 return self._iterfilter(iterrange)
3712 return self._iterfilter(iterrange)
3713 return iter(iterrange)
3713 return iter(iterrange)
3714
3714
3715 def __contains__(self, rev):
3715 def __contains__(self, rev):
3716 hidden = self._hiddenrevs
3716 hidden = self._hiddenrevs
3717 return ((self._start <= rev < self._end)
3717 return ((self._start <= rev < self._end)
3718 and not (hidden and rev in hidden))
3718 and not (hidden and rev in hidden))
3719
3719
3720 def __nonzero__(self):
3720 def __nonzero__(self):
3721 for r in self:
3721 for r in self:
3722 return True
3722 return True
3723 return False
3723 return False
3724
3724
3725 def __len__(self):
3725 def __len__(self):
3726 if not self._hiddenrevs:
3726 if not self._hiddenrevs:
3727 return abs(self._end - self._start)
3727 return abs(self._end - self._start)
3728 else:
3728 else:
3729 count = 0
3729 count = 0
3730 start = self._start
3730 start = self._start
3731 end = self._end
3731 end = self._end
3732 for rev in self._hiddenrevs:
3732 for rev in self._hiddenrevs:
3733 if (end < rev <= start) or (start <= rev < end):
3733 if (end < rev <= start) or (start <= rev < end):
3734 count += 1
3734 count += 1
3735 return abs(self._end - self._start) - count
3735 return abs(self._end - self._start) - count
3736
3736
3737 def isascending(self):
3737 def isascending(self):
3738 return self._ascending
3738 return self._ascending
3739
3739
3740 def isdescending(self):
3740 def isdescending(self):
3741 return not self._ascending
3741 return not self._ascending
3742
3742
3743 def first(self):
3743 def first(self):
3744 if self._ascending:
3744 if self._ascending:
3745 it = self.fastasc
3745 it = self.fastasc
3746 else:
3746 else:
3747 it = self.fastdesc
3747 it = self.fastdesc
3748 for x in it():
3748 for x in it():
3749 return x
3749 return x
3750 return None
3750 return None
3751
3751
3752 def last(self):
3752 def last(self):
3753 if self._ascending:
3753 if self._ascending:
3754 it = self.fastdesc
3754 it = self.fastdesc
3755 else:
3755 else:
3756 it = self.fastasc
3756 it = self.fastasc
3757 for x in it():
3757 for x in it():
3758 return x
3758 return x
3759 return None
3759 return None
3760
3760
3761 def __repr__(self):
3761 def __repr__(self):
3762 d = {False: '-', True: '+'}[self._ascending]
3762 d = {False: '-', True: '+'}[self._ascending]
3763 return '<%s%s %d:%d>' % (type(self).__name__, d,
3763 return '<%s%s %d:%d>' % (type(self).__name__, d,
3764 self._start, self._end - 1)
3764 self._start, self._end - 1)
3765
3765
3766 class fullreposet(spanset):
3766 class fullreposet(spanset):
3767 """a set containing all revisions in the repo
3767 """a set containing all revisions in the repo
3768
3768
3769 This class exists to host special optimization and magic to handle virtual
3769 This class exists to host special optimization and magic to handle virtual
3770 revisions such as "null".
3770 revisions such as "null".
3771 """
3771 """
3772
3772
3773 def __init__(self, repo):
3773 def __init__(self, repo):
3774 super(fullreposet, self).__init__(repo)
3774 super(fullreposet, self).__init__(repo)
3775
3775
3776 def __and__(self, other):
3776 def __and__(self, other):
3777 """As self contains the whole repo, all of the other set should also be
3777 """As self contains the whole repo, all of the other set should also be
3778 in self. Therefore `self & other = other`.
3778 in self. Therefore `self & other = other`.
3779
3779
3780 This boldly assumes the other contains valid revs only.
3780 This boldly assumes the other contains valid revs only.
3781 """
3781 """
3782 # other not a smartset, make is so
3782 # other not a smartset, make is so
3783 if not util.safehasattr(other, 'isascending'):
3783 if not util.safehasattr(other, 'isascending'):
3784 # filter out hidden revision
3784 # filter out hidden revision
3785 # (this boldly assumes all smartset are pure)
3785 # (this boldly assumes all smartset are pure)
3786 #
3786 #
3787 # `other` was used with "&", let's assume this is a set like
3787 # `other` was used with "&", let's assume this is a set like
3788 # object.
3788 # object.
3789 other = baseset(other - self._hiddenrevs)
3789 other = baseset(other - self._hiddenrevs)
3790
3790
3791 # XXX As fullreposet is also used as bootstrap, this is wrong.
3791 # XXX As fullreposet is also used as bootstrap, this is wrong.
3792 #
3792 #
3793 # With a giveme312() revset returning [3,1,2], this makes
3793 # With a giveme312() revset returning [3,1,2], this makes
3794 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3794 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3795 # We cannot just drop it because other usage still need to sort it:
3795 # We cannot just drop it because other usage still need to sort it:
3796 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3796 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3797 #
3797 #
3798 # There is also some faulty revset implementations that rely on it
3798 # There is also some faulty revset implementations that rely on it
3799 # (eg: children as of its state in e8075329c5fb)
3799 # (eg: children as of its state in e8075329c5fb)
3800 #
3800 #
3801 # When we fix the two points above we can move this into the if clause
3801 # When we fix the two points above we can move this into the if clause
3802 other.sort(reverse=self.isdescending())
3802 other.sort(reverse=self.isdescending())
3803 return other
3803 return other
3804
3804
3805 def prettyformatset(revs):
3805 def prettyformatset(revs):
3806 lines = []
3806 lines = []
3807 rs = repr(revs)
3807 rs = repr(revs)
3808 p = 0
3808 p = 0
3809 while p < len(rs):
3809 while p < len(rs):
3810 q = rs.find('<', p + 1)
3810 q = rs.find('<', p + 1)
3811 if q < 0:
3811 if q < 0:
3812 q = len(rs)
3812 q = len(rs)
3813 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3813 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3814 assert l >= 0
3814 assert l >= 0
3815 lines.append((l, rs[p:q].rstrip()))
3815 lines.append((l, rs[p:q].rstrip()))
3816 p = q
3816 p = q
3817 return '\n'.join(' ' * l + s for l, s in lines)
3817 return '\n'.join(' ' * l + s for l, s in lines)
3818
3818
3819 def loadpredicate(ui, extname, registrarobj):
3819 def loadpredicate(ui, extname, registrarobj):
3820 """Load revset predicates from specified registrarobj
3820 """Load revset predicates from specified registrarobj
3821 """
3821 """
3822 for name, func in registrarobj._table.iteritems():
3822 for name, func in registrarobj._table.iteritems():
3823 symbols[name] = func
3823 symbols[name] = func
3824 if func._safe:
3824 if func._safe:
3825 safesymbols.add(name)
3825 safesymbols.add(name)
3826
3826
3827 # load built-in predicates explicitly to setup safesymbols
3827 # load built-in predicates explicitly to setup safesymbols
3828 loadpredicate(None, None, predicate)
3828 loadpredicate(None, None, predicate)
3829
3829
3830 # tell hggettext to extract docstrings from these functions:
3830 # tell hggettext to extract docstrings from these functions:
3831 i18nfunctions = symbols.values()
3831 i18nfunctions = symbols.values()
@@ -1,3568 +1,3576
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:
1258 'x:y' takes ordering parameter into account:
1259
1259
1260 $ try -p optimized '3:0 & 0:3 & not 2:1'
1260 $ try -p optimized '3:0 & 0:3 & not 2:1'
1261 * optimized:
1261 * optimized:
1262 (difference
1262 (difference
1263 (and
1263 (and
1264 (range
1264 (range
1265 ('symbol', '3')
1265 ('symbol', '3')
1266 ('symbol', '0')
1266 ('symbol', '0')
1267 define)
1267 define)
1268 (range
1268 (range
1269 ('symbol', '0')
1269 ('symbol', '0')
1270 ('symbol', '3')
1270 ('symbol', '3')
1271 follow)
1271 follow)
1272 define)
1272 define)
1273 (range
1273 (range
1274 ('symbol', '2')
1274 ('symbol', '2')
1275 ('symbol', '1')
1275 ('symbol', '1')
1276 any)
1276 any)
1277 define)
1277 define)
1278 * set:
1278 * set:
1279 <filteredset
1279 <filteredset
1280 <filteredset
1280 <filteredset
1281 <spanset- 0:3>,
1281 <spanset- 0:3>,
1282 <spanset+ 0:3>>,
1282 <spanset+ 0:3>>,
1283 <not
1283 <not
1284 <spanset+ 1:2>>>
1284 <spanset+ 1:2>>>
1285 3
1285 3
1286 0
1286 0
1287
1287
1288 '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
1289 the left expression:
1289 the left expression:
1290
1290
1291 $ try --optimize '2:0 & (0 + 1 + 2)'
1291 $ try --optimize '2:0 & (0 + 1 + 2)'
1292 (and
1292 (and
1293 (range
1293 (range
1294 ('symbol', '2')
1294 ('symbol', '2')
1295 ('symbol', '0'))
1295 ('symbol', '0'))
1296 (group
1296 (group
1297 (or
1297 (or
1298 (list
1298 (list
1299 ('symbol', '0')
1299 ('symbol', '0')
1300 ('symbol', '1')
1300 ('symbol', '1')
1301 ('symbol', '2')))))
1301 ('symbol', '2')))))
1302 * optimized:
1302 * optimized:
1303 (and
1303 (and
1304 (range
1304 (range
1305 ('symbol', '2')
1305 ('symbol', '2')
1306 ('symbol', '0')
1306 ('symbol', '0')
1307 define)
1307 define)
1308 (func
1308 (func
1309 ('symbol', '_list')
1309 ('symbol', '_list')
1310 ('string', '0\x001\x002')
1310 ('string', '0\x001\x002')
1311 follow)
1311 follow)
1312 define)
1312 define)
1313 * set:
1313 * set:
1314 <filteredset
1314 <filteredset
1315 <spanset- 0:2>,
1315 <spanset- 0:2>,
1316 <baseset [0, 1, 2]>>
1316 <baseset [0, 1, 2]>>
1317 2
1317 2
1318 1
1318 1
1319 0
1319 0
1320
1320
1321 'A + B' should take the ordering of the left expression:
1321 'A + B' should take the ordering of the left expression:
1322
1322
1323 $ try --optimize '2:0 & (0:1 + 2)'
1323 $ try --optimize '2:0 & (0:1 + 2)'
1324 (and
1324 (and
1325 (range
1325 (range
1326 ('symbol', '2')
1326 ('symbol', '2')
1327 ('symbol', '0'))
1327 ('symbol', '0'))
1328 (group
1328 (group
1329 (or
1329 (or
1330 (list
1330 (list
1331 (range
1331 (range
1332 ('symbol', '0')
1332 ('symbol', '0')
1333 ('symbol', '1'))
1333 ('symbol', '1'))
1334 ('symbol', '2')))))
1334 ('symbol', '2')))))
1335 * optimized:
1335 * optimized:
1336 (and
1336 (and
1337 (range
1337 (range
1338 ('symbol', '2')
1338 ('symbol', '2')
1339 ('symbol', '0')
1339 ('symbol', '0')
1340 define)
1340 define)
1341 (or
1341 (or
1342 (list
1342 (list
1343 (range
1343 (range
1344 ('symbol', '0')
1344 ('symbol', '0')
1345 ('symbol', '1')
1345 ('symbol', '1')
1346 follow)
1346 follow)
1347 ('symbol', '2'))
1347 ('symbol', '2'))
1348 follow)
1348 follow)
1349 define)
1349 define)
1350 * set:
1350 * set:
1351 <filteredset
1351 <filteredset
1352 <spanset- 0:2>,
1352 <spanset- 0:2>,
1353 <addset
1353 <addset
1354 <spanset+ 0:1>,
1354 <spanset+ 0:1>,
1355 <baseset [2]>>>
1355 <baseset [2]>>>
1356 2
1356 2
1357 1
1357 1
1358 0
1358 0
1359
1359
1360 '_intlist(a b)' should behave like 'a + b':
1360 '_intlist(a b)' should behave like 'a + b':
1361
1361
1362 $ trylist --optimize '2:0 & %ld' 0 1 2
1362 $ trylist --optimize '2:0 & %ld' 0 1 2
1363 (and
1363 (and
1364 (range
1364 (range
1365 ('symbol', '2')
1365 ('symbol', '2')
1366 ('symbol', '0'))
1366 ('symbol', '0'))
1367 (func
1367 (func
1368 ('symbol', '_intlist')
1368 ('symbol', '_intlist')
1369 ('string', '0\x001\x002')))
1369 ('string', '0\x001\x002')))
1370 * optimized:
1370 * optimized:
1371 (and
1371 (and
1372 (func
1372 (func
1373 ('symbol', '_intlist')
1373 ('symbol', '_intlist')
1374 ('string', '0\x001\x002')
1374 ('string', '0\x001\x002')
1375 follow)
1375 follow)
1376 (range
1376 (range
1377 ('symbol', '2')
1377 ('symbol', '2')
1378 ('symbol', '0')
1378 ('symbol', '0')
1379 define)
1379 define)
1380 define)
1380 define)
1381 * set:
1381 * set:
1382 <filteredset
1382 <filteredset
1383 <spanset- 0:2>,
1383 <spanset- 0:2>,
1384 <baseset+ [0, 1, 2]>>
1384 <baseset+ [0, 1, 2]>>
1385 2
1385 2
1386 1
1386 1
1387 0
1387 0
1388
1388
1389 $ trylist --optimize '%ld & 2:0' 0 2 1
1389 $ trylist --optimize '%ld & 2:0' 0 2 1
1390 (and
1390 (and
1391 (func
1391 (func
1392 ('symbol', '_intlist')
1392 ('symbol', '_intlist')
1393 ('string', '0\x002\x001'))
1393 ('string', '0\x002\x001'))
1394 (range
1394 (range
1395 ('symbol', '2')
1395 ('symbol', '2')
1396 ('symbol', '0')))
1396 ('symbol', '0')))
1397 * optimized:
1397 * optimized:
1398 (and
1398 (and
1399 (func
1399 (func
1400 ('symbol', '_intlist')
1400 ('symbol', '_intlist')
1401 ('string', '0\x002\x001')
1401 ('string', '0\x002\x001')
1402 define)
1402 define)
1403 (range
1403 (range
1404 ('symbol', '2')
1404 ('symbol', '2')
1405 ('symbol', '0')
1405 ('symbol', '0')
1406 follow)
1406 follow)
1407 define)
1407 define)
1408 * set:
1408 * set:
1409 <filteredset
1409 <filteredset
1410 <baseset [0, 2, 1]>,
1410 <baseset [0, 2, 1]>,
1411 <spanset- 0:2>>
1411 <spanset- 0:2>>
1412 0
1412 0
1413 2
1413 2
1414 1
1414 1
1415
1415
1416 '_hexlist(a b)' should behave like 'a + b':
1416 '_hexlist(a b)' should behave like 'a + b':
1417
1417
1418 $ 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`
1419 (and
1419 (and
1420 (range
1420 (range
1421 ('symbol', '2')
1421 ('symbol', '2')
1422 ('symbol', '0'))
1422 ('symbol', '0'))
1423 (func
1423 (func
1424 ('symbol', '_hexlist')
1424 ('symbol', '_hexlist')
1425 ('string', '*'))) (glob)
1425 ('string', '*'))) (glob)
1426 * optimized:
1426 * optimized:
1427 (and
1427 (and
1428 (range
1428 (range
1429 ('symbol', '2')
1429 ('symbol', '2')
1430 ('symbol', '0')
1430 ('symbol', '0')
1431 define)
1431 define)
1432 (func
1432 (func
1433 ('symbol', '_hexlist')
1433 ('symbol', '_hexlist')
1434 ('string', '*') (glob)
1434 ('string', '*') (glob)
1435 follow)
1435 follow)
1436 define)
1436 define)
1437 * set:
1437 * set:
1438 <filteredset
1438 <filteredset
1439 <spanset- 0:2>,
1439 <spanset- 0:2>,
1440 <baseset [0, 1, 2]>>
1440 <baseset [0, 1, 2]>>
1441 2
1441 2
1442 1
1442 1
1443 0
1443 0
1444
1444
1445 $ trylist --optimize --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`
1446 (and
1446 (and
1447 (func
1447 (func
1448 ('symbol', '_hexlist')
1448 ('symbol', '_hexlist')
1449 ('string', '*')) (glob)
1449 ('string', '*')) (glob)
1450 (range
1450 (range
1451 ('symbol', '2')
1451 ('symbol', '2')
1452 ('symbol', '0')))
1452 ('symbol', '0')))
1453 * optimized:
1453 * optimized:
1454 (and
1454 (and
1455 (range
1455 (range
1456 ('symbol', '2')
1456 ('symbol', '2')
1457 ('symbol', '0')
1457 ('symbol', '0')
1458 follow)
1458 follow)
1459 (func
1459 (func
1460 ('symbol', '_hexlist')
1460 ('symbol', '_hexlist')
1461 ('string', '*') (glob)
1461 ('string', '*') (glob)
1462 define)
1462 define)
1463 define)
1463 define)
1464 * set:
1464 * set:
1465 <baseset [0, 2, 1]>
1465 <baseset [0, 2, 1]>
1466 0
1466 0
1467 2
1467 2
1468 1
1468 1
1469
1469
1470 '_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
1471 matter:
1471 matter:
1472
1472
1473 $ try -p optimized '2:0 & not (0 + 1)'
1473 $ try -p optimized '2:0 & not (0 + 1)'
1474 * optimized:
1474 * optimized:
1475 (difference
1475 (difference
1476 (range
1476 (range
1477 ('symbol', '2')
1477 ('symbol', '2')
1478 ('symbol', '0')
1478 ('symbol', '0')
1479 define)
1479 define)
1480 (func
1480 (func
1481 ('symbol', '_list')
1481 ('symbol', '_list')
1482 ('string', '0\x001')
1482 ('string', '0\x001')
1483 any)
1483 any)
1484 define)
1484 define)
1485 * set:
1485 * set:
1486 <filteredset
1486 <filteredset
1487 <spanset- 0:2>,
1487 <spanset- 0:2>,
1488 <not
1488 <not
1489 <baseset [0, 1]>>>
1489 <baseset [0, 1]>>>
1490 2
1490 2
1491
1491
1492 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1492 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1493 * optimized:
1493 * optimized:
1494 (difference
1494 (difference
1495 (range
1495 (range
1496 ('symbol', '2')
1496 ('symbol', '2')
1497 ('symbol', '0')
1497 ('symbol', '0')
1498 define)
1498 define)
1499 (and
1499 (and
1500 (range
1500 (range
1501 ('symbol', '0')
1501 ('symbol', '0')
1502 ('symbol', '2')
1502 ('symbol', '2')
1503 any)
1503 any)
1504 (func
1504 (func
1505 ('symbol', '_list')
1505 ('symbol', '_list')
1506 ('string', '0\x001')
1506 ('string', '0\x001')
1507 any)
1507 any)
1508 any)
1508 any)
1509 define)
1509 define)
1510 * set:
1510 * set:
1511 <filteredset
1511 <filteredset
1512 <spanset- 0:2>,
1512 <spanset- 0:2>,
1513 <not
1513 <not
1514 <baseset [0, 1]>>>
1514 <baseset [0, 1]>>>
1515 2
1515 2
1516
1516
1517 because 'present()' does nothing other than suppressing an error, the
1517 because 'present()' does nothing other than suppressing an error, the
1518 ordering requirement should be forwarded to the nested expression
1518 ordering requirement should be forwarded to the nested expression
1519
1519
1520 $ try -p optimized 'present(2 + 0 + 1)'
1520 $ try -p optimized 'present(2 + 0 + 1)'
1521 * optimized:
1521 * optimized:
1522 (func
1522 (func
1523 ('symbol', 'present')
1523 ('symbol', 'present')
1524 (func
1524 (func
1525 ('symbol', '_list')
1525 ('symbol', '_list')
1526 ('string', '2\x000\x001')
1526 ('string', '2\x000\x001')
1527 define)
1527 define)
1528 define)
1528 define)
1529 * set:
1529 * set:
1530 <baseset [2, 0, 1]>
1530 <baseset [2, 0, 1]>
1531 2
1531 2
1532 0
1532 0
1533 1
1533 1
1534
1534
1535 $ try --optimize '2:0 & present(0 + 1 + 2)'
1535 $ try --optimize '2:0 & present(0 + 1 + 2)'
1536 (and
1536 (and
1537 (range
1537 (range
1538 ('symbol', '2')
1538 ('symbol', '2')
1539 ('symbol', '0'))
1539 ('symbol', '0'))
1540 (func
1540 (func
1541 ('symbol', 'present')
1541 ('symbol', 'present')
1542 (or
1542 (or
1543 (list
1543 (list
1544 ('symbol', '0')
1544 ('symbol', '0')
1545 ('symbol', '1')
1545 ('symbol', '1')
1546 ('symbol', '2')))))
1546 ('symbol', '2')))))
1547 * optimized:
1547 * optimized:
1548 (and
1548 (and
1549 (range
1549 (range
1550 ('symbol', '2')
1550 ('symbol', '2')
1551 ('symbol', '0')
1551 ('symbol', '0')
1552 define)
1552 define)
1553 (func
1553 (func
1554 ('symbol', 'present')
1554 ('symbol', 'present')
1555 (func
1555 (func
1556 ('symbol', '_list')
1556 ('symbol', '_list')
1557 ('string', '0\x001\x002')
1557 ('string', '0\x001\x002')
1558 follow)
1558 follow)
1559 follow)
1559 follow)
1560 define)
1560 define)
1561 * set:
1561 * set:
1562 <filteredset
1562 <filteredset
1563 <spanset- 0:2>,
1563 <spanset- 0:2>,
1564 <baseset [0, 1, 2]>>
1564 <baseset [0, 1, 2]>>
1565 2
1565 2
1566 1
1566 1
1567 0
1567 0
1568
1568
1569 'reverse()' should take effect only if it is the outermost expression:
1569 'reverse()' should take effect only if it is the outermost expression:
1570
1570
1571 $ try --optimize '0:2 & reverse(all())'
1571 $ try --optimize '0:2 & reverse(all())'
1572 (and
1572 (and
1573 (range
1573 (range
1574 ('symbol', '0')
1574 ('symbol', '0')
1575 ('symbol', '2'))
1575 ('symbol', '2'))
1576 (func
1576 (func
1577 ('symbol', 'reverse')
1577 ('symbol', 'reverse')
1578 (func
1578 (func
1579 ('symbol', 'all')
1579 ('symbol', 'all')
1580 None)))
1580 None)))
1581 * optimized:
1581 * optimized:
1582 (and
1582 (and
1583 (range
1583 (range
1584 ('symbol', '0')
1584 ('symbol', '0')
1585 ('symbol', '2')
1585 ('symbol', '2')
1586 define)
1586 define)
1587 (func
1587 (func
1588 ('symbol', 'reverse')
1588 ('symbol', 'reverse')
1589 (func
1589 (func
1590 ('symbol', 'all')
1590 ('symbol', 'all')
1591 None
1591 None
1592 define)
1592 define)
1593 follow)
1593 follow)
1594 define)
1594 define)
1595 * set:
1595 * set:
1596 <filteredset
1596 <filteredset
1597 <spanset+ 0:2>,
1597 <spanset+ 0:2>,
1598 <spanset+ 0:9>>
1598 <spanset+ 0:9>>
1599 0
1599 0
1600 1
1600 1
1601 2
1601 2
1602
1602
1603 'sort()' should take effect only if it is the outermost expression:
1603 'sort()' should take effect only if it is the outermost expression:
1604
1604
1605 $ try --optimize '0:2 & sort(all(), -rev)'
1605 $ try --optimize '0:2 & sort(all(), -rev)'
1606 (and
1606 (and
1607 (range
1607 (range
1608 ('symbol', '0')
1608 ('symbol', '0')
1609 ('symbol', '2'))
1609 ('symbol', '2'))
1610 (func
1610 (func
1611 ('symbol', 'sort')
1611 ('symbol', 'sort')
1612 (list
1612 (list
1613 (func
1613 (func
1614 ('symbol', 'all')
1614 ('symbol', 'all')
1615 None)
1615 None)
1616 (negate
1616 (negate
1617 ('symbol', 'rev')))))
1617 ('symbol', 'rev')))))
1618 * optimized:
1618 * optimized:
1619 (and
1619 (and
1620 (range
1620 (range
1621 ('symbol', '0')
1621 ('symbol', '0')
1622 ('symbol', '2')
1622 ('symbol', '2')
1623 define)
1623 define)
1624 (func
1624 (func
1625 ('symbol', 'sort')
1625 ('symbol', 'sort')
1626 (list
1626 (list
1627 (func
1627 (func
1628 ('symbol', 'all')
1628 ('symbol', 'all')
1629 None
1629 None
1630 define)
1630 define)
1631 ('string', '-rev'))
1631 ('string', '-rev'))
1632 follow)
1632 follow)
1633 define)
1633 define)
1634 * set:
1634 * set:
1635 <filteredset
1635 <filteredset
1636 <spanset- 0:2>,
1636 <spanset+ 0:2>,
1637 <spanset+ 0:9>>
1637 <spanset+ 0:9>>
1638 0
1639 1
1638 2
1640 2
1639 1
1641
1640 0
1642 invalid argument passed to noop sort():
1641 BROKEN: should be '0 1 2'
1643
1644 $ log '0:2 & sort()'
1645 hg: parse error: sort requires one or two arguments
1646 [255]
1647 $ log '0:2 & sort(all(), -invalid)'
1648 hg: parse error: unknown sort key '-invalid'
1649 [255]
1642
1650
1643 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1651 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1644
1652
1645 $ try --optimize '2:0 & first(1 + 0 + 2)'
1653 $ try --optimize '2:0 & first(1 + 0 + 2)'
1646 (and
1654 (and
1647 (range
1655 (range
1648 ('symbol', '2')
1656 ('symbol', '2')
1649 ('symbol', '0'))
1657 ('symbol', '0'))
1650 (func
1658 (func
1651 ('symbol', 'first')
1659 ('symbol', 'first')
1652 (or
1660 (or
1653 (list
1661 (list
1654 ('symbol', '1')
1662 ('symbol', '1')
1655 ('symbol', '0')
1663 ('symbol', '0')
1656 ('symbol', '2')))))
1664 ('symbol', '2')))))
1657 * optimized:
1665 * optimized:
1658 (and
1666 (and
1659 (range
1667 (range
1660 ('symbol', '2')
1668 ('symbol', '2')
1661 ('symbol', '0')
1669 ('symbol', '0')
1662 define)
1670 define)
1663 (func
1671 (func
1664 ('symbol', 'first')
1672 ('symbol', 'first')
1665 (func
1673 (func
1666 ('symbol', '_list')
1674 ('symbol', '_list')
1667 ('string', '1\x000\x002')
1675 ('string', '1\x000\x002')
1668 define)
1676 define)
1669 follow)
1677 follow)
1670 define)
1678 define)
1671 * set:
1679 * set:
1672 <baseset
1680 <baseset
1673 <limit n=1, offset=0,
1681 <limit n=1, offset=0,
1674 <spanset- 0:2>,
1682 <spanset- 0:2>,
1675 <baseset [1, 0, 2]>>>
1683 <baseset [1, 0, 2]>>>
1676 1
1684 1
1677
1685
1678 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1686 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1679 (and
1687 (and
1680 (range
1688 (range
1681 ('symbol', '2')
1689 ('symbol', '2')
1682 ('symbol', '0'))
1690 ('symbol', '0'))
1683 (not
1691 (not
1684 (func
1692 (func
1685 ('symbol', 'last')
1693 ('symbol', 'last')
1686 (or
1694 (or
1687 (list
1695 (list
1688 ('symbol', '0')
1696 ('symbol', '0')
1689 ('symbol', '2')
1697 ('symbol', '2')
1690 ('symbol', '1'))))))
1698 ('symbol', '1'))))))
1691 * optimized:
1699 * optimized:
1692 (difference
1700 (difference
1693 (range
1701 (range
1694 ('symbol', '2')
1702 ('symbol', '2')
1695 ('symbol', '0')
1703 ('symbol', '0')
1696 define)
1704 define)
1697 (func
1705 (func
1698 ('symbol', 'last')
1706 ('symbol', 'last')
1699 (func
1707 (func
1700 ('symbol', '_list')
1708 ('symbol', '_list')
1701 ('string', '0\x002\x001')
1709 ('string', '0\x002\x001')
1702 define)
1710 define)
1703 any)
1711 any)
1704 define)
1712 define)
1705 * set:
1713 * set:
1706 <filteredset
1714 <filteredset
1707 <spanset- 0:2>,
1715 <spanset- 0:2>,
1708 <not
1716 <not
1709 <baseset
1717 <baseset
1710 <last n=1,
1718 <last n=1,
1711 <fullreposet+ 0:9>,
1719 <fullreposet+ 0:9>,
1712 <baseset [1, 2, 0]>>>>>
1720 <baseset [1, 2, 0]>>>>>
1713 2
1721 2
1714 0
1722 0
1715
1723
1716 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1724 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1717
1725
1718 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1726 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1719 (and
1727 (and
1720 (range
1728 (range
1721 ('symbol', '2')
1729 ('symbol', '2')
1722 ('symbol', '0'))
1730 ('symbol', '0'))
1723 (range
1731 (range
1724 (group
1732 (group
1725 (or
1733 (or
1726 (list
1734 (list
1727 ('symbol', '1')
1735 ('symbol', '1')
1728 ('symbol', '0')
1736 ('symbol', '0')
1729 ('symbol', '2'))))
1737 ('symbol', '2'))))
1730 (group
1738 (group
1731 (or
1739 (or
1732 (list
1740 (list
1733 ('symbol', '0')
1741 ('symbol', '0')
1734 ('symbol', '2')
1742 ('symbol', '2')
1735 ('symbol', '1'))))))
1743 ('symbol', '1'))))))
1736 * optimized:
1744 * optimized:
1737 (and
1745 (and
1738 (range
1746 (range
1739 ('symbol', '2')
1747 ('symbol', '2')
1740 ('symbol', '0')
1748 ('symbol', '0')
1741 define)
1749 define)
1742 (range
1750 (range
1743 (func
1751 (func
1744 ('symbol', '_list')
1752 ('symbol', '_list')
1745 ('string', '1\x000\x002')
1753 ('string', '1\x000\x002')
1746 define)
1754 define)
1747 (func
1755 (func
1748 ('symbol', '_list')
1756 ('symbol', '_list')
1749 ('string', '0\x002\x001')
1757 ('string', '0\x002\x001')
1750 define)
1758 define)
1751 follow)
1759 follow)
1752 define)
1760 define)
1753 * set:
1761 * set:
1754 <filteredset
1762 <filteredset
1755 <spanset- 0:2>,
1763 <spanset- 0:2>,
1756 <baseset [1]>>
1764 <baseset [1]>>
1757 1
1765 1
1758
1766
1759 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1767 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1760 the ordering rule is determined before the rewrite; in this example,
1768 the ordering rule is determined before the rewrite; in this example,
1761 'B' follows the order of the initial set, which is the same order as 'A'
1769 'B' follows the order of the initial set, which is the same order as 'A'
1762 since 'A' also follows the order:
1770 since 'A' also follows the order:
1763
1771
1764 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1772 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1765 (and
1773 (and
1766 (func
1774 (func
1767 ('symbol', 'contains')
1775 ('symbol', 'contains')
1768 ('string', 'glob:*'))
1776 ('string', 'glob:*'))
1769 (group
1777 (group
1770 (or
1778 (or
1771 (list
1779 (list
1772 ('symbol', '2')
1780 ('symbol', '2')
1773 ('symbol', '0')
1781 ('symbol', '0')
1774 ('symbol', '1')))))
1782 ('symbol', '1')))))
1775 * optimized:
1783 * optimized:
1776 (and
1784 (and
1777 (func
1785 (func
1778 ('symbol', '_list')
1786 ('symbol', '_list')
1779 ('string', '2\x000\x001')
1787 ('string', '2\x000\x001')
1780 follow)
1788 follow)
1781 (func
1789 (func
1782 ('symbol', 'contains')
1790 ('symbol', 'contains')
1783 ('string', 'glob:*')
1791 ('string', 'glob:*')
1784 define)
1792 define)
1785 define)
1793 define)
1786 * set:
1794 * set:
1787 <filteredset
1795 <filteredset
1788 <baseset+ [0, 1, 2]>,
1796 <baseset+ [0, 1, 2]>,
1789 <contains 'glob:*'>>
1797 <contains 'glob:*'>>
1790 0
1798 0
1791 1
1799 1
1792 2
1800 2
1793
1801
1794 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1802 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1795 the order appropriately:
1803 the order appropriately:
1796
1804
1797 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1805 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1798 (and
1806 (and
1799 (func
1807 (func
1800 ('symbol', 'reverse')
1808 ('symbol', 'reverse')
1801 (func
1809 (func
1802 ('symbol', 'contains')
1810 ('symbol', 'contains')
1803 ('string', 'glob:*')))
1811 ('string', 'glob:*')))
1804 (group
1812 (group
1805 (or
1813 (or
1806 (list
1814 (list
1807 ('symbol', '0')
1815 ('symbol', '0')
1808 ('symbol', '2')
1816 ('symbol', '2')
1809 ('symbol', '1')))))
1817 ('symbol', '1')))))
1810 * optimized:
1818 * optimized:
1811 (and
1819 (and
1812 (func
1820 (func
1813 ('symbol', '_list')
1821 ('symbol', '_list')
1814 ('string', '0\x002\x001')
1822 ('string', '0\x002\x001')
1815 follow)
1823 follow)
1816 (func
1824 (func
1817 ('symbol', 'reverse')
1825 ('symbol', 'reverse')
1818 (func
1826 (func
1819 ('symbol', 'contains')
1827 ('symbol', 'contains')
1820 ('string', 'glob:*')
1828 ('string', 'glob:*')
1821 define)
1829 define)
1822 define)
1830 define)
1823 define)
1831 define)
1824 * set:
1832 * set:
1825 <filteredset
1833 <filteredset
1826 <baseset- [0, 1, 2]>,
1834 <baseset- [0, 1, 2]>,
1827 <contains 'glob:*'>>
1835 <contains 'glob:*'>>
1828 2
1836 2
1829 1
1837 1
1830 0
1838 0
1831
1839
1832 test sort revset
1840 test sort revset
1833 --------------------------------------------
1841 --------------------------------------------
1834
1842
1835 test when adding two unordered revsets
1843 test when adding two unordered revsets
1836
1844
1837 $ log 'sort(keyword(issue) or modifies(b))'
1845 $ log 'sort(keyword(issue) or modifies(b))'
1838 4
1846 4
1839 6
1847 6
1840
1848
1841 test when sorting a reversed collection in the same way it is
1849 test when sorting a reversed collection in the same way it is
1842
1850
1843 $ log 'sort(reverse(all()), -rev)'
1851 $ log 'sort(reverse(all()), -rev)'
1844 9
1852 9
1845 8
1853 8
1846 7
1854 7
1847 6
1855 6
1848 5
1856 5
1849 4
1857 4
1850 3
1858 3
1851 2
1859 2
1852 1
1860 1
1853 0
1861 0
1854
1862
1855 test when sorting a reversed collection
1863 test when sorting a reversed collection
1856
1864
1857 $ log 'sort(reverse(all()), rev)'
1865 $ log 'sort(reverse(all()), rev)'
1858 0
1866 0
1859 1
1867 1
1860 2
1868 2
1861 3
1869 3
1862 4
1870 4
1863 5
1871 5
1864 6
1872 6
1865 7
1873 7
1866 8
1874 8
1867 9
1875 9
1868
1876
1869
1877
1870 test sorting two sorted collections in different orders
1878 test sorting two sorted collections in different orders
1871
1879
1872 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1880 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1873 2
1881 2
1874 6
1882 6
1875 8
1883 8
1876 9
1884 9
1877
1885
1878 test sorting two sorted collections in different orders backwards
1886 test sorting two sorted collections in different orders backwards
1879
1887
1880 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1888 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1881 9
1889 9
1882 8
1890 8
1883 6
1891 6
1884 2
1892 2
1885
1893
1886 test empty sort key which is noop
1894 test empty sort key which is noop
1887
1895
1888 $ log 'sort(0 + 2 + 1, "")'
1896 $ log 'sort(0 + 2 + 1, "")'
1889 0
1897 0
1890 2
1898 2
1891 1
1899 1
1892
1900
1893 test invalid sort keys
1901 test invalid sort keys
1894
1902
1895 $ log 'sort(all(), -invalid)'
1903 $ log 'sort(all(), -invalid)'
1896 hg: parse error: unknown sort key '-invalid'
1904 hg: parse error: unknown sort key '-invalid'
1897 [255]
1905 [255]
1898
1906
1899 $ cd ..
1907 $ cd ..
1900
1908
1901 test sorting by multiple keys including variable-length strings
1909 test sorting by multiple keys including variable-length strings
1902
1910
1903 $ hg init sorting
1911 $ hg init sorting
1904 $ cd sorting
1912 $ cd sorting
1905 $ cat <<EOF >> .hg/hgrc
1913 $ cat <<EOF >> .hg/hgrc
1906 > [ui]
1914 > [ui]
1907 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1915 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1908 > [templatealias]
1916 > [templatealias]
1909 > p5(s) = pad(s, 5)
1917 > p5(s) = pad(s, 5)
1910 > EOF
1918 > EOF
1911 $ hg branch -qf b12
1919 $ hg branch -qf b12
1912 $ hg ci -m m111 -u u112 -d '111 10800'
1920 $ hg ci -m m111 -u u112 -d '111 10800'
1913 $ hg branch -qf b11
1921 $ hg branch -qf b11
1914 $ hg ci -m m12 -u u111 -d '112 7200'
1922 $ hg ci -m m12 -u u111 -d '112 7200'
1915 $ hg branch -qf b111
1923 $ hg branch -qf b111
1916 $ hg ci -m m11 -u u12 -d '111 3600'
1924 $ hg ci -m m11 -u u12 -d '111 3600'
1917 $ hg branch -qf b112
1925 $ hg branch -qf b112
1918 $ hg ci -m m111 -u u11 -d '120 0'
1926 $ hg ci -m m111 -u u11 -d '120 0'
1919 $ hg branch -qf b111
1927 $ hg branch -qf b111
1920 $ hg ci -m m112 -u u111 -d '110 14400'
1928 $ hg ci -m m112 -u u111 -d '110 14400'
1921 created new head
1929 created new head
1922
1930
1923 compare revisions (has fast path):
1931 compare revisions (has fast path):
1924
1932
1925 $ hg log -r 'sort(all(), rev)'
1933 $ hg log -r 'sort(all(), rev)'
1926 0 b12 m111 u112 111 10800
1934 0 b12 m111 u112 111 10800
1927 1 b11 m12 u111 112 7200
1935 1 b11 m12 u111 112 7200
1928 2 b111 m11 u12 111 3600
1936 2 b111 m11 u12 111 3600
1929 3 b112 m111 u11 120 0
1937 3 b112 m111 u11 120 0
1930 4 b111 m112 u111 110 14400
1938 4 b111 m112 u111 110 14400
1931
1939
1932 $ hg log -r 'sort(all(), -rev)'
1940 $ hg log -r 'sort(all(), -rev)'
1933 4 b111 m112 u111 110 14400
1941 4 b111 m112 u111 110 14400
1934 3 b112 m111 u11 120 0
1942 3 b112 m111 u11 120 0
1935 2 b111 m11 u12 111 3600
1943 2 b111 m11 u12 111 3600
1936 1 b11 m12 u111 112 7200
1944 1 b11 m12 u111 112 7200
1937 0 b12 m111 u112 111 10800
1945 0 b12 m111 u112 111 10800
1938
1946
1939 compare variable-length strings (issue5218):
1947 compare variable-length strings (issue5218):
1940
1948
1941 $ hg log -r 'sort(all(), branch)'
1949 $ hg log -r 'sort(all(), branch)'
1942 1 b11 m12 u111 112 7200
1950 1 b11 m12 u111 112 7200
1943 2 b111 m11 u12 111 3600
1951 2 b111 m11 u12 111 3600
1944 4 b111 m112 u111 110 14400
1952 4 b111 m112 u111 110 14400
1945 3 b112 m111 u11 120 0
1953 3 b112 m111 u11 120 0
1946 0 b12 m111 u112 111 10800
1954 0 b12 m111 u112 111 10800
1947
1955
1948 $ hg log -r 'sort(all(), -branch)'
1956 $ hg log -r 'sort(all(), -branch)'
1949 0 b12 m111 u112 111 10800
1957 0 b12 m111 u112 111 10800
1950 3 b112 m111 u11 120 0
1958 3 b112 m111 u11 120 0
1951 2 b111 m11 u12 111 3600
1959 2 b111 m11 u12 111 3600
1952 4 b111 m112 u111 110 14400
1960 4 b111 m112 u111 110 14400
1953 1 b11 m12 u111 112 7200
1961 1 b11 m12 u111 112 7200
1954
1962
1955 $ hg log -r 'sort(all(), desc)'
1963 $ hg log -r 'sort(all(), desc)'
1956 2 b111 m11 u12 111 3600
1964 2 b111 m11 u12 111 3600
1957 0 b12 m111 u112 111 10800
1965 0 b12 m111 u112 111 10800
1958 3 b112 m111 u11 120 0
1966 3 b112 m111 u11 120 0
1959 4 b111 m112 u111 110 14400
1967 4 b111 m112 u111 110 14400
1960 1 b11 m12 u111 112 7200
1968 1 b11 m12 u111 112 7200
1961
1969
1962 $ hg log -r 'sort(all(), -desc)'
1970 $ hg log -r 'sort(all(), -desc)'
1963 1 b11 m12 u111 112 7200
1971 1 b11 m12 u111 112 7200
1964 4 b111 m112 u111 110 14400
1972 4 b111 m112 u111 110 14400
1965 0 b12 m111 u112 111 10800
1973 0 b12 m111 u112 111 10800
1966 3 b112 m111 u11 120 0
1974 3 b112 m111 u11 120 0
1967 2 b111 m11 u12 111 3600
1975 2 b111 m11 u12 111 3600
1968
1976
1969 $ hg log -r 'sort(all(), user)'
1977 $ hg log -r 'sort(all(), user)'
1970 3 b112 m111 u11 120 0
1978 3 b112 m111 u11 120 0
1971 1 b11 m12 u111 112 7200
1979 1 b11 m12 u111 112 7200
1972 4 b111 m112 u111 110 14400
1980 4 b111 m112 u111 110 14400
1973 0 b12 m111 u112 111 10800
1981 0 b12 m111 u112 111 10800
1974 2 b111 m11 u12 111 3600
1982 2 b111 m11 u12 111 3600
1975
1983
1976 $ hg log -r 'sort(all(), -user)'
1984 $ hg log -r 'sort(all(), -user)'
1977 2 b111 m11 u12 111 3600
1985 2 b111 m11 u12 111 3600
1978 0 b12 m111 u112 111 10800
1986 0 b12 m111 u112 111 10800
1979 1 b11 m12 u111 112 7200
1987 1 b11 m12 u111 112 7200
1980 4 b111 m112 u111 110 14400
1988 4 b111 m112 u111 110 14400
1981 3 b112 m111 u11 120 0
1989 3 b112 m111 u11 120 0
1982
1990
1983 compare dates (tz offset should have no effect):
1991 compare dates (tz offset should have no effect):
1984
1992
1985 $ hg log -r 'sort(all(), date)'
1993 $ hg log -r 'sort(all(), date)'
1986 4 b111 m112 u111 110 14400
1994 4 b111 m112 u111 110 14400
1987 0 b12 m111 u112 111 10800
1995 0 b12 m111 u112 111 10800
1988 2 b111 m11 u12 111 3600
1996 2 b111 m11 u12 111 3600
1989 1 b11 m12 u111 112 7200
1997 1 b11 m12 u111 112 7200
1990 3 b112 m111 u11 120 0
1998 3 b112 m111 u11 120 0
1991
1999
1992 $ hg log -r 'sort(all(), -date)'
2000 $ hg log -r 'sort(all(), -date)'
1993 3 b112 m111 u11 120 0
2001 3 b112 m111 u11 120 0
1994 1 b11 m12 u111 112 7200
2002 1 b11 m12 u111 112 7200
1995 0 b12 m111 u112 111 10800
2003 0 b12 m111 u112 111 10800
1996 2 b111 m11 u12 111 3600
2004 2 b111 m11 u12 111 3600
1997 4 b111 m112 u111 110 14400
2005 4 b111 m112 u111 110 14400
1998
2006
1999 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2007 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2000 because '-k' reverses the comparison, not the list itself:
2008 because '-k' reverses the comparison, not the list itself:
2001
2009
2002 $ hg log -r 'sort(0 + 2, date)'
2010 $ hg log -r 'sort(0 + 2, date)'
2003 0 b12 m111 u112 111 10800
2011 0 b12 m111 u112 111 10800
2004 2 b111 m11 u12 111 3600
2012 2 b111 m11 u12 111 3600
2005
2013
2006 $ hg log -r 'sort(0 + 2, -date)'
2014 $ hg log -r 'sort(0 + 2, -date)'
2007 0 b12 m111 u112 111 10800
2015 0 b12 m111 u112 111 10800
2008 2 b111 m11 u12 111 3600
2016 2 b111 m11 u12 111 3600
2009
2017
2010 $ hg log -r 'reverse(sort(0 + 2, date))'
2018 $ hg log -r 'reverse(sort(0 + 2, date))'
2011 2 b111 m11 u12 111 3600
2019 2 b111 m11 u12 111 3600
2012 0 b12 m111 u112 111 10800
2020 0 b12 m111 u112 111 10800
2013
2021
2014 sort by multiple keys:
2022 sort by multiple keys:
2015
2023
2016 $ hg log -r 'sort(all(), "branch -rev")'
2024 $ hg log -r 'sort(all(), "branch -rev")'
2017 1 b11 m12 u111 112 7200
2025 1 b11 m12 u111 112 7200
2018 4 b111 m112 u111 110 14400
2026 4 b111 m112 u111 110 14400
2019 2 b111 m11 u12 111 3600
2027 2 b111 m11 u12 111 3600
2020 3 b112 m111 u11 120 0
2028 3 b112 m111 u11 120 0
2021 0 b12 m111 u112 111 10800
2029 0 b12 m111 u112 111 10800
2022
2030
2023 $ hg log -r 'sort(all(), "-desc -date")'
2031 $ hg log -r 'sort(all(), "-desc -date")'
2024 1 b11 m12 u111 112 7200
2032 1 b11 m12 u111 112 7200
2025 4 b111 m112 u111 110 14400
2033 4 b111 m112 u111 110 14400
2026 3 b112 m111 u11 120 0
2034 3 b112 m111 u11 120 0
2027 0 b12 m111 u112 111 10800
2035 0 b12 m111 u112 111 10800
2028 2 b111 m11 u12 111 3600
2036 2 b111 m11 u12 111 3600
2029
2037
2030 $ hg log -r 'sort(all(), "user -branch date rev")'
2038 $ hg log -r 'sort(all(), "user -branch date rev")'
2031 3 b112 m111 u11 120 0
2039 3 b112 m111 u11 120 0
2032 4 b111 m112 u111 110 14400
2040 4 b111 m112 u111 110 14400
2033 1 b11 m12 u111 112 7200
2041 1 b11 m12 u111 112 7200
2034 0 b12 m111 u112 111 10800
2042 0 b12 m111 u112 111 10800
2035 2 b111 m11 u12 111 3600
2043 2 b111 m11 u12 111 3600
2036
2044
2037 toposort prioritises graph branches
2045 toposort prioritises graph branches
2038
2046
2039 $ hg up 2
2047 $ hg up 2
2040 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2048 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2041 $ touch a
2049 $ touch a
2042 $ hg addremove
2050 $ hg addremove
2043 adding a
2051 adding a
2044 $ hg ci -m 't1' -u 'tu' -d '130 0'
2052 $ hg ci -m 't1' -u 'tu' -d '130 0'
2045 created new head
2053 created new head
2046 $ echo 'a' >> a
2054 $ echo 'a' >> a
2047 $ hg ci -m 't2' -u 'tu' -d '130 0'
2055 $ hg ci -m 't2' -u 'tu' -d '130 0'
2048 $ hg book book1
2056 $ hg book book1
2049 $ hg up 4
2057 $ hg up 4
2050 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2058 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2051 (leaving bookmark book1)
2059 (leaving bookmark book1)
2052 $ touch a
2060 $ touch a
2053 $ hg addremove
2061 $ hg addremove
2054 adding a
2062 adding a
2055 $ hg ci -m 't3' -u 'tu' -d '130 0'
2063 $ hg ci -m 't3' -u 'tu' -d '130 0'
2056
2064
2057 $ hg log -r 'sort(all(), topo)'
2065 $ hg log -r 'sort(all(), topo)'
2058 7 b111 t3 tu 130 0
2066 7 b111 t3 tu 130 0
2059 4 b111 m112 u111 110 14400
2067 4 b111 m112 u111 110 14400
2060 3 b112 m111 u11 120 0
2068 3 b112 m111 u11 120 0
2061 6 b111 t2 tu 130 0
2069 6 b111 t2 tu 130 0
2062 5 b111 t1 tu 130 0
2070 5 b111 t1 tu 130 0
2063 2 b111 m11 u12 111 3600
2071 2 b111 m11 u12 111 3600
2064 1 b11 m12 u111 112 7200
2072 1 b11 m12 u111 112 7200
2065 0 b12 m111 u112 111 10800
2073 0 b12 m111 u112 111 10800
2066
2074
2067 $ hg log -r 'sort(all(), -topo)'
2075 $ hg log -r 'sort(all(), -topo)'
2068 0 b12 m111 u112 111 10800
2076 0 b12 m111 u112 111 10800
2069 1 b11 m12 u111 112 7200
2077 1 b11 m12 u111 112 7200
2070 2 b111 m11 u12 111 3600
2078 2 b111 m11 u12 111 3600
2071 5 b111 t1 tu 130 0
2079 5 b111 t1 tu 130 0
2072 6 b111 t2 tu 130 0
2080 6 b111 t2 tu 130 0
2073 3 b112 m111 u11 120 0
2081 3 b112 m111 u11 120 0
2074 4 b111 m112 u111 110 14400
2082 4 b111 m112 u111 110 14400
2075 7 b111 t3 tu 130 0
2083 7 b111 t3 tu 130 0
2076
2084
2077 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2085 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2078 6 b111 t2 tu 130 0
2086 6 b111 t2 tu 130 0
2079 5 b111 t1 tu 130 0
2087 5 b111 t1 tu 130 0
2080 7 b111 t3 tu 130 0
2088 7 b111 t3 tu 130 0
2081 4 b111 m112 u111 110 14400
2089 4 b111 m112 u111 110 14400
2082 3 b112 m111 u11 120 0
2090 3 b112 m111 u11 120 0
2083 2 b111 m11 u12 111 3600
2091 2 b111 m11 u12 111 3600
2084 1 b11 m12 u111 112 7200
2092 1 b11 m12 u111 112 7200
2085 0 b12 m111 u112 111 10800
2093 0 b12 m111 u112 111 10800
2086
2094
2087 topographical sorting can't be combined with other sort keys, and you can't
2095 topographical sorting can't be combined with other sort keys, and you can't
2088 use the topo.firstbranch option when topo sort is not active:
2096 use the topo.firstbranch option when topo sort is not active:
2089
2097
2090 $ hg log -r 'sort(all(), "topo user")'
2098 $ hg log -r 'sort(all(), "topo user")'
2091 hg: parse error: topo sort order cannot be combined with other sort keys
2099 hg: parse error: topo sort order cannot be combined with other sort keys
2092 [255]
2100 [255]
2093
2101
2094 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2102 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2095 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2103 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2096 [255]
2104 [255]
2097
2105
2098 topo.firstbranch should accept any kind of expressions:
2106 topo.firstbranch should accept any kind of expressions:
2099
2107
2100 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2108 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2101 0 b12 m111 u112 111 10800
2109 0 b12 m111 u112 111 10800
2102
2110
2103 $ cd ..
2111 $ cd ..
2104 $ cd repo
2112 $ cd repo
2105
2113
2106 test subtracting something from an addset
2114 test subtracting something from an addset
2107
2115
2108 $ log '(outgoing() or removes(a)) - removes(a)'
2116 $ log '(outgoing() or removes(a)) - removes(a)'
2109 8
2117 8
2110 9
2118 9
2111
2119
2112 test intersecting something with an addset
2120 test intersecting something with an addset
2113
2121
2114 $ log 'parents(outgoing() or removes(a))'
2122 $ log 'parents(outgoing() or removes(a))'
2115 1
2123 1
2116 4
2124 4
2117 5
2125 5
2118 8
2126 8
2119
2127
2120 test that `or` operation combines elements in the right order:
2128 test that `or` operation combines elements in the right order:
2121
2129
2122 $ log '3:4 or 2:5'
2130 $ log '3:4 or 2:5'
2123 3
2131 3
2124 4
2132 4
2125 2
2133 2
2126 5
2134 5
2127 $ log '3:4 or 5:2'
2135 $ log '3:4 or 5:2'
2128 3
2136 3
2129 4
2137 4
2130 5
2138 5
2131 2
2139 2
2132 $ log 'sort(3:4 or 2:5)'
2140 $ log 'sort(3:4 or 2:5)'
2133 2
2141 2
2134 3
2142 3
2135 4
2143 4
2136 5
2144 5
2137 $ log 'sort(3:4 or 5:2)'
2145 $ log 'sort(3:4 or 5:2)'
2138 2
2146 2
2139 3
2147 3
2140 4
2148 4
2141 5
2149 5
2142
2150
2143 test that more than one `-r`s are combined in the right order and deduplicated:
2151 test that more than one `-r`s are combined in the right order and deduplicated:
2144
2152
2145 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2153 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2146 3
2154 3
2147 4
2155 4
2148 5
2156 5
2149 2
2157 2
2150 0
2158 0
2151 1
2159 1
2152
2160
2153 test that `or` operation skips duplicated revisions from right-hand side
2161 test that `or` operation skips duplicated revisions from right-hand side
2154
2162
2155 $ try 'reverse(1::5) or ancestors(4)'
2163 $ try 'reverse(1::5) or ancestors(4)'
2156 (or
2164 (or
2157 (list
2165 (list
2158 (func
2166 (func
2159 ('symbol', 'reverse')
2167 ('symbol', 'reverse')
2160 (dagrange
2168 (dagrange
2161 ('symbol', '1')
2169 ('symbol', '1')
2162 ('symbol', '5')))
2170 ('symbol', '5')))
2163 (func
2171 (func
2164 ('symbol', 'ancestors')
2172 ('symbol', 'ancestors')
2165 ('symbol', '4'))))
2173 ('symbol', '4'))))
2166 * set:
2174 * set:
2167 <addset
2175 <addset
2168 <baseset- [1, 3, 5]>,
2176 <baseset- [1, 3, 5]>,
2169 <generatorset+>>
2177 <generatorset+>>
2170 5
2178 5
2171 3
2179 3
2172 1
2180 1
2173 0
2181 0
2174 2
2182 2
2175 4
2183 4
2176 $ try 'sort(ancestors(4) or reverse(1::5))'
2184 $ try 'sort(ancestors(4) or reverse(1::5))'
2177 (func
2185 (func
2178 ('symbol', 'sort')
2186 ('symbol', 'sort')
2179 (or
2187 (or
2180 (list
2188 (list
2181 (func
2189 (func
2182 ('symbol', 'ancestors')
2190 ('symbol', 'ancestors')
2183 ('symbol', '4'))
2191 ('symbol', '4'))
2184 (func
2192 (func
2185 ('symbol', 'reverse')
2193 ('symbol', 'reverse')
2186 (dagrange
2194 (dagrange
2187 ('symbol', '1')
2195 ('symbol', '1')
2188 ('symbol', '5'))))))
2196 ('symbol', '5'))))))
2189 * set:
2197 * set:
2190 <addset+
2198 <addset+
2191 <generatorset+>,
2199 <generatorset+>,
2192 <baseset- [1, 3, 5]>>
2200 <baseset- [1, 3, 5]>>
2193 0
2201 0
2194 1
2202 1
2195 2
2203 2
2196 3
2204 3
2197 4
2205 4
2198 5
2206 5
2199
2207
2200 test optimization of trivial `or` operation
2208 test optimization of trivial `or` operation
2201
2209
2202 $ try --optimize '0|(1)|"2"|-2|tip|null'
2210 $ try --optimize '0|(1)|"2"|-2|tip|null'
2203 (or
2211 (or
2204 (list
2212 (list
2205 ('symbol', '0')
2213 ('symbol', '0')
2206 (group
2214 (group
2207 ('symbol', '1'))
2215 ('symbol', '1'))
2208 ('string', '2')
2216 ('string', '2')
2209 (negate
2217 (negate
2210 ('symbol', '2'))
2218 ('symbol', '2'))
2211 ('symbol', 'tip')
2219 ('symbol', 'tip')
2212 ('symbol', 'null')))
2220 ('symbol', 'null')))
2213 * optimized:
2221 * optimized:
2214 (func
2222 (func
2215 ('symbol', '_list')
2223 ('symbol', '_list')
2216 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2224 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2217 define)
2225 define)
2218 * set:
2226 * set:
2219 <baseset [0, 1, 2, 8, 9, -1]>
2227 <baseset [0, 1, 2, 8, 9, -1]>
2220 0
2228 0
2221 1
2229 1
2222 2
2230 2
2223 8
2231 8
2224 9
2232 9
2225 -1
2233 -1
2226
2234
2227 $ try --optimize '0|1|2:3'
2235 $ try --optimize '0|1|2:3'
2228 (or
2236 (or
2229 (list
2237 (list
2230 ('symbol', '0')
2238 ('symbol', '0')
2231 ('symbol', '1')
2239 ('symbol', '1')
2232 (range
2240 (range
2233 ('symbol', '2')
2241 ('symbol', '2')
2234 ('symbol', '3'))))
2242 ('symbol', '3'))))
2235 * optimized:
2243 * optimized:
2236 (or
2244 (or
2237 (list
2245 (list
2238 (func
2246 (func
2239 ('symbol', '_list')
2247 ('symbol', '_list')
2240 ('string', '0\x001')
2248 ('string', '0\x001')
2241 define)
2249 define)
2242 (range
2250 (range
2243 ('symbol', '2')
2251 ('symbol', '2')
2244 ('symbol', '3')
2252 ('symbol', '3')
2245 define))
2253 define))
2246 define)
2254 define)
2247 * set:
2255 * set:
2248 <addset
2256 <addset
2249 <baseset [0, 1]>,
2257 <baseset [0, 1]>,
2250 <spanset+ 2:3>>
2258 <spanset+ 2:3>>
2251 0
2259 0
2252 1
2260 1
2253 2
2261 2
2254 3
2262 3
2255
2263
2256 $ try --optimize '0:1|2|3:4|5|6'
2264 $ try --optimize '0:1|2|3:4|5|6'
2257 (or
2265 (or
2258 (list
2266 (list
2259 (range
2267 (range
2260 ('symbol', '0')
2268 ('symbol', '0')
2261 ('symbol', '1'))
2269 ('symbol', '1'))
2262 ('symbol', '2')
2270 ('symbol', '2')
2263 (range
2271 (range
2264 ('symbol', '3')
2272 ('symbol', '3')
2265 ('symbol', '4'))
2273 ('symbol', '4'))
2266 ('symbol', '5')
2274 ('symbol', '5')
2267 ('symbol', '6')))
2275 ('symbol', '6')))
2268 * optimized:
2276 * optimized:
2269 (or
2277 (or
2270 (list
2278 (list
2271 (range
2279 (range
2272 ('symbol', '0')
2280 ('symbol', '0')
2273 ('symbol', '1')
2281 ('symbol', '1')
2274 define)
2282 define)
2275 ('symbol', '2')
2283 ('symbol', '2')
2276 (range
2284 (range
2277 ('symbol', '3')
2285 ('symbol', '3')
2278 ('symbol', '4')
2286 ('symbol', '4')
2279 define)
2287 define)
2280 (func
2288 (func
2281 ('symbol', '_list')
2289 ('symbol', '_list')
2282 ('string', '5\x006')
2290 ('string', '5\x006')
2283 define))
2291 define))
2284 define)
2292 define)
2285 * set:
2293 * set:
2286 <addset
2294 <addset
2287 <addset
2295 <addset
2288 <spanset+ 0:1>,
2296 <spanset+ 0:1>,
2289 <baseset [2]>>,
2297 <baseset [2]>>,
2290 <addset
2298 <addset
2291 <spanset+ 3:4>,
2299 <spanset+ 3:4>,
2292 <baseset [5, 6]>>>
2300 <baseset [5, 6]>>>
2293 0
2301 0
2294 1
2302 1
2295 2
2303 2
2296 3
2304 3
2297 4
2305 4
2298 5
2306 5
2299 6
2307 6
2300
2308
2301 unoptimized `or` looks like this
2309 unoptimized `or` looks like this
2302
2310
2303 $ try --no-optimized -p analyzed '0|1|2|3|4'
2311 $ try --no-optimized -p analyzed '0|1|2|3|4'
2304 * analyzed:
2312 * analyzed:
2305 (or
2313 (or
2306 (list
2314 (list
2307 ('symbol', '0')
2315 ('symbol', '0')
2308 ('symbol', '1')
2316 ('symbol', '1')
2309 ('symbol', '2')
2317 ('symbol', '2')
2310 ('symbol', '3')
2318 ('symbol', '3')
2311 ('symbol', '4'))
2319 ('symbol', '4'))
2312 define)
2320 define)
2313 * set:
2321 * set:
2314 <addset
2322 <addset
2315 <addset
2323 <addset
2316 <baseset [0]>,
2324 <baseset [0]>,
2317 <baseset [1]>>,
2325 <baseset [1]>>,
2318 <addset
2326 <addset
2319 <baseset [2]>,
2327 <baseset [2]>,
2320 <addset
2328 <addset
2321 <baseset [3]>,
2329 <baseset [3]>,
2322 <baseset [4]>>>>
2330 <baseset [4]>>>>
2323 0
2331 0
2324 1
2332 1
2325 2
2333 2
2326 3
2334 3
2327 4
2335 4
2328
2336
2329 test that `_list` should be narrowed by provided `subset`
2337 test that `_list` should be narrowed by provided `subset`
2330
2338
2331 $ log '0:2 and (null|1|2|3)'
2339 $ log '0:2 and (null|1|2|3)'
2332 1
2340 1
2333 2
2341 2
2334
2342
2335 test that `_list` should remove duplicates
2343 test that `_list` should remove duplicates
2336
2344
2337 $ log '0|1|2|1|2|-1|tip'
2345 $ log '0|1|2|1|2|-1|tip'
2338 0
2346 0
2339 1
2347 1
2340 2
2348 2
2341 9
2349 9
2342
2350
2343 test unknown revision in `_list`
2351 test unknown revision in `_list`
2344
2352
2345 $ log '0|unknown'
2353 $ log '0|unknown'
2346 abort: unknown revision 'unknown'!
2354 abort: unknown revision 'unknown'!
2347 [255]
2355 [255]
2348
2356
2349 test integer range in `_list`
2357 test integer range in `_list`
2350
2358
2351 $ log '-1|-10'
2359 $ log '-1|-10'
2352 9
2360 9
2353 0
2361 0
2354
2362
2355 $ log '-10|-11'
2363 $ log '-10|-11'
2356 abort: unknown revision '-11'!
2364 abort: unknown revision '-11'!
2357 [255]
2365 [255]
2358
2366
2359 $ log '9|10'
2367 $ log '9|10'
2360 abort: unknown revision '10'!
2368 abort: unknown revision '10'!
2361 [255]
2369 [255]
2362
2370
2363 test '0000' != '0' in `_list`
2371 test '0000' != '0' in `_list`
2364
2372
2365 $ log '0|0000'
2373 $ log '0|0000'
2366 0
2374 0
2367 -1
2375 -1
2368
2376
2369 test ',' in `_list`
2377 test ',' in `_list`
2370 $ log '0,1'
2378 $ log '0,1'
2371 hg: parse error: can't use a list in this context
2379 hg: parse error: can't use a list in this context
2372 (see hg help "revsets.x or y")
2380 (see hg help "revsets.x or y")
2373 [255]
2381 [255]
2374 $ try '0,1,2'
2382 $ try '0,1,2'
2375 (list
2383 (list
2376 ('symbol', '0')
2384 ('symbol', '0')
2377 ('symbol', '1')
2385 ('symbol', '1')
2378 ('symbol', '2'))
2386 ('symbol', '2'))
2379 hg: parse error: can't use a list in this context
2387 hg: parse error: can't use a list in this context
2380 (see hg help "revsets.x or y")
2388 (see hg help "revsets.x or y")
2381 [255]
2389 [255]
2382
2390
2383 test that chained `or` operations make balanced addsets
2391 test that chained `or` operations make balanced addsets
2384
2392
2385 $ try '0:1|1:2|2:3|3:4|4:5'
2393 $ try '0:1|1:2|2:3|3:4|4:5'
2386 (or
2394 (or
2387 (list
2395 (list
2388 (range
2396 (range
2389 ('symbol', '0')
2397 ('symbol', '0')
2390 ('symbol', '1'))
2398 ('symbol', '1'))
2391 (range
2399 (range
2392 ('symbol', '1')
2400 ('symbol', '1')
2393 ('symbol', '2'))
2401 ('symbol', '2'))
2394 (range
2402 (range
2395 ('symbol', '2')
2403 ('symbol', '2')
2396 ('symbol', '3'))
2404 ('symbol', '3'))
2397 (range
2405 (range
2398 ('symbol', '3')
2406 ('symbol', '3')
2399 ('symbol', '4'))
2407 ('symbol', '4'))
2400 (range
2408 (range
2401 ('symbol', '4')
2409 ('symbol', '4')
2402 ('symbol', '5'))))
2410 ('symbol', '5'))))
2403 * set:
2411 * set:
2404 <addset
2412 <addset
2405 <addset
2413 <addset
2406 <spanset+ 0:1>,
2414 <spanset+ 0:1>,
2407 <spanset+ 1:2>>,
2415 <spanset+ 1:2>>,
2408 <addset
2416 <addset
2409 <spanset+ 2:3>,
2417 <spanset+ 2:3>,
2410 <addset
2418 <addset
2411 <spanset+ 3:4>,
2419 <spanset+ 3:4>,
2412 <spanset+ 4:5>>>>
2420 <spanset+ 4:5>>>>
2413 0
2421 0
2414 1
2422 1
2415 2
2423 2
2416 3
2424 3
2417 4
2425 4
2418 5
2426 5
2419
2427
2420 no crash by empty group "()" while optimizing `or` operations
2428 no crash by empty group "()" while optimizing `or` operations
2421
2429
2422 $ try --optimize '0|()'
2430 $ try --optimize '0|()'
2423 (or
2431 (or
2424 (list
2432 (list
2425 ('symbol', '0')
2433 ('symbol', '0')
2426 (group
2434 (group
2427 None)))
2435 None)))
2428 * optimized:
2436 * optimized:
2429 (or
2437 (or
2430 (list
2438 (list
2431 ('symbol', '0')
2439 ('symbol', '0')
2432 None)
2440 None)
2433 define)
2441 define)
2434 hg: parse error: missing argument
2442 hg: parse error: missing argument
2435 [255]
2443 [255]
2436
2444
2437 test that chained `or` operations never eat up stack (issue4624)
2445 test that chained `or` operations never eat up stack (issue4624)
2438 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2446 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2439
2447
2440 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2448 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2441 0
2449 0
2442 1
2450 1
2443
2451
2444 test that repeated `-r` options never eat up stack (issue4565)
2452 test that repeated `-r` options never eat up stack (issue4565)
2445 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2453 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2446
2454
2447 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2455 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2448 0
2456 0
2449 1
2457 1
2450
2458
2451 check that conversion to only works
2459 check that conversion to only works
2452 $ try --optimize '::3 - ::1'
2460 $ try --optimize '::3 - ::1'
2453 (minus
2461 (minus
2454 (dagrangepre
2462 (dagrangepre
2455 ('symbol', '3'))
2463 ('symbol', '3'))
2456 (dagrangepre
2464 (dagrangepre
2457 ('symbol', '1')))
2465 ('symbol', '1')))
2458 * optimized:
2466 * optimized:
2459 (func
2467 (func
2460 ('symbol', 'only')
2468 ('symbol', 'only')
2461 (list
2469 (list
2462 ('symbol', '3')
2470 ('symbol', '3')
2463 ('symbol', '1'))
2471 ('symbol', '1'))
2464 define)
2472 define)
2465 * set:
2473 * set:
2466 <baseset+ [3]>
2474 <baseset+ [3]>
2467 3
2475 3
2468 $ try --optimize 'ancestors(1) - ancestors(3)'
2476 $ try --optimize 'ancestors(1) - ancestors(3)'
2469 (minus
2477 (minus
2470 (func
2478 (func
2471 ('symbol', 'ancestors')
2479 ('symbol', 'ancestors')
2472 ('symbol', '1'))
2480 ('symbol', '1'))
2473 (func
2481 (func
2474 ('symbol', 'ancestors')
2482 ('symbol', 'ancestors')
2475 ('symbol', '3')))
2483 ('symbol', '3')))
2476 * optimized:
2484 * optimized:
2477 (func
2485 (func
2478 ('symbol', 'only')
2486 ('symbol', 'only')
2479 (list
2487 (list
2480 ('symbol', '1')
2488 ('symbol', '1')
2481 ('symbol', '3'))
2489 ('symbol', '3'))
2482 define)
2490 define)
2483 * set:
2491 * set:
2484 <baseset+ []>
2492 <baseset+ []>
2485 $ try --optimize 'not ::2 and ::6'
2493 $ try --optimize 'not ::2 and ::6'
2486 (and
2494 (and
2487 (not
2495 (not
2488 (dagrangepre
2496 (dagrangepre
2489 ('symbol', '2')))
2497 ('symbol', '2')))
2490 (dagrangepre
2498 (dagrangepre
2491 ('symbol', '6')))
2499 ('symbol', '6')))
2492 * optimized:
2500 * optimized:
2493 (func
2501 (func
2494 ('symbol', 'only')
2502 ('symbol', 'only')
2495 (list
2503 (list
2496 ('symbol', '6')
2504 ('symbol', '6')
2497 ('symbol', '2'))
2505 ('symbol', '2'))
2498 define)
2506 define)
2499 * set:
2507 * set:
2500 <baseset+ [3, 4, 5, 6]>
2508 <baseset+ [3, 4, 5, 6]>
2501 3
2509 3
2502 4
2510 4
2503 5
2511 5
2504 6
2512 6
2505 $ try --optimize 'ancestors(6) and not ancestors(4)'
2513 $ try --optimize 'ancestors(6) and not ancestors(4)'
2506 (and
2514 (and
2507 (func
2515 (func
2508 ('symbol', 'ancestors')
2516 ('symbol', 'ancestors')
2509 ('symbol', '6'))
2517 ('symbol', '6'))
2510 (not
2518 (not
2511 (func
2519 (func
2512 ('symbol', 'ancestors')
2520 ('symbol', 'ancestors')
2513 ('symbol', '4'))))
2521 ('symbol', '4'))))
2514 * optimized:
2522 * optimized:
2515 (func
2523 (func
2516 ('symbol', 'only')
2524 ('symbol', 'only')
2517 (list
2525 (list
2518 ('symbol', '6')
2526 ('symbol', '6')
2519 ('symbol', '4'))
2527 ('symbol', '4'))
2520 define)
2528 define)
2521 * set:
2529 * set:
2522 <baseset+ [3, 5, 6]>
2530 <baseset+ [3, 5, 6]>
2523 3
2531 3
2524 5
2532 5
2525 6
2533 6
2526
2534
2527 no crash by empty group "()" while optimizing to "only()"
2535 no crash by empty group "()" while optimizing to "only()"
2528
2536
2529 $ try --optimize '::1 and ()'
2537 $ try --optimize '::1 and ()'
2530 (and
2538 (and
2531 (dagrangepre
2539 (dagrangepre
2532 ('symbol', '1'))
2540 ('symbol', '1'))
2533 (group
2541 (group
2534 None))
2542 None))
2535 * optimized:
2543 * optimized:
2536 (and
2544 (and
2537 None
2545 None
2538 (func
2546 (func
2539 ('symbol', 'ancestors')
2547 ('symbol', 'ancestors')
2540 ('symbol', '1')
2548 ('symbol', '1')
2541 define)
2549 define)
2542 define)
2550 define)
2543 hg: parse error: missing argument
2551 hg: parse error: missing argument
2544 [255]
2552 [255]
2545
2553
2546 invalid function call should not be optimized to only()
2554 invalid function call should not be optimized to only()
2547
2555
2548 $ log '"ancestors"(6) and not ancestors(4)'
2556 $ log '"ancestors"(6) and not ancestors(4)'
2549 hg: parse error: not a symbol
2557 hg: parse error: not a symbol
2550 [255]
2558 [255]
2551
2559
2552 $ log 'ancestors(6) and not "ancestors"(4)'
2560 $ log 'ancestors(6) and not "ancestors"(4)'
2553 hg: parse error: not a symbol
2561 hg: parse error: not a symbol
2554 [255]
2562 [255]
2555
2563
2556 we can use patterns when searching for tags
2564 we can use patterns when searching for tags
2557
2565
2558 $ log 'tag("1..*")'
2566 $ log 'tag("1..*")'
2559 abort: tag '1..*' does not exist!
2567 abort: tag '1..*' does not exist!
2560 [255]
2568 [255]
2561 $ log 'tag("re:1..*")'
2569 $ log 'tag("re:1..*")'
2562 6
2570 6
2563 $ log 'tag("re:[0-9].[0-9]")'
2571 $ log 'tag("re:[0-9].[0-9]")'
2564 6
2572 6
2565 $ log 'tag("literal:1.0")'
2573 $ log 'tag("literal:1.0")'
2566 6
2574 6
2567 $ log 'tag("re:0..*")'
2575 $ log 'tag("re:0..*")'
2568
2576
2569 $ log 'tag(unknown)'
2577 $ log 'tag(unknown)'
2570 abort: tag 'unknown' does not exist!
2578 abort: tag 'unknown' does not exist!
2571 [255]
2579 [255]
2572 $ log 'tag("re:unknown")'
2580 $ log 'tag("re:unknown")'
2573 $ log 'present(tag("unknown"))'
2581 $ log 'present(tag("unknown"))'
2574 $ log 'present(tag("re:unknown"))'
2582 $ log 'present(tag("re:unknown"))'
2575 $ log 'branch(unknown)'
2583 $ log 'branch(unknown)'
2576 abort: unknown revision 'unknown'!
2584 abort: unknown revision 'unknown'!
2577 [255]
2585 [255]
2578 $ log 'branch("literal:unknown")'
2586 $ log 'branch("literal:unknown")'
2579 abort: branch 'unknown' does not exist!
2587 abort: branch 'unknown' does not exist!
2580 [255]
2588 [255]
2581 $ log 'branch("re:unknown")'
2589 $ log 'branch("re:unknown")'
2582 $ log 'present(branch("unknown"))'
2590 $ log 'present(branch("unknown"))'
2583 $ log 'present(branch("re:unknown"))'
2591 $ log 'present(branch("re:unknown"))'
2584 $ log 'user(bob)'
2592 $ log 'user(bob)'
2585 2
2593 2
2586
2594
2587 $ log '4::8'
2595 $ log '4::8'
2588 4
2596 4
2589 8
2597 8
2590 $ log '4:8'
2598 $ log '4:8'
2591 4
2599 4
2592 5
2600 5
2593 6
2601 6
2594 7
2602 7
2595 8
2603 8
2596
2604
2597 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2605 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2598 4
2606 4
2599 2
2607 2
2600 5
2608 5
2601
2609
2602 $ log 'not 0 and 0:2'
2610 $ log 'not 0 and 0:2'
2603 1
2611 1
2604 2
2612 2
2605 $ log 'not 1 and 0:2'
2613 $ log 'not 1 and 0:2'
2606 0
2614 0
2607 2
2615 2
2608 $ log 'not 2 and 0:2'
2616 $ log 'not 2 and 0:2'
2609 0
2617 0
2610 1
2618 1
2611 $ log '(1 and 2)::'
2619 $ log '(1 and 2)::'
2612 $ log '(1 and 2):'
2620 $ log '(1 and 2):'
2613 $ log '(1 and 2):3'
2621 $ log '(1 and 2):3'
2614 $ log 'sort(head(), -rev)'
2622 $ log 'sort(head(), -rev)'
2615 9
2623 9
2616 7
2624 7
2617 6
2625 6
2618 5
2626 5
2619 4
2627 4
2620 3
2628 3
2621 2
2629 2
2622 1
2630 1
2623 0
2631 0
2624 $ log '4::8 - 8'
2632 $ log '4::8 - 8'
2625 4
2633 4
2626
2634
2627 matching() should preserve the order of the input set:
2635 matching() should preserve the order of the input set:
2628
2636
2629 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2637 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2630 2
2638 2
2631 3
2639 3
2632 1
2640 1
2633
2641
2634 $ log 'named("unknown")'
2642 $ log 'named("unknown")'
2635 abort: namespace 'unknown' does not exist!
2643 abort: namespace 'unknown' does not exist!
2636 [255]
2644 [255]
2637 $ log 'named("re:unknown")'
2645 $ log 'named("re:unknown")'
2638 abort: no namespace exists that match 'unknown'!
2646 abort: no namespace exists that match 'unknown'!
2639 [255]
2647 [255]
2640 $ log 'present(named("unknown"))'
2648 $ log 'present(named("unknown"))'
2641 $ log 'present(named("re:unknown"))'
2649 $ log 'present(named("re:unknown"))'
2642
2650
2643 $ log 'tag()'
2651 $ log 'tag()'
2644 6
2652 6
2645 $ log 'named("tags")'
2653 $ log 'named("tags")'
2646 6
2654 6
2647
2655
2648 issue2437
2656 issue2437
2649
2657
2650 $ log '3 and p1(5)'
2658 $ log '3 and p1(5)'
2651 3
2659 3
2652 $ log '4 and p2(6)'
2660 $ log '4 and p2(6)'
2653 4
2661 4
2654 $ log '1 and parents(:2)'
2662 $ log '1 and parents(:2)'
2655 1
2663 1
2656 $ log '2 and children(1:)'
2664 $ log '2 and children(1:)'
2657 2
2665 2
2658 $ log 'roots(all()) or roots(all())'
2666 $ log 'roots(all()) or roots(all())'
2659 0
2667 0
2660 $ hg debugrevspec 'roots(all()) or roots(all())'
2668 $ hg debugrevspec 'roots(all()) or roots(all())'
2661 0
2669 0
2662 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2670 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2663 9
2671 9
2664 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2672 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2665 4
2673 4
2666
2674
2667 issue2654: report a parse error if the revset was not completely parsed
2675 issue2654: report a parse error if the revset was not completely parsed
2668
2676
2669 $ log '1 OR 2'
2677 $ log '1 OR 2'
2670 hg: parse error at 2: invalid token
2678 hg: parse error at 2: invalid token
2671 [255]
2679 [255]
2672
2680
2673 or operator should preserve ordering:
2681 or operator should preserve ordering:
2674 $ log 'reverse(2::4) or tip'
2682 $ log 'reverse(2::4) or tip'
2675 4
2683 4
2676 2
2684 2
2677 9
2685 9
2678
2686
2679 parentrevspec
2687 parentrevspec
2680
2688
2681 $ log 'merge()^0'
2689 $ log 'merge()^0'
2682 6
2690 6
2683 $ log 'merge()^'
2691 $ log 'merge()^'
2684 5
2692 5
2685 $ log 'merge()^1'
2693 $ log 'merge()^1'
2686 5
2694 5
2687 $ log 'merge()^2'
2695 $ log 'merge()^2'
2688 4
2696 4
2689 $ log 'merge()^^'
2697 $ log 'merge()^^'
2690 3
2698 3
2691 $ log 'merge()^1^'
2699 $ log 'merge()^1^'
2692 3
2700 3
2693 $ log 'merge()^^^'
2701 $ log 'merge()^^^'
2694 1
2702 1
2695
2703
2696 $ log 'merge()~0'
2704 $ log 'merge()~0'
2697 6
2705 6
2698 $ log 'merge()~1'
2706 $ log 'merge()~1'
2699 5
2707 5
2700 $ log 'merge()~2'
2708 $ log 'merge()~2'
2701 3
2709 3
2702 $ log 'merge()~2^1'
2710 $ log 'merge()~2^1'
2703 1
2711 1
2704 $ log 'merge()~3'
2712 $ log 'merge()~3'
2705 1
2713 1
2706
2714
2707 $ log '(-3:tip)^'
2715 $ log '(-3:tip)^'
2708 4
2716 4
2709 6
2717 6
2710 8
2718 8
2711
2719
2712 $ log 'tip^foo'
2720 $ log 'tip^foo'
2713 hg: parse error: ^ expects a number 0, 1, or 2
2721 hg: parse error: ^ expects a number 0, 1, or 2
2714 [255]
2722 [255]
2715
2723
2716 Bogus function gets suggestions
2724 Bogus function gets suggestions
2717 $ log 'add()'
2725 $ log 'add()'
2718 hg: parse error: unknown identifier: add
2726 hg: parse error: unknown identifier: add
2719 (did you mean adds?)
2727 (did you mean adds?)
2720 [255]
2728 [255]
2721 $ log 'added()'
2729 $ log 'added()'
2722 hg: parse error: unknown identifier: added
2730 hg: parse error: unknown identifier: added
2723 (did you mean adds?)
2731 (did you mean adds?)
2724 [255]
2732 [255]
2725 $ log 'remo()'
2733 $ log 'remo()'
2726 hg: parse error: unknown identifier: remo
2734 hg: parse error: unknown identifier: remo
2727 (did you mean one of remote, removes?)
2735 (did you mean one of remote, removes?)
2728 [255]
2736 [255]
2729 $ log 'babar()'
2737 $ log 'babar()'
2730 hg: parse error: unknown identifier: babar
2738 hg: parse error: unknown identifier: babar
2731 [255]
2739 [255]
2732
2740
2733 Bogus function with a similar internal name doesn't suggest the internal name
2741 Bogus function with a similar internal name doesn't suggest the internal name
2734 $ log 'matches()'
2742 $ log 'matches()'
2735 hg: parse error: unknown identifier: matches
2743 hg: parse error: unknown identifier: matches
2736 (did you mean matching?)
2744 (did you mean matching?)
2737 [255]
2745 [255]
2738
2746
2739 Undocumented functions aren't suggested as similar either
2747 Undocumented functions aren't suggested as similar either
2740 $ log 'wdir2()'
2748 $ log 'wdir2()'
2741 hg: parse error: unknown identifier: wdir2
2749 hg: parse error: unknown identifier: wdir2
2742 [255]
2750 [255]
2743
2751
2744 multiple revspecs
2752 multiple revspecs
2745
2753
2746 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2754 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2747 8
2755 8
2748 9
2756 9
2749 4
2757 4
2750 5
2758 5
2751 6
2759 6
2752 7
2760 7
2753
2761
2754 test usage in revpair (with "+")
2762 test usage in revpair (with "+")
2755
2763
2756 (real pair)
2764 (real pair)
2757
2765
2758 $ hg diff -r 'tip^^' -r 'tip'
2766 $ hg diff -r 'tip^^' -r 'tip'
2759 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2767 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2760 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2768 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2761 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2769 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2762 @@ -0,0 +1,1 @@
2770 @@ -0,0 +1,1 @@
2763 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2771 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2764 $ hg diff -r 'tip^^::tip'
2772 $ hg diff -r 'tip^^::tip'
2765 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2773 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2766 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2774 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2767 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2775 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2768 @@ -0,0 +1,1 @@
2776 @@ -0,0 +1,1 @@
2769 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2777 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2770
2778
2771 (single rev)
2779 (single rev)
2772
2780
2773 $ hg diff -r 'tip^' -r 'tip^'
2781 $ hg diff -r 'tip^' -r 'tip^'
2774 $ hg diff -r 'tip^:tip^'
2782 $ hg diff -r 'tip^:tip^'
2775
2783
2776 (single rev that does not looks like a range)
2784 (single rev that does not looks like a range)
2777
2785
2778 $ hg diff -r 'tip^::tip^ or tip^'
2786 $ hg diff -r 'tip^::tip^ or tip^'
2779 diff -r d5d0dcbdc4d9 .hgtags
2787 diff -r d5d0dcbdc4d9 .hgtags
2780 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2788 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2781 +++ b/.hgtags * (glob)
2789 +++ b/.hgtags * (glob)
2782 @@ -0,0 +1,1 @@
2790 @@ -0,0 +1,1 @@
2783 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2791 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2784 $ hg diff -r 'tip^ or tip^'
2792 $ hg diff -r 'tip^ or tip^'
2785 diff -r d5d0dcbdc4d9 .hgtags
2793 diff -r d5d0dcbdc4d9 .hgtags
2786 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2794 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2787 +++ b/.hgtags * (glob)
2795 +++ b/.hgtags * (glob)
2788 @@ -0,0 +1,1 @@
2796 @@ -0,0 +1,1 @@
2789 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2797 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2790
2798
2791 (no rev)
2799 (no rev)
2792
2800
2793 $ hg diff -r 'author("babar") or author("celeste")'
2801 $ hg diff -r 'author("babar") or author("celeste")'
2794 abort: empty revision range
2802 abort: empty revision range
2795 [255]
2803 [255]
2796
2804
2797 aliases:
2805 aliases:
2798
2806
2799 $ echo '[revsetalias]' >> .hg/hgrc
2807 $ echo '[revsetalias]' >> .hg/hgrc
2800 $ echo 'm = merge()' >> .hg/hgrc
2808 $ echo 'm = merge()' >> .hg/hgrc
2801 (revset aliases can override builtin revsets)
2809 (revset aliases can override builtin revsets)
2802 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2810 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2803 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2811 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2804 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2812 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2805 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2813 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2806 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2814 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2807
2815
2808 $ try m
2816 $ try m
2809 ('symbol', 'm')
2817 ('symbol', 'm')
2810 * expanded:
2818 * expanded:
2811 (func
2819 (func
2812 ('symbol', 'merge')
2820 ('symbol', 'merge')
2813 None)
2821 None)
2814 * set:
2822 * set:
2815 <filteredset
2823 <filteredset
2816 <fullreposet+ 0:9>,
2824 <fullreposet+ 0:9>,
2817 <merge>>
2825 <merge>>
2818 6
2826 6
2819
2827
2820 $ HGPLAIN=1
2828 $ HGPLAIN=1
2821 $ export HGPLAIN
2829 $ export HGPLAIN
2822 $ try m
2830 $ try m
2823 ('symbol', 'm')
2831 ('symbol', 'm')
2824 abort: unknown revision 'm'!
2832 abort: unknown revision 'm'!
2825 [255]
2833 [255]
2826
2834
2827 $ HGPLAINEXCEPT=revsetalias
2835 $ HGPLAINEXCEPT=revsetalias
2828 $ export HGPLAINEXCEPT
2836 $ export HGPLAINEXCEPT
2829 $ try m
2837 $ try m
2830 ('symbol', 'm')
2838 ('symbol', 'm')
2831 * expanded:
2839 * expanded:
2832 (func
2840 (func
2833 ('symbol', 'merge')
2841 ('symbol', 'merge')
2834 None)
2842 None)
2835 * set:
2843 * set:
2836 <filteredset
2844 <filteredset
2837 <fullreposet+ 0:9>,
2845 <fullreposet+ 0:9>,
2838 <merge>>
2846 <merge>>
2839 6
2847 6
2840
2848
2841 $ unset HGPLAIN
2849 $ unset HGPLAIN
2842 $ unset HGPLAINEXCEPT
2850 $ unset HGPLAINEXCEPT
2843
2851
2844 $ try 'p2(.)'
2852 $ try 'p2(.)'
2845 (func
2853 (func
2846 ('symbol', 'p2')
2854 ('symbol', 'p2')
2847 ('symbol', '.'))
2855 ('symbol', '.'))
2848 * expanded:
2856 * expanded:
2849 (func
2857 (func
2850 ('symbol', 'p1')
2858 ('symbol', 'p1')
2851 ('symbol', '.'))
2859 ('symbol', '.'))
2852 * set:
2860 * set:
2853 <baseset+ [8]>
2861 <baseset+ [8]>
2854 8
2862 8
2855
2863
2856 $ HGPLAIN=1
2864 $ HGPLAIN=1
2857 $ export HGPLAIN
2865 $ export HGPLAIN
2858 $ try 'p2(.)'
2866 $ try 'p2(.)'
2859 (func
2867 (func
2860 ('symbol', 'p2')
2868 ('symbol', 'p2')
2861 ('symbol', '.'))
2869 ('symbol', '.'))
2862 * set:
2870 * set:
2863 <baseset+ []>
2871 <baseset+ []>
2864
2872
2865 $ HGPLAINEXCEPT=revsetalias
2873 $ HGPLAINEXCEPT=revsetalias
2866 $ export HGPLAINEXCEPT
2874 $ export HGPLAINEXCEPT
2867 $ try 'p2(.)'
2875 $ try 'p2(.)'
2868 (func
2876 (func
2869 ('symbol', 'p2')
2877 ('symbol', 'p2')
2870 ('symbol', '.'))
2878 ('symbol', '.'))
2871 * expanded:
2879 * expanded:
2872 (func
2880 (func
2873 ('symbol', 'p1')
2881 ('symbol', 'p1')
2874 ('symbol', '.'))
2882 ('symbol', '.'))
2875 * set:
2883 * set:
2876 <baseset+ [8]>
2884 <baseset+ [8]>
2877 8
2885 8
2878
2886
2879 $ unset HGPLAIN
2887 $ unset HGPLAIN
2880 $ unset HGPLAINEXCEPT
2888 $ unset HGPLAINEXCEPT
2881
2889
2882 test alias recursion
2890 test alias recursion
2883
2891
2884 $ try sincem
2892 $ try sincem
2885 ('symbol', 'sincem')
2893 ('symbol', 'sincem')
2886 * expanded:
2894 * expanded:
2887 (func
2895 (func
2888 ('symbol', 'descendants')
2896 ('symbol', 'descendants')
2889 (func
2897 (func
2890 ('symbol', 'merge')
2898 ('symbol', 'merge')
2891 None))
2899 None))
2892 * set:
2900 * set:
2893 <addset+
2901 <addset+
2894 <filteredset
2902 <filteredset
2895 <fullreposet+ 0:9>,
2903 <fullreposet+ 0:9>,
2896 <merge>>,
2904 <merge>>,
2897 <generatorset+>>
2905 <generatorset+>>
2898 6
2906 6
2899 7
2907 7
2900
2908
2901 test infinite recursion
2909 test infinite recursion
2902
2910
2903 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2911 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2904 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2912 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2905 $ try recurse1
2913 $ try recurse1
2906 ('symbol', 'recurse1')
2914 ('symbol', 'recurse1')
2907 hg: parse error: infinite expansion of revset alias "recurse1" detected
2915 hg: parse error: infinite expansion of revset alias "recurse1" detected
2908 [255]
2916 [255]
2909
2917
2910 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2918 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2911 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2919 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2912 $ try "level2(level1(1, 2), 3)"
2920 $ try "level2(level1(1, 2), 3)"
2913 (func
2921 (func
2914 ('symbol', 'level2')
2922 ('symbol', 'level2')
2915 (list
2923 (list
2916 (func
2924 (func
2917 ('symbol', 'level1')
2925 ('symbol', 'level1')
2918 (list
2926 (list
2919 ('symbol', '1')
2927 ('symbol', '1')
2920 ('symbol', '2')))
2928 ('symbol', '2')))
2921 ('symbol', '3')))
2929 ('symbol', '3')))
2922 * expanded:
2930 * expanded:
2923 (or
2931 (or
2924 (list
2932 (list
2925 ('symbol', '3')
2933 ('symbol', '3')
2926 (or
2934 (or
2927 (list
2935 (list
2928 ('symbol', '1')
2936 ('symbol', '1')
2929 ('symbol', '2')))))
2937 ('symbol', '2')))))
2930 * set:
2938 * set:
2931 <addset
2939 <addset
2932 <baseset [3]>,
2940 <baseset [3]>,
2933 <baseset [1, 2]>>
2941 <baseset [1, 2]>>
2934 3
2942 3
2935 1
2943 1
2936 2
2944 2
2937
2945
2938 test nesting and variable passing
2946 test nesting and variable passing
2939
2947
2940 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
2948 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
2941 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
2949 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
2942 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
2950 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
2943 $ try 'nested(2:5)'
2951 $ try 'nested(2:5)'
2944 (func
2952 (func
2945 ('symbol', 'nested')
2953 ('symbol', 'nested')
2946 (range
2954 (range
2947 ('symbol', '2')
2955 ('symbol', '2')
2948 ('symbol', '5')))
2956 ('symbol', '5')))
2949 * expanded:
2957 * expanded:
2950 (func
2958 (func
2951 ('symbol', 'max')
2959 ('symbol', 'max')
2952 (range
2960 (range
2953 ('symbol', '2')
2961 ('symbol', '2')
2954 ('symbol', '5')))
2962 ('symbol', '5')))
2955 * set:
2963 * set:
2956 <baseset
2964 <baseset
2957 <max
2965 <max
2958 <fullreposet+ 0:9>,
2966 <fullreposet+ 0:9>,
2959 <spanset+ 2:5>>>
2967 <spanset+ 2:5>>>
2960 5
2968 5
2961
2969
2962 test chained `or` operations are flattened at parsing phase
2970 test chained `or` operations are flattened at parsing phase
2963
2971
2964 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
2972 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
2965 $ try 'chainedorops(0:1, 1:2, 2:3)'
2973 $ try 'chainedorops(0:1, 1:2, 2:3)'
2966 (func
2974 (func
2967 ('symbol', 'chainedorops')
2975 ('symbol', 'chainedorops')
2968 (list
2976 (list
2969 (range
2977 (range
2970 ('symbol', '0')
2978 ('symbol', '0')
2971 ('symbol', '1'))
2979 ('symbol', '1'))
2972 (range
2980 (range
2973 ('symbol', '1')
2981 ('symbol', '1')
2974 ('symbol', '2'))
2982 ('symbol', '2'))
2975 (range
2983 (range
2976 ('symbol', '2')
2984 ('symbol', '2')
2977 ('symbol', '3'))))
2985 ('symbol', '3'))))
2978 * expanded:
2986 * expanded:
2979 (or
2987 (or
2980 (list
2988 (list
2981 (range
2989 (range
2982 ('symbol', '0')
2990 ('symbol', '0')
2983 ('symbol', '1'))
2991 ('symbol', '1'))
2984 (range
2992 (range
2985 ('symbol', '1')
2993 ('symbol', '1')
2986 ('symbol', '2'))
2994 ('symbol', '2'))
2987 (range
2995 (range
2988 ('symbol', '2')
2996 ('symbol', '2')
2989 ('symbol', '3'))))
2997 ('symbol', '3'))))
2990 * set:
2998 * set:
2991 <addset
2999 <addset
2992 <spanset+ 0:1>,
3000 <spanset+ 0:1>,
2993 <addset
3001 <addset
2994 <spanset+ 1:2>,
3002 <spanset+ 1:2>,
2995 <spanset+ 2:3>>>
3003 <spanset+ 2:3>>>
2996 0
3004 0
2997 1
3005 1
2998 2
3006 2
2999 3
3007 3
3000
3008
3001 test variable isolation, variable placeholders are rewritten as string
3009 test variable isolation, variable placeholders are rewritten as string
3002 then parsed and matched again as string. Check they do not leak too
3010 then parsed and matched again as string. Check they do not leak too
3003 far away.
3011 far away.
3004
3012
3005 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3013 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3006 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3014 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3007 $ try 'callinjection(2:5)'
3015 $ try 'callinjection(2:5)'
3008 (func
3016 (func
3009 ('symbol', 'callinjection')
3017 ('symbol', 'callinjection')
3010 (range
3018 (range
3011 ('symbol', '2')
3019 ('symbol', '2')
3012 ('symbol', '5')))
3020 ('symbol', '5')))
3013 * expanded:
3021 * expanded:
3014 (func
3022 (func
3015 ('symbol', 'descendants')
3023 ('symbol', 'descendants')
3016 (func
3024 (func
3017 ('symbol', 'max')
3025 ('symbol', 'max')
3018 ('string', '$1')))
3026 ('string', '$1')))
3019 abort: unknown revision '$1'!
3027 abort: unknown revision '$1'!
3020 [255]
3028 [255]
3021
3029
3022 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3030 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3023 but 'all()' should never be substituded to '0()'.
3031 but 'all()' should never be substituded to '0()'.
3024
3032
3025 $ echo 'universe = all()' >> .hg/hgrc
3033 $ echo 'universe = all()' >> .hg/hgrc
3026 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3034 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3027 $ try 'shadowall(0)'
3035 $ try 'shadowall(0)'
3028 (func
3036 (func
3029 ('symbol', 'shadowall')
3037 ('symbol', 'shadowall')
3030 ('symbol', '0'))
3038 ('symbol', '0'))
3031 * expanded:
3039 * expanded:
3032 (and
3040 (and
3033 ('symbol', '0')
3041 ('symbol', '0')
3034 (func
3042 (func
3035 ('symbol', 'all')
3043 ('symbol', 'all')
3036 None))
3044 None))
3037 * set:
3045 * set:
3038 <filteredset
3046 <filteredset
3039 <baseset [0]>,
3047 <baseset [0]>,
3040 <spanset+ 0:9>>
3048 <spanset+ 0:9>>
3041 0
3049 0
3042
3050
3043 test unknown reference:
3051 test unknown reference:
3044
3052
3045 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3053 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3046 (func
3054 (func
3047 ('symbol', 'unknownref')
3055 ('symbol', 'unknownref')
3048 ('symbol', '0'))
3056 ('symbol', '0'))
3049 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3057 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3050 [255]
3058 [255]
3051
3059
3052 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3060 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3053 ('symbol', 'tip')
3061 ('symbol', 'tip')
3054 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3062 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3055 * set:
3063 * set:
3056 <baseset [9]>
3064 <baseset [9]>
3057 9
3065 9
3058
3066
3059 $ try 'tip'
3067 $ try 'tip'
3060 ('symbol', 'tip')
3068 ('symbol', 'tip')
3061 * set:
3069 * set:
3062 <baseset [9]>
3070 <baseset [9]>
3063 9
3071 9
3064
3072
3065 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3073 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3066 ('symbol', 'tip')
3074 ('symbol', 'tip')
3067 warning: bad declaration of revset alias "bad name": at 4: invalid token
3075 warning: bad declaration of revset alias "bad name": at 4: invalid token
3068 * set:
3076 * set:
3069 <baseset [9]>
3077 <baseset [9]>
3070 9
3078 9
3071 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3079 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3072 $ try 'strictreplacing("foo", tip)'
3080 $ try 'strictreplacing("foo", tip)'
3073 (func
3081 (func
3074 ('symbol', 'strictreplacing')
3082 ('symbol', 'strictreplacing')
3075 (list
3083 (list
3076 ('string', 'foo')
3084 ('string', 'foo')
3077 ('symbol', 'tip')))
3085 ('symbol', 'tip')))
3078 * expanded:
3086 * expanded:
3079 (or
3087 (or
3080 (list
3088 (list
3081 ('symbol', 'tip')
3089 ('symbol', 'tip')
3082 (func
3090 (func
3083 ('symbol', 'desc')
3091 ('symbol', 'desc')
3084 ('string', '$1'))))
3092 ('string', '$1'))))
3085 * set:
3093 * set:
3086 <addset
3094 <addset
3087 <baseset [9]>,
3095 <baseset [9]>,
3088 <filteredset
3096 <filteredset
3089 <fullreposet+ 0:9>,
3097 <fullreposet+ 0:9>,
3090 <desc '$1'>>>
3098 <desc '$1'>>>
3091 9
3099 9
3092
3100
3093 $ try 'd(2:5)'
3101 $ try 'd(2:5)'
3094 (func
3102 (func
3095 ('symbol', 'd')
3103 ('symbol', 'd')
3096 (range
3104 (range
3097 ('symbol', '2')
3105 ('symbol', '2')
3098 ('symbol', '5')))
3106 ('symbol', '5')))
3099 * expanded:
3107 * expanded:
3100 (func
3108 (func
3101 ('symbol', 'reverse')
3109 ('symbol', 'reverse')
3102 (func
3110 (func
3103 ('symbol', 'sort')
3111 ('symbol', 'sort')
3104 (list
3112 (list
3105 (range
3113 (range
3106 ('symbol', '2')
3114 ('symbol', '2')
3107 ('symbol', '5'))
3115 ('symbol', '5'))
3108 ('symbol', 'date'))))
3116 ('symbol', 'date'))))
3109 * set:
3117 * set:
3110 <baseset [4, 5, 3, 2]>
3118 <baseset [4, 5, 3, 2]>
3111 4
3119 4
3112 5
3120 5
3113 3
3121 3
3114 2
3122 2
3115 $ try 'rs(2 or 3, date)'
3123 $ try 'rs(2 or 3, date)'
3116 (func
3124 (func
3117 ('symbol', 'rs')
3125 ('symbol', 'rs')
3118 (list
3126 (list
3119 (or
3127 (or
3120 (list
3128 (list
3121 ('symbol', '2')
3129 ('symbol', '2')
3122 ('symbol', '3')))
3130 ('symbol', '3')))
3123 ('symbol', 'date')))
3131 ('symbol', 'date')))
3124 * expanded:
3132 * expanded:
3125 (func
3133 (func
3126 ('symbol', 'reverse')
3134 ('symbol', 'reverse')
3127 (func
3135 (func
3128 ('symbol', 'sort')
3136 ('symbol', 'sort')
3129 (list
3137 (list
3130 (or
3138 (or
3131 (list
3139 (list
3132 ('symbol', '2')
3140 ('symbol', '2')
3133 ('symbol', '3')))
3141 ('symbol', '3')))
3134 ('symbol', 'date'))))
3142 ('symbol', 'date'))))
3135 * set:
3143 * set:
3136 <baseset [3, 2]>
3144 <baseset [3, 2]>
3137 3
3145 3
3138 2
3146 2
3139 $ try 'rs()'
3147 $ try 'rs()'
3140 (func
3148 (func
3141 ('symbol', 'rs')
3149 ('symbol', 'rs')
3142 None)
3150 None)
3143 hg: parse error: invalid number of arguments: 0
3151 hg: parse error: invalid number of arguments: 0
3144 [255]
3152 [255]
3145 $ try 'rs(2)'
3153 $ try 'rs(2)'
3146 (func
3154 (func
3147 ('symbol', 'rs')
3155 ('symbol', 'rs')
3148 ('symbol', '2'))
3156 ('symbol', '2'))
3149 hg: parse error: invalid number of arguments: 1
3157 hg: parse error: invalid number of arguments: 1
3150 [255]
3158 [255]
3151 $ try 'rs(2, data, 7)'
3159 $ try 'rs(2, data, 7)'
3152 (func
3160 (func
3153 ('symbol', 'rs')
3161 ('symbol', 'rs')
3154 (list
3162 (list
3155 ('symbol', '2')
3163 ('symbol', '2')
3156 ('symbol', 'data')
3164 ('symbol', 'data')
3157 ('symbol', '7')))
3165 ('symbol', '7')))
3158 hg: parse error: invalid number of arguments: 3
3166 hg: parse error: invalid number of arguments: 3
3159 [255]
3167 [255]
3160 $ try 'rs4(2 or 3, x, x, date)'
3168 $ try 'rs4(2 or 3, x, x, date)'
3161 (func
3169 (func
3162 ('symbol', 'rs4')
3170 ('symbol', 'rs4')
3163 (list
3171 (list
3164 (or
3172 (or
3165 (list
3173 (list
3166 ('symbol', '2')
3174 ('symbol', '2')
3167 ('symbol', '3')))
3175 ('symbol', '3')))
3168 ('symbol', 'x')
3176 ('symbol', 'x')
3169 ('symbol', 'x')
3177 ('symbol', 'x')
3170 ('symbol', 'date')))
3178 ('symbol', 'date')))
3171 * expanded:
3179 * expanded:
3172 (func
3180 (func
3173 ('symbol', 'reverse')
3181 ('symbol', 'reverse')
3174 (func
3182 (func
3175 ('symbol', 'sort')
3183 ('symbol', 'sort')
3176 (list
3184 (list
3177 (or
3185 (or
3178 (list
3186 (list
3179 ('symbol', '2')
3187 ('symbol', '2')
3180 ('symbol', '3')))
3188 ('symbol', '3')))
3181 ('symbol', 'date'))))
3189 ('symbol', 'date'))))
3182 * set:
3190 * set:
3183 <baseset [3, 2]>
3191 <baseset [3, 2]>
3184 3
3192 3
3185 2
3193 2
3186
3194
3187 issue4553: check that revset aliases override existing hash prefix
3195 issue4553: check that revset aliases override existing hash prefix
3188
3196
3189 $ hg log -qr e
3197 $ hg log -qr e
3190 6:e0cc66ef77e8
3198 6:e0cc66ef77e8
3191
3199
3192 $ hg log -qr e --config revsetalias.e="all()"
3200 $ hg log -qr e --config revsetalias.e="all()"
3193 0:2785f51eece5
3201 0:2785f51eece5
3194 1:d75937da8da0
3202 1:d75937da8da0
3195 2:5ed5505e9f1c
3203 2:5ed5505e9f1c
3196 3:8528aa5637f2
3204 3:8528aa5637f2
3197 4:2326846efdab
3205 4:2326846efdab
3198 5:904fa392b941
3206 5:904fa392b941
3199 6:e0cc66ef77e8
3207 6:e0cc66ef77e8
3200 7:013af1973af4
3208 7:013af1973af4
3201 8:d5d0dcbdc4d9
3209 8:d5d0dcbdc4d9
3202 9:24286f4ae135
3210 9:24286f4ae135
3203
3211
3204 $ hg log -qr e: --config revsetalias.e="0"
3212 $ hg log -qr e: --config revsetalias.e="0"
3205 0:2785f51eece5
3213 0:2785f51eece5
3206 1:d75937da8da0
3214 1:d75937da8da0
3207 2:5ed5505e9f1c
3215 2:5ed5505e9f1c
3208 3:8528aa5637f2
3216 3:8528aa5637f2
3209 4:2326846efdab
3217 4:2326846efdab
3210 5:904fa392b941
3218 5:904fa392b941
3211 6:e0cc66ef77e8
3219 6:e0cc66ef77e8
3212 7:013af1973af4
3220 7:013af1973af4
3213 8:d5d0dcbdc4d9
3221 8:d5d0dcbdc4d9
3214 9:24286f4ae135
3222 9:24286f4ae135
3215
3223
3216 $ hg log -qr :e --config revsetalias.e="9"
3224 $ hg log -qr :e --config revsetalias.e="9"
3217 0:2785f51eece5
3225 0:2785f51eece5
3218 1:d75937da8da0
3226 1:d75937da8da0
3219 2:5ed5505e9f1c
3227 2:5ed5505e9f1c
3220 3:8528aa5637f2
3228 3:8528aa5637f2
3221 4:2326846efdab
3229 4:2326846efdab
3222 5:904fa392b941
3230 5:904fa392b941
3223 6:e0cc66ef77e8
3231 6:e0cc66ef77e8
3224 7:013af1973af4
3232 7:013af1973af4
3225 8:d5d0dcbdc4d9
3233 8:d5d0dcbdc4d9
3226 9:24286f4ae135
3234 9:24286f4ae135
3227
3235
3228 $ hg log -qr e:
3236 $ hg log -qr e:
3229 6:e0cc66ef77e8
3237 6:e0cc66ef77e8
3230 7:013af1973af4
3238 7:013af1973af4
3231 8:d5d0dcbdc4d9
3239 8:d5d0dcbdc4d9
3232 9:24286f4ae135
3240 9:24286f4ae135
3233
3241
3234 $ hg log -qr :e
3242 $ hg log -qr :e
3235 0:2785f51eece5
3243 0:2785f51eece5
3236 1:d75937da8da0
3244 1:d75937da8da0
3237 2:5ed5505e9f1c
3245 2:5ed5505e9f1c
3238 3:8528aa5637f2
3246 3:8528aa5637f2
3239 4:2326846efdab
3247 4:2326846efdab
3240 5:904fa392b941
3248 5:904fa392b941
3241 6:e0cc66ef77e8
3249 6:e0cc66ef77e8
3242
3250
3243 issue2549 - correct optimizations
3251 issue2549 - correct optimizations
3244
3252
3245 $ try 'limit(1 or 2 or 3, 2) and not 2'
3253 $ try 'limit(1 or 2 or 3, 2) and not 2'
3246 (and
3254 (and
3247 (func
3255 (func
3248 ('symbol', 'limit')
3256 ('symbol', 'limit')
3249 (list
3257 (list
3250 (or
3258 (or
3251 (list
3259 (list
3252 ('symbol', '1')
3260 ('symbol', '1')
3253 ('symbol', '2')
3261 ('symbol', '2')
3254 ('symbol', '3')))
3262 ('symbol', '3')))
3255 ('symbol', '2')))
3263 ('symbol', '2')))
3256 (not
3264 (not
3257 ('symbol', '2')))
3265 ('symbol', '2')))
3258 * set:
3266 * set:
3259 <filteredset
3267 <filteredset
3260 <baseset
3268 <baseset
3261 <limit n=2, offset=0,
3269 <limit n=2, offset=0,
3262 <fullreposet+ 0:9>,
3270 <fullreposet+ 0:9>,
3263 <baseset [1, 2, 3]>>>,
3271 <baseset [1, 2, 3]>>>,
3264 <not
3272 <not
3265 <baseset [2]>>>
3273 <baseset [2]>>>
3266 1
3274 1
3267 $ try 'max(1 or 2) and not 2'
3275 $ try 'max(1 or 2) and not 2'
3268 (and
3276 (and
3269 (func
3277 (func
3270 ('symbol', 'max')
3278 ('symbol', 'max')
3271 (or
3279 (or
3272 (list
3280 (list
3273 ('symbol', '1')
3281 ('symbol', '1')
3274 ('symbol', '2'))))
3282 ('symbol', '2'))))
3275 (not
3283 (not
3276 ('symbol', '2')))
3284 ('symbol', '2')))
3277 * set:
3285 * set:
3278 <filteredset
3286 <filteredset
3279 <baseset
3287 <baseset
3280 <max
3288 <max
3281 <fullreposet+ 0:9>,
3289 <fullreposet+ 0:9>,
3282 <baseset [1, 2]>>>,
3290 <baseset [1, 2]>>>,
3283 <not
3291 <not
3284 <baseset [2]>>>
3292 <baseset [2]>>>
3285 $ try 'min(1 or 2) and not 1'
3293 $ try 'min(1 or 2) and not 1'
3286 (and
3294 (and
3287 (func
3295 (func
3288 ('symbol', 'min')
3296 ('symbol', 'min')
3289 (or
3297 (or
3290 (list
3298 (list
3291 ('symbol', '1')
3299 ('symbol', '1')
3292 ('symbol', '2'))))
3300 ('symbol', '2'))))
3293 (not
3301 (not
3294 ('symbol', '1')))
3302 ('symbol', '1')))
3295 * set:
3303 * set:
3296 <filteredset
3304 <filteredset
3297 <baseset
3305 <baseset
3298 <min
3306 <min
3299 <fullreposet+ 0:9>,
3307 <fullreposet+ 0:9>,
3300 <baseset [1, 2]>>>,
3308 <baseset [1, 2]>>>,
3301 <not
3309 <not
3302 <baseset [1]>>>
3310 <baseset [1]>>>
3303 $ try 'last(1 or 2, 1) and not 2'
3311 $ try 'last(1 or 2, 1) and not 2'
3304 (and
3312 (and
3305 (func
3313 (func
3306 ('symbol', 'last')
3314 ('symbol', 'last')
3307 (list
3315 (list
3308 (or
3316 (or
3309 (list
3317 (list
3310 ('symbol', '1')
3318 ('symbol', '1')
3311 ('symbol', '2')))
3319 ('symbol', '2')))
3312 ('symbol', '1')))
3320 ('symbol', '1')))
3313 (not
3321 (not
3314 ('symbol', '2')))
3322 ('symbol', '2')))
3315 * set:
3323 * set:
3316 <filteredset
3324 <filteredset
3317 <baseset
3325 <baseset
3318 <last n=1,
3326 <last n=1,
3319 <fullreposet+ 0:9>,
3327 <fullreposet+ 0:9>,
3320 <baseset [2, 1]>>>,
3328 <baseset [2, 1]>>>,
3321 <not
3329 <not
3322 <baseset [2]>>>
3330 <baseset [2]>>>
3323
3331
3324 issue4289 - ordering of built-ins
3332 issue4289 - ordering of built-ins
3325 $ hg log -M -q -r 3:2
3333 $ hg log -M -q -r 3:2
3326 3:8528aa5637f2
3334 3:8528aa5637f2
3327 2:5ed5505e9f1c
3335 2:5ed5505e9f1c
3328
3336
3329 test revsets started with 40-chars hash (issue3669)
3337 test revsets started with 40-chars hash (issue3669)
3330
3338
3331 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3339 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3332 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3340 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3333 9
3341 9
3334 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3342 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3335 8
3343 8
3336
3344
3337 test or-ed indirect predicates (issue3775)
3345 test or-ed indirect predicates (issue3775)
3338
3346
3339 $ log '6 or 6^1' | sort
3347 $ log '6 or 6^1' | sort
3340 5
3348 5
3341 6
3349 6
3342 $ log '6^1 or 6' | sort
3350 $ log '6^1 or 6' | sort
3343 5
3351 5
3344 6
3352 6
3345 $ log '4 or 4~1' | sort
3353 $ log '4 or 4~1' | sort
3346 2
3354 2
3347 4
3355 4
3348 $ log '4~1 or 4' | sort
3356 $ log '4~1 or 4' | sort
3349 2
3357 2
3350 4
3358 4
3351 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3359 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3352 0
3360 0
3353 1
3361 1
3354 2
3362 2
3355 3
3363 3
3356 4
3364 4
3357 5
3365 5
3358 6
3366 6
3359 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3367 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3360 0
3368 0
3361 1
3369 1
3362 2
3370 2
3363 3
3371 3
3364 4
3372 4
3365 5
3373 5
3366 6
3374 6
3367
3375
3368 tests for 'remote()' predicate:
3376 tests for 'remote()' predicate:
3369 #. (csets in remote) (id) (remote)
3377 #. (csets in remote) (id) (remote)
3370 1. less than local current branch "default"
3378 1. less than local current branch "default"
3371 2. same with local specified "default"
3379 2. same with local specified "default"
3372 3. more than local specified specified
3380 3. more than local specified specified
3373
3381
3374 $ hg clone --quiet -U . ../remote3
3382 $ hg clone --quiet -U . ../remote3
3375 $ cd ../remote3
3383 $ cd ../remote3
3376 $ hg update -q 7
3384 $ hg update -q 7
3377 $ echo r > r
3385 $ echo r > r
3378 $ hg ci -Aqm 10
3386 $ hg ci -Aqm 10
3379 $ log 'remote()'
3387 $ log 'remote()'
3380 7
3388 7
3381 $ log 'remote("a-b-c-")'
3389 $ log 'remote("a-b-c-")'
3382 2
3390 2
3383 $ cd ../repo
3391 $ cd ../repo
3384 $ log 'remote(".a.b.c.", "../remote3")'
3392 $ log 'remote(".a.b.c.", "../remote3")'
3385
3393
3386 tests for concatenation of strings/symbols by "##"
3394 tests for concatenation of strings/symbols by "##"
3387
3395
3388 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3396 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3389 (_concat
3397 (_concat
3390 (_concat
3398 (_concat
3391 (_concat
3399 (_concat
3392 ('symbol', '278')
3400 ('symbol', '278')
3393 ('string', '5f5'))
3401 ('string', '5f5'))
3394 ('symbol', '1ee'))
3402 ('symbol', '1ee'))
3395 ('string', 'ce5'))
3403 ('string', 'ce5'))
3396 * concatenated:
3404 * concatenated:
3397 ('string', '2785f51eece5')
3405 ('string', '2785f51eece5')
3398 * set:
3406 * set:
3399 <baseset [0]>
3407 <baseset [0]>
3400 0
3408 0
3401
3409
3402 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3410 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3403 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3411 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3404 (func
3412 (func
3405 ('symbol', 'cat4')
3413 ('symbol', 'cat4')
3406 (list
3414 (list
3407 ('symbol', '278')
3415 ('symbol', '278')
3408 ('string', '5f5')
3416 ('string', '5f5')
3409 ('symbol', '1ee')
3417 ('symbol', '1ee')
3410 ('string', 'ce5')))
3418 ('string', 'ce5')))
3411 * expanded:
3419 * expanded:
3412 (_concat
3420 (_concat
3413 (_concat
3421 (_concat
3414 (_concat
3422 (_concat
3415 ('symbol', '278')
3423 ('symbol', '278')
3416 ('string', '5f5'))
3424 ('string', '5f5'))
3417 ('symbol', '1ee'))
3425 ('symbol', '1ee'))
3418 ('string', 'ce5'))
3426 ('string', 'ce5'))
3419 * concatenated:
3427 * concatenated:
3420 ('string', '2785f51eece5')
3428 ('string', '2785f51eece5')
3421 * set:
3429 * set:
3422 <baseset [0]>
3430 <baseset [0]>
3423 0
3431 0
3424
3432
3425 (check concatenation in alias nesting)
3433 (check concatenation in alias nesting)
3426
3434
3427 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3435 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3428 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3436 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3429 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3437 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3430 0
3438 0
3431
3439
3432 (check operator priority)
3440 (check operator priority)
3433
3441
3434 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3442 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3435 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3443 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3436 0
3444 0
3437 4
3445 4
3438
3446
3439 $ cd ..
3447 $ cd ..
3440
3448
3441 prepare repository that has "default" branches of multiple roots
3449 prepare repository that has "default" branches of multiple roots
3442
3450
3443 $ hg init namedbranch
3451 $ hg init namedbranch
3444 $ cd namedbranch
3452 $ cd namedbranch
3445
3453
3446 $ echo default0 >> a
3454 $ echo default0 >> a
3447 $ hg ci -Aqm0
3455 $ hg ci -Aqm0
3448 $ echo default1 >> a
3456 $ echo default1 >> a
3449 $ hg ci -m1
3457 $ hg ci -m1
3450
3458
3451 $ hg branch -q stable
3459 $ hg branch -q stable
3452 $ echo stable2 >> a
3460 $ echo stable2 >> a
3453 $ hg ci -m2
3461 $ hg ci -m2
3454 $ echo stable3 >> a
3462 $ echo stable3 >> a
3455 $ hg ci -m3
3463 $ hg ci -m3
3456
3464
3457 $ hg update -q null
3465 $ hg update -q null
3458 $ echo default4 >> a
3466 $ echo default4 >> a
3459 $ hg ci -Aqm4
3467 $ hg ci -Aqm4
3460 $ echo default5 >> a
3468 $ echo default5 >> a
3461 $ hg ci -m5
3469 $ hg ci -m5
3462
3470
3463 "null" revision belongs to "default" branch (issue4683)
3471 "null" revision belongs to "default" branch (issue4683)
3464
3472
3465 $ log 'branch(null)'
3473 $ log 'branch(null)'
3466 0
3474 0
3467 1
3475 1
3468 4
3476 4
3469 5
3477 5
3470
3478
3471 "null" revision belongs to "default" branch, but it shouldn't appear in set
3479 "null" revision belongs to "default" branch, but it shouldn't appear in set
3472 unless explicitly specified (issue4682)
3480 unless explicitly specified (issue4682)
3473
3481
3474 $ log 'children(branch(default))'
3482 $ log 'children(branch(default))'
3475 1
3483 1
3476 2
3484 2
3477 5
3485 5
3478
3486
3479 $ cd ..
3487 $ cd ..
3480
3488
3481 test author/desc/keyword in problematic encoding
3489 test author/desc/keyword in problematic encoding
3482 # unicode: cp932:
3490 # unicode: cp932:
3483 # u30A2 0x83 0x41(= 'A')
3491 # u30A2 0x83 0x41(= 'A')
3484 # u30C2 0x83 0x61(= 'a')
3492 # u30C2 0x83 0x61(= 'a')
3485
3493
3486 $ hg init problematicencoding
3494 $ hg init problematicencoding
3487 $ cd problematicencoding
3495 $ cd problematicencoding
3488
3496
3489 $ python > setup.sh <<EOF
3497 $ python > setup.sh <<EOF
3490 > print u'''
3498 > print u'''
3491 > echo a > text
3499 > echo a > text
3492 > hg add text
3500 > hg add text
3493 > hg --encoding utf-8 commit -u '\u30A2' -m none
3501 > hg --encoding utf-8 commit -u '\u30A2' -m none
3494 > echo b > text
3502 > echo b > text
3495 > hg --encoding utf-8 commit -u '\u30C2' -m none
3503 > hg --encoding utf-8 commit -u '\u30C2' -m none
3496 > echo c > text
3504 > echo c > text
3497 > hg --encoding utf-8 commit -u none -m '\u30A2'
3505 > hg --encoding utf-8 commit -u none -m '\u30A2'
3498 > echo d > text
3506 > echo d > text
3499 > hg --encoding utf-8 commit -u none -m '\u30C2'
3507 > hg --encoding utf-8 commit -u none -m '\u30C2'
3500 > '''.encode('utf-8')
3508 > '''.encode('utf-8')
3501 > EOF
3509 > EOF
3502 $ sh < setup.sh
3510 $ sh < setup.sh
3503
3511
3504 test in problematic encoding
3512 test in problematic encoding
3505 $ python > test.sh <<EOF
3513 $ python > test.sh <<EOF
3506 > print u'''
3514 > print u'''
3507 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3515 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3508 > echo ====
3516 > echo ====
3509 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3517 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3510 > echo ====
3518 > echo ====
3511 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3519 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3512 > echo ====
3520 > echo ====
3513 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3521 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3514 > echo ====
3522 > echo ====
3515 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3523 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3516 > echo ====
3524 > echo ====
3517 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3525 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3518 > '''.encode('cp932')
3526 > '''.encode('cp932')
3519 > EOF
3527 > EOF
3520 $ sh < test.sh
3528 $ sh < test.sh
3521 0
3529 0
3522 ====
3530 ====
3523 1
3531 1
3524 ====
3532 ====
3525 2
3533 2
3526 ====
3534 ====
3527 3
3535 3
3528 ====
3536 ====
3529 0
3537 0
3530 2
3538 2
3531 ====
3539 ====
3532 1
3540 1
3533 3
3541 3
3534
3542
3535 test error message of bad revset
3543 test error message of bad revset
3536 $ hg log -r 'foo\\'
3544 $ hg log -r 'foo\\'
3537 hg: parse error at 3: syntax error in revset 'foo\\'
3545 hg: parse error at 3: syntax error in revset 'foo\\'
3538 [255]
3546 [255]
3539
3547
3540 $ cd ..
3548 $ cd ..
3541
3549
3542 Test that revset predicate of extension isn't loaded at failure of
3550 Test that revset predicate of extension isn't loaded at failure of
3543 loading it
3551 loading it
3544
3552
3545 $ cd repo
3553 $ cd repo
3546
3554
3547 $ cat <<EOF > $TESTTMP/custompredicate.py
3555 $ cat <<EOF > $TESTTMP/custompredicate.py
3548 > from mercurial import error, registrar, revset
3556 > from mercurial import error, registrar, revset
3549 >
3557 >
3550 > revsetpredicate = registrar.revsetpredicate()
3558 > revsetpredicate = registrar.revsetpredicate()
3551 >
3559 >
3552 > @revsetpredicate('custom1()')
3560 > @revsetpredicate('custom1()')
3553 > def custom1(repo, subset, x):
3561 > def custom1(repo, subset, x):
3554 > return revset.baseset([1])
3562 > return revset.baseset([1])
3555 >
3563 >
3556 > raise error.Abort('intentional failure of loading extension')
3564 > raise error.Abort('intentional failure of loading extension')
3557 > EOF
3565 > EOF
3558 $ cat <<EOF > .hg/hgrc
3566 $ cat <<EOF > .hg/hgrc
3559 > [extensions]
3567 > [extensions]
3560 > custompredicate = $TESTTMP/custompredicate.py
3568 > custompredicate = $TESTTMP/custompredicate.py
3561 > EOF
3569 > EOF
3562
3570
3563 $ hg debugrevspec "custom1()"
3571 $ hg debugrevspec "custom1()"
3564 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3572 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3565 hg: parse error: unknown identifier: custom1
3573 hg: parse error: unknown identifier: custom1
3566 [255]
3574 [255]
3567
3575
3568 $ cd ..
3576 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now