Treci la conținut

Obiecte Git

Totul într-un git este stocat ca obiect. Există patru tipuri: bloburi (conținutul fișierelor), arbori (liste de directoare), commituri (instantanee cu metadate) și taguri (referințe adnotate). Fiecare obiect este identificat prin SHA-1 conținutului său.

Tipuri de obiecte

Blob

Un blob conține conținutul brut al unui fișier, fără metadate (nume de fișier sau permisiuni).

PHP
use Pitmaster\Object\Blob;

// Create from content
$blob = Blob::fromContent('Hello, world!');
echo $blob->content;    // 'Hello, world!'
echo $blob->size();     // 13
echo $blob->id->hex;    // SHA-1 hash
echo $blob->type->value; // 'blob' (the property is ObjectType::Blob)

Arbore

Un arbore este o listă de directoare. Fiecare intrare are un mod, un nume și un hash care indică spre un blob sau un subarbore.

PHP
use Pitmaster\Object\Tree;
use Pitmaster\Object\TreeEntry;

// Read a tree
$tree = $repo->readObject($treeHash);

foreach ($tree->entries as $entry) {
    echo "{$entry->mode} {$entry->name} {$entry->hash->hex}\n";
}

// Look up a specific entry
$entry = $tree->entry('README.md');

// Create a tree (entries already in git's sort order)
$entries = [
    new TreeEntry('100644', 'README.md', $blobId),
    new TreeEntry('100755', 'deploy.sh', $execBlobId),
    new TreeEntry('40000', 'src', $subtreeId),
];

$tree = Tree::fromEntries($entries);

Tree::fromEntries() scrie intrările în ordinea dată și șirurile de mod exact așa cum sunt furnizate, așa că sortați-le mai întâi și scrieți modurile directoarelor ca 40000. Un arbore scris cu 040000 sau în altă ordine primește un hash diferit de cel al git, iar git fsck --strict raportează modul completat cu zerouri la stânga.

TreeEntry

TreeEntry este un obiect valoare doar pentru citire:

PHP
use Pitmaster\Object\TreeEntry;

$entry->mode;   // '100644', '100755', '40000', '120000', '160000'
$entry->name;   // Filename
$entry->hash;   // ObjectId

// Type checks
$entry->isBlob();        // regular or executable file (mode 10xxxx)
$entry->isExecutable();  // mode 100755
$entry->isTree();        // mode 40000 (also accepts 040000)
$entry->isSymlink();     // mode 120000
$entry->isGitlink();     // mode 160000 (submodule)

Valori pentru moduri:

Mod Semnificație
100644 Fișier obișnuit
100755 Fișier executabil
40000 Director (arbore); afișat ca 040000 de git ls-tree
120000 Legătură simbolică
160000 Gitlink (submodul)

Intrările arborelui sunt sortate după nume, iar numele directoarelor sunt tratate ca și cum s-ar termina cu / în scopul comparației.

Commit

Un commit leagă un arbore de istoricul și metadatele sale.

PHP
use Pitmaster\Object\Commit;

$commit = $repo->head();

$commit->id;         // ObjectId
$commit->tree;       // ObjectId of the tree
$commit->parents;    // array of ObjectId (zero for root, one for normal, two+ for merge)
$commit->author;     // 'Name <email> timestamp timezone'
$commit->committer;  // 'Name <email> timestamp timezone'
$commit->message;    // Full commit message

$commit->isMerge();  // true if two or more parents

Construirea conținutului unui commit

PHP
$content = Commit::buildContent(
    tree: $treeId,
    parents: [$parentId],
    author: 'Jane <jane@example.com> 1700000000 +0000',
    committer: 'Jane <jane@example.com> 1700000000 +0000',
    message: "Add feature\n",
);

Formatul brut al unui commit:

Cod
tree <hex hash>\n
parent <hex hash>\n       (zero or more)
author <name> <<email>> <timestamp> <tz>\n
committer <name> <<email>> <timestamp> <tz>\n
\n
<message>

Tag

Tagurile adnotate sunt obiecte cu metadate.

PHP
use Pitmaster\Object\Tag;

$tag = $repo->readObject($tagHash);

$tag->object;        // ObjectId of the target
$tag->objectType;    // ObjectType enum (ObjectType::Commit, ...)
$tag->name;          // Tag name
$tag->tagger;        // 'Name <email> timestamp timezone'
$tag->message;       // Tag message

Formatul brut al unui tag:

Cod
object <hex hash>\n
type commit\n
tag <name>\n
tagger <name> <<email>> <timestamp> <tz>\n
\n
<message>

ObjectId

ObjectId este un obiect valoare doar pentru citire care reprezintă un hash SHA-1 (sau SHA-256).

PHP
use Pitmaster\Object\ObjectId;
use Pitmaster\Object\ObjectType;

// From hex string
$id = ObjectId::fromHex('a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2');

// From binary (20 bytes for SHA-1, 32 for SHA-256)
$id = ObjectId::fromBinary($rawBytes);

// Compute from content
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!');
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!', 'sha256');

