Skip to content

Commit a81692e

Browse files
Allow commit-msg hooks to manipulate the msg file
Both hooks `commit-msg` and `prepare-commit-msg` have access to the commit message file and can potentially change it. This change makes sure all PHP actions deal with this fact by loading the file for every action and storing changes back to the file.
1 parent 1e45ac8 commit a81692e

File tree

4 files changed

+128
-96
lines changed

4 files changed

+128
-96
lines changed

src/Runner/Hook/CommitMsg.php

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
namespace CaptainHook\App\Runner\Hook;
1313

1414
use CaptainHook\App\Hooks;
15-
use CaptainHook\App\Runner\Hook;
16-
use SebastianFeldmann\Git;
1715

1816
/**
1917
* CommitMsg
@@ -23,44 +21,12 @@
2321
* @link https://github.com/captainhook-git/captainhook
2422
* @since Class available since Release 3.1.0
2523
*/
26-
class CommitMsg extends Hook
24+
class CommitMsg extends MessageAware
2725
{
2826
/**
2927
* Hook to execute
3028
*
3129
* @var string
3230
*/
3331
protected string $hook = Hooks::COMMIT_MSG;
34-
35-
/**
36-
* Read the commit message from file
37-
*/
38-
public function beforeHook(): void
39-
{
40-
$commentChar = $this->repository->getConfigOperator()->getSettingSafely('core.commentchar', '#');
41-
$commitMsg = Git\CommitMessage::createFromFile(
42-
$this->io->getArgument(Hooks::ARG_MESSAGE_FILE, ''),
43-
$commentChar
44-
);
45-
46-
$this->repository->setCommitMsg($commitMsg);
47-
48-
parent::beforeHook();
49-
}
50-
51-
/**
52-
* Makes sure we do not run commit message validation for fixup commits
53-
*
54-
* @return void
55-
* @throws \Exception
56-
*/
57-
protected function runHook(): void
58-
{
59-
$msg = $this->repository->getCommitMsg();
60-
if ($msg->isFixup()) {
61-
$this->io->write(' - no commit message validation for fixup commits: skipping all actions');
62-
return;
63-
}
64-
parent::runHook();
65-
}
6632
}

src/Runner/Hook/MessageAware.php

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CaptainHook.
5+
*
6+
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace CaptainHook\App\Runner\Hook;
13+
14+
use CaptainHook\App\Config;
15+
use CaptainHook\App\Hooks;
16+
use CaptainHook\App\Runner\Hook;
17+
use CaptainHook\App\Runner\Util;
18+
use SebastianFeldmann\Git;
19+
20+
/**
21+
* Base class for message aware hooks
22+
*
23+
* Message aware hooks like `prepare-commit-msg` and `commit-msg` can potentially change the commit message.
24+
* This class makes sure the commit message is read and written before and after any php Action that is executed.
25+
*/
26+
abstract class MessageAware extends Hook
27+
{
28+
/**
29+
* Comment char '#' by default
30+
*
31+
* @var string
32+
*/
33+
protected string $commentChar;
34+
35+
/**
36+
* Path to commit message file
37+
*
38+
* @var string
39+
*/
40+
protected string $file;
41+
42+
/**
43+
* The current commit message
44+
*
45+
* @var \SebastianFeldmann\Git\CommitMessage
46+
*/
47+
protected Git\CommitMessage $commitMsg;
48+
49+
/**
50+
* Fetch the original hook arguments and message related config settings
51+
*
52+
* @return void
53+
*/
54+
public function beforeHook(): void
55+
{
56+
$this->commentChar = $this->repository->getConfigOperator()->getSettingSafely('core.commentchar', '#');
57+
$this->file = $this->io->getArgument(Hooks::ARG_MESSAGE_FILE);
58+
59+
$this->repository->setCommitMsg(
60+
Git\CommitMessage::createFromFile(
61+
$this->file,
62+
$this->commentChar
63+
)
64+
);
65+
66+
parent::beforeHook();
67+
}
68+
69+
/**
70+
* Read the commit message from file
71+
*
72+
* @param Config\Action $action
73+
* @return void
74+
*/
75+
public function beforeAction(Config\Action $action): void
76+
{
77+
// since every action can potentially update the commit message
78+
// we have to load the commit message from disk every time
79+
// only load the commit message from disk if a php action is executed
80+
if (Util::getExecType($action->getAction()) !== 'cli') {
81+
$this->commitMsg = Git\CommitMessage::createFromFile($this->file, $this->commentChar);
82+
$this->repository->setCommitMsg($this->commitMsg);
83+
}
84+
parent::beforeAction($action);
85+
}
86+
87+
/**
88+
* Write the commit message to disk so git or the next action can proceed further
89+
*
90+
* @param Config\Action $action
91+
* @return void
92+
*/
93+
public function afterAction(Config\Action $action): void
94+
{
95+
// since the action could potentially change the commit message
96+
// only write the commit message to disk if a php action was executed
97+
if (Util::getExecType($action->getAction()) !== 'cli') {
98+
// if the commit message was changed write it to disk
99+
if ($this->commitMsg->getRawContent() !== $this->repository->getCommitMsg()->getRawContent()) {
100+
file_put_contents($this->file, $this->repository->getCommitMsg()->getRawContent());
101+
}
102+
}
103+
parent::afterAction($action);
104+
}
105+
106+
/**
107+
* Makes sure we do not run commit message validation for fixup commits
108+
*
109+
* @return void
110+
* @throws \Exception
111+
*/
112+
protected function runHook(): void
113+
{
114+
$msg = $this->repository->getCommitMsg();
115+
if ($msg->isFixup() || $msg->isSquash()) {
116+
$this->io->write(' - no commit message hooks for fixup and squash commits: skipping all actions');
117+
return;
118+
}
119+
parent::runHook();
120+
}
121+
}

src/Runner/Hook/PrepareCommitMsg.php

Lines changed: 5 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@
1111

1212
namespace CaptainHook\App\Runner\Hook;
1313

14-
use CaptainHook\App\Config;
1514
use CaptainHook\App\Hooks;
16-
use CaptainHook\App\Runner\Hook;
17-
use CaptainHook\App\Runner\Util;
18-
use RuntimeException;
19-
use SebastianFeldmann\Git;
2015

2116
/**
2217
* Hook
@@ -26,7 +21,7 @@
2621
* @link https://github.com/captainhook-git/captainhook
2722
* @since Class available since Release 3.1.0
2823
*/
29-
class PrepareCommitMsg extends Hook
24+
class PrepareCommitMsg extends MessageAware
3025
{
3126
/**
3227
* Hook to execute
@@ -35,31 +30,19 @@ class PrepareCommitMsg extends Hook
3530
*/
3631
protected string $hook = Hooks::PREPARE_COMMIT_MSG;
3732

38-
/**
39-
* @var string
40-
*/
41-
private string $commentChar;
42-
43-
/**
44-
* Path to commit message file
45-
*
46-
* @var string
47-
*/
48-
private string $file;
49-
5033
/**
5134
* Commit mode, empty or [message|template|merge|squash|commit]
5235
*
5336
* @var string
5437
*/
55-
private string $mode;
38+
protected string $mode;
5639

5740
/**
5841
* Commit hash if mode is commit during -c or --amend
5942
*
6043
* @var string
6144
*/
62-
private string $hash;
45+
protected string $hash;
6346

6447
/**
6548
* Fetch the original hook arguments and message related config settings
@@ -68,43 +51,9 @@ class PrepareCommitMsg extends Hook
6851
*/
6952
public function beforeHook(): void
7053
{
71-
$this->commentChar = $this->repository->getConfigOperator()->getSettingSafely('core.commentchar', '#');
72-
$this->file = $this->io->getArgument(Hooks::ARG_MESSAGE_FILE);
73-
$this->mode = $this->io->getArgument(Hooks::ARG_MODE);
74-
$this->hash = $this->io->getArgument(Hooks::ARG_HASH);
75-
76-
if (empty($this->file)) {
77-
throw new RuntimeException('commit message file argument is missing');
78-
}
54+
$this->mode = $this->io->getArgument(Hooks::ARG_MODE);
55+
$this->hash = $this->io->getArgument(Hooks::ARG_HASH);
7956

8057
parent::beforeHook();
8158
}
82-
83-
/**
84-
* Read the commit message from file
85-
*
86-
* @param Config\Action $action
87-
* @return void
88-
*/
89-
public function beforeAction(Config\Action $action): void
90-
{
91-
$this->repository->setCommitMsg(Git\CommitMessage::createFromFile($this->file, $this->commentChar));
92-
parent::beforeAction($action);
93-
}
94-
95-
/**
96-
* Write the commit message to disk so git or the next action can proceed further
97-
*
98-
* @param Config\Action $action
99-
* @return void
100-
*/
101-
public function afterAction(Config\Action $action): void
102-
{
103-
// only write the commit message to disk if a php action was executed
104-
// and potentially changed the message object
105-
if (Util::getExecType($action->getAction()) !== 'cli') {
106-
file_put_contents($this->file, $this->repository->getCommitMsg()->getRawContent());
107-
}
108-
parent::afterAction($action);
109-
}
11059
}

tests/unit/Runner/Hook/PrepareCommitMsgTest.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,7 @@ public function testRunHookEnabled(): void
5050
file_put_contents($commitMessageFile, 'Commit Message');
5151

5252
$io->expects($this->atLeast(1))->method('write');
53-
$io->expects($this->exactly(3))->method('getArgument')->willReturnOnConsecutiveCalls(
54-
$commitMessageFile,
55-
'',
56-
''
57-
);
53+
$io->expects($this->exactly(3))->method('getArgument')->willReturn($commitMessageFile);
5854

5955
$runner = new PrepareCommitMsg($io, $config, $repo);
6056
$runner->run();

0 commit comments

Comments
 (0)