~/icsd.ir — bash
SYSTEM_ONLINE

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 مهم است؟ اجازه می‌دهد چند کار را موازی انجام دهید بدون اینکه نیمه‌کاره‌ها در main بریزند. هر فیچر، باگ‌فیکس، یا آزمایش روی branch خودش.

ساخت 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 production
  • develop: نسخه در حال توسعه
  • 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 را وارد بازی می‌کنیم.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.