Git
Chapters ▾ 2nd Edition

5.3 گیت توزیع‌شده - نگهداری یک پروژه

نگهداری یک پروژه

علاوه بر دانستن چگونگی مشارکت مؤثر در یک پروژه، شما احتمالاً می‌بایست بدانید که چگونه یک پروژه را نگهداری (Maintain) کنید. این فعل می‌تواند شامل قبول و اعمال پچ‌های تولید شده توسط format-patch و پیچ‌های ایمیل‌شده به شما یا یکپارچه‌سازی تغییرات در برنچ‌های ریموت برای مخازنی که به عنوان ریموت به پروژه‌تان اضافه کرده‌اید باشد. در صورتی که یک مخزن استاندارد را نگهداری می‌کنید یا می‌خواهید با تأیید و احراز پچ‌ها کمک کنید، لازم است بدانید که چگونه به واضح‌ترین روش ممکن در نظر دیگر مشارکت‌کنندگان کار را قبول کنید که که در دراز مدت نیز قابل مدیریت باشد.

کار در شاخه‌های موضوعی

وقتی به یکپارچه‌سازی کار جدید فکر می‌کنید، به طور کل امتحان کردنش روی یک برنچ موضوعی راه خوبی است — یک برنچ موقت مختص به امتحان آن کار جدید. بدین طریق ویرایش پچ‌های جدید به طور مستقل آسان است و در صورت کار نکردن می‌توانید آنها را تا زمان ثانوی معلق کنید. اگر یک نام ساده برنچ بر اساس کاری که در حال امتحان آن هستید انتخاب کنید، مثلاً ruby_client یا چیزی با محتوای مشابه، در صورتی که بخواهید آنرا ترک کنید و بعداً به آن بازگردید، می‌توانید به سادگی آنرا به خاطر بسپارید. نگهدارندهٔ پروژهٔ گیت سعی در این دارد که این برنچ‌ها را در فضانام‌های مختلف هم قرار دهد — مانند sc/ruby_clinet که sc آن مخفف نام شخصی است که در کار همکاری داشته است. همانطور که به خاطر دارید، می‌توانید از master به این صورت برنچ بسازید:

$ git branch sc/ruby_client master

یا اگر ترجیح می‌دهید به طور آنی به آن انتقال پیدا کنید، می‌توانید از آپشن checkout -b استفاده کنید:

$ git checkout -b sc/ruby_client master

حال آماده‌اید که کاری که در برنچ موضوعی دریافت کرده‌اید را اضافه کنید و به این فکر کنید که می‌خواهید آنرا در برنچ‌های اصلی‌تر ادغام کنید:

اعمال وصله از ایمیل

اگر یک پچ از ایمیل دریافت کرده‌اید، لازم است آنرا در برنچ موضوعی خود تعبیه و یکپارچه‌سازی کنید تا بتوانید آنرا ارزیابی کنید. دو راه برای اعمال یک پچ ایمیل شده وجود دارد: با git apply یا با git am.

اعمال وصله با apply

اگر پچی دریافت کرده‌اید که با git diff یا مدلی از دستور یونیکس diff تولید شده است (اگرچه توصیه نمی‌شود؛ بخش بعد را ببینید)، می‌توانید آنرا با دستور git apply اعمال کنید. با فرض اینکه پچ را در /tmp/patch-ruby-client.patch ذخیره کرده‌اید، می‌توانید آنرا اینگونه اعمال کنید:

$ git apply /tmp/patch-ruby-client.patch

این دستور فایل‌های درون پوشهٔ کاری شما را ویرایش می‌کند. تقریباً معادل اجرای دستور patch -p برای اعمال پچ است، هرچند کمی شکاک‌تر است و تطابیق فازی کمتری را به نسبت دستور پچ قبول می‌کند. همچنین این دستور توانایی اضافه، پاک‌کردن و تغییر نام فایل‌ها را هم دارد اگر در قالب git diff شرح داده شود در حالی که patch این توانایی‌ها را ندارد. در آخر git applly یک «همه را اعمال کن یا همه را لغو کن» است که یا همه چیز یا هیچ‌چیزی را اعمال نمی‌کند؛ در patch می‌توان به صورت جزئی پچ‌فایل‌ها را اعمال کرد که پوشهٔ کاری را به وضعی عجیب در می‌آورد. git apply به طور کل بسیار بیشتر از patch محافظ کارانه است. کامیت جدیدی برای شما نمی‌سازد — پس از اجرای آن باید تغییرات را به طور دستی استیج و کامیت کنید.

