مقدمه‌ای بر Git Merge و Git Rebase: این دو چه کاری انجام می‌دهند، و چه زمانی باید از آن‌ها استفاده کنیم؟

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 14 اردیبهشت 1398
دسته بندی ها : آموزشی

بسیاری از ما به عنوان یک توسعه دهنده باید بین Merge و Rebase انتخاب کنیم. با وجود تمام منابع موجود بر روی اینترنت، همه باور دارند که: «از Rebase استفاده نکنید، این ابزار می‌تواند باعث بروز مشکلات بزرگی شود.» در اینجا من توضیح خواهم داد که merge و rebase چه هستند، چرا باید (و نباید) از آن‌ها استفاده کنید و چگونه این کار را انجام دهید.

Git Merge و Git Rebase برای هدف مشابهی کار می‌کنند. این دو طراحی شده‌اند تا تغییراتی از شاخه‌های مختلف را در یک شاخه ادغام کنند. با این که هدف نهایی آن‌ها یکی است، اما این دو متد به روش‌هایی مختلفی به این هدف می‌رسند و دانستن تفاوت آن‌ها، همینطور که تبدیل به یک توسعه دهنده بهتر می‌شوید خوب است.

این سوال جامعه Git را نصف کرده است. برخی باور دارند که همیشه باید rebase کنید و بقیه باور دارند که همیشه باید merge کنید. هر سمت برخی منفعت‌های قانع کننده دارد.

Git Merge

Merge کردن یکی از شیوه‌های رایج برای استفاده از سیستم کنترل نسخه است. چه این شاخه‌ها برای آزمایش، چه بر طرف کردن باگ‌ها یا چه دلایل دیگر ساخته شده باشند، باز هم merge کردن محتویات یک شاخه منبع را می‌گیرد و آن‌ها را با یک شاخه هدف ادغام می‌کند. در این روند، فقط شاخه هدف تغییر کرده، و تاریخچه شاخه منبع به طور مشابه باقی می‌ماند.

نکات مثبت

  • ساده و آشنا
  • تاریخچه کامل و ترتیب وقوع را حفظ می‌کند.
  • زمینه یک شاخه را نگه می‌دارد.

نکات منفی

  • تاریخچه commitها می‌تواند با تعداد زیادی commitهای ادغام شده آلوده شود.
  • خطایابی با استفاده از git bisect می‌تواند سخت‌تر شود.

چگونه این کار را انجام دهیم؟

شاخه اصلی را با استفاده از دستورات checkout و merge ادغام کنید.

$ git checkout feature
$ git merge master

(یا)

$ git merge master feature

این کار یک Merge commit جدید در شاخه ویژگی‌ها خواهد ساخت که تاریخچه هر دو شاخه را نگه می‌دارد.

Git Rebase

Rebase یک راه دیگر برای ادغام تغییرات از یک شاخه به یک شاخه دیگر است. Rebase تمام تغییرات را در یک patch فشرده‌سازی کرده، و سپس patch مورد نظر را در یک شاخه هدف ادغام می‌کند.

Rebase‌ کردن بر خلاف merge کردن، تاریخچه را مسطح‌سازی می‌کند؛ زیرا این ابزار کار تمکیل شده را از یک شاخه به یک شاخه دیگر منتقل می‌نماید. در این روند، تاریخچه مذکور ناخواسته از بین می‌رود.

Rebaseها، نحوه منتقل شدن تغییرات از بالای سلسله مراتب به سمت پایین و mergeها هم نحوه جریان یافتن آن‌ها به سمت بالا هستند.

نکات مثبت

  • یک تاریخچه پیچیده را ساده‌سازی می‌کند.
  • دستکاری یک commit تنها ساده است.
  • از وجود نویز در ropeهای مشغول که شاخه‌های مشغولی دارند، جلوگیری می‌کند.
  • با تبدیل کردن commitهای حد واسط به یک commit تنها که می‌تواند برای گروه‌های توسعه دهنده کاربردی باشد، آن‌ها را مرتب می‌کند.

نکات منفی

  • له کردن یک ویژگی در قالب چند commit، می‌تواند زمینه را مخفی کند.
  • Rebase کردن مخازن عمومی می‌تواند در هنگام کار به عنوان یک تیم، خطرناک باشد.
  • کار بیشتری مورد نیاز است: استفاده از rebase برای بروز نگه داشتن شاخه ویژگی خود.
  • Rebase کردن با شاخه‌های کنترل از راه دور، نیاز دارد که شما آن را force push‌ کنید. بزرگ‌ترین مشکلی که افراد با آن مواجه می‌شوند، این است که force push کرده‌اند، اما git push default را تنظیم نکرده‌اند. این به یک سری بروزرسانی در تمام شاخه‌ها ختم می‌شود که نام مشابهی دارند، و این مسئله وحشتناک است.

اگر شما به طور ناصحیحی rebase‌ می‌کنید و به طور غیر عمدی تاریخچه را بازنویسی می‌کنید، این موضوع می‌تواند به مشکلات جدی ختم شود؛ پس مطمئن شوید که می‌دانید چه کاری انجام می‌دهید.

چگونه این کار را انجام دهیم؟

شاخه ویژگی را با استفاده از این دستورات، به یک شاخه اصلی rebase کنید:

$ git checkout feature
$ git rebase master

