Preskoči na vsebino

Zanke

Prestrezniki Git so izvedljivi skripti v .git/hooks/, ki se zaženejo na določenih točkah operacij git. Pitmaster zazna in sproži prestreznike z nadzorom procesov v PHP (proc_open).

Prestrezniki se izvajajo privzeto

Operacije v repozitoriju samodejno zaženejo navedene kavlje Hooks Pitmaster izvede. Če jih želite izklopiti za ročaj repozitorija, ob njegovem odpiranju podajte možnosti:

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() in Pitmaster::clone() sprejemata enake možnosti. Za izvajanje prestreznika je potreben proc_open(); v gostiteljih, kjer je ta onemogočen, odprite repozitorije z ['hooks' => false]. CLI vedno odpre repozitorij z omogočenimi prestrezniki.

Pitmaster išče prestreznike samo v <gitDir>/hooks. core.hooksPath prezre, v povezanem delovnem drevesu pa jih išče v .git/worktrees/<name>/hooks, zato se tam skripti v skupni rabi .git/hooks ne izvajajo (#12).

HookRunner, opisan spodaj, te možnosti prezre: če ga pokličete neposredno, se prestreznik vedno izvede.

HookRunner

PHP
use Pitmaster\Hooks\HookRunner;

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

Preverjanje, ali prestreznik obstaja

PHP
if ($hooks->exists('pre-commit')) {
    echo "pre-commit hook is installed\n";
}

Prestreznik obstaja, če je datoteka prisotna na poti .git/hooks/<name> in je izvedljiva.

Zagon prestreznika

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

Z argumenti

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

S standardnim vhodom

PHP
$result = $hooks->run('pre-push', ['origin', 'https://github.com/user/repo.git'], $stdinData);

Prestreznik pre-push prejme informacije o sklicu na standardnem vhodu.

Preverjanje uspešnosti

PHP
if ($hooks->runAndCheck('pre-commit')) {
    // Hook passed (exit code 0), proceed with commit
} else {
    // Hook rejected the operation
}

runAndCheck() je priročna funkcija, ki zažene prestreznik in vrne true, če je izhodna koda 0. Če prestreznik ne obstaja, vrne true (če prestreznika ni, ni ugovora).

Seznam nameščenih prestreznikov

PHP
$installed = $hooks->listHooks();
// ['pre-commit', 'commit-msg', 'post-checkout']

Navede vse izvedljive datoteke v .git/hooks/ (datotek .sample ne vključuje).

run() za manjkajoč ali neizvedljiv prestreznik vrne izhodno kodo 0 in prazen izhod. Če procesa ni mogoče zagnati, vrne izhodno kodo 1, na standardni napaki pa Failed to start hook.

Namestitev prestreznika

PHP
$hooks->install('pre-commit', "#!/bin/bash\nphp vendor/bin/phpcs --standard=PSR12 .\n");

Vsebino skripta zapiše v .git/hooks/<name> in nastavi dovoljenje za izvajanje (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);

Prestrezniki, ki jih izvaja Pitmaster

HookRunner lahko zažene kateri koli prestreznik po imenu. Repository jih zažene samodejno:

Prestreznik Izvaja ga Argumenti Lahko prekine?
pre-commit commit() nič Da (izhodna koda ni nič)
prepare-commit-msg commit() datoteka sporočila, nato message (ali merge pri zaključevanju združitve) Da
commit-msg commit() datoteka sporočila (.git/COMMIT_EDITMSG); spremembe datoteke se uporabijo Da (izhodna koda ni nič)
post-commit commit() nič Ne (informativno)
pre-rebase rebase() revizija nadrejenega repozitorija Da
post-checkout checkout() stari HEAD, novi HEAD, 1 Ne (informativno)
post-merge merge(), mergeOctopus(), mergeContinue() 0 Ne (informativno)
pre-push push() in njegove različice ime oddaljenega repozitorija, URL; vrstice sklicev na standardnem vhodu Da (izhodna koda ni nič)

Če prestreznik ne uspe, operacija sproži RuntimeException('<name> hook failed'); izhod prestreznika ni vključen. Prestrezniki za potrjevanje se izvajajo samo iz commit(): združitve, ki jih ustvari merge(), jih ne zaženejo. post-checkout se ne zažene za addWorktree(), reset() ali restore().

Strežniški prestrezniki (pre-receive, update, post-receive, post-update), pre-auto-gc in post-rewrite se nikoli ne izvajajo samodejno.

Okolje prestreznika

Ko Pitmaster zažene prestreznik, mu posreduje to okolje:

PHP
$env = [
    'GIT_DIR' => $this->gitDir,
    'GIT_WORK_TREE' => dirname($this->gitDir),
];

Prestreznik prejme samo te spremenljivke: PATH, HOME in nobena druga spremenljivka procesa PHP se ne posreduje (#13). V skriptih prestreznikov uporabljajte absolutne poti ali znotraj skripta nastavite PATH; vstavljena vrstica shebang #!/usr/bin/env php najde tolmače samo na privzeti sistemski poti.

Prestreznik se izvaja v dirname($gitDir), ki je pri običajnem imeniku .git delovno drevo.

Oblika skripta prestreznika

Prestreznike lahko napišete v katerem koli jeziku. Prva vrstica mora biti vrstica shebang:

Lupina
#!/bin/bash
# Bash hook
PHP
#!/usr/bin/env php
<?php
// PHP hook
Python
#!/usr/bin/env python3
# Python hook

Primer: pre-commit z Pitmaster

commit() že zažene pre-commit, prepare-commit-msg, commit-msg in post-commit, zato mora koda aplikacije obravnavati samo neuspeh:

PHP
try {
    $commitId = $repo->commit('Add feature');
} catch (\RuntimeException $e) {
    echo $e->getMessage(); // "pre-commit hook failed"
}

HookRunner pokličite sami samo pri ročici, odprti z ['hooks' => false], ali za prestreznike, ki jih Pitmaster ne zažene.

Vzorčni prestrezniki

Git inicializira repozitorije z datotekami prestreznikov .sample; Pitmaster::init() ustvari prazen imenik hooks/. Pitmaster v svojem listHooks() izključi datoteke .sample:

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