17 β Xatoni qidirish: bisect, blame, grep¶
β¬ οΈ Oldingi: 16 β Tarixni tozalash va reflog Β· π README Β· Keyingi: 18 β Submodule, Git LFS va monorepo β‘οΈ
Bu bobda: "ilgari ishlardi, endi buzilgan β qaysi commit buzdi?" degan azoblovchi savolga aniq javob topishni o'rganamiz.
git bisectikkilik qidiruv (binary search β oraliqni har safar yarmiga bo'lish) bilan yuzlab commit ichidan aybdorni atigi bir necha qadamda topadi;bisect runesa buni avtomatlashtiradi.git blamefaylning har bir satrini kim, qachon, qaysi commit'da yozganini ko'rsatadi.git log -Sva-G("pickaxe" β qidiruv kushkasi) qaysi commit muayyan matnni qo'shgan yoki olib tashlaganini topadi,git log --grepesa commit xabarlari ichida qidiradi. Oxirida bularni birlashtirib, aybdorni topib, nega unday bo'lganini tushunamiz.
Muammo¶
Tasavvur qiling: do'kon saytini bir oydan beri jamoa bo'lib yozyapsiz. Yuzlab commit to'plandi. Mijoz qo'ng'iroq qiladi: "narxlar noto'g'ri hisoblanyapti!". Siz kodga qaraysiz β bir qarashda hammasi joyida ko'rinadi. Eng yomoni: bilasizki, ikki hafta oldin bu yaxshi ishlardi. Demak, o'sha vaqtdan beri qilingan yuzlab commit'dan bittasi buni sindirgan. Qaysi biri?
Birinchi xayolga keladigan yo'l β commit'larni birma-bir orqaga qaytarib (yoki har birini git show bilan ochib) tekshirish. 200 ta commit bo'lsa, bu β 200 ta tekshiruv. Bir kuningiz ketadi.
Git'da bundan ancha aqlli yo'l bor. Bu bobda uchta quroldan foydalanamiz:
git bisectβ "qaysi commit sindirdi?" ni ikkilik qidiruv bilan topadi. 200 ta commit ichidan ~8 ta tekshiruvda aybdorni qo'lga oladi.git blameβ "shu satrni kim yozgan?" ni aytib beradi (har satr yonida commit + muallif + sana).git log -S/-G/--grepβ "qaysi commit shu matnni qo'shdi/o'chirdi?" yoki "qaysi commit xabarida shu so'z bor?" ni topadi.
Bu buyruqlarning hammasi faqat o'qiydi β kodingizni o'zgartirmaydi. Demak, ularni qo'rqmasdan sinashingiz mumkin. (Bitta istisno β bisect ish papkangizni vaqtincha eski commit'larga ko'chiradi, lekin oxirida bisect reset hammasini joyiga qaytaradi.)
π Sinab ko'rish uchun bo'sh papkada kichik repozitoriy oching va bilib turib bug yarating: bir nechta commit qiling, o'rtaroqda bitta funksiyani sindiring (masalan
return a+bnireturn a-bga aylantiring), keyin yana bir nechta commit qo'shing. Quyidagi misollar aynan shunday repozitoriyda sinab ko'rilgan. Loyihangizning haqiqiy.gitpapkasiga tegmang.
git bisect β aybdor commitni ikkilik qidiruv bilan topish¶
git bisect g'oyasi juda sodda, lekin g'oyat kuchli. Siz Git'ga ikki narsani aytasiz:
- bad (yomon) commit β hozir buzilgan joy. Odatda bu
HEAD(eng oxirgi holatingiz). - good (yaxshi) commit β ishlaganini aniq bilgan eski commit (masalan, ikki hafta oldingisi).
Aybdor β shu ikkisining orasida turibdi: good'da hali yaxshi edi, bad'da esa allaqachon buzilgan. Git oraliqning o'rtasidagi commit'ga sizni "tashlaydi", siz tekshirib "yaxshi" yoki "yomon" deysiz, va Git oraliqni yarmiga qisqartiradi. Shunday qilib, qadam-baqadam aybdor commit qoladi.
Bosqichma-bosqich¶
git bisect start # qidiruvni boshlash
git bisect bad # hozirgi holat (HEAD) buzilgan
git bisect good a1b2c3d # bu eski commit yaxshi ishlardi
Uchinchi qatorda a1b2c3d o'rniga ishlaganini bilgan haqiqiy commit hash'ini yozasiz (git log --oneline bilan toping). Endi Git oraliqning o'rtasidagi commit'ga o'tadi va shunday deydi:
Bisecting: 4 revisions left to test after this (roughly 2 steps)
[0de7f61146dfcd60cb18b35c1f02e9a8a9531664] ozgarish 4
E'tibor bering: Git "roughly 2 steps" deyapti β ya'ni yana taxminan 2 ta tekshiruvda tugaydi. Hozir ish papkangiz aynan o'sha o'rtadagi commit holatiga ko'chirilgan. Endi sizning vazifangiz β dasturni ishlatib ko'rish: bug bormi yoki yo'qmi? Javobga qarab bittasini yozasiz:
git bisect good # bu commit'da hammasi yaxshi edi
# yoki
git bisect bad # bu commit'da bug allaqachon bor
Har javobdan keyin Git oraliqni yarmiga qisqartirib, navbatdagi o'rtadagi commit'ga o'tkazadi. Buni oraliq bittagacha qisqargunigacha takrorlaysiz. Oxirida Git aybdorni e'lon qiladi:
fa66221da39186a0fad035d5406faf0e8d669bc0 is the first bad commit
commit fa66221da39186a0fad035d5406faf0e8d669bc0
Author: Aziz Karimov <aziz@example.com>
Date: Thu Jun 11 12:46:47 2026 +0500
refaktoring (aslida bug shu yerda)
hisob.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
... is the first bad commit β mana shu birinchi buzilgan commit, ya'ni aybdor. Endi siz aniq bilasiz: muammo qaysi commit'da, qaysi faylda va qaysi qatorda paydo bo'lgan.
Eng muhim qadam β qidiruvni yakunlash¶
Qidiruv tugagach (yoki istalgan paytda to'xtatmoqchi bo'lsangiz), albatta quyidagini yozing:
Bu sizni qidiruvdan oldingi joyingizga (odatda main branch'ning eng oxirgi holatiga) qaytaradi. bisect davomida ish papkangiz eski commit'larga ko'chib turardi β reset hammasini joyiga tiklaydi.
π Agar
bisect resetni unutsangiz, ish papkangiz allaqachon eski commit'da "osilib" qoladi (detached HEAD holati β 7-bobda ko'rgan edik). Yangi commit qilsangiz, u hech qaysi branch'ga ulanmaydi. Shuning uchun qoida: bisect'ni doimresetbilan tugat.π‘ Nega ikkilik qidiruv shunchalik tez? Har tekshiruvda oraliq ikkiga bo'linadi: 1000 commit bo'lsa, ketma-ketlik 1000 -> 500 -> 250 -> ... -> 1 ko'rinishida boradi. Bu atigi ~10 qadam! Birma-bir tekshirsangiz β 1000 qadam. Mana shu farq
bisectni qudratli qiladi.
git bisect run β qidiruvni avtomatlashtirish¶
Yuqorida har qadamda siz qo'lda "good" yoki "bad" deb baho berdingiz. Agar bug'ni avtomatik aniqlaydigan test (tekshiruv skripti) bo'lsa, bu ishni butunlay Git'ga topshirib qo'ysa bo'ladi.
G'oya: bizga shunday skript kerakki, u chiqish kodi (exit code) bilan javob bersin:
- chiqish kodi
0β commit yaxshi (good), - chiqish kodi
0dan farqli (masalan1) β commit yomon (bad).
Oddiy misol β test.sh skripti narxlar funksiyasi to'g'ri ekanini tekshiradi (bu yerda soddalik uchun matnni qidiramiz):
#!/bin/sh
if grep -q "return a+b" hisob.js; then
exit 0 # to'g'ri kod bor -> yaxshi
else
exit 1 # to'g'ri kod yo'q -> buzilgan
fi
Endi skriptni ishga tushirish huquqini beramiz va bisect'ga uni topshiramiz:
chmod +x test.sh
git bisect start
git bisect bad HEAD
git bisect good a1b2c3d
git bisect run ./test.sh
Git har bir tekshiruvni o'zi bajaradi β skriptni ishlatadi, chiqish kodiga qarab "good/bad" deb belgilaydi va keyingi commit'ga o'tadi. Siz hech narsa bosmaysiz:
running './test.sh'
Bisecting: 2 revisions left to test after this (roughly 1 step)
running './test.sh'
Bisecting: 0 revisions left to test after this (roughly 0 steps)
running './test.sh'
fa66221da39186a0fad035d5406faf0e8d669bc0 is the first bad commit
bisect found first bad commit
Tugagach, odatdagidek git bisect reset qiling.
π‘
bisect runreal loyihalarda haqiqiy test buyrug'i bilan ishlaydi:git bisect run npm test,git bisect run pytest test_narx.py,git bisect run make check. Test "o'tdi" bo'lsa exit 0, "yiqildi" bo'lsa exit 0'dan farqli β aynan bisect kutadigan til.π Ba'zi commit'larni tekshirib bo'lmaydi (masalan, o'sha commit'da loyiha umuman kompilatsiya bo'lmaydi). Bunday holatda
git bisect skipdeysiz β Git o'sha commit'ni chetlab o'tib, qo'shni boshqasini tanlaydi. Avtomatik rejimda skriptingiz 125 chiqish kodini qaytarsa, Git buni "skip" deb tushunadi.π Qidiruv jurnalini ko'rish uchun
git bisect log, uni keyinchalik qayta o'ynatish uchungit bisect replay <fayl>bor β ammo bular kamroq kerak bo'ladi.
git blame β har satrni kim yozdi?¶
bisect "qaysi commit?" ga javob beradi. git blame esa boshqacha savolga: "shu faylning aynan shu satrini kim, qachon, qaysi commit'da yozgan?".
fa66221d (Aziz Karimov 2026-06-11 12:46:47 +0500 1) function summa(a,b){ return a-b; }
353bb289 (Aziz Karimov 2026-06-11 12:46:47 +0500 2) // kichik ozgarish 1
dba9023f (Malika Tosheva 2026-06-10 09:15:02 +0500 3) // kichik ozgarish 2
8105101e (Aziz Karimov 2026-06-11 12:46:47 +0500 4) // kichik ozgarish 3
Har satr yonida to'rt narsa: qisqa hash (o'sha satrni oxirgi marta o'zgartirgan commit), muallif, sana va satr raqami. O'ngda esa β faylning haqiqiy mazmuni.
Endi narx hisobi buzilgan misolga qaytsak: aybdor satrning yonida fa66221d turibdi β demak o'sha satrni shu commit o'zgartirgan. Bu yerda muhim nuqta:
π "blame" inglizcha "ayblash" degani, lekin maqsad β kimnidir ayblash emas, balki o'zgarishning sababini topish. Hash'ni olib
git show fa66221qilsangiz, o'sha commit'ning to'liq o'zgarishini va xabarini ko'rasiz β ehtimol u atayin qilingan va sababi bor.
-L bilan faqat kerakli satrlar¶
Katta faylda hamma satrni ko'rish shart emas. -L bilan satr oralig'ini cheklaysiz:
Sanani ixchamroq ko'rsatish uchun --date=short:
Satrning oldingi holatini ko'rish¶
Aybdor satrni topdingiz, lekin avval qanday edini bilmoqchimisiz? Commit hash'idan keyin ^ qo'ying β bu "o'sha commit'dan bitta oldingi holat" degani:
Mana β aybdor commit'gacha satr return a+b (to'g'ri) edi, fa66221 esa uni return a-b (xato) ga aylantirgan. Endi muammo to'liq oydinlashdi.
π‘
git blame -wbo'sh joy (probel, tab) o'zgarishlarini e'tiborsiz qoldiradi β kod faqat formatlangani uchun "aybdor" ko'rinib qolgan satrlarni haqiqiy muallifiga bog'lab beradi. Katta refaktoringdan keyin juda asqotadi.π‘ GitHub'da blame yanada qulay: faylni oching, "Blame" tugmasini bosing β har satr yonida muallif avatari va commit'i ko'rinadi, ustiga bosib o'sha commit'ga o'tasiz.
git log -S va -G β matn qachon kirdi (pickaxe)¶
Ba'zan savol satrga emas, matnning o'ziga qaratilgan: "API_KEY so'zi qaysi commit'da qo'shildi?" yoki "return a-b xato kodi tarixga qaysi commit bilan kirdi?". Buning uchun git log -S bor β uni "pickaxe" (kushka, qazuvchi asbob) deb atashadi, chunki u tarix qatlamlarini "qazib" izlagan matnni topadi.
-S "matn" shunday ishlaydi: faylda izlangan matn necha marta uchrashi o'zgargan har bir commit'ni topadi β ya'ni matn qo'shilgan yoki o'chirilgan lahzalarni. Bu juda aniq: yuzlab commit ichidan faqat o'shalari chiqadi.
-G β kengroq to'r (regex)¶
-G ham o'xshash, lekin u naqsh (regex) bor har qanday qatorga tegilgan commit'larni topadi β matn soni o'zgarmagan bo'lsa ham. Farqni yaqqol ko'rsatadigan misol β summa so'zini ikki usul bilan qidiramiz:
Farqqa qarang: -S faqat bitta commit'ni topdi β chunki "summa" so'zi birinchi marta o'sha commit'da qo'shilgan, undan keyin uning soni o'zgarmagan. -G esa ikkita topdi β chunki fa66221 commit'da "summa" so'zi bor satr (return a+b -> return a-b) o'zgargan, garchi "summa" so'zining o'zi joyida qolgan bo'lsa ham.
| Buyruq | Nimani topadi |
|---|---|
git log -S "matn" |
Matn soni o'zgargan commit'lar (qo'shildi yoki o'chirildi) |
git log -G "regex" |
Naqsh bor satrga tegilgan har qanday commit (kengroq) |
Qoida: "shu matn qachon yozildi/o'chirildi?" β -S. "Shu matn bor satrlarga qaysi commit'lar tegdi?" β -G.
π
-Sda--pickaxe-regexqo'shsangiz,-Sham regexni tushunadi (odatda-Saniq matn izlaydi). Lekin oddiy holatda: aniq so'z uchun-S, naqsh uchun-Gdeb eslang.
git log --grep β commit xabarlarida qidirish¶
-S va -G fayl mazmunini qidiradi. --grep esa commit xabarlari ichidan qidiradi: "tarkibida 'refaktoring' so'zi bor commit'lar qani?".
Bu juda foydali, agar jamoangiz xabarlarni puxta yozsa: "bug", "fix", "narx", "login" kabi so'zlar bo'yicha tegishli commit'larni darrov topasiz.
π‘ Bir nechta
--grepni birlashtirsangiz (--grep="bug" --grep="fix"), Git ulardan birortasi bor commit'larni topadi (YOKI mantiq). Hammasi bo'lishini xohlasangiz--all-matchqo'shing. Katta-kichik harf farqini e'tiborsiz qoldirish uchun-i(--regexp-ignore-case).π
--grep(xabarda qidiradi) va--author(5-bobda ko'rgan, muallifda qidiradi) ni chalkashtirmang. Ikkalasini birlashtirsa ham bo'ladi:git log --grep="narx" --author="Malika".
git log -- fayl va git grep β qo'shimcha izquvarlar¶
5-bobda ko'rgan fayl tarixi ham izlashda asosiy quroldir. Bitta faylga tegishli barcha commit'lar:
Diff bilan birga ko'rish uchun -p qo'shing β har commit o'sha faylda nimani o'zgartirgani ketma-ket chiqadi:
Va nihoyat, git grep β bu git log emas, balki hozirgi holatdagi kod ichidan matn izlaydi (oddiy matn qidiruvi, lekin Git boshqaradigan fayllar bo'ylab tez ishlaydi):
π‘
git grepishlash papkasidagi barcha kuzatilayotgan fayllar ichini bir zumda qidiradi β.gitpapkasiga yoki.gitignoredagi fayllarga vaqt sarflamaydi. "Bu funksiya qayerda chaqirilgan?" degan savolga eng tez javob.
Hammasini birlashtirish β aybdorni topib, tushunish¶
Endi to'liq izquvarlik zanjirini ko'ramiz. Maqsad β faqat "qaysi commit?" emas, "nega unday qilingan?" ni tushunish:
git bisectbilan buzilgan commit'ni topasiz:fa66221.git show fa66221bilan o'sha commit'ni ochasiz β aniq qaysi satr, qanday o'zgargan va xabarda qanday sabab yozilganini o'qiysiz.- Agar muayyan satr ayblansa,
git blame -Lbilan o'sha satr javobgarini,fa66221^bilan undan oldingi (to'g'ri) holatini ko'rasiz. - Matnning tarixini kuzatmoqchi bo'lsangiz,
git log -S "matn"bilan u qachon kirganini topasiz.
Ko'pincha aybdor commit "bilib turib" qilingan o'zgarish bo'lib chiqadi β masalan, kimdir boshqa muammoni hal qilmoqchi bo'lib, beixtiyor yangisini yaratgan. git show ning xabari va diff'i shuni oydinlashtiradi. Shundan keyin tuzatishni o'zingiz yozasiz (yoki 6-bobdagi revert bilan o'sha commit'ni bekor qilasiz).
π Eng muhim odat: bug topilgach, uni tuzatadigan test yozing. Keyingi safar bug qaytsa,
git bisect run <test>uni avtomatik topadi. Izlash va testlash birga ishlaydi.
Endi sizda buzilishni qidirishning to'liq to'plami bor: git bisect (va bisect run) β "qaysi commit sindirdi?", git blame β "shu satrni kim yozdi?", git log -S/-G β "shu matn qachon kirdi?", git log --grep β "xabarda shu so'z bor commit'lar", git log -- fayl va git grep β fayl tarixi va joriy kod qidiruvi. Keyingi bobda katta loyihalarni boshqarish vositalarini β submodule, Git LFS va monorepo'ni o'rganamiz.
17-bob mashqlari¶
π‘ Mashqlar uchun bo'sh papkada sinov repozitoriysi yarating:
git initqiling, bir funksiyali fayl (masalanhisob.jsichidareturn a+b) bilan boshlang, bir nechta commit qiling, o'rtaroqda funksiyani bilib turib sindiring (a+b->a-b), keyin yana bir nechta commit qo'shing. Kamida 8-10 ta commit to'plansin. Loyihangizning haqiqiy.gitpapkasiga tegmang β alohida sinov papkasida ishlang.
- Sinov repozitoriyingizda
git log --onelinebilan barcha commit'larni ko'ring va o'zingiz bilib turib sindirgan commit qaysi ekanini eslab qoling β keyin bisect topa oladimi, tekshirasiz. git bisect startbilan qidiruvni boshlang. Keyingit bisect bad(HEAD buzilgan) va eng birinchi commit'nigit bisect good <hash>deb belgilang. Git sizni qaysi commit'ga olib bordi va "roughly N steps" nechani ko'rsatdi?- Bisect sizni tashlagan commit'da kodga qarang (
hisob.jsichini oching): bug bormi? Javobga qarabgit bisect goodyokigit bisect badyozing va keyingi qadamga o'ting. - Bisect'ni oxirigacha davom ettiring (har qadamda kodni tekshirib). Git oxirida qaysi commit'ni "is the first bad commit" deb e'lon qildi? U sizning bilib turib sindirgan commit'ingizmi?
- Qidiruv tugagach
git bisect resetqiling vagit log --oneline -n 1bilan tekshiring: siz yanamainning oxiriga qaytdingizmi? git bisect resetni ataylab unutib ko'ring (qilmang!), o'rnigagit statusni o'qing: Git "detached HEAD" haqida nima deyapti? Keyin baribirgit bisect resetqiling.- Endi avtomatlashtiring: bug'ni aniqlaydigan
test.shskripti yozing (grep -q "return a+b" hisob.jsbo'lsa exit 0, aks holda exit 1).chmod +x test.shqiling. git bisect start,git bisect bad HEAD,git bisect good <eski-hash>dan keyingit bisect run ./test.shni ishlating. Git aybdorni o'zi topdimi? Necha marta "running './test.sh'" yozildi?git bisect runtopgan commit, 4-mashqda qo'lda topgan commit bilan bir xilmi? Tugagachgit bisect resetqiling.git bisect logni ishlating: Git sizning "good/bad" javoblaringizni qanday yozib borgan?git blame hisob.jsni ishlating. Buzilgan funksiya turgan satr yonida qaysi commit hash'i, muallif va sana ko'rsatilgan?git blame -L 1,1 hisob.jsbilan faqat 1-satrni chiqaring. Keyin--date=shortqo'shib, sana qanday qisqarganini ko'ring.- 11-mashqda topgan aybdor commit hash'idan keyin
^qo'yib (git blame <hash>^ -L 1,1 hisob.js), o'sha satr buzilishdan oldin qanday bo'lganini ko'ring. Endi to'g'ri kod ko'rinyaptimi? git blametopgan hash'ni olib,git show <hash>bilan o'sha commit'ni to'liq oching: diff'da qaysi qator-(eski), qaysi biri+(yangi) bilan belgilangan?git log --oneline -S "return a-b" -- hisob.jsni ishlating. Necha commit chiqdi va u bug kirgan commitmi? Bu nega aniq topdi?git log --oneline -S "summa" -- hisob.jsvagit log --oneline -G "summa" -- hisob.jsni ketma-ket ishlating. Natijalar farq qildimi?-Snega kamroq commit topdi?git log --oneline --grep "<biror-soz>"bilan commit xabarlari ichidan qidiring (masalan, sindirgan commit xabariga qo'ygan so'z bo'yicha). Topdimi?git log --oneline -- hisob.jsbilan faqat shu faylning tarixini chiqaring, keyingit log -p -- hisob.jsbilan har commit'ning shu fayldagi o'zgarishini ketma-ket ko'ring.git grep "summa"vagit grep -n "return"ni ishlating: bu buyruqgit logdan nimasi bilan farq qiladi (qaysi holatni qidiradi β tarixni yoki hozirni)?- To'liq zanjirni bajaring: (a)
git bisect runbilan aybdorni toping, (b)git show <aybdor>bilan sababini o'qing, (c)git blame <aybdor>^ -L ...bilan oldingi to'g'ri holatni ko'ring, (d) muammoni qanday tuzatishni o'z so'zlaringiz bilan yozing (kodni tuzatish yokigit revertqilish).