History Rewriting
Giriş — Fotoğraf Albümünü Düzenlemek
Bir aile fotoğraf albümü düşünün. Yıllar içinde yüzlerce fotoğraf birikmiş. Ama bazı fotoğraflar var ki: bulanık çıkmış olanlar, yanlışlıkla çekilenler, keşke olmasa dediğiniz anlar. Şimdi bu albümü baştan düzenlemek istiyorsunuz — bazı fotoğrafları çıkarmak, bazılarını kes-yapıştır yapmak, bazılarındaki isimleri düzeltmek.
Git'in history rewriting araçları da buna benzer. Repository'nizin geçmişini düzenlemek, büyük dosyaları temizlemek, yanlış yazar bilgisini düzeltmek veya hassas bilgileri kaldırmak için kullanılır. Ama tıpkı fotoğraf albümünde olduğu gibi — değişiklik yaptığınızda "orijinal" artık kaybolur. Bu yüzden dikkatli olmak gerekir.
Ne Zaman History Rewriting Gerekir?
Gerçek dünya senaryoları:
1. 🔑 Secret sızıntısı
→ API anahtarı commit'e girmiş, geçmişten temizlenmeli
2. 📦 Büyük dosya
→ 500MB'lık video dosyası commit edilmiş, repo şişmiş
3. 👤 Yanlış author bilgisi
→ Kişisel email ile commit'ler atılmış, şirket emaili olmalı
4. 📁 Hassas dosya
→ Müşteri veritabanı yedeği yanlışlıkla push edilmiş
5. 🧹 Repo temizliği
→ Eskiden kullanılan ama artık gereksiz büyük binary'ler
6. 📂 Repo bölme/birleştirme
→ Monorepo'dan bir klasörü ayrı repo'ya çıkarma⚠️ Dikkat: History rewriting, paylaşılmış branch'lerde (main, develop) yapıldığında tüm ekibi etkiler. Herkesin tekrar clone/rebase yapması gerekir. Bu yüzden paylaşılmış branch'lerde history rewriting son çare olmalı ve ekiple koordineli yapılmalıdır.
git filter-repo — Modern Standart
Neden filter-repo?
Eskiden git filter-branch kullanılırdı ama o artık resmi olarak deprecated (kullanımdan kaldırılmış). Nedenleri:
filter-branch sorunları:
❌ Aşırı yavaş (büyük repo'larda saatler)
❌ Kullanımı karmaşık
❌ Hata yapma riski yüksek
❌ Bellek tüketimi fazla
❌ Tag'leri düzgün işlemez
filter-repo avantajları:
✅ 10-50x daha hızlı
✅ Kullanımı basit
✅ Güvenli (orijinali bozma riski düşük)
✅ Bellek verimli
✅ Git resmi önerisiKurulum
# pip ile (Python gerekli)
pip install git-filter-repo
# macOS
brew install git-filter-repo
# Doğrulama
git filter-repo --versionBüyük Dosyaları Geçmişten Silme
En yaygın kullanım: geçmişte commit edilmiş büyük dosyaları temizlemek.
# Önce: Repo'daki en büyük dosyaları bul
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print $3, $4}' | \
sort -rn | head -20
# Çıktı örneği:
# 524288000 data/dump.sql (500MB!)
# 104857600 assets/video.mp4 (100MB)
# 52428800 build/app.jar (50MB)
# 10485760 backup/db.bak (10MB)# Belirli dosyayı geçmişten tamamen sil
git filter-repo --path data/dump.sql --invert-paths
# Birden fazla dosya/klasör
git filter-repo \
--path data/dump.sql \
--path assets/video.mp4 \
--path build/ \
--invert-paths
# Belirli uzantıdaki tüm dosyaları sil
git filter-repo --path-glob '*.mp4' --invert-paths
git filter-repo --path-glob '*.zip' --invert-paths
# Boyuta göre sil (1MB'dan büyük blob'lar)
git filter-repo --strip-blobs-bigger-than 1M# Sonra: Temizlik ve push
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# Force push (TÜM branch'ler)
git push origin --force --all
git push origin --force --tags⚠️ Dikkat:
git filter-repoçalıştırdığınızda, tüm commit SHA'ları değişir. Bu, remote ile ilişkiyi koparır.--forceile push etmeniz gerekir ve tüm ekip üyelerinin repo'yu yeniden clone'laması gerekir.
Tam Workflow (Güvenli)
# 1. YEDEK AL
cp -r my-repo my-repo-backup
# 2. Temiz bir clone ile başla
git clone --mirror https://github.com/acme/my-repo.git
cd my-repo.git
# 3. Büyük dosyaları analiz et
git filter-repo --analyze
# Rapor oluşturulur:
# .git/filter-repo/analysis/
# blob-shas-and-paths.txt ← en büyük dosyalar burada
# path-all-sizes.txt
# ...
# 4. Temizle
git filter-repo --strip-blobs-bigger-than 10M
# 5. Sonucu doğrula
du -sh . # Repo boyutunu kontrol et
# 6. Force push
git push --mirror https://github.com/acme/my-repo.git
# 7. Ekibe bildir
# "Repo geçmişi temizlendi. Lütfen yerel kopyanızı silin
# ve yeniden clone edin."Author Bilgisini Değiştirme
Yanlış email ile atılmış commit'leri düzeltmek:
# Tüm commit'lerde eski email'i yeniyle değiştir
git filter-repo --email-callback '
return email.replace(b"ahmet@personal.com", b"ahmet@company.com")
'
# Daha karmaşık dönüşüm: mailmap dosyası ile
cat > .mailmap << 'EOF'
Ahmet Yılmaz <ahmet@company.com> <ahmet@personal.com>
Ahmet Yılmaz <ahmet@company.com> <ahmet.yilmaz@oldcompany.com>
Mehmet Demir <mehmet@company.com> <mehmet123@gmail.com>
EOF
git filter-repo --mailmap .mailmapKlasör Çıkarma (Repo Bölme)
Monorepo'dan bir klasörü kendi repo'suna çıkarmak:
# my-monorepo/
# ├── frontend/ ← bunu ayrı repo yapacağız
# ├── backend/
# └── shared/
# 1. Clone
git clone my-monorepo frontend-repo
cd frontend-repo
# 2. Sadece frontend/ klasörünü tut, gerisini sil
git filter-repo --path frontend/ --path-rename frontend/:
# Sonuç: frontend/ içindeki dosyalar root'a taşındı
# ve sadece frontend'i etkileyen commit'ler kaldıHassas Veriyi Silme
# Belirli bir string'i geçmişten sil (ör: API key)
git filter-repo --replace-text expressions.txt
# expressions.txt içeriği:
# regex:AKIAIOSFODNN7[A-Z0-9]{13}==>REDACTED_AWS_KEY
# literal:SuperSecretPassword123==>REDACTED
# glob:sk_live_*==>REDACTED_STRIPE_KEYBFG Repo-Cleaner — Hızlı ve Basit Alternatif
BFG Nedir?
BFG Repo-Cleaner, filter-repo'dan önce var olan ve hâlâ popüler bir araçtır. Özellikle büyük dosya temizleme ve secret kaldırma konusunda çok hızlı ve kullanımı kolaydır.
# Kurulum (Java gerekli)
# https://rtyley.github.io/bfg-repo-cleaner/ adresinden jar indir
# veya brew ile:
brew install bfg
# Clone (bare)
git clone --mirror https://github.com/acme/my-repo.git
cd my-repo.gitBüyük Dosya Temizleme
# 100MB'dan büyük dosyaları sil
bfg --strip-blobs-bigger-than 100M
# Belirli dosyaları sil
bfg --delete-files "*.zip"
bfg --delete-files "dump.sql"
# Belirli klasörleri sil
bfg --delete-folders "node_modules"
bfg --delete-folders ".cache"Secret Temizleme
# passwords.txt dosyasındaki değerleri geçmişten sil
cat > passwords.txt << 'EOF'
AKIAIOSFODNN7EXAMPLE
SuperSecretPassword123
sk_live_abc123xyz456
EOF
bfg --replace-text passwords.txt
# Bu, tüm geçmişte bu string'leri "***REMOVED***" ile değiştirirTemizlik ve Push
# BFG sonrası temizlik
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# Force push
git push --force --all
git push --force --tagsfilter-repo vs BFG
Özellik │ filter-repo │ BFG
──────────────────┼────────────────────┼────────────────────
Hız │ Çok hızlı │ Hızlı
Kullanım kolaylığı│ Orta │ Çok kolay
Esneklik │ Çok yüksek │ Sınırlı
Author değiştirme │ ✅ │ ❌
Path rename │ ✅ │ ❌
Secret temizleme │ ✅ │ ✅ (daha kolay)
Büyük dosya silme │ ✅ │ ✅ (daha kolay)
Repo bölme │ ✅ │ ❌
Bağımlılık │ Python │ Java
Resmi önerilen │ ✅ (Git projesi) │ HayırÖzet: Basit işler (büyük dosya silme, secret temizleme) için BFG daha pratik. Karmaşık işler (author değiştirme, repo bölme, path yeniden adlandırma) için filter-repo gerekli.
Repo Boyutunu Analiz Etme
git-sizer
# Kurulum
brew install git-sizer # macOS
# veya: https://github.com/github/git-sizer/releases
# Analiz
git-sizer --verbose
# Çıktı:
# Processing blobs: 1234
# Processing trees: 567
# Processing commits: 890
#
# Overall repository size:
# Commits 890
# Trees 1,234
# Blobs 5,678
# Annotated tags 5
#
# Biggest objects:
# Maximum blob size 524 MB ← SORUN!
# Maximum tree entries 450
#
# Biggest checkouts:
# Maximum path length 120
# Maximum path depth 8
# Number of files 2,345
# Total size of files 1.2 GB ← SORUN!Manuel Analiz
# Top 10 büyük dosya (tüm geçmiş)
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print $3, $4}' | \
sort -rn | head -10 | \
numfmt --to=iec --field=1
# Çıktı:
# 500M data/dump.sql
# 100M assets/video.mp4
# 50M build/app.jar
# 10M backup/db.bak
# Repo toplam boyutu
du -sh .git/
# Object boyutları
git count-objects -vH
# count: 1234
# size: 2.50 MiB
# in-pack: 56789
# size-pack: 1.20 GiB ← packfile boyutu
# prune-packable: 0
# garbage: 0Gerçek Dünya Senaryoları
Senaryo 1: Production Sızan AWS Key
# Durum: AWS_SECRET_ACCESS_KEY geçmişte commit edilmiş
# 2 hafta önce keşfedildi
# 1. HEMEN AWS key'i rotate edin (en önemli adım!)
# AWS Console → IAM → Security Credentials → Rotate
# 2. Repo'yu temizle
git clone --mirror https://github.com/acme/app.git
cd app.git
# 3. Secret'ı bul
git log -p --all -S 'AKIAIOSFODNN7EXAMPLE' | head -20
# 4. Temizle
cat > secrets.txt << 'EOF'
AKIAIOSFODNN7EXAMPLE
wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
EOF
bfg --replace-text secrets.txt
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force --all
# 5. Ekibi bilgilendir
# 6. .gitignore'a .env ekle
# 7. Pre-commit hook ile secret scanning ekleSenaryo 2: 2GB Video Dosyası
# Durum: Bir geliştirici demo.mp4 (2GB) commit etmiş
# .gitignore'da *.mp4 yokmuş
# 1. Dosyayı bul
git filter-repo --analyze
cat .git/filter-repo/analysis/blob-shas-and-paths.txt | head -5
# 2. Temizle
git filter-repo --path-glob '*.mp4' --invert-paths
# 3. .gitignore güncelle
echo "*.mp4" >> .gitignore
echo "*.avi" >> .gitignore
echo "*.mov" >> .gitignore
# 4. Force push
git push --force --all
# Sonuç: 2.5GB → 200MB repo 🎉Senaryo 3: Tüm Author Email'lerini Değiştirme
# Durum: Şirket email domain'i değişti
# eski@company.com → eski@newcompany.com
# mailmap oluştur
cat > my-mailmap << 'EOF'
Ahmet <ahmet@newcompany.com> <ahmet@company.com>
Mehmet <mehmet@newcompany.com> <mehmet@company.com>
Ayşe <ayse@newcompany.com> <ayse@company.com>
EOF
# Uygula
git filter-repo --mailmap my-mailmap --force
# Kontrol
git log --format='%an <%ae>' | sort -ugit filter-branch — Eski Yöntem (Deprecated)
git filter-branch artık resmi olarak kullanılması önerilmiyor ama mevcut rehberlerde hâlâ karşınıza çıkabilir. Tarihsel bilgi olarak bilin:
# ⚠️ DEPRECATED — filter-repo kullanın!
# Tüm commit'lerde author email değiştirme (eski yöntem)
git filter-branch --env-filter '
OLD_EMAIL="eski@email.com"
CORRECT_NAME="Yeni İsim"
CORRECT_EMAIL="yeni@email.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
# Bu komut:
# - Çok yavaş (her commit'i bash script ile işler)
# - Bellek verimsiz
# - Hata yapmak kolay
# - Git kendisi "use git-filter-repo instead" diyorfilter-branch kullanma sebepleri:
❌ Çok yavaş — büyük repo'larda saatler sürebilir
❌ Script karmaşıklığı — bash one-liner'lar hata riski yüksek
❌ Git 2.36+ uyarı veriyor: "git-filter-branch has caveats"
❌ Backup dosyaları oluşturur (.git/refs/original/)
filter-repo tercih etme sebepleri:
✅ 10-50x daha hızlı
✅ Declarative API — ne yapacağını söyle
✅ Git takımının resmi önerisi
✅ Daha güvenli (dry-run, analiz)Commit Tarihini Değiştirme
Bazen commit tarihini değiştirmek gerekebilir (örneğin yanlış saat dilimi):
# Son commit'in tarihini değiştir
GIT_COMMITTER_DATE="2026-01-15T10:00:00+03:00" \
git commit --amend --date="2026-01-15T10:00:00+03:00" --no-edit
# filter-repo ile toplu tarih düzeltme
git filter-repo --commit-callback '
import datetime
# Tüm commit'leri 3 saat ileri al (saat dilimi düzeltme)
commit.author_date = commit.author_date.replace(b"+0000", b"+0300")
commit.committer_date = commit.committer_date.replace(b"+0000", b"+0300")
'⚠️ Dikkat: Tarih değiştirme de history rewriting'dir. Tüm commit SHA'ları değişir. Sadece push'lanmamış commit'lerde veya özel durumlar için kullanın.
GitHub'da Geçmiş Temizleme Sonrası
GitHub, force push yapıldığında eski commit'leri hemen silmez. Cache'te kalabilir:
# GitHub cache temizleme:
# 1. Settings → General → Danger Zone → Delete cached objects
# 2. GitHub Support'a ticket açarak temizleme isteme
# Alternatif: Repo'yu silip yeniden oluşturma
# (en kesin yöntem ama issue'lar, PR'lar, star'lar kaybolur)GitHub Secret Scanning ile Entegrasyon
Eğer bir secret commit'lediyseniz:
1. Secret sızdı → GitHub otomatik uyarı gönderir (secret scanning)
2. Secret'ı HEMEN rotate edin
3. Geçmişi temizleyin (filter-repo veya BFG)
4. Force push yapın
5. GitHub → Security → Secret scanning alerts → resolve edin
6. .gitignore ve pre-commit hook güncelleyinInteractive Rebase ile Küçük Düzeltmeler
Büyük araçlar (filter-repo, BFG) her durum için gerekli değildir. Son birkaç commit'i düzeltmek için interactive rebase yeterlidir:
# Son 5 commit'i düzenle
git rebase -i HEAD~5
# Editörde:
pick abc1234 feat: add user model
pick def5678 fix: typo in model
pick ghi9012 feat: add user API
pick jkl3456 WIP: testing ← bunu silmek istiyoruz
pick mno7890 feat: add user tests
# Değiştir:
pick abc1234 feat: add user model
fixup def5678 fix: typo in model ← öncekiyle birleştir
pick ghi9012 feat: add user API
drop jkl3456 WIP: testing ← sil
pick mno7890 feat: add user tests
# Sonuç: 5 commit → 3 temiz commitRebase komutları:
pick = commit'i olduğu gibi tut
reword = commit mesajını değiştir
edit = commit'i duraklat ve düzenleme yap
squash = önceki commit ile birleştir (mesaj birleşir)
fixup = önceki commit ile birleştir (mesaj atılır)
drop = commit'i sil💡 İpucu: Interactive rebase, push'lanmamış commit'ler için güvenlidir. Push'lanmış commit'lerde force push gerekir. Genel kural: "Henüz paylaşılmamış geçmişi istediğin gibi düzenle, paylaşılmış geçmişe dokunma."
Güvenlik Kontrol Listesi (Temizlik Sonrası)
## History Rewriting Sonrası Checklist
### Teknik
- [ ] Yedek alındı mı?
- [ ] Temiz clone üzerinde mi çalışıldı?
- [ ] Temizlik doğrulandı mı? (büyük dosya/secret artık yok)
- [ ] gc ve prune çalıştırıldı mı?
- [ ] Force push yapıldı mı? (all branches + tags)
- [ ] GitHub'da "Settings → General → Danger Zone" cache temizlendi mi?
### Organizasyonel
- [ ] Ekip bilgilendirildi mi?
- [ ] Herkes yeniden clone yaptı mı?
- [ ] Secret rotate edildi mi? (sızıntı durumunda)
- [ ] .gitignore güncellendi mi?
- [ ] Pre-commit hook eklendi mi? (gelecekte tekrarı önlemek için)
- [ ] Olay raporu yazıldı mı? (sızıntı durumunda)Yaygın Hatalar
1. Yedek Almamak
# ❌ Direkt ana repo'da filter-repo çalıştırmak
cd my-repo
git filter-repo --path data/ --invert-paths
# Bir şey yanlış giderse geri dönüş yok!
# ✅ Her zaman yedek al
cp -r my-repo my-repo-backup
# veya bare clone ile çalış
git clone --mirror origin my-repo-clean.git2. Sadece Dosyayı Silmek (Geçmişi Temizlememek)
# ❌ Dosyayı silmek geçmişi temizlemez!
git rm data/dump.sql
git commit -m "remove large file"
# dump.sql hâlâ geçmişte, repo hâlâ büyük!
# ✅ Geçmişten de temizle
git filter-repo --path data/dump.sql --invert-paths3. Secret'ı Rotate Etmemek
# ❌ "Geçmişten sildim, güvendeyim"
# YANLIŞ! Git geçmişi ve GitHub cache'leri hâlâ erişilebilir olabilir
# ✅ Secret'ı HEMEN rotate edin, SONRA geçmişi temizleyin
# Rotate: yeni key oluştur, eskiyi deaktive etÖzet
Bu derste Git geçmişini yeniden yazma tekniklerini öğrendik:
🔧 git filter-repo — modern standart, hızlı, esnek, Git'in resmi önerisi
🧹 BFG Repo-Cleaner — büyük dosya ve secret temizleme için pratik araç
📦 Büyük dosya temizleme —
--strip-blobs-bigger-thanile repo küçültme👤 Author değiştirme —
--mailmapile email/isim düzeltme🔑 Secret temizleme —
--replace-textile hassas veri kaldırma📂 Repo bölme —
--pathile klasörü ayrı repo'ya çıkarma⚠️ Güvenlik — her zaman yedek al, secret rotate et, ekibi bilgilendir
Bir sonraki derste Git LFS (Large File Storage) ile büyük dosya yönetimini öğreneceğiz — geçmişi temizlemek yerine, baştan doğru yönetmek.
💡 Son İpucu: History rewriting güçlü ama riskli bir araçtır. Altın kural: "Geçmişi yazmak kolay, yeniden yazmak zor." En iyisi baştan doğru yapmaktır —
.gitignorekurun, pre-commit hook ekleyin, code review yapın. History rewriting, "son çare" olmalıdır.
AI Asistan
Sorularını yanıtlamaya hazır