ادغام کردن یا مرج کردن — عملیات ترکیب دو شاخه در Git است که تغییرات را از یک شاخه به شاخه دیگر منتقل میکند. در توسعه نرمافزار مدرن، merge روش استاندارد ادغام شاخه feature در شاخه اصلی پروژه است. بر اساس GitHub Octoverse 2024، روزانه بیش از ۱۵ میلیون مرج انجام میشود. Merge — مکانیسم کلیدی کار تیمی است که به برنامهنویسان اجازه میدهد کار چندین نفر را در یک محصول واحد ترکیب کنند.
نکات اصلی
مرج در Git — عملیات ترکیب دو یا چند تاریخچه توسعه در یک تاریخچه است. وقتی یک برنامهنویس شاخهای را ادغام میکند، Git به طور خودکار جد مشترک (base commit) را پیدا میکند و یک کامیت ادغام جدید ایجاد میکند که شامل تغییرات هر دو شاخه است. Three-way merge — الگوریتم استانداردی که سه حالت را مقایسه میکند: جد مشترک، شاخه اول و شاخه دوم.
فرآیند مرج با دستور git merge شروع میشود. Git نقطه انشعاب شاخهها را تعیین میکند و تغییرات را از شاخه مبدأ به شاخه مقصد به ترتیب اعمال میکند. اگر تغییرات تضاد نداشته باشند، Git با توجه به تنظیمات fast-forward را اجرا میکند یا یک merge commit ایجاد میکند. Fast-forward — سناریویی که در آن شاخه مقصد به سادگی به کامیتهای شاخه مبدأ منتقل میشود.
# به شاخه مقصد سوئیچ کن و ادغام کن
git checkout main
git merge feature/payment-module
# ادغام با no-fast-forward صریح
git merge --no-ff feature/payment-module
# در صورت پیچیدگی بیش از حد تعارضات، ادغام را لغو کن
git merge --abort
پرچم --no-ff (no fast-forward) حتی زمانی که fast-forward ممکن است، به اجبار یک merge commit ایجاد میکند. این کار اطلاعات مربوط به انجام تغییرات در یک شاخه جداگانه را حفظ میکند. بسیاری از تیمها این رویکرد را برای حفظ انشعاب تاریخچه به صورت آشکار ترجیح میدهند.
در Git سه استراتژی اصلی برای ادغام شاخهها وجود دارد که هر کدام برای سناریوی خاصی مناسب هستند. انتخاب استراتژی به فرهنگ تیم و الزامات مربوط به تمیزی تاریخچه پروژه بستگی دارد.
| استراتژی | نتیجه | کی استفاده کنیم |
|---|---|---|
| Standard merge | merge commit + تاریخچه کامل | تیمهایی که به تاریخچه کامل اهمیت میدهند |
| Squash merge | یک کامیت، تاریخچه فشرده | شاخههای feature با کامیتهای کوچک متعدد |
| Rebase merge | تاریخچه خطی، بدون merge commit | شاخههای شخصی feature، قبل از ایجاد PR |
Standard merge یک merge commit با دو والد ایجاد میکند. تاریخچه کامل حفظ میشود، اما گراف انشعاب پیچیدهتر میشود. Squash merge تمام کامیتهای شاخه feature را در یک کامیت ترکیب میکند و آن را روی شاخه مقصد اعمال میکند — تاریخچه خطی و تمیز میشود، اما اطلاعات مربوط به مراحل میانی از بین میرود.
Rebase، اگرچه یک ادغام کامل نیست، به همان نتیجه میرسد — تغییرات از یک شاخه به شاخه دیگر منتقل میشوند. تفاوت در این است که تاریخچه بازنویسی میشود: کامیتهای شاخه feature روی آخرین کامیت شاخه مقصد از نو ایجاد میشوند. این یک تاریخچه کاملاً خطی ایجاد میکند، اما هنگام ارسال نیاز به force push دارد.
تعارض هنگام مرج زمانی رخ میدهد که در دو شاخه خطوط یکسانی از یک فایل تغییر کرده باشند. Git نمیتواند به طور خودکار تعیین کند کدام نسخه را حفظ کند و نیاز به دخالت برنامهنویس دارد. تعارضات در فایلها به صورت نشانگرهای ویژه نمایش داده میشوند: <<<<<<<, =======, >>>>>>>.
فرآیند حل تعارض شامل چند مرحله است. ابتدا برنامهنویس فایل دارای تعارض را باز میکند و به صورت دستی تغییرات مورد نظر را انتخاب میکند. مهم است که فقط یکی از نسخهها را انتخاب نکنید، بلکه منطق هر دو تغییر را درک کنید و تصمیم درستی بگیرید. پس از ویرایش فایل، نشانگرهای تعارض حذف میشوند و تغییرات از طریق git add به staging area اضافه میشوند.
# مشاهده لیست فایلهای دارای تعارض
git status
# راهاندازی mergetool (مثلاً VS Code, IntelliJ)
git mergetool
# پس از حل تمام تعارضات
git add .
git merge --continue
# یا ادغام را به طور کامل لغو کن
git merge --abort
استفاده از ابزارهای بصری merge به طور قابل توجهی حل تعارضات را سرعت میبخشد. VS Code، IntelliJ IDEA و GitKraken رابطهایی با سه پنل ارائه میدهند: شاخه فعلی، شاخه ورودی و نتیجه. ابزار git mergetool به طور خودکار ویرایشگر پیکربندی شده را برای هر فایل دارای تعارض باز میکند.
بهترین راه برای جلوگیری از تعارضات پیچیده، همگامسازی منظم شاخه feature با شاخه اصلی است. اگر برنامهنویس روزی یک بار main را با شاخه خود ادغام کند، تعارضات کوچک و به راحتی قابل حل خواهند بود. انباشت تغییرات به مدت یک هفته، تعارضات پیچیده با خطر بالای خطا را تضمین میکند.
Rebase و merge — دو روش ترکیب تغییرات هستند و انتخاب بین آنها اغلب در تیمها بحثبرانگیز است. Rebase کامیتها را از یک شاخه به شاخه دیگر منتقل کرده و تاریخچه را بازنویسی میکند. Merge یک کامیت ادغام جدید ایجاد کرده و تاریخچه انشعاب را حفظ میکند. هر رویکرد مزایا و محدودیتهای خود را دارد.
Rebase زمانی مناسب است که برنامهنویس در شاخه feature محلی خود کار میکند و میخواهد قبل از ایجاد Pull Request یک تاریخچه خطی تمیز داشته باشد. پس از rebase، تمام کامیتها به صورت متوالی و بدون merge commit اضافی مرتب میشوند. با این حال، rebase نیاز به force push دارد و در شاخههایی که چند نفر همزمان روی آنها کار میکنند قابل استفاده نیست.
قاعده طلایی Git: از rebase روی کامیتهایی که قبلاً به مخزن مشترک ارسال شدهاند استفاده نکنید. این تضمین میکند که تاریخچه در شاخه مشترک بدون تغییر باقی میماند و سایر برنامهنویسان با کامیتهای تکراری یا از دست رفته مواجه نخواهند شد. برای ادغام شاخه feature در شاخه اصلی از merge از طریق Pull Request استفاده کنید.
فرآیند صحیح مرج — اساس توسعه پایدار است. در کار تیمی مدرن، مرج نه از طریق کنسول، بلکه از طریق Pull Request در GitHub یا Merge Request در GitLab انجام میشود. PR از بازبینی کد، بررسیهای خودکار CI عبور میکند و تنها پس از آن در شاخه اصلی ادغام میشود.
روش اول — فقط پس از گذراندن تمام بررسیها ادغام کنید. CI pipeline باید پروژه را بسازد، تستها را اجرا کند و کیفیت کد را بررسی کند. اگر حتی یک بررسی ناموفق باشد، مرج مسدود میشود. پلتفرمهای مدرن (GitHub, GitLab) دارای حفاظت داخلی هستند: branch protection rules به طور خودکار مرج را در صورت شکست CI مسدود میکنند.
روش دوم — هرگز کد خراب را ادغام نکنید. قبل از مرج، برنامهنویس باید مطمئن شود که تغییراتش build را خراب نمیکند و عملکرد موجود را پسرفت نمیدهد. برای این کار تستهای خودکار و بازبینی کد وجود دارند.
روش سوم — پس از مرج، شاخههای feature را پاک کنید. شاخهای که قبلاً ادغام شده باید حذف شود. این کار از سردرگمی و به هم ریختگی مخزن جلوگیری میکند. GitHub به طور خودکار پس از مرج PR حذف شاخه را پیشنهاد میکند و تنظیمات مخزن را میتوان برای حذف خودکار پیکربندی کرد.
سوالات متداول
مرج (merge) — ادغام دو شاخه Git در یک شاخه است. تغییرات از یک شاخه از طریق ادغام سهطرفه (three-way merge) به شاخه دیگر منتقل میشود. نتیجه در یک کامیت ادغام جدید ثبت میشود که دو کامیت والد دارد. Merge commit اطلاعات مربوط به اینکه کدام شاخهها ادغام شدهاند را حفظ میکند.
Merge با ایجاد یک کامیت ادغام جدید، تاریخچه انشعاب را حفظ میکند. Rebase تاریخچه را بازنویسی کرده و کامیتها را بدون ایجاد merge commit به شاخه دیگر منتقل میکند. Rebase تاریخچه خطی میدهد اما نیاز به force push دارد. Merge برای شاخههای مشترک ایمنتر است، rebase برای شاخههای شخصی بهتر است.
فایل دارای تعارض را باز کنید، نشانگرهای <<<<<<<, ======= و >>>>>>> را پیدا کنید، تغییرات مورد نظر را انتخاب کرده و نشانگرها را حذف کنید. فایل را با git add اضافه کنید و مرج را با git merge --continue به پایان برسانید. برای حل بصری در VS Code یا IntelliJ IDEA از git mergetool استفاده کنید.
Pull Request (یا Merge Request) هنگام ادغام شاخه feature در شاخه اصلی پروژه الزامی است. PR از بازبینی کد همکاران و بررسیهای خودکار CI عبور میکند. این استاندارد توسعه مدرن است. پوش مستقیم به شاخه main در اکثر پروژهها ممنوع است.
Squash merge تمام کامیتهای شاخه feature را قبل از ادغام در یک کامیت ترکیب میکند. این کار تاریخچه شاخه اصلی را بدون کامیتهای میانی کاری تمیز نگه میدارد. زمانی از squash merge استفاده کنید که شاخه feature شامل کامیتهای خدماتی متعدد (wip, fixes) است و نیازی به حفظ تمام مراحل میانی در تاریخچه نیست.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.