comparison mercurial/cmdutil.py @ 48363:6a454e7053a1

errors: return more detailed errors when failing to parse or apply patch This patch adds subclasses of `PatchError` so we can distinguish between failure to parse a patch from failure to apply it. It updates the callers to raise either `InputError` or `StateError` depending on which type of error occurred. Differential Revision: https://phab.mercurial-scm.org/D11824
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 19 Nov 2021 12:57:53 -0800
parents c62e4397eb28
children 220506bb213e
comparison
equal deleted inserted replaced
48362:7e6488aa1261 48363:6a454e7053a1
520 match = scmutil.match(repo[None], pats) 520 match = scmutil.match(repo[None], pats)
521 521
522 # 1. filter patch, since we are intending to apply subset of it 522 # 1. filter patch, since we are intending to apply subset of it
523 try: 523 try:
524 chunks, newopts = filterfn(ui, original_headers, match) 524 chunks, newopts = filterfn(ui, original_headers, match)
525 except error.PatchError as err: 525 except error.PatchParseError as err:
526 raise error.InputError(_(b'error parsing patch: %s') % err) 526 raise error.InputError(_(b'error parsing patch: %s') % err)
527 except error.PatchApplicationError as err:
528 raise error.StateError(_(b'error applying patch: %s') % err)
527 opts.update(newopts) 529 opts.update(newopts)
528 530
529 # We need to keep a backup of files that have been newly added and 531 # We need to keep a backup of files that have been newly added and
530 # modified during the recording process because there is a previous 532 # modified during the recording process because there is a previous
531 # version without the edit in the workdir. We also will need to restore 533 # version without the edit in the workdir. We also will need to restore
606 if dopatch: 608 if dopatch:
607 try: 609 try:
608 ui.debug(b'applying patch\n') 610 ui.debug(b'applying patch\n')
609 ui.debug(fp.getvalue()) 611 ui.debug(fp.getvalue())
610 patch.internalpatch(ui, repo, fp, 1, eolmode=None) 612 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
611 except error.PatchError as err: 613 except error.PatchParseError as err:
612 raise error.InputError(pycompat.bytestr(err)) 614 raise error.InputError(pycompat.bytestr(err))
615 except error.PatchApplicationError as err:
616 raise error.StateError(pycompat.bytestr(err))
613 del fp 617 del fp
614 618
615 # 4. We prepared working directory according to filtered 619 # 4. We prepared working directory according to filtered
616 # patch. Now is the time to delegate the job to 620 # patch. Now is the time to delegate the job to
617 # commit/qrefresh or the like! 621 # commit/qrefresh or the like!
2018 prefix=prefix, 2022 prefix=prefix,
2019 files=files, 2023 files=files,
2020 eolmode=None, 2024 eolmode=None,
2021 similarity=sim / 100.0, 2025 similarity=sim / 100.0,
2022 ) 2026 )
2023 except error.PatchError as e: 2027 except error.PatchParseError as e:
2028 raise error.InputError(pycompat.bytestr(e))
2029 except error.PatchApplicationError as e:
2024 if not partial: 2030 if not partial:
2025 raise error.Abort(pycompat.bytestr(e)) 2031 raise error.StateError(pycompat.bytestr(e))
2026 if partial: 2032 if partial:
2027 rejects = True 2033 rejects = True
2028 2034
2029 files = list(files) 2035 files = list(files)
2030 if nocommit: 2036 if nocommit:
2077 strip, 2083 strip,
2078 prefix, 2084 prefix,
2079 files, 2085 files,
2080 eolmode=None, 2086 eolmode=None,
2081 ) 2087 )
2082 except error.PatchError as e: 2088 except error.PatchParseError as e:
2083 raise error.Abort(stringutil.forcebytestr(e)) 2089 raise error.InputError(stringutil.forcebytestr(e))
2090 except error.PatchApplicationError as e:
2091 raise error.StateError(stringutil.forcebytestr(e))
2084 if opts.get(b'exact'): 2092 if opts.get(b'exact'):
2085 editor = None 2093 editor = None
2086 else: 2094 else:
2087 editor = getcommiteditor(editform=b'import.bypass') 2095 editor = getcommiteditor(editform=b'import.bypass')
2088 memctx = context.memctx( 2096 memctx = context.memctx(
3672 repo.ui, original_headers, match, operation=operation 3680 repo.ui, original_headers, match, operation=operation
3673 ) 3681 )
3674 if operation == b'discard': 3682 if operation == b'discard':
3675 chunks = patch.reversehunks(chunks) 3683 chunks = patch.reversehunks(chunks)
3676 3684
3677 except error.PatchError as err: 3685 except error.PatchParseError as err:
3678 raise error.Abort(_(b'error parsing patch: %s') % err) 3686 raise error.InputError(_(b'error parsing patch: %s') % err)
3687 except error.PatchApplicationError as err:
3688 raise error.StateError(_(b'error applying patch: %s') % err)
3679 3689
3680 # FIXME: when doing an interactive revert of a copy, there's no way of 3690 # FIXME: when doing an interactive revert of a copy, there's no way of
3681 # performing a partial revert of the added file, the only option is 3691 # performing a partial revert of the added file, the only option is
3682 # "remove added file <name> (Yn)?", so we don't need to worry about the 3692 # "remove added file <name> (Yn)?", so we don't need to worry about the
3683 # alsorestore value. Ideally we'd be able to partially revert 3693 # alsorestore value. Ideally we'd be able to partially revert
3708 dopatch = fp.tell() 3718 dopatch = fp.tell()
3709 fp.seek(0) 3719 fp.seek(0)
3710 if dopatch: 3720 if dopatch:
3711 try: 3721 try:
3712 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None) 3722 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3713 except error.PatchError as err: 3723 except error.PatchParseError as err:
3714 raise error.Abort(pycompat.bytestr(err)) 3724 raise error.InputError(pycompat.bytestr(err))
3725 except error.PatchApplicationError as err:
3726 raise error.StateError(pycompat.bytestr(err))
3715 del fp 3727 del fp
3716 else: 3728 else:
3717 for f in actions[b'revert'][0]: 3729 for f in actions[b'revert'][0]:
3718 prntstatusmsg(b'revert', f) 3730 prntstatusmsg(b'revert', f)
3719 checkout(f) 3731 checkout(f)