Skip to content

Commit 6b73138

Browse files
Paweł Sacawaasottile
authored andcommitted
Add: post-merge hook support
1 parent d7b189c commit 6b73138

File tree

9 files changed

+82
-5
lines changed

9 files changed

+82
-5
lines changed

pre_commit/commands/hook_impl.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def _ns(
7676
remote_url: Optional[str] = None,
7777
commit_msg_filename: Optional[str] = None,
7878
checkout_type: Optional[str] = None,
79+
is_squash_merge: Optional[str] = None,
7980
) -> argparse.Namespace:
8081
return argparse.Namespace(
8182
color=color,
@@ -88,6 +89,7 @@ def _ns(
8889
commit_msg_filename=commit_msg_filename,
8990
all_files=all_files,
9091
checkout_type=checkout_type,
92+
is_squash_merge=is_squash_merge,
9193
files=(),
9294
hook=None,
9395
verbose=False,
@@ -158,6 +160,7 @@ def _pre_push_ns(
158160
'post-commit': 0,
159161
'pre-commit': 0,
160162
'pre-merge-commit': 0,
163+
'post-merge': 1,
161164
'pre-push': 2,
162165
}
163166

@@ -199,6 +202,8 @@ def _run_ns(
199202
hook_type, color,
200203
from_ref=args[0], to_ref=args[1], checkout_type=args[2],
201204
)
205+
elif hook_type == 'post-merge':
206+
return _ns(hook_type, color, is_squash_merge=args[0])
202207
else:
203208
raise AssertionError(f'unexpected hook type: {hook_type}')
204209

pre_commit/commands/run.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def _compute_cols(hooks: Sequence[Hook]) -> int:
245245

246246
def _all_filenames(args: argparse.Namespace) -> Collection[str]:
247247
# these hooks do not operate on files
248-
if args.hook_stage in {'post-checkout', 'post-commit'}:
248+
if args.hook_stage in {'post-checkout', 'post-commit', 'post-merge'}:
249249
return ()
250250
elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}:
251251
return (args.commit_msg_filename,)
@@ -379,6 +379,9 @@ def run(
379379
if args.checkout_type:
380380
environ['PRE_COMMIT_CHECKOUT_TYPE'] = args.checkout_type
381381

382+
if args.is_squash_merge:
383+
environ['PRE_COMMIT_IS_SQUASH_MERGE'] = args.is_squash_merge
384+
382385
# Set pre_commit flag
383386
environ['PRE_COMMIT'] = '1'
384387

pre_commit/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# `manual` is not invoked by any installed git hook. See #719
1919
STAGES = (
2020
'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg',
21-
'post-commit', 'manual', 'post-checkout', 'push',
21+
'post-commit', 'manual', 'post-checkout', 'push', 'post-merge',
2222
)
2323

2424
DEFAULT = 'default'

pre_commit/main.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ def __call__(
6767
def _add_hook_type_option(parser: argparse.ArgumentParser) -> None:
6868
parser.add_argument(
6969
'-t', '--hook-type', choices=(
70-
'pre-commit', 'pre-merge-commit', 'pre-push',
71-
'prepare-commit-msg', 'commit-msg', 'post-commit', 'post-checkout',
70+
'pre-commit', 'pre-merge-commit', 'pre-push', 'prepare-commit-msg',
71+
'commit-msg', 'post-commit', 'post-checkout', 'post-merge',
7272
),
7373
action=AppendReplaceDefault,
7474
default=['pre-commit'],
@@ -136,6 +136,13 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
136136
'file from the index, flag=0).'
137137
),
138138
)
139+
parser.add_argument(
140+
'--is-squash-merge',
141+
help=(
142+
'During a post-merge hook, indicates whether the merge was a '
143+
'squash merge'
144+
),
145+
)
139146

140147

141148
def _adjust_args_and_chdir(args: argparse.Namespace) -> None:

testing/util.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def run_opts(
7070
show_diff_on_failure=False,
7171
commit_msg_filename='',
7272
checkout_type='',
73+
is_squash_merge='',
7374
):
7475
# These are mutually exclusive
7576
assert not (all_files and files)
@@ -88,6 +89,7 @@ def run_opts(
8889
show_diff_on_failure=show_diff_on_failure,
8990
commit_msg_filename=commit_msg_filename,
9091
checkout_type=checkout_type,
92+
is_squash_merge=is_squash_merge,
9193
)
9294

9395

tests/commands/hook_impl_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def call(*_, **__):
9797
('pre-push', ['branch_name', 'remote_name']),
9898
('commit-msg', ['.git/COMMIT_EDITMSG']),
9999
('post-commit', []),
100+
('post-merge', ['1']),
100101
('post-checkout', ['old_head', 'new_head', '1']),
101102
# multiple choices for commit-editmsg
102103
('prepare-commit-msg', ['.git/COMMIT_EDITMSG']),
@@ -157,6 +158,14 @@ def test_run_ns_post_commit():
157158
assert ns.color is True
158159

159160

161+
def test_run_ns_post_merge():
162+
ns = hook_impl._run_ns('post-merge', True, ('1',), b'')
163+
assert ns is not None
164+
assert ns.hook_stage == 'post-merge'
165+
assert ns.color is True
166+
assert ns.is_squash_merge == '1'
167+
168+
160169
def test_run_ns_post_checkout():
161170
ns = hook_impl._run_ns('post-checkout', True, ('a', 'b', 'c'), b'')
162171
assert ns is not None

tests/commands/install_uninstall_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,48 @@ def test_post_commit_integration(tempdir_factory, store):
759759
assert os.path.exists('post-commit.tmp')
760760

761761

762+
def test_post_merge_integration(tempdir_factory, store):
763+
path = git_dir(tempdir_factory)
764+
config = [
765+
{
766+
'repo': 'local',
767+
'hooks': [{
768+
'id': 'post-merge',
769+
'name': 'Post merge',
770+
'entry': 'touch post-merge.tmp',
771+
'language': 'system',
772+
'always_run': True,
773+
'verbose': True,
774+
'stages': ['post-merge'],
775+
}],
776+
},
777+
]
778+
write_config(path, config)
779+
with cwd(path):
780+
# create a simple diamond of commits for a non-trivial merge
781+
open('init', 'a').close()
782+
cmd_output('git', 'add', '.')
783+
git_commit()
784+
785+
open('master', 'a').close()
786+
cmd_output('git', 'add', '.')
787+
git_commit()
788+
789+
cmd_output('git', 'checkout', '-b', 'branch', 'HEAD^')
790+
open('branch', 'a').close()
791+
cmd_output('git', 'add', '.')
792+
git_commit()
793+
794+
cmd_output('git', 'checkout', 'master')
795+
install(C.CONFIG_FILE, store, hook_types=['post-merge'])
796+
retc, stdout, stderr = cmd_output_mocked_pre_commit_home(
797+
'git', 'merge', 'branch',
798+
tempdir_factory=tempdir_factory,
799+
)
800+
assert retc == 0
801+
assert os.path.exists('post-merge.tmp')
802+
803+
762804
def test_post_checkout_integration(tempdir_factory, store):
763805
path = git_dir(tempdir_factory)
764806
config = [

tests/commands/run_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,15 @@ def test_all_push_options_ok(cap_out, store, repo_with_passing_hook):
494494
assert b'Specify both --from-ref and --to-ref.' not in printed
495495

496496

497+
def test_is_squash_merge(cap_out, store, repo_with_passing_hook):
498+
args = run_opts(is_squash_merge='1')
499+
environ: MutableMapping[str, str] = {}
500+
ret, printed = _do_run(
501+
cap_out, store, repo_with_passing_hook, args, environ,
502+
)
503+
assert environ['PRE_COMMIT_IS_SQUASH_MERGE'] == '1'
504+
505+
497506
def test_checkout_type(cap_out, store, repo_with_passing_hook):
498507
args = run_opts(from_ref='', to_ref='', checkout_type='1')
499508
environ: MutableMapping[str, str] = {}

tests/repository_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ def test_manifest_hooks(tempdir_factory, store):
953953
require_serial=False,
954954
stages=(
955955
'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg',
956-
'post-commit', 'manual', 'post-checkout', 'push',
956+
'post-commit', 'manual', 'post-checkout', 'push', 'post-merge',
957957
),
958958
types=['file'],
959959
types_or=[],

0 commit comments

Comments
 (0)