
# Āķi

Git āķi ir izpildāmi skripti `.git/hooks/`, kas darbojas noteiktos git darbību brīžos. Pitmaster nosaka un izsauc āķus, izmantojot PHP procesu vadību (`proc_open`).

## Āķi tiek palaisti pēc noklusējuma

Repozitorija darbības automātiski palaiž āķus, kas uzskaitīti sadaļā [Āķi Pitmaster izpilda](#hooks-pitmaster-runs). Lai tos izslēgtu repozitorija tvērumam, norādiet opcijas, to atverot:

```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()` un `Pitmaster::clone()` pieņem vienas un tās pašas opcijas. Āķa palaišanai nepieciešams `proc_open()`; vidēs, kur tas ir atspējots, atveriet repozitorijus ar `['hooks' => false]`. CLI vienmēr atver repozitoriju ar ieslēgtiem āķiem.

Pitmaster meklē āķus tikai mapē `<gitDir>/hooks`. Tas ignorē `core.hooksPath`, bet saistītā darbkokā meklē mapē `.git/worktrees/<name>/hooks`, tāpēc koplietotie skripti `.git/hooks` tur nedarbojas ([#12](https://github.com/inline0/pitmaster/issues/12)).

`HookRunner`, kas aprakstīts tālāk, ignorē šīs opcijas: izsaucot to tieši, āķis vienmēr tiek palaists.

## HookRunner

```php
use Pitmaster\Hooks\HookRunner;

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

## Pārbaude, vai āķis pastāv

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

Āķis pastāv, ja fails atrodas šeit: `.git/hooks/<name>`, un ir izpildāms.

## Āķa palaišana

```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
```

### Ar argumentiem

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

### Ar stdin

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

Āķis `pre-push` saņem ref informāciju, izmantojot stdin.

### Veiksmīgas izpildes pārbaude

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

`runAndCheck()` ir palīgfunkcija, kas palaiž āķi un atgriež `true`, ja izejas kods ir 0. Ja āķa nav, tā atgriež `true` (ja āķa nav, tas nav iebildums).

## Instalēto āķu saraksts

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

Uzskaita visus izpildāmos failus mapē `.git/hooks/` (izņemot `.sample` failus).

`run()` neesošam vai neizpildāmam āķim atgriež 0 izejas kodu un tukšu izvadi. Ja procesu nevar palaist, tas atgriež 1 izejas kodu un izvada `Failed to start hook` uz stderr.

## Āķa instalēšana

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

Ieraksta skripta saturu failā `.git/hooks/<name>` un iestata izpildes atļauju (`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);
```

## Āķi, ko izpilda Pitmaster

`HookRunner` var izpildīt jebkuru āķi pēc nosaukuma. `Repository` automātiski izpilda šos āķus:

| Āķis | Izpilda | Argumenti | Var pārtraukt? |
|------|--------|-----------|-----------|
| `pre-commit` | `commit()` | nav | Jā (izejas kods nav nulle) |
| `prepare-commit-msg` | `commit()` | ziņojuma fails, pēc tam `message` (vai `merge`, pabeidzot sapludināšanu) | Jā |
| `commit-msg` | `commit()` | ziņojuma fails (`.git/COMMIT_EDITMSG`); faila izmaiņas tiek izmantotas | Jā (izejas kods nav nulle) |
| `post-commit` | `commit()` | nav | Nē (informatīvs) |
| `pre-rebase` | `rebase()` | augšupējā versija | Jā |
| `post-checkout` | `checkout()` | vecais HEAD, jaunais HEAD, `1` | Nē (informatīvs) |
| `post-merge` | `merge()`, `mergeOctopus()`, `mergeContinue()` | `0` | Nē (informatīvs) |
| `pre-push` | `push()` un tā varianti | attālā repozitorija nosaukums, URL; ref rindas stdin ievadē | Jā (izejas kods nav nulle) |

Ja āķa izpilde neizdodas, darbība izraisa `RuntimeException('<name> hook failed')`; āķa izvade netiek iekļauta. Commit āķi tiek palaisti tikai no `commit()`: `merge()` izveidoti sapludināšanas komiti tos nepalaiž. `post-checkout` netiek palaists, ja izmanto `addWorktree()`, `reset()` vai `restore()`.

Servera puses āķi (`pre-receive`, `update`, `post-receive`, `post-update`), `pre-auto-gc` un `post-rewrite` nekad netiek palaisti automātiski.

## Āķa vide

Kad Pitmaster palaiž āķi, tas nodod šādu vidi:

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

Šie ir vienīgie mainīgie, ko saņem āķis: `PATH`, `HOME` un visi pārējie PHP procesa mainīgie netiek nodoti tālāk ([#13](https://github.com/inline0/pitmaster/issues/13)). Āķa skriptos izmantojiet absolūtos ceļus vai iestatiet `PATH` pašā skriptā; `#!/usr/bin/env php` shebang atrod interpretatorus tikai sistēmas noklusējuma ceļā.

Āķis darbojas mapē `dirname($gitDir)`, kas parastam `.git` direktorijam ir darbkoks.

## Āķa skripta formāts

Āķus var rakstīt jebkurā valodā. Pirmajai rindai jābūt shebang rindai:

```bash
#!/bin/bash
# Bash hook
```

```php
#!/usr/bin/env php
<?php
// PHP hook
```

```python
#!/usr/bin/env python3
# Python hook
```

## Piemērs: pre-commit ar Pitmaster

`commit()` jau izpilda `pre-commit`, `prepare-commit-msg`, `commit-msg` un `post-commit`, tāpēc lietotnes kodam jāapstrādā tikai kļūmes gadījums:

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

Izsauciet `HookRunner` paši tikai ar apstrādātāju, kas atvērts ar `['hooks' => false]`, vai āķiem, ko Pitmaster nepalaiž.

## Āķu piemēri

Git inicializē repozitorijus ar `.sample` āķu failiem; `Pitmaster::init()` izveido tukšu `hooks/` direktoriju. Pitmaster `listHooks()` ignorē `.sample` failus:

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