Hooky
Háčky Git jsou spustitelné skripty v .git/hooks/, které se spouštějí v určitých bodech operací git. Pitmaster háčky detekuje a spouští pomocí řízení procesů v PHP (proc_open).
Háčky se spouštějí automaticky
Operace repozitáře automaticky spouštějí háčky uvedené v části Háčky, které spouští Pitmaster. Chcete-li je pro určitou instanci repozitáře vypnout, předejte při jejím otevření možnosti:
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() i Pitmaster::clone() přijímají stejné možnosti. Spuštění háčku vyžaduje proc_open(); na hostitelích, kde je zakázáno, otevírejte repozitáře pomocí ['hooks' => false]. CLI vždy otevírá repozitář se zapnutými háčky.
Pitmaster hledá háčky pouze v <gitDir>/hooks. core.hooksPath ignoruje a v propojeném pracovním stromu je hledá v .git/worktrees/<name>/hooks, takže se tam sdílené skripty .git/hooks nespouštějí (#12).
Níže popsaný HookRunner tyto možnosti ignoruje: při přímém volání háček vždy spustí.
HookRunner
use Pitmaster\Hooks\HookRunner;
$hooks = new HookRunner($repo->gitDir());
Ověření existence háčku
if ($hooks->exists('pre-commit')) {
echo "pre-commit hook is installed\n";
}
Háček existuje, pokud se soubor nachází v .git/hooks/<name> a je spustitelný.
Spuštění háčku
$result = $hooks->run('pre-commit');
echo $result['exitCode']; // 0 = success
echo $result['stdout']; // Hook's standard output
echo $result['stderr']; // Hook's standard error
S argumenty
$result = $hooks->run('commit-msg', ['/path/to/COMMIT_EDITMSG']);
Se stdin
$result = $hooks->run('pre-push', ['origin', 'https://github.com/user/repo.git'], $stdinData);
Háček pre-push dostává informace o referencích přes stdin.
Ověření úspěchu
if ($hooks->runAndCheck('pre-commit')) {
// Hook passed (exit code 0), proceed with commit
} else {
// Hook rejected the operation
}
runAndCheck() je pomocná metoda, která spustí háček a vrátí true, pokud je návratový kód 0. Pokud háček neexistuje, vrátí true (absence háčku neznamená námitku).
Seznam nainstalovaných háčků
$installed = $hooks->listHooks();
// ['pre-commit', 'commit-msg', 'post-checkout']
Vypíše všechny spustitelné soubory v .git/hooks/ (vynechá soubory .sample).
run() při chybějícím nebo nespustitelném háčku vrátí návratový kód 0 a prázdný výstup. Pokud proces nelze spustit, vrátí návratový kód 1 a na stderr vypíše Failed to start hook.
Instalace háčku
$hooks->install('pre-commit', "#!/bin/bash\nphp vendor/bin/phpcs --standard=PSR12 .\n");
Zapíše obsah skriptu do .git/hooks/<name> a nastaví oprávnění ke spuštění (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);
Háčky, které spouští Pitmaster
HookRunner může spustit libovolný háček podle názvu. Repository automaticky spouští tyto háčky:
| Háček | Spouští jej | Argumenty | Může přerušit operaci? |
|---|---|---|---|
pre-commit |
commit() |
žádné | Ano (nenulový návratový kód) |
prepare-commit-msg |
commit() |
soubor se zprávou, poté message (nebo merge při dokončení sloučení) |
Ano |
commit-msg |
commit() |
soubor se zprávou (.git/COMMIT_EDITMSG); použijí se jeho úpravy |
Ano (nenulový návratový kód) |
post-commit |
commit() |
žádné | Ne (informativní) |
pre-rebase |
rebase() |
revize upstreamu | Ano |
post-checkout |
checkout() |
starý HEAD, nový HEAD, 1 |
Ne (informativní) |
post-merge |
merge(), mergeOctopus(), mergeContinue() |
0 |
Ne (informativní) |
pre-push |
push() a jeho varianty |
název vzdáleného repozitáře, URL; řádky referencí přes stdin | Ano (nenulový návratový kód) |
Neúspěšný háček způsobí, že operace vyvolá RuntimeException('<name> hook failed'); jeho výstup se nepřipojí. Háčky commitu se spouštějí pouze z commit(): commity sloučení vytvořené pomocí merge() je nespouštějí. post-checkout se nespouští pro addWorktree(), reset() ani restore().
Serverové háčky (pre-receive, update, post-receive, post-update), pre-auto-gc a post-rewrite se nikdy nespouštějí automaticky.
Prostředí háčku
Při spuštění háčku Pitmaster předá toto prostředí:
$env = [
'GIT_DIR' => $this->gitDir,
'GIT_WORK_TREE' => dirname($this->gitDir),
];
Háček obdrží pouze tyto proměnné: PATH, HOME a žádné další proměnné procesu PHP se nepředávají (#13). Ve skriptech háčků používejte absolutní cesty nebo nastavte PATH přímo ve skriptu; shebang #!/usr/bin/env php najde interprety pouze ve výchozí systémové cestě.
Háček se spouští v dirname($gitDir), což je pracovní strom běžného adresáře .git.
Formát skriptu háčku
Háčky lze psát v libovolném jazyce. První řádek musí obsahovat shebang:
#!/bin/bash
# Bash hook
#!/usr/bin/env php
<?php
// PHP hook
#!/usr/bin/env python3
# Python hook
Příklad: pre-commit s Pitmaster
commit() už spouští pre-commit, prepare-commit-msg, commit-msg a post-commit, takže aplikační kód musí ošetřit pouze neúspěch:
try {
$commitId = $repo->commit('Add feature');
} catch (\RuntimeException $e) {
echo $e->getMessage(); // "pre-commit hook failed"
}
HookRunner volejte sami pouze na instanci otevřené pomocí ['hooks' => false] nebo u háčků, které Pitmaster nespouští.
Ukázkové háčky
Git inicializuje repozitáře se soubory háčků .sample; Pitmaster::init() vytvoří prázdný adresář hooks/. Pitmaster ve svém listHooks() vylučuje soubory .sample:
.git/hooks/
pre-commit.sample (excluded from listHooks)
commit-msg.sample (excluded from listHooks)
pre-commit (included - installed by user)