Skip to Content

 

`


Merge in GitHub

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   
  1. main مسیر خودش را داشته (A-B-C)
  2. یک Branch دیگر تغییرات خودش را داشته (D-E)
  3. با Merge، این دو مسیر ترکیب شده‌اند و یک Commit جدید (M1) ساخته شده است.

Rebase برای مرتب کردن تاریخچه و قرار دادن تغییرات یک Branch روی آخرین وضعیت Branch دیگر استفاده می‌شود. در Rebase، Git مسیرها را یکی می‌کند و Commitها را دوباره روی آخرین نسخه قرار می‌دهد.

'A---B---C---D'---E
  1. دیگر یک مسیر جداگانه وجود ندارد.
  2. تغییرات D و E طوری قرار گرفته‌اند که انگار از ابتدا بعد از C انجام شده‌اند.
  3. تاریخچه ساده‌تر و خطی‌تر دیده می‌شود.




Merge Conflict چیست؟

Merge Conflict زمانی اتفاق می‌افتد که Git نمی‌تواند به‌صورت خودکار تصمیم بگیرد که کدام تغییر را نگه دارد. معمولاً زمانی رخ می‌دهد که دو Branch یک بخش از یک فایل را به شکل متفاوت تغییر داده باشند.


رایج‌ترین دلایل ایجاد Merge Conflict


  1. تغییر یک بخش مشابه از یک فایل توسط چند Developer : مثلاً دو نفر یک تابع یا یک خط کد را به شکل متفاوت تغییر دهند.
  2. وقتی دو Branch یک فایل را تغییر می‌دهند و تغییرات آن‌ها روی بخش‌های مشابه یا وابسته از فایل باشد.
  3. . حذف شدن یک فایل در یک Branch و ویرایش همان فایل در Branch دیگر
  4. به‌روز نکردن 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:

  1. وارد Pull Request شوید.
  2. در صورت نمایش گزینه Resolve conflicts، روی آن کلیک کنید.
  3. تغییرات متضاد را بررسی کرده و تصمیم بگیرید کدام تغییرات حفظ شوند یا در صورت نیاز آن‌ها را با یکدیگر ترکیب کنید.
  4. پس از حذف علامت‌های Conflict، تغییرات را ذخیره کنید.
  5. روی Mark as resolved یا Commit merge (بسته به رابط GitHub) کلیک کنید.
  6. پس از رفع 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 با سه روش وجود دارد:

  1. Create a Merge Commit
  2. Squash and Merge
  3. 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های زیاد
- شلوغ شدن History در پروژه‌های بزرگ

- از بین رفتن جزئیات Commitهای کوچک
- مشخص نبودن مراحل توسعه داخل Feature Branch

- تغییر Hash Commitها
- مناسب نبودن برای Branchهایی که چند نفر همزمان روی آن کار می‌کنند