IlmHamroh
JavaScript Full-stack/7-qism. Git va GitHub asoslari12/36-dars19 daqiqa
Mundarija (31)

Git HEAD va refs: branch, detached HEAD va ORIG_HEAD

Qisqacha: Branch — commit hashi yozilgan kichik fayl (.git/refs/heads/main). HEAD — "hozir qayerdasiz" degan ko'rsatkich, odatda branch nomiga ishora qiladi: ref: refs/heads/main. Commit qilganda Git branch faylidagi hashni yangilaydi. HEAD to'g'ridan-to'g'ri commitga ishora qilsa — bu detached HEAD: xavfli emas, faqat yangi commitlarni branch bilan saqlab qo'yish kerak.

Bu darsda

  • .git/HEAD va .git/refs ichidagi fayllarni o'qiysiz va commit ularni qanday o'zgartirishini ko'rasiz.
  • Symbolic ref nima ekanini va git rev-parse bilan istalgan nomni hashga aylantirishni bilasiz.
  • Detached HEAD holatiga ataylab kirasiz, unda commit qilasiz va ishni yo'qotmasdan chiqasiz.
  • ORIG_HEAD bilan reset'ni bekor qilasiz, FETCH_HEAD va packed-refs ni taniysiz.

Oldin bilishingiz kerak: Git ichkaridan: obyektlar, SHA va snapshot, Xatoni tuzatish 2: reset va revert.

1. Nega bu kerak?

Oldingi darsda Git omborini ochdik: blob, tree, commit — hammasi hash nomli obyektlar. Lekin bir savol qoldi. 07b061d kabi hashlarni hech kim yodlamaydi. Biz esa har kuni main, HEAD, HEAD~1 deb yozamiz. Bu nomlar qayerda saqlanadi va qanday qilib to'g'ri commitni topadi?

Javobni bilmagan odam uchun bir holat ayniqsa qo'rqinchli. Bir kuni terminalda shu xabar chiqadi:

text
You are in 'detached HEAD' state.

"Ajralgan HEAD"! Nimadir buzildimi? Ko'pchilik shu yerda vahimaga tushadi. Aslida bu oddiy holat. Dars oxirida uni o'zingiz ataylab yaratasiz va xotirjam chiqasiz.

Hayotiy o'xshatish: kitobdagi xatcho'plar. Kitob — commitlar tarixi. Xatcho'p — branch: "shu betdaman" deb qo'yilgan yorliq. Xatcho'pni istalgan betga ko'chirish mumkin, kitob o'zgarmaydi. HEAD esa sizning barmog'ingiz: hozir qaysi xatcho'pni ushlab o'qiyapsiz. Ba'zan barmoq xatcho'psiz, to'g'ridan-to'g'ri betni ushlaydi. Bu — detached HEAD.

2. Ref — commitga yopishtirilgan nom

2.1 main fayli

«Bahor» reposida oxirgi commitlar:

bash
git log --oneline -3
text
07b061d (HEAD -> main) Menyu: lag'mon narxini qo'sh
bd98d1b Bron: rahmat sahifasini qo'sh
b09fa2a Aloqa: sahifa yarat

(Misollarni Birinchi repo darsi oxiridagi «Bahor»da ko'rsatamiz. Sizda keyingi darslardagi commitlar ham bor — hashlar boshqacha bo'ladi.)

Qavs ichidagi (HEAD -> main) ni oldingi darslarda ko'rgansiz. Bugun uning ma'nosini ochamiz. Avval main qayerda ekanini topamiz:

bash
cat .git/refs/heads/main
text
07b061d79648257f6761433b3704325a26bbab77

Hammasi shu! main branch'i — bitta qatorli matn fayli, ichida commitning 40 belgili hashi. Boshqa hech narsa yo'q.

Ref (reference, havola) — commit (yoki boshqa obyekt) hashini saqlaydigan nomli ko'rsatkich. Branch — ref'ning bir turi. Barcha ref'lar .git/refs papkasida turadi:

bash
ls .git/refs .git/refs/heads
text
.git/refs:
heads
tags

.git/refs/heads:
main

heads — branch'lar (Git'da branch'ning ichki nomi "head" — "uch", tarmoq uchi). tags — tag'lar. Hozircha bitta branch — main.

