Garbage Collection menjelaskan kapan objek boleh dibuang; chapter ini mempraktikkan caranya. git gc adalah satu perintah yang merangkum seluruh perawatan repository: mengemas objek lepas menjadi pack, menulis ulang refs, dan menghapus objek tak terjangkau yang sudah kedaluwarsa. Kita akan mengukur hasilnya pada repository buatan yang sengaja dibuat gemuk, mencatat efek sampingnya pada berkas .git, lalu membedah tiga opsi penting — --aggressive, --prune=now, dan --auto — beserta batas aman pemakaiannya.

Repository Gemuk dan Pengukuran Sebelum-Sesudah #

Ukuran selalu diukur sebelum dan sesudah — itulah disiplin yang juga kita pakai di Repository Performance . Repository latihan-gc meniru proyek yang tumbuh tanpa perawatan: 400 berkas kecil yang diimpor dua batch, lalu 16 commit yang masing-masing merevisi 25 berkas, ditutup branch dan tag.

bash
mkdir latihan-gc && cd latihan-gc
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
mkdir src
for i in $(seq 1 200); do printf "modul %d\nbaris data %d\n" "$i" "$i" > "src/modul-$i.txt"; done
git add .
git commit -m "Impor modul batch pertama"
for i in $(seq 201 400); do printf "modul %d\nbaris data %d\n" "$i" "$i" > "src/modul-$i.txt"; done
git add .
git commit -m "Impor modul batch kedua"
for c in $(seq 1 16); do
  for k in $(seq $(( (c-1)*25+1 )) $(( (c-1)*25+25 ))); do
    printf "modul %d revisi %d\nbaris data %d\n" "$k" "$c" "$k" > "src/modul-$k.txt"
  done
  git add -u
  git commit -m "Revisi 25 modul batch $c"
done
git branch fitur
git tag -a v1.0.0 -m "Rilis katalog v1.0.0"

Tiga ukuran kondisi awal: jumlah objek lepas, ukuran .git, dan banyaknya berkas objek lepas di objects/:

bash
git count-objects -v

Output yang Dihasilkan:

text
count: 855
size: 3560
in-pack: 0
packs: 0
size-pack: 0
prune-packable: 0
garbage: 0
size-garbage: 0
bash
du -sh .git
find .git/objects -type f -not -path "*/pack/*" -not -path "*/info/*" | wc -l

Output yang Dihasilkan:

text
4.7M	.git
text
855

Sekarang saatnya perawatan — dengan time agar durasinya juga tercatat:

bash
time git gc

Output yang Dihasilkan:

text
real	0m0.093s
user	0m0.030s
sys	0m0.062s

git gc sendiri tidak mencetak apa pun pada repository ini; yang tampil hanyalah laporan time (angkanya berbeda-beda antar mesin). Di terminal interaktif dengan repository jauh lebih besar, gc menampilkan progres penghitungan objek. Ukur ulang ketiga tandanya:

bash
git count-objects -v

Output yang Dihasilkan:

text
count: 0
size: 0
in-pack: 855
packs: 1
size-pack: 85
prune-packable: 0
garbage: 0
size-garbage: 0
bash
du -sh .git
find .git/objects -type f -not -path "*/pack/*" -not -path "*/info/*" | wc -l

Output yang Dihasilkan:

text
292K	.git
text
0
UkuranSebelum gcSesudah gc
Objek lepas (count)8550
Objek di pack (in-pack)0855
Total isi objek (size + size-pack)3560 KiB85 KiB
Ukuran .git4,7M292K
Berkas objek lepas8550

Seluruh 855 objek berpindah utuh ke satu pack — tidak ada yang hilang, semuanya hanya dipindahkan dan dikompresi (3560 KiB isi objek menjadi 85 KiB berkat kompresi zlib dan delta yang dibahas di Packfiles ). du turun dari 4,7M ke 292K karena 855 berkas kecil yang masing-masing memakan blok disk digantikan sedikit berkas besar.

Efek Samping: packed-refs dan info Baru #

gc tidak berhenti di objek. Refs lepas juga dikemas menjadi satu berkas .git/packed-refs:

bash
cat .git/packed-refs

Output yang Dihasilkan:

text
# pack-refs with: peeled fully-peeled sorted 
e1fbeb44ff8d29a2c54766ce08bd1f7ca0032bf7 refs/heads/fitur
e1fbeb44ff8d29a2c54766ce08bd1f7ca0032bf7 refs/heads/main
fd82e46a6364abdfa8584dfda15510d23e22f686 refs/tags/v1.0.0
^e1fbeb44ff8d29a2c54766ce08bd1f7ca0032bf7

Tiga refs kita — fitur, main, dan tag v1.0.0 — kini tinggal dalam satu berkas; baris diawali ^ adalah peeled: alamat commit yang ditunjuk objek tag (dibahas di References dan Refs ). Konsekuensinya terlihat langsung:

bash
ls .git/refs/heads/
git rev-parse main

