From 80e6149cf2a2c11b0f829d9fcb75966ab53b7fd0 Mon Sep 17 00:00:00 2001 From: Alessio Izzo Date: Thu, 18 Jan 2024 09:33:01 +0100 Subject: [PATCH 1/3] add check on pre-push stage: exit if staged files not committed yet --- .gitignore | 1 + pre_commit/commands/run.py | 9 ++++++++- tests/commands/run_test.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c2021816c..12790fccf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /.tox /dist .vscode/ +.idea diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 8ab505ffb..3c356dbee 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -371,7 +371,14 @@ def run( environ.get('_PRE_COMMIT_SKIP_POST_CHECKOUT') ): return 0 - + # prevent pushing staged files not committed (#2486) + if ( + args.hook_stage == 'pre-push' and git.get_staged_files() + ): + logger.error( + 'Staged files found. Please commit before pushing', + ) + return 1 # Expose prepare_commit_message_source / commit_object_name # as environment variables for the hooks if args.prepare_commit_message_source: diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index e4af1e162..ea9012c8b 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -1246,3 +1246,36 @@ def test_pre_commit_env_variable_set(cap_out, store, repo_with_passing_hook): cap_out, store, repo_with_passing_hook, args, environ, ) assert environ['PRE_COMMIT'] == '1' + + +def test_pre_push_fails_if_staged_files( + cap_out, store, repo_with_passing_hook, +): + config = { + 'repo': 'local', + 'hooks': [{ + 'id': 'no-todo', + 'name': 'No TODO', + 'entry': 'sh -c "! grep -iI todo $@" --', + 'language': 'system', + 'stage': 'pre-push', + }], + } + add_config_to_repo(repo_with_passing_hook, config) + + with open('placeholder.py', 'w') as staged_file: + staged_file.write('"""TODO: something"""\n') + cmd_output('git', 'add', 'placeholder.py') + git_commit() + with open('placeholder.py', 'w') as staged_file: + staged_file.write('"""Ok"""\n') + + cmd_output('git', 'add', 'placeholder.py') + + args = run_opts(hook_stage='pre-push') + ret, printed = _do_run(cap_out, store, repo_with_passing_hook, args) + assert ret == 1 + assert printed == ( + b'[ERROR] Staged files found.' + b' Please commit before pushing\n' + ) From e36cdc227c825aec8b6bff1b510d5e40afec2acc Mon Sep 17 00:00:00 2001 From: Alessio Izzo Date: Thu, 18 Jan 2024 10:02:52 +0100 Subject: [PATCH 2/3] fix failing tests --- tests/commands/run_test.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index ea9012c8b..de65e45a5 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -362,6 +362,12 @@ def test_show_diff_on_failure( {'hook': 'nope', 'hook_stage': 'pre-push'}, (b'No hook with id `nope` in stage `pre-push`',), 1, + False, + ), + ( + {'hook': 'nope', 'hook_stage': 'pre-push'}, + (b'[ERROR] Staged files found. Please commit before pushing\n',), + 1, True, ), ( @@ -828,7 +834,7 @@ def test_stages(cap_out, store, repo_with_passing_hook): 'language': 'pygrep', 'stages': [stage], } - for i, stage in enumerate(('pre-commit', 'pre-push', 'manual'), 1) + for i, stage in enumerate(('pre-commit', 'manual'), 1) ], } add_config_to_repo(repo_with_passing_hook, config) @@ -844,8 +850,7 @@ def _run_for_stage(stage): return printed assert _run_for_stage('pre-commit').startswith(b'hook 1...') - assert _run_for_stage('pre-push').startswith(b'hook 2...') - assert _run_for_stage('manual').startswith(b'hook 3...') + assert _run_for_stage('manual').startswith(b'hook 2...') def test_commit_msg_hook(cap_out, store, commit_msg_repo): From f6c6b6c55f36d9b39cbbc764e9b10268be33e086 Mon Sep 17 00:00:00 2001 From: Alessio Izzo Date: Mon, 26 Feb 2024 16:04:02 +0100 Subject: [PATCH 3/3] improvements/fixes after code review --- .gitignore | 1 - pre_commit/commands/run.py | 8 ++------ tests/commands/run_test.py | 6 ------ 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 12790fccf..c2021816c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ /.tox /dist .vscode/ -.idea diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 3c356dbee..62e19b7c8 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -372,12 +372,8 @@ def run( ): return 0 # prevent pushing staged files not committed (#2486) - if ( - args.hook_stage == 'pre-push' and git.get_staged_files() - ): - logger.error( - 'Staged files found. Please commit before pushing', - ) + if args.hook_stage == 'pre-push' and git.get_staged_files(): + logger.error('Staged files found. Please commit before pushing') return 1 # Expose prepare_commit_message_source / commit_object_name # as environment variables for the hooks diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index de65e45a5..924340b78 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -364,12 +364,6 @@ def test_show_diff_on_failure( 1, False, ), - ( - {'hook': 'nope', 'hook_stage': 'pre-push'}, - (b'[ERROR] Staged files found. Please commit before pushing\n',), - 1, - True, - ), ( {'all_files': True, 'verbose': True}, (b'foo.py',),