2.2 HEAD fayli

bash
cat .git/HEAD
text
ref: refs/heads/main

HEAD faylida hash emas, boshqa ref'ning nomi yozilgan: "men refs/heads/main ga ishora qilaman". Boshqa ref'ga ishora qiladigan ref symbolic ref (ramziy havola) deyiladi. Uni maxsus buyruq bilan ham o'qish mumkin:

bash
git symbolic-ref HEAD
text
refs/heads/main

Endi (HEAD -> main) ning ma'nosi aniq: "HEAD main ga ishora qiladi, main esa shu commitga". Zanjir ikki qadamli:

flowchart LR
  H["HEAD<br/>ref: refs/heads/main"] --> M["main<br/>07b061d…"]
  M --> C["commit 07b061d<br/>Menyu: lag'mon"]
  C --> P["commit bd98d1b"]

Nimaga qarang: HEAD commitni to'g'ridan-to'g'ri bilmaydi — u faqat branch nomini biladi. Commitni branch biladi.

Tekshirib ko'ring: .git/refs/heads/main faylini o'chirsangiz (buni qilmang!), 07b061d commiti yo'qoladimi?

Javob

Yo'q. Commit — .git/objects dagi obyekt, ref esa unga yopishtirilgan nom. Nom yo'qolsa, commit omborda qoladi, faqat unga "yetib boradigan" yo'l yo'qoladi. Xuddi xatcho'p tushib qolsa ham, kitob betlari joyida qolgani kabi. Topib olish usullarini Reflog darsida o'rganamiz.

3. Commit yorliqni suradi

Aziz bosh sahifaga ish vaqtini qo'shdi va commit qildi:

bash
echo '<p>Ish vaqti: 08:00–23:00</p>' >> index.html
git add index.html
git commit -m "Bosh sahifa: ish vaqtini qo'sh"
text
[main a0e2a64] Bosh sahifa: ish vaqtini qo'sh
 1 file changed, 1 insertion(+)

(git add CRLF ogohlantirishini chiqardi — bu darsda uni tashlab ketamiz.) Ikkala faylga yana qaraymiz:

bash
cat .git/refs/heads/main
cat .git/HEAD
text
a0e2a6459d40b45c7e214df2ee02a359f41d1806
ref: refs/heads/main

main fayli yangi hashni oldi. HEAD esa o'zgarmadi — u hamon main ga ishora qiladi. Commit paytida Git uch ish qiladi:

  1. Yangi commit obyektini yozadi. Uning parent i — HEAD orqali topilgan eski commit (07b061d).
  2. HEAD qaysi branch'ga ishora qilayotganini aniqlaydi — main.
  3. main faylidagi hashni yangi commitga almashtiradi.

Branch shuning uchun "o'sadi": har commitda yorliq bir qadam oldinga siljiydi. Reset darsida esa reset aynan shu faylni orqaga — eski hashga — yozgan edi. --soft, --mixed, --hard farqi — faqat qutiga va stolga nima bo'lishida.

4. git rev-parse — nomdan hashga

Faylni cat bilan o'qish ta'lim uchun yaxshi, lekin ishda Git'ning o'z buyrug'i ishlatiladi. git rev-parse istalgan nomni to'liq hashga aylantiradi:

bash
git rev-parse HEAD
git rev-parse --short HEAD
git rev-parse main
git rev-parse HEAD~2
text
a0e2a6459d40b45c7e214df2ee02a359f41d1806
a0e2a64
a0e2a6459d40b45c7e214df2ee02a359f41d1806
bd98d1bb2aeffb83a40f6745994ca8670af0a34d

Git nomni qanday tushunadi? U bir necha joyni tartib bilan tekshiradi: .git dagi maxsus nomlar (HEAD kabi), keyin refs/tags/, refs/heads/ va boshqa papkalar. HEAD~2 kabi yozuvda avval HEAD ni topadi, keyin ota zanjiri bo'ylab ikki qadam yuradi (Tarixni o'qish).

Yana bir necha foydali shakl:

Buyruq Nima beradi
git rev-parse --abbrev-ref HEAD Hozirgi branch nomi (main)
git rev-parse @ @ — HEAD ning qisqa yozuvi
git rev-parse --show-toplevel Repo ildiz papkasi (Birinchi repo)
git rev-parse --verify nom Nom mavjudligini tekshirish

Mavjud bo'lmagan nom bilan:

bash
git rev-parse --verify yoq-branch
text
fatal: Needed a single revision

Tarjimasi: "Bitta aniq revision (commit) kerak edi". Ya'ni yoq-branch nomidan hech qanday commit topilmadi. Skriptlarda bu xato "bunday branch yo'q" degan javob sifatida ishlatiladi.

Barcha ref'larni bir ro'yxatda git show-ref beradi:

bash
git tag -a v1.0 -m "Bahor sayti: birinchi versiya"
git show-ref
text
a0e2a6459d40b45c7e214df2ee02a359f41d1806 refs/heads/main
466a40f8f7939738aa26248689270b4388406a1f refs/tags/v1.0

Tag ham ref — refs/tags/v1.0 fayli. Uning hashi commitniki emas, chunki annotated tag o'zi alohida obyekt (oldingi darsda ko'rgan edik). Tag'larni Tag va relizlar darsida o'rganamiz.

Tekshirib ko'ring: Yangi commitdan keyin .git/HEAD fayli o'zgaradimi? .git/refs/heads/main-chi?

Javob

.git/HEAD o'zgarmaydi — unda ref: refs/heads/main yozuvi qoladi. .git/refs/heads/main o'zgaradi — unga yangi commitning hashi yoziladi. HEAD branch'ni ko'rsatadi, branch esa commitni.

5. Detached HEAD

5.1 Eski commitga borish

Jasur aka so'radi: "Menyu birinchi narx qo'shilgan kuni qanday ko'rinardi?" Versiya nazorati darsida bitta faylni git show dab5bff:menyu/index.html bilan ko'rgan edik. Lekin butun saytni brauzerda ochib ko'rmoqchi bo'lsak, fayllarning o'zi o'sha holatga qaytishi kerak.

Restore darsida git switch bilan tanishgansiz. Unga commit hashini beramiz:

bash
git switch f4238e7
text
fatal: a branch is expected, got commit 'f4238e7'
hint: If you want to detach HEAD at the commit, try again with the --detach option.

Tarjimasi: "Branch kutilgan edi, commit berildi. Maslahat: agar HEAD'ni shu commitda ajratmoqchi bo'lsangiz, --detach bilan qayta urining". switch ehtiyotkor: u ataylab so'raydi. Maslahatga amal qilamiz:

bash
git switch --detach f4238e7
text
HEAD is now at f4238e7 Menyu: osh narxini qo'sh

5.2 Nima o'zgardi?

bash
cat .git/HEAD
text
f4238e7b653db61eee50abc338446f66b8a7634d

HEAD faylida endi ref: ... emas, to'g'ridan-to'g'ri commit hashi. HEAD branch'dan "ajraldi" — shuning uchun detached HEAD (ajralgan HEAD) deyiladi.

flowchart LR
  H["HEAD<br/>f4238e7…"] --> C1["commit f4238e7<br/>osh narxi"]
  M["main<br/>a0e2a64…"] --> C3["commit a0e2a64<br/>ish vaqti"]
  C3 --> C2["… 07b061d …"]
  C2 --> C1

Nimaga qarang: main joyida — u hamon eng yangi commitni ko'rsatadi. Faqat HEAD eski commitga ko'chdi. Hech narsa o'chmadi.

Git holatni aniq aytadi:

bash
git status
cat menyu/index.html
text
HEAD detached at f4238e7
nothing to commit, working tree clean
<h1>Menyu</h1>
<p>Osh — 35 000 so'm</p>

Menyuda lag'mon yo'q — fayllar o'sha kundagi holatda. Endi index.html ni brauzerda ochsangiz, saytning o'sha kungi ko'rinishini ko'rasiz. git log --oneline esa faqat f4238e7 va undan oldingisini ko'rsatadi: HEAD dan orqaga yuradi. Yangi commitlarni ko'rish uchun --all kerak:

bash
git log --oneline --all -3
text
a0e2a64 (tag: v1.0, main) Bosh sahifa: ish vaqtini qo'sh
07b061d Menyu: lag'mon narxini qo'sh
bd98d1b Bron: rahmat sahifasini qo'sh

E'tibor bering: main yonida endi HEAD -> yo'q — HEAD boshqa joyda.

5.3 Detached holatda commit

Detached HEAD'da commit qilish mumkin. Aziz sinov uchun eski menyuga choy narxini qo'shdi:

bash
echo "<p>Choy — 3 000 so'm</p>" >> menyu/index.html
git commit -a -m "Menyu: choy narxini qo'sh (sinov)"
git log --oneline -3
text
[detached HEAD c1ecb39] Menyu: choy narxini qo'sh (sinov)
 1 file changed, 1 insertion(+)
c1ecb39 (HEAD) Menyu: choy narxini qo'sh (sinov)
f4238e7 Menyu: osh narxini qo'sh
dab5bff Bahor: sayt tuzilmasini yarat

Commit natijasida branch nomi o'rnida detached HEAD yozildi. Git yangi commit yaratdi va HEAD faylini yangiladi — boshqa hech qanday ref'ni emas. Bu commitni faqat HEAD "biladi".

5.4 Chiqish va ogohlantirish

main ga qaytamiz:

bash
git switch main
text
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  c1ecb39 Menyu: choy narxini qo'sh (sinov)

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

 git branch <new-branch-name> c1ecb39

Switched to branch 'main'

Tarjimasi: "Ogohlantirish: siz birorta branch'ingizga ulanmagan 1 ta commitni ortda qoldiryapsiz: c1ecb39. Uni saqlamoqchi bo'lsangiz, hozir yangi branch yaratish ayni vaqti: git branch <yangi-branch-nomi> c1ecb39. main branch'iga o'tildi".

Git ogohlantirdi, lekin to'xtatmadi. Endi HEAD yana main da, c1ecb39 ga esa hech qaysi ref ishora qilmaydi. git log uni ko'rsatmaydi. Commit omborda bir muddat qoladi, lekin nomsiz.

Git'ning o'zi aytgan yo'l bilan qutqaramiz. git branch nom hash — shu commitga yangi branch (yorliq) yopishtiradi. Branch'lar bilan to'liq ishlashni Branch nima darsida o'rganamiz. Hozir faqat bitta buyruq:

bash
git branch chore/eski-menyu-sinov c1ecb39
git branch
text
  chore/eski-menyu-sinov
* main

Sinov commiti endi nomli — chore/eski-menyu-sinov. U yo'qolmaydi. Nomdagi chore/ — "xizmat ishi" prefiksi; branch nomlash qoidalarini Branch nima darsida ko'ramiz. * — HEAD qaysi branch'da ekanini ko'rsatadi.

Maslahat: Detached HEAD'da commit qilmoqchi bo'lsangiz, oldindan branch yarating: git switch -c yangi-nom. Shunda chiqishda hech narsa "ortda" qolmaydi. -c (create) ni Branch nima darsida batafsil ko'rasiz.

5.5 Eski usul: git checkout

Eski maqolalarda va ko'p dasturchilarning odatida git checkout <hash> uchraydi. U ogohlantirishsiz, darhol detached holatga o'tadi — lekin uzun tushuntirish chiqaradi:

bash
git checkout dab5bff
text
Note: switching to 'dab5bff'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at dab5bff Bahor: sayt tuzilmasini yarat

Tarjimasi (asosiy qismi): "Siz 'detached HEAD' holatidasiz. Atrofga qarashingiz, tajriba uchun o'zgartirishlar kiritib, ularni commit qilishingiz mumkin. Branch'ga qaytsangiz, bu commitlarni hech qaysi branch'ga ta'sir qilmasdan tashlab ketishingiz mumkin. Yaratgan commitlaringizni saqlamoqchi bo'lsangiz, switch -c bilan yangi branch yarating".

Ya'ni Git'ning o'zi detached HEAD'ni xavfsiz "sayr qilish rejimi" deb tushuntiradi. Orqaga qaytish uchun git checkout - yoki git switch - — "oldingi joyga". Restore darsida aytilganidek, checkout juda ko'p ish qiladi, shuning uchun yangi kodda switch --detach aniqroq.

bash
git checkout -
text
Previous HEAD position was dab5bff Bahor: sayt tuzilmasini yarat
Switched to branch 'main'

Tekshirib ko'ring: Detached HEAD'da ikki commit qildingiz va git switch main bilan chiqdingiz. Ogohlantirishni o'qimay o'tib ketdingiz. Commitlar o'chib ketdimi?

Javob

Darhol o'chmaydi — ular omborda qoladi, faqat hech qanday branch ularni ko'rsatmaydi. Hashini bilsangiz (ogohlantirishda yozilgan edi), git branch nom hash bilan qutqarasiz. Hashni unutgan bo'lsangiz, uni Reflog topib beradi. Uzoq vaqt nomsiz qolgan commitlarni esa git gc oxir-oqibat o'chiradi.

6. Maxsus HEAD'lar: ORIG_HEAD va FETCH_HEAD

.git ichida HEAD dan boshqa "…_HEAD" fayllar ham paydo bo'ladi. Ikkitasi eng ko'p uchraydi.

6.1 ORIG_HEAD — xavfli amaldan oldingi joy

reset, merge va rebase kabi HEAD ni katta masofaga suradigan buyruqlar eski joyni ORIG_HEAD ga yozib qo'yadi. Sinab ko'ramiz. Aziz sinov commit qildi va uni reset --hard bilan tashladi:

bash
echo 'body { background: red; }' >> assets/css/asosiy.css
git commit -a -m "Sinov: fonni qizil qil"
git reset --hard HEAD~1
cat .git/ORIG_HEAD
git log --oneline -1 ORIG_HEAD
text
[main 1043d95] Sinov: fonni qizil qil
 1 file changed, 1 insertion(+)
HEAD is now at a0e2a64 Bosh sahifa: ish vaqtini qo'sh
1043d95e62a17a299e9cf27f8b3fe690c0a09ab6
1043d95 Sinov: fonni qizil qil

ORIG_HEAD reset'dan oldingi commitni eslab qoldi. Aziz fikridan qaytdi: "Yo'q, u commit kerak edi". Bitta buyruq:

bash
git reset --hard ORIG_HEAD
git log --oneline -2
text
HEAD is now at 1043d95 Sinov: fonni qizil qil
1043d95 (HEAD -> main) Sinov: fonni qizil qil
a0e2a64 (tag: v1.0) Bosh sahifa: ish vaqtini qo'sh

Reset bekor qilindi. Faqat bitta cheklov bor: ORIG_HEAD har yangi reset, merge yoki rebase'da qayta yoziladi — faqat oxirgi xavfli amalni eslaydi. Uzoqroq tarix uchun Reflog bor. Va eslang: commit qilinmagan ishni ORIG_HEAD ham qaytarmaydi (Reset darsidagi ogohlantirish).

(Sinovdan keyin biz yana git reset --hard HEAD~1 bilan qizil fonni olib tashladik.)

6.2 FETCH_HEAD — oxirgi olib kelingan narsa

Remote (masofaviy repo) — boshqa joydagi (masalan GitHub'dagi) nusxa. Undan yangi commitlarni olib kelish — git fetch. Bularni Remote darsida to'liq o'rganamiz. Hozir faqat FETCH_HEAD ni ko'rish uchun kichik sinov: «Bahor»ning nusxasini sinov-git/ ga olamiz. git clone — reponi nusxalash:

bash
cd ~/kurs
git clone -q bahor sinov-git/bahor-nusxa

Keyin asl «Bahor»ga somsa narxini qo'shib commit qildik va nusxaga qaytib, yangilikni olib keldik:

bash
cd sinov-git/bahor-nusxa
git fetch
cat .git/FETCH_HEAD
text
From C:/Users/.../kurs/bahor
   a0e2a64..5306a93  main       -> origin/main
5306a93db0276940096ac030e29d8a8826e211bd		branch 'main' of C:/Users/.../kurs/bahor
c1ecb39929fcba59b4a63316810fffb37e8e1d53	not-for-merge	branch 'chore/eski-menyu-sinov' of C:/Users/.../kurs/bahor

Birinchi qatorlar — git fetch ning o'z hisoboti: main a0e2a64 dan 5306a93 gacha yangilandi. origin/main — nusxadagi "asl repodagi main qayerda" degan eslatma; uni ham Remote darsida ochamiz. Keyingi qatorlar — .git/FETCH_HEAD faylining o'zi.

FETCH_HEAD — git fetch nimani olib kelganining ro'yxati: har branch uchun hash va qayerdan kelgani. Birinchi qator — asosiy natija, not-for-merge belgili qatorlar — qo'shimcha. git pull (u ham Remote darsida) aynan shu fayldan foydalanadi. Siz uni kamdan-kam o'qiysiz, lekin .git ichida ko'rsangiz — endi taniysiz.

Sinov tugadi: sinov-git/bahor-nusxa ni o'chirishingiz mumkin.

7. packed-refs — ref'lar bitta faylda

Oldingi darsda git gc obyektlarni bitta to'plamga yig'ishini ko'rdik. Ref'lar bilan ham shunday bo'ladi. «Bahor»da git gc ni ishga tushiramiz:

bash
cd ~/kurs/bahor
git gc -q
ls .git/refs/heads .git/refs/tags
text
.git/refs/heads:
chore

.git/refs/tags:

Branch fayllari yo'q! heads da faqat bo'sh chore papkasi qoldi (branch nomidagi / papka yaratadi), tags umuman bo'sh. Branch'lar qayerga ketdi?

bash
cat .git/packed-refs
text
# pack-refs with: peeled fully-peeled sorted 
c1ecb39929fcba59b4a63316810fffb37e8e1d53 refs/heads/chore/eski-menyu-sinov
5306a93db0276940096ac030e29d8a8826e211bd refs/heads/main
466a40f8f7939738aa26248689270b4388406a1f refs/tags/v1.0
^a0e2a6459d40b45c7e214df2ee02a359f41d1806

Hamma ref'lar bitta packed-refs fayliga yozildi. ^a0e2a64… qatori — yuqoridagi annotated tag qaysi commitga ishora qilishi (tezroq topish uchun). Keyingi commitda Git main ni yana alohida faylga yozadi — alohida fayl packed-refs dagidan ustun turadi.

Bundan muhim xulosa: ref'larni o'qish uchun fayllarga emas, buyruqlarga tayaning. cat .git/refs/heads/main gc dan keyin "fayl topilmadi" deydi, git rev-parse main esa har doim ishlaydi. Darsda fayllarni faqat tushunish uchun ochdik.

Yana bir sabab bor. Rasmiy "BreakingChanges" hujjatida yozilishicha, kelajakdagi Git 3.0 da yangi repolar ref'larni "files" o'rniga reftable degan yangi formatda saqlaydi. Unda refs/heads/main kabi alohida fayllar bo'lmaydi. Sabablaridan biri Windows va macOS'ga tegishli: bu tizimlar fayl nomida katta-kichik harfni farqlamaydi. Shuning uchun "files" formatida faqat harf kattaligi bilan farq qiladigan ikki ref'ni (masalan Menyu va menyu branch'lari) saqlab bo'lmaydi. Chiqish sanasi hali belgilanmagan. git rev-parse va git show-ref esa ikkala formatda ham bir xil ishlaydi.

8. Ko'p uchraydigan xatolar

8.1 Detached HEAD'dan qo'rqish

Detached HEAD — xato emas, rejim. Eski versiyani ko'rish, eski relizni tekshirish, tajriba qilish — hammasi shu rejimda bo'ladi. Faqat bitta qoida: bu yerda qilingan commitlarni chiqishdan oldin branch bilan saqlang.

8.2 Ogohlantirishni o'qimasdan chiqish

Warning: you are leaving 1 commit behind — eng muhim satr. Unda qutqarish buyrug'i tayyor yozilgan: git branch <new-branch-name> <hash>. Hashni nusxalab oling.

8.3 switch ga hash berish

text
fatal: a branch is expected, got commit 'f4238e7'

switch faqat branch'larga o'tadi. Commitga borish uchun --detach kerak — hint qatori buni aytadi.

8.4 .git fayllarini qo'lda tahrirlash

echo hash > .git/refs/heads/main "ishlaydi", lekin Git'ning himoyasini chetlab o'tadi: jurnal (reflog) yozilmaydi, xato hash yozsangiz branch buziladi. Ref'ni o'zgartirish kerak bo'lsa — git reset, git branch -f kabi buyruqlar bor.

8.5 Mavjud bo'lmagan nom

text
fatal: Needed a single revision

git rev-parse --verify topolmagan nom. Harflarni tekshiring va git show-ref bilan mavjud ref'lar ro'yxatiga qarang.

9. Mashqlar

1-mashq (oson): Kim kimga ishora qiladi?

«Bahor»da quyidagi buyruqlarning natijasini oldindan ayting, keyin tekshiring:

  1. cat .git/HEAD
  2. git symbolic-ref HEAD
  3. git rev-parse --abbrev-ref HEAD
  4. git rev-parse HEAD va git rev-parse main — bir xilmi?

Ishora: «HEAD fayli» bo'limidagi diagramma.

Yechim
  1. ref: refs/heads/main — HEAD branch nomini saqlaydi.
  2. refs/heads/main — o'sha narsa, Git buyrug'i orqali.
  3. main — qisqa branch nomi.
  4. Ha, bir xil 40 belgili hash: HEAD → main → commit. Faqat detached holatda farq qilishi mumkin — unda rev-parse --abbrev-ref HEAD HEAD deb javob beradi.

2-mashq (o'rta): Vaqt sayohati

git log --oneline bilan «Bahor»ning ikkinchi commitini (osh narxi) toping. Bo'sh joylarni to'ldiring:

bash
git switch  f4238e7
git status

git status birinchi qatorda HEAD [:detached] at f4238e7 deydi. Qaytish uchun: git switch [:main].

Ishora: «Eski commitga borish» bo'limi — switch hashni faqat maxsus flag bilan qabul qiladi.

Yechim
bash
git switch --detach f4238e7
git status
git switch main
text
HEAD is now at f4238e7 Menyu: osh narxini qo'sh
HEAD detached at f4238e7
nothing to commit, working tree clean

Oxirgi buyruq main ga qaytaradi. Detached holatda commit qilmagan bo'lsangiz, ogohlantirish chiqmaydi: Previous HEAD position was f4238e7 ... va Switched to branch 'main'.

3-mashq (qiyin): Yo'qolgan sinov

Stajyor Sardor shunday qildi:

bash
git switch --detach HEAD~3
echo '<p>Chegirma 10%</p>' >> index.html
git commit -a -m "Bosh sahifa: chegirma yozuvini qo'sh"
git switch main
clear

clear ekranni tozaladi va ogohlantirish ko'rinmay qoldi. Sardor chegirma commitini qaytarmoqchi.

  1. Commit omborda bormi?
  2. ORIG_HEAD yordam beradimi?
  3. Hashni topish uchun eng oddiy yo'l qaysi va qanday qutqarasiz?

Ishora: «Maxsus HEAD'lar» bo'limi — qaysi buyruqlar ORIG_HEAD ni yozadi? «Detached holatda commit» bo'limidagi javobni ham eslang.

Yechim
  1. Ha. Commit obyekt sifatida yozilgan, unga hech qanday ref ishora qilmayapti, xolos.
  2. Yo'q. ORIG_HEAD ni reset, merge, rebase yozadi, switch esa yozmaydi. Unda Sardorning oldingi amallari (agar bo'lsa) turibdi.
  3. Hash kerak. Eng oddiy yo'l — git reflog: u HEAD ning har bir harakatini hash bilan yozib boradi. Buyruqni Reflog darsida to'liq o'rganamiz, lekin hozir ham yozib ko'rishingiz mumkin. Bizning sinovimizdagi choy commiti u yerda shunday qator bo'lib turibdi: c1ecb39 (chore/eski-menyu-sinov) HEAD@{8}: commit: Menyu: choy narxini qo'sh (sinov) (qavs ichidagi nom — biz unga keyin qo'ygan branch). Sardor ham chegirma xabari yozilgan qatorni topadi va uning boshidagi hash bilan qutqaradi: git branch chegirma <hash>.

4-mashq: Portfolio qadami — portfolio'ning birinchi versiyasi

  1. kurs/portfolio da cat .git/HEAD va git rev-parse HEAD main ni ishga tushiring. Natijani tushuntiring.
  2. git log --oneline bilan birinchi commitingizni toping (eng pastki qator).
  3. Unga detached holatda o'ting: git switch --detach <hash>. git status nima deydi?
  4. sayt/index.html ni brauzerda oching — portfolio 07-qism boshida qanday edi? Hozirgi bilan solishtiring.
  5. main ga qayting va brauzerda sahifani yangilang.

mashqlar repo: shu darsning 1–3-mashq javoblarini mashqlar/07/12-head-refs/javoblar.md ga yozing va commit qiling: 07/12: HEAD va refs mashqlari javoblarini qo'sh.

Yechim
bash
cd ~/kurs/portfolio
cat .git/HEAD
git rev-parse HEAD main
git log --oneline
git switch --detach 63190c1
git status
git switch main

Birinchi commit hashi o'rniga o'zingiznikini qo'ying. Biz qisqa portfolio'ga bitta commit qo'shib sinadik. git switch --detach HEAD~1 va git status natijasi:

text
HEAD is now at 63190c1 Portfolio: 06-qism oxiridagi holat
HEAD detached at 63190c1
nothing to commit, working tree clean

main ga qaytganda:

text
Previous HEAD position was 63190c1 Portfolio: 06-qism oxiridagi holat
Switched to branch 'main'

Birinchi qadamda ref: refs/heads/main va ikki bir xil hash chiqadi: HEAD → main → oxirgi commit. Detached holatda fayllar birinchi commitdagi holatga qaytadi — keyingi darslardagi o'zgarishlar sahifada ko'rinmaydi. main ga qaytgach hammasi joyiga keladi. Hech narsa yo'qolmadi, chunki detached holatda commit qilmadingiz.

10. Real ishda

  • Terminal va VS Code hozirgi branch'ni HEAD dan o'qiydi. VS Code pastki panelida branch nomi yoki (detached bo'lsa) commit hashi ko'rinadi.
  • CI tizimlari — har yangi commitda kodni avtomatik tekshiradigan serverlar (masalan GitHub Actions, qism oxirida o'rganamiz). Ular reponi ko'pincha detached HEAD holatida oladi: aniq bir commit tekshiriladi. Loglarda HEAD is now at … ni ko'rsangiz — bu shu.
  • Skriptlarda git rev-parse --abbrev-ref HEAD (branch nomi) va git rev-parse --short HEAD (versiya uchun qisqa hash) juda ko'p ishlatiladi — masalan saytning pastki qismida "versiya: a0e2a64" yozish uchun.
  • Intervyu savollari: "Branch aslida nima?" (commit hashi yozilgan fayl), "Detached HEAD nima va undan qanday chiqiladi?", "HEAD~1 va ORIG_HEAD farqi?" Bu darsdan keyin uchalasiga javob bera olasiz.

Xulosa

  • Branch — ref: ichida commit hashi yozilgan fayl (.git/refs/heads/main); commit uni oldinga suradi.
  • HEAD — symbolic ref: odatda ref: refs/heads/main, ya'ni "hozirgi branch".
  • Detached HEAD — HEAD to'g'ridan-to'g'ri commitni ko'rsatadi; sayr uchun xavfsiz, commitlarni esa git branch bilan saqlang.
  • git rev-parse — nomdan hashga; ref'larni fayllardan emas, buyruqlardan o'qing (packed-refs, kelajakdagi reftable).
  • ORIG_HEAD oxirgi reset/merge/rebase'dan oldingi joyni eslaydi; FETCH_HEAD — oxirgi fetch natijasi.

Keyingi dars: .gitattributes va qator oxirlari (CRLF/LF) — Windows'dagi LF will be replaced by CRLF ogohlantirishini butunlay tartibga solamiz.

Manbalar

  • Pro Git kitobi: "Git Internals — Git References" — git-scm.com/book
  • Git hujjati: "git-rev-parse", "git-symbolic-ref", "git-switch", "gitrevisions" — git-scm.com/docs
  • Git hujjati: "BreakingChanges" (Git 3.0 va reftable) — git-scm.com/docs/BreakingChanges
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Git HEAD va refs: branch, detached HEAD va ORIGHEAD — IlmHamroh