Hooks
Git-hooks zijn uitvoerbare scripts in .git/hooks/ die op specifieke momenten tijdens git-bewerkingen worden uitgevoerd. Pitmaster detecteert en start hooks met de procesbesturing van PHP (proc_open).
Hooks worden standaard uitgevoerd
Bij repositorybewerkingen worden de hooks in Hooks Pitmaster voert uit automatisch uitgevoerd. U schakelt ze uit voor een repositoryhandle door opties mee te geven wanneer u deze opent:
use Pitmaster\Pitmaster;
// Skip .git/hooks scripts for this handle
$repo = Pitmaster::open('/path/to/project', ['hooks' => false]);
// Start no processes at all: hooks, the fsmonitor hook, clone/fetch/push, and SSH are disabled
$repo = Pitmaster::open('/path/to/project', Pitmaster::processFreeOptions());
$repo->hooksEnabled(); // false
$repo->processesEnabled(); // false for the second handle
Pitmaster::init() en Pitmaster::clone() accepteren dezelfde opties. Voor het uitvoeren van een hook is proc_open() nodig; open repositories met ['hooks' => false] op hosts waarop dit is uitgeschakeld. De CLI opent de repository altijd met hooks ingeschakeld.
Pitmaster zoekt hooks alleen in <gitDir>/hooks. Het negeert core.hooksPath en zoekt in een gekoppelde worktree in .git/worktrees/<name>/hooks, zodat gedeelde scripts in .git/hooks daar niet worden uitgevoerd (#12).
HookRunner, hieronder beschreven, negeert deze opties: als u deze rechtstreeks aanroept, wordt de hook altijd uitgevoerd.
HookRunner
use Pitmaster\Hooks\HookRunner;
$hooks = new HookRunner($repo->gitDir());
Controleren of een hook bestaat
if ($hooks->exists('pre-commit')) {
echo "pre-commit hook is installed\n";
}
Een hook bestaat als het bestand aanwezig is op .git/hooks/<name> en uitvoerbaar is.
Een hook uitvoeren
$result = $hooks->run('pre-commit');
echo $result['exitCode']; // 0 = success
echo $result['stdout']; // Hook's standard output
echo $result['stderr']; // Hook's standard error
Met argumenten
$result = $hooks->run('commit-msg', ['/path/to/COMMIT_EDITMSG']);
Met stdin
$result = $hooks->run('pre-push', ['origin', 'https://github.com/user/repo.git'], $stdinData);
De pre-push-hook ontvangt ref-informatie via stdin.
Succes controleren
if ($hooks->runAndCheck('pre-commit')) {
// Hook passed (exit code 0), proceed with commit
} else {
// Hook rejected the operation
}
runAndCheck() is een hulpfunctie die de hook uitvoert en true retourneert als de afsluitcode 0 is. Als de hook niet bestaat, retourneert deze true (geen hook betekent geen bezwaar).
Geïnstalleerde hooks weergeven
$installed = $hooks->listHooks();
// ['pre-commit', 'commit-msg', 'post-checkout']
Geeft alle uitvoerbare bestanden in .git/hooks/ weer (.sample-bestanden worden uitgesloten).
run() retourneert bij een ontbrekende of niet-uitvoerbare hook afsluitcode 0 en geen uitvoer. Als het proces niet kan worden gestart, retourneert deze afsluitcode 1 en Failed to start hook op stderr.
Een hook installeren
$hooks->install('pre-commit', "#!/bin/bash\nphp vendor/bin/phpcs --standard=PSR12 .\n");
Schrijft de scriptinhoud naar .git/hooks/<name> en stelt de uitvoerbare machtiging in (chmod 0755).
// Install a PHP hook
$hooks->install('pre-commit', <<<'HOOK'
#!/usr/bin/env php
<?php
// Run static analysis before commit
$output = [];
$returnCode = 0;
exec('php vendor/bin/phpstan analyse --no-progress 2>&1', $output, $returnCode);
if ($returnCode !== 0) {
echo "PHPStan found errors:\n";
echo implode("\n", $output) . "\n";
exit(1);
}
HOOK);
Hooks die Pitmaster uitvoert
HookRunner kan elke hook op naam uitvoeren. Repository voert de volgende hooks automatisch uit:
| Hook | Uitgevoerd door | Argumenten | Kan afbreken? |
|---|---|---|---|
pre-commit |
commit() |
geen | Ja (niet-nul-afsluitcode) |
prepare-commit-msg |
commit() |
berichtbestand, daarna message (of merge bij het voltooien van een merge) |
Ja |
commit-msg |
commit() |
berichtbestand (.git/COMMIT_EDITMSG); wijzigingen in het bestand worden gebruikt |
Ja (niet-nul-afsluitcode) |
post-commit |
commit() |
geen | Nee (informatief) |
pre-rebase |
rebase() |
de upstream-revisie | Ja |
post-checkout |
checkout() |
oude HEAD, nieuwe HEAD, 1 |
Nee (informatief) |
post-merge |
merge(), mergeOctopus(), mergeContinue() |
0 |
Nee (informatief) |
pre-push |
push() en de varianten daarvan |
remote-naam, URL; ref-regels via stdin | Ja (niet-nul-afsluitcode) |
Een mislukte hook zorgt ervoor dat de bewerking RuntimeException('<name> hook failed') genereert; de uitvoer van de hook wordt niet opgenomen. De commit-hooks worden alleen uitgevoerd vanuit commit(): mergecommits die door merge() zijn aangemaakt, voeren ze niet uit. post-checkout wordt niet uitgevoerd voor addWorktree(), reset() of restore().
Serverhooks (pre-receive, update, post-receive, post-update), pre-auto-gc en post-rewrite worden nooit automatisch uitgevoerd.
Hookomgeving
Wanneer Pitmaster een hook uitvoert, geeft het deze omgevingsvariabelen mee:
$env = [
'GIT_DIR' => $this->gitDir,
'GIT_WORK_TREE' => dirname($this->gitDir),
];
Dit zijn de enige variabelen die de hook meekrijgt: PATH, HOME en alle andere variabelen van het PHP-proces worden niet doorgegeven (#13). Gebruik absolute paden in hookscripts of stel PATH in het script in; een #!/usr/bin/env php-shebang vindt alleen interpreters in het standaard systeempad.
De hook wordt uitgevoerd in dirname($gitDir), de working tree voor een normale .git-directory.
Hookscriptindeling
Hooks kunnen in elke taal worden geschreven. De eerste regel moet een shebang zijn:
#!/bin/bash
# Bash hook
#!/usr/bin/env php
<?php
// PHP hook
#!/usr/bin/env python3
# Python hook
Voorbeeld: pre-commit met Pitmaster
commit() voert pre-commit, prepare-commit-msg, commit-msg en post-commit al uit, dus hoeft applicatiecode alleen de fout af te handelen:
try {
$commitId = $repo->commit('Add feature');
} catch (\RuntimeException $e) {
echo $e->getMessage(); // "pre-commit hook failed"
}
Roep HookRunner zelf alleen aan bij een handle die is geopend met ['hooks' => false], of voor hooks die Pitmaster niet uitvoert.
Voorbeeldhooks
Git initialiseert repositories met hookbestanden in .sample; Pitmaster::init() maakt een lege hooks/-directory aan. Pitmaster's listHooks() sluit .sample-bestanden uit:
.git/hooks/
pre-commit.sample (excluded from listHooks)
commit-msg.sample (excluded from listHooks)
pre-commit (included - installed by user)