Git Merge ile Dalları Birleştirme ve Çakışma Çözümü

Bir merge işlemi sırasında terminalin ortasında kalmak, özellikle ilk kez yaşıyorsanız, oldukça sinir bozucu bir deneyim olabilir. Çakışmalar, korkutucu görünen ama aslında Git’in size “burada iki farklı karar var, hangisi doğru?” diye sorduğu anlar. Bu yazıda merge mekanizmasını, dalları nasıl birleştireceğinizi ve çakışmaları nasıl çözeceğinizi gerçek dünya senaryolarıyla ele alacağız.

Git Merge Nedir ve Neden Kullanırız?

Git’te bir özellik geliştirmek için yeni bir dal açarsınız, işiniz bitince bu dalı ana dalla birleştirmeniz gerekir. Bu birleştirme işlemine merge diyoruz. Merge, iki dalın commit geçmişini birleştirerek ortak bir noktada buluşturmayı sağlar.

Peki neden doğrudan main üzerinde çalışmıyoruz? Çünkü production’a gidecek kodun stabil kalması gerekiyor. Özellik dalları, hata düzeltme dalları ve release dalları bu ayrımı korumamızı sağlıyor. Birden fazla geliştirici aynı projede çalışıyorsa bu izolasyon hayat kurtarır.

Temel Merge Senaryoları

Fast-Forward Merge

En basit merge türü budur. Ana dalda hiçbir değişiklik olmamışken özellik dalınızı birleştirdiğinizde Git, pointer’ı sadece öne taşır. Yeni bir commit oluşturmaz, geçmişi temiz tutar.

# Ana dala geçiş
git checkout main

# Özellik dalını fast-forward ile birleştir
git merge feature/user-login

# Sonucu kontrol et
git log --oneline --graph

Bunu şöyle düşünün: Bir trene bindiniz, tren durağa geldi, siz de durağa yürüdünüz. İki yol birleşmedi, siz sadece ilerlediniz.

Ancak büyük ekiplerde bu senaryo nadiren yaşanır. Ana dal sürekli değiştiği için çoğu zaman gerçek bir birleştirme yapmak zorunda kalırsınız.

Three-Way Merge

Bu merge türünde Git, üç commit’e bakar: iki dalın ucu ve ortak ata commit. Bu üç noktayı karşılaştırarak yeni bir merge commit oluşturur.

# feature dalında çalışıyorsunuz, main güncellendi
git checkout main
git pull origin main

# Şimdi feature dalınızı birleştirin
git merge feature/payment-module

# Merge commit mesajını özelleştirmek için
git merge --no-ff feature/payment-module -m "feat: ödeme modülü ana dalla birleştirildi"

–no-ff parametresi önemli bir seçenek. “No fast-forward” anlamına gelir ve fast-forward mümkün olsa bile zorunlu olarak bir merge commit oluşturur. Bu sayede hangi commitlerin hangi özellik dalından geldiğini geçmişte net görebilirsiniz. Büyük projelerde bu görünürlük çok değerli.

Squash Merge

Özellik dalındaki tüm commit’leri tek bir commit’e sıkıştırarak birleştirme yöntemi. Geçmişi temiz tutmak isteyenler için idealdir.

# Squash ile birleştir
git merge --squash feature/refactor-api

# Değişiklikler staged halde bekliyor, commit'le
git commit -m "refactor: API katmanı yeniden düzenlendi"

# Feature dalını sil
git branch -d feature/refactor-api

Squash merge’ü dikkatli kullanın. Eğer özellik dalındaki commit geçmişi gelecekte araştırmanız gereken bilgiler içeriyorsa, bu geçmişi kaybedersiniz.

Çakışma (Conflict) Nedir?

İki farklı dal aynı dosyanın aynı satırını farklı şekilde değiştirdiğinde Git hangisinin doğru olduğuna karar veremez. İşte bu noktada size bırakır. Buna merge conflict diyoruz.

Çakışma sadece aynı satırı değiştirmekten kaynaklanmaz. Bir dalda bir dosya silinirken diğer dalda aynı dosya değiştirildiğinde de çakışma çıkar. Bir dalda klasör yapısı değiştirilirken diğer dalda aynı konumdaki dosya güncellendiğinde de benzer durumlar yaşanabilir.

Çakışma Çözümü: Adım Adım

Gerçek bir senaryo üzerinden gidelim. main dalında config.py dosyası güncellendi, sizin feature/cache-layer dalınızda da aynı dosya değişti.

git checkout main
git merge feature/cache-layer

Terminal şunu söyler:

Auto-merging config.py
CONFLICT (content): Merge conflict in config.py
Automatic merge failed; fix conflicts and then commit the result.

Panik yapmayın. Şu anda merge yarı yolda durmuş durumda. Önce durumu anlayalım:

# Hangi dosyalarda çakışma var?
git status

# Çakışan dosyaları daha detaylı gör
git diff --name-only --diff-filter=U

config.py dosyasını açtığınızda şuna benzer bir şey görürsünüz:

