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: