Μετάβαση στο περιεχόμενο

Άγκιστρα

Τα hooks Git είναι εκτελέσιμα σκριπτ στο .git/hooks/ που εκτελούνται σε συγκεκριμένα σημεία των λειτουργιών του git. Το Pitmaster εντοπίζει και καλεί hooks μέσω του ελέγχου διεργασιών του PHP (proc_open).

Τα hooks εκτελούνται από προεπιλογή

Οι λειτουργίες αποθετηρίου εκτελούν αυτόματα τα hooks που παρατίθενται στην ενότητα Hooks που εκτελεί το 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() δέχονται τις ίδιες επιλογές. Για να εκτελεστεί ένα hook απαιτείται το proc_open()· σε συστήματα όπου είναι απενεργοποιημένο, ανοίξτε τα αποθετήρια με ['hooks' => false]. Το CLI ανοίγει πάντα το αποθετήριο με ενεργοποιημένα τα hooks.

Το Pitmaster αναζητά hooks μόνο στο <gitDir>/hooks. Αγνοεί το core.hooksPath και, σε συνδεδεμένο worktree, αναζητά στο .git/worktrees/<name>/hooks, επομένως τα κοινόχρηστα σκριπτ .git/hooks δεν εκτελούνται εκεί (#12).

Το HookRunner, που περιγράφεται παρακάτω, αγνοεί αυτές τις επιλογές: η απευθείας κλήση του εκτελεί πάντα το hook.

HookRunner

PHP
use Pitmaster\Hooks\HookRunner;

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

Έλεγχος ύπαρξης hook

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

Ένα hook υπάρχει αν το αρχείο βρίσκεται στο .git/hooks/<name> και είναι εκτελέσιμο.

Εκτέλεση 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

Με ορίσματα

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);

Το hook pre-push λαμβάνει πληροφορίες αναφοράς μέσω stdin.

Έλεγχος επιτυχίας

PHP
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

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

Παραθέτει όλα τα εκτελέσιμα αρχεία στο .git/hooks/ (εξαιρούνται τα αρχεία .sample).

Το run() επιστρέφει κωδικό εξόδου 0 με κενή έξοδο αν το hook λείπει ή δεν είναι εκτελέσιμο. Αν δεν είναι δυνατή η εκκίνηση της διεργασίας, επιστρέφει κωδικό εξόδου 1 και γράφει το Failed to start hook στο stderr.

Εγκατάσταση hook

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);

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, του περνά το εξής περιβάλλον:

PHP
$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
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] ή για 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)