Ders 07 / 15
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.
İçindekiler
Ö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.
# öğ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.
# öğ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
"""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.
# öğ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.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.