İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat