Sepanjang section ini kita mengisi kotak peralatan untuk membaca riwayat: format dan filter git log, pickaxe, blame, shortlog, sampai relasi parent. Semua alat itu bernilai penuh hanya pada satu syarat — riwayat yang dibacanya layak dibaca. Riwayat bukan laik-otomatis: kualitasnya ditentukan pada saat setiap commit dibuat, bukan saat dibutuhkan. Chapter penutup ini membandingkan dua repository dengan isi akhir yang persis sama namun nasib berbeda, lalu merangkum kebiasaan yang membedakan keduanya — kebiasaan yang membuat seluruh alat section ini bekerja cepat enam bulan kemudian.

Dua Repository dengan Isi yang Sama #

Repository pertama dikerjakan buru-buru: checkpoint disimpan kapan pun kepala kosong, pesan ditulis seadanya. Lima commit menghasilkan aplikasi diskon yang jalan:

bash
mkdir latihan-berantakan && cd latihan-berantakan
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Aplikasi Diskon\nHitung potongan harga.\n' > README.md
printf 'def hitung_diskon(total):\n    return total * 0.1\n' > diskon.py
git add README.md diskon.py
git commit -m "wip"
printf 'def hitung_diskon(total):\n    potongan = 0.1\n    print("hitung diskon", total)\n    return total - (total * potongan)\n' > diskon.py
git add diskon.py
git commit -m "wip"
printf '# Aplikasi Diskon\n\nHitung potongan harga.\n' > README.md
git add README.md
git commit -m "fix"
printf 'def ekspor(nama, hasil):\n    with open(nama, "w") as berkas:\n        berkas.write(str(hasil))\n' > ekspor.py
printf '# Aplikasi Diskon\n\nHitung potongan harga keranjang belanja.\n' > README.md
git add ekspor.py README.md
git commit -m "update"
printf 'def hitung_diskon(total):\n    potongan = 0.1\n    return total - (total * potongan)\n' > diskon.py
git add diskon.py
git commit -m "beres"

Riwayatnya:

bash
git log --oneline

Output yang Dihasilkan:

text
8eb6e56 beres
3f27847 update
94f5e57 fix
cb7dbe5 wip
6e3d9f8 wip

Repository kedua menempuh perubahan yang sama — berkas, isi, dan urutan logisnya identik — tanpa pernah menyimpan baris debug maupun pesan generik. Setiap commit menghentikan pekerjaan pada satu titik yang bermakna:

bash
cd .. && mkdir latihan-rapi && cd latihan-rapi
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Aplikasi Diskon\nHitung potongan harga.\n' > README.md
printf 'def hitung_diskon(total):\n    return total * 0.1\n' > diskon.py
git add README.md diskon.py
git commit -m "Tambah kerangka aplikasi diskon"
printf 'def ekspor(nama, hasil):\n    with open(nama, "w") as berkas:\n        berkas.write(str(hasil))\n' > ekspor.py
git add ekspor.py
git commit -m "Tambah ekspor hasil ke berkas"
printf 'def hitung_diskon(total):\n    potongan = 0.1\n    return total - (total * potongan)\n' > diskon.py
git add diskon.py
git commit -m "Gunakan variabel potongan pada rumus diskon"
printf '# Aplikasi Diskon\n\nHitung potongan harga keranjang belanja.\n' > README.md
git add README.md
git commit -m "Perjelas deskripsi aplikasi di README"
bash
git log --oneline

Output yang Dihasilkan:

text
88f2ee2 Perjelas deskripsi aplikasi di README
ae25804 Gunakan variabel potongan pada rumus diskon
1c347da Tambah ekspor hasil ke berkas
01c559b Tambah kerangka aplikasi diskon

Riwayat yang Tak Bisa Dicari #

Kedua daftar di atas sama-sama menampilkan aplikasi jadi — bedanya baru terasa saat riwayat digunakan. Dua pekan kemudian, tim menanyakan: “fitur ekspor itu kapan masuk?”. Filter --grep dari chapter Opsi git log mencarinya di dalam pesan commit:

bash
git log --grep="ekspor" --oneline

Output yang Dihasilkan:

text

Keluaran kosong — tidak satu pun pesan di latihan-berantakan menyebut ekspor. Yang menambahkan berkas ekspor.py adalah commit update, dan tak ada manusia yang bisa menebaknya dari kelima pesan itu. Pencarian yang sama pada repository kedua:

bash
git log --grep="ekspor" --oneline

Output yang Dihasilkan:

text
1c347da Tambah ekspor hasil ke berkas

Satu baris, langsung kena. Inilah hakikat riwayat sehat sebagai navigasi: filter pesan, pickaxe -S, dan blame dari chapter Mencari Commit serta git blame semuanya bergantung pada pesan yang menggambarkan isi. Pickaxe memang tetap menemukan 3f27847 di repository pertama — teks ekspor ada di dalam diff — namun commit yang ditemukannya menyandang pesan update: penemuan tanpa penjelasan.

Commit Atomik: Satu Maksud per Commit #

Isi commit update di repository pertama:

bash
git show --stat 3f27847

Output yang Dihasilkan:

text
commit 3f278478fb9797a0142442748e0747d8410b330c
Author: Budi Santoso <[email protected]>
Date:   Tue Oct 13 09:30:00 2026 +0700

    update

 README.md | 2 +-
 ekspor.py | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

Satu commit memuat dua maksud sekaligus: fitur baru (ekspor.py) dan suntingan dokumentasi (README.md). Campuran begini terasa jinak saat dibuat, lalu berubah jadi beban: fitur ekspor tidak bisa di-revert atau di-cherry-pick tanpa menyeret suntingan README ikut serta, dan peninjau harus memahami dua topik dalam satu bacaan. Padanannya di repository kedua:

bash
git show --stat 1c347da

Output yang Dihasilkan:

text
commit 1c347daa5e884ae8a4ec750b9778d595dfa4c7ad
Author: Budi Santoso <[email protected]>
Date:   Tue Oct 13 09:05:00 2026 +0700

    Tambah ekspor hasil ke berkas

 ekspor.py | 3 +++
 1 file changed, 3 insertions(+)

Satu berkas, satu maksud, satu pesan yang menyebut keduanya. Aturan commit atomik berbunyi persis begitu: satu commit menampung satu perubahan yang utuh dan bernama jelas. Ukurannya mengikuti maksudnya, bukan jumlah baris — fitur kecil cukup satu commit, fitur besar menjadi rangkaian commit atomik (kerangka, lalu perilaku, lalu perapihan), dan pekerjaan yang tidak berhubungan — misalnya refactor tata letak — tidak pernah naik satu kendaraan dengan fitur.

Isi Akhir Sama, Pohon yang Sama #

Puncak pembuktian: apakah kedisiplinan itu mengubah hasil akhir? Kita bandingkan objek tree pada puncak masing-masing repository — snapshot seluruh isi direktori yang dibekukan tiap commit:

bash
git rev-parse HEAD^{tree}

Output yang Dihasilkan (di latihan-berantakan):

text
17383cc54bb7a1876e840b92f9c60b522473f1ed
bash
git rev-parse HEAD^{tree}

Output yang Dihasilkan (di latihan-rapi):

text
17383cc54bb7a1876e840b92f9c60b522473f1ed

Hash identik — byte demi byte. Kedua repository berakhir pada isi yang persis sama karena tree dihitung dari isi, bukan dari cara mencapainya. Git sendiri tidak menilai riwayat mana yang lebih sehat; mesin menilai objek, manusia menilai jejak. Keputusan “bagaimana kita melangkah” hanya berdampak pada satu hal — keterbacaan riwayat — dan itu satu-satunya hal yang dibaca enam bulan kemudian.

Checkpoint Pribadi: wip dan Squash #

Commit wip tetap punya tempat yang sah: sebagai checkpoint pribadi selagi bekerja. Kepala memang perlu dikosongkan di tengah fitur besar, dan checkpoint lokal mencegah pekerjaan hilang. Aturannya satu kalimat: checkpoint boleh hidup hanya di branch privat, dan harus disatukan sebelum pekerjaan dibagikan. Proses menyatukannya — memeras lima commit wip menjadi tiga commit bermakna — dikerjakan lewat interactive rebase dengan aksi squash, dibahas tuntas di section Rebasing dan Riwayat Lanjutan , khususnya chapter Interactive Rebase dan Menggabungkan Commit . Untuk koreksi kecil pada commit terakhir yang belum dibagikan, --amend dari chapter Amend Commit sudah cukup.

Prinsipnya: riwayat bersama adalah produk publik tim; riwayat privat adalah papan draf. Keduanya dipisahkan oleh satu operasi squash sebelum push — bukan oleh keberanian menghapus jejak setelah orang lain melihatnya. Panduan menyumbang pada proyek terbuka umumnya menuntut hal yang sama, misalnya bab berkontribusi Pro Git di git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project .

Lakukan dan Hindari #

LakukanHindari
Satu maksud per commit; fitur besar menjadi rangkaian commit atomikMencampur fitur, refactor, dan dokumentasi dalam satu commit
Subjek imperatif yang menyebut isi; body menjelaskan alasanPesan generik: wip, update, fix, beres
Periksa git diff --staged sebelum commitgit add -A asal cepat tanpa membaca apa yang masuk
Commit ketika repository dalam keadaan benar dan bisa jalanMenyimpan keadaan setengah-jadi ke branch bersama
Checkpoint wip hanya di branch privat, squash sebelum dibagikanPush commit wip ke branch yang dipakai bersama
Format pesan yang konsisten di tim, mis. Conventional CommitsMenyalin isi diff ke body pesan — diff sudah ada

Kebiasaan dari Section Ini #

Rangkaian kebiasaan kecil di atas punya satu efek besar yang terasa setiap hari: navigasi menjadi cepat. --grep menemukan commit lewat pesannya karena pesan menggambarkan isi; pickaxe -S menemukan kelahiran dan kematian sebuah teks karena perubahan tidak tercampur; git blame menunjuk penulis yang relevan karena format ulang tidak membajak atribusi; shortlog menyusun laporan yang bisa dibaca tanpa interpretasi. Riwayat sehat bukan kosmetik — ia adalah indeks yang kita bangun satu commit demi satu commit, dan seluruh section ini adalah mesin pencarinya.

Dengan commit yang tertata dan riwayat yang bisa dibaca, satu pertanyaan muncul begitu tim bertambah besar: bagaimana banyak orang mengerjakan banyak hal bersamaan tanpa saling menabrak? Jawabannya membuat riwayat bercabang — dan itulah pintu masuk section berikutnya: Branch dan Merging .