Lewati ke konten

Objek Git

Semua hal dalam repositori git disimpan sebagai objek. Ada empat jenis: blob (isi file), tree (daftar direktori), commit (snapshot beserta metadata), dan tag (referensi beranotasi). Setiap objek diidentifikasi oleh hash SHA-1 dari isinya.

Jenis objek

Blob

Blob menyimpan isi file mentah tanpa metadata (tanpa nama file atau izin).

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)

Tree

Tree adalah daftar direktori. Setiap entri memiliki mode, nama, dan hash yang merujuk ke blob atau subpohon.

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() menulis entri sesuai urutan yang diberikan dan string mode persis seperti yang diberikan, jadi urutkan terlebih dahulu dan tulis mode direktori sebagai 40000. Tree yang ditulis dengan 040000 atau urutan berbeda akan mendapat hash yang berbeda dari git, dan git fsck --strict melaporkan mode dengan padding nol.

TreeEntry

TreeEntry adalah objek nilai readonly:

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)

Nilai mode:

Mode Arti
100644 File biasa
100755 File yang dapat dieksekusi
40000 Direktori (tree); ditampilkan sebagai 040000 oleh git ls-tree
120000 Tautan simbolis
160000 Gitlink (submodul)

Entri tree diurutkan berdasarkan nama, dengan nama direktori diperlakukan seolah diakhiri / untuk keperluan perbandingan.

Commit

Commit menghubungkan tree dengan riwayat dan metadatanya.

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

Menyusun isi 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",
);

Format commit mentah:

Kode
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

Tag beranotasi adalah objek yang memiliki metadata.

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

Format tag mentah:

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

ObjectId

ObjectId adalah objek nilai readonly yang merepresentasikan hash SHA-1 (atau 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);

Hash dihitung dari data yang tidak dikompresi: sha1("{type} {size}{content}"), atau SHA-256 dari byte yang sama. fromHex() dan fromBinary() memilih algoritme berdasarkan panjangnya.

Enum ObjectType

PHP
use Pitmaster\Object\ObjectType;

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

Format objek lepas

Objek lepas disimpan sebagai file terpisah dalam .git/objects/, dikelompokkan berdasarkan dua karakter heksadesimal pertama dari hash-nya.

Kode
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

Isi file dikompresi dengan zlib. Konten yang tidak dikompresi adalah:

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

Contohnya, blob yang berisi "Hello" disimpan sebagai:

Kode
zlib_encode("blob 5\0Hello")

Membaca objek lepas

PHP
use Pitmaster\Storage\LooseObjectStore;

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

LooseObjectStore:

  1. Menghitung jalur file dari hash
  2. Membaca dan mendekompresi file dengan zlib
  3. Mengurai header untuk mengambil jenis dan ukuran
  4. Meneruskan ke pengurai yang sesuai (Blob, Tree, Commit, Tag)

Menulis objek lepas

PHP
$store->write($object);

Proses penulisan:

  1. Serialisasikan: "{type} {size}\0{content}"
  2. Gunakan id objek, yang dihitung saat objek dibuat (pembacaan memverifikasi hash)
  3. Kompresikan: zlib_encode(serialized)
  4. Tulis ke .git/objects/{hash[0..1]}/{rest of hash}
  5. Penulisan atomik (tulis ke file sementara, lalu ganti nama)

ObjectSerializer

ObjectSerializer menangani pengodean dan penguraian format wire objek.

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 adalah penyimpanan komposit yang mencari objek lepas terlebih dahulu, lalu file 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

Penulisan langsung ke penyimpanan lepas memang disengaja. Biarkan git gc mengemas ulang ke dalam file pack.

Dukungan SHA-256

Pitmaster mengabstraksikan algoritme hash melalui ObjectId. SHA-1 adalah default. SHA-256 dipilih untuk setiap repositori saat dibuat:

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

Ini menulis extensions.objectformat = sha256 ke konfigurasi. Repositori yang dibuka kemudian membaca format dari entri tersebut, lalu setiap operasi objek, tree, indeks, dan ref menggunakan string heksadesimal 64 karakter dan hash biner 32 byte. Tidak ada sakelar global atau konstanta untuk algoritme tersebut.

Kelas objek menerima algoritme sebagai argumen saat Anda membuat objek sendiri: Blob::fromContent($content, 'sha256'), Tree::fromEntries($entries, 'sha256'), ObjectId::compute($type, $content, 'sha256'). Pitmaster::clone() membuat repositori SHA-1.

Format biner entri tree

Di dalam konten objek tree, setiap entri dikodekan sebagai:

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

Catatan: hash-nya berupa biner mentah, bukan heksadesimal, berbeda dengan hash heksadesimal pada objek commit dan tag.

Entri diurutkan dengan membandingkan nama seolah direktori memiliki akhiran /. Urutan ini harus persis agar hash tree cocok dengan hasil perhitungan git.