سرفصل‌های آموزشی
آموزش کاربردی گیت برای برنامه نویسان
Git Rebase

Git Rebase

این مستند شامل یک بحث عمیق درباره ی دستور git rebase است. همچنین، دستور Rebase در مورد تنظیم یک مخزن و بازنویسی تاریخچه ی صفحه ها، مورد بررسی قرار گرفته است. این مقاله، با جزئیات بیشتری به پیکربندی و اجرای git rebase نگاهی می اندازد. موارد استفاده مشترک از Rebase و مشکل ها، در اینجا پوشش داده خواهد شد. 

Rebase یکی از دو کاربرد Git است که در یکپارچه سازی تغییرها، از یک شاخه به شاخه ی دیگر تخصص دارد. کاربرد دیگر تغییر یکپارچه سازی، git merge است. Merge، همیشه یک رکورد تغییر که به سمت جلو حرکت می کند، است. از سوی دیگر، rebase، ویژگی های قدرتمندی برای بازنویسی تاریخچه دارد. برای بررسی دقیق Merge در مقابل Rebease، به راهنمای Merging vs Rebasing مراجعه کنید. Rebase، 2 حالت اصلی دارد: حالت "دستی" (manual) و "تعاملی" (interactive). ما در زیر حالت های مختلف Rebase را با جزئیات بیشتری بررسی خواهیم کرد. 

Git rebase چیست؟ 

Rebasing فرآیند انتقال یا ترکیب توالی Commit ها به یک Commit پایه ی جدید است. استفاده ی مجدد از محتوای گردش کار شاخه ی ویژگی (feature)، بسیار مفید است و همچنین به راحتی قابل مشاهده است. روند کلی را می توان به صورت زیر تجسم کرد:

از دید محتوا، تغییر مجدد پایه، شاخه ی شما را از یک Commit به Commit دیگر تغییر می دهد و باعث می شود این طور به نظر برسد که شاخه ی خود را از Commit دیگری ایجاد کرده اید. Git با ایجاد Commit های جدید و استفاده از آن ها در پایه ی مشخص، این کار را انجام می دهد. بسیار مهم است که درک کنیم حتی اگر شاخه، یکسان است، اما Commit ها به طور کامل جدیدند.

کاربرد Git rebase

دلیل اصلی rebasing، حفظ تاریخچه ی پروژه است. برای مثال، شرایطی را در نظر بگیرید که شاخه ی اصلی از زمانی که کار در شاخه ی "ویژگی" را شروع کرده اید پیشرفت کرده است. شما می خواهید آخرین به روزرسانی ها در شاخه ی اصلی را در شاخه ی ویژگی خود دریافت کنید، اما می خواهید تاریخچه ی شاخه خود را تمیز نگه دارید تا به نظر برسد که در حال کار کردن روی آخرین شاخه ی اصلی هستید. این کار بعدا باعث می شود که Merge تمیز شاخه ویژگی شما دوباره به شاخه اصلی برگردد. چرا ما می خواهیم "تاریخچه ی تمیز" را حفظ کنیم؟ هنگام انجام عملیات Git برای بررسی یک commit قبل، مزایای داشتن تاریخچه ی تمیز، درک می شود. سناریوی واقعی تر این است: 

  1. یک اشکال در شاخه ی اصلی شناسایی می شود. ویژگی ای که با موفقیت کار می کرد، اکنون خراب است.
  2. یک توسعه دهنده، تاریخچه ی شاخه ی اصلی را با استفاده از git log به دلیل "تمیز بودن تاریخچه" بررسی می کند و به سرعت قادر به استدلال در مورد تاریخچه ی پروژه است.
  3. توسعه دهنده، با استفاده از git log، نمی تواند تشخیص دهد که چه زمانی اشکال وارد شده است، بنابراین توسعه دهنده یک دستور git bisect را اجرا می کند.
  4. از آنجا که تاریخچه ی git تمیز است، git bisect دارای مجموعه ای از Commit ها برای مقایسه، هنگام جستجوی commit های گذشته است. توسعه دهنده به سرعت Commit ای را که باگ را ایجاد کرده، پیدا می کند و قادر است طبق آن عمل کند.

