Άγκιστρα
Τα hooks Git είναι εκτελέσιμα σκριπτ στο .git/hooks/ που εκτελούνται σε συγκεκριμένα σημεία των λειτουργιών του git. Το Pitmaster εντοπίζει και καλεί hooks μέσω του ελέγχου διεργασιών του PHP (proc_open).
Τα hooks εκτελούνται από προεπιλογή
Οι λειτουργίες αποθετηρίου εκτελούν αυτόματα τα hooks που παρατίθενται στην ενότητα Hooks που εκτελεί το Pitmaster. Για να τα απενεργοποιήσετε για έναν χειριστή αποθετηρίου, περάστε επιλογές κατά το άνοιγμά του:
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() δέχονται τις ίδιες επιλογές. Για να εκτελεστεί ένα hook απαιτείται το proc_open()· σε συστήματα όπου είναι απενεργοποιημένο, ανοίξτε τα αποθετήρια με ['hooks' => false]. Το CLI ανοίγει πάντα το αποθετήριο με ενεργοποιημένα τα hooks.
Το Pitmaster αναζητά hooks μόνο στο <gitDir>/hooks. Αγνοεί το core.hooksPath και, σε συνδεδεμένο worktree, αναζητά στο .git/worktrees/<name>/hooks, επομένως τα κοινόχρηστα σκριπτ .git/hooks δεν εκτελούνται εκεί (#12).
Το HookRunner, που περιγράφεται παρακάτω, αγνοεί αυτές τις επιλογές: η απευθείας κλήση του εκτελεί πάντα το hook.
HookRunner
use Pitmaster\Hooks\HookRunner;
$hooks = new HookRunner($repo->gitDir());
Έλεγχος ύπαρξης hook
if ($hooks->exists('pre-commit')) {
echo "pre-commit hook is installed\n";
}
Ένα hook υπάρχει αν το αρχείο βρίσκεται στο .git/hooks/<name> και είναι εκτελέσιμο.
Εκτέλεση hook
$result = $hooks->run('pre-commit');
echo $result['exitCode']; // 0 = success
echo $result['stdout']; // Hook's standard output
echo $result['stderr']; // Hook's standard error
Με ορίσματα
$result = $hooks->run('commit-msg', ['/path/to/COMMIT_EDITMSG']);
Με stdin
$result = $hooks->run('pre-push', ['origin', 'https://github.com/user/repo.git'], $stdinData);
Το hook pre-push λαμβάνει πληροφορίες αναφοράς μέσω stdin.
Έλεγχος επιτυχίας
if ($hooks->runAndCheck('pre-commit')) {
// Hook passed (exit code 0), proceed with commit
} else {
// Hook rejected the operation
}
Το runAndCheck() είναι μια βοηθητική συνάρτηση που εκτελεί το hook και επιστρέφει true αν ο κωδικός εξόδου είναι 0. Αν το hook δεν υπάρχει, επιστρέφει true (η απουσία hook σημαίνει ότι δεν υπάρχει αντίρρηση).
Λίστα εγκατεστημένων hooks
$installed = $hooks->listHooks();
// ['pre-commit', 'commit-msg', 'post-checkout']
Παραθέτει όλα τα εκτελέσιμα αρχεία στο .git/hooks/ (εξαιρούνται τα αρχεία .sample).
Το run() επιστρέφει κωδικό εξόδου 0 με κενή έξοδο αν το hook λείπει ή δεν είναι εκτελέσιμο. Αν δεν είναι δυνατή η εκκίνηση της διεργασίας, επιστρέφει κωδικό εξόδου 1 και γράφει το Failed to start hook στο stderr.
Εγκατάσταση hook
$hooks->install('pre-commit', "#!/bin/bash\nphp vendor/bin/phpcs --standard=PSR12 .\n");
Γράφει το περιεχόμενο του σκριπτ στο .git/hooks/<name> και ορίζει δικαίωμα εκτέλεσης (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);
Hooks που εκτελεί το Pitmaster
Το HookRunner μπορεί να εκτελέσει οποιοδήποτε hook βάσει ονόματος. Το Repository εκτελεί αυτόματα τα εξής:
| Hook | Εκτελείται από | Ορίσματα | Μπορεί να ακυρώσει; |
|---|---|---|---|
pre-commit |
commit() |
κανένα | Ναι (μη μηδενικός κωδικός εξόδου) |
prepare-commit-msg |
commit() |
αρχείο μηνύματος και μετά message (ή merge κατά την ολοκλήρωση συγχώνευσης) |
Ναι |
commit-msg |
commit() |
αρχείο μηνύματος (.git/COMMIT_EDITMSG)· χρησιμοποιούνται οι αλλαγές στο αρχείο |
Ναι (μη μηδενικός κωδικός εξόδου) |
post-commit |
commit() |
κανένα | Όχι (ενημερωτικό) |
pre-rebase |
rebase() |
η αναθεώρηση upstream | Ναι |
post-checkout |
checkout() |
παλιό HEAD, νέο HEAD, 1 |
Όχι (ενημερωτικό) |
post-merge |
merge(), mergeOctopus(), mergeContinue() |
0 |
Όχι (ενημερωτικό) |
pre-push |
push() και οι παραλλαγές του |
όνομα remote, URL· γραμμές ref στο stdin | Ναι (μη μηδενικός κωδικός εξόδου) |
Ένα hook που αποτυγχάνει κάνει τη λειτουργία να προκαλέσει RuntimeException('<name> hook failed')· η έξοδος του hook δεν περιλαμβάνεται. Τα hooks commit εκτελούνται μόνο από το commit(): οι συγχωνεύσεις commit που δημιουργούνται από το merge() δεν τα εκτελούν. Το post-checkout δεν εκτελείται για addWorktree(), reset() ή restore().
Τα hooks στην πλευρά του διακομιστή (pre-receive, update, post-receive, post-update), τα pre-auto-gc και post-rewrite δεν εκτελούνται ποτέ αυτόματα.
Περιβάλλον hook
Όταν το Pitmaster εκτελεί ένα hook, του περνά το εξής περιβάλλον:
$env = [
'GIT_DIR' => $this->gitDir,
'GIT_WORK_TREE' => dirname($this->gitDir),
];
Αυτές είναι οι μόνες μεταβλητές που λαμβάνει το hook: τα PATH, HOME και κάθε άλλη μεταβλητή της διεργασίας PHP δεν μεταβιβάζονται (#13). Χρησιμοποιήστε απόλυτες διαδρομές στα σκριπτ hook ή ορίστε το PATH μέσα στο σκριπτ· ένα shebang #!/usr/bin/env php εντοπίζει διερμηνευτές μόνο στην προεπιλεγμένη διαδρομή του συστήματος.
Το hook εκτελείται στο dirname($gitDir), τον κατάλογο εργασίας για έναν κανονικό κατάλογο .git.
Μορφή σκριπτ hook
Τα hooks μπορούν να γραφτούν σε οποιαδήποτε γλώσσα. Η πρώτη γραμμή πρέπει να είναι shebang:
#!/bin/bash
# Bash hook
#!/usr/bin/env php
<?php
// PHP hook
#!/usr/bin/env python3
# Python hook
Παράδειγμα: pre-commit με Pitmaster
Το commit() εκτελεί ήδη τα pre-commit, prepare-commit-msg, commit-msg και post-commit, επομένως ο κώδικας της εφαρμογής χρειάζεται μόνο να χειριστεί την αποτυχία:
try {
$commitId = $repo->commit('Add feature');
} catch (\RuntimeException $e) {
echo $e->getMessage(); // "pre-commit hook failed"
}
Καλέστε μόνοι σας το HookRunner σε χειριστή που άνοιξε με ['hooks' => false] ή για hooks που δεν εκτελεί το Pitmaster.
Ενδεικτικά hooks
Το Git αρχικοποιεί αποθετήρια με αρχεία hook .sample· το Pitmaster::init() δημιουργεί έναν κενό κατάλογο hooks/. Το Pitmaster του listHooks() εξαιρεί αρχεία .sample:
.git/hooks/
pre-commit.sample (excluded from listHooks)
commit-msg.sample (excluded from listHooks)
pre-commit (included - installed by user)