diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 6d3851f0..0ce0f383 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import logging import os +import subprocess import sys from pre_commit import color @@ -152,6 +153,13 @@ def _run_hooks(repo_hooks, args, environ): retval = 0 for repo, hook in repo_hooks: retval |= _run_single_hook(hook, repo, args, skips, cols) + if ( + retval and + args.show_diff_on_failure and + subprocess.call(('git', 'diff', '--quiet')) != 0 + ): + print('All changes made by hooks:') + subprocess.call(('git', 'diff')) return retval diff --git a/pre_commit/main.py b/pre_commit/main.py index 109f4dbf..f96eafb2 100644 --- a/pre_commit/main.py +++ b/pre_commit/main.py @@ -149,6 +149,10 @@ def main(argv=None): '--hook-stage', choices=('commit', 'push'), default='commit', help='The stage during which the hook is fired e.g. commit or push.', ) + run_parser.add_argument( + '--show-diff-on-failure', action='store_true', + help='When hooks fail, run `git diff` directly afterward.', + ) run_mutex_group = run_parser.add_mutually_exclusive_group(required=False) run_mutex_group.add_argument( '--all-files', '-a', action='store_true', default=False, diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index 056ec8f6..5e1642e2 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -58,6 +58,7 @@ def _get_opts( source='', allow_unstaged_config=False, hook_stage='commit', + show_diff_on_failure=False, ): # These are mutually exclusive assert not (all_files and files) @@ -67,11 +68,12 @@ def _get_opts( color=color, verbose=verbose, hook=hook, - hook_stage=hook_stage, no_stash=no_stash, origin=origin, source=source, allow_unstaged_config=allow_unstaged_config, + hook_stage=hook_stage, + show_diff_on_failure=show_diff_on_failure, ) @@ -151,6 +153,23 @@ def test_hook_that_modifies_but_returns_zero( ) +def test_show_diff_on_failure( + capfd, cap_out, tempdir_factory, mock_out_store_directory, +): + git_path = make_consuming_repo( + tempdir_factory, 'modified_file_returns_zero_repo', + ) + with cwd(git_path): + stage_a_file('bar.py') + _test_run( + cap_out, git_path, {'show_diff_on_failure': True}, + # we're only testing the output after running + (), 1, True, + ) + out, _ = capfd.readouterr() + assert 'diff --git' in out + + @pytest.mark.parametrize( ('options', 'outputs', 'expected_ret', 'stage'), (