شما همچنین می‌توانید از git apply استفاده کنید تا ببینید که آیا قبل از اعمال واقعی پچ، پچ امکان اعمال شدن به صورت تمیز را دارد — می‌توانید git apply --check را با پچ مورد نظر اجرا کنید:

$ git apply --check 0001-see-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply

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

اعمال وصله با am

اگر مشارکت‌کننده یک کاربر گیت است و به اندازه‌ای ماهر بود که از دستور format-patch برای ساختن پچ خود استفاده کند، آنگاه کار شما آسانتر است چرا که این پچ شامل اطلاعات نویسنده و متن کامیت هم می‌باشد. اگر می‌توانید، مشارکت‌کنندگانتان را تشویق کنید تا با استفاده از format-patch به جای diff برایتان پچ بسازند. بهتر است از git apply فقط برای پچ‌های سنتی مانند قبل استفاده کنید.

برای اعمال پچی که با format-patch ساخته شده، از git am استفاده می‌کنید (این دستور am نامیده شده بخاطر اینکه برای «اعمال یک سری پچ ا‌ز صندوق مـ‌ـیل‌ها یا ایمیل‌ها» (from a m‌ailbox) به کار می‌رود). از لحاظ فنی git am ساخته شده تا از یک فایل mbox محتویات را بخواند، که به طور ساده یک قالب متن-خالی (Plain-Text) برای ذخیرهٔ یک یا چند پیغام ایمیل در یک فایل متنی است. این فایل چیزی شبیه این است:

From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] Add limit to log function

Limit log functionality to the first 20

این ابتدای خروجی دستور git format-patch ایست که در بخش قبل ملاحظه نمودید؛ همچنین نمایانگر یک قالب mbox معتبر است. اگر کسی برای شما به درستی git send-email ارسال کرد و شما آنرا به یک قالب mbox دانلود کنید، می‌توانید git am را به آن فایل mbox سوق دهید و دستور شروع به اعمال پچ‌هایی خواهد کرد که می‌بیند. اگر از یک کلاینت ایمیل استفاده می‌کنید که می‌تواند چندین ایمیل را در قالب mbox ذخیره کند، می‌توانید تمام دسته پچ‌ها را درون یک فایل ذخیره کنید و سپس با git am آنها را یکی یکی اعمال کنید.

هرچند اگر کسی برای شما پچ فایلی که با git format-patch ساخته شده است را به یک سیستم تیکت‌زنی یا چیز مشابهی ارسال کرد، می‌توانید آن فایل را بطور محلی ذخیره کنید و فایل ذخیره شده روی دیسک را به git am دهید تا آنرا اعمال کند:

$ git am 0001-limit-log-function.patch
Applying: Add limit to log function

شما می‌توانید ملاحظه کنید که به تمیزی اعمال شد و به طور خودکار کامیت جدید را برای شما ساخت. اطلاعات نویسنده از هدرهای بخش فرستده و تاریخ ایمیل رونوشت و پیغام کامیت از موضوع و بدنهٔ (قبل از خود پچ) ایمیل گرفته می‌شود.

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

$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author:     Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit:     Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700

   Add limit to log function

   Limit log functionality to the first 20

اطلاعات Commit فرد و زمان اعمال پچ را نشان می‌دهد. اطلاعات Author فردی که اصل پچ را ساخته و زمانی که پچ اصلی ساخته شده را نشان می‌دهد.

اما ممکن است که پچ به طور تمیز اعمال نشود. احتمالاً برنچ اصلی شما خیلی دورتر از جایی که پچ ساخته‌شده دوشاخه شده است و یا پچ وابسته به پچ دیگری است که اعمال نشده است.‌ در این حالت عملیات git am شکست می‌خورد و از شما می‌پرسد که می‌خواهید چکار انجام دهید:

$ git am 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

