##// END OF EJS Templates
revset: do not transform range* operators in parsed tree...
Yuya Nishihara -
r30803:d389f19f default
parent child Browse files
Show More
@@ -1,3889 +1,3901 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import heapq
10 import heapq
11 import re
11 import re
12 import string
12 import string
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 destutil,
16 destutil,
17 encoding,
17 encoding,
18 error,
18 error,
19 hbisect,
19 hbisect,
20 match as matchmod,
20 match as matchmod,
21 node,
21 node,
22 obsolete as obsmod,
22 obsolete as obsmod,
23 parser,
23 parser,
24 pathutil,
24 pathutil,
25 phases,
25 phases,
26 pycompat,
26 pycompat,
27 registrar,
27 registrar,
28 repoview,
28 repoview,
29 util,
29 util,
30 )
30 )
31
31
32 def _revancestors(repo, revs, followfirst):
32 def _revancestors(repo, revs, followfirst):
33 """Like revlog.ancestors(), but supports followfirst."""
33 """Like revlog.ancestors(), but supports followfirst."""
34 if followfirst:
34 if followfirst:
35 cut = 1
35 cut = 1
36 else:
36 else:
37 cut = None
37 cut = None
38 cl = repo.changelog
38 cl = repo.changelog
39
39
40 def iterate():
40 def iterate():
41 revs.sort(reverse=True)
41 revs.sort(reverse=True)
42 irevs = iter(revs)
42 irevs = iter(revs)
43 h = []
43 h = []
44
44
45 inputrev = next(irevs, None)
45 inputrev = next(irevs, None)
46 if inputrev is not None:
46 if inputrev is not None:
47 heapq.heappush(h, -inputrev)
47 heapq.heappush(h, -inputrev)
48
48
49 seen = set()
49 seen = set()
50 while h:
50 while h:
51 current = -heapq.heappop(h)
51 current = -heapq.heappop(h)
52 if current == inputrev:
52 if current == inputrev:
53 inputrev = next(irevs, None)
53 inputrev = next(irevs, None)
54 if inputrev is not None:
54 if inputrev is not None:
55 heapq.heappush(h, -inputrev)
55 heapq.heappush(h, -inputrev)
56 if current not in seen:
56 if current not in seen:
57 seen.add(current)
57 seen.add(current)
58 yield current
58 yield current
59 for parent in cl.parentrevs(current)[:cut]:
59 for parent in cl.parentrevs(current)[:cut]:
60 if parent != node.nullrev:
60 if parent != node.nullrev:
61 heapq.heappush(h, -parent)
61 heapq.heappush(h, -parent)
62
62
63 return generatorset(iterate(), iterasc=False)
63 return generatorset(iterate(), iterasc=False)
64
64
65 def _revdescendants(repo, revs, followfirst):
65 def _revdescendants(repo, revs, followfirst):
66 """Like revlog.descendants() but supports followfirst."""
66 """Like revlog.descendants() but supports followfirst."""
67 if followfirst:
67 if followfirst:
68 cut = 1
68 cut = 1
69 else:
69 else:
70 cut = None
70 cut = None
71
71
72 def iterate():
72 def iterate():
73 cl = repo.changelog
73 cl = repo.changelog
74 # XXX this should be 'parentset.min()' assuming 'parentset' is a
74 # XXX this should be 'parentset.min()' assuming 'parentset' is a
75 # smartset (and if it is not, it should.)
75 # smartset (and if it is not, it should.)
76 first = min(revs)
76 first = min(revs)
77 nullrev = node.nullrev
77 nullrev = node.nullrev
78 if first == nullrev:
78 if first == nullrev:
79 # Are there nodes with a null first parent and a non-null
79 # Are there nodes with a null first parent and a non-null
80 # second one? Maybe. Do we care? Probably not.
80 # second one? Maybe. Do we care? Probably not.
81 for i in cl:
81 for i in cl:
82 yield i
82 yield i
83 else:
83 else:
84 seen = set(revs)
84 seen = set(revs)
85 for i in cl.revs(first + 1):
85 for i in cl.revs(first + 1):
86 for x in cl.parentrevs(i)[:cut]:
86 for x in cl.parentrevs(i)[:cut]:
87 if x != nullrev and x in seen:
87 if x != nullrev and x in seen:
88 seen.add(i)
88 seen.add(i)
89 yield i
89 yield i
90 break
90 break
91
91
92 return generatorset(iterate(), iterasc=True)
92 return generatorset(iterate(), iterasc=True)
93
93
94 def _reachablerootspure(repo, minroot, roots, heads, includepath):
94 def _reachablerootspure(repo, minroot, roots, heads, includepath):
95 """return (heads(::<roots> and ::<heads>))
95 """return (heads(::<roots> and ::<heads>))
96
96
97 If includepath is True, return (<roots>::<heads>)."""
97 If includepath is True, return (<roots>::<heads>)."""
98 if not roots:
98 if not roots:
99 return []
99 return []
100 parentrevs = repo.changelog.parentrevs
100 parentrevs = repo.changelog.parentrevs
101 roots = set(roots)
101 roots = set(roots)
102 visit = list(heads)
102 visit = list(heads)
103 reachable = set()
103 reachable = set()
104 seen = {}
104 seen = {}
105 # prefetch all the things! (because python is slow)
105 # prefetch all the things! (because python is slow)
106 reached = reachable.add
106 reached = reachable.add
107 dovisit = visit.append
107 dovisit = visit.append
108 nextvisit = visit.pop
108 nextvisit = visit.pop
109 # open-code the post-order traversal due to the tiny size of
109 # open-code the post-order traversal due to the tiny size of
110 # sys.getrecursionlimit()
110 # sys.getrecursionlimit()
111 while visit:
111 while visit:
112 rev = nextvisit()
112 rev = nextvisit()
113 if rev in roots:
113 if rev in roots:
114 reached(rev)
114 reached(rev)
115 if not includepath:
115 if not includepath:
116 continue
116 continue
117 parents = parentrevs(rev)
117 parents = parentrevs(rev)
118 seen[rev] = parents
118 seen[rev] = parents
119 for parent in parents:
119 for parent in parents:
120 if parent >= minroot and parent not in seen:
120 if parent >= minroot and parent not in seen:
121 dovisit(parent)
121 dovisit(parent)
122 if not reachable:
122 if not reachable:
123 return baseset()
123 return baseset()
124 if not includepath:
124 if not includepath:
125 return reachable
125 return reachable
126 for rev in sorted(seen):
126 for rev in sorted(seen):
127 for parent in seen[rev]:
127 for parent in seen[rev]:
128 if parent in reachable:
128 if parent in reachable:
129 reached(rev)
129 reached(rev)
130 return reachable
130 return reachable
131
131
132 def reachableroots(repo, roots, heads, includepath=False):
132 def reachableroots(repo, roots, heads, includepath=False):
133 """return (heads(::<roots> and ::<heads>))
133 """return (heads(::<roots> and ::<heads>))
134
134
135 If includepath is True, return (<roots>::<heads>)."""
135 If includepath is True, return (<roots>::<heads>)."""
136 if not roots:
136 if not roots:
137 return baseset()
137 return baseset()
138 minroot = roots.min()
138 minroot = roots.min()
139 roots = list(roots)
139 roots = list(roots)
140 heads = list(heads)
140 heads = list(heads)
141 try:
141 try:
142 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
142 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
143 except AttributeError:
143 except AttributeError:
144 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
144 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
145 revs = baseset(revs)
145 revs = baseset(revs)
146 revs.sort()
146 revs.sort()
147 return revs
147 return revs
148
148
149 elements = {
149 elements = {
150 # token-type: binding-strength, primary, prefix, infix, suffix
150 # token-type: binding-strength, primary, prefix, infix, suffix
151 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
151 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
152 "##": (20, None, None, ("_concat", 20), None),
152 "##": (20, None, None, ("_concat", 20), None),
153 "~": (18, None, None, ("ancestor", 18), None),
153 "~": (18, None, None, ("ancestor", 18), None),
154 "^": (18, None, None, ("parent", 18), "parentpost"),
154 "^": (18, None, None, ("parent", 18), "parentpost"),
155 "-": (5, None, ("negate", 19), ("minus", 5), None),
155 "-": (5, None, ("negate", 19), ("minus", 5), None),
156 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
156 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
157 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
157 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
158 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
158 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
159 "not": (10, None, ("not", 10), None, None),
159 "not": (10, None, ("not", 10), None, None),
160 "!": (10, None, ("not", 10), None, None),
160 "!": (10, None, ("not", 10), None, None),
161 "and": (5, None, None, ("and", 5), None),
161 "and": (5, None, None, ("and", 5), None),
162 "&": (5, None, None, ("and", 5), None),
162 "&": (5, None, None, ("and", 5), None),
163 "%": (5, None, None, ("only", 5), "onlypost"),
163 "%": (5, None, None, ("only", 5), "onlypost"),
164 "or": (4, None, None, ("or", 4), None),
164 "or": (4, None, None, ("or", 4), None),
165 "|": (4, None, None, ("or", 4), None),
165 "|": (4, None, None, ("or", 4), None),
166 "+": (4, None, None, ("or", 4), None),
166 "+": (4, None, None, ("or", 4), None),
167 "=": (3, None, None, ("keyvalue", 3), None),
167 "=": (3, None, None, ("keyvalue", 3), None),
168 ",": (2, None, None, ("list", 2), None),
168 ",": (2, None, None, ("list", 2), None),
169 ")": (0, None, None, None, None),
169 ")": (0, None, None, None, None),
170 "symbol": (0, "symbol", None, None, None),
170 "symbol": (0, "symbol", None, None, None),
171 "string": (0, "string", None, None, None),
171 "string": (0, "string", None, None, None),
172 "end": (0, None, None, None, None),
172 "end": (0, None, None, None, None),
173 }
173 }
174
174
175 keywords = set(['and', 'or', 'not'])
175 keywords = set(['and', 'or', 'not'])
176
176
177 # default set of valid characters for the initial letter of symbols
177 # default set of valid characters for the initial letter of symbols
178 _syminitletters = set(
178 _syminitletters = set(
179 string.ascii_letters +
179 string.ascii_letters +
180 string.digits + pycompat.sysstr('._@')) | set(map(chr, xrange(128, 256)))
180 string.digits + pycompat.sysstr('._@')) | set(map(chr, xrange(128, 256)))
181
181
182 # default set of valid characters for non-initial letters of symbols
182 # default set of valid characters for non-initial letters of symbols
183 _symletters = _syminitletters | set(pycompat.sysstr('-/'))
183 _symletters = _syminitletters | set(pycompat.sysstr('-/'))
184
184
185 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
185 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
186 '''
186 '''
187 Parse a revset statement into a stream of tokens
187 Parse a revset statement into a stream of tokens
188
188
189 ``syminitletters`` is the set of valid characters for the initial
189 ``syminitletters`` is the set of valid characters for the initial
190 letter of symbols.
190 letter of symbols.
191
191
192 By default, character ``c`` is recognized as valid for initial
192 By default, character ``c`` is recognized as valid for initial
193 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
193 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
194
194
195 ``symletters`` is the set of valid characters for non-initial
195 ``symletters`` is the set of valid characters for non-initial
196 letters of symbols.
196 letters of symbols.
197
197
198 By default, character ``c`` is recognized as valid for non-initial
198 By default, character ``c`` is recognized as valid for non-initial
199 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
199 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
200
200
201 Check that @ is a valid unquoted token character (issue3686):
201 Check that @ is a valid unquoted token character (issue3686):
202 >>> list(tokenize("@::"))
202 >>> list(tokenize("@::"))
203 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
203 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
204
204
205 '''
205 '''
206 if syminitletters is None:
206 if syminitletters is None:
207 syminitletters = _syminitletters
207 syminitletters = _syminitletters
208 if symletters is None:
208 if symletters is None:
209 symletters = _symletters
209 symletters = _symletters
210
210
211 if program and lookup:
211 if program and lookup:
212 # attempt to parse old-style ranges first to deal with
212 # attempt to parse old-style ranges first to deal with
213 # things like old-tag which contain query metacharacters
213 # things like old-tag which contain query metacharacters
214 parts = program.split(':', 1)
214 parts = program.split(':', 1)
215 if all(lookup(sym) for sym in parts if sym):
215 if all(lookup(sym) for sym in parts if sym):
216 if parts[0]:
216 if parts[0]:
217 yield ('symbol', parts[0], 0)
217 yield ('symbol', parts[0], 0)
218 if len(parts) > 1:
218 if len(parts) > 1:
219 s = len(parts[0])
219 s = len(parts[0])
220 yield (':', None, s)
220 yield (':', None, s)
221 if parts[1]:
221 if parts[1]:
222 yield ('symbol', parts[1], s + 1)
222 yield ('symbol', parts[1], s + 1)
223 yield ('end', None, len(program))
223 yield ('end', None, len(program))
224 return
224 return
225
225
226 pos, l = 0, len(program)
226 pos, l = 0, len(program)
227 while pos < l:
227 while pos < l:
228 c = program[pos]
228 c = program[pos]
229 if c.isspace(): # skip inter-token whitespace
229 if c.isspace(): # skip inter-token whitespace
230 pass
230 pass
231 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
231 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
232 yield ('::', None, pos)
232 yield ('::', None, pos)
233 pos += 1 # skip ahead
233 pos += 1 # skip ahead
234 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
234 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
235 yield ('..', None, pos)
235 yield ('..', None, pos)
236 pos += 1 # skip ahead
236 pos += 1 # skip ahead
237 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
237 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
238 yield ('##', None, pos)
238 yield ('##', None, pos)
239 pos += 1 # skip ahead
239 pos += 1 # skip ahead
240 elif c in "():=,-|&+!~^%": # handle simple operators
240 elif c in "():=,-|&+!~^%": # handle simple operators
241 yield (c, None, pos)
241 yield (c, None, pos)
242 elif (c in '"\'' or c == 'r' and
242 elif (c in '"\'' or c == 'r' and
243 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
243 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
244 if c == 'r':
244 if c == 'r':
245 pos += 1
245 pos += 1
246 c = program[pos]
246 c = program[pos]
247 decode = lambda x: x
247 decode = lambda x: x
248 else:
248 else:
249 decode = parser.unescapestr
249 decode = parser.unescapestr
250 pos += 1
250 pos += 1
251 s = pos
251 s = pos
252 while pos < l: # find closing quote
252 while pos < l: # find closing quote
253 d = program[pos]
253 d = program[pos]
254 if d == '\\': # skip over escaped characters
254 if d == '\\': # skip over escaped characters
255 pos += 2
255 pos += 2
256 continue
256 continue
257 if d == c:
257 if d == c:
258 yield ('string', decode(program[s:pos]), s)
258 yield ('string', decode(program[s:pos]), s)
259 break
259 break
260 pos += 1
260 pos += 1
261 else:
261 else:
262 raise error.ParseError(_("unterminated string"), s)
262 raise error.ParseError(_("unterminated string"), s)
263 # gather up a symbol/keyword
263 # gather up a symbol/keyword
264 elif c in syminitletters:
264 elif c in syminitletters:
265 s = pos
265 s = pos
266 pos += 1
266 pos += 1
267 while pos < l: # find end of symbol
267 while pos < l: # find end of symbol
268 d = program[pos]
268 d = program[pos]
269 if d not in symletters:
269 if d not in symletters:
270 break
270 break
271 if d == '.' and program[pos - 1] == '.': # special case for ..
271 if d == '.' and program[pos - 1] == '.': # special case for ..
272 pos -= 1
272 pos -= 1
273 break
273 break
274 pos += 1
274 pos += 1
275 sym = program[s:pos]
275 sym = program[s:pos]
276 if sym in keywords: # operator keywords
276 if sym in keywords: # operator keywords
277 yield (sym, None, s)
277 yield (sym, None, s)
278 elif '-' in sym:
278 elif '-' in sym:
279 # some jerk gave us foo-bar-baz, try to check if it's a symbol
279 # some jerk gave us foo-bar-baz, try to check if it's a symbol
280 if lookup and lookup(sym):
280 if lookup and lookup(sym):
281 # looks like a real symbol
281 # looks like a real symbol
282 yield ('symbol', sym, s)
282 yield ('symbol', sym, s)
283 else:
283 else:
284 # looks like an expression
284 # looks like an expression
285 parts = sym.split('-')
285 parts = sym.split('-')
286 for p in parts[:-1]:
286 for p in parts[:-1]:
287 if p: # possible consecutive -
287 if p: # possible consecutive -
288 yield ('symbol', p, s)
288 yield ('symbol', p, s)
289 s += len(p)
289 s += len(p)
290 yield ('-', None, pos)
290 yield ('-', None, pos)
291 s += 1
291 s += 1
292 if parts[-1]: # possible trailing -
292 if parts[-1]: # possible trailing -
293 yield ('symbol', parts[-1], s)
293 yield ('symbol', parts[-1], s)
294 else:
294 else:
295 yield ('symbol', sym, s)
295 yield ('symbol', sym, s)
296 pos -= 1
296 pos -= 1
297 else:
297 else:
298 raise error.ParseError(_("syntax error in revset '%s'") %
298 raise error.ParseError(_("syntax error in revset '%s'") %
299 program, pos)
299 program, pos)
300 pos += 1
300 pos += 1
301 yield ('end', None, pos)
301 yield ('end', None, pos)
302
302
303 # helpers
303 # helpers
304
304
305 _notset = object()
305 _notset = object()
306
306
307 def getsymbol(x):
307 def getsymbol(x):
308 if x and x[0] == 'symbol':
308 if x and x[0] == 'symbol':
309 return x[1]
309 return x[1]
310 raise error.ParseError(_('not a symbol'))
310 raise error.ParseError(_('not a symbol'))
311
311
312 def getstring(x, err):
312 def getstring(x, err):
313 if x and (x[0] == 'string' or x[0] == 'symbol'):
313 if x and (x[0] == 'string' or x[0] == 'symbol'):
314 return x[1]
314 return x[1]
315 raise error.ParseError(err)
315 raise error.ParseError(err)
316
316
317 def getinteger(x, err, default=_notset):
317 def getinteger(x, err, default=_notset):
318 if not x and default is not _notset:
318 if not x and default is not _notset:
319 return default
319 return default
320 try:
320 try:
321 return int(getstring(x, err))
321 return int(getstring(x, err))
322 except ValueError:
322 except ValueError:
323 raise error.ParseError(err)
323 raise error.ParseError(err)
324
324
325 def getlist(x):
325 def getlist(x):
326 if not x:
326 if not x:
327 return []
327 return []
328 if x[0] == 'list':
328 if x[0] == 'list':
329 return list(x[1:])
329 return list(x[1:])
330 return [x]
330 return [x]
331
331
332 def getargs(x, min, max, err):
332 def getargs(x, min, max, err):
333 l = getlist(x)
333 l = getlist(x)
334 if len(l) < min or (max >= 0 and len(l) > max):
334 if len(l) < min or (max >= 0 and len(l) > max):
335 raise error.ParseError(err)
335 raise error.ParseError(err)
336 return l
336 return l
337
337
338 def getargsdict(x, funcname, keys):
338 def getargsdict(x, funcname, keys):
339 return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys),
339 return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys),
340 keyvaluenode='keyvalue', keynode='symbol')
340 keyvaluenode='keyvalue', keynode='symbol')
341
341
342 def getset(repo, subset, x):
342 def getset(repo, subset, x):
343 if not x:
343 if not x:
344 raise error.ParseError(_("missing argument"))
344 raise error.ParseError(_("missing argument"))
345 s = methods[x[0]](repo, subset, *x[1:])
345 s = methods[x[0]](repo, subset, *x[1:])
346 if util.safehasattr(s, 'isascending'):
346 if util.safehasattr(s, 'isascending'):
347 return s
347 return s
348 # else case should not happen, because all non-func are internal,
348 # else case should not happen, because all non-func are internal,
349 # ignoring for now.
349 # ignoring for now.
350 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
350 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
351 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
351 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
352 % x[1][1],
352 % x[1][1],
353 '3.9')
353 '3.9')
354 return baseset(s)
354 return baseset(s)
355
355
356 def _getrevsource(repo, r):
356 def _getrevsource(repo, r):
357 extra = repo[r].extra()
357 extra = repo[r].extra()
358 for label in ('source', 'transplant_source', 'rebase_source'):
358 for label in ('source', 'transplant_source', 'rebase_source'):
359 if label in extra:
359 if label in extra:
360 try:
360 try:
361 return repo[extra[label]].rev()
361 return repo[extra[label]].rev()
362 except error.RepoLookupError:
362 except error.RepoLookupError:
363 pass
363 pass
364 return None
364 return None
365
365
366 # operator methods
366 # operator methods
367
367
368 def stringset(repo, subset, x):
368 def stringset(repo, subset, x):
369 x = repo[x].rev()
369 x = repo[x].rev()
370 if (x in subset
370 if (x in subset
371 or x == node.nullrev and isinstance(subset, fullreposet)):
371 or x == node.nullrev and isinstance(subset, fullreposet)):
372 return baseset([x])
372 return baseset([x])
373 return baseset()
373 return baseset()
374
374
375 def rangeset(repo, subset, x, y, order):
375 def rangeset(repo, subset, x, y, order):
376 m = getset(repo, fullreposet(repo), x)
376 m = getset(repo, fullreposet(repo), x)
377 n = getset(repo, fullreposet(repo), y)
377 n = getset(repo, fullreposet(repo), y)
378
378
379 if not m or not n:
379 if not m or not n:
380 return baseset()
380 return baseset()
381 return _makerangeset(repo, subset, m.first(), n.last(), order)
381 return _makerangeset(repo, subset, m.first(), n.last(), order)
382
382
383 def rangeall(repo, subset, x, order):
384 assert x is None
385 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
386
383 def rangepre(repo, subset, y, order):
387 def rangepre(repo, subset, y, order):
384 # ':y' can't be rewritten to '0:y' since '0' may be hidden
388 # ':y' can't be rewritten to '0:y' since '0' may be hidden
385 n = getset(repo, fullreposet(repo), y)
389 n = getset(repo, fullreposet(repo), y)
386 if not n:
390 if not n:
387 return baseset()
391 return baseset()
388 return _makerangeset(repo, subset, 0, n.last(), order)
392 return _makerangeset(repo, subset, 0, n.last(), order)
389
393
394 def rangepost(repo, subset, x, order):
395 m = getset(repo, fullreposet(repo), x)
396 if not m:
397 return baseset()
398 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
399
390 def _makerangeset(repo, subset, m, n, order):
400 def _makerangeset(repo, subset, m, n, order):
391 if m == n:
401 if m == n:
392 r = baseset([m])
402 r = baseset([m])
393 elif n == node.wdirrev:
403 elif n == node.wdirrev:
394 r = spanset(repo, m, len(repo)) + baseset([n])
404 r = spanset(repo, m, len(repo)) + baseset([n])
395 elif m == node.wdirrev:
405 elif m == node.wdirrev:
396 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
406 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
397 elif m < n:
407 elif m < n:
398 r = spanset(repo, m, n + 1)
408 r = spanset(repo, m, n + 1)
399 else:
409 else:
400 r = spanset(repo, m, n - 1)
410 r = spanset(repo, m, n - 1)
401
411
402 if order == defineorder:
412 if order == defineorder:
403 return r & subset
413 return r & subset
404 else:
414 else:
405 # carrying the sorting over when possible would be more efficient
415 # carrying the sorting over when possible would be more efficient
406 return subset & r
416 return subset & r
407
417
408 def dagrange(repo, subset, x, y, order):
418 def dagrange(repo, subset, x, y, order):
409 r = fullreposet(repo)
419 r = fullreposet(repo)
410 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
420 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
411 includepath=True)
421 includepath=True)
412 return subset & xs
422 return subset & xs
413
423
414 def andset(repo, subset, x, y, order):
424 def andset(repo, subset, x, y, order):
415 return getset(repo, getset(repo, subset, x), y)
425 return getset(repo, getset(repo, subset, x), y)
416
426
417 def differenceset(repo, subset, x, y, order):
427 def differenceset(repo, subset, x, y, order):
418 return getset(repo, subset, x) - getset(repo, subset, y)
428 return getset(repo, subset, x) - getset(repo, subset, y)
419
429
420 def _orsetlist(repo, subset, xs):
430 def _orsetlist(repo, subset, xs):
421 assert xs
431 assert xs
422 if len(xs) == 1:
432 if len(xs) == 1:
423 return getset(repo, subset, xs[0])
433 return getset(repo, subset, xs[0])
424 p = len(xs) // 2
434 p = len(xs) // 2
425 a = _orsetlist(repo, subset, xs[:p])
435 a = _orsetlist(repo, subset, xs[:p])
426 b = _orsetlist(repo, subset, xs[p:])
436 b = _orsetlist(repo, subset, xs[p:])
427 return a + b
437 return a + b
428
438
429 def orset(repo, subset, x, order):
439 def orset(repo, subset, x, order):
430 xs = getlist(x)
440 xs = getlist(x)
431 if order == followorder:
441 if order == followorder:
432 # slow path to take the subset order
442 # slow path to take the subset order
433 return subset & _orsetlist(repo, fullreposet(repo), xs)
443 return subset & _orsetlist(repo, fullreposet(repo), xs)
434 else:
444 else:
435 return _orsetlist(repo, subset, xs)
445 return _orsetlist(repo, subset, xs)
436
446
437 def notset(repo, subset, x, order):
447 def notset(repo, subset, x, order):
438 return subset - getset(repo, subset, x)
448 return subset - getset(repo, subset, x)
439
449
440 def listset(repo, subset, *xs):
450 def listset(repo, subset, *xs):
441 raise error.ParseError(_("can't use a list in this context"),
451 raise error.ParseError(_("can't use a list in this context"),
442 hint=_('see hg help "revsets.x or y"'))
452 hint=_('see hg help "revsets.x or y"'))
443
453
444 def keyvaluepair(repo, subset, k, v):
454 def keyvaluepair(repo, subset, k, v):
445 raise error.ParseError(_("can't use a key-value pair in this context"))
455 raise error.ParseError(_("can't use a key-value pair in this context"))
446
456
447 def func(repo, subset, a, b, order):
457 def func(repo, subset, a, b, order):
448 f = getsymbol(a)
458 f = getsymbol(a)
449 if f in symbols:
459 if f in symbols:
450 func = symbols[f]
460 func = symbols[f]
451 if getattr(func, '_takeorder', False):
461 if getattr(func, '_takeorder', False):
452 return func(repo, subset, b, order)
462 return func(repo, subset, b, order)
453 return func(repo, subset, b)
463 return func(repo, subset, b)
454
464
455 keep = lambda fn: getattr(fn, '__doc__', None) is not None
465 keep = lambda fn: getattr(fn, '__doc__', None) is not None
456
466
457 syms = [s for (s, fn) in symbols.items() if keep(fn)]
467 syms = [s for (s, fn) in symbols.items() if keep(fn)]
458 raise error.UnknownIdentifier(f, syms)
468 raise error.UnknownIdentifier(f, syms)
459
469
460 # functions
470 # functions
461
471
462 # symbols are callables like:
472 # symbols are callables like:
463 # fn(repo, subset, x)
473 # fn(repo, subset, x)
464 # with:
474 # with:
465 # repo - current repository instance
475 # repo - current repository instance
466 # subset - of revisions to be examined
476 # subset - of revisions to be examined
467 # x - argument in tree form
477 # x - argument in tree form
468 symbols = {}
478 symbols = {}
469
479
470 # symbols which can't be used for a DoS attack for any given input
480 # symbols which can't be used for a DoS attack for any given input
471 # (e.g. those which accept regexes as plain strings shouldn't be included)
481 # (e.g. those which accept regexes as plain strings shouldn't be included)
472 # functions that just return a lot of changesets (like all) don't count here
482 # functions that just return a lot of changesets (like all) don't count here
473 safesymbols = set()
483 safesymbols = set()
474
484
475 predicate = registrar.revsetpredicate()
485 predicate = registrar.revsetpredicate()
476
486
477 @predicate('_destupdate')
487 @predicate('_destupdate')
478 def _destupdate(repo, subset, x):
488 def _destupdate(repo, subset, x):
479 # experimental revset for update destination
489 # experimental revset for update destination
480 args = getargsdict(x, 'limit', 'clean check')
490 args = getargsdict(x, 'limit', 'clean check')
481 return subset & baseset([destutil.destupdate(repo, **args)[0]])
491 return subset & baseset([destutil.destupdate(repo, **args)[0]])
482
492
483 @predicate('_destmerge')
493 @predicate('_destmerge')
484 def _destmerge(repo, subset, x):
494 def _destmerge(repo, subset, x):
485 # experimental revset for merge destination
495 # experimental revset for merge destination
486 sourceset = None
496 sourceset = None
487 if x is not None:
497 if x is not None:
488 sourceset = getset(repo, fullreposet(repo), x)
498 sourceset = getset(repo, fullreposet(repo), x)
489 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
499 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
490
500
491 @predicate('adds(pattern)', safe=True)
501 @predicate('adds(pattern)', safe=True)
492 def adds(repo, subset, x):
502 def adds(repo, subset, x):
493 """Changesets that add a file matching pattern.
503 """Changesets that add a file matching pattern.
494
504
495 The pattern without explicit kind like ``glob:`` is expected to be
505 The pattern without explicit kind like ``glob:`` is expected to be
496 relative to the current directory and match against a file or a
506 relative to the current directory and match against a file or a
497 directory.
507 directory.
498 """
508 """
499 # i18n: "adds" is a keyword
509 # i18n: "adds" is a keyword
500 pat = getstring(x, _("adds requires a pattern"))
510 pat = getstring(x, _("adds requires a pattern"))
501 return checkstatus(repo, subset, pat, 1)
511 return checkstatus(repo, subset, pat, 1)
502
512
503 @predicate('ancestor(*changeset)', safe=True)
513 @predicate('ancestor(*changeset)', safe=True)
504 def ancestor(repo, subset, x):
514 def ancestor(repo, subset, x):
505 """A greatest common ancestor of the changesets.
515 """A greatest common ancestor of the changesets.
506
516
507 Accepts 0 or more changesets.
517 Accepts 0 or more changesets.
508 Will return empty list when passed no args.
518 Will return empty list when passed no args.
509 Greatest common ancestor of a single changeset is that changeset.
519 Greatest common ancestor of a single changeset is that changeset.
510 """
520 """
511 # i18n: "ancestor" is a keyword
521 # i18n: "ancestor" is a keyword
512 l = getlist(x)
522 l = getlist(x)
513 rl = fullreposet(repo)
523 rl = fullreposet(repo)
514 anc = None
524 anc = None
515
525
516 # (getset(repo, rl, i) for i in l) generates a list of lists
526 # (getset(repo, rl, i) for i in l) generates a list of lists
517 for revs in (getset(repo, rl, i) for i in l):
527 for revs in (getset(repo, rl, i) for i in l):
518 for r in revs:
528 for r in revs:
519 if anc is None:
529 if anc is None:
520 anc = repo[r]
530 anc = repo[r]
521 else:
531 else:
522 anc = anc.ancestor(repo[r])
532 anc = anc.ancestor(repo[r])
523
533
524 if anc is not None and anc.rev() in subset:
534 if anc is not None and anc.rev() in subset:
525 return baseset([anc.rev()])
535 return baseset([anc.rev()])
526 return baseset()
536 return baseset()
527
537
528 def _ancestors(repo, subset, x, followfirst=False):
538 def _ancestors(repo, subset, x, followfirst=False):
529 heads = getset(repo, fullreposet(repo), x)
539 heads = getset(repo, fullreposet(repo), x)
530 if not heads:
540 if not heads:
531 return baseset()
541 return baseset()
532 s = _revancestors(repo, heads, followfirst)
542 s = _revancestors(repo, heads, followfirst)
533 return subset & s
543 return subset & s
534
544
535 @predicate('ancestors(set)', safe=True)
545 @predicate('ancestors(set)', safe=True)
536 def ancestors(repo, subset, x):
546 def ancestors(repo, subset, x):
537 """Changesets that are ancestors of a changeset in set.
547 """Changesets that are ancestors of a changeset in set.
538 """
548 """
539 return _ancestors(repo, subset, x)
549 return _ancestors(repo, subset, x)
540
550
541 @predicate('_firstancestors', safe=True)
551 @predicate('_firstancestors', safe=True)
542 def _firstancestors(repo, subset, x):
552 def _firstancestors(repo, subset, x):
543 # ``_firstancestors(set)``
553 # ``_firstancestors(set)``
544 # Like ``ancestors(set)`` but follows only the first parents.
554 # Like ``ancestors(set)`` but follows only the first parents.
545 return _ancestors(repo, subset, x, followfirst=True)
555 return _ancestors(repo, subset, x, followfirst=True)
546
556
547 def ancestorspec(repo, subset, x, n, order):
557 def ancestorspec(repo, subset, x, n, order):
548 """``set~n``
558 """``set~n``
549 Changesets that are the Nth ancestor (first parents only) of a changeset
559 Changesets that are the Nth ancestor (first parents only) of a changeset
550 in set.
560 in set.
551 """
561 """
552 n = getinteger(n, _("~ expects a number"))
562 n = getinteger(n, _("~ expects a number"))
553 ps = set()
563 ps = set()
554 cl = repo.changelog
564 cl = repo.changelog
555 for r in getset(repo, fullreposet(repo), x):
565 for r in getset(repo, fullreposet(repo), x):
556 for i in range(n):
566 for i in range(n):
557 r = cl.parentrevs(r)[0]
567 r = cl.parentrevs(r)[0]
558 ps.add(r)
568 ps.add(r)
559 return subset & ps
569 return subset & ps
560
570
561 @predicate('author(string)', safe=True)
571 @predicate('author(string)', safe=True)
562 def author(repo, subset, x):
572 def author(repo, subset, x):
563 """Alias for ``user(string)``.
573 """Alias for ``user(string)``.
564 """
574 """
565 # i18n: "author" is a keyword
575 # i18n: "author" is a keyword
566 n = getstring(x, _("author requires a string"))
576 n = getstring(x, _("author requires a string"))
567 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
577 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
568 return subset.filter(lambda x: matcher(repo[x].user()),
578 return subset.filter(lambda x: matcher(repo[x].user()),
569 condrepr=('<user %r>', n))
579 condrepr=('<user %r>', n))
570
580
571 @predicate('bisect(string)', safe=True)
581 @predicate('bisect(string)', safe=True)
572 def bisect(repo, subset, x):
582 def bisect(repo, subset, x):
573 """Changesets marked in the specified bisect status:
583 """Changesets marked in the specified bisect status:
574
584
575 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
585 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
576 - ``goods``, ``bads`` : csets topologically good/bad
586 - ``goods``, ``bads`` : csets topologically good/bad
577 - ``range`` : csets taking part in the bisection
587 - ``range`` : csets taking part in the bisection
578 - ``pruned`` : csets that are goods, bads or skipped
588 - ``pruned`` : csets that are goods, bads or skipped
579 - ``untested`` : csets whose fate is yet unknown
589 - ``untested`` : csets whose fate is yet unknown
580 - ``ignored`` : csets ignored due to DAG topology
590 - ``ignored`` : csets ignored due to DAG topology
581 - ``current`` : the cset currently being bisected
591 - ``current`` : the cset currently being bisected
582 """
592 """
583 # i18n: "bisect" is a keyword
593 # i18n: "bisect" is a keyword
584 status = getstring(x, _("bisect requires a string")).lower()
594 status = getstring(x, _("bisect requires a string")).lower()
585 state = set(hbisect.get(repo, status))
595 state = set(hbisect.get(repo, status))
586 return subset & state
596 return subset & state
587
597
588 # Backward-compatibility
598 # Backward-compatibility
589 # - no help entry so that we do not advertise it any more
599 # - no help entry so that we do not advertise it any more
590 @predicate('bisected', safe=True)
600 @predicate('bisected', safe=True)
591 def bisected(repo, subset, x):
601 def bisected(repo, subset, x):
592 return bisect(repo, subset, x)
602 return bisect(repo, subset, x)
593
603
594 @predicate('bookmark([name])', safe=True)
604 @predicate('bookmark([name])', safe=True)
595 def bookmark(repo, subset, x):
605 def bookmark(repo, subset, x):
596 """The named bookmark or all bookmarks.
606 """The named bookmark or all bookmarks.
597
607
598 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
608 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
599 """
609 """
600 # i18n: "bookmark" is a keyword
610 # i18n: "bookmark" is a keyword
601 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
611 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
602 if args:
612 if args:
603 bm = getstring(args[0],
613 bm = getstring(args[0],
604 # i18n: "bookmark" is a keyword
614 # i18n: "bookmark" is a keyword
605 _('the argument to bookmark must be a string'))
615 _('the argument to bookmark must be a string'))
606 kind, pattern, matcher = util.stringmatcher(bm)
616 kind, pattern, matcher = util.stringmatcher(bm)
607 bms = set()
617 bms = set()
608 if kind == 'literal':
618 if kind == 'literal':
609 bmrev = repo._bookmarks.get(pattern, None)
619 bmrev = repo._bookmarks.get(pattern, None)
610 if not bmrev:
620 if not bmrev:
611 raise error.RepoLookupError(_("bookmark '%s' does not exist")
621 raise error.RepoLookupError(_("bookmark '%s' does not exist")
612 % pattern)
622 % pattern)
613 bms.add(repo[bmrev].rev())
623 bms.add(repo[bmrev].rev())
614 else:
624 else:
615 matchrevs = set()
625 matchrevs = set()
616 for name, bmrev in repo._bookmarks.iteritems():
626 for name, bmrev in repo._bookmarks.iteritems():
617 if matcher(name):
627 if matcher(name):
618 matchrevs.add(bmrev)
628 matchrevs.add(bmrev)
619 if not matchrevs:
629 if not matchrevs:
620 raise error.RepoLookupError(_("no bookmarks exist"
630 raise error.RepoLookupError(_("no bookmarks exist"
621 " that match '%s'") % pattern)
631 " that match '%s'") % pattern)
622 for bmrev in matchrevs:
632 for bmrev in matchrevs:
623 bms.add(repo[bmrev].rev())
633 bms.add(repo[bmrev].rev())
624 else:
634 else:
625 bms = set([repo[r].rev()
635 bms = set([repo[r].rev()
626 for r in repo._bookmarks.values()])
636 for r in repo._bookmarks.values()])
627 bms -= set([node.nullrev])
637 bms -= set([node.nullrev])
628 return subset & bms
638 return subset & bms
629
639
630 @predicate('branch(string or set)', safe=True)
640 @predicate('branch(string or set)', safe=True)
631 def branch(repo, subset, x):
641 def branch(repo, subset, x):
632 """
642 """
633 All changesets belonging to the given branch or the branches of the given
643 All changesets belonging to the given branch or the branches of the given
634 changesets.
644 changesets.
635
645
636 Pattern matching is supported for `string`. See
646 Pattern matching is supported for `string`. See
637 :hg:`help revisions.patterns`.
647 :hg:`help revisions.patterns`.
638 """
648 """
639 getbi = repo.revbranchcache().branchinfo
649 getbi = repo.revbranchcache().branchinfo
640
650
641 try:
651 try:
642 b = getstring(x, '')
652 b = getstring(x, '')
643 except error.ParseError:
653 except error.ParseError:
644 # not a string, but another revspec, e.g. tip()
654 # not a string, but another revspec, e.g. tip()
645 pass
655 pass
646 else:
656 else:
647 kind, pattern, matcher = util.stringmatcher(b)
657 kind, pattern, matcher = util.stringmatcher(b)
648 if kind == 'literal':
658 if kind == 'literal':
649 # note: falls through to the revspec case if no branch with
659 # note: falls through to the revspec case if no branch with
650 # this name exists and pattern kind is not specified explicitly
660 # this name exists and pattern kind is not specified explicitly
651 if pattern in repo.branchmap():
661 if pattern in repo.branchmap():
652 return subset.filter(lambda r: matcher(getbi(r)[0]),
662 return subset.filter(lambda r: matcher(getbi(r)[0]),
653 condrepr=('<branch %r>', b))
663 condrepr=('<branch %r>', b))
654 if b.startswith('literal:'):
664 if b.startswith('literal:'):
655 raise error.RepoLookupError(_("branch '%s' does not exist")
665 raise error.RepoLookupError(_("branch '%s' does not exist")
656 % pattern)
666 % pattern)
657 else:
667 else:
658 return subset.filter(lambda r: matcher(getbi(r)[0]),
668 return subset.filter(lambda r: matcher(getbi(r)[0]),
659 condrepr=('<branch %r>', b))
669 condrepr=('<branch %r>', b))
660
670
661 s = getset(repo, fullreposet(repo), x)
671 s = getset(repo, fullreposet(repo), x)
662 b = set()
672 b = set()
663 for r in s:
673 for r in s:
664 b.add(getbi(r)[0])
674 b.add(getbi(r)[0])
665 c = s.__contains__
675 c = s.__contains__
666 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
676 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
667 condrepr=lambda: '<branch %r>' % sorted(b))
677 condrepr=lambda: '<branch %r>' % sorted(b))
668
678
669 @predicate('bumped()', safe=True)
679 @predicate('bumped()', safe=True)
670 def bumped(repo, subset, x):
680 def bumped(repo, subset, x):
671 """Mutable changesets marked as successors of public changesets.
681 """Mutable changesets marked as successors of public changesets.
672
682
673 Only non-public and non-obsolete changesets can be `bumped`.
683 Only non-public and non-obsolete changesets can be `bumped`.
674 """
684 """
675 # i18n: "bumped" is a keyword
685 # i18n: "bumped" is a keyword
676 getargs(x, 0, 0, _("bumped takes no arguments"))
686 getargs(x, 0, 0, _("bumped takes no arguments"))
677 bumped = obsmod.getrevs(repo, 'bumped')
687 bumped = obsmod.getrevs(repo, 'bumped')
678 return subset & bumped
688 return subset & bumped
679
689
680 @predicate('bundle()', safe=True)
690 @predicate('bundle()', safe=True)
681 def bundle(repo, subset, x):
691 def bundle(repo, subset, x):
682 """Changesets in the bundle.
692 """Changesets in the bundle.
683
693
684 Bundle must be specified by the -R option."""
694 Bundle must be specified by the -R option."""
685
695
686 try:
696 try:
687 bundlerevs = repo.changelog.bundlerevs
697 bundlerevs = repo.changelog.bundlerevs
688 except AttributeError:
698 except AttributeError:
689 raise error.Abort(_("no bundle provided - specify with -R"))
699 raise error.Abort(_("no bundle provided - specify with -R"))
690 return subset & bundlerevs
700 return subset & bundlerevs
691
701
692 def checkstatus(repo, subset, pat, field):
702 def checkstatus(repo, subset, pat, field):
693 hasset = matchmod.patkind(pat) == 'set'
703 hasset = matchmod.patkind(pat) == 'set'
694
704
695 mcache = [None]
705 mcache = [None]
696 def matches(x):
706 def matches(x):
697 c = repo[x]
707 c = repo[x]
698 if not mcache[0] or hasset:
708 if not mcache[0] or hasset:
699 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
709 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
700 m = mcache[0]
710 m = mcache[0]
701 fname = None
711 fname = None
702 if not m.anypats() and len(m.files()) == 1:
712 if not m.anypats() and len(m.files()) == 1:
703 fname = m.files()[0]
713 fname = m.files()[0]
704 if fname is not None:
714 if fname is not None:
705 if fname not in c.files():
715 if fname not in c.files():
706 return False
716 return False
707 else:
717 else:
708 for f in c.files():
718 for f in c.files():
709 if m(f):
719 if m(f):
710 break
720 break
711 else:
721 else:
712 return False
722 return False
713 files = repo.status(c.p1().node(), c.node())[field]
723 files = repo.status(c.p1().node(), c.node())[field]
714 if fname is not None:
724 if fname is not None:
715 if fname in files:
725 if fname in files:
716 return True
726 return True
717 else:
727 else:
718 for f in files:
728 for f in files:
719 if m(f):
729 if m(f):
720 return True
730 return True
721
731
722 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
732 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
723
733
724 def _children(repo, subset, parentset):
734 def _children(repo, subset, parentset):
725 if not parentset:
735 if not parentset:
726 return baseset()
736 return baseset()
727 cs = set()
737 cs = set()
728 pr = repo.changelog.parentrevs
738 pr = repo.changelog.parentrevs
729 minrev = parentset.min()
739 minrev = parentset.min()
730 nullrev = node.nullrev
740 nullrev = node.nullrev
731 for r in subset:
741 for r in subset:
732 if r <= minrev:
742 if r <= minrev:
733 continue
743 continue
734 p1, p2 = pr(r)
744 p1, p2 = pr(r)
735 if p1 in parentset:
745 if p1 in parentset:
736 cs.add(r)
746 cs.add(r)
737 if p2 != nullrev and p2 in parentset:
747 if p2 != nullrev and p2 in parentset:
738 cs.add(r)
748 cs.add(r)
739 return baseset(cs)
749 return baseset(cs)
740
750
741 @predicate('children(set)', safe=True)
751 @predicate('children(set)', safe=True)
742 def children(repo, subset, x):
752 def children(repo, subset, x):
743 """Child changesets of changesets in set.
753 """Child changesets of changesets in set.
744 """
754 """
745 s = getset(repo, fullreposet(repo), x)
755 s = getset(repo, fullreposet(repo), x)
746 cs = _children(repo, subset, s)
756 cs = _children(repo, subset, s)
747 return subset & cs
757 return subset & cs
748
758
749 @predicate('closed()', safe=True)
759 @predicate('closed()', safe=True)
750 def closed(repo, subset, x):
760 def closed(repo, subset, x):
751 """Changeset is closed.
761 """Changeset is closed.
752 """
762 """
753 # i18n: "closed" is a keyword
763 # i18n: "closed" is a keyword
754 getargs(x, 0, 0, _("closed takes no arguments"))
764 getargs(x, 0, 0, _("closed takes no arguments"))
755 return subset.filter(lambda r: repo[r].closesbranch(),
765 return subset.filter(lambda r: repo[r].closesbranch(),
756 condrepr='<branch closed>')
766 condrepr='<branch closed>')
757
767
758 @predicate('contains(pattern)')
768 @predicate('contains(pattern)')
759 def contains(repo, subset, x):
769 def contains(repo, subset, x):
760 """The revision's manifest contains a file matching pattern (but might not
770 """The revision's manifest contains a file matching pattern (but might not
761 modify it). See :hg:`help patterns` for information about file patterns.
771 modify it). See :hg:`help patterns` for information about file patterns.
762
772
763 The pattern without explicit kind like ``glob:`` is expected to be
773 The pattern without explicit kind like ``glob:`` is expected to be
764 relative to the current directory and match against a file exactly
774 relative to the current directory and match against a file exactly
765 for efficiency.
775 for efficiency.
766 """
776 """
767 # i18n: "contains" is a keyword
777 # i18n: "contains" is a keyword
768 pat = getstring(x, _("contains requires a pattern"))
778 pat = getstring(x, _("contains requires a pattern"))
769
779
770 def matches(x):
780 def matches(x):
771 if not matchmod.patkind(pat):
781 if not matchmod.patkind(pat):
772 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
782 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
773 if pats in repo[x]:
783 if pats in repo[x]:
774 return True
784 return True
775 else:
785 else:
776 c = repo[x]
786 c = repo[x]
777 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
787 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
778 for f in c.manifest():
788 for f in c.manifest():
779 if m(f):
789 if m(f):
780 return True
790 return True
781 return False
791 return False
782
792
783 return subset.filter(matches, condrepr=('<contains %r>', pat))
793 return subset.filter(matches, condrepr=('<contains %r>', pat))
784
794
785 @predicate('converted([id])', safe=True)
795 @predicate('converted([id])', safe=True)
786 def converted(repo, subset, x):
796 def converted(repo, subset, x):
787 """Changesets converted from the given identifier in the old repository if
797 """Changesets converted from the given identifier in the old repository if
788 present, or all converted changesets if no identifier is specified.
798 present, or all converted changesets if no identifier is specified.
789 """
799 """
790
800
791 # There is exactly no chance of resolving the revision, so do a simple
801 # There is exactly no chance of resolving the revision, so do a simple
792 # string compare and hope for the best
802 # string compare and hope for the best
793
803
794 rev = None
804 rev = None
795 # i18n: "converted" is a keyword
805 # i18n: "converted" is a keyword
796 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
806 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
797 if l:
807 if l:
798 # i18n: "converted" is a keyword
808 # i18n: "converted" is a keyword
799 rev = getstring(l[0], _('converted requires a revision'))
809 rev = getstring(l[0], _('converted requires a revision'))
800
810
801 def _matchvalue(r):
811 def _matchvalue(r):
802 source = repo[r].extra().get('convert_revision', None)
812 source = repo[r].extra().get('convert_revision', None)
803 return source is not None and (rev is None or source.startswith(rev))
813 return source is not None and (rev is None or source.startswith(rev))
804
814
805 return subset.filter(lambda r: _matchvalue(r),
815 return subset.filter(lambda r: _matchvalue(r),
806 condrepr=('<converted %r>', rev))
816 condrepr=('<converted %r>', rev))
807
817
808 @predicate('date(interval)', safe=True)
818 @predicate('date(interval)', safe=True)
809 def date(repo, subset, x):
819 def date(repo, subset, x):
810 """Changesets within the interval, see :hg:`help dates`.
820 """Changesets within the interval, see :hg:`help dates`.
811 """
821 """
812 # i18n: "date" is a keyword
822 # i18n: "date" is a keyword
813 ds = getstring(x, _("date requires a string"))
823 ds = getstring(x, _("date requires a string"))
814 dm = util.matchdate(ds)
824 dm = util.matchdate(ds)
815 return subset.filter(lambda x: dm(repo[x].date()[0]),
825 return subset.filter(lambda x: dm(repo[x].date()[0]),
816 condrepr=('<date %r>', ds))
826 condrepr=('<date %r>', ds))
817
827
818 @predicate('desc(string)', safe=True)
828 @predicate('desc(string)', safe=True)
819 def desc(repo, subset, x):
829 def desc(repo, subset, x):
820 """Search commit message for string. The match is case-insensitive.
830 """Search commit message for string. The match is case-insensitive.
821
831
822 Pattern matching is supported for `string`. See
832 Pattern matching is supported for `string`. See
823 :hg:`help revisions.patterns`.
833 :hg:`help revisions.patterns`.
824 """
834 """
825 # i18n: "desc" is a keyword
835 # i18n: "desc" is a keyword
826 ds = getstring(x, _("desc requires a string"))
836 ds = getstring(x, _("desc requires a string"))
827
837
828 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
838 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
829
839
830 return subset.filter(lambda r: matcher(repo[r].description()),
840 return subset.filter(lambda r: matcher(repo[r].description()),
831 condrepr=('<desc %r>', ds))
841 condrepr=('<desc %r>', ds))
832
842
833 def _descendants(repo, subset, x, followfirst=False):
843 def _descendants(repo, subset, x, followfirst=False):
834 roots = getset(repo, fullreposet(repo), x)
844 roots = getset(repo, fullreposet(repo), x)
835 if not roots:
845 if not roots:
836 return baseset()
846 return baseset()
837 s = _revdescendants(repo, roots, followfirst)
847 s = _revdescendants(repo, roots, followfirst)
838
848
839 # Both sets need to be ascending in order to lazily return the union
849 # Both sets need to be ascending in order to lazily return the union
840 # in the correct order.
850 # in the correct order.
841 base = subset & roots
851 base = subset & roots
842 desc = subset & s
852 desc = subset & s
843 result = base + desc
853 result = base + desc
844 if subset.isascending():
854 if subset.isascending():
845 result.sort()
855 result.sort()
846 elif subset.isdescending():
856 elif subset.isdescending():
847 result.sort(reverse=True)
857 result.sort(reverse=True)
848 else:
858 else:
849 result = subset & result
859 result = subset & result
850 return result
860 return result
851
861
852 @predicate('descendants(set)', safe=True)
862 @predicate('descendants(set)', safe=True)
853 def descendants(repo, subset, x):
863 def descendants(repo, subset, x):
854 """Changesets which are descendants of changesets in set.
864 """Changesets which are descendants of changesets in set.
855 """
865 """
856 return _descendants(repo, subset, x)
866 return _descendants(repo, subset, x)
857
867
858 @predicate('_firstdescendants', safe=True)
868 @predicate('_firstdescendants', safe=True)
859 def _firstdescendants(repo, subset, x):
869 def _firstdescendants(repo, subset, x):
860 # ``_firstdescendants(set)``
870 # ``_firstdescendants(set)``
861 # Like ``descendants(set)`` but follows only the first parents.
871 # Like ``descendants(set)`` but follows only the first parents.
862 return _descendants(repo, subset, x, followfirst=True)
872 return _descendants(repo, subset, x, followfirst=True)
863
873
864 @predicate('destination([set])', safe=True)
874 @predicate('destination([set])', safe=True)
865 def destination(repo, subset, x):
875 def destination(repo, subset, x):
866 """Changesets that were created by a graft, transplant or rebase operation,
876 """Changesets that were created by a graft, transplant or rebase operation,
867 with the given revisions specified as the source. Omitting the optional set
877 with the given revisions specified as the source. Omitting the optional set
868 is the same as passing all().
878 is the same as passing all().
869 """
879 """
870 if x is not None:
880 if x is not None:
871 sources = getset(repo, fullreposet(repo), x)
881 sources = getset(repo, fullreposet(repo), x)
872 else:
882 else:
873 sources = fullreposet(repo)
883 sources = fullreposet(repo)
874
884
875 dests = set()
885 dests = set()
876
886
877 # subset contains all of the possible destinations that can be returned, so
887 # subset contains all of the possible destinations that can be returned, so
878 # iterate over them and see if their source(s) were provided in the arg set.
888 # iterate over them and see if their source(s) were provided in the arg set.
879 # Even if the immediate src of r is not in the arg set, src's source (or
889 # Even if the immediate src of r is not in the arg set, src's source (or
880 # further back) may be. Scanning back further than the immediate src allows
890 # further back) may be. Scanning back further than the immediate src allows
881 # transitive transplants and rebases to yield the same results as transitive
891 # transitive transplants and rebases to yield the same results as transitive
882 # grafts.
892 # grafts.
883 for r in subset:
893 for r in subset:
884 src = _getrevsource(repo, r)
894 src = _getrevsource(repo, r)
885 lineage = None
895 lineage = None
886
896
887 while src is not None:
897 while src is not None:
888 if lineage is None:
898 if lineage is None:
889 lineage = list()
899 lineage = list()
890
900
891 lineage.append(r)
901 lineage.append(r)
892
902
893 # The visited lineage is a match if the current source is in the arg
903 # The visited lineage is a match if the current source is in the arg
894 # set. Since every candidate dest is visited by way of iterating
904 # set. Since every candidate dest is visited by way of iterating
895 # subset, any dests further back in the lineage will be tested by a
905 # subset, any dests further back in the lineage will be tested by a
896 # different iteration over subset. Likewise, if the src was already
906 # different iteration over subset. Likewise, if the src was already
897 # selected, the current lineage can be selected without going back
907 # selected, the current lineage can be selected without going back
898 # further.
908 # further.
899 if src in sources or src in dests:
909 if src in sources or src in dests:
900 dests.update(lineage)
910 dests.update(lineage)
901 break
911 break
902
912
903 r = src
913 r = src
904 src = _getrevsource(repo, r)
914 src = _getrevsource(repo, r)
905
915
906 return subset.filter(dests.__contains__,
916 return subset.filter(dests.__contains__,
907 condrepr=lambda: '<destination %r>' % sorted(dests))
917 condrepr=lambda: '<destination %r>' % sorted(dests))
908
918
909 @predicate('divergent()', safe=True)
919 @predicate('divergent()', safe=True)
910 def divergent(repo, subset, x):
920 def divergent(repo, subset, x):
911 """
921 """
912 Final successors of changesets with an alternative set of final successors.
922 Final successors of changesets with an alternative set of final successors.
913 """
923 """
914 # i18n: "divergent" is a keyword
924 # i18n: "divergent" is a keyword
915 getargs(x, 0, 0, _("divergent takes no arguments"))
925 getargs(x, 0, 0, _("divergent takes no arguments"))
916 divergent = obsmod.getrevs(repo, 'divergent')
926 divergent = obsmod.getrevs(repo, 'divergent')
917 return subset & divergent
927 return subset & divergent
918
928
919 @predicate('extinct()', safe=True)
929 @predicate('extinct()', safe=True)
920 def extinct(repo, subset, x):
930 def extinct(repo, subset, x):
921 """Obsolete changesets with obsolete descendants only.
931 """Obsolete changesets with obsolete descendants only.
922 """
932 """
923 # i18n: "extinct" is a keyword
933 # i18n: "extinct" is a keyword
924 getargs(x, 0, 0, _("extinct takes no arguments"))
934 getargs(x, 0, 0, _("extinct takes no arguments"))
925 extincts = obsmod.getrevs(repo, 'extinct')
935 extincts = obsmod.getrevs(repo, 'extinct')
926 return subset & extincts
936 return subset & extincts
927
937
928 @predicate('extra(label, [value])', safe=True)
938 @predicate('extra(label, [value])', safe=True)
929 def extra(repo, subset, x):
939 def extra(repo, subset, x):
930 """Changesets with the given label in the extra metadata, with the given
940 """Changesets with the given label in the extra metadata, with the given
931 optional value.
941 optional value.
932
942
933 Pattern matching is supported for `value`. See
943 Pattern matching is supported for `value`. See
934 :hg:`help revisions.patterns`.
944 :hg:`help revisions.patterns`.
935 """
945 """
936 args = getargsdict(x, 'extra', 'label value')
946 args = getargsdict(x, 'extra', 'label value')
937 if 'label' not in args:
947 if 'label' not in args:
938 # i18n: "extra" is a keyword
948 # i18n: "extra" is a keyword
939 raise error.ParseError(_('extra takes at least 1 argument'))
949 raise error.ParseError(_('extra takes at least 1 argument'))
940 # i18n: "extra" is a keyword
950 # i18n: "extra" is a keyword
941 label = getstring(args['label'], _('first argument to extra must be '
951 label = getstring(args['label'], _('first argument to extra must be '
942 'a string'))
952 'a string'))
943 value = None
953 value = None
944
954
945 if 'value' in args:
955 if 'value' in args:
946 # i18n: "extra" is a keyword
956 # i18n: "extra" is a keyword
947 value = getstring(args['value'], _('second argument to extra must be '
957 value = getstring(args['value'], _('second argument to extra must be '
948 'a string'))
958 'a string'))
949 kind, value, matcher = util.stringmatcher(value)
959 kind, value, matcher = util.stringmatcher(value)
950
960
951 def _matchvalue(r):
961 def _matchvalue(r):
952 extra = repo[r].extra()
962 extra = repo[r].extra()
953 return label in extra and (value is None or matcher(extra[label]))
963 return label in extra and (value is None or matcher(extra[label]))
954
964
955 return subset.filter(lambda r: _matchvalue(r),
965 return subset.filter(lambda r: _matchvalue(r),
956 condrepr=('<extra[%r] %r>', label, value))
966 condrepr=('<extra[%r] %r>', label, value))
957
967
958 @predicate('filelog(pattern)', safe=True)
968 @predicate('filelog(pattern)', safe=True)
959 def filelog(repo, subset, x):
969 def filelog(repo, subset, x):
960 """Changesets connected to the specified filelog.
970 """Changesets connected to the specified filelog.
961
971
962 For performance reasons, visits only revisions mentioned in the file-level
972 For performance reasons, visits only revisions mentioned in the file-level
963 filelog, rather than filtering through all changesets (much faster, but
973 filelog, rather than filtering through all changesets (much faster, but
964 doesn't include deletes or duplicate changes). For a slower, more accurate
974 doesn't include deletes or duplicate changes). For a slower, more accurate
965 result, use ``file()``.
975 result, use ``file()``.
966
976
967 The pattern without explicit kind like ``glob:`` is expected to be
977 The pattern without explicit kind like ``glob:`` is expected to be
968 relative to the current directory and match against a file exactly
978 relative to the current directory and match against a file exactly
969 for efficiency.
979 for efficiency.
970
980
971 If some linkrev points to revisions filtered by the current repoview, we'll
981 If some linkrev points to revisions filtered by the current repoview, we'll
972 work around it to return a non-filtered value.
982 work around it to return a non-filtered value.
973 """
983 """
974
984
975 # i18n: "filelog" is a keyword
985 # i18n: "filelog" is a keyword
976 pat = getstring(x, _("filelog requires a pattern"))
986 pat = getstring(x, _("filelog requires a pattern"))
977 s = set()
987 s = set()
978 cl = repo.changelog
988 cl = repo.changelog
979
989
980 if not matchmod.patkind(pat):
990 if not matchmod.patkind(pat):
981 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
991 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
982 files = [f]
992 files = [f]
983 else:
993 else:
984 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
994 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
985 files = (f for f in repo[None] if m(f))
995 files = (f for f in repo[None] if m(f))
986
996
987 for f in files:
997 for f in files:
988 fl = repo.file(f)
998 fl = repo.file(f)
989 known = {}
999 known = {}
990 scanpos = 0
1000 scanpos = 0
991 for fr in list(fl):
1001 for fr in list(fl):
992 fn = fl.node(fr)
1002 fn = fl.node(fr)
993 if fn in known:
1003 if fn in known:
994 s.add(known[fn])
1004 s.add(known[fn])
995 continue
1005 continue
996
1006
997 lr = fl.linkrev(fr)
1007 lr = fl.linkrev(fr)
998 if lr in cl:
1008 if lr in cl:
999 s.add(lr)
1009 s.add(lr)
1000 elif scanpos is not None:
1010 elif scanpos is not None:
1001 # lowest matching changeset is filtered, scan further
1011 # lowest matching changeset is filtered, scan further
1002 # ahead in changelog
1012 # ahead in changelog
1003 start = max(lr, scanpos) + 1
1013 start = max(lr, scanpos) + 1
1004 scanpos = None
1014 scanpos = None
1005 for r in cl.revs(start):
1015 for r in cl.revs(start):
1006 # minimize parsing of non-matching entries
1016 # minimize parsing of non-matching entries
1007 if f in cl.revision(r) and f in cl.readfiles(r):
1017 if f in cl.revision(r) and f in cl.readfiles(r):
1008 try:
1018 try:
1009 # try to use manifest delta fastpath
1019 # try to use manifest delta fastpath
1010 n = repo[r].filenode(f)
1020 n = repo[r].filenode(f)
1011 if n not in known:
1021 if n not in known:
1012 if n == fn:
1022 if n == fn:
1013 s.add(r)
1023 s.add(r)
1014 scanpos = r
1024 scanpos = r
1015 break
1025 break
1016 else:
1026 else:
1017 known[n] = r
1027 known[n] = r
1018 except error.ManifestLookupError:
1028 except error.ManifestLookupError:
1019 # deletion in changelog
1029 # deletion in changelog
1020 continue
1030 continue
1021
1031
1022 return subset & s
1032 return subset & s
1023
1033
1024 @predicate('first(set, [n])', safe=True)
1034 @predicate('first(set, [n])', safe=True)
1025 def first(repo, subset, x):
1035 def first(repo, subset, x):
1026 """An alias for limit().
1036 """An alias for limit().
1027 """
1037 """
1028 return limit(repo, subset, x)
1038 return limit(repo, subset, x)
1029
1039
1030 def _follow(repo, subset, x, name, followfirst=False):
1040 def _follow(repo, subset, x, name, followfirst=False):
1031 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1041 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1032 "and an optional revset") % name)
1042 "and an optional revset") % name)
1033 c = repo['.']
1043 c = repo['.']
1034 if l:
1044 if l:
1035 x = getstring(l[0], _("%s expected a pattern") % name)
1045 x = getstring(l[0], _("%s expected a pattern") % name)
1036 rev = None
1046 rev = None
1037 if len(l) >= 2:
1047 if len(l) >= 2:
1038 revs = getset(repo, fullreposet(repo), l[1])
1048 revs = getset(repo, fullreposet(repo), l[1])
1039 if len(revs) != 1:
1049 if len(revs) != 1:
1040 raise error.RepoLookupError(
1050 raise error.RepoLookupError(
1041 _("%s expected one starting revision") % name)
1051 _("%s expected one starting revision") % name)
1042 rev = revs.last()
1052 rev = revs.last()
1043 c = repo[rev]
1053 c = repo[rev]
1044 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1054 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1045 ctx=repo[rev], default='path')
1055 ctx=repo[rev], default='path')
1046
1056
1047 files = c.manifest().walk(matcher)
1057 files = c.manifest().walk(matcher)
1048
1058
1049 s = set()
1059 s = set()
1050 for fname in files:
1060 for fname in files:
1051 fctx = c[fname]
1061 fctx = c[fname]
1052 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1062 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1053 # include the revision responsible for the most recent version
1063 # include the revision responsible for the most recent version
1054 s.add(fctx.introrev())
1064 s.add(fctx.introrev())
1055 else:
1065 else:
1056 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1066 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1057
1067
1058 return subset & s
1068 return subset & s
1059
1069
1060 @predicate('follow([pattern[, startrev]])', safe=True)
1070 @predicate('follow([pattern[, startrev]])', safe=True)
1061 def follow(repo, subset, x):
1071 def follow(repo, subset, x):
1062 """
1072 """
1063 An alias for ``::.`` (ancestors of the working directory's first parent).
1073 An alias for ``::.`` (ancestors of the working directory's first parent).
1064 If pattern is specified, the histories of files matching given
1074 If pattern is specified, the histories of files matching given
1065 pattern in the revision given by startrev are followed, including copies.
1075 pattern in the revision given by startrev are followed, including copies.
1066 """
1076 """
1067 return _follow(repo, subset, x, 'follow')
1077 return _follow(repo, subset, x, 'follow')
1068
1078
1069 @predicate('_followfirst', safe=True)
1079 @predicate('_followfirst', safe=True)
1070 def _followfirst(repo, subset, x):
1080 def _followfirst(repo, subset, x):
1071 # ``followfirst([pattern[, startrev]])``
1081 # ``followfirst([pattern[, startrev]])``
1072 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1082 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1073 # of every revisions or files revisions.
1083 # of every revisions or files revisions.
1074 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1084 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1075
1085
1076 @predicate('followlines(file, fromline, toline[, startrev=.])', safe=True)
1086 @predicate('followlines(file, fromline, toline[, startrev=.])', safe=True)
1077 def followlines(repo, subset, x):
1087 def followlines(repo, subset, x):
1078 """Changesets modifying `file` in line range ('fromline', 'toline').
1088 """Changesets modifying `file` in line range ('fromline', 'toline').
1079
1089
1080 Line range corresponds to 'file' content at 'startrev' and should hence be
1090 Line range corresponds to 'file' content at 'startrev' and should hence be
1081 consistent with file size. If startrev is not specified, working directory's
1091 consistent with file size. If startrev is not specified, working directory's
1082 parent is used.
1092 parent is used.
1083 """
1093 """
1084 from . import context # avoid circular import issues
1094 from . import context # avoid circular import issues
1085
1095
1086 args = getargsdict(x, 'followlines', 'file *lines startrev')
1096 args = getargsdict(x, 'followlines', 'file *lines startrev')
1087 if len(args['lines']) != 2:
1097 if len(args['lines']) != 2:
1088 raise error.ParseError(_("followlines takes at least three arguments"))
1098 raise error.ParseError(_("followlines takes at least three arguments"))
1089
1099
1090 rev = '.'
1100 rev = '.'
1091 if 'startrev' in args:
1101 if 'startrev' in args:
1092 revs = getset(repo, fullreposet(repo), args['startrev'])
1102 revs = getset(repo, fullreposet(repo), args['startrev'])
1093 if len(revs) != 1:
1103 if len(revs) != 1:
1094 raise error.ParseError(
1104 raise error.ParseError(
1095 _("followlines expects exactly one revision"))
1105 _("followlines expects exactly one revision"))
1096 rev = revs.last()
1106 rev = revs.last()
1097
1107
1098 pat = getstring(args['file'], _("followlines requires a pattern"))
1108 pat = getstring(args['file'], _("followlines requires a pattern"))
1099 if not matchmod.patkind(pat):
1109 if not matchmod.patkind(pat):
1100 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
1110 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
1101 else:
1111 else:
1102 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
1112 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
1103 files = [f for f in repo[rev] if m(f)]
1113 files = [f for f in repo[rev] if m(f)]
1104 if len(files) != 1:
1114 if len(files) != 1:
1105 raise error.ParseError(_("followlines expects exactly one file"))
1115 raise error.ParseError(_("followlines expects exactly one file"))
1106 fname = files[0]
1116 fname = files[0]
1107
1117
1108 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
1118 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
1109 for a in args['lines']]
1119 for a in args['lines']]
1110 if toline - fromline < 0:
1120 if toline - fromline < 0:
1111 raise error.ParseError(_("line range must be positive"))
1121 raise error.ParseError(_("line range must be positive"))
1112 if fromline < 1:
1122 if fromline < 1:
1113 raise error.ParseError(_("fromline must be strictly positive"))
1123 raise error.ParseError(_("fromline must be strictly positive"))
1114 fromline -= 1
1124 fromline -= 1
1115
1125
1116 fctx = repo[rev].filectx(fname)
1126 fctx = repo[rev].filectx(fname)
1117 revs = (c.rev() for c in context.blockancestors(fctx, fromline, toline))
1127 revs = (c.rev() for c in context.blockancestors(fctx, fromline, toline))
1118 return subset & generatorset(revs, iterasc=False)
1128 return subset & generatorset(revs, iterasc=False)
1119
1129
1120 @predicate('all()', safe=True)
1130 @predicate('all()', safe=True)
1121 def getall(repo, subset, x):
1131 def getall(repo, subset, x):
1122 """All changesets, the same as ``0:tip``.
1132 """All changesets, the same as ``0:tip``.
1123 """
1133 """
1124 # i18n: "all" is a keyword
1134 # i18n: "all" is a keyword
1125 getargs(x, 0, 0, _("all takes no arguments"))
1135 getargs(x, 0, 0, _("all takes no arguments"))
1126 return subset & spanset(repo) # drop "null" if any
1136 return subset & spanset(repo) # drop "null" if any
1127
1137
1128 @predicate('grep(regex)')
1138 @predicate('grep(regex)')
1129 def grep(repo, subset, x):
1139 def grep(repo, subset, x):
1130 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1140 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1131 to ensure special escape characters are handled correctly. Unlike
1141 to ensure special escape characters are handled correctly. Unlike
1132 ``keyword(string)``, the match is case-sensitive.
1142 ``keyword(string)``, the match is case-sensitive.
1133 """
1143 """
1134 try:
1144 try:
1135 # i18n: "grep" is a keyword
1145 # i18n: "grep" is a keyword
1136 gr = re.compile(getstring(x, _("grep requires a string")))
1146 gr = re.compile(getstring(x, _("grep requires a string")))
1137 except re.error as e:
1147 except re.error as e:
1138 raise error.ParseError(_('invalid match pattern: %s') % e)
1148 raise error.ParseError(_('invalid match pattern: %s') % e)
1139
1149
1140 def matches(x):
1150 def matches(x):
1141 c = repo[x]
1151 c = repo[x]
1142 for e in c.files() + [c.user(), c.description()]:
1152 for e in c.files() + [c.user(), c.description()]:
1143 if gr.search(e):
1153 if gr.search(e):
1144 return True
1154 return True
1145 return False
1155 return False
1146
1156
1147 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1157 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1148
1158
1149 @predicate('_matchfiles', safe=True)
1159 @predicate('_matchfiles', safe=True)
1150 def _matchfiles(repo, subset, x):
1160 def _matchfiles(repo, subset, x):
1151 # _matchfiles takes a revset list of prefixed arguments:
1161 # _matchfiles takes a revset list of prefixed arguments:
1152 #
1162 #
1153 # [p:foo, i:bar, x:baz]
1163 # [p:foo, i:bar, x:baz]
1154 #
1164 #
1155 # builds a match object from them and filters subset. Allowed
1165 # builds a match object from them and filters subset. Allowed
1156 # prefixes are 'p:' for regular patterns, 'i:' for include
1166 # prefixes are 'p:' for regular patterns, 'i:' for include
1157 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1167 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1158 # a revision identifier, or the empty string to reference the
1168 # a revision identifier, or the empty string to reference the
1159 # working directory, from which the match object is
1169 # working directory, from which the match object is
1160 # initialized. Use 'd:' to set the default matching mode, default
1170 # initialized. Use 'd:' to set the default matching mode, default
1161 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1171 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1162
1172
1163 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1173 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1164 pats, inc, exc = [], [], []
1174 pats, inc, exc = [], [], []
1165 rev, default = None, None
1175 rev, default = None, None
1166 for arg in l:
1176 for arg in l:
1167 s = getstring(arg, "_matchfiles requires string arguments")
1177 s = getstring(arg, "_matchfiles requires string arguments")
1168 prefix, value = s[:2], s[2:]
1178 prefix, value = s[:2], s[2:]
1169 if prefix == 'p:':
1179 if prefix == 'p:':
1170 pats.append(value)
1180 pats.append(value)
1171 elif prefix == 'i:':
1181 elif prefix == 'i:':
1172 inc.append(value)
1182 inc.append(value)
1173 elif prefix == 'x:':
1183 elif prefix == 'x:':
1174 exc.append(value)
1184 exc.append(value)
1175 elif prefix == 'r:':
1185 elif prefix == 'r:':
1176 if rev is not None:
1186 if rev is not None:
1177 raise error.ParseError('_matchfiles expected at most one '
1187 raise error.ParseError('_matchfiles expected at most one '
1178 'revision')
1188 'revision')
1179 if value != '': # empty means working directory; leave rev as None
1189 if value != '': # empty means working directory; leave rev as None
1180 rev = value
1190 rev = value
1181 elif prefix == 'd:':
1191 elif prefix == 'd:':
1182 if default is not None:
1192 if default is not None:
1183 raise error.ParseError('_matchfiles expected at most one '
1193 raise error.ParseError('_matchfiles expected at most one '
1184 'default mode')
1194 'default mode')
1185 default = value
1195 default = value
1186 else:
1196 else:
1187 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1197 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1188 if not default:
1198 if not default:
1189 default = 'glob'
1199 default = 'glob'
1190
1200
1191 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1201 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1192 exclude=exc, ctx=repo[rev], default=default)
1202 exclude=exc, ctx=repo[rev], default=default)
1193
1203
1194 # This directly read the changelog data as creating changectx for all
1204 # This directly read the changelog data as creating changectx for all
1195 # revisions is quite expensive.
1205 # revisions is quite expensive.
1196 getfiles = repo.changelog.readfiles
1206 getfiles = repo.changelog.readfiles
1197 wdirrev = node.wdirrev
1207 wdirrev = node.wdirrev
1198 def matches(x):
1208 def matches(x):
1199 if x == wdirrev:
1209 if x == wdirrev:
1200 files = repo[x].files()
1210 files = repo[x].files()
1201 else:
1211 else:
1202 files = getfiles(x)
1212 files = getfiles(x)
1203 for f in files:
1213 for f in files:
1204 if m(f):
1214 if m(f):
1205 return True
1215 return True
1206 return False
1216 return False
1207
1217
1208 return subset.filter(matches,
1218 return subset.filter(matches,
1209 condrepr=('<matchfiles patterns=%r, include=%r '
1219 condrepr=('<matchfiles patterns=%r, include=%r '
1210 'exclude=%r, default=%r, rev=%r>',
1220 'exclude=%r, default=%r, rev=%r>',
1211 pats, inc, exc, default, rev))
1221 pats, inc, exc, default, rev))
1212
1222
1213 @predicate('file(pattern)', safe=True)
1223 @predicate('file(pattern)', safe=True)
1214 def hasfile(repo, subset, x):
1224 def hasfile(repo, subset, x):
1215 """Changesets affecting files matched by pattern.
1225 """Changesets affecting files matched by pattern.
1216
1226
1217 For a faster but less accurate result, consider using ``filelog()``
1227 For a faster but less accurate result, consider using ``filelog()``
1218 instead.
1228 instead.
1219
1229
1220 This predicate uses ``glob:`` as the default kind of pattern.
1230 This predicate uses ``glob:`` as the default kind of pattern.
1221 """
1231 """
1222 # i18n: "file" is a keyword
1232 # i18n: "file" is a keyword
1223 pat = getstring(x, _("file requires a pattern"))
1233 pat = getstring(x, _("file requires a pattern"))
1224 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1234 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1225
1235
1226 @predicate('head()', safe=True)
1236 @predicate('head()', safe=True)
1227 def head(repo, subset, x):
1237 def head(repo, subset, x):
1228 """Changeset is a named branch head.
1238 """Changeset is a named branch head.
1229 """
1239 """
1230 # i18n: "head" is a keyword
1240 # i18n: "head" is a keyword
1231 getargs(x, 0, 0, _("head takes no arguments"))
1241 getargs(x, 0, 0, _("head takes no arguments"))
1232 hs = set()
1242 hs = set()
1233 cl = repo.changelog
1243 cl = repo.changelog
1234 for ls in repo.branchmap().itervalues():
1244 for ls in repo.branchmap().itervalues():
1235 hs.update(cl.rev(h) for h in ls)
1245 hs.update(cl.rev(h) for h in ls)
1236 return subset & baseset(hs)
1246 return subset & baseset(hs)
1237
1247
1238 @predicate('heads(set)', safe=True)
1248 @predicate('heads(set)', safe=True)
1239 def heads(repo, subset, x):
1249 def heads(repo, subset, x):
1240 """Members of set with no children in set.
1250 """Members of set with no children in set.
1241 """
1251 """
1242 s = getset(repo, subset, x)
1252 s = getset(repo, subset, x)
1243 ps = parents(repo, subset, x)
1253 ps = parents(repo, subset, x)
1244 return s - ps
1254 return s - ps
1245
1255
1246 @predicate('hidden()', safe=True)
1256 @predicate('hidden()', safe=True)
1247 def hidden(repo, subset, x):
1257 def hidden(repo, subset, x):
1248 """Hidden changesets.
1258 """Hidden changesets.
1249 """
1259 """
1250 # i18n: "hidden" is a keyword
1260 # i18n: "hidden" is a keyword
1251 getargs(x, 0, 0, _("hidden takes no arguments"))
1261 getargs(x, 0, 0, _("hidden takes no arguments"))
1252 hiddenrevs = repoview.filterrevs(repo, 'visible')
1262 hiddenrevs = repoview.filterrevs(repo, 'visible')
1253 return subset & hiddenrevs
1263 return subset & hiddenrevs
1254
1264
1255 @predicate('keyword(string)', safe=True)
1265 @predicate('keyword(string)', safe=True)
1256 def keyword(repo, subset, x):
1266 def keyword(repo, subset, x):
1257 """Search commit message, user name, and names of changed files for
1267 """Search commit message, user name, and names of changed files for
1258 string. The match is case-insensitive.
1268 string. The match is case-insensitive.
1259
1269
1260 For a regular expression or case sensitive search of these fields, use
1270 For a regular expression or case sensitive search of these fields, use
1261 ``grep(regex)``.
1271 ``grep(regex)``.
1262 """
1272 """
1263 # i18n: "keyword" is a keyword
1273 # i18n: "keyword" is a keyword
1264 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1274 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1265
1275
1266 def matches(r):
1276 def matches(r):
1267 c = repo[r]
1277 c = repo[r]
1268 return any(kw in encoding.lower(t)
1278 return any(kw in encoding.lower(t)
1269 for t in c.files() + [c.user(), c.description()])
1279 for t in c.files() + [c.user(), c.description()])
1270
1280
1271 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1281 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1272
1282
1273 @predicate('limit(set[, n[, offset]])', safe=True)
1283 @predicate('limit(set[, n[, offset]])', safe=True)
1274 def limit(repo, subset, x):
1284 def limit(repo, subset, x):
1275 """First n members of set, defaulting to 1, starting from offset.
1285 """First n members of set, defaulting to 1, starting from offset.
1276 """
1286 """
1277 args = getargsdict(x, 'limit', 'set n offset')
1287 args = getargsdict(x, 'limit', 'set n offset')
1278 if 'set' not in args:
1288 if 'set' not in args:
1279 # i18n: "limit" is a keyword
1289 # i18n: "limit" is a keyword
1280 raise error.ParseError(_("limit requires one to three arguments"))
1290 raise error.ParseError(_("limit requires one to three arguments"))
1281 # i18n: "limit" is a keyword
1291 # i18n: "limit" is a keyword
1282 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1292 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1283 # i18n: "limit" is a keyword
1293 # i18n: "limit" is a keyword
1284 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1294 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1285 if ofs < 0:
1295 if ofs < 0:
1286 raise error.ParseError(_("negative offset"))
1296 raise error.ParseError(_("negative offset"))
1287 os = getset(repo, fullreposet(repo), args['set'])
1297 os = getset(repo, fullreposet(repo), args['set'])
1288 result = []
1298 result = []
1289 it = iter(os)
1299 it = iter(os)
1290 for x in xrange(ofs):
1300 for x in xrange(ofs):
1291 y = next(it, None)
1301 y = next(it, None)
1292 if y is None:
1302 if y is None:
1293 break
1303 break
1294 for x in xrange(lim):
1304 for x in xrange(lim):
1295 y = next(it, None)
1305 y = next(it, None)
1296 if y is None:
1306 if y is None:
1297 break
1307 break
1298 elif y in subset:
1308 elif y in subset:
1299 result.append(y)
1309 result.append(y)
1300 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1310 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1301 lim, ofs, subset, os))
1311 lim, ofs, subset, os))
1302
1312
1303 @predicate('last(set, [n])', safe=True)
1313 @predicate('last(set, [n])', safe=True)
1304 def last(repo, subset, x):
1314 def last(repo, subset, x):
1305 """Last n members of set, defaulting to 1.
1315 """Last n members of set, defaulting to 1.
1306 """
1316 """
1307 # i18n: "last" is a keyword
1317 # i18n: "last" is a keyword
1308 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1318 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1309 lim = 1
1319 lim = 1
1310 if len(l) == 2:
1320 if len(l) == 2:
1311 # i18n: "last" is a keyword
1321 # i18n: "last" is a keyword
1312 lim = getinteger(l[1], _("last expects a number"))
1322 lim = getinteger(l[1], _("last expects a number"))
1313 os = getset(repo, fullreposet(repo), l[0])
1323 os = getset(repo, fullreposet(repo), l[0])
1314 os.reverse()
1324 os.reverse()
1315 result = []
1325 result = []
1316 it = iter(os)
1326 it = iter(os)
1317 for x in xrange(lim):
1327 for x in xrange(lim):
1318 y = next(it, None)
1328 y = next(it, None)
1319 if y is None:
1329 if y is None:
1320 break
1330 break
1321 elif y in subset:
1331 elif y in subset:
1322 result.append(y)
1332 result.append(y)
1323 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1333 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1324
1334
1325 @predicate('max(set)', safe=True)
1335 @predicate('max(set)', safe=True)
1326 def maxrev(repo, subset, x):
1336 def maxrev(repo, subset, x):
1327 """Changeset with highest revision number in set.
1337 """Changeset with highest revision number in set.
1328 """
1338 """
1329 os = getset(repo, fullreposet(repo), x)
1339 os = getset(repo, fullreposet(repo), x)
1330 try:
1340 try:
1331 m = os.max()
1341 m = os.max()
1332 if m in subset:
1342 if m in subset:
1333 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1343 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1334 except ValueError:
1344 except ValueError:
1335 # os.max() throws a ValueError when the collection is empty.
1345 # os.max() throws a ValueError when the collection is empty.
1336 # Same as python's max().
1346 # Same as python's max().
1337 pass
1347 pass
1338 return baseset(datarepr=('<max %r, %r>', subset, os))
1348 return baseset(datarepr=('<max %r, %r>', subset, os))
1339
1349
1340 @predicate('merge()', safe=True)
1350 @predicate('merge()', safe=True)
1341 def merge(repo, subset, x):
1351 def merge(repo, subset, x):
1342 """Changeset is a merge changeset.
1352 """Changeset is a merge changeset.
1343 """
1353 """
1344 # i18n: "merge" is a keyword
1354 # i18n: "merge" is a keyword
1345 getargs(x, 0, 0, _("merge takes no arguments"))
1355 getargs(x, 0, 0, _("merge takes no arguments"))
1346 cl = repo.changelog
1356 cl = repo.changelog
1347 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1357 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1348 condrepr='<merge>')
1358 condrepr='<merge>')
1349
1359
1350 @predicate('branchpoint()', safe=True)
1360 @predicate('branchpoint()', safe=True)
1351 def branchpoint(repo, subset, x):
1361 def branchpoint(repo, subset, x):
1352 """Changesets with more than one child.
1362 """Changesets with more than one child.
1353 """
1363 """
1354 # i18n: "branchpoint" is a keyword
1364 # i18n: "branchpoint" is a keyword
1355 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1365 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1356 cl = repo.changelog
1366 cl = repo.changelog
1357 if not subset:
1367 if not subset:
1358 return baseset()
1368 return baseset()
1359 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1369 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1360 # (and if it is not, it should.)
1370 # (and if it is not, it should.)
1361 baserev = min(subset)
1371 baserev = min(subset)
1362 parentscount = [0]*(len(repo) - baserev)
1372 parentscount = [0]*(len(repo) - baserev)
1363 for r in cl.revs(start=baserev + 1):
1373 for r in cl.revs(start=baserev + 1):
1364 for p in cl.parentrevs(r):
1374 for p in cl.parentrevs(r):
1365 if p >= baserev:
1375 if p >= baserev:
1366 parentscount[p - baserev] += 1
1376 parentscount[p - baserev] += 1
1367 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1377 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1368 condrepr='<branchpoint>')
1378 condrepr='<branchpoint>')
1369
1379
1370 @predicate('min(set)', safe=True)
1380 @predicate('min(set)', safe=True)
1371 def minrev(repo, subset, x):
1381 def minrev(repo, subset, x):
1372 """Changeset with lowest revision number in set.
1382 """Changeset with lowest revision number in set.
1373 """
1383 """
1374 os = getset(repo, fullreposet(repo), x)
1384 os = getset(repo, fullreposet(repo), x)
1375 try:
1385 try:
1376 m = os.min()
1386 m = os.min()
1377 if m in subset:
1387 if m in subset:
1378 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1388 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1379 except ValueError:
1389 except ValueError:
1380 # os.min() throws a ValueError when the collection is empty.
1390 # os.min() throws a ValueError when the collection is empty.
1381 # Same as python's min().
1391 # Same as python's min().
1382 pass
1392 pass
1383 return baseset(datarepr=('<min %r, %r>', subset, os))
1393 return baseset(datarepr=('<min %r, %r>', subset, os))
1384
1394
1385 @predicate('modifies(pattern)', safe=True)
1395 @predicate('modifies(pattern)', safe=True)
1386 def modifies(repo, subset, x):
1396 def modifies(repo, subset, x):
1387 """Changesets modifying files matched by pattern.
1397 """Changesets modifying files matched by pattern.
1388
1398
1389 The pattern without explicit kind like ``glob:`` is expected to be
1399 The pattern without explicit kind like ``glob:`` is expected to be
1390 relative to the current directory and match against a file or a
1400 relative to the current directory and match against a file or a
1391 directory.
1401 directory.
1392 """
1402 """
1393 # i18n: "modifies" is a keyword
1403 # i18n: "modifies" is a keyword
1394 pat = getstring(x, _("modifies requires a pattern"))
1404 pat = getstring(x, _("modifies requires a pattern"))
1395 return checkstatus(repo, subset, pat, 0)
1405 return checkstatus(repo, subset, pat, 0)
1396
1406
1397 @predicate('named(namespace)')
1407 @predicate('named(namespace)')
1398 def named(repo, subset, x):
1408 def named(repo, subset, x):
1399 """The changesets in a given namespace.
1409 """The changesets in a given namespace.
1400
1410
1401 Pattern matching is supported for `namespace`. See
1411 Pattern matching is supported for `namespace`. See
1402 :hg:`help revisions.patterns`.
1412 :hg:`help revisions.patterns`.
1403 """
1413 """
1404 # i18n: "named" is a keyword
1414 # i18n: "named" is a keyword
1405 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1415 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1406
1416
1407 ns = getstring(args[0],
1417 ns = getstring(args[0],
1408 # i18n: "named" is a keyword
1418 # i18n: "named" is a keyword
1409 _('the argument to named must be a string'))
1419 _('the argument to named must be a string'))
1410 kind, pattern, matcher = util.stringmatcher(ns)
1420 kind, pattern, matcher = util.stringmatcher(ns)
1411 namespaces = set()
1421 namespaces = set()
1412 if kind == 'literal':
1422 if kind == 'literal':
1413 if pattern not in repo.names:
1423 if pattern not in repo.names:
1414 raise error.RepoLookupError(_("namespace '%s' does not exist")
1424 raise error.RepoLookupError(_("namespace '%s' does not exist")
1415 % ns)
1425 % ns)
1416 namespaces.add(repo.names[pattern])
1426 namespaces.add(repo.names[pattern])
1417 else:
1427 else:
1418 for name, ns in repo.names.iteritems():
1428 for name, ns in repo.names.iteritems():
1419 if matcher(name):
1429 if matcher(name):
1420 namespaces.add(ns)
1430 namespaces.add(ns)
1421 if not namespaces:
1431 if not namespaces:
1422 raise error.RepoLookupError(_("no namespace exists"
1432 raise error.RepoLookupError(_("no namespace exists"
1423 " that match '%s'") % pattern)
1433 " that match '%s'") % pattern)
1424
1434
1425 names = set()
1435 names = set()
1426 for ns in namespaces:
1436 for ns in namespaces:
1427 for name in ns.listnames(repo):
1437 for name in ns.listnames(repo):
1428 if name not in ns.deprecated:
1438 if name not in ns.deprecated:
1429 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1439 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1430
1440
1431 names -= set([node.nullrev])
1441 names -= set([node.nullrev])
1432 return subset & names
1442 return subset & names
1433
1443
1434 @predicate('id(string)', safe=True)
1444 @predicate('id(string)', safe=True)
1435 def node_(repo, subset, x):
1445 def node_(repo, subset, x):
1436 """Revision non-ambiguously specified by the given hex string prefix.
1446 """Revision non-ambiguously specified by the given hex string prefix.
1437 """
1447 """
1438 # i18n: "id" is a keyword
1448 # i18n: "id" is a keyword
1439 l = getargs(x, 1, 1, _("id requires one argument"))
1449 l = getargs(x, 1, 1, _("id requires one argument"))
1440 # i18n: "id" is a keyword
1450 # i18n: "id" is a keyword
1441 n = getstring(l[0], _("id requires a string"))
1451 n = getstring(l[0], _("id requires a string"))
1442 if len(n) == 40:
1452 if len(n) == 40:
1443 try:
1453 try:
1444 rn = repo.changelog.rev(node.bin(n))
1454 rn = repo.changelog.rev(node.bin(n))
1445 except (LookupError, TypeError):
1455 except (LookupError, TypeError):
1446 rn = None
1456 rn = None
1447 else:
1457 else:
1448 rn = None
1458 rn = None
1449 pm = repo.changelog._partialmatch(n)
1459 pm = repo.changelog._partialmatch(n)
1450 if pm is not None:
1460 if pm is not None:
1451 rn = repo.changelog.rev(pm)
1461 rn = repo.changelog.rev(pm)
1452
1462
1453 if rn is None:
1463 if rn is None:
1454 return baseset()
1464 return baseset()
1455 result = baseset([rn])
1465 result = baseset([rn])
1456 return result & subset
1466 return result & subset
1457
1467
1458 @predicate('obsolete()', safe=True)
1468 @predicate('obsolete()', safe=True)
1459 def obsolete(repo, subset, x):
1469 def obsolete(repo, subset, x):
1460 """Mutable changeset with a newer version."""
1470 """Mutable changeset with a newer version."""
1461 # i18n: "obsolete" is a keyword
1471 # i18n: "obsolete" is a keyword
1462 getargs(x, 0, 0, _("obsolete takes no arguments"))
1472 getargs(x, 0, 0, _("obsolete takes no arguments"))
1463 obsoletes = obsmod.getrevs(repo, 'obsolete')
1473 obsoletes = obsmod.getrevs(repo, 'obsolete')
1464 return subset & obsoletes
1474 return subset & obsoletes
1465
1475
1466 @predicate('only(set, [set])', safe=True)
1476 @predicate('only(set, [set])', safe=True)
1467 def only(repo, subset, x):
1477 def only(repo, subset, x):
1468 """Changesets that are ancestors of the first set that are not ancestors
1478 """Changesets that are ancestors of the first set that are not ancestors
1469 of any other head in the repo. If a second set is specified, the result
1479 of any other head in the repo. If a second set is specified, the result
1470 is ancestors of the first set that are not ancestors of the second set
1480 is ancestors of the first set that are not ancestors of the second set
1471 (i.e. ::<set1> - ::<set2>).
1481 (i.e. ::<set1> - ::<set2>).
1472 """
1482 """
1473 cl = repo.changelog
1483 cl = repo.changelog
1474 # i18n: "only" is a keyword
1484 # i18n: "only" is a keyword
1475 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1485 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1476 include = getset(repo, fullreposet(repo), args[0])
1486 include = getset(repo, fullreposet(repo), args[0])
1477 if len(args) == 1:
1487 if len(args) == 1:
1478 if not include:
1488 if not include:
1479 return baseset()
1489 return baseset()
1480
1490
1481 descendants = set(_revdescendants(repo, include, False))
1491 descendants = set(_revdescendants(repo, include, False))
1482 exclude = [rev for rev in cl.headrevs()
1492 exclude = [rev for rev in cl.headrevs()
1483 if not rev in descendants and not rev in include]
1493 if not rev in descendants and not rev in include]
1484 else:
1494 else:
1485 exclude = getset(repo, fullreposet(repo), args[1])
1495 exclude = getset(repo, fullreposet(repo), args[1])
1486
1496
1487 results = set(cl.findmissingrevs(common=exclude, heads=include))
1497 results = set(cl.findmissingrevs(common=exclude, heads=include))
1488 # XXX we should turn this into a baseset instead of a set, smartset may do
1498 # XXX we should turn this into a baseset instead of a set, smartset may do
1489 # some optimizations from the fact this is a baseset.
1499 # some optimizations from the fact this is a baseset.
1490 return subset & results
1500 return subset & results
1491
1501
1492 @predicate('origin([set])', safe=True)
1502 @predicate('origin([set])', safe=True)
1493 def origin(repo, subset, x):
1503 def origin(repo, subset, x):
1494 """
1504 """
1495 Changesets that were specified as a source for the grafts, transplants or
1505 Changesets that were specified as a source for the grafts, transplants or
1496 rebases that created the given revisions. Omitting the optional set is the
1506 rebases that created the given revisions. Omitting the optional set is the
1497 same as passing all(). If a changeset created by these operations is itself
1507 same as passing all(). If a changeset created by these operations is itself
1498 specified as a source for one of these operations, only the source changeset
1508 specified as a source for one of these operations, only the source changeset
1499 for the first operation is selected.
1509 for the first operation is selected.
1500 """
1510 """
1501 if x is not None:
1511 if x is not None:
1502 dests = getset(repo, fullreposet(repo), x)
1512 dests = getset(repo, fullreposet(repo), x)
1503 else:
1513 else:
1504 dests = fullreposet(repo)
1514 dests = fullreposet(repo)
1505
1515
1506 def _firstsrc(rev):
1516 def _firstsrc(rev):
1507 src = _getrevsource(repo, rev)
1517 src = _getrevsource(repo, rev)
1508 if src is None:
1518 if src is None:
1509 return None
1519 return None
1510
1520
1511 while True:
1521 while True:
1512 prev = _getrevsource(repo, src)
1522 prev = _getrevsource(repo, src)
1513
1523
1514 if prev is None:
1524 if prev is None:
1515 return src
1525 return src
1516 src = prev
1526 src = prev
1517
1527
1518 o = set([_firstsrc(r) for r in dests])
1528 o = set([_firstsrc(r) for r in dests])
1519 o -= set([None])
1529 o -= set([None])
1520 # XXX we should turn this into a baseset instead of a set, smartset may do
1530 # XXX we should turn this into a baseset instead of a set, smartset may do
1521 # some optimizations from the fact this is a baseset.
1531 # some optimizations from the fact this is a baseset.
1522 return subset & o
1532 return subset & o
1523
1533
1524 @predicate('outgoing([path])', safe=True)
1534 @predicate('outgoing([path])', safe=True)
1525 def outgoing(repo, subset, x):
1535 def outgoing(repo, subset, x):
1526 """Changesets not found in the specified destination repository, or the
1536 """Changesets not found in the specified destination repository, or the
1527 default push location.
1537 default push location.
1528 """
1538 """
1529 # Avoid cycles.
1539 # Avoid cycles.
1530 from . import (
1540 from . import (
1531 discovery,
1541 discovery,
1532 hg,
1542 hg,
1533 )
1543 )
1534 # i18n: "outgoing" is a keyword
1544 # i18n: "outgoing" is a keyword
1535 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1545 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1536 # i18n: "outgoing" is a keyword
1546 # i18n: "outgoing" is a keyword
1537 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1547 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1538 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1548 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1539 dest, branches = hg.parseurl(dest)
1549 dest, branches = hg.parseurl(dest)
1540 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1550 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1541 if revs:
1551 if revs:
1542 revs = [repo.lookup(rev) for rev in revs]
1552 revs = [repo.lookup(rev) for rev in revs]
1543 other = hg.peer(repo, {}, dest)
1553 other = hg.peer(repo, {}, dest)
1544 repo.ui.pushbuffer()
1554 repo.ui.pushbuffer()
1545 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1555 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1546 repo.ui.popbuffer()
1556 repo.ui.popbuffer()
1547 cl = repo.changelog
1557 cl = repo.changelog
1548 o = set([cl.rev(r) for r in outgoing.missing])
1558 o = set([cl.rev(r) for r in outgoing.missing])
1549 return subset & o
1559 return subset & o
1550
1560
1551 @predicate('p1([set])', safe=True)
1561 @predicate('p1([set])', safe=True)
1552 def p1(repo, subset, x):
1562 def p1(repo, subset, x):
1553 """First parent of changesets in set, or the working directory.
1563 """First parent of changesets in set, or the working directory.
1554 """
1564 """
1555 if x is None:
1565 if x is None:
1556 p = repo[x].p1().rev()
1566 p = repo[x].p1().rev()
1557 if p >= 0:
1567 if p >= 0:
1558 return subset & baseset([p])
1568 return subset & baseset([p])
1559 return baseset()
1569 return baseset()
1560
1570
1561 ps = set()
1571 ps = set()
1562 cl = repo.changelog
1572 cl = repo.changelog
1563 for r in getset(repo, fullreposet(repo), x):
1573 for r in getset(repo, fullreposet(repo), x):
1564 ps.add(cl.parentrevs(r)[0])
1574 ps.add(cl.parentrevs(r)[0])
1565 ps -= set([node.nullrev])
1575 ps -= set([node.nullrev])
1566 # XXX we should turn this into a baseset instead of a set, smartset may do
1576 # XXX we should turn this into a baseset instead of a set, smartset may do
1567 # some optimizations from the fact this is a baseset.
1577 # some optimizations from the fact this is a baseset.
1568 return subset & ps
1578 return subset & ps
1569
1579
1570 @predicate('p2([set])', safe=True)
1580 @predicate('p2([set])', safe=True)
1571 def p2(repo, subset, x):
1581 def p2(repo, subset, x):
1572 """Second parent of changesets in set, or the working directory.
1582 """Second parent of changesets in set, or the working directory.
1573 """
1583 """
1574 if x is None:
1584 if x is None:
1575 ps = repo[x].parents()
1585 ps = repo[x].parents()
1576 try:
1586 try:
1577 p = ps[1].rev()
1587 p = ps[1].rev()
1578 if p >= 0:
1588 if p >= 0:
1579 return subset & baseset([p])
1589 return subset & baseset([p])
1580 return baseset()
1590 return baseset()
1581 except IndexError:
1591 except IndexError:
1582 return baseset()
1592 return baseset()
1583
1593
1584 ps = set()
1594 ps = set()
1585 cl = repo.changelog
1595 cl = repo.changelog
1586 for r in getset(repo, fullreposet(repo), x):
1596 for r in getset(repo, fullreposet(repo), x):
1587 ps.add(cl.parentrevs(r)[1])
1597 ps.add(cl.parentrevs(r)[1])
1588 ps -= set([node.nullrev])
1598 ps -= set([node.nullrev])
1589 # XXX we should turn this into a baseset instead of a set, smartset may do
1599 # XXX we should turn this into a baseset instead of a set, smartset may do
1590 # some optimizations from the fact this is a baseset.
1600 # some optimizations from the fact this is a baseset.
1591 return subset & ps
1601 return subset & ps
1592
1602
1593 def parentpost(repo, subset, x, order):
1603 def parentpost(repo, subset, x, order):
1594 return p1(repo, subset, x)
1604 return p1(repo, subset, x)
1595
1605
1596 @predicate('parents([set])', safe=True)
1606 @predicate('parents([set])', safe=True)
1597 def parents(repo, subset, x):
1607 def parents(repo, subset, x):
1598 """
1608 """
1599 The set of all parents for all changesets in set, or the working directory.
1609 The set of all parents for all changesets in set, or the working directory.
1600 """
1610 """
1601 if x is None:
1611 if x is None:
1602 ps = set(p.rev() for p in repo[x].parents())
1612 ps = set(p.rev() for p in repo[x].parents())
1603 else:
1613 else:
1604 ps = set()
1614 ps = set()
1605 cl = repo.changelog
1615 cl = repo.changelog
1606 up = ps.update
1616 up = ps.update
1607 parentrevs = cl.parentrevs
1617 parentrevs = cl.parentrevs
1608 for r in getset(repo, fullreposet(repo), x):
1618 for r in getset(repo, fullreposet(repo), x):
1609 if r == node.wdirrev:
1619 if r == node.wdirrev:
1610 up(p.rev() for p in repo[r].parents())
1620 up(p.rev() for p in repo[r].parents())
1611 else:
1621 else:
1612 up(parentrevs(r))
1622 up(parentrevs(r))
1613 ps -= set([node.nullrev])
1623 ps -= set([node.nullrev])
1614 return subset & ps
1624 return subset & ps
1615
1625
1616 def _phase(repo, subset, target):
1626 def _phase(repo, subset, target):
1617 """helper to select all rev in phase <target>"""
1627 """helper to select all rev in phase <target>"""
1618 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1628 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1619 if repo._phasecache._phasesets:
1629 if repo._phasecache._phasesets:
1620 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1630 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1621 s = baseset(s)
1631 s = baseset(s)
1622 s.sort() # set are non ordered, so we enforce ascending
1632 s.sort() # set are non ordered, so we enforce ascending
1623 return subset & s
1633 return subset & s
1624 else:
1634 else:
1625 phase = repo._phasecache.phase
1635 phase = repo._phasecache.phase
1626 condition = lambda r: phase(repo, r) == target
1636 condition = lambda r: phase(repo, r) == target
1627 return subset.filter(condition, condrepr=('<phase %r>', target),
1637 return subset.filter(condition, condrepr=('<phase %r>', target),
1628 cache=False)
1638 cache=False)
1629
1639
1630 @predicate('draft()', safe=True)
1640 @predicate('draft()', safe=True)
1631 def draft(repo, subset, x):
1641 def draft(repo, subset, x):
1632 """Changeset in draft phase."""
1642 """Changeset in draft phase."""
1633 # i18n: "draft" is a keyword
1643 # i18n: "draft" is a keyword
1634 getargs(x, 0, 0, _("draft takes no arguments"))
1644 getargs(x, 0, 0, _("draft takes no arguments"))
1635 target = phases.draft
1645 target = phases.draft
1636 return _phase(repo, subset, target)
1646 return _phase(repo, subset, target)
1637
1647
1638 @predicate('secret()', safe=True)
1648 @predicate('secret()', safe=True)
1639 def secret(repo, subset, x):
1649 def secret(repo, subset, x):
1640 """Changeset in secret phase."""
1650 """Changeset in secret phase."""
1641 # i18n: "secret" is a keyword
1651 # i18n: "secret" is a keyword
1642 getargs(x, 0, 0, _("secret takes no arguments"))
1652 getargs(x, 0, 0, _("secret takes no arguments"))
1643 target = phases.secret
1653 target = phases.secret
1644 return _phase(repo, subset, target)
1654 return _phase(repo, subset, target)
1645
1655
1646 def parentspec(repo, subset, x, n, order):
1656 def parentspec(repo, subset, x, n, order):
1647 """``set^0``
1657 """``set^0``
1648 The set.
1658 The set.
1649 ``set^1`` (or ``set^``), ``set^2``
1659 ``set^1`` (or ``set^``), ``set^2``
1650 First or second parent, respectively, of all changesets in set.
1660 First or second parent, respectively, of all changesets in set.
1651 """
1661 """
1652 try:
1662 try:
1653 n = int(n[1])
1663 n = int(n[1])
1654 if n not in (0, 1, 2):
1664 if n not in (0, 1, 2):
1655 raise ValueError
1665 raise ValueError
1656 except (TypeError, ValueError):
1666 except (TypeError, ValueError):
1657 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1667 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1658 ps = set()
1668 ps = set()
1659 cl = repo.changelog
1669 cl = repo.changelog
1660 for r in getset(repo, fullreposet(repo), x):
1670 for r in getset(repo, fullreposet(repo), x):
1661 if n == 0:
1671 if n == 0:
1662 ps.add(r)
1672 ps.add(r)
1663 elif n == 1:
1673 elif n == 1:
1664 ps.add(cl.parentrevs(r)[0])
1674 ps.add(cl.parentrevs(r)[0])
1665 elif n == 2:
1675 elif n == 2:
1666 parents = cl.parentrevs(r)
1676 parents = cl.parentrevs(r)
1667 if parents[1] != node.nullrev:
1677 if parents[1] != node.nullrev:
1668 ps.add(parents[1])
1678 ps.add(parents[1])
1669 return subset & ps
1679 return subset & ps
1670
1680
1671 @predicate('present(set)', safe=True)
1681 @predicate('present(set)', safe=True)
1672 def present(repo, subset, x):
1682 def present(repo, subset, x):
1673 """An empty set, if any revision in set isn't found; otherwise,
1683 """An empty set, if any revision in set isn't found; otherwise,
1674 all revisions in set.
1684 all revisions in set.
1675
1685
1676 If any of specified revisions is not present in the local repository,
1686 If any of specified revisions is not present in the local repository,
1677 the query is normally aborted. But this predicate allows the query
1687 the query is normally aborted. But this predicate allows the query
1678 to continue even in such cases.
1688 to continue even in such cases.
1679 """
1689 """
1680 try:
1690 try:
1681 return getset(repo, subset, x)
1691 return getset(repo, subset, x)
1682 except error.RepoLookupError:
1692 except error.RepoLookupError:
1683 return baseset()
1693 return baseset()
1684
1694
1685 # for internal use
1695 # for internal use
1686 @predicate('_notpublic', safe=True)
1696 @predicate('_notpublic', safe=True)
1687 def _notpublic(repo, subset, x):
1697 def _notpublic(repo, subset, x):
1688 getargs(x, 0, 0, "_notpublic takes no arguments")
1698 getargs(x, 0, 0, "_notpublic takes no arguments")
1689 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1699 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1690 if repo._phasecache._phasesets:
1700 if repo._phasecache._phasesets:
1691 s = set()
1701 s = set()
1692 for u in repo._phasecache._phasesets[1:]:
1702 for u in repo._phasecache._phasesets[1:]:
1693 s.update(u)
1703 s.update(u)
1694 s = baseset(s - repo.changelog.filteredrevs)
1704 s = baseset(s - repo.changelog.filteredrevs)
1695 s.sort()
1705 s.sort()
1696 return subset & s
1706 return subset & s
1697 else:
1707 else:
1698 phase = repo._phasecache.phase
1708 phase = repo._phasecache.phase
1699 target = phases.public
1709 target = phases.public
1700 condition = lambda r: phase(repo, r) != target
1710 condition = lambda r: phase(repo, r) != target
1701 return subset.filter(condition, condrepr=('<phase %r>', target),
1711 return subset.filter(condition, condrepr=('<phase %r>', target),
1702 cache=False)
1712 cache=False)
1703
1713
1704 @predicate('public()', safe=True)
1714 @predicate('public()', safe=True)
1705 def public(repo, subset, x):
1715 def public(repo, subset, x):
1706 """Changeset in public phase."""
1716 """Changeset in public phase."""
1707 # i18n: "public" is a keyword
1717 # i18n: "public" is a keyword
1708 getargs(x, 0, 0, _("public takes no arguments"))
1718 getargs(x, 0, 0, _("public takes no arguments"))
1709 phase = repo._phasecache.phase
1719 phase = repo._phasecache.phase
1710 target = phases.public
1720 target = phases.public
1711 condition = lambda r: phase(repo, r) == target
1721 condition = lambda r: phase(repo, r) == target
1712 return subset.filter(condition, condrepr=('<phase %r>', target),
1722 return subset.filter(condition, condrepr=('<phase %r>', target),
1713 cache=False)
1723 cache=False)
1714
1724
1715 @predicate('remote([id [,path]])', safe=True)
1725 @predicate('remote([id [,path]])', safe=True)
1716 def remote(repo, subset, x):
1726 def remote(repo, subset, x):
1717 """Local revision that corresponds to the given identifier in a
1727 """Local revision that corresponds to the given identifier in a
1718 remote repository, if present. Here, the '.' identifier is a
1728 remote repository, if present. Here, the '.' identifier is a
1719 synonym for the current local branch.
1729 synonym for the current local branch.
1720 """
1730 """
1721
1731
1722 from . import hg # avoid start-up nasties
1732 from . import hg # avoid start-up nasties
1723 # i18n: "remote" is a keyword
1733 # i18n: "remote" is a keyword
1724 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1734 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1725
1735
1726 q = '.'
1736 q = '.'
1727 if len(l) > 0:
1737 if len(l) > 0:
1728 # i18n: "remote" is a keyword
1738 # i18n: "remote" is a keyword
1729 q = getstring(l[0], _("remote requires a string id"))
1739 q = getstring(l[0], _("remote requires a string id"))
1730 if q == '.':
1740 if q == '.':
1731 q = repo['.'].branch()
1741 q = repo['.'].branch()
1732
1742
1733 dest = ''
1743 dest = ''
1734 if len(l) > 1:
1744 if len(l) > 1:
1735 # i18n: "remote" is a keyword
1745 # i18n: "remote" is a keyword
1736 dest = getstring(l[1], _("remote requires a repository path"))
1746 dest = getstring(l[1], _("remote requires a repository path"))
1737 dest = repo.ui.expandpath(dest or 'default')
1747 dest = repo.ui.expandpath(dest or 'default')
1738 dest, branches = hg.parseurl(dest)
1748 dest, branches = hg.parseurl(dest)
1739 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1749 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1740 if revs:
1750 if revs:
1741 revs = [repo.lookup(rev) for rev in revs]
1751 revs = [repo.lookup(rev) for rev in revs]
1742 other = hg.peer(repo, {}, dest)
1752 other = hg.peer(repo, {}, dest)
1743 n = other.lookup(q)
1753 n = other.lookup(q)
1744 if n in repo:
1754 if n in repo:
1745 r = repo[n].rev()
1755 r = repo[n].rev()
1746 if r in subset:
1756 if r in subset:
1747 return baseset([r])
1757 return baseset([r])
1748 return baseset()
1758 return baseset()
1749
1759
1750 @predicate('removes(pattern)', safe=True)
1760 @predicate('removes(pattern)', safe=True)
1751 def removes(repo, subset, x):
1761 def removes(repo, subset, x):
1752 """Changesets which remove files matching pattern.
1762 """Changesets which remove files matching pattern.
1753
1763
1754 The pattern without explicit kind like ``glob:`` is expected to be
1764 The pattern without explicit kind like ``glob:`` is expected to be
1755 relative to the current directory and match against a file or a
1765 relative to the current directory and match against a file or a
1756 directory.
1766 directory.
1757 """
1767 """
1758 # i18n: "removes" is a keyword
1768 # i18n: "removes" is a keyword
1759 pat = getstring(x, _("removes requires a pattern"))
1769 pat = getstring(x, _("removes requires a pattern"))
1760 return checkstatus(repo, subset, pat, 2)
1770 return checkstatus(repo, subset, pat, 2)
1761
1771
1762 @predicate('rev(number)', safe=True)
1772 @predicate('rev(number)', safe=True)
1763 def rev(repo, subset, x):
1773 def rev(repo, subset, x):
1764 """Revision with the given numeric identifier.
1774 """Revision with the given numeric identifier.
1765 """
1775 """
1766 # i18n: "rev" is a keyword
1776 # i18n: "rev" is a keyword
1767 l = getargs(x, 1, 1, _("rev requires one argument"))
1777 l = getargs(x, 1, 1, _("rev requires one argument"))
1768 try:
1778 try:
1769 # i18n: "rev" is a keyword
1779 # i18n: "rev" is a keyword
1770 l = int(getstring(l[0], _("rev requires a number")))
1780 l = int(getstring(l[0], _("rev requires a number")))
1771 except (TypeError, ValueError):
1781 except (TypeError, ValueError):
1772 # i18n: "rev" is a keyword
1782 # i18n: "rev" is a keyword
1773 raise error.ParseError(_("rev expects a number"))
1783 raise error.ParseError(_("rev expects a number"))
1774 if l not in repo.changelog and l != node.nullrev:
1784 if l not in repo.changelog and l != node.nullrev:
1775 return baseset()
1785 return baseset()
1776 return subset & baseset([l])
1786 return subset & baseset([l])
1777
1787
1778 @predicate('matching(revision [, field])', safe=True)
1788 @predicate('matching(revision [, field])', safe=True)
1779 def matching(repo, subset, x):
1789 def matching(repo, subset, x):
1780 """Changesets in which a given set of fields match the set of fields in the
1790 """Changesets in which a given set of fields match the set of fields in the
1781 selected revision or set.
1791 selected revision or set.
1782
1792
1783 To match more than one field pass the list of fields to match separated
1793 To match more than one field pass the list of fields to match separated
1784 by spaces (e.g. ``author description``).
1794 by spaces (e.g. ``author description``).
1785
1795
1786 Valid fields are most regular revision fields and some special fields.
1796 Valid fields are most regular revision fields and some special fields.
1787
1797
1788 Regular revision fields are ``description``, ``author``, ``branch``,
1798 Regular revision fields are ``description``, ``author``, ``branch``,
1789 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1799 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1790 and ``diff``.
1800 and ``diff``.
1791 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1801 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1792 contents of the revision. Two revisions matching their ``diff`` will
1802 contents of the revision. Two revisions matching their ``diff`` will
1793 also match their ``files``.
1803 also match their ``files``.
1794
1804
1795 Special fields are ``summary`` and ``metadata``:
1805 Special fields are ``summary`` and ``metadata``:
1796 ``summary`` matches the first line of the description.
1806 ``summary`` matches the first line of the description.
1797 ``metadata`` is equivalent to matching ``description user date``
1807 ``metadata`` is equivalent to matching ``description user date``
1798 (i.e. it matches the main metadata fields).
1808 (i.e. it matches the main metadata fields).
1799
1809
1800 ``metadata`` is the default field which is used when no fields are
1810 ``metadata`` is the default field which is used when no fields are
1801 specified. You can match more than one field at a time.
1811 specified. You can match more than one field at a time.
1802 """
1812 """
1803 # i18n: "matching" is a keyword
1813 # i18n: "matching" is a keyword
1804 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1814 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1805
1815
1806 revs = getset(repo, fullreposet(repo), l[0])
1816 revs = getset(repo, fullreposet(repo), l[0])
1807
1817
1808 fieldlist = ['metadata']
1818 fieldlist = ['metadata']
1809 if len(l) > 1:
1819 if len(l) > 1:
1810 fieldlist = getstring(l[1],
1820 fieldlist = getstring(l[1],
1811 # i18n: "matching" is a keyword
1821 # i18n: "matching" is a keyword
1812 _("matching requires a string "
1822 _("matching requires a string "
1813 "as its second argument")).split()
1823 "as its second argument")).split()
1814
1824
1815 # Make sure that there are no repeated fields,
1825 # Make sure that there are no repeated fields,
1816 # expand the 'special' 'metadata' field type
1826 # expand the 'special' 'metadata' field type
1817 # and check the 'files' whenever we check the 'diff'
1827 # and check the 'files' whenever we check the 'diff'
1818 fields = []
1828 fields = []
1819 for field in fieldlist:
1829 for field in fieldlist:
1820 if field == 'metadata':
1830 if field == 'metadata':
1821 fields += ['user', 'description', 'date']
1831 fields += ['user', 'description', 'date']
1822 elif field == 'diff':
1832 elif field == 'diff':
1823 # a revision matching the diff must also match the files
1833 # a revision matching the diff must also match the files
1824 # since matching the diff is very costly, make sure to
1834 # since matching the diff is very costly, make sure to
1825 # also match the files first
1835 # also match the files first
1826 fields += ['files', 'diff']
1836 fields += ['files', 'diff']
1827 else:
1837 else:
1828 if field == 'author':
1838 if field == 'author':
1829 field = 'user'
1839 field = 'user'
1830 fields.append(field)
1840 fields.append(field)
1831 fields = set(fields)
1841 fields = set(fields)
1832 if 'summary' in fields and 'description' in fields:
1842 if 'summary' in fields and 'description' in fields:
1833 # If a revision matches its description it also matches its summary
1843 # If a revision matches its description it also matches its summary
1834 fields.discard('summary')
1844 fields.discard('summary')
1835
1845
1836 # We may want to match more than one field
1846 # We may want to match more than one field
1837 # Not all fields take the same amount of time to be matched
1847 # Not all fields take the same amount of time to be matched
1838 # Sort the selected fields in order of increasing matching cost
1848 # Sort the selected fields in order of increasing matching cost
1839 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1849 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1840 'files', 'description', 'substate', 'diff']
1850 'files', 'description', 'substate', 'diff']
1841 def fieldkeyfunc(f):
1851 def fieldkeyfunc(f):
1842 try:
1852 try:
1843 return fieldorder.index(f)
1853 return fieldorder.index(f)
1844 except ValueError:
1854 except ValueError:
1845 # assume an unknown field is very costly
1855 # assume an unknown field is very costly
1846 return len(fieldorder)
1856 return len(fieldorder)
1847 fields = list(fields)
1857 fields = list(fields)
1848 fields.sort(key=fieldkeyfunc)
1858 fields.sort(key=fieldkeyfunc)
1849
1859
1850 # Each field will be matched with its own "getfield" function
1860 # Each field will be matched with its own "getfield" function
1851 # which will be added to the getfieldfuncs array of functions
1861 # which will be added to the getfieldfuncs array of functions
1852 getfieldfuncs = []
1862 getfieldfuncs = []
1853 _funcs = {
1863 _funcs = {
1854 'user': lambda r: repo[r].user(),
1864 'user': lambda r: repo[r].user(),
1855 'branch': lambda r: repo[r].branch(),
1865 'branch': lambda r: repo[r].branch(),
1856 'date': lambda r: repo[r].date(),
1866 'date': lambda r: repo[r].date(),
1857 'description': lambda r: repo[r].description(),
1867 'description': lambda r: repo[r].description(),
1858 'files': lambda r: repo[r].files(),
1868 'files': lambda r: repo[r].files(),
1859 'parents': lambda r: repo[r].parents(),
1869 'parents': lambda r: repo[r].parents(),
1860 'phase': lambda r: repo[r].phase(),
1870 'phase': lambda r: repo[r].phase(),
1861 'substate': lambda r: repo[r].substate,
1871 'substate': lambda r: repo[r].substate,
1862 'summary': lambda r: repo[r].description().splitlines()[0],
1872 'summary': lambda r: repo[r].description().splitlines()[0],
1863 'diff': lambda r: list(repo[r].diff(git=True),)
1873 'diff': lambda r: list(repo[r].diff(git=True),)
1864 }
1874 }
1865 for info in fields:
1875 for info in fields:
1866 getfield = _funcs.get(info, None)
1876 getfield = _funcs.get(info, None)
1867 if getfield is None:
1877 if getfield is None:
1868 raise error.ParseError(
1878 raise error.ParseError(
1869 # i18n: "matching" is a keyword
1879 # i18n: "matching" is a keyword
1870 _("unexpected field name passed to matching: %s") % info)
1880 _("unexpected field name passed to matching: %s") % info)
1871 getfieldfuncs.append(getfield)
1881 getfieldfuncs.append(getfield)
1872 # convert the getfield array of functions into a "getinfo" function
1882 # convert the getfield array of functions into a "getinfo" function
1873 # which returns an array of field values (or a single value if there
1883 # which returns an array of field values (or a single value if there
1874 # is only one field to match)
1884 # is only one field to match)
1875 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1885 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1876
1886
1877 def matches(x):
1887 def matches(x):
1878 for rev in revs:
1888 for rev in revs:
1879 target = getinfo(rev)
1889 target = getinfo(rev)
1880 match = True
1890 match = True
1881 for n, f in enumerate(getfieldfuncs):
1891 for n, f in enumerate(getfieldfuncs):
1882 if target[n] != f(x):
1892 if target[n] != f(x):
1883 match = False
1893 match = False
1884 if match:
1894 if match:
1885 return True
1895 return True
1886 return False
1896 return False
1887
1897
1888 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1898 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1889
1899
1890 @predicate('reverse(set)', safe=True, takeorder=True)
1900 @predicate('reverse(set)', safe=True, takeorder=True)
1891 def reverse(repo, subset, x, order):
1901 def reverse(repo, subset, x, order):
1892 """Reverse order of set.
1902 """Reverse order of set.
1893 """
1903 """
1894 l = getset(repo, subset, x)
1904 l = getset(repo, subset, x)
1895 if order == defineorder:
1905 if order == defineorder:
1896 l.reverse()
1906 l.reverse()
1897 return l
1907 return l
1898
1908
1899 @predicate('roots(set)', safe=True)
1909 @predicate('roots(set)', safe=True)
1900 def roots(repo, subset, x):
1910 def roots(repo, subset, x):
1901 """Changesets in set with no parent changeset in set.
1911 """Changesets in set with no parent changeset in set.
1902 """
1912 """
1903 s = getset(repo, fullreposet(repo), x)
1913 s = getset(repo, fullreposet(repo), x)
1904 parents = repo.changelog.parentrevs
1914 parents = repo.changelog.parentrevs
1905 def filter(r):
1915 def filter(r):
1906 for p in parents(r):
1916 for p in parents(r):
1907 if 0 <= p and p in s:
1917 if 0 <= p and p in s:
1908 return False
1918 return False
1909 return True
1919 return True
1910 return subset & s.filter(filter, condrepr='<roots>')
1920 return subset & s.filter(filter, condrepr='<roots>')
1911
1921
1912 _sortkeyfuncs = {
1922 _sortkeyfuncs = {
1913 'rev': lambda c: c.rev(),
1923 'rev': lambda c: c.rev(),
1914 'branch': lambda c: c.branch(),
1924 'branch': lambda c: c.branch(),
1915 'desc': lambda c: c.description(),
1925 'desc': lambda c: c.description(),
1916 'user': lambda c: c.user(),
1926 'user': lambda c: c.user(),
1917 'author': lambda c: c.user(),
1927 'author': lambda c: c.user(),
1918 'date': lambda c: c.date()[0],
1928 'date': lambda c: c.date()[0],
1919 }
1929 }
1920
1930
1921 def _getsortargs(x):
1931 def _getsortargs(x):
1922 """Parse sort options into (set, [(key, reverse)], opts)"""
1932 """Parse sort options into (set, [(key, reverse)], opts)"""
1923 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1933 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1924 if 'set' not in args:
1934 if 'set' not in args:
1925 # i18n: "sort" is a keyword
1935 # i18n: "sort" is a keyword
1926 raise error.ParseError(_('sort requires one or two arguments'))
1936 raise error.ParseError(_('sort requires one or two arguments'))
1927 keys = "rev"
1937 keys = "rev"
1928 if 'keys' in args:
1938 if 'keys' in args:
1929 # i18n: "sort" is a keyword
1939 # i18n: "sort" is a keyword
1930 keys = getstring(args['keys'], _("sort spec must be a string"))
1940 keys = getstring(args['keys'], _("sort spec must be a string"))
1931
1941
1932 keyflags = []
1942 keyflags = []
1933 for k in keys.split():
1943 for k in keys.split():
1934 fk = k
1944 fk = k
1935 reverse = (k[0] == '-')
1945 reverse = (k[0] == '-')
1936 if reverse:
1946 if reverse:
1937 k = k[1:]
1947 k = k[1:]
1938 if k not in _sortkeyfuncs and k != 'topo':
1948 if k not in _sortkeyfuncs and k != 'topo':
1939 raise error.ParseError(_("unknown sort key %r") % fk)
1949 raise error.ParseError(_("unknown sort key %r") % fk)
1940 keyflags.append((k, reverse))
1950 keyflags.append((k, reverse))
1941
1951
1942 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1952 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1943 # i18n: "topo" is a keyword
1953 # i18n: "topo" is a keyword
1944 raise error.ParseError(_('topo sort order cannot be combined '
1954 raise error.ParseError(_('topo sort order cannot be combined '
1945 'with other sort keys'))
1955 'with other sort keys'))
1946
1956
1947 opts = {}
1957 opts = {}
1948 if 'topo.firstbranch' in args:
1958 if 'topo.firstbranch' in args:
1949 if any(k == 'topo' for k, reverse in keyflags):
1959 if any(k == 'topo' for k, reverse in keyflags):
1950 opts['topo.firstbranch'] = args['topo.firstbranch']
1960 opts['topo.firstbranch'] = args['topo.firstbranch']
1951 else:
1961 else:
1952 # i18n: "topo" and "topo.firstbranch" are keywords
1962 # i18n: "topo" and "topo.firstbranch" are keywords
1953 raise error.ParseError(_('topo.firstbranch can only be used '
1963 raise error.ParseError(_('topo.firstbranch can only be used '
1954 'when using the topo sort key'))
1964 'when using the topo sort key'))
1955
1965
1956 return args['set'], keyflags, opts
1966 return args['set'], keyflags, opts
1957
1967
1958 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1968 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1959 def sort(repo, subset, x, order):
1969 def sort(repo, subset, x, order):
1960 """Sort set by keys. The default sort order is ascending, specify a key
1970 """Sort set by keys. The default sort order is ascending, specify a key
1961 as ``-key`` to sort in descending order.
1971 as ``-key`` to sort in descending order.
1962
1972
1963 The keys can be:
1973 The keys can be:
1964
1974
1965 - ``rev`` for the revision number,
1975 - ``rev`` for the revision number,
1966 - ``branch`` for the branch name,
1976 - ``branch`` for the branch name,
1967 - ``desc`` for the commit message (description),
1977 - ``desc`` for the commit message (description),
1968 - ``user`` for user name (``author`` can be used as an alias),
1978 - ``user`` for user name (``author`` can be used as an alias),
1969 - ``date`` for the commit date
1979 - ``date`` for the commit date
1970 - ``topo`` for a reverse topographical sort
1980 - ``topo`` for a reverse topographical sort
1971
1981
1972 The ``topo`` sort order cannot be combined with other sort keys. This sort
1982 The ``topo`` sort order cannot be combined with other sort keys. This sort
1973 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1983 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1974 specifies what topographical branches to prioritize in the sort.
1984 specifies what topographical branches to prioritize in the sort.
1975
1985
1976 """
1986 """
1977 s, keyflags, opts = _getsortargs(x)
1987 s, keyflags, opts = _getsortargs(x)
1978 revs = getset(repo, subset, s)
1988 revs = getset(repo, subset, s)
1979
1989
1980 if not keyflags or order != defineorder:
1990 if not keyflags or order != defineorder:
1981 return revs
1991 return revs
1982 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1992 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1983 revs.sort(reverse=keyflags[0][1])
1993 revs.sort(reverse=keyflags[0][1])
1984 return revs
1994 return revs
1985 elif keyflags[0][0] == "topo":
1995 elif keyflags[0][0] == "topo":
1986 firstbranch = ()
1996 firstbranch = ()
1987 if 'topo.firstbranch' in opts:
1997 if 'topo.firstbranch' in opts:
1988 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1998 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1989 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1999 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1990 istopo=True)
2000 istopo=True)
1991 if keyflags[0][1]:
2001 if keyflags[0][1]:
1992 revs.reverse()
2002 revs.reverse()
1993 return revs
2003 return revs
1994
2004
1995 # sort() is guaranteed to be stable
2005 # sort() is guaranteed to be stable
1996 ctxs = [repo[r] for r in revs]
2006 ctxs = [repo[r] for r in revs]
1997 for k, reverse in reversed(keyflags):
2007 for k, reverse in reversed(keyflags):
1998 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
2008 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1999 return baseset([c.rev() for c in ctxs])
2009 return baseset([c.rev() for c in ctxs])
2000
2010
2001 def _toposort(revs, parentsfunc, firstbranch=()):
2011 def _toposort(revs, parentsfunc, firstbranch=()):
2002 """Yield revisions from heads to roots one (topo) branch at a time.
2012 """Yield revisions from heads to roots one (topo) branch at a time.
2003
2013
2004 This function aims to be used by a graph generator that wishes to minimize
2014 This function aims to be used by a graph generator that wishes to minimize
2005 the number of parallel branches and their interleaving.
2015 the number of parallel branches and their interleaving.
2006
2016
2007 Example iteration order (numbers show the "true" order in a changelog):
2017 Example iteration order (numbers show the "true" order in a changelog):
2008
2018
2009 o 4
2019 o 4
2010 |
2020 |
2011 o 1
2021 o 1
2012 |
2022 |
2013 | o 3
2023 | o 3
2014 | |
2024 | |
2015 | o 2
2025 | o 2
2016 |/
2026 |/
2017 o 0
2027 o 0
2018
2028
2019 Note that the ancestors of merges are understood by the current
2029 Note that the ancestors of merges are understood by the current
2020 algorithm to be on the same branch. This means no reordering will
2030 algorithm to be on the same branch. This means no reordering will
2021 occur behind a merge.
2031 occur behind a merge.
2022 """
2032 """
2023
2033
2024 ### Quick summary of the algorithm
2034 ### Quick summary of the algorithm
2025 #
2035 #
2026 # This function is based around a "retention" principle. We keep revisions
2036 # This function is based around a "retention" principle. We keep revisions
2027 # in memory until we are ready to emit a whole branch that immediately
2037 # in memory until we are ready to emit a whole branch that immediately
2028 # "merges" into an existing one. This reduces the number of parallel
2038 # "merges" into an existing one. This reduces the number of parallel
2029 # branches with interleaved revisions.
2039 # branches with interleaved revisions.
2030 #
2040 #
2031 # During iteration revs are split into two groups:
2041 # During iteration revs are split into two groups:
2032 # A) revision already emitted
2042 # A) revision already emitted
2033 # B) revision in "retention". They are stored as different subgroups.
2043 # B) revision in "retention". They are stored as different subgroups.
2034 #
2044 #
2035 # for each REV, we do the following logic:
2045 # for each REV, we do the following logic:
2036 #
2046 #
2037 # 1) if REV is a parent of (A), we will emit it. If there is a
2047 # 1) if REV is a parent of (A), we will emit it. If there is a
2038 # retention group ((B) above) that is blocked on REV being
2048 # retention group ((B) above) that is blocked on REV being
2039 # available, we emit all the revisions out of that retention
2049 # available, we emit all the revisions out of that retention
2040 # group first.
2050 # group first.
2041 #
2051 #
2042 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
2052 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
2043 # available, if such subgroup exist, we add REV to it and the subgroup is
2053 # available, if such subgroup exist, we add REV to it and the subgroup is
2044 # now awaiting for REV.parents() to be available.
2054 # now awaiting for REV.parents() to be available.
2045 #
2055 #
2046 # 3) finally if no such group existed in (B), we create a new subgroup.
2056 # 3) finally if no such group existed in (B), we create a new subgroup.
2047 #
2057 #
2048 #
2058 #
2049 # To bootstrap the algorithm, we emit the tipmost revision (which
2059 # To bootstrap the algorithm, we emit the tipmost revision (which
2050 # puts it in group (A) from above).
2060 # puts it in group (A) from above).
2051
2061
2052 revs.sort(reverse=True)
2062 revs.sort(reverse=True)
2053
2063
2054 # Set of parents of revision that have been emitted. They can be considered
2064 # Set of parents of revision that have been emitted. They can be considered
2055 # unblocked as the graph generator is already aware of them so there is no
2065 # unblocked as the graph generator is already aware of them so there is no
2056 # need to delay the revisions that reference them.
2066 # need to delay the revisions that reference them.
2057 #
2067 #
2058 # If someone wants to prioritize a branch over the others, pre-filling this
2068 # If someone wants to prioritize a branch over the others, pre-filling this
2059 # set will force all other branches to wait until this branch is ready to be
2069 # set will force all other branches to wait until this branch is ready to be
2060 # emitted.
2070 # emitted.
2061 unblocked = set(firstbranch)
2071 unblocked = set(firstbranch)
2062
2072
2063 # list of groups waiting to be displayed, each group is defined by:
2073 # list of groups waiting to be displayed, each group is defined by:
2064 #
2074 #
2065 # (revs: lists of revs waiting to be displayed,
2075 # (revs: lists of revs waiting to be displayed,
2066 # blocked: set of that cannot be displayed before those in 'revs')
2076 # blocked: set of that cannot be displayed before those in 'revs')
2067 #
2077 #
2068 # The second value ('blocked') correspond to parents of any revision in the
2078 # The second value ('blocked') correspond to parents of any revision in the
2069 # group ('revs') that is not itself contained in the group. The main idea
2079 # group ('revs') that is not itself contained in the group. The main idea
2070 # of this algorithm is to delay as much as possible the emission of any
2080 # of this algorithm is to delay as much as possible the emission of any
2071 # revision. This means waiting for the moment we are about to display
2081 # revision. This means waiting for the moment we are about to display
2072 # these parents to display the revs in a group.
2082 # these parents to display the revs in a group.
2073 #
2083 #
2074 # This first implementation is smart until it encounters a merge: it will
2084 # This first implementation is smart until it encounters a merge: it will
2075 # emit revs as soon as any parent is about to be emitted and can grow an
2085 # emit revs as soon as any parent is about to be emitted and can grow an
2076 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2086 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2077 # retains new branches but gives up on any special ordering for ancestors
2087 # retains new branches but gives up on any special ordering for ancestors
2078 # of merges. The implementation can be improved to handle this better.
2088 # of merges. The implementation can be improved to handle this better.
2079 #
2089 #
2080 # The first subgroup is special. It corresponds to all the revision that
2090 # The first subgroup is special. It corresponds to all the revision that
2081 # were already emitted. The 'revs' lists is expected to be empty and the
2091 # were already emitted. The 'revs' lists is expected to be empty and the
2082 # 'blocked' set contains the parents revisions of already emitted revision.
2092 # 'blocked' set contains the parents revisions of already emitted revision.
2083 #
2093 #
2084 # You could pre-seed the <parents> set of groups[0] to a specific
2094 # You could pre-seed the <parents> set of groups[0] to a specific
2085 # changesets to select what the first emitted branch should be.
2095 # changesets to select what the first emitted branch should be.
2086 groups = [([], unblocked)]
2096 groups = [([], unblocked)]
2087 pendingheap = []
2097 pendingheap = []
2088 pendingset = set()
2098 pendingset = set()
2089
2099
2090 heapq.heapify(pendingheap)
2100 heapq.heapify(pendingheap)
2091 heappop = heapq.heappop
2101 heappop = heapq.heappop
2092 heappush = heapq.heappush
2102 heappush = heapq.heappush
2093 for currentrev in revs:
2103 for currentrev in revs:
2094 # Heap works with smallest element, we want highest so we invert
2104 # Heap works with smallest element, we want highest so we invert
2095 if currentrev not in pendingset:
2105 if currentrev not in pendingset:
2096 heappush(pendingheap, -currentrev)
2106 heappush(pendingheap, -currentrev)
2097 pendingset.add(currentrev)
2107 pendingset.add(currentrev)
2098 # iterates on pending rev until after the current rev have been
2108 # iterates on pending rev until after the current rev have been
2099 # processed.
2109 # processed.
2100 rev = None
2110 rev = None
2101 while rev != currentrev:
2111 while rev != currentrev:
2102 rev = -heappop(pendingheap)
2112 rev = -heappop(pendingheap)
2103 pendingset.remove(rev)
2113 pendingset.remove(rev)
2104
2114
2105 # Seek for a subgroup blocked, waiting for the current revision.
2115 # Seek for a subgroup blocked, waiting for the current revision.
2106 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2116 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2107
2117
2108 if matching:
2118 if matching:
2109 # The main idea is to gather together all sets that are blocked
2119 # The main idea is to gather together all sets that are blocked
2110 # on the same revision.
2120 # on the same revision.
2111 #
2121 #
2112 # Groups are merged when a common blocking ancestor is
2122 # Groups are merged when a common blocking ancestor is
2113 # observed. For example, given two groups:
2123 # observed. For example, given two groups:
2114 #
2124 #
2115 # revs [5, 4] waiting for 1
2125 # revs [5, 4] waiting for 1
2116 # revs [3, 2] waiting for 1
2126 # revs [3, 2] waiting for 1
2117 #
2127 #
2118 # These two groups will be merged when we process
2128 # These two groups will be merged when we process
2119 # 1. In theory, we could have merged the groups when
2129 # 1. In theory, we could have merged the groups when
2120 # we added 2 to the group it is now in (we could have
2130 # we added 2 to the group it is now in (we could have
2121 # noticed the groups were both blocked on 1 then), but
2131 # noticed the groups were both blocked on 1 then), but
2122 # the way it works now makes the algorithm simpler.
2132 # the way it works now makes the algorithm simpler.
2123 #
2133 #
2124 # We also always keep the oldest subgroup first. We can
2134 # We also always keep the oldest subgroup first. We can
2125 # probably improve the behavior by having the longest set
2135 # probably improve the behavior by having the longest set
2126 # first. That way, graph algorithms could minimise the length
2136 # first. That way, graph algorithms could minimise the length
2127 # of parallel lines their drawing. This is currently not done.
2137 # of parallel lines their drawing. This is currently not done.
2128 targetidx = matching.pop(0)
2138 targetidx = matching.pop(0)
2129 trevs, tparents = groups[targetidx]
2139 trevs, tparents = groups[targetidx]
2130 for i in matching:
2140 for i in matching:
2131 gr = groups[i]
2141 gr = groups[i]
2132 trevs.extend(gr[0])
2142 trevs.extend(gr[0])
2133 tparents |= gr[1]
2143 tparents |= gr[1]
2134 # delete all merged subgroups (except the one we kept)
2144 # delete all merged subgroups (except the one we kept)
2135 # (starting from the last subgroup for performance and
2145 # (starting from the last subgroup for performance and
2136 # sanity reasons)
2146 # sanity reasons)
2137 for i in reversed(matching):
2147 for i in reversed(matching):
2138 del groups[i]
2148 del groups[i]
2139 else:
2149 else:
2140 # This is a new head. We create a new subgroup for it.
2150 # This is a new head. We create a new subgroup for it.
2141 targetidx = len(groups)
2151 targetidx = len(groups)
2142 groups.append(([], set([rev])))
2152 groups.append(([], set([rev])))
2143
2153
2144 gr = groups[targetidx]
2154 gr = groups[targetidx]
2145
2155
2146 # We now add the current nodes to this subgroups. This is done
2156 # We now add the current nodes to this subgroups. This is done
2147 # after the subgroup merging because all elements from a subgroup
2157 # after the subgroup merging because all elements from a subgroup
2148 # that relied on this rev must precede it.
2158 # that relied on this rev must precede it.
2149 #
2159 #
2150 # we also update the <parents> set to include the parents of the
2160 # we also update the <parents> set to include the parents of the
2151 # new nodes.
2161 # new nodes.
2152 if rev == currentrev: # only display stuff in rev
2162 if rev == currentrev: # only display stuff in rev
2153 gr[0].append(rev)
2163 gr[0].append(rev)
2154 gr[1].remove(rev)
2164 gr[1].remove(rev)
2155 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2165 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2156 gr[1].update(parents)
2166 gr[1].update(parents)
2157 for p in parents:
2167 for p in parents:
2158 if p not in pendingset:
2168 if p not in pendingset:
2159 pendingset.add(p)
2169 pendingset.add(p)
2160 heappush(pendingheap, -p)
2170 heappush(pendingheap, -p)
2161
2171
2162 # Look for a subgroup to display
2172 # Look for a subgroup to display
2163 #
2173 #
2164 # When unblocked is empty (if clause), we were not waiting for any
2174 # When unblocked is empty (if clause), we were not waiting for any
2165 # revisions during the first iteration (if no priority was given) or
2175 # revisions during the first iteration (if no priority was given) or
2166 # if we emitted a whole disconnected set of the graph (reached a
2176 # if we emitted a whole disconnected set of the graph (reached a
2167 # root). In that case we arbitrarily take the oldest known
2177 # root). In that case we arbitrarily take the oldest known
2168 # subgroup. The heuristic could probably be better.
2178 # subgroup. The heuristic could probably be better.
2169 #
2179 #
2170 # Otherwise (elif clause) if the subgroup is blocked on
2180 # Otherwise (elif clause) if the subgroup is blocked on
2171 # a revision we just emitted, we can safely emit it as
2181 # a revision we just emitted, we can safely emit it as
2172 # well.
2182 # well.
2173 if not unblocked:
2183 if not unblocked:
2174 if len(groups) > 1: # display other subset
2184 if len(groups) > 1: # display other subset
2175 targetidx = 1
2185 targetidx = 1
2176 gr = groups[1]
2186 gr = groups[1]
2177 elif not gr[1] & unblocked:
2187 elif not gr[1] & unblocked:
2178 gr = None
2188 gr = None
2179
2189
2180 if gr is not None:
2190 if gr is not None:
2181 # update the set of awaited revisions with the one from the
2191 # update the set of awaited revisions with the one from the
2182 # subgroup
2192 # subgroup
2183 unblocked |= gr[1]
2193 unblocked |= gr[1]
2184 # output all revisions in the subgroup
2194 # output all revisions in the subgroup
2185 for r in gr[0]:
2195 for r in gr[0]:
2186 yield r
2196 yield r
2187 # delete the subgroup that you just output
2197 # delete the subgroup that you just output
2188 # unless it is groups[0] in which case you just empty it.
2198 # unless it is groups[0] in which case you just empty it.
2189 if targetidx:
2199 if targetidx:
2190 del groups[targetidx]
2200 del groups[targetidx]
2191 else:
2201 else:
2192 gr[0][:] = []
2202 gr[0][:] = []
2193 # Check if we have some subgroup waiting for revisions we are not going to
2203 # Check if we have some subgroup waiting for revisions we are not going to
2194 # iterate over
2204 # iterate over
2195 for g in groups:
2205 for g in groups:
2196 for r in g[0]:
2206 for r in g[0]:
2197 yield r
2207 yield r
2198
2208
2199 @predicate('subrepo([pattern])')
2209 @predicate('subrepo([pattern])')
2200 def subrepo(repo, subset, x):
2210 def subrepo(repo, subset, x):
2201 """Changesets that add, modify or remove the given subrepo. If no subrepo
2211 """Changesets that add, modify or remove the given subrepo. If no subrepo
2202 pattern is named, any subrepo changes are returned.
2212 pattern is named, any subrepo changes are returned.
2203 """
2213 """
2204 # i18n: "subrepo" is a keyword
2214 # i18n: "subrepo" is a keyword
2205 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2215 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2206 pat = None
2216 pat = None
2207 if len(args) != 0:
2217 if len(args) != 0:
2208 pat = getstring(args[0], _("subrepo requires a pattern"))
2218 pat = getstring(args[0], _("subrepo requires a pattern"))
2209
2219
2210 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2220 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2211
2221
2212 def submatches(names):
2222 def submatches(names):
2213 k, p, m = util.stringmatcher(pat)
2223 k, p, m = util.stringmatcher(pat)
2214 for name in names:
2224 for name in names:
2215 if m(name):
2225 if m(name):
2216 yield name
2226 yield name
2217
2227
2218 def matches(x):
2228 def matches(x):
2219 c = repo[x]
2229 c = repo[x]
2220 s = repo.status(c.p1().node(), c.node(), match=m)
2230 s = repo.status(c.p1().node(), c.node(), match=m)
2221
2231
2222 if pat is None:
2232 if pat is None:
2223 return s.added or s.modified or s.removed
2233 return s.added or s.modified or s.removed
2224
2234
2225 if s.added:
2235 if s.added:
2226 return any(submatches(c.substate.keys()))
2236 return any(submatches(c.substate.keys()))
2227
2237
2228 if s.modified:
2238 if s.modified:
2229 subs = set(c.p1().substate.keys())
2239 subs = set(c.p1().substate.keys())
2230 subs.update(c.substate.keys())
2240 subs.update(c.substate.keys())
2231
2241
2232 for path in submatches(subs):
2242 for path in submatches(subs):
2233 if c.p1().substate.get(path) != c.substate.get(path):
2243 if c.p1().substate.get(path) != c.substate.get(path):
2234 return True
2244 return True
2235
2245
2236 if s.removed:
2246 if s.removed:
2237 return any(submatches(c.p1().substate.keys()))
2247 return any(submatches(c.p1().substate.keys()))
2238
2248
2239 return False
2249 return False
2240
2250
2241 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2251 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2242
2252
2243 def _substringmatcher(pattern, casesensitive=True):
2253 def _substringmatcher(pattern, casesensitive=True):
2244 kind, pattern, matcher = util.stringmatcher(pattern,
2254 kind, pattern, matcher = util.stringmatcher(pattern,
2245 casesensitive=casesensitive)
2255 casesensitive=casesensitive)
2246 if kind == 'literal':
2256 if kind == 'literal':
2247 if not casesensitive:
2257 if not casesensitive:
2248 pattern = encoding.lower(pattern)
2258 pattern = encoding.lower(pattern)
2249 matcher = lambda s: pattern in encoding.lower(s)
2259 matcher = lambda s: pattern in encoding.lower(s)
2250 else:
2260 else:
2251 matcher = lambda s: pattern in s
2261 matcher = lambda s: pattern in s
2252 return kind, pattern, matcher
2262 return kind, pattern, matcher
2253
2263
2254 @predicate('tag([name])', safe=True)
2264 @predicate('tag([name])', safe=True)
2255 def tag(repo, subset, x):
2265 def tag(repo, subset, x):
2256 """The specified tag by name, or all tagged revisions if no name is given.
2266 """The specified tag by name, or all tagged revisions if no name is given.
2257
2267
2258 Pattern matching is supported for `name`. See
2268 Pattern matching is supported for `name`. See
2259 :hg:`help revisions.patterns`.
2269 :hg:`help revisions.patterns`.
2260 """
2270 """
2261 # i18n: "tag" is a keyword
2271 # i18n: "tag" is a keyword
2262 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2272 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2263 cl = repo.changelog
2273 cl = repo.changelog
2264 if args:
2274 if args:
2265 pattern = getstring(args[0],
2275 pattern = getstring(args[0],
2266 # i18n: "tag" is a keyword
2276 # i18n: "tag" is a keyword
2267 _('the argument to tag must be a string'))
2277 _('the argument to tag must be a string'))
2268 kind, pattern, matcher = util.stringmatcher(pattern)
2278 kind, pattern, matcher = util.stringmatcher(pattern)
2269 if kind == 'literal':
2279 if kind == 'literal':
2270 # avoid resolving all tags
2280 # avoid resolving all tags
2271 tn = repo._tagscache.tags.get(pattern, None)
2281 tn = repo._tagscache.tags.get(pattern, None)
2272 if tn is None:
2282 if tn is None:
2273 raise error.RepoLookupError(_("tag '%s' does not exist")
2283 raise error.RepoLookupError(_("tag '%s' does not exist")
2274 % pattern)
2284 % pattern)
2275 s = set([repo[tn].rev()])
2285 s = set([repo[tn].rev()])
2276 else:
2286 else:
2277 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2287 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2278 else:
2288 else:
2279 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2289 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2280 return subset & s
2290 return subset & s
2281
2291
2282 @predicate('tagged', safe=True)
2292 @predicate('tagged', safe=True)
2283 def tagged(repo, subset, x):
2293 def tagged(repo, subset, x):
2284 return tag(repo, subset, x)
2294 return tag(repo, subset, x)
2285
2295
2286 @predicate('unstable()', safe=True)
2296 @predicate('unstable()', safe=True)
2287 def unstable(repo, subset, x):
2297 def unstable(repo, subset, x):
2288 """Non-obsolete changesets with obsolete ancestors.
2298 """Non-obsolete changesets with obsolete ancestors.
2289 """
2299 """
2290 # i18n: "unstable" is a keyword
2300 # i18n: "unstable" is a keyword
2291 getargs(x, 0, 0, _("unstable takes no arguments"))
2301 getargs(x, 0, 0, _("unstable takes no arguments"))
2292 unstables = obsmod.getrevs(repo, 'unstable')
2302 unstables = obsmod.getrevs(repo, 'unstable')
2293 return subset & unstables
2303 return subset & unstables
2294
2304
2295
2305
2296 @predicate('user(string)', safe=True)
2306 @predicate('user(string)', safe=True)
2297 def user(repo, subset, x):
2307 def user(repo, subset, x):
2298 """User name contains string. The match is case-insensitive.
2308 """User name contains string. The match is case-insensitive.
2299
2309
2300 Pattern matching is supported for `string`. See
2310 Pattern matching is supported for `string`. See
2301 :hg:`help revisions.patterns`.
2311 :hg:`help revisions.patterns`.
2302 """
2312 """
2303 return author(repo, subset, x)
2313 return author(repo, subset, x)
2304
2314
2305 @predicate('wdir', safe=True)
2315 @predicate('wdir', safe=True)
2306 def wdir(repo, subset, x):
2316 def wdir(repo, subset, x):
2307 """Working directory. (EXPERIMENTAL)"""
2317 """Working directory. (EXPERIMENTAL)"""
2308 # i18n: "wdir" is a keyword
2318 # i18n: "wdir" is a keyword
2309 getargs(x, 0, 0, _("wdir takes no arguments"))
2319 getargs(x, 0, 0, _("wdir takes no arguments"))
2310 if node.wdirrev in subset or isinstance(subset, fullreposet):
2320 if node.wdirrev in subset or isinstance(subset, fullreposet):
2311 return baseset([node.wdirrev])
2321 return baseset([node.wdirrev])
2312 return baseset()
2322 return baseset()
2313
2323
2314 def _orderedlist(repo, subset, x):
2324 def _orderedlist(repo, subset, x):
2315 s = getstring(x, "internal error")
2325 s = getstring(x, "internal error")
2316 if not s:
2326 if not s:
2317 return baseset()
2327 return baseset()
2318 # remove duplicates here. it's difficult for caller to deduplicate sets
2328 # remove duplicates here. it's difficult for caller to deduplicate sets
2319 # because different symbols can point to the same rev.
2329 # because different symbols can point to the same rev.
2320 cl = repo.changelog
2330 cl = repo.changelog
2321 ls = []
2331 ls = []
2322 seen = set()
2332 seen = set()
2323 for t in s.split('\0'):
2333 for t in s.split('\0'):
2324 try:
2334 try:
2325 # fast path for integer revision
2335 # fast path for integer revision
2326 r = int(t)
2336 r = int(t)
2327 if str(r) != t or r not in cl:
2337 if str(r) != t or r not in cl:
2328 raise ValueError
2338 raise ValueError
2329 revs = [r]
2339 revs = [r]
2330 except ValueError:
2340 except ValueError:
2331 revs = stringset(repo, subset, t)
2341 revs = stringset(repo, subset, t)
2332
2342
2333 for r in revs:
2343 for r in revs:
2334 if r in seen:
2344 if r in seen:
2335 continue
2345 continue
2336 if (r in subset
2346 if (r in subset
2337 or r == node.nullrev and isinstance(subset, fullreposet)):
2347 or r == node.nullrev and isinstance(subset, fullreposet)):
2338 ls.append(r)
2348 ls.append(r)
2339 seen.add(r)
2349 seen.add(r)
2340 return baseset(ls)
2350 return baseset(ls)
2341
2351
2342 # for internal use
2352 # for internal use
2343 @predicate('_list', safe=True, takeorder=True)
2353 @predicate('_list', safe=True, takeorder=True)
2344 def _list(repo, subset, x, order):
2354 def _list(repo, subset, x, order):
2345 if order == followorder:
2355 if order == followorder:
2346 # slow path to take the subset order
2356 # slow path to take the subset order
2347 return subset & _orderedlist(repo, fullreposet(repo), x)
2357 return subset & _orderedlist(repo, fullreposet(repo), x)
2348 else:
2358 else:
2349 return _orderedlist(repo, subset, x)
2359 return _orderedlist(repo, subset, x)
2350
2360
2351 def _orderedintlist(repo, subset, x):
2361 def _orderedintlist(repo, subset, x):
2352 s = getstring(x, "internal error")
2362 s = getstring(x, "internal error")
2353 if not s:
2363 if not s:
2354 return baseset()
2364 return baseset()
2355 ls = [int(r) for r in s.split('\0')]
2365 ls = [int(r) for r in s.split('\0')]
2356 s = subset
2366 s = subset
2357 return baseset([r for r in ls if r in s])
2367 return baseset([r for r in ls if r in s])
2358
2368
2359 # for internal use
2369 # for internal use
2360 @predicate('_intlist', safe=True, takeorder=True)
2370 @predicate('_intlist', safe=True, takeorder=True)
2361 def _intlist(repo, subset, x, order):
2371 def _intlist(repo, subset, x, order):
2362 if order == followorder:
2372 if order == followorder:
2363 # slow path to take the subset order
2373 # slow path to take the subset order
2364 return subset & _orderedintlist(repo, fullreposet(repo), x)
2374 return subset & _orderedintlist(repo, fullreposet(repo), x)
2365 else:
2375 else:
2366 return _orderedintlist(repo, subset, x)
2376 return _orderedintlist(repo, subset, x)
2367
2377
2368 def _orderedhexlist(repo, subset, x):
2378 def _orderedhexlist(repo, subset, x):
2369 s = getstring(x, "internal error")
2379 s = getstring(x, "internal error")
2370 if not s:
2380 if not s:
2371 return baseset()
2381 return baseset()
2372 cl = repo.changelog
2382 cl = repo.changelog
2373 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2383 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2374 s = subset
2384 s = subset
2375 return baseset([r for r in ls if r in s])
2385 return baseset([r for r in ls if r in s])
2376
2386
2377 # for internal use
2387 # for internal use
2378 @predicate('_hexlist', safe=True, takeorder=True)
2388 @predicate('_hexlist', safe=True, takeorder=True)
2379 def _hexlist(repo, subset, x, order):
2389 def _hexlist(repo, subset, x, order):
2380 if order == followorder:
2390 if order == followorder:
2381 # slow path to take the subset order
2391 # slow path to take the subset order
2382 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2392 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2383 else:
2393 else:
2384 return _orderedhexlist(repo, subset, x)
2394 return _orderedhexlist(repo, subset, x)
2385
2395
2386 methods = {
2396 methods = {
2387 "range": rangeset,
2397 "range": rangeset,
2398 "rangeall": rangeall,
2388 "rangepre": rangepre,
2399 "rangepre": rangepre,
2400 "rangepost": rangepost,
2389 "dagrange": dagrange,
2401 "dagrange": dagrange,
2390 "string": stringset,
2402 "string": stringset,
2391 "symbol": stringset,
2403 "symbol": stringset,
2392 "and": andset,
2404 "and": andset,
2393 "or": orset,
2405 "or": orset,
2394 "not": notset,
2406 "not": notset,
2395 "difference": differenceset,
2407 "difference": differenceset,
2396 "list": listset,
2408 "list": listset,
2397 "keyvalue": keyvaluepair,
2409 "keyvalue": keyvaluepair,
2398 "func": func,
2410 "func": func,
2399 "ancestor": ancestorspec,
2411 "ancestor": ancestorspec,
2400 "parent": parentspec,
2412 "parent": parentspec,
2401 "parentpost": parentpost,
2413 "parentpost": parentpost,
2402 }
2414 }
2403
2415
2404 # Constants for ordering requirement, used in _analyze():
2416 # Constants for ordering requirement, used in _analyze():
2405 #
2417 #
2406 # If 'define', any nested functions and operations can change the ordering of
2418 # If 'define', any nested functions and operations can change the ordering of
2407 # the entries in the set. If 'follow', any nested functions and operations
2419 # the entries in the set. If 'follow', any nested functions and operations
2408 # should take the ordering specified by the first operand to the '&' operator.
2420 # should take the ordering specified by the first operand to the '&' operator.
2409 #
2421 #
2410 # For instance,
2422 # For instance,
2411 #
2423 #
2412 # X & (Y | Z)
2424 # X & (Y | Z)
2413 # ^ ^^^^^^^
2425 # ^ ^^^^^^^
2414 # | follow
2426 # | follow
2415 # define
2427 # define
2416 #
2428 #
2417 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2429 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
2418 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2430 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
2419 #
2431 #
2420 # 'any' means the order doesn't matter. For instance,
2432 # 'any' means the order doesn't matter. For instance,
2421 #
2433 #
2422 # X & !Y
2434 # X & !Y
2423 # ^
2435 # ^
2424 # any
2436 # any
2425 #
2437 #
2426 # 'y()' can either enforce its ordering requirement or take the ordering
2438 # 'y()' can either enforce its ordering requirement or take the ordering
2427 # specified by 'x()' because 'not()' doesn't care the order.
2439 # specified by 'x()' because 'not()' doesn't care the order.
2428 #
2440 #
2429 # Transition of ordering requirement:
2441 # Transition of ordering requirement:
2430 #
2442 #
2431 # 1. starts with 'define'
2443 # 1. starts with 'define'
2432 # 2. shifts to 'follow' by 'x & y'
2444 # 2. shifts to 'follow' by 'x & y'
2433 # 3. changes back to 'define' on function call 'f(x)' or function-like
2445 # 3. changes back to 'define' on function call 'f(x)' or function-like
2434 # operation 'x (f) y' because 'f' may have its own ordering requirement
2446 # operation 'x (f) y' because 'f' may have its own ordering requirement
2435 # for 'x' and 'y' (e.g. 'first(x)')
2447 # for 'x' and 'y' (e.g. 'first(x)')
2436 #
2448 #
2437 anyorder = 'any' # don't care the order
2449 anyorder = 'any' # don't care the order
2438 defineorder = 'define' # should define the order
2450 defineorder = 'define' # should define the order
2439 followorder = 'follow' # must follow the current order
2451 followorder = 'follow' # must follow the current order
2440
2452
2441 # transition table for 'x & y', from the current expression 'x' to 'y'
2453 # transition table for 'x & y', from the current expression 'x' to 'y'
2442 _tofolloworder = {
2454 _tofolloworder = {
2443 anyorder: anyorder,
2455 anyorder: anyorder,
2444 defineorder: followorder,
2456 defineorder: followorder,
2445 followorder: followorder,
2457 followorder: followorder,
2446 }
2458 }
2447
2459
2448 def _matchonly(revs, bases):
2460 def _matchonly(revs, bases):
2449 """
2461 """
2450 >>> f = lambda *args: _matchonly(*map(parse, args))
2462 >>> f = lambda *args: _matchonly(*map(parse, args))
2451 >>> f('ancestors(A)', 'not ancestors(B)')
2463 >>> f('ancestors(A)', 'not ancestors(B)')
2452 ('list', ('symbol', 'A'), ('symbol', 'B'))
2464 ('list', ('symbol', 'A'), ('symbol', 'B'))
2453 """
2465 """
2454 if (revs is not None
2466 if (revs is not None
2455 and revs[0] == 'func'
2467 and revs[0] == 'func'
2456 and getsymbol(revs[1]) == 'ancestors'
2468 and getsymbol(revs[1]) == 'ancestors'
2457 and bases is not None
2469 and bases is not None
2458 and bases[0] == 'not'
2470 and bases[0] == 'not'
2459 and bases[1][0] == 'func'
2471 and bases[1][0] == 'func'
2460 and getsymbol(bases[1][1]) == 'ancestors'):
2472 and getsymbol(bases[1][1]) == 'ancestors'):
2461 return ('list', revs[2], bases[1][2])
2473 return ('list', revs[2], bases[1][2])
2462
2474
2463 def _fixops(x):
2475 def _fixops(x):
2464 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2476 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2465 handled well by our simple top-down parser"""
2477 handled well by our simple top-down parser"""
2466 if not isinstance(x, tuple):
2478 if not isinstance(x, tuple):
2467 return x
2479 return x
2468
2480
2469 op = x[0]
2481 op = x[0]
2470 if op == 'parent':
2482 if op == 'parent':
2471 # x^:y means (x^) : y, not x ^ (:y)
2483 # x^:y means (x^) : y, not x ^ (:y)
2472 # x^: means (x^) :, not x ^ (:)
2484 # x^: means (x^) :, not x ^ (:)
2473 post = ('parentpost', x[1])
2485 post = ('parentpost', x[1])
2474 if x[2][0] == 'dagrangepre':
2486 if x[2][0] == 'dagrangepre':
2475 return _fixops(('dagrange', post, x[2][1]))
2487 return _fixops(('dagrange', post, x[2][1]))
2476 elif x[2][0] == 'rangepre':
2488 elif x[2][0] == 'rangepre':
2477 return _fixops(('range', post, x[2][1]))
2489 return _fixops(('range', post, x[2][1]))
2478 elif x[2][0] == 'rangeall':
2490 elif x[2][0] == 'rangeall':
2479 return _fixops(('rangepost', post))
2491 return _fixops(('rangepost', post))
2480 elif op == 'or':
2492 elif op == 'or':
2481 # make number of arguments deterministic:
2493 # make number of arguments deterministic:
2482 # x + y + z -> (or x y z) -> (or (list x y z))
2494 # x + y + z -> (or x y z) -> (or (list x y z))
2483 return (op, _fixops(('list',) + x[1:]))
2495 return (op, _fixops(('list',) + x[1:]))
2484
2496
2485 return (op,) + tuple(_fixops(y) for y in x[1:])
2497 return (op,) + tuple(_fixops(y) for y in x[1:])
2486
2498
2487 def _analyze(x, order):
2499 def _analyze(x, order):
2488 if x is None:
2500 if x is None:
2489 return x
2501 return x
2490
2502
2491 op = x[0]
2503 op = x[0]
2492 if op == 'minus':
2504 if op == 'minus':
2493 return _analyze(('and', x[1], ('not', x[2])), order)
2505 return _analyze(('and', x[1], ('not', x[2])), order)
2494 elif op == 'only':
2506 elif op == 'only':
2495 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2507 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2496 return _analyze(t, order)
2508 return _analyze(t, order)
2497 elif op == 'onlypost':
2509 elif op == 'onlypost':
2498 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2510 return _analyze(('func', ('symbol', 'only'), x[1]), order)
2499 elif op == 'dagrangepre':
2511 elif op == 'dagrangepre':
2500 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2512 return _analyze(('func', ('symbol', 'ancestors'), x[1]), order)
2501 elif op == 'dagrangepost':
2513 elif op == 'dagrangepost':
2502 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2514 return _analyze(('func', ('symbol', 'descendants'), x[1]), order)
2503 elif op == 'rangeall':
2504 return _analyze(('rangepre', ('string', 'tip')), order)
2505 elif op == 'rangepost':
2506 return _analyze(('range', x[1], ('string', 'tip')), order)
2507 elif op == 'negate':
2515 elif op == 'negate':
2508 s = getstring(x[1], _("can't negate that"))
2516 s = getstring(x[1], _("can't negate that"))
2509 return _analyze(('string', '-' + s), order)
2517 return _analyze(('string', '-' + s), order)
2510 elif op in ('string', 'symbol'):
2518 elif op in ('string', 'symbol'):
2511 return x
2519 return x
2512 elif op == 'and':
2520 elif op == 'and':
2513 ta = _analyze(x[1], order)
2521 ta = _analyze(x[1], order)
2514 tb = _analyze(x[2], _tofolloworder[order])
2522 tb = _analyze(x[2], _tofolloworder[order])
2515 return (op, ta, tb, order)
2523 return (op, ta, tb, order)
2516 elif op == 'or':
2524 elif op == 'or':
2517 return (op, _analyze(x[1], order), order)
2525 return (op, _analyze(x[1], order), order)
2518 elif op == 'not':
2526 elif op == 'not':
2519 return (op, _analyze(x[1], anyorder), order)
2527 return (op, _analyze(x[1], anyorder), order)
2520 elif op in ('rangepre', 'parentpost'):
2528 elif op == 'rangeall':
2529 return (op, None, order)
2530 elif op in ('rangepre', 'rangepost', 'parentpost'):
2521 return (op, _analyze(x[1], defineorder), order)
2531 return (op, _analyze(x[1], defineorder), order)
2522 elif op == 'group':
2532 elif op == 'group':
2523 return _analyze(x[1], order)
2533 return _analyze(x[1], order)
2524 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2534 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2525 ta = _analyze(x[1], defineorder)
2535 ta = _analyze(x[1], defineorder)
2526 tb = _analyze(x[2], defineorder)
2536 tb = _analyze(x[2], defineorder)
2527 return (op, ta, tb, order)
2537 return (op, ta, tb, order)
2528 elif op == 'list':
2538 elif op == 'list':
2529 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2539 return (op,) + tuple(_analyze(y, order) for y in x[1:])
2530 elif op == 'keyvalue':
2540 elif op == 'keyvalue':
2531 return (op, x[1], _analyze(x[2], order))
2541 return (op, x[1], _analyze(x[2], order))
2532 elif op == 'func':
2542 elif op == 'func':
2533 f = getsymbol(x[1])
2543 f = getsymbol(x[1])
2534 d = defineorder
2544 d = defineorder
2535 if f == 'present':
2545 if f == 'present':
2536 # 'present(set)' is known to return the argument set with no
2546 # 'present(set)' is known to return the argument set with no
2537 # modification, so forward the current order to its argument
2547 # modification, so forward the current order to its argument
2538 d = order
2548 d = order
2539 return (op, x[1], _analyze(x[2], d), order)
2549 return (op, x[1], _analyze(x[2], d), order)
2540 raise ValueError('invalid operator %r' % op)
2550 raise ValueError('invalid operator %r' % op)
2541
2551
2542 def analyze(x, order=defineorder):
2552 def analyze(x, order=defineorder):
2543 """Transform raw parsed tree to evaluatable tree which can be fed to
2553 """Transform raw parsed tree to evaluatable tree which can be fed to
2544 optimize() or getset()
2554 optimize() or getset()
2545
2555
2546 All pseudo operations should be mapped to real operations or functions
2556 All pseudo operations should be mapped to real operations or functions
2547 defined in methods or symbols table respectively.
2557 defined in methods or symbols table respectively.
2548
2558
2549 'order' specifies how the current expression 'x' is ordered (see the
2559 'order' specifies how the current expression 'x' is ordered (see the
2550 constants defined above.)
2560 constants defined above.)
2551 """
2561 """
2552 return _analyze(x, order)
2562 return _analyze(x, order)
2553
2563
2554 def _optimize(x, small):
2564 def _optimize(x, small):
2555 if x is None:
2565 if x is None:
2556 return 0, x
2566 return 0, x
2557
2567
2558 smallbonus = 1
2568 smallbonus = 1
2559 if small:
2569 if small:
2560 smallbonus = .5
2570 smallbonus = .5
2561
2571
2562 op = x[0]
2572 op = x[0]
2563 if op in ('string', 'symbol'):
2573 if op in ('string', 'symbol'):
2564 return smallbonus, x # single revisions are small
2574 return smallbonus, x # single revisions are small
2565 elif op == 'and':
2575 elif op == 'and':
2566 wa, ta = _optimize(x[1], True)
2576 wa, ta = _optimize(x[1], True)
2567 wb, tb = _optimize(x[2], True)
2577 wb, tb = _optimize(x[2], True)
2568 order = x[3]
2578 order = x[3]
2569 w = min(wa, wb)
2579 w = min(wa, wb)
2570
2580
2571 # (::x and not ::y)/(not ::y and ::x) have a fast path
2581 # (::x and not ::y)/(not ::y and ::x) have a fast path
2572 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2582 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2573 if tm:
2583 if tm:
2574 return w, ('func', ('symbol', 'only'), tm, order)
2584 return w, ('func', ('symbol', 'only'), tm, order)
2575
2585
2576 if tb is not None and tb[0] == 'not':
2586 if tb is not None and tb[0] == 'not':
2577 return wa, ('difference', ta, tb[1], order)
2587 return wa, ('difference', ta, tb[1], order)
2578
2588
2579 if wa > wb:
2589 if wa > wb:
2580 return w, (op, tb, ta, order)
2590 return w, (op, tb, ta, order)
2581 return w, (op, ta, tb, order)
2591 return w, (op, ta, tb, order)
2582 elif op == 'or':
2592 elif op == 'or':
2583 # fast path for machine-generated expression, that is likely to have
2593 # fast path for machine-generated expression, that is likely to have
2584 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2594 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2585 order = x[2]
2595 order = x[2]
2586 ws, ts, ss = [], [], []
2596 ws, ts, ss = [], [], []
2587 def flushss():
2597 def flushss():
2588 if not ss:
2598 if not ss:
2589 return
2599 return
2590 if len(ss) == 1:
2600 if len(ss) == 1:
2591 w, t = ss[0]
2601 w, t = ss[0]
2592 else:
2602 else:
2593 s = '\0'.join(t[1] for w, t in ss)
2603 s = '\0'.join(t[1] for w, t in ss)
2594 y = ('func', ('symbol', '_list'), ('string', s), order)
2604 y = ('func', ('symbol', '_list'), ('string', s), order)
2595 w, t = _optimize(y, False)
2605 w, t = _optimize(y, False)
2596 ws.append(w)
2606 ws.append(w)
2597 ts.append(t)
2607 ts.append(t)
2598 del ss[:]
2608 del ss[:]
2599 for y in getlist(x[1]):
2609 for y in getlist(x[1]):
2600 w, t = _optimize(y, False)
2610 w, t = _optimize(y, False)
2601 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2611 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2602 ss.append((w, t))
2612 ss.append((w, t))
2603 continue
2613 continue
2604 flushss()
2614 flushss()
2605 ws.append(w)
2615 ws.append(w)
2606 ts.append(t)
2616 ts.append(t)
2607 flushss()
2617 flushss()
2608 if len(ts) == 1:
2618 if len(ts) == 1:
2609 return ws[0], ts[0] # 'or' operation is fully optimized out
2619 return ws[0], ts[0] # 'or' operation is fully optimized out
2610 # we can't reorder trees by weight because it would change the order.
2620 # we can't reorder trees by weight because it would change the order.
2611 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2621 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2612 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2622 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2613 return max(ws), (op, ('list',) + tuple(ts), order)
2623 return max(ws), (op, ('list',) + tuple(ts), order)
2614 elif op == 'not':
2624 elif op == 'not':
2615 # Optimize not public() to _notpublic() because we have a fast version
2625 # Optimize not public() to _notpublic() because we have a fast version
2616 if x[1][:3] == ('func', ('symbol', 'public'), None):
2626 if x[1][:3] == ('func', ('symbol', 'public'), None):
2617 order = x[1][3]
2627 order = x[1][3]
2618 newsym = ('func', ('symbol', '_notpublic'), None, order)
2628 newsym = ('func', ('symbol', '_notpublic'), None, order)
2619 o = _optimize(newsym, not small)
2629 o = _optimize(newsym, not small)
2620 return o[0], o[1]
2630 return o[0], o[1]
2621 else:
2631 else:
2622 o = _optimize(x[1], not small)
2632 o = _optimize(x[1], not small)
2623 order = x[2]
2633 order = x[2]
2624 return o[0], (op, o[1], order)
2634 return o[0], (op, o[1], order)
2625 elif op in ('rangepre', 'parentpost'):
2635 elif op == 'rangeall':
2636 return smallbonus, x
2637 elif op in ('rangepre', 'rangepost', 'parentpost'):
2626 o = _optimize(x[1], small)
2638 o = _optimize(x[1], small)
2627 order = x[2]
2639 order = x[2]
2628 return o[0], (op, o[1], order)
2640 return o[0], (op, o[1], order)
2629 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2641 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2630 wa, ta = _optimize(x[1], small)
2642 wa, ta = _optimize(x[1], small)
2631 wb, tb = _optimize(x[2], small)
2643 wb, tb = _optimize(x[2], small)
2632 order = x[3]
2644 order = x[3]
2633 return wa + wb, (op, ta, tb, order)
2645 return wa + wb, (op, ta, tb, order)
2634 elif op == 'list':
2646 elif op == 'list':
2635 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2647 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2636 return sum(ws), (op,) + ts
2648 return sum(ws), (op,) + ts
2637 elif op == 'keyvalue':
2649 elif op == 'keyvalue':
2638 w, t = _optimize(x[2], small)
2650 w, t = _optimize(x[2], small)
2639 return w, (op, x[1], t)
2651 return w, (op, x[1], t)
2640 elif op == 'func':
2652 elif op == 'func':
2641 f = getsymbol(x[1])
2653 f = getsymbol(x[1])
2642 wa, ta = _optimize(x[2], small)
2654 wa, ta = _optimize(x[2], small)
2643 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2655 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2644 'keyword', 'outgoing', 'user', 'destination'):
2656 'keyword', 'outgoing', 'user', 'destination'):
2645 w = 10 # slow
2657 w = 10 # slow
2646 elif f in ('modifies', 'adds', 'removes'):
2658 elif f in ('modifies', 'adds', 'removes'):
2647 w = 30 # slower
2659 w = 30 # slower
2648 elif f == "contains":
2660 elif f == "contains":
2649 w = 100 # very slow
2661 w = 100 # very slow
2650 elif f == "ancestor":
2662 elif f == "ancestor":
2651 w = 1 * smallbonus
2663 w = 1 * smallbonus
2652 elif f in ('reverse', 'limit', 'first', 'wdir', '_intlist'):
2664 elif f in ('reverse', 'limit', 'first', 'wdir', '_intlist'):
2653 w = 0
2665 w = 0
2654 elif f == "sort":
2666 elif f == "sort":
2655 w = 10 # assume most sorts look at changelog
2667 w = 10 # assume most sorts look at changelog
2656 else:
2668 else:
2657 w = 1
2669 w = 1
2658 order = x[3]
2670 order = x[3]
2659 return w + wa, (op, x[1], ta, order)
2671 return w + wa, (op, x[1], ta, order)
2660 raise ValueError('invalid operator %r' % op)
2672 raise ValueError('invalid operator %r' % op)
2661
2673
2662 def optimize(tree):
2674 def optimize(tree):
2663 """Optimize evaluatable tree
2675 """Optimize evaluatable tree
2664
2676
2665 All pseudo operations should be transformed beforehand.
2677 All pseudo operations should be transformed beforehand.
2666 """
2678 """
2667 _weight, newtree = _optimize(tree, small=True)
2679 _weight, newtree = _optimize(tree, small=True)
2668 return newtree
2680 return newtree
2669
2681
2670 # the set of valid characters for the initial letter of symbols in
2682 # the set of valid characters for the initial letter of symbols in
2671 # alias declarations and definitions
2683 # alias declarations and definitions
2672 _aliassyminitletters = _syminitletters | set(pycompat.sysstr('$'))
2684 _aliassyminitletters = _syminitletters | set(pycompat.sysstr('$'))
2673
2685
2674 def _parsewith(spec, lookup=None, syminitletters=None):
2686 def _parsewith(spec, lookup=None, syminitletters=None):
2675 """Generate a parse tree of given spec with given tokenizing options
2687 """Generate a parse tree of given spec with given tokenizing options
2676
2688
2677 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2689 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2678 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2690 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2679 >>> _parsewith('$1')
2691 >>> _parsewith('$1')
2680 Traceback (most recent call last):
2692 Traceback (most recent call last):
2681 ...
2693 ...
2682 ParseError: ("syntax error in revset '$1'", 0)
2694 ParseError: ("syntax error in revset '$1'", 0)
2683 >>> _parsewith('foo bar')
2695 >>> _parsewith('foo bar')
2684 Traceback (most recent call last):
2696 Traceback (most recent call last):
2685 ...
2697 ...
2686 ParseError: ('invalid token', 4)
2698 ParseError: ('invalid token', 4)
2687 """
2699 """
2688 p = parser.parser(elements)
2700 p = parser.parser(elements)
2689 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2701 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2690 syminitletters=syminitletters))
2702 syminitletters=syminitletters))
2691 if pos != len(spec):
2703 if pos != len(spec):
2692 raise error.ParseError(_('invalid token'), pos)
2704 raise error.ParseError(_('invalid token'), pos)
2693 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2705 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2694
2706
2695 class _aliasrules(parser.basealiasrules):
2707 class _aliasrules(parser.basealiasrules):
2696 """Parsing and expansion rule set of revset aliases"""
2708 """Parsing and expansion rule set of revset aliases"""
2697 _section = _('revset alias')
2709 _section = _('revset alias')
2698
2710
2699 @staticmethod
2711 @staticmethod
2700 def _parse(spec):
2712 def _parse(spec):
2701 """Parse alias declaration/definition ``spec``
2713 """Parse alias declaration/definition ``spec``
2702
2714
2703 This allows symbol names to use also ``$`` as an initial letter
2715 This allows symbol names to use also ``$`` as an initial letter
2704 (for backward compatibility), and callers of this function should
2716 (for backward compatibility), and callers of this function should
2705 examine whether ``$`` is used also for unexpected symbols or not.
2717 examine whether ``$`` is used also for unexpected symbols or not.
2706 """
2718 """
2707 return _parsewith(spec, syminitletters=_aliassyminitletters)
2719 return _parsewith(spec, syminitletters=_aliassyminitletters)
2708
2720
2709 @staticmethod
2721 @staticmethod
2710 def _trygetfunc(tree):
2722 def _trygetfunc(tree):
2711 if tree[0] == 'func' and tree[1][0] == 'symbol':
2723 if tree[0] == 'func' and tree[1][0] == 'symbol':
2712 return tree[1][1], getlist(tree[2])
2724 return tree[1][1], getlist(tree[2])
2713
2725
2714 def expandaliases(ui, tree):
2726 def expandaliases(ui, tree):
2715 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2727 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2716 tree = _aliasrules.expand(aliases, tree)
2728 tree = _aliasrules.expand(aliases, tree)
2717 # warn about problematic (but not referred) aliases
2729 # warn about problematic (but not referred) aliases
2718 for name, alias in sorted(aliases.iteritems()):
2730 for name, alias in sorted(aliases.iteritems()):
2719 if alias.error and not alias.warned:
2731 if alias.error and not alias.warned:
2720 ui.warn(_('warning: %s\n') % (alias.error))
2732 ui.warn(_('warning: %s\n') % (alias.error))
2721 alias.warned = True
2733 alias.warned = True
2722 return tree
2734 return tree
2723
2735
2724 def foldconcat(tree):
2736 def foldconcat(tree):
2725 """Fold elements to be concatenated by `##`
2737 """Fold elements to be concatenated by `##`
2726 """
2738 """
2727 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2739 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2728 return tree
2740 return tree
2729 if tree[0] == '_concat':
2741 if tree[0] == '_concat':
2730 pending = [tree]
2742 pending = [tree]
2731 l = []
2743 l = []
2732 while pending:
2744 while pending:
2733 e = pending.pop()
2745 e = pending.pop()
2734 if e[0] == '_concat':
2746 if e[0] == '_concat':
2735 pending.extend(reversed(e[1:]))
2747 pending.extend(reversed(e[1:]))
2736 elif e[0] in ('string', 'symbol'):
2748 elif e[0] in ('string', 'symbol'):
2737 l.append(e[1])
2749 l.append(e[1])
2738 else:
2750 else:
2739 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2751 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2740 raise error.ParseError(msg)
2752 raise error.ParseError(msg)
2741 return ('string', ''.join(l))
2753 return ('string', ''.join(l))
2742 else:
2754 else:
2743 return tuple(foldconcat(t) for t in tree)
2755 return tuple(foldconcat(t) for t in tree)
2744
2756
2745 def parse(spec, lookup=None):
2757 def parse(spec, lookup=None):
2746 return _parsewith(spec, lookup=lookup)
2758 return _parsewith(spec, lookup=lookup)
2747
2759
2748 def posttreebuilthook(tree, repo):
2760 def posttreebuilthook(tree, repo):
2749 # hook for extensions to execute code on the optimized tree
2761 # hook for extensions to execute code on the optimized tree
2750 pass
2762 pass
2751
2763
2752 def match(ui, spec, repo=None, order=defineorder):
2764 def match(ui, spec, repo=None, order=defineorder):
2753 """Create a matcher for a single revision spec
2765 """Create a matcher for a single revision spec
2754
2766
2755 If order=followorder, a matcher takes the ordering specified by the input
2767 If order=followorder, a matcher takes the ordering specified by the input
2756 set.
2768 set.
2757 """
2769 """
2758 return matchany(ui, [spec], repo=repo, order=order)
2770 return matchany(ui, [spec], repo=repo, order=order)
2759
2771
2760 def matchany(ui, specs, repo=None, order=defineorder):
2772 def matchany(ui, specs, repo=None, order=defineorder):
2761 """Create a matcher that will include any revisions matching one of the
2773 """Create a matcher that will include any revisions matching one of the
2762 given specs
2774 given specs
2763
2775
2764 If order=followorder, a matcher takes the ordering specified by the input
2776 If order=followorder, a matcher takes the ordering specified by the input
2765 set.
2777 set.
2766 """
2778 """
2767 if not specs:
2779 if not specs:
2768 def mfunc(repo, subset=None):
2780 def mfunc(repo, subset=None):
2769 return baseset()
2781 return baseset()
2770 return mfunc
2782 return mfunc
2771 if not all(specs):
2783 if not all(specs):
2772 raise error.ParseError(_("empty query"))
2784 raise error.ParseError(_("empty query"))
2773 lookup = None
2785 lookup = None
2774 if repo:
2786 if repo:
2775 lookup = repo.__contains__
2787 lookup = repo.__contains__
2776 if len(specs) == 1:
2788 if len(specs) == 1:
2777 tree = parse(specs[0], lookup)
2789 tree = parse(specs[0], lookup)
2778 else:
2790 else:
2779 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2791 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2780
2792
2781 if ui:
2793 if ui:
2782 tree = expandaliases(ui, tree)
2794 tree = expandaliases(ui, tree)
2783 tree = foldconcat(tree)
2795 tree = foldconcat(tree)
2784 tree = analyze(tree, order)
2796 tree = analyze(tree, order)
2785 tree = optimize(tree)
2797 tree = optimize(tree)
2786 posttreebuilthook(tree, repo)
2798 posttreebuilthook(tree, repo)
2787 return makematcher(tree)
2799 return makematcher(tree)
2788
2800
2789 def makematcher(tree):
2801 def makematcher(tree):
2790 """Create a matcher from an evaluatable tree"""
2802 """Create a matcher from an evaluatable tree"""
2791 def mfunc(repo, subset=None):
2803 def mfunc(repo, subset=None):
2792 if subset is None:
2804 if subset is None:
2793 subset = fullreposet(repo)
2805 subset = fullreposet(repo)
2794 if util.safehasattr(subset, 'isascending'):
2806 if util.safehasattr(subset, 'isascending'):
2795 result = getset(repo, subset, tree)
2807 result = getset(repo, subset, tree)
2796 else:
2808 else:
2797 result = getset(repo, baseset(subset), tree)
2809 result = getset(repo, baseset(subset), tree)
2798 return result
2810 return result
2799 return mfunc
2811 return mfunc
2800
2812
2801 def formatspec(expr, *args):
2813 def formatspec(expr, *args):
2802 '''
2814 '''
2803 This is a convenience function for using revsets internally, and
2815 This is a convenience function for using revsets internally, and
2804 escapes arguments appropriately. Aliases are intentionally ignored
2816 escapes arguments appropriately. Aliases are intentionally ignored
2805 so that intended expression behavior isn't accidentally subverted.
2817 so that intended expression behavior isn't accidentally subverted.
2806
2818
2807 Supported arguments:
2819 Supported arguments:
2808
2820
2809 %r = revset expression, parenthesized
2821 %r = revset expression, parenthesized
2810 %d = int(arg), no quoting
2822 %d = int(arg), no quoting
2811 %s = string(arg), escaped and single-quoted
2823 %s = string(arg), escaped and single-quoted
2812 %b = arg.branch(), escaped and single-quoted
2824 %b = arg.branch(), escaped and single-quoted
2813 %n = hex(arg), single-quoted
2825 %n = hex(arg), single-quoted
2814 %% = a literal '%'
2826 %% = a literal '%'
2815
2827
2816 Prefixing the type with 'l' specifies a parenthesized list of that type.
2828 Prefixing the type with 'l' specifies a parenthesized list of that type.
2817
2829
2818 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2830 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2819 '(10 or 11):: and ((this()) or (that()))'
2831 '(10 or 11):: and ((this()) or (that()))'
2820 >>> formatspec('%d:: and not %d::', 10, 20)
2832 >>> formatspec('%d:: and not %d::', 10, 20)
2821 '10:: and not 20::'
2833 '10:: and not 20::'
2822 >>> formatspec('%ld or %ld', [], [1])
2834 >>> formatspec('%ld or %ld', [], [1])
2823 "_list('') or 1"
2835 "_list('') or 1"
2824 >>> formatspec('keyword(%s)', 'foo\\xe9')
2836 >>> formatspec('keyword(%s)', 'foo\\xe9')
2825 "keyword('foo\\\\xe9')"
2837 "keyword('foo\\\\xe9')"
2826 >>> b = lambda: 'default'
2838 >>> b = lambda: 'default'
2827 >>> b.branch = b
2839 >>> b.branch = b
2828 >>> formatspec('branch(%b)', b)
2840 >>> formatspec('branch(%b)', b)
2829 "branch('default')"
2841 "branch('default')"
2830 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2842 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2831 "root(_list('a\\x00b\\x00c\\x00d'))"
2843 "root(_list('a\\x00b\\x00c\\x00d'))"
2832 '''
2844 '''
2833
2845
2834 def quote(s):
2846 def quote(s):
2835 return repr(str(s))
2847 return repr(str(s))
2836
2848
2837 def argtype(c, arg):
2849 def argtype(c, arg):
2838 if c == 'd':
2850 if c == 'd':
2839 return str(int(arg))
2851 return str(int(arg))
2840 elif c == 's':
2852 elif c == 's':
2841 return quote(arg)
2853 return quote(arg)
2842 elif c == 'r':
2854 elif c == 'r':
2843 parse(arg) # make sure syntax errors are confined
2855 parse(arg) # make sure syntax errors are confined
2844 return '(%s)' % arg
2856 return '(%s)' % arg
2845 elif c == 'n':
2857 elif c == 'n':
2846 return quote(node.hex(arg))
2858 return quote(node.hex(arg))
2847 elif c == 'b':
2859 elif c == 'b':
2848 return quote(arg.branch())
2860 return quote(arg.branch())
2849
2861
2850 def listexp(s, t):
2862 def listexp(s, t):
2851 l = len(s)
2863 l = len(s)
2852 if l == 0:
2864 if l == 0:
2853 return "_list('')"
2865 return "_list('')"
2854 elif l == 1:
2866 elif l == 1:
2855 return argtype(t, s[0])
2867 return argtype(t, s[0])
2856 elif t == 'd':
2868 elif t == 'd':
2857 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2869 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2858 elif t == 's':
2870 elif t == 's':
2859 return "_list('%s')" % "\0".join(s)
2871 return "_list('%s')" % "\0".join(s)
2860 elif t == 'n':
2872 elif t == 'n':
2861 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2873 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2862 elif t == 'b':
2874 elif t == 'b':
2863 return "_list('%s')" % "\0".join(a.branch() for a in s)
2875 return "_list('%s')" % "\0".join(a.branch() for a in s)
2864
2876
2865 m = l // 2
2877 m = l // 2
2866 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2878 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2867
2879
2868 ret = ''
2880 ret = ''
2869 pos = 0
2881 pos = 0
2870 arg = 0
2882 arg = 0
2871 while pos < len(expr):
2883 while pos < len(expr):
2872 c = expr[pos]
2884 c = expr[pos]
2873 if c == '%':
2885 if c == '%':
2874 pos += 1
2886 pos += 1
2875 d = expr[pos]
2887 d = expr[pos]
2876 if d == '%':
2888 if d == '%':
2877 ret += d
2889 ret += d
2878 elif d in 'dsnbr':
2890 elif d in 'dsnbr':
2879 ret += argtype(d, args[arg])
2891 ret += argtype(d, args[arg])
2880 arg += 1
2892 arg += 1
2881 elif d == 'l':
2893 elif d == 'l':
2882 # a list of some type
2894 # a list of some type
2883 pos += 1
2895 pos += 1
2884 d = expr[pos]
2896 d = expr[pos]
2885 ret += listexp(list(args[arg]), d)
2897 ret += listexp(list(args[arg]), d)
2886 arg += 1
2898 arg += 1
2887 else:
2899 else:
2888 raise error.Abort(_('unexpected revspec format character %s')
2900 raise error.Abort(_('unexpected revspec format character %s')
2889 % d)
2901 % d)
2890 else:
2902 else:
2891 ret += c
2903 ret += c
2892 pos += 1
2904 pos += 1
2893
2905
2894 return ret
2906 return ret
2895
2907
2896 def prettyformat(tree):
2908 def prettyformat(tree):
2897 return parser.prettyformat(tree, ('string', 'symbol'))
2909 return parser.prettyformat(tree, ('string', 'symbol'))
2898
2910
2899 def depth(tree):
2911 def depth(tree):
2900 if isinstance(tree, tuple):
2912 if isinstance(tree, tuple):
2901 return max(map(depth, tree)) + 1
2913 return max(map(depth, tree)) + 1
2902 else:
2914 else:
2903 return 0
2915 return 0
2904
2916
2905 def funcsused(tree):
2917 def funcsused(tree):
2906 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2918 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2907 return set()
2919 return set()
2908 else:
2920 else:
2909 funcs = set()
2921 funcs = set()
2910 for s in tree[1:]:
2922 for s in tree[1:]:
2911 funcs |= funcsused(s)
2923 funcs |= funcsused(s)
2912 if tree[0] == 'func':
2924 if tree[0] == 'func':
2913 funcs.add(tree[1][1])
2925 funcs.add(tree[1][1])
2914 return funcs
2926 return funcs
2915
2927
2916 def _formatsetrepr(r):
2928 def _formatsetrepr(r):
2917 """Format an optional printable representation of a set
2929 """Format an optional printable representation of a set
2918
2930
2919 ======== =================================
2931 ======== =================================
2920 type(r) example
2932 type(r) example
2921 ======== =================================
2933 ======== =================================
2922 tuple ('<not %r>', other)
2934 tuple ('<not %r>', other)
2923 str '<branch closed>'
2935 str '<branch closed>'
2924 callable lambda: '<branch %r>' % sorted(b)
2936 callable lambda: '<branch %r>' % sorted(b)
2925 object other
2937 object other
2926 ======== =================================
2938 ======== =================================
2927 """
2939 """
2928 if r is None:
2940 if r is None:
2929 return ''
2941 return ''
2930 elif isinstance(r, tuple):
2942 elif isinstance(r, tuple):
2931 return r[0] % r[1:]
2943 return r[0] % r[1:]
2932 elif isinstance(r, str):
2944 elif isinstance(r, str):
2933 return r
2945 return r
2934 elif callable(r):
2946 elif callable(r):
2935 return r()
2947 return r()
2936 else:
2948 else:
2937 return repr(r)
2949 return repr(r)
2938
2950
2939 class abstractsmartset(object):
2951 class abstractsmartset(object):
2940
2952
2941 def __nonzero__(self):
2953 def __nonzero__(self):
2942 """True if the smartset is not empty"""
2954 """True if the smartset is not empty"""
2943 raise NotImplementedError()
2955 raise NotImplementedError()
2944
2956
2945 def __contains__(self, rev):
2957 def __contains__(self, rev):
2946 """provide fast membership testing"""
2958 """provide fast membership testing"""
2947 raise NotImplementedError()
2959 raise NotImplementedError()
2948
2960
2949 def __iter__(self):
2961 def __iter__(self):
2950 """iterate the set in the order it is supposed to be iterated"""
2962 """iterate the set in the order it is supposed to be iterated"""
2951 raise NotImplementedError()
2963 raise NotImplementedError()
2952
2964
2953 # Attributes containing a function to perform a fast iteration in a given
2965 # Attributes containing a function to perform a fast iteration in a given
2954 # direction. A smartset can have none, one, or both defined.
2966 # direction. A smartset can have none, one, or both defined.
2955 #
2967 #
2956 # Default value is None instead of a function returning None to avoid
2968 # Default value is None instead of a function returning None to avoid
2957 # initializing an iterator just for testing if a fast method exists.
2969 # initializing an iterator just for testing if a fast method exists.
2958 fastasc = None
2970 fastasc = None
2959 fastdesc = None
2971 fastdesc = None
2960
2972
2961 def isascending(self):
2973 def isascending(self):
2962 """True if the set will iterate in ascending order"""
2974 """True if the set will iterate in ascending order"""
2963 raise NotImplementedError()
2975 raise NotImplementedError()
2964
2976
2965 def isdescending(self):
2977 def isdescending(self):
2966 """True if the set will iterate in descending order"""
2978 """True if the set will iterate in descending order"""
2967 raise NotImplementedError()
2979 raise NotImplementedError()
2968
2980
2969 def istopo(self):
2981 def istopo(self):
2970 """True if the set will iterate in topographical order"""
2982 """True if the set will iterate in topographical order"""
2971 raise NotImplementedError()
2983 raise NotImplementedError()
2972
2984
2973 def min(self):
2985 def min(self):
2974 """return the minimum element in the set"""
2986 """return the minimum element in the set"""
2975 if self.fastasc is None:
2987 if self.fastasc is None:
2976 v = min(self)
2988 v = min(self)
2977 else:
2989 else:
2978 for v in self.fastasc():
2990 for v in self.fastasc():
2979 break
2991 break
2980 else:
2992 else:
2981 raise ValueError('arg is an empty sequence')
2993 raise ValueError('arg is an empty sequence')
2982 self.min = lambda: v
2994 self.min = lambda: v
2983 return v
2995 return v
2984
2996
2985 def max(self):
2997 def max(self):
2986 """return the maximum element in the set"""
2998 """return the maximum element in the set"""
2987 if self.fastdesc is None:
2999 if self.fastdesc is None:
2988 return max(self)
3000 return max(self)
2989 else:
3001 else:
2990 for v in self.fastdesc():
3002 for v in self.fastdesc():
2991 break
3003 break
2992 else:
3004 else:
2993 raise ValueError('arg is an empty sequence')
3005 raise ValueError('arg is an empty sequence')
2994 self.max = lambda: v
3006 self.max = lambda: v
2995 return v
3007 return v
2996
3008
2997 def first(self):
3009 def first(self):
2998 """return the first element in the set (user iteration perspective)
3010 """return the first element in the set (user iteration perspective)
2999
3011
3000 Return None if the set is empty"""
3012 Return None if the set is empty"""
3001 raise NotImplementedError()
3013 raise NotImplementedError()
3002
3014
3003 def last(self):
3015 def last(self):
3004 """return the last element in the set (user iteration perspective)
3016 """return the last element in the set (user iteration perspective)
3005
3017
3006 Return None if the set is empty"""
3018 Return None if the set is empty"""
3007 raise NotImplementedError()
3019 raise NotImplementedError()
3008
3020
3009 def __len__(self):
3021 def __len__(self):
3010 """return the length of the smartsets
3022 """return the length of the smartsets
3011
3023
3012 This can be expensive on smartset that could be lazy otherwise."""
3024 This can be expensive on smartset that could be lazy otherwise."""
3013 raise NotImplementedError()
3025 raise NotImplementedError()
3014
3026
3015 def reverse(self):
3027 def reverse(self):
3016 """reverse the expected iteration order"""
3028 """reverse the expected iteration order"""
3017 raise NotImplementedError()
3029 raise NotImplementedError()
3018
3030
3019 def sort(self, reverse=True):
3031 def sort(self, reverse=True):
3020 """get the set to iterate in an ascending or descending order"""
3032 """get the set to iterate in an ascending or descending order"""
3021 raise NotImplementedError()
3033 raise NotImplementedError()
3022
3034
3023 def __and__(self, other):
3035 def __and__(self, other):
3024 """Returns a new object with the intersection of the two collections.
3036 """Returns a new object with the intersection of the two collections.
3025
3037
3026 This is part of the mandatory API for smartset."""
3038 This is part of the mandatory API for smartset."""
3027 if isinstance(other, fullreposet):
3039 if isinstance(other, fullreposet):
3028 return self
3040 return self
3029 return self.filter(other.__contains__, condrepr=other, cache=False)
3041 return self.filter(other.__contains__, condrepr=other, cache=False)
3030
3042
3031 def __add__(self, other):
3043 def __add__(self, other):
3032 """Returns a new object with the union of the two collections.
3044 """Returns a new object with the union of the two collections.
3033
3045
3034 This is part of the mandatory API for smartset."""
3046 This is part of the mandatory API for smartset."""
3035 return addset(self, other)
3047 return addset(self, other)
3036
3048
3037 def __sub__(self, other):
3049 def __sub__(self, other):
3038 """Returns a new object with the substraction of the two collections.
3050 """Returns a new object with the substraction of the two collections.
3039
3051
3040 This is part of the mandatory API for smartset."""
3052 This is part of the mandatory API for smartset."""
3041 c = other.__contains__
3053 c = other.__contains__
3042 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
3054 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
3043 cache=False)
3055 cache=False)
3044
3056
3045 def filter(self, condition, condrepr=None, cache=True):
3057 def filter(self, condition, condrepr=None, cache=True):
3046 """Returns this smartset filtered by condition as a new smartset.
3058 """Returns this smartset filtered by condition as a new smartset.
3047
3059
3048 `condition` is a callable which takes a revision number and returns a
3060 `condition` is a callable which takes a revision number and returns a
3049 boolean. Optional `condrepr` provides a printable representation of
3061 boolean. Optional `condrepr` provides a printable representation of
3050 the given `condition`.
3062 the given `condition`.
3051
3063
3052 This is part of the mandatory API for smartset."""
3064 This is part of the mandatory API for smartset."""
3053 # builtin cannot be cached. but do not needs to
3065 # builtin cannot be cached. but do not needs to
3054 if cache and util.safehasattr(condition, 'func_code'):
3066 if cache and util.safehasattr(condition, 'func_code'):
3055 condition = util.cachefunc(condition)
3067 condition = util.cachefunc(condition)
3056 return filteredset(self, condition, condrepr)
3068 return filteredset(self, condition, condrepr)
3057
3069
3058 class baseset(abstractsmartset):
3070 class baseset(abstractsmartset):
3059 """Basic data structure that represents a revset and contains the basic
3071 """Basic data structure that represents a revset and contains the basic
3060 operation that it should be able to perform.
3072 operation that it should be able to perform.
3061
3073
3062 Every method in this class should be implemented by any smartset class.
3074 Every method in this class should be implemented by any smartset class.
3063 """
3075 """
3064 def __init__(self, data=(), datarepr=None, istopo=False):
3076 def __init__(self, data=(), datarepr=None, istopo=False):
3065 """
3077 """
3066 datarepr: a tuple of (format, obj, ...), a function or an object that
3078 datarepr: a tuple of (format, obj, ...), a function or an object that
3067 provides a printable representation of the given data.
3079 provides a printable representation of the given data.
3068 """
3080 """
3069 self._ascending = None
3081 self._ascending = None
3070 self._istopo = istopo
3082 self._istopo = istopo
3071 if not isinstance(data, list):
3083 if not isinstance(data, list):
3072 if isinstance(data, set):
3084 if isinstance(data, set):
3073 self._set = data
3085 self._set = data
3074 # set has no order we pick one for stability purpose
3086 # set has no order we pick one for stability purpose
3075 self._ascending = True
3087 self._ascending = True
3076 data = list(data)
3088 data = list(data)
3077 self._list = data
3089 self._list = data
3078 self._datarepr = datarepr
3090 self._datarepr = datarepr
3079
3091
3080 @util.propertycache
3092 @util.propertycache
3081 def _set(self):
3093 def _set(self):
3082 return set(self._list)
3094 return set(self._list)
3083
3095
3084 @util.propertycache
3096 @util.propertycache
3085 def _asclist(self):
3097 def _asclist(self):
3086 asclist = self._list[:]
3098 asclist = self._list[:]
3087 asclist.sort()
3099 asclist.sort()
3088 return asclist
3100 return asclist
3089
3101
3090 def __iter__(self):
3102 def __iter__(self):
3091 if self._ascending is None:
3103 if self._ascending is None:
3092 return iter(self._list)
3104 return iter(self._list)
3093 elif self._ascending:
3105 elif self._ascending:
3094 return iter(self._asclist)
3106 return iter(self._asclist)
3095 else:
3107 else:
3096 return reversed(self._asclist)
3108 return reversed(self._asclist)
3097
3109
3098 def fastasc(self):
3110 def fastasc(self):
3099 return iter(self._asclist)
3111 return iter(self._asclist)
3100
3112
3101 def fastdesc(self):
3113 def fastdesc(self):
3102 return reversed(self._asclist)
3114 return reversed(self._asclist)
3103
3115
3104 @util.propertycache
3116 @util.propertycache
3105 def __contains__(self):
3117 def __contains__(self):
3106 return self._set.__contains__
3118 return self._set.__contains__
3107
3119
3108 def __nonzero__(self):
3120 def __nonzero__(self):
3109 return bool(self._list)
3121 return bool(self._list)
3110
3122
3111 def sort(self, reverse=False):
3123 def sort(self, reverse=False):
3112 self._ascending = not bool(reverse)
3124 self._ascending = not bool(reverse)
3113 self._istopo = False
3125 self._istopo = False
3114
3126
3115 def reverse(self):
3127 def reverse(self):
3116 if self._ascending is None:
3128 if self._ascending is None:
3117 self._list.reverse()
3129 self._list.reverse()
3118 else:
3130 else:
3119 self._ascending = not self._ascending
3131 self._ascending = not self._ascending
3120 self._istopo = False
3132 self._istopo = False
3121
3133
3122 def __len__(self):
3134 def __len__(self):
3123 return len(self._list)
3135 return len(self._list)
3124
3136
3125 def isascending(self):
3137 def isascending(self):
3126 """Returns True if the collection is ascending order, False if not.
3138 """Returns True if the collection is ascending order, False if not.
3127
3139
3128 This is part of the mandatory API for smartset."""
3140 This is part of the mandatory API for smartset."""
3129 if len(self) <= 1:
3141 if len(self) <= 1:
3130 return True
3142 return True
3131 return self._ascending is not None and self._ascending
3143 return self._ascending is not None and self._ascending
3132
3144
3133 def isdescending(self):
3145 def isdescending(self):
3134 """Returns True if the collection is descending order, False if not.
3146 """Returns True if the collection is descending order, False if not.
3135
3147
3136 This is part of the mandatory API for smartset."""
3148 This is part of the mandatory API for smartset."""
3137 if len(self) <= 1:
3149 if len(self) <= 1:
3138 return True
3150 return True
3139 return self._ascending is not None and not self._ascending
3151 return self._ascending is not None and not self._ascending
3140
3152
3141 def istopo(self):
3153 def istopo(self):
3142 """Is the collection is in topographical order or not.
3154 """Is the collection is in topographical order or not.
3143
3155
3144 This is part of the mandatory API for smartset."""
3156 This is part of the mandatory API for smartset."""
3145 if len(self) <= 1:
3157 if len(self) <= 1:
3146 return True
3158 return True
3147 return self._istopo
3159 return self._istopo
3148
3160
3149 def first(self):
3161 def first(self):
3150 if self:
3162 if self:
3151 if self._ascending is None:
3163 if self._ascending is None:
3152 return self._list[0]
3164 return self._list[0]
3153 elif self._ascending:
3165 elif self._ascending:
3154 return self._asclist[0]
3166 return self._asclist[0]
3155 else:
3167 else:
3156 return self._asclist[-1]
3168 return self._asclist[-1]
3157 return None
3169 return None
3158
3170
3159 def last(self):
3171 def last(self):
3160 if self:
3172 if self:
3161 if self._ascending is None:
3173 if self._ascending is None:
3162 return self._list[-1]
3174 return self._list[-1]
3163 elif self._ascending:
3175 elif self._ascending:
3164 return self._asclist[-1]
3176 return self._asclist[-1]
3165 else:
3177 else:
3166 return self._asclist[0]
3178 return self._asclist[0]
3167 return None
3179 return None
3168
3180
3169 def __repr__(self):
3181 def __repr__(self):
3170 d = {None: '', False: '-', True: '+'}[self._ascending]
3182 d = {None: '', False: '-', True: '+'}[self._ascending]
3171 s = _formatsetrepr(self._datarepr)
3183 s = _formatsetrepr(self._datarepr)
3172 if not s:
3184 if not s:
3173 l = self._list
3185 l = self._list
3174 # if _list has been built from a set, it might have a different
3186 # if _list has been built from a set, it might have a different
3175 # order from one python implementation to another.
3187 # order from one python implementation to another.
3176 # We fallback to the sorted version for a stable output.
3188 # We fallback to the sorted version for a stable output.
3177 if self._ascending is not None:
3189 if self._ascending is not None:
3178 l = self._asclist
3190 l = self._asclist
3179 s = repr(l)
3191 s = repr(l)
3180 return '<%s%s %s>' % (type(self).__name__, d, s)
3192 return '<%s%s %s>' % (type(self).__name__, d, s)
3181
3193
3182 class filteredset(abstractsmartset):
3194 class filteredset(abstractsmartset):
3183 """Duck type for baseset class which iterates lazily over the revisions in
3195 """Duck type for baseset class which iterates lazily over the revisions in
3184 the subset and contains a function which tests for membership in the
3196 the subset and contains a function which tests for membership in the
3185 revset
3197 revset
3186 """
3198 """
3187 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3199 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3188 """
3200 """
3189 condition: a function that decide whether a revision in the subset
3201 condition: a function that decide whether a revision in the subset
3190 belongs to the revset or not.
3202 belongs to the revset or not.
3191 condrepr: a tuple of (format, obj, ...), a function or an object that
3203 condrepr: a tuple of (format, obj, ...), a function or an object that
3192 provides a printable representation of the given condition.
3204 provides a printable representation of the given condition.
3193 """
3205 """
3194 self._subset = subset
3206 self._subset = subset
3195 self._condition = condition
3207 self._condition = condition
3196 self._condrepr = condrepr
3208 self._condrepr = condrepr
3197
3209
3198 def __contains__(self, x):
3210 def __contains__(self, x):
3199 return x in self._subset and self._condition(x)
3211 return x in self._subset and self._condition(x)
3200
3212
3201 def __iter__(self):
3213 def __iter__(self):
3202 return self._iterfilter(self._subset)
3214 return self._iterfilter(self._subset)
3203
3215
3204 def _iterfilter(self, it):
3216 def _iterfilter(self, it):
3205 cond = self._condition
3217 cond = self._condition
3206 for x in it:
3218 for x in it:
3207 if cond(x):
3219 if cond(x):
3208 yield x
3220 yield x
3209
3221
3210 @property
3222 @property
3211 def fastasc(self):
3223 def fastasc(self):
3212 it = self._subset.fastasc
3224 it = self._subset.fastasc
3213 if it is None:
3225 if it is None:
3214 return None
3226 return None
3215 return lambda: self._iterfilter(it())
3227 return lambda: self._iterfilter(it())
3216
3228
3217 @property
3229 @property
3218 def fastdesc(self):
3230 def fastdesc(self):
3219 it = self._subset.fastdesc
3231 it = self._subset.fastdesc
3220 if it is None:
3232 if it is None:
3221 return None
3233 return None
3222 return lambda: self._iterfilter(it())
3234 return lambda: self._iterfilter(it())
3223
3235
3224 def __nonzero__(self):
3236 def __nonzero__(self):
3225 fast = None
3237 fast = None
3226 candidates = [self.fastasc if self.isascending() else None,
3238 candidates = [self.fastasc if self.isascending() else None,
3227 self.fastdesc if self.isdescending() else None,
3239 self.fastdesc if self.isdescending() else None,
3228 self.fastasc,
3240 self.fastasc,
3229 self.fastdesc]
3241 self.fastdesc]
3230 for candidate in candidates:
3242 for candidate in candidates:
3231 if candidate is not None:
3243 if candidate is not None:
3232 fast = candidate
3244 fast = candidate
3233 break
3245 break
3234
3246
3235 if fast is not None:
3247 if fast is not None:
3236 it = fast()
3248 it = fast()
3237 else:
3249 else:
3238 it = self
3250 it = self
3239
3251
3240 for r in it:
3252 for r in it:
3241 return True
3253 return True
3242 return False
3254 return False
3243
3255
3244 def __len__(self):
3256 def __len__(self):
3245 # Basic implementation to be changed in future patches.
3257 # Basic implementation to be changed in future patches.
3246 # until this gets improved, we use generator expression
3258 # until this gets improved, we use generator expression
3247 # here, since list comprehensions are free to call __len__ again
3259 # here, since list comprehensions are free to call __len__ again
3248 # causing infinite recursion
3260 # causing infinite recursion
3249 l = baseset(r for r in self)
3261 l = baseset(r for r in self)
3250 return len(l)
3262 return len(l)
3251
3263
3252 def sort(self, reverse=False):
3264 def sort(self, reverse=False):
3253 self._subset.sort(reverse=reverse)
3265 self._subset.sort(reverse=reverse)
3254
3266
3255 def reverse(self):
3267 def reverse(self):
3256 self._subset.reverse()
3268 self._subset.reverse()
3257
3269
3258 def isascending(self):
3270 def isascending(self):
3259 return self._subset.isascending()
3271 return self._subset.isascending()
3260
3272
3261 def isdescending(self):
3273 def isdescending(self):
3262 return self._subset.isdescending()
3274 return self._subset.isdescending()
3263
3275
3264 def istopo(self):
3276 def istopo(self):
3265 return self._subset.istopo()
3277 return self._subset.istopo()
3266
3278
3267 def first(self):
3279 def first(self):
3268 for x in self:
3280 for x in self:
3269 return x
3281 return x
3270 return None
3282 return None
3271
3283
3272 def last(self):
3284 def last(self):
3273 it = None
3285 it = None
3274 if self.isascending():
3286 if self.isascending():
3275 it = self.fastdesc
3287 it = self.fastdesc
3276 elif self.isdescending():
3288 elif self.isdescending():
3277 it = self.fastasc
3289 it = self.fastasc
3278 if it is not None:
3290 if it is not None:
3279 for x in it():
3291 for x in it():
3280 return x
3292 return x
3281 return None #empty case
3293 return None #empty case
3282 else:
3294 else:
3283 x = None
3295 x = None
3284 for x in self:
3296 for x in self:
3285 pass
3297 pass
3286 return x
3298 return x
3287
3299
3288 def __repr__(self):
3300 def __repr__(self):
3289 xs = [repr(self._subset)]
3301 xs = [repr(self._subset)]
3290 s = _formatsetrepr(self._condrepr)
3302 s = _formatsetrepr(self._condrepr)
3291 if s:
3303 if s:
3292 xs.append(s)
3304 xs.append(s)
3293 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3305 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3294
3306
3295 def _iterordered(ascending, iter1, iter2):
3307 def _iterordered(ascending, iter1, iter2):
3296 """produce an ordered iteration from two iterators with the same order
3308 """produce an ordered iteration from two iterators with the same order
3297
3309
3298 The ascending is used to indicated the iteration direction.
3310 The ascending is used to indicated the iteration direction.
3299 """
3311 """
3300 choice = max
3312 choice = max
3301 if ascending:
3313 if ascending:
3302 choice = min
3314 choice = min
3303
3315
3304 val1 = None
3316 val1 = None
3305 val2 = None
3317 val2 = None
3306 try:
3318 try:
3307 # Consume both iterators in an ordered way until one is empty
3319 # Consume both iterators in an ordered way until one is empty
3308 while True:
3320 while True:
3309 if val1 is None:
3321 if val1 is None:
3310 val1 = next(iter1)
3322 val1 = next(iter1)
3311 if val2 is None:
3323 if val2 is None:
3312 val2 = next(iter2)
3324 val2 = next(iter2)
3313 n = choice(val1, val2)
3325 n = choice(val1, val2)
3314 yield n
3326 yield n
3315 if val1 == n:
3327 if val1 == n:
3316 val1 = None
3328 val1 = None
3317 if val2 == n:
3329 if val2 == n:
3318 val2 = None
3330 val2 = None
3319 except StopIteration:
3331 except StopIteration:
3320 # Flush any remaining values and consume the other one
3332 # Flush any remaining values and consume the other one
3321 it = iter2
3333 it = iter2
3322 if val1 is not None:
3334 if val1 is not None:
3323 yield val1
3335 yield val1
3324 it = iter1
3336 it = iter1
3325 elif val2 is not None:
3337 elif val2 is not None:
3326 # might have been equality and both are empty
3338 # might have been equality and both are empty
3327 yield val2
3339 yield val2
3328 for val in it:
3340 for val in it:
3329 yield val
3341 yield val
3330
3342
3331 class addset(abstractsmartset):
3343 class addset(abstractsmartset):
3332 """Represent the addition of two sets
3344 """Represent the addition of two sets
3333
3345
3334 Wrapper structure for lazily adding two structures without losing much
3346 Wrapper structure for lazily adding two structures without losing much
3335 performance on the __contains__ method
3347 performance on the __contains__ method
3336
3348
3337 If the ascending attribute is set, that means the two structures are
3349 If the ascending attribute is set, that means the two structures are
3338 ordered in either an ascending or descending way. Therefore, we can add
3350 ordered in either an ascending or descending way. Therefore, we can add
3339 them maintaining the order by iterating over both at the same time
3351 them maintaining the order by iterating over both at the same time
3340
3352
3341 >>> xs = baseset([0, 3, 2])
3353 >>> xs = baseset([0, 3, 2])
3342 >>> ys = baseset([5, 2, 4])
3354 >>> ys = baseset([5, 2, 4])
3343
3355
3344 >>> rs = addset(xs, ys)
3356 >>> rs = addset(xs, ys)
3345 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3357 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3346 (True, True, False, True, 0, 4)
3358 (True, True, False, True, 0, 4)
3347 >>> rs = addset(xs, baseset([]))
3359 >>> rs = addset(xs, baseset([]))
3348 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3360 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3349 (True, True, False, 0, 2)
3361 (True, True, False, 0, 2)
3350 >>> rs = addset(baseset([]), baseset([]))
3362 >>> rs = addset(baseset([]), baseset([]))
3351 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3363 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3352 (False, False, None, None)
3364 (False, False, None, None)
3353
3365
3354 iterate unsorted:
3366 iterate unsorted:
3355 >>> rs = addset(xs, ys)
3367 >>> rs = addset(xs, ys)
3356 >>> # (use generator because pypy could call len())
3368 >>> # (use generator because pypy could call len())
3357 >>> list(x for x in rs) # without _genlist
3369 >>> list(x for x in rs) # without _genlist
3358 [0, 3, 2, 5, 4]
3370 [0, 3, 2, 5, 4]
3359 >>> assert not rs._genlist
3371 >>> assert not rs._genlist
3360 >>> len(rs)
3372 >>> len(rs)
3361 5
3373 5
3362 >>> [x for x in rs] # with _genlist
3374 >>> [x for x in rs] # with _genlist
3363 [0, 3, 2, 5, 4]
3375 [0, 3, 2, 5, 4]
3364 >>> assert rs._genlist
3376 >>> assert rs._genlist
3365
3377
3366 iterate ascending:
3378 iterate ascending:
3367 >>> rs = addset(xs, ys, ascending=True)
3379 >>> rs = addset(xs, ys, ascending=True)
3368 >>> # (use generator because pypy could call len())
3380 >>> # (use generator because pypy could call len())
3369 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3381 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3370 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3382 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3371 >>> assert not rs._asclist
3383 >>> assert not rs._asclist
3372 >>> len(rs)
3384 >>> len(rs)
3373 5
3385 5
3374 >>> [x for x in rs], [x for x in rs.fastasc()]
3386 >>> [x for x in rs], [x for x in rs.fastasc()]
3375 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3387 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3376 >>> assert rs._asclist
3388 >>> assert rs._asclist
3377
3389
3378 iterate descending:
3390 iterate descending:
3379 >>> rs = addset(xs, ys, ascending=False)
3391 >>> rs = addset(xs, ys, ascending=False)
3380 >>> # (use generator because pypy could call len())
3392 >>> # (use generator because pypy could call len())
3381 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3393 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3382 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3394 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3383 >>> assert not rs._asclist
3395 >>> assert not rs._asclist
3384 >>> len(rs)
3396 >>> len(rs)
3385 5
3397 5
3386 >>> [x for x in rs], [x for x in rs.fastdesc()]
3398 >>> [x for x in rs], [x for x in rs.fastdesc()]
3387 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3399 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3388 >>> assert rs._asclist
3400 >>> assert rs._asclist
3389
3401
3390 iterate ascending without fastasc:
3402 iterate ascending without fastasc:
3391 >>> rs = addset(xs, generatorset(ys), ascending=True)
3403 >>> rs = addset(xs, generatorset(ys), ascending=True)
3392 >>> assert rs.fastasc is None
3404 >>> assert rs.fastasc is None
3393 >>> [x for x in rs]
3405 >>> [x for x in rs]
3394 [0, 2, 3, 4, 5]
3406 [0, 2, 3, 4, 5]
3395
3407
3396 iterate descending without fastdesc:
3408 iterate descending without fastdesc:
3397 >>> rs = addset(generatorset(xs), ys, ascending=False)
3409 >>> rs = addset(generatorset(xs), ys, ascending=False)
3398 >>> assert rs.fastdesc is None
3410 >>> assert rs.fastdesc is None
3399 >>> [x for x in rs]
3411 >>> [x for x in rs]
3400 [5, 4, 3, 2, 0]
3412 [5, 4, 3, 2, 0]
3401 """
3413 """
3402 def __init__(self, revs1, revs2, ascending=None):
3414 def __init__(self, revs1, revs2, ascending=None):
3403 self._r1 = revs1
3415 self._r1 = revs1
3404 self._r2 = revs2
3416 self._r2 = revs2
3405 self._iter = None
3417 self._iter = None
3406 self._ascending = ascending
3418 self._ascending = ascending
3407 self._genlist = None
3419 self._genlist = None
3408 self._asclist = None
3420 self._asclist = None
3409
3421
3410 def __len__(self):
3422 def __len__(self):
3411 return len(self._list)
3423 return len(self._list)
3412
3424
3413 def __nonzero__(self):
3425 def __nonzero__(self):
3414 return bool(self._r1) or bool(self._r2)
3426 return bool(self._r1) or bool(self._r2)
3415
3427
3416 @util.propertycache
3428 @util.propertycache
3417 def _list(self):
3429 def _list(self):
3418 if not self._genlist:
3430 if not self._genlist:
3419 self._genlist = baseset(iter(self))
3431 self._genlist = baseset(iter(self))
3420 return self._genlist
3432 return self._genlist
3421
3433
3422 def __iter__(self):
3434 def __iter__(self):
3423 """Iterate over both collections without repeating elements
3435 """Iterate over both collections without repeating elements
3424
3436
3425 If the ascending attribute is not set, iterate over the first one and
3437 If the ascending attribute is not set, iterate over the first one and
3426 then over the second one checking for membership on the first one so we
3438 then over the second one checking for membership on the first one so we
3427 dont yield any duplicates.
3439 dont yield any duplicates.
3428
3440
3429 If the ascending attribute is set, iterate over both collections at the
3441 If the ascending attribute is set, iterate over both collections at the
3430 same time, yielding only one value at a time in the given order.
3442 same time, yielding only one value at a time in the given order.
3431 """
3443 """
3432 if self._ascending is None:
3444 if self._ascending is None:
3433 if self._genlist:
3445 if self._genlist:
3434 return iter(self._genlist)
3446 return iter(self._genlist)
3435 def arbitraryordergen():
3447 def arbitraryordergen():
3436 for r in self._r1:
3448 for r in self._r1:
3437 yield r
3449 yield r
3438 inr1 = self._r1.__contains__
3450 inr1 = self._r1.__contains__
3439 for r in self._r2:
3451 for r in self._r2:
3440 if not inr1(r):
3452 if not inr1(r):
3441 yield r
3453 yield r
3442 return arbitraryordergen()
3454 return arbitraryordergen()
3443 # try to use our own fast iterator if it exists
3455 # try to use our own fast iterator if it exists
3444 self._trysetasclist()
3456 self._trysetasclist()
3445 if self._ascending:
3457 if self._ascending:
3446 attr = 'fastasc'
3458 attr = 'fastasc'
3447 else:
3459 else:
3448 attr = 'fastdesc'
3460 attr = 'fastdesc'
3449 it = getattr(self, attr)
3461 it = getattr(self, attr)
3450 if it is not None:
3462 if it is not None:
3451 return it()
3463 return it()
3452 # maybe half of the component supports fast
3464 # maybe half of the component supports fast
3453 # get iterator for _r1
3465 # get iterator for _r1
3454 iter1 = getattr(self._r1, attr)
3466 iter1 = getattr(self._r1, attr)
3455 if iter1 is None:
3467 if iter1 is None:
3456 # let's avoid side effect (not sure it matters)
3468 # let's avoid side effect (not sure it matters)
3457 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3469 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3458 else:
3470 else:
3459 iter1 = iter1()
3471 iter1 = iter1()
3460 # get iterator for _r2
3472 # get iterator for _r2
3461 iter2 = getattr(self._r2, attr)
3473 iter2 = getattr(self._r2, attr)
3462 if iter2 is None:
3474 if iter2 is None:
3463 # let's avoid side effect (not sure it matters)
3475 # let's avoid side effect (not sure it matters)
3464 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3476 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3465 else:
3477 else:
3466 iter2 = iter2()
3478 iter2 = iter2()
3467 return _iterordered(self._ascending, iter1, iter2)
3479 return _iterordered(self._ascending, iter1, iter2)
3468
3480
3469 def _trysetasclist(self):
3481 def _trysetasclist(self):
3470 """populate the _asclist attribute if possible and necessary"""
3482 """populate the _asclist attribute if possible and necessary"""
3471 if self._genlist is not None and self._asclist is None:
3483 if self._genlist is not None and self._asclist is None:
3472 self._asclist = sorted(self._genlist)
3484 self._asclist = sorted(self._genlist)
3473
3485
3474 @property
3486 @property
3475 def fastasc(self):
3487 def fastasc(self):
3476 self._trysetasclist()
3488 self._trysetasclist()
3477 if self._asclist is not None:
3489 if self._asclist is not None:
3478 return self._asclist.__iter__
3490 return self._asclist.__iter__
3479 iter1 = self._r1.fastasc
3491 iter1 = self._r1.fastasc
3480 iter2 = self._r2.fastasc
3492 iter2 = self._r2.fastasc
3481 if None in (iter1, iter2):
3493 if None in (iter1, iter2):
3482 return None
3494 return None
3483 return lambda: _iterordered(True, iter1(), iter2())
3495 return lambda: _iterordered(True, iter1(), iter2())
3484
3496
3485 @property
3497 @property
3486 def fastdesc(self):
3498 def fastdesc(self):
3487 self._trysetasclist()
3499 self._trysetasclist()
3488 if self._asclist is not None:
3500 if self._asclist is not None:
3489 return self._asclist.__reversed__
3501 return self._asclist.__reversed__
3490 iter1 = self._r1.fastdesc
3502 iter1 = self._r1.fastdesc
3491 iter2 = self._r2.fastdesc
3503 iter2 = self._r2.fastdesc
3492 if None in (iter1, iter2):
3504 if None in (iter1, iter2):
3493 return None
3505 return None
3494 return lambda: _iterordered(False, iter1(), iter2())
3506 return lambda: _iterordered(False, iter1(), iter2())
3495
3507
3496 def __contains__(self, x):
3508 def __contains__(self, x):
3497 return x in self._r1 or x in self._r2
3509 return x in self._r1 or x in self._r2
3498
3510
3499 def sort(self, reverse=False):
3511 def sort(self, reverse=False):
3500 """Sort the added set
3512 """Sort the added set
3501
3513
3502 For this we use the cached list with all the generated values and if we
3514 For this we use the cached list with all the generated values and if we
3503 know they are ascending or descending we can sort them in a smart way.
3515 know they are ascending or descending we can sort them in a smart way.
3504 """
3516 """
3505 self._ascending = not reverse
3517 self._ascending = not reverse
3506
3518
3507 def isascending(self):
3519 def isascending(self):
3508 return self._ascending is not None and self._ascending
3520 return self._ascending is not None and self._ascending
3509
3521
3510 def isdescending(self):
3522 def isdescending(self):
3511 return self._ascending is not None and not self._ascending
3523 return self._ascending is not None and not self._ascending
3512
3524
3513 def istopo(self):
3525 def istopo(self):
3514 # not worth the trouble asserting if the two sets combined are still
3526 # not worth the trouble asserting if the two sets combined are still
3515 # in topographical order. Use the sort() predicate to explicitly sort
3527 # in topographical order. Use the sort() predicate to explicitly sort
3516 # again instead.
3528 # again instead.
3517 return False
3529 return False
3518
3530
3519 def reverse(self):
3531 def reverse(self):
3520 if self._ascending is None:
3532 if self._ascending is None:
3521 self._list.reverse()
3533 self._list.reverse()
3522 else:
3534 else:
3523 self._ascending = not self._ascending
3535 self._ascending = not self._ascending
3524
3536
3525 def first(self):
3537 def first(self):
3526 for x in self:
3538 for x in self:
3527 return x
3539 return x
3528 return None
3540 return None
3529
3541
3530 def last(self):
3542 def last(self):
3531 self.reverse()
3543 self.reverse()
3532 val = self.first()
3544 val = self.first()
3533 self.reverse()
3545 self.reverse()
3534 return val
3546 return val
3535
3547
3536 def __repr__(self):
3548 def __repr__(self):
3537 d = {None: '', False: '-', True: '+'}[self._ascending]
3549 d = {None: '', False: '-', True: '+'}[self._ascending]
3538 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3550 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3539
3551
3540 class generatorset(abstractsmartset):
3552 class generatorset(abstractsmartset):
3541 """Wrap a generator for lazy iteration
3553 """Wrap a generator for lazy iteration
3542
3554
3543 Wrapper structure for generators that provides lazy membership and can
3555 Wrapper structure for generators that provides lazy membership and can
3544 be iterated more than once.
3556 be iterated more than once.
3545 When asked for membership it generates values until either it finds the
3557 When asked for membership it generates values until either it finds the
3546 requested one or has gone through all the elements in the generator
3558 requested one or has gone through all the elements in the generator
3547 """
3559 """
3548 def __init__(self, gen, iterasc=None):
3560 def __init__(self, gen, iterasc=None):
3549 """
3561 """
3550 gen: a generator producing the values for the generatorset.
3562 gen: a generator producing the values for the generatorset.
3551 """
3563 """
3552 self._gen = gen
3564 self._gen = gen
3553 self._asclist = None
3565 self._asclist = None
3554 self._cache = {}
3566 self._cache = {}
3555 self._genlist = []
3567 self._genlist = []
3556 self._finished = False
3568 self._finished = False
3557 self._ascending = True
3569 self._ascending = True
3558 if iterasc is not None:
3570 if iterasc is not None:
3559 if iterasc:
3571 if iterasc:
3560 self.fastasc = self._iterator
3572 self.fastasc = self._iterator
3561 self.__contains__ = self._asccontains
3573 self.__contains__ = self._asccontains
3562 else:
3574 else:
3563 self.fastdesc = self._iterator
3575 self.fastdesc = self._iterator
3564 self.__contains__ = self._desccontains
3576 self.__contains__ = self._desccontains
3565
3577
3566 def __nonzero__(self):
3578 def __nonzero__(self):
3567 # Do not use 'for r in self' because it will enforce the iteration
3579 # Do not use 'for r in self' because it will enforce the iteration
3568 # order (default ascending), possibly unrolling a whole descending
3580 # order (default ascending), possibly unrolling a whole descending
3569 # iterator.
3581 # iterator.
3570 if self._genlist:
3582 if self._genlist:
3571 return True
3583 return True
3572 for r in self._consumegen():
3584 for r in self._consumegen():
3573 return True
3585 return True
3574 return False
3586 return False
3575
3587
3576 def __contains__(self, x):
3588 def __contains__(self, x):
3577 if x in self._cache:
3589 if x in self._cache:
3578 return self._cache[x]
3590 return self._cache[x]
3579
3591
3580 # Use new values only, as existing values would be cached.
3592 # Use new values only, as existing values would be cached.
3581 for l in self._consumegen():
3593 for l in self._consumegen():
3582 if l == x:
3594 if l == x:
3583 return True
3595 return True
3584
3596
3585 self._cache[x] = False
3597 self._cache[x] = False
3586 return False
3598 return False
3587
3599
3588 def _asccontains(self, x):
3600 def _asccontains(self, x):
3589 """version of contains optimised for ascending generator"""
3601 """version of contains optimised for ascending generator"""
3590 if x in self._cache:
3602 if x in self._cache:
3591 return self._cache[x]
3603 return self._cache[x]
3592
3604
3593 # Use new values only, as existing values would be cached.
3605 # Use new values only, as existing values would be cached.
3594 for l in self._consumegen():
3606 for l in self._consumegen():
3595 if l == x:
3607 if l == x:
3596 return True
3608 return True
3597 if l > x:
3609 if l > x:
3598 break
3610 break
3599
3611
3600 self._cache[x] = False
3612 self._cache[x] = False
3601 return False
3613 return False
3602
3614
3603 def _desccontains(self, x):
3615 def _desccontains(self, x):
3604 """version of contains optimised for descending generator"""
3616 """version of contains optimised for descending generator"""
3605 if x in self._cache:
3617 if x in self._cache:
3606 return self._cache[x]
3618 return self._cache[x]
3607
3619
3608 # Use new values only, as existing values would be cached.
3620 # Use new values only, as existing values would be cached.
3609 for l in self._consumegen():
3621 for l in self._consumegen():
3610 if l == x:
3622 if l == x:
3611 return True
3623 return True
3612 if l < x:
3624 if l < x:
3613 break
3625 break
3614
3626
3615 self._cache[x] = False
3627 self._cache[x] = False
3616 return False
3628 return False
3617
3629
3618 def __iter__(self):
3630 def __iter__(self):
3619 if self._ascending:
3631 if self._ascending:
3620 it = self.fastasc
3632 it = self.fastasc
3621 else:
3633 else:
3622 it = self.fastdesc
3634 it = self.fastdesc
3623 if it is not None:
3635 if it is not None:
3624 return it()
3636 return it()
3625 # we need to consume the iterator
3637 # we need to consume the iterator
3626 for x in self._consumegen():
3638 for x in self._consumegen():
3627 pass
3639 pass
3628 # recall the same code
3640 # recall the same code
3629 return iter(self)
3641 return iter(self)
3630
3642
3631 def _iterator(self):
3643 def _iterator(self):
3632 if self._finished:
3644 if self._finished:
3633 return iter(self._genlist)
3645 return iter(self._genlist)
3634
3646
3635 # We have to use this complex iteration strategy to allow multiple
3647 # We have to use this complex iteration strategy to allow multiple
3636 # iterations at the same time. We need to be able to catch revision
3648 # iterations at the same time. We need to be able to catch revision
3637 # removed from _consumegen and added to genlist in another instance.
3649 # removed from _consumegen and added to genlist in another instance.
3638 #
3650 #
3639 # Getting rid of it would provide an about 15% speed up on this
3651 # Getting rid of it would provide an about 15% speed up on this
3640 # iteration.
3652 # iteration.
3641 genlist = self._genlist
3653 genlist = self._genlist
3642 nextrev = self._consumegen().next
3654 nextrev = self._consumegen().next
3643 _len = len # cache global lookup
3655 _len = len # cache global lookup
3644 def gen():
3656 def gen():
3645 i = 0
3657 i = 0
3646 while True:
3658 while True:
3647 if i < _len(genlist):
3659 if i < _len(genlist):
3648 yield genlist[i]
3660 yield genlist[i]
3649 else:
3661 else:
3650 yield nextrev()
3662 yield nextrev()
3651 i += 1
3663 i += 1
3652 return gen()
3664 return gen()
3653
3665
3654 def _consumegen(self):
3666 def _consumegen(self):
3655 cache = self._cache
3667 cache = self._cache
3656 genlist = self._genlist.append
3668 genlist = self._genlist.append
3657 for item in self._gen:
3669 for item in self._gen:
3658 cache[item] = True
3670 cache[item] = True
3659 genlist(item)
3671 genlist(item)
3660 yield item
3672 yield item
3661 if not self._finished:
3673 if not self._finished:
3662 self._finished = True
3674 self._finished = True
3663 asc = self._genlist[:]
3675 asc = self._genlist[:]
3664 asc.sort()
3676 asc.sort()
3665 self._asclist = asc
3677 self._asclist = asc
3666 self.fastasc = asc.__iter__
3678 self.fastasc = asc.__iter__
3667 self.fastdesc = asc.__reversed__
3679 self.fastdesc = asc.__reversed__
3668
3680
3669 def __len__(self):
3681 def __len__(self):
3670 for x in self._consumegen():
3682 for x in self._consumegen():
3671 pass
3683 pass
3672 return len(self._genlist)
3684 return len(self._genlist)
3673
3685
3674 def sort(self, reverse=False):
3686 def sort(self, reverse=False):
3675 self._ascending = not reverse
3687 self._ascending = not reverse
3676
3688
3677 def reverse(self):
3689 def reverse(self):
3678 self._ascending = not self._ascending
3690 self._ascending = not self._ascending
3679
3691
3680 def isascending(self):
3692 def isascending(self):
3681 return self._ascending
3693 return self._ascending
3682
3694
3683 def isdescending(self):
3695 def isdescending(self):
3684 return not self._ascending
3696 return not self._ascending
3685
3697
3686 def istopo(self):
3698 def istopo(self):
3687 # not worth the trouble asserting if the two sets combined are still
3699 # not worth the trouble asserting if the two sets combined are still
3688 # in topographical order. Use the sort() predicate to explicitly sort
3700 # in topographical order. Use the sort() predicate to explicitly sort
3689 # again instead.
3701 # again instead.
3690 return False
3702 return False
3691
3703
3692 def first(self):
3704 def first(self):
3693 if self._ascending:
3705 if self._ascending:
3694 it = self.fastasc
3706 it = self.fastasc
3695 else:
3707 else:
3696 it = self.fastdesc
3708 it = self.fastdesc
3697 if it is None:
3709 if it is None:
3698 # we need to consume all and try again
3710 # we need to consume all and try again
3699 for x in self._consumegen():
3711 for x in self._consumegen():
3700 pass
3712 pass
3701 return self.first()
3713 return self.first()
3702 return next(it(), None)
3714 return next(it(), None)
3703
3715
3704 def last(self):
3716 def last(self):
3705 if self._ascending:
3717 if self._ascending:
3706 it = self.fastdesc
3718 it = self.fastdesc
3707 else:
3719 else:
3708 it = self.fastasc
3720 it = self.fastasc
3709 if it is None:
3721 if it is None:
3710 # we need to consume all and try again
3722 # we need to consume all and try again
3711 for x in self._consumegen():
3723 for x in self._consumegen():
3712 pass
3724 pass
3713 return self.first()
3725 return self.first()
3714 return next(it(), None)
3726 return next(it(), None)
3715
3727
3716 def __repr__(self):
3728 def __repr__(self):
3717 d = {False: '-', True: '+'}[self._ascending]
3729 d = {False: '-', True: '+'}[self._ascending]
3718 return '<%s%s>' % (type(self).__name__, d)
3730 return '<%s%s>' % (type(self).__name__, d)
3719
3731
3720 class spanset(abstractsmartset):
3732 class spanset(abstractsmartset):
3721 """Duck type for baseset class which represents a range of revisions and
3733 """Duck type for baseset class which represents a range of revisions and
3722 can work lazily and without having all the range in memory
3734 can work lazily and without having all the range in memory
3723
3735
3724 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3736 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3725 notable points:
3737 notable points:
3726 - when x < y it will be automatically descending,
3738 - when x < y it will be automatically descending,
3727 - revision filtered with this repoview will be skipped.
3739 - revision filtered with this repoview will be skipped.
3728
3740
3729 """
3741 """
3730 def __init__(self, repo, start=0, end=None):
3742 def __init__(self, repo, start=0, end=None):
3731 """
3743 """
3732 start: first revision included the set
3744 start: first revision included the set
3733 (default to 0)
3745 (default to 0)
3734 end: first revision excluded (last+1)
3746 end: first revision excluded (last+1)
3735 (default to len(repo)
3747 (default to len(repo)
3736
3748
3737 Spanset will be descending if `end` < `start`.
3749 Spanset will be descending if `end` < `start`.
3738 """
3750 """
3739 if end is None:
3751 if end is None:
3740 end = len(repo)
3752 end = len(repo)
3741 self._ascending = start <= end
3753 self._ascending = start <= end
3742 if not self._ascending:
3754 if not self._ascending:
3743 start, end = end + 1, start +1
3755 start, end = end + 1, start +1
3744 self._start = start
3756 self._start = start
3745 self._end = end
3757 self._end = end
3746 self._hiddenrevs = repo.changelog.filteredrevs
3758 self._hiddenrevs = repo.changelog.filteredrevs
3747
3759
3748 def sort(self, reverse=False):
3760 def sort(self, reverse=False):
3749 self._ascending = not reverse
3761 self._ascending = not reverse
3750
3762
3751 def reverse(self):
3763 def reverse(self):
3752 self._ascending = not self._ascending
3764 self._ascending = not self._ascending
3753
3765
3754 def istopo(self):
3766 def istopo(self):
3755 # not worth the trouble asserting if the two sets combined are still
3767 # not worth the trouble asserting if the two sets combined are still
3756 # in topographical order. Use the sort() predicate to explicitly sort
3768 # in topographical order. Use the sort() predicate to explicitly sort
3757 # again instead.
3769 # again instead.
3758 return False
3770 return False
3759
3771
3760 def _iterfilter(self, iterrange):
3772 def _iterfilter(self, iterrange):
3761 s = self._hiddenrevs
3773 s = self._hiddenrevs
3762 for r in iterrange:
3774 for r in iterrange:
3763 if r not in s:
3775 if r not in s:
3764 yield r
3776 yield r
3765
3777
3766 def __iter__(self):
3778 def __iter__(self):
3767 if self._ascending:
3779 if self._ascending:
3768 return self.fastasc()
3780 return self.fastasc()
3769 else:
3781 else:
3770 return self.fastdesc()
3782 return self.fastdesc()
3771
3783
3772 def fastasc(self):
3784 def fastasc(self):
3773 iterrange = xrange(self._start, self._end)
3785 iterrange = xrange(self._start, self._end)
3774 if self._hiddenrevs:
3786 if self._hiddenrevs:
3775 return self._iterfilter(iterrange)
3787 return self._iterfilter(iterrange)
3776 return iter(iterrange)
3788 return iter(iterrange)
3777
3789
3778 def fastdesc(self):
3790 def fastdesc(self):
3779 iterrange = xrange(self._end - 1, self._start - 1, -1)
3791 iterrange = xrange(self._end - 1, self._start - 1, -1)
3780 if self._hiddenrevs:
3792 if self._hiddenrevs:
3781 return self._iterfilter(iterrange)
3793 return self._iterfilter(iterrange)
3782 return iter(iterrange)
3794 return iter(iterrange)
3783
3795
3784 def __contains__(self, rev):
3796 def __contains__(self, rev):
3785 hidden = self._hiddenrevs
3797 hidden = self._hiddenrevs
3786 return ((self._start <= rev < self._end)
3798 return ((self._start <= rev < self._end)
3787 and not (hidden and rev in hidden))
3799 and not (hidden and rev in hidden))
3788
3800
3789 def __nonzero__(self):
3801 def __nonzero__(self):
3790 for r in self:
3802 for r in self:
3791 return True
3803 return True
3792 return False
3804 return False
3793
3805
3794 def __len__(self):
3806 def __len__(self):
3795 if not self._hiddenrevs:
3807 if not self._hiddenrevs:
3796 return abs(self._end - self._start)
3808 return abs(self._end - self._start)
3797 else:
3809 else:
3798 count = 0
3810 count = 0
3799 start = self._start
3811 start = self._start
3800 end = self._end
3812 end = self._end
3801 for rev in self._hiddenrevs:
3813 for rev in self._hiddenrevs:
3802 if (end < rev <= start) or (start <= rev < end):
3814 if (end < rev <= start) or (start <= rev < end):
3803 count += 1
3815 count += 1
3804 return abs(self._end - self._start) - count
3816 return abs(self._end - self._start) - count
3805
3817
3806 def isascending(self):
3818 def isascending(self):
3807 return self._ascending
3819 return self._ascending
3808
3820
3809 def isdescending(self):
3821 def isdescending(self):
3810 return not self._ascending
3822 return not self._ascending
3811
3823
3812 def first(self):
3824 def first(self):
3813 if self._ascending:
3825 if self._ascending:
3814 it = self.fastasc
3826 it = self.fastasc
3815 else:
3827 else:
3816 it = self.fastdesc
3828 it = self.fastdesc
3817 for x in it():
3829 for x in it():
3818 return x
3830 return x
3819 return None
3831 return None
3820
3832
3821 def last(self):
3833 def last(self):
3822 if self._ascending:
3834 if self._ascending:
3823 it = self.fastdesc
3835 it = self.fastdesc
3824 else:
3836 else:
3825 it = self.fastasc
3837 it = self.fastasc
3826 for x in it():
3838 for x in it():
3827 return x
3839 return x
3828 return None
3840 return None
3829
3841
3830 def __repr__(self):
3842 def __repr__(self):
3831 d = {False: '-', True: '+'}[self._ascending]
3843 d = {False: '-', True: '+'}[self._ascending]
3832 return '<%s%s %d:%d>' % (type(self).__name__, d,
3844 return '<%s%s %d:%d>' % (type(self).__name__, d,
3833 self._start, self._end - 1)
3845 self._start, self._end - 1)
3834
3846
3835 class fullreposet(spanset):
3847 class fullreposet(spanset):
3836 """a set containing all revisions in the repo
3848 """a set containing all revisions in the repo
3837
3849
3838 This class exists to host special optimization and magic to handle virtual
3850 This class exists to host special optimization and magic to handle virtual
3839 revisions such as "null".
3851 revisions such as "null".
3840 """
3852 """
3841
3853
3842 def __init__(self, repo):
3854 def __init__(self, repo):
3843 super(fullreposet, self).__init__(repo)
3855 super(fullreposet, self).__init__(repo)
3844
3856
3845 def __and__(self, other):
3857 def __and__(self, other):
3846 """As self contains the whole repo, all of the other set should also be
3858 """As self contains the whole repo, all of the other set should also be
3847 in self. Therefore `self & other = other`.
3859 in self. Therefore `self & other = other`.
3848
3860
3849 This boldly assumes the other contains valid revs only.
3861 This boldly assumes the other contains valid revs only.
3850 """
3862 """
3851 # other not a smartset, make is so
3863 # other not a smartset, make is so
3852 if not util.safehasattr(other, 'isascending'):
3864 if not util.safehasattr(other, 'isascending'):
3853 # filter out hidden revision
3865 # filter out hidden revision
3854 # (this boldly assumes all smartset are pure)
3866 # (this boldly assumes all smartset are pure)
3855 #
3867 #
3856 # `other` was used with "&", let's assume this is a set like
3868 # `other` was used with "&", let's assume this is a set like
3857 # object.
3869 # object.
3858 other = baseset(other - self._hiddenrevs)
3870 other = baseset(other - self._hiddenrevs)
3859
3871
3860 other.sort(reverse=self.isdescending())
3872 other.sort(reverse=self.isdescending())
3861 return other
3873 return other
3862
3874
3863 def prettyformatset(revs):
3875 def prettyformatset(revs):
3864 lines = []
3876 lines = []
3865 rs = repr(revs)
3877 rs = repr(revs)
3866 p = 0
3878 p = 0
3867 while p < len(rs):
3879 while p < len(rs):
3868 q = rs.find('<', p + 1)
3880 q = rs.find('<', p + 1)
3869 if q < 0:
3881 if q < 0:
3870 q = len(rs)
3882 q = len(rs)
3871 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3883 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3872 assert l >= 0
3884 assert l >= 0
3873 lines.append((l, rs[p:q].rstrip()))
3885 lines.append((l, rs[p:q].rstrip()))
3874 p = q
3886 p = q
3875 return '\n'.join(' ' * l + s for l, s in lines)
3887 return '\n'.join(' ' * l + s for l, s in lines)
3876
3888
3877 def loadpredicate(ui, extname, registrarobj):
3889 def loadpredicate(ui, extname, registrarobj):
3878 """Load revset predicates from specified registrarobj
3890 """Load revset predicates from specified registrarobj
3879 """
3891 """
3880 for name, func in registrarobj._table.iteritems():
3892 for name, func in registrarobj._table.iteritems():
3881 symbols[name] = func
3893 symbols[name] = func
3882 if func._safe:
3894 if func._safe:
3883 safesymbols.add(name)
3895 safesymbols.add(name)
3884
3896
3885 # load built-in predicates explicitly to setup safesymbols
3897 # load built-in predicates explicitly to setup safesymbols
3886 loadpredicate(None, None, predicate)
3898 loadpredicate(None, None, predicate)
3887
3899
3888 # tell hggettext to extract docstrings from these functions:
3900 # tell hggettext to extract docstrings from these functions:
3889 i18nfunctions = symbols.values()
3901 i18nfunctions = symbols.values()
@@ -1,3654 +1,3654 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat > testrevset.py << EOF
3 $ cat > testrevset.py << EOF
4 > import mercurial.revset
4 > import mercurial.revset
5 >
5 >
6 > baseset = mercurial.revset.baseset
6 > baseset = mercurial.revset.baseset
7 >
7 >
8 > def r3232(repo, subset, x):
8 > def r3232(repo, subset, x):
9 > """"simple revset that return [3,2,3,2]
9 > """"simple revset that return [3,2,3,2]
10 >
10 >
11 > revisions duplicated on purpose.
11 > revisions duplicated on purpose.
12 > """
12 > """
13 > if 3 not in subset:
13 > if 3 not in subset:
14 > if 2 in subset:
14 > if 2 in subset:
15 > return baseset([2,2])
15 > return baseset([2,2])
16 > return baseset()
16 > return baseset()
17 > return baseset([3,3,2,2])
17 > return baseset([3,3,2,2])
18 >
18 >
19 > mercurial.revset.symbols['r3232'] = r3232
19 > mercurial.revset.symbols['r3232'] = r3232
20 > EOF
20 > EOF
21 $ cat >> $HGRCPATH << EOF
21 $ cat >> $HGRCPATH << EOF
22 > [extensions]
22 > [extensions]
23 > testrevset=$TESTTMP/testrevset.py
23 > testrevset=$TESTTMP/testrevset.py
24 > EOF
24 > EOF
25
25
26 $ try() {
26 $ try() {
27 > hg debugrevspec --debug "$@"
27 > hg debugrevspec --debug "$@"
28 > }
28 > }
29
29
30 $ log() {
30 $ log() {
31 > hg log --template '{rev}\n' -r "$1"
31 > hg log --template '{rev}\n' -r "$1"
32 > }
32 > }
33
33
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
34 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 these predicates use '\0' as a separator:
35 these predicates use '\0' as a separator:
36
36
37 $ cat <<EOF > debugrevlistspec.py
37 $ cat <<EOF > debugrevlistspec.py
38 > from __future__ import absolute_import
38 > from __future__ import absolute_import
39 > from mercurial import (
39 > from mercurial import (
40 > cmdutil,
40 > cmdutil,
41 > node as nodemod,
41 > node as nodemod,
42 > revset,
42 > revset,
43 > )
43 > )
44 > cmdtable = {}
44 > cmdtable = {}
45 > command = cmdutil.command(cmdtable)
45 > command = cmdutil.command(cmdtable)
46 > @command('debugrevlistspec',
46 > @command('debugrevlistspec',
47 > [('', 'optimize', None, 'print parsed tree after optimizing'),
47 > [('', 'optimize', None, 'print parsed tree after optimizing'),
48 > ('', 'bin', None, 'unhexlify arguments')])
48 > ('', 'bin', None, 'unhexlify arguments')])
49 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
49 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
50 > if opts['bin']:
50 > if opts['bin']:
51 > args = map(nodemod.bin, args)
51 > args = map(nodemod.bin, args)
52 > expr = revset.formatspec(fmt, list(args))
52 > expr = revset.formatspec(fmt, list(args))
53 > if ui.verbose:
53 > if ui.verbose:
54 > tree = revset.parse(expr, lookup=repo.__contains__)
54 > tree = revset.parse(expr, lookup=repo.__contains__)
55 > ui.note(revset.prettyformat(tree), "\n")
55 > ui.note(revset.prettyformat(tree), "\n")
56 > if opts["optimize"]:
56 > if opts["optimize"]:
57 > opttree = revset.optimize(revset.analyze(tree))
57 > opttree = revset.optimize(revset.analyze(tree))
58 > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n")
58 > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n")
59 > func = revset.match(ui, expr, repo)
59 > func = revset.match(ui, expr, repo)
60 > revs = func(repo)
60 > revs = func(repo)
61 > if ui.verbose:
61 > if ui.verbose:
62 > ui.note("* set:\n", revset.prettyformatset(revs), "\n")
62 > ui.note("* set:\n", revset.prettyformatset(revs), "\n")
63 > for c in revs:
63 > for c in revs:
64 > ui.write("%s\n" % c)
64 > ui.write("%s\n" % c)
65 > EOF
65 > EOF
66 $ cat <<EOF >> $HGRCPATH
66 $ cat <<EOF >> $HGRCPATH
67 > [extensions]
67 > [extensions]
68 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
68 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
69 > EOF
69 > EOF
70 $ trylist() {
70 $ trylist() {
71 > hg debugrevlistspec --debug "$@"
71 > hg debugrevlistspec --debug "$@"
72 > }
72 > }
73
73
74 $ hg init repo
74 $ hg init repo
75 $ cd repo
75 $ cd repo
76
76
77 $ echo a > a
77 $ echo a > a
78 $ hg branch a
78 $ hg branch a
79 marked working directory as branch a
79 marked working directory as branch a
80 (branches are permanent and global, did you want a bookmark?)
80 (branches are permanent and global, did you want a bookmark?)
81 $ hg ci -Aqm0
81 $ hg ci -Aqm0
82
82
83 $ echo b > b
83 $ echo b > b
84 $ hg branch b
84 $ hg branch b
85 marked working directory as branch b
85 marked working directory as branch b
86 $ hg ci -Aqm1
86 $ hg ci -Aqm1
87
87
88 $ rm a
88 $ rm a
89 $ hg branch a-b-c-
89 $ hg branch a-b-c-
90 marked working directory as branch a-b-c-
90 marked working directory as branch a-b-c-
91 $ hg ci -Aqm2 -u Bob
91 $ hg ci -Aqm2 -u Bob
92
92
93 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
93 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
94 2
94 2
95 $ hg log -r "extra('branch')" --template '{rev}\n'
95 $ hg log -r "extra('branch')" --template '{rev}\n'
96 0
96 0
97 1
97 1
98 2
98 2
99 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
99 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
100 0 a
100 0 a
101 2 a-b-c-
101 2 a-b-c-
102
102
103 $ hg co 1
103 $ hg co 1
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 $ hg branch +a+b+c+
105 $ hg branch +a+b+c+
106 marked working directory as branch +a+b+c+
106 marked working directory as branch +a+b+c+
107 $ hg ci -Aqm3
107 $ hg ci -Aqm3
108
108
109 $ hg co 2 # interleave
109 $ hg co 2 # interleave
110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
111 $ echo bb > b
111 $ echo bb > b
112 $ hg branch -- -a-b-c-
112 $ hg branch -- -a-b-c-
113 marked working directory as branch -a-b-c-
113 marked working directory as branch -a-b-c-
114 $ hg ci -Aqm4 -d "May 12 2005"
114 $ hg ci -Aqm4 -d "May 12 2005"
115
115
116 $ hg co 3
116 $ hg co 3
117 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 $ hg branch !a/b/c/
118 $ hg branch !a/b/c/
119 marked working directory as branch !a/b/c/
119 marked working directory as branch !a/b/c/
120 $ hg ci -Aqm"5 bug"
120 $ hg ci -Aqm"5 bug"
121
121
122 $ hg merge 4
122 $ hg merge 4
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 (branch merge, don't forget to commit)
124 (branch merge, don't forget to commit)
125 $ hg branch _a_b_c_
125 $ hg branch _a_b_c_
126 marked working directory as branch _a_b_c_
126 marked working directory as branch _a_b_c_
127 $ hg ci -Aqm"6 issue619"
127 $ hg ci -Aqm"6 issue619"
128
128
129 $ hg branch .a.b.c.
129 $ hg branch .a.b.c.
130 marked working directory as branch .a.b.c.
130 marked working directory as branch .a.b.c.
131 $ hg ci -Aqm7
131 $ hg ci -Aqm7
132
132
133 $ hg branch all
133 $ hg branch all
134 marked working directory as branch all
134 marked working directory as branch all
135
135
136 $ hg co 4
136 $ hg co 4
137 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 $ hg branch Γ©
138 $ hg branch Γ©
139 marked working directory as branch \xc3\xa9 (esc)
139 marked working directory as branch \xc3\xa9 (esc)
140 $ hg ci -Aqm9
140 $ hg ci -Aqm9
141
141
142 $ hg tag -r6 1.0
142 $ hg tag -r6 1.0
143 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
143 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
144
144
145 $ hg clone --quiet -U -r 7 . ../remote1
145 $ hg clone --quiet -U -r 7 . ../remote1
146 $ hg clone --quiet -U -r 8 . ../remote2
146 $ hg clone --quiet -U -r 8 . ../remote2
147 $ echo "[paths]" >> .hg/hgrc
147 $ echo "[paths]" >> .hg/hgrc
148 $ echo "default = ../remote1" >> .hg/hgrc
148 $ echo "default = ../remote1" >> .hg/hgrc
149
149
150 trivial
150 trivial
151
151
152 $ try 0:1
152 $ try 0:1
153 (range
153 (range
154 ('symbol', '0')
154 ('symbol', '0')
155 ('symbol', '1'))
155 ('symbol', '1'))
156 * set:
156 * set:
157 <spanset+ 0:1>
157 <spanset+ 0:1>
158 0
158 0
159 1
159 1
160 $ try --optimize :
160 $ try --optimize :
161 (rangeall
161 (rangeall
162 None)
162 None)
163 * optimized:
163 * optimized:
164 (rangepre
164 (rangeall
165 ('string', 'tip')
165 None
166 define)
166 define)
167 * set:
167 * set:
168 <spanset+ 0:9>
168 <spanset+ 0:9>
169 0
169 0
170 1
170 1
171 2
171 2
172 3
172 3
173 4
173 4
174 5
174 5
175 6
175 6
176 7
176 7
177 8
177 8
178 9
178 9
179 $ try 3::6
179 $ try 3::6
180 (dagrange
180 (dagrange
181 ('symbol', '3')
181 ('symbol', '3')
182 ('symbol', '6'))
182 ('symbol', '6'))
183 * set:
183 * set:
184 <baseset+ [3, 5, 6]>
184 <baseset+ [3, 5, 6]>
185 3
185 3
186 5
186 5
187 6
187 6
188 $ try '0|1|2'
188 $ try '0|1|2'
189 (or
189 (or
190 (list
190 (list
191 ('symbol', '0')
191 ('symbol', '0')
192 ('symbol', '1')
192 ('symbol', '1')
193 ('symbol', '2')))
193 ('symbol', '2')))
194 * set:
194 * set:
195 <baseset [0, 1, 2]>
195 <baseset [0, 1, 2]>
196 0
196 0
197 1
197 1
198 2
198 2
199
199
200 names that should work without quoting
200 names that should work without quoting
201
201
202 $ try a
202 $ try a
203 ('symbol', 'a')
203 ('symbol', 'a')
204 * set:
204 * set:
205 <baseset [0]>
205 <baseset [0]>
206 0
206 0
207 $ try b-a
207 $ try b-a
208 (minus
208 (minus
209 ('symbol', 'b')
209 ('symbol', 'b')
210 ('symbol', 'a'))
210 ('symbol', 'a'))
211 * set:
211 * set:
212 <filteredset
212 <filteredset
213 <baseset [1]>,
213 <baseset [1]>,
214 <not
214 <not
215 <baseset [0]>>>
215 <baseset [0]>>>
216 1
216 1
217 $ try _a_b_c_
217 $ try _a_b_c_
218 ('symbol', '_a_b_c_')
218 ('symbol', '_a_b_c_')
219 * set:
219 * set:
220 <baseset [6]>
220 <baseset [6]>
221 6
221 6
222 $ try _a_b_c_-a
222 $ try _a_b_c_-a
223 (minus
223 (minus
224 ('symbol', '_a_b_c_')
224 ('symbol', '_a_b_c_')
225 ('symbol', 'a'))
225 ('symbol', 'a'))
226 * set:
226 * set:
227 <filteredset
227 <filteredset
228 <baseset [6]>,
228 <baseset [6]>,
229 <not
229 <not
230 <baseset [0]>>>
230 <baseset [0]>>>
231 6
231 6
232 $ try .a.b.c.
232 $ try .a.b.c.
233 ('symbol', '.a.b.c.')
233 ('symbol', '.a.b.c.')
234 * set:
234 * set:
235 <baseset [7]>
235 <baseset [7]>
236 7
236 7
237 $ try .a.b.c.-a
237 $ try .a.b.c.-a
238 (minus
238 (minus
239 ('symbol', '.a.b.c.')
239 ('symbol', '.a.b.c.')
240 ('symbol', 'a'))
240 ('symbol', 'a'))
241 * set:
241 * set:
242 <filteredset
242 <filteredset
243 <baseset [7]>,
243 <baseset [7]>,
244 <not
244 <not
245 <baseset [0]>>>
245 <baseset [0]>>>
246 7
246 7
247
247
248 names that should be caught by fallback mechanism
248 names that should be caught by fallback mechanism
249
249
250 $ try -- '-a-b-c-'
250 $ try -- '-a-b-c-'
251 ('symbol', '-a-b-c-')
251 ('symbol', '-a-b-c-')
252 * set:
252 * set:
253 <baseset [4]>
253 <baseset [4]>
254 4
254 4
255 $ log -a-b-c-
255 $ log -a-b-c-
256 4
256 4
257 $ try '+a+b+c+'
257 $ try '+a+b+c+'
258 ('symbol', '+a+b+c+')
258 ('symbol', '+a+b+c+')
259 * set:
259 * set:
260 <baseset [3]>
260 <baseset [3]>
261 3
261 3
262 $ try '+a+b+c+:'
262 $ try '+a+b+c+:'
263 (rangepost
263 (rangepost
264 ('symbol', '+a+b+c+'))
264 ('symbol', '+a+b+c+'))
265 * set:
265 * set:
266 <spanset+ 3:9>
266 <spanset+ 3:9>
267 3
267 3
268 4
268 4
269 5
269 5
270 6
270 6
271 7
271 7
272 8
272 8
273 9
273 9
274 $ try ':+a+b+c+'
274 $ try ':+a+b+c+'
275 (rangepre
275 (rangepre
276 ('symbol', '+a+b+c+'))
276 ('symbol', '+a+b+c+'))
277 * set:
277 * set:
278 <spanset+ 0:3>
278 <spanset+ 0:3>
279 0
279 0
280 1
280 1
281 2
281 2
282 3
282 3
283 $ try -- '-a-b-c-:+a+b+c+'
283 $ try -- '-a-b-c-:+a+b+c+'
284 (range
284 (range
285 ('symbol', '-a-b-c-')
285 ('symbol', '-a-b-c-')
286 ('symbol', '+a+b+c+'))
286 ('symbol', '+a+b+c+'))
287 * set:
287 * set:
288 <spanset- 3:4>
288 <spanset- 3:4>
289 4
289 4
290 3
290 3
291 $ log '-a-b-c-:+a+b+c+'
291 $ log '-a-b-c-:+a+b+c+'
292 4
292 4
293 3
293 3
294
294
295 $ try -- -a-b-c--a # complains
295 $ try -- -a-b-c--a # complains
296 (minus
296 (minus
297 (minus
297 (minus
298 (minus
298 (minus
299 (negate
299 (negate
300 ('symbol', 'a'))
300 ('symbol', 'a'))
301 ('symbol', 'b'))
301 ('symbol', 'b'))
302 ('symbol', 'c'))
302 ('symbol', 'c'))
303 (negate
303 (negate
304 ('symbol', 'a')))
304 ('symbol', 'a')))
305 abort: unknown revision '-a'!
305 abort: unknown revision '-a'!
306 [255]
306 [255]
307 $ try Γ©
307 $ try Γ©
308 ('symbol', '\xc3\xa9')
308 ('symbol', '\xc3\xa9')
309 * set:
309 * set:
310 <baseset [9]>
310 <baseset [9]>
311 9
311 9
312
312
313 no quoting needed
313 no quoting needed
314
314
315 $ log ::a-b-c-
315 $ log ::a-b-c-
316 0
316 0
317 1
317 1
318 2
318 2
319
319
320 quoting needed
320 quoting needed
321
321
322 $ try '"-a-b-c-"-a'
322 $ try '"-a-b-c-"-a'
323 (minus
323 (minus
324 ('string', '-a-b-c-')
324 ('string', '-a-b-c-')
325 ('symbol', 'a'))
325 ('symbol', 'a'))
326 * set:
326 * set:
327 <filteredset
327 <filteredset
328 <baseset [4]>,
328 <baseset [4]>,
329 <not
329 <not
330 <baseset [0]>>>
330 <baseset [0]>>>
331 4
331 4
332
332
333 $ log '1 or 2'
333 $ log '1 or 2'
334 1
334 1
335 2
335 2
336 $ log '1|2'
336 $ log '1|2'
337 1
337 1
338 2
338 2
339 $ log '1 and 2'
339 $ log '1 and 2'
340 $ log '1&2'
340 $ log '1&2'
341 $ try '1&2|3' # precedence - and is higher
341 $ try '1&2|3' # precedence - and is higher
342 (or
342 (or
343 (list
343 (list
344 (and
344 (and
345 ('symbol', '1')
345 ('symbol', '1')
346 ('symbol', '2'))
346 ('symbol', '2'))
347 ('symbol', '3')))
347 ('symbol', '3')))
348 * set:
348 * set:
349 <addset
349 <addset
350 <baseset []>,
350 <baseset []>,
351 <baseset [3]>>
351 <baseset [3]>>
352 3
352 3
353 $ try '1|2&3'
353 $ try '1|2&3'
354 (or
354 (or
355 (list
355 (list
356 ('symbol', '1')
356 ('symbol', '1')
357 (and
357 (and
358 ('symbol', '2')
358 ('symbol', '2')
359 ('symbol', '3'))))
359 ('symbol', '3'))))
360 * set:
360 * set:
361 <addset
361 <addset
362 <baseset [1]>,
362 <baseset [1]>,
363 <baseset []>>
363 <baseset []>>
364 1
364 1
365 $ try '1&2&3' # associativity
365 $ try '1&2&3' # associativity
366 (and
366 (and
367 (and
367 (and
368 ('symbol', '1')
368 ('symbol', '1')
369 ('symbol', '2'))
369 ('symbol', '2'))
370 ('symbol', '3'))
370 ('symbol', '3'))
371 * set:
371 * set:
372 <baseset []>
372 <baseset []>
373 $ try '1|(2|3)'
373 $ try '1|(2|3)'
374 (or
374 (or
375 (list
375 (list
376 ('symbol', '1')
376 ('symbol', '1')
377 (group
377 (group
378 (or
378 (or
379 (list
379 (list
380 ('symbol', '2')
380 ('symbol', '2')
381 ('symbol', '3'))))))
381 ('symbol', '3'))))))
382 * set:
382 * set:
383 <addset
383 <addset
384 <baseset [1]>,
384 <baseset [1]>,
385 <baseset [2, 3]>>
385 <baseset [2, 3]>>
386 1
386 1
387 2
387 2
388 3
388 3
389 $ log '1.0' # tag
389 $ log '1.0' # tag
390 6
390 6
391 $ log 'a' # branch
391 $ log 'a' # branch
392 0
392 0
393 $ log '2785f51ee'
393 $ log '2785f51ee'
394 0
394 0
395 $ log 'date(2005)'
395 $ log 'date(2005)'
396 4
396 4
397 $ log 'date(this is a test)'
397 $ log 'date(this is a test)'
398 hg: parse error at 10: unexpected token: symbol
398 hg: parse error at 10: unexpected token: symbol
399 [255]
399 [255]
400 $ log 'date()'
400 $ log 'date()'
401 hg: parse error: date requires a string
401 hg: parse error: date requires a string
402 [255]
402 [255]
403 $ log 'date'
403 $ log 'date'
404 abort: unknown revision 'date'!
404 abort: unknown revision 'date'!
405 [255]
405 [255]
406 $ log 'date('
406 $ log 'date('
407 hg: parse error at 5: not a prefix: end
407 hg: parse error at 5: not a prefix: end
408 [255]
408 [255]
409 $ log 'date("\xy")'
409 $ log 'date("\xy")'
410 hg: parse error: invalid \x escape
410 hg: parse error: invalid \x escape
411 [255]
411 [255]
412 $ log 'date(tip)'
412 $ log 'date(tip)'
413 abort: invalid date: 'tip'
413 abort: invalid date: 'tip'
414 [255]
414 [255]
415 $ log '0:date'
415 $ log '0:date'
416 abort: unknown revision 'date'!
416 abort: unknown revision 'date'!
417 [255]
417 [255]
418 $ log '::"date"'
418 $ log '::"date"'
419 abort: unknown revision 'date'!
419 abort: unknown revision 'date'!
420 [255]
420 [255]
421 $ hg book date -r 4
421 $ hg book date -r 4
422 $ log '0:date'
422 $ log '0:date'
423 0
423 0
424 1
424 1
425 2
425 2
426 3
426 3
427 4
427 4
428 $ log '::date'
428 $ log '::date'
429 0
429 0
430 1
430 1
431 2
431 2
432 4
432 4
433 $ log '::"date"'
433 $ log '::"date"'
434 0
434 0
435 1
435 1
436 2
436 2
437 4
437 4
438 $ log 'date(2005) and 1::'
438 $ log 'date(2005) and 1::'
439 4
439 4
440 $ hg book -d date
440 $ hg book -d date
441
441
442 function name should be a symbol
442 function name should be a symbol
443
443
444 $ log '"date"(2005)'
444 $ log '"date"(2005)'
445 hg: parse error: not a symbol
445 hg: parse error: not a symbol
446 [255]
446 [255]
447
447
448 keyword arguments
448 keyword arguments
449
449
450 $ log 'extra(branch, value=a)'
450 $ log 'extra(branch, value=a)'
451 0
451 0
452
452
453 $ log 'extra(branch, a, b)'
453 $ log 'extra(branch, a, b)'
454 hg: parse error: extra takes at most 2 arguments
454 hg: parse error: extra takes at most 2 arguments
455 [255]
455 [255]
456 $ log 'extra(a, label=b)'
456 $ log 'extra(a, label=b)'
457 hg: parse error: extra got multiple values for keyword argument 'label'
457 hg: parse error: extra got multiple values for keyword argument 'label'
458 [255]
458 [255]
459 $ log 'extra(label=branch, default)'
459 $ log 'extra(label=branch, default)'
460 hg: parse error: extra got an invalid argument
460 hg: parse error: extra got an invalid argument
461 [255]
461 [255]
462 $ log 'extra(branch, foo+bar=baz)'
462 $ log 'extra(branch, foo+bar=baz)'
463 hg: parse error: extra got an invalid argument
463 hg: parse error: extra got an invalid argument
464 [255]
464 [255]
465 $ log 'extra(unknown=branch)'
465 $ log 'extra(unknown=branch)'
466 hg: parse error: extra got an unexpected keyword argument 'unknown'
466 hg: parse error: extra got an unexpected keyword argument 'unknown'
467 [255]
467 [255]
468
468
469 $ try 'foo=bar|baz'
469 $ try 'foo=bar|baz'
470 (keyvalue
470 (keyvalue
471 ('symbol', 'foo')
471 ('symbol', 'foo')
472 (or
472 (or
473 (list
473 (list
474 ('symbol', 'bar')
474 ('symbol', 'bar')
475 ('symbol', 'baz'))))
475 ('symbol', 'baz'))))
476 hg: parse error: can't use a key-value pair in this context
476 hg: parse error: can't use a key-value pair in this context
477 [255]
477 [255]
478
478
479 right-hand side should be optimized recursively
479 right-hand side should be optimized recursively
480
480
481 $ try --optimize 'foo=(not public())'
481 $ try --optimize 'foo=(not public())'
482 (keyvalue
482 (keyvalue
483 ('symbol', 'foo')
483 ('symbol', 'foo')
484 (group
484 (group
485 (not
485 (not
486 (func
486 (func
487 ('symbol', 'public')
487 ('symbol', 'public')
488 None))))
488 None))))
489 * optimized:
489 * optimized:
490 (keyvalue
490 (keyvalue
491 ('symbol', 'foo')
491 ('symbol', 'foo')
492 (func
492 (func
493 ('symbol', '_notpublic')
493 ('symbol', '_notpublic')
494 None
494 None
495 any))
495 any))
496 hg: parse error: can't use a key-value pair in this context
496 hg: parse error: can't use a key-value pair in this context
497 [255]
497 [255]
498
498
499 parsed tree at stages:
499 parsed tree at stages:
500
500
501 $ hg debugrevspec -p all '()'
501 $ hg debugrevspec -p all '()'
502 * parsed:
502 * parsed:
503 (group
503 (group
504 None)
504 None)
505 * expanded:
505 * expanded:
506 (group
506 (group
507 None)
507 None)
508 * concatenated:
508 * concatenated:
509 (group
509 (group
510 None)
510 None)
511 * analyzed:
511 * analyzed:
512 None
512 None
513 * optimized:
513 * optimized:
514 None
514 None
515 hg: parse error: missing argument
515 hg: parse error: missing argument
516 [255]
516 [255]
517
517
518 $ hg debugrevspec --no-optimized -p all '()'
518 $ hg debugrevspec --no-optimized -p all '()'
519 * parsed:
519 * parsed:
520 (group
520 (group
521 None)
521 None)
522 * expanded:
522 * expanded:
523 (group
523 (group
524 None)
524 None)
525 * concatenated:
525 * concatenated:
526 (group
526 (group
527 None)
527 None)
528 * analyzed:
528 * analyzed:
529 None
529 None
530 hg: parse error: missing argument
530 hg: parse error: missing argument
531 [255]
531 [255]
532
532
533 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
533 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
534 * parsed:
534 * parsed:
535 (minus
535 (minus
536 (group
536 (group
537 (or
537 (or
538 (list
538 (list
539 ('symbol', '0')
539 ('symbol', '0')
540 ('symbol', '1'))))
540 ('symbol', '1'))))
541 ('symbol', '1'))
541 ('symbol', '1'))
542 * analyzed:
542 * analyzed:
543 (and
543 (and
544 (or
544 (or
545 (list
545 (list
546 ('symbol', '0')
546 ('symbol', '0')
547 ('symbol', '1'))
547 ('symbol', '1'))
548 define)
548 define)
549 (not
549 (not
550 ('symbol', '1')
550 ('symbol', '1')
551 follow)
551 follow)
552 define)
552 define)
553 * optimized:
553 * optimized:
554 (difference
554 (difference
555 (func
555 (func
556 ('symbol', '_list')
556 ('symbol', '_list')
557 ('string', '0\x001')
557 ('string', '0\x001')
558 define)
558 define)
559 ('symbol', '1')
559 ('symbol', '1')
560 define)
560 define)
561 0
561 0
562
562
563 $ hg debugrevspec -p unknown '0'
563 $ hg debugrevspec -p unknown '0'
564 abort: invalid stage name: unknown
564 abort: invalid stage name: unknown
565 [255]
565 [255]
566
566
567 $ hg debugrevspec -p all --optimize '0'
567 $ hg debugrevspec -p all --optimize '0'
568 abort: cannot use --optimize with --show-stage
568 abort: cannot use --optimize with --show-stage
569 [255]
569 [255]
570
570
571 verify optimized tree:
571 verify optimized tree:
572
572
573 $ hg debugrevspec --verify '0|1'
573 $ hg debugrevspec --verify '0|1'
574
574
575 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
575 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
576 * analyzed:
576 * analyzed:
577 (and
577 (and
578 (func
578 (func
579 ('symbol', 'r3232')
579 ('symbol', 'r3232')
580 None
580 None
581 define)
581 define)
582 ('symbol', '2')
582 ('symbol', '2')
583 define)
583 define)
584 * optimized:
584 * optimized:
585 (and
585 (and
586 ('symbol', '2')
586 ('symbol', '2')
587 (func
587 (func
588 ('symbol', 'r3232')
588 ('symbol', 'r3232')
589 None
589 None
590 define)
590 define)
591 define)
591 define)
592 * analyzed set:
592 * analyzed set:
593 <baseset [2]>
593 <baseset [2]>
594 * optimized set:
594 * optimized set:
595 <baseset [2, 2]>
595 <baseset [2, 2]>
596 --- analyzed
596 --- analyzed
597 +++ optimized
597 +++ optimized
598 2
598 2
599 +2
599 +2
600 [1]
600 [1]
601
601
602 $ hg debugrevspec --no-optimized --verify-optimized '0'
602 $ hg debugrevspec --no-optimized --verify-optimized '0'
603 abort: cannot use --verify-optimized with --no-optimized
603 abort: cannot use --verify-optimized with --no-optimized
604 [255]
604 [255]
605
605
606 Test that symbols only get parsed as functions if there's an opening
606 Test that symbols only get parsed as functions if there's an opening
607 parenthesis.
607 parenthesis.
608
608
609 $ hg book only -r 9
609 $ hg book only -r 9
610 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
610 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
611 8
611 8
612 9
612 9
613
613
614 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
614 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
615 may be hidden (issue5385)
615 may be hidden (issue5385)
616
616
617 $ try -p parsed -p analyzed ':'
617 $ try -p parsed -p analyzed ':'
618 * parsed:
618 * parsed:
619 (rangeall
619 (rangeall
620 None)
620 None)
621 * analyzed:
621 * analyzed:
622 (rangepre
622 (rangeall
623 ('string', 'tip')
623 None
624 define)
624 define)
625 * set:
625 * set:
626 <spanset+ 0:9>
626 <spanset+ 0:9>
627 0
627 0
628 1
628 1
629 2
629 2
630 3
630 3
631 4
631 4
632 5
632 5
633 6
633 6
634 7
634 7
635 8
635 8
636 9
636 9
637 $ try -p analyzed ':1'
637 $ try -p analyzed ':1'
638 * analyzed:
638 * analyzed:
639 (rangepre
639 (rangepre
640 ('symbol', '1')
640 ('symbol', '1')
641 define)
641 define)
642 * set:
642 * set:
643 <spanset+ 0:1>
643 <spanset+ 0:1>
644 0
644 0
645 1
645 1
646 $ try -p analyzed ':(1|2)'
646 $ try -p analyzed ':(1|2)'
647 * analyzed:
647 * analyzed:
648 (rangepre
648 (rangepre
649 (or
649 (or
650 (list
650 (list
651 ('symbol', '1')
651 ('symbol', '1')
652 ('symbol', '2'))
652 ('symbol', '2'))
653 define)
653 define)
654 define)
654 define)
655 * set:
655 * set:
656 <spanset+ 0:2>
656 <spanset+ 0:2>
657 0
657 0
658 1
658 1
659 2
659 2
660 $ try -p analyzed ':(1&2)'
660 $ try -p analyzed ':(1&2)'
661 * analyzed:
661 * analyzed:
662 (rangepre
662 (rangepre
663 (and
663 (and
664 ('symbol', '1')
664 ('symbol', '1')
665 ('symbol', '2')
665 ('symbol', '2')
666 define)
666 define)
667 define)
667 define)
668 * set:
668 * set:
669 <baseset []>
669 <baseset []>
670
670
671 infix/suffix resolution of ^ operator (issue2884):
671 infix/suffix resolution of ^ operator (issue2884):
672
672
673 x^:y means (x^):y
673 x^:y means (x^):y
674
674
675 $ try '1^:2'
675 $ try '1^:2'
676 (range
676 (range
677 (parentpost
677 (parentpost
678 ('symbol', '1'))
678 ('symbol', '1'))
679 ('symbol', '2'))
679 ('symbol', '2'))
680 * set:
680 * set:
681 <spanset+ 0:2>
681 <spanset+ 0:2>
682 0
682 0
683 1
683 1
684 2
684 2
685
685
686 $ try '1^::2'
686 $ try '1^::2'
687 (dagrange
687 (dagrange
688 (parentpost
688 (parentpost
689 ('symbol', '1'))
689 ('symbol', '1'))
690 ('symbol', '2'))
690 ('symbol', '2'))
691 * set:
691 * set:
692 <baseset+ [0, 1, 2]>
692 <baseset+ [0, 1, 2]>
693 0
693 0
694 1
694 1
695 2
695 2
696
696
697 $ try '9^:'
697 $ try '9^:'
698 (rangepost
698 (rangepost
699 (parentpost
699 (parentpost
700 ('symbol', '9')))
700 ('symbol', '9')))
701 * set:
701 * set:
702 <spanset+ 8:9>
702 <spanset+ 8:9>
703 8
703 8
704 9
704 9
705
705
706 x^:y should be resolved before omitting group operators
706 x^:y should be resolved before omitting group operators
707
707
708 $ try '1^(:2)'
708 $ try '1^(:2)'
709 (parent
709 (parent
710 ('symbol', '1')
710 ('symbol', '1')
711 (group
711 (group
712 (rangepre
712 (rangepre
713 ('symbol', '2'))))
713 ('symbol', '2'))))
714 hg: parse error: ^ expects a number 0, 1, or 2
714 hg: parse error: ^ expects a number 0, 1, or 2
715 [255]
715 [255]
716
716
717 x^:y should be resolved recursively
717 x^:y should be resolved recursively
718
718
719 $ try 'sort(1^:2)'
719 $ try 'sort(1^:2)'
720 (func
720 (func
721 ('symbol', 'sort')
721 ('symbol', 'sort')
722 (range
722 (range
723 (parentpost
723 (parentpost
724 ('symbol', '1'))
724 ('symbol', '1'))
725 ('symbol', '2')))
725 ('symbol', '2')))
726 * set:
726 * set:
727 <spanset+ 0:2>
727 <spanset+ 0:2>
728 0
728 0
729 1
729 1
730 2
730 2
731
731
732 $ try '(3^:4)^:2'
732 $ try '(3^:4)^:2'
733 (range
733 (range
734 (parentpost
734 (parentpost
735 (group
735 (group
736 (range
736 (range
737 (parentpost
737 (parentpost
738 ('symbol', '3'))
738 ('symbol', '3'))
739 ('symbol', '4'))))
739 ('symbol', '4'))))
740 ('symbol', '2'))
740 ('symbol', '2'))
741 * set:
741 * set:
742 <spanset+ 0:2>
742 <spanset+ 0:2>
743 0
743 0
744 1
744 1
745 2
745 2
746
746
747 $ try '(3^::4)^::2'
747 $ try '(3^::4)^::2'
748 (dagrange
748 (dagrange
749 (parentpost
749 (parentpost
750 (group
750 (group
751 (dagrange
751 (dagrange
752 (parentpost
752 (parentpost
753 ('symbol', '3'))
753 ('symbol', '3'))
754 ('symbol', '4'))))
754 ('symbol', '4'))))
755 ('symbol', '2'))
755 ('symbol', '2'))
756 * set:
756 * set:
757 <baseset+ [0, 1, 2]>
757 <baseset+ [0, 1, 2]>
758 0
758 0
759 1
759 1
760 2
760 2
761
761
762 $ try '(9^:)^:'
762 $ try '(9^:)^:'
763 (rangepost
763 (rangepost
764 (parentpost
764 (parentpost
765 (group
765 (group
766 (rangepost
766 (rangepost
767 (parentpost
767 (parentpost
768 ('symbol', '9'))))))
768 ('symbol', '9'))))))
769 * set:
769 * set:
770 <spanset+ 4:9>
770 <spanset+ 4:9>
771 4
771 4
772 5
772 5
773 6
773 6
774 7
774 7
775 8
775 8
776 9
776 9
777
777
778 x^ in alias should also be resolved
778 x^ in alias should also be resolved
779
779
780 $ try 'A' --config 'revsetalias.A=1^:2'
780 $ try 'A' --config 'revsetalias.A=1^:2'
781 ('symbol', 'A')
781 ('symbol', 'A')
782 * expanded:
782 * expanded:
783 (range
783 (range
784 (parentpost
784 (parentpost
785 ('symbol', '1'))
785 ('symbol', '1'))
786 ('symbol', '2'))
786 ('symbol', '2'))
787 * set:
787 * set:
788 <spanset+ 0:2>
788 <spanset+ 0:2>
789 0
789 0
790 1
790 1
791 2
791 2
792
792
793 $ try 'A:2' --config 'revsetalias.A=1^'
793 $ try 'A:2' --config 'revsetalias.A=1^'
794 (range
794 (range
795 ('symbol', 'A')
795 ('symbol', 'A')
796 ('symbol', '2'))
796 ('symbol', '2'))
797 * expanded:
797 * expanded:
798 (range
798 (range
799 (parentpost
799 (parentpost
800 ('symbol', '1'))
800 ('symbol', '1'))
801 ('symbol', '2'))
801 ('symbol', '2'))
802 * set:
802 * set:
803 <spanset+ 0:2>
803 <spanset+ 0:2>
804 0
804 0
805 1
805 1
806 2
806 2
807
807
808 but not beyond the boundary of alias expansion, because the resolution should
808 but not beyond the boundary of alias expansion, because the resolution should
809 be made at the parsing stage
809 be made at the parsing stage
810
810
811 $ try '1^A' --config 'revsetalias.A=:2'
811 $ try '1^A' --config 'revsetalias.A=:2'
812 (parent
812 (parent
813 ('symbol', '1')
813 ('symbol', '1')
814 ('symbol', 'A'))
814 ('symbol', 'A'))
815 * expanded:
815 * expanded:
816 (parent
816 (parent
817 ('symbol', '1')
817 ('symbol', '1')
818 (rangepre
818 (rangepre
819 ('symbol', '2')))
819 ('symbol', '2')))
820 hg: parse error: ^ expects a number 0, 1, or 2
820 hg: parse error: ^ expects a number 0, 1, or 2
821 [255]
821 [255]
822
822
823 ancestor can accept 0 or more arguments
823 ancestor can accept 0 or more arguments
824
824
825 $ log 'ancestor()'
825 $ log 'ancestor()'
826 $ log 'ancestor(1)'
826 $ log 'ancestor(1)'
827 1
827 1
828 $ log 'ancestor(4,5)'
828 $ log 'ancestor(4,5)'
829 1
829 1
830 $ log 'ancestor(4,5) and 4'
830 $ log 'ancestor(4,5) and 4'
831 $ log 'ancestor(0,0,1,3)'
831 $ log 'ancestor(0,0,1,3)'
832 0
832 0
833 $ log 'ancestor(3,1,5,3,5,1)'
833 $ log 'ancestor(3,1,5,3,5,1)'
834 1
834 1
835 $ log 'ancestor(0,1,3,5)'
835 $ log 'ancestor(0,1,3,5)'
836 0
836 0
837 $ log 'ancestor(1,2,3,4,5)'
837 $ log 'ancestor(1,2,3,4,5)'
838 1
838 1
839
839
840 test ancestors
840 test ancestors
841
841
842 $ log 'ancestors(5)'
842 $ log 'ancestors(5)'
843 0
843 0
844 1
844 1
845 3
845 3
846 5
846 5
847 $ log 'ancestor(ancestors(5))'
847 $ log 'ancestor(ancestors(5))'
848 0
848 0
849 $ log '::r3232()'
849 $ log '::r3232()'
850 0
850 0
851 1
851 1
852 2
852 2
853 3
853 3
854
854
855 $ log 'author(bob)'
855 $ log 'author(bob)'
856 2
856 2
857 $ log 'author("re:bob|test")'
857 $ log 'author("re:bob|test")'
858 0
858 0
859 1
859 1
860 2
860 2
861 3
861 3
862 4
862 4
863 5
863 5
864 6
864 6
865 7
865 7
866 8
866 8
867 9
867 9
868 $ log 'author(r"re:\S")'
868 $ log 'author(r"re:\S")'
869 0
869 0
870 1
870 1
871 2
871 2
872 3
872 3
873 4
873 4
874 5
874 5
875 6
875 6
876 7
876 7
877 8
877 8
878 9
878 9
879 $ log 'branch(Γ©)'
879 $ log 'branch(Γ©)'
880 8
880 8
881 9
881 9
882 $ log 'branch(a)'
882 $ log 'branch(a)'
883 0
883 0
884 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
884 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
885 0 a
885 0 a
886 2 a-b-c-
886 2 a-b-c-
887 3 +a+b+c+
887 3 +a+b+c+
888 4 -a-b-c-
888 4 -a-b-c-
889 5 !a/b/c/
889 5 !a/b/c/
890 6 _a_b_c_
890 6 _a_b_c_
891 7 .a.b.c.
891 7 .a.b.c.
892 $ log 'children(ancestor(4,5))'
892 $ log 'children(ancestor(4,5))'
893 2
893 2
894 3
894 3
895
895
896 $ log 'children(4)'
896 $ log 'children(4)'
897 6
897 6
898 8
898 8
899 $ log 'children(null)'
899 $ log 'children(null)'
900 0
900 0
901
901
902 $ log 'closed()'
902 $ log 'closed()'
903 $ log 'contains(a)'
903 $ log 'contains(a)'
904 0
904 0
905 1
905 1
906 3
906 3
907 5
907 5
908 $ log 'contains("../repo/a")'
908 $ log 'contains("../repo/a")'
909 0
909 0
910 1
910 1
911 3
911 3
912 5
912 5
913 $ log 'desc(B)'
913 $ log 'desc(B)'
914 5
914 5
915 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
915 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
916 5 5 bug
916 5 5 bug
917 6 6 issue619
917 6 6 issue619
918 $ log 'descendants(2 or 3)'
918 $ log 'descendants(2 or 3)'
919 2
919 2
920 3
920 3
921 4
921 4
922 5
922 5
923 6
923 6
924 7
924 7
925 8
925 8
926 9
926 9
927 $ log 'file("b*")'
927 $ log 'file("b*")'
928 1
928 1
929 4
929 4
930 $ log 'filelog("b")'
930 $ log 'filelog("b")'
931 1
931 1
932 4
932 4
933 $ log 'filelog("../repo/b")'
933 $ log 'filelog("../repo/b")'
934 1
934 1
935 4
935 4
936 $ log 'follow()'
936 $ log 'follow()'
937 0
937 0
938 1
938 1
939 2
939 2
940 4
940 4
941 8
941 8
942 9
942 9
943 $ log 'grep("issue\d+")'
943 $ log 'grep("issue\d+")'
944 6
944 6
945 $ try 'grep("(")' # invalid regular expression
945 $ try 'grep("(")' # invalid regular expression
946 (func
946 (func
947 ('symbol', 'grep')
947 ('symbol', 'grep')
948 ('string', '('))
948 ('string', '('))
949 hg: parse error: invalid match pattern: unbalanced parenthesis
949 hg: parse error: invalid match pattern: unbalanced parenthesis
950 [255]
950 [255]
951 $ try 'grep("\bissue\d+")'
951 $ try 'grep("\bissue\d+")'
952 (func
952 (func
953 ('symbol', 'grep')
953 ('symbol', 'grep')
954 ('string', '\x08issue\\d+'))
954 ('string', '\x08issue\\d+'))
955 * set:
955 * set:
956 <filteredset
956 <filteredset
957 <fullreposet+ 0:9>,
957 <fullreposet+ 0:9>,
958 <grep '\x08issue\\d+'>>
958 <grep '\x08issue\\d+'>>
959 $ try 'grep(r"\bissue\d+")'
959 $ try 'grep(r"\bissue\d+")'
960 (func
960 (func
961 ('symbol', 'grep')
961 ('symbol', 'grep')
962 ('string', '\\bissue\\d+'))
962 ('string', '\\bissue\\d+'))
963 * set:
963 * set:
964 <filteredset
964 <filteredset
965 <fullreposet+ 0:9>,
965 <fullreposet+ 0:9>,
966 <grep '\\bissue\\d+'>>
966 <grep '\\bissue\\d+'>>
967 6
967 6
968 $ try 'grep(r"\")'
968 $ try 'grep(r"\")'
969 hg: parse error at 7: unterminated string
969 hg: parse error at 7: unterminated string
970 [255]
970 [255]
971 $ log 'head()'
971 $ log 'head()'
972 0
972 0
973 1
973 1
974 2
974 2
975 3
975 3
976 4
976 4
977 5
977 5
978 6
978 6
979 7
979 7
980 9
980 9
981 $ log 'heads(6::)'
981 $ log 'heads(6::)'
982 7
982 7
983 $ log 'keyword(issue)'
983 $ log 'keyword(issue)'
984 6
984 6
985 $ log 'keyword("test a")'
985 $ log 'keyword("test a")'
986 $ log 'limit(head(), 1)'
986 $ log 'limit(head(), 1)'
987 0
987 0
988 $ log 'limit(author("re:bob|test"), 3, 5)'
988 $ log 'limit(author("re:bob|test"), 3, 5)'
989 5
989 5
990 6
990 6
991 7
991 7
992 $ log 'limit(author("re:bob|test"), offset=6)'
992 $ log 'limit(author("re:bob|test"), offset=6)'
993 6
993 6
994 $ log 'limit(author("re:bob|test"), offset=10)'
994 $ log 'limit(author("re:bob|test"), offset=10)'
995 $ log 'limit(all(), 1, -1)'
995 $ log 'limit(all(), 1, -1)'
996 hg: parse error: negative offset
996 hg: parse error: negative offset
997 [255]
997 [255]
998 $ log 'matching(6)'
998 $ log 'matching(6)'
999 6
999 6
1000 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1000 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1001 6
1001 6
1002 7
1002 7
1003
1003
1004 Testing min and max
1004 Testing min and max
1005
1005
1006 max: simple
1006 max: simple
1007
1007
1008 $ log 'max(contains(a))'
1008 $ log 'max(contains(a))'
1009 5
1009 5
1010
1010
1011 max: simple on unordered set)
1011 max: simple on unordered set)
1012
1012
1013 $ log 'max((4+0+2+5+7) and contains(a))'
1013 $ log 'max((4+0+2+5+7) and contains(a))'
1014 5
1014 5
1015
1015
1016 max: no result
1016 max: no result
1017
1017
1018 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1018 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1019
1019
1020 max: no result on unordered set
1020 max: no result on unordered set
1021
1021
1022 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1022 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1023
1023
1024 min: simple
1024 min: simple
1025
1025
1026 $ log 'min(contains(a))'
1026 $ log 'min(contains(a))'
1027 0
1027 0
1028
1028
1029 min: simple on unordered set
1029 min: simple on unordered set
1030
1030
1031 $ log 'min((4+0+2+5+7) and contains(a))'
1031 $ log 'min((4+0+2+5+7) and contains(a))'
1032 0
1032 0
1033
1033
1034 min: empty
1034 min: empty
1035
1035
1036 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1036 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1037
1037
1038 min: empty on unordered set
1038 min: empty on unordered set
1039
1039
1040 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1040 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1041
1041
1042
1042
1043 $ log 'merge()'
1043 $ log 'merge()'
1044 6
1044 6
1045 $ log 'branchpoint()'
1045 $ log 'branchpoint()'
1046 1
1046 1
1047 4
1047 4
1048 $ log 'modifies(b)'
1048 $ log 'modifies(b)'
1049 4
1049 4
1050 $ log 'modifies("path:b")'
1050 $ log 'modifies("path:b")'
1051 4
1051 4
1052 $ log 'modifies("*")'
1052 $ log 'modifies("*")'
1053 4
1053 4
1054 6
1054 6
1055 $ log 'modifies("set:modified()")'
1055 $ log 'modifies("set:modified()")'
1056 4
1056 4
1057 $ log 'id(5)'
1057 $ log 'id(5)'
1058 2
1058 2
1059 $ log 'only(9)'
1059 $ log 'only(9)'
1060 8
1060 8
1061 9
1061 9
1062 $ log 'only(8)'
1062 $ log 'only(8)'
1063 8
1063 8
1064 $ log 'only(9, 5)'
1064 $ log 'only(9, 5)'
1065 2
1065 2
1066 4
1066 4
1067 8
1067 8
1068 9
1068 9
1069 $ log 'only(7 + 9, 5 + 2)'
1069 $ log 'only(7 + 9, 5 + 2)'
1070 4
1070 4
1071 6
1071 6
1072 7
1072 7
1073 8
1073 8
1074 9
1074 9
1075
1075
1076 Test empty set input
1076 Test empty set input
1077 $ log 'only(p2())'
1077 $ log 'only(p2())'
1078 $ log 'only(p1(), p2())'
1078 $ log 'only(p1(), p2())'
1079 0
1079 0
1080 1
1080 1
1081 2
1081 2
1082 4
1082 4
1083 8
1083 8
1084 9
1084 9
1085
1085
1086 Test '%' operator
1086 Test '%' operator
1087
1087
1088 $ log '9%'
1088 $ log '9%'
1089 8
1089 8
1090 9
1090 9
1091 $ log '9%5'
1091 $ log '9%5'
1092 2
1092 2
1093 4
1093 4
1094 8
1094 8
1095 9
1095 9
1096 $ log '(7 + 9)%(5 + 2)'
1096 $ log '(7 + 9)%(5 + 2)'
1097 4
1097 4
1098 6
1098 6
1099 7
1099 7
1100 8
1100 8
1101 9
1101 9
1102
1102
1103 Test operand of '%' is optimized recursively (issue4670)
1103 Test operand of '%' is optimized recursively (issue4670)
1104
1104
1105 $ try --optimize '8:9-8%'
1105 $ try --optimize '8:9-8%'
1106 (onlypost
1106 (onlypost
1107 (minus
1107 (minus
1108 (range
1108 (range
1109 ('symbol', '8')
1109 ('symbol', '8')
1110 ('symbol', '9'))
1110 ('symbol', '9'))
1111 ('symbol', '8')))
1111 ('symbol', '8')))
1112 * optimized:
1112 * optimized:
1113 (func
1113 (func
1114 ('symbol', 'only')
1114 ('symbol', 'only')
1115 (difference
1115 (difference
1116 (range
1116 (range
1117 ('symbol', '8')
1117 ('symbol', '8')
1118 ('symbol', '9')
1118 ('symbol', '9')
1119 define)
1119 define)
1120 ('symbol', '8')
1120 ('symbol', '8')
1121 define)
1121 define)
1122 define)
1122 define)
1123 * set:
1123 * set:
1124 <baseset+ [8, 9]>
1124 <baseset+ [8, 9]>
1125 8
1125 8
1126 9
1126 9
1127 $ try --optimize '(9)%(5)'
1127 $ try --optimize '(9)%(5)'
1128 (only
1128 (only
1129 (group
1129 (group
1130 ('symbol', '9'))
1130 ('symbol', '9'))
1131 (group
1131 (group
1132 ('symbol', '5')))
1132 ('symbol', '5')))
1133 * optimized:
1133 * optimized:
1134 (func
1134 (func
1135 ('symbol', 'only')
1135 ('symbol', 'only')
1136 (list
1136 (list
1137 ('symbol', '9')
1137 ('symbol', '9')
1138 ('symbol', '5'))
1138 ('symbol', '5'))
1139 define)
1139 define)
1140 * set:
1140 * set:
1141 <baseset+ [2, 4, 8, 9]>
1141 <baseset+ [2, 4, 8, 9]>
1142 2
1142 2
1143 4
1143 4
1144 8
1144 8
1145 9
1145 9
1146
1146
1147 Test the order of operations
1147 Test the order of operations
1148
1148
1149 $ log '7 + 9%5 + 2'
1149 $ log '7 + 9%5 + 2'
1150 7
1150 7
1151 2
1151 2
1152 4
1152 4
1153 8
1153 8
1154 9
1154 9
1155
1155
1156 Test explicit numeric revision
1156 Test explicit numeric revision
1157 $ log 'rev(-2)'
1157 $ log 'rev(-2)'
1158 $ log 'rev(-1)'
1158 $ log 'rev(-1)'
1159 -1
1159 -1
1160 $ log 'rev(0)'
1160 $ log 'rev(0)'
1161 0
1161 0
1162 $ log 'rev(9)'
1162 $ log 'rev(9)'
1163 9
1163 9
1164 $ log 'rev(10)'
1164 $ log 'rev(10)'
1165 $ log 'rev(tip)'
1165 $ log 'rev(tip)'
1166 hg: parse error: rev expects a number
1166 hg: parse error: rev expects a number
1167 [255]
1167 [255]
1168
1168
1169 Test hexadecimal revision
1169 Test hexadecimal revision
1170 $ log 'id(2)'
1170 $ log 'id(2)'
1171 abort: 00changelog.i@2: ambiguous identifier!
1171 abort: 00changelog.i@2: ambiguous identifier!
1172 [255]
1172 [255]
1173 $ log 'id(23268)'
1173 $ log 'id(23268)'
1174 4
1174 4
1175 $ log 'id(2785f51eece)'
1175 $ log 'id(2785f51eece)'
1176 0
1176 0
1177 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1177 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1178 8
1178 8
1179 $ log 'id(d5d0dcbdc4a)'
1179 $ log 'id(d5d0dcbdc4a)'
1180 $ log 'id(d5d0dcbdc4w)'
1180 $ log 'id(d5d0dcbdc4w)'
1181 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1181 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1182 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1182 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1183 $ log 'id(1.0)'
1183 $ log 'id(1.0)'
1184 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1184 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1185
1185
1186 Test null revision
1186 Test null revision
1187 $ log '(null)'
1187 $ log '(null)'
1188 -1
1188 -1
1189 $ log '(null:0)'
1189 $ log '(null:0)'
1190 -1
1190 -1
1191 0
1191 0
1192 $ log '(0:null)'
1192 $ log '(0:null)'
1193 0
1193 0
1194 -1
1194 -1
1195 $ log 'null::0'
1195 $ log 'null::0'
1196 -1
1196 -1
1197 0
1197 0
1198 $ log 'null:tip - 0:'
1198 $ log 'null:tip - 0:'
1199 -1
1199 -1
1200 $ log 'null: and null::' | head -1
1200 $ log 'null: and null::' | head -1
1201 -1
1201 -1
1202 $ log 'null: or 0:' | head -2
1202 $ log 'null: or 0:' | head -2
1203 -1
1203 -1
1204 0
1204 0
1205 $ log 'ancestors(null)'
1205 $ log 'ancestors(null)'
1206 -1
1206 -1
1207 $ log 'reverse(null:)' | tail -2
1207 $ log 'reverse(null:)' | tail -2
1208 0
1208 0
1209 -1
1209 -1
1210 BROKEN: should be '-1'
1210 BROKEN: should be '-1'
1211 $ log 'first(null:)'
1211 $ log 'first(null:)'
1212 BROKEN: should be '-1'
1212 BROKEN: should be '-1'
1213 $ log 'min(null:)'
1213 $ log 'min(null:)'
1214 $ log 'tip:null and all()' | tail -2
1214 $ log 'tip:null and all()' | tail -2
1215 1
1215 1
1216 0
1216 0
1217
1217
1218 Test working-directory revision
1218 Test working-directory revision
1219 $ hg debugrevspec 'wdir()'
1219 $ hg debugrevspec 'wdir()'
1220 2147483647
1220 2147483647
1221 $ hg debugrevspec 'tip or wdir()'
1221 $ hg debugrevspec 'tip or wdir()'
1222 9
1222 9
1223 2147483647
1223 2147483647
1224 $ hg debugrevspec '0:tip and wdir()'
1224 $ hg debugrevspec '0:tip and wdir()'
1225 $ log '0:wdir()' | tail -3
1225 $ log '0:wdir()' | tail -3
1226 8
1226 8
1227 9
1227 9
1228 2147483647
1228 2147483647
1229 $ log 'wdir():0' | head -3
1229 $ log 'wdir():0' | head -3
1230 2147483647
1230 2147483647
1231 9
1231 9
1232 8
1232 8
1233 $ log 'wdir():wdir()'
1233 $ log 'wdir():wdir()'
1234 2147483647
1234 2147483647
1235 $ log '(all() + wdir()) & min(. + wdir())'
1235 $ log '(all() + wdir()) & min(. + wdir())'
1236 9
1236 9
1237 $ log '(all() + wdir()) & max(. + wdir())'
1237 $ log '(all() + wdir()) & max(. + wdir())'
1238 2147483647
1238 2147483647
1239 $ log '(all() + wdir()) & first(wdir() + .)'
1239 $ log '(all() + wdir()) & first(wdir() + .)'
1240 2147483647
1240 2147483647
1241 $ log '(all() + wdir()) & last(. + wdir())'
1241 $ log '(all() + wdir()) & last(. + wdir())'
1242 2147483647
1242 2147483647
1243
1243
1244 $ log 'outgoing()'
1244 $ log 'outgoing()'
1245 8
1245 8
1246 9
1246 9
1247 $ log 'outgoing("../remote1")'
1247 $ log 'outgoing("../remote1")'
1248 8
1248 8
1249 9
1249 9
1250 $ log 'outgoing("../remote2")'
1250 $ log 'outgoing("../remote2")'
1251 3
1251 3
1252 5
1252 5
1253 6
1253 6
1254 7
1254 7
1255 9
1255 9
1256 $ log 'p1(merge())'
1256 $ log 'p1(merge())'
1257 5
1257 5
1258 $ log 'p2(merge())'
1258 $ log 'p2(merge())'
1259 4
1259 4
1260 $ log 'parents(merge())'
1260 $ log 'parents(merge())'
1261 4
1261 4
1262 5
1262 5
1263 $ log 'p1(branchpoint())'
1263 $ log 'p1(branchpoint())'
1264 0
1264 0
1265 2
1265 2
1266 $ log 'p2(branchpoint())'
1266 $ log 'p2(branchpoint())'
1267 $ log 'parents(branchpoint())'
1267 $ log 'parents(branchpoint())'
1268 0
1268 0
1269 2
1269 2
1270 $ log 'removes(a)'
1270 $ log 'removes(a)'
1271 2
1271 2
1272 6
1272 6
1273 $ log 'roots(all())'
1273 $ log 'roots(all())'
1274 0
1274 0
1275 $ log 'reverse(2 or 3 or 4 or 5)'
1275 $ log 'reverse(2 or 3 or 4 or 5)'
1276 5
1276 5
1277 4
1277 4
1278 3
1278 3
1279 2
1279 2
1280 $ log 'reverse(all())'
1280 $ log 'reverse(all())'
1281 9
1281 9
1282 8
1282 8
1283 7
1283 7
1284 6
1284 6
1285 5
1285 5
1286 4
1286 4
1287 3
1287 3
1288 2
1288 2
1289 1
1289 1
1290 0
1290 0
1291 $ log 'reverse(all()) & filelog(b)'
1291 $ log 'reverse(all()) & filelog(b)'
1292 4
1292 4
1293 1
1293 1
1294 $ log 'rev(5)'
1294 $ log 'rev(5)'
1295 5
1295 5
1296 $ log 'sort(limit(reverse(all()), 3))'
1296 $ log 'sort(limit(reverse(all()), 3))'
1297 7
1297 7
1298 8
1298 8
1299 9
1299 9
1300 $ log 'sort(2 or 3 or 4 or 5, date)'
1300 $ log 'sort(2 or 3 or 4 or 5, date)'
1301 2
1301 2
1302 3
1302 3
1303 5
1303 5
1304 4
1304 4
1305 $ log 'tagged()'
1305 $ log 'tagged()'
1306 6
1306 6
1307 $ log 'tag()'
1307 $ log 'tag()'
1308 6
1308 6
1309 $ log 'tag(1.0)'
1309 $ log 'tag(1.0)'
1310 6
1310 6
1311 $ log 'tag(tip)'
1311 $ log 'tag(tip)'
1312 9
1312 9
1313
1313
1314 Test order of revisions in compound expression
1314 Test order of revisions in compound expression
1315 ----------------------------------------------
1315 ----------------------------------------------
1316
1316
1317 The general rule is that only the outermost (= leftmost) predicate can
1317 The general rule is that only the outermost (= leftmost) predicate can
1318 enforce its ordering requirement. The other predicates should take the
1318 enforce its ordering requirement. The other predicates should take the
1319 ordering defined by it.
1319 ordering defined by it.
1320
1320
1321 'A & B' should follow the order of 'A':
1321 'A & B' should follow the order of 'A':
1322
1322
1323 $ log '2:0 & 0::2'
1323 $ log '2:0 & 0::2'
1324 2
1324 2
1325 1
1325 1
1326 0
1326 0
1327
1327
1328 'head()' combines sets in right order:
1328 'head()' combines sets in right order:
1329
1329
1330 $ log '2:0 & head()'
1330 $ log '2:0 & head()'
1331 2
1331 2
1332 1
1332 1
1333 0
1333 0
1334
1334
1335 'x:y' takes ordering parameter into account:
1335 'x:y' takes ordering parameter into account:
1336
1336
1337 $ try -p optimized '3:0 & 0:3 & not 2:1'
1337 $ try -p optimized '3:0 & 0:3 & not 2:1'
1338 * optimized:
1338 * optimized:
1339 (difference
1339 (difference
1340 (and
1340 (and
1341 (range
1341 (range
1342 ('symbol', '3')
1342 ('symbol', '3')
1343 ('symbol', '0')
1343 ('symbol', '0')
1344 define)
1344 define)
1345 (range
1345 (range
1346 ('symbol', '0')
1346 ('symbol', '0')
1347 ('symbol', '3')
1347 ('symbol', '3')
1348 follow)
1348 follow)
1349 define)
1349 define)
1350 (range
1350 (range
1351 ('symbol', '2')
1351 ('symbol', '2')
1352 ('symbol', '1')
1352 ('symbol', '1')
1353 any)
1353 any)
1354 define)
1354 define)
1355 * set:
1355 * set:
1356 <filteredset
1356 <filteredset
1357 <filteredset
1357 <filteredset
1358 <spanset- 0:3>,
1358 <spanset- 0:3>,
1359 <spanset+ 0:3>>,
1359 <spanset+ 0:3>>,
1360 <not
1360 <not
1361 <spanset+ 1:2>>>
1361 <spanset+ 1:2>>>
1362 3
1362 3
1363 0
1363 0
1364
1364
1365 'a + b', which is optimized to '_list(a b)', should take the ordering of
1365 'a + b', which is optimized to '_list(a b)', should take the ordering of
1366 the left expression:
1366 the left expression:
1367
1367
1368 $ try --optimize '2:0 & (0 + 1 + 2)'
1368 $ try --optimize '2:0 & (0 + 1 + 2)'
1369 (and
1369 (and
1370 (range
1370 (range
1371 ('symbol', '2')
1371 ('symbol', '2')
1372 ('symbol', '0'))
1372 ('symbol', '0'))
1373 (group
1373 (group
1374 (or
1374 (or
1375 (list
1375 (list
1376 ('symbol', '0')
1376 ('symbol', '0')
1377 ('symbol', '1')
1377 ('symbol', '1')
1378 ('symbol', '2')))))
1378 ('symbol', '2')))))
1379 * optimized:
1379 * optimized:
1380 (and
1380 (and
1381 (range
1381 (range
1382 ('symbol', '2')
1382 ('symbol', '2')
1383 ('symbol', '0')
1383 ('symbol', '0')
1384 define)
1384 define)
1385 (func
1385 (func
1386 ('symbol', '_list')
1386 ('symbol', '_list')
1387 ('string', '0\x001\x002')
1387 ('string', '0\x001\x002')
1388 follow)
1388 follow)
1389 define)
1389 define)
1390 * set:
1390 * set:
1391 <filteredset
1391 <filteredset
1392 <spanset- 0:2>,
1392 <spanset- 0:2>,
1393 <baseset [0, 1, 2]>>
1393 <baseset [0, 1, 2]>>
1394 2
1394 2
1395 1
1395 1
1396 0
1396 0
1397
1397
1398 'A + B' should take the ordering of the left expression:
1398 'A + B' should take the ordering of the left expression:
1399
1399
1400 $ try --optimize '2:0 & (0:1 + 2)'
1400 $ try --optimize '2:0 & (0:1 + 2)'
1401 (and
1401 (and
1402 (range
1402 (range
1403 ('symbol', '2')
1403 ('symbol', '2')
1404 ('symbol', '0'))
1404 ('symbol', '0'))
1405 (group
1405 (group
1406 (or
1406 (or
1407 (list
1407 (list
1408 (range
1408 (range
1409 ('symbol', '0')
1409 ('symbol', '0')
1410 ('symbol', '1'))
1410 ('symbol', '1'))
1411 ('symbol', '2')))))
1411 ('symbol', '2')))))
1412 * optimized:
1412 * optimized:
1413 (and
1413 (and
1414 (range
1414 (range
1415 ('symbol', '2')
1415 ('symbol', '2')
1416 ('symbol', '0')
1416 ('symbol', '0')
1417 define)
1417 define)
1418 (or
1418 (or
1419 (list
1419 (list
1420 (range
1420 (range
1421 ('symbol', '0')
1421 ('symbol', '0')
1422 ('symbol', '1')
1422 ('symbol', '1')
1423 follow)
1423 follow)
1424 ('symbol', '2'))
1424 ('symbol', '2'))
1425 follow)
1425 follow)
1426 define)
1426 define)
1427 * set:
1427 * set:
1428 <filteredset
1428 <filteredset
1429 <spanset- 0:2>,
1429 <spanset- 0:2>,
1430 <addset
1430 <addset
1431 <spanset+ 0:1>,
1431 <spanset+ 0:1>,
1432 <baseset [2]>>>
1432 <baseset [2]>>>
1433 2
1433 2
1434 1
1434 1
1435 0
1435 0
1436
1436
1437 '_intlist(a b)' should behave like 'a + b':
1437 '_intlist(a b)' should behave like 'a + b':
1438
1438
1439 $ trylist --optimize '2:0 & %ld' 0 1 2
1439 $ trylist --optimize '2:0 & %ld' 0 1 2
1440 (and
1440 (and
1441 (range
1441 (range
1442 ('symbol', '2')
1442 ('symbol', '2')
1443 ('symbol', '0'))
1443 ('symbol', '0'))
1444 (func
1444 (func
1445 ('symbol', '_intlist')
1445 ('symbol', '_intlist')
1446 ('string', '0\x001\x002')))
1446 ('string', '0\x001\x002')))
1447 * optimized:
1447 * optimized:
1448 (and
1448 (and
1449 (func
1449 (func
1450 ('symbol', '_intlist')
1450 ('symbol', '_intlist')
1451 ('string', '0\x001\x002')
1451 ('string', '0\x001\x002')
1452 follow)
1452 follow)
1453 (range
1453 (range
1454 ('symbol', '2')
1454 ('symbol', '2')
1455 ('symbol', '0')
1455 ('symbol', '0')
1456 define)
1456 define)
1457 define)
1457 define)
1458 * set:
1458 * set:
1459 <filteredset
1459 <filteredset
1460 <spanset- 0:2>,
1460 <spanset- 0:2>,
1461 <baseset+ [0, 1, 2]>>
1461 <baseset+ [0, 1, 2]>>
1462 2
1462 2
1463 1
1463 1
1464 0
1464 0
1465
1465
1466 $ trylist --optimize '%ld & 2:0' 0 2 1
1466 $ trylist --optimize '%ld & 2:0' 0 2 1
1467 (and
1467 (and
1468 (func
1468 (func
1469 ('symbol', '_intlist')
1469 ('symbol', '_intlist')
1470 ('string', '0\x002\x001'))
1470 ('string', '0\x002\x001'))
1471 (range
1471 (range
1472 ('symbol', '2')
1472 ('symbol', '2')
1473 ('symbol', '0')))
1473 ('symbol', '0')))
1474 * optimized:
1474 * optimized:
1475 (and
1475 (and
1476 (func
1476 (func
1477 ('symbol', '_intlist')
1477 ('symbol', '_intlist')
1478 ('string', '0\x002\x001')
1478 ('string', '0\x002\x001')
1479 define)
1479 define)
1480 (range
1480 (range
1481 ('symbol', '2')
1481 ('symbol', '2')
1482 ('symbol', '0')
1482 ('symbol', '0')
1483 follow)
1483 follow)
1484 define)
1484 define)
1485 * set:
1485 * set:
1486 <filteredset
1486 <filteredset
1487 <baseset [0, 2, 1]>,
1487 <baseset [0, 2, 1]>,
1488 <spanset- 0:2>>
1488 <spanset- 0:2>>
1489 0
1489 0
1490 2
1490 2
1491 1
1491 1
1492
1492
1493 '_hexlist(a b)' should behave like 'a + b':
1493 '_hexlist(a b)' should behave like 'a + b':
1494
1494
1495 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1495 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1496 (and
1496 (and
1497 (range
1497 (range
1498 ('symbol', '2')
1498 ('symbol', '2')
1499 ('symbol', '0'))
1499 ('symbol', '0'))
1500 (func
1500 (func
1501 ('symbol', '_hexlist')
1501 ('symbol', '_hexlist')
1502 ('string', '*'))) (glob)
1502 ('string', '*'))) (glob)
1503 * optimized:
1503 * optimized:
1504 (and
1504 (and
1505 (range
1505 (range
1506 ('symbol', '2')
1506 ('symbol', '2')
1507 ('symbol', '0')
1507 ('symbol', '0')
1508 define)
1508 define)
1509 (func
1509 (func
1510 ('symbol', '_hexlist')
1510 ('symbol', '_hexlist')
1511 ('string', '*') (glob)
1511 ('string', '*') (glob)
1512 follow)
1512 follow)
1513 define)
1513 define)
1514 * set:
1514 * set:
1515 <filteredset
1515 <filteredset
1516 <spanset- 0:2>,
1516 <spanset- 0:2>,
1517 <baseset [0, 1, 2]>>
1517 <baseset [0, 1, 2]>>
1518 2
1518 2
1519 1
1519 1
1520 0
1520 0
1521
1521
1522 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1522 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1523 (and
1523 (and
1524 (func
1524 (func
1525 ('symbol', '_hexlist')
1525 ('symbol', '_hexlist')
1526 ('string', '*')) (glob)
1526 ('string', '*')) (glob)
1527 (range
1527 (range
1528 ('symbol', '2')
1528 ('symbol', '2')
1529 ('symbol', '0')))
1529 ('symbol', '0')))
1530 * optimized:
1530 * optimized:
1531 (and
1531 (and
1532 (range
1532 (range
1533 ('symbol', '2')
1533 ('symbol', '2')
1534 ('symbol', '0')
1534 ('symbol', '0')
1535 follow)
1535 follow)
1536 (func
1536 (func
1537 ('symbol', '_hexlist')
1537 ('symbol', '_hexlist')
1538 ('string', '*') (glob)
1538 ('string', '*') (glob)
1539 define)
1539 define)
1540 define)
1540 define)
1541 * set:
1541 * set:
1542 <baseset [0, 2, 1]>
1542 <baseset [0, 2, 1]>
1543 0
1543 0
1544 2
1544 2
1545 1
1545 1
1546
1546
1547 '_list' should not go through the slow follow-order path if order doesn't
1547 '_list' should not go through the slow follow-order path if order doesn't
1548 matter:
1548 matter:
1549
1549
1550 $ try -p optimized '2:0 & not (0 + 1)'
1550 $ try -p optimized '2:0 & not (0 + 1)'
1551 * optimized:
1551 * optimized:
1552 (difference
1552 (difference
1553 (range
1553 (range
1554 ('symbol', '2')
1554 ('symbol', '2')
1555 ('symbol', '0')
1555 ('symbol', '0')
1556 define)
1556 define)
1557 (func
1557 (func
1558 ('symbol', '_list')
1558 ('symbol', '_list')
1559 ('string', '0\x001')
1559 ('string', '0\x001')
1560 any)
1560 any)
1561 define)
1561 define)
1562 * set:
1562 * set:
1563 <filteredset
1563 <filteredset
1564 <spanset- 0:2>,
1564 <spanset- 0:2>,
1565 <not
1565 <not
1566 <baseset [0, 1]>>>
1566 <baseset [0, 1]>>>
1567 2
1567 2
1568
1568
1569 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1569 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1570 * optimized:
1570 * optimized:
1571 (difference
1571 (difference
1572 (range
1572 (range
1573 ('symbol', '2')
1573 ('symbol', '2')
1574 ('symbol', '0')
1574 ('symbol', '0')
1575 define)
1575 define)
1576 (and
1576 (and
1577 (range
1577 (range
1578 ('symbol', '0')
1578 ('symbol', '0')
1579 ('symbol', '2')
1579 ('symbol', '2')
1580 any)
1580 any)
1581 (func
1581 (func
1582 ('symbol', '_list')
1582 ('symbol', '_list')
1583 ('string', '0\x001')
1583 ('string', '0\x001')
1584 any)
1584 any)
1585 any)
1585 any)
1586 define)
1586 define)
1587 * set:
1587 * set:
1588 <filteredset
1588 <filteredset
1589 <spanset- 0:2>,
1589 <spanset- 0:2>,
1590 <not
1590 <not
1591 <baseset [0, 1]>>>
1591 <baseset [0, 1]>>>
1592 2
1592 2
1593
1593
1594 because 'present()' does nothing other than suppressing an error, the
1594 because 'present()' does nothing other than suppressing an error, the
1595 ordering requirement should be forwarded to the nested expression
1595 ordering requirement should be forwarded to the nested expression
1596
1596
1597 $ try -p optimized 'present(2 + 0 + 1)'
1597 $ try -p optimized 'present(2 + 0 + 1)'
1598 * optimized:
1598 * optimized:
1599 (func
1599 (func
1600 ('symbol', 'present')
1600 ('symbol', 'present')
1601 (func
1601 (func
1602 ('symbol', '_list')
1602 ('symbol', '_list')
1603 ('string', '2\x000\x001')
1603 ('string', '2\x000\x001')
1604 define)
1604 define)
1605 define)
1605 define)
1606 * set:
1606 * set:
1607 <baseset [2, 0, 1]>
1607 <baseset [2, 0, 1]>
1608 2
1608 2
1609 0
1609 0
1610 1
1610 1
1611
1611
1612 $ try --optimize '2:0 & present(0 + 1 + 2)'
1612 $ try --optimize '2:0 & present(0 + 1 + 2)'
1613 (and
1613 (and
1614 (range
1614 (range
1615 ('symbol', '2')
1615 ('symbol', '2')
1616 ('symbol', '0'))
1616 ('symbol', '0'))
1617 (func
1617 (func
1618 ('symbol', 'present')
1618 ('symbol', 'present')
1619 (or
1619 (or
1620 (list
1620 (list
1621 ('symbol', '0')
1621 ('symbol', '0')
1622 ('symbol', '1')
1622 ('symbol', '1')
1623 ('symbol', '2')))))
1623 ('symbol', '2')))))
1624 * optimized:
1624 * optimized:
1625 (and
1625 (and
1626 (range
1626 (range
1627 ('symbol', '2')
1627 ('symbol', '2')
1628 ('symbol', '0')
1628 ('symbol', '0')
1629 define)
1629 define)
1630 (func
1630 (func
1631 ('symbol', 'present')
1631 ('symbol', 'present')
1632 (func
1632 (func
1633 ('symbol', '_list')
1633 ('symbol', '_list')
1634 ('string', '0\x001\x002')
1634 ('string', '0\x001\x002')
1635 follow)
1635 follow)
1636 follow)
1636 follow)
1637 define)
1637 define)
1638 * set:
1638 * set:
1639 <filteredset
1639 <filteredset
1640 <spanset- 0:2>,
1640 <spanset- 0:2>,
1641 <baseset [0, 1, 2]>>
1641 <baseset [0, 1, 2]>>
1642 2
1642 2
1643 1
1643 1
1644 0
1644 0
1645
1645
1646 'reverse()' should take effect only if it is the outermost expression:
1646 'reverse()' should take effect only if it is the outermost expression:
1647
1647
1648 $ try --optimize '0:2 & reverse(all())'
1648 $ try --optimize '0:2 & reverse(all())'
1649 (and
1649 (and
1650 (range
1650 (range
1651 ('symbol', '0')
1651 ('symbol', '0')
1652 ('symbol', '2'))
1652 ('symbol', '2'))
1653 (func
1653 (func
1654 ('symbol', 'reverse')
1654 ('symbol', 'reverse')
1655 (func
1655 (func
1656 ('symbol', 'all')
1656 ('symbol', 'all')
1657 None)))
1657 None)))
1658 * optimized:
1658 * optimized:
1659 (and
1659 (and
1660 (range
1660 (range
1661 ('symbol', '0')
1661 ('symbol', '0')
1662 ('symbol', '2')
1662 ('symbol', '2')
1663 define)
1663 define)
1664 (func
1664 (func
1665 ('symbol', 'reverse')
1665 ('symbol', 'reverse')
1666 (func
1666 (func
1667 ('symbol', 'all')
1667 ('symbol', 'all')
1668 None
1668 None
1669 define)
1669 define)
1670 follow)
1670 follow)
1671 define)
1671 define)
1672 * set:
1672 * set:
1673 <filteredset
1673 <filteredset
1674 <spanset+ 0:2>,
1674 <spanset+ 0:2>,
1675 <spanset+ 0:9>>
1675 <spanset+ 0:9>>
1676 0
1676 0
1677 1
1677 1
1678 2
1678 2
1679
1679
1680 'sort()' should take effect only if it is the outermost expression:
1680 'sort()' should take effect only if it is the outermost expression:
1681
1681
1682 $ try --optimize '0:2 & sort(all(), -rev)'
1682 $ try --optimize '0:2 & sort(all(), -rev)'
1683 (and
1683 (and
1684 (range
1684 (range
1685 ('symbol', '0')
1685 ('symbol', '0')
1686 ('symbol', '2'))
1686 ('symbol', '2'))
1687 (func
1687 (func
1688 ('symbol', 'sort')
1688 ('symbol', 'sort')
1689 (list
1689 (list
1690 (func
1690 (func
1691 ('symbol', 'all')
1691 ('symbol', 'all')
1692 None)
1692 None)
1693 (negate
1693 (negate
1694 ('symbol', 'rev')))))
1694 ('symbol', 'rev')))))
1695 * optimized:
1695 * optimized:
1696 (and
1696 (and
1697 (range
1697 (range
1698 ('symbol', '0')
1698 ('symbol', '0')
1699 ('symbol', '2')
1699 ('symbol', '2')
1700 define)
1700 define)
1701 (func
1701 (func
1702 ('symbol', 'sort')
1702 ('symbol', 'sort')
1703 (list
1703 (list
1704 (func
1704 (func
1705 ('symbol', 'all')
1705 ('symbol', 'all')
1706 None
1706 None
1707 define)
1707 define)
1708 ('string', '-rev'))
1708 ('string', '-rev'))
1709 follow)
1709 follow)
1710 define)
1710 define)
1711 * set:
1711 * set:
1712 <filteredset
1712 <filteredset
1713 <spanset+ 0:2>,
1713 <spanset+ 0:2>,
1714 <spanset+ 0:9>>
1714 <spanset+ 0:9>>
1715 0
1715 0
1716 1
1716 1
1717 2
1717 2
1718
1718
1719 invalid argument passed to noop sort():
1719 invalid argument passed to noop sort():
1720
1720
1721 $ log '0:2 & sort()'
1721 $ log '0:2 & sort()'
1722 hg: parse error: sort requires one or two arguments
1722 hg: parse error: sort requires one or two arguments
1723 [255]
1723 [255]
1724 $ log '0:2 & sort(all(), -invalid)'
1724 $ log '0:2 & sort(all(), -invalid)'
1725 hg: parse error: unknown sort key '-invalid'
1725 hg: parse error: unknown sort key '-invalid'
1726 [255]
1726 [255]
1727
1727
1728 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1728 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1729
1729
1730 $ try --optimize '2:0 & first(1 + 0 + 2)'
1730 $ try --optimize '2:0 & first(1 + 0 + 2)'
1731 (and
1731 (and
1732 (range
1732 (range
1733 ('symbol', '2')
1733 ('symbol', '2')
1734 ('symbol', '0'))
1734 ('symbol', '0'))
1735 (func
1735 (func
1736 ('symbol', 'first')
1736 ('symbol', 'first')
1737 (or
1737 (or
1738 (list
1738 (list
1739 ('symbol', '1')
1739 ('symbol', '1')
1740 ('symbol', '0')
1740 ('symbol', '0')
1741 ('symbol', '2')))))
1741 ('symbol', '2')))))
1742 * optimized:
1742 * optimized:
1743 (and
1743 (and
1744 (range
1744 (range
1745 ('symbol', '2')
1745 ('symbol', '2')
1746 ('symbol', '0')
1746 ('symbol', '0')
1747 define)
1747 define)
1748 (func
1748 (func
1749 ('symbol', 'first')
1749 ('symbol', 'first')
1750 (func
1750 (func
1751 ('symbol', '_list')
1751 ('symbol', '_list')
1752 ('string', '1\x000\x002')
1752 ('string', '1\x000\x002')
1753 define)
1753 define)
1754 follow)
1754 follow)
1755 define)
1755 define)
1756 * set:
1756 * set:
1757 <baseset
1757 <baseset
1758 <limit n=1, offset=0,
1758 <limit n=1, offset=0,
1759 <spanset- 0:2>,
1759 <spanset- 0:2>,
1760 <baseset [1, 0, 2]>>>
1760 <baseset [1, 0, 2]>>>
1761 1
1761 1
1762
1762
1763 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1763 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1764 (and
1764 (and
1765 (range
1765 (range
1766 ('symbol', '2')
1766 ('symbol', '2')
1767 ('symbol', '0'))
1767 ('symbol', '0'))
1768 (not
1768 (not
1769 (func
1769 (func
1770 ('symbol', 'last')
1770 ('symbol', 'last')
1771 (or
1771 (or
1772 (list
1772 (list
1773 ('symbol', '0')
1773 ('symbol', '0')
1774 ('symbol', '2')
1774 ('symbol', '2')
1775 ('symbol', '1'))))))
1775 ('symbol', '1'))))))
1776 * optimized:
1776 * optimized:
1777 (difference
1777 (difference
1778 (range
1778 (range
1779 ('symbol', '2')
1779 ('symbol', '2')
1780 ('symbol', '0')
1780 ('symbol', '0')
1781 define)
1781 define)
1782 (func
1782 (func
1783 ('symbol', 'last')
1783 ('symbol', 'last')
1784 (func
1784 (func
1785 ('symbol', '_list')
1785 ('symbol', '_list')
1786 ('string', '0\x002\x001')
1786 ('string', '0\x002\x001')
1787 define)
1787 define)
1788 any)
1788 any)
1789 define)
1789 define)
1790 * set:
1790 * set:
1791 <filteredset
1791 <filteredset
1792 <spanset- 0:2>,
1792 <spanset- 0:2>,
1793 <not
1793 <not
1794 <baseset
1794 <baseset
1795 <last n=1,
1795 <last n=1,
1796 <fullreposet+ 0:9>,
1796 <fullreposet+ 0:9>,
1797 <baseset [1, 2, 0]>>>>>
1797 <baseset [1, 2, 0]>>>>>
1798 2
1798 2
1799 0
1799 0
1800
1800
1801 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1801 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1802
1802
1803 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1803 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1804 (and
1804 (and
1805 (range
1805 (range
1806 ('symbol', '2')
1806 ('symbol', '2')
1807 ('symbol', '0'))
1807 ('symbol', '0'))
1808 (range
1808 (range
1809 (group
1809 (group
1810 (or
1810 (or
1811 (list
1811 (list
1812 ('symbol', '1')
1812 ('symbol', '1')
1813 ('symbol', '0')
1813 ('symbol', '0')
1814 ('symbol', '2'))))
1814 ('symbol', '2'))))
1815 (group
1815 (group
1816 (or
1816 (or
1817 (list
1817 (list
1818 ('symbol', '0')
1818 ('symbol', '0')
1819 ('symbol', '2')
1819 ('symbol', '2')
1820 ('symbol', '1'))))))
1820 ('symbol', '1'))))))
1821 * optimized:
1821 * optimized:
1822 (and
1822 (and
1823 (range
1823 (range
1824 ('symbol', '2')
1824 ('symbol', '2')
1825 ('symbol', '0')
1825 ('symbol', '0')
1826 define)
1826 define)
1827 (range
1827 (range
1828 (func
1828 (func
1829 ('symbol', '_list')
1829 ('symbol', '_list')
1830 ('string', '1\x000\x002')
1830 ('string', '1\x000\x002')
1831 define)
1831 define)
1832 (func
1832 (func
1833 ('symbol', '_list')
1833 ('symbol', '_list')
1834 ('string', '0\x002\x001')
1834 ('string', '0\x002\x001')
1835 define)
1835 define)
1836 follow)
1836 follow)
1837 define)
1837 define)
1838 * set:
1838 * set:
1839 <filteredset
1839 <filteredset
1840 <spanset- 0:2>,
1840 <spanset- 0:2>,
1841 <baseset [1]>>
1841 <baseset [1]>>
1842 1
1842 1
1843
1843
1844 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1844 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
1845 the ordering rule is determined before the rewrite; in this example,
1845 the ordering rule is determined before the rewrite; in this example,
1846 'B' follows the order of the initial set, which is the same order as 'A'
1846 'B' follows the order of the initial set, which is the same order as 'A'
1847 since 'A' also follows the order:
1847 since 'A' also follows the order:
1848
1848
1849 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1849 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1850 (and
1850 (and
1851 (func
1851 (func
1852 ('symbol', 'contains')
1852 ('symbol', 'contains')
1853 ('string', 'glob:*'))
1853 ('string', 'glob:*'))
1854 (group
1854 (group
1855 (or
1855 (or
1856 (list
1856 (list
1857 ('symbol', '2')
1857 ('symbol', '2')
1858 ('symbol', '0')
1858 ('symbol', '0')
1859 ('symbol', '1')))))
1859 ('symbol', '1')))))
1860 * optimized:
1860 * optimized:
1861 (and
1861 (and
1862 (func
1862 (func
1863 ('symbol', '_list')
1863 ('symbol', '_list')
1864 ('string', '2\x000\x001')
1864 ('string', '2\x000\x001')
1865 follow)
1865 follow)
1866 (func
1866 (func
1867 ('symbol', 'contains')
1867 ('symbol', 'contains')
1868 ('string', 'glob:*')
1868 ('string', 'glob:*')
1869 define)
1869 define)
1870 define)
1870 define)
1871 * set:
1871 * set:
1872 <filteredset
1872 <filteredset
1873 <baseset+ [0, 1, 2]>,
1873 <baseset+ [0, 1, 2]>,
1874 <contains 'glob:*'>>
1874 <contains 'glob:*'>>
1875 0
1875 0
1876 1
1876 1
1877 2
1877 2
1878
1878
1879 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1879 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
1880 the order appropriately:
1880 the order appropriately:
1881
1881
1882 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1882 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1883 (and
1883 (and
1884 (func
1884 (func
1885 ('symbol', 'reverse')
1885 ('symbol', 'reverse')
1886 (func
1886 (func
1887 ('symbol', 'contains')
1887 ('symbol', 'contains')
1888 ('string', 'glob:*')))
1888 ('string', 'glob:*')))
1889 (group
1889 (group
1890 (or
1890 (or
1891 (list
1891 (list
1892 ('symbol', '0')
1892 ('symbol', '0')
1893 ('symbol', '2')
1893 ('symbol', '2')
1894 ('symbol', '1')))))
1894 ('symbol', '1')))))
1895 * optimized:
1895 * optimized:
1896 (and
1896 (and
1897 (func
1897 (func
1898 ('symbol', '_list')
1898 ('symbol', '_list')
1899 ('string', '0\x002\x001')
1899 ('string', '0\x002\x001')
1900 follow)
1900 follow)
1901 (func
1901 (func
1902 ('symbol', 'reverse')
1902 ('symbol', 'reverse')
1903 (func
1903 (func
1904 ('symbol', 'contains')
1904 ('symbol', 'contains')
1905 ('string', 'glob:*')
1905 ('string', 'glob:*')
1906 define)
1906 define)
1907 define)
1907 define)
1908 define)
1908 define)
1909 * set:
1909 * set:
1910 <filteredset
1910 <filteredset
1911 <baseset- [0, 1, 2]>,
1911 <baseset- [0, 1, 2]>,
1912 <contains 'glob:*'>>
1912 <contains 'glob:*'>>
1913 2
1913 2
1914 1
1914 1
1915 0
1915 0
1916
1916
1917 test sort revset
1917 test sort revset
1918 --------------------------------------------
1918 --------------------------------------------
1919
1919
1920 test when adding two unordered revsets
1920 test when adding two unordered revsets
1921
1921
1922 $ log 'sort(keyword(issue) or modifies(b))'
1922 $ log 'sort(keyword(issue) or modifies(b))'
1923 4
1923 4
1924 6
1924 6
1925
1925
1926 test when sorting a reversed collection in the same way it is
1926 test when sorting a reversed collection in the same way it is
1927
1927
1928 $ log 'sort(reverse(all()), -rev)'
1928 $ log 'sort(reverse(all()), -rev)'
1929 9
1929 9
1930 8
1930 8
1931 7
1931 7
1932 6
1932 6
1933 5
1933 5
1934 4
1934 4
1935 3
1935 3
1936 2
1936 2
1937 1
1937 1
1938 0
1938 0
1939
1939
1940 test when sorting a reversed collection
1940 test when sorting a reversed collection
1941
1941
1942 $ log 'sort(reverse(all()), rev)'
1942 $ log 'sort(reverse(all()), rev)'
1943 0
1943 0
1944 1
1944 1
1945 2
1945 2
1946 3
1946 3
1947 4
1947 4
1948 5
1948 5
1949 6
1949 6
1950 7
1950 7
1951 8
1951 8
1952 9
1952 9
1953
1953
1954
1954
1955 test sorting two sorted collections in different orders
1955 test sorting two sorted collections in different orders
1956
1956
1957 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1957 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1958 2
1958 2
1959 6
1959 6
1960 8
1960 8
1961 9
1961 9
1962
1962
1963 test sorting two sorted collections in different orders backwards
1963 test sorting two sorted collections in different orders backwards
1964
1964
1965 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1965 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1966 9
1966 9
1967 8
1967 8
1968 6
1968 6
1969 2
1969 2
1970
1970
1971 test empty sort key which is noop
1971 test empty sort key which is noop
1972
1972
1973 $ log 'sort(0 + 2 + 1, "")'
1973 $ log 'sort(0 + 2 + 1, "")'
1974 0
1974 0
1975 2
1975 2
1976 1
1976 1
1977
1977
1978 test invalid sort keys
1978 test invalid sort keys
1979
1979
1980 $ log 'sort(all(), -invalid)'
1980 $ log 'sort(all(), -invalid)'
1981 hg: parse error: unknown sort key '-invalid'
1981 hg: parse error: unknown sort key '-invalid'
1982 [255]
1982 [255]
1983
1983
1984 $ cd ..
1984 $ cd ..
1985
1985
1986 test sorting by multiple keys including variable-length strings
1986 test sorting by multiple keys including variable-length strings
1987
1987
1988 $ hg init sorting
1988 $ hg init sorting
1989 $ cd sorting
1989 $ cd sorting
1990 $ cat <<EOF >> .hg/hgrc
1990 $ cat <<EOF >> .hg/hgrc
1991 > [ui]
1991 > [ui]
1992 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1992 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1993 > [templatealias]
1993 > [templatealias]
1994 > p5(s) = pad(s, 5)
1994 > p5(s) = pad(s, 5)
1995 > EOF
1995 > EOF
1996 $ hg branch -qf b12
1996 $ hg branch -qf b12
1997 $ hg ci -m m111 -u u112 -d '111 10800'
1997 $ hg ci -m m111 -u u112 -d '111 10800'
1998 $ hg branch -qf b11
1998 $ hg branch -qf b11
1999 $ hg ci -m m12 -u u111 -d '112 7200'
1999 $ hg ci -m m12 -u u111 -d '112 7200'
2000 $ hg branch -qf b111
2000 $ hg branch -qf b111
2001 $ hg ci -m m11 -u u12 -d '111 3600'
2001 $ hg ci -m m11 -u u12 -d '111 3600'
2002 $ hg branch -qf b112
2002 $ hg branch -qf b112
2003 $ hg ci -m m111 -u u11 -d '120 0'
2003 $ hg ci -m m111 -u u11 -d '120 0'
2004 $ hg branch -qf b111
2004 $ hg branch -qf b111
2005 $ hg ci -m m112 -u u111 -d '110 14400'
2005 $ hg ci -m m112 -u u111 -d '110 14400'
2006 created new head
2006 created new head
2007
2007
2008 compare revisions (has fast path):
2008 compare revisions (has fast path):
2009
2009
2010 $ hg log -r 'sort(all(), rev)'
2010 $ hg log -r 'sort(all(), rev)'
2011 0 b12 m111 u112 111 10800
2011 0 b12 m111 u112 111 10800
2012 1 b11 m12 u111 112 7200
2012 1 b11 m12 u111 112 7200
2013 2 b111 m11 u12 111 3600
2013 2 b111 m11 u12 111 3600
2014 3 b112 m111 u11 120 0
2014 3 b112 m111 u11 120 0
2015 4 b111 m112 u111 110 14400
2015 4 b111 m112 u111 110 14400
2016
2016
2017 $ hg log -r 'sort(all(), -rev)'
2017 $ hg log -r 'sort(all(), -rev)'
2018 4 b111 m112 u111 110 14400
2018 4 b111 m112 u111 110 14400
2019 3 b112 m111 u11 120 0
2019 3 b112 m111 u11 120 0
2020 2 b111 m11 u12 111 3600
2020 2 b111 m11 u12 111 3600
2021 1 b11 m12 u111 112 7200
2021 1 b11 m12 u111 112 7200
2022 0 b12 m111 u112 111 10800
2022 0 b12 m111 u112 111 10800
2023
2023
2024 compare variable-length strings (issue5218):
2024 compare variable-length strings (issue5218):
2025
2025
2026 $ hg log -r 'sort(all(), branch)'
2026 $ hg log -r 'sort(all(), branch)'
2027 1 b11 m12 u111 112 7200
2027 1 b11 m12 u111 112 7200
2028 2 b111 m11 u12 111 3600
2028 2 b111 m11 u12 111 3600
2029 4 b111 m112 u111 110 14400
2029 4 b111 m112 u111 110 14400
2030 3 b112 m111 u11 120 0
2030 3 b112 m111 u11 120 0
2031 0 b12 m111 u112 111 10800
2031 0 b12 m111 u112 111 10800
2032
2032
2033 $ hg log -r 'sort(all(), -branch)'
2033 $ hg log -r 'sort(all(), -branch)'
2034 0 b12 m111 u112 111 10800
2034 0 b12 m111 u112 111 10800
2035 3 b112 m111 u11 120 0
2035 3 b112 m111 u11 120 0
2036 2 b111 m11 u12 111 3600
2036 2 b111 m11 u12 111 3600
2037 4 b111 m112 u111 110 14400
2037 4 b111 m112 u111 110 14400
2038 1 b11 m12 u111 112 7200
2038 1 b11 m12 u111 112 7200
2039
2039
2040 $ hg log -r 'sort(all(), desc)'
2040 $ hg log -r 'sort(all(), desc)'
2041 2 b111 m11 u12 111 3600
2041 2 b111 m11 u12 111 3600
2042 0 b12 m111 u112 111 10800
2042 0 b12 m111 u112 111 10800
2043 3 b112 m111 u11 120 0
2043 3 b112 m111 u11 120 0
2044 4 b111 m112 u111 110 14400
2044 4 b111 m112 u111 110 14400
2045 1 b11 m12 u111 112 7200
2045 1 b11 m12 u111 112 7200
2046
2046
2047 $ hg log -r 'sort(all(), -desc)'
2047 $ hg log -r 'sort(all(), -desc)'
2048 1 b11 m12 u111 112 7200
2048 1 b11 m12 u111 112 7200
2049 4 b111 m112 u111 110 14400
2049 4 b111 m112 u111 110 14400
2050 0 b12 m111 u112 111 10800
2050 0 b12 m111 u112 111 10800
2051 3 b112 m111 u11 120 0
2051 3 b112 m111 u11 120 0
2052 2 b111 m11 u12 111 3600
2052 2 b111 m11 u12 111 3600
2053
2053
2054 $ hg log -r 'sort(all(), user)'
2054 $ hg log -r 'sort(all(), user)'
2055 3 b112 m111 u11 120 0
2055 3 b112 m111 u11 120 0
2056 1 b11 m12 u111 112 7200
2056 1 b11 m12 u111 112 7200
2057 4 b111 m112 u111 110 14400
2057 4 b111 m112 u111 110 14400
2058 0 b12 m111 u112 111 10800
2058 0 b12 m111 u112 111 10800
2059 2 b111 m11 u12 111 3600
2059 2 b111 m11 u12 111 3600
2060
2060
2061 $ hg log -r 'sort(all(), -user)'
2061 $ hg log -r 'sort(all(), -user)'
2062 2 b111 m11 u12 111 3600
2062 2 b111 m11 u12 111 3600
2063 0 b12 m111 u112 111 10800
2063 0 b12 m111 u112 111 10800
2064 1 b11 m12 u111 112 7200
2064 1 b11 m12 u111 112 7200
2065 4 b111 m112 u111 110 14400
2065 4 b111 m112 u111 110 14400
2066 3 b112 m111 u11 120 0
2066 3 b112 m111 u11 120 0
2067
2067
2068 compare dates (tz offset should have no effect):
2068 compare dates (tz offset should have no effect):
2069
2069
2070 $ hg log -r 'sort(all(), date)'
2070 $ hg log -r 'sort(all(), date)'
2071 4 b111 m112 u111 110 14400
2071 4 b111 m112 u111 110 14400
2072 0 b12 m111 u112 111 10800
2072 0 b12 m111 u112 111 10800
2073 2 b111 m11 u12 111 3600
2073 2 b111 m11 u12 111 3600
2074 1 b11 m12 u111 112 7200
2074 1 b11 m12 u111 112 7200
2075 3 b112 m111 u11 120 0
2075 3 b112 m111 u11 120 0
2076
2076
2077 $ hg log -r 'sort(all(), -date)'
2077 $ hg log -r 'sort(all(), -date)'
2078 3 b112 m111 u11 120 0
2078 3 b112 m111 u11 120 0
2079 1 b11 m12 u111 112 7200
2079 1 b11 m12 u111 112 7200
2080 0 b12 m111 u112 111 10800
2080 0 b12 m111 u112 111 10800
2081 2 b111 m11 u12 111 3600
2081 2 b111 m11 u12 111 3600
2082 4 b111 m112 u111 110 14400
2082 4 b111 m112 u111 110 14400
2083
2083
2084 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2084 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2085 because '-k' reverses the comparison, not the list itself:
2085 because '-k' reverses the comparison, not the list itself:
2086
2086
2087 $ hg log -r 'sort(0 + 2, date)'
2087 $ hg log -r 'sort(0 + 2, date)'
2088 0 b12 m111 u112 111 10800
2088 0 b12 m111 u112 111 10800
2089 2 b111 m11 u12 111 3600
2089 2 b111 m11 u12 111 3600
2090
2090
2091 $ hg log -r 'sort(0 + 2, -date)'
2091 $ hg log -r 'sort(0 + 2, -date)'
2092 0 b12 m111 u112 111 10800
2092 0 b12 m111 u112 111 10800
2093 2 b111 m11 u12 111 3600
2093 2 b111 m11 u12 111 3600
2094
2094
2095 $ hg log -r 'reverse(sort(0 + 2, date))'
2095 $ hg log -r 'reverse(sort(0 + 2, date))'
2096 2 b111 m11 u12 111 3600
2096 2 b111 m11 u12 111 3600
2097 0 b12 m111 u112 111 10800
2097 0 b12 m111 u112 111 10800
2098
2098
2099 sort by multiple keys:
2099 sort by multiple keys:
2100
2100
2101 $ hg log -r 'sort(all(), "branch -rev")'
2101 $ hg log -r 'sort(all(), "branch -rev")'
2102 1 b11 m12 u111 112 7200
2102 1 b11 m12 u111 112 7200
2103 4 b111 m112 u111 110 14400
2103 4 b111 m112 u111 110 14400
2104 2 b111 m11 u12 111 3600
2104 2 b111 m11 u12 111 3600
2105 3 b112 m111 u11 120 0
2105 3 b112 m111 u11 120 0
2106 0 b12 m111 u112 111 10800
2106 0 b12 m111 u112 111 10800
2107
2107
2108 $ hg log -r 'sort(all(), "-desc -date")'
2108 $ hg log -r 'sort(all(), "-desc -date")'
2109 1 b11 m12 u111 112 7200
2109 1 b11 m12 u111 112 7200
2110 4 b111 m112 u111 110 14400
2110 4 b111 m112 u111 110 14400
2111 3 b112 m111 u11 120 0
2111 3 b112 m111 u11 120 0
2112 0 b12 m111 u112 111 10800
2112 0 b12 m111 u112 111 10800
2113 2 b111 m11 u12 111 3600
2113 2 b111 m11 u12 111 3600
2114
2114
2115 $ hg log -r 'sort(all(), "user -branch date rev")'
2115 $ hg log -r 'sort(all(), "user -branch date rev")'
2116 3 b112 m111 u11 120 0
2116 3 b112 m111 u11 120 0
2117 4 b111 m112 u111 110 14400
2117 4 b111 m112 u111 110 14400
2118 1 b11 m12 u111 112 7200
2118 1 b11 m12 u111 112 7200
2119 0 b12 m111 u112 111 10800
2119 0 b12 m111 u112 111 10800
2120 2 b111 m11 u12 111 3600
2120 2 b111 m11 u12 111 3600
2121
2121
2122 toposort prioritises graph branches
2122 toposort prioritises graph branches
2123
2123
2124 $ hg up 2
2124 $ hg up 2
2125 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2125 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2126 $ touch a
2126 $ touch a
2127 $ hg addremove
2127 $ hg addremove
2128 adding a
2128 adding a
2129 $ hg ci -m 't1' -u 'tu' -d '130 0'
2129 $ hg ci -m 't1' -u 'tu' -d '130 0'
2130 created new head
2130 created new head
2131 $ echo 'a' >> a
2131 $ echo 'a' >> a
2132 $ hg ci -m 't2' -u 'tu' -d '130 0'
2132 $ hg ci -m 't2' -u 'tu' -d '130 0'
2133 $ hg book book1
2133 $ hg book book1
2134 $ hg up 4
2134 $ hg up 4
2135 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2135 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2136 (leaving bookmark book1)
2136 (leaving bookmark book1)
2137 $ touch a
2137 $ touch a
2138 $ hg addremove
2138 $ hg addremove
2139 adding a
2139 adding a
2140 $ hg ci -m 't3' -u 'tu' -d '130 0'
2140 $ hg ci -m 't3' -u 'tu' -d '130 0'
2141
2141
2142 $ hg log -r 'sort(all(), topo)'
2142 $ hg log -r 'sort(all(), topo)'
2143 7 b111 t3 tu 130 0
2143 7 b111 t3 tu 130 0
2144 4 b111 m112 u111 110 14400
2144 4 b111 m112 u111 110 14400
2145 3 b112 m111 u11 120 0
2145 3 b112 m111 u11 120 0
2146 6 b111 t2 tu 130 0
2146 6 b111 t2 tu 130 0
2147 5 b111 t1 tu 130 0
2147 5 b111 t1 tu 130 0
2148 2 b111 m11 u12 111 3600
2148 2 b111 m11 u12 111 3600
2149 1 b11 m12 u111 112 7200
2149 1 b11 m12 u111 112 7200
2150 0 b12 m111 u112 111 10800
2150 0 b12 m111 u112 111 10800
2151
2151
2152 $ hg log -r 'sort(all(), -topo)'
2152 $ hg log -r 'sort(all(), -topo)'
2153 0 b12 m111 u112 111 10800
2153 0 b12 m111 u112 111 10800
2154 1 b11 m12 u111 112 7200
2154 1 b11 m12 u111 112 7200
2155 2 b111 m11 u12 111 3600
2155 2 b111 m11 u12 111 3600
2156 5 b111 t1 tu 130 0
2156 5 b111 t1 tu 130 0
2157 6 b111 t2 tu 130 0
2157 6 b111 t2 tu 130 0
2158 3 b112 m111 u11 120 0
2158 3 b112 m111 u11 120 0
2159 4 b111 m112 u111 110 14400
2159 4 b111 m112 u111 110 14400
2160 7 b111 t3 tu 130 0
2160 7 b111 t3 tu 130 0
2161
2161
2162 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2162 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2163 6 b111 t2 tu 130 0
2163 6 b111 t2 tu 130 0
2164 5 b111 t1 tu 130 0
2164 5 b111 t1 tu 130 0
2165 7 b111 t3 tu 130 0
2165 7 b111 t3 tu 130 0
2166 4 b111 m112 u111 110 14400
2166 4 b111 m112 u111 110 14400
2167 3 b112 m111 u11 120 0
2167 3 b112 m111 u11 120 0
2168 2 b111 m11 u12 111 3600
2168 2 b111 m11 u12 111 3600
2169 1 b11 m12 u111 112 7200
2169 1 b11 m12 u111 112 7200
2170 0 b12 m111 u112 111 10800
2170 0 b12 m111 u112 111 10800
2171
2171
2172 topographical sorting can't be combined with other sort keys, and you can't
2172 topographical sorting can't be combined with other sort keys, and you can't
2173 use the topo.firstbranch option when topo sort is not active:
2173 use the topo.firstbranch option when topo sort is not active:
2174
2174
2175 $ hg log -r 'sort(all(), "topo user")'
2175 $ hg log -r 'sort(all(), "topo user")'
2176 hg: parse error: topo sort order cannot be combined with other sort keys
2176 hg: parse error: topo sort order cannot be combined with other sort keys
2177 [255]
2177 [255]
2178
2178
2179 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2179 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2180 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2180 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2181 [255]
2181 [255]
2182
2182
2183 topo.firstbranch should accept any kind of expressions:
2183 topo.firstbranch should accept any kind of expressions:
2184
2184
2185 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2185 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2186 0 b12 m111 u112 111 10800
2186 0 b12 m111 u112 111 10800
2187
2187
2188 $ cd ..
2188 $ cd ..
2189 $ cd repo
2189 $ cd repo
2190
2190
2191 test subtracting something from an addset
2191 test subtracting something from an addset
2192
2192
2193 $ log '(outgoing() or removes(a)) - removes(a)'
2193 $ log '(outgoing() or removes(a)) - removes(a)'
2194 8
2194 8
2195 9
2195 9
2196
2196
2197 test intersecting something with an addset
2197 test intersecting something with an addset
2198
2198
2199 $ log 'parents(outgoing() or removes(a))'
2199 $ log 'parents(outgoing() or removes(a))'
2200 1
2200 1
2201 4
2201 4
2202 5
2202 5
2203 8
2203 8
2204
2204
2205 test that `or` operation combines elements in the right order:
2205 test that `or` operation combines elements in the right order:
2206
2206
2207 $ log '3:4 or 2:5'
2207 $ log '3:4 or 2:5'
2208 3
2208 3
2209 4
2209 4
2210 2
2210 2
2211 5
2211 5
2212 $ log '3:4 or 5:2'
2212 $ log '3:4 or 5:2'
2213 3
2213 3
2214 4
2214 4
2215 5
2215 5
2216 2
2216 2
2217 $ log 'sort(3:4 or 2:5)'
2217 $ log 'sort(3:4 or 2:5)'
2218 2
2218 2
2219 3
2219 3
2220 4
2220 4
2221 5
2221 5
2222 $ log 'sort(3:4 or 5:2)'
2222 $ log 'sort(3:4 or 5:2)'
2223 2
2223 2
2224 3
2224 3
2225 4
2225 4
2226 5
2226 5
2227
2227
2228 test that more than one `-r`s are combined in the right order and deduplicated:
2228 test that more than one `-r`s are combined in the right order and deduplicated:
2229
2229
2230 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2230 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2231 3
2231 3
2232 4
2232 4
2233 5
2233 5
2234 2
2234 2
2235 0
2235 0
2236 1
2236 1
2237
2237
2238 test that `or` operation skips duplicated revisions from right-hand side
2238 test that `or` operation skips duplicated revisions from right-hand side
2239
2239
2240 $ try 'reverse(1::5) or ancestors(4)'
2240 $ try 'reverse(1::5) or ancestors(4)'
2241 (or
2241 (or
2242 (list
2242 (list
2243 (func
2243 (func
2244 ('symbol', 'reverse')
2244 ('symbol', 'reverse')
2245 (dagrange
2245 (dagrange
2246 ('symbol', '1')
2246 ('symbol', '1')
2247 ('symbol', '5')))
2247 ('symbol', '5')))
2248 (func
2248 (func
2249 ('symbol', 'ancestors')
2249 ('symbol', 'ancestors')
2250 ('symbol', '4'))))
2250 ('symbol', '4'))))
2251 * set:
2251 * set:
2252 <addset
2252 <addset
2253 <baseset- [1, 3, 5]>,
2253 <baseset- [1, 3, 5]>,
2254 <generatorset+>>
2254 <generatorset+>>
2255 5
2255 5
2256 3
2256 3
2257 1
2257 1
2258 0
2258 0
2259 2
2259 2
2260 4
2260 4
2261 $ try 'sort(ancestors(4) or reverse(1::5))'
2261 $ try 'sort(ancestors(4) or reverse(1::5))'
2262 (func
2262 (func
2263 ('symbol', 'sort')
2263 ('symbol', 'sort')
2264 (or
2264 (or
2265 (list
2265 (list
2266 (func
2266 (func
2267 ('symbol', 'ancestors')
2267 ('symbol', 'ancestors')
2268 ('symbol', '4'))
2268 ('symbol', '4'))
2269 (func
2269 (func
2270 ('symbol', 'reverse')
2270 ('symbol', 'reverse')
2271 (dagrange
2271 (dagrange
2272 ('symbol', '1')
2272 ('symbol', '1')
2273 ('symbol', '5'))))))
2273 ('symbol', '5'))))))
2274 * set:
2274 * set:
2275 <addset+
2275 <addset+
2276 <generatorset+>,
2276 <generatorset+>,
2277 <baseset- [1, 3, 5]>>
2277 <baseset- [1, 3, 5]>>
2278 0
2278 0
2279 1
2279 1
2280 2
2280 2
2281 3
2281 3
2282 4
2282 4
2283 5
2283 5
2284
2284
2285 test optimization of trivial `or` operation
2285 test optimization of trivial `or` operation
2286
2286
2287 $ try --optimize '0|(1)|"2"|-2|tip|null'
2287 $ try --optimize '0|(1)|"2"|-2|tip|null'
2288 (or
2288 (or
2289 (list
2289 (list
2290 ('symbol', '0')
2290 ('symbol', '0')
2291 (group
2291 (group
2292 ('symbol', '1'))
2292 ('symbol', '1'))
2293 ('string', '2')
2293 ('string', '2')
2294 (negate
2294 (negate
2295 ('symbol', '2'))
2295 ('symbol', '2'))
2296 ('symbol', 'tip')
2296 ('symbol', 'tip')
2297 ('symbol', 'null')))
2297 ('symbol', 'null')))
2298 * optimized:
2298 * optimized:
2299 (func
2299 (func
2300 ('symbol', '_list')
2300 ('symbol', '_list')
2301 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2301 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2302 define)
2302 define)
2303 * set:
2303 * set:
2304 <baseset [0, 1, 2, 8, 9, -1]>
2304 <baseset [0, 1, 2, 8, 9, -1]>
2305 0
2305 0
2306 1
2306 1
2307 2
2307 2
2308 8
2308 8
2309 9
2309 9
2310 -1
2310 -1
2311
2311
2312 $ try --optimize '0|1|2:3'
2312 $ try --optimize '0|1|2:3'
2313 (or
2313 (or
2314 (list
2314 (list
2315 ('symbol', '0')
2315 ('symbol', '0')
2316 ('symbol', '1')
2316 ('symbol', '1')
2317 (range
2317 (range
2318 ('symbol', '2')
2318 ('symbol', '2')
2319 ('symbol', '3'))))
2319 ('symbol', '3'))))
2320 * optimized:
2320 * optimized:
2321 (or
2321 (or
2322 (list
2322 (list
2323 (func
2323 (func
2324 ('symbol', '_list')
2324 ('symbol', '_list')
2325 ('string', '0\x001')
2325 ('string', '0\x001')
2326 define)
2326 define)
2327 (range
2327 (range
2328 ('symbol', '2')
2328 ('symbol', '2')
2329 ('symbol', '3')
2329 ('symbol', '3')
2330 define))
2330 define))
2331 define)
2331 define)
2332 * set:
2332 * set:
2333 <addset
2333 <addset
2334 <baseset [0, 1]>,
2334 <baseset [0, 1]>,
2335 <spanset+ 2:3>>
2335 <spanset+ 2:3>>
2336 0
2336 0
2337 1
2337 1
2338 2
2338 2
2339 3
2339 3
2340
2340
2341 $ try --optimize '0:1|2|3:4|5|6'
2341 $ try --optimize '0:1|2|3:4|5|6'
2342 (or
2342 (or
2343 (list
2343 (list
2344 (range
2344 (range
2345 ('symbol', '0')
2345 ('symbol', '0')
2346 ('symbol', '1'))
2346 ('symbol', '1'))
2347 ('symbol', '2')
2347 ('symbol', '2')
2348 (range
2348 (range
2349 ('symbol', '3')
2349 ('symbol', '3')
2350 ('symbol', '4'))
2350 ('symbol', '4'))
2351 ('symbol', '5')
2351 ('symbol', '5')
2352 ('symbol', '6')))
2352 ('symbol', '6')))
2353 * optimized:
2353 * optimized:
2354 (or
2354 (or
2355 (list
2355 (list
2356 (range
2356 (range
2357 ('symbol', '0')
2357 ('symbol', '0')
2358 ('symbol', '1')
2358 ('symbol', '1')
2359 define)
2359 define)
2360 ('symbol', '2')
2360 ('symbol', '2')
2361 (range
2361 (range
2362 ('symbol', '3')
2362 ('symbol', '3')
2363 ('symbol', '4')
2363 ('symbol', '4')
2364 define)
2364 define)
2365 (func
2365 (func
2366 ('symbol', '_list')
2366 ('symbol', '_list')
2367 ('string', '5\x006')
2367 ('string', '5\x006')
2368 define))
2368 define))
2369 define)
2369 define)
2370 * set:
2370 * set:
2371 <addset
2371 <addset
2372 <addset
2372 <addset
2373 <spanset+ 0:1>,
2373 <spanset+ 0:1>,
2374 <baseset [2]>>,
2374 <baseset [2]>>,
2375 <addset
2375 <addset
2376 <spanset+ 3:4>,
2376 <spanset+ 3:4>,
2377 <baseset [5, 6]>>>
2377 <baseset [5, 6]>>>
2378 0
2378 0
2379 1
2379 1
2380 2
2380 2
2381 3
2381 3
2382 4
2382 4
2383 5
2383 5
2384 6
2384 6
2385
2385
2386 unoptimized `or` looks like this
2386 unoptimized `or` looks like this
2387
2387
2388 $ try --no-optimized -p analyzed '0|1|2|3|4'
2388 $ try --no-optimized -p analyzed '0|1|2|3|4'
2389 * analyzed:
2389 * analyzed:
2390 (or
2390 (or
2391 (list
2391 (list
2392 ('symbol', '0')
2392 ('symbol', '0')
2393 ('symbol', '1')
2393 ('symbol', '1')
2394 ('symbol', '2')
2394 ('symbol', '2')
2395 ('symbol', '3')
2395 ('symbol', '3')
2396 ('symbol', '4'))
2396 ('symbol', '4'))
2397 define)
2397 define)
2398 * set:
2398 * set:
2399 <addset
2399 <addset
2400 <addset
2400 <addset
2401 <baseset [0]>,
2401 <baseset [0]>,
2402 <baseset [1]>>,
2402 <baseset [1]>>,
2403 <addset
2403 <addset
2404 <baseset [2]>,
2404 <baseset [2]>,
2405 <addset
2405 <addset
2406 <baseset [3]>,
2406 <baseset [3]>,
2407 <baseset [4]>>>>
2407 <baseset [4]>>>>
2408 0
2408 0
2409 1
2409 1
2410 2
2410 2
2411 3
2411 3
2412 4
2412 4
2413
2413
2414 test that `_list` should be narrowed by provided `subset`
2414 test that `_list` should be narrowed by provided `subset`
2415
2415
2416 $ log '0:2 and (null|1|2|3)'
2416 $ log '0:2 and (null|1|2|3)'
2417 1
2417 1
2418 2
2418 2
2419
2419
2420 test that `_list` should remove duplicates
2420 test that `_list` should remove duplicates
2421
2421
2422 $ log '0|1|2|1|2|-1|tip'
2422 $ log '0|1|2|1|2|-1|tip'
2423 0
2423 0
2424 1
2424 1
2425 2
2425 2
2426 9
2426 9
2427
2427
2428 test unknown revision in `_list`
2428 test unknown revision in `_list`
2429
2429
2430 $ log '0|unknown'
2430 $ log '0|unknown'
2431 abort: unknown revision 'unknown'!
2431 abort: unknown revision 'unknown'!
2432 [255]
2432 [255]
2433
2433
2434 test integer range in `_list`
2434 test integer range in `_list`
2435
2435
2436 $ log '-1|-10'
2436 $ log '-1|-10'
2437 9
2437 9
2438 0
2438 0
2439
2439
2440 $ log '-10|-11'
2440 $ log '-10|-11'
2441 abort: unknown revision '-11'!
2441 abort: unknown revision '-11'!
2442 [255]
2442 [255]
2443
2443
2444 $ log '9|10'
2444 $ log '9|10'
2445 abort: unknown revision '10'!
2445 abort: unknown revision '10'!
2446 [255]
2446 [255]
2447
2447
2448 test '0000' != '0' in `_list`
2448 test '0000' != '0' in `_list`
2449
2449
2450 $ log '0|0000'
2450 $ log '0|0000'
2451 0
2451 0
2452 -1
2452 -1
2453
2453
2454 test ',' in `_list`
2454 test ',' in `_list`
2455 $ log '0,1'
2455 $ log '0,1'
2456 hg: parse error: can't use a list in this context
2456 hg: parse error: can't use a list in this context
2457 (see hg help "revsets.x or y")
2457 (see hg help "revsets.x or y")
2458 [255]
2458 [255]
2459 $ try '0,1,2'
2459 $ try '0,1,2'
2460 (list
2460 (list
2461 ('symbol', '0')
2461 ('symbol', '0')
2462 ('symbol', '1')
2462 ('symbol', '1')
2463 ('symbol', '2'))
2463 ('symbol', '2'))
2464 hg: parse error: can't use a list in this context
2464 hg: parse error: can't use a list in this context
2465 (see hg help "revsets.x or y")
2465 (see hg help "revsets.x or y")
2466 [255]
2466 [255]
2467
2467
2468 test that chained `or` operations make balanced addsets
2468 test that chained `or` operations make balanced addsets
2469
2469
2470 $ try '0:1|1:2|2:3|3:4|4:5'
2470 $ try '0:1|1:2|2:3|3:4|4:5'
2471 (or
2471 (or
2472 (list
2472 (list
2473 (range
2473 (range
2474 ('symbol', '0')
2474 ('symbol', '0')
2475 ('symbol', '1'))
2475 ('symbol', '1'))
2476 (range
2476 (range
2477 ('symbol', '1')
2477 ('symbol', '1')
2478 ('symbol', '2'))
2478 ('symbol', '2'))
2479 (range
2479 (range
2480 ('symbol', '2')
2480 ('symbol', '2')
2481 ('symbol', '3'))
2481 ('symbol', '3'))
2482 (range
2482 (range
2483 ('symbol', '3')
2483 ('symbol', '3')
2484 ('symbol', '4'))
2484 ('symbol', '4'))
2485 (range
2485 (range
2486 ('symbol', '4')
2486 ('symbol', '4')
2487 ('symbol', '5'))))
2487 ('symbol', '5'))))
2488 * set:
2488 * set:
2489 <addset
2489 <addset
2490 <addset
2490 <addset
2491 <spanset+ 0:1>,
2491 <spanset+ 0:1>,
2492 <spanset+ 1:2>>,
2492 <spanset+ 1:2>>,
2493 <addset
2493 <addset
2494 <spanset+ 2:3>,
2494 <spanset+ 2:3>,
2495 <addset
2495 <addset
2496 <spanset+ 3:4>,
2496 <spanset+ 3:4>,
2497 <spanset+ 4:5>>>>
2497 <spanset+ 4:5>>>>
2498 0
2498 0
2499 1
2499 1
2500 2
2500 2
2501 3
2501 3
2502 4
2502 4
2503 5
2503 5
2504
2504
2505 no crash by empty group "()" while optimizing `or` operations
2505 no crash by empty group "()" while optimizing `or` operations
2506
2506
2507 $ try --optimize '0|()'
2507 $ try --optimize '0|()'
2508 (or
2508 (or
2509 (list
2509 (list
2510 ('symbol', '0')
2510 ('symbol', '0')
2511 (group
2511 (group
2512 None)))
2512 None)))
2513 * optimized:
2513 * optimized:
2514 (or
2514 (or
2515 (list
2515 (list
2516 ('symbol', '0')
2516 ('symbol', '0')
2517 None)
2517 None)
2518 define)
2518 define)
2519 hg: parse error: missing argument
2519 hg: parse error: missing argument
2520 [255]
2520 [255]
2521
2521
2522 test that chained `or` operations never eat up stack (issue4624)
2522 test that chained `or` operations never eat up stack (issue4624)
2523 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2523 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2524
2524
2525 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2525 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2526 0
2526 0
2527 1
2527 1
2528
2528
2529 test that repeated `-r` options never eat up stack (issue4565)
2529 test that repeated `-r` options never eat up stack (issue4565)
2530 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2530 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2531
2531
2532 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2532 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2533 0
2533 0
2534 1
2534 1
2535
2535
2536 check that conversion to only works
2536 check that conversion to only works
2537 $ try --optimize '::3 - ::1'
2537 $ try --optimize '::3 - ::1'
2538 (minus
2538 (minus
2539 (dagrangepre
2539 (dagrangepre
2540 ('symbol', '3'))
2540 ('symbol', '3'))
2541 (dagrangepre
2541 (dagrangepre
2542 ('symbol', '1')))
2542 ('symbol', '1')))
2543 * optimized:
2543 * optimized:
2544 (func
2544 (func
2545 ('symbol', 'only')
2545 ('symbol', 'only')
2546 (list
2546 (list
2547 ('symbol', '3')
2547 ('symbol', '3')
2548 ('symbol', '1'))
2548 ('symbol', '1'))
2549 define)
2549 define)
2550 * set:
2550 * set:
2551 <baseset+ [3]>
2551 <baseset+ [3]>
2552 3
2552 3
2553 $ try --optimize 'ancestors(1) - ancestors(3)'
2553 $ try --optimize 'ancestors(1) - ancestors(3)'
2554 (minus
2554 (minus
2555 (func
2555 (func
2556 ('symbol', 'ancestors')
2556 ('symbol', 'ancestors')
2557 ('symbol', '1'))
2557 ('symbol', '1'))
2558 (func
2558 (func
2559 ('symbol', 'ancestors')
2559 ('symbol', 'ancestors')
2560 ('symbol', '3')))
2560 ('symbol', '3')))
2561 * optimized:
2561 * optimized:
2562 (func
2562 (func
2563 ('symbol', 'only')
2563 ('symbol', 'only')
2564 (list
2564 (list
2565 ('symbol', '1')
2565 ('symbol', '1')
2566 ('symbol', '3'))
2566 ('symbol', '3'))
2567 define)
2567 define)
2568 * set:
2568 * set:
2569 <baseset+ []>
2569 <baseset+ []>
2570 $ try --optimize 'not ::2 and ::6'
2570 $ try --optimize 'not ::2 and ::6'
2571 (and
2571 (and
2572 (not
2572 (not
2573 (dagrangepre
2573 (dagrangepre
2574 ('symbol', '2')))
2574 ('symbol', '2')))
2575 (dagrangepre
2575 (dagrangepre
2576 ('symbol', '6')))
2576 ('symbol', '6')))
2577 * optimized:
2577 * optimized:
2578 (func
2578 (func
2579 ('symbol', 'only')
2579 ('symbol', 'only')
2580 (list
2580 (list
2581 ('symbol', '6')
2581 ('symbol', '6')
2582 ('symbol', '2'))
2582 ('symbol', '2'))
2583 define)
2583 define)
2584 * set:
2584 * set:
2585 <baseset+ [3, 4, 5, 6]>
2585 <baseset+ [3, 4, 5, 6]>
2586 3
2586 3
2587 4
2587 4
2588 5
2588 5
2589 6
2589 6
2590 $ try --optimize 'ancestors(6) and not ancestors(4)'
2590 $ try --optimize 'ancestors(6) and not ancestors(4)'
2591 (and
2591 (and
2592 (func
2592 (func
2593 ('symbol', 'ancestors')
2593 ('symbol', 'ancestors')
2594 ('symbol', '6'))
2594 ('symbol', '6'))
2595 (not
2595 (not
2596 (func
2596 (func
2597 ('symbol', 'ancestors')
2597 ('symbol', 'ancestors')
2598 ('symbol', '4'))))
2598 ('symbol', '4'))))
2599 * optimized:
2599 * optimized:
2600 (func
2600 (func
2601 ('symbol', 'only')
2601 ('symbol', 'only')
2602 (list
2602 (list
2603 ('symbol', '6')
2603 ('symbol', '6')
2604 ('symbol', '4'))
2604 ('symbol', '4'))
2605 define)
2605 define)
2606 * set:
2606 * set:
2607 <baseset+ [3, 5, 6]>
2607 <baseset+ [3, 5, 6]>
2608 3
2608 3
2609 5
2609 5
2610 6
2610 6
2611
2611
2612 no crash by empty group "()" while optimizing to "only()"
2612 no crash by empty group "()" while optimizing to "only()"
2613
2613
2614 $ try --optimize '::1 and ()'
2614 $ try --optimize '::1 and ()'
2615 (and
2615 (and
2616 (dagrangepre
2616 (dagrangepre
2617 ('symbol', '1'))
2617 ('symbol', '1'))
2618 (group
2618 (group
2619 None))
2619 None))
2620 * optimized:
2620 * optimized:
2621 (and
2621 (and
2622 None
2622 None
2623 (func
2623 (func
2624 ('symbol', 'ancestors')
2624 ('symbol', 'ancestors')
2625 ('symbol', '1')
2625 ('symbol', '1')
2626 define)
2626 define)
2627 define)
2627 define)
2628 hg: parse error: missing argument
2628 hg: parse error: missing argument
2629 [255]
2629 [255]
2630
2630
2631 invalid function call should not be optimized to only()
2631 invalid function call should not be optimized to only()
2632
2632
2633 $ log '"ancestors"(6) and not ancestors(4)'
2633 $ log '"ancestors"(6) and not ancestors(4)'
2634 hg: parse error: not a symbol
2634 hg: parse error: not a symbol
2635 [255]
2635 [255]
2636
2636
2637 $ log 'ancestors(6) and not "ancestors"(4)'
2637 $ log 'ancestors(6) and not "ancestors"(4)'
2638 hg: parse error: not a symbol
2638 hg: parse error: not a symbol
2639 [255]
2639 [255]
2640
2640
2641 we can use patterns when searching for tags
2641 we can use patterns when searching for tags
2642
2642
2643 $ log 'tag("1..*")'
2643 $ log 'tag("1..*")'
2644 abort: tag '1..*' does not exist!
2644 abort: tag '1..*' does not exist!
2645 [255]
2645 [255]
2646 $ log 'tag("re:1..*")'
2646 $ log 'tag("re:1..*")'
2647 6
2647 6
2648 $ log 'tag("re:[0-9].[0-9]")'
2648 $ log 'tag("re:[0-9].[0-9]")'
2649 6
2649 6
2650 $ log 'tag("literal:1.0")'
2650 $ log 'tag("literal:1.0")'
2651 6
2651 6
2652 $ log 'tag("re:0..*")'
2652 $ log 'tag("re:0..*")'
2653
2653
2654 $ log 'tag(unknown)'
2654 $ log 'tag(unknown)'
2655 abort: tag 'unknown' does not exist!
2655 abort: tag 'unknown' does not exist!
2656 [255]
2656 [255]
2657 $ log 'tag("re:unknown")'
2657 $ log 'tag("re:unknown")'
2658 $ log 'present(tag("unknown"))'
2658 $ log 'present(tag("unknown"))'
2659 $ log 'present(tag("re:unknown"))'
2659 $ log 'present(tag("re:unknown"))'
2660 $ log 'branch(unknown)'
2660 $ log 'branch(unknown)'
2661 abort: unknown revision 'unknown'!
2661 abort: unknown revision 'unknown'!
2662 [255]
2662 [255]
2663 $ log 'branch("literal:unknown")'
2663 $ log 'branch("literal:unknown")'
2664 abort: branch 'unknown' does not exist!
2664 abort: branch 'unknown' does not exist!
2665 [255]
2665 [255]
2666 $ log 'branch("re:unknown")'
2666 $ log 'branch("re:unknown")'
2667 $ log 'present(branch("unknown"))'
2667 $ log 'present(branch("unknown"))'
2668 $ log 'present(branch("re:unknown"))'
2668 $ log 'present(branch("re:unknown"))'
2669 $ log 'user(bob)'
2669 $ log 'user(bob)'
2670 2
2670 2
2671
2671
2672 $ log '4::8'
2672 $ log '4::8'
2673 4
2673 4
2674 8
2674 8
2675 $ log '4:8'
2675 $ log '4:8'
2676 4
2676 4
2677 5
2677 5
2678 6
2678 6
2679 7
2679 7
2680 8
2680 8
2681
2681
2682 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2682 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2683 4
2683 4
2684 2
2684 2
2685 5
2685 5
2686
2686
2687 $ log 'not 0 and 0:2'
2687 $ log 'not 0 and 0:2'
2688 1
2688 1
2689 2
2689 2
2690 $ log 'not 1 and 0:2'
2690 $ log 'not 1 and 0:2'
2691 0
2691 0
2692 2
2692 2
2693 $ log 'not 2 and 0:2'
2693 $ log 'not 2 and 0:2'
2694 0
2694 0
2695 1
2695 1
2696 $ log '(1 and 2)::'
2696 $ log '(1 and 2)::'
2697 $ log '(1 and 2):'
2697 $ log '(1 and 2):'
2698 $ log '(1 and 2):3'
2698 $ log '(1 and 2):3'
2699 $ log 'sort(head(), -rev)'
2699 $ log 'sort(head(), -rev)'
2700 9
2700 9
2701 7
2701 7
2702 6
2702 6
2703 5
2703 5
2704 4
2704 4
2705 3
2705 3
2706 2
2706 2
2707 1
2707 1
2708 0
2708 0
2709 $ log '4::8 - 8'
2709 $ log '4::8 - 8'
2710 4
2710 4
2711
2711
2712 matching() should preserve the order of the input set:
2712 matching() should preserve the order of the input set:
2713
2713
2714 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2714 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2715 2
2715 2
2716 3
2716 3
2717 1
2717 1
2718
2718
2719 $ log 'named("unknown")'
2719 $ log 'named("unknown")'
2720 abort: namespace 'unknown' does not exist!
2720 abort: namespace 'unknown' does not exist!
2721 [255]
2721 [255]
2722 $ log 'named("re:unknown")'
2722 $ log 'named("re:unknown")'
2723 abort: no namespace exists that match 'unknown'!
2723 abort: no namespace exists that match 'unknown'!
2724 [255]
2724 [255]
2725 $ log 'present(named("unknown"))'
2725 $ log 'present(named("unknown"))'
2726 $ log 'present(named("re:unknown"))'
2726 $ log 'present(named("re:unknown"))'
2727
2727
2728 $ log 'tag()'
2728 $ log 'tag()'
2729 6
2729 6
2730 $ log 'named("tags")'
2730 $ log 'named("tags")'
2731 6
2731 6
2732
2732
2733 issue2437
2733 issue2437
2734
2734
2735 $ log '3 and p1(5)'
2735 $ log '3 and p1(5)'
2736 3
2736 3
2737 $ log '4 and p2(6)'
2737 $ log '4 and p2(6)'
2738 4
2738 4
2739 $ log '1 and parents(:2)'
2739 $ log '1 and parents(:2)'
2740 1
2740 1
2741 $ log '2 and children(1:)'
2741 $ log '2 and children(1:)'
2742 2
2742 2
2743 $ log 'roots(all()) or roots(all())'
2743 $ log 'roots(all()) or roots(all())'
2744 0
2744 0
2745 $ hg debugrevspec 'roots(all()) or roots(all())'
2745 $ hg debugrevspec 'roots(all()) or roots(all())'
2746 0
2746 0
2747 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2747 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2748 9
2748 9
2749 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2749 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2750 4
2750 4
2751
2751
2752 issue2654: report a parse error if the revset was not completely parsed
2752 issue2654: report a parse error if the revset was not completely parsed
2753
2753
2754 $ log '1 OR 2'
2754 $ log '1 OR 2'
2755 hg: parse error at 2: invalid token
2755 hg: parse error at 2: invalid token
2756 [255]
2756 [255]
2757
2757
2758 or operator should preserve ordering:
2758 or operator should preserve ordering:
2759 $ log 'reverse(2::4) or tip'
2759 $ log 'reverse(2::4) or tip'
2760 4
2760 4
2761 2
2761 2
2762 9
2762 9
2763
2763
2764 parentrevspec
2764 parentrevspec
2765
2765
2766 $ log 'merge()^0'
2766 $ log 'merge()^0'
2767 6
2767 6
2768 $ log 'merge()^'
2768 $ log 'merge()^'
2769 5
2769 5
2770 $ log 'merge()^1'
2770 $ log 'merge()^1'
2771 5
2771 5
2772 $ log 'merge()^2'
2772 $ log 'merge()^2'
2773 4
2773 4
2774 $ log '(not merge())^2'
2774 $ log '(not merge())^2'
2775 $ log 'merge()^^'
2775 $ log 'merge()^^'
2776 3
2776 3
2777 $ log 'merge()^1^'
2777 $ log 'merge()^1^'
2778 3
2778 3
2779 $ log 'merge()^^^'
2779 $ log 'merge()^^^'
2780 1
2780 1
2781
2781
2782 $ log 'merge()~0'
2782 $ log 'merge()~0'
2783 6
2783 6
2784 $ log 'merge()~1'
2784 $ log 'merge()~1'
2785 5
2785 5
2786 $ log 'merge()~2'
2786 $ log 'merge()~2'
2787 3
2787 3
2788 $ log 'merge()~2^1'
2788 $ log 'merge()~2^1'
2789 1
2789 1
2790 $ log 'merge()~3'
2790 $ log 'merge()~3'
2791 1
2791 1
2792
2792
2793 $ log '(-3:tip)^'
2793 $ log '(-3:tip)^'
2794 4
2794 4
2795 6
2795 6
2796 8
2796 8
2797
2797
2798 $ log 'tip^foo'
2798 $ log 'tip^foo'
2799 hg: parse error: ^ expects a number 0, 1, or 2
2799 hg: parse error: ^ expects a number 0, 1, or 2
2800 [255]
2800 [255]
2801
2801
2802 Bogus function gets suggestions
2802 Bogus function gets suggestions
2803 $ log 'add()'
2803 $ log 'add()'
2804 hg: parse error: unknown identifier: add
2804 hg: parse error: unknown identifier: add
2805 (did you mean adds?)
2805 (did you mean adds?)
2806 [255]
2806 [255]
2807 $ log 'added()'
2807 $ log 'added()'
2808 hg: parse error: unknown identifier: added
2808 hg: parse error: unknown identifier: added
2809 (did you mean adds?)
2809 (did you mean adds?)
2810 [255]
2810 [255]
2811 $ log 'remo()'
2811 $ log 'remo()'
2812 hg: parse error: unknown identifier: remo
2812 hg: parse error: unknown identifier: remo
2813 (did you mean one of remote, removes?)
2813 (did you mean one of remote, removes?)
2814 [255]
2814 [255]
2815 $ log 'babar()'
2815 $ log 'babar()'
2816 hg: parse error: unknown identifier: babar
2816 hg: parse error: unknown identifier: babar
2817 [255]
2817 [255]
2818
2818
2819 Bogus function with a similar internal name doesn't suggest the internal name
2819 Bogus function with a similar internal name doesn't suggest the internal name
2820 $ log 'matches()'
2820 $ log 'matches()'
2821 hg: parse error: unknown identifier: matches
2821 hg: parse error: unknown identifier: matches
2822 (did you mean matching?)
2822 (did you mean matching?)
2823 [255]
2823 [255]
2824
2824
2825 Undocumented functions aren't suggested as similar either
2825 Undocumented functions aren't suggested as similar either
2826 $ log 'tagged2()'
2826 $ log 'tagged2()'
2827 hg: parse error: unknown identifier: tagged2
2827 hg: parse error: unknown identifier: tagged2
2828 [255]
2828 [255]
2829
2829
2830 multiple revspecs
2830 multiple revspecs
2831
2831
2832 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2832 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2833 8
2833 8
2834 9
2834 9
2835 4
2835 4
2836 5
2836 5
2837 6
2837 6
2838 7
2838 7
2839
2839
2840 test usage in revpair (with "+")
2840 test usage in revpair (with "+")
2841
2841
2842 (real pair)
2842 (real pair)
2843
2843
2844 $ hg diff -r 'tip^^' -r 'tip'
2844 $ hg diff -r 'tip^^' -r 'tip'
2845 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2845 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2846 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2846 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2847 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2847 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2848 @@ -0,0 +1,1 @@
2848 @@ -0,0 +1,1 @@
2849 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2849 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2850 $ hg diff -r 'tip^^::tip'
2850 $ hg diff -r 'tip^^::tip'
2851 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2851 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2852 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2852 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2853 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2853 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2854 @@ -0,0 +1,1 @@
2854 @@ -0,0 +1,1 @@
2855 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2855 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2856
2856
2857 (single rev)
2857 (single rev)
2858
2858
2859 $ hg diff -r 'tip^' -r 'tip^'
2859 $ hg diff -r 'tip^' -r 'tip^'
2860 $ hg diff -r 'tip^:tip^'
2860 $ hg diff -r 'tip^:tip^'
2861
2861
2862 (single rev that does not looks like a range)
2862 (single rev that does not looks like a range)
2863
2863
2864 $ hg diff -r 'tip^::tip^ or tip^'
2864 $ hg diff -r 'tip^::tip^ or tip^'
2865 diff -r d5d0dcbdc4d9 .hgtags
2865 diff -r d5d0dcbdc4d9 .hgtags
2866 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2866 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2867 +++ b/.hgtags * (glob)
2867 +++ b/.hgtags * (glob)
2868 @@ -0,0 +1,1 @@
2868 @@ -0,0 +1,1 @@
2869 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2869 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2870 $ hg diff -r 'tip^ or tip^'
2870 $ hg diff -r 'tip^ or tip^'
2871 diff -r d5d0dcbdc4d9 .hgtags
2871 diff -r d5d0dcbdc4d9 .hgtags
2872 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2872 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2873 +++ b/.hgtags * (glob)
2873 +++ b/.hgtags * (glob)
2874 @@ -0,0 +1,1 @@
2874 @@ -0,0 +1,1 @@
2875 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2875 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2876
2876
2877 (no rev)
2877 (no rev)
2878
2878
2879 $ hg diff -r 'author("babar") or author("celeste")'
2879 $ hg diff -r 'author("babar") or author("celeste")'
2880 abort: empty revision range
2880 abort: empty revision range
2881 [255]
2881 [255]
2882
2882
2883 aliases:
2883 aliases:
2884
2884
2885 $ echo '[revsetalias]' >> .hg/hgrc
2885 $ echo '[revsetalias]' >> .hg/hgrc
2886 $ echo 'm = merge()' >> .hg/hgrc
2886 $ echo 'm = merge()' >> .hg/hgrc
2887 (revset aliases can override builtin revsets)
2887 (revset aliases can override builtin revsets)
2888 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2888 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2889 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2889 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2890 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2890 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2891 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2891 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2892 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2892 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2893
2893
2894 $ try m
2894 $ try m
2895 ('symbol', 'm')
2895 ('symbol', 'm')
2896 * expanded:
2896 * expanded:
2897 (func
2897 (func
2898 ('symbol', 'merge')
2898 ('symbol', 'merge')
2899 None)
2899 None)
2900 * set:
2900 * set:
2901 <filteredset
2901 <filteredset
2902 <fullreposet+ 0:9>,
2902 <fullreposet+ 0:9>,
2903 <merge>>
2903 <merge>>
2904 6
2904 6
2905
2905
2906 $ HGPLAIN=1
2906 $ HGPLAIN=1
2907 $ export HGPLAIN
2907 $ export HGPLAIN
2908 $ try m
2908 $ try m
2909 ('symbol', 'm')
2909 ('symbol', 'm')
2910 abort: unknown revision 'm'!
2910 abort: unknown revision 'm'!
2911 [255]
2911 [255]
2912
2912
2913 $ HGPLAINEXCEPT=revsetalias
2913 $ HGPLAINEXCEPT=revsetalias
2914 $ export HGPLAINEXCEPT
2914 $ export HGPLAINEXCEPT
2915 $ try m
2915 $ try m
2916 ('symbol', 'm')
2916 ('symbol', 'm')
2917 * expanded:
2917 * expanded:
2918 (func
2918 (func
2919 ('symbol', 'merge')
2919 ('symbol', 'merge')
2920 None)
2920 None)
2921 * set:
2921 * set:
2922 <filteredset
2922 <filteredset
2923 <fullreposet+ 0:9>,
2923 <fullreposet+ 0:9>,
2924 <merge>>
2924 <merge>>
2925 6
2925 6
2926
2926
2927 $ unset HGPLAIN
2927 $ unset HGPLAIN
2928 $ unset HGPLAINEXCEPT
2928 $ unset HGPLAINEXCEPT
2929
2929
2930 $ try 'p2(.)'
2930 $ try 'p2(.)'
2931 (func
2931 (func
2932 ('symbol', 'p2')
2932 ('symbol', 'p2')
2933 ('symbol', '.'))
2933 ('symbol', '.'))
2934 * expanded:
2934 * expanded:
2935 (func
2935 (func
2936 ('symbol', 'p1')
2936 ('symbol', 'p1')
2937 ('symbol', '.'))
2937 ('symbol', '.'))
2938 * set:
2938 * set:
2939 <baseset+ [8]>
2939 <baseset+ [8]>
2940 8
2940 8
2941
2941
2942 $ HGPLAIN=1
2942 $ HGPLAIN=1
2943 $ export HGPLAIN
2943 $ export HGPLAIN
2944 $ try 'p2(.)'
2944 $ try 'p2(.)'
2945 (func
2945 (func
2946 ('symbol', 'p2')
2946 ('symbol', 'p2')
2947 ('symbol', '.'))
2947 ('symbol', '.'))
2948 * set:
2948 * set:
2949 <baseset+ []>
2949 <baseset+ []>
2950
2950
2951 $ HGPLAINEXCEPT=revsetalias
2951 $ HGPLAINEXCEPT=revsetalias
2952 $ export HGPLAINEXCEPT
2952 $ export HGPLAINEXCEPT
2953 $ try 'p2(.)'
2953 $ try 'p2(.)'
2954 (func
2954 (func
2955 ('symbol', 'p2')
2955 ('symbol', 'p2')
2956 ('symbol', '.'))
2956 ('symbol', '.'))
2957 * expanded:
2957 * expanded:
2958 (func
2958 (func
2959 ('symbol', 'p1')
2959 ('symbol', 'p1')
2960 ('symbol', '.'))
2960 ('symbol', '.'))
2961 * set:
2961 * set:
2962 <baseset+ [8]>
2962 <baseset+ [8]>
2963 8
2963 8
2964
2964
2965 $ unset HGPLAIN
2965 $ unset HGPLAIN
2966 $ unset HGPLAINEXCEPT
2966 $ unset HGPLAINEXCEPT
2967
2967
2968 test alias recursion
2968 test alias recursion
2969
2969
2970 $ try sincem
2970 $ try sincem
2971 ('symbol', 'sincem')
2971 ('symbol', 'sincem')
2972 * expanded:
2972 * expanded:
2973 (func
2973 (func
2974 ('symbol', 'descendants')
2974 ('symbol', 'descendants')
2975 (func
2975 (func
2976 ('symbol', 'merge')
2976 ('symbol', 'merge')
2977 None))
2977 None))
2978 * set:
2978 * set:
2979 <addset+
2979 <addset+
2980 <filteredset
2980 <filteredset
2981 <fullreposet+ 0:9>,
2981 <fullreposet+ 0:9>,
2982 <merge>>,
2982 <merge>>,
2983 <generatorset+>>
2983 <generatorset+>>
2984 6
2984 6
2985 7
2985 7
2986
2986
2987 test infinite recursion
2987 test infinite recursion
2988
2988
2989 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2989 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2990 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2990 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2991 $ try recurse1
2991 $ try recurse1
2992 ('symbol', 'recurse1')
2992 ('symbol', 'recurse1')
2993 hg: parse error: infinite expansion of revset alias "recurse1" detected
2993 hg: parse error: infinite expansion of revset alias "recurse1" detected
2994 [255]
2994 [255]
2995
2995
2996 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2996 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2997 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2997 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2998 $ try "level2(level1(1, 2), 3)"
2998 $ try "level2(level1(1, 2), 3)"
2999 (func
2999 (func
3000 ('symbol', 'level2')
3000 ('symbol', 'level2')
3001 (list
3001 (list
3002 (func
3002 (func
3003 ('symbol', 'level1')
3003 ('symbol', 'level1')
3004 (list
3004 (list
3005 ('symbol', '1')
3005 ('symbol', '1')
3006 ('symbol', '2')))
3006 ('symbol', '2')))
3007 ('symbol', '3')))
3007 ('symbol', '3')))
3008 * expanded:
3008 * expanded:
3009 (or
3009 (or
3010 (list
3010 (list
3011 ('symbol', '3')
3011 ('symbol', '3')
3012 (or
3012 (or
3013 (list
3013 (list
3014 ('symbol', '1')
3014 ('symbol', '1')
3015 ('symbol', '2')))))
3015 ('symbol', '2')))))
3016 * set:
3016 * set:
3017 <addset
3017 <addset
3018 <baseset [3]>,
3018 <baseset [3]>,
3019 <baseset [1, 2]>>
3019 <baseset [1, 2]>>
3020 3
3020 3
3021 1
3021 1
3022 2
3022 2
3023
3023
3024 test nesting and variable passing
3024 test nesting and variable passing
3025
3025
3026 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3026 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3027 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3027 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3028 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3028 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3029 $ try 'nested(2:5)'
3029 $ try 'nested(2:5)'
3030 (func
3030 (func
3031 ('symbol', 'nested')
3031 ('symbol', 'nested')
3032 (range
3032 (range
3033 ('symbol', '2')
3033 ('symbol', '2')
3034 ('symbol', '5')))
3034 ('symbol', '5')))
3035 * expanded:
3035 * expanded:
3036 (func
3036 (func
3037 ('symbol', 'max')
3037 ('symbol', 'max')
3038 (range
3038 (range
3039 ('symbol', '2')
3039 ('symbol', '2')
3040 ('symbol', '5')))
3040 ('symbol', '5')))
3041 * set:
3041 * set:
3042 <baseset
3042 <baseset
3043 <max
3043 <max
3044 <fullreposet+ 0:9>,
3044 <fullreposet+ 0:9>,
3045 <spanset+ 2:5>>>
3045 <spanset+ 2:5>>>
3046 5
3046 5
3047
3047
3048 test chained `or` operations are flattened at parsing phase
3048 test chained `or` operations are flattened at parsing phase
3049
3049
3050 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3050 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3051 $ try 'chainedorops(0:1, 1:2, 2:3)'
3051 $ try 'chainedorops(0:1, 1:2, 2:3)'
3052 (func
3052 (func
3053 ('symbol', 'chainedorops')
3053 ('symbol', 'chainedorops')
3054 (list
3054 (list
3055 (range
3055 (range
3056 ('symbol', '0')
3056 ('symbol', '0')
3057 ('symbol', '1'))
3057 ('symbol', '1'))
3058 (range
3058 (range
3059 ('symbol', '1')
3059 ('symbol', '1')
3060 ('symbol', '2'))
3060 ('symbol', '2'))
3061 (range
3061 (range
3062 ('symbol', '2')
3062 ('symbol', '2')
3063 ('symbol', '3'))))
3063 ('symbol', '3'))))
3064 * expanded:
3064 * expanded:
3065 (or
3065 (or
3066 (list
3066 (list
3067 (range
3067 (range
3068 ('symbol', '0')
3068 ('symbol', '0')
3069 ('symbol', '1'))
3069 ('symbol', '1'))
3070 (range
3070 (range
3071 ('symbol', '1')
3071 ('symbol', '1')
3072 ('symbol', '2'))
3072 ('symbol', '2'))
3073 (range
3073 (range
3074 ('symbol', '2')
3074 ('symbol', '2')
3075 ('symbol', '3'))))
3075 ('symbol', '3'))))
3076 * set:
3076 * set:
3077 <addset
3077 <addset
3078 <spanset+ 0:1>,
3078 <spanset+ 0:1>,
3079 <addset
3079 <addset
3080 <spanset+ 1:2>,
3080 <spanset+ 1:2>,
3081 <spanset+ 2:3>>>
3081 <spanset+ 2:3>>>
3082 0
3082 0
3083 1
3083 1
3084 2
3084 2
3085 3
3085 3
3086
3086
3087 test variable isolation, variable placeholders are rewritten as string
3087 test variable isolation, variable placeholders are rewritten as string
3088 then parsed and matched again as string. Check they do not leak too
3088 then parsed and matched again as string. Check they do not leak too
3089 far away.
3089 far away.
3090
3090
3091 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3091 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3092 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3092 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3093 $ try 'callinjection(2:5)'
3093 $ try 'callinjection(2:5)'
3094 (func
3094 (func
3095 ('symbol', 'callinjection')
3095 ('symbol', 'callinjection')
3096 (range
3096 (range
3097 ('symbol', '2')
3097 ('symbol', '2')
3098 ('symbol', '5')))
3098 ('symbol', '5')))
3099 * expanded:
3099 * expanded:
3100 (func
3100 (func
3101 ('symbol', 'descendants')
3101 ('symbol', 'descendants')
3102 (func
3102 (func
3103 ('symbol', 'max')
3103 ('symbol', 'max')
3104 ('string', '$1')))
3104 ('string', '$1')))
3105 abort: unknown revision '$1'!
3105 abort: unknown revision '$1'!
3106 [255]
3106 [255]
3107
3107
3108 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3108 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3109 but 'all()' should never be substituted to '0()'.
3109 but 'all()' should never be substituted to '0()'.
3110
3110
3111 $ echo 'universe = all()' >> .hg/hgrc
3111 $ echo 'universe = all()' >> .hg/hgrc
3112 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3112 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3113 $ try 'shadowall(0)'
3113 $ try 'shadowall(0)'
3114 (func
3114 (func
3115 ('symbol', 'shadowall')
3115 ('symbol', 'shadowall')
3116 ('symbol', '0'))
3116 ('symbol', '0'))
3117 * expanded:
3117 * expanded:
3118 (and
3118 (and
3119 ('symbol', '0')
3119 ('symbol', '0')
3120 (func
3120 (func
3121 ('symbol', 'all')
3121 ('symbol', 'all')
3122 None))
3122 None))
3123 * set:
3123 * set:
3124 <filteredset
3124 <filteredset
3125 <baseset [0]>,
3125 <baseset [0]>,
3126 <spanset+ 0:9>>
3126 <spanset+ 0:9>>
3127 0
3127 0
3128
3128
3129 test unknown reference:
3129 test unknown reference:
3130
3130
3131 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3131 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3132 (func
3132 (func
3133 ('symbol', 'unknownref')
3133 ('symbol', 'unknownref')
3134 ('symbol', '0'))
3134 ('symbol', '0'))
3135 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3135 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3136 [255]
3136 [255]
3137
3137
3138 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3138 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3139 ('symbol', 'tip')
3139 ('symbol', 'tip')
3140 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3140 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3141 * set:
3141 * set:
3142 <baseset [9]>
3142 <baseset [9]>
3143 9
3143 9
3144
3144
3145 $ try 'tip'
3145 $ try 'tip'
3146 ('symbol', 'tip')
3146 ('symbol', 'tip')
3147 * set:
3147 * set:
3148 <baseset [9]>
3148 <baseset [9]>
3149 9
3149 9
3150
3150
3151 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3151 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3152 ('symbol', 'tip')
3152 ('symbol', 'tip')
3153 warning: bad declaration of revset alias "bad name": at 4: invalid token
3153 warning: bad declaration of revset alias "bad name": at 4: invalid token
3154 * set:
3154 * set:
3155 <baseset [9]>
3155 <baseset [9]>
3156 9
3156 9
3157 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3157 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3158 $ try 'strictreplacing("foo", tip)'
3158 $ try 'strictreplacing("foo", tip)'
3159 (func
3159 (func
3160 ('symbol', 'strictreplacing')
3160 ('symbol', 'strictreplacing')
3161 (list
3161 (list
3162 ('string', 'foo')
3162 ('string', 'foo')
3163 ('symbol', 'tip')))
3163 ('symbol', 'tip')))
3164 * expanded:
3164 * expanded:
3165 (or
3165 (or
3166 (list
3166 (list
3167 ('symbol', 'tip')
3167 ('symbol', 'tip')
3168 (func
3168 (func
3169 ('symbol', 'desc')
3169 ('symbol', 'desc')
3170 ('string', '$1'))))
3170 ('string', '$1'))))
3171 * set:
3171 * set:
3172 <addset
3172 <addset
3173 <baseset [9]>,
3173 <baseset [9]>,
3174 <filteredset
3174 <filteredset
3175 <fullreposet+ 0:9>,
3175 <fullreposet+ 0:9>,
3176 <desc '$1'>>>
3176 <desc '$1'>>>
3177 9
3177 9
3178
3178
3179 $ try 'd(2:5)'
3179 $ try 'd(2:5)'
3180 (func
3180 (func
3181 ('symbol', 'd')
3181 ('symbol', 'd')
3182 (range
3182 (range
3183 ('symbol', '2')
3183 ('symbol', '2')
3184 ('symbol', '5')))
3184 ('symbol', '5')))
3185 * expanded:
3185 * expanded:
3186 (func
3186 (func
3187 ('symbol', 'reverse')
3187 ('symbol', 'reverse')
3188 (func
3188 (func
3189 ('symbol', 'sort')
3189 ('symbol', 'sort')
3190 (list
3190 (list
3191 (range
3191 (range
3192 ('symbol', '2')
3192 ('symbol', '2')
3193 ('symbol', '5'))
3193 ('symbol', '5'))
3194 ('symbol', 'date'))))
3194 ('symbol', 'date'))))
3195 * set:
3195 * set:
3196 <baseset [4, 5, 3, 2]>
3196 <baseset [4, 5, 3, 2]>
3197 4
3197 4
3198 5
3198 5
3199 3
3199 3
3200 2
3200 2
3201 $ try 'rs(2 or 3, date)'
3201 $ try 'rs(2 or 3, date)'
3202 (func
3202 (func
3203 ('symbol', 'rs')
3203 ('symbol', 'rs')
3204 (list
3204 (list
3205 (or
3205 (or
3206 (list
3206 (list
3207 ('symbol', '2')
3207 ('symbol', '2')
3208 ('symbol', '3')))
3208 ('symbol', '3')))
3209 ('symbol', 'date')))
3209 ('symbol', 'date')))
3210 * expanded:
3210 * expanded:
3211 (func
3211 (func
3212 ('symbol', 'reverse')
3212 ('symbol', 'reverse')
3213 (func
3213 (func
3214 ('symbol', 'sort')
3214 ('symbol', 'sort')
3215 (list
3215 (list
3216 (or
3216 (or
3217 (list
3217 (list
3218 ('symbol', '2')
3218 ('symbol', '2')
3219 ('symbol', '3')))
3219 ('symbol', '3')))
3220 ('symbol', 'date'))))
3220 ('symbol', 'date'))))
3221 * set:
3221 * set:
3222 <baseset [3, 2]>
3222 <baseset [3, 2]>
3223 3
3223 3
3224 2
3224 2
3225 $ try 'rs()'
3225 $ try 'rs()'
3226 (func
3226 (func
3227 ('symbol', 'rs')
3227 ('symbol', 'rs')
3228 None)
3228 None)
3229 hg: parse error: invalid number of arguments: 0
3229 hg: parse error: invalid number of arguments: 0
3230 [255]
3230 [255]
3231 $ try 'rs(2)'
3231 $ try 'rs(2)'
3232 (func
3232 (func
3233 ('symbol', 'rs')
3233 ('symbol', 'rs')
3234 ('symbol', '2'))
3234 ('symbol', '2'))
3235 hg: parse error: invalid number of arguments: 1
3235 hg: parse error: invalid number of arguments: 1
3236 [255]
3236 [255]
3237 $ try 'rs(2, data, 7)'
3237 $ try 'rs(2, data, 7)'
3238 (func
3238 (func
3239 ('symbol', 'rs')
3239 ('symbol', 'rs')
3240 (list
3240 (list
3241 ('symbol', '2')
3241 ('symbol', '2')
3242 ('symbol', 'data')
3242 ('symbol', 'data')
3243 ('symbol', '7')))
3243 ('symbol', '7')))
3244 hg: parse error: invalid number of arguments: 3
3244 hg: parse error: invalid number of arguments: 3
3245 [255]
3245 [255]
3246 $ try 'rs4(2 or 3, x, x, date)'
3246 $ try 'rs4(2 or 3, x, x, date)'
3247 (func
3247 (func
3248 ('symbol', 'rs4')
3248 ('symbol', 'rs4')
3249 (list
3249 (list
3250 (or
3250 (or
3251 (list
3251 (list
3252 ('symbol', '2')
3252 ('symbol', '2')
3253 ('symbol', '3')))
3253 ('symbol', '3')))
3254 ('symbol', 'x')
3254 ('symbol', 'x')
3255 ('symbol', 'x')
3255 ('symbol', 'x')
3256 ('symbol', 'date')))
3256 ('symbol', 'date')))
3257 * expanded:
3257 * expanded:
3258 (func
3258 (func
3259 ('symbol', 'reverse')
3259 ('symbol', 'reverse')
3260 (func
3260 (func
3261 ('symbol', 'sort')
3261 ('symbol', 'sort')
3262 (list
3262 (list
3263 (or
3263 (or
3264 (list
3264 (list
3265 ('symbol', '2')
3265 ('symbol', '2')
3266 ('symbol', '3')))
3266 ('symbol', '3')))
3267 ('symbol', 'date'))))
3267 ('symbol', 'date'))))
3268 * set:
3268 * set:
3269 <baseset [3, 2]>
3269 <baseset [3, 2]>
3270 3
3270 3
3271 2
3271 2
3272
3272
3273 issue4553: check that revset aliases override existing hash prefix
3273 issue4553: check that revset aliases override existing hash prefix
3274
3274
3275 $ hg log -qr e
3275 $ hg log -qr e
3276 6:e0cc66ef77e8
3276 6:e0cc66ef77e8
3277
3277
3278 $ hg log -qr e --config revsetalias.e="all()"
3278 $ hg log -qr e --config revsetalias.e="all()"
3279 0:2785f51eece5
3279 0:2785f51eece5
3280 1:d75937da8da0
3280 1:d75937da8da0
3281 2:5ed5505e9f1c
3281 2:5ed5505e9f1c
3282 3:8528aa5637f2
3282 3:8528aa5637f2
3283 4:2326846efdab
3283 4:2326846efdab
3284 5:904fa392b941
3284 5:904fa392b941
3285 6:e0cc66ef77e8
3285 6:e0cc66ef77e8
3286 7:013af1973af4
3286 7:013af1973af4
3287 8:d5d0dcbdc4d9
3287 8:d5d0dcbdc4d9
3288 9:24286f4ae135
3288 9:24286f4ae135
3289
3289
3290 $ hg log -qr e: --config revsetalias.e="0"
3290 $ hg log -qr e: --config revsetalias.e="0"
3291 0:2785f51eece5
3291 0:2785f51eece5
3292 1:d75937da8da0
3292 1:d75937da8da0
3293 2:5ed5505e9f1c
3293 2:5ed5505e9f1c
3294 3:8528aa5637f2
3294 3:8528aa5637f2
3295 4:2326846efdab
3295 4:2326846efdab
3296 5:904fa392b941
3296 5:904fa392b941
3297 6:e0cc66ef77e8
3297 6:e0cc66ef77e8
3298 7:013af1973af4
3298 7:013af1973af4
3299 8:d5d0dcbdc4d9
3299 8:d5d0dcbdc4d9
3300 9:24286f4ae135
3300 9:24286f4ae135
3301
3301
3302 $ hg log -qr :e --config revsetalias.e="9"
3302 $ hg log -qr :e --config revsetalias.e="9"
3303 0:2785f51eece5
3303 0:2785f51eece5
3304 1:d75937da8da0
3304 1:d75937da8da0
3305 2:5ed5505e9f1c
3305 2:5ed5505e9f1c
3306 3:8528aa5637f2
3306 3:8528aa5637f2
3307 4:2326846efdab
3307 4:2326846efdab
3308 5:904fa392b941
3308 5:904fa392b941
3309 6:e0cc66ef77e8
3309 6:e0cc66ef77e8
3310 7:013af1973af4
3310 7:013af1973af4
3311 8:d5d0dcbdc4d9
3311 8:d5d0dcbdc4d9
3312 9:24286f4ae135
3312 9:24286f4ae135
3313
3313
3314 $ hg log -qr e:
3314 $ hg log -qr e:
3315 6:e0cc66ef77e8
3315 6:e0cc66ef77e8
3316 7:013af1973af4
3316 7:013af1973af4
3317 8:d5d0dcbdc4d9
3317 8:d5d0dcbdc4d9
3318 9:24286f4ae135
3318 9:24286f4ae135
3319
3319
3320 $ hg log -qr :e
3320 $ hg log -qr :e
3321 0:2785f51eece5
3321 0:2785f51eece5
3322 1:d75937da8da0
3322 1:d75937da8da0
3323 2:5ed5505e9f1c
3323 2:5ed5505e9f1c
3324 3:8528aa5637f2
3324 3:8528aa5637f2
3325 4:2326846efdab
3325 4:2326846efdab
3326 5:904fa392b941
3326 5:904fa392b941
3327 6:e0cc66ef77e8
3327 6:e0cc66ef77e8
3328
3328
3329 issue2549 - correct optimizations
3329 issue2549 - correct optimizations
3330
3330
3331 $ try 'limit(1 or 2 or 3, 2) and not 2'
3331 $ try 'limit(1 or 2 or 3, 2) and not 2'
3332 (and
3332 (and
3333 (func
3333 (func
3334 ('symbol', 'limit')
3334 ('symbol', 'limit')
3335 (list
3335 (list
3336 (or
3336 (or
3337 (list
3337 (list
3338 ('symbol', '1')
3338 ('symbol', '1')
3339 ('symbol', '2')
3339 ('symbol', '2')
3340 ('symbol', '3')))
3340 ('symbol', '3')))
3341 ('symbol', '2')))
3341 ('symbol', '2')))
3342 (not
3342 (not
3343 ('symbol', '2')))
3343 ('symbol', '2')))
3344 * set:
3344 * set:
3345 <filteredset
3345 <filteredset
3346 <baseset
3346 <baseset
3347 <limit n=2, offset=0,
3347 <limit n=2, offset=0,
3348 <fullreposet+ 0:9>,
3348 <fullreposet+ 0:9>,
3349 <baseset [1, 2, 3]>>>,
3349 <baseset [1, 2, 3]>>>,
3350 <not
3350 <not
3351 <baseset [2]>>>
3351 <baseset [2]>>>
3352 1
3352 1
3353 $ try 'max(1 or 2) and not 2'
3353 $ try 'max(1 or 2) and not 2'
3354 (and
3354 (and
3355 (func
3355 (func
3356 ('symbol', 'max')
3356 ('symbol', 'max')
3357 (or
3357 (or
3358 (list
3358 (list
3359 ('symbol', '1')
3359 ('symbol', '1')
3360 ('symbol', '2'))))
3360 ('symbol', '2'))))
3361 (not
3361 (not
3362 ('symbol', '2')))
3362 ('symbol', '2')))
3363 * set:
3363 * set:
3364 <filteredset
3364 <filteredset
3365 <baseset
3365 <baseset
3366 <max
3366 <max
3367 <fullreposet+ 0:9>,
3367 <fullreposet+ 0:9>,
3368 <baseset [1, 2]>>>,
3368 <baseset [1, 2]>>>,
3369 <not
3369 <not
3370 <baseset [2]>>>
3370 <baseset [2]>>>
3371 $ try 'min(1 or 2) and not 1'
3371 $ try 'min(1 or 2) and not 1'
3372 (and
3372 (and
3373 (func
3373 (func
3374 ('symbol', 'min')
3374 ('symbol', 'min')
3375 (or
3375 (or
3376 (list
3376 (list
3377 ('symbol', '1')
3377 ('symbol', '1')
3378 ('symbol', '2'))))
3378 ('symbol', '2'))))
3379 (not
3379 (not
3380 ('symbol', '1')))
3380 ('symbol', '1')))
3381 * set:
3381 * set:
3382 <filteredset
3382 <filteredset
3383 <baseset
3383 <baseset
3384 <min
3384 <min
3385 <fullreposet+ 0:9>,
3385 <fullreposet+ 0:9>,
3386 <baseset [1, 2]>>>,
3386 <baseset [1, 2]>>>,
3387 <not
3387 <not
3388 <baseset [1]>>>
3388 <baseset [1]>>>
3389 $ try 'last(1 or 2, 1) and not 2'
3389 $ try 'last(1 or 2, 1) and not 2'
3390 (and
3390 (and
3391 (func
3391 (func
3392 ('symbol', 'last')
3392 ('symbol', 'last')
3393 (list
3393 (list
3394 (or
3394 (or
3395 (list
3395 (list
3396 ('symbol', '1')
3396 ('symbol', '1')
3397 ('symbol', '2')))
3397 ('symbol', '2')))
3398 ('symbol', '1')))
3398 ('symbol', '1')))
3399 (not
3399 (not
3400 ('symbol', '2')))
3400 ('symbol', '2')))
3401 * set:
3401 * set:
3402 <filteredset
3402 <filteredset
3403 <baseset
3403 <baseset
3404 <last n=1,
3404 <last n=1,
3405 <fullreposet+ 0:9>,
3405 <fullreposet+ 0:9>,
3406 <baseset [2, 1]>>>,
3406 <baseset [2, 1]>>>,
3407 <not
3407 <not
3408 <baseset [2]>>>
3408 <baseset [2]>>>
3409
3409
3410 issue4289 - ordering of built-ins
3410 issue4289 - ordering of built-ins
3411 $ hg log -M -q -r 3:2
3411 $ hg log -M -q -r 3:2
3412 3:8528aa5637f2
3412 3:8528aa5637f2
3413 2:5ed5505e9f1c
3413 2:5ed5505e9f1c
3414
3414
3415 test revsets started with 40-chars hash (issue3669)
3415 test revsets started with 40-chars hash (issue3669)
3416
3416
3417 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3417 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3418 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3418 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3419 9
3419 9
3420 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3420 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3421 8
3421 8
3422
3422
3423 test or-ed indirect predicates (issue3775)
3423 test or-ed indirect predicates (issue3775)
3424
3424
3425 $ log '6 or 6^1' | sort
3425 $ log '6 or 6^1' | sort
3426 5
3426 5
3427 6
3427 6
3428 $ log '6^1 or 6' | sort
3428 $ log '6^1 or 6' | sort
3429 5
3429 5
3430 6
3430 6
3431 $ log '4 or 4~1' | sort
3431 $ log '4 or 4~1' | sort
3432 2
3432 2
3433 4
3433 4
3434 $ log '4~1 or 4' | sort
3434 $ log '4~1 or 4' | sort
3435 2
3435 2
3436 4
3436 4
3437 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3437 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3438 0
3438 0
3439 1
3439 1
3440 2
3440 2
3441 3
3441 3
3442 4
3442 4
3443 5
3443 5
3444 6
3444 6
3445 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3445 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3446 0
3446 0
3447 1
3447 1
3448 2
3448 2
3449 3
3449 3
3450 4
3450 4
3451 5
3451 5
3452 6
3452 6
3453
3453
3454 tests for 'remote()' predicate:
3454 tests for 'remote()' predicate:
3455 #. (csets in remote) (id) (remote)
3455 #. (csets in remote) (id) (remote)
3456 1. less than local current branch "default"
3456 1. less than local current branch "default"
3457 2. same with local specified "default"
3457 2. same with local specified "default"
3458 3. more than local specified specified
3458 3. more than local specified specified
3459
3459
3460 $ hg clone --quiet -U . ../remote3
3460 $ hg clone --quiet -U . ../remote3
3461 $ cd ../remote3
3461 $ cd ../remote3
3462 $ hg update -q 7
3462 $ hg update -q 7
3463 $ echo r > r
3463 $ echo r > r
3464 $ hg ci -Aqm 10
3464 $ hg ci -Aqm 10
3465 $ log 'remote()'
3465 $ log 'remote()'
3466 7
3466 7
3467 $ log 'remote("a-b-c-")'
3467 $ log 'remote("a-b-c-")'
3468 2
3468 2
3469 $ cd ../repo
3469 $ cd ../repo
3470 $ log 'remote(".a.b.c.", "../remote3")'
3470 $ log 'remote(".a.b.c.", "../remote3")'
3471
3471
3472 tests for concatenation of strings/symbols by "##"
3472 tests for concatenation of strings/symbols by "##"
3473
3473
3474 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3474 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3475 (_concat
3475 (_concat
3476 (_concat
3476 (_concat
3477 (_concat
3477 (_concat
3478 ('symbol', '278')
3478 ('symbol', '278')
3479 ('string', '5f5'))
3479 ('string', '5f5'))
3480 ('symbol', '1ee'))
3480 ('symbol', '1ee'))
3481 ('string', 'ce5'))
3481 ('string', 'ce5'))
3482 * concatenated:
3482 * concatenated:
3483 ('string', '2785f51eece5')
3483 ('string', '2785f51eece5')
3484 * set:
3484 * set:
3485 <baseset [0]>
3485 <baseset [0]>
3486 0
3486 0
3487
3487
3488 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3488 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3489 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3489 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3490 (func
3490 (func
3491 ('symbol', 'cat4')
3491 ('symbol', 'cat4')
3492 (list
3492 (list
3493 ('symbol', '278')
3493 ('symbol', '278')
3494 ('string', '5f5')
3494 ('string', '5f5')
3495 ('symbol', '1ee')
3495 ('symbol', '1ee')
3496 ('string', 'ce5')))
3496 ('string', 'ce5')))
3497 * expanded:
3497 * expanded:
3498 (_concat
3498 (_concat
3499 (_concat
3499 (_concat
3500 (_concat
3500 (_concat
3501 ('symbol', '278')
3501 ('symbol', '278')
3502 ('string', '5f5'))
3502 ('string', '5f5'))
3503 ('symbol', '1ee'))
3503 ('symbol', '1ee'))
3504 ('string', 'ce5'))
3504 ('string', 'ce5'))
3505 * concatenated:
3505 * concatenated:
3506 ('string', '2785f51eece5')
3506 ('string', '2785f51eece5')
3507 * set:
3507 * set:
3508 <baseset [0]>
3508 <baseset [0]>
3509 0
3509 0
3510
3510
3511 (check concatenation in alias nesting)
3511 (check concatenation in alias nesting)
3512
3512
3513 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3513 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3514 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3514 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3515 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3515 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3516 0
3516 0
3517
3517
3518 (check operator priority)
3518 (check operator priority)
3519
3519
3520 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3520 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3521 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3521 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3522 0
3522 0
3523 4
3523 4
3524
3524
3525 $ cd ..
3525 $ cd ..
3526
3526
3527 prepare repository that has "default" branches of multiple roots
3527 prepare repository that has "default" branches of multiple roots
3528
3528
3529 $ hg init namedbranch
3529 $ hg init namedbranch
3530 $ cd namedbranch
3530 $ cd namedbranch
3531
3531
3532 $ echo default0 >> a
3532 $ echo default0 >> a
3533 $ hg ci -Aqm0
3533 $ hg ci -Aqm0
3534 $ echo default1 >> a
3534 $ echo default1 >> a
3535 $ hg ci -m1
3535 $ hg ci -m1
3536
3536
3537 $ hg branch -q stable
3537 $ hg branch -q stable
3538 $ echo stable2 >> a
3538 $ echo stable2 >> a
3539 $ hg ci -m2
3539 $ hg ci -m2
3540 $ echo stable3 >> a
3540 $ echo stable3 >> a
3541 $ hg ci -m3
3541 $ hg ci -m3
3542
3542
3543 $ hg update -q null
3543 $ hg update -q null
3544 $ echo default4 >> a
3544 $ echo default4 >> a
3545 $ hg ci -Aqm4
3545 $ hg ci -Aqm4
3546 $ echo default5 >> a
3546 $ echo default5 >> a
3547 $ hg ci -m5
3547 $ hg ci -m5
3548
3548
3549 "null" revision belongs to "default" branch (issue4683)
3549 "null" revision belongs to "default" branch (issue4683)
3550
3550
3551 $ log 'branch(null)'
3551 $ log 'branch(null)'
3552 0
3552 0
3553 1
3553 1
3554 4
3554 4
3555 5
3555 5
3556
3556
3557 "null" revision belongs to "default" branch, but it shouldn't appear in set
3557 "null" revision belongs to "default" branch, but it shouldn't appear in set
3558 unless explicitly specified (issue4682)
3558 unless explicitly specified (issue4682)
3559
3559
3560 $ log 'children(branch(default))'
3560 $ log 'children(branch(default))'
3561 1
3561 1
3562 2
3562 2
3563 5
3563 5
3564
3564
3565 $ cd ..
3565 $ cd ..
3566
3566
3567 test author/desc/keyword in problematic encoding
3567 test author/desc/keyword in problematic encoding
3568 # unicode: cp932:
3568 # unicode: cp932:
3569 # u30A2 0x83 0x41(= 'A')
3569 # u30A2 0x83 0x41(= 'A')
3570 # u30C2 0x83 0x61(= 'a')
3570 # u30C2 0x83 0x61(= 'a')
3571
3571
3572 $ hg init problematicencoding
3572 $ hg init problematicencoding
3573 $ cd problematicencoding
3573 $ cd problematicencoding
3574
3574
3575 $ python > setup.sh <<EOF
3575 $ python > setup.sh <<EOF
3576 > print u'''
3576 > print u'''
3577 > echo a > text
3577 > echo a > text
3578 > hg add text
3578 > hg add text
3579 > hg --encoding utf-8 commit -u '\u30A2' -m none
3579 > hg --encoding utf-8 commit -u '\u30A2' -m none
3580 > echo b > text
3580 > echo b > text
3581 > hg --encoding utf-8 commit -u '\u30C2' -m none
3581 > hg --encoding utf-8 commit -u '\u30C2' -m none
3582 > echo c > text
3582 > echo c > text
3583 > hg --encoding utf-8 commit -u none -m '\u30A2'
3583 > hg --encoding utf-8 commit -u none -m '\u30A2'
3584 > echo d > text
3584 > echo d > text
3585 > hg --encoding utf-8 commit -u none -m '\u30C2'
3585 > hg --encoding utf-8 commit -u none -m '\u30C2'
3586 > '''.encode('utf-8')
3586 > '''.encode('utf-8')
3587 > EOF
3587 > EOF
3588 $ sh < setup.sh
3588 $ sh < setup.sh
3589
3589
3590 test in problematic encoding
3590 test in problematic encoding
3591 $ python > test.sh <<EOF
3591 $ python > test.sh <<EOF
3592 > print u'''
3592 > print u'''
3593 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3593 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3594 > echo ====
3594 > echo ====
3595 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3595 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3596 > echo ====
3596 > echo ====
3597 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3597 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3598 > echo ====
3598 > echo ====
3599 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3599 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3600 > echo ====
3600 > echo ====
3601 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3601 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3602 > echo ====
3602 > echo ====
3603 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3603 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3604 > '''.encode('cp932')
3604 > '''.encode('cp932')
3605 > EOF
3605 > EOF
3606 $ sh < test.sh
3606 $ sh < test.sh
3607 0
3607 0
3608 ====
3608 ====
3609 1
3609 1
3610 ====
3610 ====
3611 2
3611 2
3612 ====
3612 ====
3613 3
3613 3
3614 ====
3614 ====
3615 0
3615 0
3616 2
3616 2
3617 ====
3617 ====
3618 1
3618 1
3619 3
3619 3
3620
3620
3621 test error message of bad revset
3621 test error message of bad revset
3622 $ hg log -r 'foo\\'
3622 $ hg log -r 'foo\\'
3623 hg: parse error at 3: syntax error in revset 'foo\\'
3623 hg: parse error at 3: syntax error in revset 'foo\\'
3624 [255]
3624 [255]
3625
3625
3626 $ cd ..
3626 $ cd ..
3627
3627
3628 Test that revset predicate of extension isn't loaded at failure of
3628 Test that revset predicate of extension isn't loaded at failure of
3629 loading it
3629 loading it
3630
3630
3631 $ cd repo
3631 $ cd repo
3632
3632
3633 $ cat <<EOF > $TESTTMP/custompredicate.py
3633 $ cat <<EOF > $TESTTMP/custompredicate.py
3634 > from mercurial import error, registrar, revset
3634 > from mercurial import error, registrar, revset
3635 >
3635 >
3636 > revsetpredicate = registrar.revsetpredicate()
3636 > revsetpredicate = registrar.revsetpredicate()
3637 >
3637 >
3638 > @revsetpredicate('custom1()')
3638 > @revsetpredicate('custom1()')
3639 > def custom1(repo, subset, x):
3639 > def custom1(repo, subset, x):
3640 > return revset.baseset([1])
3640 > return revset.baseset([1])
3641 >
3641 >
3642 > raise error.Abort('intentional failure of loading extension')
3642 > raise error.Abort('intentional failure of loading extension')
3643 > EOF
3643 > EOF
3644 $ cat <<EOF > .hg/hgrc
3644 $ cat <<EOF > .hg/hgrc
3645 > [extensions]
3645 > [extensions]
3646 > custompredicate = $TESTTMP/custompredicate.py
3646 > custompredicate = $TESTTMP/custompredicate.py
3647 > EOF
3647 > EOF
3648
3648
3649 $ hg debugrevspec "custom1()"
3649 $ hg debugrevspec "custom1()"
3650 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3650 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3651 hg: parse error: unknown identifier: custom1
3651 hg: parse error: unknown identifier: custom1
3652 [255]
3652 [255]
3653
3653
3654 $ cd ..
3654 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now