Preskoči na vsebino

Git LFS

Git Large File Storage (LFS) velike datoteke v repozitoriju nadomesti z majhnimi datotekami kazalcev. Dejanska vsebina datotek je shranjena na ločenem strežniku LFS. Pitmaster lahko razčleni in ustvari kazalce LFS ter komunicira s strežniki LFS prek paketnega API. Ne bere .gitattributes in ne izvaja filtrov LFS clean in smudge: add() datoteke shrani nespremenjene, checkout() pa zapiše datoteke kazalcev. Pridobivanje dejanske vsebine prepusti vaši kodi, kot je prikazano spodaj.

Datoteke kazalcev LFS

Ko datoteko sledi LFS, git namesto dejanske vsebine shrani datoteko kazalca:

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

Kazalec vsebuje:

  • version: različico specifikacije LFS URL
  • oid: zgoščeno vrednost SHA-256 dejanske vsebine datoteke (s predpono sha256:)
  • size: dejansko velikost datoteke v bajtih

Razčlenjevanje kazalcev

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() vrne null, če manjka vrstica version, oid sha256: ali size. Vrednosti ne preverja (sprejme nešestnajstiški OID ali nenumerično velikost).

Zaznavanje kazalcev

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

isPointer() brez popolnega razčlenjevanja preveri prisotnost polj različice URL, oid sha256: in size.

Ustvarjanje kazalcev

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

Serializacija kazalcev

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

Odjemalec LFS

LfsClient komunicira s strežniki LFS prek paketnega API (POST na <url>/objects/batch).

Ustvarjanje odjemalca

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

Metoda fromRemoteUrl() sledi običajni konvenciji: doda /info/lfs k git URL (in po potrebi doda .git). Uporaben LFS URL je mogoče pridobiti samo iz oddaljenih URL-jev HTTP(S); oddaljeni repozitoriji v slogu SCP SSH (git@host:repo.git) ne delujejo.

LfsClient uporablja ovoj toka HTTP iz PHP (mora biti omogočen allow_url_fopen). Ne pošilja poverilnic in prezre preslikavo header, vrnjeno z vsakim dejanjem, zato deluje samo s strežniki LFS in URL-ji za shranjevanje, ki ne zahtevajo preverjanja pristnosti. Ni vezan na ročico repozitorija, zato izvaja omrežne zahteve tudi pri delu z repozitorijem, odprtim z Pitmaster::processFreeOptions().

Prenos datotek

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

V ozadju download():

  1. Pošlje paketno zahtevo za operacijo download
  2. Od strežnika prejme URL za prenos
  3. Iz tega URL pridobi vsebino datoteke

Preneseni bajti se ne preverijo glede na OID ali velikost.

Paketni prenos

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

Nalaganje datotek

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

Nalaganje pošlje paketno zahtevo za operacijo upload. Če strežnik objekt že ima, ne vrne URL za nalaganje in operacija ne naredi ničesar. Rezultat same zahteve za nalaganje se ne preveri, zato neuspešno nalaganje ni sporočeno, dejanje verify pa ni poklicano.

Paketno nalaganje

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
}

Protokol paketnega API

Paketni LFS API uporablja JSON prek HTTP:

Zahteva

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

Odgovor

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

Povezovanje LFS z operacijami repozitorija

Pri branju repozitorija, ki uporablja LFS, je lahko vsebina bloba kazalec LFS namesto dejanskih podatkov datoteke. Za pridobitev dejanske vsebine:

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

Obravnava napak

Neuspešna ali neveljavna paketna zahteva in neuspešna zahteva za prenos sprožijo ProtocolException. Tudi download() sproži napako, če strežnik za objekt ne vrne URL za prenos, pri čemer ne poda sporočila o napaki strežnika. batchDownload() in batchUpload() ne sprožita napake za napake posameznih objektov, temveč jih vrneta v polju 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";
}

Lastnosti kazalca LFS

Razred LfsPointer je samo za branje:

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