این دستور علامت‌های تداخل را در هر فایلی که با آن مشکل دارد می‌گذارد، بسیار شبیه به عملیات مرج یا ریبیس تداخل دار. شما این مشکل را به روش بسیار مشابهی حل می‌کنید — برای حل تداخل فایل را ویرایش می‌کند، فایل جدید را استیج کرده و بعد git am --resolved را برای ادامه دادن به پچ بعدی اجرا می‌کنید:

$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: See if this helps the gem

اگر از گیت می‌خواهید راه کمی هوشمندانه‌تری را برای حل تداخل پیش گیرد، یک آپشن -3 به آن دهید تا باعث شود گیت سعی کند یک مرج سه طرفه انجام دهد. این آپشن به طور پیش‌فرض فعال نیست و اگر کامیت پچ می‌گوید که کامیت بر مبنای چیزی است که در مخزن شما نیست کار نمی‌کند. اگر آن کامیت را دارید — اگر پچ بر مبنای یک کامیت عمومی بود — پس آپشن -3 غالباً بسیار هوشمندانه‌تر یک پچ تداخل‌آمیز را اعمال می‌کند:

$ git am -3 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.

در این مورد، بدون آپشن -3 پچ به عنوان یک تداخل در نظر گرفته می‌شد. اما از زمانی که آپشن -3 استفاده شد پچ به تمیزی اعمال شد.

اگر تعدادی پچ را از یک mbox اعمال می‌کنید، این امکان را نیز دارید که دستور am را در حالت تعاملی اجرا کنید که با هر پچی که پیدا می‌کند می‌ایستد و از شما می‌پرسد که آیا می‌خواهید آنرا اعمال کنید:

$ git am -3 -i mbox
Commit Body is:
--------------------------
See if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all

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

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

چک‌اوت کردن برنچ‌های ریموت

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

به طور مثال اگر جسیکا برای شما ایمیلی با این مضمون می‌فرست که ویژگی عالی در برنچ ruby-client مخزن خودش دارد، شما می‌توانید آنرا با اضافه کردن ریموت و چک‌اوت کردن آن برنچ به طور محلی امتحان کنید:

$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client

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

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

از دیگر مزایای این روش این است که شما می‌توانید تاریخچهٔ کامیت‌ها را هم بگیرید. اگرچه ممکن است مشکلات مشروع مرجی داشته باشید، که می‌دانید که کار آنها بر اساس چه نقطه‌ای از تاریخچهٔ شماست؛ یک مرج سه طرفه مناسب پیش‌فرض است، جای اینکه مجبور باشید یک -3 ارائه کنید و به این امیدوار باشید که پچ از کامیت عمومی آمده باشد که شما به آن دسترسی دارید.

اگر به طور دائم با فردی کار نمی‌کنید اما باز هم می‌خواهید از این طریق پول کنید، می‌توانید URL مخزن ریموت را به دستور git pull بدهید. این کار یک ساده انجام می‌دهد و URL مرجع ریموت را ذخیره نمی‌کند:

$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
 * branch            HEAD       -> FETCH_HEAD
Merge made by the 'recursive' strategy.

تشخیص تغییرات معرفی شده

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

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

$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Oct 24 09:53:59 2008 -0700

    See if this helps the gem

commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date:   Mon Oct 22 19:38:36 2008 -0700

    Update gemspec to hopefully work better

برای دیدن تغییراتی که هر کامیت ارائه می‌کند، به خاطر داشته باشید که می‌توانید آپشن -p را به git log بدهید و دستور در انتهای هر کامیت دیف آنرا هم اضافه می‌کند.

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

$ git diff master

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

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

به جای این، چیزی که واقعاً می‌خواهید ببینید تغییراتی است که به برنچ موضوعی اضافه شده است — کاری که در صورت مرج برنچ با master معرفی می‌شود. شما این کار را با واداری گیت به مقایسهٔ آخرین کامیت برنچ موضوعی‌تان با اولین والد مشترکی که با برنچ master دارد انجام می‌دهید.

از لحاظ فنی می‌توانید با تشخیص صریح والد مشترک و سپس اجرای دیف با آن والد این کار را به انجام برسانید:

$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db

یا به طور مختصرتر:

$ git diff $(git merge-base contrib master)

هرچند هیچکدام از این دو خیلی عرف نیست، بنابراین گیت مختصری برای انجام همین کار ارائه می‌کند: سینتکس سه نقطه. در متن دستور git diff می‌توانید سه نقطه بعد از برنچ دیگری بگذارید تا یک diff بین آخرین کامیت برنچی که روی آن هستید و والد مشترکش با برنچ دیگری بگیرید:

$ git diff master...contrib

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

تعبیه و یکپارچه‌سازی کار مشارکت‌شده

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

ادغام روندهای کاری

یک روند کاری ساده این است که تمام کار را مستقیماً با master مرج کنید. در این سناریو شما یک برنچ master دارید که شامل کد باثبات شماست. وقتی که کاری در برنچ موضوعی دارید که فکر می‌کنید کامل است یا کاری از کسی دیگر دارید که به شما داده شده و شما آنرا تأیید کرده‌اید، آنرا با برنچ مستر مرج، آن برنچ تازه مرج شدهٔ موضوعی را پاک و این فرآیند را تکرار می‌کنید.

مثلاً اگر مخزنی داریم که در آن دو برنچ کار شده با نام‌های ruby_client و php_client داریم که شبیه تاریخچه با تعدادی برنچ موضوعی. است و ruby_client و سپس php_client را مرج کنیم، تاریخچهٔ شما شبیه بعد مرج یک برنچ موضوعی. خواهد شد.

History with several topic branches.
نمودار 73. تاریخچه با تعدادی برنچ موضوعی.
After a topic branch merge.
نمودار 74. بعد مرج یک برنچ موضوعی.

این احتمالاً ساده‌ترین روند کاری است، اما اگر با پروژه‌های بزرگ‌تر یا باثبات‌تر سروکار دارید و می‌خواهید به آنچه ارائه می‌کنید محتاط باشید، می‌تواند مشکل‌ساز باشد.

اگر پروژهٔ مهم‌تری دارید، ممکن است بخواهید از یک چرخهٔ مرج دوفازی استفاده کنید. در این سناریو شما دو برنچ با قدمت دارید، master و develop که master فقط موقعی بروزرسانی می‌شود که یک نسخه خیلی باثبات تهیه شده و همهٔ کدهای جدید در برنچ develop تعبیه می‌شوند. عموماً شما هردوی این برنچ‌ها را به مخزن عمومی پوش می‌کنید. هر بار که برنچ موضوعی جدیدی برای مرج کردن دارید (قبل از مرج یک برنچ موضوعی.)، آنرا به develop مرج می‌کنید (بعد مرج یک برنچ موضوعی.)؛ سپس هنگامی که یک تگ انتشار داشتید، master را به هرجایی که برنچ موقتاً باثبات develop هست fast-forward می‌کنید (بعد از یک انتشار از پروژه.).

Before a topic branch merge.
نمودار 75. قبل از مرج یک برنچ موضوعی.
After a topic branch merge.
نمودار 76. بعد مرج یک برنچ موضوعی.
After a topic branch release.
نمودار 77. بعد از یک انتشار از پروژه.

از این طریق هنگامی که مخزن پروژهٔ شما را کلون می‌کنند، یا master را برای ساختن آخرین نسخهٔ باثبات چک‌اوت می‌کنند و آنرا به روز نگه می‌دارند و یا برنچ develop را چک‌اوت می‌کنند که شامل محتوای بروز بیشتری است. همچنین می توانید با داشتن یک برنچ integrate که در آن تمام کارها مرج‌شده است این مفهوم را گسترش دهید. پس از این هنگامی که کدبیس شما روی آن برنچ باثبات است و تست‌ها را می‌گذراند، می‌توانید آنرا در برنچ develop بریزید؛ و وقتی که برای مدتی باثبات جلوه کرد برنچ master خود را fast-forward کنید.

روند کاری ادغام-بزرگ

پروژهٔ گیت چهار برنچ باقدمت دارد: master، next، و pu (Proposed Updates، بروزرسانی پیشنهادی) برای کارهای جدید و maint (Maintenance Backports، بک‌پورت‌های نگهداری). وقتی کار جدید توسط مشارکت‌کنندگان معرفی می‌شود، درون برنچ موضوعی در مخزن نگهدارنده، مشابه چیزی که توصیف کردیم، جمع‌آوری می‌شود (مدیریت مجموعه‌ای پیچیده از برنچ‌های موضوعی موازی مشارکت‌شده. را ببینید). در این نقطه موضوعات در پی دریافتن اینکه آیا برای استفاده آماده و امن هستند و یا احتیاج است بیشتر روی آنها کار شود ارزیابی می‌شوند. اگر امن بودند به next مرج می‌شوند و آن برنچ پوش می‌شود تا همه بتوانند موضوعاتی که با هم یکپارچه‌سازی شده‌اند را امتحان کنند.

Managing a complex series of parallel contributed topic branches.
نمودار 78. مدیریت مجموعه‌ای پیچیده از برنچ‌های موضوعی موازی مشارکت‌شده.

اگر موضوعات هنوز احتیاج به کار دارند، به‌جای next در pu مرج می‌شوند. وقتی تعیین شد که کاملاً باثبات هستند، موضوعات در master بازادغام می‌شوند. برنچ‌های next و pu پس از آن از master بازسازی می‌شوند. این به آن معناست که master تقریباً همیشه رو به جلو حرکت می‌کند، next هر از گاهی ریبیس می‌شود و pu حتی بیشتر ریبیس می‌شود:

Merging contributed topic branches into long-term integration branches.
نمودار 79. مرج برنچ‌های موضوعی مشارکت‌شده به برنچ‌های یکپارچه‌سازی باقدمت.

هنگامی که یک برنچ موضوعی بالآخره به master مرج شده‌، از مخزن حذف می‌شود. پروژهٔ گیت علاوه بر این یک برنچ maint دارد که از آخرین انتشار فورک می‌شود تا پچ‌های بک‌پورت را ارائه کند، در حالتی که انتشار نگهدارنده‌ای لازم باشد. بنابراین وقتی مخزن گیت را کلون می‌کنید چهار برنچ دارید که می‌توانید برای ارزیابی پروژه در مراحل مختلف توسعه، با توجه به اینکه چقدر می‌خواهید بروز باشید یا چگونه می‌خواهید مشارکت کنید، چک‌اوت کنید؛ و نگهدارنده یک روند کاری ساختاریافته برای کمک به تیمار مشارکت‌های جدید دارد. روند کاری پروژهٔ گیت تخصصی‌شده است. برای درک بهتر این موضوع می‌توانید به راهنمای نگهدارندهٔ گیت مراجعه کنید.

روند کاری ریبیس و چری-پیک

دیگر نگهدارنده‌ها ترجیح می‌دهند که کار مشارکت‌شده را بجای مرج به نوک برنچ master خود ریبیس یا چری-پیک کنند تا بیشتر تاریخچه را خطی نگه‌دارند. هنگامی که کار خود را در یک برنچ موضوعی دارید و تصمیم گرفته‌اید که آنرا یکپارچه کنید، به آن برنچ می‌روید و دستور ریبیس را برای بازسازی تغییرات روی نوک برنچ master (یا develop و امثالهم) اعمال می‌کنید. اگر درست کار کرد، می‌توانید برنچ master خود را fast-forward کنید و به یک تاریخچهٔ خطی برخواهید خورد.

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

Example history before a cherry-pick.
نمودار 80. مثال تاریخچه‌ای قبل از چری-پیک‌کردن.

اگر می‌خواهید تغییرات کامیت e43a6 را به برنچ master خود پول کنید می‌توانید دستور زیر را اجرا کنید:

$ git cherry-pick e43a6
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)

این کار همان تغییراتی را که در e43a6 معرفی شده‌اند را پول می‌کند، لکن یک کامیت جدید با یک مقدار SHA-1 جدید خواهید گرفت چرا که تاریخ اعمال شدن آن متفاوت است. حال تاریخچهٔ شما شبیه به این است:

History after cherry-picking a commit on a topic branch.
نمودار 81. تاریخچه پس از چری-پیک کردن یک کامیت از یک برنچ موضوعی.

حال می‌توانید برنچ موضوعی را پاک کنید و کامیت‌هایی را که نمی‌خواستید پول کنید پاک کنید.

ررره

اگر کلی ریبیس و مرج انجام می‌دهید و یک برنچ موضوعی باقدمت را نگهداری می‌کنید، گیت قابلیتی با نام «rerere» دارد که به شما کمک می‌کند.

ررره مخفف «Reuse Recorded Resolution» — راهی برای مخفف کردن حل کردن دستی تداخلات — است. هنگامی که ررره فعال است، گیت مجموعه‌ای از ایمیج‌های پیش و پس از مرج موفق را نگه می‌دارد و پس از این اگر متوجه شد که تداخلی دقیقاً مشابه چیزی که سابقاً حل کرده‌اید وجود دارد، بدون اینکه سر شما را درد بیاورد، خودش از راه‌حل سابق شما استفاده می‌کند.

این ویژگی با دو بخش می‌آید: گزینه تنظیمات و یک دستور. گزینهٔ تنظیمات آن rerere.enabled است و آنقدر کاربرد دارد که آنرا در تنظیمات جهانی خود قرار دهید:

$ git config --global rerere.enabled true

حال هرگاه که مرجی که تداخلی را حل می‌کند انجام دهید، برای روز مبادا حلال آن در کش ذخیره می‌شود.

اگر لازم است می‌توانید با کش ررره به وسیلهٔ دستور git rerere تعامل داشته باشید. هنگامی که تنها اجرا شود، گیت پایگاه دادهٔ حلال‌هایش را چک می‌کند و سعی می‌کند تطبیقی با هر تداخل مرج فعلی پیدا کرده و آن را حل کند (اگرچه اگر rerere.enabled روی true تنظیم شده باشد این کار به طور خودکار انجام می‌شود). همچنین زیردستوراتی برای مشاهدهٔ آنچه که ذخیره خواهد شد، پاک کردن حلال‌های خاص از کش و پاک کردن کل کش وجود دارد. ررره را با جزئیات بیشتر در Rerere مورد بررسی قرار می‌دهیم.

برچسب زدن انتشاراتتان

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

$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09

اگر تگ‌هایتان را امضا می‌کنید، ممکن است مسئله انتشار کلید PGP عمومی مورد استفاده در تگ‌ها را نیز داشته باشید. نگهدارنده‌های پروژه گیت این مشکل را با اضافه کردن کلید عمومی خود به عنوان یک بلاب به مخزن و سپس اضافه کردن تگی که مستقیماً به آن محتوا اشاره می‌کند حل کرده‌اند. برای انجام این کار می‌توانید با اجرای gpg --list-keys دریابید چه کلیدی را می‌خواهید:

$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid                  Scott Chacon <schacon@gmail.com>
sub   2048g/45D02282 2009-02-09 [expires: 2010-02-09]

سپس می‌توانید مستقیماً کلید را به پایگاه‌داده گیت به واسطهٔ صادر و پایپ کردن آن به git hash-object وارد کنید، که یک بلاب جدید با آن محتوا در گیت می‌سازد و به شما SHA-1 بلاب را می‌دهد.

$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92

حال که محتوای کلید خود را در گیت دارید، می‌توانید یک تگ که با مقدار SHA-1 جدیدی که دستور hash-object به شما داد، مستقیماً به آن اشاره می‌کنید بسازید:

$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

اگر دستور git push --tags را اجرا کنید تگ maintainer-pgp-pub با همه به اشتراک گذاشته می‌شود. اگر شخصی بخواهد که صحت یک تگ را تأیید کند می‌تواند با پول مستقیم بلاب کلید PGP شما از پایگاه‌داده و وارد کردن مستقیم آن به GPG این کار را انجام دهد:

$ git show maintainer-pgp-pub | gpg --import

همچنین افراد می‌توانند که از آن کلید برای تأیید صحت تمام تگ‌هایی که شما امضا کرده‌اید استفاده کنند. همچنین اگر دستورالعمل‌هایی را در پیغام تگ اضافه کنید، git show <tag> به کاربر نهایی دستورات دقیق‌تر دربارهٔ تأیید صحت تگ را نشان می‌دهد.

ساختن یک شمارهٔ بیلد

از آنجایی که گیت شماره‌های افزایشی یکنواختی مانند v123 یا معادل آنرا ندارد تا به هر کامیت اعمال کند، اگر می‌خواهید یک نام خوانا با یک کامیت ارائه شود می‌توانید git describe را روی آن کامیت اجرا کنید. در جواب گیت رشته‌ای محتوی نام جدیدترین تگ‌هایی که پیش از آن کامیت معرفی شده‌اند می‌سازد که به همراه شمارهٔ کامیت از زمان آن تگ و در آخر بخشی از مقدار SHA-1 کامیت توصیف‌شده می‌باشد (با پیشوند «g» به معنی گیت):

$ git describe master
v1.6.2-rc1-20-g8c5b85c

از این طریق می‌توانید یک اسنپ‌شات یا بیلد صادر کنید و آنرا با نامی قابل فهم برای مردم نامگذاری کنید. فی‌الواقع اگر گیت را از سورس کد کلون شده از مخزن گیت بسازید git --version به شما چیزی را مشابه با همین نشان می‌دهد. اگر کامیتی را توصیف (describe) می‌کنید که به طور مستقیم تگ کرده‌اید، صرفاً به شما نام تگ را می‌دهد.

به طور پیش‌فرض دستور git describe به تگ‌های توصیف‌شده احتیاج دارد (تگ‌هایی که با فلگ‌های -a یا -s ساخته شده‌اند)؛ اگر می‌خواهید از مزیت‌های تگ‌های سبک (توصیف نشده) هم استفاده کنید، آپشن --tags را نیز به دستور اضافه کنید. همچنین می‌توانید از این رشته برای دستور git checkout یا git show استفاده کنید، هرچند وابسته به مقدار SHA-1 مختصر در آخر خروجی است که ممکن است همیشه کارا نماند. به طور مثال هستهٔ لینوکس از ۸ به ۱۰ حرف پرید تا از یکتا بودن آبجکت‌های SHA-1 مطمئن باشد، بنابراین نام‌های خروجی‌های قدیمی git describe نامعتبر شدند.

آماده‌سازی یک انتشار

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

$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz

اگر شخصی آن تاربال را باز کند، آخرین اسنپ‌شات پروژه شما را زیر یک پوشهٔ project نام پیدا می‌کند. همچنین شما می‌توانید یک آرشیو زیپ‌شده را به شکل مشابهی بسازید، اما با فرستادن آپشن --format=zip به git archive:

$ git archive master --prefix='project/' --format=zip > `git describe master`.zip

حال می‌توانید یک زیپ و تاربال زیبا از انتشار پروژهٔ خود داشته باشید تا بتوانید آنرا روی وبسایت خود آپلود کرده یا به دیگران ایمیل کنید.

شورت‌لاگ

وقت این است که به لیست ایمیل افرادی که می‌خواهند بدانند چه اتفاقی در پروژه افتاده ایمیل بزنید. یک راه مفید سریع برای گرفتن نوعی لاگ تغییراتی که زمان آخرین انتشار یا ایمیل به پروژه شما اضافه شده استفاده از دستور git shortlog است. این دستور تمام کامیت‌های بردی را که به آن داده‌اید را خلاصه می‌کند؛‌ به طور مثال، اگر انتشار قبلی شما v1.0.1 نام داشت، دستور زیر به شما خلاصه‌ای از تمام کامیت‌هایی که از انتشار قبل داشته‌اید را می‌دهد:

$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (6):
      Add support for annotated tags to Grit::Tag
      Add packed-refs annotated tag support.
      Add Grit::Commit#to_patch
      Update version and History.txt
      Remove stray `puts`
      Make ls_tree ignore nils

Tom Preston-Werner (4):
      fix dates in history
      dynamic version method
      Version bump to 1.0.2
      Regenerated gemspec for version 1.0.2

خلاصه‌ای از تمام کامیت‌هایی را که می‌توانید به لیستتان ایمیل بزنید، از v1.0.1 به بعد با دسته‌بندی بر اساس نویسنده، می‌گیرید.

scroll-to-top