← Kursa Dön
📄 Text · 30 min

Push ve Pull

Giriş: Neden Push ve Pull Bu Kadar Önemli?

Git'in dağıtık yapısı sayesinde herkes kendi yerel repo'sunda bağımsız çalışabilir. Ama bir noktada kodunu paylaşman ve başkalarının kodunu alman gerekiyor. İşte push ve pull bu köprüyü kurar.

Bu iki komut her gün onlarca kez kullanacağın komutlar. Doğru kullanmak rahat, yanlış kullanmak felaket. Özellikle --force bayrağı, dikkatsiz kullanıldığında başkalarının çalışmalarını silebilir.

Bu derste:

  • git push'un tüm varyasyonları

  • git pull'un farklı modları

  • --force-with-lease nedir ve neden --force'tan daha güvenlidir

  • Yaygın senaryolar ve çözümleri


🎬 Analoji: Paylaşılan Belge

Google Docs'ta bir belge düşün:

  • Push = Senin değişikliklerini belgeye yükleme. "Benim yazdıklarım artık herkesin görebileceği yerde."

  • Pull = Belgede başkalarının yaptığı değişiklikleri alma. "Ayşe ne eklemiş, bakayım."

  • Force Push = Belgenin tüm geçmişini kendi versiyonunla değiştirme. "Herkesin yazdığını silip benim versiyonumu koydum." ⚠️ Tehlikeli!


git push — Kodunu Gönder

Temel Kullanım

# Genel sözdizimi
$ git push <remote> <branch>

# En yaygın kullanım
$ git push origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 350 bytes | 350.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://github.com/tolgahan/projem.git
   a1b2c3d..d4e5f6g  main -> main

Bu çıktıyı okuyalım:

  • Git nesneleri sıkıştırılıp gönderildi

  • a1b2c3d..d4e5f6g — önceki commit'ten yeni commit'e güncellendi

  • main -> main — yerel main'den remote main'e

--set-upstream (-u): Bağlantı Kur

İlk push'ta -u bayrağı ile yerel branch'i remote branch'e bağlarsın:

# İlk push
$ git push -u origin main
Branch 'main' set up to track remote branch 'main' from 'origin'.

# Bundan sonra sadece şu yeterli:
$ git push
# origin main'i otomatik bilir

$ git pull
# origin main'den otomatik çeker

Yeni bir feature branch için:

$ git switch -c feature/search
# ... çalış, commit at ...
$ git push -u origin feature/search
# İlk push'ta -u kullan
# Sonrakilerinde sadece git push yeterli

push Çıktıları ve Anlamları

# Başarılı push
$ git push
To https://github.com/tolgahan/projem.git
   a1b2c3d..d4e5f6g  main -> main
# ✅ Sorunsuz gönderildi

# Reddedilen push
$ git push
To https://github.com/tolgahan/projem.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/...'
hint: Updates were rejected because the remote contains work that
hint: you do not have locally. Integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
# ❌ Reddedildi — remote'ta sende olmayan commit'ler var

Rejected durumu: Birileri senden önce push etmiş. Önce pull yapıp değişiklikleri birleştirmen gerekiyor:

# 1. Remote'taki değişiklikleri al
$ git pull origin main

# 2. Conflict varsa çöz (önceki derslerde öğrendik)

# 3. Tekrar push et
$ git push origin main

push.default Ayarı

# Mevcut branch'i push et (önerilen)
$ git config --global push.default current

# Sadece izlenen branch'i push et
$ git config --global push.default simple  # Git 2.x varsayılanı

# Tüm eşleşen branch'leri push et (eski davranış — önerilmez)
$ git config --global push.default matching

Tag Push'lama

Tag'ler normal push ile gönderilmez:

# Tek tag push'la
$ git push origin v1.0.0

# Tüm tag'leri push'la
$ git push origin --tags

# Hem commit hem tag push'la
$ git push --follow-tags

git push --force: Tehlikeli Alan

Neden Force Push Gerekir?

Bazı durumlarda normal push reddedilir ve --force gerekir:

  1. git commit --amend ile son commit'i değiştirdin

  2. git rebase yaptın

  3. Geçmişi yeniden yazdın (git reset)

Bu durumların hepsinde commit hash'leri değişir. Remote'taki geçmiş ile senin geçmişin uyumsuz olur.

