Git'in Gerçekten Nasıl Çalıştığını Açıklıyor: Blok, Ağac ve DAG Yapısı

Git'in Gerçekten Nasıl Çalıştığını Açıklıyor: Blok, Ağac ve DAG Yapısı
audio-thumbnail
Git'in Gerçekten Nasıl Çalıştığını Açıklıyor: Blok, Ağac ve DAG Yapısı
0:00
/0

Git'in Gerçekten Nasıl Çalıştığını Açıklıyor: Blok, Ağac ve DAG Yapısı

Git, geliştiricilerin günlük hayatında kullandığı bir araç olmasına rağmen, çoğu kişi onun nasıl çalıştığını tam anlamaz. Sıkça duyduğumuz bir ifade şudur: "Git değişiklikleri takip eder. Bir dosyayı değiştirip commit yaparsanız, Git eski dosya ile yeni dosya arasındaki farkı (diff) hesaplar ve bu değişikliği kaydeder." Bu, oldukça mantıklı bir mantık. Çünkü git diff veya git show komutlarını kullandığımızda, eklenen satırların yeşil, silinenlerin kırmızı olduğunu görürüz.

Ama büyük bir şaşkınlık: Git bu şekilde çalışmaz. Git, dosyaların değişikliklerini (diff) takip etmez. Git, patch (değişiklik) kaydetmez. Git’in iç yapısında "dosya isimleri" veya "dosya taşıma işlemleri" gibi kavramlar bile yoktur. Git, tamamen farklı bir yapıya sahiptir: İçindeki veriler, içerik adresli (content-addressed) bir nesne deposu ve bu depo, Doğrusal Olmayan Yönlendirilmiş Graf (DAG) yapısının temelinde çalışır.

Bu makalede, Git'in iç yapısını geliştiricilere anlaşılır bir şekilde anlatacağız. Git'in temel yapı taşlarını, yani Blok (Blob), Ağac (Tree), DAG (Directed Acyclic Graph) ve İndeks (Index) kavramlarını adım adım ele alacağız.


1. Git'in İçinde Neler Var? .git Klasörü

Git'i bir projede başlatmak için git init komutunu çalıştırırsanız, proje klasörünüzde gizli bir klasör oluşur: .git. Bu klasör, Git'in tüm verilerini tutar. İçinde en önemli klasör .git/objects dir. Bu klasör, Git'in veri depolama merkezidir.

Git, her bir dosyayı, klasörü, commit mesajını ve etiketi (tag) bu klasörde, dört temel nesne türü olarak depolar:

a. Blob (İçerik Bloğu)

  • Dosyanın ham içeriklerini (raw bytes) saklar.
  • Dosya adı, yolu veya izinleri saklamaz.
  • Aynı içeriğe sahip farklı dosyalar, aynı blob nesnesini paylaşır.
  • Örneğin, README.md, main.py ve config.json dosyalarının içeriği aynıysa, hepsi aynı SHA-1 hash değerine sahip olur.

b. Tree (Ağaç)

  • Bir klasörü temsil eder.
  • İçindeki dosyaların isimlerini, izinlerini ve blob hash değerlerini içerir.
  • Klasör içindeki alt klasörler de aynı şekilde ağaç yapısında temsil edilir.
  • Bir klasördeki her dosya veya alt klasör, bir tree nesnesine bağlanır.

c. Commit (İşlem)

  • Projenin bir anlık görüntüsünü (snapshot) temsil eder.
  • Bir tree nesnesine (proje kök klasörünü) ve ebeveyn commitlerin hash değerlerine (parents) işaret eder.
  • Ayrıca sahibi (author), committer, ve commit mesajını içerir.
  • Commit'ler, DAG yapısında birbirine bağlanır.

d. Annotated Tag (İmzalı Etiket)

  • Bir commit'e sabitlenmiş bir isim (örneğin v1.0) ve bu isme ait ek bilgiler (tarih, imzalayan kişi, doğrulama mesajı) içerir.
  • Git'in sürüm kontrolü için kullanılır.

2. İçerik Adresleme (Content-Addressing): Git'in Gizli Gücü

Git, her nesneyi hash (kriptografik karma) değerine göre tanımlar. Bu işlem, içerik adresleme olarak bilinir.

Örneğin:
Bir dosyaya hello world\n yazıp Git'in düşük seviye hash araçlarını kullanırsak:

echo "hello world" | git hash-object -w --stdin

Bu komut, dosyanın içeriğine göre bir hash oluşturur ve .git/objects klasörüne kaydeder. Bu hash, 3b18e512dba79e4c8300dd08aeb37f8e728b8dad gibi bir değer olur.

Nasıl Hesaplanır?

Git, dosya içeriğine şunları ekler: - Nesne türü (örneğin blob) - İçeriğin uzunluğu - Bir null karakter (\0)

Sonra bu tamamlanmış veriyi SHA-1 algoritmasına sokar. Yani:

header = "blob 12\0"
content = "hello world\n"
store = header + content
sha1(store) = 3b18e512dba79e4c8300dd08aeb37f8e728b8dad

Önemli Not: Bu hesaplama sırasında dosya adı kullanılmaz. Yani README.md, main.py veya config.json dosyaları aynı içerikteyse, hepsi aynı hash değerine sahip olur. Git, aynı içeriğe sahip dosyaları tek bir blob olarak depolar.


