Mercurial > hg
comparison mercurial/cmdutil.py @ 29498:1b38cfde9530
revert: don't backup if no files reverted in interactive mode (issue4793)
When reverting interactively, we always backup files before prompting the user
to find out if they actually want to revert them. This can create spurious
*.orig files if a user enters an interactive revert session and then doesn't
revert any files. Instead, we should only backup files that are actually being
touched.
author | skarlage <skarlage@fb.com> |
---|---|
date | Thu, 30 Jun 2016 08:38:19 -0700 |
parents | 33a6b750b5b9 |
children | 9c2cc107547f |
comparison
equal
deleted
inserted
replaced
29497:ee2027195847 | 29498:1b38cfde9530 |
---|---|
3141 | 3141 |
3142 # "constant" that convey the backup strategy. | 3142 # "constant" that convey the backup strategy. |
3143 # All set to `discard` if `no-backup` is set do avoid checking | 3143 # All set to `discard` if `no-backup` is set do avoid checking |
3144 # no_backup lower in the code. | 3144 # no_backup lower in the code. |
3145 # These values are ordered for comparison purposes | 3145 # These values are ordered for comparison purposes |
3146 backupinteractive = 3 # do backup if interactively modified | |
3146 backup = 2 # unconditionally do backup | 3147 backup = 2 # unconditionally do backup |
3147 check = 1 # check if the existing file differs from target | 3148 check = 1 # check if the existing file differs from target |
3148 discard = 0 # never do backup | 3149 discard = 0 # never do backup |
3149 if opts.get('no_backup'): | 3150 if opts.get('no_backup'): |
3150 backup = check = discard | 3151 backupinteractive = backup = check = discard |
3152 if interactive: | |
3153 dsmodifiedbackup = backupinteractive | |
3154 else: | |
3155 dsmodifiedbackup = backup | |
3156 tobackup = set() | |
3151 | 3157 |
3152 backupanddel = actions['remove'] | 3158 backupanddel = actions['remove'] |
3153 if not opts.get('no_backup'): | 3159 if not opts.get('no_backup'): |
3154 backupanddel = actions['drop'] | 3160 backupanddel = actions['drop'] |
3155 | 3161 |
3163 # Modified compared to target, no local change | 3169 # Modified compared to target, no local change |
3164 (modified, actions['revert'], discard), | 3170 (modified, actions['revert'], discard), |
3165 # Modified compared to target, but local file is deleted | 3171 # Modified compared to target, but local file is deleted |
3166 (deleted, actions['revert'], discard), | 3172 (deleted, actions['revert'], discard), |
3167 # Modified compared to target, local change | 3173 # Modified compared to target, local change |
3168 (dsmodified, actions['revert'], backup), | 3174 (dsmodified, actions['revert'], dsmodifiedbackup), |
3169 # Added since target | 3175 # Added since target |
3170 (added, actions['remove'], discard), | 3176 (added, actions['remove'], discard), |
3171 # Added in working directory | 3177 # Added in working directory |
3172 (dsadded, actions['forget'], discard), | 3178 (dsadded, actions['forget'], discard), |
3173 # Added since target, have local modification | 3179 # Added since target, have local modification |
3198 for table, (xlist, msg), dobackup in disptable: | 3204 for table, (xlist, msg), dobackup in disptable: |
3199 if abs not in table: | 3205 if abs not in table: |
3200 continue | 3206 continue |
3201 if xlist is not None: | 3207 if xlist is not None: |
3202 xlist.append(abs) | 3208 xlist.append(abs) |
3203 if dobackup and (backup <= dobackup | 3209 if dobackup: |
3204 or wctx[abs].cmp(ctx[abs])): | 3210 # If in interactive mode, don't automatically create |
3211 # .orig files (issue4793) | |
3212 if dobackup == backupinteractive: | |
3213 tobackup.add(abs) | |
3214 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])): | |
3205 bakname = scmutil.origpath(ui, repo, rel) | 3215 bakname = scmutil.origpath(ui, repo, rel) |
3206 ui.note(_('saving current version of %s as %s\n') % | 3216 ui.note(_('saving current version of %s as %s\n') % |
3207 (rel, bakname)) | 3217 (rel, bakname)) |
3208 if not opts.get('dry_run'): | 3218 if not opts.get('dry_run'): |
3209 if interactive: | 3219 if interactive: |
3219 break | 3229 break |
3220 | 3230 |
3221 if not opts.get('dry_run'): | 3231 if not opts.get('dry_run'): |
3222 needdata = ('revert', 'add', 'undelete') | 3232 needdata = ('revert', 'add', 'undelete') |
3223 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata]) | 3233 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata]) |
3224 _performrevert(repo, parents, ctx, actions, interactive) | 3234 _performrevert(repo, parents, ctx, actions, interactive, tobackup) |
3225 | 3235 |
3226 if targetsubs: | 3236 if targetsubs: |
3227 # Revert the subrepos on the revert list | 3237 # Revert the subrepos on the revert list |
3228 for sub in targetsubs: | 3238 for sub in targetsubs: |
3229 try: | 3239 try: |
3234 | 3244 |
3235 def _revertprefetch(repo, ctx, *files): | 3245 def _revertprefetch(repo, ctx, *files): |
3236 """Let extension changing the storage layer prefetch content""" | 3246 """Let extension changing the storage layer prefetch content""" |
3237 pass | 3247 pass |
3238 | 3248 |
3239 def _performrevert(repo, parents, ctx, actions, interactive=False): | 3249 def _performrevert(repo, parents, ctx, actions, interactive=False, |
3250 tobackup=None): | |
3240 """function that actually perform all the actions computed for revert | 3251 """function that actually perform all the actions computed for revert |
3241 | 3252 |
3242 This is an independent function to let extension to plug in and react to | 3253 This is an independent function to let extension to plug in and react to |
3243 the imminent revert. | 3254 the imminent revert. |
3244 | 3255 |
3314 | 3325 |
3315 except patch.PatchError as err: | 3326 except patch.PatchError as err: |
3316 raise error.Abort(_('error parsing patch: %s') % err) | 3327 raise error.Abort(_('error parsing patch: %s') % err) |
3317 | 3328 |
3318 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks) | 3329 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks) |
3330 if tobackup is None: | |
3331 tobackup = set() | |
3319 # Apply changes | 3332 # Apply changes |
3320 fp = stringio() | 3333 fp = stringio() |
3321 for c in chunks: | 3334 for c in chunks: |
3335 # Create a backup file only if this hunk should be backed up | |
3336 if ishunk(c) and c.header.filename() in tobackup: | |
3337 abs = c.header.filename() | |
3338 target = repo.wjoin(abs) | |
3339 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs)) | |
3340 util.copyfile(target, bakname) | |
3341 tobackup.remove(abs) | |
3322 c.write(fp) | 3342 c.write(fp) |
3323 dopatch = fp.tell() | 3343 dopatch = fp.tell() |
3324 fp.seek(0) | 3344 fp.seek(0) |
3325 if dopatch: | 3345 if dopatch: |
3326 try: | 3346 try: |