<<<<<<< HEAD
DATABASE_TIMEOUT = 30
CACHE_ENABLED = False
=======
DATABASE_TIMEOUT = 60
CACHE_ENABLED = True
CACHE_TTL = 300
>>>>>>> feature/cache-layer

Bu işaretler şu anlama gelir:

  • <<<<<<< HEAD: Şu anki dalınızdaki (main) versiyon buradan başlar
  • =======: Ayraç, iki versiyon burada ayrışır
  • >>>>>>> feature/cache-layer: Birleştirmeye çalıştığınız dalın versiyonu buraya kadar

Bu işaretleri, aralarındaki kodu doğru hale getirerek temizlemeniz gerekiyor. Doğru versiyon şöyle olabilir:

DATABASE_TIMEOUT = 60
CACHE_ENABLED = True
CACHE_TTL = 300

Dosyayı düzenlip kaydettikten sonra:

# Çözülen dosyayı işaretle
git add config.py

# Merge'ü tamamla
git commit

Git otomatik olarak “Merge branch ‘feature/cache-layer'” şeklinde bir commit mesajı önerir. İstersen değiştirebilirsin.

Merge Araçları ile Çakışma Çözümü

Manuel düzenleme bazen karışık olabilir, özellikle büyük dosyalarda. Git’in yerleşik merge tool mekanizması devreye giriyor.

# Yapılandırılmış merge aracını aç
git mergetool

# Belirli bir araç kullan
git mergetool --tool=vimdiff

# VS Code kullanıyorsanız
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
git mergetool

Popüler merge araçları arasında vimdiff, meld, kdiff3 ve VS Code’un yerleşik merge editörü sayılabilir. VS Code özellikle “Accept Current Change”, “Accept Incoming Change”, “Accept Both Changes” butonlarıyla işi kolaylaştırıyor.

Merge’ü Geri Almak

Bazen merge başlatırsınız ama çakışmalar sandığınızdan karmaşık çıkar. Ya da merge tamamlandıktan sonra bir hata fark edersiniz. Her iki durumda da çözüm var.

# Merge ortadayken iptal et
git merge --abort

# Merge tamamlandıktan sonra geri al (merge commit henüz push edilmediyse)
git reset --hard HEAD~1

# Merge commit push edildiyse, revert ile geri al
git revert -m 1 HEAD

git revert -m 1 HEAD komutundaki -m 1 parametresi “mainline” anlamına geliyor. Merge commit’in birinci ebeveynini (genellikle main dalı) referans al demek. Bu komut merge’ü geri alan yeni bir commit oluşturur, tarihi değiştirmez.

Rebase mi, Merge mi?

Bu soruyu sormadan geçmek olmaz. Her ikisi de dalları birleştirmek için kullanılır ama çalışma mantıkları farklıdır.

Merge, iki dalın geçmişini koruyarak birleştirir ve yeni bir commit noktası oluşturur. Rebase ise commit’lerinizi alıp hedef dalın ucuna yeniden uygular, sanki o dalda başından beri çalışıyormuşsunuz gibi.

# Rebase yaklaşımı
git checkout feature/user-profile
git rebase main

# Çakışma çıkarsa, çöz sonra:
git add .
git rebase --continue

# Rebase'i iptal etmek için:
git rebase --abort

Genel kural şu: Paylaşılan dallarda rebase yapmayın. Eğer başkalarının da kullandığı bir dalın geçmişini rebase ile değiştirirseniz, o kişiler için ciddi sorunlar çıkartırsınız. Feature dallarınızda main’i rebase etmek makul, ama main’i rebase etmek tehlikeli.

Gerçek Dünya Senaryosu: Ekip Ortamında Merge Akışı

Beş kişilik bir ekipte çalışıyorsunuz. Her geliştirici ayrı bir feature dalında çalışıyor ve sprint sonunda main’e merge ediliyor. İşte tipik bir akış:

# Sabah işe başlangıç: Main'i güncelle
git checkout main
git pull origin main

# Kendi dalına geç
git checkout feature/invoice-generation

# Varsa main'deki değişiklikleri dalına al
git merge main

# Çalış, commit yap...
git add src/invoice/
git commit -m "feat: PDF çıktısı eklendi"

# Pull request açmadan önce dalını push et
git push origin feature/invoice-generation

Bu noktada ekip arkadaşlarınız pull request’i inceler. Merge edilmeden önce:

# Main tekrar güncellenmiş olabilir, dalı senkronize et
git fetch origin
git merge origin/main

# Çakışma varsa çöz
# Sonra push et
git push origin feature/invoice-generation

Bazı ekipler main yerine develop adında bir entegrasyon dalı kullanır. Özellik dalları önce develop’a merge edilir, test edilir, sonra develop main’e merge edilir. Bu yaklaşım production’ı daha iyi korur.

Merge Stratejileri

Git’in birden fazla merge stratejisi var. Çoğu zaman bunu düşünmenize gerek kalmaz ama bilinç dışı çalışmaması için faydalı:

ort: Git 2.33 itibarıyla varsayılan strateji. Önceki recursive‘in yerini aldı, daha hızlı ve güvenilir.

recursive: Eski varsayılan strateji. Hala destekleniyor.

