Git LFS
Git Large File Storage (LFS) înlocuiește fișierele mari din depozit cu fișiere pointer mici. Conținutul real al fișierelor este stocat pe un server LFS separat. Pitmaster poate analiza și crea pointere LFS și poate comunica cu serverele LFS prin API batch. Nu citește .gitattributes și nu rulează filtrele LFS clean și smudge: add() stochează fișierele așa cum sunt, iar checkout() scrie fișiere pointer. Preluarea conținutului real revine codului dumneavoastră, după cum se arată mai jos.
Fișiere pointer LFS
Când un fișier este urmărit de LFS, git stochează un fișier pointer în locul conținutului real:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345
Pointerul conține:
version: versiunea specificației LFS URLoid: hash-ul SHA-256 al conținutului real al fișierului (cu prefixulsha256:)size: dimensiunea reală a fișierului în octeți
Analizarea pointerelor
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() returnează null dacă lipsește linia version, oid sha256: sau size. Nu validează valorile (un OID non-hexazecimal sau o dimensiune nenumerică este acceptată).
Detectarea pointerelor
if (LfsPointer::isPointer($blobContent)) {
// This blob is an LFS pointer, not actual file content
$pointer = LfsPointer::parse($blobContent);
}
isPointer() verifică prezența câmpurilor versiune URL, oid sha256: și size fără analizare completă.
Crearea pointerelor
// 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
Serializarea pointerelor
$content = $pointer->serialize();
// "version https://git-lfs.github.com/spec/v1\noid sha256:4d7a21...\nsize 12345\n"
Client LFS
LfsClient comunică cu serverele LFS folosind API batch (POST către <url>/objects/batch).
Crearea unui client
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() urmează convenția standard: adaugă /info/lfs la git URL (adăugând .git dacă lipsește). Numai URL-urile remote HTTP(S) oferă un URL LFS utilizabil; remote-urile în stil SCP SSH (git@host:repo.git) nu oferă unul.
LfsClient folosește wrapper-ul de flux HTTP din PHP (trebuie activat allow_url_fopen). Nu trimite credențiale și ignoră maparea header returnată pentru fiecare acțiune, deci funcționează doar cu servere LFS și URL-uri de stocare care nu necesită autentificare. Nu este legat de un handle de depozit, așa că efectuează cereri de rețea chiar și când lucrați cu un depozit deschis cu Pitmaster::processFreeOptions().
Descărcarea fișierelor
// Single file download
$content = $client->download($pointer->oid, $pointer->size);
file_put_contents('large-file.bin', $content);
În fundal, download():
- Trimite o cerere batch pentru operațiunea
download - Primește de la server un URL de descărcare
- Preia conținutul fișierului de la acel URL
Octeții descărcați nu sunt verificați în raport cu OID-ul sau dimensiunea.
Descărcare batch
$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']
}
}
Încărcarea fișierelor
$client->upload($pointer->oid, $pointer->size, $fileContent);
Încărcarea trimite o cerere batch pentru operațiunea upload. Dacă serverul are deja obiectul, nu returnează niciun URL de încărcare, iar operațiunea nu face nimic. Rezultatul cererii de încărcare în sine nu este verificat, așa că o încărcare eșuată nu este raportată, iar acțiunea verify nu este apelată.
Încărcare batch
$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
}
Protocolul API batch
Batch-ul LFS API folosește JSON prin HTTP:
Cerere
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}
]
}
Răspuns
{
"objects": [
{
"oid": "abc123...",
"size": 12345,
"actions": {
"download": {
"href": "https://storage.example.com/abc123...",
"header": {
"Authorization": "Bearer token"
}
}
}
}
]
}
Integrarea LFS cu operațiunile asupra depozitului
Când citiți un depozit care folosește LFS, conținutul blob-urilor poate consta în pointere LFS în locul datelor reale ale fișierelor. Pentru a obține conținutul real:
$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;
}
Tratarea erorilor
O cerere batch eșuată sau nevalidă, precum și o cerere de descărcare eșuată, aruncă ProtocolException. download() aruncă, de asemenea, o excepție când serverul nu returnează niciun URL de descărcare pentru obiect, fără mesajul de eroare al serverului. batchDownload() și batchUpload() nu aruncă excepții pentru erori la nivel de obiect; le returnează în câmpul error.
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";
}
Proprietățile pointerelor LFS
Clasa LfsPointer este numai pentru citire:
$pointer->oid; // string: SHA-256 hash (hex)
$pointer->size; // int: file size in bytes
$pointer->version; // string: spec version URL