İçeriğe geç
academia.sh

Ders 14 / 15

Değişiklik İsteği Akışı

Akışın seçtiği tümleştirme biçimi tarihçeye 5, 3, 2 ve 3 soru bırakır; üç istekte alınan 11 kararın 4'ü işleme mesajına yazılır, 7'si hiçbir biçimde tarihçeye girmez ve sıkıştırmada yazılanların da hepsi ayrı okunabilir olmaktan çıkar.

İçindekiler

Önceki ders çatal modelinde katkıcının yukarı depoya itme yetkisi bulunmadığını ölçtü. Buna rağmen on iki işleme bir biçimde yukarıya geçti. Geçişi yapan şey bir komut değildir: yetkisi olmayan biri hiçbir komutla karşı tarafın dalını değiştiremez. Geçişi yapan şey bir akıştır ve akışın son adımını yetkisi olan biri çalıştırır.

Bu ders akışın mekaniğini kurar: öneri nasıl sunulur, döngü nasıl döner, tümleştirme kararı nerede verilir. İnceleme ekseninin kendisi — bir değişikliğin anlam, gerçekleştirim, test ve belge açısından nasıl incelendiği — ve inceleme kültürü bu dersin konusu değildir; bunlar Kod İnceleme ve Ekip Süreci kursunda kurulur. Burada ölçülen tek şey, akışın tarihçeye ne bıraktığıdır.

Akışın Döngüsü

Değişiklik isteği (pull request), bir dalın başka bir dala alınması için yapılan biçimsel öneridir. Depo platformlarının sağladığı bir sınıf yeteneğidir; ürün adı bu derste geçmez ve akışın mekaniği ürüne bağlı değildir.

Döngü dört adımdır. Öneri: katkıcı dalını itki noktasına iter ve isteği açar; istek iki uç adı taşır — kaynak dal ve hedef dal. Tartışma: öneri okunur, sorular sorulur, kararlar alınır. Düzeltme: katkıcı dalını değiştirir ve yeniden iter; istek kendiliğinden güncellenir, çünkü istek bir dosya kopyası değil, iki dalın adına yapılmış bir göndermedir. Tümleştirme: yetkili biri isteği kapatır ve dalı hedefe alır.

Döngünün iki adımı arasına otomatik denetimler girebilir: öneri her itildiğinde bir hat çalışır ve sonucu isteğe iliştirir. Hattın kendisi bu kursun konusu değildir ve Sürekli Tümleştirme ve Teslimat kursunda kurulur; buradan görülen tek şey, denetim sonucunun da tartışma alanında durduğu ve tarihçeye girmediğidir.

Dördüncü adımın bir seçim taşıdığı çoğu zaman gözden kaçar. Tümleştirmeyi yapan kişi, dört tümleştirme biçiminden birini seçer ve bu seçim tarihçeye ne kalacağını belirler. Akışın kendisi tarafsız değildir: hangi biçimin varsayılan olduğu, deponun aylar sonra hangi soruları yanıtlayabileceğini belirler.

Öneri Bir İletidir

İsteğin bir depo platformu olmadan da karşılığı vardır ve bu karşılık akışın ne olduğunu doğrudan gösterir.

# öğretilen komut — örnek döküm, çalıştırılmamıştır

git request-pull yukari/ana https://ornek.test/katkici/olcum-aglari.git olcum

The following changes since commit a1b2c3d:
  Ana dala ölçüm dizinini ekle
are available in the Git repository at:
  https://ornek.test/katkici/olcum-aglari.git olcum
for you to fetch changes up to d4e5f6a:
  Ölçüm eşiğini yapılandırmadan oku

 olcum.py |  22 ++++++++
 ayar.py  |   6 +--
 2 files changed, 25 insertions(+), 3 deletions(-)

Çıktı üç şey taşıyor: ortak tabanın adı, katkının bulunduğu adres ve dal, ve dal ucunun adı. İçerik taşımıyor. İstek bir kopya değil, nereden getirileceğinin tarifidir; alan taraf getirir, inceler ve karar verir. Depo platformlarının sunduğu istek de aynı şeydir, üstüne bir tartışma alanı eklenmiştir.

Tartışma ve Düzeltme

Tartışmadan doğan düzeltmenin iki yolu vardır ve tarihçeye etkileri ayrıdır.

Birinci yol dala yeni işleme eklemektir. Düzeltme ayrı bir işleme olur, ayrı bir mesaj taşır ve tarihçe döngünün döndüğünü kaydeder. Ölçümün “işleme mesajına yazılan karar” sütunu bu yolu sayar.

İkinci yol dalı yeniden yazmaktır: düzeltme, kusuru getiren işlemenin içine sokulur ve dal yeniden itilir. Katkı dalı yalnız katkıcıya ait olduğu için bu, paylaşılan dalda yeniden temellendirmenin taşıdığı riski taşımaz — ama tarihçe artık düzeltmenin yapıldığını değil, yalnız son hâli gösterir. Bu ders zorlayıcı itmenin tam biçimini yazmaz; yazılması gereken tek şey şudur: karşı taraftaki dal ezilir ve ezilen hâl başka bir kopyada bulunmuyorsa geri alınamaz. Katkı dalında bu bilinçli bir tercihtir, hedef dalda değildir.

İki yol arasındaki seçim bir okunaklılık–yanıtlanabilirlik ödünleşimidir ve Birleştirme konusunun ikinci iddiasının akış içindeki karşılığıdır: temiz görünen dal, döngünün kaydını taşımayan daldır.

Hedef Dal İlerlerken

Tartışma sürerken hedef dal durmaz. Başka istekler tümleştirilir ve önerinin yaslandığı taban geriler. İsteğin gösterdiği fark bu yüzden iki ayrı biçimde hesaplanabilir.

Birinci biçim, iki dal ucunu doğrudan karşılaştırır ve hedef dalda bu arada olan her şeyi de farka katar. İkinci biçim, iki dalın ortak tabanını bulur ve katkı dalını yalnız o tabandan bugüne karşılaştırır; böylece farkta yalnız katkıcının yaptığı iş görünür. Akışların büyük bölümü ikincisini gösterir ve bu, üçüncü dersteki eskiyen taban gözleminin doğrudan sonucudur: taban gerilemiş olsa bile öneri okunur kalır.

İkisi arasındaki seçim çakışma anında görünür hâle gelir. Ortak tabana göre temiz duran bir öneri, hedef dalın son hâliyle çakışabilir; akış bunu ancak tümleştirmeyi denediğinde öğrenir. Bu yüzden birçok akış, tümleştirmeden önce katkı dalının güncel tabana taşınmasını ister ve bu istek döngüye fazladan bir tur ekler.

İsteğin kapanışı da tek biçimli değildir. Tümleştirilerek kapanan bir istek tarihçeye iz bırakır; tümleştirilmeden kapanan bir istek hiçbir iz bırakmaz. İkinci durumda öneri, tartışma ve alınan kararların tamamı yalnız akışın kendi kaydında kalır — depoda hiçbir karşılığı yoktur. Ölçüm bu dersi yalnız kapanan ve tümleştirilen istekler üzerinden kurar; kapatılan isteklerin kaybı tanım gereği tamdır.

Ölçümün Varsayımları

  • UD19 — Üç dalın her biri bir değişiklik isteğidir ve dörder işleme önerir; gelişim kurgusu değişmez, üstüne bir akış katmanı eklenir.
  • UD20 — Tartışma turu ve alınan karar sayısı kurgudan üretilir; kâhin biziz, çünkü akışı biz kurduk. Kararın içeriği ölçüme girmez, yalnız nereye yazıldığı girer.
  • UD21 — Bir karar ancak bir işleme mesajına yazıldıysa tarihçeye girer. Tartışma alanının kendisi deponun dışındadır ve ölçüm onu tarihçe saymaz.
  • UD22 — Yazılmış bir karar, yazıldığı işleme tümleştirmeden sonra ayrı okunabiliyorsa okunur kalır; sıkıştırılmış bir grupta ayrı okunabilirlik yoktur.
  • UD23 — “Gönderme yeri”, akışın istek numarasını yazabileceği işlemedir: birleştirme işlemesi ya da sıkıştırma işlemesi. Yeniden temellendirme ve ileri sarma böyle bir işleme üretmez.
  • UD24 — Dört tümleştirme biçiminin tanımı Birleştirme konusundaki tanımın aynısıdır ve burada değiştirilmez; değişen tek şey, biçimi seçenin akış olmasıdır.

Ölçüm

"""Değişiklik isteği akışı: döngüden tarihçeye ne geçiyor.

Bölüm 1 - üç istek, tartışma turu ve alınan karar.
Bölüm 2 - akışın seçtiği tümleştirme biçiminin bıraktığı soru ve karar izi.
"""
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():
    """Gerçek gelişim: üç dal, dörder işleme, biri kusuru getiriyor."""
    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):
    """Her tümleştirme biçimi ayrı bir tarihçe üretir."""
    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(t):
    return all(x.get("dal_kaydi") for x in t if not x.get("birlesme"))


