Git
Chapters ▾ 2nd Edition

3.2 شاخه‌سازی در گیت - مقدمات برنچ‌سازی و مرج‌کردن

مقدمات برنچ‌سازی و مرج‌کردن

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

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

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

  3. کمی پرداختن به کارهای آن برنچ

در این وهله شما تماسی از تماسی دریافت می‌کنید که یک مورد بحرانی پیش آمده و باید یک راه حل سریع (هات‌فیکس) برای آن آماده کنید. شما کارهای زیر را انجام خواهید داد:

  1. تعویض برنچ به برنچ پروداکشن (برنچ احتمالی که نسخه‌های با ثبات و کاری را در خود نگه می‌دارد)

  2. ساختن یک برنچ برای اضافه کردن هات‌فیکس به پروژه

  3. پس از اینکه تست شد برنچ هات‌فیکس را مرج می‌کنید و تغییرات را به برنچ پروداکشن پوش می‌کنید.

  4. به یوزراستوری قبلی که روی آن کار می‌کردید باز می‌گردید و ادامهٔ کارتان را انجام می‌دهید.

مقدمات برنچ‌سازی

در ابتدا فرض کنیم که شما روی پروژه‌ای کار می‌کنید و از قبل تعدادی کامیت روی برنچ master دارید.

A simple commit history.
Figure 17. یک تاریخچهٔ کامیت ساده

شما تصمیم گرفته‌اید که روی ایشو #53 در سیستم پیگیری-مشکلی (Issue-Tracking System) که کمپانی شما از آن استفاده می‌کند کار کنید. برای ساختن یک برنچ جدید و تعویض برنچ در آن واحد می‌توانید دستور git checkout را با کلید -b اجرا کنید:

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

این دستور جایگزین کوتاهی برای خطوط زیر است:

$ git branch iss53
$ git checkout iss53
Creating a new branch pointer.
Figure 18. ساختن یک نشانگر برنچ جدید

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

$ vim index.html
$ git commit -a -m 'Create new footer [issue 53]'
The `iss53` branch has moved forward with your work.
Figure 19. برنچ iss53 با کار شما به جلو رفته است

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

اگرچه قبل از اینکه آن کار را انجام دهید باید دقت کنید که اگر پوشه کاری یا استیج شما تغییرات کامیت‌نشده‌ای دارد که باعث ایجاد تداخل (Conflict) در برنچی که می‌خواهید به آن چک‌اوت کنید می‌شود، گیت به شما اجازهٔ تعویض برنچ نمی‌دهد. همیشه بهتر است قبل از تعویض برنچ پوشه کاری را تمیز کنید. راه‌های متفاوتی برای انجام این تمیزکاری وجود دارد (من جمله استش (Stash) و اصلاح (Amend) کامیت) که در Stashing and Cleaning به آن خواهیم پرداخت. فعلاً بیاید فرض کنیم که تغییرات را همه کامیت کرده‌اید و می‌توانید به برنچ master خودت انتقال پیدا کنید:

$ git checkout master
Switched to branch 'master'

تا اینجای کار پوشه کاری پروژهٔ شما دقیقاً مانند قبل از زمانی است که شروع به کار روی ایشو #53 کردید و می‌توانید روی هات‌فیکس خود تمرکز کنید. این نکته مهمی برای به خاطر سپردن است: هنگامی که برنچ را تعویض می‌کنید، گیت پوشه کاری شما را بازنشانی می‌کند تا دقیقاً مشابه زمانی شود که اولین بار کامیت روی آن برنچ را ساختید. گیت فایل‌ها را به طور خودکار فایل‌ها را کم زیاد یا ویرایش می‌کند تا مطمئن شود که کپی فعال شما عیناً مطابق زمانی است که آخرین کامیت را روی برنچ ثبت کرده‌اید.

در ادامه شما باید به هات‌فیکس خود بپردازید. بیاید یک برنچ 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`.
Figure 20. برنچ هات‌فیکس در ادامهٔ 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 است که شما هم روی آن کار می‌کنید. به جای انجام کار اضافی گیت به سادگی نشانگر را به جلو هل می‌دهد. به بیان دیگر وقتی سعی می‌کنید یک کامیت را با کایمت دیگری که می‌توان از طریق یپمودن مسیر کامیت اول به آن رسید، مرج کنید، گیت نشانگر را به جلو می‌کشد چون دوشاخگی در مسیر وجود ندارد که گیت بخواهد آنرا ادغام کند — به این کار “fast-forward” (فست-فوروارد) می‌گویند.

اکنون تغییرات شما در اسنپ‌شات کامیتی که برنچ master به آن اشاره می‌کند موجود است و شما می‌توانید فیکس خود را ارائه کنید.

`master` is fast-forwarded to `hotfix`.
Figure 21. master به 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`.
Figure 22. کار روی iss53 ادامه پیدا خواهد کرد

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

مرج‌کردن مقدماتی

فرض کنید به این نتیجه رسیده‌اید که کار روی ایشوی #53 تمام شده و آماده است تا با برنچ 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 که قبلاً انجام داده‌اید تفاوت می‌کند. در این مورد، تاریخچهٔ توسعهٔ شما از یک نقطه به آنور دوشاخه شده بود. چرا که کامیتی که روی برنچ کاری شماست، در مسیر مسقتیم برنچی که با آن ادغام می‌شود نیست و گیت باید در این رابطه کاری کند. در این شرایط گیت یک مرج سه طرفه، با استفاده از دو اسنپ‌شاتی که نوک برنچ‌ها به آنها اشاره می‌کنند و یک والد مشترک از دو برنچ انجام می‌دهد.

Three snapshots used in a typical merge.
Figure 23. در یک مرج معمولی سه اسنپ‌شات استفاده می‌شود

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

A merge commit.
Figure 24. یک مرج کامیت

توجه کنید که کار شما ادغام شده است و شما دیگر نیازی به برنچ iss53 ندارید. شما می‌توانید ایشو را در سیستم پیگیری-مشکلتان ببندید و برنچ را پاک کنید:

$ git branch -d iss53

تداخلات مرج پایه

هر از گاهی این فرآیند به این آسانی انجام نمی‌پذیرد. اگر دو تغییر متفاوت در یک بخش از یک فایل در دو برنچی که در حال ادغامشان هستید ایجاد کردید، گیت قادر نخواهد بود که بی‌نقص آنها را مرج کند. اگر فیکس ایشو #53 شما همان بخشی را از یک فایل ویرایش کرده که در برنچ hotfix هم ویرایش شده، شما یک تداخل (Conflict) مرج خواهید دید که چیزی شبیه زیر خواهد بود:

$ 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")

هر چیزی که تداخل دارد و حل نشده مانده باشد به عنوان «مرج‌نشده» (Unmerged) لیست می‌شود. گیت چند نشاندهٔ استاندارد حل-تداخل را به فایل‌هایی که در تداخل پیدا کرده‌اند اضافه می‌کند تا شما بتوانید آن‌ها را باز کرده و حل کنید. فایل شما بخشی خواهد داشت که شبیه زیر خواهد بود:

<<<<<<< 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 را روی هر فایل اجرا کنید تا آنرا به عنوان حل شده علامت‌گذاری کنید. استیج‌کردن فایل آنرا به عنوان حل شده در گیت علامت‌گذاری می‌کند.

اگر می‌خواهید تا از ابزاری گرافیکی برای حل این مسائل استفاده کنید، می‌توانید 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):

اگر می‌خواهید از ابزار مرجی به جز ابزار پیش‌فرض استفاده کنید (در این مورد گیت opendiff را انتخاب کرده چرا که دستور روی یک مک اجرا شده)، می‌توانید لیست تمام ابزارهای پشتیبانی شده را در ابتدا پس از “one of the following tools” مشاهده کنید. کافیست نام ابزاری که ترجیح می‌دهید استفاده کنید وارد کنید:

Note

اگر به ابزار پیشرفته‌تری برای حل تداخل‌های خاص دارید، ما به بحث مرج‌کردن در Advanced Merging بیشتر می‌پردازیم.

پس از اینکه ابزار مرج را بستید، گیت از شما می‌پرسد که آیا مرج موفقیت‌آمیز بود. اگر به اسکریپت بگویید که موفقیت‌آمیز بود، فایل‌ها را استیج می‌کند تا آنها را برای شما به عنوان حل شده علامت زده باشد. شما می‌توانید با اجرای دوبارهٔ 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

اگر از این خروجی راضی هستید و تصدیق می‌کنید که همه چیز استیج شده است می‌توانید 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
#

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