16 β Tarixni tozalash va reflog¶
β¬ οΈ Oldingi: 15 β Stash, tag va cherry-pick Β· π README Β· Keyingi: 17 β Xatoni qidirish: bisect, blame, grep β‘οΈ
Bu bobda: ikki bir-biriga qarama-qarshidek tuyulgan, aslida bir tanganing ikki tomoni bo'lgan ko'nikmani o'rganamiz: tarixni chiroyli qilishni va "hammasini yo'qotdim!" holatidan qutqarishni. Oxirgi commit'ni
git commit --amendbilan chuqur tuzatamiz; interaktiv rebase orqali tarixni qayta yozamiz β bir nechta mayda commit'ni bittaga birlashtirish (squash), xabarni o'zgartirish (reword), tartibni almashtirish (reorder) va keraksiz commit'ni o'chirish (drop); push qilingan tarixni qayta yozishning xavfini va negagit push --force-with-lease(--force'dan ko'ra xavfsiz "ijaraga olib" majburlash) ishlatish kerakligini ko'ramiz;git reflog(HEAD harakatlari jurnali) bilan o'chgan commit va branch'ni β hattoreset --harddan keyin ham β tiklaymiz;git fsckbilan "osilib qolgan" commit'larni qidiramiz; va tarixdan sirni (parol, token) o'chirish g'oyasi bilan tanishamiz. Eng muhimi β tarixni tozalashning oltin qoidasini yodda mahkam o'rnatamiz.
Muammo¶
Tasavvur qiling: jamoa bilan kichik veb-do'kon ustida ishlayapsiz. O'zingizning feature/savatcha branch'ingizda bir kun davomida ishladingiz va commit tarixingiz mana shunday ko'rinishga keldi:
a1b2c3d savatcha tugmasi
b2c3d4e tuzatish
c3d4e5f yana tuzatish
d4e5f6a typo
e5f6a7b ishladi nihoyat!!!
f6a7b8c debug print qoshdim
Ish ishlaydi, lekin bu tarix β uyat. "yana tuzatish", "typo", "ishladi nihoyat!!!" β bularni hech kim ko'rmasligi kerak. Ertaga buni Pull Request qilib jamoaga yuborasiz va katta dasturchi (senior) shu olti qatorni ko'rib bosh chayqaydi. Aslida bu olti commit bitta mantiqiy ishni anglatadi: "savatcha tugmasi qo'shildi". Uni bitta toza commitga aylantirish kerak.
Ikkinchi muammo β buning teskarisi. Tarixni "tozalayman" deb git reset --hard yozdingiz-u, kechagi muhim ishingiz ko'z oldingizda yo'qoldi. Terminal jim. git log bo'm-bo'sh. Yuragingiz orziqib ketdi: "Bir kunlik ishim ketdi!".
Yaxshi xabar: Git deyarli hech narsani darrov o'chirmaydi. U sizning har bir harakatingizni maxfiy daftarchada yozib boradi β bu reflog. Bu bob ikki narsani o'rgatadi: tarixni qanday qilib professional darajada toza qilish, va xato qilganda uni qanday qilib orqaga qaytarish. Ikkalasi ham bitta tushunchaga tayanadi: Git'da commit'lar darrov yo'qolmaydi.
π 6-bobda biz reset, revert va amend bilan yuzaki tanishgan edik. Bu bobda ularning chuqur, kundalik amaliyotini va eng kuchli qutqaruvchi vosita β reflog'ni o'rganamiz. 6-bobni eslamasangiz, bir ko'z yugurtirib oling.
commit --amend β oxirgi commit'ni almashtirish (chuqur)¶
git commit --amend β eng ko'p ishlatiladigan "tozalash" vositasi. U oxirgi commit'ni yangisi bilan almashtiradi. Uchta asosiy holatda kerak bo'ladi.
1-holat: xabarni tuzatish. Commit qildingiz, lekin xabarda xato bor:
git commit -m "C2: feacher qoshildi" # "feature" deb yozmoqchi edingiz
git commit --amend -m "C2: feature qoshildi"
Natijada git log da yangi commit qo'shilmaydi β oxirgi commit'ning o'zi yangilanadi:
2-holat: unutilgan faylni qo'shish. Commit qildingiz, lekin bitta faylni add qilishni unutdingiz:
git add config.txt # unutilgan faylni qo'shamiz
git commit --amend --no-edit # oxirgi commitga singdiramiz, xabar tegmaydi
π --no-edit β "matn muharririni ochma, eski xabarni qoldir" degani. Bu juda qulay: faqat fayl qo'shiladi.
Natijani tekshiramiz:
Ikkala fayl ham bitta commit'da! Yangi "config qoshildi" commit'i yaratilmadi.
3-holat: sodir bo'lgan o'zgarishni qo'shish. Kod yozib, add qildingiz, keyin "buni alohida commit qilmay, oldingisiga qo'shay" dedingiz:
β οΈ Eng muhim ogohlantirish: --amend oxirgi commit'ni almashtiradi β ya'ni yangi hash yaratadi. Sinab ko'ramiz:
git log --oneline -1 # amenddan OLDIN
# dac0889 C2: feature qoshildi
# ... amend qilamiz ...
git log --oneline -1 # amenddan KEYIN
# 147c102 C2: feature qoshildi
Xabar bir xil, lekin hash o'zgardi: dac0889 β 147c102. Demak, amend ham tarixni qayta yozishdir. Bu nima uchun muhimligini hozir tushunamiz.
π Oltin qoida (birinchi marta): --amend ni faqat push qilinmagan oxirgi commit'da ishlating. Agar commit allaqachon push qilingan bo'lsa β amend qilmang, chunki hash o'zgaradi va remote (masofadagi server) bilan to'qnashuv chiqadi. Bu qoidaning to'liq shaklini bob oxirida ko'ramiz.
π‘ Misollardagi dac0889, 147c102 kabi hash'lar β shunchaki namuna. Sizning kompyuteringizda har bir commit'ning hash'i boshqacha bo'ladi, chunki u commit vaqti, muallifi va mazmuniga qarab hisoblanadi. Shu sababli buyruqlarda hash'ni qo'lda ko'chirgandan ko'ra HEAD, HEAD~1 kabi nisbiy nomlardan foydalanish ishonchliroq.
Interaktiv rebase β tarixni qayta yozish ustaxonasi¶
--amend faqat oxirgi commit bilan ishlaydi. Bobning boshidagi olti commit'li chalkash tarix-chi? Bir nechta commit'ni birdaniga tahrirlash uchun interaktiv rebase kerak β Git'ning eng kuchli vositasi.
9-bobda oddiy rebase'ni ko'rgan edingiz: u commit'larni boshqa asosga "ko'chirar" edi. Interaktiv rebase esa undan ham kuchliroq β u sizga commit'lar ro'yxatini muharrirda ochib beradi va siz har bir commit bilan nima qilishni belgilaysiz.
Bu yerda HEAD~4 β "oxirgi 4 commit'ni tahrirlayman" degani. Git matn muharririni ochadi va shunday ro'yxat ko'rsatadi:
pick a1b2c3d savatcha tugmasi
pick b2c3d4e tuzatish
pick c3d4e5f yana tuzatish
pick d4e5f6a typo
# Rebase ... onto ...
# Commands:
# p, pick <commit> = commitni o'z holicha ishlatish
# r, reword <commit> = commitni ishlatish, lekin xabarni o'zgartirish
# s, squash <commit> = commitni oldingisiga BIRLASHTIRISH (xabarlar qo'shiladi)
# f, fixup <commit> = squash kabi, lekin bu commit xabarini TASHLAB yuboradi
# d, drop <commit> = commitni o'chirish
# ... (qatorlarni o'rin almashtirsangiz β commitlar tartibi o'zgaradi)
π Diqqat: commit'lar bu yerda eskidan yangiga (tepadan pastga) tartiblangan β bu git logning teskarisi. Yuqoridagi commit eng eski.
Endi har bir qatordagi pick so'zini xohlagan amalga almashtirasiz. Muharrirni saqlab yopganingizda, Git ko'rsatmalaringizni yuqoridan pastga bajaradi. Quyida har bir amalni alohida ko'ramiz.
π 9-bobdan eslatma: agar rebase yarmida konflikt chiqsa, uni hal qilib git rebase --continue qilasiz; butun amaldan voz kechmoqchi bo'lsangiz, istalgan paytda git rebase --abort β hammasi rebase boshlanishidan oldingi holatga qaytadi. Bu β sizning "xavfsizlik tugmangiz".
squash β mayda commit'larni bittaga birlashtirish¶
Bobning boshidagi muammoga qaytaylik. Olti mayda commit'ni bittaga birlashtirmoqchimiz. To'rtta commit bilan misol qilaylik:
git rebase -i HEAD~4 ochilganda, birinchisini pick qoldirib, qolganlarini squash qilamiz:
π Mantiq: squash "meni oldingi qatorga qo'sh" degani. Shuning uchun eng yuqoridagi (eng eski) commit pick bo'lib qoladi β qolganlari unga yopishadi. Birinchi qatorni squash qilib bo'lmaydi, chunki uning "oldingisi" yo'q.
Saqlab yopgach, Git yangi, birlashgan commit uchun xabarni so'raydi β to'rtala eski xabarni ko'rsatib, sizga yangi, toza xabar yozish imkonini beradi. Masalan: savatcha tugmasi qoshildi. Natija:
To'rtta mayda commit o'rniga β bitta toza commit. Tarix endi uyat emas.
π‘ fixup vs squash: squash har bir commit xabarini saqlab, sizga ularni tahrirlash imkonini beradi. fixup esa xuddi shunday birlashtiradi, lekin qo'shilayotgan commit'ning xabarini butunlay tashlab yuboradi β agar qo'shimcha commit'lar "tuzatish", "typo" kabi keraksiz xabarlarga ega bo'lsa, fixup qulayroq.
reword β commit xabarini o'zgartirish¶
Faqat bitta commit'ning xabarini tuzatmoqchimisiz, lekin u oxirgi emas (shuning uchun --amend ishlamaydi)? reword ishlating:
pick 297ae4f A funksiya
reword 0b2ba9d B funksiya # bu commit xabarini o'zgartiramiz
pick ea653df C funksiya
Saqlab yopgach, Git faqat reword belgilangan commit uchun muharrirni ochib, yangi xabar so'raydi. Qolgan commit'larga tegmaydi.
reorder β commit'lar tartibini almashtirish¶
Muharrirdagi qatorlarning o'rnini almashtiring β Git commit'larni shu yangi tartibda qayta quradi:
β οΈ Diqqat: agar commit'lar bir-biriga bog'liq bo'lsa (masalan, biri ikkinchisi yaratgan faylni o'zgartirsa), tartibni almashtirish konflikt keltirib chiqarishi mumkin. Bunday holda Git to'xtaydi, siz hal qilib git rebase --continue qilasiz. Mustaqil commit'larda (turli fayllar) konflikt bo'lmaydi.
drop β commit'ni butunlay o'chirish¶
Tarixda keraksiz commit bormi (masalan, "debug print qoshdim")? Uning oldidagi pickni dropga almashtiring (yoki qatorni butunlay o'chiring):
Saqlangach, "C2 BAD" commit'i va u kiritgan o'zgarishlar tarixdan butunlay yo'qoladi:
π Mustaqil fayllarda drop toza ishlaydi. Lekin agar o'chirilayotgan commit keyingi commit'lar tayangan o'zgarishni kiritgan bo'lsa, konflikt chiqadi β bu mantiqan to'g'ri: "poydevorni olib tashlasangiz, ustidagi qavat qoqiladi".
Avtomatik usul: fixup va autosquash¶
Professional dasturchilar tez-tez ishlatadigan chiroyli qolip: kod yozib turib, "voy, bundan oldingi A commit'ida xato bor edi" deb sezsangiz, o'sha tuzatishni darrov A commit'iga belgilab qo'yishingiz mumkin:
Bu maxsus commit yaratadi, uning xabari maqsadli commit subjektining oldiga fixup! qo'shilgan ko'rinishi bo'ladi β agar HEAD~1 ning subjekti "A funksiya" bo'lsa, yangi commit xabari aynan fixup! A funksiya bo'ladi (Git maqsad subjektini so'zma-so'z ko'chiradi). Keyinroq, bitta buyruq bilan Git hamma fixup! commit'larni o'z joylariga avtomatik singdiradi:
--autosquash flagi tufayli Git muharrirni allaqachon to'g'ri tartiblangan holda ochadi β fixup! commit o'zi tegishli commit ostiga avtomatik joylashadi. Siz faqat saqlab yopasiz, qolganini Git qiladi. Natijada fixup! commit'i A commit'iga singib ketadi.
π‘ Bu qolip β git config --global rebase.autosquash true bilan doimiy yoqib qo'yilsa, har safar --autosquash yozish shart bo'lmaydi.
XAVF: push qilingan tarixni qayta yozish¶
Hozirgacha hamma narsa lokal (faqat sizning kompyuteringizda) edi. Endi eng nozik mavzuga keldik. Yuqoridagi amallarning hammasi β amend, squash, drop, reorder β commit hash'larini o'zgartiradi. Lokal branch'da bu xavfsiz. Lekin agar siz o'sha commit'larni allaqachon push qilgan bo'lsangiz-chi?
Muammo shunday: siz tarixni qayta yozdingiz, endi sizdagi commit'lar va remotedagi commit'lar boshqa hash'larga ega. Oddiy git push buni rad etadi:
! [rejected] main -> main (non-fast-forward)
hint: Updates were rejected because the tip of your current branch is behind
Git sizni ehtiyot qilyapti: "Sizning tarixingiz remotenikidan farq qiladi, oddiy push bilan ustiga yozib bo'lmaydi". Bu yerda ikki yo'l bor.
Nega oddiy --force XAVFLI¶
Birinchi (yomon) yo'l β majburlash:
--force Git'ga aytadi: "Menga remoteda nima borligi qiziq emas, mening tarixim bilan ustiga yoz". Muammo shundaki, ehtimol siz push qilgandan keyin hamkasbingiz o'sha branch'ga yangi commit qo'shgandir. --force esa uning ishini so'ramasdan o'chirib tashlaydi. Hamkasbingizning bir kunlik ishi yo'qoladi, va buni hech kim sezmaydi.
--force-with-lease β xavfsiz majburlash¶
To'g'ri yo'l β --force-with-lease ("ijaraga olib majburlash"):
Bu buyruq aqlliroq. U remote'ga majburan yozishdan oldin tekshiradi: "Men oxirgi marta ko'rgan holatdan beri remote o'zgarmaganmi?".
- Agar remote o'zgarmagan bo'lsa (hamkasb hech narsa qo'shmagan) β push o'tadi:
- Agar remote o'zgargan bo'lsa (hamkasb commit qo'shgan) β push rad etiladi:
"stale info" (eskirgan ma'lumot) β Git sizga aytyapti: "To'xtang! Remoteda siz bilmagan yangi narsa bor. Avval git fetch qiling, hamkasbingiz nima qo'shganini ko'ring, keyin qaror qabul qiling". Mana shu bir lahzalik to'xtatish hamkasbingizning ishini saqlab qoladi.
π‘ Yodlash qoidasi: "--force hech qachon, --force-with-lease har doim". Ko'p jamoalar --forceni butunlay taqiqlaydi. Hatto siz git config --global alias.pushf "push --force-with-lease" deb qisqartma yasab, --forceni unutib yuborishingiz mumkin.
π Eng yaxshisi β push qilingan branch tarixini umuman qayta yozmaslik. Tarixni faqat hali hech kim ko'rmagan, lokal yoki shaxsiy branch'da tozalang. Buni quyida "oltin qoida" sifatida rasmiylashtiramiz.
Oltin qoida: qachon tarixni qayta yozish mumkin?¶
Bu β bobning eng muhim jumlasi. Yodlab oling:
Tarixni faqat o'zingizdan boshqa hech kim ko'rmagan commit'larda qayta yozing.
Amalda bu shunday bo'linadi:
| Holat | Tarixni qayta yozish (amend/rebase/squash) | Nega |
|---|---|---|
| Lokal, hali push qilinmagan | β Bemalol | Tarix faqat sizniki, hech kimga xalal bermaydi |
| O'zingizning shaxsiy feature branch'ingiz (boshqa hech kim ishlamaydi) | β Mumkin (force-with-lease bilan) | Faqat siz tayanasiz |
Umumiy branch (main, develop) β boshqalar tayanadi |
β Hech qachon | Hamma tarixingizni buzasiz |
| Boshqalar bilan birga ishlayotgan feature branch | β Avval kelishing | Boshqalarning ishi to'qnashadi |
β Eng katta xato: main yoki jamoa tayanadigan branch tarixini qayta yozib, --force qilish. Hamma jamoaning lokal nusxasi remotedan farq qila boshlaydi va keyingi pull da dahshatli chalkashlik chiqadi.
β
To'g'ri amaliyot: ishingizni shaxsiy branch'da toza-pokiza qiling (squash, reword), keyin uni Pull Request orqali mainga merge qiling. Merge'dan keyin main tarixiga hech qachon tegmang.
reflog β "hammasini yo'qotdim!" dan qutqaruvchi¶
Endi bobning ikkinchi yarmiga β qutqaruvga o'tamiz. Yuqoridagi amallar (reset --hard, rebase, --force) xato qilingan bo'lsa-chi? Yaxshi xabar: Git HEAD'ning har bir harakatini maxfiy jurnalda yozadi. Bu β reflog ("reference log").
Reflog git logdan tubdan farq qiladi:
- git log β commit tarixini ko'rsatadi (loyihaning rasmiy o'tmishi).
- git reflog β siz nima qilganingizni ko'rsatadi (har bir commit, checkout, reset, rebase, merge). Bu sizning shaxsiy "qadamlar jurnalingiz".
Eng muhimi: reset --hard bilan commit'ni "o'chirsangiz" ham, u reflog'da qoladi. Demak, tiklash mumkin.
Ssenariy: reset --hard'dan keyin tiklash¶
Aytaylik, tarixingiz shunday:
Va siz xato bilan ikkita commit'ni o'chirib yubordingiz:
git log endi faqat C1'ni ko'rsatadi β C2 va C3 "yo'qoldi". Vahimaga tushmang. Reflog'ga qarang:
f80abcb HEAD@{0}: reset: moving to HEAD~2
74a18c6 HEAD@{1}: commit: C3
a7d8c54 HEAD@{2}: commit: C2: muhim ish
f80abcb HEAD@{3}: commit (initial): C1
Ko'ryapsizmi? HEAD@{1} β bu reset'dan oldingi holat, ya'ni biz o'chirgan C3 commit'i hali shu yerda turibdi! Uni tiklaymiz:
Tarix to'liq tiklandi! C2 va C3 qaytib keldi.
π HEAD@{n} β "n harakat oldingi HEAD" degani. HEAD@{0} β hozirgi holat, HEAD@{1} β bitta oldingi, va hokazo. Reflog ro'yxatidan kerakli holatni topib, uning HEAD@{n} belgisini yoki hash'ini ishlatasiz.
Tushib qolgan rebase'ni tiklash¶
Reflog faqat reset uchun emas. Interaktiv rebase'da chalkashib ketdingizmi? git reflog da rebase'dan oldingi holatni topib, o'sha yerga qaytasiz:
git reflog
# ... rebase boshlanishidan oldingi qatorni toping, masalan HEAD@{5} ...
git reset --hard HEAD@{5}
π‘ Buning uchun maxsus, qulayroq belgi ham bor: git reset --hard ORIG_HEAD. Git har bir "xavfli" amaldan (rebase, merge, reset) oldin HEAD'ning eski qiymatini ORIG_HEADga saqlab qo'yadi. Demak, rebase yoki merge yoqmadimi β git reset --hard ORIG_HEAD ko'pincha bir buyruq bilan ortga qaytaradi.
O'chgan branch'ni tiklash¶
Reflog HEAD harakatlarini saqlaydi β demak, o'chirgan branchni ham tiklash mumkin (chunki branch ustida ishlaganingiz HEAD reflogiga tushgan).
Aytaylik, ishingizni tugatdingiz deb o'ylab, branch'ni o'chirdingiz:
π E'tibor bering β Git o'chirilgan branch'ning oxirgi hash'ini (c6bc46a) aytib qoldi. Agar o'sha qatorni ko'rgan bo'lsangiz, tiklash juda oson. Agar ko'rmagan bo'lsangiz, reflog'dan topasiz:
5c1e4c0 HEAD@{0}: checkout: moving from yangi-feature to main
c6bc46a HEAD@{1}: commit: Feature ishi
5c1e4c0 HEAD@{2}: checkout: moving from main to yangi-feature
HEAD@{1} β branch'dagi oxirgi commit (c6bc46a). Branch'ni shu commit'dan qayta yaratamiz:
Branch tiklandi, butun ishi joyida.
git fsck β osilib qolgan commit'larni qidirish¶
Reflog vaqt o'tishi bilan tozalanadi (sukut bo'yicha 90 kun). Agar reflog yozuvi ham yo'qolgan bo'lsa-chi? Yana bir qutqaruvchi bor β git fsck ("file system check"). U repozitoriydagi hech bir branch yoki HEAD ko'rsatmagan, ammo hali o'chmagan commit'larni β "dangling" (osilib qolgan) commit'larni β topadi:
Topilgan hash'ni git show <hash> bilan ko'rib, kerakli commit ekaniga ishonch hosil qilasiz, so'ng undan branch yaratib tiklaysiz:
π git fsck β "oxirgi chora". Avval doim git reflog ni sinab ko'ring (u qulayroq). fsck reflog ham yordam bermaganda, masalan reflog tozalanib ketganda asqotadi.
β οΈ Eslatma: Git osilib qolgan commit'larni abadiy saqlamaydi. Vaqti-vaqti bilan ishlovchi "axlat yig'uvchi" β git gc (garbage collector) β eskirgan, hech kimga keraksiz commit'larni butunlay o'chiradi. Shuning uchun tiklashni cho'zmang: xatoni sezganingizda darrov harakat qiling.
Sirni tarixdan o'chirish (parol, token)¶
Eng og'riqli holat: kodingizga parol, API token yoki maxfiy kalitni yozib qo'yib, uni commit qilib, push qildingiz. Endi sir GitHub'da, va butun tarixda turibdi. Faylni o'chirib yangi commit qilish yetarli emas β sir eski commit'lar ichida qoladi, har kim git log orqali topadi.
Bu yerda achchiq haqiqat: sirni tarixdan to'liq o'chirish β har bir commit'ni qayta yozishni talab qiladi. Bu β eng og'ir "tarixni qayta yozish" turi va u butun tarixdagi hash'larni o'zgartiradi.
Buning uchun ikkita maxsus vosita bor (ikkalasi ham Git bilan birga kelmaydi, alohida o'rnatiladi):
-
git-filter-repo β rasmiy tavsiya etilgan zamonaviy vosita. Python bilan o'rnatiladi (
bu yerdapip install git-filter-repo). Misol g'oyasi (buyruqni o'zingiz sinab ko'rmasangiz ham, mantiqini tushuning):sirlar.txtβ almashtirilishi kerak bo'lgan matnlar (parollar) ro'yxati. -
BFG Repo-Cleaner β Java asosidagi soddalashtirilgan vosita (
.jarfayl). Katta repozitoriylarda tezroq:
β Eski qo'llanmalardagi git filter-branch ni ishlatmang β Git'ning o'zi uni ishga tushirganda ogohlantirish beradi ("a glut of gotchas" β "tuzoqlarga to'la") va git-filter-reponi tavsiya qiladi.
π Eng muhim haqiqat: sir bir marta push qilingan bo'lsa, u allaqachon "tarqalgan" deb hisoblang. Tarixni tozalashdan oldin yoki keyin β o'sha parol yoki token'ni darrov bekor qiling va yangisini yarating (masalan, GitHub token'ni o'chirib qayta generatsiya qiling). Tarix tozalansa ham, kimdir uni allaqachon ko'chirib olgan bo'lishi mumkin.
β
Eng yaxshi yechim β sirni umuman commit qilmaslik: parol va token'larni .env faylida saqlang va uni .gitignorega qo'shing (4-bobdan eslang). Oldini olish β davolashdan oson. Bu mavzuni 23-bobda (Xavfsizlik) chuqurroq ko'ramiz.
Hammasini bir joyda¶
| Vazifa | Buyruq |
|---|---|
| Oxirgi commit xabarini tuzatish | git commit --amend -m "..." |
| Oxirgi commitga unutilgan fayl qo'shish | git add f && git commit --amend --no-edit |
| Bir nechta commit'ni bittaga birlashtirish | git rebase -i HEAD~N + squash/fixup |
| Commit xabarini o'zgartirish (oxirgi emas) | git rebase -i HEAD~N + reword |
| Keraksiz commit'ni o'chirish | git rebase -i HEAD~N + drop |
| Commit'larni qayta tartiblash | git rebase -i HEAD~N + qatorlar o'rnini almashtirish |
| Tuzatishni avtomatik joylashtirish | git commit --fixup <commit> + git rebase -i --autosquash |
| Qayta yozilgan tarixni xavfsiz push qilish | git push --force-with-lease |
| Yo'qolgan commit'ni tiklash | git reflog + git reset --hard HEAD@{n} |
| Rebase/merge'ni ortga qaytarish | git reset --hard ORIG_HEAD |
| O'chgan branch'ni tiklash | git reflog + git switch -c <nom> <hash> |
| Osilib qolgan commit'larni qidirish | git fsck --no-reflogs |
| Tarixdan sir o'chirish | git filter-repo yoki BFG (+ token'ni bekor qilish) |
π Butun bobning yuragi ikki jumlada: (1) Tarixni faqat hech kim ko'rmagan, lokal commit'larda qayta yozing β push qilingani umumiy mulk. (2) Xato qilsangiz vahimaga tushmang β Git deyarli hech narsani darrov o'chirmaydi, git reflog ko'p hollarda ishingizni qaytarib beradi.
16-bob mashqlari¶
Quyidagi mashqlarni alohida sinov papkasida bajaring β loyihangizning haqiqiy .git katalogiga tegmang. Avval biror joyda yangi papka yarating, unda git init qiling va sinov fayllari bilan ishlang. Har bir mashqdan oldin va keyin git log --oneline hamda git reflog qilib, holat qanday o'zgarganini kuzating. 15-, 16- va 17-mashqlar uchun bo'sh (bare) "remote" yaratish kerak bo'ladi: git init --bare ../remote.git.
- Yangi sinov papkasi yarating,
git initqiling,default branchmainekanini tekshiring vaapp.txtfaylida bitta commit yarating (xabarni ataylab xato yozing, masalan "feacher qoshildi"). - 1-mashqdagi commit xabarini
git commit --amend -m "..."bilan tuzating.git log --onelineda yangi commit qo'shilmaganini, faqat xabar o'zgarganini tasdiqlang. --amenddan oldin va keyin commit hash'ini solishtiring: hash o'zgarganini ko'ring va nega bu "tarixni almashtirish" hisoblanishini o'z so'zlaringiz bilan izohlang.- Bitta commit yarating, keyin yangi
config.txtfaylini yaratib, unigit addqiling vagit commit --amend --no-editbilan oxirgi commitga singdiring.git show --stat HEADbilan ikkala fayl ham bitta commit'da ekanini tekshiring. - Avval bitta poydevor commit yarating ("C0 baza", masalan
baza.txt), so'ng uning ustiga to'rtta ketma-ket commit qo'shing ("WIP 1" dan "WIP 4" gacha) β jami besh commit bo'ladi. Poydevor commit shart, chunkiHEAD~4poydevor commit'ni rebase'ning tayanchi (uning oxirgi to'rt commit'ini tahrirlaymiz) sifatida ishlatadi; poydevorsiz, ya'ni aniq to'rt commit ustidaHEAD~4qilsangiz,fatal: invalid upstream 'HEAD~4'xatosi chiqadi (ildiz commit'ining ota-onasi yo'q).git rebase -i HEAD~4ni oching, ro'yxatni (to'rt WIP commit ko'rinadi, poydevor esa tayanch sifatida tepada# Rebase ... onto ...qatorida qoladi) o'qib chiqing vapick,squash,reword,dropizohlarini tushunib, hech narsa o'zgartirmasdan saqlab yoping (tarix o'zgarmasligi kerak). π‘ Eslatma: agar poydevorsiz, aynan to'rt commit'ni ham birlashtirmoqchi bo'lsangiz,HEAD~4o'rnigagit rebase -i --rootishlating β u eng birinchi commit'dan boshlab ochadi. - 5-mashqdagi tuzilmani (poydevor "C0 baza" + to'rt "WIP" commit) qaytadan tayyorlang. Bu safar
git rebase -i HEAD~4da birinchi WIP'nipick, qolgan uchtasinisquashqilib, hammasini bitta toza commit'ga ("savatcha tugmasi qoshildi") birlashtiring. (Poydevor commit shu yerda ham zarur β usizHEAD~4xato beradi.) - Xuddi shu squash'ni
squasho'rnigafixupbilan qiling va farqni payqang:fixupda qo'shilayotgan commit xabarlari ko'rsatilmaydi. - Uchta commit yarating (A, B, C β har biri alohida faylda).
rebase -iorqali o'rtadagi B commit'ining xabarinirewordbilan o'zgartiring. A va C tegilmaganini tasdiqlang. - Uchta mustaqil commit (turli fayllar) yarating va
rebase -ida ularning ikkitasining qatorlar o'rnini almashtirib, tartibni o'zgartiring.git logda yangi tartibni ko'ring. - Avval poydevor commit ("C0 baza") yarating, so'ng uning ustiga to'rtta commit qo'shing (har biri alohida faylda), ulardan biri "debug print qoshdim" bo'lsin.
git rebase -i HEAD~4da o'shanidropqiling va u kiritgan fayl tarixdan yo'qolganini tekshiring. (Poydevorsiz, aniq to'rt commit ustidaHEAD~4fatal: invalid upstreamberadi; xohlasangiz--rootham ishlatsa bo'ladi.) - Avval poydevor commit ("C0 baza") yarating, so'ng bir-biriga bog'liq ikki commit qo'shing (birinchisi
data.txtfaylini yaratsin, ikkinchisi o'sha faylni o'zgartirsin).git rebase -i HEAD~2da birinchisini (data.txtyaratgan commit'ni)dropqilib ko'ring va konflikt (modify/delete) chiqishini kuzating; so'nggit rebase --abortbilan ortga qayting. (Poydevor commit shart β usiz, aniq ikki commit ustidaHEAD~2fatal: invalid upstream 'HEAD~2'beradi va konfliktni umuman ko'rmaysiz.) - Ikki commit (A funksiya, B funksiya) yarating. A'da kichik xato bor deb faraz qilib, tuzatishni yozing va
git commit --fixupbilan A ga belgilang. So'nggit rebase -i --autosquashbilan avtomatik singdiring. - Uchta commit'li tarix yarating.
git reset --hard HEAD~2bilan ikkita commit'ni "o'chiring".git logularning yo'qolganini,git reflogesa hali turganini ko'rsatishini tasdiqlang. - 13-mashqdan so'ng
git reflogda o'chgan commit'niHEAD@{1}da toping vagit reset --hard HEAD@{1}bilan butun tarixni tiklang. - Bo'sh "remote" yarating (
git init --bare ../remote.git), unga branch'ingiznipush -uqiling. So'ng oxirgi commit'ni--amendqilib o'zgartiring va oddiygit pushni sinab ko'ring β u "rejected" bo'lishini kuzating. - 15-mashqdan so'ng
git push --force-with-leasebilan o'zgartirilgan tarixni majburan push qiling va bu safar muvaffaqiyatli o'tishini ko'ring. - Remote'ni boshqa papkaga
git cloneqiling, klon ichida yangi commit qo'shib push qiling (go'yo "hamkasb" ish qildi). So'ng asosiy papkangizda tarixni qayta yozib--force-with-leaseqiling β u "stale info" bilan rad etilishini va shu tariqa hamkasb ishini saqlab qolishini kuzating. - Yangi branch yarating (
git switch -c sinov-branch), unda bitta commit qiling,mainga qayting vagit branch -D sinov-branchbilan o'chiring.git reflogorqali o'chgan branch'ning hash'ini topib,git switch -c sinov-branch <hash>bilan tiklang. - 18-mashqdan keyin
git fsck --no-reflogsni ishga tushiring va "dangling commit" qatorida o'sha branch'ning oxirgi commit'i ko'rinishini kuzating.git show <hash>bilan u haqiqatan o'sha commit ekanini tasdiqlang. - Sirni o'chirish ssenariysini "qog'ozda" mashq qiling: bir faylga soxta parol (masalan
PAROL=12345) yozib commit qiling, keyin uni o'chirib yangi commit qiling.git log -pbilan parol eski commit'da hali turganini ko'ring. So'ng bu sirni butunlay o'chirish uchun qanday qadamlar kerakligini (git filter-repoyoki BFG g'oyasi + token'ni bekor qilish) yozma ravishda rejalashtiring β haqiqiy vositani o'rnatish shart emas.