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.pytabanda yoktur; üçüncü adımdaolcumveraporonu ekler, bu yüzdenkimlikdalı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,
kimlikdalındanrapordalı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
"""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 -ciki 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.