2780 |
2780 |
2781 # `names` is a mapping for all elements in working copy and target revision |
2781 # `names` is a mapping for all elements in working copy and target revision |
2782 # The mapping is in the form: |
2782 # The mapping is in the form: |
2783 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>) |
2783 # <abs path in repo> -> (<path from CWD>, <exactly specified by matcher?>) |
2784 names = {} |
2784 names = {} |
|
2785 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) |
2785 |
2786 |
2786 with repo.wlock(): |
2787 with repo.wlock(): |
2787 ## filling of the `names` mapping |
2788 ## filling of the `names` mapping |
2788 # walk dirstate to fill `names` |
2789 # walk dirstate to fill `names` |
2789 |
2790 |
2795 targetsubs = sorted(s for s in wctx.substate if m(s)) |
2796 targetsubs = sorted(s for s in wctx.substate if m(s)) |
2796 |
2797 |
2797 if not m.always(): |
2798 if not m.always(): |
2798 matcher = matchmod.badmatch(m, lambda x, y: False) |
2799 matcher = matchmod.badmatch(m, lambda x, y: False) |
2799 for abs in wctx.walk(matcher): |
2800 for abs in wctx.walk(matcher): |
2800 names[abs] = m.rel(abs), m.exact(abs) |
2801 names[abs] = m.exact(abs) |
2801 |
2802 |
2802 # walk target manifest to fill `names` |
2803 # walk target manifest to fill `names` |
2803 |
2804 |
2804 def badfn(path, msg): |
2805 def badfn(path, msg): |
2805 if path in names: |
2806 if path in names: |
2812 return |
2813 return |
2813 ui.warn("%s: %s\n" % (m.rel(path), msg)) |
2814 ui.warn("%s: %s\n" % (m.rel(path), msg)) |
2814 |
2815 |
2815 for abs in ctx.walk(matchmod.badmatch(m, badfn)): |
2816 for abs in ctx.walk(matchmod.badmatch(m, badfn)): |
2816 if abs not in names: |
2817 if abs not in names: |
2817 names[abs] = m.rel(abs), m.exact(abs) |
2818 names[abs] = m.exact(abs) |
2818 |
2819 |
2819 # Find status of all file in `names`. |
2820 # Find status of all file in `names`. |
2820 m = scmutil.matchfiles(repo, names) |
2821 m = scmutil.matchfiles(repo, names) |
2821 |
2822 |
2822 changes = repo.status(node1=node, match=m, |
2823 changes = repo.status(node1=node, match=m, |
2823 unknown=True, ignored=True, clean=True) |
2824 unknown=True, ignored=True, clean=True) |
2824 else: |
2825 else: |
2825 changes = repo.status(node1=node, match=m) |
2826 changes = repo.status(node1=node, match=m) |
2826 for kind in changes: |
2827 for kind in changes: |
2827 for abs in kind: |
2828 for abs in kind: |
2828 names[abs] = m.rel(abs), m.exact(abs) |
2829 names[abs] = m.exact(abs) |
2829 |
2830 |
2830 m = scmutil.matchfiles(repo, names) |
2831 m = scmutil.matchfiles(repo, names) |
2831 |
2832 |
2832 modified = set(changes.modified) |
2833 modified = set(changes.modified) |
2833 added = set(changes.added) |
2834 added = set(changes.added) |
2885 mergeadd.remove(path) |
2886 mergeadd.remove(path) |
2886 dsadded |= mergeadd |
2887 dsadded |= mergeadd |
2887 dsmodified -= mergeadd |
2888 dsmodified -= mergeadd |
2888 |
2889 |
2889 # if f is a rename, update `names` to also revert the source |
2890 # if f is a rename, update `names` to also revert the source |
2890 cwd = repo.getcwd() |
|
2891 for f in localchanges: |
2891 for f in localchanges: |
2892 src = repo.dirstate.copied(f) |
2892 src = repo.dirstate.copied(f) |
2893 # XXX should we check for rename down to target node? |
2893 # XXX should we check for rename down to target node? |
2894 if src and src not in names and repo.dirstate[src] == 'r': |
2894 if src and src not in names and repo.dirstate[src] == 'r': |
2895 dsremoved.add(src) |
2895 dsremoved.add(src) |
2896 names[src] = (repo.pathto(src, cwd), True) |
2896 names[src] = True |
2897 |
2897 |
2898 # determine the exact nature of the deleted changesets |
2898 # determine the exact nature of the deleted changesets |
2899 deladded = set(_deleted) |
2899 deladded = set(_deleted) |
2900 for path in _deleted: |
2900 for path in _deleted: |
2901 if path in mf: |
2901 if path in mf: |
2998 (clean, actions['noop'], discard), |
2998 (clean, actions['noop'], discard), |
2999 # Existing file, not tracked anywhere |
2999 # Existing file, not tracked anywhere |
3000 (unknown, actions['unknown'], discard), |
3000 (unknown, actions['unknown'], discard), |
3001 ) |
3001 ) |
3002 |
3002 |
3003 for abs, (rel, exact) in sorted(names.items()): |
3003 for abs, exact in sorted(names.items()): |
3004 # target file to be touch on disk (relative to cwd) |
3004 # target file to be touch on disk (relative to cwd) |
3005 target = repo.wjoin(abs) |
3005 target = repo.wjoin(abs) |
3006 # search the entry in the dispatch table. |
3006 # search the entry in the dispatch table. |
3007 # if the file is in any of these sets, it was touched in the working |
3007 # if the file is in any of these sets, it was touched in the working |
3008 # directory parent and we are sure it needs to be reverted. |
3008 # directory parent and we are sure it needs to be reverted. |
3015 # If in interactive mode, don't automatically create |
3015 # If in interactive mode, don't automatically create |
3016 # .orig files (issue4793) |
3016 # .orig files (issue4793) |
3017 if dobackup == backupinteractive: |
3017 if dobackup == backupinteractive: |
3018 tobackup.add(abs) |
3018 tobackup.add(abs) |
3019 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])): |
3019 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])): |
3020 bakname = scmutil.backuppath(ui, repo, abs) |
3020 absbakname = scmutil.backuppath(ui, repo, abs) |
3021 relbakname = os.path.relpath(bakname) |
3021 bakname = os.path.relpath(absbakname, |
|
3022 start=repo.root) |
3022 ui.note(_('saving current version of %s as %s\n') % |
3023 ui.note(_('saving current version of %s as %s\n') % |
3023 (rel, relbakname)) |
3024 (uipathfn(abs), uipathfn(bakname))) |
3024 if not opts.get('dry_run'): |
3025 if not opts.get('dry_run'): |
3025 if interactive: |
3026 if interactive: |
3026 util.copyfile(target, bakname) |
3027 util.copyfile(target, absbakname) |
3027 else: |
3028 else: |
3028 util.rename(target, bakname) |
3029 util.rename(target, absbakname) |
3029 if opts.get('dry_run'): |
3030 if opts.get('dry_run'): |
3030 if ui.verbose or not exact: |
3031 if ui.verbose or not exact: |
3031 ui.status(msg % rel) |
3032 ui.status(msg % uipathfn(abs)) |
3032 elif exact: |
3033 elif exact: |
3033 ui.warn(msg % rel) |
3034 ui.warn(msg % uipathfn(abs)) |
3034 break |
3035 break |
3035 |
3036 |
3036 if not opts.get('dry_run'): |
3037 if not opts.get('dry_run'): |
3037 needdata = ('revert', 'add', 'undelete') |
3038 needdata = ('revert', 'add', 'undelete') |
3038 oplist = [actions[name][0] for name in needdata] |
3039 oplist = [actions[name][0] for name in needdata] |
3039 prefetch = scmutil.prefetchfiles |
3040 prefetch = scmutil.prefetchfiles |
3040 matchfiles = scmutil.matchfiles |
3041 matchfiles = scmutil.matchfiles |
3041 prefetch(repo, [ctx.rev()], |
3042 prefetch(repo, [ctx.rev()], |
3042 matchfiles(repo, |
3043 matchfiles(repo, |
3043 [f for sublist in oplist for f in sublist])) |
3044 [f for sublist in oplist for f in sublist])) |
3044 _performrevert(repo, parents, ctx, names, actions, interactive, |
3045 _performrevert(repo, parents, ctx, names, uipathfn, actions, |
3045 tobackup) |
3046 interactive, tobackup) |
3046 |
3047 |
3047 if targetsubs: |
3048 if targetsubs: |
3048 # Revert the subrepos on the revert list |
3049 # Revert the subrepos on the revert list |
3049 for sub in targetsubs: |
3050 for sub in targetsubs: |
3050 try: |
3051 try: |
3052 **pycompat.strkwargs(opts)) |
3053 **pycompat.strkwargs(opts)) |
3053 except KeyError: |
3054 except KeyError: |
3054 raise error.Abort("subrepository '%s' does not exist in %s!" |
3055 raise error.Abort("subrepository '%s' does not exist in %s!" |
3055 % (sub, short(ctx.node()))) |
3056 % (sub, short(ctx.node()))) |
3056 |
3057 |
3057 def _performrevert(repo, parents, ctx, names, actions, interactive=False, |
3058 def _performrevert(repo, parents, ctx, names, uipathfn, actions, |
3058 tobackup=None): |
3059 interactive=False, tobackup=None): |
3059 """function that actually perform all the actions computed for revert |
3060 """function that actually perform all the actions computed for revert |
3060 |
3061 |
3061 This is an independent function to let extension to plug in and react to |
3062 This is an independent function to let extension to plug in and react to |
3062 the imminent revert. |
3063 the imminent revert. |
3063 |
3064 |
3078 except OSError: |
3079 except OSError: |
3079 pass |
3080 pass |
3080 repo.dirstate.remove(f) |
3081 repo.dirstate.remove(f) |
3081 |
3082 |
3082 def prntstatusmsg(action, f): |
3083 def prntstatusmsg(action, f): |
3083 rel, exact = names[f] |
3084 exact = names[f] |
3084 if repo.ui.verbose or not exact: |
3085 if repo.ui.verbose or not exact: |
3085 repo.ui.status(actions[action][1] % rel) |
3086 repo.ui.status(actions[action][1] % uipathfn(f)) |
3086 |
3087 |
3087 audit_path = pathutil.pathauditor(repo.root, cached=True) |
3088 audit_path = pathutil.pathauditor(repo.root, cached=True) |
3088 for f in actions['forget'][0]: |
3089 for f in actions['forget'][0]: |
3089 if interactive: |
3090 if interactive: |
3090 choice = repo.ui.promptchoice( |
3091 choice = repo.ui.promptchoice( |
3091 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f) |
3092 _("forget added file %s (Yn)?$$ &Yes $$ &No") % uipathfn(f)) |
3092 if choice == 0: |
3093 if choice == 0: |
3093 prntstatusmsg('forget', f) |
3094 prntstatusmsg('forget', f) |
3094 repo.dirstate.drop(f) |
3095 repo.dirstate.drop(f) |
3095 else: |
3096 else: |
3096 excluded_files.append(f) |
3097 excluded_files.append(f) |