Git va GitHub

Branch va birlashtirish (merge)

Branch (o'zbekcha: shox yoki tarmoq) โ€” Git'ning eng kuchli imkoniyatlaridan biri. U sizga asosiy kodga tegmasdan, alohida yo'nalishda ishlashga imkon beradi. Yangi funksiya yozmoqchimisiz, yoki xatoni tuzatmoqchimisiz โ€” buni alohida branch'da qilib, tayyor bo'lgach asosiy kodga birlashtirasiz. Ushbu darsda branch yaratish, ular orasida o'tish, birlashtirish (merge) va merge konfliktini hal qilishni chuqur o'rganamiz.

Bu darsdagi barcha buyruqlar terminalda (buyruq qatorida) ishga tushiriladi. Ular Git o'rnatilgan mahalliy kompyuteringizda ishlaydi. Kurs sahifasidagi playground'da terminal buyruqlari ishlamaydi โ€” shuning uchun quyidagi bloklar statik misol sifatida keltiriladi. Ularni o'z kompyuteringizdagi haqiqiy Git omborida sinab ko'ring.

Branch nima va nega kerak?

Har bir Git ombori (repository) kamida bitta branch'ga ega. An'anaviy nomi master edi, hozirgi zamonaviy loyihalarda esa ko'pincha main deb ataladi. Bu โ€” asosiy, ishonchli kod turadigan yo'nalish.

Branch โ€” bu commitlar (o'zgarishlar tarixi) ustidagi siljib boruvchi ko'rsatkich (pointer). Har safar yangi commit qilganingizda, joriy branch avtomatik ravishda oxirgi commit'ga siljiydi. Yangi branch ochsangiz, siz shu paytdagi commit'dan yangi mustaqil yo'nalish boshlaysiz.

Branch nima uchun kerak? Bir nechta amaliy sabab:

Mavjud branch'larni ko'rish: git branch

Omborda qanday branch'lar borligini va hozir qaysi biridasiz โ€” buni git branch buyrug'i ko'rsatadi. Joriy branch oldida yulduzcha (*) turadi:

$ git branch
* main

Bu omborda hozircha bitta main branch bor va biz aynan undamiz. Barcha branch'larni (masofaviylarni ham) batafsil ko'rish uchun:

$ git branch -a
* main
  feature-login
  remotes/origin/main

Bu yerda mahalliy main va feature-login, hamda masofaviy (GitHub'dagi) origin/main ko'rinadi.

Yangi branch yaratish: git branch nomi

Yangi branch yaratish uchun uning nomini beramiz. Masalan, feature-login nomli branch ochamiz:

$ git branch feature-login

Diqqat: bu buyruq branch'ni faqat yaratadi, lekin sizni unga o'tkazmaydi. Siz hali ham eski branch'dasiz. Buni git branch bilan tekshiring:

$ git branch
  feature-login
* main

Ko'rib turganingizdek, feature-login yaratildi, lekin yulduzcha hali mainda.

Branch nomlarida bo'sh joy ishlatmang. Ko'p so'zlarni chiziqcha bilan bog'lang: feature-login, fix-header-bug, update-readme. Ko'plab jamoalar nomlarda prefiks ishlatadi: feature/, fix/, hotfix/ โ€” masalan feature/user-profile.

Branch'lar orasida o'tish: git checkout va git switch

Yaratilgan branch'ga o'tish uchun ikki xil yo'l bor. Eskiroq va universal usul โ€” git checkout:

$ git checkout feature-login
Switched to branch 'feature-login'

Endi git branch yulduzchani feature-loginda ko'rsatadi. Bu branch'da qilgan har bir commit faqat shu branch tarixiga tushadi.

Zamonaviy Git (2.23-versiyadan boshlab) yanada tushunarli buyruq taqdim etdi โ€” git switch. U faqat branch almashtirish uchun mo'ljallangan (chalkashlik kamroq):

$ git switch feature-login
Switched to branch 'feature-login'
git checkout juda ko'p vazifani bajaradi: branch almashtirish, fayllarni tiklash, ma'lum commit'ni ko'rish. Bu chalkashlikka olib kelardi. Shuning uchun Git jamoasi uni ikkiga bo'ldi: branch almashtirish uchun git switch, fayllarni tiklash uchun esa git restore. Yangi loyihalarda switch va restoreni afzal ko'ring, lekin checkout ham hamma joyda ishlaydi.

Yaratish va o'tishni bir buyruqda: -b va -c

Ko'pincha branch'ni yaratib, darhol unga o'tmoqchi bo'lasiz. Har safar ikki buyruq yozish shart emas โ€” buni bitta buyruqda qilish mumkin. checkout uchun -b bayrog'i:

$ git checkout -b feature-login
Switched to a new branch 'feature-login'

Zamonaviy switchda xuddi shu narsa -c (create) bayrog'i bilan qilinadi:

$ git switch -c feature-login
Switched to a new branch 'feature-login'

Ikkalasi ham: yangi feature-login branch'ini yaratadi va sizni darhol unga o'tkazadi. Bu โ€” amaliyotda eng ko'p ishlatiladigan usul.

Branch'da ishlash: to'liq amaliy misol

Keling, to'liq oqimni ko'rib chiqamiz. mainda turibmiz, yangi funksiya uchun branch ochamiz, o'zgartiramiz va commit qilamiz:

# 1. Yangi branch yaratib, unga o'tamiz
$ git switch -c feature-login
Switched to a new branch 'feature-login'

# 2. Fayllarni tahrirlaymiz (masalan, login.js yaratamiz)
# ... kod yozamiz ...

# 3. O'zgarishni saqlaymiz
$ git add login.js
$ git commit -m 'Login sahifasini qoshdim'
[feature-login 1a2b3c4] Login sahifasini qoshdim
 1 file changed, 40 insertions(+)

Endi login.js faqat feature-login branch'ida mavjud. Agar mainga qaytsangiz, u fayl u yerda ko'rinmaydi (chunki commit boshqa branch'da):

$ git switch main
Switched to branch 'main'

$ ls
index.html   style.css
# login.js bu yerda yoq โ€” u feature-login branchida qoldi
Branch almashtirishdan oldin joriy o'zgarishlaringizni commit qiling (yoki git stash bilan vaqtincha yashiring). Aks holda saqlanmagan o'zgarishlar bilan branch almashtirmoqchi bo'lsangiz, Git ogohlantirish beradi va ba'zan almashtirishga yo'l qo'ymaydi. git stashni keyingi darsda ko'ramiz.

Branch'larni birlashtirish: git merge

Branch'da ish tugagach, uni asosiy kodga qo'shish kerak. Bu jarayon merge (birlashtirish) deb ataladi. Muhim qoida: siz qaysi branch'ga birlashtirmoqchi bo'lsangiz, avval o'sha branch'ga o'tasiz, keyin kerakli branch'ni unga tortasiz.

Masalan, feature-loginni mainga birlashtiramiz. Avval mainga o'tamiz, keyin feature-loginni birlashtiramiz:

# 1. Maqsad branchiga (main) otamiz
$ git switch main
Switched to branch 'main'

# 2. feature-login ni main ga birlashtiramiz
$ git merge feature-login
Updating a1b2c3d..1a2b3c4
Fast-forward
 login.js | 40 ++++++++++++++++++++++
 1 file changed, 40 insertions(+)

Endi login.js va uning barcha o'zgarishlari mainga qo'shildi. Merge muvaffaqiyatli tugadi.

Yuqoridagi natijada Fast-forward yozuvini ko'rdingiz. Bu โ€” merge'ning eng oddiy turi. U shunda sodir bo'ladiki, main branch feature-login ajralib chiqqandan beri o'zgarmagan bo'lsa. Bu holda Git shunchaki main ko'rsatkichini oldinga suradi โ€” yangi commit yaratmaydi.

Agar ikkala branch ham parallel o'zgargan bo'lsa, Git merge commit deb ataladigan maxsus commit yaratadi. Bu ikki tarixni bittaga bog'laydigan tugun. Bunda tahrirlagich ochilib, sizdan birlashtirish xabarini so'raydi (yoki -m bilan berasiz):

$ git merge feature-login
Merge made by the 'ort' strategy.
 login.js | 40 ++++++++++++++++++++++
 1 file changed, 40 insertions(+)

Merge konflikti nima?

Merge har doim ham silliq o'tmaydi. Merge konflikti โ€” bu ikkala branch'da bir xil faylning bir xil qatori turli xil tarzda o'zgartirilganda yuz beradi. Git qaysi versiyani tanlashni bilmaydi va sizdan qo'lda hal qilishni so'raydi.

Masalan, mainda kimdir index.htmlning 1-qatorini o'zgartirdi, siz esa feature-loginda xuddi shu qatorni boshqacha o'zgartirdingiz. Merge qilganda:

$ git merge feature-login
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Git to'xtaydi va aytadi: konfliktni o'zingiz hal qiling. Hozirgi holatni git status ham ko'rsatadi:

$ git status
On branch main
You have unmerged paths.
  (fix conflicts and run 'git commit')

Unmerged paths:
  (use 'git add <file>...' to mark resolution)
        both modified:   index.html

Merge konfliktini hal qilish

Konfliktli faylni matn tahrirlagichda ochsangiz, Git maxsus belgilar qo'yganini ko'rasiz. Ular ikkala versiyani ko'rsatadi:

<<<<<<< HEAD
<h1>Bosh sahifa</h1>
=======
<h1>Xush kelibsiz!</h1>
>>>>>>> feature-login

Bu belgilarni tushunamiz:

Konfliktni hal qilish โ€” bu shu belgilarni o'chirib, faylni to'g'ri yakuniy holatga keltirish demakdir. Siz bir versiyani tanlashingiz, yoki ikkalasini birlashtirishingiz mumkin. Masalan, quyidagi variantni qoldiramiz:

<h1>Xush kelibsiz!</h1>

Barcha <<<, ===, >>> belgilarini butunlay olib tashlaganingizga ishonch hosil qiling. Keyin faylni saqlab, hal qilinganini Git'ga bildiramiz:

# konflikt hal qilingan faylni belgilaymiz
$ git add index.html

# birlashtirishni yakunlaymiz
$ git commit -m 'index.html dagi merge konfliktini hal qildim'
[main 5f6g7h8] index.html dagi merge konfliktini hal qildim
Konfliktlarni qo'rqmang โ€” ular normal holat, ayniqsa jamoada ishlaganda. Zamonaviy tahrirlagichlar (masalan, VS Code) konfliktli faylni chiroyli ko'rsatadi va "Accept Current", "Accept Incoming", "Accept Both" tugmalarini taqdim etadi. Bu qo'lda belgilar bilan ishlashdan ancha qulay.
Agar merge o'rtasida hamma narsani bekor qilib, boshlang'ich holatga qaytmoqchi bo'lsangiz: git merge --abort. Bu birlashtirishni to'liq to'xtatadi va omborni merge boshlanishidan oldingi holatga qaytaradi.

Keraksiz branch'ni o'chirish: git branch -d

Branch birlashtirilib, endi kerak bo'lmasa, uni o'chirib tozalik saqlash yaxshi odat. Muvaffaqiyatli birlashtirilgan branch'ni -d (delete) bilan o'chiramiz:

$ git branch -d feature-login
Deleted branch feature-login (was 1a2b3c4).

Agar branch hali hech qayerga birlashtirilmagan bo'lsa, -d uni o'chirmaydi โ€” Git ish yo'qolib qolishidan ogohlantiradi:

$ git branch -d feature-test
error: The branch 'feature-test' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-test'.

Agar rostdan ham keraksiz bo'lsa va birlashtirilmagan ishni yo'qotishga rozimisiz โ€” katta -D (majburiy o'chirish) ishlatiladi:

$ git branch -D feature-test
Deleted branch feature-test (was 9z8y7x6).
-D (katta harf) branch'ni birlashtirilmagan bo'lsa ham majburan o'chiradi. Undagi commitlar boshqa branch'da bo'lmasa โ€” butunlay yo'qoladi. Shuning uchun -Dni faqat o'zgarishlar kerak emasligiga to'liq ishonganingizda ishlating.

Xulosa

Ushbu darsda branch bilan ishlashning to'liq davrini o'rgandik:

Branch'lar Git ish oqimining yuragidir. Keyingi darsda mahalliy omborni GitHub bilan bog'lab, kodni internetga yuborishni o'rganamiz.