git merge: branch'ler nasıl birleştirilir ve çakışmalar nasıl çözülür
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ı
| Bayrak | Ne yapar | Ne zaman işe yarar |
|---|---|---|
--no-ff | Fast-forward mümkün olsa bile her zaman merge commit'i oluşturur | Hangi commit'lerin tek bir özellikten geldiğini geçmişte görmek istediğinizde |
--ff-only | Yalnızca ileri sarma ile birleştirir, aksi halde hata verir | Yerel branch'i, istemeden merge commit'i oluşturmadan güncellemek için |
--squash | Branch'in tüm değişikliklerini index'e koyar, ama commit yapmaz ve branch ile bağlantı kurmaz | Bir özelliği tek ve düzenli bir commit'e dönüştürmek istediğinizde |
--no-commit | Birleştirmeyi yapar, ama commit'ten önce durur | Sonucu kontrol etmek ya da commit öncesi testleri çalıştırmak için |
-m "metin" | Merge commit'inin mesajını belirler | Betiklerde ya da editör açılmadan birleştirmede |
--abort | Baş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 tamamlar | Son 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ır | Hangi 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'deykengit merge mainçalıştırmak ya da bir özelliği başka bir özelliğe katmak. Birleştirmeden öncegit branch --show-currentile hangi branch'te olduğunuzu kontrol edin. - Eski bir branch ile birleştirmek. Bir haftadır güncellenmemiş yerel
main'i birleştirmek. Öncegit 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 initile oluşturulmuş, uzak depoda ise zaten bir README var. Birleştirme gerçekten gerekliyse--allow-unrelated-historiesekleyin. - Çakışma işaretlerini commit etmek. Derleme
<<<<<<<yüzünden çöker. Commit'ten öncegit 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 konu “Sıfırdan Güvenilir Geliştiriciye Git” kursunda uygulamalı olarak ayrıntılı işleniyor.
Kursa git