Git LFS
Git Large File Storage (LFS) sostituisce i file di grandi dimensioni nel repository con piccoli file puntatore. Il contenuto effettivo viene archiviato su un server LFS separato. Pitmaster può analizzare e creare puntatori LFS e comunicare con i server LFS tramite l’API batch. Non legge .gitattributes né esegue i filtri clean e smudge di LFS: add() archivia i file così come sono e checkout() scrive i file puntatore. Il recupero del contenuto effettivo spetta al vostro codice, come mostrato sotto.
File puntatore LFS
Quando un file è tracciato da LFS, git archivia un file puntatore invece del contenuto effettivo:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345
Il puntatore contiene:
version: la versione della specifica LFS URLoid: hash SHA-256 del contenuto effettivo del file (preceduto dasha256:)size: la dimensione effettiva del file in byte
Analizzare i puntatori
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() restituisce null se manca la riga version, oid sha256: o size. Non convalida i valori (accetta un OID non esadecimale o una dimensione non numerica).
Rilevare i puntatori
if (LfsPointer::isPointer($blobContent)) {
// This blob is an LFS pointer, not actual file content
$pointer = LfsPointer::parse($blobContent);
}
isPointer() verifica la presenza dei campi version URL, oid sha256: e size senza eseguire un’analisi completa.
Creare puntatori
// 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
Serializzare i puntatori
$content = $pointer->serialize();
// "version https://git-lfs.github.com/spec/v1\noid sha256:4d7a21...\nsize 12345\n"
Client LFS
Il LfsClient comunica con i server LFS usando l’API batch (POST a <url>/objects/batch).
Creare un 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);
Il metodo fromRemoteUrl() segue la convenzione standard: aggiunge /info/lfs all’git URL (aggiungendo .git se non è presente). Solo gli URL remoti HTTP(S) forniscono un URL LFS utilizzabile; i remoti SSH in stile SCP (git@host:repo.git) non lo fanno.
LfsClient usa il wrapper di stream HTTP di PHP (allow_url_fopen deve essere abilitato). Non invia credenziali e ignora la mappa header restituita con ogni azione, quindi funziona solo con server LFS e URL di archiviazione che non richiedono autenticazione. Non è associato a un handle del repository, quindi invia richieste di rete anche quando si lavora con un repository aperto con Pitmaster::processFreeOptions().
Scaricare file
// Single file download
$content = $client->download($pointer->oid, $pointer->size);
file_put_contents('large-file.bin', $content);
In pratica, download():
- Invia una richiesta batch per l’operazione
download - Riceve dal server un URL di download
- Recupera il contenuto del file da quell’URL
I byte scaricati non vengono verificati rispetto all’OID o alla dimensione.
Download 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']
}
}
Caricare file
$client->upload($pointer->oid, $pointer->size, $fileContent);
Il caricamento invia una richiesta batch per l’operazione upload. Se il server possiede già l’oggetto, non restituisce alcun URL di caricamento e l’operazione non fa nulla. Il risultato della richiesta di caricamento non viene verificato, quindi un errore di caricamento non viene segnalato e l’azione verify non viene chiamata.
Caricamento 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
}
Protocollo batch API
L’API batch LFS usa JSON su HTTP:
Richiesta
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}
]
}
Risposta
{
"objects": [
{
"oid": "abc123...",
"size": 12345,
"actions": {
"download": {
"href": "https://storage.example.com/abc123...",
"header": {
"Authorization": "Bearer token"
}
}
}
}
]
}
Integrare LFS con le operazioni sul repository
Quando si legge un repository che usa LFS, il contenuto dei blob può essere costituito da puntatori LFS anziché dai dati effettivi dei file. Per ottenere il contenuto effettivo:
$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;
}
Gestione degli errori
Una richiesta batch non riuscita o non valida, e una richiesta di download non riuscita, generano ProtocolException. Anche download() genera un errore quando il server non restituisce alcun URL di download per l’oggetto, senza includere il messaggio di errore del server. batchDownload() e batchUpload() non generano errori per gli errori dei singoli oggetti, ma li restituiscono nel campo 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à dei puntatori LFS
La classe LfsPointer è di sola lettura:
$pointer->oid; // string: SHA-256 hash (hex)
$pointer->size; // int: file size in bytes
$pointer->version; // string: spec version URL