Merge چیست؟
Merge در GitHub به معنی ادغام کردن تغییرات کد از یک Branch به Branch دیگر است. این کار باعث میشود تغییرات انجامشده در یک Branch با Branch مقصد ترکیب شوند و یک نسخه یکپارچه از پروژه ایجاد شود. معمولاً این Branch مقصد، main است، اما Merge میتواند بین هر دو Branch انجام شود.
چه زمانی از Merge استفاده میشود؟
زمانی از Merge استفاده میشود که تغییرات انجامشده در یک Branch آماده انتقال به Branch دیگری باشند. معمولاً بعد از اینکه یک Developer تغییرات موردنظر خود را در یک Branch جداگانه اعمال کرد و از درست بودن آنها مطمئن شد، یک Pull Request ایجاد میکند تا تغییرات بررسی و بازبینی شوند. پس از تأیید Pull Request و اطمینان از صحت کد، عملیات Merge انجام میشود تا تغییرات Branch مبدأ با Branch مقصد (معمولاً main) ادغام شوند.

تفاوت Merge و Rebase چیست؟
Merge برای ادغام کردن دو Branch استفاده میشود. در Merge، Git تاریخچه هر دو Branch را حفظ میکند و یک Commit جدید به نام Merge Commit ایجاد میکند که نشان میدهد این دو مسیر در این نقطه با هم ترکیب شدهاند.
A---B---C-----M1
/ \
----D--E
- main مسیر خودش را داشته (A-B-C)
- یک Branch دیگر تغییرات خودش را داشته (D-E)
- با Merge، این دو مسیر ترکیب شدهاند و یک Commit جدید (M1) ساخته شده است.
Rebase برای مرتب کردن تاریخچه و قرار دادن تغییرات یک Branch روی آخرین وضعیت Branch دیگر استفاده میشود. در Rebase، Git مسیرها را یکی میکند و Commitها را دوباره روی آخرین نسخه قرار میدهد.
'A---B---C---D'---E
- دیگر یک مسیر جداگانه وجود ندارد.
- تغییرات D و E طوری قرار گرفتهاند که انگار از ابتدا بعد از C انجام شدهاند.
- تاریخچه سادهتر و خطیتر دیده میشود.

