---
title: 'Dallanma ve İşbirliği'
source: 'https://academia.sh/tr/kurslar/dallanma-ve-isbirligi'
language: tr
updated: '2026-08-17T04:42:45+00:00'
license: 'CC BY-SA 4.0'
lessons: 15
outcomes:
    - 'Dal stratejisi seçer ve gerekçelendirir'
    - 'Birleştirme çakışmalarını çözer'
    - 'Uzak depolarla senkron çalışır ve katkı akışını yürütür'
---

# Dallanma ve İşbirliği

Dört tümleştirme biçimi aynı kodu üretir ve ayrı tarihçeler bırakır; kurs her biçimi bıraktığı tarihçeye sorulan altı sorunun kaçını yanıtlayabildiğiyle ölçer.

## Dallar

İşaretçi olarak dal ve dallanmanın ucuzluğu, dal oluşturma ile çalışma alanının uyarlanması, ekip içinde okunabilir adlandırma düzeni ve ayrık HEAD durumunun nasıl oluştuğu ile nasıl çıkıldığı.

### Dal Kavramı

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/dal-kavrami

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.

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.

1. **Özellik gruplaması** — hangi işlemeler hangi işe ait?
2. **Kusurun yalıtımı** — kusuru getiren işleme tek başına bulunabiliyor mu?
3. **Gerçek sıra** — işler hangi sırayla geliştirildi?
4. **Dosya izi** — bir dosyaya dokunan her işleme ayrı ayrı bulunabiliyor mu?
5. **Dalın bütünlüğü** — bir dalın işlemeleri bir arada mı duruyor?
6. **Ç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.

```text
# öğ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**. `HEAD`in 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:

```text
# 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:

```text
# öğ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 `HEAD`in 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

```python
"""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, `HEAD`in 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/heads` altı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.
- `HEAD` bir işlemeyi değil bir dal başvurusunu gösterir; işleme yapmak yalnız dal
  başvurusundaki kimliği değiştirir, `HEAD` yerinde 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.

### Dal Oluşturma ve Geçiş

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/dal-olusturma-ve-gecis

Dal açmak ile dala geçmek ayrı işlemlerdir: geçiş yalnız iki dal arasında farklı olan dosyayı yeniden yazar ve elde kalan on iki durumun 3'ü taşınır, 5'i geçişi engeller, 4'ü geride kalır; sessizce kaybolan hiçbiri yoktur.

Önceki ders dallanmanın maliyetini ölçtü: üç dalın açılışı **0** yeni işleme ve **123** bayt
yazıyor. Ölçüm bir şeyi kasten dışarıda bıraktı. Bir dal adı yazıldıktan sonra `refs/heads`
altında yeni bir başvuru vardır, ama çalışma dizininde tek bir dosya değişmemiştir. `HEAD`
hâlâ eski dalı gösterir, ekrandaki kod hâlâ eski dalın kodudur. Ad yazılmıştır, o ada
geçilmemiştir.

Bu dersin sorusu geçişin kendisidir. Geçiş bir başvuru yazmaktan ibaret değildir; çalışma
dizinini ve hazırlık alanını hedef dalın gördüğü hâle getirmek zorundadır. İki soru buradan
doğar: geçiş kaç dosyaya dokunur ve o sırada elde işlenmemiş bir değişiklik varsa ona ne
olur? İkinci sorunun tek bir yanıtı yoktur — değişikliğin türüne ve dosyanın iki dal
arasında farklı olup olmadığına göre üç ayrı sonuç doğar.

#### İki Ayrı Komut

Dal oluşturmak ve dala geçmek aracın iki ayrı çağrısıdır. Birincisi yalnız başvuruyu yazar;
ikincisi `HEAD`i, hazırlık alanını ve çalışma dizinini birlikte günceller.

```text
# öğretilen komut ve örnek döküm — çalıştırılmamıştır

$ git branch olcum
$ git status --short --branch
## ana

$ git switch olcum
Switched to branch 'olcum'
$ git status --short --branch
## olcum
```

İlk çağrıdan sonra `git status` hâlâ `ana` diyor: dal var, geçilmemiş. İkinci çağrıdan sonra
aynı satır `olcum` diyor. Arada dosya adı geçmiyor, çünkü bu iki dal arasında henüz fark
yok; ölçüm birazdan farkın olduğu durumu sayacak.

İki işi tek çağrıda yapan bir kısayol vardır: `git switch -c olcum` önce başvuruyu yazar,
sonra ona geçer; eski biçimi `git checkout -b olcum`'dur. `git checkout` aşırı yüklü bir
addır — aynı komut hem dal değiştirir hem tek bir dosyayı geri alır ve ayrım yalnız
çağrının biçiminden okunur. `git switch` ile `git restore` bu iki işi iki ada böler. Kurs
boyunca ayrık adlar kullanılır.

#### Geçişin Üç Adımı

Bir dala geçmek üç ayrı yazma işlemidir ve üçü tek bir işlem gibi davranır: biri
başarısız olursa hiçbiri uygulanmaz.

Birincisi `HEAD`in yeniden yazılmasıdır. Önceki derste `HEAD`in bir işlemeyi değil bir dal
adını gösterdiği kurulmuştu; geçiş bu simgesel başvurunun içeriğini değiştirir ve maliyeti
tek bir satırdır. İkincisi hazırlık alanının hedef dalın ağacına göre yeniden kurulmasıdır.
Üçüncüsü çalışma dizinidir: hedef dalda **farklı** olan dosyalar hedefteki sürümle
değiştirilir.

Üçüncü adımın kapsamı kritiktir. Geçiş dalın tüm dosyalarını yeniden yazmaz, yalnız iki dal
arasında **farklı** olanları yazar. Fark iki türlüdür: içerik farkı (dosya iki dalda da var
ama sürümleri ayrı) ve varlık farkı (dosya bir dalda var, ötekinde yok). İkincisinde geçiş
ya bir dosya yaratır ya siler. Bu yüzden geçişin maliyeti dalın büyüklüğüne değil, iki dal
arasındaki farka bağlıdır — ve ölçümün üst tablosu bunu sayar.

#### Elde Kalan Değişiklik: Üç Kural

Geçiş anında çalışma ağacında işlenmemiş bir değişiklik varsa aracın davranışı üç kuralla
belirlenir.

**Birinci kural.** Dosya iki dal arasında **özdeşse** geçiş o dosyaya dokunmaz. Üstündeki
her işlenmemiş değişiklik — çalışma dizinindeki de, hazırlık alanına alınmış olanı da —
olduğu gibi yeni dalda belirir. Bu genellikle istenen davranıştır: bir dalda başlanan küçük
bir düzeltme, o dosya öteki dalda değişmemişse yanınızda gelir.

**İkinci kural.** Dosya iki dal arasında **farklıysa** geçiş onu yeniden yazmak zorundadır.
Üstünde işlenmemiş bir değişiklik varsa araç bu değişikliği ezmek yerine geçişi **durdurur**.
Hiçbir şey yazılmaz, `HEAD` yerinde kalır, hata iletisi hangi dosyanın engellediğini söyler.

**Üçüncü kural.** İzlenmeyen dosya tarihçenin dışındadır ve hiçbir dalın ağacında yer almaz.
Hedef dalda aynı adda bir dosya yoksa geçişle birlikte olduğu yerde durur. Aynı adda izlenen
bir dosya varsa geçiş onun üzerine yazacaktır ve yine **durdurulur**. Buradaki ince nokta
şudur: dosya tarihçenin dışında olduğu hâlde geçişi engeller, çünkü engelin nedeni dosyanın
kaydı değil, adının çakışmasıdır.

```text
# öğretilen komut ve örnek döküm — çalıştırılmamıştır

$ git switch rapor
error: Your local changes to the following files would be overwritten by checkout:
	rapor.py
Please commit your changes or stash them before you switch branches.
Aborting

$ git switch rapor
error: The following untracked working tree files would be overwritten by checkout:
	ayar.py
Please move or remove them before you switch branches.
Aborting
```

İki ileti iki ayrı kuraldan geliyor ve ikisi de aynı sonucu bildiriyor: geçiş yapılmadı.
İkinci iletinin önerdiği çözüm de farklıdır — izlenmeyen dosya işlenemez ya da zulaya
alınamaz, taşınması ya da silinmesi istenir.

#### Üç Çıkış Yolu

Engellenen bir geçişin önünde dört seçenek vardır ve üçü değişikliği korur.

Birincisi değişikliği **işlemektir**. Değişiklik eski dalın ucuna yazılır, bir başvurudan
erişilebilir olur ve geçiş serbest kalır. Değişiklik geçişle birlikte gelmez; eski dalda
kalır ve oraya dönüldüğünde bulunur.

İkincisi **zulaya (stash)** almaktır. `git stash push` işlenmemiş değişikliği çalışma
ağacından alır ve `refs/stash` altındaki ayrı bir başvuruya yazar; ağaç temizlenir, geçiş
serbest kalır. `git stash list` yığını listeler, `git stash pop` en üsttekini geri uygular.
Zula bir dala bağlı değildir — başka bir dalda da açılabilir ve o dalda dosya farklıysa geri
uygulama bir çakışma çıkarabilir. Bu davranış birleştirme konusunun kapsamındadır ve orada
ele alınır.

Üçüncüsü değişikliği **taşımayı zorlamaktır**: `git switch -m` seçeneği işlenmemiş
değişikliği hedef dalın sürümüyle birleştirmeyi dener. Başarılı olduğunda değişiklik yeni
dalda belirir; olmadığında çalışma ağacında çözülmesi gereken bir çakışma bırakır. Bu ders
çakışma çözmez ve seçeneği yalnız adıyla anar.

Dördüncüsü değişiklikten **vazgeçmektir**. `git switch` komutunun bunun için bir seçeneği
vardır ve işlenmemiş değişikliği geri alınamaz biçimde siler; değişiklik hiçbir zaman bir
işlemeye ya da bir başvuruya yazılmadığı için kurtarılabileceği bir yer yoktur. Bu ders o
seçeneği çalıştırılabilir biçimde yazmaz. Güvenli alışkanlık şudur: vazgeçmeden önce zulaya
alın; zula geri alınabilir, silme alınamaz.

Ölçümün varsayımları:

- **DL7** — Kurgu ortak tanımın on iki işlemesidir ve değiştirilmez. Taban ağacında üç dosya
  vardır: `olcum.py`, `rapor.py`, `kimlik.py`. `ayar.py` tabanda **yoktur**; üçüncü adımda
  `olcum` ve `rapor` onu ekler, bu yüzden `kimlik` dalının ağacında bulunmaz.
- **DL8** — Bir dalın çalışma ağacı, o dal ucundan görülen dosya sürümleridir: dala ait son
  işlemenin bıraktığı sürüm, dokunulmamışsa taban sürümü.
- **DL9** — Geçiş yalnız iki dal arasında **farklı** olan dosyayı yeniden yazar. Fark hem
  içerik farkını hem varlık farkını kapsar.
- **DL10** — Üç kural uygulanır: özdeş dosyadaki değişiklik **taşınır**; farklı dosyadaki
  işlenmemiş değişiklik geçişi **engeller**; izlenmeyen dosya hedefte aynı adda bir dosya
  yoksa taşınır, varsa geçişi engeller.
- **DL11** — On iki durum tek bir geçişte, `kimlik` dalından `rapor` dalına, denenir. Beş
  değişiklik türü sayılır: çalışma dizininde değiştirilmiş, hazırlık alanına alınmış,
  izlenmeyen, işlenmiş, zulaya alınmış.
- **DL12** — İşlenmiş ve zulaya alınmış değişiklik geçişi ilgilendirmez: ikisi de bir
  başvuruya yazılmıştır. Geçiş bunları taşımaz, bulundukları yerde bırakır; ölçüm bu sonucu
  **geride kalır** diye sayar.
- **DL13** — **Sessiz kayıp**, bir değişikliğin kullanıcıya bildirilmeden ortadan kalkmasıdır
  ve ayrı sayılır. Durdurulan geçiş bir kayıp değildir; hiçbir şey yazılmamıştır.
- **DL14** — Ölçüm bir dosya sistemi ölçümü değildir. Sayılan iki şey vardır: geçişin yeniden
  yazmak zorunda olduğu dosya ve geçiş anında elde kalan durumun nereye gittiği.

#### Ölçüm

```python
"""Dal olusturma ile dala gecis ayri islemlerdir.

Bolum 1 - iki dal arasindaki dosya farki: gecisin yeniden yazdigi dosyalar.
Bolum 2 - on iki durum: elde kalan degisiklik gecisten sonra nerede.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
TABAN = ("olcum.py", "rapor.py", "kimlik.py")   # ayar.py tabanda yoktur


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def agac(kayit, dal):
    """Bir dal ucundaki calisma agaci: dosya -> surum."""
    s = {d: "taban" for d in TABAN}
    for k in kayit:
        if k["dal"] == dal:
            s[k["dosya"]] = f"{dal}{k['sira']}"
    return s


def fark(kayit, kaynak, hedef):
    """Gecisin yeniden yazmak zorunda oldugu dosyalar."""
    a, b = agac(kayit, kaynak), agac(kayit, hedef)
    return sorted(d for d in set(a) | set(b) if a.get(d) != b.get(d))


KAYNAK, HEDEF = "kimlik", "rapor"
DURUMLAR = (("olcum.py", "çalışma dizini"), ("rapor.py", "çalışma dizini"),
            ("kimlik.py", "çalışma dizini"), ("olcum.py", "hazırlık alanı"),
            ("rapor.py", "hazırlık alanı"), ("kimlik.py", "hazırlık alanı"),
            ("not.txt", "izlenmeyen"), ("ayar.py", "izlenmeyen"),
            ("rapor.py", "işlenmiş"), ("kimlik.py", "işlenmiş"),
            ("rapor.py", "zulaya alınmış"), ("kimlik.py", "zulaya alınmış"))
YER = {"taşınır": "çalışma ağacında", "engellenir": "eski dalda, geçiş olmadı",
       "işlenmiş": "eski dalın ucunda", "zulaya alınmış": "zula yığınında"}


def sonuc(dosya, tur, farkli, hedef_agac):
    if tur in ("işlenmiş", "zulaya alınmış"):
        return "geride kalır"
    if tur == "izlenmeyen":
        return "engellenir" if dosya in hedef_agac else "taşınır"
    return "engellenir" if dosya in farkli else "taşınır"


kayit = gelisim()
print(f"kurgu: {len(kayit)} işleme, {len(DALLAR)} dal, taban ağacı "
      f"{len(TABAN)} dosya, dalların dokunduğu dosya "
      f"{len({k['dosya'] for k in kayit})}")
print()
print(f"{'geçiş':<20s}{'yeniden yazılan dosya':>22s}   hangileri")
for a, b in [("taban", d) for d in DALLAR] + [("kimlik", "rapor"),
                                              ("olcum", "rapor")]:
    f = fark(kayit, a, b)
    print(f"  {a + ' -> ' + b:<18s}{len(f):22d}   {', '.join(f)}")

farkli, hedef_agac = set(fark(kayit, KAYNAK, HEDEF)), agac(kayit, HEDEF)
print()
print(f"geçiş {KAYNAK} -> {HEDEF}: elde kalan on iki durum")
print(f"{'  elde ne var':<34s}{'hedefte':<9s}{'sonuç':<14s}değişiklik nerede")
sayac = {}
for dosya, tur in DURUMLAR:
    if tur in ("işlenmiş", "zulaya alınmış"):
        hedefte = "-"
    elif dosya not in hedef_agac:
        hedefte = "yok"
    elif tur == "izlenmeyen":
        hedefte = "var"
    else:
        hedefte = "farklı" if dosya in farkli else "özdeş"
    s = sonuc(dosya, tur, farkli, hedef_agac)
    sayac[s] = sayac.get(s, 0) + 1
    print(f"  {dosya + ' (' + tur + ')':<32s}{hedefte:<9s}{s:<14s}"
          f"{YER[tur] if s == 'geride kalır' else YER[s]}")
print()
for s in ("taşınır", "engellenir", "geride kalır"):
    print(f"{s:<14s}{sayac.get(s, 0):3d} / {len(DURUMLAR)}")
print(f"{'sessiz kayıp':<14s}{0:3d} / {len(DURUMLAR)}")
```

```
kurgu: 12 işleme, 3 dal, taban ağacı 3 dosya, dalların dokunduğu dosya 4

geçiş                yeniden yazılan dosya   hangileri
  taban -> olcum                         2   ayar.py, olcum.py
  taban -> rapor                         2   ayar.py, rapor.py
  taban -> kimlik                        1   kimlik.py
  kimlik -> rapor                        3   ayar.py, kimlik.py, rapor.py
  olcum -> rapor                         3   ayar.py, olcum.py, rapor.py

geçiş kimlik -> rapor: elde kalan on iki durum
  elde ne var                     hedefte  sonuç         değişiklik nerede
  olcum.py (çalışma dizini)       özdeş    taşınır       çalışma ağacında
  rapor.py (çalışma dizini)       farklı   engellenir    eski dalda, geçiş olmadı
  kimlik.py (çalışma dizini)      farklı   engellenir    eski dalda, geçiş olmadı
  olcum.py (hazırlık alanı)       özdeş    taşınır       çalışma ağacında
  rapor.py (hazırlık alanı)       farklı   engellenir    eski dalda, geçiş olmadı
  kimlik.py (hazırlık alanı)      farklı   engellenir    eski dalda, geçiş olmadı
  not.txt (izlenmeyen)            yok      taşınır       çalışma ağacında
  ayar.py (izlenmeyen)            var      engellenir    eski dalda, geçiş olmadı
  rapor.py (işlenmiş)             -        geride kalır  eski dalın ucunda
  kimlik.py (işlenmiş)            -        geride kalır  eski dalın ucunda
  rapor.py (zulaya alınmış)       -        geride kalır  zula yığınında
  kimlik.py (zulaya alınmış)      -        geride kalır  zula yığınında

taşınır         3 / 12
engellenir      5 / 12
geride kalır    4 / 12
sessiz kayıp    0 / 12
```

#### Sayıların Okunması

Üst tablo geçişin maliyetini veriyor. Tabandan `kimlik` dalına geçiş **1** dosya yazıyor,
`olcum` ve `rapor` dallarına geçiş **2** dosya. İki dal arasındaki geçişler **3** dosya
yazıyor, çünkü iki dalın da kendi dosyası ile ortak `ayar.py` dosyası farklıdır. Ağaçtaki
dördüncü dosya her satırda yerinde kalıyor.

Bu sayı önceki dersin ölçüsünü tamamlıyor. Dal açmak **41** bayt yazıyordu ve ağacın
büyüklüğünden bağımsızdı. Dala geçmek ağaca dokunur, ama tüm ağaca değil: dört bin dosyalık
bir ağaçta iki dal üç dosyada ayrılıyorsa geçiş **3** dosya yazar. Kopya modelinde aynı iş
dört bin dosyanın tamamını kopyalamayı gerektirirdi. İşaretçi modelinin geçişteki karşılığı
budur — maliyet ağacın değil, **farkın** büyüklüğüdür.

Alt tablo dersin ana ölçüsü. On iki durumun **3**'ü taşınıyor, **5**'i geçişi engelliyor,
**4**'ü geride kalıyor. Sessiz kayıp **0**.

Taşınan üçünün ortak yanı, geçişin o dosyaya hiç dokunmamasıdır. `olcum.py` iki dalda da
taban sürümündedir, bu yüzden hem çalışma dizinindeki hem hazırlık alanındaki değişikliği
yanında getirir. `not.txt` hiçbir dalın ağacında yoktur ve geçişin umurunda değildir.
İkisinde de taşıma bir yetenek değil, bir **çakışmama** sonucudur.

Engellenen beşinin de tek bir nedeni var: dosya iki dal arasında farklı ve geçiş onu yeniden
yazacak. Çalışma dizini ile hazırlık alanı arasında bu bakımdan hiçbir fark yok — dördü de
aynı iki dosyadan, `rapor.py` ile `kimlik.py`'den geliyor. Beşincisi, `ayar.py`, ayrı bir
yoldan aynı sonuca varıyor: dosya izlenmiyor, hiçbir işlemede geçmiyor, ama hedef dalda aynı
adda izlenen bir dosya var. Tarihçenin dışında olmak geçişi engellememek anlamına gelmiyor.

Sıfır sessiz kayıp bir tasarım kararıdır. Araç hiçbir durumda işlenmemiş bir değişikliği
bildirmeden silmez; engellemeyi seçer. **5/12** oranındaki durdurma bir kusur değil, o
kararın bedelidir. Bunun karşılığı şudur: iş akışında bir geçiş engellendiğinde ortada
kaybedilmiş bir şey yoktur, yalnız kayda alınmamış bir şey vardır.

Geride kalan dördü aracın değil kullanıcının kararıdır ve ikisi arasında bir fark vardır.
İşlenmiş değişiklik eski dalın ucundadır; bir dal başvurusundan erişilir ve dal adı
listelendiğinde görünür. Zulaya alınmış değişiklik `refs/stash` altındadır; erişilebilir ama
hiçbir dalda görünmez ve unutulduğunda bulunması dal aramaktan zordur. Bir işlemenin hiçbir
dal başvurusundan erişilemediği durum bu konunun dördüncü dersinin konusudur.

On iki durumluk kümede ölçülebilen en küçük fark **1/12**'dir. Taşınan ile engellenen
arasındaki **2/12**'lik fark bu bandın iki katıdır ve savunulabilir; daha ince ayrımlar bu
kurguyla savunulamaz.

#### Özet

- Dal oluşturmak yalnız bir başvuru yazar; dala geçmek `HEAD`i, hazırlık alanını ve çalışma
  dizinini birlikte günceller. `git switch -c` iki işi tek çağrıda yapar.
- Geçiş dalın tüm dosyalarını değil, iki dal arasında **farklı** olanları yeniden yazar;
  kurguda bu sayı tabandan dallara **1** ile **2**, dallar arasında **3** dosyadır.
- Özdeş dosyadaki işlenmemiş değişiklik taşınır; farklı dosyadaki değişiklik geçişi
  durdurur; izlenmeyen dosya yalnız hedefte aynı adda bir dosya varsa geçişi durdurur.
- On iki durumun **3**'ü taşınır, **5**'i engellenir, **4**'ü geride kalır; sessiz kayıp
  **0**'dır — araç işlenmemiş değişikliği silmek yerine geçişi durdurur.
- Engellenen geçişin üç güvenli çıkışı vardır: işleme, zula ve taşımayı birleştirmeyle
  zorlama; vazgeçme geri alınamaz ve zula onun güvenli karşılığıdır.

#### Sonraki Adım

Geçişin hangi dosyaya dokunacağı iki dalın farkından okunuyor ve bu, adına hiç bakmadan
hesaplanan bir sayı. Ad ise başka bir soruya hizmet eder. Ölçümde `kimlik` ve `rapor`
adları geçişin sonucunu hiç değiştirmedi; ama aynı depoda otuz dal varken hangisinin hangi
işe ait olduğu, hangisinin kapatılabileceği, hangisinin uzun ömürlü olduğu yalnız adlardan
okunur. Sonraki ders adlandırma düzenini ölçer: tarihçenin birinci sorusu olan özellik
gruplaması dal kaydı tutulduğunda evet der, ama adlar düzensizse grubun ne olduğu yine de
okunamaz — ve ölçüm bu iki koşulun birbirinin yerine geçmediğini gösterir.

### Dal Adlandırma

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/dal-adlandirma

Dal adlandırma düzeni tarihçenin birinci sorusuna dal kaydının yapamadığını ekler: iki tümleştirme biçimi ile dört ad düzeninin sekiz bileşiminden yalnız 3'ünde grup gerçekten okunur, çünkü kayıt ile okunabilir ad ayrı iki koşuldur.

Önceki ders geçişin hangi dosyaya dokunacağını iki dalın farkından hesapladı ve bu hesap
dalların adına hiç bakmadı. `kimlik` dalından `rapor` dalına geçiş üç dosya yazıyordu;
dallar `yeni` ile `deneme` diye adlansaydı aynı üç dosyayı yazacaktı. Araç bakımından ad bir
etikettir ve hiçbir davranışı değiştirmez.

Ad başka bir soruya hizmet eder. Aynı depoda otuz dal varken hangisinin hangi işe ait
olduğu, hangisinin kapatılabileceği, hangisinin ay boyunca yaşayacağı yalnız adlardan
okunur. Kursun ölçü ekseninde bunun karşılığı **birinci sorudur**: hangi işlemeler hangi işe
ait? Bu ders o sorunun iki ayrı koşulu olduğunu gösterir. Tarihçenin dal kaydını tutması
gerekir — ve kaydın taşıdığı adın bir şey söylüyor olması gerekir. İkisi birbirinin yerine
geçmez ve ölçüm bunu sayar.

#### Adın İki İşi

Bir dal adı önce bir **yoldur**. `refs/heads/olcum` başvurusunun adı `olcum`'dur ve ad,
başvurunun nerede aranacağını belirler. Eğik çizgi bu yolda bir ayraçtır: `ozellik/olcum`
adı `refs/heads/ozellik/olcum` başvurusuna karşılık gelir ve `ozellik` bir hiyerarşi
basamağı olur. Araç bu basamağı tanır — desen eşleştiren her komut eğik çizgiyi bir sınır
gibi kullanabilir.

Ad ikinci olarak bir **bildirimdir**. Aracın okumadığı, insanın okuduğu bir alandır. Bir
işleme mesajı gibi, sonradan sorulacak sorulara yanıt taşımak üzere yazılır ya da yazılmaz.
**Dal adlandırma düzeni (branch naming convention)** bu ikinci işi düzenli hâle getirir:
adın hangi alanları hangi sırayla taşıyacağını belirler.

Alanlar tipik olarak üçtür. **Tür** işin sınıfını verir (özellik, düzeltme, bakım) ve eğik
çizgiden önce durur. **İş kaydı** adı dışarıdaki bir kayda bağlar; bir numara ya da kısa
kod olur. **Kısa ad** işin kendisini bir iki kelimeyle söyler. Ölçüm bu üç alanı ayrı ayrı
sayar, çünkü her biri ayrı bir soruya yanıt verir ve bir düzen üçünü birden taşımak zorunda
değildir.

#### Adın Geçerliliği

Ad bir dosya yolu olarak yazıldığı için biçimi serbest değildir. Araç geçerliliği ayrı bir
alt komutla sınar.

```text
# öğretilen komut ve örnek döküm — çalıştırılmamıştır

$ git check-ref-format --branch 'ozellik/olcum'
ozellik/olcum
$ git check-ref-format --branch 'ozellik olcum'
fatal: 'ozellik olcum' is not a valid branch name

$ git branch --list 'ozellik/*'
  ozellik/214-olcum
  ozellik/215-rapor
  ozellik/216-kimlik
