تفاوت Git Rebase و Merge در این است که Merge تاریخچه را حفظ می‌کند و یک «merge commit» می‌سازد، اما Rebase تاریخچه را بازنویسی می‌کند و کامیت‌ها را روی مبنای جدید می‌چیند تا خطی شود. قاعده عملی: برای شاخه‌های عمومی و ادغام نهایی از Merge استفاده کنید؛ برای شاخه‌های شخصی و تمیزکردن تاریخچه قبل از انتشار، Rebase انتخاب بهتری است. هرگز شاخه‌ای را که دیگران از آن pull می‌گیرند Rebase نکنید.

تفاوت Git Rebase و Merge به زبان ساده

هر دو روش هدف مشترکی دارند: همگام کردن تغییرات شاخه‌ها. اما نحوه برخورد با تاریخچه متفاوت است:

  • Merge: دو شاخه را به هم متصل می‌کند و یک merge commit می‌سازد. تاریخچه به صورت شاخه‌ای باقی می‌ماند و مسیر دقیق توسعه حفظ می‌شود.
  • Rebase: پایه شاخه شما را تغییر می‌دهد و کامیت‌ها را یکی یکی روی مبنای جدید «اعمال» می‌کند. تاریخچه خطی، اما شناسه‌های کامیت (SHA) تغییر می‌کنند.

نتیجه عملی: Merge امن‌تر برای همکاری است، Rebase مرتب‌تر برای خوانایی تاریخچه و قبل از اشتراک‌گذاری.

چه زمانی از Merge استفاده کنیم؟

  • ادغام ویژگی در شاخه‌های عمومی مثل main یا develop: تاریخچه واقعی حفظ می‌شود، خطر کمتری برای هم‌تیمی‌ها دارد.
  • زمانی که شواهد ادغام (context) مهم است: در پروژه‌های پیچیده، merge commit‌ها نقاط ادغام را شفاف می‌کنند.
  • وقتی امضای کامیت‌ها یا ارجاع به SHAs اهمیت دارد: Rebase شناسه‌ها را عوض می‌کند؛ Merge آن‌ها را نگه می‌دارد.
  • انتشار نسخه و hotfixهای حساس: حداقل ریسک برای تاریخچه مشترک.

چه زمانی از Rebase استفاده کنیم؟

  • به‌روزرسانی شاخه شخصی قبل از باز کردن PR: تغییرات main را روی شاخه خودتان Rebase کنید تا تاریخچه خطی و تعارض‌ها زودتر حل شوند.
  • تمیز کردن تاریخچه قبل از اشتراک‌گذاری: با interactive rebase کامیت‌های ویرایشی را squash/fixup کنید.
  • هماهنگ نگه داشتن مخزن محلی: تنظیم pull با –rebase تاریخچه محلی شما را بدون merge commitهای اضافی خطی نگه می‌دارد.

توجه مهم: شاخه‌ای که روی ریموت منتشر شده و دیگران از آن pull می‌گیرند را Rebase نکنید. اگر مجبور شدید، فقط با --force-with-lease و بعد از هماهنگی تیمی.

نمونه عملی: یک فلو روزمره تیمی

هدف

روی شاخه feature کار می‌کنید؛ main جلوتر رفته. می‌خواهید ویژگی را با تاریخچه تمیز و کم‌ریسک ادغام کنید.

مراحل

  1. ایجاد شاخه ویژگی و چند کامیت:
git checkout -b feature/login
git commit -m "feat: add login form"
git commit -m "fix: validation edge cases"
  1. همگام‌سازی با main با Rebase (فقط شاخه شخصی):
git fetch origin
git rebase origin/main
# در صورت تعارض:
#   فایل‌ها را اصلاح کنید
git add path/to/file
git rebase --continue
# در صورت پشیمانی:
#   git rebase --abort
  1. تمیز کردن تاریخچه (اختیاری، قبل از انتشار):
git rebase -i HEAD~4
# در ویرایشگر:
# pick c1 feat: add login form
# squash c2 fix: address review comments
# fixup c3 style: lint
# pick c4 feat: remember me
  1. انتشار شاخه (در صورت Rebase، به push دقت کنید):
git push -u origin feature/login --force-with-lease
  1. ادغام در main روی سرور:
  • استراتژی «Merge commit» اگر حفظ تاریخچه شاخه‌ها مهم است.
  • «Squash and merge» اگر می‌خواهید یک کامیت تمیز در main داشته باشید.
  • «Rebase and merge» اگر تیم تاریخچه کاملا خطی در main می‌خواهد و تعارض‌ها قبلا حل شده‌اند.

