İçeriğe geç
academia.sh

Ders 04 / 15

Ayrık HEAD Durumu

Ayrık HEAD üstünde yapılan iki işleme hiçbir başvurudan erişilemediğinde tarihçe 15 kayıttan 13'e iner ve altı sorunun 5'i yerine 4'ünü yanıtlar; asıl bedel üç sayım sorgusunun üçünün de eksik yanıtlanması ve tarihçenin bunu bildirmemesidir.

İçindekiler

Önceki üç ders HEAD ile işleme arasında üç halkalı bir zincir kurdu: HEAD bir dal adını, dal adı bir işleme kimliğini, işleme kendi öncülünü gösterir. Bir dalda çalışmak bu zincirin tam olmasına dayanır — işleme yapıldığında orta halka ilerler, HEAD yerinde kalır ve yeni işleme bir ada bağlanmış olur.

Orta halka her zaman yerinde değildir. Araç HEADin doğrudan bir işleme kimliğini gösterdiği bir durum tanır ve buna ayrık HEAD (detached HEAD) denir. Bu ders o durumun nasıl oluştuğunu, orada yapılan işlemenin nereye bağlandığını ve zincirin bir halkası eksik kaldığında tarihçenin ne kaybettiğini ölçer. Ölçümün asıl sonucu kayıp miktarı değildir; tarihçenin kaybettiğini bilmemesidir.

HEAD Nasıl Ayrılır

Ayrık durum bir kusur ya da bozulma değildir; aracın belirli bir isteği karşılama biçimidir. İstek şudur: bir dalın ucunda değil, tarihçenin belirli bir noktasında durmak. Bir işleme kimliğine, bir etikete ya da bir uzak dal ucuna geçildiğinde HEAD bir ada bağlanamaz, çünkü hedef bir ad değildir.

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

$ git switch --detach 7c3f9d2
HEAD is now at 7c3f9d2 rapor başlığını biçimlendir
$ git status --short --branch
## HEAD (no branch)
$ git symbolic-ref HEAD
fatal: ref HEAD is not a symbolic ref

Son satır durumun tanımıdır. İlk derste aynı komut refs/heads/ana yanıtını veriyordu: HEAD bir başvuruyu gösteriyordu. Burada gösterecek bir başvuru yok, bu yüzden komut simgesel bir başvuru bulamıyor. git status de aynı şeyi başka sözcüklerle söylüyor.

Durum kasten seçilebilir. Bir hatanın hangi işlemede girdiğini ararken tarihçede ileri geri gezinmek, eski bir sürümü derleyip sınamak, bir etiketin gösterdiği ağacı incelemek — hepsi ayrık durumda yapılır ve hiçbiri sorun değildir. Sorun, ayrık durumda işleme yapmakla başlar.

Ayrık Durumda Yapılan İşleme

Ayrık durumda işleme yapmak engellenmez. İşleme nesnesi yazılır, öncülü doğru bağlanır, ağacı kaydedilir; içerik bakımından öteki işlemelerden hiçbir farkı yoktur. Değişen tek şey kimin ilerlediğidir: HEAD yeni kimliği doğrudan gösterir ve hiçbir dal başvurusu yazılmaz.

Bunun sonucu ikinci işlemede henüz görünmez, çünkü HEAD zinciri hâlâ o işlemeleri tutar. Sonuç, başka bir dala geçildiği anda doğar. HEAD artık başka bir dalı gösterir ve ayrık durumda yazılan işlemelere hiçbir başvurudan erişilemez. Araç bunu geçiş sırasında bildirir.

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

$ git switch rapor
Warning: you are leaving 2 commits behind, not connected to
any of your branches:

  b71c05e rapor: eşik tablosunu ayara taşı
  a04e9d2 rapor: özet satırını ekle

If you want to keep them by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> b71c05e

Switched to branch 'rapor'

Uyarı hem sayıyı hem kimliği veriyor ve çözümü de söylüyor: bir dal yazın. Birinci dersin ölçüsü bu önerinin bedelini vermişti — bir dal başvurusu 41 bayttır. İki işlemeyi tarihçeye bağlamak kırk bir bayt yazmaktır ve bu yüzden çıkış yolu ucuzdur.

Ayrık durumdan çıkmanın üç yolu vardır. git switch -c ad bulunduğunuz noktaya bir dal yazar ve ona geçer; bu, çalışmayı yerinde kurtaran yoldur. git switch <dal> bir dala döner ve ayrık işlemeleri geride bırakır. Üçüncüsü geriye dönüktür: git branch ad <kimlik> ile sonradan da bir başvuru yazılabilir — yeter ki kimlik hâlâ bilinsin.

Referans Günlüğü

Kimliği bilmenin yolu referans günlüğüdür (reflog). Araç HEADin ve her dal başvurusunun aldığı her değeri yerel bir günlüğe yazar: hangi işlemeye geçildi, hangi işleme yapıldı, hangi başvuru nereye kaydı. Günlük yereldir ve depoyla birlikte taşınmaz — kopyalanan bir depo kaynak deponun referans günlüğünü almaz.

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

$ git reflog
b71c05e HEAD@{0}: commit: rapor: eşik tablosunu ayara taşı
a04e9d2 HEAD@{1}: commit: rapor: özet satırını ekle
7c3f9d2 HEAD@{2}: checkout: moving from rapor to 7c3f9d2

$ git branch kurtarma b71c05e
$ git branch --contains b71c05e
  kurtarma

HEAD@{0} sözdizimi HEADin bir önceki konumlarını adlandırır ve bir işleme kimliği beklenen her yerde kullanılabilir. Üçüncü satır ayrılmanın kendisini kaydediyor; ilk iki satır ayrık durumda yapılan iki işlemeyi. Dal yazıldıktan sonra son komut işlemenin artık bir başvurudan erişilebildiğini doğruluyor.

Referans günlüğünün kendisi bir başvuru değildir ve erişilebilirlik hesabına girmez. Bir kurtarma aracıdır, bir saklama düzeneği değil: girdilerin ömrü sınırlıdır ve yapılandırılabilir bir süre sonunda düşerler; erişilemeyen işlemelere ait girdilerin ömrü ötekilerden kısadır.

Kurtarılamayan Sınır

Referans günlüğü girdisi düştükten sonra erişilemeyen bir işlemenin kimliğini verecek bir kayıt kalmaz. Nesne hâlâ veritabanındadır, ama adı yoktur ve aranacak bir yeri de yoktur.

Bir adım sonrası kalıcıdır. Araç, hiçbir başvurudan erişilemeyen nesneleri toplayan bir bakım komutu taşır; bu komutun budama seçeneği erişilemeyen nesneleri veritabanından siler. Bu ders o çağrıyı çalıştırılabilir biçimde yazmaz, çünkü sonucu geri alınamaz: silinen işlemenin içeriği hiçbir yerden geri getirilemez, referans günlüğü de silinmiş bir nesneyi geri koyamaz. Bakım komutu ayrıca kimi işlemlerin parçası olarak kendiliğinden de çalışabilir; erişilemeyen bir işlemeyi “sonra kurtarırım” diye bırakmak bu yüzden güvenli değildir.

Güvenli alışkanlık tek cümledir: önce başvuruyu yaz. Ayrık durumda bir iş yapılacaksa işe başlamadan bir dal açılır; iş bittikten sonra fark edildiyse referans günlüğü hâlâ sıcakken bir yedek dal yazılır. Kurtarmanın tüm maliyeti kırk bir bayttır ve bu bayt yazılmadığı sürece iş tek bir bakım çağrısı uzaklıktadır.

