← Kursa Dön
📄 Text · 30 min

Rebase Nedir?

Giriş — Tarihçeyi Yeniden Yazmak

Git'in en güçlü ve en tartışmalı özelliğine hoş geldin: rebase. Bu komut, commit tarihçesini yeniden yazmana olanak tanır. Doğru kullanıldığında tarihçeni tertemiz yapar; yanlış kullanıldığında ekip arkadaşlarının bir günlük çalışmasını çöpe atabilir.

Merge'ü zaten biliyorsun — iki branch'i birleştirir, bir merge commit oluşturur. Rebase ise farklı bir yaklaşım sunar: "Branch'imi sanki en baştan bu noktadan açmışım gibi yeniden uygula." Tarihçe lineer, temiz ve okunaklı olur.

Bu derste hem temel rebase'i hem de çok güçlü olan interactive rebase'i detaylıca öğreneceksin.


Merge vs Rebase — Temel Fark

Analoji — İki Farklı Hikaye Anlatma Biçimi

Bir arkadaşın sana hafta sonunu anlatıyor. İki farklı anlatım tarzı düşün:

Merge tarzı anlatım (kronolojik birleştirme): "Cumartesi sabah kahvaltı yaptım. Bu arada sen film izlemişsin. Sonra ben sinemaya gittim. Aynı zamanda sen markete gitmişsin. Pazar günü buluştuk ve her şeyi birleştirdik."

Rebase tarzı anlatım (lineer hikaye): "Sen şunları yaptın: film izledin, markete gittin. Sonra ben şunları yaptım: kahvaltı yaptım, sinemaya gittim. Ve pazar buluştuk."

Merge her iki tarihçeyi olduğu gibi koruyup bir birleştirme noktası oluşturur. Rebase ise bir tarihçeyi diğerinin ucuna "yeniden uygular" — sanki her şey sıralı olmuş gibi.

Görsel Karşılaştırma

Başlangıç durumu:

main     A───B───C
              \
feature        D───E

Merge sonrası:

main     A───B───C───────M  (M = merge commit)
              \         /
feature        D───E───┘

Rebase sonrası:

main     A───B───C
                  \
feature            D'───E'  (D ve E yeniden uygulandı)

Rebase'den sonra fast-forward merge yapılırsa:

main     A───B───C───D'───E'  (tamamen lineer!)

Temel Rebase Kullanımı

Senaryo: Feature Branch'i Güncelleme

# Durum: main branch ilerledı, feature branch geride kaldı
# main:    A → B → C → F → G  (F ve G yeni commit'ler)
# feature: A → B → C → D → E  (D ve E senin commit'lerin)

# Feature branch'tesin
git checkout feature/login

# main'deki yeni commit'leri feature'ın altına al
git rebase main

Terminal çıktısı:

First, rewinding head to replay your work on top of it...
Applying: Add login form component
Applying: Add login validation logic
Successfully rebased and updated refs/heads/feature/login.

Rebase Ne Yapar? (Adım Adım)

