Към съдържанието

Куки

Кукичките Git са изпълними скриптове в .git/hooks/, които се стартират в конкретни моменти от git операциите. Pitmaster открива и извиква кукичките чрез управлението на процеси на PHP (proc_open).

Кукичките се изпълняват по подразбиране

При операциите с хранилище кукичките, изброени в Кукички, които Pitmaster изпълнява, се стартират автоматично. За да ги изключите за дадена манипулация с хранилище, подайте опции при отварянето му:

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() и Pitmaster::clone() приемат едни и същи опции. За изпълнение на кукичка е нужен proc_open(); на хостове, които го забраняват, отваряйте хранилищата с ['hooks' => false]. CLI винаги отваря хранилището с включени кукички.

Pitmaster търси кукички само в <gitDir>/hooks. Пренебрегва core.hooksPath, а в свързано работно дърво търси в .git/worktrees/<name>/hooks, затова споделените скриптове .git/hooks не се изпълняват там (#12).

HookRunner, описан по-долу, пренебрегва тези опции: директното му извикване винаги изпълнява кукичката.

HookRunner

PHP
use Pitmaster\Hooks\HookRunner;

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

Проверка дали съществува кукичка

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

Кукичка съществува, ако файлът е наличен в .git/hooks/<name> и е изпълним.

Изпълнение на кукичка

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

С аргументи

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

Със stdin

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

Кукичката pre-push получава информация за ref чрез stdin.

Проверка за успех

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

runAndCheck() е удобен метод, който изпълнява кукичката и връща true, ако кодът за изход е 0. Ако кукичката не съществува, връща true (липсата на кукичка означава липса на възражение).

Списък на инсталираните кукички

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

Извежда списък на всички изпълними файлове в .git/hooks/ (с изключение на файловете .sample).

run() при липсваща или неизпълнима кукичка връща код за изход 0 и празен изход. Ако процесът не може да се стартира, връща код за изход 1 и Failed to start hook на stderr.

Инсталиране на кукичка

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

Записва съдържанието на скрипта в .git/hooks/<name> и задава разрешение за изпълнение (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);

Кукички, които Pitmaster изпълнява

HookRunner може да изпълнява всяка кукичка по име. Repository изпълнява автоматично следните кукички:

Кукичка Изпълнява се от Аргументи Може ли да прекъсне?
pre-commit commit() няма Да (ненулев код за изход)
prepare-commit-msg commit() файл със съобщение, след това message (или merge при завършване на сливане) Да
commit-msg commit() файл със съобщение (.git/COMMIT_EDITMSG); промените във файла се използват Да (ненулев код за изход)
post-commit commit() няма Не (само информативна)
pre-rebase rebase() ревизията нагоре по веригата Да
post-checkout checkout() старият HEAD, новият HEAD, 1 Не (само информативна)
post-merge merge(), mergeOctopus(), mergeContinue() 0 Не (само информативна)
pre-push push() и вариантите му име на отдалеченото хранилище, URL; редовете за ref се подават чрез stdin Да (ненулев код за изход)

Неуспешна кукичка кара операцията да хвърли RuntimeException('<name> hook failed'); изходът на кукичката не се включва. Кукичките за commit се изпълняват само от commit(): комитите за сливане, създадени от merge(), не ги изпълняват. post-checkout не се изпълнява за addWorktree(), reset() или restore().

Сървърните кукички (pre-receive, update, post-receive, post-update), pre-auto-gc и post-rewrite никога не се изпълняват автоматично.

Среда на кукичката

Когато Pitmaster изпълнява кукичка, ѝ подава следната среда:

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

Това са единствените променливи, достъпни за кукичката: PATH, HOME и всички останали променливи на процеса PHP не се предават (#13). Използвайте абсолютни пътища в скриптовете на кукичките или задайте PATH в самия скрипт; shebang #!/usr/bin/env php намира интерпретатори само в системния път по подразбиране.

Кукичката се изпълнява в dirname($gitDir), което е работното дърво на обикновена директория .git.

Формат на скрипта на кукичката

Кукичките могат да бъдат написани на всеки език. Първият ред трябва да е shebang:

Обвивка
#!/bin/bash
# Bash hook
PHP
#!/usr/bin/env php
<?php
// PHP hook
Python
#!/usr/bin/env python3
# Python hook

Пример: pre-commit с Pitmaster

commit() вече изпълнява pre-commit, prepare-commit-msg, commit-msg и post-commit, така че кодът на приложението трябва само да обработи неуспеха:

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

Извиквайте HookRunner сами само за манипулация с хранилище, отворена с ['hooks' => false], или за кукички, които Pitmaster не изпълнява.

Примерни кукички

Git инициализира хранилищата с файлове за кукички .sample; Pitmaster::init() създава празна директория hooks/. listHooks() на Pitmaster изключва файловете .sample:

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