Gå til indhold

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:

PHP
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

PHP
use Pitmaster\Hooks\HookRunner;

$hooks = new HookRunner($repo->gitDir());

Kontrollér, om en hook findes

PHP
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

PHP
$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

PHP
$result = $hooks->run('commit-msg', ['/path/to/COMMIT_EDITMSG']);

Med stdin

PHP
$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

PHP
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

PHP
$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

PHP
$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).

PHP
// 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ø:

PHP
$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:

Shell
#!/bin/bash
# Bash hook
PHP
#!/usr/bin/env php
<?php
// PHP hook
Python
#!/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:

PHP
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:

Kode
.git/hooks/
  pre-commit.sample       (excluded from listHooks)
  commit-msg.sample       (excluded from listHooks)
  pre-commit              (included - installed by user)