Branch و Merge در Git
Branchها قلب Git هستند. اجازه میدهند بدون تأثیر روی main، روی فیچرهای جدید کار کنید. در این فصل ساخت branch، تعویض، merge، حل تعارض و الگوهای کاری را یاد میگیریم.
Branchها قلب Git هستند. اجازه میدهند بدون تأثیر روی main، روی فیچرهای جدید کار کنید. در این فصل ساخت branch، تعویض، merge، حل تعارض و الگوهای کاری را یاد میگیریم.
Branch چیست؟
یک branch در Git اساساً یک اشارهگر سبک به یک commit است. وقتی commit جدید میسازید، این اشارهگر جلو میرود.
main: C1 ── C2 ── C3
↑
HEAD (main)
# ساخت branch جدید "feature":
main: C1 ── C2 ── C3
↑
├── main
└── feature (HEAD)
# commit جدید روی feature:
main: C1 ── C2 ── C3 ←── main
C4 ←── feature (HEAD)
ساخت Branch
# لیست branchها (* یعنی فعلی)
git branch
# * main
# ساخت branch جدید (بدون تعویض)
git branch feature-login
# ساخت + تعویض - دستور قدیمی
git checkout -b feature-login
# ساخت + تعویض - دستور جدید (Git 2.23+) - پیشنهاد
git switch -c feature-login
# تعویض به branch موجود
git switch main
git checkout main # نسخه قدیمی
# لیست همه branchها (شامل remote)
git branch -a
# آخرین commit هر branch
git branch -v
# branchهای merged شده در main
git branch --merged main
# branchهای merge نشده
git branch --no-merged main
switch vs checkout
از Git 2.23، دستورات switch و restore معرفی شدند که جایگزین checkout پیچیده میشوند:
| کار | قدیمی | جدید (پیشنهاد) |
|---|---|---|
| تعویض branch | git checkout main |
git switch main |
| ساخت + تعویض | git checkout -b feat |
git switch -c feat |
| برگرداندن فایل | git checkout file.txt |
git restore file.txt |
| unstage کردن | git reset HEAD file |
git restore --staged file |
تغییر نام و حذف Branch
# تغییر نام branch فعلی
git branch -m new-name
# تغییر نام branch دیگر
git branch -m old-name new-name
# حذف branch (فقط اگر merged شده)
git branch -d feature-login
# حذف branch بهزور (حتی اگر merge نشده)
git branch -D experimental-feature
# حذف branch روی remote
git push origin --delete feature-login
git merge – ادغام Branchها
وقتی فیچر تمام شد، باید آن را به main برگردانیم:
# 1. به branch مقصد بروید (main)
git switch main
# 2. تغییرات از feature-login را merge کنید
git merge feature-login
# 3. حذف branch (اختیاری)
git branch -d feature-login
انواع Merge
۱. Fast-Forward Merge
وقتی main از زمان branch جلو نرفته، Git فقط اشارهگر را جلو میبرد:
قبل:
main: C1 ── C2 ←── main
C3 ── C4 ←── feature
بعد از merge:
main: C1 ── C2 ── C3 ── C4 ←── main, feature
۲. Three-Way Merge
وقتی main هم تغییر کرده، یک merge commit ساخته میشود:
قبل:
main: C1 ── C2 ── C5 ←── main
C3 ── C4 ←── feature
بعد از merge:
main: C1 ── C2 ── C5 ── M ←── main
╱
C3 ── C4
گزینههای merge
# merge فقط اگر fast-forward ممکن باشد
git merge --ff-only feature
# همیشه merge commit بساز (حتی اگر fast-forward ممکن باشد)
# مفید برای حفظ تاریخچه فیچر
git merge --no-ff feature
# squash - همه commitها به یک commit ادغام شوند
git merge --squash feature
git commit -m "feat: add login feature"
حل Conflict (تعارض)
وقتی دو branch همان خط از همان فایل را تغییر میدهند، Git نمیتواند خودکار merge کند:
git merge feature-login
# Auto-merging app.py
# CONFLICT (content): Merge conflict in app.py
# Automatic merge failed; fix conflicts and then commit the result.
git status
# both modified: app.py
مرور conflict در فایل
def login(username, password):
<<<<<<< HEAD
return check_password_simple(username, password)
=======
return check_password_bcrypt(username, password)
>>>>>>> feature-login
<<<<<<< HEAD: شروع نسخه main (فعلی)=======: جداکننده>>>>>>> feature-login: پایان نسخه feature
حل دستی conflict
# 1. فایل را ویرایش کنید (یا یکی را انتخاب کنید یا ترکیب کنید)
nano app.py
# نمونه نتیجه نهایی:
# def login(username, password):
# return check_password_bcrypt(username, password)
# (نشانههای conflict را پاک کنید!)
# 2. فایل حلشده را add کنید
git add app.py
# 3. commit کنید (Git پیام merge آماده میکند)
git commit
# یا اگر میخواهید merge را لغو کنید:
git merge --abort
ابزارهای merge
# استفاده از merge tool
git mergetool
# تنظیم VS Code بهعنوان merge tool
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# یا meld (Linux)
git config --global merge.tool meld
قبل از merge: ابزارهای visual
# ببینید چه چیزی merge خواهد شد
git diff main..feature-login
# فایلهایی که در feature تغییر کردهاند اما در main نه
git log main..feature-login --oneline
# نمایش graphical
git log --graph --all --oneline --decorate
استراتژیهای Merge
Git چند الگوریتم merge دارد:
# default - recursive (Git 2.34-) یا ort (Git 2.34+)
git merge feature
# گرفتن همه تغییرات از feature، نادیده گرفتن main در conflict
git merge -X theirs feature
# نگهداشتن نسخه main در conflict
git merge -X ours feature
# نادیدهگرفتن whitespace در merge
git merge -X ignore-all-space feature
git cherry-pick – برداشتن یک commit خاص
وقتی فقط یک commit از branch دیگر را میخواهید:
# سناریو: یک bug fix در feature هست که الان لازم داریم
git switch main
git cherry-pick a3f4b2c
# چند commit
git cherry-pick a3f4b2c b2c3d4e
# بازه commitها (نه شامل اولی)
git cherry-pick a3f4b2c..b2c3d4e
# بدون commit (فقط تغییرات را stage کن)
git cherry-pick -n a3f4b2c
الگوهای Branching رایج
۱. Topic Branches
هر فیچر/باگ یک branch جدا، نام مرتبط:
git switch -c feature/user-profile
git switch -c bugfix/login-error
git switch -c hotfix/security-patch
git switch -c refactor/database-layer
۲. Long-running Branches
برخی branchها همیشه وجود دارند:
mainیاmaster: نسخه stable productiondevelop: نسخه در حال توسعهstaging: نسخه تست
قراردادهای نامگذاری
الگوهای رایج:
feature/user-authentication
feature/JIRA-123-add-search
bugfix/null-pointer-in-cart
fix/cart-checkout-error
hotfix/security-cve-2024-1234
release/v2.5.0
experimental/new-algorithm
spike/redis-evaluation
chore/update-dependencies
docs/api-documentation
Branch با تغییرات uncommitted
گاهی در میانه کار باید به branch دیگر بروید:
# تلاش برای تعویض با تغییرات pending
git switch main
# error: Your local changes to the following files would be overwritten by checkout
# راهحل ۱: stash (در فصل ۸ مفصل)
git stash
git switch main
# ... کارهای دیگر ...
git switch feature-x
git stash pop
# راهحل ۲: commit موقت (WIP)
git add .
git commit -m "WIP: temporary"
git switch main
# بعداً بازگردید:
git switch feature-x
git reset HEAD~1 # حذف WIP commit، تغییرات باقی میمانند
مقایسه Branchها
# تغییرات بین دو branch
git diff main feature-login
# فقط commitهایی که در feature هستند نه در main
git log main..feature-login
# فقط در main
git log feature-login..main
# هر دو طرف (تفاوتهای یکطرفه)
git log --left-right main...feature-login
# فایلهای تغییریافته
git diff --name-only main feature-login
مثال کامل: یک فیچر
# شروع از main بهروز
git switch main
git pull # فصل بعد
# branch جدید
git switch -c feature/dark-mode
# توسعه
echo ".dark { background: #000; }" > dark.css
git add dark.css
git commit -m "feat: add dark mode CSS"
# تست و اصلاح
echo ".dark { color: #fff; }" >> dark.css
git add dark.css
git commit -m "feat: add dark mode text color"
# بازگشت به main
git switch main
# merge با حفظ تاریخچه فیچر
git merge --no-ff feature/dark-mode -m "Merge feature/dark-mode"
# حذف branch
git branch -d feature/dark-mode
# بررسی نتیجه
git log --oneline --graph
بهترین شیوهها
- هرگز روی main مستقیم کار نکنید (در پروژههای تیمی)
- هر فیچر = یک branch جدا
- نام branch توصیفی –
feature/user-profileنهmy-branch - main را همیشه deployable نگه دارید
- قبل از merge، با main بهروز کنید:
git pull origin main - commitها را squash کنید قبل از merge برای تاریخچه تمیز (در فصل ۸)
- branchهای merge شده را پاک کنید
switchبهجایcheckout
جمعبندی
- Branch یک اشارهگر سبک به یک commit است
git switch -c name: ساخت + تعویض- Fast-forward: سادهترین merge
- Three-way merge: وقتی هر دو branch جلو رفتهاند
- Conflict وقتی همان خط در دو branch تغییر کرده
- cherry-pick برای برداشتن یک commit خاص
- topic branches: هر کار یک branch
در فصل بعد، با remote کار میکنیم: GitHub را وارد بازی میکنیم.