نحوه کار مخزن‌ها در گیت اساسا با ابزارهای دیگر متفاوته. یکی از تفاوت‌های مشخص بین اون‌ها شاخه‌سازی است. در بیشتر ابزارهای VCS شاخه‌سازی کلی کار داره. اون‌ها شاخه‌سازی رو خیلی سخت گرفتن و بیشتر توسعه‌دهنده ها بیخیال میشن و سراغ گردش کارهایی میرن که زیاد به شاخه‌سازی نیاز نداشته باشه.

در گیت قضیه برعکسه: شاخه‌سازی در گیت اینقدر ساده و راحته که بیشتر مردم بیش از حد استفاده می‌کنن ولی گاهی برای مدیریت اون‌ها دچار سردرگمی می‌شن.

این مقاله قصد داره برخی از این سردرگمی‌ها رو با آموزش سوییچ کردن شاخه‌ها در گیت به شیوه صحیح، برطرف کنه. اما قبل از اون با یک سری اصول پایه شروع می‌کنیم، توضیح می‌دیم که شاخه‌ها در گیت چی هستند، چطور کار می کنند و چطور اون‌ها رو می‌سازید.

قبل از اتمام مقاله، یک نکته ویژه رو بهتون یاد می‌دیم، چطور از راه دور شاخه‌ها رو بررسی کنید. خب بیاین شروع کنیم.

شاخه‌های گیت چطور کار می‌کنند؟

شاخه‌ها در گیت چطور کار می‌کنند؟ اولین چیزی که باید بدونید اینه که مخازن در گیت از دو قسمت شیء‌ها و مرجع‌ها تشکیل شده.

اصلی‌ترین نوع شیء‌ها در مخزن گیت commit‌ها هستند. مرجع‌ها به مرجع‌ها یا شیء‌های دیگر اشاره می‌کنند. همون‌طور که احتمالا حدس زدید، اصلی‌ترین نوع مرجع‌ها شاخه‌ها هستند.

شیء‌ها در گیت تغییر‌ناپذیر هستند. شما به هیچ‌وجه نمی‌تونید یک commit رو تغییر بدین یا جای اون رو در تاریخچه عوض کنید. دستوراتی هستند که به نظر میاد تغییرات ایجاد می‌کنند، اما در واقع commit‌های جدید می‌سازند.

اما برعکس شیء‌ها، مرجع‌ها خیلی تغییر می‌کنند. برای مثال وقتی یک commit جدید می‌سازید، شاخه به روزرسانی می‌شه تا به اون commit اشاره کنه.

وقتی یک شاخه جدیدی می‌سازید، اتفاقی که میفته اینه که یک مرجع جدید ساخته می‌شه که به یک commit اشاره می‌کنه. به همین دلیل ساختن شاخه‌ها در گیت خیلی ساده و سریع انجام می‌شه.

چطور می‌تونم یک شاخه جدید بسازم؟

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

kdir git-switch-demo # creating a folder
cd git-switch-demo
git init # initializing a repository
touch file1.txt # creating the first file
git add . # adding the file to the stage
git commit -m "Create first file" # commiting the file
touch file2.txt
git add .
git commit -m "Create second file"
touch file3.txt
git add .
git commit -m "Create third file"

در مثال بالا ما یک مخزن جدید ایجاد کردیم و سه commit به اون اضافه کردیم و برای هر commit یک فایل درست کردیم. در این شکل وضعیت فعلی مخزن رو می‌بینید:

برای ایجاد یک شاخه جدید از نقطه فعلی، فقط کافیه دستور <git branch <branch-name رو اجرا کنیم. در اینجا ما نام شاخه رو example تعیین می‌کنیم:

git branch example

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

Git Switch Branch

حالا اگر وقتی در شاخه master بودیم یک commit جدید اضافه می‌کردیم چه اتفاقی می‌افتاد؟ آیا تاثیری روی شاخه example می‌ذاشت؟ در جواب باید بگم که نه، نمی‌ذاشت.

دستور زیر رو اجرا کنید:

echo "Another file" > file4.txt
git add .
git commit -m "Create fourth file"

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

Git Switch Branch

چطور بین شاخه‌ها سوییچ کنیم؟

تا قبل از این از دستور checkout برای این کار استفاده می‌شد. از نسخه 2.23 گیت، دستور switch و restore command برای این کار اضافه شد، هرچند هنوز هم می‌تونید از دستور checkout استفاده کنید.

دلیل این کار داشتن دستورات مشخص و جداگانه برای گروه‌های مختلف از از وظایفی بود که دستور checkout برای اون‌ها استفاده می‌شد.

چطور از دستور Checkout در گیت استفاده کنیم؟

راه قدیمی‌تر و شناخته‌شده‌تر برای سوییچ کردن بین شاخه‌ها در گیت، استفاده از دستور checkout است. در مثال بالا، اگه بخوایم شاخه example رو تغییر بدیم، فقط باید دستور زیر رو اجرا کنیم:

git checkout example

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

حالا شما در یک شاخه جدید هستید. در این شاخه می‌تونید هر چقدر که خواستید commit اضافه کنید، بدون این که تاثیری روی شاخه master داشته باشه.

دستور checkout به همراه نام شاخه، درختی که بر روی اون در حال کار هستیم، ایندکس و مرجع‌های HEAD رو که به شاخه‌ای که در اون دستور رو اجرا کردید اشاره می‌کنند، به‌روزرسانی می‌کنه.

شاید براتون سوال پیش بیاد که اگر درست در لحظه سوییچ تغییرات رو برگردونید چی می‌شه؟  اون تغییرات نگه داشته می‌شوند تا بتونید در شاخه جدید اون‌ها رو به عنوان commit ثبت کنید.

گیت به شما اجازه می‌ده از دستور checkout به روش‌های مختلفی استفاده کنید. برای مثال یکی از حالت‌هایی که خیلی در اون استفاده می‌شه زمانیه که می‌خواهید یک شاخه جدید بسازید و بلافاصله به اون سوییچ کنید.

در واقع من میگم ساختن یک شاخه و سوییچ نکردن بهش در همون لحظه یک استثناء است که کمتر اتفاق میفته. بنابراین گیت به ما یک راه میان‌بر پیشنهاد می‌ده. به جای ساختن یک شاخه و بعد سوییچ کردن بهش، می‌تونیم این مراحل رو در یک مرحله و با اجرای دستور checkout به همراه پارامتر b- استفاده کنیم. 

بنابراین دستور زیر:

git checkout -b new

معادل این دستوره:

git branch new
git checkout new

دستور checkout فقط برای شاخه ها استفاده نمی‌شه. از اون برای commit‌ها هم می‌تونید استفاده کنید. اما چرا باید این کار رو بکنید؟

خب این که بدونید پروژه قبلا و در یک بازه خاص چه شکلی بوده می‌تونه براتون مفید باشه، به خصوص برای آزمایش‌های مربوط به پروژه؛ اما فقط این نیست. استفاده از دستور checkout برای commit مخزن شما رو در حالت detached HEAD قرار می‌ده، که در این حالت شما می‌تونید commit‌های مختلف رو به صورت آزمایشی اضافه کنید و بعد تصمیم بگیرید که اون‌ها رو نگه دارید یا حذفشون کنید.

سوییچ گیت چی هست؟

تا چند وقت قبل، دستور checkout تنها راه سوییچ کردن بین شاخه‌ها بود. مشکل اینجاست که این دستور کارهای دیگری هم انجام می‌ده، که ممکنه باعث سردرگمی، به خصوص بین کاربران جدید بشه.نسخه 2.23.0 گیت این مشکل رو با اضافه کردن دستورات switch و restore حل کرده.

در این مقاله با دستور restore کاری نداریم، اما دستور switch راه جدید سوییچ بین شاخه‌ها در گیت است. صفحه دستورالعمل، فهرست تمام پارامترهای مربوط به این دستور رو مشخص کرده. در ابتدایی‌ترین حالت، این دستور رو مثل دستور git checkout استفاده می‌کنیم، با این تفاوت که به جای checkout باید switch رو قرار بدیم:

git switch example

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

git switch -

حالا اگه بخوایم یک شاخه ایجاد کنیم و بلافاصله بهش سوییچ کنیم چکار باید بکنیم؟ برای دستور checkout می‌تونیم از میان‌بر زیر استفاده کنیم:

