Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/cmdutil.py @ 36232: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
36231:a228b2f55ad6 | 36232: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 |