Malam 1 Mei 2026 saya mendorong beberapa commit ke repo salah satu klien, lalu membuka log sekadar memastikan urutannya benar sebelum menutup laptop. Tidak ada error, tidak ada penolakan dari remote, tidak ada satu pun perintah yang berhenti di tengah. Yang menyambut saya cuma tiga baris subject yang identik dan sama sekali tidak berbunyi.
$ git log --format="%s" -3
@'
@'
@'Tiga commit mendarat dengan @' sebagai seluruh isi pesannya. Bukan pesan yang terpotong di tengah, bukan pesan yang kehilangan badannya dan menyisakan subject. Dua karakter itu memang seluruh pesan commit-nya, dan ketiganya sudah ada di remote.
Pesannya saya susun sebagai here-string
Ketiga commit itu pesannya panjang. Ada subject, baris kosong, lalu beberapa butir penjelasan. Untuk pesan berbentuk begitu saya terbiasa memakai here-string PowerShell, karena baris barunya tersimpan apa adanya dan saya tidak perlu memikirkan escape sama sekali:
git commit -m @'
feat(modul): ringkasan perubahan
- butir pertama
- butir kedua
'@@' di awal dan '@ di akhir adalah penanda here-string. Selama yang membaca perintah itu memang PowerShell, semua yang ada di antara kedua penanda masuk sebagai satu string tunggal ke argumen -m, lengkap dengan baris kosongnya. Bentuk ini nyaman sekali, dan itu justru yang membuatnya berbahaya.
Terminal saya bukan PowerShell
Lingkungan yang saya pakai melaporkan Platform: win32 dan Shell: PowerShell di headernya. Saya membaca baris itu dan langsung menganggapnya kebenaran. Padahal header semacam itu menggambarkan sistemnya, bukan jendela terminal yang sedang benar-benar terbuka di depan saya, dan yang terbuka waktu itu CMD.
CMD tidak punya sintaks here-string sama sekali. Tidak ada penanda yang dia kenali, jadi @' tidak berarti apa-apa selain teks harfiah. Argumen -m menerima dua karakter itu apa adanya, dan baris pembuka pesan berubah menjadi seluruh pesannya. Git sendiri tidak punya alasan mengeluh, karena dari sudut pandangnya dia baru saja diberi string satu baris yang sah dan tidak kosong.
Itu juga yang menjelaskan kenapa jumlahnya tiga. Saya menjalankan bentuk yang sama tiga kali berturut-turut, dan tidak ada satu pun yang berhenti dengan error, jadi tidak ada yang menahan saya untuk lanjut.
Cara paling murah membedakan kedua shell itu bukan membaca header, tapi melihat bentuk prompt-nya:
C:\path> CMD, tanpa awalan apa pun
PS C:\path> PowerShell, ada PS di depanMembereskan tiga commit yang telanjur naik
Karena isinya sudah benar dan yang rusak cuma pesannya, tiga commit itu saya gulung balik tanpa menyentuh berkasnya. Angka tiga di HEAD~3 persis sejumlah commit yang kacau:
git reset --soft HEAD~3
# stage ulang, lalu commit ulang dengan pesan yang benar
git push --forceForce-push ke branch yang sudah dilihat orang lain selalu punya konsekuensi, dan itu bagian paling tidak enak dari kejadian ini. Kesalahannya sendiri sepele, tapi ongkos memperbaikinya menyeret riwayat yang sudah publik.
Aturannya sekarang: pesan tinggal di berkas, bukan di perintah
Perbaikan yang saya ambil bukan mencari sintaks here-string versi CMD, melainkan berhenti menitipkan teks pesan ke shell mana pun. Pesan multi-baris saya tulis dulu ke berkas, baru commit-nya menunjuk ke berkas itu:
git commit -F C:\temp\msg-1.txt
git log -1 --format="%H %s"-F itu asli git, bukan fitur shell, jadi perilakunya sama persis di CMD, di PowerShell, maupun di bash. Satu berkas untuk satu commit, dinamai berurutan, dan teks pesannya tidak pernah ikut tertanam di dalam perintah sama sekali.
Baris kedua sama pentingnya dengan baris pertama. Setiap commit saya periksa dengan git log -1 --format="%H %s" sebelum lanjut ke commit berikutnya, dan kalau subject-nya tidak cocok dengan yang saya maksud, saya berhenti di situ. Kesalahan yang sama tiga kali berturut-turut itu bukan tiga kesalahan, itu satu kesalahan yang dibiarkan menumpuk.
Aturan ini tidak berlaku untuk semua commit. Untuk commit sepele satu baris, git commit -m "..." tetap aman, karena satu baris memang tidak menabrak apa pun di shell. Yang saya khususkan untuk pendekatan berkas adalah pesan yang punya paragraf, punya butir, punya baris kosong, atau apa pun yang lebih dari satu baris. Di Windows, yang jadi platform utama saya sehari-hari, batas satu baris itu yang saya pakai sebagai pemicu. Aturan yang sama saya terapkan ke badan pull request lewat gh pr create --body-file, karena persoalannya identik.
Berkas pesannya sendiri tidak perlu diributkan. Kalau tertinggal di folder sementara, tidak ada yang rusak. Menghapusnya setelah push cuma soal kerapian.
Yang saya coret dari kebiasaan
Ada empat bentuk yang sejak itu tidak saya pakai lagi:
- Here-string di perintah yang akan dijalankan di terminal, karena rapuh berpindah shell dan rusaknya diam-diam.
- Percaya
Shell: PowerShelldari header lingkungan, karena terminal yang benar-benar terbuka bisa berbeda. - Escape
\ninline di dalam-m "...", karena tiap shell menafsirkannya dengan caranya sendiri. - Menyalin pesan multi-baris ke argumen
-mberkutip, karena bug kutip dan escape-nya banyak.
Kalau suatu saat saya memang butuh konstruksi yang khusus PowerShell, entah here-string, ternary, atau rantai pipeline, shell-nya saya pastikan dulu sebelum perintahnya dijalankan. Header lingkungannya sudah pernah keliru sekali, dan itu cukup.
Gerbong keduanya: git add -A menyapu skrip sementara
Pola yang sama, yaitu perintah nyaman yang cakupannya lebih luas dari yang saya kira, terbakar lagi pada 21 Juli 2026. Saya rutin membuat skrip scratch-*.ts di akar repo untuk verifikasi ujung ke ujung, misalnya membuat lalu menghapus dokumen uji. Alur deploy saya memakai git add -A, dan perintah itu men-stage skrip sementara yang kebetulan masih tergeletak di pohon kerja. Skripnya ikut ter-commit dan ter-push ke dua remote, salah satunya repo yang bisa dilihat publik. Membersihkannya butuh satu commit git rm susulan plus deploy lagi.
Skrip itu sendiri tidak berbahaya saat runtime. Dia .ts di akar, bukan di dalam app/, dan tidak diimpor dari mana pun. Tapi tidak berbahaya bukan alasan untuk membiarkannya mendarat di repo klien.
rm -f scratch-*.ts
git status --short
git add src/util.ts src/page.tsxTiga baris itu tiga jalan keluar yang berbeda, dan salah satunya sudah cukup. Buang dulu skripnya sebelum commit deploy, atau baca git status --short dan pastikan yang ter-stage memang cuma yang saya maksud, atau tinggalkan -A dan sebutkan path-nya satu per satu. Perbaikan yang lebih tahan lama, menaruh scratch-* di .gitignore, baru berstatus usulan. Saya belum menjalankannya.
Yang saya bawa pulang
- Kegagalan yang paling mahal bukan yang berteriak, tapi yang diterima dengan tenang. CMD menerima
@'sebagai pesan commit yang sah, git menerimanya juga, dan tidak ada satu pun lapisan yang merasa perlu memberi tahu saya. - Header lingkungan menggambarkan sistemnya, bukan jendela terminal yang sedang dipakai. Bentuk prompt jauh lebih jujur:
C:\path>berarti CMD,PS C:\path>berarti PowerShell. - Kalau sebuah perintah bergantung pada sintaks khas satu shell, dia berhenti jadi perintah git dan berubah jadi perintah shell.
git commit -Fmengembalikannya ke wilayah git, dan di situ perilakunya sama di mana-mana. - Ada alasan tambahan untuk pendekatan berkas yang belum saya uji sendiri. Katanya bahkan di PowerShell asli here-string bisa rusak kalau proses paste meratakan baris barunya jadi satu baris, dan sebagian emulator terminal memang berperilaku begitu. Kalau itu benar, berkas tetap pilihan yang lebih aman terlepas shell-nya, dan saya tidak melihat alasan untuk menunggu bukti sebelum memakainya.
- Verifikasi satu commit sebelum menulis commit berikutnya.
git log -1 --format="%H %s"cuma makan satu detik, dan itu selisih antara satu commit yang perlu diperbaiki dan tiga commit yang menyeret force-push.