```

Kuralların çoğu iki kaygıdan doğar. Birincisi kabuk ve komut satırı: boşluk, `*`, `[`, `?`
ve ters eğik çizgi kabukta anlam taşır ve tırnaklanmadan yazıldığında başka bir şeye
dönüşür. İkincisi aracın kendi başvuru sözdizimi: `~`, `^`, `:`, `..` ve `@{` işaretleri bir
işlemeyi ada göre çözerken kullanılan operatörlerdir. `ozellik/olcum~1` bir dal adı olsaydı
"bu dalın bir önceki işlemesi" ifadesi okunamaz hâle gelirdi. `.lock` uzantısı ise başvuru
yazarken kullanılan kilit dosyalarıyla çakışır.

Bir kural daha var ve ötekilerden ayrı bir yerden gelir. `refs/heads` altında bir ad hem
dosya hem dizin olamaz; bu yüzden `ozellik` adında bir dal, `ozellik/olcum` adında bir dal
varken açılamaz.

```text
# öğretilen komut ve örnek döküm — çalıştırılmamıştır

$ git branch ozellik
fatal: cannot lock ref 'refs/heads/ozellik': 'refs/heads/ozellik/olcum' exists

$ git branch --merged ana
  ozellik/214-olcum
$ git branch -d ozellik/214-olcum
Deleted branch ozellik/214-olcum (was 6e2d70b).
```

Bu, düzenin doğrudan bir sonucudur: bir öneki dal adı olarak da kullanmak, o önek altındaki
her adı engeller. Önek yalnız önek olarak kalmalıdır.

#### Düzenin Ödediği İşler

Ad düzeninin karşılığını verdiği üç günlük iş vardır ve üçü de listeleme komutlarıyla
yapılır.

Birincisi **süzmedir**. `git branch --list 'ozellik/*'` yalnız o öneki taşıyan dalları
listeler. Düzensiz adlarda bu desen hiçbir şey seçmez; otuz dallık bir depoda hangi dalın
hangi türe ait olduğunu ancak tek tek okuyarak öğrenirsiniz.

İkincisi **kapatmadır**. `git branch --merged ana` ana dala alınmış dalları listeler; bunlar
silinebilecek olanlardır. `git branch -d` yalnız birleşmiş bir dalı siler ve birleşmemişse
reddeder. Zorlayan biçimi bu kontrolü atlar ve önceki dersin ölçüsü bunun bedelini vermişti:
bir dal başvurusu silindiğinde o daldan erişilen dört işleme hiçbir başvurudan erişilemez
hâle gelir. Ad düzeni burada dolaylı ama gerçek bir katkı yapar — hangi dalın kısa ömürlü
olduğu adından okunuyorsa, silinecek dal listesi bir tahmin olmaktan çıkar.

Üçüncüsü **ömür ayrımıdır**. **Uzun ömürlü dal (long-lived branch)** deponun yaşadığı sürece
duran, silinmeyen ve tek bir işe ait olmayan daldır; adı sabittir ve tür öneki taşımaz.
**Kısa ömürlü dal (short-lived branch)** tek bir iş için açılır, iş tümleştirilince silinir;
adı tür önekini ve iş kaydını taşır. İki sınıfın ad biçimi ayrıldığında liste okunduğunda
hangisinin bir gün kapanacağı görülür. Ayrım burada yalnız adlandırma düzeyinde kurulur; dal
stratejilerinin karşılaştırması birleştirme konusunun son dersine bırakılmıştır.

Ölçümün varsayımları:

- **DL15** — Kurgu ortak tanımın on iki işlemesidir ve değiştirilmez. Ad düzeni kurguya
  eklenen bir katmandır; **altı sorunun tanımına dokunmaz.**
- **DL16** — Ölçüm birinci soruyu, `s1_gruplama` işlevini ortak tanımdaki tanımıyla
  kullanır: tarihçedeki her işlemenin bir **dal kaydı** taşıyıp taşımadığına bakar. Bu ders
  öteki beş soruyu ölçmez; onlar birleştirme konusunda ölçülür.
- **DL17** — İki tümleştirme biçimi karşılaştırılır: birleştirme işlemesi dal kaydını tutar,
  ileri sarma tutmaz. Biçimlerin tanımı ortak tanımdan alınır ve değiştirilmez.
- **DL18** — Dört ad düzeni denenir: serbest, kısa ad, türe göre, türe ve kayda göre. Her
  düzen üç dala üç ad verir.
- **DL19** — **Addan iş okunur** ölçütü kâhinle karşılaştırmadır: adın içinde işin kurgudaki
  adı geçiyorsa okunur sayılır. **Addan tür okunur** ölçütü eğik çizgiden önceki parçanın
  bilinen tür listesinde bulunmasını arar; ayraçsız bir kelime tür sayılmaz. **İş kaydı**
  ölçütü adın parçaları arasında sayısal bir alan arar.
- **DL20** — **Grup okunur** ölçütü iki koşulun birleşimidir: tarihçe dal kaydını tutuyor
  **ve** üç adın üçü de işi veriyor. Koşullardan biri düşerse grup okunmaz.
- **DL21** — Ad geçerliliği aracın kural kümesinin bir alt kümesiyle sınanır; ölçüm bir
  ayrıştırıcı değil, kuralların hangi adayı neden reddettiğini gösteren bir sayımdır.

#### Ölçüm

```python
"""Dal adlandirma duzeni: dal kaydi ile okunabilir ad ayri kosullardir.

Bolum 1 - dort ad duzeni, dort olcut: addan ne okunuyor.
Bolum 2 - bicim x duzen caprazi: birinci soru gercekten okunuyor mu.
Bolum 3 - ad gecerliligi ve onek cakismasi.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
TURLER = ("ozellik", "duzeltme", "bakim")
DUZENLER = {
    "serbest": {"olcum": "yeni", "rapor": "deneme", "kimlik": "duzeltme"},
    "kısa ad": {"olcum": "olcum", "rapor": "rapor", "kimlik": "kimlik"},
    "türe göre": {"olcum": "ozellik/olcum", "rapor": "ozellik/rapor",
                  "kimlik": "ozellik/kimlik"},
    "türe ve kayda göre": {"olcum": "ozellik/214-olcum",
                           "rapor": "ozellik/215-rapor",
                           "kimlik": "ozellik/216-kimlik"}}
BICIMLER = ("birlestirme islemesi", "ileri sarma")


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def tumlestir(kayit, bicim):
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit
                                   if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True,
                      "zaman_korundu": True})
    elif bicim == "ileri sarma":
        for k in sorted(kayit, key=lambda x: x["zaman"]):
            t.append({**k, "dal_kaydi": None, "zaman_korundu": True})
    return t


def s1_gruplama(t):
    """Ortak tanimin birinci sorusu; tanimi degistirilmez."""
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def isi_verir(ad, dal):
    return dal in ad


def turu_verir(ad):
    return "/" in ad and ad.split("/")[0] in TURLER


def kayda_gider(ad):
    return any(p.isdigit() for p in ad.replace("/", "-").split("-"))


def suzulur(ad, onek="ozellik/"):
    return ad.startswith(onek)


YASAK = set(" ~^:?*[\\")


def gecerli(ad):
    if not ad or ad.startswith("/") or ad.endswith("/") or "//" in ad:
        return "eğik çizgi konumu"
    if ad.startswith("-"):
        return "tire ile başlıyor"
    if any(c in YASAK for c in ad):
        return "yasak karakter"
    if ".." in ad or "@{" in ad or ad.endswith(".lock"):
        return "yasak dizi"
    if any(p.startswith(".") for p in ad.split("/")):
        return "nokta ile başlayan parça"
    return "geçerli"


def onek_cakismasi(adlar):
    return [(a, b) for a in adlar for b in adlar
            if a != b and b.startswith(a + "/")]


kayit = gelisim()
T = {b: tumlestir(kayit, b) for b in BICIMLER}
print(f"kurgu: {len(kayit)} işleme, {len(DALLAR)} dal; ad düzeni kurguyu da "
      f"altı sorunun tanımını da değiştirmez")
print()
print(f"{'ad düzeni':<22s}{'addan iş':>9s}{'addan tür':>10s}"
      f"{'iş kaydı':>9s}{'önekle süzülen':>15s}")
for ad, kume in DUZENLER.items():
    print(f"  {ad:<20s}{sum(isi_verir(kume[d], d) for d in DALLAR):7d}/3"
          f"{sum(turu_verir(kume[d]) for d in DALLAR):8d}/3"
          f"{sum(kayda_gider(kume[d]) for d in DALLAR):7d}/3"
          f"{sum(suzulur(kume[d]) for d in DALLAR):15d}")
print()
print(f"{'biçim':<24s}{'ad düzeni':<20s}{'dal kaydı':<10s}{'addan iş':>9s}"
      f"{'grup okunur':>12s}")
okunur = 0
for b in BICIMLER:
    kayit_var = s1_gruplama(T[b])
    for ad, kume in DUZENLER.items():
        n = sum(isi_verir(kume[d], d) for d in DALLAR)
        g = kayit_var and n == len(DALLAR)
        okunur += g
        print(f"  {b:<22s}{ad:<20s}{('var' if kayit_var else 'yok'):<10s}"
              f"{(str(n) + '/3') if kayit_var else '-':>9s}"
              f"{('evet' if g else 'hayır'):>12s}")
print(f"birinci soru: dal kaydı {sum(s1_gruplama(T[b]) for b in BICIMLER)}"
      f" / {len(BICIMLER)} biçimde var; grup okunur "
      f"{okunur} / {len(BICIMLER) * len(DUZENLER)} satırda")
print()
ADAYLAR = ("ozellik/olcum", "ozellik olcum", "ozellik/olcum~1",
           "ozellik/..olcum", "ozellik/olcum.lock", "ozellik//olcum",
           "-olcum", "ozellik/olcum@{1}")
print(f"{'aday ad':<24s}sonuç")
for a in ADAYLAR:
    print(f"  {a:<22s}{gecerli(a)}")
KUME = ("ozellik", "ozellik/olcum", "ozellik/rapor", "ozellik/kimlik")
print(f"\nönek çakışması: {len(onek_cakismasi(KUME))} çift — "
      f"ozellik adı ozellik/ altındaki üç dalla birlikte var olamaz")
```

```
kurgu: 12 işleme, 3 dal; ad düzeni kurguyu da altı sorunun tanımını da değiştirmez

ad düzeni              addan iş addan tür iş kaydı önekle süzülen
  serbest                   0/3       0/3      0/3              0
  kısa ad                   3/3       0/3      0/3              0
  türe göre                 3/3       3/3      0/3              3
  türe ve kayda göre        3/3       3/3      3/3              3

biçim                   ad düzeni           dal kaydı  addan iş grup okunur
  birlestirme islemesi  serbest             var             0/3       hayır
  birlestirme islemesi  kısa ad             var             3/3        evet
  birlestirme islemesi  türe göre           var             3/3        evet
  birlestirme islemesi  türe ve kayda göre  var             3/3        evet
  ileri sarma           serbest             yok               -       hayır
  ileri sarma           kısa ad             yok               -       hayır
  ileri sarma           türe göre           yok               -       hayır
  ileri sarma           türe ve kayda göre  yok               -       hayır
birinci soru: dal kaydı 1 / 2 biçimde var; grup okunur 3 / 8 satırda

aday ad                 sonuç
  ozellik/olcum         geçerli
  ozellik olcum         yasak karakter
  ozellik/olcum~1       yasak karakter
  ozellik/..olcum       yasak dizi
  ozellik/olcum.lock    yasak dizi
  ozellik//olcum        eğik çizgi konumu
  -olcum                tire ile başlıyor
  ozellik/olcum@{1}     yasak dizi

önek çakışması: 3 çift — ozellik adı ozellik/ altındaki üç dalla birlikte var olamaz
```

#### Sayıların Okunması

Üst tablo dört düzeni alan alan karşılaştırıyor. Serbest adlar dört ölçütün dördünde de
sıfır veriyor: `yeni`, `deneme` ve `duzeltme` adlarından hangisinin hangi işe ait olduğu
çıkarılamıyor. Üçüncü ad özellikle seçildi — `duzeltme` bir tür adı gibi görünüyor ama ayraç
taşımadığı için tür sayılmıyor ve gösterdiği iş `kimlik`, yani ad yalnız susmuyor, yanlış
yönlendiriyor.

Kısa ad düzeni ilk sütunu **3/3** yapıyor ve tek başına en büyük sıçramayı burada
kazandırıyor. Tür öneki ikinci sütunu açıyor ve **önekle süzülen** sütununu **0**'dan
**3**'e çıkarıyor; süzmenin koşulu adın işi vermesi değil, ortak bir önek taşımasıdır. İş
kaydı alanı yalnız dördüncü düzende var ve yalnız üçüncü sütunu açıyor. Her alan bir sütun
açıyor, hiçbiri iki sütun açmıyor: alanlar birbirinin yerine geçmiyor.

Orta tablo dersin ana ölçüsü. Sekiz satırın **3**'ünde grup okunuyor ve okunmayan beş satır
iki ayrı nedenden okunmuyor.

Dört satır **dal kaydının yokluğundan** düşüyor. İleri sarma biçiminde dört ad düzeninin
dördü de aynı sonucu veriyor, çünkü ad tarihçeye hiç girmiyor: dal adı yalnız dal yaşadığı
sürece vardır, dal silindiğinde yok olur ve tarihçede ondan geriye bir iz kalmaz. En özenli
adlandırma düzeni bile kayıt tutulmayan bir biçimde sıfır katkı yapar.

Beşinci satır **adın boşluğundan** düşüyor. Birleştirme işlemesi biçiminde dal kaydı var;
tarihçe üç grubu ayırt ediyor ve `s1_gruplama` bu satırda da evet diyor. Ama kaydın taşıdığı
ad `yeni`, `deneme` ve `duzeltme`. Kaydın varlığı grubun **sınırını** verir, adı ise grubun
**adını** verir; ikincisi olmadan tarihçe "bu on iki işleme üç gruba ayrılıyor" der ve
grupların ne olduğunu söylemez. Ölçünün söylediği tam olarak budur: dal kaydı **1/2** biçimde
var, ama grup **3/8** satırda okunuyor.

Sekiz satırlık kümede ölçülebilen en küçük fark **1/8**'dir. Kayıt kaybının bedeli
**4/8**, ad kaybının bedeli **1/8**; ikisi aynı büyüklükte değildir ve sıralamaları
tersine çevrilemez. Kayıt tutmayan bir biçimde adlandırma düzeni tartışmasının karşılığı
yoktur; kayıt tutan bir biçimde ise düzenin bedeli tek bir satırdır ve o satır ödenmezse
kaydın kendisi yarı yarıya boşa gider.

Alt tablo adın biçim kurallarını veriyor. Sekiz adaydan **1**'i geçerli, **7**'si dört ayrı
nedenle reddediliyor. Reddedilenlerin hiçbiri bir yazım kusuru değil; her biri adın ya
kabukla ya aracın başvuru sözdizimiyle çakıştığı bir yerde duruyor. Son satırdaki **3**
çakışma ise düzenin kendi içinden gelen tek kısıttır: bir öneki dal adı olarak kullanmak, o
önek altındaki her adı imkânsız kılar.

#### Özet

- Dal adı hem `refs/heads` altındaki bir yol hem insan için bir bildirimdir; eğik çizgi bu
  yolda bir hiyerarşi basamağı kurar ve desenle süzmeyi mümkün kılar.
- Ad düzeni üç alan taşıyabilir — tür, iş kaydı, kısa ad — ve ölçümde her alan yalnız bir
  ölçütü açar: alanlar birbirinin yerine geçmez.
- Sekiz bileşimin **3**'ünde grup okunur; düşen beş satırın **4**'ü dal kaydının
  yokluğundan, **1**'i adın hiçbir şey söylememesinden düşer.
- Kayıt tutmayan bir tümleştirme biçiminde ad tarihçeye hiç girmez ve adlandırma düzeninin
  katkısı **0**'dır; kayıt tutan biçimde düzensiz ad kaydın yarısını boşa çıkarır.
- Sekiz aday addan **1**'i geçerlidir; reddedilenler kabuk imleriyle ve aracın başvuru
  sözdizimiyle çakışır ve bir önek dal adı olarak kullanıldığında altındaki **3** ad
  birden engellenir.

#### Sonraki Adım

Ad, bir işlemeler kümesini bir işe bağlayan şeydir; kayıt onun sınırını, düzen onun anlamını
taşır. İkisi de `HEAD`in bir dal adını göstermesine dayanır — önceki derslerde kurulan
zincir üç halkalıydı: `HEAD` bir dal adını, dal adı bir işleme kimliğini, işleme kendi
öncülünü gösterir. Peki orta halka çıkarsa? `HEAD` bir dal adı yerine doğrudan bir işleme
kimliğini gösterdiğinde yapılan işlemeler hiçbir dala bağlanmaz. Sonraki ders bu durumun
nasıl oluştuğunu, orada yapılan işlemenin altı sorunun hangilerinin dışına düştüğünü ve
tarihçenin kaybettiğini bilmediği hâlde nasıl yanıt vermeyi sürdürdüğünü ölçer.

### Ayrık HEAD Durumu

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/ayrik-head-durumu

Ayrık HEAD üstünde yapılan iki işleme hiçbir başvurudan erişilemediğinde tarihçe 15 kayıttan 13'e iner ve altı sorunun 5'i yerine 4'ünü yanıtlar; asıl bedel üç sayım sorgusunun üçünün de eksik yanıtlanması ve tarihçenin bunu bildirmemesidir.

Önceki üç ders `HEAD` ile işleme arasında üç halkalı bir zincir kurdu: `HEAD` bir dal adını,
dal adı bir işleme kimliğini, işleme kendi öncülünü gösterir. Bir dalda çalışmak bu zincirin
tam olmasına dayanır — işleme yapıldığında orta halka ilerler, `HEAD` yerinde kalır ve yeni
işleme bir ada bağlanmış olur.

Orta halka her zaman yerinde değildir. Araç `HEAD`in doğrudan bir işleme kimliğini
gösterdiği bir durum tanır ve buna **ayrık HEAD (detached HEAD)** denir. Bu ders o durumun
nasıl oluştuğunu, orada yapılan işlemenin nereye bağlandığını ve zincirin bir halkası eksik
kaldığında tarihçenin ne kaybettiğini ölçer. Ölçümün asıl sonucu kayıp miktarı değildir;
tarihçenin kaybettiğini **bilmemesidir**.

#### HEAD Nasıl Ayrılır

Ayrık durum bir kusur ya da bozulma değildir; aracın belirli bir isteği karşılama biçimidir.
İstek şudur: bir dalın ucunda değil, tarihçenin belirli bir noktasında durmak. Bir işleme
kimliğine, bir etikete ya da bir uzak dal ucuna geçildiğinde `HEAD` bir ada bağlanamaz,
çünkü hedef bir ad değildir.

```text
# öğretilen komut ve örnek döküm — çalıştırılmamıştır

$ git switch --detach 7c3f9d2
HEAD is now at 7c3f9d2 rapor başlığını biçimlendir
$ git status --short --branch
## HEAD (no branch)
$ git symbolic-ref HEAD
fatal: ref HEAD is not a symbolic ref
```

Son satır durumun tanımıdır. İlk derste aynı komut `refs/heads/ana` yanıtını veriyordu:
`HEAD` bir başvuruyu gösteriyordu. Burada gösterecek bir başvuru yok, bu yüzden komut
simgesel bir başvuru bulamıyor. `git status` de aynı şeyi başka sözcüklerle söylüyor.

Durum kasten seçilebilir. Bir hatanın hangi işlemede girdiğini ararken tarihçede ileri geri
gezinmek, eski bir sürümü derleyip sınamak, bir etiketin gösterdiği ağacı incelemek — hepsi
ayrık durumda yapılır ve hiçbiri sorun değildir. Sorun, ayrık durumda **işleme yapmakla**
başlar.

#### Ayrık Durumda Yapılan İşleme

Ayrık durumda işleme yapmak engellenmez. İşleme nesnesi yazılır, öncülü doğru bağlanır,
ağacı kaydedilir; içerik bakımından öteki işlemelerden hiçbir farkı yoktur. Değişen tek şey
kimin ilerlediğidir: `HEAD` yeni kimliği doğrudan gösterir ve **hiçbir dal başvurusu
yazılmaz**.

Bunun sonucu ikinci işlemede henüz görünmez, çünkü `HEAD` zinciri hâlâ o işlemeleri tutar.
Sonuç, başka bir dala geçildiği anda doğar. `HEAD` artık başka bir dalı gösterir ve ayrık
durumda yazılan işlemelere hiçbir başvurudan erişilemez. Araç bunu geçiş sırasında bildirir.

```text
# öğretilen komut ve örnek döküm — çalıştırılmamıştır

$ git switch rapor
Warning: you are leaving 2 commits behind, not connected to
any of your branches:

  b71c05e rapor: eşik tablosunu ayara taşı
  a04e9d2 rapor: özet satırını ekle

If you want to keep them by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> b71c05e

Switched to branch 'rapor'
```

Uyarı hem sayıyı hem kimliği veriyor ve çözümü de söylüyor: bir dal yazın. Birinci dersin
ölçüsü bu önerinin bedelini vermişti — bir dal başvurusu **41 bayttır**. İki işlemeyi
tarihçeye bağlamak kırk bir bayt yazmaktır ve bu yüzden çıkış yolu ucuzdur.

Ayrık durumdan çıkmanın üç yolu vardır. `git switch -c ad` bulunduğunuz noktaya bir dal
yazar ve ona geçer; bu, çalışmayı yerinde kurtaran yoldur. `git switch <dal>` bir dala döner
ve ayrık işlemeleri geride bırakır. Üçüncüsü geriye dönüktür: `git branch ad <kimlik>` ile
sonradan da bir başvuru yazılabilir — yeter ki kimlik hâlâ bilinsin.

#### Referans Günlüğü

Kimliği bilmenin yolu **referans günlüğüdür (reflog)**. Araç `HEAD`in ve her dal
başvurusunun aldığı **her değeri** yerel bir günlüğe yazar: hangi işlemeye geçildi, hangi
işleme yapıldı, hangi başvuru nereye kaydı. Günlük yereldir ve depoyla birlikte
taşınmaz — kopyalanan bir depo kaynak deponun referans günlüğünü almaz.

```text
# öğretilen komut ve örnek döküm — çalıştırılmamıştır

$ git reflog
b71c05e HEAD@{0}: commit: rapor: eşik tablosunu ayara taşı
a04e9d2 HEAD@{1}: commit: rapor: özet satırını ekle
7c3f9d2 HEAD@{2}: checkout: moving from rapor to 7c3f9d2

$ git branch kurtarma b71c05e
$ git branch --contains b71c05e
  kurtarma
```

`HEAD@{0}` sözdizimi `HEAD`in bir önceki konumlarını adlandırır ve bir işleme kimliği
beklenen her yerde kullanılabilir. Üçüncü satır ayrılmanın kendisini kaydediyor; ilk iki
satır ayrık durumda yapılan iki işlemeyi. Dal yazıldıktan sonra son komut işlemenin artık
bir başvurudan erişilebildiğini doğruluyor.

Referans günlüğünün kendisi bir başvuru değildir ve erişilebilirlik hesabına girmez. Bir
kurtarma aracıdır, bir saklama düzeneği değil: girdilerin ömrü sınırlıdır ve
yapılandırılabilir bir süre sonunda düşerler; erişilemeyen işlemelere ait girdilerin ömrü
ötekilerden kısadır.

#### Kurtarılamayan Sınır

Referans günlüğü girdisi düştükten sonra erişilemeyen bir işlemenin kimliğini verecek bir
kayıt kalmaz. Nesne hâlâ veritabanındadır, ama adı yoktur ve aranacak bir yeri de yoktur.

Bir adım sonrası kalıcıdır. Araç, hiçbir başvurudan erişilemeyen nesneleri toplayan bir
bakım komutu taşır; bu komutun budama seçeneği erişilemeyen nesneleri **veritabanından
siler**. Bu ders o çağrıyı çalıştırılabilir biçimde yazmaz, çünkü sonucu **geri alınamaz**:
silinen işlemenin içeriği hiçbir yerden geri getirilemez, referans günlüğü de silinmiş bir
nesneyi geri koyamaz. Bakım komutu ayrıca kimi işlemlerin parçası olarak kendiliğinden de
çalışabilir; erişilemeyen bir işlemeyi "sonra kurtarırım" diye bırakmak bu yüzden güvenli
değildir.

Güvenli alışkanlık tek cümledir: **önce başvuruyu yaz.** Ayrık durumda bir iş yapılacaksa
işe başlamadan bir dal açılır; iş bittikten sonra fark edildiyse referans günlüğü hâlâ
sıcakken bir yedek dal yazılır. Kurtarmanın tüm maliyeti kırk bir bayttır ve bu bayt
yazılmadığı sürece iş tek bir bakım çağrısı uzaklıktadır.

Ölçümün varsayımları:

- **DL22** — Kurgu ortak tanımın on iki işlemesidir ve `gelisim` değiştirilmez. Ders
  kurgunun tek bir adımını değiştirir: `rapor` işinin **ikinci ve üçüncü** işlemesi ayrık
  HEAD üstünde yapılmıştır ve dal ucuna bağlanmamıştır.
- **DL23** — Erişilebilirlik başvurulardan başlar. Bir işlemeye hiçbir dal başvurusundan
  ulaşılamıyorsa o işleme **tarihçenin dışındadır**; nesne veritabanında durması bunu
  değiştirmez.
- **DL24** — Ölçüm tek bir tümleştirme biçimi kullanır: birleştirme işlemesi. Tanımı ortak
  tanımdan alınır ve değiştirilmez; bu ders biçimleri karşılaştırmaz.
- **DL25** — Altı sorunun tanımı değiştirilmez ve yalnız **erişilebilen** tarihçeye sorulur.
  Soru, kendisine verilmeyen bir işlemeden haberdar olamaz.
- **DL26** — Üç **sayım sorgusu** ayrıca sorulur ve kâhinle karşılaştırılır: `rapor` işine
  ait işleme sayısı, kusuru getiren işleme sayısı, ortak dosyaya dokunan işleme sayısı.
  Kâhin kurguyu bildiği için gerçek sayıyı verir.
- **DL27** — Referans günlüğü bir başvuru **değildir** ve erişilebilirlik hesabına girmez;
  yalnız erişilemeyen bir işlemenin kimliğini bulmaya yarar.
- **DL28** — Kurtarma, düşen işlemeleri bir başvuruya bağlamaktır. Ölçüm kurtarılmış
  tarihçeyi düşenleri geri koyup zaman sırasına dizerek kurar; kurtarma işlemelerin
  içeriğini de sırasını da değiştirmez.
- **DL29** — Kurtarma maliyeti tek bir dal başvurusudur: kırk onaltılık basamak ve bir satır
  sonu, **41 bayt**. Birinci dersin ölçüsü burada aynen geçerlidir.

#### Ölçüm

```python
"""Ayrik HEAD: hicbir basvurudan erisilemeyen isleme tarihcenin disindadir.

Bolum 1 - ayrik durumda yapilan iki isleme tarihceden dusuyor.
Bolum 2 - alti soru uc tarihceye soruluyor: hangi soru disarida kaliyor.
Bolum 3 - sayim sorgulari kahinle karsilastiriliyor: yanit var ama eksik.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
AYRIK = ("rapor", (2, 3))     # bu iki isleme ayrik HEAD ustunde yapildi
BASVURU_BAYT = 40 + 1


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def tumlestir(kayit, bicim):
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit
                                   if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True,
                      "zaman_korundu": True})
    return t


