comparison mercurial/cmdutil.py @ 36194:39b3aab6231e

revert: use an exact matcher in interactive diff selection (issue5789) When going through _performrevert() in the interactive case, we build a matcher with files to revert and pass it patch.diff() for later selection of diff hunks to revert. The files set used to build the matcher comes from dirstate and accounts for patterns explicitly passed to revert ('hg revert -i <file>') and, in case of nonexistent pattern, this set is empty (which is expected). Unfortunately, the matcher built from scmutil.match(ctx, []) is wrong as it leads patch.diff() to rebuild a 'changes' tuple with dirstate information, ignoring user-specified pattern. This leads to the situation described in issue5789, where one gets prompted about reverting files unrelated to specified patterns because they made a typo or so. We fix this by building an exact matcher with the correct set of file paths (built earlier). Thanks to Yuya Nishihara for suggesting the correct fix.
author Denis Laxalde <denis.laxalde@logilab.fr>
date Wed, 14 Feb 2018 14:12:05 +0100
parents 62719115875d
children 01280638bdb1
comparison
equal deleted inserted replaced
36193:a228b2f55ad6 36194:39b3aab6231e
2901 Make sure you have the working directory locked when calling this function. 2901 Make sure you have the working directory locked when calling this function.
2902 """ 2902 """
2903 parent, p2 = parents 2903 parent, p2 = parents
2904 node = ctx.node() 2904 node = ctx.node()
2905 excluded_files = [] 2905 excluded_files = []
2906 matcher_opts = {"exclude": excluded_files}
2907 2906
2908 def checkout(f): 2907 def checkout(f):
2909 fc = ctx[f] 2908 fc = ctx[f]
2910 repo.wwrite(f, fc.data(), fc.flags()) 2909 repo.wwrite(f, fc.data(), fc.flags())
2911 2910
2922 choice = repo.ui.promptchoice( 2921 choice = repo.ui.promptchoice(
2923 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f) 2922 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
2924 if choice == 0: 2923 if choice == 0:
2925 repo.dirstate.drop(f) 2924 repo.dirstate.drop(f)
2926 else: 2925 else:
2927 excluded_files.append(repo.wjoin(f)) 2926 excluded_files.append(f)
2928 else: 2927 else:
2929 repo.dirstate.drop(f) 2928 repo.dirstate.drop(f)
2930 for f in actions['remove'][0]: 2929 for f in actions['remove'][0]:
2931 audit_path(f) 2930 audit_path(f)
2932 if interactive: 2931 if interactive:
2933 choice = repo.ui.promptchoice( 2932 choice = repo.ui.promptchoice(
2934 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f) 2933 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
2935 if choice == 0: 2934 if choice == 0:
2936 doremove(f) 2935 doremove(f)
2937 else: 2936 else:
2938 excluded_files.append(repo.wjoin(f)) 2937 excluded_files.append(f)
2939 else: 2938 else:
2940 doremove(f) 2939 doremove(f)
2941 for f in actions['drop'][0]: 2940 for f in actions['drop'][0]:
2942 audit_path(f) 2941 audit_path(f)
2943 repo.dirstate.remove(f) 2942 repo.dirstate.remove(f)
2953 normal = repo.dirstate.normal 2952 normal = repo.dirstate.normal
2954 2953
2955 newlyaddedandmodifiedfiles = set() 2954 newlyaddedandmodifiedfiles = set()
2956 if interactive: 2955 if interactive:
2957 # Prompt the user for changes to revert 2956 # Prompt the user for changes to revert
2958 torevert = [repo.wjoin(f) for f in actions['revert'][0]] 2957 torevert = [f for f in actions['revert'][0] if f not in excluded_files]
2959 m = scmutil.match(ctx, torevert, matcher_opts) 2958 m = scmutil.matchfiles(repo, torevert)
2960 diffopts = patch.difffeatureopts(repo.ui, whitespace=True) 2959 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
2961 diffopts.nodates = True 2960 diffopts.nodates = True
2962 diffopts.git = True 2961 diffopts.git = True
2963 operation = 'discard' 2962 operation = 'discard'
2964 reversehunks = True 2963 reversehunks = True