3. Git Dosya Sistemi Nasıl Düzenlenir?

Git, .git/objects klasöründe depolanan nesneleri, dosya sistemini aşırı yüklememek için akıllıca düzenler.

Her nesne, 40 karakterlik hash değerinin ilk iki karakteriyle bir klasörde depolanır. Kalan 38 karakter ise dosya adı olarak kullanılır.

Örneğin:
3b18e512dba79e4c8300dd08aeb37f8e728b8dad hash'ı şu şekilde depolanır:

.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad

Bu yapı, dosya sayısı çok yüksek olduğunda işletim sistemlerinin performansını etkilemez.


4. Git, Diff Değil, Tam Görüntü (Snapshot) Saklar

Git'in en büyük şaşırtmacasından biri: Git, değişiklikleri değil, tam görüntüleri kaydeder.

Eski sistemler (CVS, SVN) her commit'te dosyadaki değişiklikleri (diff) kaydederdi. Bu nedenle çok fazla depolama alanı kullanılırdı ve geri dönmek zordu.

Git ise her commit'te tam bir proje görseli (snapshot) oluşturur. Yani, her commit, proje içindeki tüm dosyaların ve klasörlerin bir anlık halini temsil eder.

Peki, bu neden fazla yer kaplamaz?

Git, immutable (değiştirilemez) ağaç yapılarını kullanır. Yani her dosya ve klasör, bir blob veya tree nesnesiyle temsil edilir ve bu nesneler değiştirilemez.

Örnek:

Bir projede 1000 dosya var. docs/guide.md dosyasında bir yazım hatası düzeltildi ve yeni bir commit yapıldı.

Git şu adımları gerçekleştirir:

  1. Yeni guide.md dosyasının içeriği bir blob olarak kaydedilir.
  2. docs/ klasörünün yeni bir tree nesnesi oluşturulur. Bu ağaç, yeni blob'a işaret eder, ama diğer dosyaların eski hash değerlerini kullanır.
  3. Proje kök klasörünün yeni bir tree nesnesi oluşturulur. Bu, yeni docs/ tree'ini içerir, diğer klasörlerin ise eski hash değerlerini kullanır.
  4. Yeni bir commit nesnesi oluşturulur. Bu commit, yeni kök tree'ye işaret eder.

Önemli: 999 dosya kopyalanmaz. Sadece bir dosya güncellenmiş, bu da bir blob ve birkaç ağaç nesnesi oluşturur. Geri kalan tüm dosyalar, aynı nesneleri kullanır.

Bu yapı, Git’in verimliliğini ve depolama etkinliğini sağlar. Bir 100MB'lik dosya 500 commit boyunca değişmezse, sadece 100MB yer kaplar, değil de 50GB.


5. Git, Delta Kullanır mı?

Git, normal commit sırasında delta (değişiklik) kullanmaz. Bunu sadece optimizasyon amaçlı yapar.

Git, çalışırken .git/objects klasöründe loose objects (gevşek nesneler) oluşturur. Bu nesneler, zamanla çok sayıda olur. Git, bu nesneleri optimize etmek için git gc (garbage collection) veya git push komutlarında Packfile adı verilen bir dosyaya birleştirir.

Bu paketleme işlemi, delta sıkıştırma (delta compression) kullanır. Yani, benzer nesneler arasında farklar hesaplanarak daha az alan kaplar.

Ama bu, normal commit işlemi sırasında değil, aralıklı olarak ve optimize amaçlıdır.


6. Git'in DAG Yapısı: Ne Demek?

Git’in en güçlü tasarımı, Directed Acyclic Graph (DAG) yapısıdır. Bu, bir graf (grafik) yapısıdır, ancak:

  • Yönlüdür: Her commit, bir veya daha fazla ebeveyn commit'e işaret eder.
  • Döngüsüz (acyclic): Bir commit, kendisine bağlı olamaz.
  • Köklü (rooted): En başta bir başlangıç commit (root) vardır.

Git, her commit’i bir düğüm olarak görür. Bu düğümler, birbirine bağlanır. Mesela:

  • commit A → commit B → commit C
  • commit A → commit D

Bu yapı, git merge, git rebase, detached HEAD gibi komutların nasıl çalıştığını açıklar.


Sonuç: Git'in Gerçek Mucizesi

Git, geliştiriciler için çok basit bir araç gibi görünse de, işleyişinde çok sofistike bir yapı var. Git’in çalışmasının temelinde:

  • İçerik adresli nesne depolama (content-addressed object store)
  • Tüm dosyaları tam görüntü olarak kaydetme
  • İşlemdeki verimlilik için içerik paylaşımı
  • DAG yapısı ile esnek sürüm kontrolü

Bu yapılar, Git’i dünyada en çok kullanılan versiyon kontrol sistemlerinden biri haline getirir.

Artık Git’in nasıl çalıştığını anladığınıza göre, git reset, git rebase ya da detached HEAD gibi komutları sadece bir sihir gibi değil, mantıklı bir yapı olarak görebilirsiniz.

Git, bir "magical incantation machine" değil, bir bilgi mimarisidir. Ve bu mimariyi anlamak, geliştiricilikte çok daha güçlü bir araç elde etmek demektir.