comparison mercurial/logcmdutil.py @ 45569:24df19a9ab87

log: pass around --follow/--follow-first options by walkopts
author Yuya Nishihara <yuya@tcha.org>
date Sat, 12 Sep 2020 21:35:26 +0900
parents c1d0f83d62c4
children bddf70c93614
comparison
equal deleted inserted replaced
45568:c1d0f83d62c4 45569:24df19a9ab87
684 684
685 # raw command-line parameters, which a matcher will be built from 685 # raw command-line parameters, which a matcher will be built from
686 pats = attr.ib() # type: List[bytes] 686 pats = attr.ib() # type: List[bytes]
687 opts = attr.ib() # type: Dict[bytes, Any] 687 opts = attr.ib() # type: Dict[bytes, Any]
688 688
689 # 0: no follow, 1: follow first, 2: follow both parents
690 follow = attr.ib(default=0) # type: int
691
689 692
690 def parseopts(ui, pats, opts): 693 def parseopts(ui, pats, opts):
691 # type: (Any, List[bytes], Dict[bytes, Any]) -> walkopts 694 # type: (Any, List[bytes], Dict[bytes, Any]) -> walkopts
692 """Parse log command options into walkopts 695 """Parse log command options into walkopts
693 696
694 The returned walkopts will be passed in to getrevs(). 697 The returned walkopts will be passed in to getrevs().
695 """ 698 """
696 return walkopts(pats=pats, opts=opts) 699 if opts.get(b'follow_first'):
700 follow = 1
701 elif opts.get(b'follow'):
702 follow = 2
703 else:
704 follow = 0
705
706 return walkopts(pats=pats, opts=opts, follow=follow)
697 707
698 708
699 def _makematcher(repo, revs, wopts): 709 def _makematcher(repo, revs, wopts):
700 """Build matcher and expanded patterns from log options 710 """Build matcher and expanded patterns from log options
701 711
714 match, pats = scmutil.matchandpats(wctx, wopts.pats, wopts.opts) 724 match, pats = scmutil.matchandpats(wctx, wopts.pats, wopts.opts)
715 slowpath = match.anypats() or ( 725 slowpath = match.anypats() or (
716 not match.always() and wopts.opts.get(b'removed') 726 not match.always() and wopts.opts.get(b'removed')
717 ) 727 )
718 if not slowpath: 728 if not slowpath:
719 follow = wopts.opts.get(b'follow') or wopts.opts.get(b'follow_first') 729 if wopts.follow and wopts.opts.get(b'rev'):
720 if follow and wopts.opts.get(b'rev'):
721 # There may be the case that a path doesn't exist in some (but 730 # There may be the case that a path doesn't exist in some (but
722 # not all) of the specified start revisions, but let's consider 731 # not all) of the specified start revisions, but let's consider
723 # the path is valid. Missing files will be warned by the matcher. 732 # the path is valid. Missing files will be warned by the matcher.
724 startctxs = [repo[r] for r in revs] 733 startctxs = [repo[r] for r in revs]
725 for f in match.files(): 734 for f in match.files():
737 b'cannot follow file not in any of the specified ' 746 b'cannot follow file not in any of the specified '
738 b'revisions: "%s"' 747 b'revisions: "%s"'
739 ) 748 )
740 % f 749 % f
741 ) 750 )
742 elif follow: 751 elif wopts.follow:
743 for f in match.files(): 752 for f in match.files():
744 if f not in wctx: 753 if f not in wctx:
745 # If the file exists, it may be a directory, so let it 754 # If the file exists, it may be a directory, so let it
746 # take the slow path. 755 # take the slow path.
747 if os.path.exists(repo.wjoin(f)): 756 if os.path.exists(repo.wjoin(f)):
827 836
828 837
829 def _makerevset(repo, wopts, slowpath): 838 def _makerevset(repo, wopts, slowpath):
830 """Return a revset string built from log options and file patterns""" 839 """Return a revset string built from log options and file patterns"""
831 opts = dict(wopts.opts) 840 opts = dict(wopts.opts)
832 # follow or not follow?
833 follow = opts.get(b'follow') or opts.get(b'follow_first')
834 841
835 # branch and only_branch are really aliases and must be handled at 842 # branch and only_branch are really aliases and must be handled at
836 # the same time 843 # the same time
837 opts[b'branch'] = opts.get(b'branch', []) + opts.get(b'only_branch', []) 844 opts[b'branch'] = opts.get(b'branch', []) + opts.get(b'only_branch', [])
838 opts[b'branch'] = [repo.lookupbranch(b) for b in opts[b'branch']] 845 opts[b'branch'] = [repo.lookupbranch(b) for b in opts[b'branch']]
852 for p in opts.get(b'include', []): 859 for p in opts.get(b'include', []):
853 matchargs.append(b'i:' + p) 860 matchargs.append(b'i:' + p)
854 for p in opts.get(b'exclude', []): 861 for p in opts.get(b'exclude', []):
855 matchargs.append(b'x:' + p) 862 matchargs.append(b'x:' + p)
856 opts[b'_matchfiles'] = matchargs 863 opts[b'_matchfiles'] = matchargs
857 elif not follow: 864 elif not wopts.follow:
858 opts[b'_patslog'] = list(wopts.pats) 865 opts[b'_patslog'] = list(wopts.pats)
859 866
860 expr = [] 867 expr = []
861 for op, val in sorted(pycompat.iteritems(opts)): 868 for op, val in sorted(pycompat.iteritems(opts)):
862 if not val: 869 if not val:
880 return expr 887 return expr
881 888
882 889
883 def _initialrevs(repo, wopts): 890 def _initialrevs(repo, wopts):
884 """Return the initial set of revisions to be filtered or followed""" 891 """Return the initial set of revisions to be filtered or followed"""
885 follow = wopts.opts.get(b'follow') or wopts.opts.get(b'follow_first')
886 if wopts.opts.get(b'rev'): 892 if wopts.opts.get(b'rev'):
887 revs = scmutil.revrange(repo, wopts.opts[b'rev']) 893 revs = scmutil.revrange(repo, wopts.opts[b'rev'])
888 elif follow and repo.dirstate.p1() == nullid: 894 elif wopts.follow and repo.dirstate.p1() == nullid:
889 revs = smartset.baseset() 895 revs = smartset.baseset()
890 elif follow: 896 elif wopts.follow:
891 revs = repo.revs(b'.') 897 revs = repo.revs(b'.')
892 else: 898 else:
893 revs = smartset.spanset(repo) 899 revs = smartset.spanset(repo)
894 revs.reverse() 900 revs.reverse()
895 return revs 901 return revs
899 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[changesetdiffer]] 905 # type: (Any, walkopts) -> Tuple[smartset.abstractsmartset, Optional[changesetdiffer]]
900 """Return (revs, differ) where revs is a smartset 906 """Return (revs, differ) where revs is a smartset
901 907
902 differ is a changesetdiffer with pre-configured file matcher. 908 differ is a changesetdiffer with pre-configured file matcher.
903 """ 909 """
904 follow = wopts.opts.get(b'follow') or wopts.opts.get(b'follow_first')
905 followfirst = wopts.opts.get(b'follow_first')
906 limit = getlimit(wopts.opts) 910 limit = getlimit(wopts.opts)
907 revs = _initialrevs(repo, wopts) 911 revs = _initialrevs(repo, wopts)
908 if not revs: 912 if not revs:
909 return smartset.baseset(), None 913 return smartset.baseset(), None
910 match, pats, slowpath = _makematcher(repo, revs, wopts) 914 match, pats, slowpath = _makematcher(repo, revs, wopts)
911 wopts = attr.evolve(wopts, pats=pats) 915 wopts = attr.evolve(wopts, pats=pats)
912 916
913 filematcher = None 917 filematcher = None
914 if follow: 918 if wopts.follow:
915 if slowpath or match.always(): 919 if slowpath or match.always():
916 revs = dagop.revancestors(repo, revs, followfirst=followfirst) 920 revs = dagop.revancestors(repo, revs, followfirst=wopts.follow == 1)
917 else: 921 else:
918 revs, filematcher = _fileancestors(repo, revs, match, followfirst) 922 revs, filematcher = _fileancestors(
923 repo, revs, match, followfirst=wopts.follow == 1
924 )
919 revs.reverse() 925 revs.reverse()
920 if filematcher is None: 926 if filematcher is None:
921 filematcher = _makenofollowfilematcher(repo, wopts.pats, wopts.opts) 927 filematcher = _makenofollowfilematcher(repo, wopts.pats, wopts.opts)
922 if filematcher is None: 928 if filematcher is None:
923 929