Commit Derinlemesine
Giriş: Neden Commit'i Bu Kadar Detaylı Öğreniyoruz?
Commit, Git'in atomu. Her şey commit'lerden oluşur: geçmiş, branch'ler, merge'ler, rebase'ler — hepsi commit zincirleri. Commit'i yüzeysel bilmek, kimyayı atomları anlamadan öğrenmeye benzer.
Bu derste üç kritik soruyu cevaplayacağız:
Bir commit'in içinde gerçekten ne var? (tree, blob, SHA-1)
İyi bir commit mesajı nasıl yazılır? (Conventional Commits)
Yanlış bir commit'i nasıl düzeltiriz? (--amend)
🎬 Analoji: Kargo Paketi
Bir commit'i bir kargo paketi gibi düşün:
┌──────────────────────────────────────────┐
│ KARGO PAKETİ │
│ │
│ 📦 İçerik (tree + blob'lar) │
│ Paket içindeki eşyalar │
│ Hangi dosyalar, hangi içerikler │
│ │
│ 🏷️ Etiket (commit mesajı) │
│ "Kış kıyafetleri — 3 kazak, 2 mont" │
│ Ne gönderdiğini açıklayan not │
│ │
│ 📋 Kargo fişi (metadata) │
│ Gönderen: Ali (author) │
│ Tarih: 1 Mart 2026 │
│ Takip no: a1b2c3d (SHA-1 hash) │
│ Önceki kargo: d4e5f6g (parent) │
│ │
└──────────────────────────────────────────┘Her kargo, kendinden öncekine referans verir. Kargoları sırayla takip edersen, tüm gönderim geçmişini görürsün.
Commit Anatomisi: İç Yapı
Git'in Dört Nesne Türü
Git, tüm verisini dört tür nesne olarak saklar. Her nesne SHA-1 hash ile tanımlanır:
Git Nesne Türleri:
1. blob (Binary Large Object)
→ Dosyanın içeriği. Sadece içerik — dosya adı yok!
2. tree (Ağaç)
→ Klasör yapısı. Hangi dosya hangi blob'a işaret ediyor.
3. commit
→ Snapshot. Tree referansı + parent + author + mesaj
4. tag (İleriki derslerde)
→ Annotated tag. Commit'e isim ve açıklama ekler.Bir Commit'in İçine Bakmak
Somut bir örnekle görelim. Diyelim ki projemizde şu dosyalar var:
web-projem/
├── index.html ("<!DOCTYPE html>...")
├── style.css ("body { margin: 0; }")
└── src/
└── app.js ("console.log('hi')")Bu projeyi commit'lediğimizde Git, şu nesneleri oluşturur:
Commit abc123
├── tree: def456 (root tree)
├── parent: (none) (ilk commit)
├── author: Tolgahan
├── date: 2026-03-01
└── message: "İlk commit"
│
▼
Tree def456 (kök dizin)
├── blob 111aaa → index.html
├── blob 222bbb → style.css
└── tree ghi789 → src/
│
▼
Tree ghi789 (src/)
└── blob 333ccc → app.jsBunu gerçek komutlarla görelim:
# Commit nesnesinin içeriği
$ git cat-file -p HEAD
tree def456789...
author Tolgahan Kaya <email> 1709283600 +0300
committer Tolgahan Kaya <email> 1709283600 +0300
İlk commit
# Tree nesnesinin içeriği
$ git cat-file -p def456789
100644 blob 111aaabbb... index.html
100644 blob 222bbbccc... style.css
040000 tree ghi789jkl... src
# Blob nesnesinin içeriği (dosya)
$ git cat-file -p 111aaabbb
<!DOCTYPE html>
<html>
<head><title>Web Projem</title></head>
</html>
# Nesne türünü öğren
$ git cat-file -t abc123
commit
$ git cat-file -t def456
tree
$ git cat-file -t 111aaa
blobSHA-1 Hash Nasıl Hesaplanır?
Git, her nesnenin hash'ini şöyle hesaplar:
SHA-1( nesne_türü + boşluk + boyut + null_byte + içerik )Örneğin "hello" içerikli bir blob için:
# Git'in yaptığını elle yapalım
$ echo -n "hello" | git hash-object --stdin
ce013625030ba8dba906f756967f9e9ca394464a
# Doğrulama
$ printf "blob 5\0hello" | sha1sum
ce013625030ba8dba906f756967f9e9ca394464aNeden önemli? Aynı içerik = aynı hash. Bu, Git'e birçok güç verir:
Deduplication: İki farklı commit'te aynı dosya varsa, blob bir kere saklanır
Bütünlük kontrolü: Hash değişirse, içerik bozulmuş demektir
Hızlı karşılaştırma: İki dosyanın aynı olup olmadığını hash'e bakarak anla (içeriği okumaya gerek yok)
Aynı içerikli dosyalar tek blob olarak saklanır:
Commit 1: Commit 2:
├── index.html → blob A ├── index.html → blob A (AYNI!)
├── style.css → blob B ├── style.css → blob C (değişmiş)
└── app.js → blob D └── app.js → blob D (AYNI!)
Sadece blob C yeni oluşturuldu. A ve D tekrar kullanılıyor.💡 İpucu: Git'in "snapshot" sakladığı doğru ama her seferinde tüm dosyaları kopyaladığı yanlış. Değişmeyen dosyalar için aynı blob referansı kullanılır. Bu yüzden Git hem snapshot avantajını yaşar hem depolama alanında tutumlu olur.
İyi Commit Mesajı Yazmak
Neden Önemli?
6 ay sonra şu log'a baktığını düşün:
# 😱 Kötü geçmiş
$ git log --oneline
f7g8h9i fix
e6f7g8h update
d5e6f7g misc changes
c4d5e6f asdf
b3c4d5e WIP
a2b3c4d initial
# 😍 İyi geçmiş
$ git log --oneline
f7g8h9i fix: Login sayfasında null pointer hatası düzeltildi
e6f7g8h feat: Kullanıcı profil fotoğrafı yükleme eklendi
d5e6f7g refactor: Veritabanı bağlantı katmanı yeniden yapılandırıldı
c4d5e6f docs: API dokümantasyonu güncellendi
b3c4d5e test: Ödeme modülü birim testleri eklendi
a2b3c4d feat: Proje iskelet yapısı oluşturulduHangisinde 3 ay önceki bir bug'ı bulmak daha kolay?
Commit Mesajı Yapısı
Bir commit mesajı üç bölümden oluşur:
<başlık> ← Kısa özet (50 karakter veya altı)
← Boş satır
<gövde> ← Detaylı açıklama (72 karakter genişlik)
← Boş satır
<footer> ← İlişkili issue'lar, breaking changesÖrnek:
feat: Kullanıcı kayıt formu eklendi
Yeni kullanıcıların sisteme kayıt olabilmesi için kayıt formu
oluşturuldu. Form, e-posta ve şifre validasyonu içerir.
Başarılı kayıt sonrasında onay e-postası gönderilir.
- E-posta format kontrolü eklendi
- Şifre güçlülük kontrolü eklendi (min 8 karakter, büyük/küçük harf)
- Captcha entegrasyonu yapıldı
Closes #42Conventional Commits
Modern projelerde yaygın olarak kullanılan bir commit mesajı standardı:
<tür>(kapsam): <açıklama>
Türler:
feat: Yeni özellik
fix: Bug düzeltme
docs: Dokümantasyon
style: Kod formatlama (mantık değişikliği yok)
refactor: Refactoring (ne bug fix ne yeni özellik)
test: Test ekleme/düzenleme
chore: Build, CI, bağımlılık güncelleme
perf: Performans iyileştirmesi
ci: CI/CD değişiklikleriÖrnekler:
git commit -m "feat: Kullanıcı profil sayfası eklendi"
git commit -m "fix: Login'de şifre hatası düzeltildi"
git commit -m "docs: README'ye kurulum adımları eklendi"
git commit -m "style: Kod formatlama düzeltmeleri"
git commit -m "refactor: Auth modülü yeniden yapılandırıldı"
git commit -m "test: Payment modülü unit testleri"
git commit -m "chore: Node.js 20'ye güncellendi"
git commit -m "perf: Anasayfa sorgusu optimize edildi"Kapsam (scope) isteğe bağlı ama faydalı:
git commit -m "feat(auth): İki faktörlü doğrulama eklendi"
git commit -m "fix(cart): Sepet toplamı yanlış hesaplanıyordu"
git commit -m "docs(api): Endpoint listesi güncellendi"Altın Kurallar
✅ İYİ commit mesajları:
─────────────────────────
1. Emir kipi kullan: "Eklendi" değil "Ekle" veya "Add"
2. İlk harf büyük (Türkçe: küçük de olabilir, tutarlı ol)
3. Başlık sonuna nokta koyma
4. 50 karakter altında tut (başlığı)
5. Neyi VE neden yaptığını açıkla
❌ KÖTÜ commit mesajları:
─────────────────────────
- "fix"
- "update"
- "asdf"
- "WIP"
- "misc"
- "a"
- "."
- "aaaaa"
- "Birçok şey değişti"⚠️ Dikkat: Commit mesajı yazma disiplini, kariyerinin en başından itibaren edinmen gereken bir alışkanlık. Code review'da commit mesajlarına bakılır. Open source projelere katkıda bulunurken, kötü commit mesajları PR'ının reddedilmesine neden olabilir.
git commit --amend: Son Commit'i Düzeltme
Yanlış bir commit mesajı yazdın veya bir dosya eklemeyi unuttun? --amend kurtarır:
Mesajı Düzeltme
$ git commit -m "feat: Lgin sayfası eklendi"
# Oops, yazım hatası!
$ git commit --amend -m "feat: Login sayfası eklendi"
[main abc1234] feat: Login sayfası eklendi
Date: Mon Mar 1 10:00:00 2026 +0300
1 file changed, 15 insertions(+)Unutulan Dosyayı Ekleme
$ git add index.html
$ git commit -m "feat: Login sayfası eklendi"
# Oops, style.css'i eklemeyi unuttum!
$ git add style.css
$ git commit --amend --no-edit
# --no-edit: Mesajı değiştirme, dosyayı ekle
$ git log --oneline -1
abc1234 feat: Login sayfası eklendi
# Tek commit, iki dosya da içindeAmend Nasıl Çalışır?
--amend aslında son commit'i silip yerine yenisini koyar:
Öncesi:
[A] ← [B] ← [C] (C yanlış)
▲
HEAD
git commit --amend sonrası:
[A] ← [B] ← [C'] (C' düzeltilmiş yeni commit)
▲
HEAD
C hâlâ var ama artık hiçbir branch onu göstermiyor.
Hash değişti: C ≠ C' (içerik/mesaj değişti → hash değişti)⚠️ Dikkat:
--amendcommit hash'ini değiştirir. Eğer commit'i zatenpushettiysen, amend yapıp tekrar push'lamak sorun çıkarır (force push gerekir). Kural: Sadece push'lanmamış commit'lerde amend kullan.
Author ve Committer Farkı
Her commit'te iki farklı kimlik bilgisi var:
$ git cat-file -p HEAD
tree abc123...
parent def456...
author Ali Yılmaz <ali@example.com> 1709283600 +0300
committer Tolgahan Kaya <tolgahan@example.com> 1709283600 +0300
feat: Login eklendiAuthor: Değişikliği yazan kişi
Committer: Değişikliği commit'leyen kişi
Genelde aynı kişidir. Ama farklı olabilir:
Ali bir patch gönderdi, Tolgahan onu commit'ledi
git cherry-pickveyagit rebaseyapıldığında committer değişirgit commit --amendyapıldığında committer güncellenir
Author Bilgisini Değiştirmek
# Bu commit için farklı author kullan
$ git commit --author="Ali Yılmaz <ali@example.com>" -m "feat: Ali'nin katkısı"
# Son commit'in author'unu değiştir
$ git commit --amend --author="Ali Yılmaz <ali@example.com>" --no-editCommit ile İlgili Faydalı Komutlar
Boş Commit
Bazen CI/CD tetiklemek veya test amacıyla boş commit atmak istersin:
$ git commit --allow-empty -m "ci: Pipeline yeniden tetiklendi"Detaylı Commit (Editör ile)
$ git commit
# Editör açılır, detaylı mesaj yazabilirsin:
feat: Kullanıcı profili güncelleme özelliği
- Profil fotoğrafı yükleme eklendi (max 5MB, JPG/PNG)
- Bio alanı eklendi (max 160 karakter)
- Sosyal medya linkleri eklenebilir
- Form validasyonu client-side ve server-side
Closes #127
Reviewed-by: Ayşe DemirTarih Değiştirme
# Belirli bir tarihte commit at (çok nadir kullanılır)
$ git commit --date="2026-01-15T10:00:00" -m "eski tarihli commit"
# Author tarihi VE committer tarihini değiştir
$ GIT_COMMITTER_DATE="2026-01-15T10:00:00" git commit --date="2026-01-15T10:00:00" -m "mesaj"Pratik Senaryo: Commit Workflow
# Yeni proje
$ mkdir commit-lab && cd commit-lab && git init
# Dosya oluştur
$ echo "# Commit Lab" > README.md
$ echo "console.log('v1');" > app.js
# İlk commit
$ git add .
$ git commit -m "feat: Proje başlatıldı"
# Bazı değişiklikler yap
$ echo "console.log('v2');" > app.js
$ echo "body { color: #333; }" > style.css
# Parça parça commit'le
$ git add app.js
$ git commit -m "refactor: App versiyonu güncellendi"
$ git add style.css
$ git commit -m "style: Temel stiller eklendi"
# Oops, son commit mesajı yanlış
$ git commit --amend -m "feat: Temel stil dosyası eklendi"
# Bir dosya eklemeyi unuttum
$ echo "p { line-height: 1.6; }" >> style.css
$ git add style.css
$ git commit --amend --no-edit
# Geçmişe bak
$ git log --oneline
c3d4e5f (HEAD -> main) feat: Temel stil dosyası eklendi
b2c3d4e refactor: App versiyonu güncellendi
a1b2c3d feat: Proje başlatıldı
# Commit detaylarını incele
$ git show b2c3d4e
# diff çıktısı ile ne değiştiğini görebilirsin
# İç yapıyı incele
$ git cat-file -p HEAD
# tree, parent, author, committer ve mesajSigned Commits — GPG ile İmzalama
Commit'lerin gerçekten sizden geldiğini kanıtlamak için GPG imzalama kullanılır. Özellikle açık kaynak projelerde ve kurumsal ortamlarda önemlidir:
# GPG key oluştur
$ gpg --full-generate-key
# RSA and RSA, 4096 bit, uzun süre (veya süresiz)
# Key ID'nizi bulun
$ gpg --list-secret-keys --keyid-format=long
# sec rsa4096/ABC123DEF456 2026-01-01 [SC]
# FINGERPRINT
# uid Tolgahan Kaya <tolgahan@email.com>
# Git'e tanıt
$ git config --global user.signingkey ABC123DEF456
$ git config --global commit.gpgsign true # Tüm commit'leri otomatik imzala
# İmzalı commit at
$ git commit -S -m "feat: Güvenli commit"
# İmzaları doğrula
$ git log --show-signature
commit abc1234 (HEAD -> main)
gpg: Signature made Mon 01 Mar 2026 12:00:00 PM +03
gpg: Good signature from "Tolgahan Kaya <tolgahan@email.com>"
# GitHub'da GPG public key'i ekle:
# Settings → SSH and GPG keys → New GPG key
$ gpg --armor --export ABC123DEF456
# Çıktıyı kopyala ve GitHub'a yapıştırGitHub'da imzalı commit'ler "Verified" badge'i ile gösterilir. Bu, commit'in gerçekten o kullanıcıdan geldiğini kanıtlar.
Verified vs Unverified:
✅ Verified → GPG imzalı, GitHub doğruladı
⚠️ Partially → Web UI ile yapılmış (GitHub imzalamış)
❌ Unverified → İmza yok veya doğrulanamadı💡 İpucu: GitHub, web arayüzünden yapılan commit'leri (PR merge, dosya düzenleme) otomatik olarak GitHub'ın GPG key'i ile imzalar. Ama local commit'lerinizi siz imzalamalısınız.
git commit --fixup ve --autosquash
İleride rebase dersinde detaylı göreceğiz ama preview olarak: --fixup ile önceki bir commit'e düzeltme ekleyebilirsin:
# Bir commit yaptın
$ git commit -m "feat: User profili eklendi" # (abc1234)
# Bir hata fark ettin — düzeltmeyi ayrı commit yap ama işaretle
$ git add fix.js
$ git commit --fixup=abc1234
# Otomatik mesaj: "fixup! feat: User profili eklendi"
# Sonra rebase ile otomatik birleştir
$ git rebase -i --autosquash main
# fixup commit, otomatik olarak abc1234'ün altına yerleşir
# ve ikisi birleşirBu, code review sırasında "şu commit'teki hatayı düzelt" isteklerini temiz bir şekilde karşılamak için mükemmeldir.
Commit Best Practices: Profesyonel İpuçları
1. Atomik Commit'ler
Her commit tek bir şeyi yapmalı. "Login eklendi ve footer CSS'i düzeltildi" → iki ayrı commit olmalı.
# ❌ Yanlış: İki farklı iş tek commit'te
$ git add .
$ git commit -m "Login eklendi ve footer düzeltildi"
# ✅ Doğru: Ayrı commit'ler
$ git add src/login.js src/auth.js
$ git commit -m "feat: Login sayfası eklendi"
$ git add styles/footer.css
$ git commit -m "fix: Footer hizalama sorunu düzeltildi"2. Sık Commit At
Gün sonunda dev bir commit yerine, anlamlı parçalarda commit at.
3. Çalışan Kod Commit'le
Her commit, projenin çalışır bir versiyonunu temsil etmeli. Yarım kalmış, derleme hatası veren kod commit'leme (stash kullan).
4. Mesajda "Ne" ve "Neden" Olsun
# ❌ Sadece ne
$ git commit -m "Timeout değeri 30'a çıkarıldı"
# ✅ Ne ve neden
$ git commit -m "fix: API timeout 30s'ye çıkarıldı
Yoğun saatlerde veritabanı sorguları 10s'yi aşıyordu ve
timeout hatası alınıyordu. 30s ile yeterli zaman tanındı.
Root cause: slow query — ayrı issue'da optimize edilecek.
Fixes #89"Özet
Commit üç nesneden oluşur: blob (dosya içeriği), tree (klasör yapısı), commit (snapshot + metadata)
Her nesne SHA-1 hash ile tanımlanır — aynı içerik = aynı hash (deduplication)
İyi commit mesajı: kısa başlık (50 kar.), boş satır, detaylı açıklama — Conventional Commits standardını kullan
git commit --amendile son commit'in mesajını veya içeriğini düzeltebilirsin — sadece push'lanmamış commit'lerde kullanAtomik commit: Her commit tek bir mantıksal değişikliği temsil etmeli
Commit mesajında ne yaptığını ve neden yaptığını açıkla — gelecekteki sen teşekkür edecek
💡 Son İpucu: Commit, yazılım geliştirmenin "cümleleridir". Nasıl iyi bir yazar her cümlesini özenle kuruyorsa, iyi bir geliştirici de her commit'ini özenle oluşturur. Başlığı net, gövdesi açıklayıcı, kapsamı dar ve atomik. Bu alışkanlığı kariyerinin başından itibaren edin — gelecekteki sen (ve ekip arkadaşların) teşekkür edecek.
*Bir sonraki derste commit geçmişini derinlemesine incelemeyi — git log, git diff, git show — öğreneceğiz!*
AI Asistan
Sorularını yanıtlamaya hazır