def s2(t):
    k = [x for x in t if x["kusurlu"]]
    return len(k) == 1 and not k[0].get("sikisik")


def s3(t):
    return all(x.get("zaman_korundu") for x in t)


def s4(t):
    return all(isinstance(x["dosya"], str) or x.get("birlesme") for x in t)


def s5(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(t):
    return any("secilen" in x for x in t)


SORULAR = (("özellik gruplaması", s1), ("kusurun yalıtımı", s2), ("gerçek sıra", s3),
           ("dosya izi", s4), ("dalın bütünlüğü", s5), ("çakışma kararı", s6))
BICIMLER = ("birlestirme islemesi", "yeniden temellendirme", "sikistirma", "ileri sarma")


def akis(kayit):
    """Her dal bir değişiklik isteği: öneri, tartışma turu ve alınan kararlar."""
    r, istekler = uretec(TOHUM + 8), []
    for dal in DALLAR:
        tur = 1 + r(3)
        kararlar = [{"dal": dal, "tur": 1 + r(tur), "mesaja_yazildi": r(3) == 0}
                    for _ in range(2 + r(3))]
        istekler.append({"dal": dal, "onerilen": sum(1 for x in kayit if x["dal"] == dal),
                         "tur": tur, "kararlar": kararlar})
    return istekler


def okunur_kalan(t, kararlar):
    """Bir karar, yazıldığı işleme ayrı okunabiliyorsa tarihçede okunur kalır."""
    ayri = {x["dal"] for x in t if not x.get("birlesme") and not x.get("sikisik")}
    return sum(1 for k in kararlar if k["mesaja_yazildi"] and k["dal"] in ayri)


def gonderme_yeri(t):
    """Akışın istek göndermesini koyabileceği işleme: birleşme ya da sıkıştırma."""
    return sum(1 for x in t if x.get("birlesme") or x.get("sikisik"))


kayit = gelisim()
istekler = akis(kayit)
kararlar = [k for i in istekler for k in i["kararlar"]]
yazilan = sum(k["mesaja_yazildi"] for k in kararlar)

print(f"değişiklik isteği {len(istekler)} | önerilen işleme "
      f"{sum(i['onerilen'] for i in istekler)} | tartışma turu "
      f"{sum(i['tur'] for i in istekler)} | alınan karar {len(kararlar)} | "
      f"işleme mesajına yazılan {yazilan}")
for i in istekler:
    print(f"  {i['dal']:7s} önerilen {i['onerilen']}  tur {i['tur']}  "
          f"karar {len(i['kararlar'])}  mesaja yazılan "
          f"{sum(k['mesaja_yazildi'] for k in i['kararlar'])}")

print()
print("akışın seçtiği biçim    işleme  yanıtlanan soru  okunur kalan karar  "
      "gönderme yeri")
for b in BICIMLER:
    t = tumlestir(kayit, b)
    print(f"  {b:22s} {len(t):6d} {sum(f(t) for _, f in SORULAR):16d}"
          f" {okunur_kalan(t, kararlar):19d} {gonderme_yeri(t):14d}")

print()
print(f"tartışmada alınan {len(kararlar)} kararın {len(kararlar) - yazilan} tanesi "
      f"hiçbir biçimde tarihçeye girmez")
değişiklik isteği 3 | önerilen işleme 12 | tartışma turu 7 | alınan karar 11 | işleme mesajına yazılan 4
  olcum   önerilen 4  tur 3  karar 3  mesaja yazılan 1
  rapor   önerilen 4  tur 3  karar 4  mesaja yazılan 1
  kimlik  önerilen 4  tur 1  karar 4  mesaja yazılan 2

akışın seçtiği biçim    işleme  yanıtlanan soru  okunur kalan karar  gönderme yeri
  birlestirme islemesi       15                5                   4              3
  yeniden temellendirme      12                3                   4              0
  sikistirma                  3                2                   0              3
  ileri sarma                12                3                   4              0

tartışmada alınan 11 kararın 7 tanesi hiçbir biçimde tarihçeye girmez

Akışın Bıraktığı Soru

Tablonun ilk iki sütunu Birleştirme konusunun ölçtüğü sayıları tekrar veriyor: 15/5, 12/3, 3/2, 12/3. Tekrar ediyorlar çünkü ölçülen şey aynı tarihçedir. Değişen tek şey, biçimi seçenin kim olduğudur.

Bu, akışın en sessiz kararıdır. Katkıcı dalını iter, tartışma yürür, sonunda biri tümleştirir ve o kişi çoğu zaman bir varsayılanı kabul eder. Varsayılan sıkıştırmaysa, deponun ay sonunda yanıtlayabileceği soru sayısı 5’ten 2’ye iner ve bu iniş hiçbir tartışmada konuşulmamıştır. Ölçünün buradaki katkısı bir kural değil, bir görünürlüktür: seçim yapılıyor, ve yapılan seçimin bir sayısı var.

Akış bir şeyi de kesin olarak yapamaz. Altıncı soru — çakışmanın nerede çözüldüğü ve hangi tarafın seçildiği — dört biçimin dördünde de yanıtsızdır. Tartışma alanında çakışmanın nasıl çözüleceği uzun uzun konuşulmuş olabilir; tarihçe bunu kaydetmez. Akışın varlığı bu boşluğu kapatmaz, yalnız boşluğun daha büyük görünmesini engeller.

Kararların Nereye Gittiği

Üst satır akışın hacmini veriyor: 3 istek, 12 önerilen işleme, 7 tartışma turu ve 11 alınan karar. Kararların 4’ü bir işleme mesajına yazılmış, 7’si yazılmamış.

Yazılmayan yedi karar hiçbir tümleştirme biçiminde tarihçeye girmez. Bulundukları tek yer akışın kendi tartışma kaydıdır ve o kayıt deponun içinde değildir: klonlanan kopyaya gelmez, getirmeyle taşınmaz, yedeklenen depoda bulunmaz. Kursun en pratik sonucu Birleştirme konusunda çakışma kararı için yazılmıştı ve burada tam olarak tekrar ediyor — gerekçe işleme mesajına yazılmazsa hiçbir yerde yoktur, ve bu kez “hiçbir yer” deponun dışında bir yerdir.

“Okunur kalan karar” sütunu ikinci kaybı gösteriyor. Yazılmış 4 karar, üç biçimde 4 olarak kalıyor; sıkıştırmada 0’a iniyor. Sıkıştırma yazılmış gerekçeleri silmez, onları ayrı okunabilir olmaktan çıkarır: on iki işlemenin mesajı üç işlemenin mesajına iner ve hangi gerekçenin hangi değişikliğe ait olduğu artık okunamaz. Doğru davranan katkıcı bile bu kayıptan korunmuş değildir.

Son sütun ters yönde bir gözlem veriyor. Akışın istek göndermesini yazabileceği bir işleme yalnız iki biçimde vardır: birleştirme işlemesi ve sıkıştırma işlemesi, ikisinde de 3. Yeniden temellendirme ve ileri sarma böyle bir işleme üretmez ve gönderme sayısı 0’dır. Bu biçimlerde tarihçeden akışa geri dönmenin hiçbir yolu yoktur: işleme mesajlarında istek numarası bulunmuyorsa, aylar sonra bir işlemenin hangi tartışmadan çıktığı sorulduğunda yanıt yoktur.

Özet

  • Değişiklik isteği bir kopya değil, iki dalın adına yapılmış bir göndermedir; katkıcı dalını güncellediğinde istek kendiliğinden güncellenir.
  • Akışın son adımı bir tümleştirme biçimi seçer ve tarihçeye 5, 3, 2 ya da 3 soru bırakır; seçim çoğu zaman bir varsayılandır ve tartışılmaz.
  • Üç istekte alınan 11 kararın 4’ü işleme mesajına yazılır; kalan 7 hiçbir biçimde tarihçeye girmez ve yalnız deponun dışındaki tartışma kaydında durur.
  • Sıkıştırma yazılmış 4 gerekçeyi 0’a indirir: mesajlar silinmez, ayrı okunabilir olmaktan çıkar.
  • Akışa geri dönmeyi sağlayan gönderme yalnız birleştirme ve sıkıştırma işlemelerine yazılabilir; yeniden temellendirme ve ileri sarmada böyle bir işleme yoktur ve gönderme sayısı 0’dır.

Sonraki Adım

Bu dersin akışı bir depo platformu varsaydı: ortak bir adres, bir tartışma alanı ve tümleştirme düğmesine basacak bir yetki. Katkının bunların hiçbiri olmadan gittiği bir biçim daha vardır ve sürüm kontrolünün en eski katkı biçimi budur: değişikliğin dosya olarak gönderilmesi. Sonraki ders yamayı ölçer — hangi üstveriyi taşıdığını, hangisini taşımadığını, ve taşımadığı alanın altı sorunun neresine düştüğünü.

İ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