در مورد استفاده از git log و git bisect در صفحه های مربوط به آن ها، بیشتر بیاموزید. 

شما برای Merge ویژگی خود در شاخه ی اصلی دو گزینه دارید: Merge مستقیم یا rebasing و سپس Merge. گزینه ی اول منجر به Merge 3 طرفه و یک Merge Commit می شود، در حالی که گزینه ی دوم منجر به Merge سریع و یک تاریخچه که به طور کامل خطی است، می شود. نمودار زیر نشان می دهد که چگونه در شاخه ی اصلی rebase می شود و Merge سریع رو به جلو (fast-forward) را تسهیل می کند. 

Rebasing، یک روش معمول برای یکپارچه کردن تغییرهای بالا دستی، در مخزن محلی شماست. هر زمان که می خواهید ببینید پیشرفت پروژه چگونه است، با دستور Git merge، که منجر به یک Commit می شود، تغییرهای بالا دستی را Pull کنید. در حقیقت، Rebasing مانند این است که بگوییم، "من می خواهم تغییرهای خود را بر اساس آن چه همه انجام داده و Commit کرده اند، تنظیم کنم."

تاریخچه ی عمومی را Rebase نکنید.

همان طور که پیش تر در بازنویسی تاریخچه بحث کردیم، هرگز نباید مرتکب Commit مجدد یا همان rebase commit شوید، زیرا آن ها را به مخزن عمومی Push می کنید. Rebase کاری که می کند این است که Commit جدید را جایگزین Commit های قدیمی می کند و به نظر می رسد که بخشی از تاریخچه ی پروژه شما، به طور ناگهانی از بین رفته است. 

Git Rebase استاندارد در مقابل Git Rebase تعاملی


Git rebase interactive زمانی است که git rebase شناسه ی i-- را می پذیرد. این مخفف "Interactive" است. بدون هیچ شناسه ای، دستور در حالت استاندارد اجرا می شود. در هر دو حالت، فرض کنید یک شاخه ی ویژگی جداگانه ایجاد کرده ایم.  

# Create a feature branch based off of master 
git checkout -b feature_branch master 
# Edit files 
git commit -a -m "Adds new feature" 

Git rebase در حالت استاندارد به طور خودکار Commit ها را در شاخه کار فعلی شما انجام می دهد و آن ها را به سر شاخه ی منتقل شده، اعمال می کند. 

git rebase

این دستور، به طور خودکار شاخه ی فعلی را که می تواند هر نوع مرجع Commit ای باشد (برای مثال یک شناسه، یک نام شاخه، یک برچسب یا یک مرجع وابسته به HEAD)، rebase می کند.

اجرای دستور git rebase با گزینه i--، جلسه ی (session) rebase تعاملی را آغاز می کند. به جای اینکه کورکورانه همه ی Commit ها را به پایگاه جدید منتقل کنید، rebase تعاملی به شما این فرصت را می دهد تا در این روند، Commit های منحصر به فرد را تغییر دهید. با این کار می توانید با حذف، تقسیم و تغییر یک سری Commit های موجود، تاریخچه را پاک کنید. مانند دستور Git commit –amend در steroids – منظور هر یک از بخش های بزرگ که شامل زیر بخش هستند - است.

git rebase --interactive 

این دستور شاخه ی فعلی را rebase می کند اما از یک session rebase تعاملی استفاده می کند. با این کار یک ویرایش گر باز می شود که می توانید برای هر Commit ای که باید rebase شود، دستورهایی را وارد کنید (توضیح بیشتر در ادامه مطلب). این دستورها تعیین می کند که چگونه Commit منحصر به فرد، به base جدید منتقل شوند. همچنین می توانید لیست Commit را دوباره مرتب کنید تا ترتیب Commit های آن ها را تغییر دهید. هنگامی که دستورهای مربوط به هر Commit را در rebase مشخص کردید، Git، شروع به پخش Commit های اعمال شده برای دستورهای rebase می کند. دستورهای ویرایش rebase به شرح زیر است: 


pick 2231360 some old commit 
pick ee2adc2 Adds new feature 


# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands) 
# 
# Commands: 
# p, pick = use commit 
# r, reword = use commit, but edit the commit message 
# e, edit = use commit, but stop for amending 
# s, squash = use commit, but meld into previous commit 
# f, fixup = like "squash", but discard this commit's log message 
# x, exec = run command (the rest of the line) using shell 
# d, drop = remove commit 

دیگر دستورهای rebase

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

  • git rebase -- d

یعنی در هنگام playback، Commit، از بلوک Commit ترکیب شده نهایی، حذف خواهد شد.

  • git rebase – p

همان طور که هست Commit می شود. پیام یا محتوای Commit را اصلاح نمی کند و همچنان یک Commit منحصر به فرد در تاریخچه ی شاخه هاست.

  • git rebase -- x

در طول playback، Shell Script خط فرمان را روی هر Commit مشخص شده، اجرا می کند. یک مثال مفید این است که مجموعه ی تست codebase خود را روی Commit خاصی اجرا کنید، که ممکن است به شناسایی رگرسیون ها در هنگام rebase کمک کند. 

Recap (خلاصه کردن) 

Rebase تعاملی، به شما امکان کنترل کامل را روی آن چه تاریخچه ی پروژه شماست می دهد. این آزادی زیادی به توسعه دهندگان می دهد، زیرا به آن ها اجازه می دهد در حالی که روی نوشتن کد متمرکز هستند، اگر یک تاریخچه ی "نامرتب" را Commit کردند، دوباره بازگردند و آن را تمیز کنند. 

بیشتر توسعه دهندگان دوست دارند قبل از Merge شاخه ی ویژگی در پایگاه کد اصلی، از یک rebase تعاملی برای تمیز کردن، استفاده کنند. این کار به آن ها این فرصت را می دهد تا قبل از Commit به تاریخچه ی پروژه "رسمی"، Commit های ناچیز را از بین ببرند، Commit های منسوخ شده را حذف کنند و مطمئن شوند که همه چیز مرتب است. از دید دیگران، به نظر می رسد که کل ویژگی در یک سری از Commit های برنامه ریزی شده، ایجاد شده است. 

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

گزینه های پیکربندی 

چند ویژگی برای rebase وجود دارد که می توان با استفاده از git config، تنظیم کرد. این گزینه ها باعث تغییر شکل و ظاهر خروجی git rebase می شوند. 

  • rebase.stat: یک boolean که به طور پیش فرض روی false تنظیم شده است. این گزینه ضامن نمایش محتوای دیداریست که نشان می دهد چه تغییرهایی از آخرین rebase رخ داده است.
  • rebase.autoSquash: یک مقدار boolean که رفتار autosquash-- را تغییر می دهد.
  • rebase.missingCommissionCheck: می تواند روی چندین مقدار تنظیم شود که رفتار rebase را در مورد Commit های از دست رفته، تغییر دهد.
warnخروجی هشدار را در حالت تعاملی چاپ می کند که نسبت به Commit های حذف شده هشدار می دهد. 
errorRebase را متوقف می کند و پیام های هشدار دهنده ی Commit را چاپ می کند. 
ignoreبه صورت پیش فرض تنظیم شده، هرگونه هشدار Commit را نادیده می گیرد. 
  • rebase.instructionFormat: یک رشته ای با قالب git log که برای قالب بندی نمایش گر rebase تعاملی استفاده می شود.

برنامه rebase پیشرفته 

آرگومان خط فرمان onto-- می تواند با git rebase استفاده شود. هنگامی که در git rebase در حالت onto-- قرار دارید، دستور به صورت زیر خواهد بود: 

git rebase --onto

دستور onto-- فرم یا rebase قدرتمندتری را امکان پذیر می سازد که به شما اجازه می دهد از منبع های خاص عبور کرده و بالای یک rebase قرار بگرد.

در این جا ما یک نمونه ی مخزن با شاخه هایی مانند زیر داریم: 

 o---o---o---o---o master
         \          o---o---o---o---o featureA
               \                o---o---o featureB 

featureB بر اساس featureA است. با این حال، ما متوجه می شویم که این ویژگی به هیچ یک از تغییرها در featureA وابسته نیست و فقط می تواند از شاخه ی اصلی ایجاد شود. 

git rebase --onto master featureA featureB 

featureA به .master تبدیل می شود و featureB مرجعی است که HEAD به آن اشاره می کند. نتایج به دست آمده عبارتند از: 

	  	   o---o---o feature
                     	  /
    	 o---o---o---o---o master      
	  \       
	   o---o---o---o---o featureA 

درک خطرهای rebase

نکته ی قابل توجه در هنگام کار با Git Rebase این است که اختلاف های Merge ممکن است در طی گردش کار rebase بیشتر شود. زمانی این اتفاق می افتد که شاخه ای با عمر طولانی داشته باشید که از Master فاصله گرفته باشد. سرانجام شما می خواهید دوباره در Master، rebase کنید و در آن زمان ممکن است Commit های جدیدی باشد که تغییرهای شاخه ی شما، ممکن است با آن ها در تضاد باشند. با انجام rebase مجدد شاخه خودتان به Master، و تکرار Commit ها، این کار به راحتی قابل اصلاح است. شناسه های خط فرمان continue-- و abort-- را می توان با git rebase استفاده کرد تا در زمان مواجه شدن با اختلاف ها، بتوان آن را پیش برد یا reset کرد.

یک هشدار جدی تر rebase تاریخچه ی تعاملی، برای Commit های از دست رفته است. اجرای rebase در حالت تعاملی و اجرای زیر دستور ها مانند squash یا drop، Commit ها را از شاخه ی لاگ سریع، حذف می کند. در نگاه اول به نظر می رسد که این Commit ها، برای همیشه از بین رفته اند. با استفاده از git reflog می توانید این Commit ها را بازیابی کرده و کل rebase را لغو کنید. برای اطلاعات بیشتر در مورد استفاده از git reflog، برای یافتن Commit های گمشده، به صفحه مستند Git reflog مراجعه کنید. 

Git Rebase به خودی خود خطرناک نیست. موارد خطر واقعی، هنگام اجرای بازنویسی تاریخچه با rebase تعاملی و Push نتایج به شاخه ای از راه دور که سایر کاربرها آن را به اشتراک گذاشته اند، به وجود می آیند. این الگویی است که باید از آن پرهیز شود زیرا این امکان وجود دارد که کارهای دیگر کاربران از راه دور را، هنگام Pull، دوباره بازنویسی کند. 

بازیابی از rebase بالا دستی 

اگر کاربر دیگری به شاخه ای که شما در حال Commit هستید، rebase و push کند، یک git pull، Commit هایی را که بر اساس آن، شاخه ی  قبلی را بنا کرده اید، بازنویسی می کند. خوشبختانه، با استفاده از git reflog می توانید دوباره از شاخه ی  از راه دور استفاده کنید. در reflog شاخه ی  از راه دور، می توانید قبل از آن، یک مرجع پیدا کنید. سپس می توانید شاخه ی  خود را با استفاده از گزینه ی --onto، همان طور که در بالا در بخش "برنامه rebase پیشرفته" گفته شد، در مقابل آن، مرجع از راه دور قرار دهید. 

خلاصه

در این مقاله ما به استفاده از git rebase پرداختیم و در مورد استفاده از مباحث پایه و پیشرفته و نمونه های پیشرفته تر، بحث کردیم. برخی از نکته های اصلی بحث عبارتند از: 

  • git rebase استاندارد و حالت های تعاملی
  • گزینه های پیکربندی git rebase
  • git rebase –onto
  • git rebase کامیت های از دست رفته

همچنین با استفاده از ابزارهای دیگری مانند git reflog ، git fetch و git push، git rebase را بررسی کردیم. برای کسب اطلاعات بیشتر به صفحات مربوط به آن ها مراجعه کنید. 

منبع 

  1. https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase
online-support-icon