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/:

bash
mkdir latihan-konsep && cd latihan-konsep
git init -b main
ls .git/hooks

Output yang Dihasilkan (dua perintah terakhir):

text
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.sample

Git 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:

bash
head -n 12 .git/hooks/pre-commit.sample

Output yang Dihasilkan:

text
#!/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=HEAD

Setiap berkas .sample menyimpan petunjuk yang sama di kepalanya: aktifkan dengan mengubah nama. Aturan aktivasi hooks hanya dua:

  • Nama berkas tanpa ekstensi — pre-commit, bukan pre-commit.sample atau pre-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:

bash
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):

text
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:

bash
git log --oneline

Output yang Dihasilkan:

text
fatal: your current branch 'main' does not have any commits yet

Sekarang balikkan putusannya — ganti exit 1 menjadi exit 0:

bash
sed -i 's/exit 1/exit 0/' .git/hooks/pre-commit
git commit -m "Commit pertama"

Output yang Dihasilkan (perintah terakhir):

text
pre-commit: pemeriksaan berjalan...
[main (root-commit) e8d97c7] Commit pertama
 1 file changed, 1 insertion(+)
 create mode 100644 halo.txt

Baris 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:

bash
cd ..
git clone latihan-konsep klon-konsep
ls klon-konsep/.git/hooks/pre-commit

Output yang Dihasilkan (dua perintah terakhir):

text
Cloning into 'klon-konsep'...
done.
ls: cannot access 'klon-konsep/.git/hooks/pre-commit': No such file or directory

Jalur 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:

SisiBerjalan diMomen khasBisa blok?
Clientkomputer penulis commitsebelum/sesudah commit, rebase, merge, pushya — operasi lokal dibatalkan
Servermesin yang menerima pushsebelum/sesudah refs diperbaruiya — 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.