
Pengembang yang tahu dan tahu cara bekerja dengan git telah tumbuh dengan urutan besar belakangan ini. Anda terbiasa dengan kecepatan eksekusi perintah. Anda terbiasa dengan kenyamanan cabang dan rollback mudah perubahan. Resolusi konflik adalah hal yang biasa sehingga programmer terbiasa menyelesaikan konflik secara heroik di tempat yang seharusnya tidak terjadi.
Tim kami di Directum sedang mengembangkan alat pengembangan untuk solusi platform. Jika Anda melihat 1C, maka Anda dapat membayangkan lingkungan kerja "pelanggan" kami - pengembang aplikasi. Menggunakan alat pengembangan ini, pengembang aplikasi menciptakan solusi aplikasi untuk pelanggan.
Tim kami menghadapi tugas menyederhanakan kehidupan pelamar kami. Kami dimanjakan dengan chip modern dari Visual Studio, ReSharper dan IDEA. Para pelamar menuntut agar kami mengintegrasikan git dari kotak ke dalam alat.
Inilah kesulitannya. Dalam alat untuk setiap jenis entitas (kontrak, laporan, direktori, modul), kunci dapat hadir. Salah satu pengembang mulai mengedit jenis entitas dan memblokirnya sampai menyelesaikan perubahan dan mengirimkannya ke server. Pengembang lain saat ini melihat jenis entitas yang sama dengan hanya baca. Pengembangan agak mengingatkan bekerja di SVN atau mengirim dokumen Word melalui surat antara beberapa pengguna. Saya ingin semuanya sekaligus, tetapi mungkin hanya satu.
Setiap jenis entitas dapat memiliki banyak penangan (membuka dokumen, validasi sebelum menyimpan, menulis ke database), di mana Anda ingin menulis kode yang berfungsi dengan instance spesifik entitas. Misalnya, mengunci tombol, menampilkan pesan kepada pengguna, atau membuat tugas baru untuk para pelaku. Semua kode dalam kerangka API yang disediakan oleh platform. Penangan adalah kelas di mana banyak metode terletak. Ketika dua orang perlu memperbaiki file yang sama dengan kode, itu tidak mungkin dilakukan, karena platform memblokir seluruh jenis entitas bersama dengan kode dependen.
Praktisi kami melakukan semuanya. Mereka diam-diam mengambil salinan "ilegal" dari lingkungan pengembangan kami, mengomentari bagian yang menghalangi, dan menggabungkan komitmen kami untuk diri mereka sendiri. Kode aplikasi disimpan di bawah git, dilakukan melalui alat pihak ketiga (git bash, SourceTree, dan lainnya). Kami membuat kesimpulan:
- Tim kami meremehkan kesediaan pengembang aplikasi untuk masuk ke dalam platform. Hormat dan hormat!
- Solusi yang mereka usulkan tidak cocok untuk produksi. Dengan git, tangan seseorang tidak terikat dan dia dapat membuat apa saja. Untuk mendukung semua keberagaman akan menjadi hal yang bodoh, jangan dibajak. Selain itu, perlu untuk mendidik pelanggan platform. Mendokumentasikan semua perintah git untuk platform akan membuat tim dokumentasi gila.

Apa yang Anda inginkan dari Git
Jadi memberi untuk produksi dengan keluar tidak baik. Kami memutuskan untuk merangkum logika operasi utama dan membatasi jumlahnya. Setidaknya untuk rilis pertama. Daftar tim dikurangi sebanyak yang mereka bisa dan tetap:
- status
- berkomitmen
- tarik
- mendorong
- atur ulang --buat ke HEAD
- setel ulang ke komit "server" terakhir
Untuk rilis pertama, mereka memutuskan untuk menolak bekerja dengan cabang. Bukan berarti itu sangat sulit, hanya saja tim tidak memenuhi sumber daya waktu.
Secara berkala, mitra kami mengirimkan pengembangan aplikasi mereka dan bertanya: "Sesuatu tidak bekerja untuk kami. Apa yang kami lakukan salah?". Dalam hal ini, aplikasi memuat dirinya sendiri dengan pengembangan orang lain dan melihat ke dalam kode. Ini digunakan untuk bekerja seperti ini:
- Pengembang mengambil arsip dengan pengembangan;
- Mengubah database lokal di konfigurasi;
- Menuangkan pengembangan orang lain ke markasnya;
- Terjebak, ditemukan kesalahan;
- Rekomendasi yang dikeluarkan;
- Dia mengembalikan perkembangannya kembali.
Metodologi baru tidak cocok dengan pendekatan lama. Aku harus menghancurkan kepalaku. Tim mengusulkan dua pendekatan untuk menyelesaikan masalah ini:
- Simpan semua pengembangan dalam satu repositori git. Jika perlu, bekerja dengan keputusan orang lain untuk membuat cabang sementara.
- Simpan pengembangan tim yang berbeda di repositori yang berbeda. Pindahkan pengaturan folder yang dimuat ke lingkungan ke dalam file konfigurasi.
Kami memutuskan untuk mengikuti jalan kedua. Yang pertama tampaknya lebih sulit untuk diimplementasikan dan, terlebih lagi, lebih mudah untuk menembak diri sendiri dengan mengganti cabang.
Namun yang kedua juga tidak manis. Perintah yang dijelaskan di atas harus berfungsi tidak hanya dalam repositori yang sama, tetapi dengan beberapa sekaligus. Apakah ada perubahan tipe entitas dari repositori yang berbeda? Kami tunjukkan di satu jendela. Ini lebih nyaman dan transparan untuk pengembang aplikasi. Dengan menekan tombol komit, alat melakukan perubahan pada masing-masing repositori. Dengan demikian, perintah pull / push / reset "under hood" bekerja dengan repositori yang berbeda secara fisik.

Libgit2sharp
Untuk bekerja dengan git, kami memilih dari dua opsi:
- Bekerja dengan git yang terinstal di sistem, menariknya melalui Process.Start dan parsing output.
- Gunakan libgit2sharp, yang melalui pinvoke menarik libgit2 library.
Tampaknya bagi kami bahwa menggunakan perpustakaan yang sudah jadi adalah solusi yang masuk akal. Sia-sia. Saya akan memberi tahu Anda sedikit kemudian mengapa. Pada awalnya, perpustakaan memberi kami kesempatan untuk segera meluncurkan prototipe yang berfungsi.
Iterasi pengembangan pertama
Itu mungkin untuk diterapkan dalam waktu sekitar satu bulan. Sebenarnya mengacaukan git itu cepat, dan sebagian besar waktu kami mencoba menyembuhkan luka yang telah dibuka karena kami telah memotong mekanisme lama untuk menyimpan file sumber. Segala sesuatu yang git status
dikembalikan dikembalikan ke antarmuka. Mengklik pada setiap file menampilkan diff. Itu tampak seperti antarmuka git gui.
Iterasi pengembangan kedua
Opsi pertama terlalu informatif. Dengan setiap jenis entitas, banyak file dikaitkan sekaligus. File-file ini menciptakan kebisingan, dan menjadi tidak jelas jenis entitas apa yang berubah dan apa tepatnya.
File yang dikelompokkan berdasarkan jenis entitas. Setiap file diberi nama yang dapat dibaca manusia, sama seperti di GUI. Metadata jenis entitas dijelaskan dalam JSON. Mereka juga perlu disajikan dalam format yang bisa dibaca manusia. Analisis perubahan dalam versi json "sebelum" dan "setelah" mulai menggunakan pustaka jsondiffpatch, dan kemudian mereka menulis implementasi JSON perbandingan mereka sendiri (selanjutnya saya akan memanggil jsondiff). Kami menjalankan hasil perbandingan melalui analisis yang menghasilkan catatan yang dapat dibaca manusia. Banyak file disembunyikan dari tampilan, meninggalkan entri sederhana di pohon perubahan.
Hasil akhirnya adalah sebagai berikut:

Mengalami masalah dengan libgit2
Libgit2 menghasilkan banyak kejutan tak terduga. Berurusan dengan beberapa berada di luar kekuatan waktu yang masuk akal. Saya akan memberi tahu Anda apa yang saya ingat.
Tidak terduga dan sulit untuk mereproduksi jatuh pada beberapa operasi standar. "Tidak ada kesalahan yang disediakan oleh perpustakaan asli" memberi tahu kami bungkusnya. Bagus Anda mengutuk, Anda sedang membangun kembali perpustakaan asli di debug, Anda mengulangi kasus yang sebelumnya dijatuhkan, tetapi tidak macet dalam mode debug. Bangun kembali dalam rilis dan jatuh lagi.
Jika alat pihak ketiga, katakan SourceTree, berjalan paralel dengan libgit2sharp, maka komit mungkin tidak melakukan beberapa file. Atau macet ketika menampilkan perbedaan pada beberapa file. Segera setelah Anda mencoba men-debug, Anda tidak dapat mereproduksi.
Di salah satu aplikasi kami, implementasi analog git status
membutuhkan waktu 40 detik. Empat puluh Carl! Pada saat yang sama, git yang diluncurkan dari konsol bekerja sebagaimana mestinya untuk sesaat. Saya menghabiskan beberapa hari untuk menyelesaikannya. Saat mencari perubahan, Libgit2 melihat atribut file folder dan membandingkannya dengan entri dalam indeks. Jika waktu modifikasi berbeda, maka ada sesuatu yang berubah di dalam folder dan Anda perlu melihat ke dalam dan / atau mencari di dalam file. Dan jika tidak ada yang berubah, maka Anda tidak harus naik ke dalam. Optimasi ini ternyata juga ada di konsol git. Saya tidak tahu untuk alasan apa, tetapi hanya untuk satu orang di indeks git, waktu dapat berubah. Karena itu, git memeriksa konten SEMUA file di repositori setiap kali ada perubahan.
Lebih dekat untuk dirilis, tim kami menyerah pada keinginan tim aplikasi dan mengganti git pull
dengan fetch + rebase + autostash
. Dan kemudian sekelompok bug datang kepada kami, termasuk dengan "Tidak ada kesalahan yang disediakan oleh perpustakaan asli".
status, tarik dan rebase bekerja lebih lama daripada memanggil perintah konsol.
Penggabungan otomatis
File dalam pengembangan dibagi menjadi dua jenis:
- File yang dilihat aplikasi di alat pengembangan. Misalnya, kode, gambar, sumber daya. File-file seperti itu perlu digabungkan seperti halnya git.
- File JSON yang dibuat oleh lingkungan pengembangan, tetapi pengembang aplikasi melihatnya hanya dalam bentuk GUI. Mereka perlu menyelesaikan konflik secara otomatis.
- File yang dibuat secara otomatis dibuat ulang saat bekerja dengan alat pengembangan. File-file ini tidak masuk ke repositori, alat segera menaruh .gitignore dengan hati-hati.
Dengan cara baru, dua aplikator yang berbeda dapat mengubah jenis entitas yang sama.
Sebagai contoh, Sasha akan mengubah informasi tentang cara menyimpan tipe entitas dalam database dan menulis handler untuk acara save, dan Sergey akan mendesain representasi entitas. Dari sudut pandang git, ini tidak akan menjadi konflik dan kedua perubahan akan bergabung tanpa kerumitan.
Dan kemudian Sasha mengubah properti Property1 dan mengatur handler untuk itu. Sergey menciptakan properti Property2 dan mengatur pawang. Jika Anda melihat situasi dari atas, perubahannya tidak bertentangan, meskipun dari sudut pandang git file yang sama terpengaruh.
Saya ingin instrumen dapat menyelesaikan situasi ini sendiri.
Contoh algoritma untuk menggabungkan dua JSON jika terjadi konflik:
Unduh dari git dasar JSON.
Unduh dari gita JSON kami.
Mengunduh JSON mereka dari git.
Menggunakan jsondiff, kami membentuk tambalan perangkat lunak basis-> dan berlaku untuk tambalan mereka. JSON yang dihasilkan disebut P1.
Menggunakan jsondiff, kami membentuk tambalan perangkat lunak base-> dan berlaku untuk tambalan kami. JSON yang dihasilkan disebut P2.
Idealnya, setelah menerapkan tambalan P1 === P2. Jika demikian, maka tulis P1 ke disk.
- Dalam kasus yang tidak sempurna (ketika konflik benar-benar ada), kami menyarankan pengguna untuk memilih antara P1 dan P2 dengan kemampuan untuk menyelesaikan dengan tangannya. Kami menulis pilihan ke disk.
Setelah penggabungan, kami memeriksa apakah status tanpa kesalahan validasi telah tercapai. Jika Anda belum tiba, batalkan penggabungan dan minta pengguna untuk mengulanginya. Ini bukan solusi terbaik, tetapi setidaknya menjamin bahwa dari upaya kedua atau ketiga, merger akan terjadi tanpa konsekuensi yang tidak menyenangkan.
Ringkasan
- Tukang daging senang bahwa mereka dapat menggunakan secara legal.
- Pengenalan git mempercepat pengembangan.
- Penggabungan otomatis umumnya terlihat seperti sulap.
- Kami meletakkan penolakan masa depan libgit2 demi memohon proses git.