آموزش گیت – قسمت پنجم

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

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

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

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

ادامۀ تغییر در شاخۀ crazy

ابتدا شاخۀ crazy را چک‌اوت کنید.

git branch
git checkout crazy
git log --oneline

Crazy یک شاخه از نوع topic branch بوده که به شکل خاص‌تری feature branch گفته می‌شود چرا که در این شاخه فیچر جدیدی به برنامه اضافه می‌کنیم. سایر برنامه‌نویس‌ها به راحتی می‌فهمند که هدف این شاخه چیست و قرار است چه چیز به پروژه اضافه کند. (البته نه با اسم crazy)!

هر تیمی یا هر شخصی در استفاده از گیت، استراتژی و workflow خاص خودش را دارد ولی دو اصل زیر در اغلب تیم‌ها مشترک است:

  • برای هر تغییر بزرگ یا major addition حتماً شاخۀ جدیدی بسازید؛
  • وقتی نمی‌توانید برای شاخه، نام مناسبی انتخاب کنید معنی‌اش این است که نباید آن شاخه را بسازید.

پیروی از این دو اصل ساده، تاثیر شگرفی در کارایی کدنویسی شما خواهد داشت.

انعکاس تغییرات شاخۀ css در شاخۀ crazy

به خاطر دارید که شاخۀ css را در شاخۀ مستر ترکیب و سپس آن شاخه را حذف کردیم. اکنون اگر بخواهیم تغییرات css را به شاخۀ crazy اعمال کنیم با مشکل مواجه می‌شویم. گیت به راحتی اجازه می‌دهد تا هر شاخه‌ای را در هر شاخۀ دیگری ادغام کنیم و این کار منحصر به شاخۀ مستر نیست. بنابراین می‌توانیم با همان دستور merge تغییرات مورد نظر را به شاخۀ crazy وارد کنیم.

git merge master
git log --oneline

دستور بالا شاخۀ مستر را در شاخه‌ای که در آن هستیم (چک‌اوت کرده‌ایم) یعنی crazy ادغام می‌کند ولی نه با روش Fast-forwarded که در درس قبلی با آن آشنا شدیم؛ بلکه با روش ادغام سه طرفه. یعنی مثل تصاویر پایین:

ادغام سه‌‌طرفه در گیت
ادغام شاخۀ مستر در شاخۀ crazy

اما چرا گیت اینجا نمی‌تواند به روش fast-forwarded عمل ادغام را انجام دهد؟

در روش fast-forwarded یادتان هست که به جای ساخته شدن کامیت جدید، نوک شاخۀ مستر به نوک شاخۀ مقصد جابه‌جا می‌شد. (ولی در تصویر بالا اینطور نیست)

شما جای گیت بودید چه می‌کردید؟ شاخۀ crazy برای fast-forwarding باید عقب‌گرد کند تا به نوک شاخۀ مستر برسد که برخلاف پیشروی (forwarding) است. در چنین شرایطی گیت از ادغام سه طرفه یا ۳way merge استفاده می‌کند. روش فوق زمانی استفاده می‌شود که بخواهید دو شاخه را که تاریخچۀ جدایی دارند با یکدیگر ادغام کنیم.

در این روش به صورت خودکار یک کامیت جدید یا اصطلاحاً merge commit ساخته می‌شود که دو شاخه را به هم مرتبط می‌کند. در واقع این کامیت که در تصویر بالا به رنگ قرمز مشاهده می‌کنید دو والد دارد. مثل این است که بگوییم کامیت جدید از دو شاخۀ crazy و master انشعاب یافته است. بعد از ادغام، شاخۀ crazy به تاریخچۀ هر دو شاخه یعنی خودش و مستر دسترسی دارد.

نام‌گذاری روش سه طرفه از این حقیقت نشات گرفته است که گیت با نگاه کردن به سه کامیت – که با شماره‌های ۱ و ۲ و ۳ در تصویر مشخص کرده‌ایم – عمل ادغام را انجام می‌دهد. چنین مکانیزم واقعاً توانایی بالای گیت را نشان می‌دهد. ما نه‌تنها قادر به ساخت شاخه‌های مجزایی برای توسعۀ پروژه هستیم بلکه توانایی اشتراک اطلاعات شاخه‌ها را هم داریم.

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

افزودن استایل به صفحۀ Rainbow

اکنون به تغییرات css از شاخۀ مستر دسترسی داریم و می‌توانیم به توسعۀ شاخۀ crazy ادامه دهیم. فایل rainbow.html را باز کنید و بعد از تگ title خط پایین را اضافه کنید تا به فایل style.css متصل شود.

<link rel="stylesheet" href="style.css" />

تغییرات را استیج و کامیت کنید، سپس لاگ بزنید

git status
git commit -a -m "Add CSS stylesheet to rainbow.html"
git log --oneline

همانطور که در دستور بالا می‌بینید خبری از git add نیست. به جای add از پرچم a- در دستور کامیت استفاده کرده‌ایم. این پرچم به گیت می‌گوید همۀ فایل‌های ترک شده را در اسنپ‌شاتِ استیج شده لحاظ کن. در واقع عمل استیجینگ و کامیت با هم انجام شده است. در استفاده از این دستور دقت کنید تا اشتباهاً فایل نامربوطی را وارد کامیت نکنید.

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

حالا باید صفحۀ rainbow را به هوم پیج یعنی صفحۀ ایندکس معرفی کنیم:

<h2>Navigation</h2>
<ul>
  <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 "Link index.html to rainbow.html"
git log --oneline

فورک کردن یک نسخۀ جدید از Rainbow

اکنون می‌خواهیم یک نسخۀ کاملاً متفاوت از فایل rainbow.html را پیاده‌سازی کنیم. طبق اصلی که پیش‌تر گفتیم یک تغییر بزرگ در راه است، پس بهتر است یک topic branch جدید درست کنیم:

git branch crazy-alt
git checkout crazy-alt

به خاطر داشته باشید که ما می‌توانیم بدون هیچ نگرانی از crazy یا master هر کاری که بخواهیم انجام دهیم. گیت وقتی شاخۀ جدیدی می‌سازد، HEAD فعلی را به آن شاخه اشاره می‌دهد و تغییرات ما در ادامۀ آن شاخه اعمال می‌کند. بعد از ساخت شاخۀ جدید، تاریخچۀ مخزن به شکل زیر در می‌آید:

ساخت شاخۀ crazy-alt

تغییر فایل Rainbow

لیست رنگ‌ها در فایل rainbow.html را از :

<ul>
  <li style="color: red">Red</li>
  <li style="color: orange">Orange</li>
  <li style="color: yellow">Yellow</li>
  <li style="color: green">Green</li>
  <li style="color: blue">Blue</li>
  <li style="color: indigo">Indigo</li>
  <li style="color: violet">Violet</li>
</ul>

به مقادیر پایین تغییر دهید:

<div style="background-color: red"></div>
<div style="background-color: orange"></div>
<div style="background-color: yellow"></div>
<div style="background-color: green"></div>
<div style="background-color: blue"></div>
<div style="background-color: indigo"></div>
<div style="background-color: violet"></div>

سپس کمی قالب‌بندی CSS به تگ head اضافه کنید. بعد از تگ meta:

<style>
  div {
    width: 300px;
    height: 50px;
  }
</style>

حالا اگر فایل rainbow را در مرورگر باز کنید، به جای متون رنگی، بلاکهای رنگی را مشاهده خواهید کرد. استیج و کامیت را فراموش نکنید:

git commit -a -m "Make a REAL rainbow"

اکنون تاریخچۀ نهایی پروژه به شکل زیر خواهد بود. کامیت‌های اول برای ساده‌تر شدن شکل حذف و با سه نقطه جایگزین شده‌اند.

ارسال کامیت روی شاخۀ crazy-alt

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

تصور کنید در حال کار بر روی شاخۀ خاصی هستید و کارفرما درخواست یک تغییر فوری در پروژۀ اصلی می‌دهد. زیبایی گیت اینجاست که هر جایی باشید می‌توانید کار را رها کرده و به شاخۀ اصلی برگردید تا تغییرات ضروری را اعمال کنید. معمولاً به چنین تغییرات فوری hotfix گفته می‌شود که اغلب در شاخۀ جدیدی از نوع hotfix branch توسعه داده شده و بعد از تست در شاخۀ مستر ادغام می‌شود. شاخه‌های هات‌فیکس تغییرات فوری را به نسخۀ اصلی پروژه اضافه می‌کنند؛ مثلاً گزارش یک باگ حساس که باید هرچه سریعتر حل شود.

به شاخۀ مستر سوئیچ کرده و بعد از ساخت یک شاخۀ جدید به آن چک‌اوت کنید:

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

بخش news در فایل index را طبق کدهای زیر تغییر دهید:

<h2 style="color: #C00">News</h2>
<ul>
  <li><a href="news-1.html">Blue Is The New Hue</a></li>
</ul>

یک صفحۀ جدید news-1.html با محتوای زیر ایجاد کنید:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Blue Is The New Hue</title>
<link rel="stylesheet" href="style.css" />
  <meta charset="utf-8" />
</head>
<body>
  <h1 style="color: #079">Blue Is The New Hue</h1>
  <p>European designers have just announced that
  <span style="color: #079">Blue</span> will be this year's
  hot color.</p>
    
  <p><a href="index.html">Return to home page</a></p>
</body>
</html>

اینجا چون یک فایل جدید ساخته‌ایم که هنوز track نشده نمی‌توانیم از git commit -a استفاده کنیم و باید از همان دستور سابق یعنی git add برای استیج کردن استفاده نماییم:

git add index.html news-1.html
git status
git commit -m "Add 1st news item"

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

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

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

اکنون شاخۀ هات‌فیکس آمادۀ ترکیب با شاخۀ مستر است. فراموش نکنید که ابتدا باید به master چک‌اوت کنید:

git checkout master
git merge news-hotfix

از آنجایی که شاخۀ مستر اکنون هات‌فیکس را دریافت کرده، می‌توانیم شاخۀ news-hotfix را حذف کنیم:

git branch -d news-hotfix
git branch

دیاگرام پایین، تاریخچۀ فعلی پروژه را به خوبی قبل و بعد از عمل merge نشان می‌دهد. احتمالاً از روی شکل بفهمید که چرا این merge از نوع fast-forwarded است.

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

همچنین توجه داشته باشید که فورک دیگری هم در تاریخچۀ پروژه داریم (کامیت قبل از شاخۀ مستر که به دو شاخه تبدیل شده)، این یعنی در آینده یک merge دیگر نیز در پیش داریم.

تکمیل کارِ شاخۀ crazy

خب بیایید با یک کامییت دیگر کار شاخۀ crazy را تمام کنیم.

git checkout crazy

دقت کنید فایل news-1.html و لینک این صفحه در فایل ایندکس وجود ندارد چون کلاً در شاخۀ مستقلی هستیم. لیست خبرها را در فایل ایندکس به صورت زیر تغییر دهید:

<h2 style="color: #C00">News</h2>
<ul>
<li><a href="rainbow.html">Our New Rainbow</a></li>
</ul>

اگر خوب دقت کنید خواهید دید که تغییرات اخیر کاملاً با تغییرات شاخۀ news-hotfix تداخل دارد. آنجا به صفحۀ news-1.html لینک دادیم و اینجا به صفحۀ rainbow.html؛ این یعنی تداخل (Conflict).

در چنین وضعیتی نباید به صورت دستی لینک news-1.html را به فایل ایندکس اضافه کنید چون این کار هیچ ربطی به شاخۀ فعلی ندارد و حتی اگر لینک این صفحه را اضافه کنید، فایل news-1.html در شاخۀ جاری وجود ندارد که کارکرد صحیح لینک را چک نمایید. با فرض اینکه هیچ تداخلی به وجود نیامده، تغییرات را استیج و کامیت کنید:

git commit -a -m "Add news item for rainbow"
git log --oneline
*۴۲fa173 Add news item for rainbow
*۷۱۴۷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

ادغام شاخۀ crazy با شاخۀ مستر

اکنون به شاخۀ مستر چک‌اوت می‌کنیم تا عمل ادغام را انجام دهیم:

git checkout master
git merge crazy

بعد از اجرای دستور با خطای زیر مواجه می‌شویم:

Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

این اولین merge conflict ما است. کانفلیکت یا تداخل زمانی بروز می‌کند که دو شاخه محتوای یکسانی را تغییر داده باشند. گیت نمی‌تواند تصمیم بگیرد که کدام نسخه را بپذیرد؛ بنابراین ناچار است از ما کسب تکلیف کند. اگر git status بگیریم دقیقاً متوجه خواهیم شد که در کدام فایل تداخل به وجود آمده است:

# On branch master
# Changes to be committed:
#
#       new file:   rainbow.html
#
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      index.html
#

ما در حال مشاهدۀ اسنپ‌شات استیج شده از merge commit هستیم. در اولین مرج سه طرفۀ که قبلاً انجام دادیم چنین پیغامی نشان داده نشد چون تداخلی وجود نداشت. ولی در حال حاضر تداخل به وجود آمده و گیت عملیات merge را متوقف کرده تا بعد از رفع مشکل، اسنپ‌شات را کامیت کند. بخش Unmerged Paths فایل‌هایی که تداخل دارند را نمایش می‌دهد. آن فایل‌ها را باز کرده (در اینجا index.html) و قسمتی که به شکل زیر است را پیدا کنید:

<<<<<<< HEAD
    <li><a href="news-1.html">Blue Is The New Hue</a></li>
=======
    <li><a href="rainbow.html">Our New Rainbow</a></li>
>>>>>>> crazy

گیت به صورت خودکار قسمت‌هایی از فایل که دچار تداخل شده را علامت‌گذاری می‌کند. ابتدا با علامت
<<<<<<< HEAD نسخۀ فعلی آن تکه کد در شاخۀ جاری را نشان می‌دهد و سپس با علامت ======= نسخۀ شاخۀ crazy را نشان می‌دهد. نام crazy در انتها آمده است.

بخوانید  راهنمای مقدماتی گیت (git)

برطرف کردن تداخل

ما می‌توانیم با پاک کردن علامت‌های یاد شده، فایل ایندکس را طوری ویرایش کنیم که هر دو نسخه را نگه‌ دارد:

<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>
</ul>

حالا می‌توانیم به گیت اعلام کنیم که تداخل را رفع کرده‌ایم و می‌تواند index.html را به اسنپ‌شات اضافه کند:

git add index.html
git status

و نهایتاً تغییرات را کامیت می‌کنیم:

git commit

در دستور بالا از پرچم m- استفاده نمی‌کنیم تا از پیغام پیش‌فرض کامیت استفاده کنیم. شکل نهایی تاریخچه بعد از ادغام به صورت زیر خواهد بود:

ادغام شاخۀ crazy در شاخۀ مستر

پاکسازی شاخه‌های اضافه در گیت

نظر به اینکه تغییرات crazy با موفقیت در شاخۀ مستر ادغام شده، دیگر به این شاخه نیازی نداریم و می‌توانیم حذفش کنیم. همینطور شاخۀ crazy-alt:

git branch -d crazy
git branch -d crazy-alt

دستور حذف شاخۀ crazy با موفقیت اجرا می‌شود ولی حذف crazy-alt با خطا مواجه می‌شود چون این شاخه هنوز با مستر ادغام نشده است و با حذف آن تغییراتش را کاملاً از دست خواهیم داد. با دستور پایین می‌توانید به گیت بگویید که موضوع را می‌دانم ولی باز هم می‌خواهم شاخه را حذف کنی. برای این کار از کاراکتر بزرگ D استفاده کنید. خودِ گیت هم در خروجی این دستور را معرفی می‌کند:

git branch -D crazy-alt

اکنون تمام شاخه‌ها حذف شده‌اند و فقط master را داریم. البته اگر به تصویر نگاه کنید می‌بینید که کامیت‌های آن شاخه‌ها همچنان در تاریخچه هست و مستر به آن‌ها دسترسی دارد؛ فقط ارجاع به آن‌ها حذف شده است.

حذف فیچر برنچ‌ها

تفاوت بین ادغام fast forward و سه طرفه فقط برای توسعه‌دهنده‌ها است و در تاریخچۀ پروژه فرقی با هم ندارند؛ هر دو به یک شکل نمایش داده می‌شوند. در درس بعدی با هر دو نوع ادغام و همچنین عوارض احتمالی استفاده از تاریخچه‌های غیر خطی آشنا خواهید شد.

جمع‌بندی

در این درس، با سه کاربرد مهم شاخه‌ها در گیت آشنا شدید:

  • ساخت شاخه‌های فیچر یا feather branch که معمولاً طول عمر بیشتری دارند؛
  • اعمال آپدیت‌های سریع یا اصطلاحاً هات‌فیکس کردن سریع پروژه؛
  • ادغام شاخه‌های فرعی در شاخۀ اصلی یعنی مستر.

در دو حالت اول به محیطی ایزوله برای پیاده‌سازی و تست تغییرات نیاز داشتیم. هر چقدر بیشتر با گیت آشنا می‌شوید پی می‌برید که تغییرات را به شکل topic branch روی نسخۀ اصلی پروژه اعمال کنید. این کار باعث می‌شود هیچگاه پروژۀ اصلی دچار آسیب نشود.

ما از شاخۀ مستر به عنوان پایه و اساس سایر شاخه‌های موقت استفاده کردیم. علاوه بر شاخۀ master بسیاری از توسعه‌دهندگان شاخۀ پایدار دیگری به نام develop هم درست می‌کنند. این کار به آن‌ها اجازه می‌دهد تا در شاخۀ مستر واقعاً نسخۀ پایدار پروژه را نگه‌داری کنند (مثلاً ریلیزهای عمومی) و کارهای آماده‌سازی شاخۀ مستر را در شاخۀ develop انجام دهند.

در این درس همچنین با ادغام سه‌طرفه آشنا شدید که دو شاخه را با ساخت یک کامیت جدید با هم ترکیب می‌کرد. این روش در کنار ادغام fast-forward واقعاً قدرت بالایی به گیت می‌دهد چرا که با استفاده از آن می‌توان آپدیت‌های مختلف را با سایر توسعه‌دهندگان به اشتراک گذاشت بدون اینکه پایداری پروژه تهدید شود.

نهایتاً با چگونگی پاکسازی مخزن از شاخه‌های فرعی آشنا شدید و دانستید چگونه یک شاخه را که تغییراتش هنوز با شاخۀ مستر ادغام نشده به اجبار حذف کنید.

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

  • “<git commit -a -m “<message> : استیج کردن همزمان فایل‌های ترک شده و کامیت کردن اسنپ‌شات با یک پیام اختصاصی؛
  • <git branch -D <branch-name : حذف اجباری یک شاخۀ ادغام نشده (مراقب باشید چون این دستور شاخه را برای همیشه حذف می‌کند و امکان بازگشت به آن وجود ندارد)

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

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

0 دیدگاه

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