آموزش گیت – قسمت ششم

نویسنده : سید ایوب کوکبی ۲۱ اردیبهشت ۱۳۹۸

در این درس، نگاه عمیق‌تری خواهیم داشت به تاریخچۀ کامیت‌ها. در گزارش پایین شش کامیت ستاره‌دار وجود دارد که به نوعی با هم مرتبطند. (ستاره‌ها برای توضیح بهتر هستند، در git log وجود ندارد). حتی شاخۀ مجزایی برای توسعۀ این ویژگی‌ها ساخته‌ایم اما همانطور که می‌بینید به شکل نامرتبی بین کامیت‌های سایر شاخه‌ها قرار گرفته‌اند. به عبارتی تاریخچۀ پروژه کمی شلخته و درهم ریخته شده است.

ec1b8cb Merge branch 'crazy'
*۴۲fa173 Add news item for rainbow
۳db88e1 Add 1st news item
*۷۱۴۷cc5 Link index.html to rainbow.html
*۶aa4b3b Add CSS stylesheet to rainbow.html
b9ae1bc Merge branch 'master' into crazy
ae4e756 Link HTML pages to stylesheet
۹۸cd46d Add CSS stylesheet
*۳۳e25c9 Rename crazy.html to rainbow.html
*۶۷۷e0e0 Add a rainbow to crazy.html
۵۰۶bb9b Revert "Add a crazzzy experiment"
*۵۱۴fbe7 Add a crazzzy experiment
۱c310d2 Add navigation links
۵۴۶۵۰a3 Create blue and orange pages
b650e4b Create index page

خوشبختانه گیت ابزاری دارد که به کمک آن می‌توان کامیت‌ها را تمییز کرد: git rebase. این دستور اجازه می‌دهد شاخه‌ها را بر اساس کامیت‌هایی که مبتنی بر آن هستند (based on) جابه‌جا کنید. در واقع کاری مثل تصویر زیر انجام می‌دهد:

عمل Rebase در گیت
rebase کردن شاخۀ فیچر روی شاخۀ مستر

بعد از Rebase، شاخۀ feature یک parent commit جدید دارد؛ یعنی همان کامیتی که شاخل مستر به آن اشاره می‌کند. rebase به جای اتصال شاخه‌ها با دستور git merge شاخۀ feature را بر فراز شاخۀ مستر قرار می‌دهد. نتیجۀ کار یک تاریخچۀ خطی عالی بوده که بیشتر شبیه داستان است تا مجموعه‌ای از ویرایش‌های درهم و برهم.

ساخت صفحۀ دربارۀ ما

کار را با ساخت صفحۀ about یا دربارۀ ما آغاز می‌کنیم. به خاطر دارید که تغییرات را باید در محیطی ایزوله پیاده‌سازی می‌کردیم تا به صورت تصادفی نسخۀ پایدار پروژه که در شاخۀ master قرار گرفته را با مشکل مواجه نکنیم. پس ابتدا یک شاخۀ جدید می‌سازیم:

git branch about
git checkout about

برای دیدن اثر rebase در قدم‌های بعدی چند کامیت الکی اضافه می‌کنیم. ابتدا یک فولدر خالی به نام about در دایرکتوری پروژه ایجاد کنید. سپس یک فایل index.html داخل آن بسازید. تغییرات را استیج و کامیت کنید. توجه کنید که با دستور git add کل یک فولدر را هم می‌شود استیج کرد:

git add about
git status
git commit -m "Add empty page in about section"

اضافه کردن صفحۀ دربارۀ ما

فایل about/index.html را باز کرده و محتویات زیر را به آن اضافه کنید:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>About Us</title>
<link rel="stylesheet" href="../style.css" />
  <meta charset="utf-8" />
</head>
<body>
  <h1>About Us</h1>
  <p>We're a small, colorful website with just two employees:</p>

  <ul>
    <li><a href="me.html">Me: The Developer</a></li>
    <li><a href="mary.html">Mary: The Graphic Designer</a></li>
  </ul>
    
  <p><a href="../index.html">Return to home page</a></p>
</body>
</html>

تغییرات را استیج و کامیت کنید:

git status
git commit -a -m "Add contents to about page"

اکنون تاریخچۀ پروژه چنین حالتی دارد:

تاریخچۀ پروژه بعد از افزودن شاخۀ about

یک بروزرسانی اورژانسی!

کارفرما دوباره یک خبر آورده تا سریعاً در بخش اخبار سایت (news) قرار دهیم. طبق روال، تغییرات را در یک شاخۀ hotfix اعمال می‌کنیم و پس از تست با شاخۀ مستر ادغام می‌کنیم:

git checkout master
git branch news-hotfix
git checkout news-hotfix
git branch

بخش news در فایل index.html را به صورت زیر تغییر دهید:

<h2 style="color: #C00">News</h2>
<ul>
  <li><a href="news-1.html">Blue Is The New Hue</a></li>
  <li><a href="rainbow.html">Our New Rainbow</a></li>
  <li><a href="news-2.html">A Red Rebellion</a></li>
</ul>

اسنپ‌شات فعلی را کامیت کنید:

git status
git commit -a -m "Add 2nd news item to index page"

حالا یک صفحۀ جدید به نام news-2.html بسازید:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>A Red Rebellion</title>
  <link rel="stylesheet" href="style.css" />
<meta charset="utf-8" />
</head>
<body>
  <h1 style="color: #C03">A Red Rebellion</h1>
    
  <p>Earlier today, several American design firms
  announced that they have completely rejected the use
  of blue in any commercial ventures. They have
  opted instead for <span style="color: #C03">Red</span>.</p>
    
  <p><a href="index.html">Return to home page</a></p>
</body>
</html>

تغییرات استیج و کامیت کنید:

git add news-2.html
git status
git commit -m "Add article for 2nd news item"

انتشار هات‌فیکس جدید

اکنون آمادۀ ادغام هات‌فیکس و انتشار آن هستیم:

git checkout master
git merge news-hotfix
git branch -d news-hotfix

بعد از ادغام، گیت با یک fast-forward شاخۀ مستر را مطابق تصویر بروزرسانی می‌کند:

fast-forwarding شاخۀ مستر به شاخۀ news-hotfix

Rebase کردن شاخۀ About

اکنون اگر از دستور merge استفاده کنیم به ناچار ادغام سه طرفه انجام می‌شود. هدف ما این است که تغییرات جدید شاخۀ مستر که در یک هاتفیکس به آن اعمال شده به فیچر برنچ ما یعنی about وارد شود. اما این بار به جای merge از دستور rebase استفاده می‌کنیم:

git checkout about
git rebase master
git log --oneline

همانطور که در تصویر می‌بینید شاخۀ about مبتنی بر Merge branch یعنی کامیت crazy-experiment است. دستور rebase، کل شاخۀ about را گرفته و آن را به نوک شاخۀ مستر می‌برد که در تصویر پایین به خوبی شاهد آن هستید. این دستور نیز مثل git merge باید ابتدا به شاخه‌ای که می‌خواهید سوئیچ شود.

ریبیس کردن شاخۀ about به شاخل مستر

بعد از rebase، شاخۀ about یک گسترش خطی از شاخۀ مستر خواهد بود که به ما اجازه می‌دهد تا بعدها از ادغام fast-forward استفاده کنیم. Rebase همچنین امکان ادغام نسخۀ بروز مستر را بدون merge کردن کامیت در اختیار ما قرار می‌دهد.

بخوانید  آموزش گیت - قسمت دوم

اضافه کردن صفحۀ بیوگرافی

می‌خواهیم یک صفحۀ about/me.html اضافه کنیم. این فایل را با محتوای زیر بسازید:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>About Me</title>
  <link rel="stylesheet" href="../style.css" />
  <meta charset="utf-8" />
</head>
<body>
  <h1>About Me</h1>
  <p>I'm a big nerd.</p>

  <h2>Interests</h2>
  <ul>
    <li>Computers</li>
    <li>Mathematics</li>
    <li>Typography</li>
  </ul>

  <p><a href="index.html">Return to about page</a></p>
</body>
</html>

سپس تغییرات را در مخزن کامیت کنید:

git add about/me.html
git commit -m "Add HTML page for personal bio"
git log --oneline

به لطف دستور rebase، شاخۀ about در ادامۀ شاخۀ مستر قرار گرفته و کامیت‌های جدید به صورت خطی ادامه پیدا می‌کنند. در واقع همۀ کامیت‌های مربوط به about با یکدگیر در یک گروه قرار می‌گیرند. این کار از ساخته شدن فورک‌های غیرضروری در تاریخچۀ پروژه جلوگیری می‌کند.

اضافه کردن یک صفحۀ ساختگی

دو اسنپ‌شات بعدی ساختگی هستند. بعداً با استفاده از دستور rebase این دو را در یک کامیت ادغام می‌کنیم. این نشان می‌دهد که دستور rebase فقط کار جابه‌جایی شاخه‌ها را انجام نمی‌دهد. دستور فوق امکان دستکاری کامت‌ها را هم به ما می‌دهد. یک فایل خالی ایجاد کنید، about/mary.html:

git add about
git status
git commit -m "Add empty HTML page for Mary's bio"

لینک کردن به صفحۀ about

در فایل ایندکس (منظورمان index.html که در ریشۀ فولدر قرار دارد) به صفحۀ about لینک کنید:

<h2>Navigation</h2>
<ul>
  <li>
    <a href="about/index.html">About Us</a>
  </li>
  <li style="color: #F90">
    <a href="orange.html">The Orange Page</a>
  </li>
  <li style="color: #00F">
    <a href="blue.html">The Blue Page</a>
  </li>
  <li>
    <a href="rainbow.html">The Rainbow Page</a>
  </li>
</ul>

کامیت کنید:

git commit -a -m "Add link to about section in home page"

پاکسازی تاریخچۀ کامیت‌ها

قبل از ادغام تغییرات در شاخۀ مستر باید اطمینان یابیم که شاخۀ about تاریخچۀ مرتب و تمییزی داشته باشد. با interactive rebase می‌توانیم کامیت‌هایی که به base جدید منتقل می‌شوند را انتخاب کنیم. برای این کار پرچم i- را در دستور rebase وارد کنید:

git rebase -i master

دستور فوق پنجرۀ تکست ادیتور را به همراه تمامی کامیت‌های شاخۀ about از قدیم به جدید نمایش می‌دهد (برخلاف دستور log که از جدید به قدیم است). این لیست در واقع نشان می‌دهد که عمل تغییر base به چه ترتیبی صورت می‌گیرد و اجازۀ انجام یک سری دستورالعمل را نیز به شما خواهد داد. به همین خاطر به آن interactive rebase گفته می‌شود یعنی با کاربر تعامل دارد. اگر بعد از اجرای دستور، پنجره را به حال خود رها کنید و هیچ عملی انجام ندهید git rebase به حالت معمولی اجرا می‌شود.

بعد از اجرای دستور می‌توانید با دستورات pick و squash تغییرات لازم را در کامیت اعمال کنید تا در فرایند Rebase بر اساس آن عمل شود. مثلاً اگر دستور squash را پیش از کامیتی قرار دهید آن کامیت را با کامیت قبلی ادغام می‌کند. ما در اینجا کامیت‌هایی که فایل‌های خالی ایجاد کرده بودیم را با یکدیگر ادغام کرده‌ایم. یعنی بعد از اجرای Rebase، سه کامیت خواهیم داشت.

pick 5cf316e Add empty page in about section
squash 964e013 Add contents to about page
pick 89db9ab Add HTML page for personal bio
squash 2bda8e5 Add empty HTML page for Mary's bio
pick 915466f Add link to about section in home page

توجه: ویرایشگر گیت Vim است. این یعنی هر جایی ادیتور باز شد، درون ادیتور Vim هستید و باید طبق اصول آن کار کنید. ویم یک ویراستار محبوب کدنویسی است که اجازه می‌دهد همۀ کارها را با صفحه‌کلید انجام دهید؛ از تایپ متن گرفته تا اجرای دستورات. در این مطلب فرصت کافی برای توضیح ویم نیست. فقط در همین حد بدانید که ویم برخلاف ایدیتورهای دیگر برای ورود اطلاعات و اجرای دستورات دو حالت مجزا دارد: normal mode و Insert Mode. حالت نرمال، حالت پیش‌فرض ویم است که با زدن دکمه‌های صفحه‌کلید می‌توانید دستورات لازم را اجرا کنید (مثلاً جستجو در متن، حذف کلمات، جایگزین کردن کاراکترها، undo و …) و حالت Insert برای ورود متن به کار می‌رود. برای وارد شدن به حالت نوشتار کافی است کلید i (مخفف insert) را بزنید. و برای حالت نرمال کلید esc.

بخوانید  آموزش گیت - قسمت هشتم

دستورات زیادی در حالت نرمال قابل اجرا هستند. مثلاً با زدن کلید w به کلمۀ بعدی متن می‌روید یا کلید / برای جستجو در متن به کار می‌رود. کلید u برای undo کردن و … . بعد از اجرای دستور git rebase -i master صفحه‌ای باز می‌شود که قبل از همه کامیت‌ها، کلمۀ pick قرار گرفته است. حالا برای تایپ squash ابتدا esc بزنید. سپس i تا وارد حالت نوشتار شوید. (پایین ادیتور باید عبارت Insert نمایش داده شود). اکنون مطابق دستورات بالا هر جایی squash نوشته‌ایم شما هم بنویسید.

