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
gelisimdeğiştirilmez. Ders kurgunun tek bir adımını değiştirir:raporiş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:
raporiş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 HEADbaş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.