Mekanisme exit code dari chapter sebelumnya berlaku untuk semua hooks — yang membedakan satu hook dari yang lain hanyalah tiga hal: momen Git menjalankannya, masukan yang diterimanya, dan apakah keluar non-zero-nya memblokir apa pun. Chapter ini memetakan sembilan hook sisi klien yang paling sering dipakai, lalu membedah tiga inti yang menanggung sebagian besar kebutuhan nyata: pre-commit, commit-msg, dan pre-push. Semua demo dijalankan pada satu repository latihan yang sama.

Peta Sembilan Hook Sisi Klien #

HookDijalankan saatMasukanBisa blok?
pre-commitsebelum pesan commit ditulistanpa argumenya — commit dibatalkan
prepare-commit-msgsebelum editor pesan terbuka$1 berkas pesan, $2 sumber pesanjarang — pesannya yang diubah
commit-msgsetelah pesan ditulis, sebelum commit lahir$1 berkas pesanya — commit dibatalkan
post-commitsetelah commit lahirtanpa argumentidak — commit sudah jadi
pre-rebasesebelum git rebase berjalan$1 upstream, $2 branchya — rebase dibatalkan
post-mergesetelah git merge/git pull selesai$1 flag squashtidak — merge sudah jadi
pre-merge-commitsetelah merge mulai, sebelum commit mergetanpa argumenya — commit merge dibatalkan
post-checkoutsetelah git switch/git checkout$1 HEAD lama, $2 HEAD baru, $3 flag branchtidak
pre-pushsebelum git push berangkat$1 nama remote, $2 URL, refs di stdinya — push dibatalkan

Repository latihan kita memakai pola standar sepanjang course ini — plus satu bare repo sebagai remote:

bash
mkdir latihan-client && cd latihan-client
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
git init --bare ../pusat-client.git
git remote add origin ../pusat-client.git
printf 'situs v1\n' > situs.md
git add situs.md
GIT_AUTHOR_DATE="2026-11-06T09:00:00+07:00" GIT_COMMITTER_DATE="2026-11-06T09:00:00+07:00" \
  git commit -m "Rilis situs v1"

pre-commit: Gerbang Sebelum Pesan Ditulis #

pre-commit adalah hook pertama yang berjalan pada git commit — sebelum editor terbuka, sebelum pesan ditulis, jadi penulisnya dihentikan paling awal. Hook ini tidak menerima argumen; cara standar melihat apa yang akan di-commit adalah git diff --cached yang membaca staging area. Sifat inilah yang menjadikannya tempat paling populer untuk pemeriksaan kualitas: linting, jejak debug, whitespace, larangan berkas besar. Demo paling sederhananya sudah kita buktikan di chapter sebelumnya (echo + exit 1 memblokir commit), dan pembangunan versi produksinya — lengkap dengan uji nyata — menjadi fokus chapter Pre-Commit Hook .

commit-msg: Pesan Calon Commit sebagai Berkas #

Hook kedua dalam urutan commit adalah commit-msg. Bedanya dengan pre-commit: hook ini berjalan setelah pesan ditulis — Git menyimpan draf pesan ke sebuah berkas sementara, lalu mengoper jalur berkas itu sebagai argumen pertama ($1). Hook membaca berkasnya, memeriksa format, dan keluar non-zero bila pesan melanggar aturan. Versi minimalnya:

bash
cat > .git/hooks/commit-msg <<'HOOK'
#!/bin/sh
pesan="$(cat "$1")"
if [ ${#pesan} -lt 10 ]; then
  echo "commit-msg: pesan terlalu pendek: '$pesan'" >&2
  exit 1
fi
HOOK
chmod +x .git/hooks/commit-msg
printf 'situs v4\n' > situs.md
git add situs.md
git commit -m "update"

Output yang Dihasilkan (perintah terakhir):

text
commit-msg: pesan terlalu pendek: 'update'

Commit dibatalkan, staging area tetap utuh, dan pesan “update” tidak pernah menjadi commit. Mari kita bedah skripnya baris per baris:

  1. pesan="$(cat "$1")" membaca seluruh isi berkas yang dioper Git — biasanya .git/COMMIT_EDITMSG — ke satu variabel.
  2. if [ ${#pesan} -lt 10 ] menghitung panjang string ${#pesan} dan membandingkannya dengan 10.
  3. >&2 mengalihkan pesan kesalahan ke stderr — konvensi hooks, karena stdout kadang ditelan oleh proses induk.
  4. exit 1 membatalkan commit.

Pesan yang memenuhi syarat lolos tanpa pesan apa pun:

bash
git commit -m "Perbarui halaman situs"

Output yang Dihasilkan:

text
[main 5484f19] Perbarui halaman situs
 1 file changed, 1 insertion(+), 1 deletion(-)

Hash 5484f19 berbeda di mesin pembaca. Validasi yang serius untuk hook ini — pola Conventional Commits beserta pengujian pesan nyata — dibangun tuntas di chapter Commit-Msg Hook .

pre-push: Argumen Remote dan Daftar Refs di stdin #

pre-push berjalan setelah git push menghitung apa yang akan dikirim, tapi sebelum jaringan disentuh — posisi terakhir untuk menahan commit yang belum siap meninggalkan mesin. Dua argumennya adalah nama remote ($1) dan URL-nya ($2); daftar refs yang akan didorong datang lewat stdin, satu baris per ref. Hook berikut menampilkan semuanya:

bash
cat > .git/hooks/pre-push <<'HOOK'
#!/bin/sh
remote="$1"
url="$2"
echo "pre-push: sasaran = $remote ($url)"
echo "pre-push: yang akan didorong:"
while read local_ref local_sha remote_ref remote_sha; do
  echo "  $local_ref $local_sha -> $remote_ref"
done
HOOK
chmod +x .git/hooks/pre-push
printf 'situs v2\n' > situs.md
git add situs.md
GIT_AUTHOR_DATE="2026-11-06T09:10:00+07:00" GIT_COMMITTER_DATE="2026-11-06T09:10:00+07:00" \
  git commit -m "Rilis situs v2"
git push -u origin main

Output yang Dihasilkan (perintah terakhir):

text
pre-push: sasaran = origin (../pusat-client.git)
pre-push: yang akan didorong:
  refs/heads/main 55729b4f1710c918cd5cd7274cadb1e8b6686b94 -> refs/heads/main
To ../pusat-client.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

Format tiap baris stdin: local_ref local_sha remote_ref remote_sha — keempat kolom dipisah spasi. Deret angka nol pada kolom remote_sha (sebelum push pertama) menandakan ref yang belum ada di server; setelah ada, kolom itu berisi hash lama. Blokirnya tinggal mengganti loop menjadi pemeriksa:

bash
cat > .git/hooks/pre-push <<'HOOK'
#!/bin/sh
while read local_ref local_sha remote_ref remote_sha; do
  if [ "$remote_ref" = "refs/heads/main" ]; then
    echo "pre-push: dorongan ke main butuh persetujuan rilis" >&2
    exit 1
  fi
done
HOOK
chmod +x .git/hooks/pre-push
printf 'situs v3\n' > situs.md
git add situs.md
GIT_AUTHOR_DATE="2026-11-06T09:20:00+07:00" GIT_COMMITTER_DATE="2026-11-06T09:20:00+07:00" \
  git commit -m "Rilis situs v3"
git push origin main

Output yang Dihasilkan (perintah terakhir):

text
pre-push: dorongan ke main butuh persetujuan rilis
error: failed to push some refs to '../pusat-client.git'

Push batal sebelum berangkat — bandingkan dengan penolakan server di chapter berikutnya, yang kelihatannya sama di terminal tapi berbeda total secara kekuatan: blokir pre-push bisa dihapus penulisnya sendiri, blokir server tidak.

Hook Pasca dan yang Jarang Dipakai #

Sisanya berperan sebagai pelengkap. post-commit berjalan setelah commit lahir — cocok untuk notifikasi; git rev-parse --short HEAD di dalamnya mengambil hash commit yang barusan tersimpan:

bash
cat > .git/hooks/post-commit <<'HOOK'
#!/bin/sh
echo "post-commit: tersimpan sebagai $(git rev-parse --short HEAD)"
HOOK
chmod +x .git/hooks/post-commit
printf 'situs v5\n' > situs.md
git add situs.md
GIT_AUTHOR_DATE="2026-11-06T09:40:00+07:00" GIT_COMMITTER_DATE="2026-11-06T09:40:00+07:00" \
  git commit -m "Rilis situs v5"

Output yang Dihasilkan (perintah terakhir):

text
post-commit: tersimpan sebagai 568c8cb
[main 568c8cb] Rilis situs v5
 1 file changed, 1 insertion(+), 1 deletion(-)

post-checkout menerima $1 HEAD lama, $2 HEAD baru, dan $3 bernilai 1 bila perpindahan antar-branch — kombinasi pas bila proyek perlu menyiapkan sesuatu tiap ganti branch. Hook berikut mencatat perpindahan:

bash
cat > .git/hooks/post-checkout <<'HOOK'
#!/bin/sh
if [ "$3" = "1" ]; then
  echo "post-checkout: pindah branch: $1 -> $2"
fi
HOOK
chmod +x .git/hooks/post-checkout
git switch -c fitur-galeri

Output yang Dihasilkan (perintah terakhir):

text
Switched to a new branch 'fitur-galeri'
post-checkout: pindah branch: 568c8cbcd7c6b930dbd68ea7e4605f96bf8f65c0 -> 568c8cbcd7c6b930dbd68ea7e4605f96bf8f65c0

prepare-commit-msg mengubah draf pesan sebelum editor terbuka — dipakai template pesan commit; pre-rebase menahan rebase berbahaya; pre-merge-commit dan post-merge menjaga momen sekitar merge, yang terakhir dibedah utuh di chapter Post-Merge Hook . Satu catatan praktis: post-merge juga terpanggil oleh git pull, karena pull pada dasarnya fetch lalu merge.

Semua hook di chapter ini berjalan di mesin penulis — dan justru itulah kelemahan strukturalnya. Kontributor mana pun bisa menghapusnya, dan repo yang di-clone memang tidak membawanya. Ketika aturan harus berlaku untuk semua orang tanpa kecuali, hooks harus pindah rumah ke mesin yang menerima push: server-side hooks, chapter berikutnya.