İçeriğe geç
Muhammet Şafak
Araçlar & Teknolojiler 10 dk okuma

Production'da Git: Senior'ın Pratik Rehberi

Workflow seçiminden bisect ile bug avına, rebase disiplininden worktree ve signed commit'lere — Git'i bir yıllarca kullandıktan sonra fark ettiklerim.


Git’i 5 yıl kullanan biri ile 15 yıl kullanan biri arasındaki fark, bildikleri komut sayısı değil. Fark şu: ilki Git’i kullanmayı öğrenmiş, ikincisi Git’le neyi ne zaman yapacağına karar vermeyi öğrenmiş.

Bu yazı temel komutlar üzerine değil. add, commit, push, pull zaten biliniyor. Bu yazı, kararlar ve az bilinen ama hayat kurtaran parçalar üzerine: hangi workflow’u seçeceğine, tarihçeni nasıl temiz tutacağına, hangi commit’in bug’ı soktuğunu nasıl bulacağına, force-push’tan nasıl kurtulacağına dair.

Daha temel branch/merge/PR disiplini için daha önce yazdığım Git akışı: branch, merge ve pull request disiplini yazıma bakabilirsin. Bu yazı onun bir üst katmanı.

1. Workflow Seçimi: GitFlow vs GitHub Flow vs Trunk-Based

“Git workflow’unuz ne?” sorusunun cevabı yoksa, bilmeden bir tane seçmişsiniz demektir — büyük ihtimalle yanlışını. Üç ana akış var:

GitFlow

