diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -1270,6 +1270,9 @@ b'ui', b'editor', default=dynamicdefault, ) coreconfigitem( + b'ui', b'detailed-exit-code', default=False, experimental=True, +) +coreconfigitem( b'ui', b'fallbackencoding', default=None, ) coreconfigitem( diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -148,6 +148,8 @@ return func() if no exception happens. otherwise do some error handling and return an exit code accordingly. does not handle all exceptions. """ + want_detailed_exit_code = ui.configbool(b'ui', b'detailed-exit-code') + exit_code = -1 try: try: return func() @@ -216,6 +218,9 @@ except error.WdirUnsupported: ui.error(_(b"abort: working directory revision cannot be specified\n")) except error.Abort as inst: + if want_detailed_exit_code: + # TODO: set exit_code for different errors here + pass ui.error(_(b"abort: %s\n") % inst.message) if inst.hint: ui.error(_(b"(%s)\n") % inst.hint) @@ -265,7 +270,7 @@ # Just in case catch this and and pass exit code to caller. return inst.code - return -1 + return exit_code def checknewlabel(repo, lbl, kind): diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -60,6 +60,8 @@ # The config knobs that will be altered (if unset) by ui.tweakdefaults. tweakrc = b""" [ui] +# Gives detailed exit codes for input/user errors, config errors, etc. +detailed-exit-code = True # The rollback command is dangerous. As a rule, don't use it. rollback = False # Make `hg status` report copy information diff --git a/tests/run-tests.py b/tests/run-tests.py --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -1439,6 +1439,7 @@ hgrc.write(b'[ui]\n') hgrc.write(b'slash = True\n') hgrc.write(b'interactive = False\n') + hgrc.write(b'detailed-exit-code = True\n') hgrc.write(b'merge = internal:merge\n') hgrc.write(b'mergemarkers = detailed\n') hgrc.write(b'promptecho = True\n') diff --git a/tests/test-basic.t b/tests/test-basic.t --- a/tests/test-basic.t +++ b/tests/test-basic.t @@ -9,6 +9,7 @@ lfs.usercache=$TESTTMP/.cache/lfs ui.slash=True ui.interactive=False + ui.detailed-exit-code=True ui.merge=internal:merge ui.mergemarkers=detailed ui.promptecho=True diff --git a/tests/test-commandserver.t b/tests/test-commandserver.t --- a/tests/test-commandserver.t +++ b/tests/test-commandserver.t @@ -211,6 +211,7 @@ lfs.usercache=$TESTTMP/.cache/lfs ui.slash=True ui.interactive=False + ui.detailed-exit-code=True ui.merge=internal:merge ui.mergemarkers=detailed ui.foo=bar @@ -222,6 +223,7 @@ *** runcommand -R foo showconfig ui defaults ui.slash=True ui.interactive=False + ui.detailed-exit-code=True ui.merge=internal:merge ui.mergemarkers=detailed ui.nontty=true diff --git a/tests/test-legacy-exit-code.t b/tests/test-legacy-exit-code.t new file mode 100644 --- /dev/null +++ b/tests/test-legacy-exit-code.t @@ -0,0 +1,35 @@ +Tests that the exit code is as expected when ui.detailed-exit-code is *not* +enabled. + + $ hg init + $ echo a > a +Expect exit code 0 on success + $ hg ci -Aqm initial + + $ hg co nonexistent + abort: unknown revision 'nonexistent'! + [255] + + $ hg co 'none()' + abort: empty revision set + [255] + + $ hg co 'invalid(' + hg: parse error at 8: not a prefix: end + (invalid( + ^ here) + [255] + + $ hg co 'invalid(' + hg: parse error at 8: not a prefix: end + (invalid( + ^ here) + [255] + + $ hg continue + abort: no operation in progress + [255] + + $ hg st --config a=b + abort: malformed --config option: 'a=b' (use --config section.name=value) + [255]