- Sürüm kontrol sisteminin iç yapısını anlamak için doğrudan Git benzeri bir sistem uyguladım
- Git’in SHA-1 ve zlib’inin yerine SHA-256 hash’i ve zstd sıkıştırması kullanıldı; depo,
.tvc dizin yapısıyla oluşturuldu
- Rust ile yazıldı ve dosya hash’leme, sıkıştırma, commit ve checkout işlevleri adım adım uygulandı
- Commit nesnesi ağaç hash’i, ebeveyn commit, yazar ve mesajı içeriyor; aynı dosya, hash tekrarını önleme sayesinde yeniden kaydedilmiyor
- Git’in bir içerik adreslemeli dosya deposu olduğu doğrudan deneyimlenirken, yapısal veri formatlarının önemi vurgulanıyor
Hash’leme ve sıkıştırma yöntemi
- Git tüm nesneleri SHA-1 hash’i ile tanımlar, ancak bu projede SHA-256 kullanıldı
- SHA-1 eski ve güvenlik açısından zayıf olsa da, bu proje bunu yalnızca dosya içeriğini tanımlamak için kullandığından güvenlik kritik değildi
- Git’in zlib’i yerine Facebook’un zstd sıkıştırma kütüphanesi benimsendi
- zstd’nin daha verimli olduğu düşünüldü ve Git uyumluluğu hedeflenmedi
- Projenin adı “tvc (Tony’s Version Control)” ve Git’teki karşılıkları gibi
.tvc ile .tvcignore dosyaları kullanılıyor
Uygulama aşamaları
- Uygulama süreci şu sırayla ilerliyor: komut argümanlarını okuma → yok sayma kurallarını okuma → dosya listesini çıktı verme → hash ve sıkıştırma → ağaç ve commit oluşturma → HEAD yönetimi → commit checkout etme
- Rust ile yazıldı;
ls komutu .tvcignore kurallarını uygulayarak yok sayılmayan dosyaları özyinelemeli biçimde tarıyor ve her dosyanın SHA-256 hash’ini çıktılıyor
- zstd kütüphanesi kullanılarak dosya sıkıştırma ve açma işlevi basitçe uygulandı
Commit yapısı
- Commit nesnesi şu bilgileri içerir
- Nesne türü (
commit)
- O andaki dosya sistemi durumu (ağaç hash’i)
- Önceki commit (HEAD)
- Yazar (author)
- Commit mesajı
- Git’ten farklı olarak yazar ve committer ayrımı atlandı; merge veya rebase işlevleri uygulanmadı
- Commit oluşturulurken ağaç nesnesi üretilip hash’leniyor, sıkıştırılıyor,
.tvc/objects/ içine kaydediliyor ve HEAD dosyası güncelleniyor
- Aynı dosya aynı hash’e sahipse yeniden kaydedilmediği için yinelenen depolama önlenebiliyor
Ağaç nesneleri ve checkout
generate_tree() fonksiyonu dizinleri dolaşarak her dosyayı hash’liyor, sıkıştırıyor ve kaydediyor; dosya adı ile hash’i birleştirerek bir dize oluşturuyor
- Alt dizinler özyinelemeli olarak işlenerek ağaç yapısı oluşturuluyor
- Commit ve ağaç nesneleri yapılandırmalar (
Commit, Tree) olarak ayrıştırılıyor ve bellekte kolay işlenebilir hale getiriliyor
generate_fs() fonksiyonu ağaç yapısına dayanarak dosya sistemini yeniden oluşturuyor ve belirtilen yolda checkout gerçekleştiriyor
Projeden çıkarılan dersler
- Git’in bir içerik adreslemeli (key-value) dosya deposu olduğu bizzat deneyimlendi
- En zor kısım nesne formatını ayrıştırmak oldu; bir sonraki seferde YAML veya JSON gibi daha açık bir format kullanılması planlanıyor
- Tüm kodlar GitHub deposunda açık: tonystr/t-version-control
1 yorum
Hacker News yorumları
Git'in recursive merge strategy destekleyen tek SCM olması ilginç
Bu yöntem geçmişteki çatışma çözüm kayıtlarını otomatik olarak hatırladığı için çok kullanışlı
Birçok kişi hâlâ rebase'i tercih ediyor ama merge uygularken mutlaka çatışma çözüm geçmişini saklama mekanizması eklemek gerekiyor
İlgili referans: Merge made by recursive strategy
Referans: Git Tools - Rerere
Bağlantı
git mergeiçinde “null” stratejisi yokÇatışmayı zaten çözmüşken sadece merge kaydı bırakmak istediğinizde bile Git ille de yardım etmeye çalışma eğiliminde
İndekse ya da working tree'ye dokunmadan yalnızca merge olduğunu kaydeden bir seçenek olmasını isterdim
Örneğin Pijul bunu yapıyor
Birden fazla commit'teki denemeleri göremiyorsunuz, geri almak zorlaşıyor ve zaten merge edilmiş branch üzerinde ek çalışmaya devam etmek güçleşiyor
Birden fazla PR tek bir yapbozun parçalarıysa, düz merge'in çok daha iyi olduğunu düşünüyorum
Her gün kullandığınız bir aracın iç yapısını öğrenmek her zaman keyifli
Özellikle Git from the Bottom Up, Git'in iç yapısını çok net anlatan harika bir yazı
Yaklaşık 20 dakikada Git komutlarının kapalı kutu gibi görünen çalışma mantığını anlayabiliyorsunuz
cat-filekomutuyla hash ID'lerini doğrudan inceleyebildiğinizi daha yeni öğrendim, oldukça hoşmuşKodlama ajanlarının nasıl plan yaptığını merak ediyorsanız, bu tür yazılar onların eğitim verisi
Ama yazar LLM yardımı aldıysa iş biraz döngüsel bir hâl de alabilir
Muhtemelen açık depoları tarayan botlar var
Kodumun LLM eğitiminde kullanıldığı fikri garip hissettiriyor
Yazının kendisinde LLM çıktısı yok ama Rust kod stili kuralları ya da algoritma karşılaştırmaları konusunda tavsiye alırken ChatGPT kullandım
CodeCrafters'ın “Build your own Git” eğitimi gerçekten çok iyi
Bir de Rust ile sıfırdan uygulayan Jon Gjengset'in canlı yayını tavsiye edilir
Ben de sürüm kontrolünün yazılım dışındaki alanlarda daha yaygın kullanılmasını isterdim
GotVC, E2E şifreleme, paralel import ve büyük dosya desteği için yapısıyla ilginç bir proje
Sonunda asıl programda açıp karşılaştırmanız gerekiyor
Bu yazı bana ugit: DIY Git in Python yazısını hatırlattı
Git'in içini derinlemesine incelerken aynı zamanda takibi kolay tutan en iyi kaynaklardan biri
Meta'nın Mercurial fork'u olan Sapling VCS, Zstd dictionary sıkıştırması kullanıyor
Açıklama belgesinde Git'in delta-compressed packfile yapısıyla karşılaştırabilirsiniz
Küçük depolarda Git'in delta sıkıştırması daha verimli ama büyük depolarda yol tabanlı dictionary sıkıştırması daha iyi
Yakın zamanda Git'e de benzer bir “path-walk” özelliği eklendi
Ben de benzer bir deneme yaptım; projemin adı “shit”
GitHub bağlantısı
Zamanında bir SPA framework'ü yapmaya çalışırken gizli karmaşıklık karşısında şaşırdığımı hatırlıyorum
React veya Angular geliştiricileri de muhtemelen böyle bir tavşan deliğine girmiştir
Git de aynı şekilde karmaşıklığını çok iyi saklıyor
PHP ile yazılmış bir Git istemcisi görmüştüm; packfile ve reftable okuyabiliyor, ayrıca LCS tabanlı diff de destekliyor
gipht-horse
Ayrıca
@işaretinin HEAD yerine kullanılabildiğini de ilk kez öğrendim; sözdizimi açısından gayet mantıklı