زمانی که با استفاده از گیت یک ریپازیتوری جدید میسازیم، به صورت خودکار یک برنچ (شاخه) تحت عنوان master
ساخته میشود که نقش شاخهٔ اصلی ریپازیتوری مذکور را بازی خواهد کرد و هر کامیتی که انجام دهیم نیز روی این شاخه اِعمال خواهد شد و این در حالی است که معمولاً تیمهای نرمافزاری از این شاخه به عنوان نسخهای از نرمافزار استفاده میکنند که قرار است روی سرورهای اصلی دیپلوی گردد. با این تفاسیر، منطقی به نظر میرسد که این بِرَنچ به عنوان فضای آزمون و خطا در حین کدنویسی قملداد نشده بلکه فضاها یا بهتر بگوییم شاخههای فرعی دیگری ساخته و در آنها اقدام به توسعهٔ فیچرهای جدید نموده سپس آنها را با شاخهٔ مَستر ادغام نمود.
یکی از مزایای وجود شاخهها در سیستم کنترل نسخهٔ گیت آن است که میتوان به تعداد توسعهدهندگانی که در تیم حضور دارند برنچهای اختصاصی ساخته و در آنِ واحد تمامی اعضای تیم بتوانند اقدام به توسعهٔ نرمافزار کنند. برای درک بهتر این موضوع، فرض کنیم دولوپری داریم به نام بهزاد قرار است قابلیت درگاه پرداخت را به پروژه بیفزاید و دولوپر دیگری به نام سهند قرار است تا رابط کاربری را تکمیل کند؛ در چنین شرایطی، میتوان دو شاخهٔ فرعی از شاخهٔ مَستر تحت عناوین دلخواهی همچون behzad-branch-gateway
و sahand-branch-ui
ایجاد کرده و هر کدام از ایشان با سهولت هرچه تمامتر شروع به کدنویسی کرده و چنانچه در نهایت کدهای نوشتهشده مورد تأیید مدیر فنی بود، با شاخهٔ اصلی (مَستر) ادغام خواهند شد.
همانطور که در تصویر فوق ملاحظه میشود، خط افقی که در سراسر نمودار ملاحظه میشود به عنوان بِرَنچ اصلی یا مَستر است که فرض میکنیم توسط توسعهدهندهٔ اصلی پروژه کامیتهایی تحت عناوین C1 و C2 انجام شده است. سپس یک شاخه از شاخهٔ اصلی جدا شده و نامی دلخواه همچون Branch1 برای آن در نظر گرفتهایم که فرضاً توسط یکی از اعضای تیم هَندل میگردد به طوری که وی سه کامیت داخل این شاخه انجام داده است. در آنِ واحد، برای یکی دیگر از اعضای تیم شاخهای به نام Branch2 ساخته شده و او نیز یک کامیت انجام داده است. در نهایت، دولوپر اصلی این پروژه کلیهٔ تغییرات اِعمالشده در دو بِرَنچ فوق را با بِرَنچ اصلی (مَستر) ادغام کرده و کلیهٔ این تغییرات را تحت عنوان C7 کامیت کرده است.
با این توضیحات تئوریک، در ادامهٔ این آموزش خواهیم دید که به چه شکل به صورت عملی میتوانیم اقدام به ساخت بِرَنچهای مختلف کرده سپس آنها را با یکدیگر ادغام نمود.
برای شروع کار با بِرَنچها، ابتدا کامند زیر را اجرا میکنیم:
/var/www/git-tutorial$ git branch
* master
اساساً دستور branch
لیستی از کلیهٔ بِرَنچها را در معرض دیدمان قرار میدهد و همانطور که پیش از این گفتیم، پس از شروع استفاده از گیت یک بِرَنچ اصلی تحت عنوان master
ساخته میشود و همانطور که در خروجی فوق ملاحظه میشود، علامت *
نشان میدهد که در حال حاضر این بِرَنچ در حال استفاده است.
نکته |
توجه داشته باشیم که در نامگذاری شاخهها نمیتوان از اِسپیس (فاصله) استفاده نمود. |
فرض کنیم که میخواهیم شروع به استایل دادن به فایل اچتیامال کنیم که این کار وظیفهٔ دولوپر فرانتاند است. برای همین منظور، یک بِرَنچ جدید اختصاصاً برای وی به صورت زیر میسازیم:
/var/www/git-tutorial$ git branch ui-feature
در واقع، پس از دستور branch
از نامی دلخواه بدون اِسپیس استفاده کردهایم و اگر مجدد به لیست بِرَنچها نگاهی بیندازیم، خواهیم داشت:
/var/www/git-tutorial$ git branch
* master
ui-feature
میبینیم که بِرَنچ جدید اضافه شده اما کماکان شاخهٔ مَستر در حالت انتخاب قرار دارد که برای سوئیچ کردن به بِرَنچ جدید، از دستور زیر میباید استفاده کرد:
/var/www/git-tutorial$ git checkout ui-feature
Switched to branch 'ui-feature'
اگر مجدد به لیست بِرَنچها نگاهی بیندازیم، خواهیم دید:
/var/www/git-tutorial$ git branch
master
* ui-feature
میبینیم که بِرَنچ اختصاصی توسعهدهندهٔ فرانتاند انتخاب شده است. حال جهت تست، در مسیر روت پروژه فایلی تحت عنوان styles.css
حاوی کدهای سیاساس زیر میسازیم:
body {
background: #ffde57;
}
اکنون این فایل را کامیت کرده و سپس لاگ میگیریم:
/var/www/git-tutorial$ git add styles.css
/var/www/git-tutorial$ git commit -m "stylesheet added"
[ui-feature b276cac] stylesheet added
1 file changed, 3 insertions(+)
create mode 100644 styles.css
میبینیم که در پیامی که پس از کامیت کردن در معرض دیدمان قرار گرفته است، آمده که کامیت مذکور در شاخهٔ ui-feature
افزوده شده است که این موضوع در لاگ گیت هم نشان داده میشود:
/var/www/git-tutorial$ git log --oneline
b276cac (HEAD -> ui-feature) stylesheet added
23d2256 (master) a new line added
fd728ca Yet another commit
حال مجدد به بِرَنچ master
سوئیچ میکنیم:
/var/www/git-tutorial$ git checkout master
Switched to branch 'master'
اکنون اگر به محتویات داخل پوشه نگاهی بیندازیم، خواهیم دید که فایل styles.css
حذف شده است و این مسئله از آنجا ناشی میگردد که ما این فایل را در بِرَنچ ui-feature
افزودهایم و بِرَنچ master
هیچ اطلاعی از این موضوع ندارد. فرض کنیم که تغییرات اِعمالشده توسط توسعهدهندهٔ فرانتاند مورد تأیید هستند و میخواهیم آنها را با بِرَنچ اصلی ادغام نماییم، اما پیش از بررسی این موضوع قصد داریم ببینیم که به چه شکل میتوان یک بِرَنچ را حذف کرد:
/var/www/git-tutorial$ git branch -d ui-feature
error: The branch 'ui-feature' is not fully merged.
If you are sure you want to delete it, run 'git branch -D ui-feature'.
با استفاده از آپشن d-
و آوردن نام شاخهٔ مد نظر، میتوان این کار را عملی ساخت اما همانطور که ملاحظه میشود، خطایی با این مضمون در معرض دیدمان قرار گرفته که «شاخهٔ ui-feature
با شاخهٔ اصلی ادغام نشده است و اگر از حذف آن مطمئن هستید، میباید به جای آپشن d-
از D-
استفاده نمایید.» که برای این منظور داریم:
/var/www/git-tutorial$ git branch -D ui-feature
Deleted branch ui-feature (was b276cac).
اکنون اگر به لیست بِرَنچها نگاهی بیندازیم خواهیم داشت:
/var/www/git-tutorial$ git branch
* master
میبینیم که بِرَنچ مد نظرمان حذف شده و صرافاً بِرَنچ اصلی موجود داشت. حال برای آن که ببینیم به چه شکل میتوان دو بِرَنچ را با یکدیگر ادغام نمود، ابتدا اقدام به ساخت یک بِرَنچ جدید میکنیم اما این بار با استفاده یک دستور ترکیبی این کار را انجام خواهیم داد:
/var/www/git-tutorial$ git checkout -b new-branch
Switched to a new branch 'new-branch'
پس از دستور checkout
، با استفاده از آپشن b-
که برگرفته از واژهٔ Branch است بِرَنچ جدیدی تحت عنوان new-branch
ساختهایم و این در حالی است که نسبت به روش قبلی ساخت یک بِرَنچ جدید، بلافاصله به این بِرَنچ سوئیچ شدهایم که برای اطمینان حاصل کردن از این موضوع، میتوانیم کامند زیر را اجرا کنیم:
/var/www/git-tutorial$ git branch
master
* new-branch
طبق روال گذشته، فایلی تحت عنوان styles.css
ساخته کدی دلخواه داخلش مینویسیم و آن را کامیت میکنیم:
/var/www/git-tutorial$ git add styles.css
/var/www/git-tutorial$ git commit -m "stylesheet added"
[new-branch e6d18e7] stylesheet added
1 file changed, 3 insertions(+)
create mode 100644 styles.css
با سوئیچ کردن به بِرَنچ مَستر با استفاده دستوری که پیش از این فرا گرفتیم، میبینیم که این فایل قابلمشاهده نیست. حال فرض کنیم دولوپر دیگری در تیم داریم که این وظیفه را دارا است تا روی کدهای جاوااسکریپت کار کند که در همین راستا، بِرَنچ جدیدی به صورت زیر برای وی میسازیم:
/var/www/git-tutorial$ git checkout -b js-branch
Switched to a new branch 'js-branch'
در این مرحله از آموزش، فایلی با نامی دلخواه همچون main.js
ساخته و کدهای زیر را داخل آن مینویسیم:
console.log('something ...');
سپس تغییراتی که در این مرحله از کار اِعمال شدهاند را کامیت میکنیم:
/var/www/git-tutorial$ git commit -m "a js file added"
[js-branch 3902bd7] a js file added
1 file changed, 1 insertion(+)
create mode 100644 main.js
حال قصد داریم تا تغییرات را با شاخهٔ اصلی ادغام کنیم که برای این منظور، ابتدا میباید وارد شاخهٔ master
شویم:
/var/www/git-tutorial$ git checkout master
Switched to branch 'master'
سپس با استفاده از دستور merge
به صورت زیر، قصد داریم تا شاخهٔ اولی که ساختیم را ادغام کنیم:
/var/www/git-tutorial$ git merge new-branch
Updating 23d2256..e6d18e7
Fast-forward
styles.css | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 styles.css
همانطور که ملاحظه میشود، کلیهٔ تغییرات انجامشده در این شاخه به شاخهٔ اصلی (مَستر) انتقال یافته و از این پس شاهد فایل styles.css
در شاخهٔ مَستر خواهیم بود (اصطلاح Fast-forward حاکی از آن است که بِرَنچ مَستر هیچ گونه تغییری نداشته بلکه فقط یک بِرَنچ دیگر با آن ادغام شده است.) طبق همین روال، بِرَنچ دیگر را نیز ادغام میکنیم:
/var/www/git-tutorial$ git merge js-branch
Merge made by the 'recursive' strategy.
main.js | 1 +
1 file changed, 1 insertion(+)
create mode 100644 main.js
تفاوت در این پروسه نسبت به مرحلهٔ قبل در آن است که مجدد ادیتور پیشفرض سیستم باز شده و پیامی در معرض دیدمان قرار میگیرد که همچون روال قبل، اگر ادیتور مذکور nano باشد میتوان دکمههای Ctrl + X را فشرد و در متن پیامی که پس از اجرای کامند فوق در معرض دیدمان قرار میگیرد جملهٔ .Merge made by the 'recursive' strategy آمده و دلیلش هم آن است که پس از ادغام بِرَنچ new-feature
، این بار بِرَنچ مَستر خود نیز آپدیت شده است چرا که کدهای داخل بِرَنچ new-branch
به آن افزوده شدهاند و از همین روی همچون مورد قبل شاهد پیام Fast-forward نخواهیم بود. به هر روی، میبینیم که از این پس فایل main.js
نیز به شاخهٔ اصلی افزوده شده است و قابلروئیت است.
آشنایی با مفهوم Conflict در Git
آنچه در ارتباط با ساخت بِرَنچهای مختلف معمولاً به کَرات رخ میدهد، مفهومی است تحت عنوان Conflict یا «تداخل» که یک توسعهدهندهای که با گیت کار میکند حتماً میباید با روشهای حل کانفلیکتها آشنا باشد. برای درک بهتر این موضوع، یک بِرَنچ جدید میسازیم:
/var/www/git-tutorial$ git checkout -b another-branch
Switched to a new branch 'another-branch'
بِرَنچ جدیدی تحت عنوان another-branch
ساخته شده که حاوی کلیهٔ تغییراتی است که تاکنون در بِرَنچ master
رخ دادهاند؛ به عبارتی، حاوی کدهای نوشتهشده در بِرَنچهای new-branch
و js-branch
است. در این مرحله، با استفاده از دستور git checkout master
مجدد به شاخهٔ اصلی سوئیچ میکنیم و تغییراتی در فایل styles.css
به صورت زیر میدهیم:
body {
background: #ffde57;
font-size: 12px;
}
همانطور که ملاحظه میشود، پراپرتی font-size
را افزودهایم سپس این تغییر را کامیت میکنیم:
/var/www/git-tutorial$ git add styles.css
/var/www/git-tutorial$ git commit -m "changes made to the styles.css file in master branch"
[master 1250bb5] changes made to the styles.css file in master branch
1 file changed, 1 insertion(+)
حال سوئیچ میکنیم به آخرین بِرَنچ تحت عنوان another-branch
که پیش از این ساختیم:
/var/www/git-tutorial$ git checkout another-branch
Switched to branch 'another-branch'
اگر اکنون به فایل styles.css
مراجعه کنیم، خواهیم دید که نسخهٔ قدیمی فایل در معرض دیدمان قرار خواهد گرفت و در ادامه آن را به صورت زیر آپدیت میکنیم:
body {
background: #ffde57;
padding: 10px;
}
حال تغییرات را کامیت میکنیم:
/var/www/git-tutorial$ git add styles.css
/var/www/git-tutorial$ git commit -m "changes made to the styles.css file in another-branch"
[another-branch dadc226] changes made to the styles.css file in another-branch
1 file changed, 1 insertion(+)
در ادامه، به منظور ادغام تغییرات ابتدا با استفاده از دستور git checkout master
وارد شاخهٔ مَستر شده سپس با استفاده از دستور زیر قصد داریم تغییرات صورتگرفته توسط بِرَنچ another-branch
را ادغام کنیم:
/var/www/git-tutorial$ git merge another-branch
Auto-merging styles.css
CONFLICT (content): Merge conflict in styles.css
Automatic merge failed; fix conflicts and then commit the result.
میبینیم با توجه به این که پیش از این فایل styles.css
توسط بِرَنچ master
آپدیت شده بود اما بِرَنچ another-branch
از آن بیخبر بود، متن پیام فوق حاکی از آن است که به تداخل برخوردهایم و اگر به فایل styles.css
مراجعه کنیم، خواهیم دید:
body {
background: #ffde57;
<<<<<<< HEAD
font-size: 12px;
=======
padding: 10px;
>>>>>>> another-branch
}
در تفسیر آنچه در بالا مشاهده میکنیم میتوان گفت که خط سوم حاکی از آن است که کد مذکور پیش از این اضافه شده بوده است و خط پنجم شروع کدهای جدیدی که باعث کانفلیک شدهاند را نشان میدهد و خط هفتم نیز پایان نقطهای است که تغییرات صورتگرفته توسط شاخهٔ another-branch
اِعمال شدهاند. کاری که در این مرحله میتوان انجام داد آن است که اگر کدها مورد تأیید هستند، خطوط سوم، پنجم و هفتم را به صورت زیر حذف میکنیم:
body {
background: #ffde57;
font-size: 12px;
padding: 10px;
}
در این مرحله چنانچه کدها رضایتبخش بودند، مجدد نیاز داریم تا تغییرات را کامیت کنیم:
/var/www/git-tutorial$ git add styles.css
/var/www/git-tutorial$ git commit
همانطور که ملاحظه میشود، از آپشن m-
برای دستور commit
استفاده نکرده و اینتر میکنیم:
Merge branch 'another-branch'
# Conflicts:
# styles.css
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
# modified: styles.css
میبینیم به صورت خودکار این پیام در معرض دیدمان قرار میگیرد و طبق روال گذشته دکمههای Ctrl + X را میزنیم.
جمعبندی
مفهوم شاخهها در گیت را به نوعی میتوان به عنوان یکی از کلیدیترین خصیصههای این سیستم ورژن کنترل تلقی کرد چرا که اساساً این امکان در اختیار تیمهای مهندسی قرار میگیرد تا بتوانند به هر تعداد که بخواهند شاخهٔ جدید ایجاد کرده و افراد متعددی در آنِ واحد روی پروژه کار کنند. در عین حال، احتمال رخداد کانفلیکت در چنین شرایط بسیار زیاد خواهد بود و یک توسعهدهندهای که ادعا میکند به گیت مسلط است حتماً میباید با راهکارهای مختلف رفع این تداخلها آشنا باشد.