Aturan tim biasanya hidup sebagai dokumen: “jalankan linter sebelum commit”, “jangan sertakan berkas besar”, “tulis pesan commit dengan format tertentu”. Selama aturan bergantung pada kebiasaan manusia, satu hari yang sibuk cukup untuk melanggarnya semua. Git menyediakan mekanisme untuk memindahkan penegakan aturan itu ke tangan mesin: hooks — berkas skrip kecil yang Git eksekusi secara otomatis pada peristiwa tertentu, sebelum commit lahir, sebelum push berangkat, atau setelah merge selesai. Chapter ini membangun fondasinya: di mana hooks tinggal, bagaimana mengaktifkannya, dan satu mekanisme sederhana yang mengatur segalanya — exit code.
Skrip yang Tinggal di .git/hooks #
Setiap repository memiliki folder khusus untuk hooks sejak detik git init dijalankan. Folder itu berada di dalam .git/hooks/:
mkdir latihan-konsep && cd latihan-konsep
git init -b main
ls .git/hooksOutput yang Dihasilkan (dua perintah terakhir):
Initialized empty Git repository in /home/z/my-project/tutorial-extracted/work/sandbox/hooks-dan-otomasi/latihan-konsep/.git/
applypatch-msg.sample
commit-msg.sample
fsmonitor-watchman.sample
post-update.sample
pre-applypatch.sample
pre-commit.sample
pre-merge-commit.sample
pre-push.sample
pre-rebase.sample
pre-receive.sample
prepare-commit-msg.sample
push-to-checkout.sample
sendemail-validate.sample
update.sampleGit menaburi folder itu dengan empat belas berkas berakhiran .sample — contoh hooks yang tidak aktif. Jalur pada keluaran Anda menyesuaikan lokasi direktori. Isinya skrip shell sungguhan yang bisa kita baca; pre-commit.sample misalnya memuat pemeriksaan whitespace siap pakai:
head -n 12 .git/hooks/pre-commit.sampleOutput yang Dihasilkan:
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEADSetiap berkas .sample menyimpan petunjuk yang sama di kepalanya: aktifkan dengan mengubah nama. Aturan aktivasi hooks hanya dua:
- Nama berkas tanpa ekstensi —
pre-commit, bukanpre-commit.sampleataupre-commit.sh. Nama berkas harus persis sama dengan nama peristiwa, karena Git mencarinya berdasarkan nama itu. - Berkas harus executable — hak akses
chmod +x. Skrip yang tidak bisa dieksekusi sistem operasi akan dilewati Git secara diam-diam.
Lokasi folder .git/hooks/ di dalam working directory juga berarti hooks berjalan dengan konteks repository — Git mengeksekusinya dari akar working tree, sehingga skrip bisa memanggil git diff, grep, atau alat lain seolah dijalankan dari akar proyek. Daftar lengkap peristiwa yang tersedia terdokumentasi di githooks
.
Exit Code: Bahasa Tunggal Hooks #
Hooks berkomunikasi dengan Git lewat mekanisme paling tua di dunia Unix: exit code. Skrip keluar dengan 0 berarti “semua baik, lanjutkan”; keluar dengan nilai selain 0 berarti “hentikan operasi”. Tidak ada API, tidak ada format khusus — skrip bebas ditulis dalam shell, Python, atau bahasa apa pun. Kita buktikan dengan hook pre-commit paling sederhana yang mungkin:
cat > .git/hooks/pre-commit <<'HOOK'
#!/bin/sh
echo "pre-commit: pemeriksaan berjalan..."
exit 1
HOOK
chmod +x .git/hooks/pre-commit
printf 'halo dunia\n' > halo.txt
git add halo.txt
git commit -m "Commit pertama"Output yang Dihasilkan (perintah terakhir):
pre-commit: pemeriksaan berjalan...Commit tidak terjadi. Hook berjalan sebelum pesan commit ditulis, mencetak pesannya, lalu keluar dengan kode 1 — dan Git membatalkan seluruh proses. Pesan dari hook muncul di terminal persis seperti keluaran perintah biasa, jadi tempat terbaik untuk memberi tahu penulis commit apa yang salah. Bahwa tidak ada satu pun commit yang lahir bisa dibuktikan:
git log --onelineOutput yang Dihasilkan:
fatal: your current branch 'main' does not have any commits yetSekarang balikkan putusannya — ganti exit 1 menjadi exit 0:
sed -i 's/exit 1/exit 0/' .git/hooks/pre-commit
git commit -m "Commit pertama"Output yang Dihasilkan (perintah terakhir):
pre-commit: pemeriksaan berjalan...
[main (root-commit) e8d97c7] Commit pertama
1 file changed, 1 insertion(+)
create mode 100644 halo.txtBaris pertama tetap tercetak — hook tetap berjalan dan tetap bicara — tetapi exit code 0 memberi lampu hijau, dan commit lahir normal. Hash e8d97c7 di mesin pembaca akan berbeda karena setiap commit di-hash dari isi dan identitas pembuatnya; perilaku blok versus lolos-nya tidak berbeda.
Hooks Tidak Ikut Saat Clone #
Folder .git memang lokal — hooks hidup di dalamnya, jadi tidak pernah ikut di-clone. Siapa pun yang meng-clone repository kita menerima folder .git/hooks/ baru yang polos, kembali berisi hanya berkas .sample:
cd ..
git clone latihan-konsep klon-konsep
ls klon-konsep/.git/hooks/pre-commitOutput yang Dihasilkan (dua perintah terakhir):
Cloning into 'klon-konsep'...
done.
ls: cannot access 'klon-konsep/.git/hooks/pre-commit': No such file or directoryJalur pada keluaran Anda menyesuaikan lokasi direktori. Hook buatan kita — yang barusan memblokir commit — tidak ada di klon. Konsekuensinya lurus tapi sering diremehkan: hooks adalah pencegahan, bukan jaminan. Rekan yang lupa memasang hook bisa mendorong commit kotor ke server; kita sendiri bisa menghapus hook kapan pun. Karena itu hooks klien paling tepat dipandang sebagai jaring pengaman untuk kelalaian sendiri, sementara penegakan aturan yang tidak bisa ditawar harus berdiri di sisi server atau tool yang terdistribusi bersama repository — pembahasan distribusi hooks menyusul di chapter Mengelola Hooks .
Peta Dua Sisi: Client dan Server #
Hooks terbagi dua kelompok besar berdasarkan tempatnya berjalan:
| Sisi | Berjalan di | Momen khas | Bisa blok? |
|---|---|---|---|
| Client | komputer penulis commit | sebelum/sesudah commit, rebase, merge, push | ya — operasi lokal dibatalkan |
| Server | mesin yang menerima push | sebelum/sesudah refs diperbarui | ya — push ditolak untuk semua orang |
Keduanya memakai mekanisme exit code yang sama persis; bedanya hanya siapa yang tidak bisa menghindar. Hook pre-commit di laptop Budi bisa Budi hapus sendiri, sedangkan hook pre-receive di server tidak bisa dihapus kontributor mana pun — di situlah protected branches platform seperti GitHub dan GitLab berdiri, seperti dibahas di Protected Branches
.
Satu detail terakhir yang sering mengejutkan pemula: beberapa hook menerima stdin, bukan argumen. Hook pre-push dan seluruh hook server membaca baris demi baris dari standar input berisi daftar refs yang didorong; hook commit-msg sebaliknya menerima argumen $1 berupa jalur berkas pesan. Format masukan tiap hook berbeda-beda, dan dua chapter berikutnya membedahnya per kelompok — peta lengkap hook sisi klien terlebih dahulu.