Ölçümün varsayımları:

  • DL22 — Kurgu ortak tanımın on iki işlemesidir ve gelisim değiştirilmez. Ders kurgunun tek bir adımını değiştirir: rapor işinin ikinci ve üçüncü işlemesi ayrık HEAD üstünde yapılmıştır ve dal ucuna bağlanmamıştır.
  • DL23 — Erişilebilirlik başvurulardan başlar. Bir işlemeye hiçbir dal başvurusundan ulaşılamıyorsa o işleme tarihçenin dışındadır; nesne veritabanında durması bunu değiştirmez.
  • DL24 — Ölçüm tek bir tümleştirme biçimi kullanır: birleştirme işlemesi. Tanımı ortak tanımdan alınır ve değiştirilmez; bu ders biçimleri karşılaştırmaz.
  • DL25 — Altı sorunun tanımı değiştirilmez ve yalnız erişilebilen tarihçeye sorulur. Soru, kendisine verilmeyen bir işlemeden haberdar olamaz.
  • DL26 — Üç sayım sorgusu ayrıca sorulur ve kâhinle karşılaştırılır: rapor işine ait işleme sayısı, kusuru getiren işleme sayısı, ortak dosyaya dokunan işleme sayısı. Kâhin kurguyu bildiği için gerçek sayıyı verir.
  • DL27 — Referans günlüğü bir başvuru değildir ve erişilebilirlik hesabına girmez; yalnız erişilemeyen bir işlemenin kimliğini bulmaya yarar.
  • DL28 — Kurtarma, düşen işlemeleri bir başvuruya bağlamaktır. Ölçüm kurtarılmış tarihçeyi düşenleri geri koyup zaman sırasına dizerek kurar; kurtarma işlemelerin içeriğini de sırasını da değiştirmez.
  • DL29 — Kurtarma maliyeti tek bir dal başvurusudur: kırk onaltılık basamak ve bir satır sonu, 41 bayt. Birinci dersin ölçüsü burada aynen geçerlidir.

Ölçüm

"""Ayrik HEAD: hicbir basvurudan erisilemeyen isleme tarihcenin disindadir.

Bolum 1 - ayrik durumda yapilan iki isleme tarihceden dusuyor.
Bolum 2 - alti soru uc tarihceye soruluyor: hangi soru disarida kaliyor.
Bolum 3 - sayim sorgulari kahinle karsilastiriliyor: yanit var ama eksik.
"""
TOHUM = 20260813
DALLAR = ("olcum", "rapor", "kimlik")
DOSYALAR = {"olcum": "olcum.py", "rapor": "rapor.py", "kimlik": "kimlik.py"}
KUSURLU = ("rapor", 2)
ORTAK_DOSYA = "ayar.py"
AYRIK = ("rapor", (2, 3))     # bu iki isleme ayrik HEAD ustunde yapildi
BASVURU_BAYT = 40 + 1


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 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))


def erisilebilen(kayit, dusen):
    dal, siralar = dusen
    return [k for k in kayit if not (k["dal"] == dal and k["sira"] in siralar)]


def kurtar(kalan, dusenler):
    """Referans gunlugunden bulunan islemeler bir basvuruya baglanir."""
    return sorted(kalan + dusenler, key=lambda x: x["zaman"])


def sorgula(kume):
    """Tarihceye ya da kurguya sorulan uc sayim sorgusu."""
    return {"rapor işine ait işleme": sum(1 for x in kume
                                          if x["dal"] == "rapor"
                                          and not x.get("birlesme")),
            "kusuru getiren işleme": sum(1 for x in kume if x["kusurlu"]),
            "ayar.py'ye dokunan işleme": sum(1 for x in kume
                                             if x["dosya"] == ORTAK_DOSYA)}


kayit = gelisim()
kalan = erisilebilen(kayit, AYRIK)
dusen = [k for k in kayit if k["dal"] == AYRIK[0] and k["sira"] in AYRIK[1]]
TARIHCELER = (("bağlı HEAD", kayit), ("ayrık HEAD", kalan),
              ("kurtarılmış", kurtar(kalan, dusen)))
T = {ad: tumlestir(k, "birlestirme islemesi") for ad, k in TARIHCELER}

print(f"kurgu: {len(kayit)} işleme; ayrık HEAD üstünde yapılan {len(dusen)} "
      f"işleme hiçbir başvurudan erişilemez")
print()
print(f"{'tarihçe':<14s}{'erişilen işleme':>16s}{'tarihçe işlemesi':>17s}"
      f"{'erişilemeyen':>13s}{'yanıtlanan':>11s}")
for ad, k in TARIHCELER:
    print(f"  {ad:<12s}{len(k):16d}{len(T[ad]):17d}"
          f"{len(kayit) - len(k):13d}"
          f"{sum(soru(T[ad]) for _, soru in SORULAR):11d}")
print()
BASLIK = (("bağlı HEAD", 12), ("ayrık HEAD", 12), ("kurtarılmış", 13))
print(f"{'soru':<22s}" + "".join(f"{a:>{w}s}" for a, w in BASLIK))
for ad, soru in SORULAR:
    print(f"  {ad:<20s}" + "".join(
        f"{('evet' if soru(T[a]) else 'hayır'):>{w}s}" for a, w in BASLIK))
print()
kahin = sorgula(kayit)
ayrik, geri = sorgula(T["ayrık HEAD"]), sorgula(T["kurtarılmış"])
print(f"{'sayım sorgusu':<28s}{'kâhin':>7s}{'ayrık HEAD':>12s}{'eksik':>7s}"
      f"{'kurtarılmış':>13s}")
for s in kahin:
    print(f"  {s:<26s}{kahin[s]:7d}{ayrik[s]:12d}{kahin[s] - ayrik[s]:7d}"
          f"{geri[s]:13d}")
print(f"\nüç sorgunun {sum(1 for s in kahin if kahin[s] != ayrik[s])}'ü eksik "
      f"yanıtlanıyor; tarihçe eksik olduğunu bildirmiyor")
print(f"kurtarma maliyeti: {len(dusen)} işlemeyi geri bağlamak için "
      f"1 başvuru, {BASVURU_BAYT} bayt")
kurgu: 12 işleme; ayrık HEAD üstünde yapılan 2 işleme hiçbir başvurudan erişilemez

tarihçe        erişilen işleme tarihçe işlemesi erişilemeyen yanıtlanan
  bağlı HEAD                12               15            0          5
  ayrık HEAD                10               13            2          4
  kurtarılmış               12               15            0          5

soru                    bağlı HEAD  ayrık HEAD  kurtarılmış
  özellik gruplaması          evet        evet         evet
  kusurun yalıtımı            evet       hayır         evet
  gerçek sıra                 evet        evet         evet
  dosya izi                   evet        evet         evet
  dalın bütünlüğü             evet        evet         evet
  çakışma kararı             hayır       hayır        hayır

sayım sorgusu                 kâhin  ayrık HEAD  eksik  kurtarılmış
  rapor işine ait işleme          4           2      2            4
  kusuru getiren işleme           1           0      1            1
  ayar.py'ye dokunan işleme       2           1      1            2

üç sorgunun 3'ü eksik yanıtlanıyor; tarihçe eksik olduğunu bildirmiyor
kurtarma maliyeti: 2 işlemeyi geri bağlamak için 1 başvuru, 41 bayt

Sayıların Okunması

Üst tablo kaybın büyüklüğünü veriyor. Erişilen işleme 12’den 10’a, tarihçedeki kayıt 15’ten 13’e iniyor; erişilemeyen 2. Yanıtlanan soru 5’ten 4’e düşüyor. On iki işlemelik kümede kayıp 2/12, altı soruluk kümede 1/6 — ikisi de kümelerin çözünürlüğünde ve savunulabilir.

Orta tablo hangi sorunun düştüğünü söylüyor ve yanıt tektir: kusurun yalıtımı. Kusuru getiren işleme rapor işinin ikincisiydi ve ayrık durumda yapılanlardan biriydi. Tarihçe artık kusurlu bir işleme görmüyor; soru “kusuru getiren işleme tek başına bulunabiliyor mu” diye sorulduğunda yanıt hayır oluyor. Bu, kaybın bildirilen yüzüdür: tarihçe bir soruyu yanıtlayamadığını söylüyor.