این کار شاخه ویژگی را به کلی به بالای شاخه اصلی منتقل می‌کند. این کار با بازنویسی تاریچه پروژه، و با ساخت commitهای جدید در شاخه اصلی انجام می‌شود.

Rebase‌ کردن تعاملی

این کار دستکاری کردن commitها را همینطور که به شاخه جدید منتقل می‌شوند، ممکن می‌سازد و از rebase‌ کردن خودکار بسیار قدرتمندتر است؛ زیرا یک کنترل کامل را بر روی تاریخچه commit شاخه ارائه می‌دهد. معمولا این کار برای تمیز کردن یک تاریخچه به هم ریخته، قبل از ادغام یک شاخه ویژگی در قالب شاخه اصلی انجام می‌شود.

$ git checkout feature
$ git rebase -i master

این کار با لیست کردن تمام commitهایی که قرار است منتقل شوند، ویرایشگر را باز می‌کند.

pick 22d6d7c Commit message#1
pick 44e8a9b Commit message#2
pick 79f1d2h Commit message#3

این کد تعریف می‌کند که شاخه‌ها پس از اجرای rebase دقیقا چه ظاهری خواهند داشت. شما با بازچینی موجودیت‌ها می‌توانید تاریخچه را به هر شکلی که می‌خواهید در آورید. برای مثال، شما می‌توانید از دستوراتی مانند fixup، squash، edit و... به جای pick استفاده کنید.

از کدام مورد استفاده کنیم؟

پس کدام یک بهتر است؟ افراد حرفه‌ای کدام را پیشنهاد می‌کنند؟

عمومی کردن و تصمیم‌گیری درباره یکی از این موارد سخت است؛ زیرا هر کدام از آن‌ها متفاوت می‌باشند. اما بالاخره باید از جایی شروع کرد.

تیم‌های کاری وقتی که شروع به تنظیم کردن سیاست‌های rebase علیه merge خود می‌نمایند، چندین سوال را باید در نظر داشته باشند. زیرا به نظر می‌رسد که یکی از این دو استراتژی کاری، بهتر از دیگری نیست. این مسئله به تیم شما بستگی دارد.

سطح rebase کرد و رقابت Git را در سازمان خود در نظر بگیرید. درجه‌ای که بر حسب آن سادگی rebase کردن در مقایسه با قابل ردیابی بودن و تاریخچه merge کردن برایتان ارزش دارد را تعیین کنید.

در آخر، تصمیم‌گیری درباره merge کردن و rebase‌ کردن باید در زمینه یک استراتژی شاخه واضح در نظر گرفته شود. یک استراتژی شاخه موفقیت‌آمیز، بر حسب سازمان‌دهی تیم‌های شما طراحی شده است.

من کدام را پیشنهاد می‌کنم؟

همینطور که تیم شما رشد می‌کند، مدیریت یا ردگیری تغییرات توسعه‌دهی با یک سیاست همیشه merge، سخت خواهد شد. برای داشتن یک تاریخچه commit واضح و قابل درک، استفاده از rebase عقلانی و موثر است.

با در نظر گرفتن این شرایط و دستور العمل‌ها، می‌توانید بهترین استفاده را از rebase ببرید:

  • شما در حال توسعه‌دهی به طور محلی هستید: اگر شما کارتان را با هیچ کس به اشتراک نگذاشته‌اید. در اینجا، شما بهتر است rebase کردن را به merge کردن ترجیح دهید، تا تاریخچه خود را مرتب نگه دارید. اگر شما مخزن شخصی خود را دارید و آن را با کسی به اشتراک نگذاشته‌اید، به راحتی می‌توانید rebase کنید.
  • کد شما برای بازبینی آماده است: شما یک درخواست pull ساخته‌اید. افراد دیگر در حال بازبینی کار شما هستند. در اینجا، خوب نیست که کار خود را rebase کنید. شما باید commitهای rework بسازید و شاخه ویژگی‌‌های خود را بروزرسانی کنید. این کار به شما در قابلیت ردگیری در درخواست‌های pull کمک می‌کند و از اختلالات اتفاقی در تاریخچه جلوگیری می‌کند.
  • بازبینی انجام شده است و آماده ادغام با شاخه هدف است. تبریک! کم مانده است که شاخه ویژگی خود را حذف کنید. با توجه به این که توسعه دهندگان از این پس دیگر در این تغییرات fetch-merge نخواهند کرد، حال شما فرصت تصفیه کردن تاریخچه خود را دارید. در اینجا، شما می‌توانید تاریخچه مذکور را بازنویسی کنید و commitهای اصلی را fold کنید. ساخت یک merge خارجی برای این commitها اختیاری است، اما ارزشش را دارد. این کار وقتی که ویژگی‌ها به شاخه اصلی بروند را ضبط می‌کند.

نتیجه گیری

امیدوارم این توضیحات بینش بهتری درباره Git merge و Git rebase به شما داده باشند. استراتژی merge علیه rebase همیشه قابل بحث است. اما شاید این مقاله در برطرف کردن شک‌های شما، و انتخاب کردن یک رویکرد خوب برای تیم خود کمک کند.

منبع

مقالات پیشنهادی

  • استفاده از GitHub Desktop با مخازن GitLab

    اگر یک برنامه‌نویس حرفه‌ای هستید که برای کنترل نسخه‌ پروژه‌های خود از git استفاده می‌کنید، قطعا نام GitHub و GitLab به گوشتان خورده‌است. git یک سیستم...

    شهریار شریعتی