Mercurial > hg
comparison hgext/fix.py @ 40532:93bab80993f4
fix: add a config to abort when a fixer tool fails
This allows users to stop and address tool failures before proceeding, instead
of the default behavior of continuing to apply any tools that didn't fail. For
example, a code formatting tool could fail if you have syntax errors, and you
might want your repo to stay in its current state while you fix the syntax
error before re-running 'hg fix'. It's conceivable that this would even be
necessary for the correctness of some fixer tools across a chain of revisions.
Differential Revision: https://phab.mercurial-scm.org/D5200
author | Danny Hooper <hooper@google.com> |
---|---|
date | Wed, 31 Oct 2018 13:11:51 -0700 |
parents | 8ebb05f747e5 |
children | 2ecf5c24d0cd |
comparison
equal
deleted
inserted
replaced
40531:e6c8a0fd3db4 | 40532:93bab80993f4 |
---|---|
17 clang-format:linerange=--lines={first}:{last} | 17 clang-format:linerange=--lines={first}:{last} |
18 clang-format:fileset=set:**.cpp or **.hpp | 18 clang-format:fileset=set:**.cpp or **.hpp |
19 | 19 |
20 The :command suboption forms the first part of the shell command that will be | 20 The :command suboption forms the first part of the shell command that will be |
21 used to fix a file. The content of the file is passed on standard input, and the | 21 used to fix a file. The content of the file is passed on standard input, and the |
22 fixed file content is expected on standard output. If there is any output on | 22 fixed file content is expected on standard output. Any output on standard error |
23 standard error, the file will not be affected. Some values may be substituted | 23 will be displayed as a warning. If the exit status is not zero, the file will |
24 into the command:: | 24 not be affected. A placeholder warning is displayed if there is a non-zero exit |
25 status but no standard error output. Some values may be substituted into the | |
26 command:: | |
25 | 27 |
26 {rootpath} The path of the file being fixed, relative to the repo root | 28 {rootpath} The path of the file being fixed, relative to the repo root |
27 {basename} The name of the file being fixed, without the directory path | 29 {basename} The name of the file being fixed, without the directory path |
28 | 30 |
29 If the :linerange suboption is set, the tool will only be run if there are | 31 If the :linerange suboption is set, the tool will only be run if there are |
40 | 42 |
41 There is also a configurable limit for the maximum size of file that will be | 43 There is also a configurable limit for the maximum size of file that will be |
42 processed by :hg:`fix`:: | 44 processed by :hg:`fix`:: |
43 | 45 |
44 [fix] | 46 [fix] |
45 maxfilesize=2MB | 47 maxfilesize = 2MB |
48 | |
49 Normally, execution of configured tools will continue after a failure (indicated | |
50 by a non-zero exit status). It can also be configured to abort after the first | |
51 such failure, so that no files will be affected if any tool fails. This abort | |
52 will also cause :hg:`fix` to exit with a non-zero status:: | |
53 | |
54 [fix] | |
55 failure = abort | |
46 | 56 |
47 """ | 57 """ |
48 | 58 |
49 from __future__ import absolute_import | 59 from __future__ import absolute_import |
50 | 60 |
97 | 107 |
98 # A good default size allows most source code files to be fixed, but avoids | 108 # A good default size allows most source code files to be fixed, but avoids |
99 # letting fixer tools choke on huge inputs, which could be surprising to the | 109 # letting fixer tools choke on huge inputs, which could be surprising to the |
100 # user. | 110 # user. |
101 configitem('fix', 'maxfilesize', default='2MB') | 111 configitem('fix', 'maxfilesize', default='2MB') |
112 | |
113 # Allow fix commands to exit non-zero if an executed fixer tool exits non-zero. | |
114 # This helps users do shell scripts that stop when a fixer tool signals a | |
115 # problem. | |
116 configitem('fix', 'failure', default='continue') | |
117 | |
118 def checktoolfailureaction(ui, message, hint=None): | |
119 """Abort with 'message' if fix.failure=abort""" | |
120 action = ui.config('fix', 'failure') | |
121 if action not in ('continue', 'abort'): | |
122 raise error.Abort(_('unknown fix.failure action: %s') % (action,), | |
123 hint=_('use "continue" or "abort"')) | |
124 if action == 'abort': | |
125 raise error.Abort(message, hint=hint) | |
102 | 126 |
103 allopt = ('', 'all', False, _('fix all non-public non-obsolete revisions')) | 127 allopt = ('', 'all', False, _('fix all non-public non-obsolete revisions')) |
104 baseopt = ('', 'base', [], _('revisions to diff against (overrides automatic ' | 128 baseopt = ('', 'base', [], _('revisions to diff against (overrides automatic ' |
105 'selection, and applies to every revision being ' | 129 'selection, and applies to every revision being ' |
106 'fixed)'), _('REV')) | 130 'fixed)'), _('REV')) |
463 newerdata, stderr = proc.communicate(newdata) | 487 newerdata, stderr = proc.communicate(newdata) |
464 if stderr: | 488 if stderr: |
465 showstderr(ui, fixctx.rev(), fixername, stderr) | 489 showstderr(ui, fixctx.rev(), fixername, stderr) |
466 if proc.returncode == 0: | 490 if proc.returncode == 0: |
467 newdata = newerdata | 491 newdata = newerdata |
468 elif not stderr: | 492 else: |
469 showstderr(ui, fixctx.rev(), fixername, | 493 if not stderr: |
470 _('exited with status %d\n') % (proc.returncode,)) | 494 message = _('exited with status %d\n') % (proc.returncode,) |
495 showstderr(ui, fixctx.rev(), fixername, message) | |
496 checktoolfailureaction( | |
497 ui, _('no fixes will be applied'), | |
498 hint=_('use --config fix.failure=continue to apply any ' | |
499 'successful fixes anyway')) | |
471 return newdata | 500 return newdata |
472 | 501 |
473 def showstderr(ui, rev, fixername, stderr): | 502 def showstderr(ui, rev, fixername, stderr): |
474 """Writes the lines of the stderr string as warnings on the ui | 503 """Writes the lines of the stderr string as warnings on the ui |
475 | 504 |