گیت Rebase به زبان ساده

گیت ریبیس
گیت ریبیس


در طی سال های اخیر بعد از درک این‌که WorkFlow یا جریان کاری در سورس کنترل چقدر در کار تیمی اهمیت داره، به تمام اطرافیانم توصیه میکردم و میکنم که از فلو مناسب برای گیت استفاده کنند. ورک فلو یک قرارداده بین افراد تیم برای یکسان بودن سیاست برنچینگ.

برنچینگ در نهایت برای ترکیب شدن کارها به git merge یا git rebase منتهی میشه.

اخیرا شاهد بحثی در شبکه های اجتماعی بودم با این مضمون که خیلی از توسعه دهنده های اطرافمون به درستی از مفهوم git rebase استفاده نمی‌کنند. شاید به دلیل اینکه مفهوم اون رو درست متوجه نشدند و یا نیاز اون رو حس نکردند. حتی خودم چون در فلویی که استفاده میکنم rebase وجود نداره خیلی از این قابلیت مفید و کاربردی استفاده نمیکردم. با این حال این متن کوتاه رو نوشتم تا در حد سوادم بتونم این مفهوم رو در ساده ترین حالت تشریح کنم.

برای بهتر نوشتن از این مفهوم از چند مقاله الهام گرفته شده است که بهتر و ساده تر از من این مساله رو مطرح کردند.

از اینجا و اینجا میتونین اصلشون رو ببینین

توجه کنید که برای خوندن این مقاله لازمه با گیت آشنا باشید و مفهوم برنچ ها رو درک کنید.

گیت مرج و ریبیس یک هدف مشترک دارن که ترکیب چند Branch توسعه است. اگرچه هدف نهایی مشترکه، مرج و ریبیس مسیر های مختلفی رو برای رسیدن به این هدف طی میکنن، خروجی مشابه البته با ردپایی متفاوت.

در ادامه از یک مخزن (repository) نمونه با دو برنچ جدا از هم master و feature استفاده خواهیم کرد و به جای هش کامیت ها از عدد برای درک بهتر استفاده میکنیم، علاوه بر اون ترتیب شماره ها ترتیب زمانی کامیت ها هم محسوب میشه به این صورت که عدد کوچکتر به معنی زودتر کامیت شدنه.

     +--3--5   master
      |
1--2--+
      |
      +--4--6   feature

گیت مرج git merge

هر مرج یک کامیت جدید است. این اصلی ترین چیزی است که باید به خاطر داشت. همزمان دلیل علاقه خیلی از توسعه دهنده ها کامیت بودن merge، دلیل نفرت خیلی از توسعه دهنده ها [ی گاها وسواسی :| ] دیگه است. در هر حال هر مرج یک کامیت است با یک تفاوت که هر کامیت مرج دو parent دارد. همه کامیت های معمول تنها یک parent دارند. چطوری بررسی کنیم؟ ترمینال رو آتیش کنین [وفاداری به متن اصلی :)) ] و git status بگیرین. دو خط اول رو برای مرج میبینید؟

commit 7777777777777777777777777777777777777777
Merge: 5555555 6666666
Author: Nima <billakh@spammer.bot>
Date:   Thu Nov 3 10:11:27 2071 +0100

    Merge branch 'feature' into master.

     +--3--5--+
          |        |
1--2--+        +--7
     |        |
          +--4--6--+


خب یک مرج انجام شده و اگه شما git log روی برنچmaster بگیرین، یک خروجی خطی به این ترتیب میگیرین:

7 6 5 4 3 2 1

به ترتیب تاریخ. البته باید گفت که این فقط ظاهر قضیه است. زیر کار(!) تاریخچه این کامیتها خطی نیست. یعنی اگه شما به کامیت 5 چک آوت کنید و لاگ بگیرید هیستوری ای که میگیرید به این ترتیب هست :

5 3 2 1

کامیت 4 یک child از 5 یا parent کامیت 3 نیست. پس تو خروجی نمیبینیمش.

fast-forward merge

تنها حالتی که مرج کردن باعث ساختن کامیت جدید نمیشه، مرج fast-forward هست.

وقتی این حالت اتفاق میفته که کامیت دیگه ای تو برنچ‌های دیگه وجود نداشته باشه.

Before

1--2--+         master
     |
      +--3--4   feature

After

1--2--3--4   master/feature

گیت ریبیس git rebase

ریبیس ایجاد مجدد کاریه که شما رو یه برنچ انجام دادید رو برنچی که میخواید باشن. یعنی به ازای کامیت هایی از شما که در برنچ feature که تو master وجود ندارن کامیت جدید بالای کار ایجاد میشه. یک بار دیگه به آرومی بخونین: کامیت جدید به ازای هر کامیت قدیمی با همون تغییرات.

وقتی شما به برنچ feature چک اوت کنین و به مستر ریبیس کنین این چیزیه که اتفاق میفته

     +--3--5   master
      |
1--2--+
      |
      +--3--5--7--8   feature
              (4)(6)

ریبیس کامیت های ۴ و ۶ رو پاک میکنه، با گرفتن تغییرات ۳ و ۵ با master سینک میشه و (اون جمله که آروم خوندین رو یادتون بیاد) کامیت جدید به ازای هر کامیت قدیمی با همون تغییرات ایجاد میشه.

تو مثال بالا کامیت ۷ با همون تغییرات کامیت ۴ و کامیت ۸ با همون تغییرات کامیت ۶ اضافه میشن. بدین ترتیب برنچ feature همه تغییرات master رو داره به علاوه تغییرات خودش.

حالا بعد از یک fast-forward merge بی دردسر شما همچین چیزی خواهید داشت:

1--2--3--5--7--8   master/feature

حالا git log به شما این خروجی رو میده :

8 7 5 3 2 1

با این تفاوت که تاریخچه کامیت ها هم به همین ترتیب خطیه و با چک اوت کردن به عقب چیزی که نباید گم شه گم نمیشه.

درنهایت کجا merge کنیم کجا rebase ؟ همونطوری که میشه حدس زد و ابتدای مقاله اشاره شد به ورک فلو توافقی تیم بستگی داره. اما به این نکته ها رو توجه کنید:

ریبیس کنید وقتی:

  • میخواید تغییرات لوکال خودتون رو مرج کنید و نیازی به تاریخچه دقیق ندارید پس چرا باید با کامیت های اضافه merge کثیفش کنید؟
  • هیستوری خطی براتون مهمه و از git bisect زیاد استفاده میکنین
  • دارین رو یه فیچری کار میکنین که خیلی طول کشیده و تیمتون تو برنچ های دیگه کیلومترها ازتون جلوترن و کار شما بعد مرج چون کامیت های قدیمی تری هستن ممکنه بالای هیستوری نباشن و شما یا هم تیمی هاتون وقتی pull میکنن گمشون کنین. در صورتی که حالت معقولش برای همه اینه که چون تغییرات جدیدی هستن باید بالاتر باشن که کسی سورپرایز نشه.

مرج کنید وقتی:

  • شما یه قسمت از تغییراتتون رو با بقیه به اشتراک گذاشتید و مهمه که ریپو های اون ها رو نترکونید. ریبیس کلی از هیستوری رو تغییر میده و مرج ساده امن تر و واسه بقیه قابل فهم تره.
  • شما واقعا به تاریخچه دقیق اهمیت میدین و میخواین که هیستوری مرج ها و کامیت ها دقیقا به همون ترتیب و جزئیات وجود داشته باشه.

پس چرا مردم از ریبیس کردن میترسن؟

بر اساس تجربه شخصی و مشاهده کامیونیتی، نگرانی توسعه دهنده ها نسبت به ریبیس کردن از اینجا ها ناشی میشه:

به خاطر مکانیزم git rebase عموما کانفلیکت های بیشتر و سخت الرفع (!)تری تجربه میشه

تجربه قبلی از دست دادن کار به خاطر بی دقتی تو interactive rebase ویا force push

چه کنیم نترسیم ؟ (نصایح الربایس)

  • زود به زود کامیت و ریبیس کنین تا برنچتون خیلی عقب نیفته
  • اگه وسطش به کانفلیکت خوردین فیکس کنین و git rebase --continue کنین ریبیس بدون تغییر مسیر ادامه پیدا میکنه. البته اینجا مرج کامیت به وجود میاد که چیز لازمیه چون حل کردن کانفلیکت جزو تاریخچه است
  • اگه کانفلیکت انقدر فجیعه که از ریبیس کردن پشیمون شدین میتونین

git rebase --abort

کنین و شما رو برگردونه اول جایی که ریبیس کردین

  • پول کردن با ریبیس هم ممکنه

git pull --rebase

  • با ریبیس کردن شما میتونین کامیت هارو پاک کنین. چندتا کامیت رو یکی کنین و مسیج های کامیت ها رو تغییر بدین این کار بهتون قدرت تغییر گذشته رو میده و یادتون نره هر تغییری در گذشته ممکنه باعث ایجاد پارادوکس بشه. هیچ وقت پدربزرگتون رو قبل از بدنیا اومدن پدرتون نکشین!
  • با تاریخچه بقیه هم ور نرین
  • اگه فورس پوش میکنین هرگز هیچوقت فورس پوش تو ریموتی که بقیه دارن روش کار میکنن نکنین مگه اینکه اولا دقیقا بدونین دارین چیکار میکنین و دوما از تک تکشون رضایت نامه کتبی گرفته باشید :|

در نهایت همونطور که اول مقاله اشاره کردم معمولا ریبیس تو فلوی کاری من جایی نداشته و حتی سعی میکنم ازش استفاده نکنم ولی اونقدر خودم رو صاحب نظر نمیدونم که برای کسی تصمیم بگیرم.