References ve HEAD
Giriş — İsimsiz Nesnelere İsim Vermek
Önceki derste Git'in nesne modelini öğrendin: her şey SHA-1 hash'lerle adresleniyordu. Ama düşünsene, her gün "abc1234def5678ghi9012jkl3456mno78901" gibi 40 karakterlik hash'lerle mi çalışacaksın? Tabii ki hayır.
İşte referanslar (refs) bu sorunu çözer. Referanslar, karmaşık hash'lere insan tarafından okunabilir isimler veren dosyalardır. main, feature/login, v1.0.0, HEAD — bunların hepsi aslında bir commit hash'ine işaret eden referanslardır.
Bu derste referans sisteminin tamamını, HEAD'in ne olduğunu, detached HEAD durumunu ve hayat kurtaran reflog'u öğreneceksin.
Referans Nedir?
Analoji — Telefon Rehberi
Arkadaşının telefon numarasını ezbere bilmene gerek yok. Rehberinde "Ahmet" yazıyor, tıklıyorsun, arama yapılıyor. "Ahmet" ismi, +90 532 123 4567 numarasına işaret eden bir referans.
Git referansları da aynı şekilde çalışır:
Referans (isim) → Commit Hash (gerçek adres)
──────────────────── ─────────────────────────────
main → abc1234def5678ghi9012jkl3456
feature/login → def5678ghi9012jkl3456mno7890
v1.0.0 → ghi9012jkl3456mno7890pqr1234
HEAD → refs/heads/main (symbolic)Referanslar Birer Dosyadır
Referanslar .git/refs/ dizinindeki basit metin dosyalarıdır. Her dosya tek bir hash içerir:
# main branch'inin referansı:
cat .git/refs/heads/main
# abc1234def5678ghi9012jkl3456mno78901
# Bu, "main" dediğimizde Git'in abc1234... commit'ine gitmesi anlamına gelir.git/refs/
├── heads/ ← Branch referansları
│ ├── main ← main branch → commit hash
│ ├── develop ← develop branch → commit hash
│ └── feature/
│ └── login ← feature/login → commit hash
├── tags/ ← Tag referansları
│ ├── v1.0.0 ← v1.0.0 tag → commit/tag hash
│ └── v1.1.0
└── remotes/ ← Remote tracking branch'ler
└── origin/
├── main ← origin/main → commit hash
└── developrefs/heads — Branch'ler
Branch'ler sadece birer referanstır — bir commit'e işaret eden dosyalar. Branch'i "özel" yapan şey, yeni commit yaptığında otomatik olarak ilerlemesi.
# Branch referansını oku
git rev-parse main
# abc1234def5678ghi9012jkl3456mno78901
# Doğrudan dosyayı oku
cat .git/refs/heads/main
# abc1234def5678ghi9012jkl3456mno78901
# Aynı sonuç! Branch = dosyadaki hashBranch Oluşturma = Dosya Oluşturma
# Bu iki komut eşdeğer:
# Porcelain (üst seviye):
git branch new-feature
# Plumbing (alt seviye):
echo "abc1234def5678ghi9012jkl3456mno78901" > .git/refs/heads/new-featureBranch İlerleme Mekanizması
Commit öncesi:
main → C (abc1234)
HEAD → refs/heads/main
Commit sonrası:
main → D (def5678) ← main artık yeni commit'e işaret ediyor
HEAD → refs/heads/main ← HEAD hâlâ main'e işaret ediyor
Yani: git commit = yeni commit oluştur + branch referansını güncelle# Commit öncesi:
cat .git/refs/heads/main
# abc1234...
# Commit yap:
git commit -m "New feature"
# Commit sonrası:
cat .git/refs/heads/main
# def5678... ← Otomatik güncellendi!refs/tags — Tag'ler
Tag referansları da .git/refs/tags/ dizinindedir:
# Lightweight tag:
cat .git/refs/tags/v1.0.0
# abc1234... ← Doğrudan commit hash'ine işaret eder
# Annotated tag:
cat .git/refs/tags/v2.0.0
# def5678... ← Tag nesnesinin hash'ine işaret eder (commit değil!)
# Tag nesnesi ise commit'e işaret ederLightweight tag referansı:
v1.0.0 ──► commit (abc1234)
Annotated tag referansı:
v2.0.0 ──► tag object (def5678) ──► commit (ghi9012)refs/remotes — Remote Tracking Branch'ler
Remote tracking branch'ler, remote repository'deki branch'lerin yerel kopyalarıdır:
# Remote tracking branch:
cat .git/refs/remotes/origin/main
# abc1234... ← origin'deki main'in son bilinen commit'i
# Bu dosya ne zaman güncellenir?
# git fetch veya git pull yaptığındaLocal branch: refs/heads/main → commit X
Remote tracking: refs/remotes/origin/main → commit Y
Remote (GitHub'daki): (Git'in yerel bilmediği) → commit Z
git fetch sonrası:
Local branch: refs/heads/main → commit X (değişmez)
Remote tracking: refs/remotes/origin/main → commit Z (güncellendi!)💡 İpucu: Remote tracking branch'ler salt okunurdur. Direkt olarak checkout yapıp üzerlerinde çalışamazsın.
git checkout origin/maindersen detached HEAD durumuna geçersin.
HEAD — "Sen Buradasın" İşareti
HEAD, Git'teki en önemli referanstır. "Şu an neredesin?" sorusunun cevabıdır.
HEAD Türleri
1. Normal HEAD (symbolic ref):
HEAD → refs/heads/main → commit hash
"main branch'indeyim"
2. Detached HEAD:
HEAD → commit hash (doğrudan)
"Hiçbir branch'te değilim, bir commit'in üzerindeyim"# Normal durum:
cat .git/HEAD
# ref: refs/heads/main
# Detached HEAD:
cat .git/HEAD
# abc1234def5678ghi9012jkl3456mno78901HEAD Nasıl Değişir?
# Branch değiştir → HEAD güncellenir
git checkout main
cat .git/HEAD
# ref: refs/heads/main
git checkout develop
cat .git/HEAD
# ref: refs/heads/develop
# Belirli bir commit'e checkout → Detached HEAD
git checkout abc1234
cat .git/HEAD
# abc1234def5678ghi9012jkl3456mno78901HEAD Kısaltmaları
# HEAD = şu anki commit
git show HEAD
# HEAD~1 = bir önceki commit (parent)
git show HEAD~1
# HEAD~2 = iki önceki commit
git show HEAD~2
# HEAD^ = ilk parent (merge commit'te)
# HEAD^2 = ikinci parent (merge commit'te)
# HEAD~1 ve HEAD^ genellikle aynı
# Fark sadece merge commit'lerde ortaya çıkar:
# HEAD
# |
# A───B───M (merge commit: M)
# \ /
# C
#
# HEAD^ = B (ilk parent, main branch'teki)
# HEAD^2 = C (ikinci parent, feature branch'teki)
# HEAD~1 = B (bir adım geri)
# HEAD~2 = A (iki adım geri, ilk parent zincirinde)Detached HEAD — Kopuk Kafa
Ne Zaman Olur?
# 1. Belirli bir commit'e checkout
git checkout abc1234
# 2. Bir tag'e checkout
git checkout v1.0.0
# 3. Remote tracking branch'e checkout
git checkout origin/main
# Git seni uyarır:
# You are in 'detached HEAD' state. You can look around, make
# experimental changes and commit them, and you can discard any
# commits you make in this state without impacting any branches
# by switching back to a branch.Detached HEAD'de Ne Olur?
Normal durum:
HEAD → main → C (commit)
Yeni commit: HEAD → main → D → C
Branch otomatik ilerler ✅
Detached HEAD:
HEAD → C (commit) (branch yok!)
Yeni commit: HEAD → D → C
Ama D hiçbir branch'a bağlı değil! ⚠️# Detached HEAD'de commit yaptın:
git checkout abc1234 # Detached HEAD
echo "test" > test.txt
git add .
git commit -m "Detached commit"
# Bu commit havada kalıyor — hiçbir branch'a bağlı değil!
# Başka bir branch'e geçersen:
git checkout main
# Yaptığın commit kaybolur!* (reflog'da kalır ama erişilmesi zor)Detached HEAD'den Kurtulma
# Yöntem 1: Yeni branch oluştur (commit'leri kaybet.me)
git checkout -b save-my-work
# Yöntem 2: Mevcut branch'e dön (commit'lerden vazgeç)
git checkout main
# Yöntem 3: Kayıp commit'i kurtarma (reflog ile)
git reflog
# abc1234 HEAD@{1}: commit: Detached commit ← Bu!
git branch rescue-branch abc1234⚠️ Dikkat: Detached HEAD'de commit yapmak tehlikelidir çünkü hiçbir branch bu commit'i referans etmez. Git'in garbage collector'ı (gc) bir süre sonra bu "yetim" commit'leri silebilir. Commit yaptıysan, mutlaka bir branch oluştur!
Symbolic Ref
HEAD bir symbolic ref'tir — başka bir referansa işaret eden referans:
# Symbolic ref oku:
git symbolic-ref HEAD
# refs/heads/main
# Symbolic ref yaz (branch değiştirmek gibi):
git symbolic-ref HEAD refs/heads/develop
# Şimdi develop branch'indesin
# Detached HEAD durumunda symbolic-ref çalışmaz:
git symbolic-ref HEAD
# fatal: ref HEAD is not a symbolic refpacked-refs — Sıkıştırılmış Referanslar
Çok sayıda branch ve tag olduğunda, her biri için ayrı dosya tutmak verimsiz olabilir. Git, bu referansları tek bir dosyada paketleyebilir:
cat .git/packed-refs# pack-refs with: peeled fully-peeled sorted
abc1234def5678ghi9012jkl3456mno78901 refs/heads/main
def5678ghi9012jkl3456mno78901pqr12345 refs/heads/develop
ghi9012jkl3456mno78901pqr12345stu56789 refs/tags/v1.0.0
^jkl3456mno78901pqr12345stu56789vwx90123 refs/tags/v1.0.0 (peeled)Git referans arama sırası:
1. .git/refs/heads/main dosyasına bak (loose ref)
2. Yoksa .git/packed-refs dosyasına bak (packed ref)
Loose ref varsa packed'ı ezer (override).
git gc sırasında loose ref'ler pack'lenir.Reflog — Hayat Kurtaran Kayıt
Analoji — Tarayıcının Geri Tuşu
Web tarayıcında "Geri" butonuna basınca önceki sayfaya dönersin. Tarayıcı, ziyaret ettiğin her sayfayı kaydeder. Yanlışlıkla bir sayfayı kapattıysan, geçmişten geri alabilirsin.
Reflog (reference log), Git'teki "geri tuşu"dur. HEAD'in veya branch'lerin geçirdiği her değişikliği kaydeder. Commit, checkout, reset, rebase, merge — her şey reflog'da.
Reflog Görüntüleme
git reflogabc1234 HEAD@{0}: commit: feat: Add search
def5678 HEAD@{1}: checkout: moving from feature to main
ghi9012 HEAD@{2}: commit: WIP: search component
jkl3456 HEAD@{3}: checkout: moving from main to feature
mno7890 HEAD@{4}: reset: moving to HEAD~2
pqr1234 HEAD@{5}: commit: feat: Add dashboard
stu5678 HEAD@{6}: commit: feat: Add login
vwx9012 HEAD@{7}: commit (initial): Initial commitHer satır:
abc1234 HEAD@{0}: commit: feat: Add search
│ │ │ │
│ │ │ └── Açıklama
│ │ └──────────────────── İşlem türü
│ └───────────────────────────────────── Reflog index
└──────────────────────────────────────────────────── Commit hashReflog ile Kurtarma Senaryoları
Senaryo 1: Yanlış git reset --hard
# Yanlışlıkla son 3 commit'i sildim!
git reset --hard HEAD~3
# Panik! Ama reflog var:
git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: feat: Important feature ← BU!
# Geri al:
git reset --hard def5678
# Commit'ler geri geldi! 🎉Senaryo 2: Yanlış Rebase Sonrası Kurtarma
# Rebase yaptım ama her şey bozuldu!
git rebase main # Conflict çıktı, yanlış çözdüm...
# Reflog'da rebase öncesi durumu bul:
git reflog
# abc1234 HEAD@{0}: rebase (finish): refs/heads/feature
# def5678 HEAD@{1}: rebase (pick): feat: Step 2
# ghi9012 HEAD@{2}: rebase (start): checkout main
# jkl3456 HEAD@{3}: commit: feat: Step 2 ← REBASE ÖNCESİ!
# Rebase öncesine dön:
git reset --hard jkl3456Senaryo 3: Silinen Branch'i Kurtarma
# Branch'i sildim ama commit'ler lazımdı!
git branch -D feature/important
# Deleted branch feature/important (was abc1234)
# Reflog'da bul:
git reflog
# ... abc1234 HEAD@{5}: commit: Last commit on feature/important
# Branch'i geri oluştur:
git branch feature/important abc1234Senaryo 4: Detached HEAD'de Yapılan Commit'i Kurtarma
# Detached HEAD'de commit yaptım, sonra branch'e döndüm
# Commit kayboldu!
git reflog
# abc1234 HEAD@{3}: commit: My detached commit ← BU!
git branch rescue abc1234
git checkout rescueBranch Reflog'u
Reflog sadece HEAD için değil, branch'ler için de tutulur:
# main branch'inin reflog'u
git reflog show main
# abc1234 main@{0}: merge feature/login: Fast-forward
# def5678 main@{1}: commit: fix: Bug fix
# ghi9012 main@{2}: commit: feat: Add featureReflog Ömrü
# Reflog sonsuza kadar saklanmaz:
# Varsayılan ayarlar:
# - Erişilebilir commit'ler: 90 gün
# - Erişilemez commit'ler: 30 gün
# Ayarları gör:
git config gc.reflogExpire
# 90.days.ago
git config gc.reflogExpireUnreachable
# 30.days.ago
# Değiştirmek istersen:
git config --global gc.reflogExpire "180 days"⚠️ Dikkat: Reflog sadece yerelde tutulur.
git cloneile klonlanan repo'da eski reflog yok. Remote repo'nun reflog'u sende yok. Reflog bir güvenlik ağıdır ama kalıcı yedek değildir.
git rev-parse — Referans Çözümleme
git rev-parse herhangi bir referansı commit hash'ine çevirir:
# Branch adından hash
git rev-parse main
# abc1234...
# Tag'den hash
git rev-parse v1.0.0
# def5678...
# HEAD
git rev-parse HEAD
# ghi9012...
# Kısa hash
git rev-parse --short HEAD
# ghi9012
# HEAD~2
git rev-parse HEAD~2
# jkl3456...
# Stash
git rev-parse stash@{0}
# mno7890...
# Branch'in var olup olmadığını kontrol et
git rev-parse --verify feature/login
# pqr1234... (varsa hash döner, yoksa hata)Referans Sözdizimi Özeti
# ═══════════════════════════════════════════
# REFERANS SÖZDİZİMİ
# ═══════════════════════════════════════════
HEAD # Şu anki commit
HEAD~1 # 1 önceki commit (parent)
HEAD~3 # 3 önceki commit
HEAD^ # İlk parent (= HEAD~1, merge dışında)
HEAD^2 # İkinci parent (sadece merge commit'lerde)
main # main branch'in son commit'i
main~3 # main'in 3 önceki commit'i
feature/login # feature/login branch'i
v1.0.0 # Tag
v1.0.0^{commit} # Tag'in işaret ettiği commit (annotated tag için)
origin/main # Remote tracking branch
HEAD@{1} # 1 adım önceki HEAD (reflog)
main@{yesterday} # Dünkü main (reflog, zaman bazlı)
main@{2.days.ago} # 2 gün önceki main
main@{5} # 5 adım önceki main (reflog index)
HEAD^{tree} # HEAD commit'inin tree nesnesi
HEAD:README.md # HEAD'deki README.md dosyasının blob'u
abc1234 # Kısa hash (minimum 4 karakter, benzersiz olmalı)
abc1234def5678 # Uzun hashPratik: Referans Sistemiyle Oynama
# Proje oluştur
mkdir refs-demo && cd refs-demo
git init
# Birkaç commit yap
echo "v1" > file.txt && git add . && git commit -m "Commit 1"
echo "v2" > file.txt && git add . && git commit -m "Commit 2"
echo "v3" > file.txt && git add . && git commit -m "Commit 3"
# ═══════════════════════════════════════
# REFERANSLARI İNCELE
# ═══════════════════════════════════════
echo "=== HEAD ==="
cat .git/HEAD
# ref: refs/heads/main
echo "=== main branch ==="
cat .git/refs/heads/main
# abc1234...
echo "=== Aynı hash mi? ==="
git rev-parse HEAD
git rev-parse main
# Evet, aynı!
# ═══════════════════════════════════════
# BRANCH OLUŞTUR (DÜŞÜK SEVİYE)
# ═══════════════════════════════════════
# Normal yol:
git branch test-branch
# Plumbing yol (aynı etkiyi yapar):
HASH=$(git rev-parse HEAD)
echo $HASH > .git/refs/heads/manual-branch
# İkisi de var:
git branch
# * main
# manual-branch
# test-branch
# ═══════════════════════════════════════
# DETACHED HEAD DENEYİ
# ═══════════════════════════════════════
# Detached HEAD'e geç
git checkout HEAD~1
cat .git/HEAD
# abc1234... (doğrudan hash — symbolic ref değil!)
# Commit yap
echo "detached" > detached.txt
git add .
git commit -m "Detached commit"
# Branch'e dön
git checkout main
# Detached commit'i reflog'da bul
git reflog
# ... HEAD@{1}: commit: Detached commit ...
# Kurtar!
git branch rescued HEAD@{1}
git log --oneline rescued
# def5678 Detached commit ← Kurtarıldı!
# ═══════════════════════════════════════
# REFLOG İNCELE
# ═══════════════════════════════════════
git reflog --all
# HEAD'in ve tüm branch'lerin reflog'u
git reflog show main
# Sadece main'in reflog'u
# Zaman bazlı sorgulama
git log -1 main@{yesterday}
# "main dün neredeydi?"
# Temizlik
git branch -D test-branch manual-branch rescuedÖzet
Bu derste Git'in referans sistemini ve HEAD'i derinlemesine öğrendik:
Referanslar commit hash'lerine insan tarafından okunabilir isimler veren dosyalardır —
refs/heads/branch'ler,refs/tags/tag'ler,refs/remotes/remote tracking branch'lerHEAD "şu an neredesin" sorusunun cevabıdır — normal durumda bir branch'e işaret eden symbolic ref, detached durumda doğrudan commit hash'i
Detached HEAD hiçbir branch'te olmadığın durumdur — burada yapılan commit'ler kaybolabilir, mutlaka branch oluştur
Reflog Git'in "geri tuşu"dur — HEAD ve branch'lerin her değişikliğini kaydeder. Yanlış reset, rebase, branch silme gibi felaketlerden kurtarır
packed-refs çok sayıda referansı tek dosyada saklar — performans optimizasyonu
git rev-parseherhangi bir referansı commit hash'ine çevirir — scriptlerde çok yararlı
Bir sonraki derste Git'in nesneleri nasıl sakladığını ve optimize ettiğini göreceğiz: packfile'lar ve garbage collection.
AI Asistan
Sorularını yanıtlamaya hazır