بعد اتمام کار دکمۀ ESC را بزنید تا وارد مد دستور شوید و سپس تایپ کنید wq: کاراکتر w یعنی save و q یعنی خروج. بعد از این کار rebase با توجه به تغییراتی که داده‌اید عملیاتش را آغاز می‌کند. گیت هنگام رسیدن به دستورات squash مجدداً ادیتور ویم را باز می‌کند و از شما درخواست می‌کند نامی برای اسنپ‌شات جدید که حاصل ترکیب دو کامیت است انتخاب کنید. بعد از انجام کار ESC را بزنید و سپس SHIFT+Z را دوبار فشار دهید تا بعد از ذخیره به گیت برگردید؛ این یک راه میانبر برای ذخیره و خروج فایل است.

مراحل اجرا:

  1. گیت، کامیت ۵cf316e را به نوک master می‌برد؛
  2. سپس اسنپ‌شات‌های ۹۶۴e013 و fcf316e را با هم ادغام می‌کند؛
  3. حالا گیت پنجره‌ای باز می‌کند تا برای اسنپ‌شات‌های ترکیب شده، نامی انتخاب کنید؛
  4. گیت فرایند فوق را برای کامیت‌های دیگر نیز اجرا می‌کند؛
  5. در انتها کامیت آخر را یعنی ۹۱۵۴۶۶f در بالای دو کامیت قبلی قرار می‌دهد. اینجا کار دستور rebase تمام شده واگر مشکلی وجود نداشته باشد در خروجی پیام موفقیت ظاهر می‌شود.

git log –oneline تغییرات نهایی تاریخچه را به شما نمایش می‌دهد. تصویر پایین حاصل عملیات را به خوبی نشان می‌دهد. دقت کنید اکنون شاخۀ about به جای ۵ کامیت سه کامیت دارد و حتی ID کامیت‌های جدید نیز تغییر کرده است.

نتیجۀ interactive rebase

با دستور interactive rebase کنترل کاملی روی تاریخچۀ پروژه خواهید داشت. البته این دستور به همان میزان که مفید است، خطرناک هم هست. برای مثال در ادغام کامیت‌ها اگر مرتکب اشتباهی شوید، تغییرات به حالت قبل باز نمی‌گردد. در درس بعدی با بازنویسی تاریخچه برای حل مشکلات رایج مخازن گیت آشنا می‌شوید.

توقف Rebase برای اصلاح یک کامیت

توقف ایجاد شده در Rebase قبلی فقط برای ویرایش پیغام کامیت بود. ما می‌توانیم حتی محتویات فایل را هم وسط عملیات Rebasing تغییر دهیم. برای این کار ابتدا دستور زیر را وارد کنید:

git rebase -i master

قبل از فایلی که می‌خواهید محتویاتش را تغییر دهید دستورالعمل edit بگذارید. سپس ذخیره و خارج شوید (Esc و سپس تایپ wq: یا Esc و سپس دوبار SHIT+Z)

pick 58dec2a Create the about page
edit 6ac8a9f Begin creating bio pages
pick 51c958c Add link to about section in home page

وقتی گیت به کامیت دوم می‌رسد کارش را متوقف می‌کند تا عمل اصلاح یا amending را انجام دهید. این کار اجازه می‌دهد تا پیش ازکامیت کردن اسنپ‌شات استیج شده آن را اصلاح کنید.

توقف عملیات rebasing برای اصلاح فایل

فایل about/mary.html را باز کنید و خط زیر را وارد نمایید:

[Mary, please update your bio!]

ما اکنون وسط کار rebase هستیم ولی به راحتی می‌توانیم تغییرات مورد نظر را در فایل mary.html بدهیم. بعد از انجام کار دستورات زیر را وارد کنید:

git add about/mary.html
git status
git commit --amend

پرچم amend– در دستور commit به گیت می‌گوید که به جای ساخت یک کامیت جدید، همین کامیت را با اسنپ‌شات استیج شده جایگزین کند.

بخوانید  چند کلام حرف حساب با برنامه‌نویسان تازه‌وارد

ادامۀ عملیات Rebase

به خاطر دارید که در میانۀ rebase از آن خارج شدیم. بعد از اعمال تغییرات در فایل mary.html که به حالت edit در آورده بودیم اکنون می‌توانیم با دستور زیر ادامۀ Rebase را اجرا کنیم:

git rebase --continue
git log --oneline

همانطور که دستور log نشان می‌دهد، تاریخچۀ کامیت‌ها هنوز مثل گذشته است چون پیغام کامیت را تغییری نداده‌ایم ولی محتویات mary.html با گذشته فرق داشته و یک ID جدید هم دارد. بنا به هر دلیلی در اثنای عملیات بودید و از ادامۀ کار منصرف شدید می‌توانید rebase –abort کنید تا همه چیز از اول شروع شود.

