Git LFS
Git Large File Storage (LFS) saugykloje didelius failus pakeičia mažais rodyklių failais. Tikrasis failų turinys saugomas atskirame LFS serveryje. Pitmaster gali analizuoti ir kurti LFS rodykles bei bendrauti su LFS serveriais naudodama paketinį API. Ji neskaito .gitattributes ir nevykdo LFS clean bei smudge filtrų: add() failus saugo tokius, kokie jie yra, o checkout() įrašo rodyklių failus. Tikrojo turinio gavimas paliekamas jūsų kodui, kaip parodyta toliau.
LFS rodyklių failai
Kai failas sekamas per LFS, git vietoj tikrojo turinio saugo rodyklės failą:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345
Rodyklėje yra:
version: LFS specifikacijos versija URLoid: tikrojo failo turinio maiša SHA-256 (prieš ją rašomasha256:)size: tikrojo failo dydis baitais
Rodyklių analizė
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() grąžina null, jei trūksta version, oid sha256: arba size eilutės. Reikšmės netikrinamos (priimamas ir ne šešioliktainis OID, ir ne skaitinis dydis).
Rodyklių aptikimas
if (LfsPointer::isPointer($blobContent)) {
// This blob is an LFS pointer, not actual file content
$pointer = LfsPointer::parse($blobContent);
}
isPointer() patikrina, ar yra versijos URL, oid sha256: ir size laukai, bet neatlieka išsamios analizės.
Rodyklių kūrimas
// 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
Rodyklių serializavimas
$content = $pointer->serialize();
// "version https://git-lfs.github.com/spec/v1\noid sha256:4d7a21...\nsize 12345\n"
LFS klientas
LfsClient bendrauja su LFS serveriais naudodama paketinį API (POST į <url>/objects/batch).
Kliento kūrimas
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() metodas atitinka standartinę konvenciją: prie git URL prideda /info/lfs (pridedama .git, jei jos nėra). Tinkamą LFS URL suteikia tik HTTP(S) nuotolinių saugyklų URL; SCP formato SSH nuotolinės saugyklos (git@host:repo.git) netinka.
LfsClient naudoja PHP srauto apvalkalą HTTP (allow_url_fopen turi būti įjungta). Jis nesiunčia prisijungimo duomenų ir nepaiso su kiekvienu veiksmu grąžinamo header susiejimo, todėl veikia tik su LFS serveriais ir saugyklos URL adresais, kuriems nereikia autentifikavimo. Jis nesusietas su saugyklos deskriptoriumi, todėl siunčia tinklo užklausas net dirbant su saugykla, atidaryta naudojant Pitmaster::processFreeOptions().
Failų atsisiuntimas
// Single file download
$content = $client->download($pointer->oid, $pointer->size);
file_put_contents('large-file.bin', $content);
Viduje download():
- Siunčia paketinę užklausą
downloadoperacijai - Iš serverio gauna atsisiuntimo URL
- Atsisiunčia failo turinį iš to URL
Atsisiųsti baitai netikrinami pagal OID ar dydį.
Paketinis atsisiuntimas
$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']
}
}
Failų įkėlimas
$client->upload($pointer->oid, $pointer->size, $fileContent);
Įkeliant siunčiama paketinė užklausa upload operacijai. Jei serveris objektą jau turi, jis negrąžina įkėlimo URL, todėl nieko nevyksta. Pats įkėlimo užklausos rezultatas netikrinamas, taigi nepavykęs įkėlimas nepranešamas, o veiksmas verify neiškviečiamas.
Paketinis įkėlimas
$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
}
Paketinio API protokolas
LFS paketinis API naudoja JSON per HTTP:
Užklausa
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}
]
}
Atsakymas
{
"objects": [
{
"oid": "abc123...",
"size": 12345,
"actions": {
"download": {
"href": "https://storage.example.com/abc123...",
"header": {
"Authorization": "Bearer token"
}
}
}
}
]
}
LFS integravimas į saugyklos operacijas
Skaitant LFS naudojančią saugyklą, blob turinys gali būti LFS rodyklės, o ne tikrieji failų duomenys. Norėdami gauti tikrąjį turinį:
$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;
}
Klaidų tvarkymas
Nepavykusi arba netinkama paketinė užklausa ir nepavykusi atsisiuntimo užklausa išmeta ProtocolException. download() taip pat išmeta klaidą, kai serveris objektui negrąžina atsisiuntimo URL, ir nenurodo serverio klaidos pranešimo. batchDownload() ir batchUpload() dėl atskirų objektų klaidų klaidų neišmeta – jos grąžinamos lauke 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";
}
LFS rodyklės savybės
Klasė LfsPointer yra tik skaitoma:
$pointer->oid; // string: SHA-256 hash (hex)
$pointer->size; // int: file size in bytes
$pointer->version; // string: spec version URL