Merge Conflict چیست؟
Merge Conflict زمانی اتفاق میافتد که Git نمیتواند بهصورت خودکار تصمیم بگیرد که کدام تغییر را نگه دارد. معمولاً زمانی رخ میدهد که دو Branch یک بخش از یک فایل را به شکل متفاوت تغییر داده باشند.
رایجترین دلایل ایجاد Merge Conflict
- تغییر یک بخش مشابه از یک فایل توسط چند Developer : مثلاً دو نفر یک تابع یا یک خط کد را به شکل متفاوت تغییر دهند.
- وقتی دو Branch یک فایل را تغییر میدهند و تغییرات آنها روی بخشهای مشابه یا وابسته از فایل باشد.
- . حذف شدن یک فایل در یک Branch و ویرایش همان فایل در Branch دیگر
- بهروز نکردن Branch و تأخیر در Merge با Branch اصلی
نحوه تشخیص Conflict در GitHub و Git
در GitHub:
پس از ایجاد Pull Request، GitHub بررسی میکند که آیا Branch مبدأ و Branch مقصد بدون مشکل قابل Merge هستند یا خیر. اگر Conflict وجود داشته باشد، پیامی مبنی بر وجود Merge Conflict نمایش داده میشود و تا زمانی که Conflict برطرف نشود، امکان Merge وجود نخواهد داشت.
در Git :
پس از اجرای دستور git merge یا git rebase، اگر Conflict ایجاد شود، Git عملیات را متوقف کرده و فایلهای دارای Conflict را مشخص میکند. با اجرای دستور زیر نیز میتوان فایلهای دارای Conflict را مشاهده کرد:
git status
چگونه Merge Conflict را رفع کنیم؟
رفع Merge Conflict در GitHub
همانطور که گفتیم اگر هنگام ایجاد Pull Request، GitHub تشخیص دهد که بین Branch مبدأ و Branch مقصد Conflict وجود دارد، پیام وجود Conflict را نمایش میدهد.
مراحل رفع Conflict در GitHub:
- وارد Pull Request شوید.
- در صورت نمایش گزینه Resolve conflicts، روی آن کلیک کنید.
- تغییرات متضاد را بررسی کرده و تصمیم بگیرید کدام تغییرات حفظ شوند یا در صورت نیاز آنها را با یکدیگر ترکیب کنید.
- پس از حذف علامتهای Conflict، تغییرات را ذخیره کنید.
- روی Mark as resolved یا Commit merge (بسته به رابط GitHub) کلیک کنید.
- پس از رفع Conflict، Pull Request دوباره آماده Merge خواهد بود.
رفع Merge Conflict در Git
اگر Conflict پیچیده باشد یا قابل حل از طریق GitHub نباشد، باید از محیط Local استفاده کنید. در این حالت Repository را روی سیستم خود دریافت کرده، تغییرات متضاد را بررسی و بهصورت دستی اصلاح میکنید. در ادامه یک مثال عملی از ایجاد و رفع Merge Conflict در محیط Local آورده شده است.
فرض کنید یک فایل به نام config.txt در پروژه وجود دارد:
Database=MySQL
Environment=Development
دو Developer بهصورت همزمان روی این فایل کار میکنند.
- ایجاد تغییر در Branch اول
Developer اول یک Branch به نام feature-database ایجاد میکند:
git checkout -b feature-database
سپس مقدار Database را تغییر میدهد:
Database=PostgreSQL
Environment=Development
و سپس تغییرات را commmit میکند.
- ایجاد تغییر در Branch دوم
Developer دوم از همان نسخه اصلی پروژه یک Branch دیگر ایجاد میکند:
git checkout main
git checkout -b feature-mongodb
او همان بخش از فایل را تغییر میدهد:
Database=MongoDB Environment=Development
و در نهایت تغییرات را commit میکند.
حال اگر بخواهیم این دو Branch را با یکدیگر merge کنیم:
git checkout feature-database
git merge feature-mongodb
در این مرحله Git نمیتواند بهصورت خودکار تصمیم بگیرد کدام تغییر باید حفظ شود و پیام زیر را نمایش میدهد:
CONFLICT (content): Merge conflict in config.txt
Automatic merge failed; fix conflicts and then commit the result.
حال چگونه این مشکل را برطرف کنیم؟
ابتدا فایل دارای conflict را بوسیله کامند "git status " بررسی میکنیم. خروجی آن نام فایل را به ما نشان می دهد.
both modified: config.txt
با باز کردن فایل Git مشکل را برای ما مشخص کرده است.
<<<<<<< HEAD
Database=PostgreSQL
=======
Database=MongoDB
>>>>>>> feature-mongodb
Environment=Development
Developer باید تصمیم بگیرد کدام تغییر باقی بماند. برای مثال، تصمیم میگیرد PostgreSQL استفاده شود:
Database=PostgreSQL
Environment=Development
بعد از حذف علامتهای Conflict، فایل ذخیره میشود.
و در نهایت فایل اصلاح شده را به stage اضافه و در آخر آن را merge میکنیم.
در این مثال مشاهده میکنیم که Git فقط وجود Conflict را تشخیص میدهد و قادر به انتخاب نسخه صحیح نیست. تصمیمگیری درباره نگه داشتن، حذف یا ترکیب تغییرات بر عهده Developer است.
روشهای Merge در GitHub
وقتی یک Pull Request در GitHub تأیید میشود، بسته به تنظیمات Repository، امکان Merge با سه روش وجود دارد:
- Create a Merge Commit
- Squash and Merge
- Rebase and Merge
Create a Merge Commit
در این روش Git یک Commit جدید برای Merge ایجاد میکند و تاریخچه هر دو Branch را حفظ میکند.
زمان مناسب استفاده:
وقتی میخواهیم تمام تاریخچه تغییرات و Branchها حفظ شوند، معمولاً در پروژههای بزرگ یا تیمهایی که Tracking تغییرات مهم است استفاده میشود.
Squash and Merge
Squash یعنی چند Commit را به یک Commit تبدیل کردن. فرض کنید یک Developer در Branch خود چند Commit دارد:
feature:
Add button
Fix button style
Fix typo
Update test
با Squash همه اینها تبدیل میشوند به یک Commit:
Add user button feature
زمان مناسب استفاده:
وقتی میخواهیم Branchهای Feature با چند Commit کوچک، به شکل یک تغییر نهایی و تمیز وارد main شوند.
Rebase and Merge
در این روش Git ابتدا Commitهای Branch را روی آخرین وضعیت Branch مقصد قرار میدهد و سپس آنها را Merge میکند.
زمان مناسب استفاده:
وقتی تیم میخواهد تاریخچه ساده و خطی داشته باشد و اعضا قبل از Merge، Branch خود را مرتب کنند.
مقایسه مزایا و معایب این سه روش:
روش | مزایا | معایب |
Create a Merge Commit | - حفظ کامل تاریخچه تغییرات - مشخص است که چه زمانی دو Branch با هم Merge شدهاند - مناسب برای پروژههایی که History اهمیت دارد | - ایجاد Commit اضافی برای Merge - شلوغتر شدن تاریخچه پروژه |
Squash and Merge | - تمیز و ساده شدن History در main - حذف Commitهای کوچک و غیرضروری مثل Fix typo یا Update test - مناسب برای پروژههای تیمی با تعداد Commit زیاد | - جزئیات Commitهای قبلی از بین میرود - مشخص نیست تغییرات یک Feature در چند مرحله انجام شده است |
Rebase and Merge | - ایجاد History مرتب و خطی - خواندن تاریخچه سادهتر میشود - حذف Merge Commitهای اضافی | - Commitها دوباره ساخته میشوند و Hash آنها تغییر میکند - برای Branchهای مشترک مناسب نیست - ممکن است برای افراد تازهکار پیچیده باشد |
مقایسه روشهای Merge در GitHub
سه روش اصلی Merge در GitHub یعنی Merge Commit، Squash and Merge و Rebase and Merge از نظر نحوه مدیریت تاریخچه، خوانایی Commitها و مناسب بودن برای پروژههای تیمی تفاوتهایی دارند.
معیار | Merge Commit | Squash and Merge | Rebase and Merge |
تاریخچه (History) | تاریخچه کامل Branchها حفظ میشود و یک Merge Commit جدید ایجاد میشود. ساختار History معمولاً شامل چند مسیر جداگانه است. | تاریخچه سادهتر میشود؛ تمام Commitهای یک Branch به یک Commit تبدیل شده و فقط یک تغییر نهایی در History دیده میشود. | تاریخچه به شکل خطی نمایش داده میشود و Merge Commit ایجاد نمیشود؛ Commitها مستقیماً پشت سر هم قرار میگیرند. |
خوانایی Commitها | گر پروژه Commitهای زیادی داشته باشد، History ممکن است شلوغ شود؛ اما تمام جزئیات تغییرات قابل مشاهده است. | خوانایی بسیار بالا است، زیرا فقط Commitهای نهایی و مهم در Branch اصلی باقی میمانند. | خوانایی بالا است، چون History مرتب و خطی است و دنبال کردن تغییرات سادهتر میشود. |
مناسب بودن برای پروژههای تیمی | مناسب برای تیمهایی که حفظ کامل تاریخچه و مشاهده مسیر توسعه اهمیت دارد. | مناسب برای اکثر تیمهای توسعه، مخصوصاً پروژههایی که تعداد زیادی Feature Branch دارند و میخواهند main تمیز بماند. | مناسب برای تیمهایی که استاندارد مشخصی برای مدیریت History دارند و اعضا با Rebase آشنا هستند. |
محدودیتها | - ایجاد Merge Commitهای زیاد | - از بین رفتن جزئیات Commitهای کوچک | - تغییر Hash Commitها |