Skip to content

Commit

Permalink
Merge pull request #1800 from psacawa/post-merge-pr
Browse files Browse the repository at this point in the history
Add support for post-merge hooks
  • Loading branch information
asottile committed Feb 27, 2021
2 parents a38f8d0 + 6b73138 commit b193d9e
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 5 deletions.
5 changes: 5 additions & 0 deletions pre_commit/commands/hook_impl.py
Expand Up @@ -76,6 +76,7 @@ def _ns(
remote_url: Optional[str] = None,
commit_msg_filename: Optional[str] = None,
checkout_type: Optional[str] = None,
is_squash_merge: Optional[str] = None,
) -> argparse.Namespace:
return argparse.Namespace(
color=color,
Expand All @@ -88,6 +89,7 @@ def _ns(
commit_msg_filename=commit_msg_filename,
all_files=all_files,
checkout_type=checkout_type,
is_squash_merge=is_squash_merge,
files=(),
hook=None,
verbose=False,
Expand Down Expand Up @@ -158,6 +160,7 @@ def _pre_push_ns(
'post-commit': 0,
'pre-commit': 0,
'pre-merge-commit': 0,
'post-merge': 1,
'pre-push': 2,
}

Expand Down Expand Up @@ -199,6 +202,8 @@ def _run_ns(
hook_type, color,
from_ref=args[0], to_ref=args[1], checkout_type=args[2],
)
elif hook_type == 'post-merge':
return _ns(hook_type, color, is_squash_merge=args[0])
else:
raise AssertionError(f'unexpected hook type: {hook_type}')

Expand Down
5 changes: 4 additions & 1 deletion pre_commit/commands/run.py
Expand Up @@ -245,7 +245,7 @@ def _compute_cols(hooks: Sequence[Hook]) -> int:

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

if args.is_squash_merge:
environ['PRE_COMMIT_IS_SQUASH_MERGE'] = args.is_squash_merge

# Set pre_commit flag
environ['PRE_COMMIT'] = '1'

Expand Down
2 changes: 1 addition & 1 deletion pre_commit/constants.py
Expand Up @@ -18,7 +18,7 @@
# `manual` is not invoked by any installed git hook. See #719
STAGES = (
'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg',
'post-commit', 'manual', 'post-checkout', 'push',
'post-commit', 'manual', 'post-checkout', 'push', 'post-merge',
)

DEFAULT = 'default'
11 changes: 9 additions & 2 deletions pre_commit/main.py
Expand Up @@ -67,8 +67,8 @@ def __call__(
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-commit', 'post-checkout',
'pre-commit', 'pre-merge-commit', 'pre-push', 'prepare-commit-msg',
'commit-msg', 'post-commit', 'post-checkout', 'post-merge',
),
action=AppendReplaceDefault,
default=['pre-commit'],
Expand Down Expand Up @@ -136,6 +136,13 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
'file from the index, flag=0).'
),
)
parser.add_argument(
'--is-squash-merge',
help=(
'During a post-merge hook, indicates whether the merge was a '
'squash merge'
),
)


def _adjust_args_and_chdir(args: argparse.Namespace) -> None:
Expand Down
2 changes: 2 additions & 0 deletions testing/util.py
Expand Up @@ -70,6 +70,7 @@ def run_opts(
show_diff_on_failure=False,
commit_msg_filename='',
checkout_type='',
is_squash_merge='',
):
# These are mutually exclusive
assert not (all_files and files)
Expand All @@ -88,6 +89,7 @@ def run_opts(
show_diff_on_failure=show_diff_on_failure,
commit_msg_filename=commit_msg_filename,
checkout_type=checkout_type,
is_squash_merge=is_squash_merge,
)


Expand Down
9 changes: 9 additions & 0 deletions tests/commands/hook_impl_test.py
Expand Up @@ -97,6 +97,7 @@ def call(*_, **__):
('pre-push', ['branch_name', 'remote_name']),
('commit-msg', ['.git/COMMIT_EDITMSG']),
('post-commit', []),
('post-merge', ['1']),
('post-checkout', ['old_head', 'new_head', '1']),
# multiple choices for commit-editmsg
('prepare-commit-msg', ['.git/COMMIT_EDITMSG']),
Expand Down Expand Up @@ -157,6 +158,14 @@ def test_run_ns_post_commit():
assert ns.color is True


def test_run_ns_post_merge():
ns = hook_impl._run_ns('post-merge', True, ('1',), b'')
assert ns is not None
assert ns.hook_stage == 'post-merge'
assert ns.color is True
assert ns.is_squash_merge == '1'


def test_run_ns_post_checkout():
ns = hook_impl._run_ns('post-checkout', True, ('a', 'b', 'c'), b'')
assert ns is not None
Expand Down
42 changes: 42 additions & 0 deletions tests/commands/install_uninstall_test.py
Expand Up @@ -762,6 +762,48 @@ def test_post_commit_integration(tempdir_factory, store):
assert os.path.exists('post-commit.tmp')


def test_post_merge_integration(tempdir_factory, store):
path = git_dir(tempdir_factory)
config = [
{
'repo': 'local',
'hooks': [{
'id': 'post-merge',
'name': 'Post merge',
'entry': 'touch post-merge.tmp',
'language': 'system',
'always_run': True,
'verbose': True,
'stages': ['post-merge'],
}],
},
]
write_config(path, config)
with cwd(path):
# create a simple diamond of commits for a non-trivial merge
open('init', 'a').close()
cmd_output('git', 'add', '.')
git_commit()

open('master', 'a').close()
cmd_output('git', 'add', '.')
git_commit()

cmd_output('git', 'checkout', '-b', 'branch', 'HEAD^')
open('branch', 'a').close()
cmd_output('git', 'add', '.')
git_commit()

cmd_output('git', 'checkout', 'master')
install(C.CONFIG_FILE, store, hook_types=['post-merge'])
retc, stdout, stderr = cmd_output_mocked_pre_commit_home(
'git', 'merge', 'branch',
tempdir_factory=tempdir_factory,
)
assert retc == 0
assert os.path.exists('post-merge.tmp')


def test_post_checkout_integration(tempdir_factory, store):
path = git_dir(tempdir_factory)
config = [
Expand Down
9 changes: 9 additions & 0 deletions tests/commands/run_test.py
Expand Up @@ -494,6 +494,15 @@ def test_all_push_options_ok(cap_out, store, repo_with_passing_hook):
assert b'Specify both --from-ref and --to-ref.' not in printed


def test_is_squash_merge(cap_out, store, repo_with_passing_hook):
args = run_opts(is_squash_merge='1')
environ: MutableMapping[str, str] = {}
ret, printed = _do_run(
cap_out, store, repo_with_passing_hook, args, environ,
)
assert environ['PRE_COMMIT_IS_SQUASH_MERGE'] == '1'


def test_checkout_type(cap_out, store, repo_with_passing_hook):
args = run_opts(from_ref='', to_ref='', checkout_type='1')
environ: MutableMapping[str, str] = {}
Expand Down
2 changes: 1 addition & 1 deletion tests/repository_test.py
Expand Up @@ -953,7 +953,7 @@ def test_manifest_hooks(tempdir_factory, store):
require_serial=False,
stages=(
'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg',
'post-commit', 'manual', 'post-checkout', 'push',
'post-commit', 'manual', 'post-checkout', 'push', 'post-merge',
),
types=['file'],
types_or=[],
Expand Down

0 comments on commit b193d9e

Please sign in to comment.