Mesin yang menolak commit kotor bekerja lebih konsisten daripada ingatan manusia — itu janji pre-commit yang sudah kita buktikan mekanismenya di dua chapter sebelumnya. Sekarang kita bangun versi produksinya: satu skrip yang menjalankan tiga pemeriksaan nyata, masing-masing menjawab kelalaian yang berbeda — jejak console.log dan print() yang lupa dihapus, whitespace error yang mengotori diff, dan berkas besar yang tidak seharusnya masuk repository. Setiap pemeriksaan diuji sungguhan: commit kotor ditolak, commit bersih lolos.
Tiga Pemeriksaan, Satu Gerbang #
Semua pemeriksaan berbagi satu sumber kebenaran: staging area. git diff --cached --name-only mencantumkan berkas yang benar-benar akan masuk commit berikutnya — bukan seluruh working directory, karena berkas yang belum di-git add bukan urusan commit ini. Skrip lengkapnya:
mkdir latihan-precommit && cd latihan-precommit
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
cat > .git/hooks/pre-commit <<'HOOK'
#!/bin/sh
# pre-commit — gerbang sebelum pesan commit ditulis.
# 1) Berkas yang benar-benar masuk commit berikutnya.
berkas="$(git diff --cached --name-only)"
# 2) Jejak debug pada berkas yang di-staging.
for f in $berkas; do
[ -f "$f" ] || continue
if grep -nE 'console\.log|print\(' "$f"; then
echo "pre-commit: jejak debug ditemukan di $f — bersihkan dulu" >&2
exit 1
fi
done
# 3) Whitespace error pada baris baru.
if ! git diff --cached --check; then
echo "pre-commit: whitespace error — lihat daftar di atas" >&2
exit 1
fi
# 4) Berkas lebih besar dari 1 MiB.
for f in $berkas; do
[ -f "$f" ] || continue
ukuran="$(stat -c %s "$f")"
if [ "$ukuran" -gt 1048576 ]; then
echo "pre-commit: $f ($ukuran byte) melebihi 1 MiB" >&2
exit 1
fi
done
HOOK
chmod +x .git/hooks/pre-commitBaris-baris kuncinya bekerja begini:
berkas="$(git diff --cached --name-only)"mengambil daftar berkas staged — baris pertama seluruh pemeriksaan mengacu ke sini.[ -f "$f" ] || continuemelewati berkas yang sudah tidak ada di working directory — misalnya berkas yang justru dihapus pada commit ini;greppada berkas tak ada akan memicu kesalahan palsu.grep -nE 'console\.log|print\('mencari dua pola debug sekaligus;-nmenyertakan nomor baris,-Emengaktifkan regex. Bilagrepmenemukan kecocokan,grepmencetak barisnya dan keluar 0 — itulah kondisi gagal kita.git diff --cached --checkadalah pemeriksaan bawaan Git untuk whitespace error; ia sekaligus mencetak lokasinya, jadi skrip tinggal melaporkan gagal.stat -c %smembaca ukuran berkas dalam byte; ambang1048576adalah 1 MiB — tim yang memakai Git LFS menaikkannya.- Setiap cabang gagal menutup dengan
exit 1— bahasa tunggal yang Git pahami sebagai “batalkan commit”.
Uji Pertama: Jejak Debug Ditolak #
Berkas uji pertama memuat persis kesalahan yang paling sering lolos: console.log yang dipakai saat menulis fungsi lalu terlupakan:
cat > app.js <<'EOF'
function sapa() {
console.log("halo dari sapa()");
return "siap";
}
EOF
git add app.js
git commit -m "feat: fungsi sapa"Output yang Dihasilkan (perintah terakhir):
2: console.log("halo dari sapa()");
pre-commit: jejak debug ditemukan di app.js — bersihkan duluBaris pertama adalah keluaran grep — nomor baris dan isinya, sehingga penulisnya tahu persis ke mana melihat. Commit batal, staging area utuh. Setelah baris debug dihapus, commit yang sama lolos tanpa drama:
cat > app.js <<'EOF'
function sapa() {
return "siap";
}
EOF
git add app.js
git commit -m "feat: fungsi sapa"Output yang Dihasilkan (perintah terakhir):
[main (root-commit) e70c3ec] feat: fungsi sapa
1 file changed, 3 insertions(+)
create mode 100644 app.jsHash e70c3ec berbeda di mesin pembaca.
Uji Kedua dan Ketiga: Whitespace dan Berkas Besar #
Pemeriksaan kedua menangkap spasi yang menggantung di ujung baris. Berkas catatan.md dibuat dengan dua spasi tersembunyi di ujung:
printf 'catatan \n' > catatan.md
git add catatan.md
git commit -m "docs: tambah catatan"Output yang Dihasilkan (perintah terakhir):
catatan.md:1: trailing whitespace.
+catatan
pre-commit: whitespace error — lihat daftar di atasgit diff --cached --check menyebut berkas, nomor baris, jenis masalahnya, lalu menampilkan baris bermasalah dengan prefiks + — spasi di ujung baris juga ikut terlihat di keluaran asli di atas. Untuk pemeriksaan ketiga, catatan diperbaiki lalu sebuah berkas besar 2,1 MB ikut di-staging:
printf 'catatan\n' > catatan.md
git add catatan.md
python3 -c "open('logo.bin','wb').write(b'\x89PNG' + bytes(2_100_000))"
git add logo.bin
git commit -m "chore: tambah aset"Output yang Dihasilkan (perintah terakhir):
pre-commit: logo.bin (2100004 byte) melebihi 1 MiBHook melaporkan nama, ukuran persisnya, dan ambang batas. Berkas besar ditarik keluar dari staging area — git reset logo.bin — dan commit catatan yang bersih kemudian lolos:
git reset logo.bin
rm logo.bin
git commit -m "docs: tambah catatan"Output yang Dihasilkan (perintah terakhir):
[main 44ba0f0] docs: tambah catatan
1 file changed, 1 insertion(+)
create mode 100644 catatan.mdPerforma dan Jalan Darurat #
pre-commit berjalan pada setiap commit — hook yang lambat menghukum frekuensi tertinggi dalam alur kerja Git. Ukurannya saat ini:
printf 'data\n' > data.md
git add data.md
time git commit -m "chore: tambah data"Output yang Dihasilkan (bagian time, di mesin Anda angkanya berbeda):
real 0m0.008s0,008 detik — tiga pemeriksaan di atas memang murah. Aturannya praktis: pemeriksaan yang lebih berat (test suite, type checker) sebaiknya tidak menunggu di hook, melainkan berjalan terpisah atau dipindah ke hook pre-push yang frekuensinya lebih rendah. Git juga menyediakan pintu darurat yang jujur:
printf 'draf cepat\n' > draf.md
git add draf.md
git commit --no-verify -m "wip: draf sementara"Output yang Dihasilkan (perintah terakhir):
[main 163f1f5] wip: draf sementara
1 file changed, 1 insertion(+)
create mode 100644 draf.mdHook ini kini menjaga commit kita — tapi hanya di mesin ini, karena .git/hooks/ tidak pernah ikut di-clone. Sebelum masalah itu dibedah, ada satu hook tetangga yang sama penting untuk kualitas riwayat: pemeriksa pesan commit.