تاریخچه چه شکلی می‌شود؟

# قبل از Rebase
A---B---C  main
     \
      D---E  feature

# بعد از Rebase (روی main)
A---B---C---D'---E'  feature

# اگر حالا merge کنید و fast-forward ممکن باشد، merge commit لازم نیست.

با Merge مستقیمِ main در feature (بدون Rebase)، یک گره ادغام می‌گیرید:

# merge feature into main (بدون rebase)
A---B---C-------F  main (F یک merge commit است)
     \     /
      D---E  feature

مزایا و معایب به‌صورت خلاصه

معیارMergeRebase
شکل تاریخچهشاخه‌ای، با merge commitخطی و مرتب
حفظ SHA و امضابلهخیر (بازنویسی می‌شود)
ریسک برای شاخه عمومیکمزیاد (در صورت استفاده اشتباه)
خوانایی برای مرور تغییراتخوب در پروژه‌های بزرگعالی در نگاه کلان
بازگشت (revert) و ردیابی با bisectقابل پیش‌بینیممکن است دشوارتر شود

نکات ایمنی و خطاهای رایج

  • هیچ‌وقت شاخه‌ای که دیگران از آن pull می‌گیرند را Rebase نکنید. اگر ناچار شدید: هماهنگی تیمی + git push --force-with-lease.
  • به جای --force از --force-with-lease استفاده کنید تا روی کار جدید همکاران overwrite نکنید.
  • تنظیم pull:
# خطی نگه داشتن pull محلی
git config --global pull.rebase true
# یا فقط برای یک مخزن
git config pull.rebase true
  • تعارض‌های Rebase را مرحله به مرحله حل کنید و هر بار git rebase --continue بزنید. برای رها کردن Rebase: git rebase --abort.
  • کامیت‌های امضا شده یا ارجاع شده در issue tracker با Rebase بی‌اعتبار می‌شوند؛ در این شرایط Merge مطمئن‌تر است.
  • در CI/CD ممکن است بازنویسی تاریخچه، buildهای اضافی یا گیج‌کنندگی ایجاد کند؛ سیاست تیمی مشخص تعریف کنید.

تصمیم سریع: کدام مناسب‌تر است؟

  • در شاخه شخصی و قبل از انتشار: Rebase برای به‌روزرسانی روی main و تمیز کردن تاریخچه.
  • برای ادغام نهایی در شاخه‌های مشترک: Merge (یا Squash and merge) برای کاهش ریسک.
  • اگر نیاز به ردیابی دقیق نقاط ادغام دارید: Merge commit.
  • اگر تیم تاریخچه کاملا خطی در main می‌خواهد و همه با قواعد موافقند: «Rebase and merge» روی سرور، با انضباط بالا.

دو مثال کاربردی تکمیلی

پاکسازی تاریخچه قبل از PR با interactive rebase

git rebase -i HEAD~6
# pick اولین کامیت اصلی
# squash یا fixup برای کامیت‌های ریز اصلاحی
# reword برای ویرایش پیام کامیت

خروجی: یک یا دو کامیت تمیز و قابل مرور. این کار را قبل از انتشار شاخه انجام دهید.

ادغام بدون fast-forward برای نگه داشتن «گِره ادغام»

git checkout main
git merge --no-ff feature/login -m "merge: feature/login"

این کار نشان می‌دهد این تغییرات یک بسته منسجم بوده‌اند.

ارتباط با ابزارهای تیمی

  • GitHub/GitLab سه گزینه ارائه می‌دهند: Merge commit، Squash and merge، Rebase and merge. انتخاب را با توجه به سیاست تیم و حساسیت به تاریخچه انجام دهید.
  • Protected branchها را طوری پیکربندی کنید که Rebase یا force-push ناخواسته روی main ممکن نباشد.

گام بعدی چیست؟

اگر می‌خواهید Git را نظام‌مندتر یاد بگیرید و این الگوها را تمرین کنید، مسیر آموزش گیت و گیت هاب نقطه شروع منظمی است. برای رفع اشکال‌های تاریخچه یا یافتن کامیت خراب، آشنایی با ابزار bisect بسیار کمک‌کننده است؛ راهنمای عملی را در آموزش Git Bisect: پیدا کردن سریع Commit خراب ببینید.

جمع‌بندی عملی: Rebase برای شاخه‌های شخصی و نظم تاریخچه، Merge برای همکاری ایمن در شاخه‌های عمومی. با این قانون ساده، هم تعارض‌ها کمتر غافلگیرتان می‌کنند و هم تاریخچه پروژه قابل اتکاتر می‌ماند.