Vincent Driessen’in 2010’da yayınladığı klasik model: main (production), develop (entegrasyon), feature/*, release/*, hotfix/* dalları. Her şey planlı, her yayın için ayrı release branch.

Ne zaman seçilir: Versiyonlu, planlı yayın takvimi olan ürünler. Mobile uygulamalar (App Store yayın döngüsü), enterprise yazılım, “v2.3.1” gibi numaralı sürümler dağıtan paket projeleri.

Ne zaman seçilmez: Web uygulamaları, sürekli deploy edilen sistemler. GitFlow’u SaaS’a uygulamak — release branch’ler çoğunlukla anlamsız, develop her zaman main’in 2-3 commit gerisinde takılır.

GitHub Flow

Sade model: main her zaman deploy edilebilir, her özellik için kısa ömürlü branch, PR ile merge, merge sonrası anında deploy. Release branch yok, develop branch yok.

Ne zaman seçilir: Küçük-orta takım (3-15 kişi), CI/CD kurulu, günde bir-iki kez deploy. Türkiye’deki agency’lerin ve startup’ların büyük çoğunluğu burada doğru karar verir.

Ne zaman seçilmez: 50+ kişilik takımlar — main’e PR sırası çok uzun olur. Veya hâlâ haftalık manuel deploy ediyorsanız — bu workflow ön koşulu “deploy bir saniyelik iş” olması.

Trunk-Based Development

Her geliştirici doğrudan main’e commit’ler (veya çok kısa ömürlü branch). Yarım kalan özellikler feature flag arkasında saklanır. Branch ömrü maksimum birkaç saat.

Ne zaman seçilir: Büyük takımlar (Google, Facebook, Spotify), continuous deployment kültürü, feature flag altyapısı kurulu. Yüksek disiplin gerektirir.

Ne zaman seçilmez: Feature flag altyapınız yoksa veya CI test coverage’ınız düşükse — main’i bozmak çok kolaylaşır.

Karar Matrisi

DurumTavsiye
Mobile uygulama, App Store dağıtımıGitFlow
SaaS, küçük-orta takım, günlük deployGitHub Flow
3 kişilik freelance agencyGitHub Flow (ya da daha basit: PR’sız doğrudan)
50+ geliştirici, deploy günde 10+Trunk-based
Versiyonlu paket / libraryGitFlow (release branch yararlı)
Plansız, “ne lazımsa o”GitHub Flow (varsayılan)

Anti-pattern: Üçünü karıştırmak. “Hem develop branch’ımız var hem feature flag kullanıyoruz hem her özellik release branch’le çıkıyor” — bu üç ayrı disiplinin yarısını uygulamak, hiçbirinin avantajını almamak demek.

2. Tarihçe Hijyeni: Rebase ve Autosquash

PR açtın, review aldın, “şu değişikliği yap, şu typo’yu düzelt” yorumları geldi. Üç ayrı düzeltme commit’i atıp PR’ı update’ledin. Şimdi PR’da 1 anlamlı commit + 3 “address review” commit’i var. Merge sonrası git log bunu okuyacak.

Bunu çözmenin iki yolu var. Squash merge (GitHub’da tek tıkla) her şeyi tek commit’e indirir — ama bu da farklı bir aşırılık: 200 satırlık 3 mantıksal değişiklik tek commit’e sıkışır.

Doğru orta yol: interactive rebase + autosquash.

Fixup Commit Workflow

Review yorumunu adresliyorsun. Yeni commit yerine:

git add .
git commit --fixup <orijinal-commit-hash>

Bu komut fixup! <orijinal commit message> başlıklı bir commit oluşturur. Aynı şekilde 2-3 fixup daha atabilirsin.

PR mergea hazır olduğunda:

git rebase -i --autosquash main

Editör açıldığında her fixup, ait olduğu commit’in altına otomatik konumlanmış ve fixup olarak işaretlenmiş olarak gelir. Hiçbir şey değiştirmeden kaydet — Git fixup’ları ait oldukları commit’lerin içine yedirir. Sonuç: tarihçe git commit --fixup öncesi kadar temiz, ama review’da yapılan düzeltmeler ilgili commit’in parçası olmuş.

Bunu daha da kolaylaştırmak için:

git config --global rebase.autosquash true

Bu ayardan sonra git rebase -i her seferinde otomatik autosquash uygular. Bir daha hatırlamana gerek kalmaz.

Golden Rule

Public branch’lerde — main, develop, başkalarının çektiği herhangi bir branch — asla rebase yapma. Tarihçeyi yeniden yazmak, başkasının clone’unda bulunan commit hash’lerini değersizleştirir. Sonuç: merge conflict cehennemi.

Rebase senin private branch’inde yapılır. PR henüz merge edilmemişse, branch hâlâ senindir — rebase serbest. Merge edildikten sonra dokunma.

3. Bisect ile Bug Avı

Klasik senaryo: production’da bir regression var. Geçen hafta çalışıyordu, bugün çalışmıyor. Aradaki 80 commit’in hangisi soktu? Manuel binary search yapacaksan 4 saatlik bir iş. Bisect ile 6 dakika.

Manuel Bisect

git bisect start
git bisect bad HEAD          # şu an bozuk
git bisect good v2.3.0       # bu sürümde çalışıyordu

Git seni iki commit’in ortasındaki commit’e götürür. Test edersin:

  • Bozuk: git bisect bad
  • Çalışıyor: git bisect good

Her cevap arama alanını yarıya indirir. 80 commit için ~7 adımda doğru commit’i bulur.

Bitirince:

git bisect reset

Otomatik Bisect

Eğer “bozuk mu çalışıyor mu” sorusu bir komutla cevaplanabiliyorsa (örn. bir test):

git bisect start HEAD v2.3.0
git bisect run npm test -- --testPathPattern=image-upload

Git senin yerine her commit’te testi çalıştırır, exit code 0 ise “good”, değilse “bad” sayar. Sen bir kahve içersin, 80 commit içinden bug’ı sokan commit’i ekranda görürsün.

Bisect’in en güzel tarafı: Hangi commit’in bug’ı soktuğunu bulduğunda, o commit’in author’ı, mesajı ve tam değişiklik kümesi gözünün önündedir. Git blame’in tek satıra bakması yerine, bisect bütün hikâyeyi verir.

4. Reflog: “Ben Commit’lerimi Kaybettim” Anları

Reset —hard yaptın. Wrong branch’e merge ettin. Force-push yedin. “Commit’lerim gitti.” Hayır, gitmedi.

Git, HEAD’in son ~90 günlük tüm hareketlerini reflog’da tutar. Hiçbir commit gerçekten silinmez — sadece ona referans veren branch/tag kalmaz. Reflog ile geri bulunabilir.

git reflog

Çıktı şuna benzer:

abc1234 HEAD@{0}: reset: moving to HEAD~3
def5678 HEAD@{1}: commit: addressed review feedback
ghi9012 HEAD@{2}: commit: refactor user service
jkl3456 HEAD@{3}: pull: Fast-forward

“Sildiğini sandığın” commit def5678. Geri getirmek için:

git checkout def5678
git switch -c kurtarilan-branch

veya doğrudan branch oluştur:

git branch kurtarilan-branch def5678

Önemli: Reflog lokaldir. Sadece kendi makinendeki Git’in tuttuğu kayıt. Eğer başkasının force-push’u sebebiyle commit kaybettiysen, onların reflog’una bakmak gerekir.

İkinci önemli nokta: garbage collection eninde sonunda gerçekten siler. Varsayılan: reflog 90 gün, “unreachable” commit’ler 30 gün sonra GC’lenir. Yani “geçen yıl yaptığım bir şeyi” kurtarmak için reflog güvenli değil.

5. Worktree: Aynı Repo, Birden Fazla Klasör

Bir feature üzerinde çalışıyorsun, branch’inde yarım kalmış değişiklikler var. Birden acil bir hotfix gerekti — main’e geçmen lazım ama mevcut işini kaybetmek istemiyorsun.

Klasik çözüm: git stash, branch değiştir, hotfix, geri dön, stash pop. İşliyor ama:

  • Stash’ler birikiyor
  • IDE state’i kaybediyor
  • Açık dosyalar değişiyor
  • Hangi stash neyin diye unutuyorsun

Daha iyi yol: worktree.

git worktree add ../hotfix-2026-05 main

Bu komut, aynı repo için ama farklı klasörde ve main branch’inde ikinci bir çalışma alanı oluşturur. Feature branch’in dokunulmamış kalır, IDE’de iki ayrı klasör açabilirsin, hotfix’i bitirip:

cd ../hotfix-2026-05
# çalış, commit, push
cd ../proje
git worktree remove ../hotfix-2026-05

Worktree’leri listelemek:

git worktree list

Tipik kullanım senaryoları:

  • Code review için PR branch’ini ayrı klasörde çekme (kendi work’üne dokunmadan)
  • Birden fazla feature paralel geliştirme
  • Acil hotfix için anlık ortam
  • Eski bir release’e bakmak gerektiğinde (git worktree add ../v2.3 v2.3.0)

Bir kez kullanmaya başlayınca stash’ten daha çok worktree açtığını fark edersin.

6. Signed Commits: GPG ve SSH

Git, commit’lere yazılan author bilgisini kontrol etmez. git config user.email "[email protected]" yazıp commit atabilirsin — Git itiraz etmez. Bu, supply chain saldırılarının en sevdiği açık.

Çözüm: commit imzalama. Her commit’i kendi anahtarınla imzalarsın, GitHub/GitLab onu doğrular ve “Verified” rozetiyle gösterir.

İki yöntem var.

GPG ile İmzalama (klasik)

gpg --full-generate-key
gpg --list-secret-keys --keyid-format=long
git config --global user.signingkey <KEY_ID>
git config --global commit.gpgsign true

GPG public key’i GitHub’a yüklersin (Settings → SSH and GPG keys). Bundan sonra her commit otomatik imzalanır.

Sorun: GPG kurulumu, key management, expire eden key’ler, başka cihaza taşırken çıkan zorluklar. Çoğu geliştirici “GPG sonra bakarım” diye erteler ve hiç imzalanmamış commit atmaya devam eder.

SSH ile İmzalama (modern)

Git 2.34+ ve GitHub 2022 sonrası: SSH key’inle commit imzalayabilirsin. Zaten Git push için kullandığın SSH key’i.

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

GitHub’da: Settings → SSH and GPG keys → “New SSH key” → key type’ı Signing Key seç (Authentication key ayrı).

Bu kadar. GPG’nin tüm kurulum zorluğunu atlamış olursun. 2024 sonrası başlayan biri için SSH signing varsayılan seçim olmalı.

Doğrulamak için:

git log --show-signature -1

Çıktıda Good "git" signature for ... görmen gerekir.

7. Tehlikeli Komutlar: --force Disiplini

Üç komut iş kaybettirebilir:

git push --force yerine --force-with-lease

--force upstream’i hiç sormadan ezer. Eğer branch’ten başka biri bir commit pushladıysa, onun commit’i kaybolur.

# Tehlikeli
git push --force

# Güvenli
git push --force-with-lease

--force-with-lease upstream’in son hâlinin senin son fetch ettiğin hâlle aynı olduğunu kontrol eder. Birisi araya commit attıysa push reddedilir, “tekrar fetch et” denir.

Bunu da otomatikleştirmek için alias:

git config --global alias.pushf 'push --force-with-lease'

git reset --hard

Working directory + index + HEAD’i hedef commit’e sıfırlar. Kaydetmediğin değişiklikler gider. Reflog’a girmez — yani geri alınamaz.

Alternatif yaklaşımlar:

  • Sadece commit’i geri alacaksan: git reset --soft HEAD~1 (değişiklikleri staging’de bırakır)
  • Working directory’yi temizleyeceksen: önce git stash ile yedek al, sonra reset

git clean -fd

Working directory’deki tracked olmayan dosyaları siler. .gitignore’da olanları da silmek için -x ekler. Yanlış parametreyle çağrılırsa node_modules veya local config’ler uçar.

Önce git clean -nfd (dry run) çalıştır, ne silineceğini gör; sonra -f ile gerçek silmeyi yap.

8. Az Bilinen Güçlü Komutlar

git rerere — “Reuse Recorded Resolution”

Aynı merge conflict’i defalarca çözüyorsan (uzun süreli feature branch + sık main rebase senaryosu klasik), rerere bir kez çözdüğün conflict’i hatırlar, bir sonraki sefer otomatik uygular:

git config --global rerere.enabled true

Bir defa açtıktan sonra unutursun, sessizce arka planda çalışır. Bir hafta sonra fark edersin: “Bu conflict’i çözdüğümü hatırlamıyorum ama Git çözmüş?”

git log -S ve git log -G — Pickaxe

Bir kod parçasının ne zaman ve hangi commit ile eklendiğini/silindiğini bulmak için:

git log -S"OldFunctionName"     # belirli stringin commit'lerini bul
git log -G"regex_pattern"        # regex ile ara

“Bu deprecated fonksiyon ne zaman kaldırılmıştı?” sorusunun en hızlı cevabı bu. Tüm tarihçeyi tarar, geriye dönük arkeoloji yapar.

git blame -L

Tüm dosya değil, belirli satır aralığını blame’le:

git blame -L 45,80 src/Service/PaymentGateway.php

Büyük dosyalarda altın değerinde — sadece ilgilendiğin bölümün yazılış hikâyesini görürsün.

git switch ve git restore

Git çok eskiden beri git checkout’u iki farklı iş için kullanıyor: branch değiştirme ve dosya geri alma. Git 2.23 (2019) bunları ayırdı:

git switch main                     # branch değiştir
git switch -c yeni-feature          # yeni branch oluştur ve geç
git restore dosya.php               # working directory'deki değişikliği geri al
git restore --staged dosya.php      # staging'den geri al (unstage)

checkout hâlâ çalışıyor ama yeni kod yazarken switch/restore kullanmak daha net. Junior’lara yeni komutları öğretmek, eski checkout’un iki anlamlı kullanımının yarattığı karışıklığı engelliyor.

Kapanış

Git’i öğrenmek bir kerelik bir iş değil. İlk yıl add, commit, push öğrenirsin. İkinci yıl branch, merge, rebase. Beşinci yıl bisect, reflog, worktree. On beşinci yıl rerere, pickaxe, signed commits. Komutlar 20 yaşında ama her geçen yıl yeni bir tane öğreniyorsun çünkü her yeni senaryo yeni bir araç gerektiriyor.

Bu yazıda işlediğim komutların hiçbiri yeni değil — bisect 2005’ten beri var. Yeni olan, onları ne zaman kullanacağını bilmek. Senior bir geliştiriciyi diğerlerinden ayıran şey komut sayısı değil; doğru komutu doğru anda hatırlamak.

Git temelleri için 2022’de yazdığım Medium serisine bakabilirsin — kurulumdan branch ve cherry-pick’e kadar başlangıç-orta seviye komutlar orada. Bu yazı onun üstüne, yıllar içinde gerçek production sorunlarının öğrettiği parçaları topluyor.

Yeni bir Git komutu öğrendiğinde — bu hafta öğreneceğin bir tane var — onu ~/.gitconfig’inde alias olarak kaydet. İki ay sonra adını unutursun, alias unutmaz.


Komut Hızlı Referansı

İhtiyaçKomut
Review düzeltmesi commit’igit commit --fixup <hash>
Fixup’ları yedirgit rebase -i --autosquash main
Bug’ı hangi commit soktu?git bisect startbad/good
Otomatik bisectgit bisect run <test-command>
Kaybolan commit’i bulgit reflog
Paralel çalışma alanıgit worktree add <path> <branch>
SSH ile imzalamagit config gpg.format ssh
Güvenli force pushgit push --force-with-lease
Conflict’leri hatırlagit config rerere.enabled true
String’in commit’ini bulgit log -S"text"
Satır aralığı blamegit blame -L 10,50 file
Modern branch geçişigit switch <branch>
Etiketler: #Git
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi