Git-objektit
Kaikki git-tietovarastossa tallennetaan objekteina. Objektityyppejä on neljä: blobit (tiedostojen sisältö), puut (hakemistolistaukset), commitit (tilannevedokset metatietoineen) ja tagit (merkityt viitteet). Kunkin objektin tunnisteena on sen sisällöstä laskettu SHA-1-tiiviste.
Objektityypit
Blob
Blob sisältää tiedoston raakasisällön ilman metatietoja (ei tiedostonimeä eikä käyttöoikeuksia).
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)
Puu
Puu on hakemistolistaus. Kullakin merkinnällä on käyttötila, nimi ja tiiviste, joka viittaa blobiin tai alipuuobjektiin.
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() kirjoittaa merkinnät annetussa järjestyksessä ja käyttötilamerkkijonot täsmälleen annettuina, joten lajittele merkinnät ensin ja kirjoita hakemistojen käyttötilaksi 40000. Komennolla 040000 tai muussa järjestyksessä kirjoitettu puu saa eri tiivisteen kuin gitin puu, ja git fsck --strict raportoi nollilla täydennetyn käyttötilan.
TreeEntry
TreeEntry on vain luku -arvo-olio:
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)
Käyttötilan arvot:
| Käyttötila | Merkitys |
|---|---|
100644 |
Tavallinen tiedosto |
100755 |
Suoritettava tiedosto |
40000 |
Hakemisto (puu); git ls-tree näyttää sen muodossa 040000 |
120000 |
Symbolinen linkki |
160000 |
Gitlink (alikomponentti) |
Puun merkinnät lajitellaan nimen mukaan. Vertailussa hakemistonimiä käsitellään ikään kuin niiden lopussa olisi /.
Commit
Commit yhdistää puun sen historiaan ja metatietoihin.
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
Commit-sisällön muodostaminen
$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",
);
Raakamuotoinen 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>
Tagi
Merkityt tagit ovat metatietoja sisältäviä objekteja.
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
Raakamuotoinen tagi:
object <hex hash>\n
type commit\n
tag <name>\n
tagger <name> <<email>> <timestamp> <tz>\n
\n
<message>
ObjectId
ObjectId on vain luku -arvo-olio, joka edustaa SHA-1- tai SHA-256-tiivistettä.
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);
Tiiviste lasketaan pakkaamattomasta datasta: sha1("{type} {size} tai SHA-256 samoista tavuista. fromHex() ja fromBinary() valitsevat algoritmin pituuden perusteella.
ObjectType-luettelotyyppi
use Pitmaster\Object\ObjectType;
ObjectType::Blob; // 'blob'
ObjectType::Tree; // 'tree'
ObjectType::Commit; // 'commit'
ObjectType::Tag; // 'tag'
Irrallisten objektien muoto
Irralliset objektit tallennetaan yksittäisinä tiedostoina hakemistoon .git/objects/, jaoteltuna tiivisteen kahden ensimmäisen heksamerkin mukaan.
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Tiedoston sisältö pakataan zlib-muodossa. Pakkaamaton sisältö on seuraava:
<type> <decimal size>\0<raw content>
Esimerkiksi "Hello"-merkkijonon sisältävä blob tallennetaan seuraavasti:
zlib_encode("blob 5\0Hello")
Irrallisten objektien lukeminen
use Pitmaster\Storage\LooseObjectStore;
$store = new LooseObjectStore('/path/to/.git/objects');
$object = $store->read($objectId);
LooseObjectStore:
- Laskee tiedostopolun tiivisteestä
- Lukee ja purkaa tiedoston zlib-pakkauksen
- Jäsentää otsakkeen ja poimii tyypin sekä koon
- Siirtää käsittelyn sopivalle jäsentimelle (
Blob,Tree,Commit,Tag)
Irrallisten objektien kirjoittaminen
$store->write($object);
Kirjoitusprosessi:
- Sarjallista:
"{type} {size}\0{content}" - Käytä objektin
id-tiivistettä, joka lasketaan objektia luotaessa (luettaessa tiiviste tarkistetaan) - Pakkaa:
zlib_encode(serialized) - Kirjoita kohteeseen
.git/objects/{hash[0..1]}/{rest of hash} - Kirjoita atomisesti (kirjoita väliaikaiseen tiedostoon ja nimeä se sitten uudelleen)
ObjectSerializer
ObjectSerializer käsittelee objektien siirtoformaatin koodauksen ja dekoodauksen.
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 on yhdistelmätallennus, joka etsii ensin irrallisia objekteja ja sitten pakettitiedostoja.
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
Kirjoittaminen suoraan irralliseen tallennukseen on tarkoituksellista. Anna komennon git gc pakata objektit pakettitiedostoihin.
SHA-256-tuki
Pitmaster abstrahoi tiivistealgoritmin rajapinnan ObjectId avulla. SHA-1 on oletus. SHA-256 valitaan tietovarastokohtaisesti sen luonnin yhteydessä:
$repo = Pitmaster::init('/path/to/project', 'sha256');
./vendor/bin/pitmaster init --object-format=sha256
Tämä kirjoittaa asetuksen extensions.objectformat = sha256 määritystiedostoon. Myöhemmin avatut tietovarastot lukevat muodon tästä asetuksesta, ja kaikki objekti-, puu-, indeksi- ja viiteoperaatiot käyttävät sen jälkeen 64-merkkisiä heksadesimaalimerkkijonoja ja 32 tavun binäärisiä tiivisteitä. Algoritmille ei ole yleistä vaihtoa tai vakiota.
Objektiluokille annetaan algoritmi argumenttina, kun luot objekteja itse: Blob::fromContent($content, 'sha256'), Tree::fromEntries($entries, 'sha256'), ObjectId::compute($type, $content, 'sha256'). Pitmaster::clone() luo SHA-1-tietovarastoja.
Puumerkinnän binäärimuoto
Puuobjektin sisällössä kukin merkintä koodataan seuraavasti:
<octal mode as ASCII> <space> <filename> \0 <binary hash: 20 bytes SHA-1, 32 bytes SHA-256>
Huomautus: tiiviste on raakabinaarimuodossa, ei heksamuodossa, toisin kuin commit- ja tagiobjektien heksatiivisteet.
Merkinnät lajitellaan vertailemalla nimiä ikään kuin hakemistojen perässä olisi /. Järjestyksen on oltava täsmälleen oikea, jotta puun tiiviste vastaa gitin laskemaa tiivistettä.