Remote:   [A] ← [B] ← [C]
Yerel:    [A] ← [B] ← [C']   ← amend ile C değişti, C' oldu

Normal push: REDDEDILIR (C ≠ C')
Force push: Remote'u senin versiyonla değiştirir

--force: Kaba Kuvvet

$ git push --force origin main
# veya kısaca
$ git push -f origin main

⚠️ Dikkat: --force son derece tehlikelidir. Neden? Çünkü remote'taki commit'leri silip seninkilerle değiştirir. Eğer birileri bu arada push ettiyse, onların commit'leri kaybolur.

Ali:     [A] ← [B] ← [C] ← [D]   (Ali D'yi push etti)
Sen:     [A] ← [B] ← [C']         (C'yi amend ettin)

$ git push --force
Remote:  [A] ← [B] ← [C']         (Ali'nin D commit'i SİLİNDİ!)

--force-with-lease: Güvenli Force Push

$ git push --force-with-lease origin main

--force-with-lease şunu yapar:

  1. "Remote'taki main branch, benim bildiğim commit'i mi gösteriyor?"

  2. EVET → force push yap

  3. HAYIR → "Birisi push etmiş, iptal et!" der

Senaryo 1: Kimse push etmemiş
Senin origin/main:  [A] ← [B] ← [C]    (son fetch'teki durum)
Remote'taki main:   [A] ← [B] ← [C]    (aynı!)
Senin yerel main:   [A] ← [B] ← [C']   (amend)

$ git push --force-with-lease
✅ origin/main beklediğim gibi, push ediyorum.

Senaryo 2: Birisi push etmiş
Senin origin/main:  [A] ← [B] ← [C]    (son fetch'teki durum)
Remote'taki main:   [A] ← [B] ← [C] ← [D]   (birisi D'yi push etmiş!)
Senin yerel main:   [A] ← [B] ← [C']

$ git push --force-with-lease
❌ REDDEDILDI! Remote değişmiş. Önce fetch yap.

Bu, "güvenli force push" demektir. Başkasının çalışmasını kazayla silmezsin.

💡 İpucu: --force yerine her zaman --force-with-lease kullan. Alias ekle: ``bash git config --global alias.pushf "push --force-with-lease" ` Artık git pushf` ile güvenli force push yapabilirsin.


git pull — Güncel Kal

Temel Kullanım

# Remote'tan çek ve birleştir
$ git pull origin main

remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
Unpacking objects: 100% (3/3), done.
From https://github.com/tolgahan/projem
   a1b2c3d..d4e5f6g  main       -> origin/main
Updating a1b2c3d..d4e5f6g
Fast-forward
 index.html | 5 +++++
 1 file changed, 5 insertions(+)

pull = fetch + merge

# Bu iki komut aynı işi yapar:
$ git pull origin main

$ git fetch origin
$ git merge origin/main

pull --rebase: Temiz Geçmiş

$ git pull --rebase origin main
# veya global ayar:
$ git config --global pull.rebase true

Neden rebase tercih edilir?

Normal pull (merge) sonucunda:
$ git log --oneline --graph
*   f7g8h9i Merge branch 'main' of github.com:tolgahan/projem
|\
| * d5e6f7g feat: Ayşe'nin değişikliği
* | c4d5e6f feat: Benim değişikliğim
|/
* a1b2c3d feat: Başlangıç

Pull --rebase sonucunda:
$ git log --oneline --graph
* c4d5e6f' feat: Benim değişikliğim
* d5e6f7g feat: Ayşe'nin değişikliği
* a1b2c3d feat: Başlangıç

Rebase ile gereksiz merge commit oluşmaz. Geçmiş temiz kalır.

⚠️ Dikkat: pull --rebase sırasında conflict çıkabilir. Bu durumda: ``bash # Conflict'i çöz, dosyayı düzenle $ git add dosya.txt $ git rebase --continue # Veya rebase'i iptal et $ git rebase --abort ``

pull Stratejisi Ayarları

# Varsayılan pull davranışı
$ git config --global pull.rebase true    # Her zaman rebase
$ git config --global pull.rebase false   # Her zaman merge (varsayılan)
$ git config --global pull.ff only        # Sadece fast-forward, yoksa hata ver

Yaygın Senaryolar ve Çözümleri

Senaryo 1: Push Reddedildi

$ git push origin main
 ! [rejected] main -> main (fetch first)

Çözüm:

# Yol 1: Pull + Push
$ git pull origin main
# (conflict varsa çöz)
$ git push origin main

# Yol 2: Pull --rebase + Push (daha temiz)
$ git pull --rebase origin main
# (conflict varsa çöz, git rebase --continue)
$ git push origin main

Senaryo 2: Amend Sonrası Push

$ git commit -m "feat: Login eklendi"
$ git push origin main
# Push başarılı

# Oops, mesaj yanlış!
$ git commit --amend -m "feat: Login sayfası eklendi"
$ git push origin main
 ! [rejected]  # Hash değişti, reddedildi

# Çözüm: Force with lease
$ git push --force-with-lease origin main
# ✅ Güvenli force push

Senaryo 3: Yanlış Branch'e Push

# Feature branch'ini yanlışlıkla main'e push'ladın
$ git push origin feature/login:main
# 😱

# Düzeltme: main'i eski haline getir
$ git push --force-with-lease origin HEAD~1:main
# veya doğru commit'e reset'le

Senaryo 4: Tüm Branch'leri Push

# Tüm yerel branch'leri push et
$ git push --all origin

# Tüm tag'lerle birlikte
$ git push --all origin && git push --tags origin

Senaryo 5: Remote Branch'i Sil

# Remote'taki branch'i sil
$ git push origin --delete feature/old-feature
To github.com:tolgahan/projem.git
 - [deleted]         feature/old-feature

Günlük Workflow: Push ve Pull

Sabah Rutini

# 1. Günün başında main'i güncelle
$ git switch main
$ git pull origin main

# 2. Feature branch'inde çalışmaya devam et
$ git switch feature/my-work
$ git rebase main
# main'deki yeni değişiklikleri al

Akşam Rutini

# 1. Çalışmayı push et
$ git push origin feature/my-work

# 2. Eğer PR açacaksan, push sonrası GitHub'da PR aç

Ekip Senkronizasyonu

# 1. Feature branch'ine main'deki güncel değişiklikleri al
$ git switch feature/my-work
$ git fetch origin
$ git rebase origin/main
# (conflict varsa çöz)
$ git push --force-with-lease origin feature/my-work

# 2. Başkasının feature branch'ini kontrol et
$ git fetch origin
$ git switch -c review/their-feature origin/feature/their-work
# İncele, test et

Push/Pull Hızlandırma

Shallow Clone

Büyük projelerde tüm geçmişi indirmek uzun sürer:

# Son 10 commit'i indir (daha hızlı)
$ git clone --depth 10 https://github.com/tolgahan/buyuk-proje.git

# Gerekirse tam geçmişi sonradan al
$ git fetch --unshallow

Partial Clone

Büyük dosyaları indirmeden clone'la (Git 2.22+):

# Blob'ları (dosya içerikleri) hemen indirme
$ git clone --filter=blob:none https://github.com/tolgahan/proje.git
# Dosyalar sadece checkout edildiğinde indirilir

git fetch — Push ve Pull'un Temeli

git fetch remote'taki değişiklikleri indirir ama yerel branch'leri güncellemez. Bu, "bak ama dokunma" yaklaşımıdır.

# Tüm remote branch'leri güncelle
$ git fetch origin

# Çıktı:
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (8/8), done.
Unpacking objects: 100% (8/8), done.
From https://github.com/tolgahan/projem
   a1b2c3d..d4e5f6g  main       -> origin/main
 * [new branch]      feature/x  -> origin/feature/x

# Şimdi ne olduğuna bakabilirsin:
$ git log main..origin/main --oneline
d4e5f6g feat: Yeni özellik eklendi
c3d4e5f fix: Bug düzeltildi
# 2 yeni commit var, henüz merge etmeden görebilirsin

# Beğendiysen merge et:
$ git merge origin/main

fetch vs pull Karşılaştırması

┌──────────────────────────────────────────────┐
│              git fetch                        │
│                                              │
│  Remote ──download──► origin/main            │
│                       (remote tracking ref)   │
│                                              │
│  Yerel main DEĞİŞMEDİ                       │
│  Working directory DEĞİŞMEDİ                │
│  Güvenli — sadece indir, birleştirme        │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│              git pull                         │
│                                              │
│  Remote ──download──► origin/main            │
│           ──merge───► main (yerel)           │
│                                              │
│  Yerel main GÜNCELLENDİ                     │
│  Working directory GÜNCELLENDİ              │
│  Otomatik merge — conflict çıkabilir        │
└──────────────────────────────────────────────┘

💡 İpucu: Tecrübeli geliştiriciler genellikle git fetch + git log + git merge akışını tercih eder. Böylece gelen değişiklikleri önce görür, sonra bilinçli olarak merge eder. git pull aynı şeyi tek adımda yapar ama neyin geldiğini görmeden birleştirir.

Stale Branch Temizleme

Remote'ta silinen branch'ler yerel tracking referanslarında kalır:

# Remote'ta silinmiş branch referanslarını temizle
$ git fetch --prune

# veya otomatik prune ayarı
$ git config --global fetch.prune true

# Hangi remote branch'ler kaldırılmış kontrol et
$ git remote prune origin --dry-run
# Pruning origin
# URL: https://github.com/tolgahan/projem.git
#  * [would prune] origin/feature/old-thing
#  * [would prune] origin/bugfix/resolved

Upstream Tracking — Branch Bağlantıları

Her yerel branch bir remote branch'i "takip" edebilir (tracking). Bu bağlantı kurulduğunda git push ve git pull parametresiz çalışır:

# Tracking bilgisini gör
$ git branch -vv
  feature/login  a1b2c3d [origin/feature/login] feat: Login eklendi
* main           d4e5f6g [origin/main] feat: Son güncelleme
  local-only     h7i8j9k Yerel branch (tracking yok)

# Tracking ayarla (mevcut branch için)
$ git branch --set-upstream-to=origin/main main
Branch 'main' set up to track remote branch 'main' from 'origin'.

# Tracking kaldır
$ git branch --unset-upstream

Ahead / Behind Bilgisi

Tracking ayarlıyken Git, yerel ve remote arasındaki farkı gösterir:

$ git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
  (use "git push" to publish your local commits)

# veya
Your branch is behind 'origin/main' by 3 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

# veya
Your branch and 'origin/main' have diverged,
and have 2 and 3 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
Ahead by 2:     Yerel'de 2 commit var, remote'ta yok → push et
Behind by 3:    Remote'ta 3 commit var, yerel'de yok → pull et
Diverged 2/3:   İkisinde de farklı commit'ler var → pull + push

Yaygın Hatalar

1. --force Kullanmak (force-with-lease yerine)

# ❌ Ekip arkadaşının commit'lerini silme riski
$ git push --force origin main

# ✅ Güvenli force push
$ git push --force-with-lease origin main

2. Pull Yapmadan Push Etmeye Çalışmak

# ❌ Rejected hatasına panik yapmak
$ git push origin main
 ! [rejected] main -> main (fetch first)

# ✅ Önce pull, sonra push
$ git pull --rebase origin main
$ git push origin main

3. Yanlış Remote'a Push Etmek

# ❌ Fork'un upstream'ine push
$ git push upstream main
# Yetkiniz yoksa hata alırsınız, varsa orijinal repo'ya push

# ✅ Remote'ları kontrol et
$ git remote -v
origin    https://github.com/siz/fork.git (push)
upstream  https://github.com/original/repo.git (push)

$ git push origin main  # Kendi fork'unuza

4. Tüm Branch'leri Push Etmek

# ❌ WIP branch'lerini de push etmek
$ git push --all origin
# Yarım kalmış, deneme branch'leri de remote'a gider

# ✅ Sadece çalıştığın branch'i push et
$ git push origin feature/my-work

5. Büyük Dosyaları Push Etmek

# ❌ GitHub'ın 100MB limiti
$ git push origin main
# remote: error: File data.csv is 150.00 MB; exceeds 100.00 MB limit

# ✅ Büyük dosyaları .gitignore'a ekle veya Git LFS kullan
$ echo "*.csv" >> .gitignore
# veya
$ git lfs track "*.csv"

Push ve Pull Komut Referansı

# PUSH
git push                           # Varsayılan remote'a push
git push origin main               # Belirli remote ve branch
git push -u origin feature/x      # İlk push + upstream ayarla
git push --force-with-lease        # Güvenli force push
git push --tags                    # Tag'leri push
git push origin --delete branch    # Remote branch sil
git push --all origin              # Tüm branch'leri push

# PULL
git pull                           # Varsayılan remote'tan pull
git pull origin main               # Belirli remote ve branch
git pull --rebase                  # Rebase ile pull
git pull --rebase origin main      # Belirli remote + rebase
git pull --ff-only                 # Sadece fast-forward

# FETCH
git fetch                          # Varsayılan remote'tan fetch
git fetch origin                   # Belirli remote'tan fetch
git fetch --all                    # Tüm remote'lardan fetch
git fetch --prune                  # Silinen branch'leri temizle
git fetch origin branch:local      # Uzak branch'i yerel branch'e indir

Özet

  • `git push` yerel commit'leri remote'a gönderir — ilk push'ta -u ile upstream bağlantısı kur

  • `git pull` = fetch + merge; remote'taki değişiklikleri alır ve yerel branch'le birleştirir

  • `--rebase` ile pull yaparak gereksiz merge commit'lerini önle — pull.rebase true ayarı önerilir

  • `--force` yerine `--force-with-lease` kullan — başkasının çalışmasını kazayla silmeyi önler

  • Push reddedilirse önce pull (veya pull --rebase) yap, sonra tekrar push'la

  • Remote branch silmek için git push origin --delete branch-adı kullan


*Bir sonraki derste clone ve fork kavramlarını — projeleri indirme, fork workflow ve open source katkı — öğreneceğiz!*