diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py index f315c04de..f5995e9ad 100644 --- a/pre_commit/commands/hook_impl.py +++ b/pre_commit/commands/hook_impl.py @@ -76,6 +76,8 @@ def _ns( remote_name: str | None = None, remote_url: str | None = None, commit_msg_filename: str | None = None, + prepare_commit_message_source: str | None = None, + commit_object_name: str | None = None, checkout_type: str | None = None, is_squash_merge: str | None = None, rewrite_command: str | None = None, @@ -90,6 +92,8 @@ def _ns( remote_name=remote_name, remote_url=remote_url, commit_msg_filename=commit_msg_filename, + prepare_commit_message_source=prepare_commit_message_source, + commit_object_name=commit_object_name, all_files=all_files, checkout_type=checkout_type, is_squash_merge=is_squash_merge, @@ -202,8 +206,20 @@ def _run_ns( _check_args_length(hook_type, args) if hook_type == 'pre-push': return _pre_push_ns(color, args, stdin) - elif hook_type in {'commit-msg', 'prepare-commit-msg'}: + elif hook_type in 'commit-msg': return _ns(hook_type, color, commit_msg_filename=args[0]) + elif hook_type == 'prepare-commit-msg' and len(args) == 1: + return _ns(hook_type, color, commit_msg_filename=args[0]) + elif hook_type == 'prepare-commit-msg' and len(args) == 2: + return _ns( + hook_type, color, commit_msg_filename=args[0], + prepare_commit_message_source=args[1], + ) + elif hook_type == 'prepare-commit-msg' and len(args) == 3: + return _ns( + hook_type, color, commit_msg_filename=args[0], + prepare_commit_message_source=args[1], commit_object_name=args[2], + ) elif hook_type in {'post-commit', 'pre-merge-commit', 'pre-commit'}: return _ns(hook_type, color) elif hook_type == 'post-checkout': diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 37f989b57..ad3d766ef 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -361,6 +361,16 @@ def run( ): return 0 + # Expose prepare_commit_message_source / commit_object_name + # as environment variables for the hooks + if args.prepare_commit_message_source: + environ['PRE_COMMIT_COMMIT_MSG_SOURCE'] = ( + args.prepare_commit_message_source + ) + + if args.commit_object_name: + environ['PRE_COMMIT_COMMIT_OBJECT_NAME'] = args.commit_object_name + # Expose from-ref / to-ref as environment variables for hooks to consume if args.from_ref and args.to_ref: # legacy names diff --git a/pre_commit/main.py b/pre_commit/main.py index 6d2814b37..41278ca98 100644 --- a/pre_commit/main.py +++ b/pre_commit/main.py @@ -107,6 +107,20 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None: '--commit-msg-filename', help='Filename to check when running during `commit-msg`', ) + parser.add_argument( + '--prepare-commit-message-source', + help=( + 'Source of the commit message ' + '(typically the second argument to .git/hooks/prepare-commit-msg)' + ), + ) + parser.add_argument( + '--commit-object-name', + help=( + 'Commit object name ' + '(typically the third argument to .git/hooks/prepare-commit-msg)' + ), + ) parser.add_argument( '--remote-name', help='Remote name used by `git push`.', ) diff --git a/testing/util.py b/testing/util.py index 0dd178406..e807f0482 100644 --- a/testing/util.py +++ b/testing/util.py @@ -76,6 +76,8 @@ def run_opts( hook_stage='commit', show_diff_on_failure=False, commit_msg_filename='', + prepare_commit_message_source='', + commit_object_name='', checkout_type='', is_squash_merge='', rewrite_command='', @@ -97,6 +99,8 @@ def run_opts( hook_stage=hook_stage, show_diff_on_failure=show_diff_on_failure, commit_msg_filename=commit_msg_filename, + prepare_commit_message_source=prepare_commit_message_source, + commit_object_name=commit_object_name, checkout_type=checkout_type, is_squash_merge=is_squash_merge, rewrite_command=rewrite_command, diff --git a/tests/commands/hook_impl_test.py b/tests/commands/hook_impl_test.py index 3e20874e3..aa321dabc 100644 --- a/tests/commands/hook_impl_test.py +++ b/tests/commands/hook_impl_test.py @@ -154,6 +154,42 @@ def test_run_ns_commit_msg(): assert ns.commit_msg_filename == '.git/COMMIT_MSG' +def test_run_ns_prepare_commit_msg_one_arg(): + ns = hook_impl._run_ns( + 'prepare-commit-msg', False, + ('.git/COMMIT_MSG',), b'', + ) + assert ns is not None + assert ns.hook_stage == 'prepare-commit-msg' + assert ns.color is False + assert ns.commit_msg_filename == '.git/COMMIT_MSG' + + +def test_run_ns_prepare_commit_msg_two_arg(): + ns = hook_impl._run_ns( + 'prepare-commit-msg', False, + ('.git/COMMIT_MSG', 'message'), b'', + ) + assert ns is not None + assert ns.hook_stage == 'prepare-commit-msg' + assert ns.color is False + assert ns.commit_msg_filename == '.git/COMMIT_MSG' + assert ns.prepare_commit_message_source == 'message' + + +def test_run_ns_prepare_commit_msg_three_arg(): + ns = hook_impl._run_ns( + 'prepare-commit-msg', False, + ('.git/COMMIT_MSG', 'message', 'HEAD'), b'', + ) + assert ns is not None + assert ns.hook_stage == 'prepare-commit-msg' + assert ns.color is False + assert ns.commit_msg_filename == '.git/COMMIT_MSG' + assert ns.prepare_commit_message_source == 'message' + assert ns.commit_object_name == 'HEAD' + + def test_run_ns_post_commit(): ns = hook_impl._run_ns('post-commit', True, (), b'') assert ns is not None diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index 085b063f3..2634c0c5e 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -810,7 +810,12 @@ def test_prepare_commit_msg_hook(cap_out, store, prepare_commit_msg_repo): cap_out, store, prepare_commit_msg_repo, - {'hook_stage': 'prepare-commit-msg', 'commit_msg_filename': filename}, + { + 'hook_stage': 'prepare-commit-msg', + 'commit_msg_filename': filename, + 'prepare_commit_message_source': 'commit', + 'commit_object_name': 'HEAD', + }, expected_outputs=[b'Add "Signed off by:"', b'Passed'], expected_ret=0, stage=False,