ADIM 1: feature branch'teki commit'leri "sakla"
        (D ve E commit'lerini kenara koy)

ADIM 2: feature branch'i main'in ucuna taşı
        (HEAD artık G'yi gösteriyor)

ADIM 3: Saklanan commit'leri sırasıyla yeniden uygula
        D → D' (yeni hash ile)
        E → E' (yeni hash ile)

SONUÇ:
main     A───B───C───F───G
                          \
feature                    D'───E'

⚠️ Dikkat: Rebase commit hash'lerini değiştirir! D ve E artık D' ve E' oldu. İçerik aynı, ama farklı commit'ler. Bu, paylaşılmış branch'lerde tehlikeli olabilir.

Rebase Sonrası Fast-Forward Merge

# Rebase'den sonra main'e geç ve merge et
git checkout main
git merge feature/login  # Bu bir fast-forward merge olur!
Updating abc1234..def5678
Fast-forward
 src/login.js | 42 +++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

Sonuç: Tamamen lineer, temiz bir tarihçe:

main  A───B───C───F───G───D'───E'

Rebase Conflict Çözme

Rebase sırasında da conflict çıkabilir. Ama merge'den farklı olarak, her commit tek tek uygulanır — yani her commit'te ayrı conflict olabilir.

git rebase main

Conflict çıkarsa:

CONFLICT (content): Merge conflict in src/config.js
error: could not apply abc1234... Update config
Resolve all conflicts manually, mark them as resolved with
"git add <pathspec>" then run "git rebase --continue".
hint: Or use "git rebase --skip" to skip this commit.
hint: Or use "git rebase --abort" to abort the rebase.

Conflict Çözüm Adımları

# 1. Conflict'li dosyayı aç ve düzelt
# <<<<<<< HEAD, ======= ve >>>>>>> işaretlerini temizle

# 2. Düzelttiğini işaretle
git add src/config.js

# 3. Rebase'e devam et
git rebase --continue

# Eğer bu commit'i atlamak istiyorsan:
git rebase --skip

# Eğer vazgeçmek istiyorsan (her şeyi geri al):
git rebase --abort

Conflict Akışı

Commit D uygulanıyor... ──── Conflict! ──► Çöz → git add → git rebase --continue
                                                          │
Commit E uygulanıyor... ──── Conflict! ──► Çöz → git add → git rebase --continue
                                                          │
                                                          ▼
                                              Successfully rebased ✅

Interactive Rebase — Git'in İsviçre Çakısı

Interactive rebase (git rebase -i), Git'in en güçlü araçlarından biridir. Commit'lerin üzerinde tam kontrol sağlar: sıralama, birleştirme, düzenleme, silme, mesaj değiştirme…

Analoji — Video Düzenleme

Bir video çektin. Ham haliyle paylaşmak yerine video düzenleme programında açıyorsun:

  • Bazı sahneleri birleştiriyorsun (squash)

  • Bazılarının açıklamasını değiştiriyorsun (reword)

  • Bazılarını kırpıyorsun (edit)

  • Bazılarını tamamen siliyorsun (drop)

  • Sahnelerin sırasını değiştiriyorsun (reorder)

Interactive rebase, commit tarihçeni bir video gibi düzenlemeni sağlar.

Kullanım

# Son 4 commit'i düzenle
git rebase -i HEAD~4

# Belirli bir commit'ten itibaren düzenle
git rebase -i abc1234

# Branch'in ayrıldığı noktadan itibaren düzenle
git rebase -i main

Bu komutu çalıştırdığında editörde şöyle bir dosya açılır:

pick abc1234 feat: Add user model
pick def5678 fix: Fix typo in user model
pick ghi9012 WIP: Working on validation
pick jkl3456 feat: Add validation logic

# Rebase abc1234..jkl3456 onto 789xyz (4 commands)
#
# Commands:
# p, pick   = use commit as is (commit'i olduğu gibi kullan)
# r, reword = use commit, but edit the commit message (mesajı değiştir)
# e, edit   = use commit, but stop for amending (commit'i düzenle)
# s, squash = use commit, but meld into previous commit (öncekiyle birleştir)
# f, fixup  = like "squash", but discard this commit's log message (sessiz birleştir)
# d, drop   = remove commit (commit'i sil)
#
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here, THAT COMMIT WILL BE LOST.

Interactive Rebase Komutları Detaylı

1. pick — Olduğu Gibi Kullan

pick abc1234 feat: Add user model

Commit'i değiştirmeden kullan. Varsayılan davranış.

2. reword — Commit Mesajını Değiştir

reword abc1234 feat: Add user model

Commit'in içeriği aynı kalır, sadece mesajı değiştirebilirsin. Kaydettiğinde yeni bir editör açılır ve mesajı düzenlersin.

# Örnek: Typo düzeltme
# "feat: Add usr model" → "feat: Add user model"

3. squash — Önceki Commit ile Birleştir

pick abc1234 feat: Add user model
squash def5678 fix: Fix typo in user model

İki commit tek bir commit'e birleşir. Her iki commit'in mesajı da editörde gösterilir — düzenleyebilirsin:

# This is a combination of 2 commits.
# This is the 1st commit message:

feat: Add user model

# This is the commit message #2:

fix: Fix typo in user model

# Düzenle ve kaydet:
feat: Add user model with proper naming

4. fixup — Sessiz Birleştirme

pick abc1234 feat: Add user model
fixup def5678 fix: Fix typo in user model

Squash gibi birleştirir ama ikinci commit'in mesajını atar. Sonuç sadece "feat: Add user model" olur. "fix typo", "WIP" gibi anlamsız commit'leri temizlemek için harika.

5. edit — Commit'i Düzenle

edit abc1234 feat: Add user model

Rebase bu commit'te durur. İstediğin değişiklikleri yapabilirsin:

# Rebase durdu, düzenle:
# Dosya ekle, değiştir, sil...
git add src/user.js
git commit --amend  # Bu commit'i güncelle
git rebase --continue  # Devam et

Bir commit'i iki commit'e bölmek için de kullanılır:

edit abc1234 feat: Add user model and validation  # Bu çok büyük bir commit

# Rebase durduğunda:
git reset HEAD~1  # Commit'i geri al (dosyalar working directory'de kalır)

# İki ayrı commit yap:
git add src/models/user.js
git commit -m "feat: Add user model"

git add src/validators/user.js
git commit -m "feat: Add user validation"

git rebase --continue

6. drop — Commit'i Sil

drop ghi9012 WIP: Working on validation

Bu commit tamamen tarihçeden silinir. Satırı silmek de aynı etkiyi yapar.

⚠️ Dikkat: drop edilen commit geri gelmez (reflog hariç). Emin olmadan kullanma!


Pratik Interactive Rebase Senaryoları

Senaryo 1: Commit'leri Temizleme (En Yaygın)

PR'ında şu commit'ler var:

abc1234 feat: Add shopping cart
def5678 WIP
ghi9012 fix typo
jkl3456 still working on it
mno7890 feat: Add checkout flow
pqr1234 forgot to add file

Bu tarihçeyi temizleyelim:

git rebase -i HEAD~6

Editörde:

pick abc1234 feat: Add shopping cart
fixup def5678 WIP
fixup ghi9012 fix typo
fixup jkl3456 still working on it
pick mno7890 feat: Add checkout flow
fixup pqr1234 forgot to add file

Sonuç — tertemiz iki commit:

feat: Add shopping cart
feat: Add checkout flow

Senaryo 2: Commit Sırasını Değiştirme

git rebase -i HEAD~3

Editörde satırları taşı (kes-yapıştır):

# Önce:
pick abc1234 feat: Add API
pick def5678 feat: Add Database
pick ghi9012 feat: Add Models

# Sonra (sırayı değiştir):
pick def5678 feat: Add Database
pick ghi9012 feat: Add Models
pick abc1234 feat: Add API

Senaryo 3: Commit Mesajlarını Düzenleme

git rebase -i HEAD~3
reword abc1234 addd user loginn  # typo'lu mesaj
pick def5678 feat: Add logout
pick ghi9012 feat: Add profile page

Kaydedince yeni bir editör açılır:

# Eski mesajı düzenle:
feat: Add user login

Senaryo 4: Autosquash ile fixup! ve squash! Commit'leri

Git'in akıllı bir özelliği: commit mesajına fixup! veya squash! öneki koyarsan, interactive rebase otomatik olarak doğru yere yerleştirir.

# Ana commit
git commit -m "feat: Add user dashboard"

# Sonra bir düzeltme yaptın
git commit -m "fixup! feat: Add user dashboard"

# Interactive rebase ile otomatik birleştir
git rebase -i --autosquash HEAD~5

Editörde otomatik olarak düzenlenir:

pick abc1234 feat: Add user dashboard
fixup def5678 fixup! feat: Add user dashboard  # Otomatik yerleşti!
pick ghi9012 feat: Add settings page

💡 İpucu: git config --global rebase.autoSquash true ile autosquash'ı varsayılan olarak açabilirsin. Her git rebase -i otomatik olarak fixup!/squash! commit'lerini düzenler.


The Golden Rule of Rebasing

Git dünyasının en önemli kuralı:

╔══════════════════════════════════════════════════════════════╗
║                                                              ║
║   🏆 ALTIN KURAL:                                           ║
║                                                              ║
║   Paylaşılmış (push edilmiş) commit'leri                    ║
║   ASLA rebase etme!                                          ║
║                                                              ║
╚══════════════════════════════════════════════════════════════╝

Neden?

Rebase commit hash'lerini değiştirir. Eğer başka birisi senin eski commit'lerini baz alarak çalışıyorsa, rebase sonrası her şey bozulur:

SENARYO: Ahmet ve Sen aynı branch üzerinde çalışıyorsunuz

Başlangıç (ikisinde de aynı):
main  A───B───C

Sen rebase yaptın:
main  A───B'───C'  (hash'ler değişti!)

Ahmet hâlâ eski versiyonda:
main  A───B───C

Ahmet push etmeye çalışıyor: CONFLICT!
Ahmet pull yapıyor: Aynı değişiklikler iki kere görünüyor!

KAOS. 💥

Ne Zaman Rebase Güvenli?

✅ GÜVENLİ:
  - Henüz push etmediğin yerel commit'ler
  - Sadece SENİN çalıştığın feature branch
  - PR'ı merge etmeden önce temizlik (squash)

❌ TEHLİKELİ:
  - main/develop branch'i (paylaşımlı)
  - Başkalarının da push ettiği branch'ler
  - Zaten merge edilmiş commit'ler

Force Push Gereksinimi

Rebase yaptıktan sonra push etmek istersen, normal git push çalışmaz çünkü tarihçe değişti:

# ❌ Normal push başarısız olur:
git push
# ! [rejected] feature -> feature (non-fast-forward)

# ✅ Force push gerekir:
git push --force-with-lease

# ❌ Asla çıplak --force kullanma:
git push --force  # Başkasının push'unu ezebilir!

--force-with-lease farkı:

--force            → "Ne olursa olsun, remote'u benim versiyonumla değiştir"
--force-with-lease → "Remote son gördüğüm haldeyse değiştir, 
                      birisi push ettiyse DURDUR"

💡 İpucu: git config --global alias.pushf 'push --force-with-lease' ile kısaltma tanımla. Artık git pushf yeterli.


Rebase vs Merge — Hangisini Seçmeli?

Karar Ağacı

Branch sadece benim mi?
├── Evet → Rebase kullan (temiz tarihçe)
│          git pull --rebase
│          git rebase main
│
└── Hayır (paylaşımlı branch)
    ├── Merge kullan (güvenli)
    │   git merge main
    │
    └── Rebase istiyorsan:
        → Ekiple konuş, herkes aynı anda güncellesin
        → git push --force-with-lease

Karşılaştırma

┌────────────────────┬──────────────────┬──────────────────┐
│     Özellik        │     Merge        │     Rebase       │
├────────────────────┼──────────────────┼──────────────────┤
│ Tarihçe            │ Dallı, karmaşık  │ Lineer, temiz    │
│ Merge commit       │ Var              │ Yok              │
│ Orijinal commit    │ Korunur          │ Hash değişir     │
│ Conflict çözümü    │ Bir kez          │ Her commit'te    │
│ Güvenlik           │ Güvenli          │ Dikkat gerekir   │
│ Paylaşımlı branch  │ ✅ Uygun         │ ⚠️ Tehlikeli     │
│ git log okuması    │ Karmaşık         │ Kolay            │
│ Revert kolaylığı   │ Kolay            │ Zor olabilir     │
│ Branch ilişkisi    │ Görünür          │ Kaybolur         │
└────────────────────┴──────────────────┴──────────────────┘

Ekip Stratejileri

Strateji 1: "Merge Everything" (Güvenli)
  Her branch merge edilir. Tarihçe karmaşık ama güvenli.
  
Strateji 2: "Rebase + Merge" (Temiz)
  Feature branch'i rebase et, sonra merge (fast-forward).
  Temiz tarihçe ama dikkat gerekir.

Strateji 3: "Squash + Merge" (Pratik)
  Feature branch'teki tüm commit'ler squash edilir.
  Her PR = tek commit. En yaygın tercih.

Strateji 4: "Rebase + Squash" (Titiz)
  Önce interactive rebase ile commit'leri temizle,
  sonra squash merge yap. En temiz tarihçe.

git pull --rebase

git pull aslında git fetch + git merge dir. Ama --rebase parametresiyle git fetch + git rebase yaparsın:

# Normal pull (merge oluşturur):
git pull origin main
# fetch + merge → merge commit oluşabilir

# Rebase pull (lineer kalır):
git pull --rebase origin main
# fetch + rebase → merge commit oluşmaz

Varsayılan Olarak Ayarlama

# Tüm pull'ları rebase modunda yap
git config --global pull.rebase true

# Sadece bu repo için
git config pull.rebase true

Bu ayar özellikle birden fazla kişinin aynı branch'te çalıştığı (pek önerilmese de) durumlarda gereksiz merge commit'leri önler.


--onto ile Gelişmiş Rebase

Bazen bir branch'i farklı bir noktaya taşımak istersin. --onto bunu sağlar.

Senaryo: Branch'i Farklı Bir Base'e Taşıma

# Durum: feature branch'i yanlışlıkla eski bir noktadan açtın
main          A───B───C───D───E
                   \
old-feature         F───G
                         \
sub-feature               H───I  ← Bunu main'in ucuna taşımak istiyorsun
# sub-feature'ı old-feature'dan kopar, main'in ucuna bağla
git rebase --onto main old-feature sub-feature
# Sonuç:
main          A───B───C───D───E
                   \              \
old-feature         F───G          H'───I'  (sub-feature)

--onto Sözdizimi

git rebase --onto <yeni-base> <eski-base> <branch>

# "branch'teki eski-base'den sonraki commit'leri al,
#  yeni-base'in üzerine uygula"

Rebase Sorun Giderme

"Rebase yaptım ama korkuyorum"

# Rebase'den önceki duruma dönmek her zaman mümkün:
git reflog
# abc1234 HEAD@{2}: rebase finished: refs/heads/feature onto def5678
# ghi9012 HEAD@{3}: rebase: checkout main
# jkl3456 HEAD@{4}: commit: feat: last commit before rebase  ← BU!

git reset --hard jkl3456  # Rebase'den önceki hale dön

"Conflict çok fazla, vazgeçiyorum"

git rebase --abort  # Her şeyi geri al, rebase başlamadan önceki hale dön

"Bu commit'i atlamak istiyorum"

git rebase --skip  # Bu commit'i atlayıp devam et

Özet

Bu derste Git'in en güçlü tarihçe aracı olan rebase'i öğrendik:

  • Rebase, commit'leri başka bir noktanın üzerine yeniden uygular — merge'den farklı olarak lineer bir tarihçe oluşturur

  • Interactive rebase (git rebase -i) ile commit'leri düzenleyebilir, birleştirebilir, silebilir, sıralayabilirsin — pick, reword, squash, fixup, edit, drop komutlarıyla tam kontrol

  • Altın Kural: Paylaşılmış commit'leri asla rebase etme — hash'ler değişir ve ekip arkadaşlarının tarihçesi bozulur

  • Force push (--force-with-lease) rebase sonrası gereklidir ama dikkatli kullanılmalıdır

  • git pull --rebase ile gereksiz merge commit'lerini önleyebilirsin

  • Rebase'den dönüş her zaman mümkün: git rebase --abort veya git reflog ile eski hale dönebilirsin

Bir sonraki derste commit tarihçesinin dedektifi olacağız: cherry-pick, bisect ve blame.