Chapter sebelumnya memperkenalkan database objek Git dan membuktikan keberadaan keempat jenisnya lewat git cat-file -t. Namun mengenal jenisnya berbeda dengan membaca isinya: blob menyimpan apa persisnya, dan di mana nama berkas bersembunyi? Bagaimana satu commit bisa “memuat” seluruh isi proyek padahal isinya cuma beberapa baris? Kita jawab dengan membedah keempat objek dari repository latihan-model — dibuka satu per satu, tiap baris keluaran dijelaskan, lalu rantai antar-objek kita telusuri utuh dari commit sampai blob.
Blob: Isi Berkas Tanpa Nama #
Objek paling sederhana adalah blob. Kita baca isi blob README.md dari commit terakhir:
git cat-file -p HEAD:README.mdOutput yang Dihasilkan:
Toko Kopi SenjaIsinya persis byte demi byte berkas README.md saat di-commit — dan tidak ada apa pun selain itu: tidak ada nama berkas, tidak ada stempel waktu, tidak ada path. Nama “README.md” tidak tinggal di blob; nama tercatat di objek tree yang menunjuk blob ini. Konsekuensinya: dua berkas berisi sama akan menjadi satu blob yang sama persis, di mana pun letaknya — tema yang kita bedah tuntas pada chapter Content-Addressable Storage
.
Bukti paling telanjang datang dari plumbing git hash-object, yang menghitung — dan dengan flag -w, menyimpankan — blob tanpa menyentuh git add sama sekali. Kita buat berkas baru di luar riwayat:
printf 'kopi susu 22000\n' > catatan.txt
git hash-object catatan.txtOutput yang Dihasilkan:
6f2d4812666ace84160db636ee7e5e8b2bee810ePerintah pertama hanya menghitung alamatnya. Tambahkan -w (write) agar objek benar-benar masuk database:
git hash-object -w catatan.txtOutput yang Dihasilkan:
6f2d4812666ace84160db636ee7e5e8b2bee810eHash yang sama — objek kini tersimpan, dan kita periksa jenis serta isinya langsung dari alamatnya:
git cat-file -t 6f2d4812666ace84160db636ee7e5e8b2bee810eOutput yang Dihasilkan:
blobgit cat-file -p 6f2d4812666ace84160db636ee7e5e8b2bee810eOutput yang Dihasilkan:
kopi susu 22000Blob hidup di database, tetapi mari kita cek: apakah riwayat tahu keberadaannya?
git ls-tree HEADOutput yang Dihasilkan:
100644 blob e86980e618bbec64678d440b7116f5c47af02d1a README.md
040000 tree 3546a381364be478a413f43b7176d88bf9c04881 srcTidak ada catatan.txt — snapshot commit tidak berubah — dan git status --short masih menganggapnya untracked (?? catatan.txt). Inilah pembagian kerjanya: git hash-object -w hanya menyimpan isi; nama catatan.txt baru dikenal Git saat path itu dicatat di index — materi chapter Index dan Cache
. git add sebenarnya mengerjakan dua hal sekaligus: menyimpan blob seperti di atas, lalu mendaftarkan nama berkasnya. Hitung objeknya untuk membuktikan blob tadi memang menempati satu slot:
git count-objectsOutput yang Dihasilkan:
9 objects, 36 kilobytesDelapan objek dari chapter sebelumnya bertambah tepat satu blob — meskipun belum ada commit, tree, atau ref yang menyentuhnya.
Tree: Daftar Entri Folder #
Kalau blob adalah isi, tree adalah daftar isi folder. Objek tree root dari commit terakhir bisa dibaca dengan cat-file -p atau format yang lebih rapi milik git ls-tree:
git ls-tree HEADOutput yang Dihasilkan:
100644 blob e86980e618bbec64678d440b7116f5c47af02d1a README.md
040000 tree 3546a381364be478a413f43b7176d88bf9c04881 srcTiap baris adalah satu entri dengan empat kolom. Mode 100644 berarti berkas biasa, 040000 berarti direktori; jenis objek yang ditunjuk menyusul, lalu hash-nya, lalu nama entri — nama berkas dan nama folder memang hidup di sini, bukan di blob. Mode yang paling sering dijumpai:
| Mode | Arti |
|---|---|
100644 | Berkas biasa |
100755 | Berkas yang dapat dieksekusi |
040000 | Direktori (anak tree) |
120000 | Symbolic link |
Entri src berjenis tree — direktori pada Git adalah tree bersarang di dalam tree root. Kita turun satu tingkat:
git ls-tree HEAD src/Output yang Dihasilkan:
100644 blob d433484e4c05ef2efa746269a28947b95c83b103 src/menu.txtTree src berisi satu entri: blob menu.txt. Opsi -r meratakan seluruh pohon menjadi satu daftar:
git ls-tree -r HEADOutput yang Dihasilkan:
100644 blob e86980e618bbec64678d440b7116f5c47af02d1a README.md
100644 blob d433484e4c05ef2efa746269a28947b95c83b103 src/menu.txtBerkas bersarang berapa pun levelnya tetap hanya tree-menunjuk-tree sampai entri blob terakhir; tiap tree hanya mengenal nama anak langsungnya.
Commit: Metadata plus Tree Root plus Parent #
Naik satu tingkat dari tree: objek commit yang menjahit metadata dengan satu tree root. Ini isinya utuh:
git cat-file -p HEADOutput yang Dihasilkan:
tree 701a5eb4b6a56f1af348fad9565ab669f3867e3b
parent 169716071f0a73fec30229e89588b3b53af92aa9
author Budi Santoso <[email protected]> 1793931000 +0700
committer Budi Santoso <[email protected]> 1793931000 +0700
Tambah daftar menuMari kita bedah tiap barisnya. Baris tree mencatat tree root — seluruh isi proyek pada commit ini dirangkum oleh satu alamat itu. Baris parent menunjuk commit “Buat toko” sebelumnya; commit pertama tidak punya baris ini, dan commit merge punya dua baris parent. Baris author dan committer menyimpan identitas serta stempel waktu Unix — dua nilai sama di sini karena commit belum ditulis ulang, tapi keduanya bisa berbeda hasil rebase atau --amend, sebagaimana dibahas di Anatomi Commit
. Baris kosong lalu pesan commit menutup objek. Totalnya: satu tree root, satu parent, dan metadata — perintah git commit pada dasarnya hanya menulis objek 7 baris seperti ini.
Tag: Objek Keempat #
Tag ringan hanyalah ref tanpa objek, tetapi tag yang kita buat dengan -a tersimpan sebagai objek penuh — jenis keempat:
git cat-file -t v1.0.0Output yang Dihasilkan:
tagIsi objeknya:
git cat-file -p v1.0.0Output yang Dihasilkan:
object 455518b28637c16916025256825c0986c1be377f
type commit
tag v1.0.0
tagger Budi Santoso <[email protected]> 1793931120 +0700
Rilis pertamaStrukturnya: object menyimpan hash sasaran (commit “Tambah daftar menu”), type jenis sasarannya, tag nama tag, tagger siapa dan kapan tag dibuat, lalu pesannya. Semua metadata yang absen pada tag ringan hidup permanen di sini — perbedaan kedua jenis tag sudah kita bahas pada Annotated Tag
.
Menelusuri Rantai Utuh: Commit ke Blob #
Empat potongan kini siap dirangkai. Dari nama HEAD saja, kita telusuri rantai lengkapnya dengan tiga cat-file berurutan — masing-masing mengambil alamat dari baris keluaran sebelumnya:
git cat-file -p HEADOutput yang Dihasilkan:
tree 701a5eb4b6a56f1af348fad9565ab669f3867e3b
parent 169716071f0a73fec30229e89588b3b53af92aa9
author Budi Santoso <[email protected]> 1793931000 +0700
committer Budi Santoso <[email protected]> 1793931000 +0700
Tambah daftar menuBaris tree memberi kita alamat berikutnya:
git cat-file -p 701a5eb4b6a56f1af348fad9565ab669f3867e3bOutput yang Dihasilkan:
100644 blob e86980e618bbec64678d440b7116f5c47af02d1a README.md
040000 tree 3546a381364be478a413f43b7176d88bf9c04881 srcEntri blob pertama memberi alamat terakhir:
git cat-file -p e86980e618bbec64678d440b7116f5c47af02d1aOutput yang Dihasilkan:
Toko Kopi SenjaTiga lompatan — commit ke tree, tree ke blob, blob ke isi berkas — itulah seluruh perjalanan yang Git kerjakan setiap kali kita menjalankan git show, git diff, atau checkout. Rantai lengkap repository ini:
Bab Git Objects Pro Git menutup bedah ini dengan kesimpulan yang kini terasa nyata: seluruh sistem hanya terdiri dari objek-objek yang saling menunjuk.
Satu pertanyaan besar masih menggantung: mengapa alamat objek dihitung dari isi, bukan nomor urut seperti database biasa? Mengapa dua berkas identik selalu jatuh ke alamat yang sama, dan satu byte yang berubah membuat alamat berubah total? Sifat alamat-isi inilah yang memberi Git integritas, deduplikasi, dan kecepatan clone — pembahasan penuhnya ada di chapter berikutnya, Content-Addressable Storage .