
# Git LFS

Git Lielo failu krātuve (LFS) aizstāj lielus failus repozitorijā ar maziem rādītāja failiem. Faktiskais failu saturs tiek glabāts atsevišķā LFS serverī. Pitmaster var parsēt un izveidot LFS rādītājus, kā arī sazināties ar LFS serveriem, izmantojot pakešu API. Tas nelasa `.gitattributes` un nedarbina LFS clean un smudge filtrus: `add()` glabā failus nemainītus, bet `checkout()` raksta rādītāja failus. Faktiskā satura iegūšana ir jūsu koda ziņā, kā parādīts tālāk.

## LFS rādītāja faili

Ja fails tiek pārvaldīts ar LFS, git saglabā rādītāja failu faktiskā satura vietā:

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

Rādītājā ir:
- `version`: LFS specifikācijas versija URL
- `oid`: faktiskā faila satura SHA-256 jaucējvērtība (ar prefiksu `sha256:`)
- `size`: faktiskā faila lielums baitos

### Rādītāju parsēšana

```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()` atgriež `null`, ja trūkst `version`, `oid sha256:` vai `size` rindas. Tas nepārbauda vērtību derīgumu (tiek pieņemts arī neheksadecimāls OID vai nen skaitlisks lielums).

### Rādītāju noteikšana

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

`isPointer()` pārbauda, vai ir versijas URL, `oid sha256:` un `size` lauki, neveicot pilnu parsēšanu.

### Rādītāju izveide

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

### Rādītāju serializēšana

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

## LFS klients

`LfsClient` sazinās ar LFS serveriem, izmantojot pakešu API (POST uz `<url>/objects/batch`).

### Klienta izveide

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

Metode `fromRemoteUrl()` darbojas atbilstoši standarta konvencijai: pievieno `/info/lfs` git URL (ja tā nav, pievienojot `.git`). Izmantojamas LFS URL nodrošina tikai attālo repozitoriju URL ar HTTP(S); SCP stila attālie repozitoriji SSH (`git@host:repo.git`) neder.

`LfsClient` izmanto PHP HTTP straumes apvalku (jābūt iespējotam `allow_url_fopen`). Tas nesūta akreditācijas datus un ignorē `header` karti, kas tiek atgriezta kopā ar katru darbību, tāpēc tas darbojas tikai ar LFS serveriem un krātuves URL, kuriem nav vajadzīga autentifikācija. Tas nav piesaistīts repozitorija objektam, tāpēc veic tīkla pieprasījumus arī tad, ja strādājat ar repozitoriju, kas atvērts ar `Pitmaster::processFreeOptions()`.

### Failu lejupielāde

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

Aizkulisēs `download()`:
1. Nosūta pakešu pieprasījumu `download` darbībai
2. Saņem no servera lejupielādes URL
3. Iegūst faila saturu no šī URL

Lejupielādētie baiti netiek pārbaudīti pret OID vai lielumu.

### Pakešu lejupielāde

```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']
    }
}
```

### Failu augšupielāde

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

Augšupielāde nosūta pakešu pieprasījumu `upload` darbībai. Ja serverim objekts jau ir, tas neatgriež augšupielādes URL, un darbība neko nedara. Pats augšupielādes pieprasījuma rezultāts netiek pārbaudīts, tāpēc par neveiksmīgu augšupielādi netiek ziņots un `verify` darbība netiek izsaukta.

### Pakešu augšupielāde

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

## Pakešu API protokols

LFS pakešu API izmanto JSON, izmantojot HTTP:

### Pieprasījums

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

### Atbilde

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

## LFS integrēšana repozitorija darbībās

Lasot repozitoriju, kas izmanto LFS, blob saturs var būt LFS rādītāji, nevis faktiskie faila dati. Lai iegūtu īsto saturu:

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

## Kļūdu apstrāde

Neveiksmīgs vai nederīgs pakešu pieprasījums un neveiksmīgs lejupielādes pieprasījums izraisa `ProtocolException`. `download()` arī izraisa kļūdu, ja serveris objektam neatgriež lejupielādes URL, neiekļaujot servera kļūdas ziņojumu. `batchDownload()` un `batchUpload()` neizraisa kļūdu atsevišķu objektu kļūdu gadījumā; tās atgriež kļūdas laukā `error`.

```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 rādītāju īpašības

Klase `LfsPointer` ir tikai lasāma:

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