// Zero hash
$id = ObjectId::zero();
$id = ObjectId::zero('sha256');

// Properties
$id->hex;      // 40-character hex string (64 for SHA-256)
$id->binary;   // 20-byte raw binary (32 for SHA-256)
$id->algo;     // 'sha1' or 'sha256'
$id->isSha256();
$id->isZero();

// Comparison
$id->equals($otherId);

Hashul este calculat pe baza datelor necomprimate: sha1("{type} {size}{content}") sau SHA-256 ai acelorași octeți. fromHex() și fromBinary() aleg algoritmul în funcție de lungime.

Enum ObjectType

PHP
use Pitmaster\Object\ObjectType;

ObjectType::Blob;    // 'blob'
ObjectType::Tree;    // 'tree'
ObjectType::Commit;  // 'commit'
ObjectType::Tag;     // 'tag'

Formatul obiectelor loose

Obiectele loose sunt stocate ca fișiere individuale în .git/objects/, organizate după primele două caractere hexazecimale ale hashului.

Cod
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

Conținutul fișierului este comprimat cu zlib. Conținutul necomprimat este:

Cod
<type> <decimal size>\0<raw content>

De exemplu, un blob care conține „Hello” este stocat astfel:

Cod
zlib_encode("blob 5\0Hello")

Citirea obiectelor loose

PHP
use Pitmaster\Storage\LooseObjectStore;

$store = new LooseObjectStore('/path/to/.git/objects');
$object = $store->read($objectId);

LooseObjectStore:

  1. Calculează calea fișierului din hash
  2. Citește și decomprimă fișierul cu zlib
  3. Analizează antetul pentru a extrage tipul și dimensiunea
  4. Deleagă procesarea către analizatorul corespunzător (Blob, Tree, Commit, Tag)

Scrierea obiectelor loose

PHP
$store->write($object);

Procesul de scriere:

  1. Serializare: "{type} {size}\0{content}"
  2. Folosiți id obiectului, calculat la construirea acestuia (citirile verifică hashul)
  3. Comprimare: zlib_encode(serialized)
  4. Scriere în .git/objects/{hash[0..1]}/{rest of hash}
  5. Scriere atomică (scriere într-un fișier temporar, apoi redenumire)

ObjectSerializer

ObjectSerializer gestionează codificarea și decodificarea formatului wire al obiectului.

PHP
use Pitmaster\Storage\ObjectSerializer;

// Encode
$raw = ObjectSerializer::encodeRaw($object);
// "blob 5\0Hello"
$compressed = ObjectSerializer::encode($object);
// zlib-compressed form, as stored in a loose object file

// Decode
$object = ObjectSerializer::decodeRaw($raw);
$object = ObjectSerializer::decode($compressed, $expectedHex); // throws CorruptObjectException on a hash mismatch

ObjectDatabase

ObjectDatabase este un depozit compozit care caută mai întâi obiectele loose, apoi fișierele pack.

PHP
use Pitmaster\Storage\ObjectDatabase;

$db = new ObjectDatabase('/path/to/.git/objects');

$object = $db->read($objectId);    // Search loose, then packs
$db->write($object);               // Always writes to loose
$exists = $db->exists($objectId);  // Check loose, then packs
$all = $db->listAll();             // All hashes from loose + packs

Scrierea directă în stocarea loose este intenționată. Lăsați git gc să împacheteze din nou obiectele în fișiere pack.

Compatibilitate cu SHA-256

Pitmaster abstractizează algoritmul hash prin ObjectId. SHA-1 este valoarea implicită. SHA-256 este ales pentru fiecare depozit la crearea acestuia:

PHP
$repo = Pitmaster::init('/path/to/project', 'sha256');
Shell
./vendor/bin/pitmaster init --object-format=sha256

Acest lucru scrie extensions.objectformat = sha256 în configurație. Depozitele deschise ulterior citesc formatul din acea intrare, iar fiecare operație asupra obiectelor, arborilor, indexului și referințelor folosește apoi șiruri hexazecimale de 64 de caractere și hashuri binare de 32 de octeți. Nu există un comutator global sau o constantă pentru algoritm.

Clasele de obiecte primesc algoritmul ca argument atunci când construiți singuri obiecte: Blob::fromContent($content, 'sha256'), Tree::fromEntries($entries, 'sha256'), ObjectId::compute($type, $content, 'sha256'). Pitmaster::clone() creează depozite SHA-1.

Formatul binar al intrărilor de arbore

În conținutul unui obiect arbore, fiecare intrare este codificată astfel:

Cod
<octal mode as ASCII> <space> <filename> \0 <binary hash: 20 bytes SHA-1, 32 bytes SHA-256>

Notă: hashul este binar brut, nu hexazecimal, spre deosebire de hashurile hexazecimale din obiectele commit și tag.

Intrările sunt sortate comparând numele ca și cum directoarele ar avea la final /. Ordinea de sortare trebuie să fie exactă pentru ca hashul arborelui să corespundă calculului efectuat de git.