Rehber

git merge: branch'ler nasıl birleştirilir ve çakışmalar nasıl çözülür

Güncellendi: · Skillaria.Top

git merge, başka bir branch'in geçmişini mevcut branch'e katar: mevcut branch ilerlemediyse Git yalnızca işaretçiyi ileri taşır (fast-forward), aksi halde iki ebeveyni olan bir merge commit'i oluşturur. Hedef branch'e geçip git merge branch-adi çalıştırın; çakışma olursa dosyaları düzeltip git add ve git commit yapın ya da git merge --abort ile her şeyi geri alın.

git merge ne yapar

git merge, belirttiğiniz branch'in değişikliklerini bulunduğunuz branch'e yerleştirir. Yön önemlidir: birleştirme «kendinize» yapılır. feature/login'i main'e katmak için önce main'e geçer, sonra birleştirme komutunu çalıştırırsınız.

git switch main
git pull                 # sunucudan güncel main'i al
git merge feature/login

Git kodu satır satır elle kopyalamaz. İki branch'in ortak atasını (merge base) bulur, her iki sürümü bu atayla karşılaştırır ve değişiklikleri birleştirir. İki taraf farklı yerleri değiştirdiyse sonuç otomatik oluşur. Aynı yer değiştirildiyse çakışma çıkar ve kararı siz verirsiniz.

İki mod: fast-forward ve üçlü birleştirme

Fast-forward — birleştirme commit'i gerekmediğinde

Diyelim feature'ı main'den ayırdınız, üç commit yaptınız ve bu sırada main'e kimse bir şey eklemedi. O zaman main'in geçmişi, feature'ın geçmişinin başlangıcıdır. Git, main işaretçisini feature'ın son commit'ine ileri taşır ve yeni bir commit oluşturmaz.

$ git merge feature
Updating 3f2a1c4..9b7e0d2
Fast-forward
 src/auth.js | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

Geçmiş doğrusal kalır, ama «burada bir feature branch'i vardı» bilgisi kaybolur. Bu bazen iyidir, bazen değildir; ayrıntısı aşağıda, --no-ff bölümünde.

Üçlü birleştirme ve merge commit'i

İki branch da ortak atadan sonra ilerlemişse işaretçiyi ileri taşımak mümkün değildir. Git üçlü birleştirme yapar: ortak ata (base), «bizim» taraf (mevcut branch) ve «onların» tarafı (birleştirilen branch). Sonuç, iki ebeveyni olan ayrı bir merge commit'i olarak kaydedilir.

$ git merge feature
Merge made by the 'ort' strategy.
 src/auth.js   | 42 ++++++++++++++++++++
 src/router.js |  3 +-
 2 files changed, 44 insertions(+), 1 deletion(-)

Git, Merge branch 'feature' gibi bir mesajla bir editör açar. Bu mesajı olduğu gibi bırakabilir ya da neden birleştirdiğinizi ekleyebilirsiniz. Ortaya çıkan dallanmayı görmek için:

git log --oneline --graph --all

Birleştirme stratejisi ort, Git 2.34'ten beri varsayılandır; önceden recursive idi. Günlük işte stratejiyi değiştirmeniz gerekmez.

Kullanışlı git merge bayrakları

BayrakNe yaparNe zaman işe yarar
--no-ffFast-forward mümkün olsa bile her zaman merge commit'i oluştururHangi commit'lerin tek bir özellikten geldiğini geçmişte görmek istediğinizde
--ff-onlyYalnızca ileri sarma ile birleştirir, aksi halde hata verirYerel branch'i, istemeden merge commit'i oluşturmadan güncellemek için
--squashBranch'in tüm değişikliklerini index'e koyar, ama commit yapmaz ve branch ile bağlantı kurmazBir özelliği tek ve düzenli bir commit'e dönüştürmek istediğinizde
--no-commitBirleştirmeyi yapar, ama commit'ten önce dururSonucu kontrol etmek ya da commit öncesi testleri çalıştırmak için
-m "metin"Merge commit'inin mesajını belirlerBetiklerde ya da editör açılmadan birleştirmede
--abortBaşlatılmış birleştirmeyi iptal eder ve öncesindeki duruma döndürürÇakışma çok fazla ve baştan başlamak gerektiğinde
--continueÇakışmalar çözüldükten sonra birleştirmeyi tamamlarSon adımda git commit'in alternatifi
-X ours / -X theirsÇakışan parçalarda otomatik olarak bizim ya da onların sürümünü alırHangi sürümün doğru olduğunu kesin biliyorsanız (dikkatli olun!)