Output yang Dihasilkan:

text
e1fbeb44ff8d29a2c54766ce08bd1f7ca0032bf7

Direktori refs/heads/ kosong — ls tidak mencetak apa pun — tetapi git rev-parse main tetap bekerja: Git mencari ref di refs lepas dan di packed-refs secara transparan. Efek samping ketiga ada di objects/info/:

bash
ls .git/objects/info/

Output yang Dihasilkan:

text
commit-graph
packs

Berkas commit-graph (akselerasi penelusuran riwayat — dibahas penuh di Repository Performance ) ikut ditulis karena gc.writeCommitGraph bernilai true secara bawaan, dan packs adalah daftar pack untuk server protokol HTTP sederhana. Semua perubahan ini aman: tidak satu pun perintah sehari-hari yang perlu tahu bentuk penyimpanan di baliknya.

gc –aggressive: Lebih Rapat, Lebih Lama #

Opsi --aggressive memerintahkan repack dengan jendela pencarian delta yang jauh lebih lebar — gc.aggressiveWindow bawaannya 250 dibanding jendela biasa 10 — dan memaksa delta lama dihitung ulang dari nol. Repository kita sengaja ditambah satu commit lagi agar gc punya pekerjaan segar:

bash
printf 'catatan rilis\n' > CATATAN.md
git add CATATAN.md
git commit -m "Tambah catatan rilis"
time git gc --aggressive

Output yang Dihasilkan:

text
real	0m0.040s
user	0m0.018s
sys	0m0.022s
bash
git count-objects -v

Output yang Dihasilkan:

text
count: 0
size: 0
in-pack: 858
packs: 1
size-pack: 86
prune-packable: 0
garbage: 0
size-garbage: 0

Jujur saja: pada repository 858 objek ini --aggressive nyaris tak terasa bedanya — keduanya sepersekian detik, dan size-pack 86 KiB tidak jauh berbeda dari 85 KiB. Dokumentasi git-gc menyebut biayanya dengan gamblang: opsi ini “menghabiskan jauh lebih banyak waktu” dan untuk kebanyakan repository “tidak layak dipakai” tanpa benchmark khusus. Peningkatan baru terasa pada repo dengan objek-objek serupa yang belum terdelta dengan baik — khas kasus impor massal.

gc –prune=now: Membuang Tanpa Tenggang #

Seperti di Garbage Collection , gc bawaan menahan objek tak terjangkau selama dua minggu. Opsi --prune=now meniadakan tenggang itu. Kita uji dengan satu blob lepas tak terjangkau:

bash
printf 'catatan yang salah alamat\n' | git hash-object -w --stdin
git count-objects

Output yang Dihasilkan:

text
1 objects, 4 kilobytes
bash
git gc --prune=now
git count-objects

Output yang Dihasilkan:

text
0 objects, 0 kilobytes

Blob itu lenyap seketika — gc berjalan senyap tanpa keluaran.

Opsi terakhir, --auto, tidak untuk diketik manual: itu bendera internal yang dipakai perintah porcelain dan git maintenance agar gc memeriksa ambang dulu dan langsung berhenti bila tidak ada yang perlu dikerjakan — mekanisme lengkapnya sudah kita lihat di Garbage Collection .

Kapan gc Manual Masuk Akal #

Dengan gc otomatis dan git maintenance yang akan kita bahas berikutnya, gc manual bukan rutinitas harian. Tabel ini merangkum situasi yang benar-benar membutuhkannya:

SituasiLayak gc manual?Alasan
Baru menulis ulang riwayat besar (filter-repo, rebase massal)yabanyak objek tak terjangkau dan pack lama tidak optimal
Baru impor massal atau migrasi dari VCS lainyahasil impor sering berpack buruk; --aggressive layak dicoba di sini
Menyiapkan repository untuk dibagikan (bundle, clone jaringan lambat)yaukuran pack memengaruhi transfer
Runner CI dengan disk sempitya, terjadwalruang terpakai harus dijaga
Workstation harian biasatidakgc otomatis dan git maintenance menangani
Repository di network share yang dipakai bersamaantidakberbahaya — lihat bawah

Satu larangan penting: jangan menjalankan gc pada repository yang berada di berkas jaringan (NFS, SMB) dan sedang dipakai orang lain. gc menimpa pack, memindahkan refs, dan menghapus berkas lama; proses Git lain yang kebetulan membaca berkas yang sama bisa menangkap keadaan setengah jadi, dan semantik berkas jaringan memperbesar risikonya. Untuk repository bersama, lakukan perawatan di satu tempat — misalnya di repository bare pusat saat sepi — atau biarkan setiap anggota merawat clone-nya masing-masing.

Mengingat bahwa semuanya ini mungkin terlupakan kalau bergantung pada ingatan, Git menyediakan cara modern untuk menjadwalkan perawatan: daemon git maintenance yang menjalankan tugas-tugas ini secara bertahap di latar belakang — chapter berikutnya membedahnya tuntas.