def s1_gruplama(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2_kusur(t):
    kusurlu = [x for x in t if x["kusurlu"]]
    return len(kusurlu) == 1 and not kusurlu[0].get("sikisik")


def s3_sira(t):
    return all(x.get("zaman_korundu") for x in t)


def s4_dosya(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5_kapsam(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6_cakisma(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1_gruplama), ("kusurun yalıtımı", s2_kusur),
           ("gerçek sıra", s3_sira), ("dosya izi", s4_dosya),
           ("dalın bütünlüğü", s5_kapsam), ("çakışma kararı", s6_cakisma))


def erisilebilen(kayit, dusen):
    dal, siralar = dusen
    return [k for k in kayit if not (k["dal"] == dal and k["sira"] in siralar)]


def kurtar(kalan, dusenler):
    """Referans gunlugunden bulunan islemeler bir basvuruya baglanir."""
    return sorted(kalan + dusenler, key=lambda x: x["zaman"])


def sorgula(kume):
    """Tarihceye ya da kurguya sorulan uc sayim sorgusu."""
    return {"rapor işine ait işleme": sum(1 for x in kume
                                          if x["dal"] == "rapor"
                                          and not x.get("birlesme")),
            "kusuru getiren işleme": sum(1 for x in kume if x["kusurlu"]),
            "ayar.py'ye dokunan işleme": sum(1 for x in kume
                                             if x["dosya"] == ORTAK_DOSYA)}


kayit = gelisim()
kalan = erisilebilen(kayit, AYRIK)
dusen = [k for k in kayit if k["dal"] == AYRIK[0] and k["sira"] in AYRIK[1]]
TARIHCELER = (("bağlı HEAD", kayit), ("ayrık HEAD", kalan),
              ("kurtarılmış", kurtar(kalan, dusen)))
T = {ad: tumlestir(k, "birlestirme islemesi") for ad, k in TARIHCELER}

print(f"kurgu: {len(kayit)} işleme; ayrık HEAD üstünde yapılan {len(dusen)} "
      f"işleme hiçbir başvurudan erişilemez")
print()
print(f"{'tarihçe':<14s}{'erişilen işleme':>16s}{'tarihçe işlemesi':>17s}"
      f"{'erişilemeyen':>13s}{'yanıtlanan':>11s}")
for ad, k in TARIHCELER:
    print(f"  {ad:<12s}{len(k):16d}{len(T[ad]):17d}"
          f"{len(kayit) - len(k):13d}"
          f"{sum(soru(T[ad]) for _, soru in SORULAR):11d}")
print()
BASLIK = (("bağlı HEAD", 12), ("ayrık HEAD", 12), ("kurtarılmış", 13))
print(f"{'soru':<22s}" + "".join(f"{a:>{w}s}" for a, w in BASLIK))
for ad, soru in SORULAR:
    print(f"  {ad:<20s}" + "".join(
        f"{('evet' if soru(T[a]) else 'hayır'):>{w}s}" for a, w in BASLIK))
print()
kahin = sorgula(kayit)
ayrik, geri = sorgula(T["ayrık HEAD"]), sorgula(T["kurtarılmış"])
print(f"{'sayım sorgusu':<28s}{'kâhin':>7s}{'ayrık HEAD':>12s}{'eksik':>7s}"
      f"{'kurtarılmış':>13s}")
for s in kahin:
    print(f"  {s:<26s}{kahin[s]:7d}{ayrik[s]:12d}{kahin[s] - ayrik[s]:7d}"
          f"{geri[s]:13d}")
print(f"\nüç sorgunun {sum(1 for s in kahin if kahin[s] != ayrik[s])}'ü eksik "
      f"yanıtlanıyor; tarihçe eksik olduğunu bildirmiyor")
print(f"kurtarma maliyeti: {len(dusen)} işlemeyi geri bağlamak için "
      f"1 başvuru, {BASVURU_BAYT} bayt")
```

```
kurgu: 12 işleme; ayrık HEAD üstünde yapılan 2 işleme hiçbir başvurudan erişilemez

tarihçe        erişilen işleme tarihçe işlemesi erişilemeyen yanıtlanan
  bağlı HEAD                12               15            0          5
  ayrık HEAD                10               13            2          4
  kurtarılmış               12               15            0          5

soru                    bağlı HEAD  ayrık HEAD  kurtarılmış
  özellik gruplaması          evet        evet         evet
  kusurun yalıtımı            evet       hayır         evet
  gerçek sıra                 evet        evet         evet
  dosya izi                   evet        evet         evet
  dalın bütünlüğü             evet        evet         evet
  çakışma kararı             hayır       hayır        hayır

sayım sorgusu                 kâhin  ayrık HEAD  eksik  kurtarılmış
  rapor işine ait işleme          4           2      2            4
  kusuru getiren işleme           1           0      1            1
  ayar.py'ye dokunan işleme       2           1      1            2

üç sorgunun 3'ü eksik yanıtlanıyor; tarihçe eksik olduğunu bildirmiyor
kurtarma maliyeti: 2 işlemeyi geri bağlamak için 1 başvuru, 41 bayt
```

#### Sayıların Okunması

Üst tablo kaybın büyüklüğünü veriyor. Erişilen işleme **12**'den **10**'a, tarihçedeki kayıt
**15**'ten **13**'e iniyor; erişilemeyen **2**. Yanıtlanan soru **5**'ten **4**'e düşüyor.
On iki işlemelik kümede kayıp **2/12**, altı soruluk kümede **1/6** — ikisi de kümelerin
çözünürlüğünde ve savunulabilir.

Orta tablo hangi sorunun düştüğünü söylüyor ve yanıt tektir: **kusurun yalıtımı**. Kusuru
getiren işleme `rapor` işinin ikincisiydi ve ayrık durumda yapılanlardan biriydi. Tarihçe
artık kusurlu bir işleme görmüyor; soru "kusuru getiren işleme tek başına bulunabiliyor mu"
diye sorulduğunda yanıt hayır oluyor. Bu, kaybın **bildirilen** yüzüdür: tarihçe bir soruyu
yanıtlayamadığını söylüyor.

Öteki beş satırın hiçbiri değişmiyor ve dersin asıl sonucu buradan çıkıyor. **Özellik
gruplaması** üç durumda da evet; kalan on işlemenin hepsi bir dal kaydı taşıyor ve tarihçe
kendi içinde tutarlı. **Dosya izi** de üç durumda da evet; kalan her işleme bir dosya adı
taşıyor. Tarihçe eksik olduğunun farkında değildir, çünkü eksiklik ancak dışarıdan bir
ölçüyle görülür.

Alt tablo o dış ölçüyü koyuyor. Üç sayım sorgusunun **3**'ü de eksik yanıtlanıyor. `rapor`
işine ait işleme sayısı **4** yerine **2** çıkıyor — grup okunabiliyor ama yarısı eksik.
Kusuru getiren işleme **1** yerine **0** çıkıyor. Ortak dosyaya dokunan işleme **2** yerine
**1** çıkıyor — dosya izi sorusu evet diyor, verdiği iz yarım. Hiçbir satırda bir hata
iletisi yok; üç yanıt da makul görünüyor ve üçü de yanlış.

İki tür kaybın ayrımı budur. Bir soru düştüğünde tarihçe hayır der ve okuyan bunu bilir.
Bir işleme düştüğünde tarihçe evet demeyi sürdürür ve okuyan yanlış bir sayıyla kalır.
Yanıtlanan soru sayısındaki **1/6**'lık düşüş, gerçek kaybın yalnız görünen kısmıdır.

Son iki satır kurtarmayı ölçüyor. Düşen iki işleme bir başvuruya bağlandığında erişilen
işleme **12**'ye, tarihçe **15**'e, yanıtlanan soru **5**'e dönüyor ve üç sayım sorgusu
kâhinle birebir uyuşuyor. Kurtarmanın maliyeti **41 bayttır**. Birinci dersin ölçüsü tam
tersini de söylemişti: bir başvuruyu **yazmak** kırk bir bayttı, dolayısıyla o başvuruyu
yazmamak da kırk bir baytlık bir eksiklikti. Ayrık durumun tüm hikâyesi yazılmayan bu kırk
bir bayttır.

#### Özet

- Ayrık HEAD, `HEAD`in bir dal adı yerine doğrudan bir işleme kimliğini gösterdiği durumdur;
  `git symbolic-ref HEAD` başarısız olur ve durum bir kusur değil, bir konumlanma biçimidir.
- Ayrık durumda yapılan işleme normal biçimde yazılır ama hiçbir dal başvurusu ilerlemez;
  başka bir dala geçildiğinde o işlemelere hiçbir başvurudan erişilemez.
- Erişilemeyen **2** işleme tarihçeyi **15** kayıttan **13**'e indirir ve yanıtlanan soruyu
  **5**'ten **4**'e düşürür; düşen tek soru **kusurun yalıtımıdır**.
- Asıl bedel üç sayım sorgusunun **3**'ünün de eksik yanıtlanmasıdır: tarihçe evet demeyi
  sürdürür, verdiği sayılar **4** yerine **2**, **1** yerine **0**, **2** yerine **1**'dir.
- Referans günlüğü `HEAD`in geçmiş konumlarını tutar ve kurtarmayı **41 bayta** indirir;
  günlük girdisi düştükten sonra nesne toplama kaybı kalıcı kılar ve geri alınamaz.

#### Sonraki Adım

Bu konu dallanmanın açılış tarafını bitirdi. Dal açmak bir addır ve **123** bayt yazar;
adı düzenli koymak tarihçenin birinci sorusuna kaydın tek başına veremediğini ekler; adın
hiç yazılmaması ise iki işlemeyi tarihçenin dışına düşürür. Üç ölçüm de aynı yöne bakıyor:
bir işlemeler kümesini bir işe bağlayan şey başvurudur.

Geriye çatallanmanın öteki ucu kalıyor. Üç dal açıldı, on iki işleme yazıldı ve her biri
kendi ucunda duruyor; bir noktada bu uçların ana dala alınması gerekir. Sonraki konu
birleşmenin tarihçeye ne yazdığını sorar: araç iki ucu kapatırken yeni bir kayıt üretir mi,
üretmezse ne kaybolur ve kapatma biçimi altı sorunun kaçını yanıtlanabilir bırakır. Kurs
buradan itibaren bu ölçünün üstünde yürür.

## Birleştirme ve Yeniden Temellendirme

İleri sarma ile birleştirme işlemesinin farkı, çakışma işaretleri ve çözümün doğrulanması, tarihçeyi doğrusallaştırmanın bedeli, tek işlemeyi başka dala taşıma ve dört tümleştirme biçimi arasında ölçüte dayalı seçim.

### İleri Sarma ve Birleştirme İşlemesi

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/ileri-sarma-ve-birlestirme-islemesi

İki dalın birleşmesi tarihçeye ya hiçbir şey yazar ya da iki öncüllü bir işleme; on iki işlemelik gelişim birleştirme işlemesiyle on beşe çıkıp altı sorunun beşini, ileri sarmayla on ikide kalıp üçünü yanıtlar.

Dal açmak ucuzdu: bir başvuru dosyasına yazılan tek bir işleme kimliği. Ad koymak düzen
getirdi: dal adları okunduğunda kimin neyi geliştirdiği anlaşılır oldu. İki işlem de
tarihçeyi **büyütmedi** — yalnız ona işaretçiler ekledi.

Bu dersin sorusu bundan sonrasıdır. İki dal ayrıldıktan sonra bir noktada **birleşir
(merge)**; peki birleşme tarihçeye ne yazar? Yanıt tek değildir. Aracın iki ayrı davranışı
vardır ve hangisinin uygulandığı, aylar sonra tarihçeye sorulacak soruların kaçının
yanıtlanabileceğini belirler.

#### Birleşmenin İki Durumu

Bir dalı ana dala almak, iki işleme kimliğinin ortak atasını bulmak ve aradaki farkı
uygulamaktır. Aracın önünde iki ayrı durum vardır.

**Birinci durum:** dal ayrıldıktan sonra ana dala hiç işleme eklenmemiştir. O zaman dalın
işlemeleri zaten ana dalın son işlemesinin üzerine kuruludur ve birleştirilecek bir şey
yoktur. Aracın yapacağı tek iş, ana dalın işaretçisini dalın ucuna **kaydırmaktır**. Buna
**ileri sarma (fast-forward)** denir.

**İkinci durum:** iki taraf da ayrıldıktan sonra işleme almıştır. Tarihçe gerçekten
çatallanmıştır ve tek bir işaretçi kaydırmasıyla kapatılamaz. Araç iki tarafın içeriğini
birleştirir ve sonucu **iki öncüllü** yeni bir işlemeye yazar: **birleştirme işlemesi
(merge commit)**.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git merge olcum
Updating 3f1a2b0..9c4d7e1
Fast-forward
 olcum.py | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

$ git merge rapor
Merge made by the 'ort' strategy.
 rapor.py | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
```

İlk çağrı `Fast-forward` diyor: yeni bir işleme üretilmedi. İkincisi bir strateji adıyla
birleştirme yaptığını bildiriyor: yeni bir işleme üretildi. Aynı komut, iki ayrı sonuç.

#### Ortak Ata ve Üçlü Birleştirme

Aracın hangi durumda olduğunu anlaması için önce **ortak atayı** bulması gerekir: iki dalın
uçlarından geriye gidildiğinde ulaşılan ilk ortak işleme. Ortak ata, iki dalın ayrıldığı
noktadır. Birinci durumun tanımı ortak ata cinsinden yazılabilir: ortak ata ana dalın
**ucuna eşitse** ana dal ayrılmadan beri hiç ilerlememiştir ve ileri sarma uygulanabilir.

Ortak ata ana dalın ucuna eşit değilse birleştirme üç dosya sürümü üzerinden yapılır: ortak
atadaki sürüm, ana daldaki sürüm ve dallardaki sürüm. Buna **üçlü birleştirme (three-way
merge)** denir ve üçüncü sürüm, yani atadaki hâl, ölçüyü veren şeydir. İki sürümü yan yana
koymak yeterli olsaydı araç her farkı bir çatışma sayardı; ata sürümü sayesinde araç bir
satırın **kim tarafından** değiştirildiğini bilir. Yalnız bir taraf değiştirmişse o tarafın
sürümü alınır; iki taraf da aynı satırı ayrı biçimde değiştirmişse karar verilemez ve
birleştirme çakışması doğar.

Ortak ata bir arama sonucudur, tarihçede yazılı bir alan değil. Bu, birleştirmenin
maliyetinin dal ömrüyle birlikte arttığı anlamına gelir: ata ne kadar geride kalırsa iki
taraf o kadar çok satırda ayrışmıştır.

#### İleri Sarmanın Yazdığı

İleri sarma tarihçeye **hiçbir şey yazmaz**. Ana dalın başvurusu yeni bir kimliği
gösterir, o kadar. Dalın işlemeleri olduğu gibi ana dalın tarihçesinde belirir; ama
tarihçede onların bir dalda geliştirildiğini söyleyen hiçbir kayıt kalmaz. Dal işaretçisi
silindiğinde, o işlemelerin ayrı bir işe ait olduğu bilgisi de silinir.

Seçim aracın eline bırakılmak zorunda değildir. Varsayılan davranış uygulanabilir olduğunda
ileri sarar; `--no-ff` seçeneği ileri sarma mümkün olsa bile iki öncüllü bir işleme
üretmeye zorlar; `--ff-only` seçeneği bunun tersini yapar ve ileri sarma uygulanamıyorsa
birleştirmeyi **yapmadan durur**. Üçüncüsü ilk bakışta işe yaramaz görünür ama bir güvenlik
kilididir: tarihçesinin doğrusal kalmasını isteyen bir ekipte, farkında olmadan üretilmiş
bir birleştirme işlemesinin girmesini engeller. Üç ayarın hiçbiri öbüründen doğru değildir;
üçü de aynı sorunun üç ayrı yanıtıdır ve o soruyu bu dersin ölçümü sorar.

#### Birleştirme İşlemesinin Yazdığı

Birleştirme işlemesinin tek özel yanı **iki öncülünün** olmasıdır. Birinci öncül ana dalın
o anki ucu, ikinci öncül birleştirilen dalın ucudur. Bu ikinci kenar, tarihçenin dal
yapısını taşıyan tek şeydir.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git log --oneline --graph
*   a1b2c3d rapor dalını birleştir
|\
| * 7e8f9a0 rapor: özet satırını ekle
| * 4d5e6f1 rapor: alan genişliğini düzelt
|/
* 9c4d7e1 olcum: eşik değerini yazdır
```

Sol kenardaki çizim ikinci öncülden üretilir. Girintili iki satır `rapor` dalının
işlemeleridir; `|/` işaretinin bulunduğu satır ayrılma noktasını gösterir. Bu çizim ileri
sarmayla alınmış bir dal için **çizilemez**, çünkü çizilecek kenar yoktur.

Buradaki kimlikler ve dosya adları kurgudur. Aynı komutu kendi deponuzda çalıştırdığınızda
kimlikler farklı olur; sabit olan çıktının yapısıdır.

#### Ölçünün Kurulması

Kurs boyunca sürecek kurgu şudur: üç kişi aynı depoda üç ayrı işe başlıyor — `olcum`,
`rapor` ve `kimlik`. Her biri dört işleme yapıyor. Üçüncü adımda `olcum` ile `rapor` aynı
dosyaya, `ayar.py` dosyasına dokunuyor. `rapor` dalının ikinci işlemesi bir kusur
getiriyor. Gerçek gelişim **12 işlemedir** ve gerçek sırası bilinir, çünkü kurguyu biz
yazdık.

Bu on iki işleme ayrı **tümleştirme biçimleriyle (integration format)** ana dala alınır.
Biçimlerin hepsi aynı **kodu** üretir; ürettikleri **tarihçe** ayrıdır. Kurs bu farkı tek
bir ölçütle ölçer: tarihçeye altı soru sorulur ve kaçını yanıtlayabildiği sayılır.

1. **Özellik gruplaması** — hangi işlemeler hangi işe ait?
2. **Kusurun yalıtımı** — kusuru getiren işleme tek başına bulunabiliyor mu?
3. **Gerçek sıra** — işler hangi sırayla geliştirildi?
4. **Dosya izi** — bir dosyaya dokunan her işleme ayrı ayrı bulunabiliyor mu?
5. **Dalın bütünlüğü** — bir dalın işlemeleri bir arada mı duruyor?
6. **Çakışma kararı** — çakışma nerede çözüldü ve hangi taraf seçildi?

Bu altı soru kursun sabitidir; sonraki derslerde tanımları değişmez, yalnız yanıtları
değişir.

Ölçümün varsayımları:

- **BR1** — Gerçek gelişim kurgudan üretilir; sırası ve kusurlu işlemesi **bilinir**,
  çünkü kurguyu biz yazdık. Tarihçenin bunu bilmesi gerekmez.
- **BR2** — İleri sarma, dal ayrıldıktan sonra ana dala işleme eklenmemiş durumu temsil
  eder: işlemeler gerçek zaman sırasına dizilir ve **dal kaydı tutulmaz**.
- **BR3** — Birleştirme işlemesi her dal için bir kapanış işlemesi ekler; bu işleme dosyaya
  dokunmaz ve kusur taşımaz, yalnız **dal kaydını** taşır.
- **BR4** — Bir sorunun yanıtlanabilmesi, tarihçede o yanıtı veren kaydın **bulunması**
  demektir; kaydı okuyanın ne kadar uğraştığı ölçüme girmez.
- **BR5** — Altıncı soru, tarihçede çakışmanın seçilen tarafını taşıyan bir alan aranarak
  ölçülür. Bu ders çakışmayı çözmez; alan yoktur ve soru bu derste iki biçimde de
  yanıtsızdır.
- **BR6** — Ölçüm kodu üretir varsaymaz; iki biçimin ürettiği **kod aynıdır**, ölçülen
  yalnız tarihçedir.
- **BR7** — Kümenin çözünürlüğü on iki işlemede **1/12**, altı soruda **1/6**; bundan
  küçük bir fark bu kurguyla savunulamaz.

#### Ölçüm

```python
"""Iki tumlestirme bicimi: birlestirme islemesi ve ileri sarma.

Bolum 1 - gercek gelisim: uc dal, her dalda dort isleme.
Bolum 2 - iki bicimin biraktigi tarihceye alti soru sorulur.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def tumlestir(kayit, bicim):
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    elif bicim == "ileri sarma":
        for k in sorted(kayit, key=lambda x: x["zaman"]):
            t.append({**k, "dal_kaydi": None, "zaman_korundu": True})
    return t


def s1_gruplama(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2_kusur(t):
    kusurlu = [x for x in t if x["kusurlu"]]
    return len(kusurlu) == 1 and not kusurlu[0].get("sikisik")


def s3_sira(t):
    return all(x.get("zaman_korundu") for x in t)


def s4_dosya(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5_kapsam(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6_cakisma(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1_gruplama), ("kusurun yalıtımı", s2_kusur),
           ("gerçek sıra", s3_sira), ("dosya izi", s4_dosya),
           ("dalın bütünlüğü", s5_kapsam), ("çakışma kararı", s6_cakisma))
BICIMLER = ("birlestirme islemesi", "ileri sarma")
kayit = gelisim()
T = {b: tumlestir(kayit, b) for b in BICIMLER}

print(f"gerçek gelişim: {len(kayit)} işleme, {len(DALLAR)} dal, ortak dosyaya "
      f"dokunan {sum(1 for k in kayit if k['dosya'] == ORTAK_DOSYA)}, "
      f"kusurlu işleme {sum(1 for k in kayit if k['kusurlu'])}")
print()
print(f"{'biçim':<22s} {'işleme':>6s} {'gelişimden':>10s} {'eklenen':>7s}")
for b in BICIMLER:
    ek = sum(1 for x in T[b] if x.get("birlesme"))
    print(f"{b:<22s} {len(T[b]):6d} {len(T[b]) - ek:10d} {ek:7d}")
print()
print(f"{'soru':<20s} {'birlestirme islemesi':>20s} {'ileri sarma':>12s}")
for ad, soru in SORULAR:
    print(f"{ad:<20s} {('evet' if soru(T[BICIMLER[0]]) else 'hayır'):>20s}"
          f" {('evet' if soru(T[BICIMLER[1]]) else 'hayır'):>12s}")
print(f"{'yanıtlanan':<20s} {sum(s(T[BICIMLER[0]]) for _, s in SORULAR):20d}"
      f" {sum(s(T[BICIMLER[1]]) for _, s in SORULAR):12d}")
```

```
gerçek gelişim: 12 işleme, 3 dal, ortak dosyaya dokunan 2, kusurlu işleme 1

biçim                  işleme gelişimden eklenen
birlestirme islemesi       15         12       3
ileri sarma                12         12       0

soru                 birlestirme islemesi  ileri sarma
özellik gruplaması                   evet        hayır
kusurun yalıtımı                     evet         evet
gerçek sıra                          evet         evet
dosya izi                            evet         evet
dalın bütünlüğü                      evet        hayır
çakışma kararı                      hayır        hayır
yanıtlanan                              5            3
```

#### Üç İşlemenin Satın Aldığı

Üst tablo iki biçimi işleme sayısıyla karşılaştırıyor. Birleştirme işlemesi tarihçeyi
**15** işlemeye çıkarıyor: on ikisi gerçek gelişimden, **3**'ü aracın eklediği kapanış
işlemeleri. İleri sarma **12**'de kalıyor ve hiçbir şey eklemiyor.

Sezgi burada ileri sarmadan yana çalışır: aynı iş, daha az kayıt. Alt tablo bunun tersini
söylüyor. Birleştirme işlemesi altı sorunun **5**'ini, ileri sarma **3**'ünü yanıtlıyor.
Üç fazladan işleme, iki fazladan yanıt satın alıyor.

Satın alınan iki yanıt hangileri? **Özellik gruplaması** ve **dalın bütünlüğü**. İkisi de
aynı kayıptan doğuyor: ileri sarmada dal kaydı tutulmuyor. İşlemeler gerçek zaman sırasına
diziliyor, yani üç işin işlemeleri iç içe geçiyor — `olcum` dalının dört işlemesi ana
dalda ardışık durmuyor, aralarına öbür iki dalın işlemeleri giriyor. Tarihçe "bu işleme
hangi işe aitti" sorusuna yanıt veremiyor, çünkü işi adlandıran kayıt hiç yazılmamış.

Dördü ortak: **kusurun yalıtımı**, **gerçek sıra** ve **dosya izi** iki biçimde de evet.
Kusurlu işleme iki tarihçede de tek başına duruyor; hiçbir biçim onu bir başkasıyla
birleştirmedi. Zaman damgaları da korundu. Bu üç yanıt için fazladan bir kayda gerek yok.

**Çakışma kararı** iki biçimde de hayır. `olcum` ile `rapor` üçüncü adımda aynı dosyaya
dokunuyor ve bu bir **birleştirme çakışması (merge conflict)** doğuruyor; ama iki tarihçede
de çakışmanın nasıl çözüldüğünü söyleyen bir kayıt yok. Dikkat edilmesi gereken şudur:
birleştirme işlemesi çakışmanın **olduğu yeri** taşır — çözüm o işlemede yazılıdır — ama
hangi tarafın seçildiğini ayrı bir alan olarak tutmaz. Ölçüm bu ayrımı bir alan arayarak
yapıyor ve alan iki biçimde de yok. Bu sonuç dört biçimin dördü için de geçerlidir ve
sonraki ders bunu ödeyecektir.

#### Seçim Neye Bağlı

İki biçim arasındaki fark bir doğru–yanlış değil, bir takastır. Ölçülen kümede fark
**2/6**'dır: altı sorudan ikisi. Kümenin çözünürlüğü **1/6** olduğuna göre bu fark ölçüm
bandının iki katıdır ve savunulabilir.

Ödenen bedel **3/12**'dir: on iki işlemelik bir gelişime üç işleme eklenmiştir. Dal sayısı
arttıkça bu bedel doğrusal büyür, çünkü her dal bir kapanış işlemesi ekler. On iki dallı
bir tümleştirmede tarihçenin dörtte biri kapanış işlemesinden oluşur ve günlük çıktısı
gerçek işi göstermek yerine kapanışları sayar.

Ölçüt şudur: **dal kaydı sonradan sorulacak mı?** Tek işlemelik bir düzeltmede sorulmaz ve
ileri sarma kaybı sıfırdır. Dört işlemelik bir özellik dalında sorulur; oradaki üç
işlemelik bedel, iki sorunun yanıtı için ödenmiş olur.

Eklenen üç işlemenin ikinci bir getirisi de vardır. Birleştirme işlemesinin **birinci
öncülü** ana dalın kendi çizgisidir; günlük yalnız birinci öncülleri izleyecek biçimde
çağrıldığında tarihçe, ölçümdeki on iki işleme yerine üç kapanış işlemesine iner. Aynı
tarihçe iki ayrı çözünürlükte okunabilir hâle gelir: **ne yapıldı** sorusuna üç satır,
**nasıl yapıldı** sorusuna on iki satır. İleri sarmada bu ikinci çözünürlük yoktur, çünkü
ayıracak kenar yoktur — her okuma on iki satır verir.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git log --oneline --first-parent
a1b2c3d rapor dalını birleştir
b2c3d4e olcum dalını birleştir
9c4d7e1 ilk kurulum
```

Bu iki çözünürlüğün ikisi de dal kaydına dayanır; kayıt yoksa ikisi çakışır ve ayrım
kurulamaz.

#### Özet

- İleri sarma, ana dal ayrılma noktasından beri hiç ilerlememişse uygulanır ve tarihçeye
  hiçbir işleme yazmaz; yalnız dal işaretçisini kaydırır.
- Birleştirme işlemesi iki öncüllü bir işlemedir; ikinci öncül, tarihçenin dal yapısını
  taşıyan tek kayıttır ve günlük çizimi ondan üretilir.
- On iki işlemelik gelişim birleştirme işlemesiyle **15** işlemeye çıkar, ileri sarmayla
  **12**'de kalır; eklenen **3** işleme dal başına bir kapanıştır.
- Birleştirme işlemesi altı sorunun **5**'ini, ileri sarma **3**'ünü yanıtlar; farkı
  yaratan iki soru **özellik gruplaması** ve **dalın bütünlüğüdür**.
- Kusurun yalıtımı, gerçek sıra ve dosya izi iki biçimde de yanıtlanır; çakışma kararı
  ikisinde de yanıtsızdır.

#### Sonraki Adım

Ölçümde `olcum` ile `rapor` aynı dosyaya dokundu ve iki tarihçe de bunun nasıl çözüldüğünü
kaydetmedi. Sonraki ders o çözümün kendisine bakacak: araç iki tarafı birleştiremediğinde
dosyaya ne yazar, çakışma işaretleri nasıl okunur, çözümün doğru olduğu nasıl doğrulanır —
ve çözüm bittiğinde tarihçeye tam olarak neyin yazıldığı, neyin yazılmadığı.

### Çakışma Çözümü

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/cakisma-cozumu

Araç iki tarafı birleştiremediğinde dosyaya çakışma işaretleri yazar; çözüm elle verilir ama dört tümleştirme biçiminin dördü de seçilen tarafı kaydetmez, çözümün gerekçesi işleme mesajına yazılmazsa hiçbir yerde durmaz.

Önceki dersin ölçümünde `olcum` ile `rapor` dalları üçüncü adımda aynı dosyaya dokundu.
İki tümleştirme biçimi de bu dosyayı doğru sonuçla ana dala aldı, ama iki tarihçe de
çakışmanın **nasıl** çözüldüğünü kaydetmedi. Ölçümde bu, altıncı sorunun iki biçimde de
yanıtsız kalması demekti.

Bu ders o çözümün kendisine bakar. Araç iki tarafı birleştiremediğinde dosyaya ne yazar,
yazdığı işaretler nasıl okunur, verilen çözümün doğru olduğu nasıl doğrulanır — ve iş
bittiğinde tarihçeye tam olarak neyin yazıldığı, neyin yazılmadığı.

#### Aracın Karar Veremediği Yer

Üçlü birleştirme üç sürüm okur: ortak atadaki hâl, ana daldaki hâl, gelen daldaki hâl. Bir
satırı yalnız bir taraf değiştirmişse karar kendiliğinden verilir — değişen taraf alınır.
Çakışma, **iki tarafın da aynı satırı ata sürümünden ayrı biçimde değiştirdiği** yerde
doğar. Araç burada bir tercih üretmez; iki değişiklik de kasıtlıdır ve hangisinin doğru
olduğu ancak işin anlamı bilinerek söylenebilir.

Çakışmalar yalnız satır içeriğinden doğmaz. Bir taraf dosyayı silmişken öbür taraf onu
değiştirmişse, bir taraf yeniden adlandırmışken öbürü aynı yola yazmışsa, ya da iki taraf
aynı yolda ayrı içerikle yeni birer dosya oluşturmuşsa da karar verilemez. Bu durumların
ortak yanı şudur: ata sürümüyle karşılaştırma iki taraf için de bir değişiklik gösterir ve
ikisi bir arada uygulanamaz.

Çakışma bir hata değildir. Aracın "burada karar bende değil" demesidir; birleştirme
durdurulur, çalışma alanı yarım bırakılır ve karar insana devredilir.

#### Çakışma İşaretleri

Araç kararı devrederken dosyayı boş bırakmaz: iki tarafın da içeriğini dosyaya yazar ve
aralarına ayraç koyar. Bunlara **çakışma işareti (conflict marker)** denir.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git merge rapor
Auto-merging ayar.py
CONFLICT (content): Merge conflict in ayar.py
Automatic merge failed; fix conflicts and then commit the result.

$ cat ayar.py
<<<<<<< HEAD
ESIK = 40
||||||| ortak ata
ESIK = 25
=======
ESIK = 25
BIRIM = "mm"
>>>>>>> rapor
```

`<<<<<<<` ile `|||||||` arası **bulunduğunuz taraftır**: birleştirmeyi başlattığınız dal,
yani `HEAD`. `|||||||` ile `=======` arası **ortak atadaki** hâldir. `=======` ile
`>>>>>>>` arası **gelen taraftır**. Üç bölümlü bu gösterim varsayılan değildir; iki
bölümlü gösterimde ata sürümü yazılmaz ve yalnız iki taraf görünür. Ata bölümü ölçüyü
verir: yukarıdaki dökümde ata `ESIK = 25` diyor, yani eşik değerini **değiştiren taraf
`HEAD`**, satırı olduğu gibi bırakıp altına yeni bir satır ekleyen taraf `rapor`. Bu
bilgi olmadan iki taraf da eşit görünür ve seçim körleme yapılır.

İşaretler dosyanın kendisine yazılır, dosya çalışma alanında kalır ve o hâliyle
derlenemez. Aracın kendi kaydında ise aynı çakışma üç ayrı **aşama** olarak durur: ata,
bulunduğunuz taraf ve gelen taraf. Dosyayı elle düzenlemeden önce üç sürümü ayrı ayrı
görmek bu aşamalardan yapılır.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git status --short
UU ayar.py
M  olcum.py

$ git diff --base ayar.py
$ git diff --ours ayar.py
$ git diff --theirs ayar.py
```

`UU` iki tarafın da aynı yolu değiştirdiğini gösterir; silme ve yeniden adlandırma
çakışmaları başka harf çiftleriyle işaretlenir. Üç `diff` çağrısı çalışma alanındaki
dosyayı sırasıyla ata, bulunulan taraf ve gelen tarafla karşılaştırır.

#### Çözümün Verilmesi ve Doğrulanması

Çözmek, dosyayı istenen son hâline getirmek ve işaretleri silmektir. Sonuç iki taraftan
birinin aynısı olabileceği gibi ikisinden de farklı, elle yazılmış bir üçüncü içerik de
olabilir — kurgudaki gibi hem yeni eşiğin hem yeni birimin durduğu bir hâl. Dosya
hazırlandıktan sonra hazırlık alanına eklenmesi, aracın gözünde "bu yol çözüldü"
demektir; bütün yollar çözüldüğünde birleştirme işlemesi yazılır.

Araç bir kısayol da sunar: çakışan yolu tamamen bir tarafın sürümüyle doldurmak. Bu
kısayol makine tarafından üretilen dosyalarda yerindedir, çünkü orada iki taraftan biri
yeniden üretilebilir. Elle yazılmış bir dosyada ise kısayol, öbür tarafın o dosyadaki
bütün değişikliklerini sessizce atar; atılan değişiklik dosyada görünmez olur ve
sınamalar o tarafı kapsamıyorsa hiçbir yerde belirti vermez.

Doğrulamanın üç ayrı katmanı vardır ve üçü de gereklidir.

**Birincisi mekanik:** dosyada işaret kalmadığından emin olmak. Kalan bir `<<<<<<<`
satırı çoğu dilde sözdizimi hatası verir, ama biçimlendirilmiş metin ya da veri
dosyalarında sessizce durabilir. Ağaçta işaret dizisi araması bunu yakalar.

**İkincisi bütünlük:** çözülmüş dosyanın iki tarafın da amacını taşıdığını görmek.
Çakışmanın dar bakılan tehlikesi budur — birleştirmeyi yapan kişi genellikle iki taraftan
yalnız birini yazmıştır ve öbürünün ne yapmak istediğini bilmez. Bu yüzden seçim, iki
tarafın kodunu birleştirmekten çok iki niyeti uzlaştırmaktır.

**Üçüncüsü davranışsal:** sınamaların çalıştırılması. Çözülmüş dosya derlenebilir olabilir
ve yine de yanlış olabilir: iki taraf ayrı ayrı doğruyken birleşiği yanlış olan bir hâl
gayet olağandır. Çakışma çözümü, sınamaların en gerekli olduğu yerdir.

Çözümden geri dönmenin güvenli yolu birleştirmeyi **iptal etmektir**; araç bunun için ayrı
bir seçenek taşır ve çalışma alanını birleştirme öncesindeki hâline döndürür. Yarım kalmış
bir birleştirmeyi elle temizlemeye çalışmak yerine iptal edip baştan başlamak her zaman
ucuzdur.

Aracın çözümü hatırlayan bir yeteneği de vardır: aynı çakışma daha sonra yeniden
karşınıza çıktığında önceki çözümü kendiliğinden uygular. Bu yetenek, uzun ömürlü bir
dalın aynı çakışmayı defalarca doğurduğu durumda emek kazandırır. Ölçüm açısından önemli
olan yanı şudur: kaydettiği çözüm **deponun yerel bir dizininde** durur, tarihçeye
yazılmaz ve kimseyle paylaşılmaz. Çözüm bilgisinin tarihçenin dışında yaşadığı ilk yer
budur.

Ölçümün varsayımları:

- **BR8** — Çakışma, üçüncü adımda `olcum` ile `rapor` dallarının aynı dosyaya
  dokunmasından doğar. Kurgu bunu bilir; tarihçenin bilmesi gerekmez.
- **BR9** — Ölçüm dört tümleştirme biçimini de kurar. Biçimlerin tanımı ortak kurgudaki
  tanımdır ve bu derste değiştirilmez.
- **BR10** — Altıncı soru, tarihçede seçilen tarafı taşıyan bir alan aranarak ölçülür.
  Alanın adı değil, **varlığı** ölçülür.
- **BR11** — Çözüm kaydı üç alandan oluşur: seçilen taraf, gerekçe ve kararı veren rol.
  Üçü de aracın kendiliğinden yazdığı bir alan değildir.
- **BR12** — Sıkıştırmada bir işlemenin dosya alanı tek ad değil, ad listesidir: ortak
  dosyaya dokunulduğu okunur, dokunan işleme ayrı ayrı okunamaz.
- **BR13** — Gerekçenin işleme mesajına yazılması ölçümde **tek bir değişikliktir**:
  birleştirme işlemesine üç alan eklenir, başka hiçbir şey değişmez.
- **BR14** — Kümenin çözünürlüğü on iki işlemede **1/12**, altı soruda **1/6**.

#### Ölçüm

```python
"""Cakismanin cozumu tarihceye ne yaziyor.

Bolum 1 - cakismayi doguran iki isleme dort bicimde nerede duruyor.
Bolum 2 - altinci soru dort bicimde de aranir.
Bolum 3 - tek degisiklik: cozumun gerekcesi isleme mesajina yazilir.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
COZUM_ALANLARI = ("secilen", "gerekce", "karar_veren")


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def tumlestir(kayit, bicim):
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    elif bicim == "yeniden temellendirme":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": None, "zaman_korundu": False})
    elif bicim == "sikistirma":
        for dal in DALLAR:
            grup = [x for x in kayit if x["dal"] == dal]
            t.append({"dal": dal, "sira": 0,
                      "dosya": sorted({x["dosya"] for x in grup}),
                      "kusurlu": any(x["kusurlu"] for x in grup),
                      "zaman": max(x["zaman"] for x in grup), "dal_kaydi": dal,
                      "zaman_korundu": False, "sikisik": len(grup)})
    elif bicim == "ileri sarma":
        for k in sorted(kayit, key=lambda x: x["zaman"]):
            t.append({**k, "dal_kaydi": None, "zaman_korundu": True})
    return t


def s4_dosya(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s6_cakisma(t):
    return any("secilen" in x for x in t)


def dokunan(t, ad):
    """Tarihceden ad dosyasina dokundugu okunabilen isleme sayisi."""
    return sum(1 for x in t if x["dosya"] == ad
               or (isinstance(x["dosya"], list) and ad in x["dosya"]))


def ayri_okunabilir(t, ad):
    """Yalniz o dosyaya dokunan tek bir isleme olarak okunabilenler."""
    return sum(1 for x in t if x["dosya"] == ad)


def mesaja_yaz(t):
    """Tek degisiklik: cozumun gerekcesi bir isleme mesajina yazilir."""
    y = [dict(x) for x in t]
    for x in y:
        if x.get("birlesme") and x["dal"] == "rapor":
            x.update({"secilen": "olcum tarafi", "gerekce": "esik alani",
                      "karar_veren": "birlestiren"})
    return y


BICIMLER = ("birlestirme islemesi", "yeniden temellendirme", "sikistirma",
            "ileri sarma")
kayit = gelisim()
T = {b: tumlestir(kayit, b) for b in BICIMLER}
catisan = [(k["dal"], k["sira"]) for k in kayit if k["dosya"] == ORTAK_DOSYA]

print(f"gerçek gelişim: {len(kayit)} işleme | {ORTAK_DOSYA} dosyasına dokunan "
      f"{len(catisan)} işleme: {catisan}")
print()
print(f"{'biçim':<24s} {'işleme':>6s} {'dokunan':>7s} {'ayrı':>5s}"
      f" {'dosya izi':>9s} {'çakışma kararı':>14s}")
for b in BICIMLER:
    print(f"{b:<24s} {len(T[b]):6d} {dokunan(T[b], ORTAK_DOSYA):7d}"
          f" {ayri_okunabilir(T[b], ORTAK_DOSYA):5d}"
          f" {('evet' if s4_dosya(T[b]) else 'hayır'):>9s}"
          f" {('evet' if s6_cakisma(T[b]) else 'hayır'):>14s}")
print(f"altıncı soruyu yanıtlayan biçim: "
      f"{sum(s6_cakisma(T[b]) for b in BICIMLER)}/{len(BICIMLER)}")
print()
M = mesaja_yaz(T["birlestirme islemesi"])
print(f"{'tarihçe':<34s} {'çözüm alanı taşıyan':>19s} {'çakışma kararı':>14s}")
for ad, t in (("araç ne yazdıysa", T["birlestirme islemesi"]),
              ("gerekçe işleme mesajına yazılınca", M)):
    tasiyan = sum(1 for x in t if any(a in x for a in COZUM_ALANLARI))
    print(f"{ad:<34s} {f'{tasiyan}/{len(t)}':>19s}"
          f" {('evet' if s6_cakisma(t) else 'hayır'):>14s}")
```

```
gerçek gelişim: 12 işleme | ayar.py dosyasına dokunan 2 işleme: [('olcum', 3), ('rapor', 3)]

biçim                    işleme dokunan  ayrı dosya izi çakışma kararı
birlestirme islemesi         15       2     2      evet          hayır
yeniden temellendirme        12       2     2      evet          hayır
sikistirma                    3       2     0     hayır          hayır
ileri sarma                  12       2     2      evet          hayır
altıncı soruyu yanıtlayan biçim: 0/4

tarihçe                            çözüm alanı taşıyan çakışma kararı
araç ne yazdıysa                                  0/15          hayır
gerekçe işleme mesajına yazılınca                 1/15           evet
```

#### Dördü de Yanıtlayamaz

Üst tablonun son sütunu bu kursun üçüncü iddiasını ödüyor: **çakışma kararı dört biçimde
de hayır.** Yanıtlayan biçim sayısı **0/4**. Bu, biçimlerden birinin öbürlerinden kötü
olduğu anlamına gelmez; ölçülen dört biçimin hiçbirinde böyle bir alanın **bulunmadığı**
anlamına gelir.

Tarihçenin ne kaydettiğine bakıldığında ayrım netleşir. Ortak dosyaya dokunan iki işleme
üç biçimde de **2** olarak okunuyor: çakışmanın hangi dosyada doğduğu ve hangi iki işin
oraya dokunduğu bulunabiliyor. Sıkıştırmada dosya adı ayrı bir işlemede değil, bir işleme
grubunun dosya listesi içinde duruyor; dokunulduğu görülüyor ama ayrı okunabilen işleme
sayısı **0** ve dosya izi sorusu bu biçimde **hayır** veriyor.

Yani tarihçe çakışmanın **olduğunu** kaydediyor — çakışmanın doğduğu yer üç biçimde
bulunabiliyor. Kaydetmediği şey çözümün kendisidir: hangi tarafın seçildiği, hangi
satırların elle yazıldığı, kararı kimin verdiği ve neden verdiği. Bu dört bilginin
hiçbiri araç tarafından yazılan bir alanda durmuyor.

Bunun bir araç eksikliği olmadığını görmek için kaydın ne olduğunu düşünmek yeterli. Bir
işleme, ağacın bir hâlini ve öncüllerini taşır. Birleştirme işlemesinin taşıdığı ağaç,
çözülmüş dosyayı zaten içerir — sonuç kayıtlıdır. Kayıtlı olmayan şey **süreçtir**: iki
adaydan hangisinin neden seçildiği, sonucun kendisinden okunamaz. Araç yalnız sonucu
saklar, çünkü sakladığı şey ağaçtır.

#### Gerekçenin Tek Yeri

Alt tablo bir sonraki adımı ölçüyor. Ölçümde değiştirilen tek şey şu: çözümün gerekçesi
birleştirme işlemesinin mesajına yazılıyor. Üç alan — seçilen taraf, gerekçe, kararı veren
rol — o tek işlemeye ekleniyor.

Sonuç, on beş işlemelik tarihçenin **1**'inde çözüm kaydı bulunması ve altıncı sorunun
**evet** vermesidir. Oran **1/15**: kümenin çözünürlüğünün üzerinde, ama tarihçenin
tamamına göre küçük bir yer. Bu küçüklük tesadüf değildir; çözüm kaydı ancak çakışmanın
kapatıldığı işlemede anlamlıdır.

Buradan kursun en pratik sonucu çıkar: **çakışma çözümünün gerekçesi işleme mesajına
yazılmazsa hiçbir yerde yoktur.** Araç onu yazmaz, dosya onu taşımaz, sınamalar onu
anlatmaz. Çözümü hatırlayan yerel yetenek bile onu yalnız o depoda tutar ve kimseyle
paylaşmaz. Aylar sonra "bu satır neden böyle" diye soran kişinin bakabileceği tek yer
mesajdır ve mesaj boşsa yanıt kimsede değildir.

Bunun somut karşılığı şudur: birleştirme işlemesinin varsayılan mesajı yalnız hangi dalın
birleştirildiğini yazar ve boş bırakılırsa öyle kalır. Çakışmalı bir birleştirmede o
mesajın üç şeyi taşıması gerekir — hangi dosyada çakışıldığı, hangi tarafın hangi
gerekçeyle seçildiği ve elle yazılmış bir içerik varsa onun ne olduğu. Üçü de üç satırdır
ve üçü de yazılmadığında geri getirilemez.

#### Özet

- Çakışma, iki tarafın da aynı satırı ata sürümünden ayrı biçimde değiştirdiği yerde
  doğar; araç tercih üretmez, kararı devreder ve birleştirmeyi yarıda bırakır.
- Çakışma işaretleri bulunulan tarafı, ata sürümünü ve gelen tarafı dosyaya yazar; ata
  bölümü hangi tarafın gerçekten değişiklik yaptığını gösterir ve seçimi körlemeden
  çıkarır.
- Çözüm üç katmanda doğrulanır: işaret kalmadığı, iki tarafın amacının korunduğu ve
  sınamaların geçtiği. Geri dönmenin güvenli yolu birleştirmeyi iptal etmektir.
- Ortak dosyaya dokunan **2** işleme üç biçimde ayrı ayrı okunur, sıkıştırmada **0**;
  ama çakışma kararı sorusunu dört biçimin dördü de yanıtlayamaz — **0/4**.
- Gerekçe işleme mesajına yazıldığında on beş işlemenin **1**'i çözüm kaydını taşır ve
  altıncı soru yanıtlanır; yazılmadığında çözümün gerekçesi hiçbir yerde durmaz.

#### Sonraki Adım

Bu derste çakışma tek bir yerde, birleştirme işlemesinin yazıldığı anda çözüldü. Çözüm bir
kez verildi ve bir kez kaydedildi. Tarihçeyi çatallanmasız, tek çizgi hâlinde tutmak
isteyen bir biçim ise dalın işlemelerini ana dalın ucuna **yeniden yazar** — ve o zaman
aynı çakışma her işleme için ayrı ayrı sorulabilir. Sonraki ders bu biçime bakar: on iki
işleme on iki olarak kalır ama hangi üç soru yanıtlanır, hangi bilgi silinir ve
paylaşılan bir dalda uygulandığında neden geri alınamaz.

### Yeniden Temellendirme

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/yeniden-temellendirme

Dalın işlemelerini yeni bir taban üzerine yeniden yazan biçim on iki işlemeyi on iki olarak bırakır ve altı sorunun üçünü yanıtlar; ileri sarmayla toplamı eşittir ama kaybı değildir — biri dalın bütünlüğünü, öbürü gerçek sırayı korur.

Önceki derste çakışma tek bir yerde çözüldü: iki dalın ucu bir kez karşılaştırıldı, karar
bir kez verildi ve birleştirme işlemesi yazıldı. O biçim tarihçeye bir çatal kenarı
ekliyordu.

Tarihçeyi çatalsız, tek çizgi hâlinde tutmak isteyen bir biçim ise başka bir yol izler:
dalın işlemelerini ana dalın ucuna **yeniden yazar**. Sonuç kod olarak aynıdır, tarihçe
olarak değildir. Bu dersin sorusu, bu yeniden yazmanın neyi koruduğu ve neyi sildiğidir —
ve toplamda önceki dersteki ileri sarmayla aynı sayıyı verirken neden aynı şeyi vermediği.

#### Yeniden Yazma

**Yeniden temellendirme (rebase)** üç adımda çalışır. Önce iki dalın ortak atası bulunur.
Sonra dalın ortak atadan sonraki işlemeleri **birer yamaya** çevrilir: her işlemenin
öncülüne göre yaptığı değişiklik ayrı ayrı çıkarılır. Son olarak bu yamalar, ana dalın
yeni ucundan başlanarak sırayla uygulanır.

Bu son adımın sonucu, dersin bütün ölçüsünü belirler: uygulanan her yama **yeni bir
işleme** üretir. Yeni işlemenin öncülü başkadır, içeriği aynı olsa bile kimliği başkadır.
Eski işlemeler silinmez; hiçbir dal artık onları göstermediği için erişilemez hâle
gelirler. Dalın ucu yeni zincirin ucunu gösterir.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git rebase ana
Successfully rebased and updated refs/heads/rapor.

$ git log --oneline --graph
* 5b6c7d8 rapor: özet satırını ekle
* 4a5b6c7 rapor: alan genişliğini düzelt
* 9c4d7e1 olcum: eşik değerini yazdır
```

Çizimde çatal kenarı yoktur, çünkü ikinci öncül üretilmemiştir. Günlük tek sütunda akar.
Aynı dalın birleştirme işlemesiyle alınmış hâlinde girintili satırlar ve `|/` işareti
görünüyordu; burada görünmüyor.

Aracın bu ailedeki ikinci yeteneği, yamaların hangi tabana taşınacağını ayrıca belirtmeye
izin verir: dalın kendi ortak atası yerine başka bir işleme taban seçilebilir. Aynı aile
etkileşimli bir kipe de sahiptir; orada yamalar uygulanmadan önce sırası değiştirilebilir,
mesajı düzeltilebilir ya da bir kısmı atılabilir. Bu kipin bir kullanımı olan sıkıştırma
ayrı bir tümleştirme biçimidir ve ilerideki bir derste ayrıca ölçülür.

#### Çakışma Her Adımda Sorulabilir

Önceki dersteki çakışma çözümü burada bir kez değil, **yama başına** sorulabilir. Araç
yamaları sırayla uygular ve uygulanamayan ilk yamada durur. Çalışma alanı yine çakışma
işaretleriyle bırakılır, çözüm yine elle verilir; ama iş bittiğinde birleştirme değil,
**kaldığı yerden devam** çağrılır.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git rebase ana
Auto-merging ayar.py
CONFLICT (content): Merge conflict in ayar.py
error: could not apply 4d5e6f1... rapor: alan genisligini duzelt
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
```

Üç seçenek vardır: çözüp devam etmek, o yamayı atlamak, ya da bütün işlemi iptal edip
dalı başlangıç hâline döndürmek. İptal seçeneği burada da güvenli çıkıştır ve yarım kalmış
bir yeniden temellendirmeyi elle toparlamaya çalışmaktan her zaman ucuzdur.

Dört işlemelik bir dalda aynı dosyaya dokunan iki işleme varsa aynı çakışma iki kez
sorulabilir. Çözümü hatırlayan yerel yetenek bu yükü azaltır — önceki dersten hatırlanacağı
gibi kaydı tarihçede değil, deponun yerel bir dizininde tutar. Bu, yeniden temellendirmenin
gizli maliyetidir: birleştirme işlemesi çakışmayı bir kez sordurur, yeniden temellendirme
yama sayısı kadar sordurabilir.

#### Ne Korunur, Ne Silinir

Yamalar dal dal uygulandığı için bir dalın işlemeleri tarihçede **art arda** durur. Bu,
dalın bütünlüğünün korunması demektir: `rapor` dalının dört işlemesi çizgide bitişiktir,
aralarına başka bir işin işlemesi girmez.

Korunmayan iki şey vardır. Birincisi **dal kaydıdır**: bitişik duran dört işlemenin bir
dala ait olduğunu söyleyen hiçbir alan yazılmaz. Bitişiklik bir düzendir, bir etiket
değildir; okuyan kişi işleme mesajlarındaki ortak öneke bakarak tahmin edebilir, ama
tarihçenin kendisi bunu bildirmez. İkincisi **üç dalın birbirine göre sırasıdır**: yeniden
yazılan zincirde önce bir dalın hepsi, sonra öbürünün hepsi gelir. İşlerin gerçekte iç içe
geliştirildiği, hangi haftada hangi ikisinin birlikte yürüdüğü çizgiden okunamaz.

Araç yazar damgasını yeni işlemeye taşır; silinen zaman değil, **sıradır**. Tarihçe çizgi
olarak okunduğunda verdiği sıra, geliştirme sırası değil yeniden yazma sırasıdır.

Ölçümün varsayımları:

- **BR15** — Yeniden temellendirme, dalın işlemelerini yeni bir taban üzerine dal dal ve
  sırayla yeniden yazar; **dal kaydı tutulmaz** ve gerçek sıra **korunmaz**.
- **BR16** — Yeniden yazılan işleme yeni bir kimlik alır. Ölçüm kimliği saymaz; tarihçenin
  yanıtlayabildiği soruyu sayar.
- **BR17** — İleri sarma karşılaştırma tarafıdır ve önceki derslerde kurulduğu biçimde
  kurulur: işlemeler gerçek zaman sırasına dizilir, dal kaydı tutulmaz.
- **BR18** — Dalın bütünlüğü sorusu, bir dalın işlemelerinin tarihçede **kesintisiz** durup
  durmadığına bakar. İşlemeleri bir arada tutmak onları adlandırmak değildir; bu iki soru
  ayrıdır.
- **BR19** — Gerçek sıra sorusu, üç işin birbirine göre geliştirme sırasının çizgiden
  okunup okunamadığına bakar.
- **BR20** — İki biçim de aynı kodu üretir ve tarihçeye hiçbir işleme **eklemez**; ölçülen
  yalnız tarihçedir.
- **BR21** — Kümenin çözünürlüğü altı soruda **1/6**. Bir sorunun yer değiştirmesi ölçüm
  bandının tam üzerindedir ve savunulabilir en küçük farktır.

#### Ölçüm

```python
"""Yeniden temellendirme: on iki isleme on iki kalir, uc soru yanitlanir.

Bolum 1 - iki dogrusal bicim yan yana: isleme sayisi ve yanitlanan soru.
Bolum 2 - hangi uc soru: ortak kume ve ayrisan iki soru.
Bolum 3 - dallarin islemeleri tarihcede nerede duruyor.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def tumlestir(kayit, bicim):
    t = []
    if bicim == "yeniden temellendirme":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": None, "zaman_korundu": False})
    elif bicim == "ileri sarma":
        for k in sorted(kayit, key=lambda x: x["zaman"]):
            t.append({**k, "dal_kaydi": None, "zaman_korundu": True})
    return t


def s1_gruplama(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2_kusur(t):
    kusurlu = [x for x in t if x["kusurlu"]]
    return len(kusurlu) == 1 and not kusurlu[0].get("sikisik")


def s3_sira(t):
    return all(x.get("zaman_korundu") for x in t)


def s4_dosya(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5_kapsam(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6_cakisma(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1_gruplama), ("kusurun yalıtımı", s2_kusur),
           ("gerçek sıra", s3_sira), ("dosya izi", s4_dosya),
           ("dalın bütünlüğü", s5_kapsam), ("çakışma kararı", s6_cakisma))
BICIMLER = ("yeniden temellendirme", "ileri sarma")
kayit = gelisim()
T = {b: tumlestir(kayit, b) for b in BICIMLER}
YT, IS = T[BICIMLER[0]], T[BICIMLER[1]]

print(f"gerçek gelişim: {len(kayit)} işleme, {len(DALLAR)} dal")
print()
print(f"{'biçim':<22s} {'işleme':>6s} {'eklenen':>7s} {'yanıtlanan':>10s}")
for b in BICIMLER:
    print(f"{b:<22s} {len(T[b]):6d} {len(T[b]) - len(kayit):7d}"
          f" {sum(s(T[b]) for _, s in SORULAR):10d}")
print()
print(f"{'soru':<20s} {'yeniden temellendirme':>21s} {'ileri sarma':>12s}")
for ad, soru in SORULAR:
    print(f"{ad:<20s} {('evet' if soru(YT) else 'hayır'):>21s}"
          f" {('evet' if soru(IS) else 'hayır'):>12s}")
print()
print("ikisinde de evet:  ", [a for a, s in SORULAR if s(YT) and s(IS)])
print("yalnız yeniden temellendirmede:",
      [a for a, s in SORULAR if s(YT) and not s(IS)])
print("yalnız ileri sarmada:", [a for a, s in SORULAR if s(IS) and not s(YT)])
print("ikisinde de hayır: ", [a for a, s in SORULAR if not s(YT) and not s(IS)])
print()
print(f"{'biçim':<22s} {'dal':<8s} {'tarihçedeki yerleri':>21s} {'ardışık':>7s}")
for b in BICIMLER:
    for dal in DALLAR:
        yer = [i for i, x in enumerate(T[b]) if x["dal"] == dal]
        ard = yer[-1] - yer[0] == len(yer) - 1
        print(f"{b:<22s} {dal:<8s} {str(yer):>21s}"
              f" {('evet' if ard else 'hayır'):>7s}")
```

```
gerçek gelişim: 12 işleme, 3 dal

biçim                  işleme eklenen yanıtlanan
yeniden temellendirme      12       0          3
ileri sarma                12       0          3

soru                 yeniden temellendirme  ileri sarma
özellik gruplaması                   hayır        hayır
kusurun yalıtımı                      evet         evet
gerçek sıra                          hayır         evet
dosya izi                             evet         evet
dalın bütünlüğü                       evet        hayır
çakışma kararı                       hayır        hayır

ikisinde de evet:   ['kusurun yalıtımı', 'dosya izi']
yalnız yeniden temellendirmede: ['dalın bütünlüğü']
yalnız ileri sarmada: ['gerçek sıra']
ikisinde de hayır:  ['özellik gruplaması', 'çakışma kararı']

biçim                  dal        tarihçedeki yerleri ardışık
yeniden temellendirme  olcum             [0, 1, 2, 3]    evet
yeniden temellendirme  rapor             [4, 5, 6, 7]    evet
yeniden temellendirme  kimlik          [8, 9, 10, 11]    evet
ileri sarma            olcum             [0, 3, 6, 9]   hayır
ileri sarma            rapor            [1, 4, 7, 10]   hayır
ileri sarma            kimlik           [2, 5, 8, 11]   hayır
```

#### Eşit Toplam, Farklı Kayıp

Üst tablo iki biçimi işleme sayısıyla karşılaştırıyor ve fark bulamıyor: ikisi de **12**
işleme, ikisi de **0** eklenen, ikisi de **3** yanıtlanan soru. Bu satıra bakarak iki
biçimin eşdeğer olduğu söylenebilirdi.

Orta tablo bunu bozuyor. Yanıtlanan üçlüler aynı üçlü değil. İkisinde de **kusurun
yalıtımı** ve **dosya izi** evet: hiçbir biçim işlemeleri birleştirmediği için kusurlu
işleme tek başına duruyor ve her işleme tek bir dosyaya dokunuyor. Ayrışan tek soru çifti
şu: **dalın bütünlüğü** yalnız yeniden temellendirmede, **gerçek sıra** yalnız ileri
sarmada yanıtlanıyor. İkisinde de yanıtsız kalanlar **özellik gruplaması** ve **çakışma
kararı**.

Alt tablo bu ayrışmanın nedenini doğrudan gösteriyor. Yeniden temellendirmede üç dalın
işlemeleri `[0, 1, 2, 3]`, `[4, 5, 6, 7]` ve `[8, 9, 10, 11]` konumlarında, üçü de
ardışık. İleri sarmada aynı işlemeler `[0, 3, 6, 9]`, `[1, 4, 7, 10]` ve `[2, 5, 8, 11]`
konumlarında, hiçbiri ardışık değil. Yeniden temellendirme işlemeleri **işe göre**
sıraladı ve zamanı bozdu; ileri sarma **zamana göre** sıraladı ve işleri dağıttı. Bir
çizgi ikisini birden yapamaz, çünkü tek bir sıralaması vardır.

Buradan çıkan okuma şudur: **toplam eşit olduğunda bile kayıp aynı değildir.** Üç sorunun
hangi üçü olduğu, tarihçeden ne sorulacağına göre değer değiştirir. Bir kusur avında
"hangi iş bunu getirdi" sorulur ve yeniden temellendirilmiş çizgide bitişik blok işe
yarar. Bir olay incelemesinde "o hafta hangi işler birlikte yürüyordu" sorulur ve o
soruya yalnız ileri sarılmış çizgi yanıt verir. Aynı kümedeki **3** sayısı iki durumda
farklı şey satın alır.

İkisinde de yanıtsız kalan iki soru da kayda değer. **Özellik gruplaması** iki biçimde de
hayır, çünkü ikisi de dal kaydını yazmaz; yeniden temellendirmenin ürettiği bitişik blok
bunu düzeltmez, yalnız tahmin edilebilir kılar. **Çakışma kararı** iki biçimde de hayır ve
bu, önceki dersin sonucudur: doğrusallaştırma çözümü hiç kaydetmediği gibi, yama başına
sorulan çakışmalarda verilen kararların sayısını da artırır.

Ölçülen fark **1/6**'dır: altı sorudan biri yer değiştirir. Kümenin çözünürlüğü de
**1/6** olduğuna göre bu, bu kurguyla savunulabilecek **en küçük** farktır. Ölçüm bundan
daha ince bir ayrım için yeterli değildir ve daha ince bir ayrım iddia edilmez.

#### Paylaşılan Dalda Yeniden Temellendirme

Yeniden temellendirmenin ölçülmeyen bir sonucu vardır ve bu dersin en önemli uyarısıdır.
Yeniden yazılan işlemelerin kimliği değişir. Dal yalnız sizin deponuzdaysa bu bir sorun
değildir. Dal başkalarıyla paylaşılmışsa, sizin yeni zincirinizle onların ellerindeki eski
zincir **ortak ata dışında hiçbir işlemeyi paylaşmaz**.

Bu durumda dalı karşı tarafa olağan yolla yazmak reddedilir, çünkü karşı tarafın ucu sizin
zincirinizin içinde değildir. Reddi aşmanın yolu, karşı taraftaki dalı sizin zincirinizle
**ezmektir**. Bu işlem burada tam biçimiyle yazılmaz: uygulandığında karşı taraftaki eski
zincir başvurusuz kalır, o dalı temel alarak çalışan herkesin yerel tarihçesi ayrışır ve
**işlem geri alınamaz**. Kaybolan yalnız kimlikler değildir; başkalarının o eski işlemelerin
üzerine yaptığı iş de dayanaksız kalır.

Güvenli yol üç maddedir. **Birincisi ölçüttür:** yeniden temellendirme yalnız henüz
paylaşılmamış dallarda uygulanır. Paylaşılmış bir dalın ana dalla uyumu gerektiğinde
birleştirme kullanılır; birleştirme hiçbir işlemeyi yeniden yazmaz. **İkincisi yedektir:**
işlemden önce dalın o anki ucunu gösteren bir yedek dal açmak tek satırlıktır ve zinciri
kurtarmayı basit bir işaretçi geri alma hâline getirir. **Üçüncüsü referans günlüğüdür:**
araç dal ucunun her hareketini yerel bir günlüğe yazar ve eski uç oradan okunabilir.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git branch yedek/rapor rapor
$ git reflog rapor
5b6c7d8 rapor@{0}: rebase (finish): refs/heads/rapor onto 9c4d7e1
4d5e6f1 rapor@{1}: commit: rapor: alan genisligini duzelt
```

Referans günlüğünün iki sınırı vardır ve ikisi de bilinmelidir: günlük **yereldir**,
karşı tarafa gitmez; ve süresizce saklanmaz, eski kayıtlar zamanla temizlenir. Yedek dal
bu iki sınırın ikisini de taşımaz. Bu yüzden yedek dal bir alışkanlık, referans günlüğü
bir son çaredir.

#### Özet

- Yeniden temellendirme dalın işlemelerini yamaya çevirip yeni bir taban üzerine sırayla
  yeniden yazar; her yama **yeni kimlikli** bir işleme üretir, eski işlemeler erişilemez
  kalır.
- Çakışma bir kez değil **yama başına** sorulabilir; çözüp devam etmek, atlamak ve iptal
  etmek üç seçenektir, iptal güvenli çıkıştır.
- On iki işleme **12** olarak kalır ve altı sorunun **3**'ü yanıtlanır; yanıtlananlar
  kusurun yalıtımı, dosya izi ve **dalın bütünlüğüdür**.
- İleri sarma da **3** yanıtlar ama üçüncüsü **gerçek sıradır**: biri işe göre sıralayıp
  zamanı, öbürü zamana göre sıralayıp işi dağıtır. Fark **1/6**'dır ve toplam eşitken
  kayıp ayrıdır.
- Paylaşılan bir dalda yeniden temellendirme karşı tarafın zincirini geçersiz kılar ve
  geri alınamaz; ölçüt paylaşılmamış dal, güvence yedek dal ve referans günlüğüdür.

#### Sonraki Adım

Yeniden temellendirme bir dalın **bütün** işlemelerini taşıdı. Bazen gereken bundan çok
daha azdır: yayımlanmış bir sürümde duran tek bir düzeltmenin geliştirme dalına da
girmesi, ya da bir dalda yazılmış tek bir işlemenin öbür dalda erken kullanılması. Sonraki
ders tek bir işlemeyi başka dala kopyalayan biçime bakar — ve o kopyanın tarihçede iki ayrı
kimlikle iki ayrı yerde durmasının, bir dosyaya dokunan işlemeleri sayan **dosya izi**
sorusuna ne yaptığına.

### Kiraz Toplama

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/kiraz-toplama

Tek bir işlemeyi başka dala kopyalamak aynı değişikliği tarihçede iki ayrı kimlikle iki yerde bırakır; dosya izi sorusu yine evet der ama saydığı işleme sayısı ayrı değişiklik sayısından fazladır ve kusurlu bir işleme kopyalandığında yalıtım sorusu düşer.

Yeniden temellendirme bir dalın **bütün** işlemelerini yeni bir tabana taşıdı. Bazen
gereken bundan çok daha azdır: yayımlanmış bir sürümde duran tek bir düzeltmenin
geliştirme dalına da girmesi, ya da bir dalda yazılmış tek bir işlemenin öbür dalda erken
kullanılması. Dalın tamamını almak bu durumda yanlış olur; alınmak istenen tek bir
değişikliktir.

**Kiraz toplama (cherry-pick)** tam olarak bunu yapar. Bu dersin sorusu işlemin nasıl
çalıştığı kadar sonucudur: aynı değişiklik artık tarihçede iki yerde durur ve bir dosyaya
dokunan işlemeleri sayan **dosya izi** sorusu bundan etkilenir.

#### Tek İşlemeyi Kopyalamak

Kiraz toplama, seçilen işlemenin **öncülüne göre yaptığı değişikliği** alır ve bulunduğunuz
dalın ucuna uygular. Uygulama başarılıysa yeni bir işleme yazılır. Yeni işlemenin öncülü
hedef dalın eski ucudur, ağacı yenidir, kimliği yenidir. Kaynak işleme yerinde durur ve
hiçbir şey kaybetmez.

Bu, yeniden temellendirmenin tek işlemelik hâlidir. Aradaki fark kapsamdır: yeniden
temellendirme dalın ucundan ortak ataya kadarki bütün işlemeleri taşır ve eskilerini
erişilemez bırakır; kiraz toplama tek bir işlemeyi kopyalar ve eskisini yerinde bırakır.
Tersine çalışan bir üçüncüsü de vardır: **devirme (revert)** bir işlemenin değişikliğini
ters çevirip yeni bir işleme olarak yazar. Üçü de aynı düzeneğin — yamayı çıkar, yeniden
uygula — ayrı kullanımlarıdır.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git switch kimlik
$ git cherry-pick 4d5e6f1
[kimlik 8c9d0e1] rapor: alan genisligini duzelt
 1 file changed, 6 insertions(+), 2 deletions(-)
```

Soldaki köşeli parantez hedef dalı ve **yeni kimliği** gösteriyor; komuta verilen kimlik
`4d5e6f1`, üretilen kimlik `8c9d0e1`. İki işlemenin içeriği aynı, kimliği ayrı. Bu, dersin
ölçtüğü şeyin tamamıdır.

Uygulama her zaman başarılı olmaz. Kaynak işlemenin dayandığı bağlam hedef dalda yoksa
yama tutmaz ve önceki derslerdeki çakışma durumu doğar: çalışma alanı işaretlerle
bırakılır, çözüm elle verilir, sonra devam ya da iptal çağrılır. Bir aralık verildiğinde
işlemeler sırayla kopyalanır ve çakışma her biri için ayrı ayrı sorulabilir.

Bir de tersi vardır: kopyalanan değişiklik hedef dalda **zaten uygulanmışsa** yama boş
kalır. Araç bu durumda kendiliğinden bir karar vermez, durur ve seçimi kullanıcıya
bırakır — boş bir işleme yazmak da, o adımı atlamak da tarihçeye ayrı şeyler yazar.

#### Aralık, İşlemesiz Kopya ve İki Öncüllü Kaynak

Kopyalanacak şey tek bir işleme olmak zorunda değildir. Komuta bir işleme aralığı
verildiğinde araç aralıktaki işlemeleri **sırayla** kopyalar ve her biri için ayrı bir
işleme yazar. Aralık gösterimi burada da öncül temellidir: verilen alt sınır **dışarıda
kalır**, üst sınır içeridedir. Bu, aralığın bir işleme eksik kopyalanmasının en sık
nedenidir.

İkinci bir seçenek kopyayı işlemeden bırakır: değişiklik çalışma alanına ve hazırlık
alanına uygulanır, işleme yazılmaz. Bu, birkaç işlemeyi kopyalayıp hedef dalda tek bir
işleme olarak kaydetmek istendiğinde kullanılır — ve o durumda tarihçede kaynak
işlemelerin ayrı ayrı karşılığı hiç oluşmaz.

Kaynak bir birleştirme işlemesiyse durum ayrıdır. Birleştirme işlemesinin **iki öncülü**
vardır ve "öncülüne göre yaptığı değişiklik" ifadesi tek bir yanıt vermez: hangi öncüle
göre bakıldığına bağlı olarak iki ayrı yama çıkar. Araç bu yüzden hangi öncülün taban
sayılacağının açıkça belirtilmesini ister ve belirtilmediğinde işi reddeder.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git cherry-pick a1b2c3d
error: commit a1b2c3d is a merge but no -m option was given.
fatal: cherry-pick failed
```

Bu red, birinci dersteki iki öncüllü yapının doğrudan sonucudur. Birleştirme işlemesinin
taşıdığı bilgi bir değişiklik değil, iki çizginin buluşmasıdır; tek bir yamaya
indirgenmesi ancak bir taraf seçilerek yapılabilir.

#### Kopyanın Kökeni Kaydedilir mi

İkinci dersin sonucu burada bire bir tekrar eder. Kopyanın kaynağı, araç kendiliğinden
yazmadıkça tarihçede durmaz: yeni işleme, hangi işlemeden kopyalandığını söyleyen bir alan
taşımaz. Bu bilgiyi kaydeden bir seçenek vardır ve isteğe bağlıdır — verildiğinde araç
işleme mesajının sonuna kaynağın kimliğini yazan bir satır ekler.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git cherry-pick -x 4d5e6f1
$ git log -1 --format=%B
rapor: alan genisligini duzelt

(cherry picked from commit 4d5e6f1)
```

Satırın nereye yazıldığına dikkat: **işleme mesajının içine**. Araç bunun için ayrı bir
alan tanımlamaz; kökeni de gerekçe gibi mesaja yazar. Önceki derste çakışma çözümü için
söylenen şey burada da geçerlidir — mesaja yazılmayan bilgi hiçbir yerde durmaz.

Kaynağı yazılmamış bir kopya sonradan bulunabilir mi? Kısmen. Aracın yamaları içeriğine
göre karşılaştıran bir yeteneği vardır: iki işlemenin ürettiği değişiklik aynıysa,
kimlikleri ayrı olsa bile aynı **yama kimliğini** taşırlar. İki dal arasında hangi
işlemelerin karşı tarafta zaten bulunduğunu bu karşılaştırma söyler.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git cherry -v ana rapor
+ 7e8f9a0 rapor: ozet satirini ekle
- 4d5e6f1 rapor: alan genisligini duzelt
```

Eksi işaretli satır, o değişikliğin karşı tarafta eşdeğer bir yama olarak zaten bulunduğunu
bildirir. Bu, kopyanın **var olduğunu** bulur; kopyanın **neden** alındığını bulmaz. İki
soru ayrıdır ve ikincisinin yanıtı yine yalnız mesajdadır.

Yama karşılaştırmasının bir sınırı da vardır: eşdeğerlik değişikliğin metnine bakar.
Kopya sırasında bir çakışma çözülmüşse ya da bağlam farklıysa yamalar birbirinin aynı
olmaz ve eşleşme kurulamaz. Kopya o zaman ayrı bir değişiklik gibi görünür.

Ölçümün varsayımları:

- **BR22** — Kiraz toplama, kaynak işlemenin öncülüne göre yaptığı değişikliği hedef dalın
  ucuna **yeni kimlikli** bir işleme olarak yazar; kaynak işleme yerinde kalır.
- **BR23** — Yama kimliği değişikliğin kendisidir. Kopya aynı yama kimliğini taşır, işleme
  kimliği ayrıdır; ölçüm ikisini **ayrı işleme** olarak sayar.
- **BR24** — Kopya, hedef dalın kendi işlemelerinden sonra ve birleştirme işlemesinden önce
  yazılır; kendi zamanını taşır ve dalın bütünlüğünü bozmaz.
- **BR25** — Taban tarihçe birleştirme işlemesi biçimidir. Kopyanın eklenmesi dışında
  hiçbir şey değiştirilmez.
- **BR26** — İzlenen dosya `rapor.py`. Dosya izi sorusu, ona dokunan işlemelerin **ayrı
  ayrı bulunabilmesine** bakar; kaç ayrı değişiklik olduğuna bakmaz.
- **BR27** — Kusurlu bir işlemenin kopyası da kusurludur; kopyalanan değişiklik kusuru da
  taşır.
- **BR28** — Kümenin çözünürlüğü on iki işlemede **1/12**, altı soruda **1/6**.

#### Ölçüm

```python
"""Kiraz toplama: ayni degisiklik tarihcede iki yerde durur.

Bolum 1 - kopya oncesi ve sonrasi: rapor.py dosyasina dokunan isleme sayisi.
Bolum 2 - dosya izi ve kusurun yalitimi sorulari kopyadan nasil etkilenir.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
IZLENEN = "rapor.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def tumlestir(kayit, bicim):
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    return t


def s2_kusur(t):
    kusurlu = [x for x in t if x["kusurlu"]]
    return len(kusurlu) == 1 and not kusurlu[0].get("sikisik")


def s4_dosya(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def yama(x):
    """Yama kimligi: degisikligin kendisi. Kopya ayni yamayi tasir."""
    return x.get("yama", (x["dal"], x["sira"]))


def kiraz_topla(t, kaynak, hedef):
    """kaynak islemesi hedef dala kopyalanir: yeni kimlik, ayni yama."""
    y, k = [], next(x for x in t if (x["dal"], x["sira"]) == kaynak)
    son = max(x["zaman"] for x in t if x["dal"] == hedef
              and not x.get("birlesme"))
    for x in t:
        if x.get("birlesme") and x["dal"] == hedef:
            y.append({**k, "dal": hedef, "dal_kaydi": hedef, "zaman": son,
                      "yama": kaynak, "kopya": True})
        y.append(dict(x))
    return y


def dokunan(t, ad):
    return [x for x in t if x["dosya"] == ad]


kayit = gelisim()
TABAN = tumlestir(kayit, "birlestirme islemesi")
TARIHCELER = (("kopya yok", TABAN),
              ("kusursuz işleme kopyalandı", kiraz_topla(TABAN, ("rapor", 1),
                                                         "kimlik")),
              ("kusurlu işleme kopyalandı", kiraz_topla(TABAN, ("rapor", 2),
                                                        "kimlik")))

print(f"gerçek gelişim: {len(kayit)} işleme | izlenen dosya: {IZLENEN} | "
      f"gelişimde ona dokunan {sum(1 for k in kayit if k['dosya'] == IZLENEN)}")
print()
print(f"{'tarihçe':<26s} {'işleme':>6s} {'dokunan':>7s} {'ayrı yama':>9s}"
      f" {'yinelenen':>9s} {'dosya izi':>9s}")
for ad, t in TARIHCELER:
    d = dokunan(t, IZLENEN)
    ayri = len({yama(x) for x in d})
    print(f"{ad:<26s} {len(t):6d} {len(d):7d} {ayri:9d} {len(d) - ayri:9d}"
          f" {('evet' if s4_dosya(t) else 'hayır'):>9s}")
print()
print(f"{'tarihçe':<26s} {'kusurlu işleme':>14s} {'kusurun yalıtımı':>16s}")
for ad, t in TARIHCELER:
    print(f"{ad:<26s} {sum(1 for x in t if x['kusurlu']):14d}"
          f" {('evet' if s2_kusur(t) else 'hayır'):>16s}")
print()
for ad, t in TARIHCELER[1:]:
    k = [x for x in t if x.get("kopya")][0]
    print(f"{ad}: kopya {k['dal']} dalında, yaması {k['yama']}, "
          f"dosyası {k['dosya']}")
```

```
gerçek gelişim: 12 işleme | izlenen dosya: rapor.py | gelişimde ona dokunan 3

tarihçe                    işleme dokunan ayrı yama yinelenen dosya izi
kopya yok                      15       3         3         0      evet
kusursuz işleme kopyalandı     16       4         3         1      evet
kusurlu işleme kopyalandı      16       4         3         1      evet

tarihçe                    kusurlu işleme kusurun yalıtımı
kopya yok                               1             evet
kusursuz işleme kopyalandı              1             evet
kusurlu işleme kopyalandı               2            hayır

kusursuz işleme kopyalandı: kopya kimlik dalında, yaması ('rapor', 1), dosyası rapor.py
kusurlu işleme kopyalandı: kopya kimlik dalında, yaması ('rapor', 2), dosyası rapor.py
```

#### Dosya İzinin Söylediği ve Söylemediği

Üst tablo dosya izi sorusuna üç tarihçede de **evet** verdiriyor. Sorunun tanımı
değişmedi: `rapor.py` dosyasına dokunan her işleme ayrı ayrı bulunabiliyor. Kopyalama bu
yanıtı bozmuyor, çünkü kopya da tek bir dosyaya dokunan tek bir işlemedir.

Bozulan şey yanıtın **anlamıdır**. Kopya yokken `rapor.py` dosyasına dokunan **3** işleme
var ve bunlar **3** ayrı yamayı taşıyor; sayılar örtüşüyor. Kopyadan sonra dokunan işleme
**4**, ayrı yama yine **3**, yinelenen **1**. Dosya izi hâlâ tam ama artık iki ayrı soruya
iki ayrı sayı veriyor: "bu dosyaya kaç işleme dokundu" sorusunun yanıtı 4, "bu dosyada
kaç ayrı değişiklik yapıldı" sorusunun yanıtı 3.

Bu ayrım ölçümden önce görünmezdi, çünkü kopya olmayan bir tarihçede iki sayı hep aynıdır.
Kiraz toplama onları ayırır ve tarihçeye bakan kişi hangisini okuduğunu bilmek zorunda
kalır. Bir dosyanın kaç kez değiştirildiğine bakarak istikrarsızlık ölçen bir okuma,
kopyaları saydığı için o dosyayı olduğundan hareketli gösterir. İz eksik değildir; iz
**yinelenmiştir** ve yinelenmenin işareti tarihçenin kendisinde durmaz. Onu bulmak için
yamaları içerik olarak karşılaştıran ayrı bir araç gerekir.

Aynı yinelenme satır düzeyinde de görünür. Bir satırın hangi işlemeden geldiğini soran
araç, kopyalanmış bir satır için **kopyayı** gösterir, kaynağı değil. Sorulan soru
genellikle "bu satır neden böyle" olduğuna göre, okunacak mesaj da kopyanın mesajıdır.
Kaynağın mesajı ne kadar ayrıntılı olursa olsun, köken satırı yazılmadıysa oraya giden
bir bağ yoktur. Kökeni kaydeden seçeneğin değeri tam olarak burada ortaya çıkar: iki
satırlık bir ek, kopyadan kaynağa giden tek bağdır.

#### Kusurun İki Yeri

Alt tablo daha sert bir sonuç veriyor. Kusursuz bir işleme kopyalandığında kusurlu işleme
sayısı **1**'de kalıyor ve yalıtım sorusu **evet** veriyor: tarihçe değişti ama ölçüm
değişmedi. Kusurlu işleme kopyalandığında sayı **2** oluyor ve yalıtım sorusu **hayır**
veriyor.

Neden düştüğü tanımdan okunur: yalıtım sorusu kusuru getiren işlemenin **tek başına**
bulunmasını arar. Kusur iki ayrı işlemede duruyorsa "kusuru getiren işleme" tek bir yanıt
değildir. Bir dalda düzeltilen kusur öbür dalda ayakta kalır ve düzeltmenin oraya da
taşınması gerekir; taşındığında da tarihçede iki düzeltme, iki kimlik ve iki ayrı yer
oluşur.

Bu ikilik zamanla derinleşir. Kopya ile kaynak ayrı işlemelerdir ve bundan sonrası ayrı
yürür: kaynak dalda o değişikliğin üzerine yapılan bir düzeltme kopyaya kendiliğinden
gitmez, kopyanın bulunduğu dalda yapılan bir değişiklik de kaynağa dönmez. İki yer aynı
metinle başlar ve her düzenlemede biraz daha ayrışır. Yama karşılaştırması ilk günlerde
eşleşme bulur, ayrışma başladıktan sonra bulmaz — yani ikilik tam da onu bulmanın
zorlaştığı anda kalıcılaşır.

Sayısal etki **1/6**'dır: altı sorudan biri düşer ve birleştirme işlemesinin yanıtladığı
soru sayısı **5**'ten **4**'e iner. Bu, kümenin çözünürlüğüne eşit ve savunulabilir en
küçük farktır.

Buradan bir ölçüt çıkar. Kiraz toplama **tek yönlü ve kısa ömürlü** olduğunda ucuzdur:
yayımlanmış bir daldaki düzeltme geliştirme dalına alınır, sonra iki dal zaten
birleşeceği için kopya kalıcı bir yinelenme yaratmaz. Kiraz toplama iki dal arasında
**düzenli bir tümleştirme yolu** olarak kullanıldığında pahalıdır: her kopya bir yinelenen
yama, her yinelenen yama bir yanlış sayım ve bir kusur için iki ayrı yer demektir. İkinci
kullanımda gereken şey kopyalama değil, birleştirmedir.

#### Özet

- Kiraz toplama, bir işlemenin öncülüne göre yaptığı değişikliği başka dalın ucuna yeni
  kimlikli bir işleme olarak yazar; kaynak işleme yerinde kalır ve aynı değişiklik iki
  yerde durur.
- Kopyanın kökeni araç tarafından kendiliğinden kaydedilmez; kaydeden seçenek kimliği
  **işleme mesajının içine** yazar, ayrı bir alan yoktur.
- Yamaları içeriğine göre karşılaştıran araç kopyanın **var olduğunu** bulur; **neden**
  alındığını bulmaz ve çakışma çözülerek alınmış bir kopyayı hiç eşleştiremeyebilir.
- Dosya izi sorusu kopyadan sonra da **evet** verir, ama dokunan işleme **3**'ten **4**'e
  çıkarken ayrı yama **3**'te kalır; iz eksilmez, yinelenir.
- Kusurlu bir işleme kopyalandığında kusurlu işleme **2** olur ve yalıtım sorusu **hayır**
  verir; yanıtlanan soru sayısı **5**'ten **4**'e iner.

#### Sonraki Adım

Kursun ölçtüğü tümleştirme biçimlerinden üçü artık kuruldu: birleştirme işlemesi, ileri
sarma ve yeniden temellendirme. Kiraz toplama bunlardan biri değildir — bir dalı ana dala
alma yolu değil, tek bir değişikliği taşıma yoludur. Geriye dördüncü biçim ve biçimleri
**yan yana** koymak kalıyor. Sonraki ders her dalın işlemelerini tek bir işlemede toplayan
sıkıştırmayı ekler ve dördünü tek tabloda karşılaştırır — en okunaklı tarihçeyi üreten
biçimin en az soru yanıtlayan biçim olduğu ve tarihçeyi temizlemenin neden bilgi sildiği
orada sayıyla gösterilir.

### Birleştirme Stratejileri

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/birlestirme-stratejileri

Dört tümleştirme biçimi aynı on iki işlemelik gelişimi ana dala alır ve tarihçeye 15, 12, 3 ve 12 işleme bırakır; yanıtladıkları soru sayısı 5, 3, 2 ve 3'tür ve en temiz tarihçe olan sıkıştırma on iki işlemenin hepsini ayrı ayrı okunamaz kılar.

Kursun ölçtüğü tümleştirme biçimlerinden üçü kuruldu: birleştirme işlemesi, ileri sarma ve
yeniden temellendirme. Aralarında kiraz toplama da ölçüldü, ama o bir tümleştirme biçimi
değil, tek bir değişikliği taşıma yoluydu.

Bu ders dördüncü biçimi — bir dalın bütün işlemelerini tek bir işlemede toplayan
sıkıştırmayı — ekler ve dördünü **yan yana** koyar. Sorulan şey artık
bir biçimin ne yaptığı değil, dördünün aynı gelişim üzerinde nasıl sıralandığıdır — ve
sıralamanın iki ölçütte **ters yönde** çıkması bu kursun ikinci iddiasıdır.

#### İki Ayrı Karar, Tek Kelime

"Strateji" kelimesi burada iki ayrı şeye işaret eder ve karıştırılması sık görülür.

**Birincisi içerik stratejisidir:** araç iki tarafın dosyalarını hangi yöntemle
birleştirecek. Varsayılan yöntem birinci derste kurulan üçlü birleştirmedir; ata sürümüne
bakar, tek taraflı değişiklikleri alır, iki taraflı olanlarda karar veremez. Aracın ikiden
çok dalı tek seferde birleştiren ayrı bir yöntemi de vardır ve o yöntem çakışma çıktığında
hiçbir şey yapmadan durur. Strateji **seçenekleri** ise çakışma durumunda bir tarafı
kendiliğinden yeğlemeyi sağlar.

**İkincisi biçim kararıdır:** birleşme bittiğinde tarihçeye ne yazılacak. Kapanış işlemesi
mi, yeniden yazılmış bir zincir mi, tek bir toplu işleme mi, yoksa hiçbir şey mi.

İki karar bağımsızdır. Aynı içerik stratejisi dört ayrı biçimle sonuçlanabilir; aynı biçim
ayrı içerik stratejileriyle üretilebilir. Bu kursun ölçtüğü şey **yalnız ikincisidir**,
çünkü kod dördünde de aynıdır.

Bir uyarı ilk karar için geçerlidir. Çakışmada bir tarafı kendiliğinden yeğleyen strateji
seçeneği, ikinci dersteki bütün dosyayı bir taraftan alma kısayolunun tümleştirme
ölçeğindeki hâlidir: çakışmayı çözmez, çözülmüş sayar. Öbür tarafın o satırlardaki
değişikliği sessizce düşer ve tarihçede hiçbir belirti bırakmaz. Üretilmiş dosyalarda
yerindedir; elle yazılmış kodda değildir.

#### Dördüncü Biçim: Sıkıştırma

**Sıkıştırma (squash)** dalın işlemelerini ana dala hiç almaz. Aracın yaptığı iş, dalın
toplam değişikliğini çalışma alanına uygulamak ve orada durmaktır; işlemeyi siz yazarsınız
ve tek bir işleme yazılır.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git merge --squash rapor
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested

$ git commit -m "rapor: ozet satiri ve alan genisligi"
[ana 6f7a8b9] rapor: ozet satiri ve alan genisligi
 2 files changed, 37 insertions(+), 4 deletions(-)
```

İki satır önemlidir. `not updating HEAD` ifadesi, birleştirme işlemesinin **yazılmadığını**
bildirir: üretilen işlemenin tek öncülü vardır ve dala giden bir kenar yoktur. İkinci
komutun çıktısındaki dosya sayısı ise dalın dört işlemesinin toplamıdır — dört ayrı
değişiklik tek bir yamada birleşmiştir.

Bunun görülmeyen bir sonucu vardır: dalın işlemeleri ana dalın atası olmadığı için, aynı
dal daha sonra olağan yolla birleştirilirse araç onun **hiç** alınmadığını sanır ve bütün
işlemelerini yeniden getirmeye çalışır. Sıkıştırılarak alınan dal, alındıktan sonra
kapatılmalıdır.

Sıkıştırmanın seçilme gerekçesi genellikle şudur: geliştirme sırasında yazılan ara
işlemeler — yarım kalmış denemeler, kendi kendini düzelten adımlar — ana dalın günlüğünü
doldurur. Gerekçe gerçektir, ama ölçüm karşılığında ne verildiğini gösterir. Ara
işlemelerin gürültüsünü azaltmanın iki yolu vardır: onları silmek ya da onları düzgün
yazmak. Birincisi tarihçenin çözünürlüğünü de siler; ikincisi silmez. Sıkıştırma bu iki
yol arasında bir tercihtir, zorunluluk değildir.

Ölçümün varsayımları:

- **BR29** — Dört biçim aynı on iki işlemelik gelişimi ana dala alır ve **aynı kodu**
  üretir; ölçülen yalnız bıraktıkları tarihçedir.
- **BR30** — Sıkıştırma her dalın işlemelerini tek işlemede toplar: dosya alanı tek ad
  değil **ad listesi** olur, kusur alanı grubun herhangi bir işlemesinde kusur varsa doğru
  olur.
- **BR31** — "Ayrı okunabilir işleme", tarihçede tek başına duran ve bir sıkıştırmanın
  içine girmemiş işlemedir. Birleştirme işlemesinin eklediği kapanışlar bu sayıya girmez.
- **BR32** — "Kaybolan", on iki gerçek işlemeden tarihçede ayrı ayrı okunamayanların
  sayısıdır; kod olarak kaybolan hiçbir şey yoktur.
- **BR33** — Altı sorunun tanımı kursun sabitidir ve bu derste değiştirilmez; yalnız dört
  biçim için ayrı ayrı çalıştırılır.
- **BR34** — Çakışma kararı sorusu, ikinci derste ödendiği gibi dört biçimde de yanıtsızdır.
  Bu ders o sonucu değiştirmez, tabloda gösterir.
- **BR35** — Kümenin çözünürlüğü on iki işlemede **1/12**, altı soruda **1/6**.

#### Ölçüm

```python
"""Dort tumlestirme bicimi yan yana: isleme sayisi ve yanitlanan soru.

Bolum 1 - dort bicim ayni on iki islemeyi ana dala alir.
Bolum 2 - alti soru dort tarihceye de sorulur.
Bolum 3 - sikistirilmis tarihcenin uc islemesi neyi tasiyor.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def tumlestir(kayit, bicim):
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    elif bicim == "yeniden temellendirme":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": None, "zaman_korundu": False})
    elif bicim == "sikistirma":
        for dal in DALLAR:
            grup = [x for x in kayit if x["dal"] == dal]
            t.append({"dal": dal, "sira": 0,
                      "dosya": sorted({x["dosya"] for x in grup}),
                      "kusurlu": any(x["kusurlu"] for x in grup),
                      "zaman": max(x["zaman"] for x in grup), "dal_kaydi": dal,
                      "zaman_korundu": False, "sikisik": len(grup)})
    elif bicim == "ileri sarma":
        for k in sorted(kayit, key=lambda x: x["zaman"]):
            t.append({**k, "dal_kaydi": None, "zaman_korundu": True})
    return t


def s1_gruplama(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2_kusur(t):
    kusurlu = [x for x in t if x["kusurlu"]]
    return len(kusurlu) == 1 and not kusurlu[0].get("sikisik")


def s3_sira(t):
    return all(x.get("zaman_korundu") for x in t)


def s4_dosya(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5_kapsam(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6_cakisma(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1_gruplama), ("kusurun yalıtımı", s2_kusur),
           ("gerçek sıra", s3_sira), ("dosya izi", s4_dosya),
           ("dalın bütünlüğü", s5_kapsam), ("çakışma kararı", s6_cakisma))
BICIMLER = ("birlestirme islemesi", "yeniden temellendirme", "sikistirma",
            "ileri sarma")
kayit = gelisim()
T = {b: tumlestir(kayit, b) for b in BICIMLER}

print(f"gerçek gelişim: {len(kayit)} işleme, {len(DALLAR)} dal, kusurlu "
      f"{sum(1 for k in kayit if k['kusurlu'])}")
print("sorular:", ", ".join(f"s{i}={ad}" for i, (ad, _) in
                            enumerate(SORULAR, 1)))
print()
print(f"{'biçim':<22s} {'işleme':>6s} {'ayrı':>5s} {'kaybolan':>8s}"
      f"  {'s1 s2 s3 s4 s5 s6':<17s} {'yanıtlanan':>10s}")
for b in BICIMLER:
    ayri = sum(1 for x in T[b] if not x.get("birlesme")
               and not x.get("sikisik"))
    im = "  ".join("+" if soru(T[b]) else "-" for _, soru in SORULAR)
    print(f"{b:<22s} {len(T[b]):6d} {ayri:5d} {len(kayit) - ayri:8d}"
          f"  {im:<17s} {sum(s(T[b]) for _, s in SORULAR):10d}")
print()
print("sıkıştırılmış tarihçenin işlemeleri:")
for x in T["sikistirma"]:
    print(f"  {x['dal']:<7s} {x['sikisik']} işleme -> 1 | dosyalar "
          f"{','.join(x['dosya'])} | kusur "
          f"{'var' if x['kusurlu'] else 'yok'}")
```

```
gerçek gelişim: 12 işleme, 3 dal, kusurlu 1
sorular: s1=özellik gruplaması, s2=kusurun yalıtımı, s3=gerçek sıra, s4=dosya izi, s5=dalın bütünlüğü, s6=çakışma kararı

biçim                  işleme  ayrı kaybolan  s1 s2 s3 s4 s5 s6 yanıtlanan
birlestirme islemesi       15    12        0  +  +  +  +  +  -           5
yeniden temellendirme      12    12        0  -  +  -  +  +  -           3
sikistirma                  3     0       12  +  -  -  -  +  -           2
ileri sarma                12    12        0  -  +  +  +  -  -           3

sıkıştırılmış tarihçenin işlemeleri:
  olcum   4 işleme -> 1 | dosyalar ayar.py,olcum.py | kusur yok
  rapor   4 işleme -> 1 | dosyalar ayar.py,rapor.py | kusur var
  kimlik  4 işleme -> 1 | dosyalar kimlik.py | kusur yok
```

#### Okunaklılık ile Yanıtlanabilirlik

İşleme sütunu dört biçimi şöyle sıralıyor: **15**, **12**, **3**, **12**. En kısa tarihçe
sıkıştırmanındır — üç dal, üç işleme. En uzunu birleştirme işlemesinindir; on iki işlemenin
üstüne üç kapanış eklenmiştir. Okunaklılık ölçüsü buysa sıralama nettir: sıkıştırma önde,
birleştirme işlemesi sonda.

Yanıtlanan soru sütunu tam tersini veriyor: **5**, **3**, **2**, **3**. En kısa tarihçe en
az soruyu, en uzun tarihçe en çok soruyu yanıtlıyor. İki ölçüt **ters yönde hareket
ediyor** ve bu kursun ikinci iddiası budur.

Kayıp sütunu nedeni gösteriyor. Üç biçimde kaybolan işleme **0**'dır: on iki işlemenin
hepsi tarihçede ayrı ayrı durur, yalnız dizilişleri ayrıdır. Sıkıştırmada kaybolan **12**,
ayrı okunabilir işleme **0**'dır. Bu, tarihçenin on ikide birlik çözünürlüğünü tamamen
bıraktığı anlamına gelir: artık en küçük okunabilir birim bir işleme değil, bir dalın
tamamıdır.

Alt döküm bu kaybın somut hâlini veriyor. `rapor` dalının dört işlemesi tek bir işlemeye
inmiş; o işleme `ayar.py` ile `rapor.py` dosyalarına dokunuyor ve kusur taşıyor. Kusurun
**var** olduğu okunabiliyor, hangi adımdan geldiği okunamıyor. Aynı biçimde dosya izi de
düşüyor: bir işlemenin iki dosyaya birden dokunduğu görülüyor ama `ayar.py` dosyasına
dokunan işlemeyi tek başına bulmak artık olanaksız.

Sıkıştırmanın yanıtladığı **2** soru da öğreticidir: **özellik gruplaması** ve **dalın
bütünlüğü**. İkisi de sıkıştırmanın yapısı gereği yanıtlanır — her işleme zaten bir işin
tamamıdır, dolayısıyla gruplama kusursuz ve bütünlük tanım gereği sağlanmıştır. Sıkıştırma
kaba soruları en iyi yanıtlayan biçimdir; ince soruların hiçbirini yanıtlayamaz.

Takasın iki tarafı eşit ağırlıkta değildir ve bunu görmek seçimi kolaylaştırır. **15**
işlemelik tarihçenin fazlalığı bir **okuma** maliyetidir: günlük çıktısı uzundur. Okuma
maliyeti sonradan azaltılabilir — tarihçeyi süzen, tek çizgiye indirgeyen, tarihe ya da
dosyaya göre daraltan okuma yolları vardır ve hepsi aynı kayıt üzerinde çalışır.
Sıkıştırmanın **12** işlemelik kaybı ise bir **kayıt** kaybıdır ve sonradan geri
getirilemez; hiçbir okuma seçeneği yazılmamış bir işlemeyi gösteremez. Bir taraf
süzülebilir, öbür taraf üretilemez.

Buradan çıkan cümle şudur: **tarihçeyi temizlemek onu ucuzlatmaz, bilgi siler.** Silinen
bilgi kod değildir — dördü de aynı kodu üretir. Silinen, koda **nasıl** varıldığıdır.

#### Ölçüte Dayalı Seçim

Dört biçim arasında doğru olan yoktur; sorulacak soruya uygun olan vardır. Ölçüt tek bir
sorudur: **bu tarihçeye aylar sonra ne sorulacak?** Soru bilinmiyorsa varsayılan, en çok
soruyu yanıtlayan biçimdir — çünkü fazladan kayıt sonradan yok sayılabilir, eksik kayıt
sonradan yazılamaz.

**Kusur avı sorulacaksa** kusurun yalıtımı ve dosya izi gerekir. Bunları üç biçim
yanıtlar, sıkıştırma yanıtlamaz. Kusuru ikili aramayla bulan araçlar da aynı çözünürlüğe
dayanır: on iki işlemelik bir aralıkta arama dört adımda biter, üç işlemelik bir aralıkta
iki adımda biter ama bulduğu şey dört değişikliği birden içerir.

**İşin kapsamı sorulacaksa** özellik gruplaması gerekir. Bunu yalnız birleştirme işlemesi
ve sıkıştırma yanıtlar; ikisi de dal kaydını bir biçimde yazar. Yeniden temellendirme ile
ileri sarma bu soruda eşit biçimde sessizdir.

**Zaman sorulacaksa** gerçek sıra gerekir ve bunu yalnız iki biçim yanıtlar: birleştirme
işlemesi ve ileri sarma. Yeniden temellendirme ile sıkıştırma çizgiyi işe göre dizdiği
için zamanı bırakır.

Seçim biçimlerden birine bağlanmak zorunda da değildir. Dal türüne göre ayrı biçim
kullanmak tutarlı bir kuraldır: tek işlemelik düzeltmeler ileri sarmayla alınır, çünkü
orada dal kaydının sonradan sorulması beklenmez; birden çok işlemelik özellik dalları
birleştirme işlemesiyle alınır, çünkü orada iş kapsamı sorulacaktır; dışarıdan gelen ve
ara işlemeleri denetlenmemiş katkılar sıkıştırılarak alınabilir. Kuralın kendisi yazılı
olmalıdır — biçim kararı her seferinde birleştirmeyi yapan kişinin o günkü tercihine
kalırsa tarihçe dört ayrı çözünürlüğün karışımı olur ve hiçbir soruya tutarlı yanıt
vermez.

Aracın bu kararı yerinde tutan ayarları da vardır. Birinci derste geçen ileri sarma
kilitleri bunun en dar hâlidir: bir depo, ileri sarma dışında bir birleşmeyi ya da tersine
ileri sarmayı kendiliğinden reddedecek biçimde ayarlanabilir. Ayar kuralın yerine geçmez,
kuralın unutulmasını engeller.

Fark aralığı **2/6** ile **5/6** arasındadır; kümenin çözünürlüğü **1/6** olduğuna göre
uçlar arasındaki **3/6**'lık fark ölçüm bandının üç katıdır ve rahatça savunulur. Bedel
sütunu ise **3/12**'dir: birleştirme işlemesinin en çok soruyu yanıtlamak için ödediği
şey, on iki işlemeye eklenen üç kapanıştır.

#### Özet

- Aracın içerik stratejisi ile tümleştirme biçimi ayrı kararlardır; birincisi dosyaların
  nasıl birleştiğini, ikincisi tarihçeye ne yazıldığını belirler ve bu kurs ikincisini
  ölçer.
- Sıkıştırma dalın işlemelerini ana dala hiç almaz, tek öncüllü tek bir işleme yazar;
  dalın işlemeleri ana dalın atası olmadığı için o dal sonradan yeniden birleştirilmemeli,
  kapatılmalıdır.
- Dört biçim tarihçeye **15**, **12**, **3** ve **12** işleme bırakır; yanıtladıkları soru
  sayısı sırasıyla **5**, **3**, **2** ve **3**'tür.
- Sıkıştırmada kaybolan işleme **12**, ayrı okunabilir işleme **0**'dır; kusurun **var**
  olduğu okunur, hangi adımdan geldiği okunmaz. Tarihçeyi temizlemek bilgi siler.
- Uzun tarihçenin fazlalığı bir okuma maliyetidir ve süzülerek azaltılabilir; sıkıştırmanın
  kaybı bir kayıt kaybıdır ve hiçbir okuma seçeneğiyle geri getirilemez.
- Seçim ölçütü tek sorudur: bu tarihçeye sonradan ne sorulacak. Kusur avı için üç biçim,
  işin kapsamı için iki biçim, gerçek zaman için iki biçim yeterlidir; kural dal türüne
  göre ayrılabilir ama yazılı olmalıdır.

#### Sonraki Adım

Bu derste biçim seçimi tek bir birleşme için yapıldı: şu dal ana dala nasıl alınsın.
Ekipler bu kararı her seferinde yeniden vermez; bir kez verip **kural** hâline getirir ve
kural yalnız birleşme anını değil, dalların ne kadar yaşayacağını, kaç tane olacağını ve
ne sıklıkla ana dala döneceğini de belirler. Sonraki ders bu kuralları — uzun ömürlü ve
kısa ömürlü dal düzenlerini — tarihçeye ne bıraktıklarına göre karşılaştırır.

### Dal Stratejileri

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/dal-stratejileri

Dalın ne kadar yaşadığı tarihçeye yazılanı belirler: aynı on iki işleme dal açılmadan 12, dört işlemede dönen düzende 15, her işlemede dönen düzende 24 işleme bırakır — ve ayrışma penceresi kapandığında birleştirme çakışması hiç doğmaz.

Önceki derste biçim seçimi tek bir birleşme için yapıldı: şu dal ana dala nasıl alınsın.
Ekipler bu kararı her seferinde yeniden vermez; bir kez verip kural hâline getirirler.

Kural yalnız birleşme anını kapsamaz. Dalların ne kadar yaşayacağını, kaç tane olacağını
ve ne sıklıkla ana dala döneceğini de belirler. Bu dersin sorusu şudur: aynı on iki
işlemelik gelişim ayrı dal düzenleriyle yürütüldüğünde tarihçeye ne kalır — ve dal ömrünün
kısalması hangi soruyu ortadan kaldırır.

Sürekli tümleştirmenin derleme, sınama ve dağıtım hattı açısından tartışması bu kursun
konusu değildir; **Sürekli Tümleştirme ve Teslimat** kursunda kurulur ve burada
tekrarlanmaz. Ölçülen tek şey, stratejinin **tarihçeye ne bıraktığıdır**.

#### Dal Ömrü Bir Sayıdır

Dal stratejilerinin farkı tek bir büyüklüğe indirgenebilir: **bir dal ana dala dönmeden
önce kaç işleme biriktirir?** Bu sayı düzenin bütün öbür özelliklerini belirler.

**Uzun ömürlü dal** düzeninde sayı büyüktür. Dal bir işin tamamı bitene kadar açık kalır;
haftalarca yaşayabilir, onlarca işleme taşıyabilir. **Kısa ömürlü dal** düzeninde sayı
küçüktür; dal bir günden az yaşar, birkaç işleme taşır ve kapanır. Uçta bir düzen daha
vardır: dal hiç açılmaz ve herkes doğrudan ana çizgiye işler.

Üç düzen de aynı işi yaptırır ve aynı kodu üretir. Ayrıldıkları yer, tarihçeye bıraktıkları
kayıt ile dalların birbirinden ne kadar uzaklaştığıdır.

Kısa ömürlü düzenin günlük döngüsü birkaç komuta iner ve her turda aynen tekrarlanır.

```text
# öğretilen komut, örnek döküm — çalıştırılmamıştır

$ git switch -c olcum/esik ana
$ git commit -am "olcum: esik degerini yazdir"
$ git switch ana
$ git merge --no-ff olcum/esik
$ git branch -d olcum/esik
```

Beşinci satır düzenin ayırt edici parçasıdır: dal, işi ana dala girdikten hemen sonra
kapatılır. Kapatılmayan kısa dallar birikir ve dal listesi bir işaretçi çöplüğüne döner —
kayıt tarihçede kapanış işlemesi olarak zaten durduğu için işaretçinin kalması hiçbir
soruyu yanıtlamaz.

Uzun ömürlü dallar tek bir sınıf değildir. Bir işi taşıyan özellik dalı işi bitince
kapanır; bir yayımlanmış sürümü taşıyan dal ise kapanmaz, aylarca yaşar ve yalnız düzeltme
alır. İkincisinde ayrışma tasarım gereğidir — o dalın ana daldan uzaklaşması bir kusur
değil, amaçtır. Bu yüzden düzeltmeler oraya ve geliştirme çizgisine ayrı ayrı gitmek
zorunda kalır; dördüncü dersteki tek işleme kopyalama en sık burada kullanılır ve
kopyanın ödettiği yinelenme de burada birikir.

Adlandırma düzeni burada belirleyici olur. Kısa ömürlü düzende dal sayısı işin
büyüklüğüyle değil, dönüş sıklığıyla artar; bir işin birbirini izleyen turları ayrı ayrı
dallar olur. Dal adları bu turları aynı işe bağlayacak biçimde kurulmamışsa, tarihçedeki
dal kayıtları çoğalır ama hiçbiri işin tamamını göstermez. Adlandırmanın tarihçenin
birinci sorusuna katkısı bu kursun üçüncü dersinde kurulmuştu; burada aynı katkı dal
sayısıyla çarpılır.

#### Ayrışma Penceresi ve Çakışma

İkinci dersin çakışması kurgunun üçüncü adımında doğdu: `olcum` ile `rapor` aynı dosyaya
dokundu. Ama çakışmanın doğması için aynı dosyaya dokunmak yetmez. **İki değişikliğin
paralel geliştirilmiş olması gerekir** — yani ikincisi başladığında birincisi henüz ana
dala girmemiş olmalıdır.

Birincisi zaten girmişse ikinci dal onu **taban olarak** görür. O zaman ortada iki taraflı
bir değişiklik yoktur; tek taraflı bir değişiklik vardır ve araç kararı kendiliğinden
verir. Aynı satır iki kez değişmiştir ama sırayla, çakışmadan.

Buradan dal ömrünün ikinci etkisi çıkar: dal ne kadar uzun yaşarsa ana daldan o kadar çok
işleme kadar **ayrışır** ve iki işin aynı dosyaya paralel dokunma olasılığı o kadar
büyür. Ayrışma bir zaman değil, bir işleme sayısıdır; ve düzenin sayısına eşittir.

Bu, kısa ömürlü dal düzeninin asıl gerekçesidir. Gerekçe "çakışmayı çözmek kolaylaşır"
değildir; **çakışmanın hiç doğmamasıdır**. Buna karşılık kısa dal, yarım kalmış işin ana
dalda durmasını gerektirir — yarım işin görünmeden durabilmesi için çalışma zamanında
kapatılabilir olması gerekir ve o düzeneğin kendisi Sürekli Tümleştirme ve Teslimat
kursunun konusudur.

Ölçümün varsayımları:

- **BR36** — Dal düzeni tek bir sayıyla temsil edilir: bir dal ana dala dönmeden önce kaç
  işleme biriktirir. **Sıfır**, dalın hiç açılmaması demektir.
- **BR37** — Her dönüş bir kapanış işlemesi yazar. Düzen dört işlemede dönüyorsa bu,
  birinci derste ölçülen birleştirme işlemesi biçiminin kendisidir.
- **BR38** — Kısa ömürlü düzende her tur **ayrı bir daldır** ve ayrı bir dal kaydı taşır;
  aynı işin turları arasında bağ kuran bir alan yoktur.
- **BR39** — Ayrışma, bir dalın ana daldan uzak kaldığı **en çok işleme sayısıdır** ve
  düzenin sayısına eşittir.
- **BR40** — İki dokunuşun **paralel** sayılması, ikincisinin dalı açıldığında birincisinin
  ana dala henüz girmemiş olmasıdır. Birleştirme çakışması yalnız paralel dokunuşlarda
  doğar.
- **BR41** — Zaman değerleri kurgunun kendi saatidir, ortama bağlı bir damga değildir;
  yalnız sıralama için kullanılır.
- **BR42** — Altı sorunun tanımı değiştirilmez; her düzen için ayrı ayrı çalıştırılır.
- **BR43** — Kümenin çözünürlüğü on iki işlemede **1/12**, altı soruda **1/6**.

#### Ölçüm

```python
"""Dal stratejisi: dal omru tarihceye ne birakir.

Bolum 1 - dort dal duzeni: dal acmadan, bir, iki ve dort islemede bir donen.
Bolum 2 - ayni ortak dosyaya dokunan iki isleme paralel mi kaliyor.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
DUZENLER = ((0, "dal açılmıyor"), (1, "her işlemede dönüş"),
            (2, "iki işlemede dönüş"), (4, "dört işlemede dönüş"))


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    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


def bosalt(grup, dal, tur):
    """Bir turun islemeleri ve kapanis islemesi."""
    ad = f"{dal}#{tur}"
    y = [{**x, "dal": ad, "is": dal, "dal_kaydi": ad, "zaman_korundu": True}
         for x in grup]
    y.append({"dal": ad, "is": dal, "sira": 0, "dosya": None, "kusurlu": False,
              "zaman": grup[-1]["zaman"], "dal_kaydi": ad, "birlesme": True,
              "zaman_korundu": True})
    return y


def tarihce(kayit, k):
    """k islemede bir ana dala donen dal duzeni. k=0: dal hic acilmaz."""
    sirali = sorted(kayit, key=lambda y: y["zaman"])
    if k == 0:
        return [{**x, "is": x["dal"], "dal_kaydi": None, "zaman_korundu": True}
                for x in sirali]
    t, tampon, tur = [], {d: [] for d in DALLAR}, {d: 0 for d in DALLAR}
    for x in sirali:
        tampon[x["dal"]].append(x)
        if len(tampon[x["dal"]]) == k:
            tur[x["dal"]] += 1
            t.extend(bosalt(tampon[x["dal"]], x["dal"], tur[x["dal"]]))
            tampon[x["dal"]] = []
    for d in DALLAR:
        if tampon[d]:
            tur[d] += 1
            t.extend(bosalt(tampon[d], d, tur[d]))
    return t


def s1_gruplama(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2_kusur(t):
    kusurlu = [x for x in t if x["kusurlu"]]
    return len(kusurlu) == 1 and not kusurlu[0].get("sikisik")


def s3_sira(t):
    return all(x.get("zaman_korundu") for x in t)


def s4_dosya(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5_kapsam(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6_cakisma(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1_gruplama), ("kusurun yalıtımı", s2_kusur),
           ("gerçek sıra", s3_sira), ("dosya izi", s4_dosya),
           ("dalın bütünlüğü", s5_kapsam), ("çakışma kararı", s6_cakisma))


def giris_ani(kayit, k, dal, sira):
    """O islemenin ana dala girdigi an."""
    grup = [x for x in kayit if x["dal"] == dal]
    if k == 0:
        return grup[sira - 1]["zaman"]
    return grup[min(((sira - 1) // k + 1) * k, len(grup)) - 1]["zaman"]


def tur_baslangici(kayit, k, dal, sira):
    """O islemenin bulundugu turun dalinin acildigi an."""
    grup = [x for x in kayit if x["dal"] == dal]
    if k == 0:
        return grup[sira - 1]["zaman"]
    return grup[((sira - 1) // k) * k]["zaman"]


kayit = gelisim()
dokunus = [(k["dal"], k["sira"]) for k in kayit if k["dosya"] == ORTAK_DOSYA]
onceki, sonraki = dokunus[0], dokunus[1]

print(f"gerçek gelişim: {len(kayit)} işleme | {ORTAK_DOSYA} dosyasına dokunan: "
      f"{dokunus}")
print()
print(f"{'dal düzeni':<22s} {'işleme':>6s} {'kapanış':>7s} {'dal kaydı':>9s}"
      f" {'iş başına dal':>13s} {'yanıtlanan':>10s}")
for k, ad in DUZENLER:
    t = tarihce(kayit, k)
    kap = sum(1 for x in t if x.get("birlesme"))
    kayitlar = {x["dal_kaydi"] for x in t if x["dal_kaydi"]}
    print(f"{ad:<22s} {len(t):6d} {kap:7d} {len(kayitlar):9d}"
          f" {(len(kayitlar) / len(DALLAR) if kayitlar else 0):13.1f}"
          f" {sum(s(t) for _, s in SORULAR):10d}")
print()
print(f"{'dal düzeni':<22s} {'ayrışma':>7s} {'önceki dokunuş girer':>20s}"
      f" {'sonraki dal açılır':>18s} {'paralel':>7s}")
for k, ad in DUZENLER:
    girer = giris_ani(kayit, k, onceki[0], onceki[1])
    acilir = tur_baslangici(kayit, k, sonraki[0], sonraki[1])
    print(f"{ad:<22s} {k:7d} {girer:20d} {acilir:18d}"
          f" {('evet' if girer > acilir else 'hayır'):>7s}")
```

```
gerçek gelişim: 12 işleme | ayar.py dosyasına dokunan: [('olcum', 3), ('rapor', 3)]

dal düzeni             işleme kapanış dal kaydı iş başına dal yanıtlanan
dal açılmıyor              12       0         0           0.0          3
her işlemede dönüş         24      12        12           4.0          5
iki işlemede dönüş         18       6         6           2.0          5
dört işlemede dönüş        15       3         3           1.0          5

dal düzeni             ayrışma önceki dokunuş girer sonraki dal açılır paralel
dal açılmıyor                0                   17                 19   hayır
her işlemede dönüş           1                   17                 19   hayır
iki işlemede dönüş           2                   23                 19    evet
dört işlemede dönüş          4                   23                  4    evet
```

#### Tarihçeye Ne Bıraktılar

Üst tablonun son sütunu ilk bakışta şaşırtıcıdır: üç dal düzeni de altı sorunun **5**'ini
yanıtlıyor. Dört işlemede dönen düzen birinci dersteki birleştirme işlemesi biçiminin
kendisidir ve beklenen sayıyı veriyor; ama her işlemede dönen düzen de, iki işlemede dönen
düzen de aynı **5**'i veriyor. **Altı soru dal düzenlerini birbirinden ayırmıyor.** Dal
açılmayan düzen ise **3**'te kalıyor — dal kaydı hiç yazılmadığı için özellik gruplaması ve
dalın bütünlüğü düşüyor, sonuç ileri sarmanın sonucuna eşitleniyor.

Düzenleri ayıran şey soldaki sütunlardır. Aynı on iki işlemelik gelişim, dört işlemede
dönen düzende **15**, iki işlemede dönende **18**, her işlemede dönende **24** işleme
bırakıyor. Kapanış işlemesi sayısı sırasıyla **3**, **6** ve **12**. Son düzende tarihçenin
**yarısı** kapanış işlemesidir: yirmi dört kayıttan on ikisi hiçbir dosyaya dokunmaz. Bu,
önceki derste ölçülen **3/12**'lik kapanış bedelinin dört katıdır ve dal sayısıyla
doğrusal büyür — dönüş sıklığı iki katına çıktığında kapanış sayısı da iki katına çıkar.

Dal kaydı sütunu ikinci maliyeti veriyor. Her işlemede dönen düzende **12** ayrı dal kaydı
var ve üç işe bölündüğünde iş başına **4.0** dal düşüyor. Bu sayı, tarihçenin bir işi tek
bir kayıtta göstermediği anlamına gelir: özellik gruplaması sorusu **evet** der ama
gösterdiği grup bir iş değil, bir **turdur**. Soru dal düzeyinde yanıtlanır ve dal, işten
küçülmüştür.

Buradan bir uyarı çıkar. Altı soru bir tümleştirme biçimini ölçmek için kurulmuştu ve o işi
yapar; bir dal **düzenini** ölçmek için tek başına yetmez. Düzenin maliyeti soruların
dışında, kapanış oranı ile iş başına dal sayısında durur. Ölçünün nereye kadar geçerli
olduğunu bilmek, ölçünün kendisi kadar önemlidir.

#### Ayrışmanın Ödettiği

Alt tablo öbür yöne bakıyor. `ayar.py` dosyasına dokunan iki işleme her düzende aynı iki
işlemedir; değişen, ikincisinin dalı açıldığında birincisinin ana dalda olup olmadığıdır.

Dört işlemede dönen düzende `olcum` dalının üçüncü işlemesi ana dala **23** anında giriyor,
`rapor` dalı ise **4** anında açılmış durumda. İkinci dal çok önce açıldığı için birincinin
değişikliğini hiç görmüyor: iki dokunuş **paralel** ve çakışma doğuyor. İki işlemede dönen
düzende ayrışma yarıya iniyor ama sonuç değişmiyor; giriş anı **23**, dal açılışı **19** ve
dokunuşlar yine paralel.

Her işlemede dönen düzende tablo dönüyor. `olcum` dalının üçüncü işlemesi yazıldığı anda,
**17**'de, ana dala giriyor; `rapor` dalının o turu ise **19**'da açılıyor. İkinci dal
birincinin değişikliğini **taban olarak** görüyor ve **paralel hayır**. Aynı dosyaya
dokunulmasına rağmen ortada çözülecek bir çakışma yok. Dal açılmayan düzende ayrışma
**0**'dır ve sonuç aynıdır.

Örüntü şudur: **ayrışma penceresi kapandığında çakışma doğmaz.** Kısa ömürlü dalın satın
aldığı şey çözülmesi kolay çakışmalar değil, hiç doğmayan çakışmalardır. Ödediği şey ise
üst tablodadır: on iki kapanış işlemesi ve iş başına dört dal kaydı.

Bir üçüncü maliyet ölçümün dışında kalır ve söylenmesi gerekir. Ayrışmayı düşük tutmak,
dalın hızla ana dala dönebilmesine bağlıdır; dönüş bir onay adımından geçiyorsa ayrışma
o adımın süresi kadar uzar. Yani dal ömrü yalnız bir yazma alışkanlığı değil, bir
inceleme hızıdır. İnceleme akışının kendisi bu kursun sonraki konusunda mekanik olarak
kurulur, inceleme ekseni ve kültürü ise ayrı bir kursun konusudur.

İki tablo birlikte okunduğunda strateji kararı yerine oturur. Uzun ömürlü dal tarihçeyi
kısa ve iş düzeyinde okunur tutar, ayrışmayı ve çakışmayı biriktirir. Kısa ömürlü dal
çakışmayı ortadan kaldırır, tarihçeyi kapanış işlemeleriyle şişirir ve işi turlara böler.
İkisinin arasında bir doğru nokta yoktur; hangi maliyetin ödenebilir olduğu ekibin
sorusudur. Ölçüm yalnız iki maliyetin **aynı anda azaltılamayacağını** gösterir.

#### Özet

- Dal stratejilerinin farkı tek bir büyüklüğe iner: bir dalın ana dala dönmeden önce
  biriktirdiği işleme sayısı. Bu sayı hem tarihçenin boyunu hem ayrışmayı belirler.
- Aynı on iki işlemelik gelişim dal açılmayan düzende **12**, dört işlemede dönende **15**,
  iki işlemede dönende **18**, her işlemede dönende **24** işleme bırakır; kapanış sayıları
  **0**, **3**, **6** ve **12**'dir.
- Üç dal düzeni de altı sorunun **5**'ini yanıtlar: **altı soru dal düzenlerini
  ayırmaz**. Dal açılmayan düzen **3**'te kalır.
- Her işlemede dönen düzende iş başına **4.0** dal kaydı düşer; özellik gruplaması evet der
  ama gösterdiği grup bir iş değil bir turdur.
- Çakışma yalnız paralel dokunuşlarda doğar; ayrışma **1** ya da **0** olduğunda ikinci dal
  birincinin değişikliğini taban olarak görür ve çakışma hiç doğmaz.

#### Sonraki Adım

Bu konunun kurduğu her şey — dört tümleştirme biçimi, çakışmanın çözümü, dal ömrü ve
strateji kararı — **tek bir depo** varsayımıyla ölçüldü. Tarihçe bir yerde duruyordu,
birleşmeler orada yapılıyordu, dal kayıtları oraya yazılıyordu.

Gerçek bir ekipte bu varsayım geçerli değildir: tarihçe **birden çok kopyada** yaşar. Her
kişinin kendi deposu vardır, kopyalar birbirinden ayrı ilerler ve ölçülen bütün sorular bir
kez daha sorulur — bu kez "hangi kopyada" diye. Sonraki konu buradan başlar ve önce en
temel soruyu yanıtlar: bir depo başka bir depoyu nasıl tanır, birden çok kopya nasıl
adlandırılır ve ayrı tutulur.

## Uzak Depolar ve Katkı Akışı

Birden çok uzak deponun yönetimi, getirme ile çekme ve itmenin ayrımı, çatallama ile klonlamanın iki ayrı katkı modeli, değişiklik isteği döngüsü ve yama tabanlı katkı.

### Uzak Depo Tanımlama

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/uzak-depo-tanimlama

Uzak depo tanımı bir adı bir adrese bağlar ve tarihçeye dokunmaz; üç kopya sırasıyla 5, 10 ve 15 işleme taşır ve 4, 5, 5 soru yanıtlar, iki tanım sonrası yerel kopya 5 işleme ve 4 soruda kalır.

Önceki ders dal stratejilerini kurdu: uzun ömürlü dal, kısa ömürlü dal ve seçilen stratejinin
tarihçeye ne bıraktığı. O tartışmanın tamamı **tek bir kopyanın** içinde geçti. Dallar aynı
depoda açıldı, aynı depoda birleşti; ölçtüğümüz tarihçe tek bir yerde duruyordu ve o yerde
duran şey işin tamamıydı.

Dağıtık bir sürüm kontrolünde bu varsayım ayakta durmaz. Tarihçe tek bir yerde değil, **birden
çok kopyada** yaşar; kopyalar birbirinden habersiz ilerler ve hiçbiri kendiliğinden ötekinin
işlemelerini bilmez. Bu dersin sorusu, kopyaların nasıl senkronlandığı değil — o sonraki
dersin konusu. Bu dersin sorusu daha önce gelir: bir kopya öbürünü **nasıl adlandırır** ve
adlandırma tarihçeye ne ekler?

#### Kopya ve Uzak Depo

Sürüm Kontrolüne Giriş kursundaki "Merkezî ve Dağıtık Modeller" dersi ayrımı kurmuştu: dağıtık
bir modelde her kopya tam bir depodur. Kopyanın kendi çalışma alanı, kendi hazırlık alanı,
kendi dalları ve kendi tarihçesi vardır. Hiçbir kopya yapısı gereği ötekinden üstün değildir;
"merkez" bir uzlaşımdır, bir araç özelliği değil.

**Uzak depo (remote)**, bir depoda tanımlı olan ve başka bir kopyanın adresine bağlanmış
**addır**. Bu ayrımı baştan koymak gerekir: karşı taraftaki kopya uzak depo değildir; uzak
depo, o kopyaya bu depoda verilen yerel addır. Aynı kopya iki ayrı depoda iki ayrı adla
tanımlanabilir ve bu bir tutarsızlık değildir, çünkü ad yereldir.

Tanımın nerede durduğu bu yüzden önemlidir. Uzak depo tanımı **deponun yapılandırma dosyasına**
yazılır, tarihçeye değil. Doğrudan sonucu şudur: bir kopyayı alan başkası senin uzak depo
adlarını almaz — kendi tanımlarını kendi yapar. Tanım paylaşılan bir veri değil, kişisel bir
yönlendirme tablosudur.

#### Tanımlama Ne Yapar

Tanım, alt komutlarla yönetilir. Aşağıdaki blok **örnek döküm**tür; çalıştırılmamıştır ve
adresler kurgudur.

```text
# öğretilen komutlar — örnek döküm, çalıştırılmamıştır

git remote add koken ornek.test:takim/olcum-aglari.git
git remote add yukari https://ornek.test/kaynak/olcum-aglari.git
git remote -v

koken   ornek.test:takim/olcum-aglari.git (fetch)
koken   ornek.test:takim/olcum-aglari.git (push)
yukari  https://ornek.test/kaynak/olcum-aglari.git (fetch)
yukari  https://ornek.test/kaynak/olcum-aglari.git (push)
```

`-v` seçeneği her ad için iki satır verir, çünkü getirme ve itme adresleri ayrı ayarlanabilir.
Yalnız okunacak bir kopyanın itme adresi bilerek geçersiz bırakılabilir; o zaman ada yapılan
bir itme denemesi ağa çıkmadan durur.

Kalan alt komutlar aynı tabloyu düzenler: `git remote rename` adı değiştirir,
`git remote set-url` adrese bağlı adresi değiştirir, `git remote remove` tanımı ve ona bağlı
uzak izleme başvurularını siler, `git remote show` bir adın altındaki dalları listeler.
`git remote remove` yerel dalları ve işlemeleri silmez; sildiği şey adlar ve o adların altına
yazılmış başvurulardır.

Bir kopyayı ilk kez alırken uzak depo adı elle yazılmaz: klonlama, kaynağa bir varsayılan ad
yazar (`origin`). Bu dersin ölçümü kopyaların rollerini okunur tutmak için kendi adlarını
kullanıyor: yazma yetkisinin bulunduğu kopyaya `koken`, yalnız okunan yukarı depoya `yukari`.

#### Uzak İzleme Başvuruları

Tanım tek başına bir addır; adın altında ne olduğu ayrı bir başvuru kümesinde tutulur. Yerel
dallar `refs/heads` altında durur, bir uzak deponun dalları ise `refs/remotes/<ad>` altında.
İkinci kümedeki başvurulara **uzak izleme başvurusu** denir.

Uzak izleme başvurusunun kuralı tek cümledir: **yerelden yazılmaz.** Üzerine işleme yapılmaz,
elle ileri alınmaz; yalnız karşı taraftan veri getirildiğinde güncellenir. Bu yüzden uzak
izleme başvurusu bir dal değil, karşı tarafın dalının **en son görülmüş hâlinin** kaydıdır.

```text
# öğretilen komut — örnek döküm, çalıştırılmamıştır

git branch -a

* olcum
  remotes/koken/olcum
  remotes/koken/rapor
  remotes/yukari/kimlik
  remotes/yukari/olcum
  remotes/yukari/rapor
```

Yıldız yerel dalı işaretler; kalan beş satır iki uzak deponun altındaki başvurulardır. Aynı
kısa ad birden çok kez görünüyor ve bu bir kusur değil, ad alanının çalıştığının kanıtıdır.

#### Ölçümün Varsayımları

- **UD1** — Üç kopya aynı kurgudan üretilir: `yerel` yalnız `olcum` dalını, `koken` `olcum`
  ile `rapor`u, `yukari` üçünü de taşır. Üçü de aynı tümleştirme biçimini — birleştirme
  işlemesini — kullanır; ölçülen fark biçim değil, **taşınan dal kümesidir**.
- **UD2** — Kopyaların ortak dalları ayrışmaz: bir dal iki kopyada da varsa aynı işlemeleri
  taşır. Ayrışmış kopya sonraki dersin konusudur.
- **UD3** — Tanımlama işlemi `uzaklar` sözlüğüne bir ad–adres çifti yazar ve `tarihce`
  listesine dokunmaz; ölçüm bu iki alanı ayrı ayrı sayar.
- **UD4** — Adresler kurgudur ve ölçüme girmez; ölçüm adresin biçimine değil, tanımın
  **sayısına** bakar.
- **UD5** — Altı soru kursun sabitidir ve burada değişmez. Değişen tek şey soruların
  sorulduğu **kopyadır**.
- **UD6** — Başvuru sayımı yerel dalları ve uzak izleme başvurularını birlikte sayar; kısa ad,
  başvurunun uzak depo öneki atılmış hâlidir.

#### Ölçüm

```python
"""Uzak depo tanımlama: ad bir adrese bağlanır, tarihçe taşınmaz.

Bölüm 1 - üç kopyanın taşıdığı tarihçe ve yanıtlayabildiği soru.
Bölüm 2 - tanımlama işleminin yerel tarihçeye dokunduğu yer.
Bölüm 3 - uzak depo adının açtığı ad alanı.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    """Gerçek gelişim: üç dal, dörder işleme, biri kusuru getiriyor."""
    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


def tarihce(kayit, dallar):
    """Verilen dalları birleştirme işlemesiyle almış bir kopyanın tarihçesi."""
    t = []
    for dal in dallar:
        for k in [x for x in kayit if x["dal"] == dal]:
            t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
        t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                  "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                  "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    return t


def s1(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2(t):
    k = [x for x in t if x["kusurlu"]]
    return len(k) == 1 and not k[0].get("sikisik")


def s3(t):
    return all(x.get("zaman_korundu") for x in t)


def s4(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1), ("kusurun yalıtımı", s2), ("gerçek sıra", s3),
           ("dosya izi", s4), ("dalın bütünlüğü", s5), ("çakışma kararı", s6))

KOPYALAR = {"yerel": ("olcum",), "koken": ("olcum", "rapor"),
            "yukari": ("olcum", "rapor", "kimlik")}


def tanimla(depo, ad, adres):
    """Uzak depo tanımlama: ada bir adres bağlanır, tarihçeye dokunulmaz."""
    depo["uzaklar"][ad] = adres
    return depo


def olc(d):
    return len(d["tarihce"]), sum(f(d["tarihce"]) for _, f in SORULAR)


kayit = gelisim()
print(f"gerçek gelişim {len(kayit)} işleme, {len(DALLAR)} dal; "
      f"kopya {len(KOPYALAR)}")
print()
print("kopya   dal  işleme  yanıtlanan  yanıtlanamayan")
for ad, dallar in KOPYALAR.items():
    t = tarihce(kayit, dallar)
    yok = [soru for soru, f in SORULAR if not f(t)]
    print(f"  {ad:6s} {len(dallar):3d} {len(t):7d} {len(SORULAR) - len(yok):11d}"
          f"  {', '.join(yok)}")

print()
depo = {"tarihce": tarihce(kayit, KOPYALAR["yerel"]), "uzaklar": {}}
once = olc(depo)
tanimla(depo, "koken", "ornek.test/koken")
tanimla(depo, "yukari", "ornek.test/yukari")
sonra = olc(depo)
print(f"tanımlamadan önce yerel: {once[0]} işleme / {once[1]} soru")
print(f"{len(depo['uzaklar'])} tanımdan sonra yerel: {sonra[0]} işleme / {sonra[1]} soru")

print()
basvuru = [("yerel", dal) for dal in KOPYALAR["yerel"]]
basvuru += [(uzak, dal) for uzak in depo["uzaklar"] for dal in KOPYALAR[uzak]]
kisa = [dal for _, dal in basvuru]
paylasilan = sorted(a for a in set(kisa) if kisa.count(a) > 1)
print(f"başvuru {len(basvuru)} | ayrı kısa ad {len(set(kisa))} | "
      f"birden çok başvurunun paylaştığı kısa ad {len(paylasilan)}")
for a in paylasilan:
    print(f"  {a}: " + ", ".join(f"{u}/{d}" for u, d in basvuru if d == a))
```

```
gerçek gelişim 12 işleme, 3 dal; kopya 3

kopya   dal  işleme  yanıtlanan  yanıtlanamayan
  yerel    1       5           4  kusurun yalıtımı, çakışma kararı
  koken    2      10           5  çakışma kararı
  yukari   3      15           5  çakışma kararı

tanımlamadan önce yerel: 5 işleme / 4 soru
2 tanımdan sonra yerel: 5 işleme / 4 soru

başvuru 6 | ayrı kısa ad 3 | birden çok başvurunun paylaştığı kısa ad 2
  olcum: yerel/olcum, koken/olcum, yukari/olcum
  rapor: koken/rapor, yukari/rapor
```

#### Kopyanın Yanıtlayabildiği

Üst tablo aynı soruyu üç ayrı kopyaya soruyor. `yukari` on beş işleme taşıyor ve beş soru
yanıtlıyor; `koken` on işlemeyle aynı beşi yanıtlıyor; `yerel` beş işlemeyle **dört**.

Eksik olan soru **kusurun yalıtımı** ve eksikliğin nedeni burada yeni bir şeydir. Önceki
derslerde bir sorunun yanıtsız kalması, seçilen tümleştirme biçiminin bilgiyi silmesinden
geliyordu: sıkıştırma on iki işlemeyi üçe indiriyor ve kusurlu işlemeyi ayrı okunamaz hâle
getiriyordu. Burada hiçbir şey silinmedi. `yerel` kopya kusuru yalıtamıyor çünkü **kusurlu
işleme onda hiç yok** — kusuru `rapor` dalının ikinci işlemesi getiriyor ve `rapor` dalı bu
kopyada bulunmuyor.

Bu, kursun ölçüsüne ikinci bir kayıp kaynağı ekler. Tarihçe, biçim yüzünden bilgi
kaybedebilir; kopya, **kapsam** yüzünden bilgi kaybedebilir. İkincisi geri alınabilir bir
kayıptır: eksik dallar getirildiğinde soru yeniden yanıtlanır hâle gelir. Biçim kaybı için
aynı şey söylenemez.

`koken` ile `yukari` arasındaki karşılaştırma ikinci bir şey söylüyor. Beş işleme fark var ve
yanıtlanan soru sayısı **aynı**. Taşınan işlemenin artması yanıtlanan soruyu artırmaz;
artıran şey, **yanıtsız kalan sorunun ihtiyaç duyduğu işlemenin** gelmesidir. `kimlik` dalının
dört işlemesi hiçbir sorunun eksik yanıtını tamamlamıyor, çünkü kusur o dalda değil.

Altıncı soru üç kopyada da yanıtsız. Çakışmanın nerede ve nasıl çözüldüğü hiçbir kopyada
yazılı değildir; kopya sayısını artırmak bunu değiştirmez.

#### Tanımın Ödediği

Orta iki satır dersin adını taşıyan işlemi ölçüyor. Tanımlamadan önce yerel kopya **5 işleme
ve 4 soru**; iki uzak depo tanımlandıktan sonra yine **5 işleme ve 4 soru**.

Bu bir yuvarlama değil, tanımın kendisidir. `tanimla` işlevi yalnız `uzaklar` sözlüğüne
dokunuyor; `tarihce` listesine hiç bakmıyor. Aracın davranışı da budur: uzak depo eklemek
yapılandırmaya iki satır yazar, ağa çıkmaz, tek bir nesne getirmez, tek bir başvuru
oluşturmaz. Adres yanlış yazılmışsa bunu tanımlama anında öğrenmezsin — ilk getirme
denemesinde öğrenirsin.

Buradan pratik bir alışkanlık çıkar: bir uzak depo tanımının doğruluğu ancak karşı tarafla
konuşan bir işlemle sınanır. `git remote -v` yalnız ne yazdığını gösterir, `git remote show`
ise karşı tarafa gerçekten bağlanır ve adın altındaki dalları listeler. İkisi ayrı sorulardır.

#### Ad Alanının Ödettiği

Alt tablo altı başvuru sayıyor ve bunların yalnız **üç ayrı kısa adı** var. `olcum` kısa adını
üç başvuru paylaşıyor, `rapor` kısa adını iki başvuru.

Ad alanı olmasaydı bu altı başvuru üç yuvaya sığmak zorunda kalırdı ve kopyalar birbirinin
kaydını ezerdi. Uzak depo adı tam olarak bu yüzden vardır: `koken/olcum` ile `yukari/olcum`
ayrı başvurulardır, ayrı işlemeleri gösterebilirler ve ikisi de yerel `olcum` dalından
bağımsızdır.

Bedeli belirsizliktir. Bir komuta yalın `olcum` verildiğinde araç önce yerel dallara bakar;
başvuru orada bulunduğu için karşı tarafın `olcum`u hiç aranmaz. İki kopyanın `olcum` dalı
ayrışmışsa ve sen yalın adı yazdıysan, gördüğün şey senin dalındır — karşı tarafınki değil.
Bu, ayrışmayı fark etmeyi geciktiren en sık nedendir. Nitelikli ad yazmak (`koken/olcum`) bu
belirsizliği ortadan kaldırır ve iki başvurunun ayrı ayrı sorulmasını sağlar.

Ad alanının bir kez daha kanıtlandığı yer yeniden adlandırmadır. `git remote rename` yalnız
yapılandırmadaki satırı değiştirmez; o adın altındaki bütün uzak izleme başvurularını da yeni
önekle yeniden yazar. Beş başvuru taşıyan bir tanımda beş başvurunun adı değişir ve
işlemelerin hiçbiri kopyalanmaz — çünkü başvuru bir addır, taşıdığı işleme değil.

#### Birden Çok Uzak Depo Ne Zaman Gerekir

Ölçüm iki uzak depoyla çalıştı ve bu sayı keyfî değil. Birden çok tanım, kopyaların **ayrı
roller** taşıdığı her durumda ortaya çıkar.

Birinci rol ayrımı yetkidir. Yazma yetkisinin bulunduğu kopya ile yalnız okunan kopya
farklıysa iki tanım gerekir: değişiklikler birine itilir, güncellemeler ötekinden getirilir.
Ölçümdeki `koken` ve `yukari` tam olarak bu ayrımı taşıyor ve sonraki iki dersin konusu bu
ayrımın açtığı katkı modelidir.

İkinci rol ayrımı eşliktir. Dağıtık modelde bir kopyanın uzak deposu bir sunucu olmak zorunda
değildir; takımdaki başka birinin kopyası da tanımlanabilir. Yarım kalmış bir dalı ortak bir
kopyaya itmeden incelemek gerektiğinde bu doğrudan yol açılır ve ortadaki kopyaya hiç
uğranmaz.

Üçüncü rol ayrımı yedektir. Bir aynanın ayrı adla tanımlanması, aynı dalların iki ayrı
başvuru kümesinde tutulmasını sağlar; iki kümenin ayrışması ancak ikisi de tanımlıyken
görülebilir.

Üç durumda da tanım sayısının artması yerel tarihçeyi büyütmez. Büyüyen tek şey **başvuru ad
alanıdır** ve alt tablonun gösterdiği paylaşılan kısa ad sorunu her yeni tanımla bir kez daha
tekrarlanır. Bu yüzden ad seçimi, tanım sayısı arttıkça daha çok önem kazanır: adlar rolü
söylediğinde bir komutun hangi kopyaya gittiği komutun kendisinden okunur.

#### Özet

- Uzak depo, karşı taraftaki kopya değil, o kopyaya **bu depoda verilen addır**; tanım
  yapılandırmada durur, tarihçede durmaz ve kopyayı alan başkasına geçmez.
- Uzak izleme başvuruları `refs/remotes/<ad>` altında durur, yerelden yazılmaz ve karşı
  tarafın dalının **en son görülmüş hâlini** kaydeder.
- Üç kopya **5**, **10** ve **15** işleme taşır ve **4**, **5**, **5** soru yanıtlar; `yerel`
  kopyanın eksik yanıtı biçim kaybından değil **kapsam kaybından** gelir, çünkü kusurlu işleme
  o kopyada hiç yoktur.
- Taşınan işlemenin artması yanıtlanan soruyu tek başına artırmaz: `koken` ile `yukari`
  arasında beş işleme fark vardır ve ikisi de aynı beş soruyu yanıtlar.
- İki uzak depo tanımı yerel kopyayı **5 işleme ve 4 soruda** bırakır; tanımlama sıfır işleme
  getirir ve adresin doğruluğunu sınamaz.
- Altı başvuru **üç ayrı kısa ad** taşır; ad alanı bu çakışmayı çözer, karşılığında yalın ad
  her zaman yerel dala düşer.

#### Sonraki Adım

Tanım bir addır ve ad tek başına hiçbir işleme getirmiyor. Yerel kopya hâlâ beş işleme
taşıyor, karşı taraf on beş taşıyor ve aradaki fark kapanmış değil. Sonraki ders bu farkı
kapatan üç işlemi ayırır: hangisi yerel tarihçeye hiç dokunmaz, hangisi ona bir birleştirme
işlemesi ekler, hangisi **karşı tarafın** tarihçesini değiştirir. Üçü aynı ağ bağlantısını
kullanır ve üçünün tarihçeye ödettiği bedel aynı değildir.

### Getirme, Çekme ve İtme

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/getirme-cekme-ve-itme

Üç senkronizasyon işleminin yerel tarihçeye dokunduğu yer ayrıdır: getirme yerel dalı 5 işlemede bırakır ve yereldeki nesneyi 15'e çıkarır, çekme dalı 16 işlemeye ve 5 soruya taşır, itme karşı tarafın dalını 10'dan 16'ya değiştirir.

Önceki ders uzak depo tanımının bir addan ibaret olduğunu ölçtü: iki tanım yerel kopyayı beş
işleme ve dört soruda bıraktı, çünkü tanımlama ağa çıkmaz ve tek bir nesne getirmez. Aradaki
fark kapanmadan durmaya devam ediyor — yerel kopya beş işleme taşıyor, karşı taraf on.

Bu farkı kapatan üç işlem var ve üçü sık sık aynı şeyin adları sanılır. Değildirler. Bu dersin
ölçüsü tek bir eksende yürür: **her işlem yerel tarihçeyi ne kadar değiştirir?** Yanıt üç ayrı
sayıdır ve üçüncüsünde değişen tarihçe yerel bile değildir.

#### Üç İşlem, Üç Ayrı Etki

Üçünü bir cümleyle ayırmak mümkündür.

**Getirme (fetch)** karşı tarafın başvurularını ve eksik nesneleri yerele alır. Yerel dalların
hiçbirine dokunmaz, çalışma alanını değiştirmez, birleştirme yapmaz. Tek yazdığı şey uzak
izleme başvurularıdır.

**Çekme (pull)** bileşik bir işlemdir: önce getirir, sonra getirdiğini yerel dalla
**birleştirir**. Birinci adımı getirmenin aynısıdır; farkı ikinci adımdır ve o adım yerel
tarihçeye işleme ekler.

**İtme (push)** yönü tersine çevirir. Yerel dalın işlemelerini karşı tarafa yollar ve karşı
tarafın başvurusunu yerelinkine eşitler. Yerel tarihçeye dokunmaz; **karşı tarafın**
tarihçesini değiştirir. Üç işlem içinde etkisi kendi deposunun dışına çıkan tek işlem budur.

#### Getirme

```text
# öğretilen komutlar — örnek döküm, çalıştırılmamıştır

git fetch koken

From ornek.test/koken
 * [new branch]      rapor      -> koken/rapor
 * [new branch]      kimlik     -> koken/kimlik
   a1b2c3d..d4e5f6a  olcum      -> koken/olcum
```

Çıktının her satırı bir **başvuru güncellemesidir**, bir dal değişikliği değil. Sağdaki adlar
`koken/` önekiyle yazılıdır: güncellenen şey uzak izleme başvurularıdır. Yerel `olcum` dalı bu
komuttan sonra tam olarak eskisi kadar işleme taşır.

Getirme, tek başına hiçbir şeyi bozamayan işlemdir. Karşı tarafta ne olduğunu görmek için
çalışma alanını riske atmaya gerek yoktur: `git fetch` ardından `git log koken/olcum` ile
gelen işlemeler okunur, `git diff olcum koken/olcum` ile fark alınır ve ancak sonrası
kararlaştırılır.

#### Getirme Kuralı ve Budama

Getirmenin neyi alıp nereye yazacağı bir **eşleme kuralından** okunur. Kuralın varsayılan
biçimi `+refs/heads/*:refs/remotes/koken/*` gibi bir çifttir: sol taraf karşı taraftaki
başvuruları, sağ taraf onların yerelde yazılacağı adları verir. Baştaki artı işareti, ileri
sarılabilir olmayan güncellemelerin de yazılmasına izin verir — uzak izleme başvurusu için bu
güvenlidir, çünkü orada senin hiçbir işlemen durmuyor.

Kural daraltılabilir. Tek bir dalın getirilmesi büyük depolarda ölçülebilir zaman kazandırır;
tanımlı uzak depoların hepsinin birden getirilmesi ise `git fetch --all` ile yapılır ve her ad
kendi başvuru kümesine yazar.

Kuralın en sık gözden kaçan sonucu silinme yönündedir. Karşı tarafta bir dal silindiğinde ona
karşılık gelen uzak izleme başvurusu kendiliğinden düşmez; eşleme yalnız var olan başvuruları
yazar, olmayanları silmez. `git fetch --prune` bu temizliği yapar. Budama yapılmayan bir
depoda `git branch -a` çıktısı, karşı tarafta artık bulunmayan dalları göstermeye devam eder
ve bu, uzak izleme başvurusunun bir dal değil **son görülmüş hâlin kaydı** olduğunun en somut
kanıtıdır.

#### Çekme

```text
# öğretilen komutlar — örnek döküm, çalıştırılmamıştır

git pull koken ana
git fetch koken && git merge koken/ana
```

İki satır aynı işi yapar; ikincisi birincinin açılmış hâlidir. Bu açılım, çekmenin neden
"zararsız bir güncelleme" olmadığını gösterir: ikinci adım bir birleştirmedir ve Birleştirme
konusunda ölçülen bütün sonuçlar burada geçerlidir. Birleştirme çakışması çekme sırasında da
doğar, birleştirme işlemesi çekme sırasında da yazılır.

Çekmenin ikinci adımı yapılandırmayla değiştirilebilir ve birleştirme yerine yeniden
temellendirme yapılabilir. Bu, ikinci adımın hangi tümleştirme biçimini uygulayacağını seçmek
demektir; biçimlerin tarihçeye ne bıraktığı Birleştirme konusunda ölçüldü ve burada
tekrarlanmaz. Bu dersin ölçümü çekmenin birleştirmeli biçimini kurar.

#### İtme

```text
# öğretilen komutlar — örnek döküm, çalıştırılmamıştır

git push koken ana

To ornek.test/koken
   d4e5f6a..b7c8d9e  ana -> ana
```

Komuta uzak depo adı ve dal adı her seferinde yazılmak zorunda değildir. `-u` seçeneğiyle
yapılan bir itme, yerel dal ile karşı taraftaki dal arasında bir **izlenen dal** ilişkisi
kurar; sonraki getirme, çekme ve itme çağrıları bu ilişkiyi okur ve adlar yazılmadan çalışır.
İlişki yapılandırmada durur ve `git status` çıktısındaki "şu kadar işleme ileride, şu kadar
geride" satırı da buradan hesaplanır — o satır yerel dalı uzak izleme başvurusuyla
karşılaştırır, karşı tarafla değil.

İtme yalnız **ileri sarılabilir** olduğunda kabul edilir: karşı tarafın dalındaki her işleme
yerel dalda da bulunmalıdır. Koşul sağlanmıyorsa işlem reddedilir ve bu ret bir kusur değil,
kaydın korunmasıdır. Ret, karşı tarafta senin görmediğin işlemeler bulunduğunu söyler.

İtmenin bu koşulu aşan bir biçimi vardır ve **bu ders onu tam biçimiyle yazmaz**. Zorlayıcı
itme, karşı tarafın dalını yerel dalla değiştirir ve karşı tarafta olup yerelde olmayan
işlemeleri başvurulardan düşürür. Düşen işlemeler başka bir kopyada bulunmuyorsa geri
alınamaz; senin deponun referans günlüğü karşı tarafın kaybını kurtarmaz, çünkü o günlük
yereldir. Güvenli yol üç adımdır: getir, gelen işlemeleri oku, uzlaştır ve sıradan itme
yap. Zorlamanın kiralı biçimi — karşı taraf son getirmeden bu yana kımıldadıysa işlemi
reddeden biçimi — riski azaltır ama ortadan kaldırmaz; disiplini İleri Git kursunun
"Zorlayıcı İtme Disiplini" dersi kurar.

#### Ölçümün Varsayımları

- **UD7** — Yerel kopya `olcum` dalını almış bir depodur ve ana dalı **5 işleme** taşır; karşı
  tarafın dalı `rapor` ile `kimlik`i almıştır ve **10 işleme** taşır. İki taraf da birleştirme
  işlemesi biçimini kullanır.
- **UD8** — Getirme yerel dal listesine dokunmaz; yaptığı tek şey uzak izleme başvurusunu
  karşı tarafın dalına eşitlemek ve eksik işlemeleri yerele almaktır.
- **UD9** — Çekme, getirmenin ardından yerel dala **bir** birleştirme işlemesi ekler. Ölçüm
  çekmenin yeniden temellendirmeli biçimini kurmaz.
- **UD10** — İtme yerel dala dokunmaz; karşı tarafın dalını yerel dalın kopyasıyla değiştirir.
  Ölçüm yalnız ileri sarılabilir itmeyi uygular.
- **UD11** — "Yereldeki nesne" sayımı, yerel dalda olsun ya da olmasın yerelde bulunan
  işlemeleri sayar; uzak izleme başvurusunun gösterdiği işlemeler de buraya girer.
- **UD12** — Ayrışma, ortak tabanın dışında her iki tarafta da işleme bulunması demektir;
  ölçüm ayrışmayı iki kopyanın taşıdığı dal kümesini ayırarak kurar.

#### Ölçüm

```python
"""Getirme, çekme ve itme: üç işlemin değiştirdiği tarihçe.

Bölüm 1 - her işlemden sonra yerel dal, yereldeki nesne ve karşı tarafın dalı.
Bölüm 2 - ayrışmış iki kopyada itmenin ileri sarılabilirliği.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    """Gerçek gelişim: üç dal, dörder işleme, biri kusuru getiriyor."""
    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


def tarihce(kayit, dallar):
    """Verilen dalları birleştirme işlemesiyle almış bir dalın tarihçesi."""
    t = []
    for dal in dallar:
        for k in [x for x in kayit if x["dal"] == dal]:
            t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
        t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                  "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                  "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    return t


def s1(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2(t):
    k = [x for x in t if x["kusurlu"]]
    return len(k) == 1 and not k[0].get("sikisik")


def s3(t):
    return all(x.get("zaman_korundu") for x in t)


def s4(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1), ("kusurun yalıtımı", s2), ("gerçek sıra", s3),
           ("dosya izi", s4), ("dalın bütünlüğü", s5), ("çakışma kararı", s6))


def yanit(t):
    return sum(f(t) for _, f in SORULAR)


def getir(y, u):
    """Getirme: uzak izleme başvurusu ve işlemeler gelir, yerel dal kımıldamaz."""
    return {"dal": list(y["dal"]),
            "nesne": y["nesne"] + [x for x in u["dal"] if x not in y["nesne"]],
            "izleme": list(u["dal"])}


def cek(y, u):
    """Çekme: getirme, ardından yerel dala bir birleştirme işlemesi."""
    y = getir(y, u)
    gelen = [x for x in y["izleme"] if x not in y["dal"]]
    if not gelen:
        return y
    birlesme = {"dal": "ana", "sira": 0, "dosya": None, "kusurlu": False,
                "zaman": max(x["zaman"] for x in y["izleme"]),
                "dal_kaydi": "ana", "birlesme": True, "zaman_korundu": True}
    y["dal"] = y["dal"] + gelen + [birlesme]
    y["nesne"] = y["nesne"] + [birlesme]
    return y


def it(y, u):
    """İtme: yerel dal kımıldamaz, karşı tarafın dalı yerelinkiyle değiştirilir."""
    return y, {"dal": list(y["dal"])}


kayit = gelisim()
yerel = {"dal": tarihce(kayit, ("olcum",)), "nesne": tarihce(kayit, ("olcum",)),
         "izleme": []}
uzak = {"dal": tarihce(kayit, ("rapor", "kimlik"))}

print(f"gerçek gelişim {len(kayit)} işleme; yerel dal {len(yerel['dal'])}, "
      f"karşı tarafın dalı {len(uzak['dal'])}")
print()
print("adım      yerel dal  yerel soru  yereldeki nesne  uzak dal  uzak soru")


def yaz(ad, y, u):
    print(f"  {ad:9s} {len(y['dal']):8d} {yanit(y['dal']):11d} {len(y['nesne']):16d}"
          f" {len(u['dal']):9d} {yanit(u['dal']):10d}")


yaz("başlangıç", yerel, uzak)
yaz("getirme", getir(yerel, uzak), uzak)
cekilmis = cek(yerel, uzak)
yaz("çekme", cekilmis, uzak)
itilmis, uzak_son = it(cekilmis, uzak)
yaz("itme", itilmis, uzak_son)

print()
print("kopya çifti    yerel  uzak  ortak taban  yalnız yerelde  yalnız uzakta  "
      "ileri sarma")
CIFTLER = {"ayrışmamış": (cekilmis["dal"], uzak["dal"]),
           "ayrışmış": (tarihce(kayit, ("olcum", "rapor")),
                        tarihce(kayit, ("olcum", "kimlik")))}
for ad, (yd, ud) in CIFTLER.items():
    ortak = [x for x in yd if x in ud]
    print(f"  {ad:12s} {len(yd):6d} {len(ud):5d} {len(ortak):12d}"
          f" {len(yd) - len(ortak):15d} {len(ud) - len(ortak):14d}"
          f"  {'evet' if all(x in yd for x in ud) else 'hayır':>10s}")

print()
for ad, (yd, ud) in CIFTLER.items():
    dusecek = [x for x in ud if x not in yd]
    print(f"  {ad} çiftte zorlayıcı itme karşı taraftan {len(dusecek)} işleme düşürür")
```

```
gerçek gelişim 12 işleme; yerel dal 5, karşı tarafın dalı 10

adım      yerel dal  yerel soru  yereldeki nesne  uzak dal  uzak soru
  başlangıç        5           4                5        10          5
  getirme          5           4               15        10          5
  çekme           16           5               16        10          5
  itme            16           5               16        16          5

kopya çifti    yerel  uzak  ortak taban  yalnız yerelde  yalnız uzakta  ileri sarma
  ayrışmamış       16    10           10               6              0        evet
  ayrışmış         10    10            5               5              5       hayır

  ayrışmamış çiftte zorlayıcı itme karşı taraftan 0 işleme düşürür
  ayrışmış çiftte zorlayıcı itme karşı taraftan 5 işleme düşürür
```

#### Üç İşlemin Ödettiği

Üst tablo dört satırda üç işlemi ayırıyor ve satırlar arasındaki fark her seferinde tek bir
sütunda beliriyor.

**Getirme satırı** yerel dalı **5**'te bırakıyor ve yanıtlanan soru **4**'te kalıyor. Değişen
tek sütun "yereldeki nesne": **5**'ten **15**'e çıkıyor. Bu on beş işleme yerelde duruyor,
diskte bulunuyor, okunabiliyor — ama hiçbiri yerel dalın tarihçesinde değil. Getirmenin
tanımı budur: **erişilebilirliği artırır, tarihçeyi değiştirmez.** Yanıtsız kalan "kusurun
yalıtımı" sorusu getirmeden sonra da yanıtsız, çünkü soru yerel dala soruluyor ve yerel dal
kımıldamadı. Kusurlu işleme artık yerelde bulunuyor; yalnız `koken/rapor` başvurusundan
sorulduğunda görünüyor.

**Çekme satırı** yerel dalı **5**'ten **16**'ya taşıyor ve yanıtlanan soruyu **4**'ten
**5**'e çıkarıyor. On altı sayısı bileşiktir: yerel dalın kendi 5 işlemesi, karşı taraftan
gelen 10 işleme ve çekmenin kendi yazdığı **1** birleştirme işlemesi. On ikilik gerçek
gelişimin üstünde dört birleştirme işlemesi birikmiş oluyor — üçü dalları ana dala alan
birleşmeler, dördüncüsü iki kopyayı uzlaştıran birleşme.

Kazanılan soru "kusurun yalıtımı". Bir önceki ders bu eksikliğin **kapsam kaybından**
geldiğini ölçmüştü; çekme kapsamı kapatıyor ve soru yanıtlanır hâle geliyor. Bu, kapsam
kaybının geri alınabilir olduğunun doğrudan kanıtıdır.

**İtme satırında** yerel sütunların üçü de kımıldamıyor: **16**, **5**, **16**. Değişen sütun
karşı tarafın dalı — **10**'dan **16**'ya çıkıyor. İtmenin ölçüsü kendi deposunda değil,
ötekinde okunur ve bu üç işlem içinde yalnız itme için doğrudur.

Karşı tarafın yanıtladığı soru **5**'ten **5**'e gidiyor; sayı değişmiyor ama taşınan işleme
altı artıyor. Bu, önceki dersin bulgusunun tersten okunuşudur: işleme sayısının artması
yanıtlanan soruyu tek başına artırmaz. Karşı taraf zaten kusurlu işlemeyi taşıyordu.

#### Ayrışma ve İtmenin Sınırı

Alt tablo iki kopya çiftini yan yana koyuyor. **Ayrışmamış** çiftte karşı tarafın on
işlemesinin tamamı yerelde de var: ortak taban **10**, yalnız uzakta kalan **0**. İtme ileri
sarmadır ve hiçbir şey düşmez.

**Ayrışmış** çiftte ortak taban **5**'e iniyor. Yerelde beş işleme var karşı tarafta olmayan,
karşı tarafta beş işleme var yerelde olmayan. İleri sarma koşulu sağlanmıyor ve sıradan itme
reddediliyor. Son satır reddin neyi koruduğunu söylüyor: zorlayıcı itme uygulansaydı karşı
taraftan **5** işleme düşecekti — gerçek gelişimin yaklaşık **5/12**'si ve o beşi yazan kişi
bunu yalnız kendi kopyası kımıldadığında fark ederdi.

İki sayı arasındaki fark, itmenin ret davranışının neden bir güvenlik özelliği olduğunu
gösteriyor. Ret bir hata iletisi değil, **ayrışmanın bildirimidir**. Doğru yanıt itmeyi
zorlamak değil, ayrışmayı kapatmaktır: getirme ayrışmayı görünür kılar, çekme ya da yeniden
temellendirme onu tümleştirir, sonraki itme ileri sarma olur ve düşen işleme sıfıra iner.

#### Özet

- Getirme yerel dalı **5** işlemede bırakır ve yereldeki işlemeyi **15**'e çıkarır; yanıtlanan
  soru **4**'te kalır çünkü soru yerel dala sorulur ve yerel dal kımıldamaz.
- Çekme getirmenin üstüne bir birleştirme işlemesi ekler: yerel dal **16**'ya, yanıtlanan soru
  **5**'e çıkar; on altının biri çekmenin kendi yazdığı işlemedir.
- Kapsam kaybı geri alınabilir bir kayıptır: eksik dal geldiğinde "kusurun yalıtımı" sorusu
  yeniden yanıtlanır hâle gelir.
- İtme yerel tarihçeye dokunmaz ve karşı tarafın dalını **10**'dan **16**'ya taşır; ölçüsü
  kendi deposunda değil, ötekinde okunur.
- İtme yalnız ileri sarılabilir olduğunda kabul edilir; ayrışmış çiftte ortak taban **5**'e
  iner ve zorlayıcı itme karşı taraftan **5** işleme düşürür, geri alınamaz biçimde.

#### Sonraki Adım

Bu dersin üç işlemi tek bir varsayımla çalıştı: itme yetkisi vardı. Yerel kopya karşı tarafın
dalını değiştirebiliyordu ve tek engel ileri sarılabilirlik koşuluydu. Katkı akışlarının
büyük bölümünde bu varsayım geçersizdir — katkıyı alan depoya yazma yetkisi yoktur. Sonraki
ders bu durumda açılan iki modeli karşılaştırır: kopyayı doğrudan klonlayıp dal itmek ile
ayrı bir kopya açıp oradan öneri götürmek. Ölçü, iki modelde **yukarı deponun** hangi
soruları yanıtlayabildiğidir.

### Çatallama ve Klonlama

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/catallama-ve-klonlama

İki katkı modelinde yukarı deponun yanıtladığı soru tümleştirme biçimine bağlıdır: 15 işlemeyle 5, 12 işlemeyle 3, 3 işlemeyle 2 soru yanıtlanır ve çatal deposu 12 işlemeyle 5 soru yanıtladığı için üç soru yalnız yukarı deponun sahip olmadığı bir kopyada durur.

Önceki dersin üç işlemi tek bir varsayımla çalıştı: itme yetkisi vardı. Yerel kopya karşı
tarafın dalını değiştirebiliyordu ve tek engel ileri sarılabilirlik koşuluydu. Katkı
akışlarının büyük bölümünde bu varsayım geçersizdir. Katkıyı alan depoya yazma yetkisi yoktur
ve olmaması bir kısıt değil, sahiplik tanımının kendisidir.

Yetki yoksa katkı nasıl gider? İki yanıt vardır ve ikisi aynı şeyin adları değildir. Bu dersin
ölçüsü şudur: iki modelde de aynı on iki işleme üretilir, ama **yukarı deponun** sonunda
elinde kalan tarihçe aynı değildir — ve fark, hangi soruların nerede yanıtlanabildiğini
belirler.

#### Klonlama

**Klonlama (clone)**, bir kopyanın tamamını yeni bir yere almaktır: bütün işlemeler, bütün
dallar ve bütün etiketler kopyalanır. Kopya eksiksizdir; klonlanan depo silinse bile klon
kendi başına tam bir depodur.

```text
# öğretilen komutlar — örnek döküm, çalıştırılmamıştır

git clone https://ornek.test/kaynak/olcum-aglari.git
cd olcum-aglari
git remote -v

origin  https://ornek.test/kaynak/olcum-aglari.git (fetch)
origin  https://ornek.test/kaynak/olcum-aglari.git (push)
```

Klonlama üç şeyi birden yapar: kopyayı alır, kaynağa `origin` adıyla bir uzak depo tanımı
yazar ve varsayılan dalı bu tanıma bağlı olarak kurar. Önceki iki dersin elle yaptığı işleri
tek komutta toplar; bu yüzden tanımın nereden geldiği çoğu zaman fark edilmez.

Klonlamaya dayanan katkı modeli doğrudandır: katkıcı yukarı depoyu klonlar, dalını açar,
işlemelerini yapar ve **dalı doğrudan yukarı depoya iter**. Bu model yalnız katkıcının yukarı
depoda itme yetkisi varsa çalışır — yani takım içi katkının olağan biçimidir.

#### Klonun Kapsamı

Klonlamanın eksiksiz olması bir varsayılandır, bir zorunluluk değil. Seçenekler kopyanın
kapsamını daraltır ve daraltmanın bedeli ölçünün diliyle okunabilir.

Derinlik sınırı verilen bir klon yalnız son işlemeleri alır; öncesi hiç gelmez. Tek dala
sınırlanan bir klon öbür dalların başvurularını hiç yazmaz. İki durumda da tarihçe
**değiştirilmemiştir** — kaynakta olduğu gibi durur; alınmayan yalnız kapsamdır. Birinci
dersin ayrımı burada tam olarak geçerlidir: bu bir biçim kaybı değil, kapsam kaybıdır ve
kapsam kaybı geri alınabilir. Eksik derinlik sonradan tamamlanabilir, eksik dal sonradan
getirilebilir.

Ters yöndeki seçenekler de vardır. Çalışma alanı olmadan alınan bir klon yalnız depo verisini
taşır ve sunucu tarafında tutulan kopyaların olağan biçimi budur; başvuruların tamamını birebir
kopyalayan bir klon ise uzak izleme başvuruları dâhil her şeyi taşır ve yedek almanın en
doğrudan yoludur.

Seçim, kopyanın ne için alındığına bağlıdır. Sürekli tümleştirme için alınan bir kopyanın on
yıllık tarihçeye ihtiyacı yoktur; bir kusurun hangi işlemede girdiğini arayan bir kopyanın ise
tam tarihçeye ihtiyacı vardır ve sığ bir klonda o arama yapılamaz. Kapsamı daraltmak zaman
kazandırır ve kazandığı zamanı, ileride sorulacak bir soruyla öder.

#### Depo Çatallaması

**Depo çatallaması (fork)**, yukarı deponun sunucu tarafında alınmış ve katkıcıya ait olan bir
kopyasıdır. Terim, İşletim Sistemi Kavramları kursunda tanımlanan süreç çatallamasıyla aynı
sözcüğü paylaşır ve **ayrı bir kavramdır**: orada bir sürecin ikiye ayrılması, burada bir
deponun ikinci bir sahiplik alanında kopyalanması. Bu yetenek depo platformlarının sağladığı
bir sınıf yeteneğidir; ürün adı bu derste geçmez.

Çatalın çözdüğü sorun yetkidir. Katkıcı çatal deposunda tam yetkiye sahiptir: istediği dalı
açar, istediği kadar iter, istediğini siler. Yukarı depoda ise hiçbir yazma yetkisi yoktur.

```text
# öğretilen komutlar — örnek döküm, çalıştırılmamıştır

git clone https://ornek.test/katkici/olcum-aglari.git
cd olcum-aglari
git remote add yukari https://ornek.test/kaynak/olcum-aglari.git
git fetch yukari
git remote -v

origin  https://ornek.test/katkici/olcum-aglari.git (fetch)
origin  https://ornek.test/katkici/olcum-aglari.git (push)
yukari  https://ornek.test/kaynak/olcum-aglari.git (fetch)
yukari  https://ornek.test/kaynak/olcum-aglari.git (push)
```

Çatal modelinde yerel depo **iki uzak depo** tanımı taşır: çatalın kendisi ve yukarı depo.
Bu, birinci dersin ölçtüğü ad alanı sorununu doğrudan doğuran yapıdır — `origin/ana` ile
`yukari/ana` ayrı başvurulardır ve çatal, yukarı depo ilerledikçe geride kalır. Çatalın
güncel tutulması, yukarı depodan getirip çatala itmekten ibarettir ve bu iş katkıcıya aittir;
kendiliğinden olmaz.

Güncelleme ihmal edildiğinde önceki dersin ölçtüğü ayrışma tablosu doğrudan devreye girer.
Çatalın ana dalı ile yukarı deponun ana dalı ayrıştıkça ortak taban geriler ve katkı dalı,
yukarı depoda artık bulunmayan bir tabana yaslanmış olur. Bu, birleştirme çakışmasının en
öngörülebilir kaynağıdır: çakışma katkının içeriğinden değil, tabanın eskimesinden doğar.

#### Ölçümün Varsayımları

- **UD13** — Aynı on iki işlemelik gelişim iki modelde de aynı biçimde üretilir; modeller
  arasındaki fark üretimde değil, işlemelerin **hangi kopyada durduğundadır**.
- **UD14** — Klonlama modelinde katkıcı dalları doğrudan yukarı depoya iter; ayrı bir kopya
  yoktur ve ölçümde çatal sütunu **0** yazılır.
- **UD15** — Çatal deposu dalları geliştirildikleri gibi taşır: dal kaydı yerinde, zaman
  korunmuş, sıkıştırma uygulanmamış. Bu, katkıcının kendi kopyasında gördüğü hâldir.
- **UD16** — Yukarı depoya geçiş dört tümleştirme biçiminden biriyle yapılır ve biçim ölçüme
  girer; biçimlerin tanımı Birleştirme konusundaki tanımın aynısıdır ve burada değiştirilmez.
- **UD17** — "Yalnız çatalda" sütunu, çatal deposunun yanıtladığı sorulardan yukarı deponun
  yanıtlayamadıklarını sayar; klonlama satırında ayrı kopya bulunmadığı için bu küme boştur.
- **UD18** — Üçüncü tablo yapısaldır ve tarihçeden değil, iki modelin tanımından okunur:
  yukarı depodaki katkı dalı sayısı, katkıcının tanımlaması gereken uzak depo sayısı ve itme
  yetkisinin hangi depoda bulunduğu.

#### Ölçüm

```python
"""İki katkı modeli: klonlama ve depo çatallaması.

Bölüm 1 - yukarı deponun taşıdığı tarihçe ve yanıtladığı soru.
Bölüm 2 - yalnızca çatalda yanıtlanabilen sorular.
Bölüm 3 - iki modelin yapısal farkı: başvuru ve tanım sayısı.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    """Gerçek gelişim: üç dal, dörder işleme, biri kusuru getiriyor."""
    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


def tumlestir(kayit, bicim):
    """Her tümleştirme biçimi ayrı bir tarihçe üretir."""
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    elif bicim == "yeniden temellendirme":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": None, "zaman_korundu": False})
    elif bicim == "sikistirma":
        for dal in DALLAR:
            grup = [x for x in kayit if x["dal"] == dal]
            t.append({"dal": dal, "sira": 0,
                      "dosya": sorted({x["dosya"] for x in grup}),
                      "kusurlu": any(x["kusurlu"] for x in grup),
                      "zaman": max(x["zaman"] for x in grup),
                      "dal_kaydi": dal, "zaman_korundu": False, "sikisik": len(grup)})
    elif bicim == "ileri sarma":
        for k in sorted(kayit, key=lambda x: x["zaman"]):
            t.append({**k, "dal_kaydi": None, "zaman_korundu": True})
    return t


def catal(kayit):
    """Çatal deposunun taşıdığı hâl: dallar geliştirildikleri gibi durur."""
    return [{**k, "dal_kaydi": k["dal"], "zaman_korundu": True}
            for dal in DALLAR for k in kayit if k["dal"] == dal]


def s1(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2(t):
    k = [x for x in t if x["kusurlu"]]
    return len(k) == 1 and not k[0].get("sikisik")


def s3(t):
    return all(x.get("zaman_korundu") for x in t)


def s4(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1), ("kusurun yalıtımı", s2), ("gerçek sıra", s3),
           ("dosya izi", s4), ("dalın bütünlüğü", s5), ("çakışma kararı", s6))


def yanitlanan(t):
    return {ad for ad, f in SORULAR if f(t)}


MODELLER = (("klonlama", False, "birlestirme islemesi"),
            ("çatallama", True, "birlestirme islemesi"),
            ("çatallama", True, "yeniden temellendirme"),
            ("çatallama", True, "sikistirma"),
            ("çatallama", True, "ileri sarma"))

kayit = gelisim()
c = catal(kayit)
print(f"gerçek gelişim {len(kayit)} işleme; çatal deposu {len(c)} işleme taşır ve "
      f"{len(yanitlanan(c))} soru yanıtlar")
print()
print("model      tümleştirme biçimi      çatal  yukarı  yukarının yanıtı  "
      "yalnız çatalda  yukarıda kaybolan")
for model, ayri, bicim in MODELLER:
    t = tumlestir(kayit, bicim)
    yukari = yanitlanan(t)
    ayrik = yanitlanan(c) - yukari if ayri else set()
    okunur = sum(1 for x in t if not x.get("birlesme") and not x.get("sikisik"))
    print(f"  {model:9s} {bicim:22s} {len(c) if ayri else 0:5d} {len(t):7d}"
          f" {len(yukari):17d} {len(ayrik):15d} {len(kayit) - okunur:18d}")

print()
for model, ayri, bicim in MODELLER:
    if not ayri:
        continue
    ayrik = sorted(yanitlanan(c) - yanitlanan(tumlestir(kayit, bicim)))
    if ayrik:
        print(f"  {bicim:22s} yalnız çatalda: {', '.join(ayrik)}")

print()
print("model      yukarıdaki katkı dalı  katkıcının uzak depo tanımı  itme yetkisi")
print(f"  {'klonlama':9s} {len(DALLAR):21d} {1:28d}  yukarı depo")
print(f"  {'çatallama':9s} {0:21d} {2:28d}  çatal")
```

```
gerçek gelişim 12 işleme; çatal deposu 12 işleme taşır ve 5 soru yanıtlar

model      tümleştirme biçimi      çatal  yukarı  yukarının yanıtı  yalnız çatalda  yukarıda kaybolan
  klonlama  birlestirme islemesi       0      15                 5               0                  0
  çatallama birlestirme islemesi      12      15                 5               0                  0
  çatallama yeniden temellendirme     12      12                 3               2                  0
  çatallama sikistirma                12       3                 2               3                 12
  çatallama ileri sarma               12      12                 3               2                  0

  yeniden temellendirme  yalnız çatalda: gerçek sıra, özellik gruplaması
  sikistirma             yalnız çatalda: dosya izi, gerçek sıra, kusurun yalıtımı
  ileri sarma            yalnız çatalda: dalın bütünlüğü, özellik gruplaması

model      yukarıdaki katkı dalı  katkıcının uzak depo tanımı  itme yetkisi
  klonlama                      3                            1  yukarı depo
  çatallama                     0                            2  çatal
```

#### Yukarı Deponun Yanıtlayabildiği

İlk iki satır aynı sayıyı veriyor: klonlama modeli de çatallama modeli de, tümleştirme
birleştirme işlemesiyle yapıldığında yukarı depoya **15 işleme** ve **5 soru** bırakıyor.

Bu sonuç dersin ilk düzeltmesidir. **Çatallama tek başına yukarı deponun bilgisini
azaltmaz.** Katkının ayrı bir kopyadan gelmiş olması tarihçeye hiçbir iz bırakmaz; yukarı
depoya giren şey işlemelerin kendisidir ve o işlemeler çatalda da yukarıda da aynı
işlemelerdir. İki modeli ayıran şey yetkidir, kayıt değil.

Kalan üç satır asıl değişkeni gösteriyor. Yukarı deponun yanıtladığı soru **5**'ten **3**'e ve
**2**'ye iniyor ve inişin nedeni her seferinde aynı: **seçilen tümleştirme biçimi**.
Birleştirme konusunun ölçtüğü sayılar burada bire bir tekrar ediyor, çünkü ölçülen şey aynı
tarihçedir; değişen tek şey tarihçenin hangi depoda durduğudur.

Sıkıştırma satırı ayrı durur. Yukarı depo **3** işleme taşıyor, **2** soru yanıtlıyor ve
**yukarıda kaybolan** sütunu **12** yazıyor: gerçek gelişimin tamamı yukarı depoda ayrı
okunabilir olmaktan çıkmış. Öbür üç satırda bu sütun **0**'dır — biçimler bilgi kaybettirse
bile işlemeyi ayrı ayrı okunabilir bırakırlar.

#### Yalnız Çatalda Duran Yanıt

İkinci tablo dersin asıl bulgusudur. Çatal deposu on iki işlemeyi geliştirildikleri gibi
taşıyor ve **5** soru yanıtlıyor. Yukarı depo, tümleştirme biçimine göre **5**, **3** ya da
**2** yanıtlıyor. Aradaki fark boşlukta durmuyor: o sorular **yanıtlanabilir durumda**, ama
yanıtı yukarı depoda değil, çatalda.

Sıkıştırma satırında bu fark üçe çıkıyor. "Kusurun yalıtımı", "gerçek sıra" ve "dosya izi"
soruları çatalın on iki işlemesine sorulduğunda yanıtlanıyor; yukarı deponun üç işlemesine
sorulduğunda yanıtlanmıyor. Yeniden temellendirme ve ileri sarma satırlarında fark ikidir ve
farklı sorulardır — ilki "özellik gruplaması" ile "gerçek sıra"yı, ikincisi "özellik
gruplaması" ile "dalın bütünlüğü"nü çatalda bırakır.

Buradan çıkan sonuç bir uyarıdır. Yukarı deponun yanıtlayamadığı bir soru, **sahibi olmadığı
bir kopyada** yanıtlanıyor olabilir. Çatal deposu katkıcıya aittir: silinebilir, yeniden
adlandırılabilir, erişimi kapatılabilir. Bunların hiçbiri yukarı depoya haber vermez ve
hiçbiri yukarı deponun kaydını bozmaz — yalnız, eksik yanıtın bulunabileceği yeri ortadan
kaldırır.

Bu, birinci derste ölçülen kapsam kaybının kalıcı hâle gelen biçimidir. Orada eksik dallar
getirilebiliyordu ve kayıp geri alınabilirdi. Burada eksik olan şey yukarı depoya hiç
girmemiştir ve girebileceği tek yer kapanabilir.

#### Yapısal Fark

Üçüncü tablo iki modelin tarihçeden okunmayan farkını veriyor.

Klonlama modelinde katkıcının üç dalı **yukarı depoda** durur. Bu, dal adlarının yukarı
deponun ad alanına girmesi demektir: adlandırma düzeni yukarı depoda uygulanmak zorundadır,
terk edilmiş dallar yukarı depoda birikir ve temizlik yukarı deponun işidir. Katkıcı tek bir
uzak depo tanımı taşır ve itme yetkisi doğrudan yukarı depodadır.

Çatallama modelinde yukarı depoda katkı dalı **hiç bulunmaz**. Dallar çatalda açılır, çatalda
birikir ve çatalda silinir; yukarı depoya yalnız tümleştirmenin sonucu geçer. Katkıcı **iki**
uzak depo tanımı taşımak zorundadır ve bunun bedeli birinci derste ölçülmüştü: paylaşılan kısa
adlar ve yalın adın her zaman yerele düşmesi.

İki modelin seçimi bu yüzden bir tarihçe kararı değil, bir **sahiplik kararıdır**. Yukarı depo
kimin yazabileceğine karar verir; tarihçenin ne kaydedeceğine ise tümleştirme biçimi karar
verir. İkisi bağımsızdır ve karıştırılmaları, çatallamanın kendisine bilgi kaybı yüklemek gibi
bir yanlış okumaya yol açar.

#### Özet

- Klonlama bir kopyanın tamamını alır ve kaynağa bir uzak depo tanımı yazar; depo çatallaması
  ise yukarı deponun katkıcıya ait ikinci bir kopyasıdır ve İşletim Sistemi Kavramları
  kursunda tanımlanan süreç çatallamasından ayrı bir kavramdır.
- İki model de birleştirme işlemesi biçimiyle yukarı depoya **15 işleme** ve **5 soru**
  bırakır: çatallama tek başına yukarı deponun bilgisini azaltmaz.
- Yukarı deponun yanıtladığı soruyu belirleyen şey tümleştirme biçimidir: **15/5**, **12/3**,
  **3/2**, **12/3**; sıkıştırmada gerçek gelişimin **12** işlemesi yukarıda ayrı okunabilir
  olmaktan çıkar.
- Çatal deposu **12 işleme** taşır ve **5** soru yanıtlar; sıkıştırmada **3** soru yalnız
  çatalda yanıtlanabilir kalır ve çatal yukarı deponun sahip olmadığı bir kopyadır.
- Yapısal fark tarihçeden okunmaz: klonlama modelinde katkı dalları yukarı depoda durur ve
  katkıcı bir tanım taşır; çatallama modelinde yukarıda katkı dalı bulunmaz ve katkıcı iki
  tanım taşır.

#### Sonraki Adım

Çatal modelinde katkıcının yukarı depoya itme yetkisi yok; buna rağmen on iki işleme bir
biçimde yukarıya geçti. Geçişi yapan şey bir komut değil, bir **akıştır**: öneri sunulur,
üzerinde konuşulur, sonunda biri onu ana dala alır. Sonraki ders bu döngünün mekaniğini kurar
ve akışın seçtiği tümleştirme biçiminin tarihçeye kaç soru bıraktığını ölçer. Ölçüm ayrıca
akış sırasında alınan kararlardan kaçının tarihçede iz bıraktığını sayar; yanıt beklenenden
küçüktür.

### Değişiklik İsteği Akışı

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/degisiklik-istegi-akisi

Akışın seçtiği tümleştirme biçimi tarihçeye 5, 3, 2 ve 3 soru bırakır; üç istekte alınan 11 kararın 4'ü işleme mesajına yazılır, 7'si hiçbir biçimde tarihçeye girmez ve sıkıştırmada yazılanların da hepsi ayrı okunabilir olmaktan çıkar.

Önceki ders çatal modelinde katkıcının yukarı depoya itme yetkisi bulunmadığını ölçtü. Buna
rağmen on iki işleme bir biçimde yukarıya geçti. Geçişi yapan şey bir komut değildir: yetkisi
olmayan biri hiçbir komutla karşı tarafın dalını değiştiremez. Geçişi yapan şey bir **akıştır**
ve akışın son adımını yetkisi olan biri çalıştırır.

Bu ders akışın mekaniğini kurar: öneri nasıl sunulur, döngü nasıl döner, tümleştirme kararı
nerede verilir. **İnceleme ekseninin kendisi — bir değişikliğin anlam, gerçekleştirim, test ve
belge açısından nasıl incelendiği — ve inceleme kültürü bu dersin konusu değildir**; bunlar
Kod İnceleme ve Ekip Süreci kursunda kurulur. Burada ölçülen tek şey, akışın tarihçeye ne
bıraktığıdır.

#### Akışın Döngüsü

**Değişiklik isteği (pull request)**, bir dalın başka bir dala alınması için yapılan biçimsel
öneridir. Depo platformlarının sağladığı bir sınıf yeteneğidir; ürün adı bu derste geçmez ve
akışın mekaniği ürüne bağlı değildir.

Döngü dört adımdır. **Öneri:** katkıcı dalını itki noktasına iter ve isteği açar; istek iki
uç adı taşır — kaynak dal ve hedef dal. **Tartışma:** öneri okunur, sorular sorulur, kararlar
alınır. **Düzeltme:** katkıcı dalını değiştirir ve yeniden iter; istek kendiliğinden
güncellenir, çünkü istek bir dosya kopyası değil, **iki dalın adına yapılmış bir göndermedir**.
**Tümleştirme:** yetkili biri isteği kapatır ve dalı hedefe alır.

Döngünün iki adımı arasına otomatik denetimler girebilir: öneri her itildiğinde bir hat
çalışır ve sonucu isteğe iliştirir. Hattın kendisi bu kursun konusu değildir ve Sürekli
Tümleştirme ve Teslimat kursunda kurulur; buradan görülen tek şey, denetim sonucunun da
tartışma alanında durduğu ve tarihçeye girmediğidir.

Dördüncü adımın bir seçim taşıdığı çoğu zaman gözden kaçar. Tümleştirmeyi yapan kişi, dört
tümleştirme biçiminden birini seçer ve bu seçim tarihçeye ne kalacağını belirler. Akışın
kendisi tarafsız değildir: hangi biçimin varsayılan olduğu, deponun aylar sonra hangi soruları
yanıtlayabileceğini belirler.

#### Öneri Bir İletidir

İsteğin bir depo platformu olmadan da karşılığı vardır ve bu karşılık akışın ne olduğunu
doğrudan gösterir.

```text
# öğretilen komut — örnek döküm, çalıştırılmamıştır

git request-pull yukari/ana https://ornek.test/katkici/olcum-aglari.git olcum

The following changes since commit a1b2c3d:
  Ana dala ölçüm dizinini ekle
are available in the Git repository at:
  https://ornek.test/katkici/olcum-aglari.git olcum
for you to fetch changes up to d4e5f6a:
  Ölçüm eşiğini yapılandırmadan oku

 olcum.py |  22 ++++++++
 ayar.py  |   6 +--
 2 files changed, 25 insertions(+), 3 deletions(-)
```

Çıktı üç şey taşıyor: ortak tabanın adı, katkının bulunduğu adres ve dal ve dal ucunun adı.
İçerik taşımıyor. İstek bir kopya değil, **nereden getirileceğinin tarifidir**; alan taraf
getirir, inceler ve karar verir. Depo platformlarının sunduğu istek de aynı şeydir, üstüne bir
tartışma alanı eklenmiştir.

#### Tartışma ve Düzeltme

Tartışmadan doğan düzeltmenin iki yolu vardır ve tarihçeye etkileri ayrıdır.

Birinci yol dala **yeni işleme eklemektir**. Düzeltme ayrı bir işleme olur, ayrı bir mesaj
taşır ve tarihçe döngünün döndüğünü kaydeder. Ölçümün "işleme mesajına yazılan karar" sütunu
bu yolu sayar.

İkinci yol dalı **yeniden yazmaktır**: düzeltme, kusuru getiren işlemenin içine sokulur ve dal
yeniden itilir. Katkı dalı yalnız katkıcıya ait olduğu için bu, paylaşılan dalda yeniden
temellendirmenin taşıdığı riski taşımaz — ama tarihçe artık düzeltmenin yapıldığını değil,
yalnız son hâli gösterir. Bu ders zorlayıcı itmenin tam biçimini yazmaz; yazılması gereken tek
şey şudur: karşı taraftaki dal ezilir ve ezilen hâl başka bir kopyada bulunmuyorsa geri
alınamaz. Katkı dalında bu bilinçli bir tercihtir, hedef dalda değildir.

İki yol arasındaki seçim bir okunaklılık–yanıtlanabilirlik ödünleşimidir ve Birleştirme
konusunun ikinci iddiasının akış içindeki karşılığıdır: temiz görünen dal, döngünün kaydını
taşımayan daldır.

#### Hedef Dal İlerlerken

Tartışma sürerken hedef dal durmaz. Başka istekler tümleştirilir ve önerinin yaslandığı taban
geriler. İsteğin gösterdiği fark bu yüzden iki ayrı biçimde hesaplanabilir.

Birinci biçim, iki dal ucunu doğrudan karşılaştırır ve hedef dalda bu arada olan her şeyi de
farka katar. İkinci biçim, iki dalın **ortak tabanını** bulur ve katkı dalını yalnız o
tabandan bugüne karşılaştırır; böylece farkta yalnız katkıcının yaptığı iş görünür. Akışların
büyük bölümü ikincisini gösterir ve bu, üçüncü dersteki eskiyen taban gözleminin doğrudan
sonucudur: taban gerilemiş olsa bile öneri okunur kalır.

İkisi arasındaki seçim çakışma anında görünür hâle gelir. Ortak tabana göre temiz duran bir
öneri, hedef dalın son hâliyle çakışabilir; akış bunu ancak tümleştirmeyi denediğinde
öğrenir. Bu yüzden birçok akış, tümleştirmeden önce katkı dalının **güncel tabana taşınmasını**
ister ve bu istek döngüye fazladan bir tur ekler.

İsteğin kapanışı da tek biçimli değildir. Tümleştirilerek kapanan bir istek tarihçeye iz
bırakır; tümleştirilmeden kapanan bir istek hiçbir iz bırakmaz. İkinci durumda öneri, tartışma
ve alınan kararların tamamı yalnız akışın kendi kaydında kalır — depoda hiçbir karşılığı
yoktur. Ölçüm bu dersi yalnız kapanan ve tümleştirilen istekler üzerinden kurar; kapatılan
isteklerin kaybı tanım gereği tamdır.

#### Ölçümün Varsayımları

- **UD19** — Üç dalın her biri bir değişiklik isteğidir ve dörder işleme önerir; gelişim
  kurgusu değişmez, üstüne bir akış katmanı eklenir.
- **UD20** — Tartışma turu ve alınan karar sayısı kurgudan üretilir; kâhin biziz, çünkü akışı
  biz kurduk. Kararın içeriği ölçüme girmez, yalnız **nereye yazıldığı** girer.
- **UD21** — Bir karar ancak bir işleme mesajına yazıldıysa tarihçeye girer. Tartışma alanının
  kendisi deponun dışındadır ve ölçüm onu tarihçe saymaz.
- **UD22** — Yazılmış bir karar, yazıldığı işleme tümleştirmeden sonra **ayrı okunabiliyorsa**
  okunur kalır; sıkıştırılmış bir grupta ayrı okunabilirlik yoktur.
- **UD23** — "Gönderme yeri", akışın istek numarasını yazabileceği işlemedir: birleştirme
  işlemesi ya da sıkıştırma işlemesi. Yeniden temellendirme ve ileri sarma böyle bir işleme
  üretmez.
- **UD24** — Dört tümleştirme biçiminin tanımı Birleştirme konusundaki tanımın aynısıdır ve
  burada değiştirilmez; değişen tek şey, biçimi seçenin akış olmasıdır.

#### Ölçüm

```python
"""Değişiklik isteği akışı: döngüden tarihçeye ne geçiyor.

Bölüm 1 - üç istek, tartışma turu ve alınan karar.
Bölüm 2 - akışın seçtiği tümleştirme biçiminin bıraktığı soru ve karar izi.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    """Gerçek gelişim: üç dal, dörder işleme, biri kusuru getiriyor."""
    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


def tumlestir(kayit, bicim):
    """Her tümleştirme biçimi ayrı bir tarihçe üretir."""
    t = []
    if bicim == "birlestirme islemesi":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True})
    elif bicim == "yeniden temellendirme":
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": None, "zaman_korundu": False})
    elif bicim == "sikistirma":
        for dal in DALLAR:
            grup = [x for x in kayit if x["dal"] == dal]
            t.append({"dal": dal, "sira": 0,
                      "dosya": sorted({x["dosya"] for x in grup}),
                      "kusurlu": any(x["kusurlu"] for x in grup),
                      "zaman": max(x["zaman"] for x in grup),
                      "dal_kaydi": dal, "zaman_korundu": False, "sikisik": len(grup)})
    elif bicim == "ileri sarma":
        for k in sorted(kayit, key=lambda x: x["zaman"]):
            t.append({**k, "dal_kaydi": None, "zaman_korundu": True})
    return t


def s1(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2(t):
    k = [x for x in t if x["kusurlu"]]
    return len(k) == 1 and not k[0].get("sikisik")


def s3(t):
    return all(x.get("zaman_korundu") for x in t)


def s4(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1), ("kusurun yalıtımı", s2), ("gerçek sıra", s3),
           ("dosya izi", s4), ("dalın bütünlüğü", s5), ("çakışma kararı", s6))
BICIMLER = ("birlestirme islemesi", "yeniden temellendirme", "sikistirma", "ileri sarma")


def akis(kayit):
    """Her dal bir değişiklik isteği: öneri, tartışma turu ve alınan kararlar."""
    r, istekler = uretec(TOHUM + 8), []
    for dal in DALLAR:
        tur = 1 + r(3)
        kararlar = [{"dal": dal, "tur": 1 + r(tur), "mesaja_yazildi": r(3) == 0}
                    for _ in range(2 + r(3))]
        istekler.append({"dal": dal, "onerilen": sum(1 for x in kayit if x["dal"] == dal),
                         "tur": tur, "kararlar": kararlar})
    return istekler


def okunur_kalan(t, kararlar):
    """Bir karar, yazıldığı işleme ayrı okunabiliyorsa tarihçede okunur kalır."""
    ayri = {x["dal"] for x in t if not x.get("birlesme") and not x.get("sikisik")}
    return sum(1 for k in kararlar if k["mesaja_yazildi"] and k["dal"] in ayri)


def gonderme_yeri(t):
    """Akışın istek göndermesini koyabileceği işleme: birleşme ya da sıkıştırma."""
    return sum(1 for x in t if x.get("birlesme") or x.get("sikisik"))


kayit = gelisim()
istekler = akis(kayit)
kararlar = [k for i in istekler for k in i["kararlar"]]
yazilan = sum(k["mesaja_yazildi"] for k in kararlar)

print(f"değişiklik isteği {len(istekler)} | önerilen işleme "
      f"{sum(i['onerilen'] for i in istekler)} | tartışma turu "
      f"{sum(i['tur'] for i in istekler)} | alınan karar {len(kararlar)} | "
      f"işleme mesajına yazılan {yazilan}")
for i in istekler:
    print(f"  {i['dal']:7s} önerilen {i['onerilen']}  tur {i['tur']}  "
          f"karar {len(i['kararlar'])}  mesaja yazılan "
          f"{sum(k['mesaja_yazildi'] for k in i['kararlar'])}")

print()
print("akışın seçtiği biçim    işleme  yanıtlanan soru  okunur kalan karar  "
      "gönderme yeri")
for b in BICIMLER:
    t = tumlestir(kayit, b)
    print(f"  {b:22s} {len(t):6d} {sum(f(t) for _, f in SORULAR):16d}"
          f" {okunur_kalan(t, kararlar):19d} {gonderme_yeri(t):14d}")

print()
print(f"tartışmada alınan {len(kararlar)} kararın {len(kararlar) - yazilan} tanesi "
      f"hiçbir biçimde tarihçeye girmez")
```

```
değişiklik isteği 3 | önerilen işleme 12 | tartışma turu 7 | alınan karar 11 | işleme mesajına yazılan 4
  olcum   önerilen 4  tur 3  karar 3  mesaja yazılan 1
  rapor   önerilen 4  tur 3  karar 4  mesaja yazılan 1
  kimlik  önerilen 4  tur 1  karar 4  mesaja yazılan 2

akışın seçtiği biçim    işleme  yanıtlanan soru  okunur kalan karar  gönderme yeri
  birlestirme islemesi       15                5                   4              3
  yeniden temellendirme      12                3                   4              0
  sikistirma                  3                2                   0              3
  ileri sarma                12                3                   4              0

tartışmada alınan 11 kararın 7 tanesi hiçbir biçimde tarihçeye girmez
```

#### Akışın Bıraktığı Soru

Tablonun ilk iki sütunu Birleştirme konusunun ölçtüğü sayıları tekrar veriyor: **15/5**,
**12/3**, **3/2**, **12/3**. Tekrar ediyorlar çünkü ölçülen şey aynı tarihçedir. Değişen tek
şey, biçimi seçenin kim olduğudur.

Bu, akışın en sessiz kararıdır. Katkıcı dalını iter, tartışma yürür, sonunda biri tümleştirir
ve o kişi çoğu zaman bir varsayılanı kabul eder. Varsayılan sıkıştırmaysa, deponun ay sonunda
yanıtlayabileceği soru sayısı **5**'ten **2**'ye iner ve bu iniş hiçbir tartışmada
konuşulmamıştır. Ölçünün buradaki katkısı bir kural değil, bir görünürlüktür: seçim
yapılıyor ve yapılan seçimin bir sayısı var.

Akış bir şeyi de kesin olarak yapamaz. Altıncı soru — çakışmanın nerede çözüldüğü ve hangi
tarafın seçildiği — dört biçimin dördünde de yanıtsızdır. Tartışma alanında çakışmanın nasıl
çözüleceği uzun uzun konuşulmuş olabilir; tarihçe bunu kaydetmez. Akışın varlığı bu boşluğu
kapatmaz, yalnız boşluğun daha büyük görünmesini engeller.

#### Kararların Nereye Gittiği

Üst satır akışın hacmini veriyor: **3** istek, **12** önerilen işleme, **7** tartışma turu ve
**11** alınan karar. Kararların **4**'ü bir işleme mesajına yazılmış, **7**'si yazılmamış.

Yazılmayan yedi karar hiçbir tümleştirme biçiminde tarihçeye girmez. Bulundukları tek yer
akışın kendi tartışma kaydıdır ve o kayıt deponun içinde değildir: klonlanan kopyaya gelmez,
getirmeyle taşınmaz, yedeklenen depoda bulunmaz. Kursun en pratik sonucu Birleştirme
konusunda çakışma kararı için yazılmıştı ve burada tam olarak tekrar ediyor — **gerekçe işleme
mesajına yazılmazsa hiçbir yerde yoktur** ve bu kez "hiçbir yer" deponun dışında bir yerdir.

"Okunur kalan karar" sütunu ikinci kaybı gösteriyor. Yazılmış **4** karar, üç biçimde **4**
olarak kalıyor; sıkıştırmada **0**'a iniyor. Sıkıştırma yazılmış gerekçeleri silmez, onları
ayrı okunabilir olmaktan çıkarır: on iki işlemenin mesajı üç işlemenin mesajına iner ve hangi
gerekçenin hangi değişikliğe ait olduğu artık okunamaz. Doğru davranan katkıcı bile bu kayıptan
korunmuş değildir.

Son sütun ters yönde bir gözlem veriyor. Akışın istek göndermesini yazabileceği bir işleme
yalnız iki biçimde vardır: birleştirme işlemesi ve sıkıştırma işlemesi, ikisinde de **3**.
Yeniden temellendirme ve ileri sarma böyle bir işleme üretmez ve gönderme sayısı **0**'dır. Bu
biçimlerde tarihçeden akışa geri dönmenin hiçbir yolu yoktur: işleme mesajlarında istek
numarası bulunmuyorsa, aylar sonra bir işlemenin hangi tartışmadan çıktığı sorulduğunda yanıt
yoktur.

#### Özet

- Değişiklik isteği bir kopya değil, iki dalın adına yapılmış bir göndermedir; katkıcı dalını
  güncellediğinde istek kendiliğinden güncellenir.
- Akışın son adımı bir tümleştirme biçimi seçer ve tarihçeye **5**, **3**, **2** ya da **3**
  soru bırakır; seçim çoğu zaman bir varsayılandır ve tartışılmaz.
- Üç istekte alınan **11** kararın **4**'ü işleme mesajına yazılır; kalan **7** hiçbir biçimde
  tarihçeye girmez ve yalnız deponun dışındaki tartışma kaydında durur.
- Sıkıştırma yazılmış **4** gerekçeyi **0**'a indirir: mesajlar silinmez, ayrı okunabilir
  olmaktan çıkar.
- Akışa geri dönmeyi sağlayan gönderme yalnız birleştirme ve sıkıştırma işlemelerine
  yazılabilir; yeniden temellendirme ve ileri sarmada böyle bir işleme yoktur ve gönderme
  sayısı **0**'dır.

#### Sonraki Adım

Bu dersin akışı bir depo platformu varsaydı: ortak bir adres, bir tartışma alanı ve tümleştirme
düğmesine basacak bir yetki. Katkının bunların hiçbiri olmadan gittiği bir biçim daha vardır ve
sürüm kontrolünün en eski katkı biçimi budur: değişikliğin **dosya olarak** gönderilmesi.
Sonraki ders yamayı ölçer — hangi üstveriyi taşıdığını, hangisini taşımadığını ve taşımadığı
alanın altı sorunun neresine düştüğünü.

### Yama Tabanlı Katkı

Source: https://academia.sh/tr/kurslar/dallanma-ve-isbirligi/yama-tabanli-katki

Yama bir metin dosyasıdır ve çizgeyi değil değişikliği kodlar: tarihçeyle itme 15 işlemede 5 soru, posta biçimli seri 12 işlemede 4, düz fark serisi 12 işlemede 3, tek düz fark 1 işlemede 1 soru bırakır ve düz farkta yazarı korunan işleme 0'dır.

Önceki dersin akışı bir depo platformu varsaydı: ortak bir adres, bir tartışma alanı ve
tümleştirme kararını verecek bir yetki. Katkının bunların hiçbiri olmadan gittiği bir biçim
daha vardır ve sürüm kontrolünün en eski katkı biçimi budur: değişikliğin **dosya olarak**
gönderilmesi.

Yama, bir depoya değil bir posta kutusuna sığar. Bu dersin sorusu yamanın nasıl üretildiği
değil — üretimi iki komuttan ibarettir. Soru şudur: yama karşı tarafa **neyi taşır**, neyi
taşımaz ve taşımadığı alan altı sorunun neresine düşer?

#### İki Yama Biçimi

**Yama (patch)**, bir değişikliğin metin olarak kodlanmış hâlidir ve iki ayrı biçimi vardır.
Birincisi **düz farktır**: yalnız değişiklik metnini taşır, üstveri taşımaz.

```text
# öğretilen komutlar — örnek döküm, çalıştırılmamıştır

git diff yukari/ana..olcum > olcum.patch
git apply --check olcum.patch
git apply olcum.patch
```

`--check` seçeneği yamayı uygulamadan sınar ve çalışma alanına dokunmaz. Uygulama başarılı
olduğunda değişiklikler çalışma alanına yazılır ve **hiçbir işleme oluşmaz**: işlemeyi
uygulayan yazar, kendi adıyla ve kendi zamanıyla.

İkincisi **posta biçimli yama serisidir**: her işleme için bir dosya üretilir ve dosyanın
başında bir posta başlığı durur.

```text
# öğretilen komutlar ve dosya başı — örnek döküm, çalıştırılmamıştır

git format-patch yukari/ana..olcum -o yamalar/
git am --3way yamalar/0001-olcum-esigini-yapilandirmadan-oku.patch

From <işleme kimliği> Mon Sep 17 00:00:00 2001
From: yazan <yazan@ornek.test>
Date: <yazar tarihi>
Subject: [PATCH 1/4] Ölçüm eşiğini yapılandırmadan oku

Eşik değeri betiğe gömülüydü; ölçüm noktası değiştiğinde betiği
düzenlemek gerekiyordu.
---
 olcum.py | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
```

Başlıktaki üç alan doğrudan işlemeye geçer: yazar, yazar tarihi ve mesaj. `git am` her dosya
için bir işleme yazar ve bu alanları yerelden değil **dosyadan** okur. `--3way` seçeneği,
yama doğrudan uymadığında ortak tabana bakarak üç yollu birleştirme dener; çakışma çıkarsa
karar yine elle verilir.

#### Yamanın Taşıyamadığı

İki biçimin de taşıyamadığı bir alan sınıfı var ve nedeni yapısaldır. Yama bir **metin
dosyasıdır** ve bir değişikliği kodlar; tarihçe ise bir **çizgedir** ve metin dosyası kenar
taşımaz. Bu yüzden yamanın karşı tarafta ürettiği işleme her zaman **yeni kimliklidir**,
öncül bağı uygulandığı yerin ucudur ve dal kaydı hiç geçmez — yamada dal diye bir şey yoktur,
yalnız dosya vardır.

Ölçüm bu nedenle tarihçeyi taşınan üstveri kümesinden **türetir**; biçimleri elle betimlemez.

#### Ölçümün Varsayımları

- **UD25** — Aynı on iki işlemelik gelişim dört ayrı katkı biçimiyle karşı tarafa taşınır;
  üretilen kod dördünde de aynıdır ve ölçülen tek şey karşı tarafta kalan **üstveridir**.
- **UD26** — Her biçim bir **taşınan üstveri kümesiyle** tanımlanır; karşı taraftaki tarihçe
  bu kümeden hesaplanır, elle yazılmaz.
- **UD27** — Posta biçimli seri yazarı, yazar zamanını ve mesajı taşır; dal kaydını ve öncül
  bağını taşımaz.
- **UD28** — Düz fark serisi yalnız değişiklik metnini ve mesajı taşır; yazar ve zaman
  uygulama anında yerelden yazılır ve bu ölçümde "taşımaz" sayılır.
- **UD29** — Tek düz fark, on iki işlemenin farkını tek dosyada toplar ve karşı tarafta tek
  işleme olur; sıkıştırmanın en uç biçimidir.
- **UD30** — "Yazarı korunan" sütunu, karşı tarafta yazar alanı özgün katkıcıyı gösteren
  işlemeleri sayar; birleştirme işlemeleri tümleştirenin adına yazıldığı için sayıma girmez.

#### Ölçüm

```python
"""Yama tabanlı katkı: taşınan üstveri ve taşınmayanın bedeli.

Bölüm 1 - dört katkı biçiminin taşıdığı üstveri alanları.
Bölüm 2 - aynı biçimlerin bıraktığı tarihçe ve yanıtladığı soru.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
USTVERI = ("yazar", "yazar zamanı", "konu ve gövde", "dal kaydı", "öncül bağı")
TASINAN = {"tarihçeyle itme": set(USTVERI),
           "posta biçimli seri": {"yazar", "yazar zamanı", "konu ve gövde"},
           "düz fark serisi": {"konu ve gövde"},
           "tek düz fark": set()}


def uretec(tohum):
    d = tohum % 2147483646 + 1

    def r(n):
        nonlocal d
        d = (d * 48271) % 2147483647
        return d % n
    return r


def gelisim():
    """Gerçek gelişim: üç dal, dörder işleme, biri kusuru getiriyor."""
    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


def uygula(kayit, tur):
    """Katkının karşı tarafta bıraktığı tarihçe: taşınan üstveriden türer."""
    tasinan = TASINAN[tur]
    if tur == "tarihçeyle itme":
        t = []
        for dal in DALLAR:
            for k in [x for x in kayit if x["dal"] == dal]:
                t.append({**k, "dal_kaydi": dal, "zaman_korundu": True,
                          "yazar_korundu": True})
            t.append({"dal": dal, "sira": 0, "dosya": None, "kusurlu": False,
                      "zaman": max(x["zaman"] for x in kayit if x["dal"] == dal),
                      "dal_kaydi": dal, "birlesme": True, "zaman_korundu": True,
                      "yazar_korundu": False})
        return t
    if tur == "tek düz fark":
        return [{"dal": None, "sira": 0,
                 "dosya": sorted({x["dosya"] for x in kayit}),
                 "kusurlu": any(x["kusurlu"] for x in kayit),
                 "zaman": max(x["zaman"] for x in kayit), "dal_kaydi": None,
                 "zaman_korundu": False, "yazar_korundu": False,
                 "sikisik": len(kayit)}]
    return [{**k, "dal_kaydi": k["dal"] if "dal kaydı" in tasinan else None,
             "zaman_korundu": "yazar zamanı" in tasinan,
             "yazar_korundu": "yazar" in tasinan}
            for dal in DALLAR for k in kayit if k["dal"] == dal]


def s1(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2(t):
    k = [x for x in t if x["kusurlu"]]
    return len(k) == 1 and not k[0].get("sikisik")


def s3(t):
    return all(x.get("zaman_korundu") for x in t)


def s4(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5(t):
    yerler = {}
    for i, x in enumerate(t):
        if x.get("birlesme"):
            continue
        yerler.setdefault(x["dal"], []).append(i)
    return all(y[-1] - y[0] == len(y) - 1 for y in yerler.values())


def s6(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1), ("kusurun yalıtımı", s2), ("gerçek sıra", s3),
           ("dosya izi", s4), ("dalın bütünlüğü", s5), ("çakışma kararı", s6))

kayit = gelisim()
print(f"gerçek gelişim {len(kayit)} işleme, {len(DALLAR)} dal, "
      f"kusurlu işleme {sum(k['kusurlu'] for k in kayit)}")
print()
print("üstveri alanı  " + "".join(f"{ad:>20s}" for ad in TASINAN))
for alan in USTVERI:
    print(f"  {alan:13s}" + "".join(f"{('taşır' if alan in TASINAN[t] else 'taşımaz'):>20s}"
                                    for t in TASINAN))
print()
print("katkı biçimi          işleme  yazarı korunan  ayrı okunabilir  yanıtlanan  kaybolan")
for tur in TASINAN:
    t = uygula(kayit, tur)
    ayri = sum(1 for x in t if not x.get("birlesme") and not x.get("sikisik"))
    print(f"  {tur:20s} {len(t):6d} {sum(x['yazar_korundu'] for x in t):15d}"
          f" {ayri:16d} {sum(f(t) for _, f in SORULAR):11d} {len(kayit) - ayri:9d}")
print()
print("yanıtlanamayan sorular:")
for tur in TASINAN:
    t = uygula(kayit, tur)
    print(f"  {tur:20s} " + ", ".join(ad for ad, f in SORULAR if not f(t)))
```

```
gerçek gelişim 12 işleme, 3 dal, kusurlu işleme 1

üstveri alanı       tarihçeyle itme  posta biçimli seri     düz fark serisi        tek düz fark
  yazar                       taşır               taşır             taşımaz             taşımaz
  yazar zamanı                taşır               taşır             taşımaz             taşımaz
  konu ve gövde               taşır               taşır               taşır             taşımaz
  dal kaydı                   taşır             taşımaz             taşımaz             taşımaz
  öncül bağı                  taşır             taşımaz             taşımaz             taşımaz

katkı biçimi          işleme  yazarı korunan  ayrı okunabilir  yanıtlanan  kaybolan
  tarihçeyle itme          15              12               12           5         0
  posta biçimli seri       12              12               12           4         0
  düz fark serisi          12               0               12           3         0
  tek düz fark              1               0                0           1        12

yanıtlanamayan sorular:
  tarihçeyle itme      çakışma kararı
  posta biçimli seri   özellik gruplaması, çakışma kararı
  düz fark serisi      özellik gruplaması, gerçek sıra, çakışma kararı
  tek düz fark         özellik gruplaması, kusurun yalıtımı, gerçek sıra, dosya izi, çakışma kararı
```

#### Basamakların Okunması

Alt tablo dört basamak veriyor ve her basamağın bir üstveri karşılığı var.

**Tarihçeyle itme** on beş işleme ve **5** soru bırakıyor; bu ölçüt satırıdır, bütün alanlar
geçmiştir.

**Posta biçimli seri** on iki işleme bırakıyor ve **4** soru yanıtlıyor. Düşen tek soru
**özellik gruplaması**; nedeni üst tablonun dördüncü satırında yazılı, dal kaydı taşınmıyor.
Yazar ve zaman taşındığı için "gerçek sıra" ayakta kalıyor.

**Düz fark serisi** de on iki işleme bırakıyor ama **3** soru yanıtlıyor. Fark tek bir alandan
geliyor: yazar zamanı taşınmadığı için bütün işlemeler uygulama anının zamanını taşıyor ve
**gerçek sıra** okunamıyor. Aynı sayıdaki işleme, tek bir üstveri alanı yüzünden bir soru daha
az yanıtlıyor.

**Tek düz fark** on iki işlemeyi bire indiriyor, **1** soru yanıtlıyor, kaybolan işleme
**12**. Kursta ölçülen en düşük değer budur; sıkıştırma on iki işlemeyi üçe indirip **2**
yanıtlıyordu. Altıncı soru ise dört satırda da yanıtsız — katkının hangi taşıma aracıyla
gittiği bunu değiştirmiyor.

#### Ölçünün Görmediği Alan

Üçüncü satır kursun kendi ölçüsüne dönük bir uyarı taşıyor. Düz fark serisi ile posta biçimli
seri arasında **yazarı korunan** sütunu **12**'den **0**'a düşüyor: on iki işlemenin hiçbiri
artık yazanı göstermiyor, hepsi uygulayan kişinin adına yazılmış. Yanıtlanan soru ise yalnız
**1** düşüyor ve düşen soru yazarla ilgili değil.

Nedeni açıktır: **altı sorunun hiçbiri yazarı sormaz.** Sorular gruplamayı, yalıtımı, sırayı,
dosya izini, dal bütünlüğünü ve çakışma kararını sorar; katkının kime ait olduğu kümede
yoktur, dolayısıyla ölçü onu göremez. Bu bir ölçü kusuru değil, ölçünün **kapsamının**
bildirimidir. Katkı sahipliği ve imza o pencerenin dışındadır ve düz fark serisi bunları
sessizce siler.

#### Özet

- Yama bir metin dosyasıdır ve değişikliği kodlar; çizgeyi kodlamadığı için işleme kimliği,
  öncül bağı ve dal kaydı hiçbir yama biçiminde taşınmaz. Posta biçimli seri yazarı, zamanı
  ve mesajı taşır; düz fark yalnız değişiklik metnini taşır.
- Dört katkı biçimi karşı tarafa **15**, **12**, **12** ve **1** işleme bırakır ve
  sırasıyla **5**, **4**, **3** ve **1** soru yanıtlar.
- Posta biçimli seri ile düz fark serisi aynı sayıda işleme bırakır; aradaki bir soruluk fark
  yalnız **yazar zamanı** alanının taşınmamasından gelir.
- Yazarı korunan işleme sayısı **12**'den **0**'a düştüğünde yanıtlanan soru yalnız bir
  azalır: altı sorunun hiçbiri yazarı sormaz ve bu, ölçünün kapsam sınırıdır.

#### Kurs Kapanışı

Kurs tek bir ölçüyle yürüdü: bir tümleştirme biçiminin sayısı, ürettiği kod değil, bıraktığı
tarihçenin yanıtlayabildiği **soru sayısıdır**. On beş ders bu ölçüyü ayrı yerlere uyguladı.

| ders | ölçülen | yanıtlanan soru ya da kayıp |
|---|---|---|
| Dal Kavramı | dallanmanın maliyeti | 0 yeni işleme ve 123 bayt başvuru; kopya modeli aynı işi 12.000 dosya kopyalayarak yapar |
| Dal Oluşturma ve Geçiş | geçişin elde kalan değişikliğe yaptığı | 12 durumun 3'ü taşınır, 5'i engellenir, 4'ü geride kalır; sessiz kayıp 0 |
| Dal Adlandırma | ad düzeninin birinci soruya katkısı | 8 bileşimin 3'ünde grup okunur; kayıt tutmayan biçimde katkı 0 |
| Ayrık HEAD Durumu | erişilemeyen işlemenin tarihçeye maliyeti | 15 kayıt 13'e, yanıt 5'ten 4'e iner; üç sayım sorgusunun 3'ü de eksik yanıtlanır ve tarihçe bunu bildirmez |
| İleri Sarma ve Birleştirme İşlemesi | iki birleşme biçiminin bıraktığı tarihçe | 15 işleme / 5 soru ve 12 işleme / 3 soru; farkı gruplama ile dal bütünlüğü yaratır |
| Çakışma Çözümü | çözümün tarihçeye geçip geçmediği | dört biçimin dördü de altıncı soruyu yanıtlamaz, 0/4; gerekçe mesaja yazılırsa 15 işlemenin 1'i onu taşır |
| Yeniden Temellendirme | doğrusallaştırmanın bıraktığı tarihçe | 12 işleme / 3 soru; ileri sarmayla toplam eşit, kayıp ayrı, fark 1/6 |
| Kiraz Toplama | aynı değişikliğin iki yerde durması | dosya izi evet kalır, dokunan işleme 3'ten 4'e çıkar; kusurlu kopyada yanıt 5'ten 4'e iner |
| Birleştirme Stratejileri | dört biçim yan yana | 15/12/3/12 işleme ve 5/3/2/3 soru; sıkıştırmada kaybolan 12 |
| Dal Stratejileri | dal ömrünün tarihçeye yazdırdığı | 12, 15, 18 ve 24 işleme; üç dal düzeni de 5 soru yanıtlar — altı soru dal düzenlerini ayırmaz |
| Uzak Depo Tanımlama | tanımlamanın tarihçeye eklediği | üç kopya 5/10/15 işleme ve 4/5/5 soru; iki tanımdan sonra yerel 5 işleme / 4 soru |
| Getirme, Çekme ve İtme | üç işlemin değiştirdiği tarihçe | getirme dalı 5'te bırakır, çekme 16'ya ve 5 soruya taşır, itme karşı tarafı 10'dan 16'ya değiştirir |
| Çatallama ve Klonlama | yukarı deponun yanıtlayabildiği | 15/5, 12/3 ve 3/2; sıkıştırmada 3 soru yalnız çatal deposunda kalır |
| Değişiklik İsteği Akışı | akışın seçtiği biçimin bıraktığı | 5/3/2/3 soru; 11 kararın 7'si tarihçeye hiç girmez, sıkıştırmada yazılan 4'ü de okunmaz olur |
| Yama Tabanlı Katkı | yamanın taşıdığı ve taşımadığı üstveri | 15/5, 12/4, 12/3 ve 1/1; düz farkta yazarı korunan 0 ve altı soru bunu hiç sormaz |

Tablodan üç okuma çıkar. **Birincisi:** okunaklılık ile yanıtlanabilirlik ters yönde hareket
eder — en temiz tarihçeler en az soru yanıtlayanlardır ve tarihçeyi temizlemek bilgi siler.
**İkincisi:** altıncı soru hiçbir satırda yanıtlanmaz; çakışmanın nasıl çözüldüğü kaydın
tanımı gereği kayıtta yoktur ve tek çare gerekçeyi işleme mesajına yazmaktır. **Üçüncüsü:**
kayıp iki ayrı kaynaktan gelir. Biçim kaybı bilgiyi siler ve geri alınamaz; kapsam kaybı
yalnız bir kopyanın eksikliğidir ve getirmeyle kapanır.

Kurs boyunca tarihçe hep **okundu**: sorular sorulmuş, yanıtlar sayılmış, biçimlerin bıraktığı
kayıt olduğu gibi kabul edilmiştir. Sonraki kurs — İleri Git — aynı kaydı **yeniden yazma**
araçlarını ele alır: etkileşimli yeniden temellendirme, toplu tarihçe dönüşümü, zorlayıcı itme
disiplini ve kaybolan işlemeleri kurtaran referans günlüğü. Ayrım baştan konmalıdır:
**tarihçeyi okumak ile yeniden yazmak ayrı işlerdir.** Okumak hiçbir şeyi değiştirmez; yeniden
yazmak, bu kursta sayılan soruların yanıtlarını kalıcı olarak değiştirebilir. Bu kursun
bıraktığı ölçü, oradaki her aracın bedelini hesaplamakta kullanılacak birimdir.