--no-ff: özellik sınırlarını korumak

Birçok ekip özellikleri yalnızca --no-ff ile birleştirir. Böylece grafikte her görev ayrı bir «balon» olarak görünür ve tek bir git revert -m 1 <merge-commit> ile geri alınabilir. Fast-forward'da bu sınır yoktur; geri almak için her commit'i ayrı ayrı geri almak gerekir.

--squash: on commit yerine tek commit

git switch main
git merge --squash feature/login
git commit -m "E-posta ve parola ile giriş"

--squash sonrası Git branch'i birleştirilmiş saymaz: git branch -d feature/login «not fully merged» uyarısıyla silmeyi reddeder. Her şeyin yerinde olduğundan emin olunca git branch -D ile silin. Ayrıca squash'tan sonra aynı branch'te çalışmaya devam etmeyin: bir sonraki birleştirme zaten birleşmiş değişiklikleri yeniden getirir ve neredeyse kesin çakışma çıkar.

Birleştirme çakışması ve çözümü

Çakışma bir kaza değil, normal bir çalışma durumudur. Tek anlamı şudur: iki branch aynı satırları değiştirmiş (ya da biri, diğerinin düzenlediği bir dosyayı silmiş) ve Git hangi sürümün doğru olduğunu tahmin etmeyi göze alamıyor.

$ git merge feature/login
Auto-merging src/config.js
CONFLICT (content): Merge conflict in src/config.js
Automatic merge failed; fix conflicts and then commit the result.

1. adım: Neyin çakıştığına bakın

$ git status
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   src/config.js

2. adım: Dosyayı düzenleyin

Çakışan dosyada Git işaretler koyar:

<<<<<<< HEAD
const TIMEOUT = 5000;
=======
const TIMEOUT = 10000;
>>>>>>> feature/login

Üst kısım mevcut branch'iniz (HEAD), alt kısım birleştirilen branch'tir. Doğru olanı bırakın (ya da ikisini birleştirin), üç işaret satırını silin ve dosyayı kaydedin. Sık yapılan hata, dosyanın ortasındaki bir işareti unutmaktır; commit'ten önce git diff --check kalan işaretleri gösterir.

3. adım: Çözümü işaretleyin ve birleştirmeyi tamamlayın

git add src/config.js
git commit          # ya da git merge --continue

Çakışma tamamen tek bir tarafı seçmekle çözülüyorsa dosyayı elle düzenlemenize gerek yok:

git checkout --ours   src/config.js   # mevcut branch'in sürümünü tut
git checkout --theirs src/config.js   # birleştirilen branch'in sürümünü al
git add src/config.js

Editörde çözmek daha kolay

VS Code, JetBrains IDE'leri ve diğer editörler çakışmayı üç sütunda gösterir ve «mevcut / gelen / ikisini de kabul et» düğmeleri sunar. git mergetool ile harici bir araç da bağlayabilirsiniz. Faydalı bir ayar: git config --global merge.conflictStyle zdiff3. Bu, işaretlere ortak atadaki orijinal sürümü de ekler; böylece kimin neyi değiştirdiği anlaşılır.

Vazgeçtiniz — birleştirmeyi iptal edin

git merge --abort

Bu komut çalışma dizinini ve index'i git merge öncesindeki duruma döndürür. Bu yüzden birleştirmeden önce çalışma dizinini temiz tutun: yarım kalan değişiklikleri commit edin ya da git stash ile saklayın; yoksa bunları birleştirme sonuçlarından ayırmak zorlaşır.

Yapılmış bir merge nasıl geri alınır

Merge commit'i henüz sunucuya gönderilmediyse en kolay yol branch'i geriye almaktır:

git reset --hard ORIG_HEAD

Git, birleştirme gibi tehlikeli işlemlerden önce ORIG_HEAD referansını kaydeder; bu, birleştirmeden önceki branch konumudur. --hard commit edilmemiş değişiklikleri siler, bu yüzden önce git status'a bakın.

Birleştirme zaten ortak depodaysa geçmiş yeniden yazılmaz; ters yönde yeni bir commit oluşturulur:

git revert -m 1 a1b2c3d

