Klaim mengapa penggabungan lebih baik dalam DVCS daripada di Subversion sebagian besar didasarkan pada bagaimana percabangan dan penggabungan bekerja di Subversion beberapa waktu lalu. Subversi sebelum 1.5.0 tidak menyimpan informasi tentang kapan cabang digabung, jadi ketika Anda ingin menggabungkan Anda harus menentukan rentang revisi mana yang harus digabung.
Jadi mengapa penggabungan Subversion menghisap ?
Renungkan contoh ini:
1 2 4 6 8
trunk o-->o-->o---->o---->o
\
\ 3 5 7
b1 +->o---->o---->o
Ketika kami ingin menggabungkan perubahan b1 ke dalam trunk, kami akan mengeluarkan perintah berikut, sambil berdiri di folder yang memiliki trunk check out:
svn merge -r 2:7 {link to branch b1}
... yang akan mencoba untuk menggabungkan perubahan dari b1
ke direktori kerja lokal Anda. Dan kemudian Anda melakukan perubahan setelah Anda menyelesaikan konflik dan menguji hasilnya. Saat Anda komit, pohon revisi akan terlihat seperti ini:
1 2 4 6 8 9
trunk o-->o-->o---->o---->o-->o "the merge commit is at r9"
\
\ 3 5 7
b1 +->o---->o---->o
Namun cara menentukan rentang revisi ini cepat hilang ketika pohon versi tumbuh karena subversi tidak memiliki data meta kapan dan apa revisi yang digabungkan. Renungkan apa yang terjadi kemudian:
12 14
trunk …-->o-------->o
"Okay, so when did we merge last time?"
13 15
b1 …----->o-------->o
Ini sebagian besar merupakan masalah oleh desain repositori yang dimiliki Subversion, untuk membuat cabang Anda perlu membuat direktori virtual baru di repositori yang akan menampung salinan trunk tetapi tidak menyimpan informasi mengenai kapan dan apa hal-hal yang digabungkan kembali. Itu akan menyebabkan konflik gabungan yang buruk di kali. Yang lebih buruk lagi adalah bahwa Subversion menggunakan penggabungan dua arah secara default, yang memiliki beberapa batasan yang melumpuhkan dalam penggabungan otomatis ketika dua kepala cabang tidak dibandingkan dengan leluhur mereka yang sama.
Untuk mengurangi Subversi ini sekarang menyimpan data meta untuk cabang dan bergabung. Itu akan menyelesaikan semua masalah bukan?
Dan oh, omong-omong, Subversi masih menyebalkan ...
Pada sistem terpusat, seperti subversi, direktori virtual menyedot. Mengapa? Karena setiap orang memiliki akses untuk melihatnya ... bahkan yang eksperimental sampah. Percabangan baik jika Anda ingin bereksperimen tetapi Anda tidak ingin melihat eksperimen orang lain dan bibinya . Ini adalah kebisingan kognitif yang serius. Semakin banyak cabang yang Anda tambahkan, semakin banyak omong kosong yang akan Anda lihat.
Semakin banyak cabang publik yang Anda miliki di repositori, semakin sulit melacak semua cabang yang berbeda. Jadi pertanyaan yang akan Anda miliki adalah apakah cabang masih dalam pengembangan atau jika benar-benar mati yang sulit dikatakan dalam sistem kontrol versi terpusat.
Sebagian besar waktu, dari apa yang saya lihat, organisasi akan default untuk menggunakan satu cabang besar. Yang memalukan karena pada gilirannya akan sulit untuk melacak versi pengujian dan rilis, dan apa pun yang baik berasal dari percabangan.
Jadi mengapa DVCS, seperti Git, Mercurial dan Bazaar, lebih baik daripada Subversion di percabangan dan penggabungan?
Ada alasan yang sangat sederhana mengapa: percabangan adalah konsep kelas satu . Tidak ada direktori virtual oleh desain dan cabang adalah objek keras dalam DVCS yang perlu sedemikian rupa agar dapat bekerja hanya dengan sinkronisasi repositori (yaitu push dan pull ).
Hal pertama yang Anda lakukan ketika Anda bekerja dengan DVCS adalah mengkloning repositori (git's clone
, hg's clone
dan bzr's branch
). Kloning secara konseptual sama dengan membuat cabang dalam kontrol versi. Beberapa menyebutnya forking atau branching (meskipun yang terakhir sering juga digunakan untuk merujuk ke cabang co-located), tetapi itu adalah hal yang sama. Setiap pengguna menjalankan repositori mereka sendiri yang artinya Anda memiliki percabangan per-pengguna .
Struktur versi bukan pohon , melainkan grafik . Lebih khusus grafik asiklik terarah (DAG, artinya grafik yang tidak memiliki siklus). Anda benar-benar tidak perlu memikirkan spesifik DAG selain setiap komit memiliki satu atau lebih referensi induk (yang berdasarkan komit itu). Jadi grafik berikut ini akan menunjukkan panah antara revisi secara terbalik karena ini.
Contoh penggabungan yang sangat sederhana adalah ini; bayangkan repositori sentral dipanggil origin
dan seorang pengguna, Alice, mengkloning repositori ke mesinnya.
a… b… c…
origin o<---o<---o
^master
|
| clone
v
a… b… c…
alice o<---o<---o
^master
^origin/master
Apa yang terjadi selama klon adalah bahwa setiap revisi disalin ke Alice persis seperti mereka (yang divalidasi oleh hash-id yang dapat diidentifikasi secara unik), dan menandai di mana cabang asal berada.
Alice kemudian mengerjakan repo-nya, melakukan dalam repositori miliknya sendiri dan memutuskan untuk mendorong perubahannya:
a… b… c…
origin o<---o<---o
^ master
"what'll happen after a push?"
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
Solusinya agak sederhana, satu-satunya hal yang origin
perlu dilakukan repositori adalah mengambil semua revisi baru dan memindahkan cabangnya ke revisi terbaru (yang git memanggil "fast-forward"):
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
Kasus penggunaan, yang saya ilustrasikan di atas, bahkan tidak perlu menggabungkan apa pun . Jadi masalahnya sebenarnya bukan dengan algoritma penggabungan karena algoritma penggabungan tiga arah hampir sama antara semua sistem kontrol versi. Masalahnya lebih tentang struktur daripada apa pun .
Jadi, bagaimana kalau Anda menunjukkan contoh yang memiliki penggabungan nyata ?
Memang contoh di atas adalah kasus penggunaan yang sangat sederhana, jadi mari kita lakukan yang lebih memutar meskipun yang lebih umum. Ingat itu origin
dimulai dengan tiga revisi? Nah, orang yang melakukannya, sebut saja dia Bob , telah mengerjakan sendiri dan membuat komit di repositori sendiri:
a… b… c… f…
bob o<---o<---o<---o
^ master
^ origin/master
"can Bob push his changes?"
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
Sekarang Bob tidak bisa mendorong perubahannya langsung ke origin
repositori. Bagaimana sistem mendeteksi ini adalah dengan memeriksa apakah revisi Bob langsung diturunkan dari origin
, yang dalam kasus ini tidak. Setiap upaya untuk mendorong akan menghasilkan sistem mengatakan sesuatu yang mirip dengan " Eh ... aku takut tidak bisa membiarkanmu melakukan itu Bob ."
Jadi Bob harus menarik dan menggabungkan perubahan (dengan git pull
; atau hg pull
dan merge
; atau bzr merge
). Ini adalah proses dua langkah. Bob pertama harus mengambil revisi baru, yang akan menyalinnya dari origin
repositori. Kita sekarang dapat melihat bahwa grafik berbeda:
v master
a… b… c… f…
bob o<---o<---o<---o
^
| d… e…
+----o<---o
^ origin/master
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
Langkah kedua dari proses tarik adalah menggabungkan tip yang berbeda dan membuat komitmen terhadap hasilnya:
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
^ origin/master
Mudah-mudahan penggabungan tidak akan mengalami konflik (jika Anda mengantisipasi mereka, Anda dapat melakukan dua langkah secara manual di git dengan fetch
dan merge
). Yang nanti perlu dilakukan adalah mendorong perubahan itu lagi origin
, yang akan menghasilkan penggabungan cepat karena komit gabungan adalah keturunan langsung dari yang terbaru dalam origin
repositori:
v origin/master
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
v master
a… b… c… f… 1…
origin o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
Ada opsi lain untuk menggabungkan git dan hg, yang disebut rebase , yang akan memindahkan perubahan Bob setelah perubahan terbaru. Karena saya tidak ingin jawaban ini menjadi lebih bertele-tele saya akan membiarkan Anda membaca dokumen git , lincah atau bazaar tentang itu.
Sebagai latihan untuk pembaca, coba gambarkan cara kerjanya dengan pengguna lain yang terlibat. Demikian pula halnya dengan contoh di atas dengan Bob. Penggabungan antar repositori lebih mudah daripada yang Anda pikirkan karena semua revisi / komit dapat diidentifikasi secara unik.
Ada juga masalah pengiriman tambalan antara masing-masing pengembang, yang merupakan masalah besar dalam Subversion yang dimitigasi dalam git, hg dan bzr oleh revisi yang dapat diidentifikasi secara unik. Setelah seseorang menggabungkan perubahannya (yaitu membuat komit gabungan) dan mengirimkannya kepada semua orang di tim untuk dikonsumsi dengan mendorong ke repositori pusat atau mengirim tambalan maka mereka tidak perlu khawatir tentang gabungan tersebut, karena itu sudah terjadi . Martin Fowler menyebut cara ini untuk melakukan integrasi bebas pilih - pilih .
Karena strukturnya berbeda dari Subversion, dengan alih-alih menggunakan DAG, itu memungkinkan percabangan dan penggabungan dilakukan dengan cara yang lebih mudah tidak hanya untuk sistem tetapi juga bagi pengguna.