Lewati ke konten

Hook

Hook Git adalah skrip yang dapat dijalankan di .git/hooks/ dan berjalan pada titik tertentu dalam operasi git. Pitmaster mendeteksi dan memanggil hook menggunakan kendali proses milik PHP (proc_open).

Hook dijalankan secara default

Operasi repositori menjalankan hook yang tercantum di Hook Pitmaster berjalan secara otomatis. Untuk menonaktifkannya pada handle repositori, berikan opsi saat membukanya:

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() dan Pitmaster::clone() menerima opsi yang sama. Menjalankan hook memerlukan proc_open(); pada host yang menonaktifkannya, buka repositori dengan ['hooks' => false]. CLI selalu membuka repositori dengan hook diaktifkan.

Pitmaster hanya mencari hook di <gitDir>/hooks. core.hooksPath diabaikan, dan pada worktree tertaut hook dicari di .git/worktrees/<name>/hooks, sehingga skrip .git/hooks bersama tidak berjalan di sana (#12).

HookRunner, yang dijelaskan di bawah, mengabaikan opsi ini: pemanggilan langsung selalu menjalankan hook.

HookRunner

PHP
use Pitmaster\Hooks\HookRunner;

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

Memeriksa apakah hook tersedia

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

Hook tersedia jika filenya ada di .git/hooks/<name> dan dapat dieksekusi.

Menjalankan 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

Dengan argumen

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

Dengan stdin

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

Hook pre-push menerima informasi ref melalui stdin.

Memeriksa keberhasilan

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

runAndCheck() adalah fungsi praktis yang menjalankan hook dan mengembalikan true jika kode keluar bernilai 0. Jika hook tidak tersedia, hasilnya true (tidak adanya hook berarti tidak ada keberatan).

Mencantumkan hook yang terpasang

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

Mencantumkan semua file yang dapat dieksekusi di .git/hooks/ (tidak termasuk file .sample).

run() mengembalikan kode keluar 0 dengan keluaran kosong jika hook tidak ada atau tidak dapat dieksekusi. Jika proses tidak dapat dimulai, hasilnya kode keluar 1 dengan Failed to start hook pada stderr.

Memasang hook

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

Menulis konten skrip ke .git/hooks/<name> dan mengatur izin eksekusi (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);

Hook yang dijalankan Pitmaster

HookRunner dapat menjalankan hook apa pun berdasarkan nama. Repository menjalankan hook berikut secara otomatis:

Hook Dijalankan oleh Argumen Dapat membatalkan?
pre-commit commit() tidak ada Ya (kode keluar bukan nol)
prepare-commit-msg commit() file pesan, lalu message (atau merge saat menyelesaikan merge) Ya
commit-msg commit() file pesan (.git/COMMIT_EDITMSG); perubahan pada file akan digunakan Ya (kode keluar bukan nol)
post-commit commit() tidak ada Tidak (hanya informasi)
pre-rebase rebase() revisi upstream Ya
post-checkout checkout() HEAD lama, HEAD baru, 1 Tidak (hanya informasi)
post-merge merge(), mergeOctopus(), mergeContinue() 0 Tidak (hanya informasi)
pre-push push() beserta variannya nama remote, URL; baris ref melalui stdin Ya (kode keluar bukan nol)

Hook yang gagal menyebabkan operasi melempar RuntimeException('<name> hook failed'); keluarannya tidak disertakan. Hook commit hanya dijalankan dari commit(): commit merge yang dibuat oleh merge() tidak menjalankannya. post-checkout tidak dijalankan untuk addWorktree(), reset(), atau restore().

Hook sisi server (pre-receive, update, post-receive, post-update), pre-auto-gc, dan post-rewrite tidak pernah dijalankan secara otomatis.

Lingkungan hook

Saat Pitmaster menjalankan hook, variabel lingkungan berikut diteruskan:

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

Hanya variabel ini yang diterima hook: PATH, HOME, dan semua variabel lain dari proses PHP tidak diteruskan (#13). Gunakan path absolut dalam skrip hook, atau atur PATH di dalam skrip; shebang #!/usr/bin/env php hanya menemukan interpreter di path default sistem.

Hook berjalan di dirname($gitDir), yaitu working tree untuk direktori .git biasa.

Format skrip hook

Hook dapat ditulis dalam bahasa apa pun. Baris pertama harus berupa shebang:

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

Contoh: pre-commit dengan Pitmaster

commit() sudah menjalankan pre-commit, prepare-commit-msg, commit-msg, dan post-commit, sehingga kode aplikasi hanya perlu menangani kegagalan:

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

Panggil HookRunner sendiri hanya pada handle yang dibuka dengan ['hooks' => false], atau untuk hook yang tidak dijalankan oleh Pitmaster.

Contoh hook

Git menginisialisasi repositori dengan file hook .sample; Pitmaster::init() membuat direktori hooks/ kosong. Pitmaster mengecualikan file .sample melalui listHooks():

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