Skip to content

Commit 18fa004

Browse files
Andrew Hareasottile
authored andcommitted
Add post-checkout
1 parent 1c641b1 commit 18fa004

File tree

9 files changed

+77
-5
lines changed

9 files changed

+77
-5
lines changed

pre_commit/commands/hook_impl.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def _ns(
7474
remote_name: Optional[str] = None,
7575
remote_url: Optional[str] = None,
7676
commit_msg_filename: Optional[str] = None,
77+
checkout_type: Optional[str] = None,
7778
) -> argparse.Namespace:
7879
return argparse.Namespace(
7980
color=color,
@@ -84,6 +85,7 @@ def _ns(
8485
remote_url=remote_url,
8586
commit_msg_filename=commit_msg_filename,
8687
all_files=all_files,
88+
checkout_type=checkout_type,
8789
files=(),
8890
hook=None,
8991
verbose=False,
@@ -157,6 +159,11 @@ def _run_ns(
157159
return _ns(hook_type, color, commit_msg_filename=args[0])
158160
elif hook_type in {'pre-merge-commit', 'pre-commit'}:
159161
return _ns(hook_type, color)
162+
elif hook_type == 'post-checkout':
163+
return _ns(
164+
hook_type, color, source=args[0], origin=args[1],
165+
checkout_type=args[2],
166+
)
160167
else:
161168
raise AssertionError(f'unexpected hook type: {hook_type}')
162169

pre_commit/commands/run.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ def run(
316316
environ['PRE_COMMIT_REMOTE_NAME'] = args.remote_name
317317
environ['PRE_COMMIT_REMOTE_URL'] = args.remote_url
318318

319+
if args.checkout_type:
320+
environ['PRE_COMMIT_CHECKOUT_TYPE'] = args.checkout_type
321+
319322
with contextlib.ExitStack() as exit_stack:
320323
if stash:
321324
exit_stack.enter_context(staged_files_only(store.directory))

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', 'manual',
21-
'push',
21+
'post-checkout', 'push',
2222
)
2323

2424
DEFAULT = 'default'

pre_commit/main.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _add_hook_type_option(parser: argparse.ArgumentParser) -> None:
7979
parser.add_argument(
8080
'-t', '--hook-type', choices=(
8181
'pre-commit', 'pre-merge-commit', 'pre-push',
82-
'prepare-commit-msg', 'commit-msg',
82+
'prepare-commit-msg', 'commit-msg', 'post-checkout',
8383
),
8484
action=AppendReplaceDefault,
8585
default=['pre-commit'],
@@ -92,11 +92,17 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
9292
parser.add_argument('--verbose', '-v', action='store_true', default=False)
9393
parser.add_argument(
9494
'--origin', '-o',
95-
help="The origin branch's commit_id when using `git push`.",
95+
help=(
96+
"The origin branch's commit_id when using `git push`. "
97+
'The ref of the previous HEAD when using `git checkout`.'
98+
),
9699
)
97100
parser.add_argument(
98101
'--source', '-s',
99-
help="The remote branch's commit_id when using `git push`.",
102+
help=(
103+
"The remote branch's commit_id when using `git push`. "
104+
'The ref of the new HEAD when using `git checkout`.'
105+
),
100106
)
101107
parser.add_argument(
102108
'--commit-msg-filename',
@@ -123,6 +129,14 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
123129
'--files', nargs='*', default=[],
124130
help='Specific filenames to run hooks on.',
125131
)
132+
parser.add_argument(
133+
'--checkout-type',
134+
help=(
135+
'Indicates whether the checkout was a branch checkout '
136+
'(changing branches, flag=1) or a file checkout (retrieving a '
137+
'file from the index, flag=0).'
138+
),
139+
)
126140

127141

128142
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
@@ -72,6 +72,7 @@ def run_opts(
7272
hook_stage='commit',
7373
show_diff_on_failure=False,
7474
commit_msg_filename='',
75+
checkout_type='',
7576
):
7677
# These are mutually exclusive
7778
assert not (all_files and files)
@@ -88,6 +89,7 @@ def run_opts(
8889
hook_stage=hook_stage,
8990
show_diff_on_failure=show_diff_on_failure,
9091
commit_msg_filename=commit_msg_filename,
92+
checkout_type=checkout_type,
9193
)
9294

9395

tests/commands/hook_impl_test.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ def test_run_ns_commit_msg():
104104
assert ns.commit_msg_filename == '.git/COMMIT_MSG'
105105

106106

107+
def test_run_ns_post_checkout():
108+
ns = hook_impl._run_ns('post-checkout', True, ('a', 'b', 'c'), b'')
109+
assert ns is not None
110+
assert ns.hook_stage == 'post-checkout'
111+
assert ns.color is True
112+
assert ns.source == 'a'
113+
assert ns.origin == 'b'
114+
assert ns.checkout_type == 'c'
115+
116+
107117
@pytest.fixture
108118
def push_example(tempdir_factory):
109119
src = git_dir(tempdir_factory)

tests/commands/install_uninstall_test.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from testing.fixtures import git_dir
2121
from testing.fixtures import make_consuming_repo
2222
from testing.fixtures import remove_config_from_repo
23+
from testing.fixtures import write_config
2324
from testing.util import cmd_output_mocked_pre_commit_home
2425
from testing.util import cwd
2526
from testing.util import git_commit
@@ -725,6 +726,31 @@ def test_commit_msg_legacy(commit_msg_repo, tempdir_factory, store):
725726
assert second_line.startswith('Must have "Signed off by:"...')
726727

727728

729+
def test_post_checkout_integration(tempdir_factory, store):
730+
path = git_dir(tempdir_factory)
731+
config = {
732+
'repo': 'local',
733+
'hooks': [{
734+
'id': 'post-checkout',
735+
'name': 'Post checkout',
736+
'entry': 'bash -c "echo ${PRE_COMMIT_ORIGIN}"',
737+
'language': 'system',
738+
'always_run': True,
739+
'verbose': True,
740+
'stages': ['post-checkout'],
741+
}],
742+
}
743+
write_config(path, config)
744+
with cwd(path):
745+
cmd_output('git', 'add', '.')
746+
git_commit()
747+
install(C.CONFIG_FILE, store, hook_types=['post-checkout'])
748+
retc, _, stderr = cmd_output('git', 'checkout', '-b', 'feature')
749+
assert retc == 0
750+
_, head, _ = cmd_output('git', 'rev-parse', 'HEAD')
751+
assert head in str(stderr)
752+
753+
728754
def test_prepare_commit_msg_integration_failing(
729755
failing_prepare_commit_msg_repo, tempdir_factory, store,
730756
):

tests/commands/run_test.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from pre_commit.commands.run import filter_by_include_exclude
1919
from pre_commit.commands.run import run
2020
from pre_commit.util import cmd_output
21+
from pre_commit.util import EnvironT
2122
from pre_commit.util import make_executable
2223
from testing.auto_namedtuple import auto_namedtuple
2324
from testing.fixtures import add_config_to_repo
@@ -466,6 +467,15 @@ def test_all_push_options_ok(cap_out, store, repo_with_passing_hook):
466467
assert b'Specify both --origin and --source.' not in printed
467468

468469

470+
def test_checkout_type(cap_out, store, repo_with_passing_hook):
471+
args = run_opts(origin='', source='', checkout_type='1')
472+
environ: EnvironT = {}
473+
ret, printed = _do_run(
474+
cap_out, store, repo_with_passing_hook, args, environ,
475+
)
476+
assert environ['PRE_COMMIT_CHECKOUT_TYPE'] == '1'
477+
478+
469479
def test_has_unmerged_paths(in_merge_conflict):
470480
assert _has_unmerged_paths() is True
471481
cmd_output('git', 'add', '.')

tests/repository_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ def test_manifest_hooks(tempdir_factory, store):
871871
require_serial=False,
872872
stages=(
873873
'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg',
874-
'manual', 'push',
874+
'manual', 'post-checkout', 'push',
875875
),
876876
types=['file'],
877877
verbose=False,

0 commit comments

Comments
 (0)