Git LFS
Git Large File Storage (LFS) заменя големите файлове в хранилището ви с малки файлове-указатели. Действителното съдържание се съхранява на отделен LFS сървър. Pitmaster може да парсва и създава LFS указатели и да комуникира с LFS сървъри чрез пакетния API. Не чете .gitattributes и не изпълнява LFS филтрите clean и smudge: add() съхранява файловете без промени, а checkout() записва файлове-указатели. Извличането на действителното съдържание остава задача на вашия код, както е показано по-долу.
Файлове-указатели LFS
Когато файлът се проследява от LFS, git съхранява файл-указател вместо действителното съдържание:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345
Указателят съдържа:
version: версията на спецификацията LFS URLoid: хеш SHA-256 на действителното съдържание на файла (с префиксsha256:)size: действителният размер на файла в байтове
Парсване на указатели
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() връща null, ако липсва редът version, oid sha256: или size. Стойностите не се валидират (приемат се не-шестнадесетичен OID и нечислов размер).
Откриване на указатели
if (LfsPointer::isPointer($blobContent)) {
// This blob is an LFS pointer, not actual file content
$pointer = LfsPointer::parse($blobContent);
}
isPointer() проверява дали присъстват полетата version URL, oid sha256: и size, без да парсва изцяло.
Създаване на указатели
// 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
Сериализиране на указатели
$content = $pointer->serialize();
// "version https://git-lfs.github.com/spec/v1\noid sha256:4d7a21...\nsize 12345\n"
LFS клиент
LfsClient комуникира с LFS сървъри чрез пакетния API (POST към <url>/objects/batch).
Създаване на клиент
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() следва стандартната конвенция: добавя /info/lfs към git URL (като добавя .git, ако липсва). Само отдалечените адреси HTTP(S) дават използваем LFS URL; отдалечените адреси във формат SCP SSH (git@host:repo.git) не работят.
LfsClient използва обвивката за поток HTTP на PHP (трябва да е активирана allow_url_fopen). Не изпраща идентификационни данни и игнорира картата header, върната с всяко действие, затова работи само с LFS сървъри и URL адреси за съхранение, които не изискват удостоверяване. Не е свързан с манипулация на хранилище, затова изпраща мрежови заявки дори при работа с хранилище, отворено с Pitmaster::processFreeOptions().
Изтегляне на файлове
// Single file download
$content = $client->download($pointer->oid, $pointer->size);
file_put_contents('large-file.bin', $content);
На практика download():
- Изпраща пакетна заявка за операцията
download - Получава URL за изтегляне от сървъра
- Извлича съдържанието на файла от този URL
Изтеглените байтове не се проверяват спрямо OID или размера.
Пакетно изтегляне
$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']
}
}
Качване на файлове
$client->upload($pointer->oid, $pointer->size, $fileContent);
При качване се изпраща пакетна заявка за операцията upload. Ако сървърът вече има обекта, той не връща URL за качване и операцията не прави нищо. Резултатът от самата заявка за качване не се проверява, затова неуспешното качване не се съобщава и действието verify не се извиква.
Пакетно качване
$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
}
Пакетен протокол API
Пакетният API на LFS използва JSON през HTTP:
Заявка
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}
]
}
Отговор
{
"objects": [
{
"oid": "abc123...",
"size": 12345,
"actions": {
"download": {
"href": "https://storage.example.com/abc123...",
"header": {
"Authorization": "Bearer token"
}
}
}
}
]
}
Интегриране на LFS с операциите на хранилището
При четене на хранилище, което използва LFS, съдържанието на blob може да е указател към LFS, а не действителните данни на файла. За да получите действителното съдържание:
$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;
}
Обработка на грешки
Неуспешна или невалидна пакетна заявка, както и неуспешна заявка за изтегляне, хвърля ProtocolException. download() също хвърля грешка, когато сървърът не върне URL за изтегляне на обекта, без съобщението за грешка от сървъра. batchDownload() и batchUpload() не хвърлят грешка при грешки за отделни обекти, а ги връщат в полето 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 указателите
Класът LfsPointer е само за четене:
$pointer->oid; // string: SHA-256 hash (hex)
$pointer->size; // int: file size in bytes
$pointer->version; // string: spec version URL