İçeriğe geç
academia.sh

Ders 02 / 15

Dal Oluşturma ve Geçiş

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.

İçindekiler

Ö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 HEADi, hazırlık alanını ve çalışma dizinini birlikte günceller.

# öğ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 HEADin yeniden yazılmasıdır. Önceki derste HEADin 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.

# öğ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.
  • DL13Sessiz 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

"""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 HEADi, 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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat