Di Packfiles
kita melihat git gc mengemas objek — tetapi gc punya pekerjaan kedua yang lebih menakutkan: menghapus objek. Database Git tidak pernah menimpa apa pun, jadi setiap amend, reset, dan rebase meninggalkan objek lama yang kehilangan penunjuk. Git menahannya untuk sementara sebagai cadangan, lalu secara berkala membuangnya lewat mekanisme garbage collection. Chapter ini membedah siklus hidup objek dari lahir sampai terhapus: siapa yang menjaganya tetap hidup, berapa lama masa tenggangnya, kapan pembersihan otomatis berjalan — dan mengapa gc justru harus ditahan saat kita sedang panik kehilangan commit.
Objek Hidup Selama Masih Terjangkau #
Aturan hidup-matinya satu objek ditentukan oleh reachability. Sebuah objek reachable (terjangkau) bila bisa ditelusuri dari titik awal tertentu: branch, tag, HEAD, refs remote, hingga isi staging index. Mulai dari titik-titik itu, Git berjalan menyusuri rantai — commit menunjuk tree, tree menunjuk blob dan tree anak — dan semua yang tersentuh adalah objek hidup. Objek unreachable (tak terjangkau) adalah yang kehilangan seluruh jalur menuju dirinya; objek seperti inilah yang dilaporkan git fsck
sebagai dangling dan dibedah tuntas di Dangling Objects
.
Ada satu pengecualian penting: reflog juga dihitung sebagai penjaga. git reflog
mencatat setiap posisi lama HEAD dan branch, sehingga commit yang baru saja “dibuang” masih terjangkau lewat catatan perjalanan itu. Kita buktikan dengan amend klasik:
mkdir latihan-siklus && cd latihan-siklus
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf 'laporan v1\n' > laporan.txt
git add laporan.txt
git commit -m "Tulis laporan v1"
printf 'laporan v2\n' > laporan.txt
git add laporan.txt
git commit -m "Perbarui isi laporan"
printf 'lampiran A\n' > lampiran.txt
git add lampiran.txt
git commit --amend -m "Tambah lampiran dan rapikan judul"
git reflogOutput yang Dihasilkan:
72550bd HEAD@{0}: commit (amend): Tambah lampiran dan rapikan judul
f1e81f9 HEAD@{1}: commit: Tambah lampiran
fc827f2 HEAD@{2}: commit: Perbarui isi laporan
12143f7 HEAD@{3}: commit (initial): Tulis laporan v1Amend membuat commit baru 72550bd menggantikan f1e81f9 — branch kini menunjuk yang baru, dan commit lama kehilangan semua refs. Namun HEAD@{1} masih mencatatnya. Dua versi fsck memperlihatkan peran jangkar itu:
git fsck
git fsck --no-reflogsPerintah pertama tidak mencetak apa pun. Keluaran berikut berasal dari perintah kedua:
Output yang Dihasilkan:
dangling commit f1e81f90f4d62d779e010be8dff81c6287c797aagit fsck bawaan tidak melaporkan apa pun — bagi fsck, objek itu masih hidup karena reflog menjaganya. Tambahkan --no-reflogs dan kebenaran mentahnya muncul: f1e81f9 adalah commit dangling. Hash di mesin pembaca akan berbeda, tetapi polanya sama.
Tiga Pekerjaan gc: Reflog Expire, Prune, Repack #
git gc sebenarnya menjalankan tiga pekerjaan berurutan. Pertama, reflog expire: entri reflog yang lebih tua dari masa tenggang dihapus (inilah yang mencabut jangkar). Kedua, prune: objek tak terjangkau yang telah melewati masa tenggang dihapus. Ketiga, repack: semua objek hidup dikemas menjadi pack seperti chapter sebelumnya. Masa tenggangnya diatur tiga konfigurasi berikut:
| Pengaturan | Nilai bawaan | Efek |
|---|---|---|
gc.pruneExpire | 2 minggu | objek tak terjangkau yang lebih muda dari dua minggu tidak boleh dihapus |
gc.reflogExpireUnreachable | 30 hari | entri reflog yang menunjuk objek tak terjangkau dihapus setelah sebulan |
gc.reflogExpire | 90 hari | entri reflog HEAD dan branch lokal dihapus setelah tiga bulan |
Tabel ini adalah jendela penyelamatan: commit yang “hilang” hari ini dijamin Git menahannya minimal dua minggu ke depan, apa pun yang terjadi. Jalankan gc biasa — di repository sekecil ini git gc tidak mencetak apa pun — lalu periksa ketiga buktinya:
git gc
git count-objects -vOutput yang Dihasilkan:
count: 0
size: 0
in-pack: 10
packs: 1
size-pack: 2
prune-packable: 0
garbage: 0
size-garbage: 0git fsck --no-reflogsOutput yang Dihasilkan:
dangling commit f1e81f90f4d62d779e010be8dff81c6287c797aagit cat-file -t f1e81f90f4d62d779e010be8dff81c6287c797aaOutput yang Dihasilkan:
commitgit gc mengemas 10 objek dan semuanya lolos: yang dangling masih tercatat di reflog dan reflog kita masih segar, jadi fsck tetap melaporkannya dan cat-file masih bisa membacanya. Objek muda tidak disentuh. Git modern bahkan tidak meledakkan objek tak terjangkau yang sudah lepas dari pack: objek semacam itu dikemas ke cruft pack terpisah yang ditemani berkas .mtimes — catatan umur tiap objek, yang kelak menjadi dasar hitungan masa tenggang.
Masa tenggang bisa kita habiskan paksa dengan dua langkah yang sama seperti di Dangling Objects — keduanya senyap tanpa keluaran:
git reflog expire --expire-unreachable=now --all
git gc --prune=now
git count-objects -vOutput yang Dihasilkan:
count: 0
size: 0
in-pack: 9
packs: 1
size-pack: 2
prune-packable: 0
garbage: 0
size-garbage: 0Jangkar reflog dicabut, --prune=now meniadakan tenggang dua minggu, dan paket menyusut dari 10 objek menjadi 9 — commit f1e81f9 resmi terhapus, ditandai git fsck --no-reflogs yang kini bersih tanpa keluaran. Setelah saat itu cat-file -t f1e81f9... akan gagal dengan fatal: Not a valid object name.
gc Otomatis #
Sebagian perintah porcelain — git commit, git merge, git rebase, git pull — menutup pekerjaannya dengan pemeriksaan pembersihan otomatis. Versi Git terbaru meneruskan pemeriksaan itu ke git maintenance run --auto (senyap, diatur maintenance.auto), yang pada akhirnya tetap menjalankan git gc --auto. Ambang picunya: objek lepas melebihi gc.auto (bawaan 6700) atau jumlah pack melebihi gc.autoPackLimit (bawaan 64). Karena Git menghitung ambang lewat sampel statistik — menghitung isi satu subdirektori objects/17 lalu dikalikan 256 — angka di kehidupan nyata jarang tercapai; pesan gc otomatis justru biasa muncul setelah operasi besar seperti merge panjang atau menulis ulang riwayat.
Sandbox kita menurunkan ambang agar pemicunya terlihat:
git config gc.auto 2
git config gc.autoDetach falseKonfigurasi gc.autoDetach false membuat gc berjalan di depan; nilai bawaannya true akan melepas gc ke proses latar belakang. Siapkan 700 objek lepas lalu picu:
mkdir -p src
for i in $(seq 1 700); do printf "modul %d\nbaris data %d\n" "$i" "$i" > "src/modul-$i.txt"; done
git add .
git gc --auto
git count-objects -vOutput yang Dihasilkan:
Auto packing the repository for optimum performance.
See "git help gc" for manual housekeeping.count: 0
size: 0
in-pack: 700
packs: 1
size-pack: 41
prune-packable: 0
garbage: 0
size-garbage: 0Dua baris pesan itulah yang di dunia nyata muncul sebagai “Auto packing the repository in background for optimum performance.” — gc otomatis jarang mengganggu karena dikerjakan di latar belakang. Lanjutkan dengan commit biasa:
git commit -m "Impor 700 modul"Output yang Dihasilkan:
[main (root-commit) 54b8d8b] Impor 700 modul
700 files changed, 1400 insertions(+)
create mode 100644 src/modul-1.txt
create mode 100644 src/modul-10.txt
create mode 100644 src/modul-100.txtKeluaran itu dipotong — aslinya berlanjut dengan 698 baris create mode lainnya. Commit tersebut juga menjalankan pemeriksaan otomatis, dan kali ini senyap tanpa pesan: jumlah objek lepas yang ditinggalkannya (3 objek commit dan tree) jauh di bawah ambang, jadi gc tidak perlu bekerja. Barulah saat kita menimbuni objects/ dengan 700 objek lepas tak terjangkau, gc otomatis menolak diam:
for i in $(seq 1 700); do printf "draf %d\n" "$i" | git hash-object -w --stdin; done
git gc --autoOutput yang Dihasilkan:
Auto packing the repository for optimum performance.
See "git help gc" for manual housekeeping.
warning: There are too many unreachable loose objects; run 'git prune' to remove them.Peringatan itu menegaskan prinsipnya: gc otomatis mengemas objek hidup, tetapi tidak pernah menghapus objek tak terjangkau yang masih muda — ia menyarankan git prune, bukan menjalankannya sendiri. Konfigurasi gc.aggressiveWindow satu paket tema yang sama: nilai jendela pencarian basis delta ketika gc berjalan agresif (bawaan 250) — makin besar jendelanya, makin rapat delta yang ditemukan, makin lama pula gc bekerja. Pembahasan lengkapnya menyusul di git gc
.
Siklus hidup, masa tenggang, dan pemicunya kini jelas. Yang belum: bagaimana menjalankan gc secara manual dengan sadar — kapan hasilnya terasa, apa efek samping berkasnya, dan opsi apa yang aman dipakai — semuanya dibedah di chapter berikutnya: git gc . Rujukan lengkap pengaturan waktu ada di dokumentasi git-gc .