Öteki beş satırın hiçbiri değişmiyor ve dersin asıl sonucu buradan çıkıyor. Özellik gruplaması üç durumda da evet; kalan on işlemenin hepsi bir dal kaydı taşıyor ve tarihçe kendi içinde tutarlı. Dosya izi de üç durumda da evet; kalan her işleme bir dosya adı taşıyor. Tarihçe eksik olduğunun farkında değildir, çünkü eksiklik ancak dışarıdan bir ölçüyle görülür.

Alt tablo o dış ölçüyü koyuyor. Üç sayım sorgusunun 3’ü de eksik yanıtlanıyor. rapor işine ait işleme sayısı 4 yerine 2 çıkıyor — grup okunabiliyor ama yarısı eksik. Kusuru getiren işleme 1 yerine 0 çıkıyor. Ortak dosyaya dokunan işleme 2 yerine 1 çıkıyor — dosya izi sorusu evet diyor, verdiği iz yarım. Hiçbir satırda bir hata iletisi yok; üç yanıt da makul görünüyor ve üçü de yanlış.

İki tür kaybın ayrımı budur. Bir soru düştüğünde tarihçe hayır der ve okuyan bunu bilir. Bir işleme düştüğünde tarihçe evet demeyi sürdürür ve okuyan yanlış bir sayıyla kalır. Yanıtlanan soru sayısındaki 1/6’lık düşüş, gerçek kaybın yalnız görünen kısmıdır.

Son iki satır kurtarmayı ölçüyor. Düşen iki işleme bir başvuruya bağlandığında erişilen işleme 12’ye, tarihçe 15’e, yanıtlanan soru 5’e dönüyor ve üç sayım sorgusu kâhinle birebir uyuşuyor. Kurtarmanın maliyeti 41 bayttır. Birinci dersin ölçüsü tam tersini de söylemişti: bir başvuruyu yazmak kırk bir bayttı, dolayısıyla o başvuruyu yazmamak da kırk bir baytlık bir eksiklikti. Ayrık durumun tüm hikâyesi yazılmayan bu kırk bir bayttır.

Özet

  • Ayrık HEAD, HEADin bir dal adı yerine doğrudan bir işleme kimliğini gösterdiği durumdur; git symbolic-ref HEAD başarısız olur ve durum bir kusur değil, bir konumlanma biçimidir.
  • Ayrık durumda yapılan işleme normal biçimde yazılır ama hiçbir dal başvurusu ilerlemez; başka bir dala geçildiğinde o işlemelere hiçbir başvurudan erişilemez.
  • Erişilemeyen 2 işleme tarihçeyi 15 kayıttan 13’e indirir ve yanıtlanan soruyu 5’ten 4’e düşürür; düşen tek soru kusurun yalıtımıdır.
  • Asıl bedel üç sayım sorgusunun 3’ünün de eksik yanıtlanmasıdır: tarihçe evet demeyi sürdürür, verdiği sayılar 4 yerine 2, 1 yerine 0, 2 yerine 1’dir.
  • Referans günlüğü HEADin geçmiş konumlarını tutar ve kurtarmayı 41 bayta indirir; günlük girdisi düştükten sonra nesne toplama kaybı kalıcı kılar ve geri alınamaz.

Sonraki Adım

Bu konu dallanmanın açılış tarafını bitirdi. Dal açmak bir addır ve 123 bayt yazar; adı düzenli koymak tarihçenin birinci sorusuna kaydın tek başına veremediğini ekler; adın hiç yazılmaması ise iki işlemeyi tarihçenin dışına düşürür. Üç ölçüm de aynı yöne bakıyor: bir işlemeler kümesini bir işe bağlayan şey başvurudur.

Geriye çatallanmanın öteki ucu kalıyor. Üç dal açıldı, on iki işleme yazıldı ve her biri kendi ucunda duruyor; bir noktada bu uçların ana dala alınması gerekir. Sonraki konu birleşmenin tarihçeye ne yazdığını sorar: araç iki ucu kapatırken yeni bir kayıt üretir mi, üretmezse ne kaybolur, ve kapatma biçimi altı sorunun kaçını yanıtlanabilir bırakır. Kurs buradan itibaren bu ölçünün üstünde yürür.

İ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