Hooks
Git-hooks er eksekverbare scripts i .git/hooks/, der køres på bestemte tidspunkter under git-operationer. Pitmaster registrerer og kalder hooks ved hjælp af PHPs processtyring (proc_open).
Hooks køres som standard
Ved repository-operationer køres de hooks, der er angivet under Hooks, som Pitmaster kører automatisk. Slå dem fra for et repository-håndtag ved at angive indstillinger, når det åbnes:
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() og Pitmaster::clone() accepterer de samme indstillinger. Det kræver proc_open() at køre en hook; på værter, hvor det er deaktiveret, skal repositories åbnes med ['hooks' => false]. CLI åbner altid repositoriet med hooks aktiveret.
Pitmaster leder kun efter hooks i <gitDir>/hooks. Den ignorerer core.hooksPath, og i et tilknyttet worktree leder den i .git/worktrees/<name>/hooks, så de delte .git/hooks-scripts ikke køres dér (#12).
HookRunner, som beskrives nedenfor, ignorerer disse indstillinger: et direkte kald kører altid hooken.
HookRunner
use Pitmaster\Hooks\HookRunner;
$hooks = new HookRunner($repo->gitDir());
Kontrollér, om en hook findes
if ($hooks->exists('pre-commit')) {
echo "pre-commit hook is installed\n";
}
En hook findes, hvis filen ligger på .git/hooks/<name> og er eksekverbar.
Kør en hook
$result = $hooks->run('pre-commit');
echo $result['exitCode']; // 0 = success
echo $result['stdout']; // Hook's standard output
echo $result['stderr']; // Hook's standard error
Med argumenter
$result = $hooks->run('commit-msg', ['/path/to/COMMIT_EDITMSG']);
Med stdin
$result = $hooks->run('pre-push', ['origin', 'https://github.com/user/repo.git'], $stdinData);
Hooken pre-push modtager ref-oplysninger via stdin.
Kontrollér, om den lykkedes
if ($hooks->runAndCheck('pre-commit')) {
// Hook passed (exit code 0), proceed with commit
} else {
// Hook rejected the operation
}
runAndCheck() er en genvej, der kører hooken og returnerer true, hvis exitkoden er 0. Hvis hooken ikke findes, returnerer den true (ingen hook betyder ingen indsigelse).
Vis installerede hooks
$installed = $hooks->listHooks();
// ['pre-commit', 'commit-msg', 'post-checkout']
Viser alle eksekverbare filer i .git/hooks/ (udelader .sample-filer).
run() returnerer exitkode 0 uden output, hvis hooken mangler eller ikke er eksekverbar. Hvis processen ikke kan startes, returneres exitkode 1 med Failed to start hook på stderr.
Installér en hook
$hooks->install('pre-commit', "#!/bin/bash\nphp vendor/bin/phpcs --standard=PSR12 .\n");
Skriver scriptindholdet til .git/hooks/<name> og angiver eksekverbar tilladelse (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, som Pitmaster kører
HookRunner kan køre enhver hook efter navn. Repository kører automatisk disse:
| Hook | Køres af | Argumenter | Kan afbryde? |
|---|---|---|---|
pre-commit |
commit() |
ingen | Ja (exitkode forskellig fra nul) |
prepare-commit-msg |
commit() |
meddelelsesfil, derefter message (eller merge ved afslutning af en merge) |
Ja |
commit-msg |
commit() |
meddelelsesfil (.git/COMMIT_EDITMSG); ændringer i filen anvendes |
Ja (exitkode forskellig fra nul) |
post-commit |
commit() |
ingen | Nej (kun til orientering) |
pre-rebase |
rebase() |
upstream-revisionen | Ja |
post-checkout |
checkout() |
gammel HEAD, ny HEAD, 1 |
Nej (kun til orientering) |
post-merge |
merge(), mergeOctopus(), mergeContinue() |
0 |
Nej (kun til orientering) |
pre-push |
push() og dens varianter |
fjernnavn, URL; ref-linjer via stdin | Ja (exitkode forskellig fra nul) |
En fejlet hook får operationen til at kaste RuntimeException('<name> hook failed'); hookens output medtages ikke. Commit-hooks køres kun fra commit(): merge-commits oprettet af merge() kører dem ikke. post-checkout køres ikke for addWorktree(), reset() eller restore().
Server-side-hooks (pre-receive, update, post-receive, post-update), pre-auto-gc og post-rewrite køres aldrig automatisk.
Hook-miljø
Når Pitmaster kører en hook, videregiver den dette miljø:
$env = [
'GIT_DIR' => $this->gitDir,
'GIT_WORK_TREE' => dirname($this->gitDir),
];
Dette er de eneste variabler, hooken får: PATH, HOME og alle andre variabler fra PHP-processen videregives ikke (#13). Brug absolutte stier i hook-scripts, eller angiv PATH i scriptet; en #!/usr/bin/env php-shebang finder kun fortolkere i systemets standardsti.
Hooken kører i dirname($gitDir), som er working tree for en normal .git-mappe.
Hook-scriptformat
Hooks kan skrives på ethvert sprog. Første linje skal være en shebang:
#!/bin/bash
# Bash hook
#!/usr/bin/env php
<?php
// PHP hook
#!/usr/bin/env python3
# Python hook
Eksempel: pre-commit med Pitmaster
commit() kører allerede pre-commit, prepare-commit-msg, commit-msg og post-commit, så applikationskoden behøver kun at håndtere fejlen:
try {
$commitId = $repo->commit('Add feature');
} catch (\RuntimeException $e) {
echo $e->getMessage(); // "pre-commit hook failed"
}
Kald kun selv HookRunner på et håndtag, der er åbnet med ['hooks' => false], eller for hooks, som Pitmaster ikke kører.
Eksempel-hooks
Git initialiserer repositories med .sample-hookfiler; Pitmaster::init() opretter en tom hooks/-mappe. Pitmasters listHooks() udelader .sample-filer:
.git/hooks/
pre-commit.sample (excluded from listHooks)
commit-msg.sample (excluded from listHooks)
pre-commit (included - installed by user)