Günlük Kullanılan Git Komutları: add, commit, push ve pull
Sabah ilk kahveni içerken bile aklının bir köşesinde “acaba push ettim mi?” sorusu dönüyorsa, ya da bir ekip arkadaşın “sen pull yaptın mı?” diye sorarken ne demek istediğini tam anlayamıyorsan, bu yazı tam sana göre. Git’i teorik olarak öğrenmek ayrı bir şey, günlük iş akışında gerçekten verimli kullanmak ayrı bir şey. Yıllarca üretim ortamlarında, ekip projelerinde ve bazen de gece yarısı acil müdahalelerde bu komutları kullandıktan sonra biriken deneyimi burada paylaşıyorum.
Önce Kafamızdaki Modeli Doğru Kuralım
Git’i anlamanın en büyük engeli, insanların onu başka sürüm kontrol sistemleriyle karşılaştırarak öğrenmeye çalışması. SVN gibi merkezi sistemlerden geliyorsan özellikle bu tuzağa düşebilirsin.
Git’te üç katman var ve bu katmanları içselleştirmeden komutlar havada kalıyor:
- Working Directory: Dosyalarını düzenlediğin, üzerinde çalıştığın alan. Ham gerçeklik burası.
- Staging Area (Index): Commit’e hazırladığın değişikliklerin beklediği ara bölge. Fotoğraf çekmeden önce pozisyon aldığın alan.
- Repository (.git): Kalıcı tarih. Commit’lerin yaşadığı yer.
Bir de bunun üzerine uzak repository (remote) var. GitHub, GitLab, Bitbucket ya da kendi sunucundaki bare repo. push ve pull işte bu uzak repo ile olan köprü.
Bu dört komut aslında şu hikayeyi anlatıyor: Çalış (working directory), seç (add), kaydet (commit), paylaş (push), güncelle (pull).
git add: Seçici Olmayı Öğren
Çoğu kişi git add . yazıp geçiyor. Evet, çalışıyor. Ama bu alışkanlık zamanla problemlere yol açıyor. Bir config dosyasını, bir .env dosyasını, ya da test amaçlı yazdığın geçici bir scripti commit’e dahil etmek istemediğin durumlar olacak.
# Tek bir dosya eklemek
git add src/app.py
# Belirli bir dizindeki her şeyi eklemek
git add src/
# Her şeyi eklemek (dikkatli kullan)
git add .
# Sadece takip edilen dosyalardaki değişiklikleri eklemek (yeni dosyaları dahil etmez)
git add -u
# İnteraktif mod - neleri ekleyeceğini seçebilirsin
git add -i
Özellikle -p (patch) parametresini öğrenmen sana çok şey kazandıracak. Tek bir dosyada birden fazla mantıksal değişiklik yaptıysan ve bunları ayrı commit’lere bölmek istiyorsan:
# Dosya içindeki değişiklikleri parça parça (hunk) stage'e ekle
git add -p config/nginx.conf
Bu komut sana her değişiklik bloğu için “ekleyelim mi?” diye sorar. y (evet), n (hayır), s (daha küçük parçalara böl), q (çık) seçenekleriyle interaktif çalışırsın. Büyük dosyalarda gece yarısı “bu değişikliği ben mi yaptım?” sorusunun önüne geçmek için bunu kullan.
Gerçek dünya senaryosu: Bir Nginx config dosyasını hem production için bir upstream bloğu ekleyerek hem de development için bir proxy_pass değişikliği yaparak düzenlediğini düşün. Bunları tek commit’e koymak mantıklı değil. git add -p ile ayrı ayrı stage’e alıp ayrı ayrı commit edebilirsin.
Stage’de Ne Var Kontrol Et
git add sonrası ne commit edeceğini görmek için:
# Stage'deki değişiklikleri görmek
git diff --staged
# Ya da şu şekilde de yazılabilir
git diff --cached
Bu adımı atlamak, sonradan “neden bunu commit’ledim ki” dedirten durumların ana kaynağı. Özellikle büyük diff’lerde gözden kaçan şeyler olabiliyor.
git commit: Mesaj Yazmak Bir Sanattır
git commit -m "değişiklik yapıldı" yazan birini gördüğümde içim sıkışıyor. Bu mesaj hiçbir şey anlatmıyor. Üç ay sonra bu projeye döndüğünde ya da bir ekip arkadaşın bu tarihi incelediğinde hiçbir işe yaramıyor.
# Basit commit
git commit -m "Nginx upstream timeout değeri 30s'ye yükseltildi"
# Uzun açıklama eklemek için editörü aç
git commit
# Stage'e almadan direkt commit (tracked dosyalar için)
git commit -a -m "Tüm değişen tracked dosyaları commit'le"
# Önceki commit'i düzelt (henüz push etmediysen)
git commit --amend -m "Düzeltilmiş commit mesajı"
İyi bir commit mesajının yapısı konusunda çok tartışma var. Ben şu basit kuralı öneriyorum: “Bu commit uygulandığında ne olur?” sorusunu cevaplayan bir mesaj yaz.
“fixed bug” değil, “Kullanıcı oturumu kapandığında session cookie temizlenmiyordu, düzeltildi”
“update config” değil, “Redis bağlantı pool boyutu 10’dan 50’ye çıkarıldı – yüksek trafik testlerinden sonra”
# Çok satırlı commit mesajı için
git commit -m "Rate limiting middleware eklendi
- Express-rate-limit paketi entegre edildi
- IP başına dakikada 100 istek limiti
- Aşıldığında 429 Too Many Requests döndürülüyor
- /api/auth endpoint'i için ayrı, daha kısıtlı limit (20/dk)"
–amend ile Son Commit’i Düzelt
Commit ettikten hemen sonra küçük bir hata fark ettiğinde ya da mesajı değiştirmek istediğinde:
# Mesajı düzelt ve/veya ek dosya ekle
git add forgotten-file.py
git commit --amend --no-edit # Mesajı değiştirmeden dosyayı ekle
# Sadece mesajı değiştir
git commit --amend -m "Doğru mesaj bu"
Kritik uyarı: --amend yeni bir commit object oluşturur ve commit hash’i değişir. Eğer bu commit’i zaten push ettiysen ve başkaları üzerine iş yapmışsa, amend kullanmak tehlikeli. Sadece henüz push etmediğin local commit’lerde kullan.
git push: Paylaşmak Sorumluluk İster
Push işlemi görünürde basit. Ama birkaç senaryoyu bilmeden prod’da ciddi karışıklıklar yaratabilirsin.
# En temel kullanım - mevcut branch'i origin'e push et
git push origin main
# Upstream'i otomatik takip et (ilk push'ta kullanışlı)
git push -u origin feature/user-authentication
# Tüm branch'leri push et
git push --all origin
# Tag'leri push et (varsayılan olarak push edilmez!)
git push origin --tags
# Belirli bir tag'i push et
git push origin v2.1.3
-u parametresi (--set-upstream) önemli. Bunu bir kez kullandıktan sonra o branch için sadece git push yazman yeterli. Remote ve branch bilgisini tekrar tekrar yazmak zorunda kalmıyorsun.
Force Push: En Tehlikeli Komut
# TEHLIKELI - sadece ne yaptığını biliyorsan kullan
git push --force origin feature/my-branch
# Daha güvenli alternatif
git push --force-with-lease origin feature/my-branch
--force-with-lease kritik. Normal --force, remote’da ne olduğuna bakmadan üstüne yazar. Biri aynı branch’e push etmişse silersin. --force-with-lease ise “remote’daki bu branch son bildiğim haldeyse zorla yaz, değilse hata ver” der. Production branch’larında force push yasak olmalı, ama feature branch’larında --force-with-lease kullanmak makul.
Gerçek senaryo: Bir feature branch üzerinde çalışıyorsun, rebase yaptın ve history değişti. Bu branch’i push etmek için force push gerekiyor. --force-with-lease kullan, ekibine haber ver.
Push Sırasında Sık Karşılaşılan Hatalar
# "rejected - non-fast-forward" hatası aldığında
# Remote'da senin bilmediğin commit'ler var demek
# Önce pull yap, sonra push et
git pull --rebase origin main
git push origin main
git pull: Güncelleme Sandığın Kadar Basit Değil
git pull aslında iki komutun birleşimi: git fetch + git merge. Bu ayrımı bilmemek çok sayıda “neden merge commit oluştu?” sorusuna yol açıyor.
# Standart pull
git pull origin main
# Sadece remote'dan bilgileri indir, birleştirme yapma
git fetch origin
# Fetch sonrası nelerin değiştiğini gör
git log HEAD..origin/main --oneline
# Rebase ile pull - lineer history için
git pull --rebase origin main
Merge mi, Rebase mi?
Bu tartışma bitmez ama pratikte şunu söyleyebilirim: Takım içi bir convention olması gerekiyor ve herkes buna uymalı.
git pull varsayılan olarak merge yapar. Bu, “merge commit” adı verilen ekstra commit’ler oluşturur. Aktif bir projede bu commit’ler history’yi karmaşık hale getirebilir.
# Her pull'da rebase kullanmak istiyorsan global ayar
git config --global pull.rebase true
# Ya da sadece bu repo için
git config pull.rebase true
# Değişiklikleri almak ama kendi commit'lerini üstüne koymak için
git pull --rebase origin develop
# Conflict çıkarsa
# Dosyaları düzelt, sonra:
git add çözülen-dosya.py
git rebase --continue
# Ya da rebase'den vazgeç
git rebase --abort
Conflict Çıktığında Paniklememe Rehberi
Pull sonrası conflict görmek normal ve yönetilebilir bir durum.
# Conflict'li pull sonrası durum
git status
# Hem modified hem unmerged olarak görünen dosyalar conflict'li
# Dosyayı aç, <<<<<<, ======, >>>>>> işaretlerini bul
# Hangisini istediğini seç ya da ikisini birleştir
# Sonra:
git add çözülen-dosya.conf
git commit # Merge commit oluşturulur
# Aborta gitmek istersen
git merge --abort
Bir konfig dosyasında hem senin değişiklikleriniz hem de bir ekip arkadaşının değişiklikleri çakışıyorsa, doğrudan git checkout --theirs ya da git checkout --ours kullanarak bir tarafı tamamen seçebilirsin:
# Remote'dan gelen versiyonu kabul et
git checkout --theirs config/database.yml
# Kendi versiyonunu koru
git checkout --ours config/database.yml
git add config/database.yml
git commit
Günlük İş Akışı: Bunları Birlikte Kullan
Teori yeterli, şimdi gerçekçi bir günlük senaryo:
# Sabah, işe başlarken
git status # Dün ne bıraktım?
git pull --rebase origin develop # Ekipten ne gelmiş?
# Yeni özellik için branch
git checkout -b feature/cache-redis-integration
# ... çalış, çalış, çalış ...
# Ne değiştirdim?
git diff
git status
# Mantıklı parçalara bölerek stage'e al
git add -p src/cache/redis_client.py
git add src/cache/config.py
# Kontrol et
git diff --staged
# Commit
git commit -m "Redis cache client implementasyonu eklendi
- Bağlantı pool yönetimi
- TTL desteği
- Connection error için retry mekanizması"
# Daha fazla değişiklik...
git add tests/test_redis_cache.py
git commit -m "Redis cache için unit testler eklendi"
# Push (ilk kez, upstream set et)
git push -u origin feature/cache-redis-integration
Gün sonunda ya da PR açmadan önce:
# develop branch'ında ne var güncelce bak
git fetch origin
git log HEAD..origin/develop --oneline
# Eğer değişiklik varsa rebase yap
git rebase origin/develop
# Conflict çıktıysa çöz, devam et
git rebase --continue
# Güncel hali push et
git push --force-with-lease origin feature/cache-redis-integration
.gitignore ile add’i Kontrol Altında Tut
git add . kullanacaksan .gitignore dosyasının doğru konfigüre edilmesi şart. Aksi halde istemediğin dosyalar commit’e giriyor.
# .gitignore oluştururken işe yarayan kaynak
# gitignore.io sitesinden dilediğin stack için hazır şablon alabilirsin
# Yanlışlıkla eklenen bir dosyayı stage'den çıkarmak
git restore --staged secrets.env
# Eski Git versiyonlarında
git reset HEAD secrets.env
Eğer bir dosya zaten commit’lendi ve .gitignore‘a eklemek istiyorsan:
# Önce tracking'den çıkar
git rm --cached config/local_settings.py
# .gitignore'a ekle
echo "config/local_settings.py" >> .gitignore
# Commit et
git add .gitignore
git commit -m "local_settings.py tracking'den çıkarıldı"
Durumu Anlatan Komutlar: add/commit/push/pull’un Yardımcıları
Bu dört komut tek başına yetmez. Yanlarında çalıştığın komutlar da günlük rutinin parçası olmalı:
# Genel durum
git status
# Kısa özet halinde durum
git status -s
# Commit geçmişi
git log --oneline -10
# Görsel branch grafiği
git log --oneline --graph --all -15
# Remote bilgisi
git remote -v
# Hangi branch'teyim ve tracking durumu
git branch -vv
Sonuç
git add, git commit, git push, git pull. Dört komut, ama her birinin derinliği var. Yıllarca kullansan bile “bir dakika, burada tam olarak ne oldu?” dediğin anlar oluyor.
En pratik tavsiyem şu: git status ve git diff --staged komutlarını refleks haline getir. Commit etmeden önce ne commit ettiğini bil. Push etmeden önce commit geçmişine bak. Pull etmeden önce ne kadar ileride ya da geride olduğunu anla.
Hızlı çalışmak ile dikkatli çalışmak arasında seçim yapmak zorunda değilsin. git add -p ile seçici olmak, anlamlı commit mesajları yazmak, --force-with-lease kullanmak gibi alışkanlıklar başlangıçta yavaşlatıyormuş gibi hissettirse de bir ay sonra bakıyorsun, hem sen hem de ekibindekiler tarihte gezinirken, conflict çözerken, “bunu kim ne zaman değiştirdi?” sorusunu cevaplarken çok daha az zaman harcıyor. Bu zaman birikimi sonunda önemli bir fark yaratıyor.
Git’i ne kadar erken “doğru” kullanmaya başlarsan, ileride kurtarmak zorunda kaldığın durumların sayısı o kadar azalıyor. Ve inan bana, gece yarısı production’da bir şeyleri geri almaya çalışmak yerine uyumak çok daha iyi hissettiriyor.
