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 |
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 |
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(): |
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] |
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 |
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 |
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) |
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 |
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: |
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 |
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 |
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) |