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).
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.
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:
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.
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
$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:
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.
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:
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).
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} sau SHA-256 ai acelorași octeți. fromHex() și fromBinary() aleg algoritmul în funcție de lungime.
Enum ObjectType
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.
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Conținutul fișierului este comprimat cu zlib. Conținutul necomprimat este:
<type> <decimal size>\0<raw content>
De exemplu, un blob care conține „Hello” este stocat astfel:
zlib_encode("blob 5\0Hello")
Citirea obiectelor loose
use Pitmaster\Storage\LooseObjectStore;
$store = new LooseObjectStore('/path/to/.git/objects');
$object = $store->read($objectId);
LooseObjectStore:
- Calculează calea fișierului din hash
- Citește și decomprimă fișierul cu zlib
- Analizează antetul pentru a extrage tipul și dimensiunea
- Deleagă procesarea către analizatorul corespunzător (
Blob,Tree,Commit,Tag)
Scrierea obiectelor loose
$store->write($object);
Procesul de scriere:
- Serializare:
"{type} {size}\0{content}" - Folosiți
idobiectului, calculat la construirea acestuia (citirile verifică hashul) - Comprimare:
zlib_encode(serialized) - Scriere în
.git/objects/{hash[0..1]}/{rest of hash} - Scriere atomică (scriere într-un fișier temporar, apoi redenumire)
ObjectSerializer
ObjectSerializer gestionează codificarea și decodificarea formatului wire al obiectului.
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.
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:
$repo = Pitmaster::init('/path/to/project', 'sha256');
./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:
<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.