Ders 01 / 15
Dal Kavramı
Dal bir işaretçidir: on iki işlemelik bir kurguda üç dalın açılışı sıfır yeni işleme ve 123 bayt başvuru yazar, kopya modeli aynı ağacı dört bin dosyada on iki bin dosya kopyalarken işaretçi modelinin maliyeti sabit kalır.
İçindekiler
Sürüm Kontrolüne Giriş kursu tarihçeyi tek bir çizgi olarak kurdu. Çalışma dizini, hazırlık alanı ve işleme birbirini izledi; her yeni işleme bir öncekini gösterdi ve tarihçe okunurken sıra hep aynı kaldı — en yeniden en eskiye doğru tek bir yol. O kursta tarihçe doğrusaldı ve tek yazarlıydı: bir kişi, bir sıra, bir çizgi.
Birden çok kişi aynı depoda çalıştığında bu varsayım düşer. İki kişi aynı işlemeden başlayıp ayrı yönlere gider ve tarihçe artık bir çizgi değil bir çatallanmadır. Bu kursun sorusu çatallanmanın nasıl açıldığı değildir — açmak ucuzdur ve bu ders tam olarak ne kadar ucuz olduğunu ölçer. Asıl soru çatallanmanın nasıl kapatıldığıdır, çünkü kapatma biçimi tarihçenin sonradan hangi soruları yanıtlayabileceğini belirler.
Kursun Ölçü Ekseni
Kurs boyunca tek bir kurgu kullanılır. Üç kişi aynı depoda üç ayrı işe başlıyor: olcum,
rapor ve kimlik. Her biri dört işleme yapıyor ve her işleme kendi dosyasına dokunuyor;
üçüncü adımda ikisi birden ayar.py dosyasına dokunuyor. rapor işinin ikinci işlemesi bir
kusur getiriyor. Gerçek gelişim on iki işlemedir ve sırası bilinir, çünkü kurguyu biz
yazdık.
Bu on iki işleme dört ayrı tümleştirme biçimiyle (integration format) ana dala alınabilir: birleştirme işlemesi, yeniden temellendirme, sıkıştırma ve ileri sarma. Dördü de aynı kodu üretir. Ürettikleri tarihçe ayrıdır ve kurs bu farkı tek bir şeyle ölçer: tarihçeye altı soru sorulur ve kaçını yanıtlayabildiği sayılır. Bunlar kursun yanıtlanabilir soruları (answerable questions) olarak anılır ve tanımları kurs boyunca değişmez.
- Özellik gruplaması — hangi işlemeler hangi işe ait?
- Kusurun yalıtımı — kusuru getiren işleme tek başına bulunabiliyor mu?
- Gerçek sıra — işler hangi sırayla geliştirildi?
- Dosya izi — bir dosyaya dokunan her işleme ayrı ayrı bulunabiliyor mu?
- Dalın bütünlüğü — bir dalın işlemeleri bir arada mı duruyor?
- Çakışma kararı — çakışma nerede çözüldü ve hangi taraf seçildi?
Bu ders soruların hiçbirini yanıtlamaz. Ölçtüğü şey daha önceki bir adımdır: çatallanmayı açmanın maliyeti.
Dal Bir İşaretçidir
Depo Oluşturma ve Yapılandırma dersinde başvuru (ref) tanıtılmıştı: bir ada bağlanmış
işleme kimliği. Dal bundan başka bir şey değildir. refs/heads altındaki bir başvurunun
içeriği kırk onaltılık basamaklık bir kimlik ve bir satır sonudur; toplam kırk bir bayt.
# öğretilen komut ve örnek döküm — çalıştırılmamıştır $ git branch olcum $ git branch * ana olcum $ git rev-parse olcum 3f1a9c4e6b2d8057a41c9e3b7d604f2a8c15be93 $ git symbolic-ref HEAD refs/heads/ana
Başvurular dosya olarak da paketlenmiş biçimde de tutulabilir, bu yüzden refs/heads/olcum
dosyasının varlığına değil git rev-parse çıktısına güvenilir. Kimliğin kendisi depoya
özeldir; buradaki değer kurgudur ve kendi deponuzda başka bir değer görürsünüz. Sabit olan
başvurunun yapısıdır: bir ad, bir kimlik.
HEAD ise bir işlemeyi değil bir başvuruyu gösterir; buna simgesel başvuru denir. Zincir
üç halkalıdır: HEAD bir dal adını, dal adı bir işleme kimliğini, işleme de kendi öncülünü
gösterir. Bir dalda işleme yapmak yalnız orta halkayı değiştirir — dal başvurusundaki kimlik
yeni işlemenin kimliğiyle değiştirilir, kırk bir bayt yeniden yazılır. HEAD yerinde kalır,
çünkü hâlâ aynı dalı göstermektedir.
Etiketleme dersindeki hafif etiket de refs altında bir kimlik tutar; aradaki fark tek
kelimeyle hareketliliktir. Etiket bir işlemeye bir kez bağlanır ve kendiliğinden hiçbir
yere gitmez; dal başvurusu her işlemede yeniden yazılır ve dalın ucunu izler. Aynı veri
yapısının iki kullanımıdır: biri bir noktayı sabitlemek, öteki bir noktayı sürüklemek için.
Bu yüzden bir etiketin üstünde çalışılmaz, bir dalın üstünde çalışılır — ve HEAD bir dal
adını gösterdiği sürece yapılan her işleme bir yere bağlanır. HEADin bir dal adı yerine
doğrudan bir işleme kimliğini gösterdiği durum ayrı bir addır ve bu konunun dördüncü dersinde
ele alınır.
Bunun doğrudan bir sonucu var: dal işlemeleri içermez. Dal yalnız bir uç işleme gösterir ve “dalın işlemeleri” dediğimiz şey o uçtan öncül kenarları izleyerek gidilebilen kümedir. Tarihçeyi Okuma dersindeki erişilebilirlik (reachability) tanımı burada aynen geçerlidir. İki dalın ortak öncülleri iki kümede birden bulunur ve hiçbir yere kopyalanmamıştır.
Çatallanmanın Şekli
Tarihçeyi Okuma dersinde git log --graph çıktısı tek sütunluk bir çizim vermişti; o
tarihçede dallanma yoktu. Kurgunun tarihçesi üç sütunludur. Şekli komut çıktısından bağımsız
olarak çizmek daha açıktır:
# tarihçenin şekli — çizim, komut çıktısı değildir
olcum -> 4 işleme
/
taban ------+-------> rapor -> 4 işleme
\
kimlik -> 4 işleme
Üç dalın da geriye doğru izlendiğinde ulaştığı ilk ortak işlemeye ortak taban (merge base) denir. Ortak taban bir işlemedir, bir dal değildir; hangi dalın ucunda durduğunuza bakılmaksızın aynı işlemedir ve iki dal karşılaştırıldığında farkın nereden başlatılacağını belirler. Bu kursun geri kalanı ortak tabanın üstünde çalışır: birleştirme de, yeniden temellendirme de, “bu dalda ne var da ötekinde yok” sorusu da tabandan itibaren hesaplanır.
Dal uçları tek satırda da listelenebilir:
# öğretilen komut ve örnek döküm — çalıştırılmamıştır $ git branch -v * ana 0a5f3c9 Ölçüm iskeletini ekle kimlik 9d4c1a7 Kimlik doğrulama adımını tamamla olcum 6e2d70b Ölçüm eşiğini yapılandırmaya taşı rapor 7c3f9d2 Rapor başlığını biçimlendir
Yıldız HEADin hangi dalı gösterdiğini işaretler. Listedeki her satır bir ad ile bir kimlikten
ibarettir; dalın “içeriği” diye bir sütun yoktur, çünkü öyle bir şey saklanmaz.
Kopya Modeli ve İşaretçi Modeli
Sürüm kontrolünden önceki alışkanlık, bir işi ayırmak için çalışma ağacının tamamını kopyalamaktı. Bu kopya modelidir ve maliyeti açıktır: ağaçta kaç dosya varsa o kadar kopya. Maliyet ağacın büyüklüğüyle doğru orantılı büyür, çünkü ayrılan şey ağacın kendisidir.
Kopya modelinin daha ağır bir bedeli daha vardır ve bu bedel bayt değildir. Kopyalanan ağaç tarihçenin dışındadır: kopyaya bir öncül kenarı bağlanmamıştır, bir işleme kimliği yoktur ve hiçbir başvurudan erişilmez. İki kopyayı sonradan birleştirmek, ortak tabanın ne olduğunu bilmeyen bir karşılaştırma demektir — hangi değişikliğin yeni, hangisinin ötekinde zaten var olduğu ancak elle çıkarılabilir. Bu kursun altı sorusundan hiçbiri kopya modeline sorulamaz, çünkü ortada sorgulanacak bir tarihçe yoktur.
İşaretçi modelinde ayrılan şey ağaç değil, ağacın bir anlık görüntüsüne verilen addır. Dosyalar zaten nesne veritabanında durur ve içerik adresli olduğu için aynı içerik iki kez saklanmaz. Yeni dal, var olan bir işlemeye ikinci bir ad vermekten ibarettir. Ölçüm bu iki modeli aynı kurgu üzerinde yan yana koyar.
Ölçümün varsayımları:
- DL1 — Kurgu üç iş ve on iki işlemedir; kâhin kurguyu biz ürettiğimiz için bilinir ve her işlemenin hangi işe ait olduğunu, hangi dosyaya dokunduğunu söyler.
- DL2 — Üç dal ortak bir taban işlemesinden ayrılır. Taban on iki işlemenin dışındadır ve üç dalın da erişebildiği tek ortak işlemedir.
- DL3 — Bir dal başvurusu kırk onaltılık basamaklık kimlik ile bir satır sonundan oluşur: kırk bir bayt. Ölçüm bir disk ölçümü değil, yazılan içeriğin sayımıdır.
- DL4 — Kopya modeli dalı çalışma ağacının tam kopyası sayar ve maliyeti kopyalanan dosya sayısıdır. İşaretçi modeli dalı tek bir başvuru sayar ve maliyeti yazılan bayttır.
- DL5 — Çalışma ağacı dört büyüklükle denenir: kurgunun dokunduğu dosya sayısı ve üç büyütme. Kurgunun işleme sayısı bu satırlarda değişmez; değişen yalnız ağacın genişliğidir.
- DL6 — Erişilebilirlik yalnız öncül kenarlarını izler. Hiçbir başvurudan erişilemeyen bir işleme tarihçede sorgulanamaz; nesne veritabanında durması onu tarihçeye geri koymaz.
Ölçüm
"""Dal bir isaretcidir: dallanmanin maliyeti kopyalanan dosya degil, yazilan basvurudur.""" TOHUM = 20260813 DALLAR = ("olcum", "rapor", "kimlik") DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"} KUSURLU = ("rapor", 2) ORTAK_DOSYA = "ayar.py" BASVURU_BAYT = 40 + 1 # kirk onaltilik basamak + satir sonu def uretec(tohum): d = tohum % 2147483646 + 1 def r(n): nonlocal d d = (d * 48271) % 2147483647 return d % n return r def gelisim(): """Gercek gelisim: (dal, sira, dosya, kusurlu, zaman) beslileri.""" r, kayit, zaman = uretec(TOHUM), [], 0 for adim in range(1, 5): for dal in DALLAR: zaman += 1 + r(3) dosya = ORTAK_DOSYA if adim == 3 and dal in ("olcum", "rapor") else DOSYALAR[dal] kayit.append({"dal": dal, "sira": adim, "dosya": dosya, "kusurlu": (dal, adim) == KUSURLU, "zaman": zaman}) return kayit kayit = gelisim() agac = sorted({k["dosya"] for k in kayit}) print(f"kurgu: {len(kayit)} işleme, {len(DALLAR)} dal, dal başına " f"{len(kayit) // len(DALLAR)} işleme, dokunulan dosya {len(agac)} {agac}") print(f"üç dalın açılışı: yeni işleme 0, yazılan başvuru {len(DALLAR)}, " f"yazılan bayt {len(DALLAR) * BASVURU_BAYT}") print() print("çalışma ağacı kopya modeli: kopyalanan dosya işaretçi modeli: yazılan bayt") for n in (len(agac), 40, 400, 4000): print(f"{n:13d} {n * len(DALLAR):34d} {len(DALLAR) * BASVURU_BAYT:31d}") print() print("dal ucundaki işleme erişilen işleme başvuru silinirse erişilemez") for dal in DALLAR: grup = [k for k in kayit if k["dal"] == dal] print(f" {dal:8s} {grup[-1]['sira']:12d} {1 + len(grup):16d} {len(grup):29d}") print(f"üç başvurudan erişilen birleşim: {1 + len(kayit)} işleme " f"(taban + {len(kayit)}), yazılan başvuru {len(DALLAR)}")
kurgu: 12 işleme, 3 dal, dal başına 4 işleme, dokunulan dosya 4 ['ayar.py', 'kimlik.py', 'olcum.py', 'rapor.py']
üç dalın açılışı: yeni işleme 0, yazılan başvuru 3, yazılan bayt 123
çalışma ağacı kopya modeli: kopyalanan dosya işaretçi modeli: yazılan bayt
4 12 123
40 120 123
400 1200 123
4000 12000 123
dal ucundaki işleme erişilen işleme başvuru silinirse erişilemez
olcum 4 5 4
rapor 4 5 4
kimlik 4 5 4
üç başvurudan erişilen birleşim: 13 işleme (taban + 12), yazılan başvuru 3
Sayıların Okunması
İlk satır kurgunun boyutunu veriyor: 12 işleme, 3 dal, dokunulan 4 dosya. İkinci satır bu dersin ana ölçüsüdür. Üç dalın açılışı 0 yeni işleme ve 123 bayt yazıyor. Sıfır, dallanmanın tarihçeye hiçbir şey eklemediği anlamına gelir; dal açmak bir kayıt değildir, bir addır.
Orta tablo maliyetin nasıl davrandığını gösteriyor. Kurgunun dört dosyalık ağacında kopya modeli 12 dosya kopyalıyor, işaretçi modeli 123 bayt yazıyor. Ağaç kırk dosyaya çıktığında kopya modeli 120’ye, dört yüz dosyada 1200’e, dört bin dosyada 12000’e tırmanıyor. İşaretçi sütunu dört satırın dördünde de 123. Kopya modelinin maliyeti ağacın büyüklüğüne bağlıdır; işaretçi modelinin maliyeti yalnız dal sayısına bağlıdır. Aradaki fark bin katlık bir ağaçta bin kat açılır ve bu, dallanmanın neden bir istisna değil bir alışkanlık hâline gelebildiğinin tek nedenidir.
Alt tablo ikinci sonucu veriyor. Her dal ucundan 5 işlemeye erişiliyor: dalın kendi dördü ve ortak taban. Üç ucun toplamı on beş değil 13’tür, çünkü taban üç kümede birden bulunur ve bir kez sayılır. Bir dalı paylaşmak da aynı ekonomiye tabidir: aynı tabana bakan üçüncü bir ad yazmak yine kırk bir bayttır.
Kurgunun çözünürlüğü de buradan okunur. On iki işlemelik bir kümede ölçülebilen en küçük fark 1/12’dir; altı soruluk kümede en küçük fark 1/6’dır. Bir dalın dört işlemesi kümenin üçte biri, tek bir kusurlu işleme on ikide biridir. Bu çözünürlüğün altındaki farklar bu kurguyla savunulamaz ve kurs boyunca yazılan sayılar onun üstünde tutulur.
Son sütun kursun ilerisine bakıyor. Bir dal başvurusu silinirse o daldan erişilen 4 işleme hiçbir başvurudan erişilemez hâle gelir. İşlemeler nesne veritabanında durmayı sürdürür ama tarihçeye sorulan altı sorunun hiçbiri onlara ulaşamaz — çünkü sorular tarihçeye sorulur ve tarihçe, başvurulardan erişilebilen kümedir. İşaretçinin ucuzluğu ile kırılganlığı aynı gerçeğin iki yüzüdür: kırk bir bayt yazmak kolaysa, kırk bir baytı kaybetmek de kolaydır.
Ana Dal Ayrıcalıklı Değildir
Ölçümün doğrudan söylemediği ama sayılarından çıkan bir sonuç var. Üç dalın açılışı 123
bayt yazıyor ve bu bayt her dal için aynı; kurgudaki ana dalı da refs/heads altında kırk
bir baytlık bir başvurudur. Araç bakımından ana dalın hiçbir ayrıcalığı yoktur: daha hızlı
okunmaz, daha güvenli tutulmaz, silinmeye karşı korunmaz. Ayrıcalık ekibin kararıdır, veri
yapısının değil.
Bunun iki pratik sonucu vardır. Birincisi, “ana dal” adının kendisi bir sözleşmedir ve depodan
depoya değişebilir; komutlar bir varsayılan ada değil, HEADin gösterdiği başvuruya bakar.
İkincisi, ana dalı ayrıcalıklı kılan her koruma depo dışında kurulur: kimin hangi dala
yazabildiği, hangi dalın silinemeyeceği, hangi dala doğrudan işleme yapılamayacağı başvurunun
kendisinde yazılı değildir. Depo, kırk bir baytı kimin yazdığını sorgulamaz.
Bu ders için önemli olan yanı şudur: dal ucunun hangi işlemeyi gösterdiği bir veri sorusudur, o dalın ne anlama geldiği ise bir düzen sorusudur. İkincisinin karşılığı adlandırmadır ve kursun üçüncü dersi onu ölçer.
Özet
- Dal,
refs/headsaltında bir ada bağlanmış işleme kimliğidir; içeriği kırk onaltılık basamak ve bir satır sonu, toplam kırk bir bayttır. HEADbir işlemeyi değil bir dal başvurusunu gösterir; işleme yapmak yalnız dal başvurusundaki kimliği değiştirir,HEADyerinde kalır.- Dal işlemeleri içermez; “dalın işlemeleri” dal ucundan öncül kenarları izlenerek erişilen kümedir ve ortak öncüller kopyalanmadan iki kümede birden bulunur.
- Üç dalın açılışı 0 yeni işleme ve 123 bayt yazar; kopya modeli aynı işi dört dosyalık ağaçta 12, dört bin dosyalık ağaçta 12000 dosya kopyalayarak yapar.
- Bir dal ucundan 5, üç ucun birleşiminden 13 işlemeye erişilir; başvuru silinirse o daldan erişilen 4 işleme tarihçenin dışına düşer.
Sonraki Adım
Dal açmak bir addır ve maliyeti ölçüldü. Ama ad yazıldığı anda çalışma ağacı hâlâ eski dalın içeriğini taşır; dala geçmek ayrı bir işlemdir ve çalışma dizini ile hazırlık alanının yeni dala uyarlanmasını gerektirir. O sırada elde işlenmemiş bir değişiklik varsa bir karar verilmek zorundadır: değişiklik geçişle birlikte taşınacak mı, taşınamıyorsa geçiş engellenecek mi. Sonraki ders dal oluşturma ile geçişi ayırır ve on iki durum üzerinde hangi değişikliğin taşındığını, hangisinin engellendiğini, hangisinin geride kaldığını sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.