
# Clone, Fetch, Push

Pitmaster, git ağının üç temel işlemini destekler: clone (ilk indirme), fetch (artımlı indirme) ve push (yükleme). SSH URL'leri (`ssh://...`, `user@host:path`) SSH aktarımını kullanır; diğer URL'ler akıllı HTTP kullanır. Akıllı alışveriş başarısız olursa clone ve fetch, basit HTTP yöntemine geçer ([Protokoller](https://pitmaster.inline0.dev/tr/docs/network/protocols) bölümüne bakın).

Üçü de ağ aktarımlarını başlattığından, `['processes' => false]` veya `Pitmaster::processFreeOptions()` ile açılmış bir tanıtıcıda `RuntimeException` hatası fırlatırlar.

## Clone

```php
use Pitmaster\Pitmaster;

// public static function clone(string $url, string $path, ?int $depth = null, array $options = []): Repository
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/path/to/destination');
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/path/to/shallow', 1);
```

Yeni depo her zaman SHA-1 kullanır. Clone başarısız olursa ve hedef önceden yoksa, kısmen oluşturulan dizin kaldırılır.

### Clone akışı

1. **Başlat**: hedef yolda yeni ve boş bir depo oluştur
2. **Uzak depoyu yapılandır**: URL değerini `remote.origin.url` olarak kaydet ve fetch refspec'ini ayarla
3. **Ref'leri keşfet**: sunucudaki içerikleri öğrenmek için `GET /info/refs?service=git-upload-pack`
4. **Uzlaş**: tüm uzak ref'ler için `want` satırlarını gönder (hash'e göre yinelenenleri çıkar)
5. **Pack'i al**: istenen tüm nesneleri içeren pack dosyasını indir
6. **Pack'i yaz**: pack dosyasını `.git/objects/pack/` konumuna kaydet
7. **Pack'i indeksle**: hızlı aramalar için `.idx` dosyasını oluştur
8. **Ref'leri ayarla**: uzak izleme dallarını (`refs/remotes/origin/*`) ve etiketleri oluştur
9. **HEAD'i ayarla**: HEAD'i varsayılan dala yönlendir (sunucunun `symref=HEAD:...` yeteneğinden al veya sunucu göndermediyse `refs/heads/main` kullan) ve `branch.<name>.remote` ile `branch.<name>.merge` değerlerini ayarla
10. **Checkout yap**: çalışma ağacını ve indeksi yazan bu dalı checkout et

Son checkout sırasında oluşan hatalar yakalanıp yok sayılır; bu nedenle clone, boş veya kısmi bir çalışma ağacıyla dönebilir ([#19](https://github.com/inline0/pitmaster/issues/19)).

```php
$repo = Pitmaster::clone($url, $path);
$head = $repo->head();
echo $head->message;

$branches = $repo->branches();
$files = $repo->status();
```

### Çalışma ağacını oluşturma

Pack'i aldıktan sonra Pitmaster, varsayılan dal için `$repo->checkout()` çağrısı yapar. Bu işlem, HEAD commit'inin ağacındaki her dosyayı çalışma dizinine yazar ve indeksini ağaç girdilerinden oluşturur; böylece `status()` çalışma ağacının temiz olduğunu bildirir.

## Fetch

```php
// public function fetch(string $remote = 'origin', ?int $depth = null): void
$repo->fetch();              // Fetch from 'origin'
$repo->fetch('upstream');    // Fetch from a named remote
$repo->fetch('origin', 1);   // Shallow fetch
```

### Fetch akışı

1. **Uzak URL değerini oku**: `.git/config` üzerinden (`remote.<name>.url`)
2. **Ref'leri keşfet**: tüm uzak ref'lerin mevcut durumunu al
3. **İstenenler/mevcutlar için uzlaş**:
   - İstenenler: `remote.<name>.fetch` refspec'leriyle eşleşen (varsayılan `+refs/heads/*:refs/remotes/<name>/*`) ve nesneleri yerelde bulunmayan uzak ref'ler (derinlik belirtildiyse hepsi)
   - Mevcutlar: tüm yerel ref uçları
4. **Pack'i al**: yalnızca yeni nesneleri indir
5. **Pack'i yaz**: `.git/objects/pack/` konumuna kaydet ve indeksini oluştur
6. **İzleme ref'lerini güncelle**: eşleşen her uzak dal için `refs/remotes/<remote>/<branch>` değerini güncelle ve duyurulan tüm etiket ref'lerini yaz. Etiket nesneleri ayrı istenmediğinden, indirilmeyen bir nesneye ref veren yeni bir açıklamalı etiket yazılabilir ([#24](https://github.com/inline0/pitmaster/issues/24))

```php
// Fetch is incremental: only downloads what's new
$repo->fetch();

// After fetching, merge or rebase to incorporate changes (use the full ref name)
$result = $repo->merge('refs/remotes/origin/main');
```

`resolve()` adları önce `refs/heads/<name>`, sonra `refs/tags/<name>` ve son olarak adın kendisi olarak arar; bu nedenle kısa biçim olan `origin/main` bulunamaz.

Pitmaster henüz indeksleyemediği ince pack'ler ister ([#16](https://github.com/inline0/pitmaster/issues/16)). Artımlı fetch'e ince pack ile yanıt veren bir sunucu, `fetch()`'ün `PackParseException` hatası fırlatmasına neden olur.

### Zaten güncel

Uzak ref'lerin işaret ettiği her nesne yerelde zaten varsa pack istenmez ve `fetch()` ref keşfinden sonra döner. Bu durumda, uzak dallar zaten elinizde olan commit'lere ilerlemiş olsa bile izleme ref'leri güncellenmez ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Push

```php
$repo->push();                        // Push current branch to 'origin'
$repo->push('origin', 'main');        // Push a specific branch
$repo->push('upstream', 'feature');   // Push to a named remote
```

### Push akışı

1. **Uzak URL değerini oku**: `.git/config` üzerinden
2. **Gönderilecekleri belirle**: yerel dalı commit hash'ine çözümle
3. **Uzak ref'leri keşfet**: eski hash'i hesaplamak için uzak dalın mevcut durumunu al
4. **İleri sarımı denetle**: uzak dal yerel dalın atası değilse, hiçbir şey göndermeden önce `RuntimeException("Push rejected: non-fast-forward update to <branch>")` hatası fırlat
5. **Ref güncellemesini gönder**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Pack verisini gönder**: uzak deponun ihtiyaç duyduğu nesneleri içeren pack
7. **Sonucu denetle**: sunucu her ref için başarı/başarısızlık yanıtı verir

Kancalar devre dışı bırakılmadıysa, gönderimden önce `pre-push` kancası çalışır.

```php
// Push the current branch
$repo->push();

// Push a specific branch to a specific remote
$repo->push('origin', 'feature/login');
```

### Push gereksinimleri

- Mevcut dal yerelde bulunmalı
- Uzak depo, URL ile yapılandırılmış olmalı
- Uzak depo push işlemini kabul etmeli (kimlik doğrulama, izinler, ileri sarım dışı güncelleme politikaları)

### Zorla push

Pitmaster, uzak deponun canlı ref duyurusundaki eski hash'i gönderir ve `push()`, ileri sarım dışı güncellemeleri yerelde reddeder. Uzak dalın üzerine yazmak için force-with-lease kullanın:

```php
// Lease defaults to refs/remotes/<remote>/<branch>; pass an ObjectId to set it explicitly
$repo->pushForceWithLease('origin', 'main');
$repo->pushForceWithLease('origin', 'main', $repo->resolve('refs/remotes/origin/main'));

// Update several branches in one atomic request
$repo->pushAtomic('origin', ['main', 'release']);

// Make the remote mirror local refs (also deletes remote refs that no longer exist locally)
$repo->pushMirror('origin');
```

## UploadPackClient (fetch iç yapısı)

```php
use Pitmaster\Protocol\UploadPackClient;
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient();
$uploadPack = new UploadPackClient($http);

// Fetch with explicit wants and haves
$packData = $uploadPack->fetch($url, $wants, $haves);
```

İstenenler/mevcutlar uzlaşması bir pkt-line isteği oluşturur:

```
want <hash1> <capabilities>\n
want <hash2>\n
deepen <depth>\n        (only when a depth is given)
0000
have <hash3>\n
have <hash4>\n
done\n
```

Sunucu onaylarla, ardından pack verisiyle yanıt verir (yan bantla sarılmış olabilir).

## ReceivePackClient (push iç yapısı)

```php
use Pitmaster\Protocol\ReceivePackClient;
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient();
$receivePack = new ReceivePackClient($http);

$receivePack->push($url, [
    [
        'old' => $oldId,      // Current remote hash (or zero hash for new branch)
        'new' => $localId,    // Local commit hash to push
        'ref' => 'refs/heads/main',
    ],
], $packData);
```

Push isteğinin biçimi:

```
<old-hash> <new-hash> refs/heads/main\0<capabilities>\n
0000
PACK<pack data>
```

## Hata işleme

```php
use Pitmaster\Exceptions\ProtocolException;

try {
    $repo->fetch();
} catch (ProtocolException $e) {
    // Network unreachable, auth failed, server error, etc.
    echo $e->getMessage();
}

try {
    $repo->push();
} catch (\RuntimeException $e) {
    // Remote not configured, not on a branch, branch not found, non-fast-forward
    echo $e->getMessage();
}
```

`fetch()` ayrıca sıradan bir `RuntimeException` hatası (bilinmeyen uzak depo, süreç başlatamayan tanıtıcı) veya `PackParseException` hatası (indekslenemeyen pack) fırlatabilir. `ProtocolException` ve `PackParseException`, `\RuntimeException` sınıfını genişlettiğinden, `\RuntimeException` yakalanması hepsini kapsar.

## Uzak depo yapılandırması

Uzak depolar `.git/config` içinde saklanır:

```ini
[remote "origin"]
    url = https://github.com/user/repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
```

Yapılandırma API üzerinden erişin:

```php
$config = $repo->config();
$url = $config->get('remote.origin.url');
$config->set('remote.upstream.url', 'https://github.com/other/repo.git');
$config->set('remote.upstream.fetch', '+refs/heads/*:refs/remotes/upstream/*');

// set() changes the in-memory config only; write it to persist the change
$config->writeToFile($repo->commonGitDir() . '/config');
```

## Push için pack verisi

Şu anda Pitmaster, push edilen commit'lerden erişilebilen ancak uzak deponun mevcut ref'lerinden erişilemeyen nesneleri toplar ve delta sıkıştırması olmadan temel nesneler olarak bir pack'e yazar. İşlevsel açıdan doğrudur, ancak `git push`'inkinden daha büyük pack'ler üretir. Pack yazımında delta sıkıştırması planlanan bir iyileştirmedir.