git checkout -b <branch-name>

دستور جدید هم یه میان‌بر پیشنهاد می‌کنه، اما در این مورد از حرف C استفاده می‌کنیم:

git checkout -c <branch-name>

شاید براتون سوال پیش بیاد که آیا استفاده از دستور جدید ارزشش رو داره؟ من شخصا تا وقتی دستور git checkout رو تغییر ندادن از اون استفاده می‌کنم، بیشتر به خاطر این که ذهنم به اون عادت کرده.

اما برای آموزش تازه‌کارها قطعا از دستور switch استفاده می‌کنم. چون نام این دستور با کاری که انجام می‌ده منطبقه و از این رو به خاطر سپردنش خیلی راحت‌تره.

چطور یک شاخه ریموت رو سوییچ کنیم؟

اما نوبت می‌رسه به اون نکته طلایی که اول مقاله گفتیم. در این مثال ما از یک پروژه متن باز به اسم Noda Time استفاده می‌کنیم که یک API تاریخ و زمان جایگزین برای .NET است. در قدم اول مخزن رو clone می‌کنیم:

git clone https://github.com/nodatime/nodatime.git

حالا شما باید یک پوشه به اسم nodatime داشته باشید. وارد پوشه بشید و دستور زیر رو اجرا کنید:

git branch -a

دستور branch تمام شاخه‌های موجود در مخزن شما رو فهرست می‌کنه. پارامتر -a میگه که شما می‌خواین تمام شاخه‌های موجود رو ببنید نه فقط شاخه‌های محلی. نتیجه باید مشابه شکل زیر باشه.

همون‌طور که می‌بینید ما فقط یک شاخه محلی داریم که اونم شاخه master است. گزینه‌هایی که به رنگ قرمز در اومدن، شاخه‌های ریموت شما هستن. خب حالا فرض کنید می‌خوایم در شاخه slow-test دستور check out رو انجام بدیم.

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

وقتی یک شاخه جدید می‌سازید، می‌تونید یک commit یا نام شاخه رو به عنوان یک پارامتر ثبت کنید. پس برای ساختن یک شاخه محلی از شاخه ریموت slow-test فقط کافیه دستور زیر رو اجرا کنم:

git branch slow-test origin/slow-test

در این مثال من نام شاخه رو slow-test تعیین کردم، ولی می‌تونستم نام اون رو هر چیزی که می‌خوام بذارم.

من همین‌طور می‌تونستم از دستور checkout با پارامتر -b یا دستور switch با پارامتر c- استفاده کنم. بنابراین عملکرد دو دستور زیر مثل دستور بالاست.

git checkout -b slow-test origin/slow-test

git checkout -b slow-test origin/slow-test

در آخر باید بگم که یک راه ساده‌تر هم برای این کار هست. من می‌تونستم فقط دستور git checkout slow-test رو اجرا کنم و نتیجه‌ش مثل دستورات بالا می‌شد.

این دستور به این خاطر عمل می‌کنه که وقتی می‌خوایم یک شاخه رو check out کنیم و گیت شاخه‌ای با اون نام پیدا نمی‌کنه، سعی می‌کنه اون رو با یکی از شاخه‌های ریموت شما همسان‌سازی کنه. اگه موفق بشه همسان‌سازی کنه، بقیه کارها رو خودش انجام می‌ده.

سخن آخر: از شاخه های گیت به اندازه استفاده کنید.

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

قبل از این که بحث رو خاتمه بدیم، باید یک نکته رو بگم: فقط چون می‌تونید یک کاری رو انجام بدید معنیش این نیست که حتما باید انجامش بدید. گاهی اوقات مردم اینقدر از سادگی کار با شاخه‌ها در گیت لذت می‌برند که در نهایت باعث می‌شه گردش کارهاشون شاخه‌های زیادی با طول عمر بالا داشته باشه، که این موضوع فرایند توسعه رو خیلی پیچیده می‌کنه، احتمال بروز خطا خیلی زیاد می‌شه و در نهایت یکپارچه‌سازی‌ها به تاخیر میفته.