Hari Ketika Git Menolak Push Saya

Suatu hari, git begitu saja berhenti membiarkan saya menyimpan pekerjaan saya. Push saya gagal dengan sebuah server error yang belum pernah saya lihat sebelumnya — bukan konflik merge, bukan masalah perizinan, hanya sebuah penolakan mentah dari sisi sana. Saya telah melakukan commit dengan gembira selama berminggu-minggu, dan kini perintah paling rutin dalam keseharian saya tidak mau selesai.

Penyebabnya bukan bug pada git. Penyebabnya adalah bahwa selama berminggu-minggu itu saya diam-diam menjejali repository saya dengan ribuan hal yang sama sekali tidak seharusnya ada di sana, dan akhirnya repository itu tumbuh terlalu berat untuk diangkat.

Push yang tak mau jalan

Awalnya saya mengira itu hanya kebetulan — gangguan jaringan, server yang sedang bermasalah sesaat. Tapi itu terus terjadi, secara konsisten, setiap kali. Tumpukan bobot repository tampaknya menjadi penyebabnya: push terus gagal dengan cara yang sama setiap kali, dan sebanyak apa pun percobaan ulang tidak akan memperbaiki masalah yang sifatnya struktural, bukan sementara.

Ketika akhirnya saya melihat apa yang sebenarnya ada di dalam repository itu, jawabannya memalukan. Kode — hal yang menjadi tujuan adanya sebuah repository — hanya menjadi minoritas kecil di dalamnya. Bagian terbesar yang luar biasa adalah data: ribuan file hasil generate, cache, dan artefak terkompilasi, masing-masing dengan setia dilacak, diberi versi, dan ikut terbawa dalam setiap operasi.

Bagaimana sebuah repo diam-diam menjadi gudang

Tidak ada yang memutuskan untuk memasukkan data bergigabyte ke dalam version control. Itu terjadi satu langkah yang tampak masuk akal pada satu waktu. Anda men-generate beberapa file dan file-file itu mendarat di folder proyek Anda. Anda menjalankan perintah add, mungkin sedikit ceroboh, dan file-file itu ikut terbawa. Berhasil. Tidak ada yang protes. Maka Anda melakukannya lagi, dan lagi, dan repository itu menumpuk data sebagaimana sebuah laci menumpuk barang berserakan — tanpa terlihat, sampai pada hari ketika laci itu tidak mau ditutup.

Pada saat git menolak push saya, saya sedang melacak ribuan file data. Saya tidak pernah membuat pilihan sadar untuk menaruhnya di bawah version control. Saya hanya tidak pernah membuat pilihan untuk menyingkirkannya.

Data dan kode menginginkan hal yang berlawanan

Alasan mengapa mencampur keduanya itu menyakitkan adalah karena keduanya diberi versi untuk alasan yang sama sekali berbeda. Kode itu kecil, bermakna, dan berubah dengan cara yang ingin Anda lihat baris per baris — git dibangun persis untuk itu. Data itu besar, buram, dan cenderung berubah secara keseluruhan; sebuah diff baris per baris atasnya hampir tidak memberi tahu Anda apa pun, dan menyimpan setiap versi lampaunya selamanya sebagian besar adalah pemborosan. Git luar biasa untuk pekerjaan pertama; riwayat standarnya sering kali kurang cocok untuk yang kedua, setidaknya begitu datanya menjadi besar atau berubah terus-menerus.

Gabungkan keduanya dan Anda mendapatkan yang terburuk dari keduanya. Riwayat Anda membengkak dengan perubahan-perubahan raksasa yang tidak akan pernah bisa Anda baca secara bermakna, setiap clone dan push menyeret semuanya ke sana ke mari, dan hal kecil yang berharga — sumber yang sesungguhnya — berakhir terkubur dalam sebuah repository yang sebagian besar adalah pemberat.

Solusinya: tarik batas yang Anda lewatkan

Perbaikannya secara konseptual sepele dan sedikit membosankan: beri tahu git, secara eksplisit, apa yang tidak boleh dilacak. File hasil generate, cache, artefak terkompilasi — semuanya dikecualikan, dan repository itu kembali menjadi apa yang seharusnya selalu menjadi: rumah bagi hal-hal yang sengaja saya tulis — kode, konfigurasi, dokumen, tes — bukan tumpukan output hasil generate. Data itu dipindahkan ke tempat di mana hal-hal besar yang bisa di-generate ulang seharusnya berada — diproduksi sesuai permintaan, atau disimpan di suatu tempat yang dibangun untuk file besar, tapi tidak pernah lagi diberi versi baris per baris seolah-olah ia adalah sumber.

Push itu berhasil pada percobaan pertama. Perbaikannya tidak cerdas. Itu hanyalah sebuah batas yang seharusnya saya tarik pada hari pertama dan tidak pernah saya tarik.

Kebiasaan yang mendasarinya: sumber versus turunan

Pelajaran yang bertahan lama ini sebenarnya bukan tentang git. Ini adalah sebuah pertanyaan yang kini saya ajukan tentang setiap file yang dihasilkan sebuah proyek: apakah saya yang menulisnya, atau apakah kode saya yang men-generate-nya? Apakah ini sumber — hal tak tergantikan yang ditulis oleh manusia — atau apakah ini turunan, sebuah output yang bisa dibuat ulang dengan menjalankan sumbernya lagi?

Sumber umumnya layak berada di version control. File turunan yang besar atau berukuran besar biasanya tidak — file-file itu seharusnya hanya di-commit ketika reprodusibilitas atau alur kerja benar-benar menuntutnya, dan idealnya tetap bisa direproduksi dari sumbernya kapan saja. Banyak repository yang membusuk menjadi kekacauan yang tak terkelola melakukannya dengan kehilangan jejak akan perbedaan itu — dengan memperlakukan sesuatu yang turunan seolah-olah ia berharga, dan membiarkan hal-hal yang selalu bisa di-generate ulang menumpuk di samping hal-hal yang tidak akan pernah bisa digantikan.

Git tidak rusak hari itu. Ia hanya akhirnya membuat saya menyadari bahwa saya telah berhenti menjaga sumber dan output di tempatnya masing-masing yang terpisah, dan telah meminta satu alat untuk melakukan pekerjaan yang tidak pernah dirancang untuknya.

— Tidak ada sinyal, tidak ada imbal hasil, bukan nasihat investasi.