Sebelum membangun gerbang yang cerdas, kita perlu yakin memahami gerbang yang paling bodoh. Bagian ini merintis repository web-toko, lalu memasang hook pre-commit setolol mungkin: skrip lima baris yang hanya mencetak satu kalimat dan keluar. Dari hook itu kita belajar dua hal sekaligus — kapan Git menjalankannya, dan bahasa apa yang dipakai hook untuk berbicara dengan Git. Bahasa itu, exit code, kemudian kita balik putusannya di tengah bagian: satu karakter diubah, dan commit yang tadinya lahir mulai ditolak.
Menyiapkan Repository web-toko #
Mulai dari folder kerja kosong, buat repository dengan branch main sejak awal:
mkdir web-toko && cd web-toko
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"Output yang Dihasilkan (perintah git init -b main):
Initialized empty Git repository in /home/z/my-project/tutorial-extracted/work/sandbox/proyek-git/proyek-06/web-toko/.git/Isi proyeknya dua berkas: README.md yang menjelaskan proyek, dan src/app.txt berisi peta halaman aplikasi — pengganti berkas kode sungguhan, cukup untuk dilatih tanpa memasang Node.js atau framework apa pun:
printf '# web-toko\n\nAplikasi toko web untuk latihan otomasi Git hooks — Course Git\nProyek 06.\n\n## Isi\n\n- `src/app.txt` — peta halaman aplikasi\n' > README.md
mkdir src
printf 'web-toko — aplikasi toko web\n============================\n\n## Halaman\n\n- / — beranda\n- /produk — daftar produk\n\n## Catatan\n\nRintisan pertama: struktur dasar aplikasi.\n' > src/app.txt
cat README.md
cat src/app.txtOutput yang Dihasilkan (perintah cat README.md):
# web-toko
Aplikasi toko web untuk latihan otomasi Git hooks — Course Git
Proyek 06.
## Isi
- `src/app.txt` — peta halaman aplikasiOutput yang Dihasilkan (perintah cat src/app.txt):
web-toko — aplikasi toko web
============================
## Halaman
- / — beranda
- /produk — daftar produk
## Catatan
Rintisan pertama: struktur dasar aplikasi.Hook Pertama: Lima Baris yang Berjalan Sendiri #
Teori lengkapnya sudah kita baca di Konsep Git Hooks
; sekarang praktiknya. Hook pre-commit tinggal di .git/hooks/pre-commit — nama berkas harus persis sama dengan nama peristiwanya, dan berkasnya harus executable. Tulis hook pertama kita:
cat > .git/hooks/pre-commit <<'HOOK'
#!/bin/sh
echo "pre-commit berjalan"
exit 0
HOOK
chmod +x .git/hooks/pre-commitHook itu tidak memeriksa apa pun — hook hanya menyapa lalu melapor baik-baik saja lewat exit 0. Pastikan bit executable-nya terpasang:
stat -c '%A %n' .git/hooks/pre-commitOutput yang Dihasilkan:
-rwxrwxr-x .git/hooks/pre-commitTiga huruf rwx di segmen pertama berarti pemilik berkas boleh membaca, menulis, dan mengeksekusi — syarat aktivasi kedua sudah terpenuhi. Daftar lengkap seluruh nama hook yang dikenal Git — dari pre-commit sampai pre-receive — terdokumentasi di githooks
. Sekarang saatnya commit pertama, dan kali ini hook ikut menyaksikan:
git status
git add .
git commit -m "feat: rintis web-toko"Output yang Dihasilkan (perintah git status):
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
src/
nothing added to commit but untracked files present (use "git add" to track)Git melaporkan src/ sebagai satu entri karena seluruh isinya belum dilacak — masih berstatus untracked
. Lalu commit dibuat:
Output yang Dihasilkan (perintah git commit):
pre-commit berjalan
[main (root-commit) e9d7cc7] feat: rintis web-toko
2 files changed, 19 insertions(+)
create mode 100644 README.md
create mode 100644 src/app.txtPerhatikan urutan barisnya: pre-commit berjalan tercetak sebelum baris [main (root-commit) ...]. Git menjalankan hook lebih dulu, menunggu hook selesai, baru mencatat commit. Hash e9d7cc7 di mesin kita akan berbeda — hash dipengaruhi isi, pesan, dan waktu pembuatan. Riwayatnya kini berdiri:
git log --onelineOutput yang Dihasilkan:
e9d7cc7 feat: rintis web-tokoMembalik Putusan: exit 1 Menolak Commit #
Sekarang bagian yang mengubah segalanya. Siapkan perubahan baru — fitur keranjang belanja — dan masukkan ke staging area:
printf '\n## Keranjang\n\n- /keranjang — isi keranjang belanja\n' >> src/app.txt
tail -n 5 src/app.txt
git add src/app.txtOutput yang Dihasilkan (perintah tail -n 5 src/app.txt):
Rintisan pertama: struktur dasar aplikasi.
## Keranjang
- /keranjang — isi keranjang belanjaFitur keranjang kini duduk di staging area menunggu commit. Ubah putusan hook — satu kata saja, exit 0 menjadi exit 1 — lalu coba commit:
sed -i 's/exit 0/exit 1/' .git/hooks/pre-commit
git commit -m "tambah keranjang"
echo "exit code: $?"Output yang Dihasilkan (dua perintah terakhir):
pre-commit berjalan
exit code: 1Hook tetap berjalan dan tetap bersapa — baris pre-commit berjalan masih tercetak. Yang berubah hanya vonisnya: exit code: 1 menegaskan perintah git commit berakhir gagal. Periksa dampaknya pada riwayat:
git log --oneline
git statusOutput yang Dihasilkan (perintah git log --oneline):
e9d7cc7 feat: rintis web-tokoOutput yang Dihasilkan (perintah git status):
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/app.txtDua fakta penting terbaca dari keluaran itu. Pertama, riwayat masih berisi satu commit — commit keranjang tidak pernah lahir. Kedua, staging area tetap utuh memuat modified: src/app.txt; hook yang menolak commit tidak membuang pekerjaan kita, hanya menahan gerbangnya. Mekanisme lengkapnya ringkas seperti ini:
| Exit code hook | Arti bagi Git | Akibat pada git commit |
|---|---|---|
0 | “semua baik, lanjutkan” | commit dibuat — keluaran hook tetap tercetak |
selain 0 (1, 2, …) | “ada masalah, hentikan” | commit dibatalkan — staging area tidak tersentuh |
Tidak ada API, tidak ada format khusus — setiap program yang bisa mengembalikan exit code bisa menjadi hook. Alur satu putaran commit dengan dua hook yang akan kita bangun terlihat begini:
Diagram itu dibaca sebagai dua ganjalan berurutan: pre-commit memeriksa isi staging area, dan bila lolos, commit-msg mengadili pesannya. Keduanya bisa menghentikan proses dengan cara yang sama persis — mekanisme yang baru saja kita buktikan dengan membalik satu kata.
Hook demo ini sudah menunaikan tugasnya dan sekarang justru menghalangi: dengan exit 1, hook itu menolak semua commit. Hapus hooknya — hook bukan berkas terlacak, jadi rm biasa cukup, tanpa git rm:
rm .git/hooks/pre-commitPerintah itu berlalu tanpa keluaran; staging area masih memuat fitur keranjang, dan folder .git/hooks/ kembali berisi hanya berkas .sample. Hook lima baris tadi mengajarkan seluruh mekanismenya: Git memanggil, hook bicara lewat keluaran, dan hook memutus lewat exit code. Bagian berikutnya memberi hook sungguhan mata pertamanya: skrip yang membaca pesan commit dan menolak yang tak berformat: Bagian 2: Validasi Commit Message
.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
scripts/validate-commit-message.sh
Unduh berkas#!/bin/sh
# validate-commit-message.sh — inti validasi pesan commit web-toko.
# Dipanggil oleh hooks/commit-msg dengan $1 = berkas pesan sementara
# (biasanya .git/COMMIT_EDITMSG). Aturan: Conventional Commits.
berkas_pesan="$1"
subjek="$(grep -v '^#' "$berkas_pesan" | sed '/^[[:space:]]*$/d' | head -n 1)"
pola='^(feat|fix|docs|chore|refactor|test)(\(.+\))?: .+'
if [ -z "$subjek" ]; then
echo "commit-msg: pesan kosong — tulis subjek dengan format type(scope): deskripsi" >&2
exit 1
fi
if ! printf '%s\n' "$subjek" | grep -qE "$pola"; then
{
echo "commit-msg: subjek ditolak: '$subjek'"
echo " format: type(scope): deskripsi — scope opsional"
echo " type: feat | fix | docs | chore | refactor | test"
echo " contoh: 'feat: tambah keranjang' atau 'fix(ui): benahi tombol bayar'"
} >&2
exit 1
fi
if [ "${#subjek}" -gt 72 ]; then
echo "commit-msg: subjek ${#subjek} karakter — maksimal 72: '$subjek'" >&2
exit 1
fi