Chapters ▾ 2nd Edition

3.2 انشعاب‌گیری در گیت (Git Branching) - شاخه‌بندی و ادغام پایه‌ای (Basic Branching and Merging)

شاخه‌بندی و ادغام پایه‌ای (Basic Branching and Merging)

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

  1. روی یک وب‌سایت کار کنید

  2. شاخه‌ای برای داستان کاربری جدیدی که روی آن کار می‌کنید ایجاد کنید

  3. در آن شاخه کمی کار انجام دهید

در این مرحله، یک تماس دریافت می‌کنید که یک مشکل دیگر بحرانی است و باید یک اصلاح فوری (hotfix) انجام دهید. شما کارهای زیر را انجام خواهید داد:

  1. به شاخه‌ی تولید (production) خود بروید

  2. شاخه‌ای برای اضافه کردن اصلاح فوری ایجاد کنید

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

شاخه‌بندی پایه‌ای (Basic Branching)

فرض کنیم که روی پروژه خود کار می‌کنید و چند کامیت روی شاخه‌ی master انجام داده‌اید.

A simple commit history
نمودار 18. A simple commit history

تصمیم گرفته‌اید روی مسئله‌ی شماره #53 در هر سیستم پیگیری اشکالی که شرکت شما استفاده می‌کند، کار کنید. برای ایجاد شاخه جدید و همزمان جابجایی به آن، می‌توانید دستور git checkout را با گزینه‌ی -b اجرا کنید:

$ git checkout -b iss53
Switched to a new branch "iss53"

این خلاصه‌ی دستور زیر است:

$ git branch iss53
$ git checkout iss53
Creating a new branch pointer
نمودار 19. Creating a new branch pointer

شما روی وب‌سایت خود کار می‌کنید و چند کامیت انجام می‌دهید. با این کار شاخه‌ی iss53 به جلو حرکت می‌کند، چون آن شاخه را چک‌اوت کرده‌اید (یعنی HEAD شما به آن اشاره دارد):

$ vim index.html
$ git commit -a -m 'Create new footer [issue 53]'
The `iss53` branch has moved forward with your work
نمودار 20. The iss53 branch has moved forward with your work

حالا تماس می‌گیرید که مشکلی در وب‌سایت وجود دارد و باید فوراً آن را اصلاح کنید. با Git، لازم نیست اصلاح خود را همراه با تغییرات iss53 که انجام داده‌اید منتشر کنید و مجبور نیستید برای بازگرداندن آن تغییرات تلاش زیادی بکنید تا بتوانید اصلاح خود را روی نسخه‌ی تولید اعمال کنید. تنها کاری که باید انجام دهید این است که به شاخه‌ی master برگردید.

با این حال، قبل از این کار توجه کنید که اگر دایرکتوری کاری یا ناحیه‌ی staging شما تغییرات ذخیره‌نشده‌ای دارد که با شاخه‌ی مقصد تعارض دارد، Git اجازه نمی‌دهد شاخه را عوض کنید. بهتر است هنگام تعویض شاخه‌ها، وضعیت کاری شما تمیز باشد. راه‌هایی برای رفع این مشکل وجود دارد (مانند stash کردن و اصلاح کامیت‌ها) که بعداً در ذخیره موقت و پاک‌سازی (Stashing and Cleaning) بررسی خواهیم کرد. فعلاً فرض کنیم همه تغییرات خود را کامیت کرده‌اید، پس می‌توانید به شاخه‌ی master برگردید:

$ git checkout master
Switched to branch 'master'

در این مرحله، دایرکتوری کاری پروژه دقیقاً همان‌طور است که قبل از شروع کار روی مسئله‌ی شماره ۵۳ بود و می‌توانید روی اصلاح فوری تمرکز کنید. این نکته مهمی است که به یاد داشته باشید: وقتی شاخه را عوض می‌کنید، Git دایرکتوری کاری شما را به حالت آخرین کامیتی که روی آن شاخه انجام شده بازنشانی می‌کند. Git به طور خودکار فایل‌ها را اضافه، حذف یا تغییر می‌دهد تا نسخه کاری شما همان چیزی باشد که شاخه در آخرین کامیت خود داشت.

حالا باید اصلاح فوری انجام دهید. بیایید یک شاخه‌ی hotfix ایجاد کنیم تا روی آن کار کنیم تا وقتی کامل شد:

$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'Fix broken email address'
[hotfix 1fb7853] Fix broken email address
 1 file changed, 2 insertions(+)
Hotfix branch based on `master`
نمودار 21. Hotfix branch based on master

می‌توانید تست‌های خود را اجرا کنید، مطمئن شوید اصلاح به درستی انجام شده، و در نهایت شاخه‌ی hotfix را به شاخه‌ی master ادغام کنید تا در تولید منتشر شود. این کار را با دستور git merge انجام می‌دهید:

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)

در آن ادغام، عبارت “fast-forward” را مشاهده خواهید کرد. چون کامیت C4 که شاخه‌ی hotfix به آن اشاره دارد مستقیماً جلوتر از کامیت C2 است که شما روی آن هستید، Git فقط اشاره‌گر را به جلو می‌برد. به عبارت دیگر، وقتی می‌خواهید یک کامیت را با کامیتی ادغام کنید که از طریق تاریخچه‌ی کامیت اول قابل دسترسی است، Git کار را ساده می‌کند و اشاره‌گر را به جلو می‌برد چون کاری متناقض برای ادغام وجود ندارد — این حالت را “fast-forward” می‌نامند.

تغییرات شما اکنون در تصویر لحظه‌ای کامیتی است که شاخه‌ی master به آن اشاره دارد و می‌توانید اصلاح را منتشر کنید.

`master` is fast-forwarded to `hotfix`
نمودار 22. master is fast-forwarded to hotfix

پس از اینکه اصلاح بسیار مهم شما منتشر شد، آماده‌اید به کاری که پیش از وقفه انجام می‌دادید بازگردید. اما ابتدا شاخه‌ی hotfix را حذف خواهید کرد، چون دیگر به آن نیاز ندارید — شاخه‌ی master به همان نقطه اشاره می‌کند. می‌توانید با گزینه‌ی -d به دستور git branch این کار را انجام دهید:

$ git branch -d hotfix
Deleted branch hotfix (3a0874c).

حالا می‌توانید به شاخه‌ی در حال پیشرفت خود روی مسئله‌ی شماره #53 بازگردید و به کار ادامه دهید.

$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'Finish the new footer [issue 53]'
[iss53 ad82d7a] Finish the new footer [issue 53]
1 file changed, 1 insertion(+)
Work continues on `iss53`
نمودار 23. Work continues on iss53

اینجا لازم است اشاره کنیم که کاری که در شاخه‌ی hotfix انجام داده‌اید در فایل‌های شاخه‌ی iss53 وجود ندارد. اگر نیاز دارید آن را وارد کنید، می‌توانید شاخه‌ی master را به iss53 ادغام کنید با اجرای دستور git merge master، یا می‌توانید صبر کنید تا این تغییرات را زمانی که تصمیم گرفتید شاخه‌ی iss53 را به master برگردانید، ادغام کنید.

ادغام پایه‌ای (Basic Merging)

فرض کنید تصمیم گرفته‌اید کار روی مسئله‌ی شماره ۵۳ کامل شده و آماده‌ی ادغام به شاخه‌ی master است. برای انجام این کار، شاخه‌ی iss53 را به master ادغام می‌کنید، مثل کاری که قبلاً با شاخه‌ی hotfix انجام دادید. کافی است ابتدا شاخه‌ای که می‌خواهید ادغام را در آن انجام دهید چک‌اوت کنید و سپس دستور git merge را اجرا کنید:

$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html |    1 +
1 file changed, 1 insertion(+)

این کمی متفاوت از ادغام hotfix است که قبلاً انجام دادید. در این حالت، تاریخچه‌ی توسعه شما از نقطه‌ای قدیمی‌تر منشعب شده است. چون کامیتی که روی شاخه‌ی فعلی هستید، جد مستقیم شاخه‌ای که می‌خواهید ادغام کنید نیست، Git باید کار بیشتری انجام دهد. در این حالت، Git ادغام سه‌طرفه ساده‌ای انجام می‌دهد که از دو تصویر لحظه‌ای شاخه‌ها و جد مشترک آن‌ها استفاده می‌کند.

Three snapshots used in a typical merge
نمودار 24. Three snapshots used in a typical merge

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

A merge commit
نمودار 25. A merge commit

حالا که کار شما ادغام شده، دیگر نیازی به شاخه‌ی iss53 ندارید. می‌توانید مسئله را در سیستم پیگیری اشکال ببندید و شاخه را حذف کنید:

$ git branch -d iss53

مرج کانفیلیکت پایه (Basic Merge Conflicts)

گاهی اوقات این روند به‌صورت روان پیش نمی‌رود. اگر شما بخش یکسانی از یک فایل را به شکل متفاوتی در دو شاخه‌ای که می‌خواهید ادغام کنید تغییر داده باشید، گیت نمی‌تواند آنها را به‌صورت تمیز ادغام کند. اگر اصلاح شما برای مسئله شماره ۵۳ همان بخش از فایل را که شاخه hotfix تغییر داده بود، دستکاری کرده باشد، با تعارض ادغام مواجه می‌شوید که چیزی شبیه به این خواهد بود:

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

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

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

هر چیزی که تعارض ادغام دارد و هنوز حل نشده باشد، به‌عنوان «ادغام‌نشده» فهرست می‌شود. گیت نشانگرهای استاندارد حل تعارض را به فایل‌هایی که تعارض دارند اضافه می‌کند تا بتوانید آنها را به‌صورت دستی باز کرده و تعارض‌ها را رفع کنید. فایل شما بخشی دارد که چیزی شبیه به این است:

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

این یعنی نسخه در HEAD (شاخه‌ی master شما، چون وقتی دستور ادغام را اجرا کردید آن شاخه فعال بوده است) در بخش بالایی این بلاک قرار دارد (همه چیز بالای خط =======)، در حالی که نسخه شاخه‌ی iss53 شما همه چیز زیر این خط است. برای حل تعارض، باید یا یکی از دو طرف را انتخاب کنید یا خودتان محتوای هر دو را با هم ادغام نمایید. مثلاً ممکن است این تعارض را با جایگزینی کل بلاک با این محتوا حل کنید:

<div id="footer">
please contact us at email.support@github.com
</div>

این راه‌حل ترکیبی از هر دو بخش است و خطوط <<<<<<<، ======= و >>>>>>> کاملاً حذف شده‌اند. بعد از اینکه هر یک از این بخش‌ها را در هر فایل دارای تعارض حل کردید، با دستور git add هر فایل را علامت‌گذاری کنید تا به گیت نشان دهید حل شده است. اضافه کردن فایل به منطقه staging یعنی حل شدن آن در گیت ثبت شده است.

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

$ git mergetool

This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html

Normal merge conflict for 'index.html':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (opendiff):

اگر می‌خواهید از ابزار ادغام غیر از پیش‌فرض استفاده کنید (گیت در این مورد چون روی macOS اجرا شده، opendiff را انتخاب کرده است)، می‌توانید همه ابزارهای پشتیبانی‌شده را در ابتدای لیست «یکی از ابزارهای زیر» ببینید. فقط کافی است نام ابزار مورد نظر خود را تایپ کنید.

یادداشت

اگر نیاز به ابزارهای پیشرفته‌تر برای حل تعارضات پیچیده دارید، در بخش ادغام پیشرفته (Advanced Merging) بیشتر درباره ادغام پیشرفته توضیح داده شده است.

بعد از خروج از ابزار ادغام، گیت از شما می‌پرسد که آیا ادغام موفقیت‌آمیز بوده است یا نه. اگر بگویید بله، فایل به‌صورت خودکار به مرحله staging منتقل شده و به عنوان حل‌شده ثبت می‌شود. می‌توانید دوباره دستور git status را اجرا کنید تا مطمئن شوید همه تعارض‌ها حل شده‌اند:

$ git status
On branch master
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

    modified:   index.html

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

Merge branch 'iss53'

Conflicts:
    index.html
#
# 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:   index.html
#

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

scroll-to-top