Repository: pre-commit/pre-commit

Issue:
prepare-commit-msg Not passing all parameters
### search tried in the issue tracker

Yes

### describe your issue

When I run `git commit` with certain arguments I expect to get two or more arguments passed to argv of my script as per the specification:

<img width="692" alt="image" src="https://user-images.githubusercontent.com/32771451/170735134-b8162715-0725-4375-b98c-32bbb1333320.png">
 

```python
def main(argv, argc):
     print(argv, argc)
```

Will print 
```
['tools/hooks/add-commit-template.py', '.git/COMMIT_EDITMSG'] 2
```
It looks to me from looking at your auto-created hook installed that you are passing all args to the script so it maybe I haven't configured this correctly.

Thanks


### pre-commit --version

pre-commit 2.19.0

### .pre-commit-config.yaml

```yaml
repos:
   - repo:
     hooks:
        - id: commit-template-local
          name: Provide Commit Message Template
          entry: tools/hooks/add-commit-template.py
          language: python
          stages: [ prepare-commit-msg ]
```


### ~/.cache/pre-commit/pre-commit.log (if present)

_No response_


Discussion:

Resolving patch:
diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py
--- 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
--- 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
--- 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`.',
     )


Test patch:
diff --git a/testing/util.py b/testing/util.py
--- 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
--- 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
--- 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,