comparison mercurial/cmdutil.py @ 41987:c1d83d916e85

revert: option to choose what to keep, not what to discard I know the you (the reader) are probably tired of discussing how `hg revert -i -r .` should behave and so am I. And I know I'm one of the people who argued that showing the diff from the working copy to the parent was confusing. I think it is less confusing now that we show the diff from the parent to the working copy, but I still find it confusing. I think showing the diff of hunks to keep might make it easier to understand. So that's what this patch provides an option for. One argument doing it this way is that most people seem to find `hg split` natural. I suspect that is because it shows the forward diff (from parent commit to the commit) and asks you what to put in the first commit. I think the new "keep" mode for revert (this patch) matches that. In "keep" mode, all the changes are still selected by default. That means that `hg revert -i` followed by 'A' (keep all) (or 'c' in curses) will be different from `hg revert -a`. That's mostly because that was simplest. It can also be argued that it's safest. But it can also be argued that it should be consistent with `hg revert -a`. Note that in this mode, you can edit the hunks and it will do what you expect (e.g. add new lines to your file if you added a new lines when editing). The test case shows that that works. Differential Revision: https://phab.mercurial-scm.org/D6125
author Martin von Zweigbergk <martinvonz@google.com>
date Tue, 12 Mar 2019 14:17:41 -0700
parents b1bc6e5f5249
children 550a172a603b
comparison
equal deleted inserted replaced
41986:95e4ae86329f 41987:c1d83d916e85
3174 diffopts = patch.difffeatureopts(repo.ui, whitespace=True, 3174 diffopts = patch.difffeatureopts(repo.ui, whitespace=True,
3175 section='commands', 3175 section='commands',
3176 configprefix='revert.interactive.') 3176 configprefix='revert.interactive.')
3177 diffopts.nodates = True 3177 diffopts.nodates = True
3178 diffopts.git = True 3178 diffopts.git = True
3179 operation = 'discard' 3179 operation = 'apply'
3180 reversehunks = True 3180 if node == parent:
3181 if node != parent: 3181 if repo.ui.configbool('experimental',
3182 operation = 'apply' 3182 'revert.interactive.select-to-keep'):
3183 reversehunks = False 3183 operation = 'keep'
3184 if reversehunks: 3184 else:
3185 operation = 'discard'
3186
3187 if operation == 'apply':
3188 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3189 else:
3185 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts) 3190 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3186 else:
3187 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3188 originalchunks = patch.parsepatch(diff) 3191 originalchunks = patch.parsepatch(diff)
3189 3192
3190 try: 3193 try:
3191 3194
3192 chunks, opts = recordfilter(repo.ui, originalchunks, 3195 chunks, opts = recordfilter(repo.ui, originalchunks,
3193 operation=operation) 3196 operation=operation)
3194 if reversehunks: 3197 if operation == 'discard':
3195 chunks = patch.reversehunks(chunks) 3198 chunks = patch.reversehunks(chunks)
3196 3199
3197 except error.PatchError as err: 3200 except error.PatchError as err:
3198 raise error.Abort(_('error parsing patch: %s') % err) 3201 raise error.Abort(_('error parsing patch: %s') % err)
3199 3202
3203 # Apply changes 3206 # Apply changes
3204 fp = stringio() 3207 fp = stringio()
3205 # chunks are serialized per file, but files aren't sorted 3208 # chunks are serialized per file, but files aren't sorted
3206 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))): 3209 for f in sorted(set(c.header.filename() for c in chunks if ishunk(c))):
3207 prntstatusmsg('revert', f) 3210 prntstatusmsg('revert', f)
3211 files = set()
3208 for c in chunks: 3212 for c in chunks:
3209 if ishunk(c): 3213 if ishunk(c):
3210 abs = c.header.filename() 3214 abs = c.header.filename()
3211 # Create a backup file only if this hunk should be backed up 3215 # Create a backup file only if this hunk should be backed up
3212 if c.header.filename() in tobackup: 3216 if c.header.filename() in tobackup:
3213 target = repo.wjoin(abs) 3217 target = repo.wjoin(abs)
3214 bakname = scmutil.backuppath(repo.ui, repo, abs) 3218 bakname = scmutil.backuppath(repo.ui, repo, abs)
3215 util.copyfile(target, bakname) 3219 util.copyfile(target, bakname)
3216 tobackup.remove(abs) 3220 tobackup.remove(abs)
3221 if abs not in files:
3222 files.add(abs)
3223 if operation == 'keep':
3224 checkout(abs)
3217 c.write(fp) 3225 c.write(fp)
3218 dopatch = fp.tell() 3226 dopatch = fp.tell()
3219 fp.seek(0) 3227 fp.seek(0)
3220 if dopatch: 3228 if dopatch:
3221 try: 3229 try: