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