Git-objekt
Allt i ett git-förråd lagras som ett objekt. Det finns fyra typer: blobs (filinnehåll), träd (kataloglistor), commits (ögonblicksbilder med metadata) och taggar (annoterade referenser). Varje objekt identifieras av innehållets SHA-1-hash.
Objekttyper
Blob
En blob innehåller rått filinnehåll utan metadata (inget filnamn och inga behörigheter).
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)
Tree
Ett träd är en kataloglista. Varje post har ett läge, ett namn och en hash som pekar på en blob eller ett undert träd.
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() skriver posterna i angiven ordning och lägessträngarna exakt som de anges, så sortera dem först och skriv kataloglägen som 40000. Ett träd som skrivs med 040000 eller i en annan ordning får en annan hash än gits, och git fsck --strict rapporterar läget med utfyllnad av nollor.
TreeEntry
TreeEntry är ett skrivskyddat värdeobjekt:
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)
Lägesvärden:
| Läge | Betydelse |
|---|---|
100644 |
Vanlig fil |
100755 |
Körbar fil |
40000 |
Katalog (träd); visas som 040000 av git ls-tree |
120000 |
Symbolisk länk |
160000 |
Gitlink (undermodul) |
Trädposter sorteras efter namn, där katalognamn behandlas som om de slutar med / vid jämförelse.
Commit
En commit knyter ett träd till dess historik och metadata.
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
Skapa commit-innehåll
$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",
);
Det råa commit-formatet:
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
Annoterade taggar är objekt med metadata.
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
Rått taggformat:
object <hex hash>\n
type commit\n
tag <name>\n
tagger <name> <<email>> <timestamp> <tz>\n
\n
<message>
ObjectId
ObjectId är ett skrivskyddat värdeobjekt som representerar en SHA-1- (eller SHA-256-)hash.
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);
Hashen beräknas över okomprimerade data: sha1("{type} {size} eller SHA-256 av samma byte. fromHex() och fromBinary() väljer algoritm utifrån längden.
ObjectType-enum
use Pitmaster\Object\ObjectType;
ObjectType::Blob; // 'blob'
ObjectType::Tree; // 'tree'
ObjectType::Commit; // 'commit'
ObjectType::Tag; // 'tag'
Löst objektformat
Lösa objekt lagras som enskilda filer i .git/objects/, organiserade efter hashens två första hexadecimala tecken.
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Filinnehållet är zlib-komprimerat. Det okomprimerade innehållet är:
<type> <decimal size>\0<raw content>
Exempelvis lagras en blob som innehåller "Hello" så här:
zlib_encode("blob 5\0Hello")
Läsa lösa objekt
use Pitmaster\Storage\LooseObjectStore;
$store = new LooseObjectStore('/path/to/.git/objects');
$object = $store->read($objectId);
LooseObjectStore:
- Beräknar filsökvägen från hashen
- Läser och zlib-dekomprimerar filen
- Tolkar huvudet för att hämta typ och storlek
- Överlåter till rätt parser (
Blob,Tree,Commit,Tag)
Skriva lösa objekt
$store->write($object);
Skrivprocessen:
- Serialisera:
"{type} {size}\0{content}" - Använd objektets
id, som beräknades när objektet skapades (hashen verifieras vid läsning) - Komprimera:
zlib_encode(serialized) - Skriv till
.git/objects/{hash[0..1]}/{rest of hash} - Skriv atomiskt (skriv till en temporär fil och byt sedan namn)
ObjectSerializer
ObjectSerializer hanterar kodning och avkodning av objektets trådformat.
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 är ett sammansatt lager som först söker bland lösa objekt och därefter i packfiler.
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
Skrivning direkt till löslagringen är avsiktlig. Låt git gc packa om till packfiler.
Stöd för SHA-256
Pitmaster abstraherar hashalgoritmen genom ObjectId. SHA-1 är standard. SHA-256 väljs per förråd när det skapas:
$repo = Pitmaster::init('/path/to/project', 'sha256');
./vendor/bin/pitmaster init --object-format=sha256
Detta skriver extensions.objectformat = sha256 till konfigurationen. Förråd som öppnas senare läser formatet från den posten, och varje åtgärd för objekt, träd, index och referenser använder då 64 tecken långa hexadecimala strängar och binära hashvärden på 32 byte. Det finns ingen global växel eller konstant för algoritmen.
Objektklasserna tar algoritmen som argument när du själv skapar objekt: Blob::fromContent($content, 'sha256'), Tree::fromEntries($entries, 'sha256'), ObjectId::compute($type, $content, 'sha256'). Pitmaster::clone() skapar SHA-1-förråd.
Binärt format för trädposter
I innehållet i ett trädobjekt kodas varje post så här:
<octal mode as ASCII> <space> <filename> \0 <binary hash: 20 bytes SHA-1, 32 bytes SHA-256>
Obs! Hashen är rå binärdata, inte hexadecimal, till skillnad från hexadecimala hashvärden i commit- och taggobjekt.
Poster sorteras genom att namn jämförs som om kataloger hade ett avslutande /. Sorteringsordningen måste vara exakt för att trädets hash ska överensstämma med beräkningen hos git.