ours: Çakışmalarda her zaman bizim versiyonumuzu tercih eder, karşı tarafı görmezden gelir.

octopus: İkiden fazla dalı aynı anda birleştirmek için. Ama çakışma durumunda çalışmaz.

# Strateji seçerek merge
git merge -s ours feature/deprecated-module

# Strateji seçeneği ile
git merge -X theirs feature/config-update

-X theirs seçeneği çakışma durumunda karşı tarafın değişikliklerini otomatik kabul eder. -X ours ise bizimkileri. Dikkatli kullanın, düşünmeden çalıştırırsanız bazı değişiklikleri sessizce kaybedebilirsiniz.

Merge Geçmişini Okumak

Merge işlemlerinden sonra commit geçmişiniz karmaşık görünebilir. Onu anlamlı hale getirmek için:

# Dal grafiğini görselleştir
git log --oneline --graph --all

# Belirli bir merge commit'in içeriğine bak
git show <merge-commit-hash>

# Hangi branch'in hangi commit'i getirdiğini gör
git log --merges --oneline

# İki commit arasındaki farkı gör
git diff main..feature/user-auth

git log --graph çıktısı ilk bakışta ASCII sanatı gibi görünebilir ama alışınca çok bilgi verici. Hangi özellik ne zaman merge edilmiş, paralel geliştirmeler nasıl ilerlemiş, hepsi orada.

Merge Öncesi Kontrol Listesi

Yıllarca ekip ortamında çalışmış biri olarak şunu söyleyebilirim: Merge öncesi bir kontrol rutini edinmek, “neden bu kodu merge ettik?” sorusunu sormamak anlamına gelir.

# Dalın güncel olduğundan emin ol
git fetch origin
git status

# Main ile arandaki farkı gör
git log main..feature/your-branch --oneline

# Nelerin değiştiğini gör
git diff main...feature/your-branch

# Test çalıştır (projeye göre değişir)
npm test
# ya da
pytest
# ya da
go test ./...

# CI geçtikten sonra merge et
git checkout main
git merge --no-ff feature/your-branch

Testleri merge öncesi çalıştırma alışkanlığı kulağa basit gelir ama ekiplerin çoğu bunu atlar. “Zaten CI çalıştırıyor” denir, ama yerel testler lokal bağımlılık sorunlarını, ortam farklılıklarını erkenden yakalar.

Büyük Dosya Çakışmalarında Yardımcı Teknikler

Bazen çakışma onlarca satıra yayılır. Özellikle package-lock.json veya yarn.lock gibi otomatik oluşturulan dosyalarda bu çok yaygındır.

# package-lock.json çakışmasında genellikle şunu yapın:
git checkout --theirs package-lock.json
npm install

# Ya da tamamen yeniden oluştur
git checkout --ours package-lock.json
npm install
git add package-lock.json

Otomatik oluşturulan dosyalarda –theirs veya –ours kullanmak güvenlidir çünkü bu dosyaları siz değil, araçlar oluşturur. Asıl kaynağa (package.json) bakın, o dosyada çakışma yoksa lock dosyasını yeniden oluşturmak en temiz yol.

Büyük refactor çakışmalarında ise farklı bir yaklaşım işe yarar:

# Çakışan dosyaları listele
git diff --name-only --diff-filter=U

# Bir dosyada hangi kısımlar çakışıyor?
grep -n "<<<<<<" src/components/Dashboard.jsx

# Toplam çakışma sayısı
grep -c "<<<<<<" src/components/Dashboard.jsx

Merge Hook’ları ile Otomatikleştirme

Git hook’ları, merge öncesi ve sonrası otomatik kontroller yapmanızı sağlar.

# .git/hooks/pre-merge-commit dosyası oluştur
cat > .git/hooks/pre-merge-commit << 'EOF'
#!/bin/bash
echo "Merge öncesi linting çalışıyor..."
npm run lint
if [ $? -ne 0 ]; then
  echo "Lint hataları var, merge iptal edildi."
  exit 1
fi
EOF

chmod +x .git/hooks/pre-merge-commit

Bu hook her merge commit’inden önce lint çalıştırır. Hata varsa merge’ü durdurur. Ekip ortamında bu tür otomasyonlar kod kalitesini korur.

Sonuç

Merge işlemi Git’in en çok yanlış anlaşılan konularından biri. Çakışma gördüğünde panikleyip git reset --hard çekmek değil, sakin kalıp ne olduğunu anlamak gerekiyor. Git size her zaman geri dönüş yolu bırakır: git merge --abort, git reset, git reflog. Bunlar sigorta ağınız.

Pratik olarak şunu öneririm: Kendi test reponuzda kasıtlı olarak çakışma yaratın ve farklı yöntemlerle çözmeyi deneyin. Önce manuel, sonra mergetool ile, sonra --theirs/--ours ile. Bu kas belleği geliştirdiğinizde, production’da gerçek bir çakışmayla karşılaştığınızda soğukkanlılıkla çözüm üretebilirsiniz.

Dalları temiz tutun, commit mesajlarınızı anlamlı yazın ve merge öncesi her zaman güncel olduğunuzu kontrol edin. Geri kalanı zaman içinde oturur.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir