Git’te birleştirme genellikle çok kolaydır. Git, başka bir dalı bir çok defa birleştirmeyi kolaylaştırdığı için; ufak çakışmaları sık sık çözerek, çok uzun ömürlü dallarınızı bile sürekli güncel tutabilir ve böylece en sonda devasa tek bir çakışmayla karşılaşmazsınız.
Ancak, bazen zorlu çatışmalar ortaya çıkabilir. Diğer bazı sürüm kontrol sistemlerinin aksine, Git birleştirme çakışması çözümünde "aşırı akıllı" olmaya çalışmaz. Git’in felsefesi, birleştirme çözümünün net olduğunu belirlemede akıllı olmaktır. Ancak bir çakışma varsa, onu otomatik olarak çözmeye çalışmak konusunda akıllı davranmaz. Bu nedenle, hızla ayrılan iki dalı birleştirmek için çok uzun süre beklerseniz, bazı sorunlarla karşılaşabilirsiniz.
Bu bölümde, bu sorunların neler olabileceğini ve Git’in size bu daha zorlu durumlarla başa çıkmada yardımcı olmak için hangi araçları sunduğunu ele alacağız. Ayrıca, yapabileceğiniz farklı, standart olmayan birleştirmelerin bazılarını ele alacak ve yaptığınız birleştirmelerden nasıl geri adım atılacağını göreceğiz.
Çok karmaşık çatışmalar için bazı temel adımları ch03-git-branching.asc bölümünde ele almış olsak da, Git daha karmaşık çatışmaları çözmenize ve bunlarla daha iyi başa çıkmanıza yardımcı olmak için birkaç araç sağlar.
Öncelikle, mümkünse, çatışma olabilecek birleştirmeyi yapmadan önce çalışma dizininizin temiz olduğundan emin olmaya çalışın.
Geliştirmeye devam ettiğiniz bir çalışmanız varsa, bunu geçici bir dalda katkılayın veya stash
ile saklayın.
Bu burada denediğiniz herhangi bir şeyi geri almanızı sağlar.
Birleştirmeyi yaparken çalışma dizininizde kaydedilmemiş değişiklikleriniz varsa, verdiğimiz ipuçlarından bazıları bu çalışmayı korumanıza yardımcı olabilir.
Şimdi çok basit bir örnek üzerinden ilerleyelim. 'hello world' yazısını yazdıran çok basit bir Ruby dosyamız var.
#! /usr/bin/env ruby
def hello
puts 'hello world'
end
hello()
Repomuzda, whitespace
adında yeni bir dal oluşturuyoruz ve tüm Unix satır sonlarını DOS satır sonlarına çevirerek yolumuza devam ediyoruz. Esasında dosyanın her satırını sadece boşluklarla değiştirmiş olduk.
Ardından da hello world
satırını hello mundo
olarak değiştiriyoruz.
$ git checkout -b whitespace
Switched to a new branch 'whitespace'
$ unix2dos hello.rb
unix2dos: converting file hello.rb to DOS format ...
$ git commit -am 'converted hello.rb to DOS'
[whitespace 3270f76] converted hello.rb to DOS
1 file changed, 7 insertions(+), 7 deletions(-)
$ vim hello.rb
$ git diff -b
diff --git a/hello.rb b/hello.rb
index ac51efd..e85207e 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,7 @@
#! /usr/bin/env ruby
def hello
- puts 'hello world'
+ puts 'hello mundo'^M
end
hello()
$ git commit -am 'hello mundo change'
[whitespace 6d338d2] hello mundo change
1 file changed, 1 insertion(+), 1 deletion(-)
Şimdi master
dalımıza geçiyoruz ve fonksiyon için bazı dokümentasyon belgeleri ekliyoruz.
$ git checkout master
Switched to branch 'master'
$ vim hello.rb
$ git diff
diff --git a/hello.rb b/hello.rb
index ac51efd..36c06c8 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
#! /usr/bin/env ruby
+# prints out a greeting
def hello
puts 'hello world'
end
$ git commit -am 'document the function'
[master bec6336] document the function
1 file changed, 1 insertion(+)
Şimdi whitespace
dalını birleştirmeyi deneriz ve boşluk değişiklikleri nedeniyle çatışma alırız.
$ git merge whitespace
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.
Şimdi birkaç seçeneğimiz var.
Öncelikle, bu durumdan nasıl çıkacağımızı ele alalım.
Belki de çatışmaları beklemiyordunuz ve şu anda durumla uğraşmak istemiyorsunuz; bu durumda git merge --abort
komutu ile birleştirmeyi hemen iptal edebilirsiniz.
$ git status -sb
## master
UU hello.rb
$ git merge --abort
$ git status -sb
## master
git merge --abort
seçeneği, birleştirmeyi çalıştırmadan önceki durumunuza geri çevirmeye çalışır.
Bu işlemi mükemmel bir şekilde yapamayabileceğiz tek durum, çalışma dizinizde birleştirmeyi çalıştırdığınızda kaydedilmemiş veya katkılanmamış değişiklikleriniz olmasıdır. Aksi takdirde sorunsuz çalışması beklenir.
Eğer bir sebeple sıfırdan başlamak istiyorsanız, git reset --hard HEAD
komutunu da çalıştırabilir ve reponun en son katkı işlenmiş durumuna geri dönebilirsiniz.
Unutmayın ki, katkılanmamış çalışmalarınız kaybolur, bu yüzden değişikliklerinizden hiçbirini birini istemediğinizden emin olun.
Bu özel senaryoda, çakışmalar boşluklarla ilgilidir. Örnek basit olduğundan bunu biliyoruz, ancak gerçek durumlarda da çakışmayı incelediğinizde bu oldukça kolay anlaşılır. Çünkü her satır bir taraftan kaldırılır ve diğer tarafta yeniden eklenir. Varsayılan olarak, Git tüm bu satırları değişmiş olarak görür, bu yüzden dosyaları birleştiremez.
Ancak varsayılan birleştirme stratejisi argümanlar alabilir ve bunlardan birkaçı boşluk değişikliklerini doğru şekilde yok saymayla ilgilidir.
Birleştirmede çok sayıda boşluk sorunu olduğunu görürseniz, doğrudan birleştirmeyi iptal edip -Xignore-all-space
veya -Xignore-space-change
ile tekrarlayabilirsiniz.
İlk seçenek, satırları karşılaştırırken boşlukları tamamen yoksayar; ikincisi ise bir veya daha fazla boşluk karakteri dizilerini eşit kabul eder.
$ git merge -Xignore-space-change whitespace
Auto-merging hello.rb
Merge made by the 'recursive' strategy.
hello.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Bu örnekte, gerçek dosya değişiklikleri çatışmıyordu. Dolayısıyla boşluk değişikliklerini yoksaydığımızda, her şey sorunsuz bir şekilde birleşir.
Ekip üyelerinizden birinin ara sıra her şeyi (boşluklardan sekme karakterlerine veya tersi yönde) yeniden biçimlendirmek gibi bir karakteri varsa, bu özellik gerçekten hayat kurtarıcıdır.
Git boşluk ön işlemesini oldukça iyi bir şekilde ele alsa da, Git’in otomatik olarak halledemediği ancak betikle düzeltilebilecek diğer türde değişiklikler olabilir. Örnek olması açısından, Git’in boşluk değişikliğini halledemediğini ve bunu kendimizin düzeltmesi gerektiğini varsayalım.
Aslında yapmamız gereken şey, birleştirmeye çalıştığımız dosyayı, birleştirmeden önce bir dos2unix
programından geçirmektir.
Peki, bunu nasıl yapabiliriz?
Öncelikle, çakışma durumuna geliyoruz. Sonra, dosyanın kendi sürümümüzün, onların (birleştirmek istediğimiz dalın) sürümünün ve ortak sürümün (her iki tarafın da dallandığı yerden) kopyalarını almak istiyoruz. Daha sonra, onların tarafını veya kendi tarafımızı düzeltip sadece bu tek dosya için birleştirmeyi tekrar denemek istiyoruz.
Üç dosya sürümünü almak aslında oldukça kolaydır.
Git, bu sürümlerin hepsini izlemde stages
altında, her birini kendiyle ilişkilendirilmiş numaralara sahip olmak üzede saklar.
1: ortak öncel, 2: sizin sürümünüz, ve 3: birleştirmeyi yaptığınız sürüm olan MERGE_HEAD
'den gelir.
Çakışan dosyanın her bir sürümünü git show
komutunu ve özel bir sözdizimini kullanarak çıkarabilirsiniz.
$ git show :1:hello.rb > hello.common.rb
$ git show :2:hello.rb > hello.ours.rb
$ git show :3:hello.rb > hello.theirs.rb
Daha detaylı bir şekilde ilerlemek isterseniz, ls-files -u
tesisat komutunu kullanarak bu dosyaların her biri için Git bloblarının gerçek SHA-1’lerini alabilirsiniz.
$ git ls-files -u
100755 ac51efdc3df4f4fd328d1a02ad05331d8e2c9111 1 hello.rb
100755 36c06c8752c78d2aff89571132f3bf7841a7b5c3 2 hello.rb
100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3 hello.rb
:1:hello.rb
, bu blobun SHA-1’ini aramak için kullanılan bir kısaltmadır.
Artık çalışma dizinimizde her üç izlem’in de içeriğine sahip olduğumuza göre, boşluk sorununu çözmek için onların tarafını elle düzeltebilir ve az bilinen git merge-file
komutunu kullanarak dosyayı yeniden birleştirebiliriz.
$ dos2unix hello.theirs.rb
dos2unix: converting file hello.theirs.rb to Unix format ...
$ git merge-file -p \
hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb
$ git diff -b
diff --cc hello.rb
index 36c06c8,e85207e..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,8 -1,7 +1,8 @@@
#! /usr/bin/env ruby
+# prints out a greeting
def hello
- puts 'hello world'
+ puts 'hello mundo'
end
hello()
Bu noktada dosyayı güzel bir şekilde birleştirdik.
Aslında bu, boşluk değişikliklerini doğrudan yoksaymak yerine, gerçekte onları birleştirme öncesinde düzelttiği için ignore-space-change
seçeneğinden daha iyi çalışır.
ignore-space-change
birleştirmesinde, DOS satır sonlarına sahip olan satırlar biraz kafa karıştırıcıdır.
Bu katkıyı işlemeden önce, son bir kontrol amacıyla, gerçekte hangi tarafın hangi değişiklikleri yaptığını görmek isterseniz; katkılamak üzere çalışma dizinizde olup birleştirme sonucu olarak katkılayacağınız çalışmayı bu izlemlerdekilerle kıyaslamak için git diff
komutunu kullanabilirsiniz.
Şimdi hepsini deneyelim.
Sonucunuzu birleştirmeden önce dalınızdaki son durumuyla karşılaştırmak, diğer bir deyişle birleştirmenin ne tür değişiklikler getirdiğini görmek için git diff --ours
komutunu çalıştırabilirsiniz.
$ git diff --ours
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index 36c06c8..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -2,7 +2,7 @@
# prints out a greeting
def hello
- puts 'hello world'
+ puts 'hello mundo'
end
hello()
Bu şekilde, dalımızda olup biteni, bu dosyaya bu birleştirmeyle tanıttığımız şeyin, o tek satırı değiştirmek olduğunu kolaylıkla görebiliriz.
Birleştirmenin sonucunun, onların tarafındakinden nasıl farklı olduğunu görmek istiyorsak, git diff --theirs
komutunu çalıştırabiliriz.
Bu ve aşağıdaki örnekte, boşlukları çıkarmak için `-b' kullanmamız gerekiyor çünkü onu temizlenmiş 'hello.theirs.rb' dosyamızla değil, Git’tekiyle karşılaştırıyoruz.
$ git diff --theirs -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index e85207e..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
#! /usr/bin/env ruby
+# prints out a greeting
def hello
puts 'hello mundo'
end
Son olarak, dosyanın her iki taraftan da nasıl değiştiğini git diff --base
ile görebilirsiniz.
$ git diff --base -b
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index ac51efd..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,8 @@
#! /usr/bin/env ruby
+# prints out a greeting
def hello
- puts 'hello world'
+ puts 'hello mundo'
end
hello()
Bu noktada, manuel birleştirmeyi yapmak için oluşturduğumuz, ancak artık ihtiyaç duymadığımız ek dosyaları temizlemek için git clean
komutunu kullanabiliriz.
$ git clean -f
Removing hello.common.rb
Removing hello.ours.rb
Removing hello.theirs.rb
Belki bu aşamada bir nedenle çözümden memnun değilizdir. Ya da belki bir veya her iki tarafı manuel olarak düzenlemek çok da iyi çalışmamıştır ve daha fazla bağlam gerekmektedir.
Örneği biraz değiştirelim. Bu örnekte, her birinde birkaç katkı olan iki uzun ömürlü dalımız var, ancak birleştirildiğinde meşru bir içerik çakışması oluşturuyorlar.
$ git log --graph --oneline --decorate --all
* f1270f7 (HEAD, master) update README
* 9af9d3b add a README
* 694971d update phrase to hola world
| * e3eb223 (mundo) add more tests
| * 7cff591 add testing script
| * c3ffff1 changed text to hello mundo
|/
* b7dcc89 initial hello world code
Şimdi, yalnızca master
dalında bulunan üç benzersiz katkımız var ve diğer üçü de mundo
dalında bulunuyor.
mundo
dalını birleştirmeye çalışırsak, bir çakışma alırız.
$ git merge mundo
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Automatic merge failed; fix conflicts and then commit the result.
Çakışmanın ne olduğunu görmek istiyoruz. Dosyayı açarsak, şuna benzer bir şey göreceğiz:
#! /usr/bin/env ruby
def hello
<<<<<<< HEAD
puts 'hola world'
=======
puts 'hello mundo'
>>>>>>> mundo
end
hello()
Her iki tarafın da bu dosyaya içerik eklemiş, ancak bazı katkılar bu çakışmaya sebep olan dosyayı değiştirmiştir.
Şimdi, bu çatışmayı nasıl düzelteceğiniz tam olarak açık değilse, kullanabileceğiniz birkaç aracı keşfedelim. Belki de bu çatışmayı nasıl çözeceğiniz tam olarak belli değil ve daha fazla bağlama ihtiyacınız var.
Yararlı bir araç, git checkout
komutuna eklenen --conflict
seçeneğidir.
Bu komut dosyayı yeniden kontrol eder ve çakışma işaretlerini değiştirir.
İşaretleri sıfırlamak ve yeniden çözmeyi denemek istiyorsanız, bu faydalı olabilir.
--conflict
seçeneğine diff3
veya merge
(varsayılan) değerlerinden herhangi birini ekleyebilirsiniz.
Eğer diff3
'ü eklerseniz, Git biraz farklı bir çatışma işareti versiyonu kullanır; size sadece ours
(bizimki) ve theirs
(onlarınki) versiyonlarını değil, aynı zamanda daha fazla bağlam için base
versiyonunu da içerir.
$ git checkout --conflict=diff3 hello.rb
Bunu çalıştırdıktan sonra, dosya şuna benzer olacaktır:
#! /usr/bin/env ruby
def hello
<<<<<<< ours
puts 'hola world'
||||||| base
puts 'hello world'
=======
puts 'hello mundo'
>>>>>>> theirs
end
hello()
Eğer bu formatı begendiyseniz, gelecekteki birleştirme çakışmalarında varsayılan olarak ayarlamak için merge.conflictstyle
ayarını diff3
olarak değiştirebilirsiniz.
$ git config --global merge.conflictstyle diff3
git checkout
komutu ayrıca --ours
ve --theirs
seçeneklerini alabilir, bu da bir tarafı seçmek veya diğerini tamamen birleştirmeden önce seçmenin çok hızlı bir yoludur.
Bu, özellikle sadece bir tarafı seçmek istediğiniz ikili (binary) dosya çatışmaları veya başka bir dalda belirli dosyaları birleştirmek istediğiniz durumlar için kullanışlı olabilir; birleştirmeyi yapabilir ve ardından katkıyı işlemeden önce belirli dosyaları bir taraftan veya diğerinden seçebilirsiniz.
Git birleştirme çatışmalarını çözerken yararlı bir başka araç da git log
komutudur.
Bu komut çatışmalara katkıda bulunan faktörleri anlamanıza yardımcı olabilir.
Bazen iki geliştirme hattının aynı kod bölgesine dokunmasının nedenlerini hatırlamak için, biraz geçmişi gözden geçirmek gerçekten yardımcı olabilir.
Bu birleştirmeye dahil olan her iki dalın da içerdiği benzersiz tüm katkıların tam bir listesini almak için, ch07-git-tools.asc sözdizimini kullanabiliriz.
$ git log --oneline --left-right HEAD...MERGE_HEAD
< f1270f7 update README
< 9af9d3b add a README
< 694971d update phrase to hola world
> e3eb223 add more tests
> 7cff591 add testing script
> c3ffff1 changed text to hello mundo
Bu, her katkının hangi geliştirme hattında olduğuyla birlikte, sürece dahil olan toplam altı katkının güzel bir listesidir.
Ancak, bunu çok daha spesifik bir bağlama sahip olması için biraz daha basitleştirebiliriz.
git log
komutuna --merge
seçeneğini eklersek, çakışmada olan bir dosyayı değiştiren katkıları gösterecektir.
$ git log --oneline --left-right --merge
< 694971d update phrase to hola world
> c3ffff1 changed text to hello mundo
Eğer -p
seçeneği ile çalıştırırsanız, çakışmaya neden olan dosyalara ilişkin farklılıkları alırsınız.
Bu, bir şeyin neden çakıştığını anlamanıza ve daha akıllıca nasıl çözeceğinize dair ihtiyacınız olan bağlamı hızlı bir şekilde sağlamak için gerçekten faydalı olabilir.
Git, başarılı olan her birleştirmeyi izleme alır; bu nedenle çakışma durumunda git diff
komutunu çalıştırdığınızda sadece hala çakışma durumunda olanları alırsınız.
Bu, çözmeniz gerekenleri görmek için faydalı olabilir.
Birleştirme çakışmasının hemen ardından git diff
komutunu çalıştırdığınızda, size benzersiz bir formatta bir fark çıktısı verecektir.
$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,11 @@@
#! /usr/bin/env ruby
def hello
++<<<<<<< HEAD
+ puts 'hola world'
++=======
+ puts 'hello mundo'
++>>>>>>> mundo
end
hello()
Bu format Birleşik Fark
olarak adlandırılır ve her satırın yanında iki sütun veriye sahiptir.
İlk sütun, bu satırın ours
(bizim) dalı ve çalışma dizininizdeki dosya arasında farklı olup olmadığını (eklenmiş veya kaldırılmış olarak) gösterir ve ikinci sütun aynı şeyi theirs
(onların) dalı ve çalışma dizininizdeki kopya arasında yapar.
Bu örnekte <<<<<<<
ve >>>>>>>
satırlarının çalışma kopyasında olduğunu ancak birleştirmenin her iki tarafında da olmadığını görebilirsiniz.
Birleştirme aracı bunları nelerin eklenip çıkarıldığını anlamamız için yerleştirdiğinden mantıklıdır, ancak bunları kaldırmamız beklenir.
Çatışmayı çözer ve git diff
komutunu yeniden çalıştırırsak, aynı şeyi göreceğiz, ancak bu sefer biraz daha kullanışlıdır.
$ vim hello.rb
$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
#! /usr/bin/env ruby
def hello
- puts 'hola world'
- puts 'hello mundo'
++ puts 'hola mundo'
end
hello()
Bu, hola world
'un tarafımızda olduğunu ancak çalışma kopyasında olmadığını, hello mundo
'nun onların arafında olduğunu ancak çalışma kopyasında olmadığını ve son olarak hola mundo
'nun her iki tarafta da olmadığını ancak şimdi çalışma kopyasında olduğunu gösterir.
Bu, çözümü katkı olarak işlemeden önce bir gözden geçirmek açısından faydalı olabilir.
Ayrıca, birleştirmenin nasıl çözüldüğünü sonradan görmek için herhangi bir birleştirmenin git log
çıktısından da yararlanabilirsiniz.
Git, bir birleştirme katkısı üzerine git show
komutunu çalıştırdığınızda veya bir git log -p
komutuna (varsayılan olarak yalnızca birleştirme katkısı olmayan yamaları gösterir) --cc
seçeneği eklediğinizde karşınıza bu formatı çıkarır.
$ git log --cc -p -1
commit 14f41939956d80b9e17bb8721354c33f8d5b5a79
Merge: f1270f7 e3eb223
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Sep 19 18:14:49 2014 +0200
Merge branch 'mundo'
Conflicts:
hello.rb
diff --cc hello.rb
index 0399cd5,59727f0..e1d0799
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
#! /usr/bin/env ruby
def hello
- puts 'hola world'
- puts 'hello mundo'
++ puts 'hola mundo'
end
hello()
Artık bir birleştirme katkısı oluşturmayı nasıl yapacağınızı bildiğinize göre, muhtemelen bazen kazara kullanacaksınız. Git ile çalışmanın harika yanlarından biri, hata yapmanın sorun olmadığıdır, çünkü onları düzeltmek mümkündür (ve birçok durumda kolaydır).
Birleştirme komutları da istisna değildir.
Diyelim ki bir konu dalında çalışmaya başladınız, bunu yanlışlıkla master
dalına birleştirdiniz ve şimdi katkı geçmişiniz şöyle görünüyor:
Bu sorunu çözmek için istediğiniz sonuca bağlı olarak iki farklı yaklaşım bulunmaktadır.
Eğer istenmeyen birleştirme katkısı yalnızca yerel repoda varsa; en kolay ve en iyi çözüm, dalları istediğiniz yere işaret edecek şekilde taşımaktır.
Çoğu durumda, yanlış git merge
işleminden sonra git reset --hard HEAD~
komutunu takip ederseniz, bu, dal işaretçilerini aşağıdaki gibi sıfırlayacaktır:
Bir önceki konumuzda reset
komutunu ele aldık (ch07-git-tools.asc
), bu yüzden burada neler olup bittiğini anlamak çok zor olmamalı.
Hemen hatırlatalım: reset --hard
genellikle üç adımdan oluşur:
-
Dalın HEAD’inin işaret ettiği yeri taşı. Bu durumda,
master
'ı birleştirme katkısından önceki konuma (C6
) taşımak istiyoruz. -
İndeksi HEAD’e benzet.
-
Çalışma dizinini indekse benzet.
Bu yaklaşımın dezavantajı, geçmişi yeniden yazmasıdır ve bu, paylaşılan bir repoda sorun olabilir.
Neler olabileceğine dair daha fazla bilgi için ch03-git-branching.asc bölümüne göz atabilirsiniz; kısaca, yeniden yazdığınız katkıları kullanan başkaları da varsa, reset
'ten kaçınmanız gerekebilir.
Bu yaklaşım, birleştirmenin ardından başka bir katkı oluşturulduğu durumlarda da çalışmaz; işaretçileri taşımak bu değişiklikleri kaybetmeye yol açar.
Eğer dal işaretçilerini taşımak sizin işinizi görmeyecekse, Git size mevcut bir katkıdan kaynaklanan tüm değişiklikleri geri alacak yeni bir katkı işleme seçeneği sunar.
Git, bu işlemi revert
(geri alma) olarak adlandırır, ve bu senaryoda, onu şu şekilde çağırırsınız:
$ git revert -m 1 HEAD
[master b1d8379] Revert "Merge branch 'topic'"
-m 1
bayrağı, hangi öncelin mainline
(ana hat) olduğunu ve korunması gerektiğini belirtir.
HEAD
'e birleştirme çağrısında bulunduğunuzda (git merge topic
), yeni katkı iki öncele sahiptir: birinci öncel HEAD
(C6
) ve ikinci öncel birleştirilen dalın ucudur (C4
).
Burada, ikinci öncelin (C4
) birleştirmesiyle gelen tüm değişiklikleri geri almak ama birinci öncelin (C6
) tüm içeriğini de korumak istiyoruz.
Geri alma katkısı sonrasında geçmişimiz şöyle görünür:
Yeni katkı olan ^M
, tam olarak C6
ile aynı içeriğe sahiptir. Bu yüzden buradan itibaren, birleştirilmemiş katkıların halâ HEAD
'in geçmişinde bulunması dışında, sanki birleştirme hiç olmamış gibidir.
topic
'i tekrar master
'a birleştirmeye çalışırsanız Git’in kafası karışır:
$ git merge topic
Already up-to-date.
topic
'te master
'dan ulaşılamayan bir şey yoktur.
Daha fenası, topic
'e iş ekler ve tekrar birleştirirseniz, Git geri alınan birleştirmeden sonraki değişiklikleri getirecektir:
Bu durumda en iyi çözüm, özgün birleştirmeyi geri alıp (çünkü şimdi geri alınan değişiklikleri getirmek istiyorsunuz), ardından yeni bir birleştirme katkısı oluşturmaktır:
$ git revert ^M
[master 09f0126] Revert "Revert "Merge branch 'topic'""
$ git merge topic
Bu örnekte, M
ve ^M
birbirini iptal eder.
^^M
katkısı, C3
ve C4
'ten gelen değişiklikleri etkin bir şekilde birleştirirken, C8
de, C7`den gelen değişiklikleri birleştirir; böylece `topic
tamamen birleştirilmiş olur.
Şimdiye kadar iki dalın normal birleştirilmesini ele aldık, bunlar genellikle birleştirmenin "özyinelemeli" (recursive) stratejisiyle ele alınır. Ancak, dalları birleştirmenin başka yolları da vardır. Hızlı bir şekilde birkaçını ele alalım.
Öncelikle, normal "özyinelemeli" birleştirme moduyla yapabileceğimiz başka bir yararlı işlev daha var.
Zaten bir -X
ile iletilen ignore-all-space
ve ignore-space-change
seçeneklerini gördük, ancak Git’e bir çakışma gördüğünde belli bir tarafı tercih etmesini söyleyebiliriz.
Varsayılan olarak, Git birleştirilmekte olan iki dal arasında bir çakışma gördüğünde; kodunuza birleştirme çakışma işaretçilerini ekler, dosyayı çakışmış olarak işaretler ve sizin çakışmayı çözmenize izin verir.
Eğer manuel olarak çözmenize gerek kalmadan, Git’in belli bir tarafı seçerek çakışmayı otomatik olarak çözmesini tercih ederseniz; merge
komutuna -Xours
veya -Xtheirs
bayraklarını ekleyebilirsiniz.
Eğer Git bunu görürse, çakışma işaretçilerini eklemeyecektir. Birleştirilebilir tüm farkları ise birleştirir. İkili (binary) dosyalar da dahil, çakışan tüm farklarda ise, tamamen belirttiğiniz tarafları seçer.
Eğer önceki "hello world" örneğimize geri dönersek, kendi dalımıza birleştirmenin çatışmalara neden olduğunu görebiliriz.
$ git merge mundo
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.
Ancak -Xours
veya -Xtheirs
ile çalıştırırsak, bu işaretçileri eklemeyecektir.
$ git merge -Xours mundo
Auto-merging hello.rb
Merge made by the 'recursive' strategy.
hello.rb | 2 +-
test.sh | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
create mode 100644 test.sh
Bu durumda, dosyada bir tarafta hello mundo
ve diğer tarafta hola world
olan çatışma işaretçilerini almak yerine, sadece hola world
'ü seçecektir.
Ancak, bu dalda diğer çakışmayan tüm değişiklikler başarıyla birleştirilir.
Bu seçenek ayrıca önceki gördüğümüz git merge-file
komutuna da geçirilebilir, örneğin, bireysel dosya birleştirmeleri için git merge-file --ours
şeklinde.
Eğer bunun gibi bir şey yapmak istiyorsanız ve Git’in diğer taraftaki değişiklikleri bile birleştirmeye çalışmasını istemiyorsanız, daha katı bir seçenek olan ours
birleştirme stratejisi vardır.
Bu, ours
özyinelemeli birleştirme seçeneğinden farklıdır.
Bununla aslında sahte bir birleştirme yapılacaktır. Birleştirmeye çalıştığınız dala bile bakmadan, her iki dalı da öncel kabul ederek yeni bir birleştirme katkısı kaydedecektir. Sadece birleştirmenin sonucunu, mevcut dalınızdaki kodun tamamı olarak kaydeder.
$ git merge -s ours mundo
Merge made by the 'ours' strategy.
$ git diff HEAD HEAD~
$
Görebileceğiniz gibi, bulunduğumuz dal ile birleştirmenin sonucu arasında herhangi bir fark yoktur.
Birleştirme yaparken bunu, Git’i bir dalın zaten birleştirilmiş olduğu şeklinde kandırmak için kullanabilirsiniz.
Örneğin, bir release
dalından yeniden dallandınız, bu dalda bazı çalışmalar yaptınız ve bu işleri bir noktada master
dalınıza geri birleştirmek istiyorsunuz.
Ama bu arada, master
üzerindeki bazı hata düzeltmelerinin release
dalına alınması gerekiyor.
Hata düzeltme (bugfix) dalını release
dalına birleştirebilir ve aynı dalı master
dalınıza da merge -s ours
ile (bugfix
dalı zaten orada olmasına rağmen) birleştirebilirsiniz. Böylece daha sonra release
dalını tekrar birleştirdiğinizde bugfix
yüzünden çakışmalar olmaz.