انتشار تغییرات بخش About

هدف ما از دستور Interactive Rebase پاکسازی و تمییز کردن تاریخچه برای ادغام با شاخۀ مستر بود که به این هدف رسیدیم و از آنجایی که قبلاً شاخۀ about را به شاخۀ مستر Rebase کرده‌ایم، عملی ادغام به فرم fast-forward صورت می‌گیرد.

git checkout master
git log --oneline
git merge about
git log --oneline

بعد از ادغام، حذف شاخۀ غیر ضروری about را فرمواش نکنید.

git branch -d about

تاریخچۀ نهایی به شکل زیر خواهد بود. همانطور که می‌بینید، درک تاریخچۀ خطی از تاریخچۀ غیر خطی -محصول ادغام‌ سه طرفه – آسان‌تر است. البته بدی‌اش این است که نمی‌دانیم از کجا و چگونه به اینجایی که هستیم رسیده‌ایم.

ادغام و حذف شاخۀ about

جمع‌بندی

عمل Rebase با جابه‌جا کردن یک شاخه به نوک شاخۀ دیگر، نوعی ادغام fast-forward انجام می‌دهد. این دستور به شکل موثری کار دستور merge را انجام می‌دهد با این مزیت که تاریخچه همیشه به صورت خطی حفظ می‌شود. از نگاه یک ناظر خارجی، پیشرفت پروژه کاملاً خطی و برنامه‌ریزی شده دیده می‌شود بدون اینکه از فیچر برنچ‌ها و هات‌فیکس برنچ‌هایی که به صورت موازی توسعه داده شده‌اند اطلاعی داشته باشد. Rebase به شما این توانایی را می‌دهد تا دقیقاً چیزهایی که می‌خواهید در مخزن قرار بگیرد.

این دستور تاحدودی میان اهالی گیت جای بحث و مناقشه دارد. برخی افراد منافع rebase را که به تعدادی از آن‌ها در این درس پرداختیم به اندازه‌ای نمی‌دانند که ارزش بازنویسی تاریخچه را داشته باشد. این دسته از افراد معتقدند که تاریخچه باید دقیقاً روند رشد پروژه و بلاهای که سرش آمده را منعکس کند تا به صورت ناخواسته اطلاعاتی از دست نرود. بعلاوه با پیکربندی دستور log می‌توان از تاریخچه‌‌ای که شاخه‌های زیادی در آن هست خروجی خطی گرفت.

اما دستۀ دیگری از افراد معتقدند که ادغام کامیت‌ها باید معنادار باشد. ادغام کامیت‌ها باید سمبلیک باشد و نباید با mergeهای فراوان درک تاریخچه را دشوار کرد. این موضوع را می‌توان در پروژه‌های بزرگ مثل کرنل لینوکس مشاهده کرد. در این پروژه‌ها، تاریخچه کاملا خطی است تا به راحتی بتوان روند توسعۀ پروژه را درک نمود. بنابراین می‌توان نتیجه گرفت که استفاده از دستور rebase کاملاً به سلیقۀ شخصی شما برمی‌گردد. در درس بعدی کمی بیشتر با تاریخچۀ پروژه درگیر خواهیم شد. سعی می‌کنیم با Rebaseهای پیچیده اشتباهات را برطرف کنیم و شیوۀ بازیابی یک کامیت حذف شده را آموزش خواهیم داد.

چکیدۀ دستورات

  • <git rebase <new-base : جابه‌جا کردن کامیت‌های شاخۀ جاری به نوک شاخۀ new-base که می‌تواند نام شاخه یا ID یک کامیت باشد؛
  • <git rebase -i <new-base : عمل Rebase را به شکل تعاملی اجرا می‌کند و به شما اجازه می‌دهد برای هر کامیت عملیات مجزایی انجام دهید؛
  • git commit –amend : به جای ساخت یک کامیت جدید، تغییرات استیج شده را روی آخرین کامیت اعمال می‌کند؛
  • git rebase –continue : عمل rebase را بعد از ammend کردن ادامه می‌دهد؛
  • git rebase –abort : از ادامۀ عمل Rebase منصرف شده و مخزن را به حالت قبل باز می‌گرداند؛
  • <git merge –no-ff <branch-name : گیت را مجبور می‌کند تا حتی اگر امکان ادغام fast-forward وجود داشت باز هم کامیت را merge کند.

سید ایوب کوکبی

نویسنده و مترجم...

0 دیدگاه

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *