روشهای پیاده سازی شاخه هایی که در این مقاله معرفی میشوند مهمترین بخش استفاده از گیت برای کنترل ورژن پروژه میباشد. ماهیت ساده و سبک شاخه ها در گیت و همچنین ترکیب ساده آنها با یکدیگر گیت را تبدیل به کاربردی ترین و بهینه ترین ابزار تولید نرم افزار کرده است.
در تمام روشهای پیاده سازی شاخه ها در گیت از فرمان های git branch، git checkout و git merge استفاده میشوند که در مقاله قبلی به بررسی آنها پرداختیم.
انواع شاخه ها در گیت
برای سازماندهی بهتر پروژه ها بهتر است که شاخه های مختلف را با توجه به کاربرد آنها نامگذاری کنیم. در این بخش به معرفی شاخه های رایج میپردازیم. توجه داشته باشید که این نامگذاری ها تنها برای درک بهتر و دوری از پیچیدگی در تاریخچه پروژه میباشند و برای گیت شاخه، شاخه است.
تمامی شاخه ها را میتوان به دو دسته کلی با نامهای شاخه های دائمی یا permanent branches و شاخه های موضوعی-موقت یا topic branches. شاخه دائمی در بردارنده تاریخچه اصلی پروژه یا master میباشد. شاخه های موقتی یا موضوعی را برای پیاده سازی یک بخش مشخص از پروژه ایجاد میکنند و در نهایت با شاخه اصلی ترکیب میشوند و یا حذف میشوند.
شاخه های دائمی
شاخه های دائمی مانند رگهای یک پروژه میباشند. در بردارنده تمامی پیچ و خم های پروژه میباشند. اکثر برنامه نویسان از شاخه master تنها برای نسخه های پایدار و تست شده استفاده میکنند. در این روش شما هیچوقت در شاخه اصلی کامیت ثبت نمی کنید و تنها از آن برای ترکیب شاخه های موقت و ایجاد نسخه های پایدار یا stable و تست شده استفاده میکنید.
همچنین بسیاری از برنامه نویسان یک شاخه فرعی با نام develop ایجاد میکنند که کلیه کد نویسی و طراحی و توسعه در این شاخه ثبت میشوند، به این ترتیب شاخه master کاملا از جریان طراحی و توسعه پروژه جدا میشود و تنها در بردارنده نسخه های پایدار و نه لزوماً نسخه کامل پروژه اما با عملکرد درست و تست شده میباشد. به طور مثال در دیاگرام پایین چند جنبه مختلف پروژه در شاخه develop پیاده سازی شده اند و تنها در یک ترکیب نهایی به شاخه master اضافه شده اند.
شاخه های موضوعی-موقت
شاخه های موقت خود به دو دسته feature branches و hotfix branches تقسیم میشوند که از اولی برای پیاده سازی جنبه ها و خصیصه های گوناگون پروژه استفاده میشود که با استفاده از آنها بخشهای دیگر پروژه که بدرستی کار میکنند در معرض خطر کد تست نشده قرار نمیگیرند. معمولاً شاخه های موقت از دیگر شاخه های موقت ریشه میگرند نه از شاخه اصلی.
شاخه های hotfix ماهیتاً مشابه شاخه های feature یا خصیصه ای میباشند اما آنها از شاخه اصلی یا master نشات میگرند. به جای توسعه پروژه در این شاخه ها به رفع خطاها و اشتباهات و دیباگ پروژه پرداخته میشود. از این شاخه ها زمانی استفاده میشود که بروزرسانی و رفع خطاهای پیدا شده در پروژه خیلی مهم هستند و نمیتوان تا انتشار نسخه بعدی رفع آنها را به تعویق انداخت.
دوباره تاکید میکنم که این نامگذاری و طبقه بندی ارائه شده صرفاً برای درک بهتر و معرفی تکنیکها و سیاست های موجود در استفاده از شاخه ها در گیت میباشد. و خود گیت فرقی بین شاخه master و develop قائل نمیشود.
تغییر ریشه یا rebasing در گیت
تغییر ریشه همانطور که از نامش پیداست به جابجا کردن ریشه یک شاخه از یک کامیت به کامیت دیگر در شاخه نشات گرفته شده گفته میشود. همانند merge در git rebase نیز باید قبل از اجرای فرمان شاخه مد نظر را checkout کنیم. به طور مثال:
git checkout some-feature
git rebase master
این فرمان ریشه کل شاخه some-feature را به کامیت ابتدایی شاخه master منتقل میکند.
همانطور که در تصویر ملاحظه میکنید بعد از تغییر ریشه عملاً شاخه some-feature شاخه master را به صورت خطی بسط داده است. به همین نتیجه میتوان از طریق ترکیب سه طرفه نیز رسید اما همانطور که در تصویر زیر ملاحظه میکنید تغییر ریشه تایخچه پروژه را پیچیده نمیکند و حتی آنرا ساده تر نیز میکند لذا این روش مورد استفاده اکثر برنامه نویسان میباشد.
همانطور که قبلا اشاره شد برای ترکیب شاخه ها با یکدیگر گیت باید یک کامیت جدید ایجاد کند که در پروژه های بزرگ و طولانی ترکیب شاخه ها میتواند باعث ایجاد کامیت های اضافی بسیار و به هم ریختگی و پیچیدگی تاریخچه پروژه شود.
معمولا شما میخواهید که کامیتهای ثبت شده معنای خاصی داشته باشند مانند تمام شدن طراحی و توسعه یک بخش خاص از پروژه. اما کامیتهای حاصل از ترکیب شاخه ها در اکثر مواقع اضافی هستند و میتوان با استفاده از تکنیک تغییر ریشه از ایجاد آنها جلوگیری کرد. صرفاً به همین خاطر اکثر برنامه نویسان برای pull کردن تغییرات ریشه اصلی به یک ریشه فرعی از git rebase استفاده میکنند.
تغییر ریشه تعاملی در گیت
با تغییر ریشه تعاملی شما در حین تغییر ریشه میتوانید کامیتهای شاخه در حال جابجایی را بررسی و تغییر دهید و سپس جابجا کنید. برای مشخص کردن تغییر ریشه تعاملی یا interactive باید از سویچ -i در فرمان git rebase استفاده کنید:
git rebase -i master
با اجرای این فرمان ویرایشگر متن را با خلاصه هر کامیت به همراه فرمانهای مشخص کننده نحوه انتقال آن کامیت به ریشه جدید، پر میکند. به طور مثال اگر شما دو کامیت داشته باشید و interactive rebase را به صورت زیر مشخص کنید:
pick 58dec2a First commit for new feature
squash 6ac8a9f Second commit for new feature
حالت پیش فرض دستور pick کامیت اول را به صورت معمولی به ریشه جدید منتقل میکند مانند دستور git rebase ساده. اما دستور squash به گیت فرمان میدهد که کامیت دوم را با کامیت قبلی ترکیب کند که در نتیجه اجرای این دو فرمان شما تنها یک کامیت پس از تغییر ریشه خواهید داشت:
گیت چندین فرمان مختلف برای تغییر ریشه تعاملی دارد که خلاصه هریک از آنها در بخش کامنتهای فهرست تنظیمات لیست میشوند. نکته اصلی که باید به یاد داشت این هست که تغییر ریشه تعاملی امکان بازنویسی تاریخچه شاخه بر اساس نیاز را فراهم میکند. این بدان معنیست که شما به عنوان برنامه نویس میتوانید هر تعداد کامیت میانی که میخواهید در شاخه های فرعی و موقتی ایجاد کنید و زمانی کد نویسی در آن شاخه تمام شده و میخواهید آن شاخه را برای الحاق به شاخه اصلی تغییر ریشه دهید میتوانید کامیتهای میانی را با توجه به معانی و نقش آنها در تاریخچه پروژه ترکیب کنید.
بازنویسی تاریخ
همانطور که ملاحظه کردید ابزار تغییر ریشه بسیار کاربردی و قدرتمند هست اما باید در استفاده از آن دقت کرد. هر دو نوع تغییر ریشه در حقیقت کامنتها را جابجا نمیکنند بلکه کامنتهای جدید در محل جدید ریشه ایجاد میکنند. اگر کامنتها را قبل و بعد از تغییر ریشه بررسی کنید متوجه میشودی که شناسه آنها عوض شده اند. این نشان میدهد که تغییر ریشه کامنتها را از بین میبرد و کامنتهای جدید در محل جدید ریشه ایجاد میکند.
همانطور که میتوان حدس زد این تغییرات در سناریوهایی که چند تیم مختلف روی یک پروژه کار میکنند میتواند دردسر ساز شود. چون ممکن است کامیتهایی که دیگران بر اساس آنها در حال توسعه پروژه هستند بعد از تغییر ریشه پاک شوند. البته روشهای مقابله و اصلاح این اشتباهات زمانی که کار با repository های عمومی را بررسی کردیم معرفی خواهند شد. اما فعلا این قاعده را همیشه در ذهن داشته باشید:
هیچوقت شاخه ای که به repository عمومی push شده است را rebase نکنید.