-m 1 bayrağı, «ana» ebeveynin birinci ebeveyn (birleştirilen branch'in yazıldığı yer) olduğunu söyler. Dikkat: bu revert'ten sonra Git, özelliğin commit'lerini birleştirilmiş sayar. Düzeltilmiş özelliği daha sonra yeniden birleştirmek isterseniz önce revert commit'inin kendisini geri almanız gerekir.

Ekip çalışmasında merge

git pull: fetch artı merge

Sıradan git pull değişiklikleri indirir ve uzak branch'i yerel branch'e birleştirir. Siz ve bir meslektaşınız paralel commit yaptıysanız «Merge branch 'main' of ...» commit'i oluşur. Bu tür teknik birleştirmeleri istemiyorsanız git config --global pull.ff only ayarlayın (ileri sarma mümkün değilse pull durur ve kararı siz verirsiniz) ya da git pull --rebase kullanın.

Pull request ve Merge düğmesi

GitHub ve GitLab'de birleştirme genellikle pull/merge request üzerinden yapılır. «Create a merge commit» düğmesi git merge --no-ff'a, «Squash and merge» birleştirme sırasında sıkıştırmaya, «Rebase and merge» ise rebase'e karşılık gelir. Hangi modun seçileceği ekibin anlaşmasıdır; mekanizma konsoldakiyle aynıdır.

Kendi branch'inize main'i katın

Uzun ömürlü bir branch main'den uzaklaşır ve çakışmalar birikir. Ana branch'i düzenli olarak kendi branch'inize alın:

git switch feature/login
git fetch origin
git merge origin/main

Böylece çakışmalar çıktıkça küçük parçalar hâlinde çözülür ve son birleştirme main'e pürüzsüz geçer.

Sık yapılan hatalar

  • Yanlış yöne birleştirmek. main'deyken git merge main çalıştırmak ya da bir özelliği başka bir özelliğe katmak. Birleştirmeden önce git branch --show-current ile hangi branch'te olduğunuzu kontrol edin.
  • Eski bir branch ile birleştirmek. Bir haftadır güncellenmemiş yerel main'i birleştirmek. Önce git pull, sonra merge.
  • «Your local changes would be overwritten by merge». Çalışma dizininde aynı dosyalarda commit edilmemiş değişiklikler var. Commit edin ya da stash'e alın.
  • «refusing to merge unrelated histories». Branch'lerin ortak atası yok — örneğin yerel depo git init ile oluşturulmuş, uzak depoda ise zaten bir README var. Birleştirme gerçekten gerekliyse --allow-unrelated-histories ekleyin.
  • Çakışma işaretlerini commit etmek. Derleme <<<<<<< yüzünden çöker. Commit'ten önce git diff --check çalıştırın ve testleri koşturun.

Kısaca

git merge branch'leri birleştirmenin güvenli bir yoludur: mevcut commit'leri yeniden yazmaz, yalnızca yeni bir commit ekler ya da işaretçiyi ileri taşır. Mevcut branch'e birleştirin, çalışma dizinini temiz tutun, özellik sınırları önemliyse --no-ff, tek commit istiyorsanız --squash kullanın. Çakışmalar üç adımda çözülür: dosyayı düzelt, git add, git commit; git merge --abort ise sizi her zaman başlangıç noktasına döndürür. Branch birleştirme ve çakışma çözümü Git kursunda uygulamalı olarak çalışılır.

Sık sorulan sorular

git merge ile git pull arasındaki fark nedir?

git pull önce sunucudan değişiklikleri indirir (git fetch) ve ardından uzak branch'i git merge ile, ayarlıysa rebase ile mevcut branch'e birleştirir. git merge ise yalnızca yerelde bulunanla çalışır.

Birden çok branch'i aynı anda birleştirebilir miyim?

Evet, git merge feature-a feature-b octopus birleştirmesi yapar ve birden çok ebeveyni olan bir commit oluşturur; ama yalnızca çakışma yoksa. Pratikte branch'ler genellikle tek tek birleştirilir.

Hangi branch'lerin mevcut branch'e zaten birleştirildiğini nasıl görürüm?

git branch --merged tamamen birleşmiş branch'leri, git branch --no-merged henüz birleşmemiş olanları gösterir. Birleşmiş branch'ler git branch -d ile güvenle silinebilir.

Birleştirmenin ne getireceğini önceden nasıl görebilirim?

main..feature yazarak main'de olmayan commit'leri, git diff main...feature ile de branch'in ortak atasına göre değişikliklerini görebilirsiniz.

Merge sonrası ORIG_HEAD nedir?

Git'in birleştirmeden önce kaydettiği referanstır ve branch'in merge öncesindeki konumunu gösterir. Sunucuya gönderilmemiş yerel bir birleştirmeyi git reset --hard ORIG_HEAD ile geri alabilirsiniz.

Bu konuyu videoyla öğrenin

Bu konu “Sıfırdan Güvenilir Geliştiriciye Git” kursunda uygulamalı olarak ayrıntılı işleniyor.

Kursa git