A4 — Git Temeli: Commit, Branch, Merge, Rebase, Conflict#
"Git bir yedekleme aracı değil, değişimin niçin'ini taşıyan kayıttır."
Blok: A — Sezgi · Süre: ~12 saat · Ön koşul: A1
🎯 Bu modülü bitirdiğinde#
- Sıfırdan bir repo kurar, anlamlı commit'lerle bir geçmiş oluşturursun.
- Branch açar, merge eder ve bir conflict'i elle, güvenle çözersin.
- Merge ile rebase arasındaki farkı ve ne zaman hangisini kullanacağını açıklarsın.
🧠 Niye bu, niye şimdi#
C2'de (CI) ve C3'te (Terraform) her şey Git üzerinden akacak; GitOps'ta (D5) Git tek gerçek kaynak olacak. Bu mekanizmayı önce elle, tek başına yaşamadan otomasyon üzerine kurmak kör uçmaktır. Git'i "sihirli komutlar dizisi" olarak değil, bir commit grafiği (DAG) olarak anladığında, korktuğun her durum (conflict, "kayıp" commit, yanlış branch) sıradan bir grafik işlemine döner.
📖 Nasıl çalışılır#
Gövdeyi oku ve her komutu boş bir test reposunda çalıştır (git init ile bir oyun alanı aç). Git'i ezberleyerek değil, git status ve git log --oneline --graph ile grafiği sürekli görerek öğren. Bu iki komut senin gözündür; her adımdan sonra bak.
📚 Kavram haritası#
| Terim | Bir cümlede |
|---|---|
| Commit | Bir andaki dosya durumunun (snapshot) imzalı kaydı + mesaj + ana(lar) |
| Working directory | Üzerinde çalıştığın gerçek dosyalar |
| Staging (index) | Bir sonraki commit'e girecek değişikliklerin bekleme alanı |
| Branch | Bir commit'e işaret eden, hareket edebilen isim (main, feature/x) |
| HEAD | Şu an nerede olduğun — genelde bir branch'in ucu |
| Merge | İki geçmişi birleştiren commit |
| Rebase | Commit'leri başka bir taban üstüne yeniden yazma |
| Conflict | İki tarafın aynı satırı farklı değiştirmesi; Git karar veremez, sen verirsin |
| Remote | Reponun başka bir kopyası (origin) — GitHub vb. |
1️⃣ Zihinsel model: snapshot'lar ve üç alan#
Git her commit'te dosyaların tam bir fotoğrafını (snapshot) saklar — "farkları" değil (farkları o hesaplar). Her commit'in bir kimliği (hash) ve bir anası vardır; zincirlenince yönlü bir grafik (DAG) oluşur. Bir branch, bu grafikte hareket eden bir etiketten ibarettir.
Bir değişiklik commit olana kadar üç alandan geçer:
git config --global user.name "<AD_SOYAD>"
git config --global user.email "<EPOSTA>" # commit'lere yazarını koyar
git init # boş bir repo yarat (.git dizini oluşur)
git status # nerede ne var: değişen, stage'lenen, takip edilmeyen
git status'u sık sık çalıştır. "Şu an ne durumdayım?" sorusunun cevabı hep oradadır.
2️⃣ Temel döngü: değiştir → stage → commit#
echo "merhaba" > app.txt
git add app.txt # değişikliği staging'e al
git commit -m "app.txt eklendi: ilk sürüm"
git log --oneline # geçmişi tek satırlık özet olarak gör
# a1b2c3d app.txt eklendi: ilk sürüm
git add neden ayrı bir adım? Çünkü hangi değişikliklerin bu commit'e gireceğini sen seçersin. İki dosyayı değiştirip yalnız birini commit'lemek, birbiriyle ilgili değişiklikleri ayrı, anlamlı commit'lere bölmenin yoludur.
İyi commit mesajı — altı ay sonraki sana mektup#
kısa özet (50 karakter, emir kipi: "ekle", "düzelt")
Boş satırdan sonra: NİÇİN yaptığın. Kod "ne"yi zaten söyler;
commit mesajı "niçin"i taşır — bunu başka hiçbir yer taşımaz.
git log, bir yıl sonra "bu satır neden böyle?" sorusunun cevabını taşıyacak tek yerdir. "değişiklik", "fix", "update" gibi mesajlar o cevabı yok eder. Reponun commit disiplini içinCLAUDE.mdve A6 sonrası alışkanlıkların temelini burada kur.
3️⃣ Branch ve merge: paralel çalışma#
Bir branch, ana hattı bozmadan çalışmanı sağlar. İşin bitince ana hatta merge edersin.
git switch -c feature/selamlama # yeni branch aç ve ona geç (eski: git checkout -b)
echo "selam" >> app.txt
git commit -am "selamlama satırı eklendi"
git switch main # ana hatta dön
git merge feature/selamlama # feature'ı main'e birleştir
git log --oneline --graph --all # grafiği gör — dallanma ve birleşme
İki merge biçimi vardır:
| Durum | Ne olur | Sonuç |
|---|---|---|
| Fast-forward | main, feature'dan bu yana hiç ilerlemediyse | Etiket ileri kayar; ekstra commit yok, düz geçmiş |
| Merge commit | Her iki taraf da ilerlediyse | İki ana'sı olan bir birleştirme commit'i doğar |
Branch'i işin bitince sil (etiket gider, commit'ler kalır):
4️⃣ Conflict: Git karar veremez, sen verirsin#
Conflict, iki branch aynı satırı farklı değiştirdiğinde olur. Bu bir hata değil, normaldir — Git hangisinin doğru olduğunu bilemez ve sana sorar.
git merge feature/x
# CONFLICT (content): Merge conflict in app.txt
git status # hangi dosyalar çakıştı
Çakışan dosyada Git işaretçiler bırakır:
Çözüm elle: dosyayı aç, doğru sonucu yaz (üç işaretçi satırını da sil), sonra:
Conflict'ten korkma. İşaretçilerin ne dediğini oku: yukarısı senin tarafın (HEAD), aşağısı gelen taraf. Doğru birleşimi yaz. Karıştıysan
git merge --aborther şeyi merge öncesine geri alır — güvenli çıkış her zaman var.
5️⃣ Rebase: geçmişi yeniden yazmak#
Merge iki geçmişi bir birleştirme commit'iyle bağlar; rebase, commit'lerini başka bir taban üstüne taşıyarak yeniden yazar — sonuç düz, doğrusal bir geçmiş.
git switch feature/x
git rebase main # feature commit'lerini güncel main'in ucuna taşı
# çakışma olursa: çöz → git add → git rebase --continue
| Merge | Rebase | |
|---|---|---|
| Geçmiş | Gerçek (dallanma görünür) | Doğrusal (temiz) |
| Commit hash'leri | Korunur | Değişir (yeniden yazılır) |
| Ne zaman | Paylaşılan branch'leri birleştirirken | Kendi yerel branch'ini güncel tutarken |
🔒 Altın kural: paylaşılan geçmişi rebase etme#
Başkalarının da üzerinde olduğu (push edilmiş) bir branch'i rebase etmek, herkesin geçmişini bozar — hash'ler değişir, herkes kopuk kalır. Kural: rebase'i yalnız henüz kimseyle paylaşmadığın yerel commit'lerde kullan. Paylaşılanı merge et.
6️⃣ Remote — kısaca (C2/D5 için zemin)#
Şimdilik yerel yetiyor; ama C2 ve GitOps için remote'un ne olduğunu tanı:
git clone <URL> # uzak repoyu yerele kopyala (origin ayarlanır)
git push origin <branch> # yerel commit'leri uzağa gönder
git pull # uzaktakileri getir + birleştir (fetch + merge)
origin uzak reponun takma adıdır. C2'de bir push, CI pipeline'ını tetikleyecek; D5'te main'e bir merge, üretime deploy anlamına gelecek. O yüzden temiz geçmiş sadece estetik değil — otomasyonun girdisi.
7️⃣ Geri alma: restore, reset, revert, stash#
Yeni başlayanı en çok korkutan yer "yanlış yaptım, nasıl geri alırım". Üç farklı "geri alma" vardır ve karıştırılırsa veri kaybettirir:
| Komut | Ne yapar | Ne zaman |
|---|---|---|
git restore <dosya> | Çalışma dizinindeki değişikliği at (son commit'e döndür) | Henüz commit'lemediğin bir hatayı silmek |
git restore --staged <dosya> | Staging'den çıkar (değişiklik kalır) | Yanlış add'i geri almak |
git revert <commit> | O commit'i iptal eden yeni bir commit ekler | Paylaşılmış geçmişte güvenli geri alma |
git reset --soft <commit> | HEAD'i geri al, değişiklikleri staging'de bırak | Son commit'leri yeniden düzenlemek (yerel) |
git reset --hard <commit> | HEAD'i geri al, her şeyi sil | ⚠️ Yerel; yanlış kullanınca iş kaybolur |
Altın ayrım:
revertekleyerek geri alır (geçmiş korunur, paylaşımda güvenli).resetsilerek/oynatarak geri alır (geçmişi yeniden yazar, yalnız yerelde). Bir commit çoktan push edildiyserevertkullan,reset --harddeğil.
Geçici olarak kenara koy: stash#
Bir işin ortasındasın ama acil başka bir şey yapman gerekti. Yarım işi commit'lemeden kenara koy:
git stash # çalışma dizinini temizle, değişiklikleri kenara koy
git switch main # acil işi yap
git stash pop # geri dön, kenara koyduğun işi geri getir
.gitignore — neyi takip ETME#
Bazı dosyalar repoya asla girmemeli: sırlar (.env), üretilen çıktılar (node_modules/, __pycache__/, site/), yerel ayarlar. Bunları .gitignore ile dışla:
Bir dosya bir kez commit'lendiyse
.gitignoreonu geri almaz (zaten takipte). Öncegit rm --cached <dosya>ile takipten çıkar, sonra ignore et. Sızan bir sır için bu bile yetmez: geçmişte kalır — anahtarı döndür.
Kayıp değil: reflog#
"Commit'im gitti" neredeyse hiç doğru değildir. git reflog HEAD'in gittiği her yeri tutar; yanlış bir reset'ten bile geri dönebilirsin:
git reflog # HEAD'in tüm geçmişi (son hareketler üstte)
git reset --hard <iyi_hash> # reflog'da gördüğün sağlam noktaya dön
🚫 Anti-pattern tablosu#
| Anti-pattern | Niye kötü | Doğru |
|---|---|---|
| "değişiklik", "fix", "wip" commit mesajları | "Niçin"i yok eder; geçmiş okunmaz olur | Emir kipi özet + gövdede niçin |
| Her şeyi tek dev commit'te | Geri almayı/incelemeyi imkânsızlaştırır | İlgili değişiklikleri ayrı, atomik commit'lere böl |
| Paylaşılan/push'lanmış branch'i rebase etmek | Herkesin geçmişini bozar | Paylaşılanı merge et; rebase yalnız yerelde |
Conflict'te --theirs/--ours körlemesine | Yanlış tarafı seçip veri kaybedersin | İşaretçileri oku, doğru birleşimi elle yaz |
Sırları (parola, .env, anahtar) commit'lemek | Geçmişte kalıcıdır; silmek zordur | .gitignore + sır yönetimi (D3); sızarsa anahtarı döndür |
git push --force (paylaşılan branch'e) | Başkasının işini siler | --force-with-lease ve yalnız kendi branch'inde |
git add . ile her şeyi kör stage'lemek | İstemediğin dosya/sır girer | Ne stage'lediğini git status/git diff --staged ile gör |
| Büyük ikili dosyaları repoya koymak | Repo şişer, klonlama yavaşlar | Artefaktları registry'ye (C2); repo'da yalnız kaynak |
📖 İleri okuma (şimdi değil, sonra)#
| Kaynak | Ne için | Ne zaman |
|---|---|---|
01-Git-Workflow/Trunk-Based-Development.md | Takım workflow'u: kısa ömürlü branch, sık merge | C2'den önce — CI'a girmeden oku |
git help <komut> (örn. git help rebase) | Her komutun resmi, tam referansı | Bir davranışı merak ettiğinde |
🔨 Lab#
👉 labs/build/L04-git/ — (Görev taslağı: sıfırdan repo, iki branch, bilerek bir conflict üret ve çöz; aynı işi bir kez merge, bir kez rebase ile yapıp grafik farkını gör.)
✅ Kabul kriterleri#
Hepsi doğrulanmadan sonraki modüle geçme: - [ ] İki branch açıp aynı satırda bilerek bir conflict ürettin, elle çözdün ve git log --oneline --graph ile sonucu gösterdin. - [ ] Bir branch'i git rebase ile güncel main üstüne taşıdın; geçmişin merge'e kıyasla nasıl değiştiğini (hash'ler) gösterdin. - [ ] "Merge mi rebase mi" kararını, altın kuralı (paylaşılanı rebase etme) da içerecek şekilde bir örnek üzerinde yazdın. - [ ] Bir commit mesajını "özet + niçin" biçiminde yazıp, kötü bir örnekle ("fix") farkını yazılı açıkladın.
🧪 Kendini test et#
git addnedengit commit'ten ayrı bir adım? Bunu bir işine yarar örnekle açıkla.- Senaryo:
git merge feature"CONFLICT" verdi. Panik yok — sırayla ne yaparsın, ve yanlış gittiğini düşünürsen güvenli çıkışın ne? - Tasarım: Ekip arkadaşınla ortak bir
feature/apibranch'i üzerinde çalışıyorsunuz (ikiniz de push ettiniz). Sen onu güncelmainile senkronlamak istiyorsun. Merge mi rebase mi, niçin?
Cevaplar
1. Çünkü **hangi değişikliklerin commit'e gireceğini sen seçersin.** Örnek: bir bug'ı düzeltirken bir de yazım hatası fark ettin. İkisini ayrı commit'lemek istersin: `git add bugfix-dosyası && git commit -m "..."`, sonra `git add typo-dosyası && git commit -m "..."`. Staging bu ayrımı mümkün kılar; commit'ler atomik ve okunur kalır. 2. (a) `git status` — hangi dosyalar çakıştı. (b) Her çakışan dosyayı aç, `<<<<<<<`/`=======`/`>>>>>>>` işaretçilerini oku, doğru birleşimi yaz, işaretçileri sil. (c) `git add🆘 Takıldıysan#
| Belirti | Muhtemel sebep | Ne yap |
|---|---|---|
CONFLICT (content) | İki taraf aynı satırı değiştirdi | İşaretçileri elle çöz → git add → git commit |
| Yanlış branch'te commit'ledim | switch unutuldu | git log'la commit'i gör; git reset --soft HEAD~1 ile commit'i çöz (değişiklik durur), git switch <doğru-branch>'e geç ve tekrar commit'le |
| "Commit'im kayboldu" | Branch/HEAD kaydı ya da reset | git reflog — HEAD'in tüm geçmişi; commit orada durur |
rebase ortasında kaldım | Çakışma çözülmeyi bekliyor | Çöz → git add → git rebase --continue; çıkış: --abort |
push reddedildi (non-fast-forward) | Uzakta senden yeni commit var | Önce git pull (getir+birleştir), sonra push |
| Yanlışlıkla sır commit'ledim | .gitignore yoktu / git add . | Sızan sırrı döndür (invalidate); geçmişten silmek yetmez |
💼 Portfolyo çıktısı#
Temiz bir Git geçmişi ve anlamlı commit mesajları alışkanlığı — sonraki tüm repo çıktılarında (A6, C2, C3, D5) görünür ve incelenir.
⏭️ Sırada#
"İyi bir commit geçmişi, altı ay sonraki sana yazılmış bir mektuptur."