comparison mercurial/revset.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children c59eb1560c44
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
91 # ^^^^^^ 91 # ^^^^^^
92 # For most revsets, 'define' means using the order this subset provides 92 # For most revsets, 'define' means using the order this subset provides
93 # 93 #
94 # There are a few revsets that always redefine the order if 'define' is 94 # There are a few revsets that always redefine the order if 'define' is
95 # specified: 'sort(X)', 'reverse(X)', 'x:y'. 95 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
96 anyorder = 'any' # don't care the order, could be even random-shuffled 96 anyorder = b'any' # don't care the order, could be even random-shuffled
97 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order 97 defineorder = b'define' # ALWAYS redefine, or ALWAYS follow the current order
98 followorder = 'follow' # MUST follow the current order 98 followorder = b'follow' # MUST follow the current order
99 99
100 # helpers 100 # helpers
101 101
102 102
103 def getset(repo, subset, x, order=defineorder): 103 def getset(repo, subset, x, order=defineorder):
104 if not x: 104 if not x:
105 raise error.ParseError(_("missing argument")) 105 raise error.ParseError(_(b"missing argument"))
106 return methods[x[0]](repo, subset, *x[1:], order=order) 106 return methods[x[0]](repo, subset, *x[1:], order=order)
107 107
108 108
109 def _getrevsource(repo, r): 109 def _getrevsource(repo, r):
110 extra = repo[r].extra() 110 extra = repo[r].extra()
111 for label in ('source', 'transplant_source', 'rebase_source'): 111 for label in (b'source', b'transplant_source', b'rebase_source'):
112 if label in extra: 112 if label in extra:
113 try: 113 try:
114 return repo[extra[label]].rev() 114 return repo[extra[label]].rev()
115 except error.RepoLookupError: 115 except error.RepoLookupError:
116 pass 116 pass
124 # operator methods 124 # operator methods
125 125
126 126
127 def stringset(repo, subset, x, order): 127 def stringset(repo, subset, x, order):
128 if not x: 128 if not x:
129 raise error.ParseError(_("empty string is not a valid revision")) 129 raise error.ParseError(_(b"empty string is not a valid revision"))
130 x = scmutil.intrev(scmutil.revsymbol(repo, x)) 130 x = scmutil.intrev(scmutil.revsymbol(repo, x))
131 if x in subset or x in _virtualrevs and isinstance(subset, fullreposet): 131 if x in subset or x in _virtualrevs and isinstance(subset, fullreposet):
132 return baseset([x]) 132 return baseset([x])
133 return baseset() 133 return baseset()
134 134
244 def notset(repo, subset, x, order): 244 def notset(repo, subset, x, order):
245 return subset - getset(repo, subset, x, anyorder) 245 return subset - getset(repo, subset, x, anyorder)
246 246
247 247
248 def relationset(repo, subset, x, y, order): 248 def relationset(repo, subset, x, y, order):
249 raise error.ParseError(_("can't use a relation in this context")) 249 raise error.ParseError(_(b"can't use a relation in this context"))
250 250
251 251
252 def _splitrange(a, b): 252 def _splitrange(a, b):
253 """Split range with bounds a and b into two ranges at 0 and return two 253 """Split range with bounds a and b into two ranges at 0 and return two
254 tuples of numbers for use as startdepth and stopdepth arguments of 254 tuples of numbers for use as startdepth and stopdepth arguments of
283 def generationsrel(repo, subset, x, rel, z, order): 283 def generationsrel(repo, subset, x, rel, z, order):
284 # TODO: rewrite tests, and drop startdepth argument from ancestors() and 284 # TODO: rewrite tests, and drop startdepth argument from ancestors() and
285 # descendants() predicates 285 # descendants() predicates
286 a, b = getintrange( 286 a, b = getintrange(
287 z, 287 z,
288 _('relation subscript must be an integer or a range'), 288 _(b'relation subscript must be an integer or a range'),
289 _('relation subscript bounds must be integers'), 289 _(b'relation subscript bounds must be integers'),
290 deffirst=-(dagop.maxlogdepth - 1), 290 deffirst=-(dagop.maxlogdepth - 1),
291 deflast=+(dagop.maxlogdepth - 1), 291 deflast=+(dagop.maxlogdepth - 1),
292 ) 292 )
293 (ancstart, ancstop), (descstart, descstop) = _splitrange(a, b) 293 (ancstart, ancstop), (descstart, descstop) = _splitrange(a, b)
294 294
321 relnames = [r for r in subscriptrelations.keys() if len(r) > 1] 321 relnames = [r for r in subscriptrelations.keys() if len(r) > 1]
322 raise error.UnknownIdentifier(rel, relnames) 322 raise error.UnknownIdentifier(rel, relnames)
323 323
324 324
325 def subscriptset(repo, subset, x, y, order): 325 def subscriptset(repo, subset, x, y, order):
326 raise error.ParseError(_("can't use a subscript in this context")) 326 raise error.ParseError(_(b"can't use a subscript in this context"))
327 327
328 328
329 def listset(repo, subset, *xs, **opts): 329 def listset(repo, subset, *xs, **opts):
330 raise error.ParseError( 330 raise error.ParseError(
331 _("can't use a list in this context"), 331 _(b"can't use a list in this context"),
332 hint=_('see \'hg help "revsets.x or y"\''), 332 hint=_(b'see \'hg help "revsets.x or y"\''),
333 ) 333 )
334 334
335 335
336 def keyvaluepair(repo, subset, k, v, order): 336 def keyvaluepair(repo, subset, k, v, order):
337 raise error.ParseError(_("can't use a key-value pair in this context")) 337 raise error.ParseError(_(b"can't use a key-value pair in this context"))
338 338
339 339
340 def func(repo, subset, a, b, order): 340 def func(repo, subset, a, b, order):
341 f = getsymbol(a) 341 f = getsymbol(a)
342 if f in symbols: 342 if f in symbols:
367 safesymbols = set() 367 safesymbols = set()
368 368
369 predicate = registrar.revsetpredicate() 369 predicate = registrar.revsetpredicate()
370 370
371 371
372 @predicate('_destupdate') 372 @predicate(b'_destupdate')
373 def _destupdate(repo, subset, x): 373 def _destupdate(repo, subset, x):
374 # experimental revset for update destination 374 # experimental revset for update destination
375 args = getargsdict(x, 'limit', 'clean') 375 args = getargsdict(x, b'limit', b'clean')
376 return subset & baseset( 376 return subset & baseset(
377 [destutil.destupdate(repo, **pycompat.strkwargs(args))[0]] 377 [destutil.destupdate(repo, **pycompat.strkwargs(args))[0]]
378 ) 378 )
379 379
380 380
381 @predicate('_destmerge') 381 @predicate(b'_destmerge')
382 def _destmerge(repo, subset, x): 382 def _destmerge(repo, subset, x):
383 # experimental revset for merge destination 383 # experimental revset for merge destination
384 sourceset = None 384 sourceset = None
385 if x is not None: 385 if x is not None:
386 sourceset = getset(repo, fullreposet(repo), x) 386 sourceset = getset(repo, fullreposet(repo), x)
387 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)]) 387 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
388 388
389 389
390 @predicate('adds(pattern)', safe=True, weight=30) 390 @predicate(b'adds(pattern)', safe=True, weight=30)
391 def adds(repo, subset, x): 391 def adds(repo, subset, x):
392 """Changesets that add a file matching pattern. 392 """Changesets that add a file matching pattern.
393 393
394 The pattern without explicit kind like ``glob:`` is expected to be 394 The pattern without explicit kind like ``glob:`` is expected to be
395 relative to the current directory and match against a file or a 395 relative to the current directory and match against a file or a
396 directory. 396 directory.
397 """ 397 """
398 # i18n: "adds" is a keyword 398 # i18n: "adds" is a keyword
399 pat = getstring(x, _("adds requires a pattern")) 399 pat = getstring(x, _(b"adds requires a pattern"))
400 return checkstatus(repo, subset, pat, 1) 400 return checkstatus(repo, subset, pat, 1)
401 401
402 402
403 @predicate('ancestor(*changeset)', safe=True, weight=0.5) 403 @predicate(b'ancestor(*changeset)', safe=True, weight=0.5)
404 def ancestor(repo, subset, x): 404 def ancestor(repo, subset, x):
405 """A greatest common ancestor of the changesets. 405 """A greatest common ancestor of the changesets.
406 406
407 Accepts 0 or more changesets. 407 Accepts 0 or more changesets.
408 Will return empty list when passed no args. 408 Will return empty list when passed no args.
430 return baseset() 430 return baseset()
431 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth) 431 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
432 return subset & s 432 return subset & s
433 433
434 434
435 @predicate('ancestors(set[, depth])', safe=True) 435 @predicate(b'ancestors(set[, depth])', safe=True)
436 def ancestors(repo, subset, x): 436 def ancestors(repo, subset, x):
437 """Changesets that are ancestors of changesets in set, including the 437 """Changesets that are ancestors of changesets in set, including the
438 given changesets themselves. 438 given changesets themselves.
439 439
440 If depth is specified, the result only includes changesets up to 440 If depth is specified, the result only includes changesets up to
441 the specified generation. 441 the specified generation.
442 """ 442 """
443 # startdepth is for internal use only until we can decide the UI 443 # startdepth is for internal use only until we can decide the UI
444 args = getargsdict(x, 'ancestors', 'set depth startdepth') 444 args = getargsdict(x, b'ancestors', b'set depth startdepth')
445 if 'set' not in args: 445 if b'set' not in args:
446 # i18n: "ancestors" is a keyword 446 # i18n: "ancestors" is a keyword
447 raise error.ParseError(_('ancestors takes at least 1 argument')) 447 raise error.ParseError(_(b'ancestors takes at least 1 argument'))
448 startdepth = stopdepth = None 448 startdepth = stopdepth = None
449 if 'startdepth' in args: 449 if b'startdepth' in args:
450 n = getinteger( 450 n = getinteger(
451 args['startdepth'], "ancestors expects an integer startdepth" 451 args[b'startdepth'], b"ancestors expects an integer startdepth"
452 ) 452 )
453 if n < 0: 453 if n < 0:
454 raise error.ParseError("negative startdepth") 454 raise error.ParseError(b"negative startdepth")
455 startdepth = n 455 startdepth = n
456 if 'depth' in args: 456 if b'depth' in args:
457 # i18n: "ancestors" is a keyword 457 # i18n: "ancestors" is a keyword
458 n = getinteger(args['depth'], _("ancestors expects an integer depth")) 458 n = getinteger(args[b'depth'], _(b"ancestors expects an integer depth"))
459 if n < 0: 459 if n < 0:
460 raise error.ParseError(_("negative depth")) 460 raise error.ParseError(_(b"negative depth"))
461 stopdepth = n + 1 461 stopdepth = n + 1
462 return _ancestors( 462 return _ancestors(
463 repo, subset, args['set'], startdepth=startdepth, stopdepth=stopdepth 463 repo, subset, args[b'set'], startdepth=startdepth, stopdepth=stopdepth
464 ) 464 )
465 465
466 466
467 @predicate('_firstancestors', safe=True) 467 @predicate(b'_firstancestors', safe=True)
468 def _firstancestors(repo, subset, x): 468 def _firstancestors(repo, subset, x):
469 # ``_firstancestors(set)`` 469 # ``_firstancestors(set)``
470 # Like ``ancestors(set)`` but follows only the first parents. 470 # Like ``ancestors(set)`` but follows only the first parents.
471 return _ancestors(repo, subset, x, followfirst=True) 471 return _ancestors(repo, subset, x, followfirst=True)
472 472
481 c = repo[r].children() 481 c = repo[r].children()
482 if len(c) == 0: 482 if len(c) == 0:
483 break 483 break
484 if len(c) > 1: 484 if len(c) > 1:
485 raise error.RepoLookupError( 485 raise error.RepoLookupError(
486 _("revision in set has more than one child") 486 _(b"revision in set has more than one child")
487 ) 487 )
488 r = c[0].rev() 488 r = c[0].rev()
489 else: 489 else:
490 cs.add(r) 490 cs.add(r)
491 return subset & cs 491 return subset & cs
494 def ancestorspec(repo, subset, x, n, order): 494 def ancestorspec(repo, subset, x, n, order):
495 """``set~n`` 495 """``set~n``
496 Changesets that are the Nth ancestor (first parents only) of a changeset 496 Changesets that are the Nth ancestor (first parents only) of a changeset
497 in set. 497 in set.
498 """ 498 """
499 n = getinteger(n, _("~ expects a number")) 499 n = getinteger(n, _(b"~ expects a number"))
500 if n < 0: 500 if n < 0:
501 # children lookup 501 # children lookup
502 return _childrenspec(repo, subset, x, -n, order) 502 return _childrenspec(repo, subset, x, -n, order)
503 ps = set() 503 ps = set()
504 cl = repo.changelog 504 cl = repo.changelog
510 r = repo[r].p1().rev() 510 r = repo[r].p1().rev()
511 ps.add(r) 511 ps.add(r)
512 return subset & ps 512 return subset & ps
513 513
514 514
515 @predicate('author(string)', safe=True, weight=10) 515 @predicate(b'author(string)', safe=True, weight=10)
516 def author(repo, subset, x): 516 def author(repo, subset, x):
517 """Alias for ``user(string)``. 517 """Alias for ``user(string)``.
518 """ 518 """
519 # i18n: "author" is a keyword 519 # i18n: "author" is a keyword
520 n = getstring(x, _("author requires a string")) 520 n = getstring(x, _(b"author requires a string"))
521 kind, pattern, matcher = _substringmatcher(n, casesensitive=False) 521 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
522 return subset.filter( 522 return subset.filter(
523 lambda x: matcher(repo[x].user()), condrepr=('<user %r>', n) 523 lambda x: matcher(repo[x].user()), condrepr=(b'<user %r>', n)
524 ) 524 )
525 525
526 526
527 @predicate('bisect(string)', safe=True) 527 @predicate(b'bisect(string)', safe=True)
528 def bisect(repo, subset, x): 528 def bisect(repo, subset, x):
529 """Changesets marked in the specified bisect status: 529 """Changesets marked in the specified bisect status:
530 530
531 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip 531 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
532 - ``goods``, ``bads`` : csets topologically good/bad 532 - ``goods``, ``bads`` : csets topologically good/bad
535 - ``untested`` : csets whose fate is yet unknown 535 - ``untested`` : csets whose fate is yet unknown
536 - ``ignored`` : csets ignored due to DAG topology 536 - ``ignored`` : csets ignored due to DAG topology
537 - ``current`` : the cset currently being bisected 537 - ``current`` : the cset currently being bisected
538 """ 538 """
539 # i18n: "bisect" is a keyword 539 # i18n: "bisect" is a keyword
540 status = getstring(x, _("bisect requires a string")).lower() 540 status = getstring(x, _(b"bisect requires a string")).lower()
541 state = set(hbisect.get(repo, status)) 541 state = set(hbisect.get(repo, status))
542 return subset & state 542 return subset & state
543 543
544 544
545 # Backward-compatibility 545 # Backward-compatibility
546 # - no help entry so that we do not advertise it any more 546 # - no help entry so that we do not advertise it any more
547 @predicate('bisected', safe=True) 547 @predicate(b'bisected', safe=True)
548 def bisected(repo, subset, x): 548 def bisected(repo, subset, x):
549 return bisect(repo, subset, x) 549 return bisect(repo, subset, x)
550 550
551 551
552 @predicate('bookmark([name])', safe=True) 552 @predicate(b'bookmark([name])', safe=True)
553 def bookmark(repo, subset, x): 553 def bookmark(repo, subset, x):
554 """The named bookmark or all bookmarks. 554 """The named bookmark or all bookmarks.
555 555
556 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`. 556 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
557 """ 557 """
558 # i18n: "bookmark" is a keyword 558 # i18n: "bookmark" is a keyword
559 args = getargs(x, 0, 1, _('bookmark takes one or no arguments')) 559 args = getargs(x, 0, 1, _(b'bookmark takes one or no arguments'))
560 if args: 560 if args:
561 bm = getstring( 561 bm = getstring(
562 args[0], 562 args[0],
563 # i18n: "bookmark" is a keyword 563 # i18n: "bookmark" is a keyword
564 _('the argument to bookmark must be a string'), 564 _(b'the argument to bookmark must be a string'),
565 ) 565 )
566 kind, pattern, matcher = stringutil.stringmatcher(bm) 566 kind, pattern, matcher = stringutil.stringmatcher(bm)
567 bms = set() 567 bms = set()
568 if kind == 'literal': 568 if kind == b'literal':
569 if bm == pattern: 569 if bm == pattern:
570 pattern = repo._bookmarks.expandname(pattern) 570 pattern = repo._bookmarks.expandname(pattern)
571 bmrev = repo._bookmarks.get(pattern, None) 571 bmrev = repo._bookmarks.get(pattern, None)
572 if not bmrev: 572 if not bmrev:
573 raise error.RepoLookupError( 573 raise error.RepoLookupError(
574 _("bookmark '%s' does not exist") % pattern 574 _(b"bookmark '%s' does not exist") % pattern
575 ) 575 )
576 bms.add(repo[bmrev].rev()) 576 bms.add(repo[bmrev].rev())
577 else: 577 else:
578 matchrevs = set() 578 matchrevs = set()
579 for name, bmrev in repo._bookmarks.iteritems(): 579 for name, bmrev in repo._bookmarks.iteritems():
585 bms = {repo[r].rev() for r in repo._bookmarks.values()} 585 bms = {repo[r].rev() for r in repo._bookmarks.values()}
586 bms -= {node.nullrev} 586 bms -= {node.nullrev}
587 return subset & bms 587 return subset & bms
588 588
589 589
590 @predicate('branch(string or set)', safe=True, weight=10) 590 @predicate(b'branch(string or set)', safe=True, weight=10)
591 def branch(repo, subset, x): 591 def branch(repo, subset, x):
592 """ 592 """
593 All changesets belonging to the given branch or the branches of the given 593 All changesets belonging to the given branch or the branches of the given
594 changesets. 594 changesets.
595 595
603 return getbi(r)[0] 603 return getbi(r)[0]
604 except error.WdirUnsupported: 604 except error.WdirUnsupported:
605 return repo[r].branch() 605 return repo[r].branch()
606 606
607 try: 607 try:
608 b = getstring(x, '') 608 b = getstring(x, b'')
609 except error.ParseError: 609 except error.ParseError:
610 # not a string, but another revspec, e.g. tip() 610 # not a string, but another revspec, e.g. tip()
611 pass 611 pass
612 else: 612 else:
613 kind, pattern, matcher = stringutil.stringmatcher(b) 613 kind, pattern, matcher = stringutil.stringmatcher(b)
614 if kind == 'literal': 614 if kind == b'literal':
615 # note: falls through to the revspec case if no branch with 615 # note: falls through to the revspec case if no branch with
616 # this name exists and pattern kind is not specified explicitly 616 # this name exists and pattern kind is not specified explicitly
617 if repo.branchmap().hasbranch(pattern): 617 if repo.branchmap().hasbranch(pattern):
618 return subset.filter( 618 return subset.filter(
619 lambda r: matcher(getbranch(r)), condrepr=('<branch %r>', b) 619 lambda r: matcher(getbranch(r)),
620 condrepr=(b'<branch %r>', b),
620 ) 621 )
621 if b.startswith('literal:'): 622 if b.startswith(b'literal:'):
622 raise error.RepoLookupError( 623 raise error.RepoLookupError(
623 _("branch '%s' does not exist") % pattern 624 _(b"branch '%s' does not exist") % pattern
624 ) 625 )
625 else: 626 else:
626 return subset.filter( 627 return subset.filter(
627 lambda r: matcher(getbranch(r)), condrepr=('<branch %r>', b) 628 lambda r: matcher(getbranch(r)), condrepr=(b'<branch %r>', b)
628 ) 629 )
629 630
630 s = getset(repo, fullreposet(repo), x) 631 s = getset(repo, fullreposet(repo), x)
631 b = set() 632 b = set()
632 for r in s: 633 for r in s:
633 b.add(getbranch(r)) 634 b.add(getbranch(r))
634 c = s.__contains__ 635 c = s.__contains__
635 return subset.filter( 636 return subset.filter(
636 lambda r: c(r) or getbranch(r) in b, 637 lambda r: c(r) or getbranch(r) in b,
637 condrepr=lambda: '<branch %r>' % _sortedb(b), 638 condrepr=lambda: b'<branch %r>' % _sortedb(b),
638 ) 639 )
639 640
640 641
641 @predicate('phasedivergent()', safe=True) 642 @predicate(b'phasedivergent()', safe=True)
642 def phasedivergent(repo, subset, x): 643 def phasedivergent(repo, subset, x):
643 """Mutable changesets marked as successors of public changesets. 644 """Mutable changesets marked as successors of public changesets.
644 645
645 Only non-public and non-obsolete changesets can be `phasedivergent`. 646 Only non-public and non-obsolete changesets can be `phasedivergent`.
646 (EXPERIMENTAL) 647 (EXPERIMENTAL)
647 """ 648 """
648 # i18n: "phasedivergent" is a keyword 649 # i18n: "phasedivergent" is a keyword
649 getargs(x, 0, 0, _("phasedivergent takes no arguments")) 650 getargs(x, 0, 0, _(b"phasedivergent takes no arguments"))
650 phasedivergent = obsmod.getrevs(repo, 'phasedivergent') 651 phasedivergent = obsmod.getrevs(repo, b'phasedivergent')
651 return subset & phasedivergent 652 return subset & phasedivergent
652 653
653 654
654 @predicate('bundle()', safe=True) 655 @predicate(b'bundle()', safe=True)
655 def bundle(repo, subset, x): 656 def bundle(repo, subset, x):
656 """Changesets in the bundle. 657 """Changesets in the bundle.
657 658
658 Bundle must be specified by the -R option.""" 659 Bundle must be specified by the -R option."""
659 660
660 try: 661 try:
661 bundlerevs = repo.changelog.bundlerevs 662 bundlerevs = repo.changelog.bundlerevs
662 except AttributeError: 663 except AttributeError:
663 raise error.Abort(_("no bundle provided - specify with -R")) 664 raise error.Abort(_(b"no bundle provided - specify with -R"))
664 return subset & bundlerevs 665 return subset & bundlerevs
665 666
666 667
667 def checkstatus(repo, subset, pat, field): 668 def checkstatus(repo, subset, pat, field):
668 """Helper for status-related revsets (adds, removes, modifies). 669 """Helper for status-related revsets (adds, removes, modifies).
669 The field parameter says which kind is desired: 670 The field parameter says which kind is desired:
670 0: modified 671 0: modified
671 1: added 672 1: added
672 2: removed 673 2: removed
673 """ 674 """
674 hasset = matchmod.patkind(pat) == 'set' 675 hasset = matchmod.patkind(pat) == b'set'
675 676
676 mcache = [None] 677 mcache = [None]
677 678
678 def matches(x): 679 def matches(x):
679 c = repo[x] 680 c = repo[x]
699 else: 700 else:
700 for f in files: 701 for f in files:
701 if m(f): 702 if m(f):
702 return True 703 return True
703 704
704 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat)) 705 return subset.filter(matches, condrepr=(b'<status[%r] %r>', field, pat))
705 706
706 707
707 def _children(repo, subset, parentset): 708 def _children(repo, subset, parentset):
708 if not parentset: 709 if not parentset:
709 return baseset() 710 return baseset()
720 if p2 != nullrev and p2 in parentset: 721 if p2 != nullrev and p2 in parentset:
721 cs.add(r) 722 cs.add(r)
722 return baseset(cs) 723 return baseset(cs)
723 724
724 725
725 @predicate('children(set)', safe=True) 726 @predicate(b'children(set)', safe=True)
726 def children(repo, subset, x): 727 def children(repo, subset, x):
727 """Child changesets of changesets in set. 728 """Child changesets of changesets in set.
728 """ 729 """
729 s = getset(repo, fullreposet(repo), x) 730 s = getset(repo, fullreposet(repo), x)
730 cs = _children(repo, subset, s) 731 cs = _children(repo, subset, s)
731 return subset & cs 732 return subset & cs
732 733
733 734
734 @predicate('closed()', safe=True, weight=10) 735 @predicate(b'closed()', safe=True, weight=10)
735 def closed(repo, subset, x): 736 def closed(repo, subset, x):
736 """Changeset is closed. 737 """Changeset is closed.
737 """ 738 """
738 # i18n: "closed" is a keyword 739 # i18n: "closed" is a keyword
739 getargs(x, 0, 0, _("closed takes no arguments")) 740 getargs(x, 0, 0, _(b"closed takes no arguments"))
740 return subset.filter( 741 return subset.filter(
741 lambda r: repo[r].closesbranch(), condrepr='<branch closed>' 742 lambda r: repo[r].closesbranch(), condrepr=b'<branch closed>'
742 ) 743 )
743 744
744 745
745 # for internal use 746 # for internal use
746 @predicate('_commonancestorheads(set)', safe=True) 747 @predicate(b'_commonancestorheads(set)', safe=True)
747 def _commonancestorheads(repo, subset, x): 748 def _commonancestorheads(repo, subset, x):
748 # This is an internal method is for quickly calculating "heads(::x and 749 # This is an internal method is for quickly calculating "heads(::x and
749 # ::y)" 750 # ::y)"
750 751
751 # These greatest common ancestors are the same ones that the consensus bid 752 # These greatest common ancestors are the same ones that the consensus bid
754 755
755 ancs = repo.changelog._commonancestorsheads(*list(startrevs)) 756 ancs = repo.changelog._commonancestorsheads(*list(startrevs))
756 return subset & baseset(ancs) 757 return subset & baseset(ancs)
757 758
758 759
759 @predicate('commonancestors(set)', safe=True) 760 @predicate(b'commonancestors(set)', safe=True)
760 def commonancestors(repo, subset, x): 761 def commonancestors(repo, subset, x):
761 """Changesets that are ancestors of every changeset in set. 762 """Changesets that are ancestors of every changeset in set.
762 """ 763 """
763 startrevs = getset(repo, fullreposet(repo), x, order=anyorder) 764 startrevs = getset(repo, fullreposet(repo), x, order=anyorder)
764 if not startrevs: 765 if not startrevs:
766 for r in startrevs: 767 for r in startrevs:
767 subset &= dagop.revancestors(repo, baseset([r])) 768 subset &= dagop.revancestors(repo, baseset([r]))
768 return subset 769 return subset
769 770
770 771
771 @predicate('contains(pattern)', weight=100) 772 @predicate(b'contains(pattern)', weight=100)
772 def contains(repo, subset, x): 773 def contains(repo, subset, x):
773 """The revision's manifest contains a file matching pattern (but might not 774 """The revision's manifest contains a file matching pattern (but might not
774 modify it). See :hg:`help patterns` for information about file patterns. 775 modify it). See :hg:`help patterns` for information about file patterns.
775 776
776 The pattern without explicit kind like ``glob:`` is expected to be 777 The pattern without explicit kind like ``glob:`` is expected to be
777 relative to the current directory and match against a file exactly 778 relative to the current directory and match against a file exactly
778 for efficiency. 779 for efficiency.
779 """ 780 """
780 # i18n: "contains" is a keyword 781 # i18n: "contains" is a keyword
781 pat = getstring(x, _("contains requires a pattern")) 782 pat = getstring(x, _(b"contains requires a pattern"))
782 783
783 def matches(x): 784 def matches(x):
784 if not matchmod.patkind(pat): 785 if not matchmod.patkind(pat):
785 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat) 786 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
786 if pats in repo[x]: 787 if pats in repo[x]:
791 for f in c.manifest(): 792 for f in c.manifest():
792 if m(f): 793 if m(f):
793 return True 794 return True
794 return False 795 return False
795 796
796 return subset.filter(matches, condrepr=('<contains %r>', pat)) 797 return subset.filter(matches, condrepr=(b'<contains %r>', pat))
797 798
798 799
799 @predicate('converted([id])', safe=True) 800 @predicate(b'converted([id])', safe=True)
800 def converted(repo, subset, x): 801 def converted(repo, subset, x):
801 """Changesets converted from the given identifier in the old repository if 802 """Changesets converted from the given identifier in the old repository if
802 present, or all converted changesets if no identifier is specified. 803 present, or all converted changesets if no identifier is specified.
803 """ 804 """
804 805
805 # There is exactly no chance of resolving the revision, so do a simple 806 # There is exactly no chance of resolving the revision, so do a simple
806 # string compare and hope for the best 807 # string compare and hope for the best
807 808
808 rev = None 809 rev = None
809 # i18n: "converted" is a keyword 810 # i18n: "converted" is a keyword
810 l = getargs(x, 0, 1, _('converted takes one or no arguments')) 811 l = getargs(x, 0, 1, _(b'converted takes one or no arguments'))
811 if l: 812 if l:
812 # i18n: "converted" is a keyword 813 # i18n: "converted" is a keyword
813 rev = getstring(l[0], _('converted requires a revision')) 814 rev = getstring(l[0], _(b'converted requires a revision'))
814 815
815 def _matchvalue(r): 816 def _matchvalue(r):
816 source = repo[r].extra().get('convert_revision', None) 817 source = repo[r].extra().get(b'convert_revision', None)
817 return source is not None and (rev is None or source.startswith(rev)) 818 return source is not None and (rev is None or source.startswith(rev))
818 819
819 return subset.filter( 820 return subset.filter(
820 lambda r: _matchvalue(r), condrepr=('<converted %r>', rev) 821 lambda r: _matchvalue(r), condrepr=(b'<converted %r>', rev)
821 ) 822 )
822 823
823 824
824 @predicate('date(interval)', safe=True, weight=10) 825 @predicate(b'date(interval)', safe=True, weight=10)
825 def date(repo, subset, x): 826 def date(repo, subset, x):
826 """Changesets within the interval, see :hg:`help dates`. 827 """Changesets within the interval, see :hg:`help dates`.
827 """ 828 """
828 # i18n: "date" is a keyword 829 # i18n: "date" is a keyword
829 ds = getstring(x, _("date requires a string")) 830 ds = getstring(x, _(b"date requires a string"))
830 dm = dateutil.matchdate(ds) 831 dm = dateutil.matchdate(ds)
831 return subset.filter( 832 return subset.filter(
832 lambda x: dm(repo[x].date()[0]), condrepr=('<date %r>', ds) 833 lambda x: dm(repo[x].date()[0]), condrepr=(b'<date %r>', ds)
833 ) 834 )
834 835
835 836
836 @predicate('desc(string)', safe=True, weight=10) 837 @predicate(b'desc(string)', safe=True, weight=10)
837 def desc(repo, subset, x): 838 def desc(repo, subset, x):
838 """Search commit message for string. The match is case-insensitive. 839 """Search commit message for string. The match is case-insensitive.
839 840
840 Pattern matching is supported for `string`. See 841 Pattern matching is supported for `string`. See
841 :hg:`help revisions.patterns`. 842 :hg:`help revisions.patterns`.
842 """ 843 """
843 # i18n: "desc" is a keyword 844 # i18n: "desc" is a keyword
844 ds = getstring(x, _("desc requires a string")) 845 ds = getstring(x, _(b"desc requires a string"))
845 846
846 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False) 847 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
847 848
848 return subset.filter( 849 return subset.filter(
849 lambda r: matcher(repo[r].description()), condrepr=('<desc %r>', ds) 850 lambda r: matcher(repo[r].description()), condrepr=(b'<desc %r>', ds)
850 ) 851 )
851 852
852 853
853 def _descendants( 854 def _descendants(
854 repo, subset, x, followfirst=False, startdepth=None, stopdepth=None 855 repo, subset, x, followfirst=False, startdepth=None, stopdepth=None
858 return baseset() 859 return baseset()
859 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth) 860 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
860 return subset & s 861 return subset & s
861 862
862 863
863 @predicate('descendants(set[, depth])', safe=True) 864 @predicate(b'descendants(set[, depth])', safe=True)
864 def descendants(repo, subset, x): 865 def descendants(repo, subset, x):
865 """Changesets which are descendants of changesets in set, including the 866 """Changesets which are descendants of changesets in set, including the
866 given changesets themselves. 867 given changesets themselves.
867 868
868 If depth is specified, the result only includes changesets up to 869 If depth is specified, the result only includes changesets up to
869 the specified generation. 870 the specified generation.
870 """ 871 """
871 # startdepth is for internal use only until we can decide the UI 872 # startdepth is for internal use only until we can decide the UI
872 args = getargsdict(x, 'descendants', 'set depth startdepth') 873 args = getargsdict(x, b'descendants', b'set depth startdepth')
873 if 'set' not in args: 874 if b'set' not in args:
874 # i18n: "descendants" is a keyword 875 # i18n: "descendants" is a keyword
875 raise error.ParseError(_('descendants takes at least 1 argument')) 876 raise error.ParseError(_(b'descendants takes at least 1 argument'))
876 startdepth = stopdepth = None 877 startdepth = stopdepth = None
877 if 'startdepth' in args: 878 if b'startdepth' in args:
878 n = getinteger( 879 n = getinteger(
879 args['startdepth'], "descendants expects an integer startdepth" 880 args[b'startdepth'], b"descendants expects an integer startdepth"
880 ) 881 )
881 if n < 0: 882 if n < 0:
882 raise error.ParseError("negative startdepth") 883 raise error.ParseError(b"negative startdepth")
883 startdepth = n 884 startdepth = n
884 if 'depth' in args: 885 if b'depth' in args:
885 # i18n: "descendants" is a keyword 886 # i18n: "descendants" is a keyword
886 n = getinteger(args['depth'], _("descendants expects an integer depth")) 887 n = getinteger(
888 args[b'depth'], _(b"descendants expects an integer depth")
889 )
887 if n < 0: 890 if n < 0:
888 raise error.ParseError(_("negative depth")) 891 raise error.ParseError(_(b"negative depth"))
889 stopdepth = n + 1 892 stopdepth = n + 1
890 return _descendants( 893 return _descendants(
891 repo, subset, args['set'], startdepth=startdepth, stopdepth=stopdepth 894 repo, subset, args[b'set'], startdepth=startdepth, stopdepth=stopdepth
892 ) 895 )
893 896
894 897
895 @predicate('_firstdescendants', safe=True) 898 @predicate(b'_firstdescendants', safe=True)
896 def _firstdescendants(repo, subset, x): 899 def _firstdescendants(repo, subset, x):
897 # ``_firstdescendants(set)`` 900 # ``_firstdescendants(set)``
898 # Like ``descendants(set)`` but follows only the first parents. 901 # Like ``descendants(set)`` but follows only the first parents.
899 return _descendants(repo, subset, x, followfirst=True) 902 return _descendants(repo, subset, x, followfirst=True)
900 903
901 904
902 @predicate('destination([set])', safe=True, weight=10) 905 @predicate(b'destination([set])', safe=True, weight=10)
903 def destination(repo, subset, x): 906 def destination(repo, subset, x):
904 """Changesets that were created by a graft, transplant or rebase operation, 907 """Changesets that were created by a graft, transplant or rebase operation,
905 with the given revisions specified as the source. Omitting the optional set 908 with the given revisions specified as the source. Omitting the optional set
906 is the same as passing all(). 909 is the same as passing all().
907 """ 910 """
941 r = src 944 r = src
942 src = _getrevsource(repo, r) 945 src = _getrevsource(repo, r)
943 946
944 return subset.filter( 947 return subset.filter(
945 dests.__contains__, 948 dests.__contains__,
946 condrepr=lambda: '<destination %r>' % _sortedb(dests), 949 condrepr=lambda: b'<destination %r>' % _sortedb(dests),
947 ) 950 )
948 951
949 952
950 @predicate('contentdivergent()', safe=True) 953 @predicate(b'contentdivergent()', safe=True)
951 def contentdivergent(repo, subset, x): 954 def contentdivergent(repo, subset, x):
952 """ 955 """
953 Final successors of changesets with an alternative set of final 956 Final successors of changesets with an alternative set of final
954 successors. (EXPERIMENTAL) 957 successors. (EXPERIMENTAL)
955 """ 958 """
956 # i18n: "contentdivergent" is a keyword 959 # i18n: "contentdivergent" is a keyword
957 getargs(x, 0, 0, _("contentdivergent takes no arguments")) 960 getargs(x, 0, 0, _(b"contentdivergent takes no arguments"))
958 contentdivergent = obsmod.getrevs(repo, 'contentdivergent') 961 contentdivergent = obsmod.getrevs(repo, b'contentdivergent')
959 return subset & contentdivergent 962 return subset & contentdivergent
960 963
961 964
962 @predicate('expectsize(set[, size])', safe=True, takeorder=True) 965 @predicate(b'expectsize(set[, size])', safe=True, takeorder=True)
963 def expectsize(repo, subset, x, order): 966 def expectsize(repo, subset, x, order):
964 """Return the given revset if size matches the revset size. 967 """Return the given revset if size matches the revset size.
965 Abort if the revset doesn't expect given size. 968 Abort if the revset doesn't expect given size.
966 size can either be an integer range or an integer. 969 size can either be an integer range or an integer.
967 970
968 For example, ``expectsize(0:1, 3:5)`` will abort as revset size is 2 and 971 For example, ``expectsize(0:1, 3:5)`` will abort as revset size is 2 and
969 2 is not between 3 and 5 inclusive.""" 972 2 is not between 3 and 5 inclusive."""
970 973
971 args = getargsdict(x, 'expectsize', 'set size') 974 args = getargsdict(x, b'expectsize', b'set size')
972 minsize = 0 975 minsize = 0
973 maxsize = len(repo) + 1 976 maxsize = len(repo) + 1
974 err = '' 977 err = b''
975 if 'size' not in args or 'set' not in args: 978 if b'size' not in args or b'set' not in args:
976 raise error.ParseError(_('invalid set of arguments')) 979 raise error.ParseError(_(b'invalid set of arguments'))
977 minsize, maxsize = getintrange( 980 minsize, maxsize = getintrange(
978 args['size'], 981 args[b'size'],
979 _('expectsize requires a size range' ' or a positive integer'), 982 _(b'expectsize requires a size range' b' or a positive integer'),
980 _('size range bounds must be integers'), 983 _(b'size range bounds must be integers'),
981 minsize, 984 minsize,
982 maxsize, 985 maxsize,
983 ) 986 )
984 if minsize < 0 or maxsize < 0: 987 if minsize < 0 or maxsize < 0:
985 raise error.ParseError(_('negative size')) 988 raise error.ParseError(_(b'negative size'))
986 rev = getset(repo, fullreposet(repo), args['set'], order=order) 989 rev = getset(repo, fullreposet(repo), args[b'set'], order=order)
987 if minsize != maxsize and (len(rev) < minsize or len(rev) > maxsize): 990 if minsize != maxsize and (len(rev) < minsize or len(rev) > maxsize):
988 err = _( 991 err = _(
989 'revset size mismatch.' ' expected between %d and %d, got %d' 992 b'revset size mismatch.' b' expected between %d and %d, got %d'
990 ) % (minsize, maxsize, len(rev)) 993 ) % (minsize, maxsize, len(rev))
991 elif minsize == maxsize and len(rev) != minsize: 994 elif minsize == maxsize and len(rev) != minsize:
992 err = _('revset size mismatch.' ' expected %d, got %d') % ( 995 err = _(b'revset size mismatch.' b' expected %d, got %d') % (
993 minsize, 996 minsize,
994 len(rev), 997 len(rev),
995 ) 998 )
996 if err: 999 if err:
997 raise error.RepoLookupError(err) 1000 raise error.RepoLookupError(err)
999 return subset & rev 1002 return subset & rev
1000 else: 1003 else:
1001 return rev & subset 1004 return rev & subset
1002 1005
1003 1006
1004 @predicate('extdata(source)', safe=False, weight=100) 1007 @predicate(b'extdata(source)', safe=False, weight=100)
1005 def extdata(repo, subset, x): 1008 def extdata(repo, subset, x):
1006 """Changesets in the specified extdata source. (EXPERIMENTAL)""" 1009 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
1007 # i18n: "extdata" is a keyword 1010 # i18n: "extdata" is a keyword
1008 args = getargsdict(x, 'extdata', 'source') 1011 args = getargsdict(x, b'extdata', b'source')
1009 source = getstring( 1012 source = getstring(
1010 args.get('source'), 1013 args.get(b'source'),
1011 # i18n: "extdata" is a keyword 1014 # i18n: "extdata" is a keyword
1012 _('extdata takes at least 1 string argument'), 1015 _(b'extdata takes at least 1 string argument'),
1013 ) 1016 )
1014 data = scmutil.extdatasource(repo, source) 1017 data = scmutil.extdatasource(repo, source)
1015 return subset & baseset(data) 1018 return subset & baseset(data)
1016 1019
1017 1020
1018 @predicate('extinct()', safe=True) 1021 @predicate(b'extinct()', safe=True)
1019 def extinct(repo, subset, x): 1022 def extinct(repo, subset, x):
1020 """Obsolete changesets with obsolete descendants only. 1023 """Obsolete changesets with obsolete descendants only.
1021 """ 1024 """
1022 # i18n: "extinct" is a keyword 1025 # i18n: "extinct" is a keyword
1023 getargs(x, 0, 0, _("extinct takes no arguments")) 1026 getargs(x, 0, 0, _(b"extinct takes no arguments"))
1024 extincts = obsmod.getrevs(repo, 'extinct') 1027 extincts = obsmod.getrevs(repo, b'extinct')
1025 return subset & extincts 1028 return subset & extincts
1026 1029
1027 1030
1028 @predicate('extra(label, [value])', safe=True) 1031 @predicate(b'extra(label, [value])', safe=True)
1029 def extra(repo, subset, x): 1032 def extra(repo, subset, x):
1030 """Changesets with the given label in the extra metadata, with the given 1033 """Changesets with the given label in the extra metadata, with the given
1031 optional value. 1034 optional value.
1032 1035
1033 Pattern matching is supported for `value`. See 1036 Pattern matching is supported for `value`. See
1034 :hg:`help revisions.patterns`. 1037 :hg:`help revisions.patterns`.
1035 """ 1038 """
1036 args = getargsdict(x, 'extra', 'label value') 1039 args = getargsdict(x, b'extra', b'label value')
1037 if 'label' not in args: 1040 if b'label' not in args:
1038 # i18n: "extra" is a keyword 1041 # i18n: "extra" is a keyword
1039 raise error.ParseError(_('extra takes at least 1 argument')) 1042 raise error.ParseError(_(b'extra takes at least 1 argument'))
1040 # i18n: "extra" is a keyword 1043 # i18n: "extra" is a keyword
1041 label = getstring( 1044 label = getstring(
1042 args['label'], _('first argument to extra must be ' 'a string') 1045 args[b'label'], _(b'first argument to extra must be ' b'a string')
1043 ) 1046 )
1044 value = None 1047 value = None
1045 1048
1046 if 'value' in args: 1049 if b'value' in args:
1047 # i18n: "extra" is a keyword 1050 # i18n: "extra" is a keyword
1048 value = getstring( 1051 value = getstring(
1049 args['value'], _('second argument to extra must be ' 'a string') 1052 args[b'value'], _(b'second argument to extra must be ' b'a string')
1050 ) 1053 )
1051 kind, value, matcher = stringutil.stringmatcher(value) 1054 kind, value, matcher = stringutil.stringmatcher(value)
1052 1055
1053 def _matchvalue(r): 1056 def _matchvalue(r):
1054 extra = repo[r].extra() 1057 extra = repo[r].extra()
1055 return label in extra and (value is None or matcher(extra[label])) 1058 return label in extra and (value is None or matcher(extra[label]))
1056 1059
1057 return subset.filter( 1060 return subset.filter(
1058 lambda r: _matchvalue(r), condrepr=('<extra[%r] %r>', label, value) 1061 lambda r: _matchvalue(r), condrepr=(b'<extra[%r] %r>', label, value)
1059 ) 1062 )
1060 1063
1061 1064
1062 @predicate('filelog(pattern)', safe=True) 1065 @predicate(b'filelog(pattern)', safe=True)
1063 def filelog(repo, subset, x): 1066 def filelog(repo, subset, x):
1064 """Changesets connected to the specified filelog. 1067 """Changesets connected to the specified filelog.
1065 1068
1066 For performance reasons, visits only revisions mentioned in the file-level 1069 For performance reasons, visits only revisions mentioned in the file-level
1067 filelog, rather than filtering through all changesets (much faster, but 1070 filelog, rather than filtering through all changesets (much faster, but
1072 relative to the current directory and match against a file exactly 1075 relative to the current directory and match against a file exactly
1073 for efficiency. 1076 for efficiency.
1074 """ 1077 """
1075 1078
1076 # i18n: "filelog" is a keyword 1079 # i18n: "filelog" is a keyword
1077 pat = getstring(x, _("filelog requires a pattern")) 1080 pat = getstring(x, _(b"filelog requires a pattern"))
1078 s = set() 1081 s = set()
1079 cl = repo.changelog 1082 cl = repo.changelog
1080 1083
1081 if not matchmod.patkind(pat): 1084 if not matchmod.patkind(pat):
1082 f = pathutil.canonpath(repo.root, repo.getcwd(), pat) 1085 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
1121 continue 1124 continue
1122 1125
1123 return subset & s 1126 return subset & s
1124 1127
1125 1128
1126 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0) 1129 @predicate(b'first(set, [n])', safe=True, takeorder=True, weight=0)
1127 def first(repo, subset, x, order): 1130 def first(repo, subset, x, order):
1128 """An alias for limit(). 1131 """An alias for limit().
1129 """ 1132 """
1130 return limit(repo, subset, x, order) 1133 return limit(repo, subset, x, order)
1131 1134
1132 1135
1133 def _follow(repo, subset, x, name, followfirst=False): 1136 def _follow(repo, subset, x, name, followfirst=False):
1134 args = getargsdict(x, name, 'file startrev') 1137 args = getargsdict(x, name, b'file startrev')
1135 revs = None 1138 revs = None
1136 if 'startrev' in args: 1139 if b'startrev' in args:
1137 revs = getset(repo, fullreposet(repo), args['startrev']) 1140 revs = getset(repo, fullreposet(repo), args[b'startrev'])
1138 if 'file' in args: 1141 if b'file' in args:
1139 x = getstring(args['file'], _("%s expected a pattern") % name) 1142 x = getstring(args[b'file'], _(b"%s expected a pattern") % name)
1140 if revs is None: 1143 if revs is None:
1141 revs = [None] 1144 revs = [None]
1142 fctxs = [] 1145 fctxs = []
1143 for r in revs: 1146 for r in revs:
1144 ctx = mctx = repo[r] 1147 ctx = mctx = repo[r]
1145 if r is None: 1148 if r is None:
1146 ctx = repo['.'] 1149 ctx = repo[b'.']
1147 m = matchmod.match( 1150 m = matchmod.match(
1148 repo.root, repo.getcwd(), [x], ctx=mctx, default='path' 1151 repo.root, repo.getcwd(), [x], ctx=mctx, default=b'path'
1149 ) 1152 )
1150 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m)) 1153 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
1151 s = dagop.filerevancestors(fctxs, followfirst) 1154 s = dagop.filerevancestors(fctxs, followfirst)
1152 else: 1155 else:
1153 if revs is None: 1156 if revs is None:
1154 revs = baseset([repo['.'].rev()]) 1157 revs = baseset([repo[b'.'].rev()])
1155 s = dagop.revancestors(repo, revs, followfirst) 1158 s = dagop.revancestors(repo, revs, followfirst)
1156 1159
1157 return subset & s 1160 return subset & s
1158 1161
1159 1162
1160 @predicate('follow([file[, startrev]])', safe=True) 1163 @predicate(b'follow([file[, startrev]])', safe=True)
1161 def follow(repo, subset, x): 1164 def follow(repo, subset, x):
1162 """ 1165 """
1163 An alias for ``::.`` (ancestors of the working directory's first parent). 1166 An alias for ``::.`` (ancestors of the working directory's first parent).
1164 If file pattern is specified, the histories of files matching given 1167 If file pattern is specified, the histories of files matching given
1165 pattern in the revision given by startrev are followed, including copies. 1168 pattern in the revision given by startrev are followed, including copies.
1166 """ 1169 """
1167 return _follow(repo, subset, x, 'follow') 1170 return _follow(repo, subset, x, b'follow')
1168 1171
1169 1172
1170 @predicate('_followfirst', safe=True) 1173 @predicate(b'_followfirst', safe=True)
1171 def _followfirst(repo, subset, x): 1174 def _followfirst(repo, subset, x):
1172 # ``followfirst([file[, startrev]])`` 1175 # ``followfirst([file[, startrev]])``
1173 # Like ``follow([file[, startrev]])`` but follows only the first parent 1176 # Like ``follow([file[, startrev]])`` but follows only the first parent
1174 # of every revisions or files revisions. 1177 # of every revisions or files revisions.
1175 return _follow(repo, subset, x, '_followfirst', followfirst=True) 1178 return _follow(repo, subset, x, b'_followfirst', followfirst=True)
1176 1179
1177 1180
1178 @predicate( 1181 @predicate(
1179 'followlines(file, fromline:toline[, startrev=., descend=False])', safe=True 1182 b'followlines(file, fromline:toline[, startrev=., descend=False])',
1183 safe=True,
1180 ) 1184 )
1181 def followlines(repo, subset, x): 1185 def followlines(repo, subset, x):
1182 """Changesets modifying `file` in line range ('fromline', 'toline'). 1186 """Changesets modifying `file` in line range ('fromline', 'toline').
1183 1187
1184 Line range corresponds to 'file' content at 'startrev' and should hence be 1188 Line range corresponds to 'file' content at 'startrev' and should hence be
1187 1191
1188 By default, ancestors of 'startrev' are returned. If 'descend' is True, 1192 By default, ancestors of 'startrev' are returned. If 'descend' is True,
1189 descendants of 'startrev' are returned though renames are (currently) not 1193 descendants of 'startrev' are returned though renames are (currently) not
1190 followed in this direction. 1194 followed in this direction.
1191 """ 1195 """
1192 args = getargsdict(x, 'followlines', 'file *lines startrev descend') 1196 args = getargsdict(x, b'followlines', b'file *lines startrev descend')
1193 if len(args['lines']) != 1: 1197 if len(args[b'lines']) != 1:
1194 raise error.ParseError(_("followlines requires a line range")) 1198 raise error.ParseError(_(b"followlines requires a line range"))
1195 1199
1196 rev = '.' 1200 rev = b'.'
1197 if 'startrev' in args: 1201 if b'startrev' in args:
1198 revs = getset(repo, fullreposet(repo), args['startrev']) 1202 revs = getset(repo, fullreposet(repo), args[b'startrev'])
1199 if len(revs) != 1: 1203 if len(revs) != 1:
1200 raise error.ParseError( 1204 raise error.ParseError(
1201 # i18n: "followlines" is a keyword 1205 # i18n: "followlines" is a keyword
1202 _("followlines expects exactly one revision") 1206 _(b"followlines expects exactly one revision")
1203 ) 1207 )
1204 rev = revs.last() 1208 rev = revs.last()
1205 1209
1206 pat = getstring(args['file'], _("followlines requires a pattern")) 1210 pat = getstring(args[b'file'], _(b"followlines requires a pattern"))
1207 # i18n: "followlines" is a keyword 1211 # i18n: "followlines" is a keyword
1208 msg = _("followlines expects exactly one file") 1212 msg = _(b"followlines expects exactly one file")
1209 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg) 1213 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
1210 fromline, toline = util.processlinerange( 1214 fromline, toline = util.processlinerange(
1211 *getintrange( 1215 *getintrange(
1212 args['lines'][0], 1216 args[b'lines'][0],
1213 # i18n: "followlines" is a keyword 1217 # i18n: "followlines" is a keyword
1214 _("followlines expects a line number or a range"), 1218 _(b"followlines expects a line number or a range"),
1215 _("line range bounds must be integers"), 1219 _(b"line range bounds must be integers"),
1216 ) 1220 )
1217 ) 1221 )
1218 1222
1219 fctx = repo[rev].filectx(fname) 1223 fctx = repo[rev].filectx(fname)
1220 descend = False 1224 descend = False
1221 if 'descend' in args: 1225 if b'descend' in args:
1222 descend = getboolean( 1226 descend = getboolean(
1223 args['descend'], 1227 args[b'descend'],
1224 # i18n: "descend" is a keyword 1228 # i18n: "descend" is a keyword
1225 _("descend argument must be a boolean"), 1229 _(b"descend argument must be a boolean"),
1226 ) 1230 )
1227 if descend: 1231 if descend:
1228 rs = generatorset( 1232 rs = generatorset(
1229 ( 1233 (
1230 c.rev() 1234 c.rev()
1245 iterasc=False, 1249 iterasc=False,
1246 ) 1250 )
1247 return subset & rs 1251 return subset & rs
1248 1252
1249 1253
1250 @predicate('all()', safe=True) 1254 @predicate(b'all()', safe=True)
1251 def getall(repo, subset, x): 1255 def getall(repo, subset, x):
1252 """All changesets, the same as ``0:tip``. 1256 """All changesets, the same as ``0:tip``.
1253 """ 1257 """
1254 # i18n: "all" is a keyword 1258 # i18n: "all" is a keyword
1255 getargs(x, 0, 0, _("all takes no arguments")) 1259 getargs(x, 0, 0, _(b"all takes no arguments"))
1256 return subset & spanset(repo) # drop "null" if any 1260 return subset & spanset(repo) # drop "null" if any
1257 1261
1258 1262
1259 @predicate('grep(regex)', weight=10) 1263 @predicate(b'grep(regex)', weight=10)
1260 def grep(repo, subset, x): 1264 def grep(repo, subset, x):
1261 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')`` 1265 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1262 to ensure special escape characters are handled correctly. Unlike 1266 to ensure special escape characters are handled correctly. Unlike
1263 ``keyword(string)``, the match is case-sensitive. 1267 ``keyword(string)``, the match is case-sensitive.
1264 """ 1268 """
1265 try: 1269 try:
1266 # i18n: "grep" is a keyword 1270 # i18n: "grep" is a keyword
1267 gr = re.compile(getstring(x, _("grep requires a string"))) 1271 gr = re.compile(getstring(x, _(b"grep requires a string")))
1268 except re.error as e: 1272 except re.error as e:
1269 raise error.ParseError( 1273 raise error.ParseError(
1270 _('invalid match pattern: %s') % stringutil.forcebytestr(e) 1274 _(b'invalid match pattern: %s') % stringutil.forcebytestr(e)
1271 ) 1275 )
1272 1276
1273 def matches(x): 1277 def matches(x):
1274 c = repo[x] 1278 c = repo[x]
1275 for e in c.files() + [c.user(), c.description()]: 1279 for e in c.files() + [c.user(), c.description()]:
1276 if gr.search(e): 1280 if gr.search(e):
1277 return True 1281 return True
1278 return False 1282 return False
1279 1283
1280 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern)) 1284 return subset.filter(matches, condrepr=(b'<grep %r>', gr.pattern))
1281 1285
1282 1286
1283 @predicate('_matchfiles', safe=True) 1287 @predicate(b'_matchfiles', safe=True)
1284 def _matchfiles(repo, subset, x): 1288 def _matchfiles(repo, subset, x):
1285 # _matchfiles takes a revset list of prefixed arguments: 1289 # _matchfiles takes a revset list of prefixed arguments:
1286 # 1290 #
1287 # [p:foo, i:bar, x:baz] 1291 # [p:foo, i:bar, x:baz]
1288 # 1292 #
1292 # a revision identifier, or the empty string to reference the 1296 # a revision identifier, or the empty string to reference the
1293 # working directory, from which the match object is 1297 # working directory, from which the match object is
1294 # initialized. Use 'd:' to set the default matching mode, default 1298 # initialized. Use 'd:' to set the default matching mode, default
1295 # to 'glob'. At most one 'r:' and 'd:' argument can be passed. 1299 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1296 1300
1297 l = getargs(x, 1, -1, "_matchfiles requires at least one argument") 1301 l = getargs(x, 1, -1, b"_matchfiles requires at least one argument")
1298 pats, inc, exc = [], [], [] 1302 pats, inc, exc = [], [], []
1299 rev, default = None, None 1303 rev, default = None, None
1300 for arg in l: 1304 for arg in l:
1301 s = getstring(arg, "_matchfiles requires string arguments") 1305 s = getstring(arg, b"_matchfiles requires string arguments")
1302 prefix, value = s[:2], s[2:] 1306 prefix, value = s[:2], s[2:]
1303 if prefix == 'p:': 1307 if prefix == b'p:':
1304 pats.append(value) 1308 pats.append(value)
1305 elif prefix == 'i:': 1309 elif prefix == b'i:':
1306 inc.append(value) 1310 inc.append(value)
1307 elif prefix == 'x:': 1311 elif prefix == b'x:':
1308 exc.append(value) 1312 exc.append(value)
1309 elif prefix == 'r:': 1313 elif prefix == b'r:':
1310 if rev is not None: 1314 if rev is not None:
1311 raise error.ParseError( 1315 raise error.ParseError(
1312 '_matchfiles expected at most one ' 'revision' 1316 b'_matchfiles expected at most one ' b'revision'
1313 ) 1317 )
1314 if value == '': # empty means working directory 1318 if value == b'': # empty means working directory
1315 rev = node.wdirrev 1319 rev = node.wdirrev
1316 else: 1320 else:
1317 rev = value 1321 rev = value
1318 elif prefix == 'd:': 1322 elif prefix == b'd:':
1319 if default is not None: 1323 if default is not None:
1320 raise error.ParseError( 1324 raise error.ParseError(
1321 '_matchfiles expected at most one ' 'default mode' 1325 b'_matchfiles expected at most one ' b'default mode'
1322 ) 1326 )
1323 default = value 1327 default = value
1324 else: 1328 else:
1325 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix) 1329 raise error.ParseError(b'invalid _matchfiles prefix: %s' % prefix)
1326 if not default: 1330 if not default:
1327 default = 'glob' 1331 default = b'glob'
1328 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc) 1332 hasset = any(matchmod.patkind(p) == b'set' for p in pats + inc + exc)
1329 1333
1330 mcache = [None] 1334 mcache = [None]
1331 1335
1332 # This directly read the changelog data as creating changectx for all 1336 # This directly read the changelog data as creating changectx for all
1333 # revisions is quite expensive. 1337 # revisions is quite expensive.
1359 return False 1363 return False
1360 1364
1361 return subset.filter( 1365 return subset.filter(
1362 matches, 1366 matches,
1363 condrepr=( 1367 condrepr=(
1364 '<matchfiles patterns=%r, include=%r ' 1368 b'<matchfiles patterns=%r, include=%r '
1365 'exclude=%r, default=%r, rev=%r>', 1369 b'exclude=%r, default=%r, rev=%r>',
1366 pats, 1370 pats,
1367 inc, 1371 inc,
1368 exc, 1372 exc,
1369 default, 1373 default,
1370 rev, 1374 rev,
1371 ), 1375 ),
1372 ) 1376 )
1373 1377
1374 1378
1375 @predicate('file(pattern)', safe=True, weight=10) 1379 @predicate(b'file(pattern)', safe=True, weight=10)
1376 def hasfile(repo, subset, x): 1380 def hasfile(repo, subset, x):
1377 """Changesets affecting files matched by pattern. 1381 """Changesets affecting files matched by pattern.
1378 1382
1379 For a faster but less accurate result, consider using ``filelog()`` 1383 For a faster but less accurate result, consider using ``filelog()``
1380 instead. 1384 instead.
1381 1385
1382 This predicate uses ``glob:`` as the default kind of pattern. 1386 This predicate uses ``glob:`` as the default kind of pattern.
1383 """ 1387 """
1384 # i18n: "file" is a keyword 1388 # i18n: "file" is a keyword
1385 pat = getstring(x, _("file requires a pattern")) 1389 pat = getstring(x, _(b"file requires a pattern"))
1386 return _matchfiles(repo, subset, ('string', 'p:' + pat)) 1390 return _matchfiles(repo, subset, (b'string', b'p:' + pat))
1387 1391
1388 1392
1389 @predicate('head()', safe=True) 1393 @predicate(b'head()', safe=True)
1390 def head(repo, subset, x): 1394 def head(repo, subset, x):
1391 """Changeset is a named branch head. 1395 """Changeset is a named branch head.
1392 """ 1396 """
1393 # i18n: "head" is a keyword 1397 # i18n: "head" is a keyword
1394 getargs(x, 0, 0, _("head takes no arguments")) 1398 getargs(x, 0, 0, _(b"head takes no arguments"))
1395 hs = set() 1399 hs = set()
1396 cl = repo.changelog 1400 cl = repo.changelog
1397 for ls in repo.branchmap().iterheads(): 1401 for ls in repo.branchmap().iterheads():
1398 hs.update(cl.rev(h) for h in ls) 1402 hs.update(cl.rev(h) for h in ls)
1399 return subset & baseset(hs) 1403 return subset & baseset(hs)
1400 1404
1401 1405
1402 @predicate('heads(set)', safe=True, takeorder=True) 1406 @predicate(b'heads(set)', safe=True, takeorder=True)
1403 def heads(repo, subset, x, order): 1407 def heads(repo, subset, x, order):
1404 """Members of set with no children in set. 1408 """Members of set with no children in set.
1405 """ 1409 """
1406 # argument set should never define order 1410 # argument set should never define order
1407 if order == defineorder: 1411 if order == defineorder:
1419 heads.add(node.wdirrev) 1423 heads.add(node.wdirrev)
1420 heads = baseset(heads) 1424 heads = baseset(heads)
1421 return subset & heads 1425 return subset & heads
1422 1426
1423 1427
1424 @predicate('hidden()', safe=True) 1428 @predicate(b'hidden()', safe=True)
1425 def hidden(repo, subset, x): 1429 def hidden(repo, subset, x):
1426 """Hidden changesets. 1430 """Hidden changesets.
1427 """ 1431 """
1428 # i18n: "hidden" is a keyword 1432 # i18n: "hidden" is a keyword
1429 getargs(x, 0, 0, _("hidden takes no arguments")) 1433 getargs(x, 0, 0, _(b"hidden takes no arguments"))
1430 hiddenrevs = repoview.filterrevs(repo, 'visible') 1434 hiddenrevs = repoview.filterrevs(repo, b'visible')
1431 return subset & hiddenrevs 1435 return subset & hiddenrevs
1432 1436
1433 1437
1434 @predicate('keyword(string)', safe=True, weight=10) 1438 @predicate(b'keyword(string)', safe=True, weight=10)
1435 def keyword(repo, subset, x): 1439 def keyword(repo, subset, x):
1436 """Search commit message, user name, and names of changed files for 1440 """Search commit message, user name, and names of changed files for
1437 string. The match is case-insensitive. 1441 string. The match is case-insensitive.
1438 1442
1439 For a regular expression or case sensitive search of these fields, use 1443 For a regular expression or case sensitive search of these fields, use
1440 ``grep(regex)``. 1444 ``grep(regex)``.
1441 """ 1445 """
1442 # i18n: "keyword" is a keyword 1446 # i18n: "keyword" is a keyword
1443 kw = encoding.lower(getstring(x, _("keyword requires a string"))) 1447 kw = encoding.lower(getstring(x, _(b"keyword requires a string")))
1444 1448
1445 def matches(r): 1449 def matches(r):
1446 c = repo[r] 1450 c = repo[r]
1447 return any( 1451 return any(
1448 kw in encoding.lower(t) 1452 kw in encoding.lower(t)
1449 for t in c.files() + [c.user(), c.description()] 1453 for t in c.files() + [c.user(), c.description()]
1450 ) 1454 )
1451 1455
1452 return subset.filter(matches, condrepr=('<keyword %r>', kw)) 1456 return subset.filter(matches, condrepr=(b'<keyword %r>', kw))
1453 1457
1454 1458
1455 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0) 1459 @predicate(b'limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1456 def limit(repo, subset, x, order): 1460 def limit(repo, subset, x, order):
1457 """First n members of set, defaulting to 1, starting from offset. 1461 """First n members of set, defaulting to 1, starting from offset.
1458 """ 1462 """
1459 args = getargsdict(x, 'limit', 'set n offset') 1463 args = getargsdict(x, b'limit', b'set n offset')
1460 if 'set' not in args: 1464 if b'set' not in args:
1461 # i18n: "limit" is a keyword 1465 # i18n: "limit" is a keyword
1462 raise error.ParseError(_("limit requires one to three arguments")) 1466 raise error.ParseError(_(b"limit requires one to three arguments"))
1463 # i18n: "limit" is a keyword 1467 # i18n: "limit" is a keyword
1464 lim = getinteger(args.get('n'), _("limit expects a number"), default=1) 1468 lim = getinteger(args.get(b'n'), _(b"limit expects a number"), default=1)
1465 if lim < 0: 1469 if lim < 0:
1466 raise error.ParseError(_("negative number to select")) 1470 raise error.ParseError(_(b"negative number to select"))
1467 # i18n: "limit" is a keyword 1471 # i18n: "limit" is a keyword
1468 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0) 1472 ofs = getinteger(
1473 args.get(b'offset'), _(b"limit expects a number"), default=0
1474 )
1469 if ofs < 0: 1475 if ofs < 0:
1470 raise error.ParseError(_("negative offset")) 1476 raise error.ParseError(_(b"negative offset"))
1471 os = getset(repo, fullreposet(repo), args['set']) 1477 os = getset(repo, fullreposet(repo), args[b'set'])
1472 ls = os.slice(ofs, ofs + lim) 1478 ls = os.slice(ofs, ofs + lim)
1473 if order == followorder and lim > 1: 1479 if order == followorder and lim > 1:
1474 return subset & ls 1480 return subset & ls
1475 return ls & subset 1481 return ls & subset
1476 1482
1477 1483
1478 @predicate('last(set, [n])', safe=True, takeorder=True) 1484 @predicate(b'last(set, [n])', safe=True, takeorder=True)
1479 def last(repo, subset, x, order): 1485 def last(repo, subset, x, order):
1480 """Last n members of set, defaulting to 1. 1486 """Last n members of set, defaulting to 1.
1481 """ 1487 """
1482 # i18n: "last" is a keyword 1488 # i18n: "last" is a keyword
1483 l = getargs(x, 1, 2, _("last requires one or two arguments")) 1489 l = getargs(x, 1, 2, _(b"last requires one or two arguments"))
1484 lim = 1 1490 lim = 1
1485 if len(l) == 2: 1491 if len(l) == 2:
1486 # i18n: "last" is a keyword 1492 # i18n: "last" is a keyword
1487 lim = getinteger(l[1], _("last expects a number")) 1493 lim = getinteger(l[1], _(b"last expects a number"))
1488 if lim < 0: 1494 if lim < 0:
1489 raise error.ParseError(_("negative number to select")) 1495 raise error.ParseError(_(b"negative number to select"))
1490 os = getset(repo, fullreposet(repo), l[0]) 1496 os = getset(repo, fullreposet(repo), l[0])
1491 os.reverse() 1497 os.reverse()
1492 ls = os.slice(0, lim) 1498 ls = os.slice(0, lim)
1493 if order == followorder and lim > 1: 1499 if order == followorder and lim > 1:
1494 return subset & ls 1500 return subset & ls
1495 ls.reverse() 1501 ls.reverse()
1496 return ls & subset 1502 return ls & subset
1497 1503
1498 1504
1499 @predicate('max(set)', safe=True) 1505 @predicate(b'max(set)', safe=True)
1500 def maxrev(repo, subset, x): 1506 def maxrev(repo, subset, x):
1501 """Changeset with highest revision number in set. 1507 """Changeset with highest revision number in set.
1502 """ 1508 """
1503 os = getset(repo, fullreposet(repo), x) 1509 os = getset(repo, fullreposet(repo), x)
1504 try: 1510 try:
1505 m = os.max() 1511 m = os.max()
1506 if m in subset: 1512 if m in subset:
1507 return baseset([m], datarepr=('<max %r, %r>', subset, os)) 1513 return baseset([m], datarepr=(b'<max %r, %r>', subset, os))
1508 except ValueError: 1514 except ValueError:
1509 # os.max() throws a ValueError when the collection is empty. 1515 # os.max() throws a ValueError when the collection is empty.
1510 # Same as python's max(). 1516 # Same as python's max().
1511 pass 1517 pass
1512 return baseset(datarepr=('<max %r, %r>', subset, os)) 1518 return baseset(datarepr=(b'<max %r, %r>', subset, os))
1513 1519
1514 1520
1515 @predicate('merge()', safe=True) 1521 @predicate(b'merge()', safe=True)
1516 def merge(repo, subset, x): 1522 def merge(repo, subset, x):
1517 """Changeset is a merge changeset. 1523 """Changeset is a merge changeset.
1518 """ 1524 """
1519 # i18n: "merge" is a keyword 1525 # i18n: "merge" is a keyword
1520 getargs(x, 0, 0, _("merge takes no arguments")) 1526 getargs(x, 0, 0, _(b"merge takes no arguments"))
1521 cl = repo.changelog 1527 cl = repo.changelog
1522 nullrev = node.nullrev 1528 nullrev = node.nullrev
1523 1529
1524 def ismerge(r): 1530 def ismerge(r):
1525 try: 1531 try:
1526 return cl.parentrevs(r)[1] != nullrev 1532 return cl.parentrevs(r)[1] != nullrev
1527 except error.WdirUnsupported: 1533 except error.WdirUnsupported:
1528 return bool(repo[r].p2()) 1534 return bool(repo[r].p2())
1529 1535
1530 return subset.filter(ismerge, condrepr='<merge>') 1536 return subset.filter(ismerge, condrepr=b'<merge>')
1531 1537
1532 1538
1533 @predicate('branchpoint()', safe=True) 1539 @predicate(b'branchpoint()', safe=True)
1534 def branchpoint(repo, subset, x): 1540 def branchpoint(repo, subset, x):
1535 """Changesets with more than one child. 1541 """Changesets with more than one child.
1536 """ 1542 """
1537 # i18n: "branchpoint" is a keyword 1543 # i18n: "branchpoint" is a keyword
1538 getargs(x, 0, 0, _("branchpoint takes no arguments")) 1544 getargs(x, 0, 0, _(b"branchpoint takes no arguments"))
1539 cl = repo.changelog 1545 cl = repo.changelog
1540 if not subset: 1546 if not subset:
1541 return baseset() 1547 return baseset()
1542 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset 1548 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1543 # (and if it is not, it should.) 1549 # (and if it is not, it should.)
1546 for r in cl.revs(start=baserev + 1): 1552 for r in cl.revs(start=baserev + 1):
1547 for p in cl.parentrevs(r): 1553 for p in cl.parentrevs(r):
1548 if p >= baserev: 1554 if p >= baserev:
1549 parentscount[p - baserev] += 1 1555 parentscount[p - baserev] += 1
1550 return subset.filter( 1556 return subset.filter(
1551 lambda r: parentscount[r - baserev] > 1, condrepr='<branchpoint>' 1557 lambda r: parentscount[r - baserev] > 1, condrepr=b'<branchpoint>'
1552 ) 1558 )
1553 1559
1554 1560
1555 @predicate('min(set)', safe=True) 1561 @predicate(b'min(set)', safe=True)
1556 def minrev(repo, subset, x): 1562 def minrev(repo, subset, x):
1557 """Changeset with lowest revision number in set. 1563 """Changeset with lowest revision number in set.
1558 """ 1564 """
1559 os = getset(repo, fullreposet(repo), x) 1565 os = getset(repo, fullreposet(repo), x)
1560 try: 1566 try:
1561 m = os.min() 1567 m = os.min()
1562 if m in subset: 1568 if m in subset:
1563 return baseset([m], datarepr=('<min %r, %r>', subset, os)) 1569 return baseset([m], datarepr=(b'<min %r, %r>', subset, os))
1564 except ValueError: 1570 except ValueError:
1565 # os.min() throws a ValueError when the collection is empty. 1571 # os.min() throws a ValueError when the collection is empty.
1566 # Same as python's min(). 1572 # Same as python's min().
1567 pass 1573 pass
1568 return baseset(datarepr=('<min %r, %r>', subset, os)) 1574 return baseset(datarepr=(b'<min %r, %r>', subset, os))
1569 1575
1570 1576
1571 @predicate('modifies(pattern)', safe=True, weight=30) 1577 @predicate(b'modifies(pattern)', safe=True, weight=30)
1572 def modifies(repo, subset, x): 1578 def modifies(repo, subset, x):
1573 """Changesets modifying files matched by pattern. 1579 """Changesets modifying files matched by pattern.
1574 1580
1575 The pattern without explicit kind like ``glob:`` is expected to be 1581 The pattern without explicit kind like ``glob:`` is expected to be
1576 relative to the current directory and match against a file or a 1582 relative to the current directory and match against a file or a
1577 directory. 1583 directory.
1578 """ 1584 """
1579 # i18n: "modifies" is a keyword 1585 # i18n: "modifies" is a keyword
1580 pat = getstring(x, _("modifies requires a pattern")) 1586 pat = getstring(x, _(b"modifies requires a pattern"))
1581 return checkstatus(repo, subset, pat, 0) 1587 return checkstatus(repo, subset, pat, 0)
1582 1588
1583 1589
1584 @predicate('named(namespace)') 1590 @predicate(b'named(namespace)')
1585 def named(repo, subset, x): 1591 def named(repo, subset, x):
1586 """The changesets in a given namespace. 1592 """The changesets in a given namespace.
1587 1593
1588 Pattern matching is supported for `namespace`. See 1594 Pattern matching is supported for `namespace`. See
1589 :hg:`help revisions.patterns`. 1595 :hg:`help revisions.patterns`.
1590 """ 1596 """
1591 # i18n: "named" is a keyword 1597 # i18n: "named" is a keyword
1592 args = getargs(x, 1, 1, _('named requires a namespace argument')) 1598 args = getargs(x, 1, 1, _(b'named requires a namespace argument'))
1593 1599
1594 ns = getstring( 1600 ns = getstring(
1595 args[0], 1601 args[0],
1596 # i18n: "named" is a keyword 1602 # i18n: "named" is a keyword
1597 _('the argument to named must be a string'), 1603 _(b'the argument to named must be a string'),
1598 ) 1604 )
1599 kind, pattern, matcher = stringutil.stringmatcher(ns) 1605 kind, pattern, matcher = stringutil.stringmatcher(ns)
1600 namespaces = set() 1606 namespaces = set()
1601 if kind == 'literal': 1607 if kind == b'literal':
1602 if pattern not in repo.names: 1608 if pattern not in repo.names:
1603 raise error.RepoLookupError(_("namespace '%s' does not exist") % ns) 1609 raise error.RepoLookupError(
1610 _(b"namespace '%s' does not exist") % ns
1611 )
1604 namespaces.add(repo.names[pattern]) 1612 namespaces.add(repo.names[pattern])
1605 else: 1613 else:
1606 for name, ns in repo.names.iteritems(): 1614 for name, ns in repo.names.iteritems():
1607 if matcher(name): 1615 if matcher(name):
1608 namespaces.add(ns) 1616 namespaces.add(ns)
1615 1623
1616 names -= {node.nullrev} 1624 names -= {node.nullrev}
1617 return subset & names 1625 return subset & names
1618 1626
1619 1627
1620 @predicate('id(string)', safe=True) 1628 @predicate(b'id(string)', safe=True)
1621 def node_(repo, subset, x): 1629 def node_(repo, subset, x):
1622 """Revision non-ambiguously specified by the given hex string prefix. 1630 """Revision non-ambiguously specified by the given hex string prefix.
1623 """ 1631 """
1624 # i18n: "id" is a keyword 1632 # i18n: "id" is a keyword
1625 l = getargs(x, 1, 1, _("id requires one argument")) 1633 l = getargs(x, 1, 1, _(b"id requires one argument"))
1626 # i18n: "id" is a keyword 1634 # i18n: "id" is a keyword
1627 n = getstring(l[0], _("id requires a string")) 1635 n = getstring(l[0], _(b"id requires a string"))
1628 if len(n) == 40: 1636 if len(n) == 40:
1629 try: 1637 try:
1630 rn = repo.changelog.rev(node.bin(n)) 1638 rn = repo.changelog.rev(node.bin(n))
1631 except error.WdirUnsupported: 1639 except error.WdirUnsupported:
1632 rn = node.wdirrev 1640 rn = node.wdirrev
1647 return baseset() 1655 return baseset()
1648 result = baseset([rn]) 1656 result = baseset([rn])
1649 return result & subset 1657 return result & subset
1650 1658
1651 1659
1652 @predicate('none()', safe=True) 1660 @predicate(b'none()', safe=True)
1653 def none(repo, subset, x): 1661 def none(repo, subset, x):
1654 """No changesets. 1662 """No changesets.
1655 """ 1663 """
1656 # i18n: "none" is a keyword 1664 # i18n: "none" is a keyword
1657 getargs(x, 0, 0, _("none takes no arguments")) 1665 getargs(x, 0, 0, _(b"none takes no arguments"))
1658 return baseset() 1666 return baseset()
1659 1667
1660 1668
1661 @predicate('obsolete()', safe=True) 1669 @predicate(b'obsolete()', safe=True)
1662 def obsolete(repo, subset, x): 1670 def obsolete(repo, subset, x):
1663 """Mutable changeset with a newer version.""" 1671 """Mutable changeset with a newer version."""
1664 # i18n: "obsolete" is a keyword 1672 # i18n: "obsolete" is a keyword
1665 getargs(x, 0, 0, _("obsolete takes no arguments")) 1673 getargs(x, 0, 0, _(b"obsolete takes no arguments"))
1666 obsoletes = obsmod.getrevs(repo, 'obsolete') 1674 obsoletes = obsmod.getrevs(repo, b'obsolete')
1667 return subset & obsoletes 1675 return subset & obsoletes
1668 1676
1669 1677
1670 @predicate('only(set, [set])', safe=True) 1678 @predicate(b'only(set, [set])', safe=True)
1671 def only(repo, subset, x): 1679 def only(repo, subset, x):
1672 """Changesets that are ancestors of the first set that are not ancestors 1680 """Changesets that are ancestors of the first set that are not ancestors
1673 of any other head in the repo. If a second set is specified, the result 1681 of any other head in the repo. If a second set is specified, the result
1674 is ancestors of the first set that are not ancestors of the second set 1682 is ancestors of the first set that are not ancestors of the second set
1675 (i.e. ::<set1> - ::<set2>). 1683 (i.e. ::<set1> - ::<set2>).
1676 """ 1684 """
1677 cl = repo.changelog 1685 cl = repo.changelog
1678 # i18n: "only" is a keyword 1686 # i18n: "only" is a keyword
1679 args = getargs(x, 1, 2, _('only takes one or two arguments')) 1687 args = getargs(x, 1, 2, _(b'only takes one or two arguments'))
1680 include = getset(repo, fullreposet(repo), args[0]) 1688 include = getset(repo, fullreposet(repo), args[0])
1681 if len(args) == 1: 1689 if len(args) == 1:
1682 if not include: 1690 if not include:
1683 return baseset() 1691 return baseset()
1684 1692
1695 # XXX we should turn this into a baseset instead of a set, smartset may do 1703 # XXX we should turn this into a baseset instead of a set, smartset may do
1696 # some optimizations from the fact this is a baseset. 1704 # some optimizations from the fact this is a baseset.
1697 return subset & results 1705 return subset & results
1698 1706
1699 1707
1700 @predicate('origin([set])', safe=True) 1708 @predicate(b'origin([set])', safe=True)
1701 def origin(repo, subset, x): 1709 def origin(repo, subset, x):
1702 """ 1710 """
1703 Changesets that were specified as a source for the grafts, transplants or 1711 Changesets that were specified as a source for the grafts, transplants or
1704 rebases that created the given revisions. Omitting the optional set is the 1712 rebases that created the given revisions. Omitting the optional set is the
1705 same as passing all(). If a changeset created by these operations is itself 1713 same as passing all(). If a changeset created by these operations is itself
1728 # XXX we should turn this into a baseset instead of a set, smartset may do 1736 # XXX we should turn this into a baseset instead of a set, smartset may do
1729 # some optimizations from the fact this is a baseset. 1737 # some optimizations from the fact this is a baseset.
1730 return subset & o 1738 return subset & o
1731 1739
1732 1740
1733 @predicate('outgoing([path])', safe=False, weight=10) 1741 @predicate(b'outgoing([path])', safe=False, weight=10)
1734 def outgoing(repo, subset, x): 1742 def outgoing(repo, subset, x):
1735 """Changesets not found in the specified destination repository, or the 1743 """Changesets not found in the specified destination repository, or the
1736 default push location. 1744 default push location.
1737 """ 1745 """
1738 # Avoid cycles. 1746 # Avoid cycles.
1740 discovery, 1748 discovery,
1741 hg, 1749 hg,
1742 ) 1750 )
1743 1751
1744 # i18n: "outgoing" is a keyword 1752 # i18n: "outgoing" is a keyword
1745 l = getargs(x, 0, 1, _("outgoing takes one or no arguments")) 1753 l = getargs(x, 0, 1, _(b"outgoing takes one or no arguments"))
1746 # i18n: "outgoing" is a keyword 1754 # i18n: "outgoing" is a keyword
1747 dest = l and getstring(l[0], _("outgoing requires a repository path")) or '' 1755 dest = (
1756 l and getstring(l[0], _(b"outgoing requires a repository path")) or b''
1757 )
1748 if not dest: 1758 if not dest:
1749 # ui.paths.getpath() explicitly tests for None, not just a boolean 1759 # ui.paths.getpath() explicitly tests for None, not just a boolean
1750 dest = None 1760 dest = None
1751 path = repo.ui.paths.getpath(dest, default=('default-push', 'default')) 1761 path = repo.ui.paths.getpath(dest, default=(b'default-push', b'default'))
1752 if not path: 1762 if not path:
1753 raise error.Abort( 1763 raise error.Abort(
1754 _('default repository not configured!'), 1764 _(b'default repository not configured!'),
1755 hint=_("see 'hg help config.paths'"), 1765 hint=_(b"see 'hg help config.paths'"),
1756 ) 1766 )
1757 dest = path.pushloc or path.loc 1767 dest = path.pushloc or path.loc
1758 branches = path.branch, [] 1768 branches = path.branch, []
1759 1769
1760 revs, checkout = hg.addbranchrevs(repo, repo, branches, []) 1770 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1767 cl = repo.changelog 1777 cl = repo.changelog
1768 o = {cl.rev(r) for r in outgoing.missing} 1778 o = {cl.rev(r) for r in outgoing.missing}
1769 return subset & o 1779 return subset & o
1770 1780
1771 1781
1772 @predicate('p1([set])', safe=True) 1782 @predicate(b'p1([set])', safe=True)
1773 def p1(repo, subset, x): 1783 def p1(repo, subset, x):
1774 """First parent of changesets in set, or the working directory. 1784 """First parent of changesets in set, or the working directory.
1775 """ 1785 """
1776 if x is None: 1786 if x is None:
1777 p = repo[x].p1().rev() 1787 p = repo[x].p1().rev()
1790 # XXX we should turn this into a baseset instead of a set, smartset may do 1800 # XXX we should turn this into a baseset instead of a set, smartset may do
1791 # some optimizations from the fact this is a baseset. 1801 # some optimizations from the fact this is a baseset.
1792 return subset & ps 1802 return subset & ps
1793 1803
1794 1804
1795 @predicate('p2([set])', safe=True) 1805 @predicate(b'p2([set])', safe=True)
1796 def p2(repo, subset, x): 1806 def p2(repo, subset, x):
1797 """Second parent of changesets in set, or the working directory. 1807 """Second parent of changesets in set, or the working directory.
1798 """ 1808 """
1799 if x is None: 1809 if x is None:
1800 ps = repo[x].parents() 1810 ps = repo[x].parents()
1823 1833
1824 def parentpost(repo, subset, x, order): 1834 def parentpost(repo, subset, x, order):
1825 return p1(repo, subset, x) 1835 return p1(repo, subset, x)
1826 1836
1827 1837
1828 @predicate('parents([set])', safe=True) 1838 @predicate(b'parents([set])', safe=True)
1829 def parents(repo, subset, x): 1839 def parents(repo, subset, x):
1830 """ 1840 """
1831 The set of all parents for all changesets in set, or the working directory. 1841 The set of all parents for all changesets in set, or the working directory.
1832 """ 1842 """
1833 if x is None: 1843 if x is None:
1849 def _phase(repo, subset, *targets): 1859 def _phase(repo, subset, *targets):
1850 """helper to select all rev in <targets> phases""" 1860 """helper to select all rev in <targets> phases"""
1851 return repo._phasecache.getrevset(repo, targets, subset) 1861 return repo._phasecache.getrevset(repo, targets, subset)
1852 1862
1853 1863
1854 @predicate('_phase(idx)', safe=True) 1864 @predicate(b'_phase(idx)', safe=True)
1855 def phase(repo, subset, x): 1865 def phase(repo, subset, x):
1856 l = getargs(x, 1, 1, "_phase requires one argument") 1866 l = getargs(x, 1, 1, b"_phase requires one argument")
1857 target = getinteger(l[0], "_phase expects a number") 1867 target = getinteger(l[0], b"_phase expects a number")
1858 return _phase(repo, subset, target) 1868 return _phase(repo, subset, target)
1859 1869
1860 1870
1861 @predicate('draft()', safe=True) 1871 @predicate(b'draft()', safe=True)
1862 def draft(repo, subset, x): 1872 def draft(repo, subset, x):
1863 """Changeset in draft phase.""" 1873 """Changeset in draft phase."""
1864 # i18n: "draft" is a keyword 1874 # i18n: "draft" is a keyword
1865 getargs(x, 0, 0, _("draft takes no arguments")) 1875 getargs(x, 0, 0, _(b"draft takes no arguments"))
1866 target = phases.draft 1876 target = phases.draft
1867 return _phase(repo, subset, target) 1877 return _phase(repo, subset, target)
1868 1878
1869 1879
1870 @predicate('secret()', safe=True) 1880 @predicate(b'secret()', safe=True)
1871 def secret(repo, subset, x): 1881 def secret(repo, subset, x):
1872 """Changeset in secret phase.""" 1882 """Changeset in secret phase."""
1873 # i18n: "secret" is a keyword 1883 # i18n: "secret" is a keyword
1874 getargs(x, 0, 0, _("secret takes no arguments")) 1884 getargs(x, 0, 0, _(b"secret takes no arguments"))
1875 target = phases.secret 1885 target = phases.secret
1876 return _phase(repo, subset, target) 1886 return _phase(repo, subset, target)
1877 1887
1878 1888
1879 @predicate('stack([revs])', safe=True) 1889 @predicate(b'stack([revs])', safe=True)
1880 def stack(repo, subset, x): 1890 def stack(repo, subset, x):
1881 """Experimental revset for the stack of changesets or working directory 1891 """Experimental revset for the stack of changesets or working directory
1882 parent. (EXPERIMENTAL) 1892 parent. (EXPERIMENTAL)
1883 """ 1893 """
1884 if x is None: 1894 if x is None:
1901 try: 1911 try:
1902 n = int(n[1]) 1912 n = int(n[1])
1903 if n not in (0, 1, 2): 1913 if n not in (0, 1, 2):
1904 raise ValueError 1914 raise ValueError
1905 except (TypeError, ValueError): 1915 except (TypeError, ValueError):
1906 raise error.ParseError(_("^ expects a number 0, 1, or 2")) 1916 raise error.ParseError(_(b"^ expects a number 0, 1, or 2"))
1907 ps = set() 1917 ps = set()
1908 cl = repo.changelog 1918 cl = repo.changelog
1909 for r in getset(repo, fullreposet(repo), x): 1919 for r in getset(repo, fullreposet(repo), x):
1910 if n == 0: 1920 if n == 0:
1911 ps.add(r) 1921 ps.add(r)
1924 if len(parents) == 2: 1934 if len(parents) == 2:
1925 ps.add(parents[1].rev()) 1935 ps.add(parents[1].rev())
1926 return subset & ps 1936 return subset & ps
1927 1937
1928 1938
1929 @predicate('present(set)', safe=True, takeorder=True) 1939 @predicate(b'present(set)', safe=True, takeorder=True)
1930 def present(repo, subset, x, order): 1940 def present(repo, subset, x, order):
1931 """An empty set, if any revision in set isn't found; otherwise, 1941 """An empty set, if any revision in set isn't found; otherwise,
1932 all revisions in set. 1942 all revisions in set.
1933 1943
1934 If any of specified revisions is not present in the local repository, 1944 If any of specified revisions is not present in the local repository,
1940 except error.RepoLookupError: 1950 except error.RepoLookupError:
1941 return baseset() 1951 return baseset()
1942 1952
1943 1953
1944 # for internal use 1954 # for internal use
1945 @predicate('_notpublic', safe=True) 1955 @predicate(b'_notpublic', safe=True)
1946 def _notpublic(repo, subset, x): 1956 def _notpublic(repo, subset, x):
1947 getargs(x, 0, 0, "_notpublic takes no arguments") 1957 getargs(x, 0, 0, b"_notpublic takes no arguments")
1948 return _phase(repo, subset, phases.draft, phases.secret) 1958 return _phase(repo, subset, phases.draft, phases.secret)
1949 1959
1950 1960
1951 # for internal use 1961 # for internal use
1952 @predicate('_phaseandancestors(phasename, set)', safe=True) 1962 @predicate(b'_phaseandancestors(phasename, set)', safe=True)
1953 def _phaseandancestors(repo, subset, x): 1963 def _phaseandancestors(repo, subset, x):
1954 # equivalent to (phasename() & ancestors(set)) but more efficient 1964 # equivalent to (phasename() & ancestors(set)) but more efficient
1955 # phasename could be one of 'draft', 'secret', or '_notpublic' 1965 # phasename could be one of 'draft', 'secret', or '_notpublic'
1956 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments") 1966 args = getargs(x, 2, 2, b"_phaseandancestors requires two arguments")
1957 phasename = getsymbol(args[0]) 1967 phasename = getsymbol(args[0])
1958 s = getset(repo, fullreposet(repo), args[1]) 1968 s = getset(repo, fullreposet(repo), args[1])
1959 1969
1960 draft = phases.draft 1970 draft = phases.draft
1961 secret = phases.secret 1971 secret = phases.secret
1962 phasenamemap = { 1972 phasenamemap = {
1963 '_notpublic': draft, 1973 b'_notpublic': draft,
1964 'draft': draft, # follow secret's ancestors 1974 b'draft': draft, # follow secret's ancestors
1965 'secret': secret, 1975 b'secret': secret,
1966 } 1976 }
1967 if phasename not in phasenamemap: 1977 if phasename not in phasenamemap:
1968 raise error.ParseError('%r is not a valid phasename' % phasename) 1978 raise error.ParseError(b'%r is not a valid phasename' % phasename)
1969 1979
1970 minimalphase = phasenamemap[phasename] 1980 minimalphase = phasenamemap[phasename]
1971 getphase = repo._phasecache.phase 1981 getphase = repo._phasecache.phase
1972 1982
1973 def cutfunc(rev): 1983 def cutfunc(rev):
1974 return getphase(repo, rev) < minimalphase 1984 return getphase(repo, rev) < minimalphase
1975 1985
1976 revs = dagop.revancestors(repo, s, cutfunc=cutfunc) 1986 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1977 1987
1978 if phasename == 'draft': # need to remove secret changesets 1988 if phasename == b'draft': # need to remove secret changesets
1979 revs = revs.filter(lambda r: getphase(repo, r) == draft) 1989 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1980 return subset & revs 1990 return subset & revs
1981 1991
1982 1992
1983 @predicate('public()', safe=True) 1993 @predicate(b'public()', safe=True)
1984 def public(repo, subset, x): 1994 def public(repo, subset, x):
1985 """Changeset in public phase.""" 1995 """Changeset in public phase."""
1986 # i18n: "public" is a keyword 1996 # i18n: "public" is a keyword
1987 getargs(x, 0, 0, _("public takes no arguments")) 1997 getargs(x, 0, 0, _(b"public takes no arguments"))
1988 return _phase(repo, subset, phases.public) 1998 return _phase(repo, subset, phases.public)
1989 1999
1990 2000
1991 @predicate('remote([id [,path]])', safe=False) 2001 @predicate(b'remote([id [,path]])', safe=False)
1992 def remote(repo, subset, x): 2002 def remote(repo, subset, x):
1993 """Local revision that corresponds to the given identifier in a 2003 """Local revision that corresponds to the given identifier in a
1994 remote repository, if present. Here, the '.' identifier is a 2004 remote repository, if present. Here, the '.' identifier is a
1995 synonym for the current local branch. 2005 synonym for the current local branch.
1996 """ 2006 """
1997 2007
1998 from . import hg # avoid start-up nasties 2008 from . import hg # avoid start-up nasties
1999 2009
2000 # i18n: "remote" is a keyword 2010 # i18n: "remote" is a keyword
2001 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments")) 2011 l = getargs(x, 0, 2, _(b"remote takes zero, one, or two arguments"))
2002 2012
2003 q = '.' 2013 q = b'.'
2004 if len(l) > 0: 2014 if len(l) > 0:
2005 # i18n: "remote" is a keyword 2015 # i18n: "remote" is a keyword
2006 q = getstring(l[0], _("remote requires a string id")) 2016 q = getstring(l[0], _(b"remote requires a string id"))
2007 if q == '.': 2017 if q == b'.':
2008 q = repo['.'].branch() 2018 q = repo[b'.'].branch()
2009 2019
2010 dest = '' 2020 dest = b''
2011 if len(l) > 1: 2021 if len(l) > 1:
2012 # i18n: "remote" is a keyword 2022 # i18n: "remote" is a keyword
2013 dest = getstring(l[1], _("remote requires a repository path")) 2023 dest = getstring(l[1], _(b"remote requires a repository path"))
2014 dest = repo.ui.expandpath(dest or 'default') 2024 dest = repo.ui.expandpath(dest or b'default')
2015 dest, branches = hg.parseurl(dest) 2025 dest, branches = hg.parseurl(dest)
2016 revs, checkout = hg.addbranchrevs(repo, repo, branches, []) 2026 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
2017 if revs: 2027 if revs:
2018 revs = [repo.lookup(rev) for rev in revs] 2028 revs = [repo.lookup(rev) for rev in revs]
2019 other = hg.peer(repo, {}, dest) 2029 other = hg.peer(repo, {}, dest)
2023 if r in subset: 2033 if r in subset:
2024 return baseset([r]) 2034 return baseset([r])
2025 return baseset() 2035 return baseset()
2026 2036
2027 2037
2028 @predicate('removes(pattern)', safe=True, weight=30) 2038 @predicate(b'removes(pattern)', safe=True, weight=30)
2029 def removes(repo, subset, x): 2039 def removes(repo, subset, x):
2030 """Changesets which remove files matching pattern. 2040 """Changesets which remove files matching pattern.
2031 2041
2032 The pattern without explicit kind like ``glob:`` is expected to be 2042 The pattern without explicit kind like ``glob:`` is expected to be
2033 relative to the current directory and match against a file or a 2043 relative to the current directory and match against a file or a
2034 directory. 2044 directory.
2035 """ 2045 """
2036 # i18n: "removes" is a keyword 2046 # i18n: "removes" is a keyword
2037 pat = getstring(x, _("removes requires a pattern")) 2047 pat = getstring(x, _(b"removes requires a pattern"))
2038 return checkstatus(repo, subset, pat, 2) 2048 return checkstatus(repo, subset, pat, 2)
2039 2049
2040 2050
2041 @predicate('rev(number)', safe=True) 2051 @predicate(b'rev(number)', safe=True)
2042 def rev(repo, subset, x): 2052 def rev(repo, subset, x):
2043 """Revision with the given numeric identifier. 2053 """Revision with the given numeric identifier.
2044 """ 2054 """
2045 # i18n: "rev" is a keyword 2055 # i18n: "rev" is a keyword
2046 l = getargs(x, 1, 1, _("rev requires one argument")) 2056 l = getargs(x, 1, 1, _(b"rev requires one argument"))
2047 try: 2057 try:
2048 # i18n: "rev" is a keyword 2058 # i18n: "rev" is a keyword
2049 l = int(getstring(l[0], _("rev requires a number"))) 2059 l = int(getstring(l[0], _(b"rev requires a number")))
2050 except (TypeError, ValueError): 2060 except (TypeError, ValueError):
2051 # i18n: "rev" is a keyword 2061 # i18n: "rev" is a keyword
2052 raise error.ParseError(_("rev expects a number")) 2062 raise error.ParseError(_(b"rev expects a number"))
2053 if l not in repo.changelog and l not in _virtualrevs: 2063 if l not in repo.changelog and l not in _virtualrevs:
2054 return baseset() 2064 return baseset()
2055 return subset & baseset([l]) 2065 return subset & baseset([l])
2056 2066
2057 2067
2058 @predicate('_rev(number)', safe=True) 2068 @predicate(b'_rev(number)', safe=True)
2059 def _rev(repo, subset, x): 2069 def _rev(repo, subset, x):
2060 # internal version of "rev(x)" that raise error if "x" is invalid 2070 # internal version of "rev(x)" that raise error if "x" is invalid
2061 # i18n: "rev" is a keyword 2071 # i18n: "rev" is a keyword
2062 l = getargs(x, 1, 1, _("rev requires one argument")) 2072 l = getargs(x, 1, 1, _(b"rev requires one argument"))
2063 try: 2073 try:
2064 # i18n: "rev" is a keyword 2074 # i18n: "rev" is a keyword
2065 l = int(getstring(l[0], _("rev requires a number"))) 2075 l = int(getstring(l[0], _(b"rev requires a number")))
2066 except (TypeError, ValueError): 2076 except (TypeError, ValueError):
2067 # i18n: "rev" is a keyword 2077 # i18n: "rev" is a keyword
2068 raise error.ParseError(_("rev expects a number")) 2078 raise error.ParseError(_(b"rev expects a number"))
2069 repo.changelog.node(l) # check that the rev exists 2079 repo.changelog.node(l) # check that the rev exists
2070 return subset & baseset([l]) 2080 return subset & baseset([l])
2071 2081
2072 2082
2073 @predicate('revset(set)', safe=True, takeorder=True) 2083 @predicate(b'revset(set)', safe=True, takeorder=True)
2074 def revsetpredicate(repo, subset, x, order): 2084 def revsetpredicate(repo, subset, x, order):
2075 """Strictly interpret the content as a revset. 2085 """Strictly interpret the content as a revset.
2076 2086
2077 The content of this special predicate will be strictly interpreted as a 2087 The content of this special predicate will be strictly interpreted as a
2078 revset. For example, ``revset(id(0))`` will be interpreted as "id(0)" 2088 revset. For example, ``revset(id(0))`` will be interpreted as "id(0)"
2079 without possible ambiguity with a "id(0)" bookmark or tag. 2089 without possible ambiguity with a "id(0)" bookmark or tag.
2080 """ 2090 """
2081 return getset(repo, subset, x, order) 2091 return getset(repo, subset, x, order)
2082 2092
2083 2093
2084 @predicate('matching(revision [, field])', safe=True) 2094 @predicate(b'matching(revision [, field])', safe=True)
2085 def matching(repo, subset, x): 2095 def matching(repo, subset, x):
2086 """Changesets in which a given set of fields match the set of fields in the 2096 """Changesets in which a given set of fields match the set of fields in the
2087 selected revision or set. 2097 selected revision or set.
2088 2098
2089 To match more than one field pass the list of fields to match separated 2099 To match more than one field pass the list of fields to match separated
2105 2115
2106 ``metadata`` is the default field which is used when no fields are 2116 ``metadata`` is the default field which is used when no fields are
2107 specified. You can match more than one field at a time. 2117 specified. You can match more than one field at a time.
2108 """ 2118 """
2109 # i18n: "matching" is a keyword 2119 # i18n: "matching" is a keyword
2110 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments")) 2120 l = getargs(x, 1, 2, _(b"matching takes 1 or 2 arguments"))
2111 2121
2112 revs = getset(repo, fullreposet(repo), l[0]) 2122 revs = getset(repo, fullreposet(repo), l[0])
2113 2123
2114 fieldlist = ['metadata'] 2124 fieldlist = [b'metadata']
2115 if len(l) > 1: 2125 if len(l) > 1:
2116 fieldlist = getstring( 2126 fieldlist = getstring(
2117 l[1], 2127 l[1],
2118 # i18n: "matching" is a keyword 2128 # i18n: "matching" is a keyword
2119 _("matching requires a string " "as its second argument"), 2129 _(b"matching requires a string " b"as its second argument"),
2120 ).split() 2130 ).split()
2121 2131
2122 # Make sure that there are no repeated fields, 2132 # Make sure that there are no repeated fields,
2123 # expand the 'special' 'metadata' field type 2133 # expand the 'special' 'metadata' field type
2124 # and check the 'files' whenever we check the 'diff' 2134 # and check the 'files' whenever we check the 'diff'
2125 fields = [] 2135 fields = []
2126 for field in fieldlist: 2136 for field in fieldlist:
2127 if field == 'metadata': 2137 if field == b'metadata':
2128 fields += ['user', 'description', 'date'] 2138 fields += [b'user', b'description', b'date']
2129 elif field == 'diff': 2139 elif field == b'diff':
2130 # a revision matching the diff must also match the files 2140 # a revision matching the diff must also match the files
2131 # since matching the diff is very costly, make sure to 2141 # since matching the diff is very costly, make sure to
2132 # also match the files first 2142 # also match the files first
2133 fields += ['files', 'diff'] 2143 fields += [b'files', b'diff']
2134 else: 2144 else:
2135 if field == 'author': 2145 if field == b'author':
2136 field = 'user' 2146 field = b'user'
2137 fields.append(field) 2147 fields.append(field)
2138 fields = set(fields) 2148 fields = set(fields)
2139 if 'summary' in fields and 'description' in fields: 2149 if b'summary' in fields and b'description' in fields:
2140 # If a revision matches its description it also matches its summary 2150 # If a revision matches its description it also matches its summary
2141 fields.discard('summary') 2151 fields.discard(b'summary')
2142 2152
2143 # We may want to match more than one field 2153 # We may want to match more than one field
2144 # Not all fields take the same amount of time to be matched 2154 # Not all fields take the same amount of time to be matched
2145 # Sort the selected fields in order of increasing matching cost 2155 # Sort the selected fields in order of increasing matching cost
2146 fieldorder = [ 2156 fieldorder = [
2147 'phase', 2157 b'phase',
2148 'parents', 2158 b'parents',
2149 'user', 2159 b'user',
2150 'date', 2160 b'date',
2151 'branch', 2161 b'branch',
2152 'summary', 2162 b'summary',
2153 'files', 2163 b'files',
2154 'description', 2164 b'description',
2155 'substate', 2165 b'substate',
2156 'diff', 2166 b'diff',
2157 ] 2167 ]
2158 2168
2159 def fieldkeyfunc(f): 2169 def fieldkeyfunc(f):
2160 try: 2170 try:
2161 return fieldorder.index(f) 2171 return fieldorder.index(f)
2168 2178
2169 # Each field will be matched with its own "getfield" function 2179 # Each field will be matched with its own "getfield" function
2170 # which will be added to the getfieldfuncs array of functions 2180 # which will be added to the getfieldfuncs array of functions
2171 getfieldfuncs = [] 2181 getfieldfuncs = []
2172 _funcs = { 2182 _funcs = {
2173 'user': lambda r: repo[r].user(), 2183 b'user': lambda r: repo[r].user(),
2174 'branch': lambda r: repo[r].branch(), 2184 b'branch': lambda r: repo[r].branch(),
2175 'date': lambda r: repo[r].date(), 2185 b'date': lambda r: repo[r].date(),
2176 'description': lambda r: repo[r].description(), 2186 b'description': lambda r: repo[r].description(),
2177 'files': lambda r: repo[r].files(), 2187 b'files': lambda r: repo[r].files(),
2178 'parents': lambda r: repo[r].parents(), 2188 b'parents': lambda r: repo[r].parents(),
2179 'phase': lambda r: repo[r].phase(), 2189 b'phase': lambda r: repo[r].phase(),
2180 'substate': lambda r: repo[r].substate, 2190 b'substate': lambda r: repo[r].substate,
2181 'summary': lambda r: repo[r].description().splitlines()[0], 2191 b'summary': lambda r: repo[r].description().splitlines()[0],
2182 'diff': lambda r: list( 2192 b'diff': lambda r: list(
2183 repo[r].diff(opts=diffutil.diffallopts(repo.ui, {'git': True})) 2193 repo[r].diff(opts=diffutil.diffallopts(repo.ui, {b'git': True}))
2184 ), 2194 ),
2185 } 2195 }
2186 for info in fields: 2196 for info in fields:
2187 getfield = _funcs.get(info, None) 2197 getfield = _funcs.get(info, None)
2188 if getfield is None: 2198 if getfield is None:
2189 raise error.ParseError( 2199 raise error.ParseError(
2190 # i18n: "matching" is a keyword 2200 # i18n: "matching" is a keyword
2191 _("unexpected field name passed to matching: %s") 2201 _(b"unexpected field name passed to matching: %s")
2192 % info 2202 % info
2193 ) 2203 )
2194 getfieldfuncs.append(getfield) 2204 getfieldfuncs.append(getfield)
2195 # convert the getfield array of functions into a "getinfo" function 2205 # convert the getfield array of functions into a "getinfo" function
2196 # which returns an array of field values (or a single value if there 2206 # which returns an array of field values (or a single value if there
2206 match = False 2216 match = False
2207 if match: 2217 if match:
2208 return True 2218 return True
2209 return False 2219 return False
2210 2220
2211 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs)) 2221 return subset.filter(matches, condrepr=(b'<matching%r %r>', fields, revs))
2212 2222
2213 2223
2214 @predicate('reverse(set)', safe=True, takeorder=True, weight=0) 2224 @predicate(b'reverse(set)', safe=True, takeorder=True, weight=0)
2215 def reverse(repo, subset, x, order): 2225 def reverse(repo, subset, x, order):
2216 """Reverse order of set. 2226 """Reverse order of set.
2217 """ 2227 """
2218 l = getset(repo, subset, x, order) 2228 l = getset(repo, subset, x, order)
2219 if order == defineorder: 2229 if order == defineorder:
2220 l.reverse() 2230 l.reverse()
2221 return l 2231 return l
2222 2232
2223 2233
2224 @predicate('roots(set)', safe=True) 2234 @predicate(b'roots(set)', safe=True)
2225 def roots(repo, subset, x): 2235 def roots(repo, subset, x):
2226 """Changesets in set with no parent changeset in set. 2236 """Changesets in set with no parent changeset in set.
2227 """ 2237 """
2228 s = getset(repo, fullreposet(repo), x) 2238 s = getset(repo, fullreposet(repo), x)
2229 parents = repo.changelog.parentrevs 2239 parents = repo.changelog.parentrevs
2232 for p in parents(r): 2242 for p in parents(r):
2233 if 0 <= p and p in s: 2243 if 0 <= p and p in s:
2234 return False 2244 return False
2235 return True 2245 return True
2236 2246
2237 return subset & s.filter(filter, condrepr='<roots>') 2247 return subset & s.filter(filter, condrepr=b'<roots>')
2238 2248
2239 2249
2240 _sortkeyfuncs = { 2250 _sortkeyfuncs = {
2241 'rev': lambda c: c.rev(), 2251 b'rev': lambda c: c.rev(),
2242 'branch': lambda c: c.branch(), 2252 b'branch': lambda c: c.branch(),
2243 'desc': lambda c: c.description(), 2253 b'desc': lambda c: c.description(),
2244 'user': lambda c: c.user(), 2254 b'user': lambda c: c.user(),
2245 'author': lambda c: c.user(), 2255 b'author': lambda c: c.user(),
2246 'date': lambda c: c.date()[0], 2256 b'date': lambda c: c.date()[0],
2247 } 2257 }
2248 2258
2249 2259
2250 def _getsortargs(x): 2260 def _getsortargs(x):
2251 """Parse sort options into (set, [(key, reverse)], opts)""" 2261 """Parse sort options into (set, [(key, reverse)], opts)"""
2252 args = getargsdict(x, 'sort', 'set keys topo.firstbranch') 2262 args = getargsdict(x, b'sort', b'set keys topo.firstbranch')
2253 if 'set' not in args: 2263 if b'set' not in args:
2254 # i18n: "sort" is a keyword 2264 # i18n: "sort" is a keyword
2255 raise error.ParseError(_('sort requires one or two arguments')) 2265 raise error.ParseError(_(b'sort requires one or two arguments'))
2256 keys = "rev" 2266 keys = b"rev"
2257 if 'keys' in args: 2267 if b'keys' in args:
2258 # i18n: "sort" is a keyword 2268 # i18n: "sort" is a keyword
2259 keys = getstring(args['keys'], _("sort spec must be a string")) 2269 keys = getstring(args[b'keys'], _(b"sort spec must be a string"))
2260 2270
2261 keyflags = [] 2271 keyflags = []
2262 for k in keys.split(): 2272 for k in keys.split():
2263 fk = k 2273 fk = k
2264 reverse = k.startswith('-') 2274 reverse = k.startswith(b'-')
2265 if reverse: 2275 if reverse:
2266 k = k[1:] 2276 k = k[1:]
2267 if k not in _sortkeyfuncs and k != 'topo': 2277 if k not in _sortkeyfuncs and k != b'topo':
2268 raise error.ParseError( 2278 raise error.ParseError(
2269 _("unknown sort key %r") % pycompat.bytestr(fk) 2279 _(b"unknown sort key %r") % pycompat.bytestr(fk)
2270 ) 2280 )
2271 keyflags.append((k, reverse)) 2281 keyflags.append((k, reverse))
2272 2282
2273 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags): 2283 if len(keyflags) > 1 and any(k == b'topo' for k, reverse in keyflags):
2274 # i18n: "topo" is a keyword 2284 # i18n: "topo" is a keyword
2275 raise error.ParseError( 2285 raise error.ParseError(
2276 _('topo sort order cannot be combined ' 'with other sort keys') 2286 _(b'topo sort order cannot be combined ' b'with other sort keys')
2277 ) 2287 )
2278 2288
2279 opts = {} 2289 opts = {}
2280 if 'topo.firstbranch' in args: 2290 if b'topo.firstbranch' in args:
2281 if any(k == 'topo' for k, reverse in keyflags): 2291 if any(k == b'topo' for k, reverse in keyflags):
2282 opts['topo.firstbranch'] = args['topo.firstbranch'] 2292 opts[b'topo.firstbranch'] = args[b'topo.firstbranch']
2283 else: 2293 else:
2284 # i18n: "topo" and "topo.firstbranch" are keywords 2294 # i18n: "topo" and "topo.firstbranch" are keywords
2285 raise error.ParseError( 2295 raise error.ParseError(
2286 _( 2296 _(
2287 'topo.firstbranch can only be used ' 2297 b'topo.firstbranch can only be used '
2288 'when using the topo sort key' 2298 b'when using the topo sort key'
2289 ) 2299 )
2290 ) 2300 )
2291 2301
2292 return args['set'], keyflags, opts 2302 return args[b'set'], keyflags, opts
2293 2303
2294 2304
2295 @predicate( 2305 @predicate(
2296 'sort(set[, [-]key... [, ...]])', safe=True, takeorder=True, weight=10 2306 b'sort(set[, [-]key... [, ...]])', safe=True, takeorder=True, weight=10
2297 ) 2307 )
2298 def sort(repo, subset, x, order): 2308 def sort(repo, subset, x, order):
2299 """Sort set by keys. The default sort order is ascending, specify a key 2309 """Sort set by keys. The default sort order is ascending, specify a key
2300 as ``-key`` to sort in descending order. 2310 as ``-key`` to sort in descending order.
2301 2311
2316 s, keyflags, opts = _getsortargs(x) 2326 s, keyflags, opts = _getsortargs(x)
2317 revs = getset(repo, subset, s, order) 2327 revs = getset(repo, subset, s, order)
2318 2328
2319 if not keyflags or order != defineorder: 2329 if not keyflags or order != defineorder:
2320 return revs 2330 return revs
2321 if len(keyflags) == 1 and keyflags[0][0] == "rev": 2331 if len(keyflags) == 1 and keyflags[0][0] == b"rev":
2322 revs.sort(reverse=keyflags[0][1]) 2332 revs.sort(reverse=keyflags[0][1])
2323 return revs 2333 return revs
2324 elif keyflags[0][0] == "topo": 2334 elif keyflags[0][0] == b"topo":
2325 firstbranch = () 2335 firstbranch = ()
2326 if 'topo.firstbranch' in opts: 2336 if b'topo.firstbranch' in opts:
2327 firstbranch = getset(repo, subset, opts['topo.firstbranch']) 2337 firstbranch = getset(repo, subset, opts[b'topo.firstbranch'])
2328 revs = baseset( 2338 revs = baseset(
2329 dagop.toposort(revs, repo.changelog.parentrevs, firstbranch), 2339 dagop.toposort(revs, repo.changelog.parentrevs, firstbranch),
2330 istopo=True, 2340 istopo=True,
2331 ) 2341 )
2332 if keyflags[0][1]: 2342 if keyflags[0][1]:
2338 for k, reverse in reversed(keyflags): 2348 for k, reverse in reversed(keyflags):
2339 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse) 2349 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
2340 return baseset([c.rev() for c in ctxs]) 2350 return baseset([c.rev() for c in ctxs])
2341 2351
2342 2352
2343 @predicate('subrepo([pattern])') 2353 @predicate(b'subrepo([pattern])')
2344 def subrepo(repo, subset, x): 2354 def subrepo(repo, subset, x):
2345 """Changesets that add, modify or remove the given subrepo. If no subrepo 2355 """Changesets that add, modify or remove the given subrepo. If no subrepo
2346 pattern is named, any subrepo changes are returned. 2356 pattern is named, any subrepo changes are returned.
2347 """ 2357 """
2348 # i18n: "subrepo" is a keyword 2358 # i18n: "subrepo" is a keyword
2349 args = getargs(x, 0, 1, _('subrepo takes at most one argument')) 2359 args = getargs(x, 0, 1, _(b'subrepo takes at most one argument'))
2350 pat = None 2360 pat = None
2351 if len(args) != 0: 2361 if len(args) != 0:
2352 pat = getstring(args[0], _("subrepo requires a pattern")) 2362 pat = getstring(args[0], _(b"subrepo requires a pattern"))
2353 2363
2354 m = matchmod.exact(['.hgsubstate']) 2364 m = matchmod.exact([b'.hgsubstate'])
2355 2365
2356 def submatches(names): 2366 def submatches(names):
2357 k, p, m = stringutil.stringmatcher(pat) 2367 k, p, m = stringutil.stringmatcher(pat)
2358 for name in names: 2368 for name in names:
2359 if m(name): 2369 if m(name):
2380 if s.removed: 2390 if s.removed:
2381 return any(submatches(c.p1().substate.keys())) 2391 return any(submatches(c.p1().substate.keys()))
2382 2392
2383 return False 2393 return False
2384 2394
2385 return subset.filter(matches, condrepr=('<subrepo %r>', pat)) 2395 return subset.filter(matches, condrepr=(b'<subrepo %r>', pat))
2386 2396
2387 2397
2388 def _mapbynodefunc(repo, s, f): 2398 def _mapbynodefunc(repo, s, f):
2389 """(repo, smartset, [node] -> [node]) -> smartset 2399 """(repo, smartset, [node] -> [node]) -> smartset
2390 2400
2398 nodemap = cl.nodemap 2408 nodemap = cl.nodemap
2399 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap) 2409 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
2400 return smartset.baseset(result - repo.changelog.filteredrevs) 2410 return smartset.baseset(result - repo.changelog.filteredrevs)
2401 2411
2402 2412
2403 @predicate('successors(set)', safe=True) 2413 @predicate(b'successors(set)', safe=True)
2404 def successors(repo, subset, x): 2414 def successors(repo, subset, x):
2405 """All successors for set, including the given set themselves""" 2415 """All successors for set, including the given set themselves"""
2406 s = getset(repo, fullreposet(repo), x) 2416 s = getset(repo, fullreposet(repo), x)
2407 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes) 2417 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2408 d = _mapbynodefunc(repo, s, f) 2418 d = _mapbynodefunc(repo, s, f)
2411 2421
2412 def _substringmatcher(pattern, casesensitive=True): 2422 def _substringmatcher(pattern, casesensitive=True):
2413 kind, pattern, matcher = stringutil.stringmatcher( 2423 kind, pattern, matcher = stringutil.stringmatcher(
2414 pattern, casesensitive=casesensitive 2424 pattern, casesensitive=casesensitive
2415 ) 2425 )
2416 if kind == 'literal': 2426 if kind == b'literal':
2417 if not casesensitive: 2427 if not casesensitive:
2418 pattern = encoding.lower(pattern) 2428 pattern = encoding.lower(pattern)
2419 matcher = lambda s: pattern in encoding.lower(s) 2429 matcher = lambda s: pattern in encoding.lower(s)
2420 else: 2430 else:
2421 matcher = lambda s: pattern in s 2431 matcher = lambda s: pattern in s
2422 return kind, pattern, matcher 2432 return kind, pattern, matcher
2423 2433
2424 2434
2425 @predicate('tag([name])', safe=True) 2435 @predicate(b'tag([name])', safe=True)
2426 def tag(repo, subset, x): 2436 def tag(repo, subset, x):
2427 """The specified tag by name, or all tagged revisions if no name is given. 2437 """The specified tag by name, or all tagged revisions if no name is given.
2428 2438
2429 Pattern matching is supported for `name`. See 2439 Pattern matching is supported for `name`. See
2430 :hg:`help revisions.patterns`. 2440 :hg:`help revisions.patterns`.
2431 """ 2441 """
2432 # i18n: "tag" is a keyword 2442 # i18n: "tag" is a keyword
2433 args = getargs(x, 0, 1, _("tag takes one or no arguments")) 2443 args = getargs(x, 0, 1, _(b"tag takes one or no arguments"))
2434 cl = repo.changelog 2444 cl = repo.changelog
2435 if args: 2445 if args:
2436 pattern = getstring( 2446 pattern = getstring(
2437 args[0], 2447 args[0],
2438 # i18n: "tag" is a keyword 2448 # i18n: "tag" is a keyword
2439 _('the argument to tag must be a string'), 2449 _(b'the argument to tag must be a string'),
2440 ) 2450 )
2441 kind, pattern, matcher = stringutil.stringmatcher(pattern) 2451 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2442 if kind == 'literal': 2452 if kind == b'literal':
2443 # avoid resolving all tags 2453 # avoid resolving all tags
2444 tn = repo._tagscache.tags.get(pattern, None) 2454 tn = repo._tagscache.tags.get(pattern, None)
2445 if tn is None: 2455 if tn is None:
2446 raise error.RepoLookupError( 2456 raise error.RepoLookupError(
2447 _("tag '%s' does not exist") % pattern 2457 _(b"tag '%s' does not exist") % pattern
2448 ) 2458 )
2449 s = {repo[tn].rev()} 2459 s = {repo[tn].rev()}
2450 else: 2460 else:
2451 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)} 2461 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2452 else: 2462 else:
2453 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'} 2463 s = {cl.rev(n) for t, n in repo.tagslist() if t != b'tip'}
2454 return subset & s 2464 return subset & s
2455 2465
2456 2466
2457 @predicate('tagged', safe=True) 2467 @predicate(b'tagged', safe=True)
2458 def tagged(repo, subset, x): 2468 def tagged(repo, subset, x):
2459 return tag(repo, subset, x) 2469 return tag(repo, subset, x)
2460 2470
2461 2471
2462 @predicate('orphan()', safe=True) 2472 @predicate(b'orphan()', safe=True)
2463 def orphan(repo, subset, x): 2473 def orphan(repo, subset, x):
2464 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL) 2474 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2465 """ 2475 """
2466 # i18n: "orphan" is a keyword 2476 # i18n: "orphan" is a keyword
2467 getargs(x, 0, 0, _("orphan takes no arguments")) 2477 getargs(x, 0, 0, _(b"orphan takes no arguments"))
2468 orphan = obsmod.getrevs(repo, 'orphan') 2478 orphan = obsmod.getrevs(repo, b'orphan')
2469 return subset & orphan 2479 return subset & orphan
2470 2480
2471 2481
2472 @predicate('user(string)', safe=True, weight=10) 2482 @predicate(b'user(string)', safe=True, weight=10)
2473 def user(repo, subset, x): 2483 def user(repo, subset, x):
2474 """User name contains string. The match is case-insensitive. 2484 """User name contains string. The match is case-insensitive.
2475 2485
2476 Pattern matching is supported for `string`. See 2486 Pattern matching is supported for `string`. See
2477 :hg:`help revisions.patterns`. 2487 :hg:`help revisions.patterns`.
2478 """ 2488 """
2479 return author(repo, subset, x) 2489 return author(repo, subset, x)
2480 2490
2481 2491
2482 @predicate('wdir()', safe=True, weight=0) 2492 @predicate(b'wdir()', safe=True, weight=0)
2483 def wdir(repo, subset, x): 2493 def wdir(repo, subset, x):
2484 """Working directory. (EXPERIMENTAL)""" 2494 """Working directory. (EXPERIMENTAL)"""
2485 # i18n: "wdir" is a keyword 2495 # i18n: "wdir" is a keyword
2486 getargs(x, 0, 0, _("wdir takes no arguments")) 2496 getargs(x, 0, 0, _(b"wdir takes no arguments"))
2487 if node.wdirrev in subset or isinstance(subset, fullreposet): 2497 if node.wdirrev in subset or isinstance(subset, fullreposet):
2488 return baseset([node.wdirrev]) 2498 return baseset([node.wdirrev])
2489 return baseset() 2499 return baseset()
2490 2500
2491 2501
2492 def _orderedlist(repo, subset, x): 2502 def _orderedlist(repo, subset, x):
2493 s = getstring(x, "internal error") 2503 s = getstring(x, b"internal error")
2494 if not s: 2504 if not s:
2495 return baseset() 2505 return baseset()
2496 # remove duplicates here. it's difficult for caller to deduplicate sets 2506 # remove duplicates here. it's difficult for caller to deduplicate sets
2497 # because different symbols can point to the same rev. 2507 # because different symbols can point to the same rev.
2498 cl = repo.changelog 2508 cl = repo.changelog
2499 ls = [] 2509 ls = []
2500 seen = set() 2510 seen = set()
2501 for t in s.split('\0'): 2511 for t in s.split(b'\0'):
2502 try: 2512 try:
2503 # fast path for integer revision 2513 # fast path for integer revision
2504 r = int(t) 2514 r = int(t)
2505 if ('%d' % r) != t or r not in cl: 2515 if (b'%d' % r) != t or r not in cl:
2506 raise ValueError 2516 raise ValueError
2507 revs = [r] 2517 revs = [r]
2508 except ValueError: 2518 except ValueError:
2509 revs = stringset(repo, subset, t, defineorder) 2519 revs = stringset(repo, subset, t, defineorder)
2510 2520
2520 seen.add(r) 2530 seen.add(r)
2521 return baseset(ls) 2531 return baseset(ls)
2522 2532
2523 2533
2524 # for internal use 2534 # for internal use
2525 @predicate('_list', safe=True, takeorder=True) 2535 @predicate(b'_list', safe=True, takeorder=True)
2526 def _list(repo, subset, x, order): 2536 def _list(repo, subset, x, order):
2527 if order == followorder: 2537 if order == followorder:
2528 # slow path to take the subset order 2538 # slow path to take the subset order
2529 return subset & _orderedlist(repo, fullreposet(repo), x) 2539 return subset & _orderedlist(repo, fullreposet(repo), x)
2530 else: 2540 else:
2531 return _orderedlist(repo, subset, x) 2541 return _orderedlist(repo, subset, x)
2532 2542
2533 2543
2534 def _orderedintlist(repo, subset, x): 2544 def _orderedintlist(repo, subset, x):
2535 s = getstring(x, "internal error") 2545 s = getstring(x, b"internal error")
2536 if not s: 2546 if not s:
2537 return baseset() 2547 return baseset()
2538 ls = [int(r) for r in s.split('\0')] 2548 ls = [int(r) for r in s.split(b'\0')]
2539 s = subset 2549 s = subset
2540 return baseset([r for r in ls if r in s]) 2550 return baseset([r for r in ls if r in s])
2541 2551
2542 2552
2543 # for internal use 2553 # for internal use
2544 @predicate('_intlist', safe=True, takeorder=True, weight=0) 2554 @predicate(b'_intlist', safe=True, takeorder=True, weight=0)
2545 def _intlist(repo, subset, x, order): 2555 def _intlist(repo, subset, x, order):
2546 if order == followorder: 2556 if order == followorder:
2547 # slow path to take the subset order 2557 # slow path to take the subset order
2548 return subset & _orderedintlist(repo, fullreposet(repo), x) 2558 return subset & _orderedintlist(repo, fullreposet(repo), x)
2549 else: 2559 else:
2550 return _orderedintlist(repo, subset, x) 2560 return _orderedintlist(repo, subset, x)
2551 2561
2552 2562
2553 def _orderedhexlist(repo, subset, x): 2563 def _orderedhexlist(repo, subset, x):
2554 s = getstring(x, "internal error") 2564 s = getstring(x, b"internal error")
2555 if not s: 2565 if not s:
2556 return baseset() 2566 return baseset()
2557 cl = repo.changelog 2567 cl = repo.changelog
2558 ls = [cl.rev(node.bin(r)) for r in s.split('\0')] 2568 ls = [cl.rev(node.bin(r)) for r in s.split(b'\0')]
2559 s = subset 2569 s = subset
2560 return baseset([r for r in ls if r in s]) 2570 return baseset([r for r in ls if r in s])
2561 2571
2562 2572
2563 # for internal use 2573 # for internal use
2564 @predicate('_hexlist', safe=True, takeorder=True) 2574 @predicate(b'_hexlist', safe=True, takeorder=True)
2565 def _hexlist(repo, subset, x, order): 2575 def _hexlist(repo, subset, x, order):
2566 if order == followorder: 2576 if order == followorder:
2567 # slow path to take the subset order 2577 # slow path to take the subset order
2568 return subset & _orderedhexlist(repo, fullreposet(repo), x) 2578 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2569 else: 2579 else:
2570 return _orderedhexlist(repo, subset, x) 2580 return _orderedhexlist(repo, subset, x)
2571 2581
2572 2582
2573 methods = { 2583 methods = {
2574 "range": rangeset, 2584 b"range": rangeset,
2575 "rangeall": rangeall, 2585 b"rangeall": rangeall,
2576 "rangepre": rangepre, 2586 b"rangepre": rangepre,
2577 "rangepost": rangepost, 2587 b"rangepost": rangepost,
2578 "dagrange": dagrange, 2588 b"dagrange": dagrange,
2579 "string": stringset, 2589 b"string": stringset,
2580 "symbol": stringset, 2590 b"symbol": stringset,
2581 "and": andset, 2591 b"and": andset,
2582 "andsmally": andsmallyset, 2592 b"andsmally": andsmallyset,
2583 "or": orset, 2593 b"or": orset,
2584 "not": notset, 2594 b"not": notset,
2585 "difference": differenceset, 2595 b"difference": differenceset,
2586 "relation": relationset, 2596 b"relation": relationset,
2587 "relsubscript": relsubscriptset, 2597 b"relsubscript": relsubscriptset,
2588 "subscript": subscriptset, 2598 b"subscript": subscriptset,
2589 "list": listset, 2599 b"list": listset,
2590 "keyvalue": keyvaluepair, 2600 b"keyvalue": keyvaluepair,
2591 "func": func, 2601 b"func": func,
2592 "ancestor": ancestorspec, 2602 b"ancestor": ancestorspec,
2593 "parent": parentspec, 2603 b"parent": parentspec,
2594 "parentpost": parentpost, 2604 b"parentpost": parentpost,
2595 "smartset": rawsmartset, 2605 b"smartset": rawsmartset,
2596 } 2606 }
2597 2607
2598 subscriptrelations = { 2608 subscriptrelations = {
2599 "g": generationsrel, 2609 b"g": generationsrel,
2600 "generations": generationsrel, 2610 b"generations": generationsrel,
2601 } 2611 }
2602 2612
2603 2613
2604 def lookupfn(repo): 2614 def lookupfn(repo):
2605 return lambda symbol: scmutil.isrevsymbol(repo, symbol) 2615 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2625 def mfunc(repo, subset=None): 2635 def mfunc(repo, subset=None):
2626 return baseset() 2636 return baseset()
2627 2637
2628 return mfunc 2638 return mfunc
2629 if not all(specs): 2639 if not all(specs):
2630 raise error.ParseError(_("empty query")) 2640 raise error.ParseError(_(b"empty query"))
2631 if len(specs) == 1: 2641 if len(specs) == 1:
2632 tree = revsetlang.parse(specs[0], lookup) 2642 tree = revsetlang.parse(specs[0], lookup)
2633 else: 2643 else:
2634 tree = ( 2644 tree = (
2635 'or', 2645 b'or',
2636 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs), 2646 (b'list',) + tuple(revsetlang.parse(s, lookup) for s in specs),
2637 ) 2647 )
2638 2648
2639 aliases = [] 2649 aliases = []
2640 warn = None 2650 warn = None
2641 if ui: 2651 if ui:
2642 aliases.extend(ui.configitems('revsetalias')) 2652 aliases.extend(ui.configitems(b'revsetalias'))
2643 warn = ui.warn 2653 warn = ui.warn
2644 if localalias: 2654 if localalias:
2645 aliases.extend(localalias.items()) 2655 aliases.extend(localalias.items())
2646 if aliases: 2656 if aliases:
2647 tree = revsetlang.expandaliases(tree, aliases, warn=warn) 2657 tree = revsetlang.expandaliases(tree, aliases, warn=warn)