diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py index 4843fc77..d0e226f8 100644 --- a/pre_commit/commands/hook_impl.py +++ b/pre_commit/commands/hook_impl.py @@ -150,6 +150,7 @@ def _pre_push_ns( _EXPECTED_ARG_LENGTH_BY_HOOK = { 'commit-msg': 1, 'post-checkout': 3, + 'post-commit': 0, 'pre-commit': 0, 'pre-merge-commit': 0, 'pre-push': 2, @@ -186,7 +187,7 @@ def _run_ns( return _pre_push_ns(color, args, stdin) elif hook_type in {'commit-msg', 'prepare-commit-msg'}: return _ns(hook_type, color, commit_msg_filename=args[0]) - elif hook_type in {'pre-merge-commit', 'pre-commit'}: + elif hook_type in {'post-commit', 'pre-merge-commit', 'pre-commit'}: return _ns(hook_type, color) elif hook_type == 'post-checkout': return _ns( diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 8c8401ce..8a9352d4 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -221,7 +221,8 @@ def _compute_cols(hooks: Sequence[Hook]) -> int: def _all_filenames(args: argparse.Namespace) -> Collection[str]: - if args.hook_stage == 'post-checkout': # no files for post-checkout + # these hooks do not operate on files + if args.hook_stage in {'post-checkout', 'post-commit'}: return () elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}: return (args.commit_msg_filename,) diff --git a/pre_commit/constants.py b/pre_commit/constants.py index e2b8e3ac..5150fdcf 100644 --- a/pre_commit/constants.py +++ b/pre_commit/constants.py @@ -17,8 +17,8 @@ VERSION = importlib_metadata.version('pre_commit') # `manual` is not invoked by any installed git hook. See #719 STAGES = ( - 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg', 'manual', - 'post-checkout', 'push', + 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg', + 'post-commit', 'manual', 'post-checkout', 'push', ) DEFAULT = 'default' diff --git a/pre_commit/main.py b/pre_commit/main.py index 790b3477..874eb53a 100644 --- a/pre_commit/main.py +++ b/pre_commit/main.py @@ -79,7 +79,7 @@ def _add_hook_type_option(parser: argparse.ArgumentParser) -> None: parser.add_argument( '-t', '--hook-type', choices=( 'pre-commit', 'pre-merge-commit', 'pre-push', - 'prepare-commit-msg', 'commit-msg', 'post-checkout', + 'prepare-commit-msg', 'commit-msg', 'post-commit', 'post-checkout', ), action=AppendReplaceDefault, default=['pre-commit'], diff --git a/tests/commands/hook_impl_test.py b/tests/commands/hook_impl_test.py index ddf65b77..cce4a258 100644 --- a/tests/commands/hook_impl_test.py +++ b/tests/commands/hook_impl_test.py @@ -96,6 +96,7 @@ def test_run_legacy_recursive(tmpdir): ('pre-merge-commit', []), ('pre-push', ['branch_name', 'remote_name']), ('commit-msg', ['.git/COMMIT_EDITMSG']), + ('post-commit', []), ('post-checkout', ['old_head', 'new_head', '1']), # multiple choices for commit-editmsg ('prepare-commit-msg', ['.git/COMMIT_EDITMSG']), @@ -149,6 +150,13 @@ def test_run_ns_commit_msg(): assert ns.commit_msg_filename == '.git/COMMIT_MSG' +def test_run_ns_post_commit(): + ns = hook_impl._run_ns('post-commit', True, (), b'') + assert ns is not None + assert ns.hook_stage == 'post-commit' + assert ns.color is True + + def test_run_ns_post_checkout(): ns = hook_impl._run_ns('post-checkout', True, ('a', 'b', 'c'), b'') assert ns is not None diff --git a/tests/commands/install_uninstall_test.py b/tests/commands/install_uninstall_test.py index 66b91903..6d75e68a 100644 --- a/tests/commands/install_uninstall_test.py +++ b/tests/commands/install_uninstall_test.py @@ -726,6 +726,32 @@ def test_commit_msg_legacy(commit_msg_repo, tempdir_factory, store): assert second_line.startswith('Must have "Signed off by:"...') +def test_post_commit_integration(tempdir_factory, store): + path = git_dir(tempdir_factory) + config = [ + { + 'repo': 'local', + 'hooks': [{ + 'id': 'post-commit', + 'name': 'Post commit', + 'entry': 'touch post-commit.tmp', + 'language': 'system', + 'always_run': True, + 'verbose': True, + 'stages': ['post-commit'], + }], + }, + ] + write_config(path, config) + with cwd(path): + _get_commit_output(tempdir_factory) + assert not os.path.exists('post-commit.tmp') + + install(C.CONFIG_FILE, store, hook_types=['post-commit']) + _get_commit_output(tempdir_factory) + assert os.path.exists('post-commit.tmp') + + def test_post_checkout_integration(tempdir_factory, store): path = git_dir(tempdir_factory) config = [ diff --git a/tests/repository_test.py b/tests/repository_test.py index 3c7a6372..f55c34c8 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -880,7 +880,7 @@ def test_manifest_hooks(tempdir_factory, store): require_serial=False, stages=( 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg', - 'manual', 'post-checkout', 'push', + 'post-commit', 'manual', 'post-checkout', 'push', ), types=['file'], verbose=False,