
# Kancalar

Git kancaları, git işlemlerinin belirli aşamalarında çalışan, `.git/hooks/` içindeki yürütülebilir betiklerdir. Pitmaster, kancaları PHP işlem denetimini (`proc_open`) kullanarak algılar ve çağırır.

## Kancalar varsayılan olarak çalışır

Depo işlemleri, [Kancalar Pitmaster çalıştırır](#hooks-pitmaster-runs) altında listelenen kancaları otomatik olarak çalıştırır. Bir depo tanıtıcısı için bunları kapatmak üzere depoyu açarken seçenekleri iletin:

```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()` ve `Pitmaster::clone()` aynı seçenekleri kabul eder. Bir kancayı çalıştırmak için `proc_open()` gerekir; bunu devre dışı bırakan sistemlerde depoları `['hooks' => false]` ile açın. CLI depoyu her zaman kancalar etkin olarak açar.

Pitmaster kancaları yalnızca `<gitDir>/hooks` içinde arar. `core.hooksPath` konumunu yok sayar; bağlantılı bir worktree'de ise `.git/worktrees/<name>/hooks` içinde arar, bu nedenle paylaşılan `.git/hooks` betikleri burada çalışmaz ([#12](https://github.com/inline0/pitmaster/issues/12)).

Aşağıda açıklanan `HookRunner` bu seçenekleri yok sayar: doğrudan çağrıldığında kancayı her zaman çalıştırır.

## HookRunner

```php
use Pitmaster\Hooks\HookRunner;

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

## Kancanın mevcut olup olmadığını denetleme

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

`.git/hooks/<name>` konumunda dosya varsa ve yürütülebilir durumdaysa kanca mevcuttur.

## Kanca çalıştırma

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

### Bağımsız değişkenlerle

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

### Standart girdiyi kullanarak

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

`pre-push` kancası, başvuru bilgilerini standart girdiden alır.

### Başarıyı denetleme

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

`runAndCheck()`, kancayı çalıştıran ve çıkış kodu 0 ise `true` döndüren bir kolaylık yöntemidir. Kanca yoksa `true` döndürür (kanca yoksa itiraz da yoktur).

## Kurulu kancaları listeleme

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

`.git/hooks/` konumundaki yürütülebilir tüm dosyaları listeler (`.sample` dosyaları hariç).

Eksik veya yürütülemeyen bir kanca için `run()`, boş çıktı ve 0 çıkış kodu döndürür. İşlem başlatılamazsa standart hata akışında `Failed to start hook` ile birlikte 1 çıkış kodu döndürür.

## Kanca kurma

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

Betik içeriğini `.git/hooks/<name>` konumuna yazar ve yürütme iznini ayarlar (`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 tarafından çalıştırılan kancalar

`HookRunner`, ada göre herhangi bir kancayı çalıştırabilir. `Repository` şu kancaları otomatik olarak çalıştırır:

| Kanca | Çalıştıran | Bağımsız değişkenler | İşlemi durdurabilir mi? |
|------|--------|-----------|-----------|
| `pre-commit` | `commit()` | yok | Evet (sıfır olmayan çıkış) |
| `prepare-commit-msg` | `commit()` | ileti dosyası, ardından `message` (birleştirme tamamlanırken `merge`) | Evet |
| `commit-msg` | `commit()` | ileti dosyası (`.git/COMMIT_EDITMSG`); dosyadaki düzenlemeler kullanılır | Evet (sıfır olmayan çıkış) |
| `post-commit` | `commit()` | yok | Hayır (bilgilendirme amaçlı) |
| `pre-rebase` | `rebase()` | üst akış revizyonu | Evet |
| `post-checkout` | `checkout()` | eski HEAD, yeni HEAD, `1` | Hayır (bilgilendirme amaçlı) |
| `post-merge` | `merge()`, `mergeOctopus()`, `mergeContinue()` | `0` | Hayır (bilgilendirme amaçlı) |
| `pre-push` | `push()` ve türevleri | uzak depo adı, URL; başvuru satırları standart girdide | Evet (sıfır olmayan çıkış) |

Başarısız bir kanca işlemin `RuntimeException('<name> hook failed')` hatasını fırlatmasına neden olur; kancanın çıktısı eklenmez. Commit kancaları yalnızca `commit()` üzerinden çalışır: `merge()` tarafından oluşturulan birleştirme commit'leri bunları çalıştırmaz. `post-checkout`, `addWorktree()`, `reset()` veya `restore()` için çalışmaz.

Sunucu tarafı kancalar (`pre-receive`, `update`, `post-receive`, `post-update`), `pre-auto-gc` ve `post-rewrite` hiçbir zaman otomatik olarak çalıştırılmaz.

## Kanca ortamı

Pitmaster bir kancayı çalıştırırken şu ortamı iletir:

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

Kanca yalnızca şu değişkenleri alır: `PATH`, `HOME` ve PHP işleminin diğer tüm değişkenleri aktarılmaz ([#13](https://github.com/inline0/pitmaster/issues/13)). Kanca betiklerinde mutlak yollar kullanın veya betiğin içinde `PATH` ayarlayın; `#!/usr/bin/env php` shebang'i yalnızca sistemin varsayılan yolundaki yorumlayıcıları bulur.

Kanca, normal bir `.git` dizininin çalışma ağacı olan `dirname($gitDir)` içinde çalışır.

## Kanca betiği biçimi

Kancalar herhangi bir dilde yazılabilir. İlk satır bir shebang olmalıdır:

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

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

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

## Örnek: Pitmaster ile pre-commit

`commit()` zaten `pre-commit`, `prepare-commit-msg`, `commit-msg` ve `post-commit` işlemlerini yürüttüğünden uygulama kodunun yalnızca hatayı işlemesi gerekir:

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

`HookRunner` öğesini yalnızca `['hooks' => false]` ile açılmış bir tanıtıcıda veya Pitmaster tarafından çalıştırılmayan kancalar için kendiniz çağırın.

## Örnek kancalar

Git, depoları `.sample` kanca dosyalarıyla başlatır; `Pitmaster::init()` boş bir `hooks/` dizini oluşturur. Pitmaster öğesinin `listHooks()` dosyası, `.sample` dosyalarını hariç tutar:

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