##// END OF EJS Templates
revset: parse x^:: as (x^):: (issue5764)...
Yuya Nishihara -
r35556:beb667c9 default
parent child Browse files
Show More
@@ -1,694 +1,700 b''
1 1 # revsetlang.py - parser, tokenizer and utility for revision set language
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import string
11 11
12 12 from .i18n import _
13 13 from . import (
14 14 error,
15 15 node,
16 16 parser,
17 17 pycompat,
18 18 util,
19 19 )
20 20
21 21 elements = {
22 22 # token-type: binding-strength, primary, prefix, infix, suffix
23 23 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
24 24 "[": (21, None, None, ("subscript", 1, "]"), None),
25 25 "#": (21, None, None, ("relation", 21), None),
26 26 "##": (20, None, None, ("_concat", 20), None),
27 27 "~": (18, None, None, ("ancestor", 18), None),
28 28 "^": (18, None, None, ("parent", 18), "parentpost"),
29 29 "-": (5, None, ("negate", 19), ("minus", 5), None),
30 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
31 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
30 "::": (17, "dagrangeall", ("dagrangepre", 17), ("dagrange", 17),
31 "dagrangepost"),
32 "..": (17, "dagrangeall", ("dagrangepre", 17), ("dagrange", 17),
33 "dagrangepost"),
32 34 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
33 35 "not": (10, None, ("not", 10), None, None),
34 36 "!": (10, None, ("not", 10), None, None),
35 37 "and": (5, None, None, ("and", 5), None),
36 38 "&": (5, None, None, ("and", 5), None),
37 39 "%": (5, None, None, ("only", 5), "onlypost"),
38 40 "or": (4, None, None, ("or", 4), None),
39 41 "|": (4, None, None, ("or", 4), None),
40 42 "+": (4, None, None, ("or", 4), None),
41 43 "=": (3, None, None, ("keyvalue", 3), None),
42 44 ",": (2, None, None, ("list", 2), None),
43 45 ")": (0, None, None, None, None),
44 46 "]": (0, None, None, None, None),
45 47 "symbol": (0, "symbol", None, None, None),
46 48 "string": (0, "string", None, None, None),
47 49 "end": (0, None, None, None, None),
48 50 }
49 51
50 52 keywords = {'and', 'or', 'not'}
51 53
52 54 symbols = {}
53 55
54 56 _quoteletters = {'"', "'"}
55 57 _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%"))
56 58
57 59 # default set of valid characters for the initial letter of symbols
58 60 _syminitletters = set(pycompat.iterbytestr(
59 61 string.ascii_letters.encode('ascii') +
60 62 string.digits.encode('ascii') +
61 63 '._@')) | set(map(pycompat.bytechr, xrange(128, 256)))
62 64
63 65 # default set of valid characters for non-initial letters of symbols
64 66 _symletters = _syminitletters | set(pycompat.iterbytestr('-/'))
65 67
66 68 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
67 69 '''
68 70 Parse a revset statement into a stream of tokens
69 71
70 72 ``syminitletters`` is the set of valid characters for the initial
71 73 letter of symbols.
72 74
73 75 By default, character ``c`` is recognized as valid for initial
74 76 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
75 77
76 78 ``symletters`` is the set of valid characters for non-initial
77 79 letters of symbols.
78 80
79 81 By default, character ``c`` is recognized as valid for non-initial
80 82 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
81 83
82 84 Check that @ is a valid unquoted token character (issue3686):
83 85 >>> list(tokenize(b"@::"))
84 86 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
85 87
86 88 '''
87 89 program = pycompat.bytestr(program)
88 90 if syminitletters is None:
89 91 syminitletters = _syminitletters
90 92 if symletters is None:
91 93 symletters = _symletters
92 94
93 95 if program and lookup:
94 96 # attempt to parse old-style ranges first to deal with
95 97 # things like old-tag which contain query metacharacters
96 98 parts = program.split(':', 1)
97 99 if all(lookup(sym) for sym in parts if sym):
98 100 if parts[0]:
99 101 yield ('symbol', parts[0], 0)
100 102 if len(parts) > 1:
101 103 s = len(parts[0])
102 104 yield (':', None, s)
103 105 if parts[1]:
104 106 yield ('symbol', parts[1], s + 1)
105 107 yield ('end', None, len(program))
106 108 return
107 109
108 110 pos, l = 0, len(program)
109 111 while pos < l:
110 112 c = program[pos]
111 113 if c.isspace(): # skip inter-token whitespace
112 114 pass
113 115 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
114 116 yield ('::', None, pos)
115 117 pos += 1 # skip ahead
116 118 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
117 119 yield ('..', None, pos)
118 120 pos += 1 # skip ahead
119 121 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
120 122 yield ('##', None, pos)
121 123 pos += 1 # skip ahead
122 124 elif c in _simpleopletters: # handle simple operators
123 125 yield (c, None, pos)
124 126 elif (c in _quoteletters or c == 'r' and
125 127 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
126 128 if c == 'r':
127 129 pos += 1
128 130 c = program[pos]
129 131 decode = lambda x: x
130 132 else:
131 133 decode = parser.unescapestr
132 134 pos += 1
133 135 s = pos
134 136 while pos < l: # find closing quote
135 137 d = program[pos]
136 138 if d == '\\': # skip over escaped characters
137 139 pos += 2
138 140 continue
139 141 if d == c:
140 142 yield ('string', decode(program[s:pos]), s)
141 143 break
142 144 pos += 1
143 145 else:
144 146 raise error.ParseError(_("unterminated string"), s)
145 147 # gather up a symbol/keyword
146 148 elif c in syminitletters:
147 149 s = pos
148 150 pos += 1
149 151 while pos < l: # find end of symbol
150 152 d = program[pos]
151 153 if d not in symletters:
152 154 break
153 155 if d == '.' and program[pos - 1] == '.': # special case for ..
154 156 pos -= 1
155 157 break
156 158 pos += 1
157 159 sym = program[s:pos]
158 160 if sym in keywords: # operator keywords
159 161 yield (sym, None, s)
160 162 elif '-' in sym:
161 163 # some jerk gave us foo-bar-baz, try to check if it's a symbol
162 164 if lookup and lookup(sym):
163 165 # looks like a real symbol
164 166 yield ('symbol', sym, s)
165 167 else:
166 168 # looks like an expression
167 169 parts = sym.split('-')
168 170 for p in parts[:-1]:
169 171 if p: # possible consecutive -
170 172 yield ('symbol', p, s)
171 173 s += len(p)
172 174 yield ('-', None, pos)
173 175 s += 1
174 176 if parts[-1]: # possible trailing -
175 177 yield ('symbol', parts[-1], s)
176 178 else:
177 179 yield ('symbol', sym, s)
178 180 pos -= 1
179 181 else:
180 182 raise error.ParseError(_("syntax error in revset '%s'") %
181 183 program, pos)
182 184 pos += 1
183 185 yield ('end', None, pos)
184 186
185 187 # helpers
186 188
187 189 _notset = object()
188 190
189 191 def getsymbol(x):
190 192 if x and x[0] == 'symbol':
191 193 return x[1]
192 194 raise error.ParseError(_('not a symbol'))
193 195
194 196 def getstring(x, err):
195 197 if x and (x[0] == 'string' or x[0] == 'symbol'):
196 198 return x[1]
197 199 raise error.ParseError(err)
198 200
199 201 def getinteger(x, err, default=_notset):
200 202 if not x and default is not _notset:
201 203 return default
202 204 try:
203 205 return int(getstring(x, err))
204 206 except ValueError:
205 207 raise error.ParseError(err)
206 208
207 209 def getboolean(x, err):
208 210 value = util.parsebool(getsymbol(x))
209 211 if value is not None:
210 212 return value
211 213 raise error.ParseError(err)
212 214
213 215 def getlist(x):
214 216 if not x:
215 217 return []
216 218 if x[0] == 'list':
217 219 return list(x[1:])
218 220 return [x]
219 221
220 222 def getrange(x, err):
221 223 if not x:
222 224 raise error.ParseError(err)
223 225 op = x[0]
224 226 if op == 'range':
225 227 return x[1], x[2]
226 228 elif op == 'rangepre':
227 229 return None, x[1]
228 230 elif op == 'rangepost':
229 231 return x[1], None
230 232 elif op == 'rangeall':
231 233 return None, None
232 234 raise error.ParseError(err)
233 235
234 236 def getargs(x, min, max, err):
235 237 l = getlist(x)
236 238 if len(l) < min or (max >= 0 and len(l) > max):
237 239 raise error.ParseError(err)
238 240 return l
239 241
240 242 def getargsdict(x, funcname, keys):
241 243 return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys),
242 244 keyvaluenode='keyvalue', keynode='symbol')
243 245
244 246 # cache of {spec: raw parsed tree} built internally
245 247 _treecache = {}
246 248
247 249 def _cachedtree(spec):
248 250 # thread safe because parse() is reentrant and dict.__setitem__() is atomic
249 251 tree = _treecache.get(spec)
250 252 if tree is None:
251 253 _treecache[spec] = tree = parse(spec)
252 254 return tree
253 255
254 256 def _build(tmplspec, *repls):
255 257 """Create raw parsed tree from a template revset statement
256 258
257 259 >>> _build(b'f(_) and _', (b'string', b'1'), (b'symbol', b'2'))
258 260 ('and', ('func', ('symbol', 'f'), ('string', '1')), ('symbol', '2'))
259 261 """
260 262 template = _cachedtree(tmplspec)
261 263 return parser.buildtree(template, ('symbol', '_'), *repls)
262 264
263 265 def _match(patspec, tree):
264 266 """Test if a tree matches the given pattern statement; return the matches
265 267
266 268 >>> _match(b'f(_)', parse(b'f()'))
267 269 >>> _match(b'f(_)', parse(b'f(1)'))
268 270 [('func', ('symbol', 'f'), ('symbol', '1')), ('symbol', '1')]
269 271 >>> _match(b'f(_)', parse(b'f(1, 2)'))
270 272 """
271 273 pattern = _cachedtree(patspec)
272 274 return parser.matchtree(pattern, tree, ('symbol', '_'),
273 275 {'keyvalue', 'list'})
274 276
275 277 def _matchonly(revs, bases):
276 278 return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases))
277 279
278 280 def _fixops(x):
279 281 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
280 282 handled well by our simple top-down parser"""
281 283 if not isinstance(x, tuple):
282 284 return x
283 285
284 286 op = x[0]
285 287 if op == 'parent':
286 288 # x^:y means (x^) : y, not x ^ (:y)
287 289 # x^: means (x^) :, not x ^ (:)
288 290 post = ('parentpost', x[1])
289 291 if x[2][0] == 'dagrangepre':
290 292 return _fixops(('dagrange', post, x[2][1]))
293 elif x[2][0] == 'dagrangeall':
294 return _fixops(('dagrangepost', post))
291 295 elif x[2][0] == 'rangepre':
292 296 return _fixops(('range', post, x[2][1]))
293 297 elif x[2][0] == 'rangeall':
294 298 return _fixops(('rangepost', post))
295 299 elif op == 'or':
296 300 # make number of arguments deterministic:
297 301 # x + y + z -> (or x y z) -> (or (list x y z))
298 302 return (op, _fixops(('list',) + x[1:]))
299 303 elif op == 'subscript' and x[1][0] == 'relation':
300 304 # x#y[z] ternary
301 305 return _fixops(('relsubscript', x[1][1], x[1][2], x[2]))
302 306
303 307 return (op,) + tuple(_fixops(y) for y in x[1:])
304 308
305 309 def _analyze(x):
306 310 if x is None:
307 311 return x
308 312
309 313 op = x[0]
310 314 if op == 'minus':
311 315 return _analyze(_build('_ and not _', *x[1:]))
312 316 elif op == 'only':
313 317 return _analyze(_build('only(_, _)', *x[1:]))
314 318 elif op == 'onlypost':
315 319 return _analyze(_build('only(_)', x[1]))
320 elif op == 'dagrangeall':
321 raise error.ParseError(_("can't use '::' in this context"))
316 322 elif op == 'dagrangepre':
317 323 return _analyze(_build('ancestors(_)', x[1]))
318 324 elif op == 'dagrangepost':
319 325 return _analyze(_build('descendants(_)', x[1]))
320 326 elif op == 'negate':
321 327 s = getstring(x[1], _("can't negate that"))
322 328 return _analyze(('string', '-' + s))
323 329 elif op in ('string', 'symbol'):
324 330 return x
325 331 elif op == 'rangeall':
326 332 return (op, None)
327 333 elif op in {'or', 'not', 'rangepre', 'rangepost', 'parentpost'}:
328 334 return (op, _analyze(x[1]))
329 335 elif op == 'group':
330 336 return _analyze(x[1])
331 337 elif op in {'and', 'dagrange', 'range', 'parent', 'ancestor', 'relation',
332 338 'subscript'}:
333 339 ta = _analyze(x[1])
334 340 tb = _analyze(x[2])
335 341 return (op, ta, tb)
336 342 elif op == 'relsubscript':
337 343 ta = _analyze(x[1])
338 344 tb = _analyze(x[2])
339 345 tc = _analyze(x[3])
340 346 return (op, ta, tb, tc)
341 347 elif op == 'list':
342 348 return (op,) + tuple(_analyze(y) for y in x[1:])
343 349 elif op == 'keyvalue':
344 350 return (op, x[1], _analyze(x[2]))
345 351 elif op == 'func':
346 352 return (op, x[1], _analyze(x[2]))
347 353 raise ValueError('invalid operator %r' % op)
348 354
349 355 def analyze(x):
350 356 """Transform raw parsed tree to evaluatable tree which can be fed to
351 357 optimize() or getset()
352 358
353 359 All pseudo operations should be mapped to real operations or functions
354 360 defined in methods or symbols table respectively.
355 361 """
356 362 return _analyze(x)
357 363
358 364 def _optimize(x):
359 365 if x is None:
360 366 return 0, x
361 367
362 368 op = x[0]
363 369 if op in ('string', 'symbol'):
364 370 return 0.5, x # single revisions are small
365 371 elif op == 'and':
366 372 wa, ta = _optimize(x[1])
367 373 wb, tb = _optimize(x[2])
368 374 w = min(wa, wb)
369 375
370 376 # (draft/secret/_notpublic() & ::x) have a fast path
371 377 m = _match('_() & ancestors(_)', ('and', ta, tb))
372 378 if m and getsymbol(m[1]) in {'draft', 'secret', '_notpublic'}:
373 379 return w, _build('_phaseandancestors(_, _)', m[1], m[2])
374 380
375 381 # (::x and not ::y)/(not ::y and ::x) have a fast path
376 382 m = _matchonly(ta, tb) or _matchonly(tb, ta)
377 383 if m:
378 384 return w, _build('only(_, _)', *m[1:])
379 385
380 386 m = _match('not _', tb)
381 387 if m:
382 388 return wa, ('difference', ta, m[1])
383 389 if wa > wb:
384 390 op = 'andsmally'
385 391 return w, (op, ta, tb)
386 392 elif op == 'or':
387 393 # fast path for machine-generated expression, that is likely to have
388 394 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
389 395 ws, ts, ss = [], [], []
390 396 def flushss():
391 397 if not ss:
392 398 return
393 399 if len(ss) == 1:
394 400 w, t = ss[0]
395 401 else:
396 402 s = '\0'.join(t[1] for w, t in ss)
397 403 y = _build('_list(_)', ('string', s))
398 404 w, t = _optimize(y)
399 405 ws.append(w)
400 406 ts.append(t)
401 407 del ss[:]
402 408 for y in getlist(x[1]):
403 409 w, t = _optimize(y)
404 410 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
405 411 ss.append((w, t))
406 412 continue
407 413 flushss()
408 414 ws.append(w)
409 415 ts.append(t)
410 416 flushss()
411 417 if len(ts) == 1:
412 418 return ws[0], ts[0] # 'or' operation is fully optimized out
413 419 return max(ws), (op, ('list',) + tuple(ts))
414 420 elif op == 'not':
415 421 # Optimize not public() to _notpublic() because we have a fast version
416 422 if _match('public()', x[1]):
417 423 o = _optimize(_build('_notpublic()'))
418 424 return o[0], o[1]
419 425 else:
420 426 o = _optimize(x[1])
421 427 return o[0], (op, o[1])
422 428 elif op == 'rangeall':
423 429 return 1, x
424 430 elif op in ('rangepre', 'rangepost', 'parentpost'):
425 431 o = _optimize(x[1])
426 432 return o[0], (op, o[1])
427 433 elif op in ('dagrange', 'range'):
428 434 wa, ta = _optimize(x[1])
429 435 wb, tb = _optimize(x[2])
430 436 return wa + wb, (op, ta, tb)
431 437 elif op in ('parent', 'ancestor', 'relation', 'subscript'):
432 438 w, t = _optimize(x[1])
433 439 return w, (op, t, x[2])
434 440 elif op == 'relsubscript':
435 441 w, t = _optimize(x[1])
436 442 return w, (op, t, x[2], x[3])
437 443 elif op == 'list':
438 444 ws, ts = zip(*(_optimize(y) for y in x[1:]))
439 445 return sum(ws), (op,) + ts
440 446 elif op == 'keyvalue':
441 447 w, t = _optimize(x[2])
442 448 return w, (op, x[1], t)
443 449 elif op == 'func':
444 450 f = getsymbol(x[1])
445 451 wa, ta = _optimize(x[2])
446 452 w = getattr(symbols.get(f), '_weight', 1)
447 453 return w + wa, (op, x[1], ta)
448 454 raise ValueError('invalid operator %r' % op)
449 455
450 456 def optimize(tree):
451 457 """Optimize evaluatable tree
452 458
453 459 All pseudo operations should be transformed beforehand.
454 460 """
455 461 _weight, newtree = _optimize(tree)
456 462 return newtree
457 463
458 464 # the set of valid characters for the initial letter of symbols in
459 465 # alias declarations and definitions
460 466 _aliassyminitletters = _syminitletters | {'$'}
461 467
462 468 def _parsewith(spec, lookup=None, syminitletters=None):
463 469 """Generate a parse tree of given spec with given tokenizing options
464 470
465 471 >>> _parsewith(b'foo($1)', syminitletters=_aliassyminitletters)
466 472 ('func', ('symbol', 'foo'), ('symbol', '$1'))
467 473 >>> _parsewith(b'$1')
468 474 Traceback (most recent call last):
469 475 ...
470 476 ParseError: ("syntax error in revset '$1'", 0)
471 477 >>> _parsewith(b'foo bar')
472 478 Traceback (most recent call last):
473 479 ...
474 480 ParseError: ('invalid token', 4)
475 481 """
476 482 p = parser.parser(elements)
477 483 tree, pos = p.parse(tokenize(spec, lookup=lookup,
478 484 syminitletters=syminitletters))
479 485 if pos != len(spec):
480 486 raise error.ParseError(_('invalid token'), pos)
481 487 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
482 488
483 489 class _aliasrules(parser.basealiasrules):
484 490 """Parsing and expansion rule set of revset aliases"""
485 491 _section = _('revset alias')
486 492
487 493 @staticmethod
488 494 def _parse(spec):
489 495 """Parse alias declaration/definition ``spec``
490 496
491 497 This allows symbol names to use also ``$`` as an initial letter
492 498 (for backward compatibility), and callers of this function should
493 499 examine whether ``$`` is used also for unexpected symbols or not.
494 500 """
495 501 return _parsewith(spec, syminitletters=_aliassyminitletters)
496 502
497 503 @staticmethod
498 504 def _trygetfunc(tree):
499 505 if tree[0] == 'func' and tree[1][0] == 'symbol':
500 506 return tree[1][1], getlist(tree[2])
501 507
502 508 def expandaliases(tree, aliases, warn=None):
503 509 """Expand aliases in a tree, aliases is a list of (name, value) tuples"""
504 510 aliases = _aliasrules.buildmap(aliases)
505 511 tree = _aliasrules.expand(aliases, tree)
506 512 # warn about problematic (but not referred) aliases
507 513 if warn is not None:
508 514 for name, alias in sorted(aliases.iteritems()):
509 515 if alias.error and not alias.warned:
510 516 warn(_('warning: %s\n') % (alias.error))
511 517 alias.warned = True
512 518 return tree
513 519
514 520 def foldconcat(tree):
515 521 """Fold elements to be concatenated by `##`
516 522 """
517 523 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
518 524 return tree
519 525 if tree[0] == '_concat':
520 526 pending = [tree]
521 527 l = []
522 528 while pending:
523 529 e = pending.pop()
524 530 if e[0] == '_concat':
525 531 pending.extend(reversed(e[1:]))
526 532 elif e[0] in ('string', 'symbol'):
527 533 l.append(e[1])
528 534 else:
529 535 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
530 536 raise error.ParseError(msg)
531 537 return ('string', ''.join(l))
532 538 else:
533 539 return tuple(foldconcat(t) for t in tree)
534 540
535 541 def parse(spec, lookup=None):
536 542 return _parsewith(spec, lookup=lookup)
537 543
538 544 def _quote(s):
539 545 r"""Quote a value in order to make it safe for the revset engine.
540 546
541 547 >>> _quote(b'asdf')
542 548 "'asdf'"
543 549 >>> _quote(b"asdf'\"")
544 550 '\'asdf\\\'"\''
545 551 >>> _quote(b'asdf\'')
546 552 "'asdf\\''"
547 553 >>> _quote(1)
548 554 "'1'"
549 555 """
550 556 return "'%s'" % util.escapestr(pycompat.bytestr(s))
551 557
552 558 def formatspec(expr, *args):
553 559 '''
554 560 This is a convenience function for using revsets internally, and
555 561 escapes arguments appropriately. Aliases are intentionally ignored
556 562 so that intended expression behavior isn't accidentally subverted.
557 563
558 564 Supported arguments:
559 565
560 566 %r = revset expression, parenthesized
561 567 %d = int(arg), no quoting
562 568 %s = string(arg), escaped and single-quoted
563 569 %b = arg.branch(), escaped and single-quoted
564 570 %n = hex(arg), single-quoted
565 571 %% = a literal '%'
566 572
567 573 Prefixing the type with 'l' specifies a parenthesized list of that type.
568 574
569 575 >>> formatspec(b'%r:: and %lr', b'10 or 11', (b"this()", b"that()"))
570 576 '(10 or 11):: and ((this()) or (that()))'
571 577 >>> formatspec(b'%d:: and not %d::', 10, 20)
572 578 '10:: and not 20::'
573 579 >>> formatspec(b'%ld or %ld', [], [1])
574 580 "_list('') or 1"
575 581 >>> formatspec(b'keyword(%s)', b'foo\\xe9')
576 582 "keyword('foo\\\\xe9')"
577 583 >>> b = lambda: b'default'
578 584 >>> b.branch = b
579 585 >>> formatspec(b'branch(%b)', b)
580 586 "branch('default')"
581 587 >>> formatspec(b'root(%ls)', [b'a', b'b', b'c', b'd'])
582 588 "root(_list('a\\x00b\\x00c\\x00d'))"
583 589 '''
584 590
585 591 def argtype(c, arg):
586 592 if c == 'd':
587 593 return '%d' % int(arg)
588 594 elif c == 's':
589 595 return _quote(arg)
590 596 elif c == 'r':
591 597 parse(arg) # make sure syntax errors are confined
592 598 return '(%s)' % arg
593 599 elif c == 'n':
594 600 return _quote(node.hex(arg))
595 601 elif c == 'b':
596 602 return _quote(arg.branch())
597 603
598 604 def listexp(s, t):
599 605 l = len(s)
600 606 if l == 0:
601 607 return "_list('')"
602 608 elif l == 1:
603 609 return argtype(t, s[0])
604 610 elif t == 'd':
605 611 return "_intlist('%s')" % "\0".join('%d' % int(a) for a in s)
606 612 elif t == 's':
607 613 return "_list('%s')" % "\0".join(s)
608 614 elif t == 'n':
609 615 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
610 616 elif t == 'b':
611 617 return "_list('%s')" % "\0".join(a.branch() for a in s)
612 618
613 619 m = l // 2
614 620 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
615 621
616 622 expr = pycompat.bytestr(expr)
617 623 ret = ''
618 624 pos = 0
619 625 arg = 0
620 626 while pos < len(expr):
621 627 c = expr[pos]
622 628 if c == '%':
623 629 pos += 1
624 630 d = expr[pos]
625 631 if d == '%':
626 632 ret += d
627 633 elif d in 'dsnbr':
628 634 ret += argtype(d, args[arg])
629 635 arg += 1
630 636 elif d == 'l':
631 637 # a list of some type
632 638 pos += 1
633 639 d = expr[pos]
634 640 ret += listexp(list(args[arg]), d)
635 641 arg += 1
636 642 else:
637 643 raise error.Abort(_('unexpected revspec format character %s')
638 644 % d)
639 645 else:
640 646 ret += c
641 647 pos += 1
642 648
643 649 return ret
644 650
645 651 def prettyformat(tree):
646 652 return parser.prettyformat(tree, ('string', 'symbol'))
647 653
648 654 def depth(tree):
649 655 if isinstance(tree, tuple):
650 656 return max(map(depth, tree)) + 1
651 657 else:
652 658 return 0
653 659
654 660 def funcsused(tree):
655 661 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
656 662 return set()
657 663 else:
658 664 funcs = set()
659 665 for s in tree[1:]:
660 666 funcs |= funcsused(s)
661 667 if tree[0] == 'func':
662 668 funcs.add(tree[1][1])
663 669 return funcs
664 670
665 671 _hashre = util.re.compile('[0-9a-fA-F]{1,40}$')
666 672
667 673 def _ishashlikesymbol(symbol):
668 674 """returns true if the symbol looks like a hash"""
669 675 return _hashre.match(symbol)
670 676
671 677 def gethashlikesymbols(tree):
672 678 """returns the list of symbols of the tree that look like hashes
673 679
674 680 >>> gethashlikesymbols(('dagrange', ('symbol', '3'), ('symbol', 'abe3ff')))
675 681 ['3', 'abe3ff']
676 682 >>> gethashlikesymbols(('func', ('symbol', 'precursors'), ('symbol', '.')))
677 683 []
678 684 >>> gethashlikesymbols(('func', ('symbol', 'precursors'), ('symbol', '34')))
679 685 ['34']
680 686 >>> gethashlikesymbols(('symbol', 'abe3ffZ'))
681 687 []
682 688 """
683 689 if not tree:
684 690 return []
685 691
686 692 if tree[0] == "symbol":
687 693 if _ishashlikesymbol(tree[1]):
688 694 return [tree[1]]
689 695 elif len(tree) >= 3:
690 696 results = []
691 697 for subtree in tree[1:]:
692 698 results += gethashlikesymbols(subtree)
693 699 return results
694 700 return []
@@ -1,2739 +1,2776 b''
1 1 $ HGENCODING=utf-8
2 2 $ export HGENCODING
3 3 $ cat > testrevset.py << EOF
4 4 > import mercurial.revset
5 5 >
6 6 > baseset = mercurial.revset.baseset
7 7 >
8 8 > def r3232(repo, subset, x):
9 9 > """"simple revset that return [3,2,3,2]
10 10 >
11 11 > revisions duplicated on purpose.
12 12 > """
13 13 > if 3 not in subset:
14 14 > if 2 in subset:
15 15 > return baseset([2,2])
16 16 > return baseset()
17 17 > return baseset([3,3,2,2])
18 18 >
19 19 > mercurial.revset.symbols['r3232'] = r3232
20 20 > EOF
21 21 $ cat >> $HGRCPATH << EOF
22 22 > [extensions]
23 23 > drawdag=$TESTDIR/drawdag.py
24 24 > testrevset=$TESTTMP/testrevset.py
25 25 > EOF
26 26
27 27 $ try() {
28 28 > hg debugrevspec --debug "$@"
29 29 > }
30 30
31 31 $ log() {
32 32 > hg log --template '{rev}\n' -r "$1"
33 33 > }
34 34
35 35 extension to build '_intlist()' and '_hexlist()', which is necessary because
36 36 these predicates use '\0' as a separator:
37 37
38 38 $ cat <<EOF > debugrevlistspec.py
39 39 > from __future__ import absolute_import
40 40 > from mercurial import (
41 41 > node as nodemod,
42 42 > registrar,
43 43 > revset,
44 44 > revsetlang,
45 45 > smartset,
46 46 > )
47 47 > cmdtable = {}
48 48 > command = registrar.command(cmdtable)
49 49 > @command(b'debugrevlistspec',
50 50 > [('', 'optimize', None, 'print parsed tree after optimizing'),
51 51 > ('', 'bin', None, 'unhexlify arguments')])
52 52 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
53 53 > if opts['bin']:
54 54 > args = map(nodemod.bin, args)
55 55 > expr = revsetlang.formatspec(fmt, list(args))
56 56 > if ui.verbose:
57 57 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
58 58 > ui.note(revsetlang.prettyformat(tree), "\n")
59 59 > if opts["optimize"]:
60 60 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
61 61 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
62 62 > "\n")
63 63 > func = revset.match(ui, expr, repo)
64 64 > revs = func(repo)
65 65 > if ui.verbose:
66 66 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
67 67 > for c in revs:
68 68 > ui.write("%s\n" % c)
69 69 > EOF
70 70 $ cat <<EOF >> $HGRCPATH
71 71 > [extensions]
72 72 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
73 73 > EOF
74 74 $ trylist() {
75 75 > hg debugrevlistspec --debug "$@"
76 76 > }
77 77
78 78 $ hg init repo
79 79 $ cd repo
80 80
81 81 $ echo a > a
82 82 $ hg branch a
83 83 marked working directory as branch a
84 84 (branches are permanent and global, did you want a bookmark?)
85 85 $ hg ci -Aqm0
86 86
87 87 $ echo b > b
88 88 $ hg branch b
89 89 marked working directory as branch b
90 90 $ hg ci -Aqm1
91 91
92 92 $ rm a
93 93 $ hg branch a-b-c-
94 94 marked working directory as branch a-b-c-
95 95 $ hg ci -Aqm2 -u Bob
96 96
97 97 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
98 98 2
99 99 $ hg log -r "extra('branch')" --template '{rev}\n'
100 100 0
101 101 1
102 102 2
103 103 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
104 104 0 a
105 105 2 a-b-c-
106 106
107 107 $ hg co 1
108 108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109 $ hg branch +a+b+c+
110 110 marked working directory as branch +a+b+c+
111 111 $ hg ci -Aqm3
112 112
113 113 $ hg co 2 # interleave
114 114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
115 115 $ echo bb > b
116 116 $ hg branch -- -a-b-c-
117 117 marked working directory as branch -a-b-c-
118 118 $ hg ci -Aqm4 -d "May 12 2005"
119 119
120 120 $ hg co 3
121 121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 $ hg branch !a/b/c/
123 123 marked working directory as branch !a/b/c/
124 124 $ hg ci -Aqm"5 bug"
125 125
126 126 $ hg merge 4
127 127 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
128 128 (branch merge, don't forget to commit)
129 129 $ hg branch _a_b_c_
130 130 marked working directory as branch _a_b_c_
131 131 $ hg ci -Aqm"6 issue619"
132 132
133 133 $ hg branch .a.b.c.
134 134 marked working directory as branch .a.b.c.
135 135 $ hg ci -Aqm7
136 136
137 137 $ hg branch all
138 138 marked working directory as branch all
139 139
140 140 $ hg co 4
141 141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142 $ hg branch Γ©
143 143 marked working directory as branch \xc3\xa9 (esc)
144 144 $ hg ci -Aqm9
145 145
146 146 $ hg tag -r6 1.0
147 147 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
148 148
149 149 $ hg clone --quiet -U -r 7 . ../remote1
150 150 $ hg clone --quiet -U -r 8 . ../remote2
151 151 $ echo "[paths]" >> .hg/hgrc
152 152 $ echo "default = ../remote1" >> .hg/hgrc
153 153
154 154 trivial
155 155
156 156 $ try 0:1
157 157 (range
158 158 (symbol '0')
159 159 (symbol '1'))
160 160 * set:
161 161 <spanset+ 0:2>
162 162 0
163 163 1
164 164 $ try --optimize :
165 165 (rangeall
166 166 None)
167 167 * optimized:
168 168 (rangeall
169 169 None)
170 170 * set:
171 171 <spanset+ 0:10>
172 172 0
173 173 1
174 174 2
175 175 3
176 176 4
177 177 5
178 178 6
179 179 7
180 180 8
181 181 9
182 182 $ try 3::6
183 183 (dagrange
184 184 (symbol '3')
185 185 (symbol '6'))
186 186 * set:
187 187 <baseset+ [3, 5, 6]>
188 188 3
189 189 5
190 190 6
191 191 $ try '0|1|2'
192 192 (or
193 193 (list
194 194 (symbol '0')
195 195 (symbol '1')
196 196 (symbol '2')))
197 197 * set:
198 198 <baseset [0, 1, 2]>
199 199 0
200 200 1
201 201 2
202 202
203 203 names that should work without quoting
204 204
205 205 $ try a
206 206 (symbol 'a')
207 207 * set:
208 208 <baseset [0]>
209 209 0
210 210 $ try b-a
211 211 (minus
212 212 (symbol 'b')
213 213 (symbol 'a'))
214 214 * set:
215 215 <filteredset
216 216 <baseset [1]>,
217 217 <not
218 218 <baseset [0]>>>
219 219 1
220 220 $ try _a_b_c_
221 221 (symbol '_a_b_c_')
222 222 * set:
223 223 <baseset [6]>
224 224 6
225 225 $ try _a_b_c_-a
226 226 (minus
227 227 (symbol '_a_b_c_')
228 228 (symbol 'a'))
229 229 * set:
230 230 <filteredset
231 231 <baseset [6]>,
232 232 <not
233 233 <baseset [0]>>>
234 234 6
235 235 $ try .a.b.c.
236 236 (symbol '.a.b.c.')
237 237 * set:
238 238 <baseset [7]>
239 239 7
240 240 $ try .a.b.c.-a
241 241 (minus
242 242 (symbol '.a.b.c.')
243 243 (symbol 'a'))
244 244 * set:
245 245 <filteredset
246 246 <baseset [7]>,
247 247 <not
248 248 <baseset [0]>>>
249 249 7
250 250
251 251 names that should be caught by fallback mechanism
252 252
253 253 $ try -- '-a-b-c-'
254 254 (symbol '-a-b-c-')
255 255 * set:
256 256 <baseset [4]>
257 257 4
258 258 $ log -a-b-c-
259 259 4
260 260 $ try '+a+b+c+'
261 261 (symbol '+a+b+c+')
262 262 * set:
263 263 <baseset [3]>
264 264 3
265 265 $ try '+a+b+c+:'
266 266 (rangepost
267 267 (symbol '+a+b+c+'))
268 268 * set:
269 269 <spanset+ 3:10>
270 270 3
271 271 4
272 272 5
273 273 6
274 274 7
275 275 8
276 276 9
277 277 $ try ':+a+b+c+'
278 278 (rangepre
279 279 (symbol '+a+b+c+'))
280 280 * set:
281 281 <spanset+ 0:4>
282 282 0
283 283 1
284 284 2
285 285 3
286 286 $ try -- '-a-b-c-:+a+b+c+'
287 287 (range
288 288 (symbol '-a-b-c-')
289 289 (symbol '+a+b+c+'))
290 290 * set:
291 291 <spanset- 3:5>
292 292 4
293 293 3
294 294 $ log '-a-b-c-:+a+b+c+'
295 295 4
296 296 3
297 297
298 298 $ try -- -a-b-c--a # complains
299 299 (minus
300 300 (minus
301 301 (minus
302 302 (negate
303 303 (symbol 'a'))
304 304 (symbol 'b'))
305 305 (symbol 'c'))
306 306 (negate
307 307 (symbol 'a')))
308 308 abort: unknown revision '-a'!
309 309 [255]
310 310 $ try Γ©
311 311 (symbol '\xc3\xa9')
312 312 * set:
313 313 <baseset [9]>
314 314 9
315 315
316 316 no quoting needed
317 317
318 318 $ log ::a-b-c-
319 319 0
320 320 1
321 321 2
322 322
323 323 quoting needed
324 324
325 325 $ try '"-a-b-c-"-a'
326 326 (minus
327 327 (string '-a-b-c-')
328 328 (symbol 'a'))
329 329 * set:
330 330 <filteredset
331 331 <baseset [4]>,
332 332 <not
333 333 <baseset [0]>>>
334 334 4
335 335
336 336 $ log '1 or 2'
337 337 1
338 338 2
339 339 $ log '1|2'
340 340 1
341 341 2
342 342 $ log '1 and 2'
343 343 $ log '1&2'
344 344 $ try '1&2|3' # precedence - and is higher
345 345 (or
346 346 (list
347 347 (and
348 348 (symbol '1')
349 349 (symbol '2'))
350 350 (symbol '3')))
351 351 * set:
352 352 <addset
353 353 <baseset []>,
354 354 <baseset [3]>>
355 355 3
356 356 $ try '1|2&3'
357 357 (or
358 358 (list
359 359 (symbol '1')
360 360 (and
361 361 (symbol '2')
362 362 (symbol '3'))))
363 363 * set:
364 364 <addset
365 365 <baseset [1]>,
366 366 <baseset []>>
367 367 1
368 368 $ try '1&2&3' # associativity
369 369 (and
370 370 (and
371 371 (symbol '1')
372 372 (symbol '2'))
373 373 (symbol '3'))
374 374 * set:
375 375 <baseset []>
376 376 $ try '1|(2|3)'
377 377 (or
378 378 (list
379 379 (symbol '1')
380 380 (group
381 381 (or
382 382 (list
383 383 (symbol '2')
384 384 (symbol '3'))))))
385 385 * set:
386 386 <addset
387 387 <baseset [1]>,
388 388 <baseset [2, 3]>>
389 389 1
390 390 2
391 391 3
392 392 $ log '1.0' # tag
393 393 6
394 394 $ log 'a' # branch
395 395 0
396 396 $ log '2785f51ee'
397 397 0
398 398 $ log 'date(2005)'
399 399 4
400 400 $ log 'date(this is a test)'
401 401 hg: parse error at 10: unexpected token: symbol
402 402 [255]
403 403 $ log 'date()'
404 404 hg: parse error: date requires a string
405 405 [255]
406 406 $ log 'date'
407 407 abort: unknown revision 'date'!
408 408 [255]
409 409 $ log 'date('
410 410 hg: parse error at 5: not a prefix: end
411 411 [255]
412 412 $ log 'date("\xy")'
413 413 hg: parse error: invalid \x escape
414 414 [255]
415 415 $ log 'date(tip)'
416 416 hg: parse error: invalid date: 'tip'
417 417 [255]
418 418 $ log '0:date'
419 419 abort: unknown revision 'date'!
420 420 [255]
421 421 $ log '::"date"'
422 422 abort: unknown revision 'date'!
423 423 [255]
424 424 $ hg book date -r 4
425 425 $ log '0:date'
426 426 0
427 427 1
428 428 2
429 429 3
430 430 4
431 431 $ log '::date'
432 432 0
433 433 1
434 434 2
435 435 4
436 436 $ log '::"date"'
437 437 0
438 438 1
439 439 2
440 440 4
441 441 $ log 'date(2005) and 1::'
442 442 4
443 443 $ hg book -d date
444 444
445 445 function name should be a symbol
446 446
447 447 $ log '"date"(2005)'
448 448 hg: parse error: not a symbol
449 449 [255]
450 450
451 451 keyword arguments
452 452
453 453 $ log 'extra(branch, value=a)'
454 454 0
455 455
456 456 $ log 'extra(branch, a, b)'
457 457 hg: parse error: extra takes at most 2 positional arguments
458 458 [255]
459 459 $ log 'extra(a, label=b)'
460 460 hg: parse error: extra got multiple values for keyword argument 'label'
461 461 [255]
462 462 $ log 'extra(label=branch, default)'
463 463 hg: parse error: extra got an invalid argument
464 464 [255]
465 465 $ log 'extra(branch, foo+bar=baz)'
466 466 hg: parse error: extra got an invalid argument
467 467 [255]
468 468 $ log 'extra(unknown=branch)'
469 469 hg: parse error: extra got an unexpected keyword argument 'unknown'
470 470 [255]
471 471
472 472 $ try 'foo=bar|baz'
473 473 (keyvalue
474 474 (symbol 'foo')
475 475 (or
476 476 (list
477 477 (symbol 'bar')
478 478 (symbol 'baz'))))
479 479 hg: parse error: can't use a key-value pair in this context
480 480 [255]
481 481
482 482 right-hand side should be optimized recursively
483 483
484 484 $ try --optimize 'foo=(not public())'
485 485 (keyvalue
486 486 (symbol 'foo')
487 487 (group
488 488 (not
489 489 (func
490 490 (symbol 'public')
491 491 None))))
492 492 * optimized:
493 493 (keyvalue
494 494 (symbol 'foo')
495 495 (func
496 496 (symbol '_notpublic')
497 497 None))
498 498 hg: parse error: can't use a key-value pair in this context
499 499 [255]
500 500
501 501 relation-subscript operator has the highest binding strength (as function call):
502 502
503 503 $ hg debugrevspec -p parsed 'tip:tip^#generations[-1]'
504 504 * parsed:
505 505 (range
506 506 (symbol 'tip')
507 507 (relsubscript
508 508 (parentpost
509 509 (symbol 'tip'))
510 510 (symbol 'generations')
511 511 (negate
512 512 (symbol '1'))))
513 513 9
514 514 8
515 515 7
516 516 6
517 517 5
518 518 4
519 519
520 520 $ hg debugrevspec -p parsed --no-show-revs 'not public()#generations[0]'
521 521 * parsed:
522 522 (not
523 523 (relsubscript
524 524 (func
525 525 (symbol 'public')
526 526 None)
527 527 (symbol 'generations')
528 528 (symbol '0')))
529 529
530 530 left-hand side of relation-subscript operator should be optimized recursively:
531 531
532 532 $ hg debugrevspec -p analyzed -p optimized --no-show-revs \
533 533 > '(not public())#generations[0]'
534 534 * analyzed:
535 535 (relsubscript
536 536 (not
537 537 (func
538 538 (symbol 'public')
539 539 None))
540 540 (symbol 'generations')
541 541 (symbol '0'))
542 542 * optimized:
543 543 (relsubscript
544 544 (func
545 545 (symbol '_notpublic')
546 546 None)
547 547 (symbol 'generations')
548 548 (symbol '0'))
549 549
550 550 resolution of subscript and relation-subscript ternary operators:
551 551
552 552 $ hg debugrevspec -p analyzed 'tip[0]'
553 553 * analyzed:
554 554 (subscript
555 555 (symbol 'tip')
556 556 (symbol '0'))
557 557 hg: parse error: can't use a subscript in this context
558 558 [255]
559 559
560 560 $ hg debugrevspec -p analyzed 'tip#rel[0]'
561 561 * analyzed:
562 562 (relsubscript
563 563 (symbol 'tip')
564 564 (symbol 'rel')
565 565 (symbol '0'))
566 566 hg: parse error: unknown identifier: rel
567 567 [255]
568 568
569 569 $ hg debugrevspec -p analyzed '(tip#rel)[0]'
570 570 * analyzed:
571 571 (subscript
572 572 (relation
573 573 (symbol 'tip')
574 574 (symbol 'rel'))
575 575 (symbol '0'))
576 576 hg: parse error: can't use a subscript in this context
577 577 [255]
578 578
579 579 $ hg debugrevspec -p analyzed 'tip#rel[0][1]'
580 580 * analyzed:
581 581 (subscript
582 582 (relsubscript
583 583 (symbol 'tip')
584 584 (symbol 'rel')
585 585 (symbol '0'))
586 586 (symbol '1'))
587 587 hg: parse error: can't use a subscript in this context
588 588 [255]
589 589
590 590 $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
591 591 * analyzed:
592 592 (relsubscript
593 593 (relation
594 594 (symbol 'tip')
595 595 (symbol 'rel0'))
596 596 (symbol 'rel1')
597 597 (symbol '1'))
598 598 hg: parse error: unknown identifier: rel1
599 599 [255]
600 600
601 601 $ hg debugrevspec -p analyzed 'tip#rel0[0]#rel1[1]'
602 602 * analyzed:
603 603 (relsubscript
604 604 (relsubscript
605 605 (symbol 'tip')
606 606 (symbol 'rel0')
607 607 (symbol '0'))
608 608 (symbol 'rel1')
609 609 (symbol '1'))
610 610 hg: parse error: unknown identifier: rel1
611 611 [255]
612 612
613 613 parse errors of relation, subscript and relation-subscript operators:
614 614
615 615 $ hg debugrevspec '[0]'
616 616 hg: parse error at 0: not a prefix: [
617 617 [255]
618 618 $ hg debugrevspec '.#'
619 619 hg: parse error at 2: not a prefix: end
620 620 [255]
621 621 $ hg debugrevspec '#rel'
622 622 hg: parse error at 0: not a prefix: #
623 623 [255]
624 624 $ hg debugrevspec '.#rel[0'
625 625 hg: parse error at 7: unexpected token: end
626 626 [255]
627 627 $ hg debugrevspec '.]'
628 628 hg: parse error at 1: invalid token
629 629 [255]
630 630
631 631 $ hg debugrevspec '.#generations[a]'
632 632 hg: parse error: relation subscript must be an integer
633 633 [255]
634 634 $ hg debugrevspec '.#generations[1-2]'
635 635 hg: parse error: relation subscript must be an integer
636 636 [255]
637 637
638 638 parsed tree at stages:
639 639
640 640 $ hg debugrevspec -p all '()'
641 641 * parsed:
642 642 (group
643 643 None)
644 644 * expanded:
645 645 (group
646 646 None)
647 647 * concatenated:
648 648 (group
649 649 None)
650 650 * analyzed:
651 651 None
652 652 * optimized:
653 653 None
654 654 hg: parse error: missing argument
655 655 [255]
656 656
657 657 $ hg debugrevspec --no-optimized -p all '()'
658 658 * parsed:
659 659 (group
660 660 None)
661 661 * expanded:
662 662 (group
663 663 None)
664 664 * concatenated:
665 665 (group
666 666 None)
667 667 * analyzed:
668 668 None
669 669 hg: parse error: missing argument
670 670 [255]
671 671
672 672 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
673 673 * parsed:
674 674 (minus
675 675 (group
676 676 (or
677 677 (list
678 678 (symbol '0')
679 679 (symbol '1'))))
680 680 (symbol '1'))
681 681 * analyzed:
682 682 (and
683 683 (or
684 684 (list
685 685 (symbol '0')
686 686 (symbol '1')))
687 687 (not
688 688 (symbol '1')))
689 689 * optimized:
690 690 (difference
691 691 (func
692 692 (symbol '_list')
693 693 (string '0\x001'))
694 694 (symbol '1'))
695 695 0
696 696
697 697 $ hg debugrevspec -p unknown '0'
698 698 abort: invalid stage name: unknown
699 699 [255]
700 700
701 701 $ hg debugrevspec -p all --optimize '0'
702 702 abort: cannot use --optimize with --show-stage
703 703 [255]
704 704
705 705 verify optimized tree:
706 706
707 707 $ hg debugrevspec --verify '0|1'
708 708
709 709 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
710 710 * analyzed:
711 711 (and
712 712 (func
713 713 (symbol 'r3232')
714 714 None)
715 715 (symbol '2'))
716 716 * optimized:
717 717 (andsmally
718 718 (func
719 719 (symbol 'r3232')
720 720 None)
721 721 (symbol '2'))
722 722 * analyzed set:
723 723 <baseset [2]>
724 724 * optimized set:
725 725 <baseset [2, 2]>
726 726 --- analyzed
727 727 +++ optimized
728 728 2
729 729 +2
730 730 [1]
731 731
732 732 $ hg debugrevspec --no-optimized --verify-optimized '0'
733 733 abort: cannot use --verify-optimized with --no-optimized
734 734 [255]
735 735
736 736 Test that symbols only get parsed as functions if there's an opening
737 737 parenthesis.
738 738
739 739 $ hg book only -r 9
740 740 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
741 741 8
742 742 9
743 743
744 744 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
745 745 may be hidden (issue5385)
746 746
747 747 $ try -p parsed -p analyzed ':'
748 748 * parsed:
749 749 (rangeall
750 750 None)
751 751 * analyzed:
752 752 (rangeall
753 753 None)
754 754 * set:
755 755 <spanset+ 0:10>
756 756 0
757 757 1
758 758 2
759 759 3
760 760 4
761 761 5
762 762 6
763 763 7
764 764 8
765 765 9
766 766 $ try -p analyzed ':1'
767 767 * analyzed:
768 768 (rangepre
769 769 (symbol '1'))
770 770 * set:
771 771 <spanset+ 0:2>
772 772 0
773 773 1
774 774 $ try -p analyzed ':(1|2)'
775 775 * analyzed:
776 776 (rangepre
777 777 (or
778 778 (list
779 779 (symbol '1')
780 780 (symbol '2'))))
781 781 * set:
782 782 <spanset+ 0:3>
783 783 0
784 784 1
785 785 2
786 786 $ try -p analyzed ':(1&2)'
787 787 * analyzed:
788 788 (rangepre
789 789 (and
790 790 (symbol '1')
791 791 (symbol '2')))
792 792 * set:
793 793 <baseset []>
794 794
795 infix/suffix resolution of ^ operator (issue2884):
795 infix/suffix resolution of ^ operator (issue2884, issue5764):
796 796
797 797 x^:y means (x^):y
798 798
799 799 $ try '1^:2'
800 800 (range
801 801 (parentpost
802 802 (symbol '1'))
803 803 (symbol '2'))
804 804 * set:
805 805 <spanset+ 0:3>
806 806 0
807 807 1
808 808 2
809 809
810 810 $ try '1^::2'
811 811 (dagrange
812 812 (parentpost
813 813 (symbol '1'))
814 814 (symbol '2'))
815 815 * set:
816 816 <baseset+ [0, 1, 2]>
817 817 0
818 818 1
819 819 2
820 820
821 $ try '1^..2'
822 (dagrange
823 (parentpost
824 (symbol '1'))
825 (symbol '2'))
826 * set:
827 <baseset+ [0, 1, 2]>
828 0
829 1
830 2
831
821 832 $ try '9^:'
822 833 (rangepost
823 834 (parentpost
824 835 (symbol '9')))
825 836 * set:
826 837 <spanset+ 8:10>
827 838 8
828 839 9
829 840
841 $ try '9^::'
842 (dagrangepost
843 (parentpost
844 (symbol '9')))
845 * set:
846 <generatorsetasc+>
847 8
848 9
849
850 $ try '9^..'
851 (dagrangepost
852 (parentpost
853 (symbol '9')))
854 * set:
855 <generatorsetasc+>
856 8
857 9
858
830 859 x^:y should be resolved before omitting group operators
831 860
832 861 $ try '1^(:2)'
833 862 (parent
834 863 (symbol '1')
835 864 (group
836 865 (rangepre
837 866 (symbol '2'))))
838 867 hg: parse error: ^ expects a number 0, 1, or 2
839 868 [255]
840 869
841 870 x^:y should be resolved recursively
842 871
843 872 $ try 'sort(1^:2)'
844 873 (func
845 874 (symbol 'sort')
846 875 (range
847 876 (parentpost
848 877 (symbol '1'))
849 878 (symbol '2')))
850 879 * set:
851 880 <spanset+ 0:3>
852 881 0
853 882 1
854 883 2
855 884
856 885 $ try '(3^:4)^:2'
857 886 (range
858 887 (parentpost
859 888 (group
860 889 (range
861 890 (parentpost
862 891 (symbol '3'))
863 892 (symbol '4'))))
864 893 (symbol '2'))
865 894 * set:
866 895 <spanset+ 0:3>
867 896 0
868 897 1
869 898 2
870 899
871 900 $ try '(3^::4)^::2'
872 901 (dagrange
873 902 (parentpost
874 903 (group
875 904 (dagrange
876 905 (parentpost
877 906 (symbol '3'))
878 907 (symbol '4'))))
879 908 (symbol '2'))
880 909 * set:
881 910 <baseset+ [0, 1, 2]>
882 911 0
883 912 1
884 913 2
885 914
886 915 $ try '(9^:)^:'
887 916 (rangepost
888 917 (parentpost
889 918 (group
890 919 (rangepost
891 920 (parentpost
892 921 (symbol '9'))))))
893 922 * set:
894 923 <spanset+ 4:10>
895 924 4
896 925 5
897 926 6
898 927 7
899 928 8
900 929 9
901 930
902 931 x^ in alias should also be resolved
903 932
904 933 $ try 'A' --config 'revsetalias.A=1^:2'
905 934 (symbol 'A')
906 935 * expanded:
907 936 (range
908 937 (parentpost
909 938 (symbol '1'))
910 939 (symbol '2'))
911 940 * set:
912 941 <spanset+ 0:3>
913 942 0
914 943 1
915 944 2
916 945
917 946 $ try 'A:2' --config 'revsetalias.A=1^'
918 947 (range
919 948 (symbol 'A')
920 949 (symbol '2'))
921 950 * expanded:
922 951 (range
923 952 (parentpost
924 953 (symbol '1'))
925 954 (symbol '2'))
926 955 * set:
927 956 <spanset+ 0:3>
928 957 0
929 958 1
930 959 2
931 960
932 961 but not beyond the boundary of alias expansion, because the resolution should
933 962 be made at the parsing stage
934 963
935 964 $ try '1^A' --config 'revsetalias.A=:2'
936 965 (parent
937 966 (symbol '1')
938 967 (symbol 'A'))
939 968 * expanded:
940 969 (parent
941 970 (symbol '1')
942 971 (rangepre
943 972 (symbol '2')))
944 973 hg: parse error: ^ expects a number 0, 1, or 2
945 974 [255]
946 975
976 '::' itself isn't a valid expression
977
978 $ try '::'
979 (dagrangeall
980 None)
981 hg: parse error: can't use '::' in this context
982 [255]
983
947 984 ancestor can accept 0 or more arguments
948 985
949 986 $ log 'ancestor()'
950 987 $ log 'ancestor(1)'
951 988 1
952 989 $ log 'ancestor(4,5)'
953 990 1
954 991 $ log 'ancestor(4,5) and 4'
955 992 $ log 'ancestor(0,0,1,3)'
956 993 0
957 994 $ log 'ancestor(3,1,5,3,5,1)'
958 995 1
959 996 $ log 'ancestor(0,1,3,5)'
960 997 0
961 998 $ log 'ancestor(1,2,3,4,5)'
962 999 1
963 1000
964 1001 test ancestors
965 1002
966 1003 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
967 1004 @ 9
968 1005 o 8
969 1006 | o 7
970 1007 | o 6
971 1008 |/|
972 1009 | o 5
973 1010 o | 4
974 1011 | o 3
975 1012 o | 2
976 1013 |/
977 1014 o 1
978 1015 o 0
979 1016
980 1017 $ log 'ancestors(5)'
981 1018 0
982 1019 1
983 1020 3
984 1021 5
985 1022 $ log 'ancestor(ancestors(5))'
986 1023 0
987 1024 $ log '::r3232()'
988 1025 0
989 1026 1
990 1027 2
991 1028 3
992 1029
993 1030 test ancestors with depth limit
994 1031
995 1032 (depth=0 selects the node itself)
996 1033
997 1034 $ log 'reverse(ancestors(9, depth=0))'
998 1035 9
999 1036
1000 1037 (interleaved: '4' would be missing if heap queue were higher depth first)
1001 1038
1002 1039 $ log 'reverse(ancestors(8:9, depth=1))'
1003 1040 9
1004 1041 8
1005 1042 4
1006 1043
1007 1044 (interleaved: '2' would be missing if heap queue were higher depth first)
1008 1045
1009 1046 $ log 'reverse(ancestors(7+8, depth=2))'
1010 1047 8
1011 1048 7
1012 1049 6
1013 1050 5
1014 1051 4
1015 1052 2
1016 1053
1017 1054 (walk example above by separate queries)
1018 1055
1019 1056 $ log 'reverse(ancestors(8, depth=2)) + reverse(ancestors(7, depth=2))'
1020 1057 8
1021 1058 4
1022 1059 2
1023 1060 7
1024 1061 6
1025 1062 5
1026 1063
1027 1064 (walk 2nd and 3rd ancestors)
1028 1065
1029 1066 $ log 'reverse(ancestors(7, depth=3, startdepth=2))'
1030 1067 5
1031 1068 4
1032 1069 3
1033 1070 2
1034 1071
1035 1072 (interleaved: '4' would be missing if higher-depth ancestors weren't scanned)
1036 1073
1037 1074 $ log 'reverse(ancestors(7+8, depth=2, startdepth=2))'
1038 1075 5
1039 1076 4
1040 1077 2
1041 1078
1042 1079 (note that 'ancestors(x, depth=y, startdepth=z)' does not identical to
1043 1080 'ancestors(x, depth=y) - ancestors(x, depth=z-1)' because a node may have
1044 1081 multiple depths)
1045 1082
1046 1083 $ log 'reverse(ancestors(7+8, depth=2) - ancestors(7+8, depth=1))'
1047 1084 5
1048 1085 2
1049 1086
1050 1087 test bad arguments passed to ancestors()
1051 1088
1052 1089 $ log 'ancestors(., depth=-1)'
1053 1090 hg: parse error: negative depth
1054 1091 [255]
1055 1092 $ log 'ancestors(., depth=foo)'
1056 1093 hg: parse error: ancestors expects an integer depth
1057 1094 [255]
1058 1095
1059 1096 test descendants
1060 1097
1061 1098 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1062 1099 @ 9
1063 1100 o 8
1064 1101 | o 7
1065 1102 | o 6
1066 1103 |/|
1067 1104 | o 5
1068 1105 o | 4
1069 1106 | o 3
1070 1107 o | 2
1071 1108 |/
1072 1109 o 1
1073 1110 o 0
1074 1111
1075 1112 (null is ultimate root and has optimized path)
1076 1113
1077 1114 $ log 'null:4 & descendants(null)'
1078 1115 -1
1079 1116 0
1080 1117 1
1081 1118 2
1082 1119 3
1083 1120 4
1084 1121
1085 1122 (including merge)
1086 1123
1087 1124 $ log ':8 & descendants(2)'
1088 1125 2
1089 1126 4
1090 1127 6
1091 1128 7
1092 1129 8
1093 1130
1094 1131 (multiple roots)
1095 1132
1096 1133 $ log ':8 & descendants(2+5)'
1097 1134 2
1098 1135 4
1099 1136 5
1100 1137 6
1101 1138 7
1102 1139 8
1103 1140
1104 1141 test descendants with depth limit
1105 1142
1106 1143 (depth=0 selects the node itself)
1107 1144
1108 1145 $ log 'descendants(0, depth=0)'
1109 1146 0
1110 1147 $ log 'null: & descendants(null, depth=0)'
1111 1148 -1
1112 1149
1113 1150 (p2 = null should be ignored)
1114 1151
1115 1152 $ log 'null: & descendants(null, depth=2)'
1116 1153 -1
1117 1154 0
1118 1155 1
1119 1156
1120 1157 (multiple paths: depth(6) = (2, 3))
1121 1158
1122 1159 $ log 'descendants(1+3, depth=2)'
1123 1160 1
1124 1161 2
1125 1162 3
1126 1163 4
1127 1164 5
1128 1165 6
1129 1166
1130 1167 (multiple paths: depth(5) = (1, 2), depth(6) = (2, 3))
1131 1168
1132 1169 $ log 'descendants(3+1, depth=2, startdepth=2)'
1133 1170 4
1134 1171 5
1135 1172 6
1136 1173
1137 1174 (multiple depths: depth(6) = (0, 2, 4), search for depth=2)
1138 1175
1139 1176 $ log 'descendants(0+3+6, depth=3, startdepth=1)'
1140 1177 1
1141 1178 2
1142 1179 3
1143 1180 4
1144 1181 5
1145 1182 6
1146 1183 7
1147 1184
1148 1185 (multiple depths: depth(6) = (0, 4), no match)
1149 1186
1150 1187 $ log 'descendants(0+6, depth=3, startdepth=1)'
1151 1188 1
1152 1189 2
1153 1190 3
1154 1191 4
1155 1192 5
1156 1193 7
1157 1194
1158 1195 test ancestors/descendants relation subscript:
1159 1196
1160 1197 $ log 'tip#generations[0]'
1161 1198 9
1162 1199 $ log '.#generations[-1]'
1163 1200 8
1164 1201 $ log '.#g[(-1)]'
1165 1202 8
1166 1203
1167 1204 $ hg debugrevspec -p parsed 'roots(:)#g[2]'
1168 1205 * parsed:
1169 1206 (relsubscript
1170 1207 (func
1171 1208 (symbol 'roots')
1172 1209 (rangeall
1173 1210 None))
1174 1211 (symbol 'g')
1175 1212 (symbol '2'))
1176 1213 2
1177 1214 3
1178 1215
1179 1216 test author
1180 1217
1181 1218 $ log 'author(bob)'
1182 1219 2
1183 1220 $ log 'author("re:bob|test")'
1184 1221 0
1185 1222 1
1186 1223 2
1187 1224 3
1188 1225 4
1189 1226 5
1190 1227 6
1191 1228 7
1192 1229 8
1193 1230 9
1194 1231 $ log 'author(r"re:\S")'
1195 1232 0
1196 1233 1
1197 1234 2
1198 1235 3
1199 1236 4
1200 1237 5
1201 1238 6
1202 1239 7
1203 1240 8
1204 1241 9
1205 1242 $ log 'branch(Γ©)'
1206 1243 8
1207 1244 9
1208 1245 $ log 'branch(a)'
1209 1246 0
1210 1247 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
1211 1248 0 a
1212 1249 2 a-b-c-
1213 1250 3 +a+b+c+
1214 1251 4 -a-b-c-
1215 1252 5 !a/b/c/
1216 1253 6 _a_b_c_
1217 1254 7 .a.b.c.
1218 1255 $ log 'children(ancestor(4,5))'
1219 1256 2
1220 1257 3
1221 1258
1222 1259 $ log 'children(4)'
1223 1260 6
1224 1261 8
1225 1262 $ log 'children(null)'
1226 1263 0
1227 1264
1228 1265 $ log 'closed()'
1229 1266 $ log 'contains(a)'
1230 1267 0
1231 1268 1
1232 1269 3
1233 1270 5
1234 1271 $ log 'contains("../repo/a")'
1235 1272 0
1236 1273 1
1237 1274 3
1238 1275 5
1239 1276 $ log 'desc(B)'
1240 1277 5
1241 1278 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
1242 1279 5 5 bug
1243 1280 6 6 issue619
1244 1281 $ log 'descendants(2 or 3)'
1245 1282 2
1246 1283 3
1247 1284 4
1248 1285 5
1249 1286 6
1250 1287 7
1251 1288 8
1252 1289 9
1253 1290 $ log 'file("b*")'
1254 1291 1
1255 1292 4
1256 1293 $ log 'filelog("b")'
1257 1294 1
1258 1295 4
1259 1296 $ log 'filelog("../repo/b")'
1260 1297 1
1261 1298 4
1262 1299 $ log 'follow()'
1263 1300 0
1264 1301 1
1265 1302 2
1266 1303 4
1267 1304 8
1268 1305 9
1269 1306 $ log 'grep("issue\d+")'
1270 1307 6
1271 1308 $ try 'grep("(")' # invalid regular expression
1272 1309 (func
1273 1310 (symbol 'grep')
1274 1311 (string '('))
1275 1312 hg: parse error: invalid match pattern: unbalanced parenthesis
1276 1313 [255]
1277 1314 $ try 'grep("\bissue\d+")'
1278 1315 (func
1279 1316 (symbol 'grep')
1280 1317 (string '\x08issue\\d+'))
1281 1318 * set:
1282 1319 <filteredset
1283 1320 <fullreposet+ 0:10>,
1284 1321 <grep '\x08issue\\d+'>>
1285 1322 $ try 'grep(r"\bissue\d+")'
1286 1323 (func
1287 1324 (symbol 'grep')
1288 1325 (string '\\bissue\\d+'))
1289 1326 * set:
1290 1327 <filteredset
1291 1328 <fullreposet+ 0:10>,
1292 1329 <grep '\\bissue\\d+'>>
1293 1330 6
1294 1331 $ try 'grep(r"\")'
1295 1332 hg: parse error at 7: unterminated string
1296 1333 [255]
1297 1334 $ log 'head()'
1298 1335 0
1299 1336 1
1300 1337 2
1301 1338 3
1302 1339 4
1303 1340 5
1304 1341 6
1305 1342 7
1306 1343 9
1307 1344 $ log 'heads(6::)'
1308 1345 7
1309 1346 $ log 'keyword(issue)'
1310 1347 6
1311 1348 $ log 'keyword("test a")'
1312 1349
1313 1350 Test first (=limit) and last
1314 1351
1315 1352 $ log 'limit(head(), 1)'
1316 1353 0
1317 1354 $ log 'limit(author("re:bob|test"), 3, 5)'
1318 1355 5
1319 1356 6
1320 1357 7
1321 1358 $ log 'limit(author("re:bob|test"), offset=6)'
1322 1359 6
1323 1360 $ log 'limit(author("re:bob|test"), offset=10)'
1324 1361 $ log 'limit(all(), 1, -1)'
1325 1362 hg: parse error: negative offset
1326 1363 [255]
1327 1364 $ log 'limit(all(), -1)'
1328 1365 hg: parse error: negative number to select
1329 1366 [255]
1330 1367 $ log 'limit(all(), 0)'
1331 1368
1332 1369 $ log 'last(all(), -1)'
1333 1370 hg: parse error: negative number to select
1334 1371 [255]
1335 1372 $ log 'last(all(), 0)'
1336 1373 $ log 'last(all(), 1)'
1337 1374 9
1338 1375 $ log 'last(all(), 2)'
1339 1376 8
1340 1377 9
1341 1378
1342 1379 Test smartset.slice() by first/last()
1343 1380
1344 1381 (using unoptimized set, filteredset as example)
1345 1382
1346 1383 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1347 1384 * set:
1348 1385 <filteredset
1349 1386 <spanset+ 0:8>,
1350 1387 <branch 're:'>>
1351 1388 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1352 1389 4
1353 1390 5
1354 1391 6
1355 1392 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1356 1393 3
1357 1394 2
1358 1395 1
1359 1396 $ log 'last(0:7 & branch("re:"), 2)'
1360 1397 6
1361 1398 7
1362 1399
1363 1400 (using baseset)
1364 1401
1365 1402 $ hg debugrevspec --no-show-revs -s 0+1+2+3+4+5+6+7
1366 1403 * set:
1367 1404 <baseset [0, 1, 2, 3, 4, 5, 6, 7]>
1368 1405 $ hg debugrevspec --no-show-revs -s 0::7
1369 1406 * set:
1370 1407 <baseset+ [0, 1, 2, 3, 4, 5, 6, 7]>
1371 1408 $ log 'limit(0+1+2+3+4+5+6+7, 3, 4)'
1372 1409 4
1373 1410 5
1374 1411 6
1375 1412 $ log 'limit(sort(0::7, rev), 3, 4)'
1376 1413 4
1377 1414 5
1378 1415 6
1379 1416 $ log 'limit(sort(0::7, -rev), 3, 4)'
1380 1417 3
1381 1418 2
1382 1419 1
1383 1420 $ log 'last(sort(0::7, rev), 2)'
1384 1421 6
1385 1422 7
1386 1423 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 6)'
1387 1424 * set:
1388 1425 <baseset+ [6, 7]>
1389 1426 6
1390 1427 7
1391 1428 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 9)'
1392 1429 * set:
1393 1430 <baseset+ []>
1394 1431 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 6)'
1395 1432 * set:
1396 1433 <baseset- [0, 1]>
1397 1434 1
1398 1435 0
1399 1436 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 9)'
1400 1437 * set:
1401 1438 <baseset- []>
1402 1439 $ hg debugrevspec -s 'limit(0::7, 0)'
1403 1440 * set:
1404 1441 <baseset+ []>
1405 1442
1406 1443 (using spanset)
1407 1444
1408 1445 $ hg debugrevspec --no-show-revs -s 0:7
1409 1446 * set:
1410 1447 <spanset+ 0:8>
1411 1448 $ log 'limit(0:7, 3, 4)'
1412 1449 4
1413 1450 5
1414 1451 6
1415 1452 $ log 'limit(7:0, 3, 4)'
1416 1453 3
1417 1454 2
1418 1455 1
1419 1456 $ log 'limit(0:7, 3, 6)'
1420 1457 6
1421 1458 7
1422 1459 $ log 'limit(7:0, 3, 6)'
1423 1460 1
1424 1461 0
1425 1462 $ log 'last(0:7, 2)'
1426 1463 6
1427 1464 7
1428 1465 $ hg debugrevspec -s 'limit(0:7, 3, 6)'
1429 1466 * set:
1430 1467 <spanset+ 6:8>
1431 1468 6
1432 1469 7
1433 1470 $ hg debugrevspec -s 'limit(0:7, 3, 9)'
1434 1471 * set:
1435 1472 <spanset+ 8:8>
1436 1473 $ hg debugrevspec -s 'limit(7:0, 3, 6)'
1437 1474 * set:
1438 1475 <spanset- 0:2>
1439 1476 1
1440 1477 0
1441 1478 $ hg debugrevspec -s 'limit(7:0, 3, 9)'
1442 1479 * set:
1443 1480 <spanset- 0:0>
1444 1481 $ hg debugrevspec -s 'limit(0:7, 0)'
1445 1482 * set:
1446 1483 <spanset+ 0:0>
1447 1484
1448 1485 Test order of first/last revisions
1449 1486
1450 1487 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1451 1488 * set:
1452 1489 <filteredset
1453 1490 <spanset- 2:5>,
1454 1491 <spanset+ 3:10>>
1455 1492 4
1456 1493 3
1457 1494
1458 1495 $ hg debugrevspec -s '3: & first(4:0, 3)'
1459 1496 * set:
1460 1497 <filteredset
1461 1498 <spanset+ 3:10>,
1462 1499 <spanset- 2:5>>
1463 1500 3
1464 1501 4
1465 1502
1466 1503 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1467 1504 * set:
1468 1505 <filteredset
1469 1506 <spanset- 0:3>,
1470 1507 <spanset+ 0:2>>
1471 1508 1
1472 1509 0
1473 1510
1474 1511 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1475 1512 * set:
1476 1513 <filteredset
1477 1514 <spanset+ 0:2>,
1478 1515 <spanset+ 0:3>>
1479 1516 0
1480 1517 1
1481 1518
1482 1519 Test scmutil.revsingle() should return the last revision
1483 1520
1484 1521 $ hg debugrevspec -s 'last(0::)'
1485 1522 * set:
1486 1523 <baseset slice=0:1
1487 1524 <generatorsetasc->>
1488 1525 9
1489 1526 $ hg identify -r '0::' --num
1490 1527 9
1491 1528
1492 1529 Test matching
1493 1530
1494 1531 $ log 'matching(6)'
1495 1532 6
1496 1533 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1497 1534 6
1498 1535 7
1499 1536
1500 1537 Testing min and max
1501 1538
1502 1539 max: simple
1503 1540
1504 1541 $ log 'max(contains(a))'
1505 1542 5
1506 1543
1507 1544 max: simple on unordered set)
1508 1545
1509 1546 $ log 'max((4+0+2+5+7) and contains(a))'
1510 1547 5
1511 1548
1512 1549 max: no result
1513 1550
1514 1551 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1515 1552
1516 1553 max: no result on unordered set
1517 1554
1518 1555 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1519 1556
1520 1557 min: simple
1521 1558
1522 1559 $ log 'min(contains(a))'
1523 1560 0
1524 1561
1525 1562 min: simple on unordered set
1526 1563
1527 1564 $ log 'min((4+0+2+5+7) and contains(a))'
1528 1565 0
1529 1566
1530 1567 min: empty
1531 1568
1532 1569 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1533 1570
1534 1571 min: empty on unordered set
1535 1572
1536 1573 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1537 1574
1538 1575
1539 1576 $ log 'merge()'
1540 1577 6
1541 1578 $ log 'branchpoint()'
1542 1579 1
1543 1580 4
1544 1581 $ log 'modifies(b)'
1545 1582 4
1546 1583 $ log 'modifies("path:b")'
1547 1584 4
1548 1585 $ log 'modifies("*")'
1549 1586 4
1550 1587 6
1551 1588 $ log 'modifies("set:modified()")'
1552 1589 4
1553 1590 $ log 'id(5)'
1554 1591 2
1555 1592 $ log 'only(9)'
1556 1593 8
1557 1594 9
1558 1595 $ log 'only(8)'
1559 1596 8
1560 1597 $ log 'only(9, 5)'
1561 1598 2
1562 1599 4
1563 1600 8
1564 1601 9
1565 1602 $ log 'only(7 + 9, 5 + 2)'
1566 1603 4
1567 1604 6
1568 1605 7
1569 1606 8
1570 1607 9
1571 1608
1572 1609 Test empty set input
1573 1610 $ log 'only(p2())'
1574 1611 $ log 'only(p1(), p2())'
1575 1612 0
1576 1613 1
1577 1614 2
1578 1615 4
1579 1616 8
1580 1617 9
1581 1618
1582 1619 Test '%' operator
1583 1620
1584 1621 $ log '9%'
1585 1622 8
1586 1623 9
1587 1624 $ log '9%5'
1588 1625 2
1589 1626 4
1590 1627 8
1591 1628 9
1592 1629 $ log '(7 + 9)%(5 + 2)'
1593 1630 4
1594 1631 6
1595 1632 7
1596 1633 8
1597 1634 9
1598 1635
1599 1636 Test operand of '%' is optimized recursively (issue4670)
1600 1637
1601 1638 $ try --optimize '8:9-8%'
1602 1639 (onlypost
1603 1640 (minus
1604 1641 (range
1605 1642 (symbol '8')
1606 1643 (symbol '9'))
1607 1644 (symbol '8')))
1608 1645 * optimized:
1609 1646 (func
1610 1647 (symbol 'only')
1611 1648 (difference
1612 1649 (range
1613 1650 (symbol '8')
1614 1651 (symbol '9'))
1615 1652 (symbol '8')))
1616 1653 * set:
1617 1654 <baseset+ [8, 9]>
1618 1655 8
1619 1656 9
1620 1657 $ try --optimize '(9)%(5)'
1621 1658 (only
1622 1659 (group
1623 1660 (symbol '9'))
1624 1661 (group
1625 1662 (symbol '5')))
1626 1663 * optimized:
1627 1664 (func
1628 1665 (symbol 'only')
1629 1666 (list
1630 1667 (symbol '9')
1631 1668 (symbol '5')))
1632 1669 * set:
1633 1670 <baseset+ [2, 4, 8, 9]>
1634 1671 2
1635 1672 4
1636 1673 8
1637 1674 9
1638 1675
1639 1676 Test the order of operations
1640 1677
1641 1678 $ log '7 + 9%5 + 2'
1642 1679 7
1643 1680 2
1644 1681 4
1645 1682 8
1646 1683 9
1647 1684
1648 1685 Test explicit numeric revision
1649 1686 $ log 'rev(-2)'
1650 1687 $ log 'rev(-1)'
1651 1688 -1
1652 1689 $ log 'rev(0)'
1653 1690 0
1654 1691 $ log 'rev(9)'
1655 1692 9
1656 1693 $ log 'rev(10)'
1657 1694 $ log 'rev(tip)'
1658 1695 hg: parse error: rev expects a number
1659 1696 [255]
1660 1697
1661 1698 Test hexadecimal revision
1662 1699 $ log 'id(2)'
1663 1700 abort: 00changelog.i@2: ambiguous identifier!
1664 1701 [255]
1665 1702 $ log 'id(23268)'
1666 1703 4
1667 1704 $ log 'id(2785f51eece)'
1668 1705 0
1669 1706 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1670 1707 8
1671 1708 $ log 'id(d5d0dcbdc4a)'
1672 1709 $ log 'id(d5d0dcbdc4w)'
1673 1710 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1674 1711 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1675 1712 $ log 'id(1.0)'
1676 1713 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1677 1714
1678 1715 Test null revision
1679 1716 $ log '(null)'
1680 1717 -1
1681 1718 $ log '(null:0)'
1682 1719 -1
1683 1720 0
1684 1721 $ log '(0:null)'
1685 1722 0
1686 1723 -1
1687 1724 $ log 'null::0'
1688 1725 -1
1689 1726 0
1690 1727 $ log 'null:tip - 0:'
1691 1728 -1
1692 1729 $ log 'null: and null::' | head -1
1693 1730 -1
1694 1731 $ log 'null: or 0:' | head -2
1695 1732 -1
1696 1733 0
1697 1734 $ log 'ancestors(null)'
1698 1735 -1
1699 1736 $ log 'reverse(null:)' | tail -2
1700 1737 0
1701 1738 -1
1702 1739 $ log 'first(null:)'
1703 1740 -1
1704 1741 $ log 'min(null:)'
1705 1742 BROKEN: should be '-1'
1706 1743 $ log 'tip:null and all()' | tail -2
1707 1744 1
1708 1745 0
1709 1746
1710 1747 Test working-directory revision
1711 1748 $ hg debugrevspec 'wdir()'
1712 1749 2147483647
1713 1750 $ hg debugrevspec 'wdir()^'
1714 1751 9
1715 1752 $ hg up 7
1716 1753 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1717 1754 $ hg debugrevspec 'wdir()^'
1718 1755 7
1719 1756 $ hg debugrevspec 'wdir()^0'
1720 1757 2147483647
1721 1758 $ hg debugrevspec 'wdir()~3'
1722 1759 5
1723 1760 $ hg debugrevspec 'ancestors(wdir())'
1724 1761 0
1725 1762 1
1726 1763 2
1727 1764 3
1728 1765 4
1729 1766 5
1730 1767 6
1731 1768 7
1732 1769 2147483647
1733 1770 $ hg debugrevspec 'wdir()~0'
1734 1771 2147483647
1735 1772 $ hg debugrevspec 'p1(wdir())'
1736 1773 7
1737 1774 $ hg debugrevspec 'p2(wdir())'
1738 1775 $ hg debugrevspec 'parents(wdir())'
1739 1776 7
1740 1777 $ hg debugrevspec 'wdir()^1'
1741 1778 7
1742 1779 $ hg debugrevspec 'wdir()^2'
1743 1780 $ hg debugrevspec 'wdir()^3'
1744 1781 hg: parse error: ^ expects a number 0, 1, or 2
1745 1782 [255]
1746 1783 For tests consistency
1747 1784 $ hg up 9
1748 1785 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1749 1786 $ hg debugrevspec 'tip or wdir()'
1750 1787 9
1751 1788 2147483647
1752 1789 $ hg debugrevspec '0:tip and wdir()'
1753 1790 $ log '0:wdir()' | tail -3
1754 1791 8
1755 1792 9
1756 1793 2147483647
1757 1794 $ log 'wdir():0' | head -3
1758 1795 2147483647
1759 1796 9
1760 1797 8
1761 1798 $ log 'wdir():wdir()'
1762 1799 2147483647
1763 1800 $ log '(all() + wdir()) & min(. + wdir())'
1764 1801 9
1765 1802 $ log '(all() + wdir()) & max(. + wdir())'
1766 1803 2147483647
1767 1804 $ log 'first(wdir() + .)'
1768 1805 2147483647
1769 1806 $ log 'last(. + wdir())'
1770 1807 2147483647
1771 1808
1772 1809 Test working-directory integer revision and node id
1773 1810 (BUG: '0:wdir()' is still needed to populate wdir revision)
1774 1811
1775 1812 $ hg debugrevspec '0:wdir() & 2147483647'
1776 1813 2147483647
1777 1814 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1778 1815 2147483647
1779 1816 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1780 1817 2147483647
1781 1818 $ hg debugrevspec '0:wdir() & ffffffffffff'
1782 1819 2147483647
1783 1820 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1784 1821 2147483647
1785 1822 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1786 1823 2147483647
1787 1824
1788 1825 $ cd ..
1789 1826
1790 1827 Test short 'ff...' hash collision
1791 1828 (BUG: '0:wdir()' is still needed to populate wdir revision)
1792 1829
1793 1830 $ hg init wdir-hashcollision
1794 1831 $ cd wdir-hashcollision
1795 1832 $ cat <<EOF >> .hg/hgrc
1796 1833 > [experimental]
1797 1834 > evolution.createmarkers=True
1798 1835 > EOF
1799 1836 $ echo 0 > a
1800 1837 $ hg ci -qAm 0
1801 1838 $ for i in 2463 2961 6726 78127; do
1802 1839 > hg up -q 0
1803 1840 > echo $i > a
1804 1841 > hg ci -qm $i
1805 1842 > done
1806 1843 $ hg up -q null
1807 1844 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1808 1845 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1809 1846 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1810 1847 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1811 1848 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1812 1849 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1813 1850 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1814 1851 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1815 1852 obsoleted 1 changesets
1816 1853
1817 1854 $ hg debugrevspec '0:wdir() & fff'
1818 1855 abort: 00changelog.i@fff: ambiguous identifier!
1819 1856 [255]
1820 1857 $ hg debugrevspec '0:wdir() & ffff'
1821 1858 abort: 00changelog.i@ffff: ambiguous identifier!
1822 1859 [255]
1823 1860 $ hg debugrevspec '0:wdir() & fffb'
1824 1861 abort: 00changelog.i@fffb: ambiguous identifier!
1825 1862 [255]
1826 1863 BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8)
1827 1864 $ hg debugrevspec '0:wdir() & id(fffb)'
1828 1865 2
1829 1866 $ hg debugrevspec '0:wdir() & ffff8'
1830 1867 4
1831 1868 $ hg debugrevspec '0:wdir() & fffff'
1832 1869 2147483647
1833 1870
1834 1871 $ cd ..
1835 1872
1836 1873 Test branch() with wdir()
1837 1874
1838 1875 $ cd repo
1839 1876
1840 1877 $ log '0:wdir() & branch("literal:Γ©")'
1841 1878 8
1842 1879 9
1843 1880 2147483647
1844 1881 $ log '0:wdir() & branch("re:Γ©")'
1845 1882 8
1846 1883 9
1847 1884 2147483647
1848 1885 $ log '0:wdir() & branch("re:^a")'
1849 1886 0
1850 1887 2
1851 1888 $ log '0:wdir() & branch(8)'
1852 1889 8
1853 1890 9
1854 1891 2147483647
1855 1892
1856 1893 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1857 1894 itself isn't returned unless it is explicitly populated.
1858 1895
1859 1896 $ log 'branch(wdir())'
1860 1897 8
1861 1898 9
1862 1899 $ log '0:wdir() & branch(wdir())'
1863 1900 8
1864 1901 9
1865 1902 2147483647
1866 1903
1867 1904 $ log 'outgoing()'
1868 1905 8
1869 1906 9
1870 1907 $ log 'outgoing("../remote1")'
1871 1908 8
1872 1909 9
1873 1910 $ log 'outgoing("../remote2")'
1874 1911 3
1875 1912 5
1876 1913 6
1877 1914 7
1878 1915 9
1879 1916 $ log 'p1(merge())'
1880 1917 5
1881 1918 $ log 'p2(merge())'
1882 1919 4
1883 1920 $ log 'parents(merge())'
1884 1921 4
1885 1922 5
1886 1923 $ log 'p1(branchpoint())'
1887 1924 0
1888 1925 2
1889 1926 $ log 'p2(branchpoint())'
1890 1927 $ log 'parents(branchpoint())'
1891 1928 0
1892 1929 2
1893 1930 $ log 'removes(a)'
1894 1931 2
1895 1932 6
1896 1933 $ log 'roots(all())'
1897 1934 0
1898 1935 $ log 'reverse(2 or 3 or 4 or 5)'
1899 1936 5
1900 1937 4
1901 1938 3
1902 1939 2
1903 1940 $ log 'reverse(all())'
1904 1941 9
1905 1942 8
1906 1943 7
1907 1944 6
1908 1945 5
1909 1946 4
1910 1947 3
1911 1948 2
1912 1949 1
1913 1950 0
1914 1951 $ log 'reverse(all()) & filelog(b)'
1915 1952 4
1916 1953 1
1917 1954 $ log 'rev(5)'
1918 1955 5
1919 1956 $ log 'sort(limit(reverse(all()), 3))'
1920 1957 7
1921 1958 8
1922 1959 9
1923 1960 $ log 'sort(2 or 3 or 4 or 5, date)'
1924 1961 2
1925 1962 3
1926 1963 5
1927 1964 4
1928 1965 $ log 'tagged()'
1929 1966 6
1930 1967 $ log 'tag()'
1931 1968 6
1932 1969 $ log 'tag(1.0)'
1933 1970 6
1934 1971 $ log 'tag(tip)'
1935 1972 9
1936 1973
1937 1974 Test order of revisions in compound expression
1938 1975 ----------------------------------------------
1939 1976
1940 1977 The general rule is that only the outermost (= leftmost) predicate can
1941 1978 enforce its ordering requirement. The other predicates should take the
1942 1979 ordering defined by it.
1943 1980
1944 1981 'A & B' should follow the order of 'A':
1945 1982
1946 1983 $ log '2:0 & 0::2'
1947 1984 2
1948 1985 1
1949 1986 0
1950 1987
1951 1988 'head()' combines sets in right order:
1952 1989
1953 1990 $ log '2:0 & head()'
1954 1991 2
1955 1992 1
1956 1993 0
1957 1994
1958 1995 'x:y' takes ordering parameter into account:
1959 1996
1960 1997 $ try -p optimized '3:0 & 0:3 & not 2:1'
1961 1998 * optimized:
1962 1999 (difference
1963 2000 (and
1964 2001 (range
1965 2002 (symbol '3')
1966 2003 (symbol '0'))
1967 2004 (range
1968 2005 (symbol '0')
1969 2006 (symbol '3')))
1970 2007 (range
1971 2008 (symbol '2')
1972 2009 (symbol '1')))
1973 2010 * set:
1974 2011 <filteredset
1975 2012 <filteredset
1976 2013 <spanset- 0:4>,
1977 2014 <spanset+ 0:4>>,
1978 2015 <not
1979 2016 <spanset+ 1:3>>>
1980 2017 3
1981 2018 0
1982 2019
1983 2020 'a + b', which is optimized to '_list(a b)', should take the ordering of
1984 2021 the left expression:
1985 2022
1986 2023 $ try --optimize '2:0 & (0 + 1 + 2)'
1987 2024 (and
1988 2025 (range
1989 2026 (symbol '2')
1990 2027 (symbol '0'))
1991 2028 (group
1992 2029 (or
1993 2030 (list
1994 2031 (symbol '0')
1995 2032 (symbol '1')
1996 2033 (symbol '2')))))
1997 2034 * optimized:
1998 2035 (and
1999 2036 (range
2000 2037 (symbol '2')
2001 2038 (symbol '0'))
2002 2039 (func
2003 2040 (symbol '_list')
2004 2041 (string '0\x001\x002')))
2005 2042 * set:
2006 2043 <filteredset
2007 2044 <spanset- 0:3>,
2008 2045 <baseset [0, 1, 2]>>
2009 2046 2
2010 2047 1
2011 2048 0
2012 2049
2013 2050 'A + B' should take the ordering of the left expression:
2014 2051
2015 2052 $ try --optimize '2:0 & (0:1 + 2)'
2016 2053 (and
2017 2054 (range
2018 2055 (symbol '2')
2019 2056 (symbol '0'))
2020 2057 (group
2021 2058 (or
2022 2059 (list
2023 2060 (range
2024 2061 (symbol '0')
2025 2062 (symbol '1'))
2026 2063 (symbol '2')))))
2027 2064 * optimized:
2028 2065 (and
2029 2066 (range
2030 2067 (symbol '2')
2031 2068 (symbol '0'))
2032 2069 (or
2033 2070 (list
2034 2071 (range
2035 2072 (symbol '0')
2036 2073 (symbol '1'))
2037 2074 (symbol '2'))))
2038 2075 * set:
2039 2076 <filteredset
2040 2077 <spanset- 0:3>,
2041 2078 <addset
2042 2079 <spanset+ 0:2>,
2043 2080 <baseset [2]>>>
2044 2081 2
2045 2082 1
2046 2083 0
2047 2084
2048 2085 '_intlist(a b)' should behave like 'a + b':
2049 2086
2050 2087 $ trylist --optimize '2:0 & %ld' 0 1 2
2051 2088 (and
2052 2089 (range
2053 2090 (symbol '2')
2054 2091 (symbol '0'))
2055 2092 (func
2056 2093 (symbol '_intlist')
2057 2094 (string '0\x001\x002')))
2058 2095 * optimized:
2059 2096 (andsmally
2060 2097 (range
2061 2098 (symbol '2')
2062 2099 (symbol '0'))
2063 2100 (func
2064 2101 (symbol '_intlist')
2065 2102 (string '0\x001\x002')))
2066 2103 * set:
2067 2104 <filteredset
2068 2105 <spanset- 0:3>,
2069 2106 <baseset+ [0, 1, 2]>>
2070 2107 2
2071 2108 1
2072 2109 0
2073 2110
2074 2111 $ trylist --optimize '%ld & 2:0' 0 2 1
2075 2112 (and
2076 2113 (func
2077 2114 (symbol '_intlist')
2078 2115 (string '0\x002\x001'))
2079 2116 (range
2080 2117 (symbol '2')
2081 2118 (symbol '0')))
2082 2119 * optimized:
2083 2120 (and
2084 2121 (func
2085 2122 (symbol '_intlist')
2086 2123 (string '0\x002\x001'))
2087 2124 (range
2088 2125 (symbol '2')
2089 2126 (symbol '0')))
2090 2127 * set:
2091 2128 <filteredset
2092 2129 <baseset [0, 2, 1]>,
2093 2130 <spanset- 0:3>>
2094 2131 0
2095 2132 2
2096 2133 1
2097 2134
2098 2135 '_hexlist(a b)' should behave like 'a + b':
2099 2136
2100 2137 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
2101 2138 (and
2102 2139 (range
2103 2140 (symbol '2')
2104 2141 (symbol '0'))
2105 2142 (func
2106 2143 (symbol '_hexlist')
2107 2144 (string '*'))) (glob)
2108 2145 * optimized:
2109 2146 (and
2110 2147 (range
2111 2148 (symbol '2')
2112 2149 (symbol '0'))
2113 2150 (func
2114 2151 (symbol '_hexlist')
2115 2152 (string '*'))) (glob)
2116 2153 * set:
2117 2154 <filteredset
2118 2155 <spanset- 0:3>,
2119 2156 <baseset [0, 1, 2]>>
2120 2157 2
2121 2158 1
2122 2159 0
2123 2160
2124 2161 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
2125 2162 (and
2126 2163 (func
2127 2164 (symbol '_hexlist')
2128 2165 (string '*')) (glob)
2129 2166 (range
2130 2167 (symbol '2')
2131 2168 (symbol '0')))
2132 2169 * optimized:
2133 2170 (andsmally
2134 2171 (func
2135 2172 (symbol '_hexlist')
2136 2173 (string '*')) (glob)
2137 2174 (range
2138 2175 (symbol '2')
2139 2176 (symbol '0')))
2140 2177 * set:
2141 2178 <baseset [0, 2, 1]>
2142 2179 0
2143 2180 2
2144 2181 1
2145 2182
2146 2183 '_list' should not go through the slow follow-order path if order doesn't
2147 2184 matter:
2148 2185
2149 2186 $ try -p optimized '2:0 & not (0 + 1)'
2150 2187 * optimized:
2151 2188 (difference
2152 2189 (range
2153 2190 (symbol '2')
2154 2191 (symbol '0'))
2155 2192 (func
2156 2193 (symbol '_list')
2157 2194 (string '0\x001')))
2158 2195 * set:
2159 2196 <filteredset
2160 2197 <spanset- 0:3>,
2161 2198 <not
2162 2199 <baseset [0, 1]>>>
2163 2200 2
2164 2201
2165 2202 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
2166 2203 * optimized:
2167 2204 (difference
2168 2205 (range
2169 2206 (symbol '2')
2170 2207 (symbol '0'))
2171 2208 (and
2172 2209 (range
2173 2210 (symbol '0')
2174 2211 (symbol '2'))
2175 2212 (func
2176 2213 (symbol '_list')
2177 2214 (string '0\x001'))))
2178 2215 * set:
2179 2216 <filteredset
2180 2217 <spanset- 0:3>,
2181 2218 <not
2182 2219 <baseset [0, 1]>>>
2183 2220 2
2184 2221
2185 2222 because 'present()' does nothing other than suppressing an error, the
2186 2223 ordering requirement should be forwarded to the nested expression
2187 2224
2188 2225 $ try -p optimized 'present(2 + 0 + 1)'
2189 2226 * optimized:
2190 2227 (func
2191 2228 (symbol 'present')
2192 2229 (func
2193 2230 (symbol '_list')
2194 2231 (string '2\x000\x001')))
2195 2232 * set:
2196 2233 <baseset [2, 0, 1]>
2197 2234 2
2198 2235 0
2199 2236 1
2200 2237
2201 2238 $ try --optimize '2:0 & present(0 + 1 + 2)'
2202 2239 (and
2203 2240 (range
2204 2241 (symbol '2')
2205 2242 (symbol '0'))
2206 2243 (func
2207 2244 (symbol 'present')
2208 2245 (or
2209 2246 (list
2210 2247 (symbol '0')
2211 2248 (symbol '1')
2212 2249 (symbol '2')))))
2213 2250 * optimized:
2214 2251 (and
2215 2252 (range
2216 2253 (symbol '2')
2217 2254 (symbol '0'))
2218 2255 (func
2219 2256 (symbol 'present')
2220 2257 (func
2221 2258 (symbol '_list')
2222 2259 (string '0\x001\x002'))))
2223 2260 * set:
2224 2261 <filteredset
2225 2262 <spanset- 0:3>,
2226 2263 <baseset [0, 1, 2]>>
2227 2264 2
2228 2265 1
2229 2266 0
2230 2267
2231 2268 'reverse()' should take effect only if it is the outermost expression:
2232 2269
2233 2270 $ try --optimize '0:2 & reverse(all())'
2234 2271 (and
2235 2272 (range
2236 2273 (symbol '0')
2237 2274 (symbol '2'))
2238 2275 (func
2239 2276 (symbol 'reverse')
2240 2277 (func
2241 2278 (symbol 'all')
2242 2279 None)))
2243 2280 * optimized:
2244 2281 (and
2245 2282 (range
2246 2283 (symbol '0')
2247 2284 (symbol '2'))
2248 2285 (func
2249 2286 (symbol 'reverse')
2250 2287 (func
2251 2288 (symbol 'all')
2252 2289 None)))
2253 2290 * set:
2254 2291 <filteredset
2255 2292 <spanset+ 0:3>,
2256 2293 <spanset+ 0:10>>
2257 2294 0
2258 2295 1
2259 2296 2
2260 2297
2261 2298 'sort()' should take effect only if it is the outermost expression:
2262 2299
2263 2300 $ try --optimize '0:2 & sort(all(), -rev)'
2264 2301 (and
2265 2302 (range
2266 2303 (symbol '0')
2267 2304 (symbol '2'))
2268 2305 (func
2269 2306 (symbol 'sort')
2270 2307 (list
2271 2308 (func
2272 2309 (symbol 'all')
2273 2310 None)
2274 2311 (negate
2275 2312 (symbol 'rev')))))
2276 2313 * optimized:
2277 2314 (and
2278 2315 (range
2279 2316 (symbol '0')
2280 2317 (symbol '2'))
2281 2318 (func
2282 2319 (symbol 'sort')
2283 2320 (list
2284 2321 (func
2285 2322 (symbol 'all')
2286 2323 None)
2287 2324 (string '-rev'))))
2288 2325 * set:
2289 2326 <filteredset
2290 2327 <spanset+ 0:3>,
2291 2328 <spanset+ 0:10>>
2292 2329 0
2293 2330 1
2294 2331 2
2295 2332
2296 2333 invalid argument passed to noop sort():
2297 2334
2298 2335 $ log '0:2 & sort()'
2299 2336 hg: parse error: sort requires one or two arguments
2300 2337 [255]
2301 2338 $ log '0:2 & sort(all(), -invalid)'
2302 2339 hg: parse error: unknown sort key '-invalid'
2303 2340 [255]
2304 2341
2305 2342 for 'A & f(B)', 'B' should not be affected by the order of 'A':
2306 2343
2307 2344 $ try --optimize '2:0 & first(1 + 0 + 2)'
2308 2345 (and
2309 2346 (range
2310 2347 (symbol '2')
2311 2348 (symbol '0'))
2312 2349 (func
2313 2350 (symbol 'first')
2314 2351 (or
2315 2352 (list
2316 2353 (symbol '1')
2317 2354 (symbol '0')
2318 2355 (symbol '2')))))
2319 2356 * optimized:
2320 2357 (and
2321 2358 (range
2322 2359 (symbol '2')
2323 2360 (symbol '0'))
2324 2361 (func
2325 2362 (symbol 'first')
2326 2363 (func
2327 2364 (symbol '_list')
2328 2365 (string '1\x000\x002'))))
2329 2366 * set:
2330 2367 <filteredset
2331 2368 <baseset [1]>,
2332 2369 <spanset- 0:3>>
2333 2370 1
2334 2371
2335 2372 $ try --optimize '2:0 & not last(0 + 2 + 1)'
2336 2373 (and
2337 2374 (range
2338 2375 (symbol '2')
2339 2376 (symbol '0'))
2340 2377 (not
2341 2378 (func
2342 2379 (symbol 'last')
2343 2380 (or
2344 2381 (list
2345 2382 (symbol '0')
2346 2383 (symbol '2')
2347 2384 (symbol '1'))))))
2348 2385 * optimized:
2349 2386 (difference
2350 2387 (range
2351 2388 (symbol '2')
2352 2389 (symbol '0'))
2353 2390 (func
2354 2391 (symbol 'last')
2355 2392 (func
2356 2393 (symbol '_list')
2357 2394 (string '0\x002\x001'))))
2358 2395 * set:
2359 2396 <filteredset
2360 2397 <spanset- 0:3>,
2361 2398 <not
2362 2399 <baseset [1]>>>
2363 2400 2
2364 2401 0
2365 2402
2366 2403 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2367 2404
2368 2405 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2369 2406 (and
2370 2407 (range
2371 2408 (symbol '2')
2372 2409 (symbol '0'))
2373 2410 (range
2374 2411 (group
2375 2412 (or
2376 2413 (list
2377 2414 (symbol '1')
2378 2415 (symbol '0')
2379 2416 (symbol '2'))))
2380 2417 (group
2381 2418 (or
2382 2419 (list
2383 2420 (symbol '0')
2384 2421 (symbol '2')
2385 2422 (symbol '1'))))))
2386 2423 * optimized:
2387 2424 (and
2388 2425 (range
2389 2426 (symbol '2')
2390 2427 (symbol '0'))
2391 2428 (range
2392 2429 (func
2393 2430 (symbol '_list')
2394 2431 (string '1\x000\x002'))
2395 2432 (func
2396 2433 (symbol '_list')
2397 2434 (string '0\x002\x001'))))
2398 2435 * set:
2399 2436 <filteredset
2400 2437 <spanset- 0:3>,
2401 2438 <baseset [1]>>
2402 2439 1
2403 2440
2404 2441 'A & B' can be rewritten as 'flipand(B, A)' by weight.
2405 2442
2406 2443 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2407 2444 (and
2408 2445 (func
2409 2446 (symbol 'contains')
2410 2447 (string 'glob:*'))
2411 2448 (group
2412 2449 (or
2413 2450 (list
2414 2451 (symbol '2')
2415 2452 (symbol '0')
2416 2453 (symbol '1')))))
2417 2454 * optimized:
2418 2455 (andsmally
2419 2456 (func
2420 2457 (symbol 'contains')
2421 2458 (string 'glob:*'))
2422 2459 (func
2423 2460 (symbol '_list')
2424 2461 (string '2\x000\x001')))
2425 2462 * set:
2426 2463 <filteredset
2427 2464 <baseset+ [0, 1, 2]>,
2428 2465 <contains 'glob:*'>>
2429 2466 0
2430 2467 1
2431 2468 2
2432 2469
2433 2470 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2434 2471 the order appropriately:
2435 2472
2436 2473 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2437 2474 (and
2438 2475 (func
2439 2476 (symbol 'reverse')
2440 2477 (func
2441 2478 (symbol 'contains')
2442 2479 (string 'glob:*')))
2443 2480 (group
2444 2481 (or
2445 2482 (list
2446 2483 (symbol '0')
2447 2484 (symbol '2')
2448 2485 (symbol '1')))))
2449 2486 * optimized:
2450 2487 (andsmally
2451 2488 (func
2452 2489 (symbol 'reverse')
2453 2490 (func
2454 2491 (symbol 'contains')
2455 2492 (string 'glob:*')))
2456 2493 (func
2457 2494 (symbol '_list')
2458 2495 (string '0\x002\x001')))
2459 2496 * set:
2460 2497 <filteredset
2461 2498 <baseset- [0, 1, 2]>,
2462 2499 <contains 'glob:*'>>
2463 2500 2
2464 2501 1
2465 2502 0
2466 2503
2467 2504 test sort revset
2468 2505 --------------------------------------------
2469 2506
2470 2507 test when adding two unordered revsets
2471 2508
2472 2509 $ log 'sort(keyword(issue) or modifies(b))'
2473 2510 4
2474 2511 6
2475 2512
2476 2513 test when sorting a reversed collection in the same way it is
2477 2514
2478 2515 $ log 'sort(reverse(all()), -rev)'
2479 2516 9
2480 2517 8
2481 2518 7
2482 2519 6
2483 2520 5
2484 2521 4
2485 2522 3
2486 2523 2
2487 2524 1
2488 2525 0
2489 2526
2490 2527 test when sorting a reversed collection
2491 2528
2492 2529 $ log 'sort(reverse(all()), rev)'
2493 2530 0
2494 2531 1
2495 2532 2
2496 2533 3
2497 2534 4
2498 2535 5
2499 2536 6
2500 2537 7
2501 2538 8
2502 2539 9
2503 2540
2504 2541
2505 2542 test sorting two sorted collections in different orders
2506 2543
2507 2544 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2508 2545 2
2509 2546 6
2510 2547 8
2511 2548 9
2512 2549
2513 2550 test sorting two sorted collections in different orders backwards
2514 2551
2515 2552 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2516 2553 9
2517 2554 8
2518 2555 6
2519 2556 2
2520 2557
2521 2558 test empty sort key which is noop
2522 2559
2523 2560 $ log 'sort(0 + 2 + 1, "")'
2524 2561 0
2525 2562 2
2526 2563 1
2527 2564
2528 2565 test invalid sort keys
2529 2566
2530 2567 $ log 'sort(all(), -invalid)'
2531 2568 hg: parse error: unknown sort key '-invalid'
2532 2569 [255]
2533 2570
2534 2571 $ cd ..
2535 2572
2536 2573 test sorting by multiple keys including variable-length strings
2537 2574
2538 2575 $ hg init sorting
2539 2576 $ cd sorting
2540 2577 $ cat <<EOF >> .hg/hgrc
2541 2578 > [ui]
2542 2579 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2543 2580 > [templatealias]
2544 2581 > p5(s) = pad(s, 5)
2545 2582 > EOF
2546 2583 $ hg branch -qf b12
2547 2584 $ hg ci -m m111 -u u112 -d '111 10800'
2548 2585 $ hg branch -qf b11
2549 2586 $ hg ci -m m12 -u u111 -d '112 7200'
2550 2587 $ hg branch -qf b111
2551 2588 $ hg ci -m m11 -u u12 -d '111 3600'
2552 2589 $ hg branch -qf b112
2553 2590 $ hg ci -m m111 -u u11 -d '120 0'
2554 2591 $ hg branch -qf b111
2555 2592 $ hg ci -m m112 -u u111 -d '110 14400'
2556 2593 created new head
2557 2594
2558 2595 compare revisions (has fast path):
2559 2596
2560 2597 $ hg log -r 'sort(all(), rev)'
2561 2598 0 b12 m111 u112 111 10800
2562 2599 1 b11 m12 u111 112 7200
2563 2600 2 b111 m11 u12 111 3600
2564 2601 3 b112 m111 u11 120 0
2565 2602 4 b111 m112 u111 110 14400
2566 2603
2567 2604 $ hg log -r 'sort(all(), -rev)'
2568 2605 4 b111 m112 u111 110 14400
2569 2606 3 b112 m111 u11 120 0
2570 2607 2 b111 m11 u12 111 3600
2571 2608 1 b11 m12 u111 112 7200
2572 2609 0 b12 m111 u112 111 10800
2573 2610
2574 2611 compare variable-length strings (issue5218):
2575 2612
2576 2613 $ hg log -r 'sort(all(), branch)'
2577 2614 1 b11 m12 u111 112 7200
2578 2615 2 b111 m11 u12 111 3600
2579 2616 4 b111 m112 u111 110 14400
2580 2617 3 b112 m111 u11 120 0
2581 2618 0 b12 m111 u112 111 10800
2582 2619
2583 2620 $ hg log -r 'sort(all(), -branch)'
2584 2621 0 b12 m111 u112 111 10800
2585 2622 3 b112 m111 u11 120 0
2586 2623 2 b111 m11 u12 111 3600
2587 2624 4 b111 m112 u111 110 14400
2588 2625 1 b11 m12 u111 112 7200
2589 2626
2590 2627 $ hg log -r 'sort(all(), desc)'
2591 2628 2 b111 m11 u12 111 3600
2592 2629 0 b12 m111 u112 111 10800
2593 2630 3 b112 m111 u11 120 0
2594 2631 4 b111 m112 u111 110 14400
2595 2632 1 b11 m12 u111 112 7200
2596 2633
2597 2634 $ hg log -r 'sort(all(), -desc)'
2598 2635 1 b11 m12 u111 112 7200
2599 2636 4 b111 m112 u111 110 14400
2600 2637 0 b12 m111 u112 111 10800
2601 2638 3 b112 m111 u11 120 0
2602 2639 2 b111 m11 u12 111 3600
2603 2640
2604 2641 $ hg log -r 'sort(all(), user)'
2605 2642 3 b112 m111 u11 120 0
2606 2643 1 b11 m12 u111 112 7200
2607 2644 4 b111 m112 u111 110 14400
2608 2645 0 b12 m111 u112 111 10800
2609 2646 2 b111 m11 u12 111 3600
2610 2647
2611 2648 $ hg log -r 'sort(all(), -user)'
2612 2649 2 b111 m11 u12 111 3600
2613 2650 0 b12 m111 u112 111 10800
2614 2651 1 b11 m12 u111 112 7200
2615 2652 4 b111 m112 u111 110 14400
2616 2653 3 b112 m111 u11 120 0
2617 2654
2618 2655 compare dates (tz offset should have no effect):
2619 2656
2620 2657 $ hg log -r 'sort(all(), date)'
2621 2658 4 b111 m112 u111 110 14400
2622 2659 0 b12 m111 u112 111 10800
2623 2660 2 b111 m11 u12 111 3600
2624 2661 1 b11 m12 u111 112 7200
2625 2662 3 b112 m111 u11 120 0
2626 2663
2627 2664 $ hg log -r 'sort(all(), -date)'
2628 2665 3 b112 m111 u11 120 0
2629 2666 1 b11 m12 u111 112 7200
2630 2667 0 b12 m111 u112 111 10800
2631 2668 2 b111 m11 u12 111 3600
2632 2669 4 b111 m112 u111 110 14400
2633 2670
2634 2671 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2635 2672 because '-k' reverses the comparison, not the list itself:
2636 2673
2637 2674 $ hg log -r 'sort(0 + 2, date)'
2638 2675 0 b12 m111 u112 111 10800
2639 2676 2 b111 m11 u12 111 3600
2640 2677
2641 2678 $ hg log -r 'sort(0 + 2, -date)'
2642 2679 0 b12 m111 u112 111 10800
2643 2680 2 b111 m11 u12 111 3600
2644 2681
2645 2682 $ hg log -r 'reverse(sort(0 + 2, date))'
2646 2683 2 b111 m11 u12 111 3600
2647 2684 0 b12 m111 u112 111 10800
2648 2685
2649 2686 sort by multiple keys:
2650 2687
2651 2688 $ hg log -r 'sort(all(), "branch -rev")'
2652 2689 1 b11 m12 u111 112 7200
2653 2690 4 b111 m112 u111 110 14400
2654 2691 2 b111 m11 u12 111 3600
2655 2692 3 b112 m111 u11 120 0
2656 2693 0 b12 m111 u112 111 10800
2657 2694
2658 2695 $ hg log -r 'sort(all(), "-desc -date")'
2659 2696 1 b11 m12 u111 112 7200
2660 2697 4 b111 m112 u111 110 14400
2661 2698 3 b112 m111 u11 120 0
2662 2699 0 b12 m111 u112 111 10800
2663 2700 2 b111 m11 u12 111 3600
2664 2701
2665 2702 $ hg log -r 'sort(all(), "user -branch date rev")'
2666 2703 3 b112 m111 u11 120 0
2667 2704 4 b111 m112 u111 110 14400
2668 2705 1 b11 m12 u111 112 7200
2669 2706 0 b12 m111 u112 111 10800
2670 2707 2 b111 m11 u12 111 3600
2671 2708
2672 2709 toposort prioritises graph branches
2673 2710
2674 2711 $ hg up 2
2675 2712 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2676 2713 $ touch a
2677 2714 $ hg addremove
2678 2715 adding a
2679 2716 $ hg ci -m 't1' -u 'tu' -d '130 0'
2680 2717 created new head
2681 2718 $ echo 'a' >> a
2682 2719 $ hg ci -m 't2' -u 'tu' -d '130 0'
2683 2720 $ hg book book1
2684 2721 $ hg up 4
2685 2722 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2686 2723 (leaving bookmark book1)
2687 2724 $ touch a
2688 2725 $ hg addremove
2689 2726 adding a
2690 2727 $ hg ci -m 't3' -u 'tu' -d '130 0'
2691 2728
2692 2729 $ hg log -r 'sort(all(), topo)'
2693 2730 7 b111 t3 tu 130 0
2694 2731 4 b111 m112 u111 110 14400
2695 2732 3 b112 m111 u11 120 0
2696 2733 6 b111 t2 tu 130 0
2697 2734 5 b111 t1 tu 130 0
2698 2735 2 b111 m11 u12 111 3600
2699 2736 1 b11 m12 u111 112 7200
2700 2737 0 b12 m111 u112 111 10800
2701 2738
2702 2739 $ hg log -r 'sort(all(), -topo)'
2703 2740 0 b12 m111 u112 111 10800
2704 2741 1 b11 m12 u111 112 7200
2705 2742 2 b111 m11 u12 111 3600
2706 2743 5 b111 t1 tu 130 0
2707 2744 6 b111 t2 tu 130 0
2708 2745 3 b112 m111 u11 120 0
2709 2746 4 b111 m112 u111 110 14400
2710 2747 7 b111 t3 tu 130 0
2711 2748
2712 2749 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2713 2750 6 b111 t2 tu 130 0
2714 2751 5 b111 t1 tu 130 0
2715 2752 7 b111 t3 tu 130 0
2716 2753 4 b111 m112 u111 110 14400
2717 2754 3 b112 m111 u11 120 0
2718 2755 2 b111 m11 u12 111 3600
2719 2756 1 b11 m12 u111 112 7200
2720 2757 0 b12 m111 u112 111 10800
2721 2758
2722 2759 topographical sorting can't be combined with other sort keys, and you can't
2723 2760 use the topo.firstbranch option when topo sort is not active:
2724 2761
2725 2762 $ hg log -r 'sort(all(), "topo user")'
2726 2763 hg: parse error: topo sort order cannot be combined with other sort keys
2727 2764 [255]
2728 2765
2729 2766 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2730 2767 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2731 2768 [255]
2732 2769
2733 2770 topo.firstbranch should accept any kind of expressions:
2734 2771
2735 2772 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2736 2773 0 b12 m111 u112 111 10800
2737 2774
2738 2775 $ cd ..
2739 2776 $ cd repo
General Comments 0
You need to be logged in to leave comments. Login now