Ders 08 / 15
Kiraz Toplama
Tek bir işlemeyi başka dala kopyalamak aynı değişikliği tarihçede iki ayrı kimlikle iki yerde bırakır; dosya izi sorusu yine evet der ama saydığı işleme sayısı ayrı değişiklik sayısından fazladır ve kusurlu bir işleme kopyalandığında yalıtım sorusu düşer.
İçindekiler
Yeniden temellendirme bir dalın bütün işlemelerini yeni bir tabana 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ı. Dalın tamamını almak bu durumda yanlış olur; alınmak istenen tek bir değişikliktir.
Kiraz toplama (cherry-pick) tam olarak bunu yapar. Bu dersin sorusu işlemin nasıl çalıştığı kadar sonucudur: aynı değişiklik artık tarihçede iki yerde durur, ve bir dosyaya dokunan işlemeleri sayan dosya izi sorusu bundan etkilenir.
Tek İşlemeyi Kopyalamak
Kiraz toplama, seçilen işlemenin öncülüne göre yaptığı değişikliği alır ve bulunduğunuz dalın ucuna uygular. Uygulama başarılıysa yeni bir işleme yazılır. Yeni işlemenin öncülü hedef dalın eski ucudur, ağacı yenidir, kimliği yenidir. Kaynak işleme yerinde durur ve hiçbir şey kaybetmez.
Bu, yeniden temellendirmenin tek işlemelik hâlidir. Aradaki fark kapsamdır: yeniden temellendirme dalın ucundan ortak ataya kadarki bütün işlemeleri taşır ve eskilerini erişilemez bırakır; kiraz toplama tek bir işlemeyi kopyalar ve eskisini yerinde bırakır. Tersine çalışan bir üçüncüsü de vardır: devirme (revert) bir işlemenin değişikliğini ters çevirip yeni bir işleme olarak yazar. Üçü de aynı düzeneğin — yamayı çıkar, yeniden uygula — ayrı kullanımlarıdır.
# öğretilen komut, örnek döküm — çalıştırılmamıştır $ git switch kimlik $ git cherry-pick 4d5e6f1 [kimlik 8c9d0e1] rapor: alan genisligini duzelt 1 file changed, 6 insertions(+), 2 deletions(-)
Soldaki köşeli parantez hedef dalı ve yeni kimliği gösteriyor; komuta verilen kimlik
4d5e6f1, üretilen kimlik 8c9d0e1. İki işlemenin içeriği aynı, kimliği ayrı. Bu, dersin
ölçtüğü şeyin tamamıdır.
Uygulama her zaman başarılı olmaz. Kaynak işlemenin dayandığı bağlam hedef dalda yoksa yama tutmaz ve önceki derslerdeki çakışma durumu doğar: çalışma alanı işaretlerle bırakılır, çözüm elle verilir, sonra devam ya da iptal çağrılır. Bir aralık verildiğinde işlemeler sırayla kopyalanır ve çakışma her biri için ayrı ayrı sorulabilir.
Bir de tersi vardır: kopyalanan değişiklik hedef dalda zaten uygulanmışsa yama boş kalır. Araç bu durumda kendiliğinden bir karar vermez, durur ve seçimi kullanıcıya bırakır — boş bir işleme yazmak da, o adımı atlamak da tarihçeye ayrı şeyler yazar.
Aralık, İşlemesiz Kopya ve İki Öncüllü Kaynak
Kopyalanacak şey tek bir işleme olmak zorunda değildir. Komuta bir işleme aralığı verildiğinde araç aralıktaki işlemeleri sırayla kopyalar ve her biri için ayrı bir işleme yazar. Aralık gösterimi burada da öncül temellidir: verilen alt sınır dışarıda kalır, üst sınır içeridedir. Bu, aralığın bir işleme eksik kopyalanmasının en sık nedenidir.
İkinci bir seçenek kopyayı işlemeden bırakır: değişiklik çalışma alanına ve hazırlık alanına uygulanır, işleme yazılmaz. Bu, birkaç işlemeyi kopyalayıp hedef dalda tek bir işleme olarak kaydetmek istendiğinde kullanılır — ve o durumda tarihçede kaynak işlemelerin ayrı ayrı karşılığı hiç oluşmaz.
Kaynak bir birleştirme işlemesiyse durum ayrıdır. Birleştirme işlemesinin iki öncülü vardır ve “öncülüne göre yaptığı değişiklik” ifadesi tek bir yanıt vermez: hangi öncüle göre bakıldığına bağlı olarak iki ayrı yama çıkar. Araç bu yüzden hangi öncülün taban sayılacağının açıkça belirtilmesini ister ve belirtilmediğinde işi reddeder.
# öğretilen komut, örnek döküm — çalıştırılmamıştır $ git cherry-pick a1b2c3d error: commit a1b2c3d is a merge but no -m option was given. fatal: cherry-pick failed
Bu red, birinci dersteki iki öncüllü yapının doğrudan sonucudur. Birleştirme işlemesinin taşıdığı bilgi bir değişiklik değil, iki çizginin buluşmasıdır; tek bir yamaya indirgenmesi ancak bir taraf seçilerek yapılabilir.
Kopyanın Kökeni Kaydedilir mi
İkinci dersin sonucu burada bire bir tekrar eder. Kopyanın kaynağı, araç kendiliğinden yazmadıkça tarihçede durmaz: yeni işleme, hangi işlemeden kopyalandığını söyleyen bir alan taşımaz. Bu bilgiyi kaydeden bir seçenek vardır ve isteğe bağlıdır — verildiğinde araç işleme mesajının sonuna kaynağın kimliğini yazan bir satır ekler.
# öğretilen komut, örnek döküm — çalıştırılmamıştır $ git cherry-pick -x 4d5e6f1 $ git log -1 --format=%B rapor: alan genisligini duzelt (cherry picked from commit 4d5e6f1)
Satırın nereye yazıldığına dikkat: işleme mesajının içine. Araç bunun için ayrı bir alan tanımlamaz; kökeni de gerekçe gibi mesaja yazar. Önceki derste çakışma çözümü için söylenen şey burada da geçerlidir — mesaja yazılmayan bilgi hiçbir yerde durmaz.
Kaynağı yazılmamış bir kopya sonradan bulunabilir mi? Kısmen. Aracın yamaları içeriğine göre karşılaştıran bir yeteneği vardır: iki işlemenin ürettiği değişiklik aynıysa, kimlikleri ayrı olsa bile aynı yama kimliğini taşırlar. İki dal arasında hangi işlemelerin karşı tarafta zaten bulunduğunu bu karşılaştırma söyler.
# öğretilen komut, örnek döküm — çalıştırılmamıştır $ git cherry -v ana rapor + 7e8f9a0 rapor: ozet satirini ekle - 4d5e6f1 rapor: alan genisligini duzelt
Eksi işaretli satır, o değişikliğin karşı tarafta eşdeğer bir yama olarak zaten bulunduğunu bildirir. Bu, kopyanın var olduğunu bulur; kopyanın neden alındığını bulmaz. İki soru ayrıdır ve ikincisinin yanıtı yine yalnız mesajdadır.
Yama karşılaştırmasının bir sınırı da vardır: eşdeğerlik değişikliğin metnine bakar. Kopya sırasında bir çakışma çözülmüşse ya da bağlam farklıysa yamalar birbirinin aynı olmaz ve eşleşme kurulamaz. Kopya o zaman ayrı bir değişiklik gibi görünür.
Ölçümün varsayımları:
- BR22 — Kiraz toplama, kaynak işlemenin öncülüne göre yaptığı değişikliği hedef dalın ucuna yeni kimlikli bir işleme olarak yazar; kaynak işleme yerinde kalır.
- BR23 — Yama kimliği değişikliğin kendisidir. Kopya aynı yama kimliğini taşır, işleme kimliği ayrıdır; ölçüm ikisini ayrı işleme olarak sayar.
- BR24 — Kopya, hedef dalın kendi işlemelerinden sonra ve birleştirme işlemesinden önce yazılır; kendi zamanını taşır ve dalın bütünlüğünü bozmaz.
- BR25 — Taban tarihçe birleştirme işlemesi biçimidir. Kopyanın eklenmesi dışında hiçbir şey değiştirilmez.
- BR26 — İzlenen dosya
rapor.py. Dosya izi sorusu, ona dokunan işlemelerin ayrı ayrı bulunabilmesine bakar; kaç ayrı değişiklik olduğuna bakmaz. - BR27 — Kusurlu bir işlemenin kopyası da kusurludur; kopyalanan değişiklik kusuru da taşır.
- BR28 — Kümenin çözünürlüğü on iki işlemede 1/12, altı soruda 1/6.
Ölçüm
"""Kiraz toplama: ayni degisiklik tarihcede iki yerde durur. Bolum 1 - kopya oncesi ve sonrasi: rapor.py dosyasina dokunan isleme sayisi. Bolum 2 - dosya izi ve kusurun yalitimi sorulari kopyadan nasil etkilenir. """ TOHUM = 20260813 DALLAR = ("olcum", "rapor", "kimlik") DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"} KUSURLU = ("rapor", 2) ORTAK_DOSYA = "ayar.py" IZLENEN = "rapor.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}) return t def s2_kusur(t): kusurlu = [x for x in t if x["kusurlu"]] return len(kusurlu) == 1 and not kusurlu[0].get("sikisik") def s4_dosya(t): return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t) def yama(x): """Yama kimligi: degisikligin kendisi. Kopya ayni yamayi tasir.""" return x.get("yama", (x["dal"], x["sira"])) def kiraz_topla(t, kaynak, hedef): """kaynak islemesi hedef dala kopyalanir: yeni kimlik, ayni yama.""" y, k = [], next(x for x in t if (x["dal"], x["sira"]) == kaynak) son = max(x["zaman"] for x in t if x["dal"] == hedef and not x.get("birlesme")) for x in t: if x.get("birlesme") and x["dal"] == hedef: y.append({**k, "dal": hedef, "dal_kaydi": hedef, "zaman": son, "yama": kaynak, "kopya": True}) y.append(dict(x)) return y def dokunan(t, ad): return [x for x in t if x["dosya"] == ad] kayit = gelisim() TABAN = tumlestir(kayit, "birlestirme islemesi") TARIHCELER = (("kopya yok", TABAN), ("kusursuz işleme kopyalandı", kiraz_topla(TABAN, ("rapor", 1), "kimlik")), ("kusurlu işleme kopyalandı", kiraz_topla(TABAN, ("rapor", 2), "kimlik"))) print(f"gerçek gelişim: {len(kayit)} işleme | izlenen dosya: {IZLENEN} | " f"gelişimde ona dokunan {sum(1 for k in kayit if k['dosya'] == IZLENEN)}") print() print(f"{'tarihçe':<26s} {'işleme':>6s} {'dokunan':>7s} {'ayrı yama':>9s}" f" {'yinelenen':>9s} {'dosya izi':>9s}") for ad, t in TARIHCELER: d = dokunan(t, IZLENEN) ayri = len({yama(x) for x in d}) print(f"{ad:<26s} {len(t):6d} {len(d):7d} {ayri:9d} {len(d) - ayri:9d}" f" {('evet' if s4_dosya(t) else 'hayır'):>9s}") print() print(f"{'tarihçe':<26s} {'kusurlu işleme':>14s} {'kusurun yalıtımı':>16s}") for ad, t in TARIHCELER: print(f"{ad:<26s} {sum(1 for x in t if x['kusurlu']):14d}" f" {('evet' if s2_kusur(t) else 'hayır'):>16s}") print() for ad, t in TARIHCELER[1:]: k = [x for x in t if x.get("kopya")][0] print(f"{ad}: kopya {k['dal']} dalında, yaması {k['yama']}, " f"dosyası {k['dosya']}")
gerçek gelişim: 12 işleme | izlenen dosya: rapor.py | gelişimde ona dokunan 3
tarihçe işleme dokunan ayrı yama yinelenen dosya izi
kopya yok 15 3 3 0 evet
kusursuz işleme kopyalandı 16 4 3 1 evet
kusurlu işleme kopyalandı 16 4 3 1 evet
tarihçe kusurlu işleme kusurun yalıtımı
kopya yok 1 evet
kusursuz işleme kopyalandı 1 evet
kusurlu işleme kopyalandı 2 hayır
kusursuz işleme kopyalandı: kopya kimlik dalında, yaması ('rapor', 1), dosyası rapor.py
kusurlu işleme kopyalandı: kopya kimlik dalında, yaması ('rapor', 2), dosyası rapor.py
Dosya İzinin Söylediği ve Söylemediği
Üst tablo dosya izi sorusuna üç tarihçede de evet verdiriyor. Sorunun tanımı
değişmedi: rapor.py dosyasına dokunan her işleme ayrı ayrı bulunabiliyor. Kopyalama bu
yanıtı bozmuyor, çünkü kopya da tek bir dosyaya dokunan tek bir işlemedir.
Bozulan şey yanıtın anlamıdır. Kopya yokken rapor.py dosyasına dokunan 3 işleme
var ve bunlar 3 ayrı yamayı taşıyor; sayılar örtüşüyor. Kopyadan sonra dokunan işleme
4, ayrı yama yine 3, yinelenen 1. Dosya izi hâlâ tam ama artık iki ayrı soruya
iki ayrı sayı veriyor: “bu dosyaya kaç işleme dokundu” sorusunun yanıtı 4, “bu dosyada
kaç ayrı değişiklik yapıldı” sorusunun yanıtı 3.
Bu ayrım ölçümden önce görünmezdi, çünkü kopya olmayan bir tarihçede iki sayı hep aynıdır. Kiraz toplama onları ayırır ve tarihçeye bakan kişi hangisini okuduğunu bilmek zorunda kalır. Bir dosyanın kaç kez değiştirildiğine bakarak istikrarsızlık ölçen bir okuma, kopyaları saydığı için o dosyayı olduğundan hareketli gösterir. İz eksik değildir; iz yinelenmiştir ve yinelenmenin işareti tarihçenin kendisinde durmaz. Onu bulmak için yamaları içerik olarak karşılaştıran ayrı bir araç gerekir.
Aynı yinelenme satır düzeyinde de görünür. Bir satırın hangi işlemeden geldiğini soran araç, kopyalanmış bir satır için kopyayı gösterir, kaynağı değil. Sorulan soru genellikle “bu satır neden böyle” olduğuna göre, okunacak mesaj da kopyanın mesajıdır. Kaynağın mesajı ne kadar ayrıntılı olursa olsun, köken satırı yazılmadıysa oraya giden bir bağ yoktur. Kökeni kaydeden seçeneğin değeri tam olarak burada ortaya çıkar: iki satırlık bir ek, kopyadan kaynağa giden tek bağdır.
Kusurun İki Yeri
Alt tablo daha sert bir sonuç veriyor. Kusursuz bir işleme kopyalandığında kusurlu işleme sayısı 1’de kalıyor ve yalıtım sorusu evet veriyor: tarihçe değişti ama ölçüm değişmedi. Kusurlu işleme kopyalandığında sayı 2 oluyor ve yalıtım sorusu hayır veriyor.
Neden düştüğü tanımdan okunur: yalıtım sorusu kusuru getiren işlemenin tek başına bulunmasını arar. Kusur iki ayrı işlemede duruyorsa “kusuru getiren işleme” tek bir yanıt değildir. Bir dalda düzeltilen kusur öbür dalda ayakta kalır ve düzeltmenin oraya da taşınması gerekir; taşındığında da tarihçede iki düzeltme, iki kimlik ve iki ayrı yer oluşur.
Bu ikilik zamanla derinleşir. Kopya ile kaynak ayrı işlemelerdir ve bundan sonrası ayrı yürür: kaynak dalda o değişikliğin üzerine yapılan bir düzeltme kopyaya kendiliğinden gitmez, kopyanın bulunduğu dalda yapılan bir değişiklik de kaynağa dönmez. İki yer aynı metinle başlar ve her düzenlemede biraz daha ayrışır. Yama karşılaştırması ilk günlerde eşleşme bulur, ayrışma başladıktan sonra bulmaz — yani ikilik tam da onu bulmanın zorlaştığı anda kalıcılaşır.
Sayısal etki 1/6’dır: altı sorudan biri düşer ve birleştirme işlemesinin yanıtladığı soru sayısı 5’ten 4’e iner. Bu, kümenin çözünürlüğüne eşit ve savunulabilir en küçük farktır.
Buradan bir ölçüt çıkar. Kiraz toplama tek yönlü ve kısa ömürlü olduğunda ucuzdur: yayımlanmış bir daldaki düzeltme geliştirme dalına alınır, sonra iki dal zaten birleşeceği için kopya kalıcı bir yinelenme yaratmaz. Kiraz toplama iki dal arasında düzenli bir tümleştirme yolu olarak kullanıldığında pahalıdır: her kopya bir yinelenen yama, her yinelenen yama bir yanlış sayım ve bir kusur için iki ayrı yer demektir. İkinci kullanımda gereken şey kopyalama değil, birleştirmedir.
Özet
- Kiraz toplama, bir işlemenin öncülüne göre yaptığı değişikliği başka dalın ucuna yeni kimlikli bir işleme olarak yazar; kaynak işleme yerinde kalır ve aynı değişiklik iki yerde durur.
- Kopyanın kökeni araç tarafından kendiliğinden kaydedilmez; kaydeden seçenek kimliği işleme mesajının içine yazar, ayrı bir alan yoktur.
- Yamaları içeriğine göre karşılaştıran araç kopyanın var olduğunu bulur; neden alındığını bulmaz, ve çakışma çözülerek alınmış bir kopyayı hiç eşleştiremeyebilir.
- Dosya izi sorusu kopyadan sonra da evet verir, ama dokunan işleme 3’ten 4’e çıkarken ayrı yama 3’te kalır; iz eksilmez, yinelenir.
- Kusurlu bir işleme kopyalandığında kusurlu işleme 2 olur ve yalıtım sorusu hayır verir; yanıtlanan soru sayısı 5’ten 4’e iner.
Sonraki Adım
Kursun ölçtüğü tümleştirme biçimlerinden üçü artık kuruldu: birleştirme işlemesi, ileri sarma ve yeniden temellendirme. Kiraz toplama bunlardan biri değildir — bir dalı ana dala alma yolu değil, tek bir değişikliği taşıma yoludur. Geriye dördüncü biçim ve biçimleri yan yana koymak kalıyor. Sonraki ders her dalın işlemelerini tek bir işlemede toplayan sıkıştırmayı ekler ve dördünü tek tabloda karşılaştırır — en okunaklı tarihçeyi üreten biçimin en az soru yanıtlayan biçim olduğu, ve tarihçeyi temizlemenin neden bilgi sildiği orada sayıyla gösterilir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.