
# Git LFS

Git Büyük Dosya Depolama (LFS), deponuzdaki büyük dosyaları küçük işaretçi dosyalarıyla değiştirir. Asıl dosya içeriği ayrı bir LFS sunucusunda saklanır. Pitmaster, LFS işaretçilerini ayrıştırıp oluşturabilir ve toplu API üzerinden LFS sunucularıyla iletişim kurabilir. `.gitattributes` dosyasını okumaz veya LFS temizleme ve leke bırakma filtrelerini çalıştırmaz: `add()` dosyaları olduğu gibi saklar, `checkout()` ise işaretçi dosyaları yazar. Gerçek içeriği getirme işi aşağıda gösterildiği gibi kodunuza aittir.

## LFS İşaretçi Dosyaları

Bir dosya LFS tarafından izlendiğinde git, asıl içerik yerine bir işaretçi dosyası saklar:

```
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345
```

İşaretçi şunları içerir:
- `version`: LFS belirtim sürümü URL
- `oid`: asıl dosya içeriğinin SHA-256 özeti (başına `sha256:` eklenir)
- `size`: asıl dosyanın bayt cinsinden boyutu

### İşaretçileri ayrıştırma

```php
use Pitmaster\Lfs\LfsPointer;

$content = file_get_contents('.git-lfs-tracked-file');
$pointer = LfsPointer::parse($content);

if ($pointer !== null) {
    echo "OID: {$pointer->oid}\n";
    echo "Size: {$pointer->size}\n";
    echo "Version: {$pointer->version}\n";
}
```

`parse()`, `version`, `oid sha256:` veya `size` satırı eksikse `null` döndürür. Değerleri doğrulamaz (onaltılık olmayan bir OID veya sayısal olmayan bir boyut kabul edilir).

### İşaretçileri algılama

```php
if (LfsPointer::isPointer($blobContent)) {
    // This blob is an LFS pointer, not actual file content
    $pointer = LfsPointer::parse($blobContent);
}
```

`isPointer()`, sürüm URL, `oid sha256:` ve `size` alanlarının mevcut olup olmadığını tümüyle ayrıştırmadan denetler.

### İşaretçi oluşturma

```php
// From known OID and size
$pointer = new LfsPointer(
    oid: '4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393',
    size: 12345,
);

// From file content (computes SHA-256 hash)
$pointer = LfsPointer::forContent($actualFileContent);

echo $pointer->oid;   // SHA-256 hash
echo $pointer->size;  // Content length
```

### İşaretçileri serileştirme

```php
$content = $pointer->serialize();
// "version https://git-lfs.github.com/spec/v1\noid sha256:4d7a21...\nsize 12345\n"
```

## LFS İstemcisi

`LfsClient`, toplu API kullanarak LFS sunucularıyla iletişim kurar (`<url>/objects/batch` adresine POST isteği gönderir).

### İstemci oluşturma

```php
use Pitmaster\Lfs\LfsClient;

// From a git remote URL (appends /info/lfs)
$client = LfsClient::fromRemoteUrl('https://github.com/user/repo.git');
// LFS URL: https://github.com/user/repo.git/info/lfs

// Direct URL
$client = new LfsClient('https://lfs.example.com/repo', timeout: 60);
```

`fromRemoteUrl()` yöntemi standart kuralı izler: git URL değerine `/info/lfs` ekler (yoksa `.git` ekleyerek). Yalnızca HTTP(S) uzak depo URL'leri kullanılabilir bir LFS URL değeri sağlar; SCP tarzı SSH uzak depoları (`git@host:repo.git`) sağlamaz.

`LfsClient`, PHP öğesinin HTTP akış sarmalayıcısını kullanır (`allow_url_fopen` etkin olmalıdır). Kimlik bilgisi göndermez ve her eylemle döndürülen `header` eşlemesini yok sayar; bu nedenle yalnızca kimlik doğrulaması gerektirmeyen LFS sunucuları ve depolama URL'leriyle çalışır. Bir depo tanıtıcısına bağlı olmadığından, `Pitmaster::processFreeOptions()` ile açılmış bir depoda çalışırken bile ağ istekleri yapar.

### Dosyaları indirme

```php
// Single file download
$content = $client->download($pointer->oid, $pointer->size);
file_put_contents('large-file.bin', $content);
```

Arka planda `download()` şunları yapar:
1. `download` işlemi için toplu istek gönderir
2. Sunucudan indirme URL bilgisi alır
3. Dosya içeriğini bu URL konumundan getirir

İndirilen baytlar OID veya boyutla karşılaştırılarak denetlenmez.

### Toplu indirme

```php
$results = $client->batchDownload([
    ['oid' => 'abc123...', 'size' => 12345],
    ['oid' => 'def456...', 'size' => 67890],
]);

foreach ($results as $result) {
    if ($result['error'] !== null) {
        echo "Error: {$result['error']}\n";
        continue;
    }

    if ($result['href'] !== null) {
        // Download from $result['href']
    }
}
```

### Dosyaları yükleme

```php
$client->upload($pointer->oid, $pointer->size, $fileContent);
```

Yükleme, `upload` işlemi için toplu istek gönderir. Sunucuda nesne zaten varsa yükleme URL bilgisi döndürmez ve işlem hiçbir şey yapmaz. Yükleme isteğinin sonucu denetlenmediğinden başarısız yükleme bildirilmez ve `verify` eylemi çağrılmaz.

### Toplu yükleme

```php
$results = $client->batchUpload([
    ['oid' => 'abc123...', 'size' => 12345],
]);

foreach ($results as $result) {
    if ($result['href'] !== null) {
        // Server needs this object, upload to $result['href']
    }
    // No href means server already has it
}
```

## Toplu API protokolü

LFS toplu API, HTTP üzerinden JSON kullanır:

### İstek

```
POST <lfs-url>/objects/batch
Content-Type: application/vnd.git-lfs+json
Accept: application/vnd.git-lfs+json

{
    "operation": "download",
    "transfers": ["basic"],
    "objects": [
        {"oid": "abc123...", "size": 12345}
    ]
}
```

### Yanıt

```json
{
    "objects": [
        {
            "oid": "abc123...",
            "size": 12345,
            "actions": {
                "download": {
                    "href": "https://storage.example.com/abc123...",
                    "header": {
                        "Authorization": "Bearer token"
                    }
                }
            }
        }
    ]
}
```

## LFS'yi depo işlemleriyle tümleştirme

LFS kullanan bir depoyu okurken blob içeriği gerçek dosya verisi yerine LFS işaretçileri olabilir. Gerçek içeriği almak için:

```php
$blob = $repo->readObject($hash);

if (LfsPointer::isPointer($blob->content)) {
    $pointer = LfsPointer::parse($blob->content);
    $remoteUrl = $repo->config()->get('remote.origin.url');
    $client = LfsClient::fromRemoteUrl($remoteUrl);
    $realContent = $client->download($pointer->oid, $pointer->size);
} else {
    $realContent = $blob->content;
}
```

## Hata işleme

Başarısız veya geçersiz bir toplu istek ya da başarısız bir indirme isteği `ProtocolException` hatasını fırlatır. Nesne için sunucu indirme URL bilgisi döndürmezse `download()` de sunucunun hata iletisini içermeden hata fırlatır. `batchDownload()` ve `batchUpload()` nesne başına hatalarda hata fırlatmaz; bunları `error` alanında döndürür.

```php
use Pitmaster\Exceptions\ProtocolException;

try {
    $content = $client->download($oid, $size);
} catch (ProtocolException $e) {
    // Server unreachable, invalid response, no download URL
    echo "LFS error: {$e->getMessage()}\n";
}
```

## LFS işaretçisi özellikleri

`LfsPointer` sınıfı salt okunurdur:

```php
$pointer->oid;      // string: SHA-256 hash (hex)
$pointer->size;     // int: file size in bytes
$pointer->version;  // string: spec version URL
```
