00 Git nedir gerçekten
Git, temelde içerik-adresli bir dosya sistemidir; versiyon kontrolü bu filesytem'in üzerine inşa edilmiştir.
Çoğu geliştirici Git'i komutlar dizisi olarak öğrenir: git add, git commit, git push. Ama bu komutların arkasında ne olduğunu bilmeden merge conflict'leri çözmeye çalışmak, semptomları tedavi edip hastalığı bırakmak gibidir. Git'in iç modelini anlamak:
Content-addressed filesystem
Git'te her şey içeriğinden hesaplanan bir SHA-1 hash ile adreslenir. Aynı içerik her zaman aynı hash'i üretir. İçerik değişirse hash değişir — bu integrity garantisi sağlar:
İçerik → SHA-1(header + içerik) → .git/objects/ab/cdef1234...
"hello" → 8c7e5a667f1b771847ad9a31b3d9cc5f4b0cc3f5
DAG — Directed Acyclic Graph
Commit'ler arasındaki bağlantı, döngüsüz yönlü bir grafik (DAG) oluşturur. Her commit, parent commit'ine(lerine) işaret eder:
A ← B ← C ← D (main)
↑
E ← F (feature)
Merge commit, iki parent'a işaret eder. Bu yapı sayesinde hangi değişikliklerin hangi commit'ten geldiği her zaman izlenebilir.
Snapshot, not diff
SVN ve diğer sistemler dosyalar arasındaki farkı saklar. Git ise her commit'te tüm proje tree'sinin snapshot'ını saklar. Değişmeyen dosyalar için yeni obje oluşturmaz — sadece aynı blob'a işaret eder:
.git/ — tüm repo burada
Tüm git verisi .git/ dizininde saklanır. Bu dizini bir yerden başka bir yere kopyalarsan, tam bir git repo elde edersin. Çalışma dizinini silersin, .git/'i tutarsan, tüm versiyon geçmişi oradadır — git checkout ile her şeyi geri getirebilirsin.
Bu bölümde
- Git: content-addressed filesystem — içerik → SHA-1 hash
- DAG: commit'ler arası döngüsüz yönlü graf yapısı
- Snapshot modeli: her commit tam tree snapshot'ı, değişmeyen dosyalar paylaşılan blob
- .git/ — tüm git verisi burada; taşısan repo gider
01 .git/ dizin anatomisi
.git/ altındaki her dosya ve dizin belirli bir görev üstlenir — bunları anlamak Git'in iç çalışmasını görünür kılar.
ls -la .git/
# Çıktı:
# drwxr-xr-x HEAD
# drwxr-xr-x config
# drwxr-xr-x description
# drwxr-xr-x hooks/
# -rw-r--r-- index
# drwxr-xr-x info/
# drwxr-xr-x logs/
# drwxr-xr-x objects/
# drwxr-xr-x packed-refs
# drwxr-xr-x refs/
# HEAD'i oku
cat .git/HEAD
# → ref: refs/heads/main
# Detached HEAD durumunda:
# → a3f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
Dosya ve dizin açıklamaları
ref: refs/heads/branchname formatında symbolic ref. Detached HEAD'de doğrudan SHA.refs/ yapısı
# Local branch'ler
ls .git/refs/heads/
# main feature/auth hotfix/crash
# Bir branch ref içeriği — tek satır SHA
cat .git/refs/heads/main
# a3f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
# Remote-tracking branch'ler
ls .git/refs/remotes/origin/
# HEAD main develop
# Tag'ler
ls .git/refs/tags/
# v1.0.0 v1.1.0 v2.0.0
# objects/ alt dizin yapısı
ls .git/objects/ | head -5
# 1a 2b 3c info pack
ls .git/objects/a3/
# f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9 (SHA'nın geri kalanı)
Bu bölümde
- HEAD: symbolic ref (refs/heads/main) veya detached SHA
- objects/: tüm git objeleri SHA ile adresli, zlib compressed
- refs/heads/, refs/tags/, refs/remotes/ hiyerarşisi
- index: staging area binary dosyası — bir sonraki commit'in tree'si
- ORIG_HEAD, MERGE_HEAD, CHERRY_PICK_HEAD — operasyon marker'ları
02 Object türleri: blob, tree, commit, tag
Git'te tam olarak dört obje tipi vardır — tüm versiyon geçmişi bu dört tip objenin ağından oluşur.
Dört obje tipi
| Tip | Ne saklar | Boyut | Örnek kullanım |
|---|---|---|---|
| blob | Dosya içeriği (ham baytlar) | Dosya boyutu | main.c, README.md, icon.png |
| tree | Dizin listesi (dosya + alt dizin pointerları) | Küçük | src/, include/, proje kökü |
| commit | Root tree SHA + parent + author + mesaj | Küçük | Her git commit komutu bir commit objesi üretir |
| tag | Annotated tag: obje SHA + tagger + mesaj | Küçük | git tag -a v1.0.0 -m "Release" |
Obje oluşturma ve inceleme
# Bir dosyadan blob oluştur, SHA döner (-w: git object db'ye yaz)
echo "hello embedded world" > hello.txt
git hash-object -w hello.txt
# → 8f9e3a2b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f
# -w olmadan: sadece hash hesapla, kaydetme
git hash-object hello.txt
# Obje tipini öğren
git cat-file -t 8f9e3a2b
# → blob
# Obje içeriğini oku
git cat-file -p 8f9e3a2b
# → hello embedded world
# Boyutunu öğren
git cat-file -s 8f9e3a2b
# → 21
Blob — dosya içeriği
Blob, dosyanın ham içeriğini saklar. Dosya adı bilgisi yoktur — bu bilgi tree objesindedir. Aynı içerikli iki farklı ada sahip dosya sadece tek bir blob oluşturur:
# Mevcut repo'dan blob oku
git cat-file -p HEAD:src/main.c
# → main.c dosyasının HEAD'deki içeriği
# Eski bir commit'teki halini oku
git cat-file -p HEAD~3:src/main.c
Tree — dizin listesi
# HEAD'in root tree'si
git cat-file -p HEAD^{tree}
# Çıktı (mode type sha name):
# 100644 blob a3f8c2d1b4e5... README.md
# 100644 blob 7b2e9f3c8d1a... CMakeLists.txt
# 040000 tree 9c4e1f2b7a8d... src
# 040000 tree 1d5f2e3c9b4a... include
# src/ alt tree'si
git cat-file -p 9c4e1f2b7a8d
# 100644 blob b5c2a7d9e1f3... main.c
# 100644 blob c6d3b8e0f2a4... utils.c
Tree girdilerindeki mode değerleri Unix dosya izinlerinden gelir: 100644 normal dosya, 100755 executable, 040000 dizin (tree), 120000 symlink.
Commit objesi
# HEAD commit'inin ham içeriği
git cat-file -p HEAD
# tree 9c4e1f2b7a8d5f3e2c1b0a9d8e7f6a5b4c3d2e1f
# parent 7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6
# author Emirhan Pehlevan <emirpehlevan@outlook.com> 1712345678 +0300
# committer Emirhan Pehlevan <emirpehlevan@outlook.com> 1712345678 +0300
#
# Add GPIO driver for STM32F4
# Merge commit — iki parent
git cat-file -p abc123
# tree ...
# parent aaa111... ← ilk parent (merge hedefi)
# parent bbb222... ← ikinci parent (merge edilen branch)
# ...
İlk commit'te parent satırı yoktur. Orphan branch'ler (git checkout --orphan) da parent'sız commit ile başlar.
Bu bölümde
- blob: dosya içeriği, adsız — ad bilgisi tree'de
- tree: mode + type + SHA + name satırları — dizin snapshot'ı
- commit: root tree + parent + author + committer + mesaj
- git hash-object -w ve git cat-file -t / -p ile obje inceleme
03 SHA-1 hashing
Her git objesi, içeriğinden hesaplanan SHA-1 hash ile adreslenir — bu yapı hem veri bütünlüğünü hem de deduplication'ı sağlar.
Git object format
Git bir objeyi hashlemeden önce içeriğin başına bir header ekler. Format: type SP size NUL content. Yani blob için: "blob 5\0hello". Bu header + content SHA-1'e verilir:
# "hello" yazısının git blob SHA'sı nedir?
# Format: "blob " + boyut + NUL + içerik
# Python ile hesapla
python3 -c "
import hashlib
content = b'hello'
header = f'blob {len(content)}\0'.encode()
sha1 = hashlib.sha1(header + content).hexdigest()
print(sha1)
"
# → ce013625030ba8dba906f756967f9e9ca394464a
# Git'in kendi hesabıyla karşılaştır
echo -n "hello" | git hash-object --stdin
# → ce013625030ba8dba906f756967f9e9ca394464a ← aynı!
# sha1sum ile (NUL byte için printf kullan)
printf "blob 5\0hello" | sha1sum
# → ce013625030ba8dba906f756967f9e9ca394464a -
Objects dizin yapısı
SHA-1 hash 40 hex karakterdir. İlk 2 karakter dizin adı, kalan 38 karakter dosya adı olur. Bu yapı tek bir dizinde binlerce dosya birikmesini engeller (inode ve filesystem performansı için):
# SHA: ce013625030ba8dba906f756967f9e9ca394464a
# Dosya: .git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
ls .git/objects/ce/
# 013625030ba8dba906f756967f9e9ca394464a
# Dosya içeriği zlib compressed — okunabilir değil
file .git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
# → zlib compressed data
# git cat-file ile oku
git cat-file -p ce013625
# → hello
Neden SHA-1?
SHA-1 seçimi kasıtlıdır: hız + yeterli collision direnci (2000'lerin başında). İçerik değişirse hash değişir — bu git'in temel integrity mekanizmasıdır. SHA-1'i bilmeden hash'i değiştiremezsin, bu da tarih manipülasyonunu zorlaştırır.
SHAttered attack ve SHA-256 geçişi
2017'de Google ve CWI SHA-1 collision ürettiler (SHAttered attack) — aynı SHA-1 hash'e sahip iki farklı PDF. Git için pratikte kritik risk düşük (blob içeriği değil commit graph'ı hedeflenmesi gerekir) ama teorik zayıflık var. Git 2.29+ (2020) ile SHA-256 objeli repo desteği geldi: git init --object-format=sha256. GitHub SHA-256 geçişini henüz tamamlamamıştır.
# SHA-256 object store ile yeni repo başlat
git init --object-format=sha256 myrepo-sha256
cd myrepo-sha256
# Artık 64 hex karakterli SHA-256 hash'ler kullanılır
echo "test" > file.txt && git add . && git commit -m "init"
git log --oneline
# → 3a8d9e2f1b4c7d5e6a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f
# Mevcut repo'nun hash formatını öğren
git rev-parse --show-object-format
# → sha1
Bu bölümde
- Git object format: "type SP size NUL content" → SHA-1
- Python ve printf ile manuel SHA-1 hesaplama ve git ile doğrulama
- objects/ab/cdef... dizin yapısı — ilk 2 hex = dizin, kalan = dosya
- SHAttered (2017) ve SHA-256 geçişi (git 2.29+, --object-format=sha256)
04 Plumbing komutları
Plumbing komutları, git'in düşük seviyeli mekanizmalarına doğrudan erişim sağlar — script ve araç yazımının temelidir.
Porcelain vs Plumbing
Porcelain komutları değişebilir — git yeni versiyonlarda git status çıktısını yeniden düzenleyebilir. Plumbing komutları ise stabil API olarak kabul edilir; script'lerde ve araçlarda plumbing kullan.
Sadece plumbing ile commit yapmak
Bir git commit komutu arka planda tam olarak bunları yapar — adım adım:
# ── Adım 1: Blob oluştur ────────────────────────────────────
echo "Merhaba, Git internals!" > hello.txt
# -w: nesneyi .git/objects/ içine yaz
BLOB_SHA=$(git hash-object -w hello.txt)
echo "Blob SHA: $BLOB_SHA"
# ── Adım 2: Index'e ekle ────────────────────────────────────
# --add: yeni dosya (henüz index'te yok)
# --cacheinfo mode,sha,path
# 100644 = normal dosya (rw-r--r--)
git update-index --add --cacheinfo 100644,$BLOB_SHA,hello.txt
# ── Adım 3: Tree yaz ────────────────────────────────────────
# Index'teki mevcut durumu tree objesine çevir
TREE_SHA=$(git write-tree)
echo "Tree SHA: $TREE_SHA"
# ── Adım 4: Commit oluştur ──────────────────────────────────
# commit-tree: tree SHA + opsiyonel -p parent
COMMIT_SHA=$(echo "İlk commit — plumbing ile" | \
git commit-tree $TREE_SHA)
echo "Commit SHA: $COMMIT_SHA"
# ── Adım 5: Branch'i commit'e işaret et ────────────────────
git update-ref refs/heads/main $COMMIT_SHA
# ── Doğrula ─────────────────────────────────────────────────
git log --oneline
git show HEAD
İkinci commit — parent ile
echo "ikinci dosya" > world.txt
BLOB2=$(git hash-object -w world.txt)
git update-index --add --cacheinfo 100644,$BLOB2,world.txt
TREE2=$(git write-tree)
# -p: parent SHA — DAG zinciri oluşur
COMMIT2=$(echo "İkinci commit" | \
git commit-tree $TREE2 -p $COMMIT_SHA)
git update-ref refs/heads/main $COMMIT2
git log --oneline
# → b9c0d1e İkinci commit
# → a7b8c9d İlk commit — plumbing ile
Plumbing komut referansı
# Index içeriği (stage area)
git ls-files --stage
# 100644 ce013625030ba8dba906f756967f9e9ca394464a 0 hello.txt
# 100644 8ab686eafeb1f44702738c8b0f24f2567c36da6d 0 world.txt
# Sütunlar: mode SHA stage-number path
# Stage 0 = normal, 1 = ancestor, 2 = ours, 3 = theirs (merge conflict)
Bu bölümde
- Porcelain (kullanıcı) vs Plumbing (script/araç) ayrımı
- 5 adımda sıfırdan commit: hash-object → update-index → write-tree → commit-tree → update-ref
- -p parent flag'i ile DAG zinciri oluşturma
- git ls-files --stage ile index'in mode + SHA + stage içeriği
05 Refs sistemi
Ref, bir SHA'ya işaret eden basit bir dosyadır — branch, tag ve remote'lar bu mekanizma üzerine inşa edilmiştir.
Ref nedir?
Bir branch, aslında sadece bir SHA hash içeren küçük bir metin dosyasıdır. main branch'i oluşturmak demek .git/refs/heads/main dosyasını oluşturup içine commit SHA'sı yazmak demektir:
# main branch hangi commit'i gösteriyor?
cat .git/refs/heads/main
# → a3f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
# Symbolic ref: HEAD → main → commit SHA
cat .git/HEAD
# → ref: refs/heads/main
# Symbolic ref okuma
git symbolic-ref HEAD
# → refs/heads/main
# git update-ref ile yeni branch oluştur
git update-ref refs/heads/feature/new-driver a3f8c2d1
# → refs/heads/feature/new-driver dosyası oluşturuldu
# Rev-parse: istediğin revision'ı SHA'ya çevir
git rev-parse HEAD
git rev-parse main
git rev-parse main~3
git rev-parse HEAD^{tree}
git rev-parse HEAD^{commit}
Revision syntax
Packed refs
Binlerce branch ve tag içeren büyük repolarda her ref için ayrı dosya tutmak verimsizdir. git gc ve git pack-refs bunları .git/packed-refs'e toplar:
# Tüm ref'leri pack et
git pack-refs --all
cat .git/packed-refs
# # pack-refs with: peeled fully-peeled sorted
# a3f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9 refs/heads/main
# 7b2e9f3c8d1a4b5c6d7e8f9a0b1c2d3e4f5a6b7c refs/heads/feature
# 9c4e1f2b7a8d5f3e2c1b0a9d8e7f6a5b4c3d2e1f refs/tags/v1.0.0
# ^d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4 ← peeled: annotated tag'ın commit SHA'sı
Lightweight vs Annotated tags
git tag v1.0. İmza, tagger bilgisi yok.git tag -a v1.0 -m "Release". Ref → tag objesi → commit.# Annotated tag oluştur
git tag -a v1.0.0 -m "v1.0.0: ilk stabil sürüm"
# Tag objesini incele
git cat-file -t v1.0.0
# → tag
git cat-file -p v1.0.0
# object a3f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
# type commit
# tag v1.0.0
# tagger Emirhan Pehlevan <emirpehlevan@outlook.com> 1712345678 +0300
#
# v1.0.0: ilk stabil sürüm
Bu bölümde
- Ref = SHA içeren metin dosyası. Branch = .git/refs/heads/branchname
- Symbolic ref: HEAD → refs/heads/main → commit SHA
- git rev-parse ile HEAD~3, HEAD^2, HEAD^{tree} revision syntax
- packed-refs: binlerce ref'i tek dosyada topla (git pack-refs --all)
- Lightweight (sadece ref) vs annotated (tag objesi) tag farkı
06 Index (staging area)
Index, bir sonraki commit'in ne içereceğini tutan binary bir önbellektir — working tree ile HEAD arasındaki tampon katman.
Index'in rolü
Working tree → git add → Index → git commit → Commit (tree objesi) (disk dosyaları) (binary) (.git/objects/)
Index'te tam bir tree snapshot'ı vardır. git commit çalıştırdığında yeni blob veya tree objeleri oluşturulmaz — index'teki SHA'lar zaten obje db'de mevcuttur, sadece tree ve commit objeleri yazılır.
Index güncellenme mekanizması
# git add: blob oluştur + index güncelle
git add src/main.c
# → 1. src/main.c içeriğinden blob SHA hesaplanır ve objects'a yazılır
# → 2. Index'te src/main.c girişi yeni SHA ile güncellenir
# git diff: working tree vs index
git diff
# git diff --cached: index vs HEAD (ne commit edilecek)
git diff --cached
# Alias: git diff --staged
# Üç yönlü karşılaştırma
# Working tree vs HEAD:
git diff HEAD
# Index içeriğini göster
git ls-files --stage
# 100644 8ab686eafeb1f44702738c8b0f24f2567c36da6d 0 CMakeLists.txt
# 100644 ce013625030ba8dba906f756967f9e9ca394464a 0 README.md
# 100644 7b2e9f3c8d1a4b5c6d7e8f9a0b1c2d3e4f5a6b7c 0 src/main.c
Merge conflict — index stage numaraları
Merge conflict sırasında index, çakışan dosyayı üç farklı stage numarasıyla tutar:
# Merge conflict durumunda ls-files --stage
git ls-files --stage src/config.c
# 100644 aaa111... 1 src/config.c ← ancestor
# 100644 bbb222... 2 src/config.c ← ours
# 100644 ccc333... 3 src/config.c ← theirs
# Her versiyonun içeriğine bak
git cat-file -p :1:src/config.c # ancestor
git cat-file -p :2:src/config.c # ours
git cat-file -p :3:src/config.c # theirs
# Conflict'i çözüp dosyayı stage 0'a al
git add src/config.c
--assume-unchanged performans hack
# Büyük, nadiren değişen dosyaları git status taramasından çıkar
git update-index --assume-unchanged large-generated-file.bin
# Geri al
git update-index --no-assume-unchanged large-generated-file.bin
# Assume-unchanged olarak işaretli dosyaları listele
git ls-files -v | grep '^[a-z]'
# Küçük harf 'h' = assume-unchanged, büyük harf 'H' = normal tracked
--assume-unchanged sadece performans içindir. Dosyayı gerçekten untrack etmez — git stash, git checkout veya git pull bu flag'i görmezden gelebilir. Gerçek anlamda takip etmemek için .gitignore veya --skip-worktree kullan.
Bu bölümde
- Index = bir sonraki commit'in tree'si, binary format (.git/index)
- git diff (working vs index) vs git diff --cached (index vs HEAD)
- Merge conflict'te stage 1 (ancestor), 2 (ours), 3 (theirs)
- git ls-files --stage ile index'in mode + SHA + stage içeriği
- --assume-unchanged: büyük dosyaları status taramasından çıkarma (hack)
07 Pack files
Loose objects verimli değildir — pack file, binlerce objeyi delta compression ile tek bir dosyada toplar.
Loose objects vs Pack files
Loose objects → git gc → pack-SHA.pack + pack-SHA.idx
(ayrı dosyalar) (sıkıştırılmış) (index)
Delta compression
Pack file, benzer objelerin yalnızca farkını (delta) saklar. Örneğin bir dosyanın 10 versiyonu varsa, Git bunların birbirinden farkını hesaplar ve en az yer kaplayan temel + delta zinciri seçer. Büyük binary dosyalar için bu optimizasyon minimal etki yapar; metin dosyaları için dramatik küçülme sağlar.
Pack file operasyonları
# Obje sayısına bak (loose vs packed)
git count-objects -v
# count: 24 ← loose object sayısı
# size: 96 ← toplam boyut (KB)
# in-pack: 4821 ← pack file içindeki obje sayısı
# packs: 1 ← pack file sayısı
# size-pack: 1842 ← pack file boyutu (KB)
# garbage: 0
# size-garbage: 0
# Manuel gc: loose objects'i pack'e taşı
git gc
# Agresif gc: daha iyi delta compression (yavaş)
git gc --aggressive
# Pack file'ları listele
ls .git/objects/pack/
# pack-a3f8c2d1...sha1.idx
# pack-a3f8c2d1...sha1.pack
# Pack içeriğini listele (tüm objeler)
git verify-pack -v .git/objects/pack/pack-*.idx | head -20
# SHA type size packed-size offset depth base-SHA
# a3f8c2d1 commit 230 185 12 0
# 7b2e9f3c blob 1024 312 197 1 a3f8c2d1 ← delta
Shallow clone ve unshallow
# Shallow clone: sadece son N commit
git clone --depth 1 https://github.com/user/repo.git
# Daha fazla history çek
git fetch --depth 50
# Tam history'ye dön (unshallow)
git fetch --unshallow
# Shallow mi değil mi?
cat .git/shallow # varsa shallow, yoksa full clone
git rev-parse --is-shallow-repository
# → true / false
# Belirli bir tarihten bu yana tam history
git fetch --shallow-since="2024-01-01"
# CI'da hız için partial clone
git clone --filter=blob:none --no-checkout https://github.com/user/repo.git
Repack ile optimizasyon
# Tüm objeleri tek bir pack file'a topla
git repack -a -d -f
# -a: tüm objeler (mevcut pack'ler dahil)
# -d: eski pack'leri sil
# -f: delta'ları yeniden hesapla
# Sonucu karşılaştır
git count-objects -v -H # -H: human-readable boyutlar
Bu bölümde
- Loose objects: ayrı dosyalar. Pack file: tek dosya + delta compression
- git gc: loose → pack. git gc --aggressive: daha iyi sıkıştırma
- git count-objects -v: loose/packed obje ve boyut istatistikleri
- git verify-pack -v: pack içeriği ve delta derinliği
- --depth 1 shallow clone, --unshallow ile full history'ye geçiş
08 Reflog
Reflog, HEAD ve branch ref'lerinin her hareketini kaydeden yerel bir zaman çizelgesidir — sildiğini sandığın şeyi geri getirmenin anahtarı.
Reflog nedir?
Her git commit, git checkout, git reset, git rebase operasyonu bir reflog girdisi oluşturur. Reflog, bu hareketlerin kronolojik kaydını tutar:
# HEAD'in son hareketleri
git reflog
# a3f8c2d HEAD@{0}: commit: GPIO driver eklendi
# 7b2e9f3 HEAD@{1}: commit: UART init düzeltildi
# 9c4e1f2 HEAD@{2}: checkout: moving from feature to main
# 1d5f2e3 HEAD@{3}: reset: moving to HEAD~1
# b5c2a7d HEAD@{4}: commit: ilk commit
# Belirli bir branch'in reflog'u
git reflog show main
# Tüm ref'lerin reflog'u
git reflog --all
# Detaylı bilgi: tarih, yazar
git reflog --date=iso
# a3f8c2d HEAD@{2024-04-12 14:23:11 +0300}: commit: GPIO driver
Silinen commit'i geri getir
# Senaryo: yanlışlıkla git reset --hard HEAD~2 yaptın
# İki commit kaybedildi — ama reflog'da hâlâ var
# Adım 1: Kaybedilen commit'i reflog'da bul
git reflog
# a3f8c2d HEAD@{0}: reset: moving to HEAD~2 ← şu anki konum
# 7b2e9f3 HEAD@{1}: commit: önemli değişiklik ← kaybolan (2)
# 9c4e1f2 HEAD@{2}: commit: kritik fix ← kaybolan (1)
# Adım 2: Kaybedilen commit'i recover et
# Yöntem A: branch oluştur
git branch recovered-work 7b2e9f3
# Yöntem B: HEAD'i eski konuma taşı
git reset --hard HEAD@{2}
# Yöntem C: cherry-pick ile sadece o commit'i al
git cherry-pick 7b2e9f3
Reflog ile eski konuma dönme
# 2 adım önceki HEAD konumuna git
git reset --hard HEAD@{2}
# Dün saat 15:00'deki konuma git
git checkout main@{"2024-04-12 15:00"}
# 3 gün önceki konuma git
git checkout main@{"3 days ago"}
Reflog expiry
# Reflog expiry'yi artır
git config gc.reflogExpire "365 days"
git config gc.reflogExpireUnreachable "90 days"
# Reflog'u manuel temizle (normalde yapma)
git reflog expire --expire=now --all
Reflog sadece lokaldir. Remote'a push edilmez, clone'da yer almaz, başka birinin makinesiyle paylaşılamaz. Reflog sayesinde kurtarabileceğin verinin zamanı sınırlıdır — git gc eski girdileri temizler. Önemli kaybolan commit'leri bulduktan hemen sonra yeni bir branch oluştur.
Bu bölümde
- Reflog: HEAD ve branch ref'lerinin her hareketinin kronolojik kaydı
- git reflog ve git reflog show main ile geçmişi görme
- Kaybolan commit: git reflog → SHA → git branch recovered SHA
- HEAD@{N}, HEAD@{"3 days ago"} syntax ile eski konuma dönme
- Reflog sadece lokal — push edilmez, clone'da olmaz
09 fsck, dangling commits ve kurtarma
git fsck, object database bütünlüğünü kontrol eder ve hiçbir ref'ten ulaşılamayan "dangling" objeleri ortaya çıkarır.
git fsck — bütünlük kontrolü
# Obje database bütünlük kontrolü
git fsck
# Checking object directories: 100% (256/256), done.
# Checking connectivity: done.
# Unreachable (dangling) objeleri göster
git fsck --unreachable
# unreachable commit 7b2e9f3c8d1a4b5c6d7e8f9a0b1c2d3e4f5a6b7c
# unreachable blob a3f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
# unreachable tree 9c4e1f2b7a8d5f3e2c1b0a9d8e7f6a5b4c3d2e1f
# Unreachable objeleri .git/lost-found/ altına yaz
git fsck --lost-found
# → .git/lost-found/commit/ — dangling commit'ler
# → .git/lost-found/other/ — dangling blob ve tree'ler
ls .git/lost-found/commit/
ls .git/lost-found/other/
Senaryo 1: git reset --hard ile kaybedilen commit
# Senaryo: git reset --hard HEAD~3 yaptın, 3 commit kayboldu
# Adım 1: Reflog'da bul (en hızlı yol)
git reflog
# HEAD@{1}: commit: son değişiklikler ← bu SHA'yı kullan
SHA=7b2e9f3c
# Adım 2: Yeni branch oluştur
git branch recover/reset-undo $SHA
# Adım 3: Branch'e geç ve kontrol et
git checkout recover/reset-undo
git log --oneline -5
# Adım 4: main'e merge et (değişiklikleri geri al)
git checkout main
git merge recover/reset-undo
Senaryo 2: yanlış git stash drop sonrası kurtarma
# Senaryo: git stash drop ile stash'i yanlışlıkla sildin
# Stash aslında bir commit objesidir — hâlâ objects'ta olabilir
# Adım 1: Dangling commit'leri bul
git fsck --unreachable | grep commit
# unreachable commit 7b2e9f3c8d1a4b5c6d7e8f9a0b1c2d3e4f5a6b7c
# unreachable commit a3f8c2d1b4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
# Adım 2: Her birini incele — stash olup olmadığını anla
git show 7b2e9f3c
# commit 7b2e9f3c ...
# Merge: aaa111 bbb222 ← stash commit'i iki parent'lıdır
# Author: ...
# WIP on main: ... ← stash mesajı genellikle "WIP on" ile başlar
# Adım 3: Stash'i geri yükle
git stash apply 7b2e9f3c
# Alternatif: branch olarak restore et
git checkout -b recovered-stash 7b2e9f3c
Senaryo 3: Bir dosyayı silip commit ettin
# Senaryo: önemli bir dosyayı sildip commit ettin
# Dosyanın eski blob'u objects'ta hâlâ var
# Eski commit'lerde dosyayı ara
git log --all --full-history -- src/deleted_file.c
# commit abc123 Delete: src/deleted_file.c silindi
# commit def456 Add: src/deleted_file.c eklendi
# Silmeden önceki commit'teki haline bak
git show abc123~1:src/deleted_file.c
# Geri yükle
git checkout abc123~1 -- src/deleted_file.c
git maintenance — otomatik gc
# Periyodik gc, commit-graph yenileme, prefetch etkinleştir
git maintenance start
# Etkin görevler
git config --list | grep maintenance
# maintenance.auto=false
# maintenance.strategy=incremental
# Manuel olarak tüm bakım görevlerini çalıştır
git maintenance run --auto
# Commit-graph: git log --graph hızlandırır
git commit-graph write --reachable
cat .git/objects/info/commit-graph | xxd | head
# Durdur
git maintenance stop
fsck ile corrupt repo tespiti
# Corrupt obje bul
git fsck
# error: sha1 mismatch 7b2e9f3c... ← bozuk obje
# error: object file .git/objects/7b/2e9f3c... is empty
# Bozuk objeyi sil ve remote'dan geri getir
rm .git/objects/7b/2e9f3c...
git fetch origin
# Remote'u güvenilir kaynak olarak kullan
git remote prune origin
git fsck --no-dangling # bütünlük kontrolü, dangling'i gizle
Git, git gc çalışana kadar ve reflog TTL dolmadan objeleri gerçek anlamda silmez. Bu yüzden git reset --hard, git stash drop, hatta git branch -D ile kaybettiğin şeylerin büyük çoğunluğu kurtarılabilir. Paniklemeden önce git reflog ve git fsck --unreachable komutlarını çalıştır.
Bu bölümde
- git fsck: obje db bütünlük kontrolü. --unreachable ile dangling objeler
- git fsck --lost-found: dangling objeleri .git/lost-found/'a yaz
- git reset --hard kurtarma: reflog → SHA → git branch recover SHA
- git stash drop kurtarma: git fsck --unreachable | grep commit → git stash apply SHA
- git maintenance start: periyodik gc ve commit-graph yenileme
- Git nadiren gerçekten siler — gc öncesi kurtarma mümkün