Bir DVCS'de birleştirmenin neden Subversion'dan daha iyi olduğu iddiası büyük ölçüde Subversion'da bir süre önce dallanma ve birleştirmenin nasıl çalıştığına dayanıyordu. Öncesinde Subversion 1.5.0 Eğer birleştirilmiş gerekiyordu revizyonlar aralığı belirtmek zorunda birleştirme istediğinde böylece dalları birleştirildiğini zaman hakkında herhangi bir bilgi depolamak vermedi.
Peki Subversion birleşmeleri neden berbat oldu ?
Bu örneği düşünün:
1 2 4 6 8
trunk o-->o-->o---->o---->o
\
\ 3 5 7
b1 +->o---->o---->o
B1'in değişikliklerini bagajda birleştirmek istediğimizde , bagajı teslim alınmış bir klasör üzerinde dururken aşağıdaki komutu veririz:
svn merge -r 2:7 {link to branch b1}
… Bu değişiklikleri b1
yerel çalışma dizininizde birleştirmeye çalışacaktır . Ve sonra herhangi bir çakışmayı çözüp sonucu test ettikten sonra değişiklikleri taahhüt edersiniz. Düzeltme ağacı taahhüt ettiğinizde şöyle görünecektir:
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
Bununla birlikte, sürüm ağacı büyüdüğünde, revizyon aralıklarını belirtmenin bu yolu, alt sürümün ne zaman ve hangi revizyonların bir araya getirildiği hakkında meta verisi olmadığından hızla elden çıkar. Sonra ne olacağını düşünün:
12 14
trunk …-->o-------->o
"Okay, so when did we merge last time?"
13 15
b1 …----->o-------->o
Bu büyük ölçüde Subversion'un sahip olduğu havuz tasarımının bir sorunudur, bir şube oluşturmak için depoda bagajın bir kopyasını barındıracak yeni bir sanal dizin oluşturmanız gerekir, ancak ne zaman ve ne hakkında herhangi bir bilgi depolamaz. işler yeniden birleşti. Bu, zaman zaman kötü birleşme çatışmalarına yol açacaktır. Daha da kötüsü, Subversion'un varsayılan olarak iki yönlü birleştirme kullanmasıdır;
Bu Subversion'ı hafifletmek için artık şube ve birleştirme için meta veriler saklanıyor. Bu tüm problemleri çözecek değil mi?
Ve bu arada, Subversion hala berbat ...
Subversiyon gibi merkezi bir sistemde sanal dizinler emilir. Neden? Çünkü herkes onları görme hakkına sahiptir… deneysel çöpleri bile. Denemek istiyorsanız dallanma iyidir ama herkesin ve teyzelerinin denemesini görmek istemezsiniz . Bu ciddi bilişsel gürültüdür. Ne kadar çok dal eklerseniz o kadar çok saçmalık görürsünüz.
Bir depoda ne kadar çok kamu şubeniz varsa, tüm farklı şubeleri takip etmek o kadar zor olacaktır. Yani sorularınız, dalın hala gelişmekte olup olmadığı veya herhangi bir merkezi sürüm kontrol sisteminde söylemesi zor olan gerçekten ölü olup olmadığıdır.
Çoğu zaman, gördüğüm kadarıyla, bir organizasyon varsayılan olarak büyük bir şube kullanacaktır. Bu bir utanç çünkü test ve sürüm sürümlerini takip etmek zor olacak ve dallanmadan başka ne gelirse.
Peki Git, Mercurial ve Bazaar gibi DVCS neden dallanma ve birleşme sırasında Subversion'dan daha iyi?
Bunun çok basit bir nedeni var: dallanma birinci sınıf bir kavram . Orada hiçbir sanal dizinleri tasarım ve dalları tarafından basitçe depolarının senkronizasyonu (yani çalışmak için bu tür olması gerekir DVCS sert nesneler vardır itme ve çekme ).
Bir DVCS ile çalışırken yaptığınız ilk şey depoları (git clone
, hg clone
ve bzr branch
) klonlamaktır . Klonlama, kavramsal olarak sürüm kontrolünde bir dal oluşturmakla aynı şeydir. Bazıları bu çatallama veya dallanma olarak adlandırılır (ikincisi genellikle birlikte konumlandırılmış dallara atıfta bulunmak için kullanılır), ancak aynı şeydir. Her kullanıcı kendi havuzunu çalıştırır, bu da kullanıcı başına dallanmaya devam ettiğiniz anlamına gelir .
Sürüm yapısı bir ağaç değil, bir grafiktir . Daha spesifik olarak yönlendirilmiş bir asiklik grafik (DAG, yani herhangi bir döngüsü olmayan bir grafik). Her bir taahhüdün bir veya daha fazla üst referansı (taahhüdün dayandığı temel) dışında bir DAG'ın özelliklerine odaklanmanız gerekmez. Bu nedenle, aşağıdaki grafikler revizyonlar arasındaki okları bu nedenle tersine gösterecektir.
Birleşmenin çok basit bir örneği bu olacaktır; denilen merkezi bir deponun origin
ve deposu makinesine klonlayan bir kullanıcı olan Alice'i hayal edin .
a… b… c…
origin o<---o<---o
^master
|
| clone
v
a… b… c…
alice o<---o<---o
^master
^origin/master
Bir klon sırasında olan her revizyonun Alice'e olduğu gibi kopyalanması (benzersiz olarak tanımlanabilen hash-id'lerle doğrulanır) ve menşe dallarının nerede olduğunu işaretler.
Alice daha sonra kendi deposunda çalışarak repo üzerinde çalışır ve değişikliklerini zorlamaya karar verir:
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
Çözüm oldukça basittir, origin
deponun yapması gereken tek şey tüm yeni revizyonları almak ve dalını en yeni revizyona (git "hızlı ileri" olarak adlandırır) taşımaktır:
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
Yukarıda açıkladığım kullanım senaryosunun hiçbir şeyi birleştirmesi bile gerekmiyor . Üç yollu birleştirme algoritması, tüm sürüm kontrol sistemleri arasında hemen hemen aynı olduğundan, bu sorun gerçekten birleştirme algoritmalarıyla ilgili değildir. Mesele, her şeyden çok yapı ile ilgilidir .
Peki bana gerçek bir birleşimi olan bir örnek göstermeye ne dersin ?
Kuşkusuz yukarıdaki örnek çok basit bir kullanım örneğidir, bu yüzden daha yaygın olsa da çok daha bükülmüş bir şey yapalım. Bunun origin
üç revizyonla başladığını hatırlıyor musunuz? Onları yapan, ona Bob diyelim , kendi başına çalışıyor ve kendi deposunda bir taahhütte bulundu:
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
Şimdi Bob değişikliklerini doğrudan origin
depoya aktaramaz. Sistemin bunu nasıl algıladığı, Bob'un revizyonlarının doğrudan origin
'' dan çıkıp çıkmadığını kontrol etmektir; Herhangi bir itme teşebbüsü sistemin " Ah ... korkarım Bob'u yapmana izin veremeyeceğini " söyleyen bir şeyle sonuçlanır .
(Git en Bob in-çekin ve sonra değişiklikleri birleştirmek zorundadır Yani pull
; ya hg en pull
ve merge
ya bzr en; merge
). Bu iki aşamalı bir süreçtir. Önce Bob, origin
depodan olduğu gibi kopyalayacak yeni düzeltmeleri getirmelidir . Şimdi grafiğin ayrıştığını görebiliyoruz:
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
Çekme işleminin ikinci adımı, sapma uçlarını birleştirmek ve sonucu taahhüt etmektir:
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
^ origin/master
İnşallah birleşme çatışmalara girmez (eğer onları tahmin ederseniz iki adımı manuel olarak fetch
ve ile manuel olarak yapabilirsiniz merge
). Daha sonra yapılması gereken, bu değişiklikleri tekrar zorlamaktır; bu origin
, birleştirme taahhüdü origin
depodaki en sonun doğrudan torunu olduğundan hızlı ileri bir birleşmeye neden olur :
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<--+
Git ve hg'de rebase adı verilen ve Bob'un değişikliklerini en yeni değişikliklerden sonra değiştirecek başka bir seçenek var. Bu cevabın daha ayrıntılı olmasını istemediğim için bunun yerine git , mercurial veya çarşı belgelerini okumanıza izin vereceğim .
Okuyucu için bir alıştırma olarak, ilgili başka bir kullanıcıyla nasıl çalışacağını çizmeyi deneyin. Benzer şekilde Bob ile yukarıdaki örnek olarak yapılır. Depolar arasında birleştirmek düşündüğünüzden daha kolaydır çünkü tüm düzeltmeler / taahhütler benzersiz bir şekilde tanımlanabilir.
Her geliştirici arasında yama gönderme sorunu da var, bu Subversion'da git, hg ve bzr'de benzersiz bir şekilde tanımlanabilir revizyonlarla hafifletilen büyük bir sorundu. Birisi değişikliklerini birleştirdikten (yani birleştirme taahhüdü yaptığında) ve ekipteki herkesin merkezi bir depoya iterek veya yamalar göndererek göndermesi için gönderdikten sonra, birleştirme konusunda endişelenmek zorunda kalmazlar, çünkü zaten oldu . Martin Fowler bu şekilde karışık entegrasyon olarak adlandırıyor .
Yapı Subversion'dan farklı olduğu için, bunun yerine bir DAG kullanılarak dallanma ve birleştirmenin sadece sistem için değil kullanıcı için de daha kolay bir şekilde yapılmasını sağlar.