Repository yang sehat belum tentap cepat. git gc bisa mengecilkan .git sampai 95%, namun keluhan yang paling sering terdengar bukan soal disk: git status yang makan lima detik di monorepo 60 ribu berkas, atau git log yang menit lamanya sebelum menampilkan apa pun. Kinerja punya sumber beratnya sendiri — dan obatnya tidak sama dengan obat kebersihan. Disiplin yang kita pakai sejak git gc
tetap berlaku: ukur dulu, ubah satu hal, ukur ulang. Chapter ini membangun baseline nyata, membedah empat sumber berat, lalu memasang akselerator bawaan Git satu per satu sambil jujur membaca angkanya.
Baseline: Mengukur Sebelum Mengubah #
Repository latihan-perf meniru proyek yang tumbuh teratur: 1200 berkas kecil tersebar di 12 direktori src/modul-*, diimpor sekali lalu direvisi 80 kali:
mkdir latihan-perf && cd latihan-perf
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
mkdir -p src
for d in $(seq 1 12); do
mkdir "src/modul-$d"
for i in $(seq 1 100); do
printf "berkas %d modul %d\nnilai %d\n" "$i" "$d" $((i + d)) > "src/modul-$d/data-$i.txt"
done
done
git add .
git commit -m "Impor 1200 berkas"
for c in $(seq 1 80); do
d=$(( ((c - 1) % 12) + 1 ))
base=$(( (c * 5) % 100 ))
for k in 0 1 2 3 4; do
i=$(( ((base + k) % 100) + 1 ))
printf "berkas %d modul %d revisi %d\nnilai %d\n" "$i" "$d" "$c" $((i + d + c)) > "src/modul-$d/data-$i.txt"
done
git add -u
git commit -m "Revisi $c"
doneTiga pengukuran pembuka — jumlah commit beserta durasi penelusurannya, durasi git status, dan ukuran .git:
time git log --oneline | wc -lOutput yang Dihasilkan:
81
real 0m0.004s
user 0m0.003s
sys 0m0.002stime git statusOutput yang Dihasilkan:
On branch main
nothing to commit, working tree clean
real 0m0.004s
user 0m0.000s
sys 0m0.003sdu -sh .gitOutput yang Dihasilkan:
8.9M .gitAngka time selalu berbeda antar mesin dan antar eksekusi — 81 commit itu hasil wc -l (satu impor ditambah 80 revisi). Yang menjadikan angka ini berguna bukan nilainya yang absolut, melainkan posisinya sebagai baseline: setiap perubahan yang kita pasang berikutnya dibandingkan ke sini.
Empat Sumber Berat #
Sebelum mengobati, kenali dulu penyakitnya. Empat sumber berat paling umum dan mekanisme yang membuatnya pelan:
| Sumber berat | Mengapa terasa |
|---|---|
| Berkas besar dalam riwayat | blob biner nyaris tak terdelta — setiap clone dan CI membayar unduhan yang sama; bedah dan solusinya di File Besar dan Git LFS |
| Ribuan berkas kecil | git status memanggil lstat untuk setiap berkas terlacak dan menelusuri setiap direktori untracked — biaya per berkas kecil, tetapi jumlahnya raksasa |
| Struktur direktori dalam | setiap jenjang direktori adalah objek tree tersendiri; penelusuran riwayat men-dekompresi banyak objek kecil beruntun |
| Object database tidak optimal | pack menumpuk tanpa digabung atau delta jarang terbentuk — obatnya repack, dibahas di Packfiles dan git gc |
Repository latihan kita mewakili dua baris tengah: banyak berkas kecil di direktori berjenjang. Dua akselerator berikutnya menyerang masing-masing sisi — penelusuran riwayat dan pemindaian working directory.
commit-graph: Cache Penelusuran Riwayat #
git log --graph --oneline harus membaca setiap commit untuk mengetahui parent dan tanggalnya — dan setiap commit adalah objek terkompresi zlib yang harus dibuka satu per satu. Berkas commit-graph meringkas metadata itu menjadi satu berkas biner: hash, parent, tanggal, dan generation number seluruh commit yang reachable. Ukur dulu kondisi tanpa commit-graph:
time git log --graph --oneline > /dev/nullOutput yang Dihasilkan:
real 0m0.003s
user 0m0.003s
sys 0m0.000sTulis berkasnya — perintah ini berjalan senyap tanpa keluaran:
git commit-graph write --reachable
ls .git/objects/info/Output yang Dihasilkan:
commit-graphBerkas commit-graph tercipta di objects/info/ — berkas packs yang kita kenal dari git gc
tidak ada karena repository ini belum pernah menjalankan gc. Ukur ulang penelusuran yang sama:
time git log --graph --oneline > /dev/nullOutput yang Dihasilkan:
real 0m0.004s
user 0m0.001s
sys 0m0.003sJujur saja: pada 81 commit tidak ada perbaikan yang terukur — selisih 0,003 dan 0,004 detik lebih kecil daripada derau pengukuran. Metadatanya memang cuma 81 baris; membukanya dari objek lepas maupun dari satu berkas sama murahnya. Efek commit-graph baru bersinar pada riwayat puluhan ribu hingga jutaan commit, ketika penghematan dekompresi menumpuk dan generation number memungkinkan penelusuran berhenti lebih awal tanpa mengunjungi seluruh cabang. Dua fakta pelengkap: git gc menulis berkas ini otomatis (gc.writeCommitGraph bawaan true), dan tugas commit-graph dari git maintenance
menulisnya secara bertahap. Rincian format berkasnya ada di dokumentasi git-commit-graph
.
Mempercepat git status: untrackedCache dan fsmonitor #
Sumber lambat git status berbeda sama sekali: bukan object database, melainkan pemindaian working directory — lstat seluruh berkas terlacak plus penelusuran direktori untracked. Kita uji dengan menimbuni repository 300 berkas draf yang tak terlacak:
mkdir -p draf
for i in $(seq 1 300); do printf "draf %d\n" "$i" > "draf/catatan-$i.txt"; done
time git statusOutput yang Dihasilkan:
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
draf/
nothing added to commit but untracked files present (use "git add" to track)
real 0m0.005s
user 0m0.002s
sys 0m0.003sLapisan obat pertama, untracked cache:
git config core.untrackedCache true
time git status
time git statusOutput yang Dihasilkan (perintah pertama):
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
draf/
nothing added to commit but untracked files present (use "git add" to track)
real 0m0.004s
user 0m0.002s
sys 0m0.003sOutput yang Dihasilkan (perintah kedua):
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
draf/
nothing added to commit but untracked files present (use "git add" to track)
real 0m0.004s
user 0m0.002s
sys 0m0.003sUntracked cache menyimpan ringkasan isi direktori untracked di dalam index, dengan waktu modifikasi direktori sebagai penanda keaslian: direktori yang tidak berubah tidak di-scan ulang. Status pertama mengisi cache, status kedua memetiknya. Pada 1500 berkas semuanya masih milidetik — penghematan besar baru terasa ketika working directory berisi puluhan ribu berkas dengan direktori draf yang jarang berubah.
Lapisan kedua menyerang biaya lstat itu sendiri. fsmonitor adalah daemon yang berlangganan event perubahan berkas dari sistem operasi — inotify di Linux, FSEvents di macOS — sehingga git status cukup bertanya “apa yang berubah?” alih-alih men-stat semuanya:
git config core.fsmonitor true
git fsmonitor--daemon statusOutput yang Dihasilkan:
fatal: fsmonitor--daemon not supported on this platformSandbox container ini tidak menyediakan fasilitas event yang dibutuhkan daemon, dan Git melaporkannya dengan jujur. Di Linux dan macOS yang normal, daemon hidup dengan sendirinya: begitu core.fsmonitor bernilai true, perintah seperti git status memanggil daemon dan menjalankannya bila belum ada — perilaku ini tercantum di dokumentasi git-fsmonitor–daemon
. Meski daemonnya absen di sandbox, git status tetap berjalan dengan pemindaian biasa (0m0.004s) — fsmonitor adalah akselerator, bukan prasyarat. Kedua pengaturan kita kini tercatat di konfigurasi lokal:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
untrackedCache = true
fsmonitor = truePrefetch dan Partial Clone #
Dua obat sisanya untuk keluhan jaringan. Prefetch sudah kita jalankan di git maintenance
: tugas terjadwal menarik objek terbaru dari remote ke refs/prefetch/ setiap jam, sehingga git fetch yang kita jalankan sendiri tinggal memutakhirkan remote-tracking branch tanpa transfer besar — berharga untuk remote sibuk atau koneksi lambat. Partial clone menyerang dari arah lain: git clone --filter=blob:none <url> mengunduh riwayat tanpa isi berkas, lalu blob diambil saat pertama kali dibutuhkan — ekstensi ini cocok untuk repository besar dan dibahas sebagai bagian dari strategi clone ringan di Shallow Clone
.
Memetakan Gejala ke Obat #
| Gejala | Obat pertama | Rujukan |
|---|---|---|
git clone dan CI makin lama, unduhan raksasa | keluarkan berkas besar dari riwayat, Git LFS | File Besar |
git status lambat | core.fsmonitor + core.untrackedCache | chapter ini |
git log / git diff lambat | git commit-graph write --reachable | chapter ini |
git fetch lambat tiap pagi | prefetch terjadwal | git maintenance |
Disk .git membengkak, objek lepas menumpuk | git gc, maintenance | git gc |
| Working tree raksasa menghambat checkout | sparse-checkout, partial clone | Sparse Checkout |
Semua obat di atas untuk repository yang sehat namun lambat. Bagaimana dengan repository yang sudah terasa sakit — ukurannya aneh, operasinya mendadak pelan, dan kita tidak tahu mulai dari mana? Chapter penutup section ini merangkum kotak peralatannya: Diagnostik Repository .