Bagaimana Saya Menjalankan Perintah yang Benar Setelah Berbulan-bulan Tak Menjalankannya
On this page
- Pendahuluan
- A. Perintah yang jatuh di celah
- B. Kenapa yang masuk-akal-tapi-keliru justru paling mahal
- C. Tiga tool yang tak berbagi apa pun selain frekuensi
- D. Ketiga script ini bukan apa
- E. Enam hambatan yang menghalangi
- Metode
- A. Menyimpan bentuknya, bukan logikanya
- B. Membuat pilihannya eksplisit, bukan pintar
- C. Mencetak konteks yang tak dimiliki keadaannya
- D. Menolak lebih awal, dan berhenti di kegagalan pertama
- E. Mengerjakan persis yang ia kerjakan, di bawah nama yang menyiratkan lebih
- F. Ketika meneruskan argumennya memang jawaban yang benar
- Hasil dan Pembahasan
- A. Pagar yang sebanding dengan yang bisa meleset
- B. Untuk apa emoji di keluarannya
- C. Di mana pendekatan ini berhenti cocok
- Simpulan
- Istilah
Ada segolongan perintah yang saya jalankan kira-kira empat kali setahun. Tak cukup sering untuk diingat, tak cukup jarang untuk dicari dari nol tiap kali, dan — inilah bagian yang menentukan — selalu pada saat ada hal lain yang sudah mulai meleset. Repository yang remote-nya harus diarahkan ke tempat baru. Working tree yang harus dikembalikan ke keadaan bersih. Disk virtual yang kehabisan ruang di tengah proses build.
Pendahuluan
A. Perintah yang jatuh di celah
Frekuensi menentukan cara sebuah perintah diingat, dan bentuknya tak senyaman kedengarannya.
Perintah yang dijalankan tiap hari tinggal di ingatan otot. Saya tak mengingat-ingat susunan git status; jari saya yang mengetikkannya. Perintah yang dijalankan sekali seumur hidup dicari dari nol, dan itu justru baik, sebab menghampirinya tanpa andaian apa pun memang sikap yang tepat.
Perkaranya duduk di tengah. Perintah yang dijalankan tiap beberapa bulan terasa cukup akrab sampai mencarinya terasa berlebihan, dan perasaan itu keliru. Yang sungguh tersisa setelah empat bulan cuma bentuk kasarnya — bahwa ia melibatkan remote, bahwa ada flag yang mengandung upstream di suatu tempat — dan itu cukup untuk menyusun perintah yang masuk akal sekaligus tak cukup untuk menyusun perintah yang benar.
B. Kenapa yang masuk-akal-tapi-keliru justru paling mahal
Perintah yang sama sekali tak saya ingat tak berbahaya. Saya berhenti, membaca manual, lalu melangkah hati-hati sebab saya sadar saya tak tahu.
Perintah yang saya ingat separuh melahirkan urutan yang berbeda. Saya mengetik sesuatu, sintaksnya sah, dan ia mengerjakan sesuatu — cuma bukan yang dimaksud. Git terutama murah hati di sini: ada beberapa verb yang menerima argumen serupa dan berperilaku sama sekali berbeda, dan tak satu pun akan bertanya apakah saya yakin.
Ketiga tool di artikel ini menyentuh benda yang enggan dibatalkan. URL remote yang menunjuk repository keliru mengirim push berikutnya ke tempat yang tak ditonton siapa pun. Working tree yang dibersihkan adalah working tree yang perubahan belum-ter-commit-nya tak lagi ada di mana pun. Disk image adalah file berisi seluruh filesystem, dan operasi atasnya tidak transaksional.
C. Tiga tool yang tak berbagi apa pun selain frekuensi
Kumpulan ini memang sengaja tak seragam. Dua operasi git; satu mengubah ukuran disk image untuk QEMU, program open-source yang menjalankan virtual machine. Ketiganya tak dirancang bersamaan — masing-masing muncul sendiri-sendiri, berselang bulan, saat kebutuhannya datang.
Jarak itulah bagian yang menarik. Seandainya polanya soal git, jalan keluarnya pun akan berbentuk git. Polanya justru soal seberapa sering sebuah perintah dipakai, dan itu sifat kehidupan kerja saya, bukan sifat teknologi mana pun — dan karena itulah tool virtualisasi berakhir di golongan yang sama dengan dua tool version control.
D. Ketiga script ini bukan apa
Tak satu pun mewujudkan apa pun. Bertiga jumlahnya enam puluh enam baris, dan bagian yang benar-benar bekerja cuma tiga perintah yang sudah disediakan git dan QEMU.
Yang disimpan script-nya adalah sebuah keputusan — flag mana, urutan mana, mana di antara dua operasi yang mirip rupanya yang benar untuk keadaan itu. Keputusan itu diambil sekali, dengan hati-hati, sambil membuka dokumentasinya. Menyimpannya berarti ia tak perlu diambil ulang pada saat perhatian sedang di tempat lain.
E. Enam hambatan yang menghalangi
Jadi tujuannya gampang dirumuskan: menjalankan perintah yang benar pada saat ia dibutuhkan, berbulan-bulan sesudah terakhir membutuhkannya, tanpa menyusunnya ulang dari ingatan separuh. Benar pada percobaan pertama, pada operasi yang percobaan keduanya belum tentu tersedia.
Gampang dirumuskan, canggung dibangun. Enam hambatan berdiri antara tujuan itu dan tiga perintah yang bisa saya jalankan tanpa ragu, dan tak satu pun menyangkut perintahnya yang sulit.
Yang pertama, ongkosnya ada pada mengingat, bukan pada mengerjakan. Tiap operasi ini cuma satu baris begitu barisnya diketahui. Bagian mahalnya adalah menariknya kembali dengan benar sesudah jeda panjang, dan sebanyak apa pun pemahaman atas tool-nya tak mencegah itu. Alternatif yang ditolak cukup memberi pelajaran: file catatan memuat teksnya tapi tak bisa dijalankan, jadi ia tetap menuntut dibaca lalu diketik ulang, sementara history | grep memunculkan tiap percobaan sebelumnya termasuk yang keliru, tanpa penanda apa pun mana yang mana.
Yang kedua, dua keadaan yang terasa identik menuntut perintah yang sama sekali berbeda. Mengarahkan repository ke sebuah remote cuma satu kalimat dalam bahasa Indonesia dan dua operasi terpisah di git, bergantung pada sudah adanya remote atau belum. Kedua perintahnya tak berbagi satu verb pun. Salah memilih menghasilkan error yang menjelaskan apa yang gagal tanpa menunjukkan operasi mana yang sebenarnya diinginkan.
Yang ketiga, perintah yang jarang dijalankan dieksekusi dengan konteks paling sedikit dan ditawari paling sedikit pula. Sesudah beberapa bulan saya tak yakin sedang berada di folder mana, branch mana yang aktif, atau remote-nya sekarang menunjuk ke mana — dan perintah yang paling membutuhkan konteks itu justru yang secara bawaan tak mencetak apa pun sebelum bertindak.
Yang keempat, berjalan di tempat yang keliru lebih murah dicegah ketimbang diperbaiki. Perintah git yang dikeluarkan di luar repository gagal tanpa akibat; yang dikeluarkan di dalam repository yang keliru justru berhasil. Sementara itu script yang menjalankan beberapa langkah punya mode kegagalannya sendiri — langkah pertama gagal, langkah kedua tetap berjalan dengan andaian langkah pertama berhasil, dan hasilnya keadaan yang tak dimaksud oleh keduanya.
Yang kelima, sebuah nama bisa menjanjikan lebih dari yang dikerjakan kodenya. Salah satu dari ketiganya bernama rollback, dan itu menyiratkan pembatalan commit. Ia tak mengerjakan itu. Ia membuang perubahan yang belum di-commit beserta file untracked, sambil meninggalkan history tak tersentuh — operasi yang sungguh berguna dengan nama yang menggambarkan operasi lain, dan itu menentukan sebab yang membaca namanya adalah orang yang sudah empat bulan tak menjalankannya.
Yang keenam, menambah validasi juga berongkos. Naluri sesudah menulis beberapa pagar adalah memasang pagar di mana-mana. Tapi ketika tool di bawahnya sudah menghasilkan error yang jelas untuk input yang keliru, pembungkus yang memeriksa lebih dulu cuma menggandakan pesan itu — dan tiap pesan yang digandakan adalah pesan yang bisa melenceng dari aslinya seiring waktu.
Masing-masing punya seksi di bawah. Empat yang pertama soal satu tool yang tumbuh berpagar sebab ia memang membutuhkannya; dua terakhir soal dua tool yang tidak, dan kenapa ketimpangan itu disengaja dan bukan kelalaian.
Metode
A. Menyimpan bentuknya, bukan logikanya
Inilah jawaban atas hambatan pertama, dan ia gagasan sekecil mungkin: taruh perintahnya di sebuah file yang namanya menggambarkan keadaannya, bukan mekanismenya.
# the name is the index into memory — not "qemu-img", but what I want
Yang saya ingat sesudah empat bulan bukan qemu-img resize. Melainkan bahwa saya ingin membuat disk image jadi lebih besar. Perintah yang dinamai menurut niatnya bisa dijangkau dari niatnya, dan cuma itulah yang masih tersisa di ingatan saat kebutuhannya datang.
Ini pula kenapa file catatan tak memecahkannya. Catatan memuat teks yang sama, tapi menjangkaunya menuntut mengingat bahwa catatan itu ada, menemukannya, lalu mengetik ulang isinya tanpa salah salin — tiga langkah yang masing-masing gagal persis pada saat saya sedang terburu-buru. Nama yang bisa dieksekusi terjangkau dengan mengetik tiga huruf pertama lalu menekan tab.
B. Membuat pilihannya eksplisit, bukan pintar
Inilah jawaban atas hambatan kedua. Dua keadaan, dua mode, dinyatakan sebagai argumen pertama.
Kedua operasi di bawahnya tak punya kesamaan apa pun. Repository yang belum punya remote butuh remote ditambahkan lalu butuh push pertamanya untuk menegakkan tracking. Repository yang remote-nya keliru butuh URL remote itu ditulis ulang, dan push sama sekali tak dibutuhkan maupun diinginkan.
if [; then
elif [; then
Alternatif yang menggoda adalah mendeteksi keadaannya — periksa apakah remote sudah ada, lalu pilih yang tepat secara otomatis. Saya menolaknya, dan alasannya menyangkut apa yang terjadi ketika deteksinya benar tapi keyakinan saya atas keadaannya yang keliru. Kalau saya mengira sebuah repository masih segar padahal tidak, tool yang pintar diam-diam mengerjakan yang satunya lalu melaporkan berhasil. Mode yang eksplisit justru gagal, dan gagal itulah yang saya inginkan saat gambaran saya atas keadaannya meleset — sebab cuma itulah penanda bahwa ia memang meleset.
C. Mencetak konteks yang tak dimiliki keadaannya
Inilah jawaban atas hambatan ketiga: sebutkan di mana kamu berdiri sebelum berbuat apa pun, dan apa yang berubah sesudahnya.
# ... the operation ...
Tak satu pun baris itu dibutuhkan operasinya. Keduanya ada sebab yang menjalankan ini punya beberapa terminal terbuka dan belum menyentuh repository ini sejak sebelum libur. Folder dan branch menjawab "apakah saya berada di tempat yang saya kira" persis pada saat pertanyaan itu paling murah dijawab — sebelum ada yang diubah.
git remote -v di penutup menjawab separuh lainnya. Sesudah mengarahkan repository ke URL yang saya ketik dari ingatan, pertanyaan yang langsung muncul adalah apakah ia mendarat dengan benar, dan alternatif dari mencetaknya adalah menjalankan perintah kedua untuk memeriksa — langkah yang justru dilewati ketika yang pertama tampak berhasil.
D. Menolak lebih awal, dan berhenti di kegagalan pertama
Inilah jawaban atas hambatan keempat, dan ia dua pagar yang menangani dua bentuk kegagalan berbeda.
if ! ; then
Pemeriksaan repository adalah pagar jenis biasa. Nilainya bukan pada mencegah kerusakan — git toh akan menolak perintahnya — melainkan pada menghasilkan pesan yang menyebut perkara sebenarnya, ketimbang error git tentang operasi yang tak sadar dipilih pemanggilnya.
set -e menangani perkara yang kurang kentara. Mode create menjalankan dua perintah berurutan, dan yang kedua mengandaikan yang pertama berhasil. Tanpanya, remote add yang gagal disusul push yang tak punya tujuan, dan keluarannya memuat dua error yang menggambarkan satu sebab yang sama. Dengannya, eksekusi berhenti di yang pertama — dan yang pertama itulah yang menjelaskan apa yang terjadi.
Ini juga beda antara sebuah script dengan sederet perintah. Deretan menjalankan segalanya tanpa peduli; script sanggup memutuskan bahwa kegagalan satu langkah membatalkan makna langkah sesudahnya.
E. Mengerjakan persis yang ia kerjakan, di bawah nama yang menyiratkan lebih
Inilah jawaban atas hambatan kelima, dan versi jujurnya: namanya memang tak cermat.
Tak ada di sini yang menyentuh commit. History persis seperti sebelumnya. Yang lenyap adalah segala yang belum di-commit: file yang diubah kembali ke asalnya, file untracked dihapus, dan working tree pulang ke apa pun yang disebut commit terkini.
Itu operasi yang kerap berguna. Eksperimen yang berujung buntu, install dependency yang menyerakkan file, hasil generate yang berantakan — seluruhnya lenyap dalam satu perintah, tanpa perlu membaca git status untuk memutuskan apa yang dibuang.
Ia juga tak bisa dipulihkan, dan ia sengaja tak bertanya. Tool sinkronisasi yang saya pakai untuk menyalin folder menolak tujuan berbahaya dan bertanya sebelum menimpa tujuan yang tak kosong, dan perbandingannya layak dinyatakan: di sana, tujuannya sebuah argumen yang bisa salah ketik, dan pagarnya melindungi dari path yang memang tak pernah dimaksud. Di sini tak ada argumen sama sekali. Perintahnya bekerja atas folder tempat saya berdiri, dan saya mengetik nama yang berbunyi bersihkan. Bertanya berarti menanyakan apakah saya memang memaksudkan satu-satunya pekerjaan yang mungkin dikerjakan perintah ini.
F. Ketika meneruskan argumennya memang jawaban yang benar
Inilah jawaban atas hambatan keenam, dan tool-nya tiga baris termasuk shebang.
#!/bin/bash
Nol validasi, nol pagar, nol yang dicetak. Tiap pemeriksaan yang mungkin saya tambahkan — apakah file-nya ada, apakah ukurannya berbentuk benar, apakah image-nya dalam format yang bisa di-resize — adalah pemeriksaan yang sudah dikerjakan qemu-img, dengan pesan error lebih baik ketimbang yang akan saya tulis, sebab ia paham format image sementara saya cuma akan mencocokkan pola pada string.
Pembungkus yang memvalidasi lebih dulu akan menghasilkan dua pesan error untuk satu perkara yang sama, dan yang saya tulis itulah yang akan basi. Seluruh sumbangan script ini cuma namanya dan urutan argumennya, dan justru itulah seluruh nilainya: sesudah enam bulan saya ingat bahwa saya ingin me-resize sebuah image dan saya ingat ia menerima image lalu ukuran, dalam urutan itu, sebab begitulah urutan yang disiratkan namanya.
Yang membuatnya sepadan jadi script ketimbang catatan adalah apa yang sebenarnya dikerjakan operasinya. Ia membesarkan image tanpa membangunnya ulang, dan itu berarti filesystem beserta seluruh isinya tetap di tempatnya — justru itulah seluruh alasan meraih perintah ini ketimbang membuat image baru lalu menyalin datanya.
Hasil dan Pembahasan
A. Pagar yang sebanding dengan yang bisa meleset
Ketiga script menerapkan kadar kehati-hatian yang sangat berbeda, dan begitu dijajarkan polanya jadi lebih terang ketimbang saat menulisnya.
| Tool | Baris | Pagar | Kenapa sebanyak itu |
|---|---|---|---|
| Menetapkan remote repository | 59 | Cek argumen, cek repository, set -e, konteks dicetak sebelum dan sesudah | Dua mode yang gampang tertukar; beberapa langkah yang satunya bergantung pada yang lain |
| Kembali ke tree yang bersih | 4 | Nol | Tak ada argumen yang bisa keliru; namanya menyebut satu-satunya akibat yang mungkin |
| Me-resize disk image | 3 | Nol | Tool di bawahnya sudah memvalidasi, lebih baik ketimbang yang sanggup dikerjakan pembungkus |
Baris pertama memang pantas berpagar sebab ada yang perlu dijaga: pilihan antar mode, argumen yang bisa menunjuk ke mana saja, dan urutan yang langkah pertamanya gagal membuat langkah kedua tak bermakna. Dua sisanya tak punya satu pun sifat itu, dan menambahkan pagar di sana cuma menghasilkan upacara, bukan keamanan.
Pertanyaan yang ternyata berguna bukan "apakah operasi ini berbahaya" — membuang pekerjaan yang belum di-commit jelas berbahaya — melainkan "adakah keputusan di dalamnya yang bisa diambil dengan keliru". Bila tak ada, pagar tak punya apa pun untuk ditangkap.
B. Untuk apa emoji di keluarannya
Salah satu script ini mencetak emoji di depan tiap baris, dan itu kelihatan sebagai hiasan padahal tak seluruhnya begitu.
Keluaran perintah yang jarang dijalankan itu dipindai, bukan dibaca. Dinding teks seragam menuntut dibaca untuk menemukan baris yang penting; simbol di depan membuat strukturnya kelihatan sekilas — ini tempat saya berada, ini yang hendak terjadi, ini hasilnya. Untuk keluaran yang muncul mungkin empat kali setahun kepada orang yang sedang menyusun ulang konteksnya, selisih itu bernilai lebih ketimbang pada tool yang jalan tiap jam.
Ini juga keputusan yang takkan saya ulangi pada apa pun yang dipanggil script lain, di mana keluarannya di-parse ketimbang dilihat, dan di mana emoji berubah jadi perkara encoding ketimbang bantuan visual.
C. Di mana pendekatan ini berhenti cocok
Ketiga script ini memuat kebiasaan satu orang, dan pemuatan itu memang intinya — tapi ia tetap membatasi di mana ketiganya berlaku.
Ketiganya mengandaikan origin sebagai satu-satunya nama remote, dan itu benar untuk repository saya sekaligus keliru bagi siapa pun yang bekerja dengan fork dan upstream. Ketiganya mencetak untuk manusia, jadi tak satu pun pantas duduk di dalam pipeline. Lalu seluruh premisnya — bahwa keputusan yang diambil sekali bisa disimpan lalu dipakai ulang — cuma berlaku selama keputusan itu masih sahih; tool yang flag-nya berubah antar versi menuntut bentuk simpanannya ditinjau ulang ketimbang dipercaya.
Simpulan
Enam puluh enam baris tersebar di tiga file, dan bagian yang bekerja cuma tiga perintah yang bukan saya yang menulisnya. Yang dimuat ketiga file itu adalah keputusan tentang cara memanggilnya: mode mana, urutan mana, flag mana, dan — pada dua kasus — keputusan bahwa tak ada upacara lanjutan yang pantas ditambahkan.
Yang tak saya duga: pagarnya ternyata terbagi begitu timpang, dan ketimpangan itu justru jawaban yang benar. Naluri saya sesudah membangun satu tool yang hati-hati adalah membuat sisanya hati-hati dengan cara yang sama, dan naluri itu keliru. Upacara yang tak menangkap apa pun bukan kehati-hatian; ia kebisingan yang membuat peringatan sungguhan lebih sulit terlihat. Yang menentukan kadarnya bukan seberapa berbahaya operasinya, melainkan adakah keputusan di dalamnya yang bisa saya ambil dengan keliru — dan untuk perintah yang saya jalankan empat kali setahun, keputusan itulah yang biasanya satu-satunya bagian yang layak dicatat.
Istilah
- Remote
- Rujukan bernama ke repository di tempat lain, yang lazimnya disebut
origin. Menambahkannya dan mengubah URL yang sudah ada adalah dua operasi terpisah dengan perintah terpisah. - Upstream branch
- Branch remote yang diikuti sebuah branch lokal, supaya push dan pull tak butuh argumen. Ditegakkan pada push pertama lewat flag khusus.
- Working tree
- File sebagaimana adanya di disk saat ini, berbeda dari yang sudah di-commit. Perubahan yang belum di-commit cuma hidup di sini, dan karena itu membuangnya tak bisa dibatalkan.
- File untracked
- File yang ada di working tree tapi tak pernah diminta dikelola git. Tak tersentuh perintah yang bekerja atas file tracked, dan karena itu membuangnya butuh perintah tersendiri.
- Disk image
- File yang memuat seluruh filesystem, dipakai virtual machine sebagai disk-nya. Me-resize-nya membesarkan file itu di tempat, sambil membiarkan isinya utuh.
- qcow2
- Format image copy-on-write milik QEMU, yang mengalokasikan ruang seiring pemakaian ketimbang di muka, dan mendukung di-resize sesudah dibuat.
Technologies covered
- Bash
- Git
- QEMU