-
1. شروع به کار (getting started)
-
2. مقدمات گیت (git basics chapter)
- 2.1 گرفتن یک مخزن گیت (Getting a Git Repository)
- 2.2 ثبت تغییرات در مخزن (Recording Changes to the Repository)
- 2.3 مشاهده تاریخچه کامیتها (Viewing the Commit History)
- 2.4 بازگرداندن تغییرات (Undoing Things)
- 2.5 کار کردن با ریموت ها (Working with Remotes)
- 2.6 تگ کردن (Tagging)
- 2.7 نام مستعار گیت (Git Aliases)
- 2.8 خلاصه (summary)
-
3. انشعابگیری در گیت (Git Branching)
-
4. گیت روی سرور (Git on the server)
- 4.1 پروتکلها (The Protocols)
- 4.2 راهاندازی گیت روی یک سرور (Getting Git on a Server)
- 4.3 ایجاد کلید عمومی SSH شما (Generating Your SSH Public Key)
- 4.4 نصب و راهاندازی سرور (Setting up server)
- 4.5 سرویسدهنده گیت (Git Daemon)
- 4.6 HTTP هوشمند (Smart HTTP)
- 4.7 گیتوب (GitWeb)
- 4.8 گیتلب (GitLab)
- 4.9 گزینههای میزبانی شخص ثالث (Third Party Hosted Options)
- 4.10 خلاصه (Summary)
-
5. گیت توزیعشده (Distributed git)
-
6. GitHub (گیت هاب)
-
7. ابزارهای گیت (Git Tools)
- 7.1 انتخاب بازبینی (Revision Selection)
- 7.2 مرحلهبندی تعاملی (Interactive Staging)
- 7.3 ذخیره موقت و پاکسازی (Stashing and Cleaning)
- 7.4 Signing Your Work (امضای کارهای شما)
- 7.5 جستجو (Searching)
- 7.6 بازنویسی تاریخچه (Rewriting History)
- 7.7 بازنشانی به زبان ساده (Reset Demystified)
- 7.8 ادغام پیشرفته (Advanced Merging)
- 7.9 بازاستفاده خودکار از حل تضادها (Rerere)
- 7.10 اشکالزدایی با گیت (Debugging with Git)
- 7.11 سابماژول ها (Submodules)
- 7.12 بستهبندی (Bundling)
- 7.13 جایگزینی (Replace)
- 7.14 ذخیرهسازی اطلاعات ورود (Credential Storage)
- 7.15 خلاصه (Summary)
-
8. سفارشیسازی Git (Customizing Git)
-
9. گیت و سیستمهای دیگر (Git and Other Systems)
-
10. (Git Internals)
- 10.1 ابزارها و دستورات سطح پایین (Plumbing and Porcelain)
- 10.2 اشیا گیت (Git Objects)
- 10.3 مراجع گیت (Git References)
- 10.4 فایلهای بسته (Packfiles)
- 10.5 نگاشت (The Refspec)
- 10.6 پروتکلهای انتقال (Transfer Protocols)
- 10.7 نگهداری و بازیابی دادهها (Maintenance and Data Recovery)
- 10.8 متغیرهای محیطی (Environment Variables)
- 10.9 (Summary)
-
A1. پیوست A: گیت در محیطهای دیگر (Git in Other Environments)
- A1.1 رابط های گرافیکی (Graphical Interfaces)
- A1.2 Git در ویژوال استودیو (Git in Visual Studio)
- A1.3 Git در Visual Studio Code (Git in Visual Studio Code)
- A1.4 Git در IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine (Git in IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine)
- A1.5 Git در Sublime Text (Git in Sublime Text)
- A1.6 گیت در بش (Git in Bash)
- A1.7 Git در Zsh (Git in Zsh)
- A1.8 Git در PowerShell (Git in PowerShell)
- A1.9 خلاصه (Summary)
-
A2. پیوست B: گنجاندن گیت در برنامههای شما (Embedding Git in your Applications)
-
A3. پیوست C: دستورات گیت (Git Commands)
- A3.1 تنظیم و پیکربندی (Setup and Config)
- A3.2 گرفتن و ایجاد پروژهها (Getting and Creating Projects)
- A3.3 نمونهبرداری پایهای (Basic Snapshotting)
- A3.4 انشعابگیری و ادغام (Branching and Merging)
- A3.5 بهاشتراکگذاری و بهروزرسانی پروژهها (Sharing and Updating Projects)
- A3.6 بازرسی و مقایسه (Inspection and Comparison)
- A3.7 عیبیابی (Debugging)
- A3.8 اعمال تغییرات به صورت پچ (Patching)
- A3.9 ایمیل (Email)
- A3.10 سیستمهای خارجی (External Systems)
- A3.11 مدیریت (Administration)
- A3.12 دستورات سطح پایین گیت (Plumbing Commands)
5.2 گیت توزیعشده (Distributed git) - مشارکت در یک پروژه (Contributing to a Project)
مشارکت در یک پروژه (Contributing to a Project)
اصلیترین دشواری در توصیف نحوه مشارکت در یک پروژه، تنوع بسیار زیاد روشهای انجام این کار است. از آنجا که گیت بسیار منعطف است، افراد میتوانند به روشهای مختلفی با هم کار کنند و توصیف اینکه شما دقیقاً چگونه باید مشارکت کنید مشکل است — هر پروژه کمی متفاوت است. برخی از متغیرهای دخیل عبارتاند از تعداد مشارکتکنندگان فعال، جریان کاری انتخابشده، دسترسی شما به کامیتها و احتمالاً روش مشارکت خارجی.
اولین متغیر تعداد مشارکتکنندگان فعال است — چند کاربر بهطور فعال کد به این پروژه اضافه میکنند و هر چند وقت یکبار؟ در بسیاری از موارد، شما دو یا سه توسعهدهنده دارید که روزانه چند کامیت دارند، یا احتمالاً کمتر برای پروژههایی که نسبتاً غیرفعال هستند. برای شرکتها یا پروژههای بزرگتر، تعداد توسعهدهندگان ممکن است به هزاران نفر برسد و صدها یا هزاران کامیت روزانه وارد شود. این اهمیت دارد چون با افزایش تعداد توسعهدهندگان، مشکلات بیشتری در اطمینان از اینکه کد شما بهدرستی اعمال میشود یا بهراحتی ادغام میشود، پیش میآید. تغییراتی که ارسال میکنید ممکن است در حین کار شما یا زمانی که تغییراتتان منتظر تایید یا اعمال هستند، توسط کارهای دیگری که ادغام شدهاند، منسوخ یا به شدت خراب شوند. چگونه میتوانید کد خود را همواره بهروز نگه داشته و کامیتهای خود را معتبر نگه دارید؟
متغیر بعدی جریان کاری مورد استفاده در پروژه است. آیا پروژه متمرکز است و هر توسعهدهنده دسترسی نوشتن برابر به شاخه اصلی کد دارد؟ آیا پروژه یک نگهدارنده یا مدیر یکپارچهسازی دارد که همه پچها را بررسی میکند؟ آیا همه پچها بازبینی همتا و تایید میشوند؟ آیا شما در این فرآیند دخیل هستید؟ آیا سیستم فرماندهی وجود دارد و باید ابتدا کار خود را به آنها ارائه دهید؟
متغیر بعدی دسترسی شما به کامیت است. جریان کاری لازم برای مشارکت در پروژه بسیار متفاوت است اگر شما دسترسی نوشتن به پروژه داشته باشید یا نداشته باشید. اگر دسترسی نوشتن ندارید، پروژه ترجیح میدهد چگونه کارهای مشارکتی را بپذیرد؟ آیا اصلاً سیاستی دارد؟ چه مقدار کار را همزمان مشارکت میکنید؟ هر چند وقت یک بار مشارکت میکنید؟
تمام این سوالات میتوانند بر نحوه مؤثر مشارکت شما در پروژه و جریانهای کاری مورد ترجیح یا قابل دسترس برای شما تأثیر بگذارند. ما جنبههایی از هر یک را در مجموعهای از موارد کاربردی بررسی خواهیم کرد، از ساده به پیچیده؛ شما باید بتوانید با استفاده از این مثالها جریانهای کاری خاص مورد نیاز خود را در عمل بسازید.
راهنماییهای کامیت (Commit Guidelines)
قبل از اینکه به موارد کاربردی خاص بپردازیم، یک نکته کوتاه درباره پیامهای کامیت داریم. داشتن یک راهنمای خوب برای ایجاد کامیتها و پایبندی به آن، کار با گیت و همکاری با دیگران را بسیار آسانتر میکند. پروژه گیت یک سند ارائه میدهد که تعدادی نکته خوب برای ایجاد کامیتهای مناسب برای ارسال پچها را بیان میکند — میتوانید آن را در کد منبع گیت در فایل Documentation/SubmittingPatches
بخوانید.
اول اینکه ارسالهای شما نباید هیچ خطای فاصله سفید داشته باشند. گیت راه سادهای برای بررسی این موضوع دارد — قبل از کامیت کردن، دستور git diff --check
را اجرا کنید که خطاهای احتمالی فاصله سفید را شناسایی و فهرست میکند.

git diff --check
اگر قبل از کامیت این دستور را اجرا کنید، میتوانید بفهمید که آیا در شرف کامیت کردن مشکلات فاصله سفید هستید که ممکن است سایر توسعهدهندگان را اذیت کند.
بعد، سعی کنید هر کامیت یک مجموعه تغییرات منطقی و جداگانه باشد. اگر ممکن است، تغییرات خود را قابل هضم کنید — مثلاً کل آخر هفته روی پنج مسئله مختلف کد نزنید و سپس همه را به صورت یک کامیت عظیم در روز دوشنبه ارسال نکنید. حتی اگر در آخر هفته کامیت نکنید، در روز دوشنبه از منطقه آمادهسازی استفاده کنید تا کار خود را حداقل به یک کامیت برای هر مسئله تقسیم کنید، با پیام مفید برای هر کامیت. اگر برخی تغییرات فایل یکسانی را ویرایش میکنند، سعی کنید از git add --patch
برای آمادهسازی جزئی فایلها استفاده کنید (که به تفصیل در مرحلهبندی تعاملی (Interactive Staging) پوشش داده شده است). تصویر پروژه در انتهای شاخه چه یک کامیت انجام دهید یا پنج کامیت، یکسان است، مادامی که همه تغییرات در نهایت اضافه شوند، پس سعی کنید کار بررسی تغییرات را برای توسعهدهندگان دیگر آسانتر کنید.
این روش همچنین برگشت یا حذف یکی از مجموعه تغییرات را در آینده آسانتر میکند. بازنویسی تاریخچه (Rewriting History) چند ترفند مفید گیت برای بازنویسی تاریخچه و آمادهسازی تعاملی فایلها را شرح میدهد — از این ابزارها برای ایجاد یک تاریخچه تمیز و قابل فهم قبل از ارسال کار به دیگران استفاده کنید.
آخرین نکتهای که باید به آن توجه کنید پیام کامیت است. عادت کردن به نوشتن پیامهای کامیت با کیفیت، استفاده و همکاری با گیت را بسیار آسانتر میکند. به طور کلی، پیامهای شما باید با یک خط کوتاه (حدود ۵۰ کاراکتر یا کمتر) که تغییرات را بهطور خلاصه توصیف کند شروع شود، سپس یک خط خالی، و بعد توضیح مفصلتر بیاید. پروژه گیت از شما میخواهد که توضیح مفصل شامل انگیزه شما برای تغییر و مقایسه اجرای آن با رفتار قبلی باشد — این یک راهنمای خوب برای پیروی است. پیام کامیت خود را به صورت امری بنویسید: «Fix bug» نه «Fixed bug» یا «Fixes bug».
در اینجا قالبی است که میتوانید دنبال کنید، که ما کمی آن را از نسخه اصلی در که در ابتدا توسط Tim Pope نوشته شده اقتباس کردهایم:
خلاصهای کوتاه با حرف بزرگ (۵۰ کاراکتر یا کمتر)
متن توضیحی مفصلتر، در صورت نیاز. آن را تقریباً تا ۷۲ کاراکتر در هر خط بپیچید. در برخی زمینهها، خط اول به عنوان موضوع ایمیل در نظر گرفته میشود و بقیه متن به عنوان بدنه. خط خالی جداکننده خلاصه از بدنه ضروری است (مگر اینکه کل بدنه را حذف کنید)؛ ابزارهایی مانند rebase اگر این دو را با هم اجرا کنید شما را گیج میکنند.
پیام کامیت خود را به صورت امری بنویسید: «Fix bug» نه «Fixed bug» یا «Fixes bug». این قرارداد با پیامهای کامیت تولید شده توسط دستورات مثل git merge و git revert همخوانی دارد.
پاراگرافهای بعدی پس از خطوط خالی میآیند.
- نکات گلولهای هم پذیرفته شدهاند
- معمولاً از خط تیره یا ستاره برای گلوله استفاده میشود، به دنبال آن یک فاصله، با خطوط خالی بین آنها، اما قراردادها متفاوت است
- از تورفتگی معلق استفاده کنید
اگر تمام پیامهای کامیت شما از این الگو پیروی کنند، کار برای شما و توسعهدهندگانی که با آنها همکاری میکنید بسیار آسانتر خواهد بود. پروژه Git پیامهای کامیتی بهخوبی قالببندیشدهای دارد—برای دیدن نحوه نمایش تاریخچه پروژه با قالببندی مناسب، دستور `git log --no-merges` را در آن اجرا کنید.
یادداشت
|
Do as we say, not as we do.
برای اختصار، بسیاری از مثالهای این کتاب پیامهای کامیت به این زیبایی ندارند؛ در عوض، ما صرفاً از گزینه خلاصه اینکه، کاری را انجام دهید که میگوییم، نه کاری را که خودمان انجام میدهیم. |
تیم کوچک خصوصی (Private Small Team)
سادهترین تنظیمی که احتمالاً با آن مواجه میشوید، پروژهای خصوصی با یک یا دو توسعهدهنده دیگر است. در اینجا منظور از «خصوصی» یعنی کد منبع بسته است—برای جهان بیرون قابل دسترسی نیست. شما و دیگر توسعهدهندگان دسترسی برای ارسال تغییرات (push) به مخزن را دارید.
در این محیط، میتوانید روند کاری مشابه آنچه در Subversion یا دیگر سیستمهای متمرکز انجام میدهید را دنبال کنید.
هنوز هم از مزایایی مانند امکان کامیت بهصورت آفلاین و مدیریت شاخهها و ادغامها بهمراتب سادهتر بهرهمند میشوید، اما جریان کاری میتواند بسیار شبیه باشد؛ تفاوت اصلی این است که ادغامها در سمت کلاینت اتفاق میافتند، نه سرور در زمان کامیت.
بیایید ببینیم وقتی دو توسعهدهنده با یک مخزن مشترک شروع به کار میکنند، اوضاع چگونه است.
توسعهدهنده اول، جان، مخزن را کلون میکند، تغییراتی ایجاد میکند و به صورت محلی کامیت میکند.
در این مثالها پیامهای پروتکل با …
جایگزین شدهاند تا کمی کوتاهتر شوند.
# John's Machine
$ git clone john@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'Remove invalid default value'
[master 738ee87] Remove invalid default value
1 files changed, 1 insertions(+), 1 deletions(-)
توسعهدهنده دوم، جسیکا، همین کار را انجام میدهد — مخزن را کلون میکند و یک تغییر را کامیت میکند:
# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'Add reset task'
[master fbff5bc] Add reset task
1 files changed, 1 insertions(+), 0 deletions(-)
حالا جسیکا کارش را به سرور میفرستد (push) و این کار بهخوبی انجام میشود:
# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master
خط آخر خروجی بالا پیام بازگشتی مفیدی از عملیات push را نشان میدهد.
قالب کلی آن به صورت <oldref>..<newref> fromref → toref
است، که در آن oldref
مرجع قدیمی، newref
مرجع جدید، fromref
نام مرجع محلی که ارسال میشود و toref
نام مرجع راه دوری است که بهروزرسانی میشود.
شما در ادامه بحث خروجیهای مشابهی خواهید دید، پس داشتن درک اولیه از معنای آنها به فهم وضعیتهای مختلف مخزن کمک خواهد کرد.
جزئیات بیشتر در مستندات git-push موجود است.
در ادامه همین مثال، کمی بعد، جان تغییراتی ایجاد میکند، آنها را به مخزن محلیاش کامیت میکند و تلاش میکند آنها را به همان سرور ارسال کند:
# John's Machine
$ git push origin master
To john@githost:simplegit.git
! [rejected] master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'
در این حالت، ارسال (push) جان به دلیل ارسال قبلی جسیکا با شکست مواجه میشود. این موضوع بهویژه اگر به Subversion عادت دارید مهم است، چون مشاهده میکنید که دو توسعهدهنده روی فایل مشابهی کار نکردهاند. اگرچه در Subversion، اگر فایلهای متفاوتی ویرایش شوند، ادغام بهصورت خودکار روی سرور انجام میشود، اما در Git ابتدا باید کامیتها را بهصورت محلی ادغام کنید. به عبارت دیگر، جان باید ابتدا تغییرات جسیکا را دریافت (fetch) و آنها را در مخزن محلی خودش ادغام کند، سپس اجازه ارسال خواهد داشت.
اولین قدم این است که جان کار جسیکا را دریافت میکند (این فقط دریافت است و هنوز ادغام انجام نشده):
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master
در این مرحله، مخزن محلی جان چیزی شبیه به این است:

حالا جان میتواند کارهای جسیکا را که دریافت کرده در کار محلی خودش ادغام کند:
$ git merge origin/master
Merge made by the 'recursive' strategy.
TODO | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
تا زمانی که این ادغام محلی بدون مشکل پیش برود، تاریخچه بهروزشده جان به این شکل خواهد بود:

origin/master
در این مرحله، جان ممکن است بخواهد کد جدید را تست کند تا مطمئن شود که کارهای جسیکا تأثیری روی کارهای خودش ندارد و اگر همه چیز خوب بود، نهایتاً میتواند کار ادغامشده جدید را به سرور ارسال کند:
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master
در نهایت، تاریخچه کامیتهای جان به این صورت خواهد بود:

origin
serverدر همین حین، جسیکا یک شاخه موضوعی جدید به نام issue54
ایجاد کرده و سه کامیت به آن شاخه زده است. او هنوز تغییرات جان را دریافت نکرده، بنابراین تاریخچه کامیتهایش به این شکل است:

در همین حین، جسیکا یک شاخه موضوعی جدید به نام issue54
ایجاد کرده و سه کامیت به آن شاخه زده است. او هنوز تغییرات جان را دریافت نکرده، بنابراین تاریخچه کامیتهایش به این شکل است:
# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
fbff5bc..72bbc59 master -> origin/master
این دستور کارهای جان که در این فاصله ارسال شده را به سیستم محلی جسیکا میآورد. اکنون تاریخچه جسیکا به این صورت است:

جسیکا فکر میکند شاخه موضوعیاش آماده است، اما میخواهد بداند کدام بخش از کارهای دریافتشده جان را باید با کار خودش ادغام کند تا بتواند تغییرات را ارسال کند. او دستور git log
را اجرا میکند تا بفهمد:
$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date: Fri May 29 16:01:27 2009 -0700
Remove invalid default value
نحوه نوشتن issue54..origin/master
یک فیلتر لاگ است که از گیت میخواهد فقط آن کامیتهایی را نمایش دهد که روی شاخه دوم (در اینجا origin/master
) هستند و روی شاخه اول (در اینجا issue54
) نیستند. این نحو را در بازههای کامیت (Commit Ranges) به تفصیل بررسی خواهیم کرد.
از خروجی بالا میبینیم که تنها یک کامیت وجود دارد که جان ساخته و جسیکا هنوز آن را با کار محلیاش ادغام نکرده است. اگر او origin/master
را ادغام کند، این تنها کامیتی است که کار محلیاش را تغییر خواهد داد.
حالا، جسیکا میتواند کار موضوعی خود را به شاخه master
ادغام کند، کار جان (origin/master
) را به شاخه master
خودش ادغام کند و سپس دوباره به سرور ارسال کند.
ابتدا (بعد از اینکه تمام کارهای شاخه موضوعی issue54
را کامیت کرده)، جسیکا به شاخه master
بازمیگردد تا برای ادغام همه این کارها آماده شود:
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
جسیکا میتواند ابتدا شاخه origin/master
یا issue54
را ادغام کند — هر دو شاخههای بالادستی هستند، پس ترتیب ادغام مهم نیست. در نهایت تصویر نهایی باید یکسان باشد؛ فقط تاریخچه متفاوت خواهد بود. او تصمیم میگیرد ابتدا شاخه issue54
را ادغام کند:
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
README | 1 +
lib/simplegit.rb | 6 +++++-
2 files changed, 6 insertions(+), 1 deletions(-)
مشکلی پیش نمیآید؛ همانطور که میبینید این یک ادغام ساده و سریع بود. جسیکا اکنون فرایند ادغام محلی را با ادغام کارهای قبلاً دریافتشده جان که در شاخه origin/master
قرار دارد، کامل میکند:
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
همه چیز به شکل تمیز ادغام میشود و تاریخچه جسیکا اکنون به این صورت است:

اکنون شاخه origin/master
از شاخه master
جسیکا قابل دسترسی است، بنابراین او باید بتواند با موفقیت تغییرات را ارسال کند (البته اگر جان در این فاصله تغییرات بیشتری ارسال نکرده باشد):
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master
هر توسعهدهنده چند بار کامیت کرده و کار یکدیگر را با موفقیت ادغام کردهاند.

این یکی از سادهترین روندهای کاری است. شما برای مدتی کار میکنید (معمولاً در یک شاخه موضوعی) و وقتی کار آماده ادغام شد، آن را به شاخه master
خود ادغام میکنید. وقتی میخواهید آن کار را به اشتراک بگذارید، شاخه master
را از origin/master
دریافت و ادغام میکنید اگر تغییر کرده باشد، و در نهایت به شاخه master
روی سرور ارسال میکنید. توالی کلی چیزی شبیه این است:

تیم خصوصی مدیریتشده (Private Managed Team)
در سناریوی بعدی، نقشهای مشارکتکنندگان در یک گروه خصوصی بزرگتر را بررسی میکنید. یاد میگیرید چگونه در محیطی کار کنید که گروههای کوچک روی ویژگیها همکاری میکنند و سپس مشارکتهای تیمی توسط گروه دیگری ادغام میشود.
فرض کنید جان و جسیکا روی یک ویژگی با هم کار میکنند (به آن “featureA” میگوییم)، در حالی که جسیکا و توسعهدهنده سومی به نام جوزی روی ویژگی دوم (مثلاً “featureB”) کار میکنند. در این حالت، شرکت از نوعی روند کاری به نام مدیر ادغام استفاده میکند که کار گروههای فردی تنها توسط مهندسان خاصی ادغام میشود و شاخه master
مخزن اصلی تنها توسط آن مهندسان بهروزرسانی میشود. در این سناریو، همه کارها در شاخههای تیمی انجام میشود و بعداً توسط ادغامکنندگان جمعآوری میشود.
بیایید روند کاری جسیکا را دنبال کنیم که روی دو ویژگی خود کار میکند و به طور موازی با دو توسعهدهنده مختلف در این محیط همکاری دارد. فرض کنیم او قبلاً مخزن خود را کلون کرده است و تصمیم میگیرد ابتدا روی featureA
کار کند. او یک شاخه جدید برای این ویژگی ایجاد میکند و روی آن کار میکند:
# Jessica's Machine
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'Add limit to log function'
[featureA 3300904] Add limit to log function
1 files changed, 1 insertions(+), 1 deletions(-)
در این مرحله، او باید کارش را با جان به اشتراک بگذارد، بنابراین کامیتهای شاخه featureA
را به سرور ارسال میکند. جسیکا دسترسی ارسال به شاخه master
ندارد — فقط ادغامکنندگان این دسترسی را دارند — پس باید به شاخه دیگری ارسال کند تا بتواند با جان همکاری کند:
$ git push -u origin featureA
...
To jessica@githost:simplegit.git
* [new branch] featureA -> featureA
جسیکا به جان ایمیل میزند و به او اطلاع میدهد که کارش را به شاخهای به نام featureA
ارسال کرده و اکنون میتواند آن را ببیند. در حالی که منتظر بازخورد جان است، جسیکا تصمیم میگیرد روی featureB
با جوزی کار کند. برای شروع، یک شاخه ویژگی جدید ایجاد میکند که بر اساس شاخه master
سرور ساخته شده است:
# Jessica's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'
اکنون، جسیکا چند کامیت روی شاخه featureB
انجام میدهد:
$ vim lib/simplegit.rb
$ git commit -am 'Make ls-tree function recursive'
[featureB e5b0fdc] Make ls-tree function recursive
1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'Add ls-files'
[featureB 8512791] Add ls-files
1 files changed, 5 insertions(+), 0 deletions(-)
مخزن جسیکا اکنون به این صورت است:

او آماده است که تغییراتش را به سرور ارسال کند، اما ایمیلی از جوزی دریافت میکند که شاخهای با مقداری کار اولیه روی “featureB” قبلاً به عنوان شاخه featureBee
به سرور فرستاده شده است. جسیکا باید پیش از ارسال تغییراتش به سرور، این تغییرات را با کار خودش ادغام کند. او ابتدا تغییرات جوزی را با دستور git fetch
دریافت میکند:
$ git fetch origin
...
From jessica@githost:simplegit
* [new branch] featureBee -> origin/featureBee
فرض کنیم جسیکا همچنان روی شاخهی featureB
که چکاوت کرده است، قرار دارد. اکنون میتواند با دستور git merge
کار جوزی را به آن شاخه ادغام کند:
$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
lib/simplegit.rb | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
در این مرحله، جسیکا میخواهد تمام کار ادغام شدهی “featureB” را به سرور ارسال کند، اما نمیخواهد صرفاً شاخهی featureB
خود را پوش کند.
در عوض، چون جوزی قبلاً شاخهی بالادستی featureBee
را ایجاد کرده است، جسیکا میخواهد به همان شاخه پوش کند، که این کار را با دستور زیر انجام میدهد:
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
fba9af8..cd685d1 featureB -> featureBee
به این اصطلاحاً refspec گفته میشود.
برای بحث مفصلتر دربارهی refspecهای گیت و کارهای مختلفی که میتوانید با آنها انجام دهید، به نگاشت (The Refspec) مراجعه کنید.
همچنین به پرچم -u
توجه کنید؛ این کوتاهشدهی --set-upstream
است که شاخهها را برای پوشیدن و دریافت آسانتر در آینده تنظیم میکند.
ناگهان، جسیکا ایمیلی از جان دریافت میکند که به او میگوید برخی تغییرات را روی شاخهی featureA
که با هم روی آن همکاری میکنند، پوش کرده است و از جسیکا میخواهد آنها را بررسی کند.
دوباره، جسیکا با اجرای دستور سادهی git fetch
تمام محتوای جدید از سرور، از جمله آخرین کار جان را دریافت میکند:
$ git fetch origin
...
From jessica@githost:simplegit
3300904..aad881d featureA -> origin/featureA
جسیکا میتواند با مقایسه محتوای شاخهی featureA
که تازه دریافت شده با نسخهی محلی خودش از همان شاخه، لاگ کار جدید جان را نمایش دهد:
$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <jsmith@example.com>
Date: Fri May 29 19:57:33 2009 -0700
Increase log output to 30 from 25
اگر جسیکا از آنچه میبیند راضی بود، میتواند کار جدید جان را با دستور زیر به شاخهی محلی featureA
خود ادغام کند:
$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
در نهایت، جسیکا ممکن است بخواهد چند تغییر کوچک در تمام آن محتوای ادغام شده اعمال کند، پس آزاد است تغییرات را انجام دهد، آنها را به شاخهی محلی featureA
خود کامیت کند و نتیجه نهایی را به سرور پوش کند:
$ git commit -am 'Add small tweak to merged content'
[featureA 774b3ed] Add small tweak to merged content
1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
3300904..774b3ed featureA -> featureA
حالا تاریخچهی کامیتهای جسیکا چیزی شبیه به این است:

در مقطعی، جسیکا، جوزی و جان به یکپارچهسازها اطلاع میدهند که شاخههای featureA
و featureBee
روی سرور آمادهی ادغام در شاخهی اصلی هستند.
پس از اینکه یکپارچهسازها این شاخهها را در شاخهی اصلی ادغام کردند، یک fetch جدید، کامیت ادغام را دریافت میکند و تاریخچه به شکل زیر درمیآید:

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

پروژهی عمومی فورک شده (Forked Public Project)
مشارکت در پروژههای عمومی کمی متفاوت است. چون شما اجازهی بهروزرسانی مستقیم شاخهها روی پروژه را ندارید، باید کار خود را به مدیران پروژه به روش دیگری برسانید. این مثال اول، مشارکت از طریق فورک کردن روی میزبانهای گیت که فورک آسان را پشتیبانی میکنند، را شرح میدهد. بسیاری از سایتهای میزبانی این ویژگی را دارند (از جمله GitHub، BitBucket، repo.or.cz و غیره) و بسیاری از مدیران پروژه این سبک مشارکت را انتظار دارند. بخش بعدی به پروژههایی میپردازد که ترجیح میدهند پچهای ارسالی از طریق ایمیل دریافت کنند.
ابتدا احتمالاً میخواهید مخزن اصلی را کلون کنید، یک شاخه موضوعی برای پچ یا سری پچهایی که قصد مشارکت دارید ایجاد کنید و کار خود را در آنجا انجام دهید. دنباله کاری بهطور کلی به این شکل است:
$ git clone <url>
$ cd project
$ git checkout -b featureA
... work ...
$ git commit
... work ...
$ git commit
یادداشت
|
شاید بخواهید از |
وقتی کار روی شاخهتان تمام شد و آماده بودید که آن را به مدیران پروژه بازگردانید، به صفحه اصلی پروژه بروید و روی دکمهی “Fork” کلیک کنید تا فورک قابل نوشتن خودتان از پروژه را بسازید.
سپس باید URL این مخزن را بهعنوان یک ریموت جدید به مخزن محلیتان اضافه کنید؛ در این مثال، فرض کنیم نام آن را myfork
میگذاریم:
$ git remote add myfork <url>
سپس باید کار جدید خود را به این مخزن پوش کنید.
پوش کردن شاخه موضوعی که روی آن کار میکنید به مخزن فورک شدهتان آسانتر است، تا اینکه آن کار را در شاخهی master
خود ادغام کرده و آن را پوش کنید.
دلیل این موضوع این است که اگر کار شما پذیرفته نشود یا بهصورت cherry-pick انتخاب شود، نیازی به عقب بردن شاخهی master
خود ندارید (عملیات cherry-pick
در گیت با جزئیات بیشتر در روندهای کاری بازپایهگذاری و انتخاب گزینشی (Rebasing and Cherry-Picking Workflows) توضیح داده شده).
اگر مدیران پروژه کار شما را merge
، rebase
یا cherry-pick
کنند، در نهایت از طریق pull گرفتن از مخزن آنها دوباره آن را دریافت خواهید کرد.
در هر صورت، میتوانید کار خود را با دستور زیر پوش کنید:
$ git push -u myfork featureA
پس از اینکه کارتان به مخزن فورک شدهتان ارسال شد، باید به نگهدارندگان پروژه اصلی اطلاع دهید که تغییراتی دارید که میخواهید ادغام کنند. این معمولاً به «درخواست کشیدن» (pull request) معروف است و معمولاً این درخواست را یا از طریق وبسایت — گیتهاب مکانیزم مخصوص خودش به نام «Pull Request» دارد که در GitHub (گیت هاب) بررسی خواهیم کرد — یا با اجرای دستور git request-pull
ایجاد میکنید و خروجی آن را به صورت دستی از طریق ایمیل برای نگهدارنده پروژه میفرستید.
دستور git request-pull
شاخه پایهای که میخواهید شاخه موضوعیتان به آن کشیده شود و آدرس مخزن گیت که میخواهید از آن کشیده شود را میگیرد و خلاصهای از همه تغییراتی که درخواست کشیدن آنها را دارید، تولید میکند. برای مثال، اگر جسیکا بخواهد برای جان درخواست کشیدن بفرستد و دو کامیت روی شاخه موضوعی که تازه ارسال کرده انجام داده باشد، میتواند این دستور را اجرا کند:
$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
Jessica Smith (1):
Create new function
are available in the git repository at:
https://githost/simplegit.git featureA
Jessica Smith (2):
Add limit to log function
Increase log output to 30 from 25
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
این خروجی را میتوان برای نگهدارنده فرستاد — این خروجی به آنها میگوید شاخه کار از کجا منشعب شده، کامیتها را خلاصه میکند و مشخص میکند کار جدید از کجا باید کشیده شود.
در پروژهای که شما نگهدارنده آن نیستید، معمولاً راحتتر است که شاخهای مثل master
همیشه شاخه origin/master
را دنبال کند و کارتان را در شاخههای موضوعی انجام دهید که در صورت رد شدن بتوانید به آسانی آنها را حذف کنید. جدا کردن موضوعات کاری در شاخههای جداگانه همچنین کار بازپایهگذاری (rebase) را آسانتر میکند، اگر در این فاصله نوک مخزن اصلی تغییر کرده باشد و کامیتهای شما به صورت تمیز اعمال نشوند. برای مثال، اگر بخواهید موضوع دومی را به پروژه ارائه دهید، روی همان شاخه موضوعی که تازه ارسال کردهاید کار نکنید — از شاخه master
مخزن اصلی شروع کنید:
$ git checkout -b featureB origin/master
... work ...
$ git commit
$ git push myfork featureB
$ git request-pull origin/master myfork
... email generated request pull to maintainer ...
$ git fetch origin
اکنون هر یک از موضوعات کاری شما در یک محفظه جداگانه قرار دارد — مشابه صف پچها — که میتوانید به دلخواه بازنویسی، بازپایهگذاری و اصلاح کنید بدون اینکه موضوعات کاری با هم تداخل یا وابستگی داشته باشند، مانند شکل زیر:

featureB
workفرض کنید نگهدارنده پروژه تعدادی پچ دیگر را دریافت کرده و شاخه اول شما را امتحان کرده، اما دیگر به صورت تمیز ادغام نمیشود. در این حالت میتوانید شاخه را روی origin/master
بازپایهگذاری کرده، تعارضها را برای نگهدارنده حل کنید و سپس تغییراتتان را دوباره ارسال نمایید:
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA
این کار تاریخچه شما را طوری بازنویسی میکند که شبیه Commit history after featureA
work شود.

featureA
workچون شاخه را بازپایهگذاری کردهاید، باید هنگام ارسال تغییرات گزینه -f
را به دستور push اضافه کنید تا بتوانید شاخه featureA
روی سرور را با کامیتی جایگزین کنید که فرزند آن نیست. راه دیگر این است که این کار جدید را به شاخه متفاوتی روی سرور (مثلاً featureAv2
) ارسال کنید.
یک سناریوی دیگر را بررسی کنیم: نگهدارنده پروژه کار روی شاخه دوم شما را دیده و ایده آن را میپسندد، اما میخواهد جزئیات پیادهسازیای را تغییر دهید. همچنین از این فرصت استفاده میکنید تا کار را بر اساس شاخه master
فعلی پروژه بسازید. یک شاخه جدید بر اساس شاخه origin/master
فعلی میسازید، تغییرات featureB
را با گزینه --squash
ادغام میکنید، تعارضها را حل میکنید، تغییر پیادهسازی را انجام میدهید و آن را به عنوان شاخه جدید ارسال میکنید:
$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
... change implementation ...
$ git commit
$ git push myfork featureBv2
گزینه --squash
همه تغییرات شاخه ادغام شده را در یک تغییر واحد جمع میکند و حالت مخزن را به گونهای تولید میکند که انگار ادغام واقعی انجام شده، بدون اینکه کامیت ادغام ساخته شود. این یعنی کامیت بعدی شما فقط یک والد خواهد داشت و به شما امکان میدهد همه تغییرات شاخه دیگر را وارد کرده و سپس قبل از ثبت کامیت جدید، تغییرات بیشتری اعمال کنید. همچنین گزینه --no-commit
میتواند مفید باشد تا ثبت کامیت ادغام را در فرآیند ادغام پیشفرض به تعویق بیندازد.
در این مرحله میتوانید به نگهدارنده اطلاع دهید که تغییرات خواسته شده را انجام دادهاید و آنها میتوانند این تغییرات را در شاخه featureBv2
شما پیدا کنند.

featureBv2
workپروژه عمومی از طریق ایمیل (Public Project over Email)
بسیاری از پروژهها رویههای مشخصی برای پذیرش اصلاحات (پچها) دارند — باید قوانین خاص هر پروژه را بررسی کنید، زیرا این قوانین متفاوت خواهند بود. از آنجا که چندین پروژه قدیمی و بزرگ وجود دارند که اصلاحات را از طریق لیست پستی توسعهدهندگان میپذیرند، اکنون مثالی از این روش را بررسی میکنیم.
روند کار مشابه حالت قبلی است — برای هر سری اصلاحاتی که روی آن کار میکنید، شاخههای موضوعی ایجاد میکنید. تفاوت در نحوه ارسال آنها به پروژه است. به جای فورک کردن پروژه و ارسال تغییرات به نسخه قابل نوشتن خودتان، نسخههای ایمیلی هر سری کامیت را تولید کرده و آنها را به لیست پستی توسعهدهندگان ارسال میکنید:
$ git checkout -b topicA
... work ...
$ git commit
... work ...
$ git commit
حالا شما دو کامیت دارید که میخواهید آنها را به فهرست پستی ارسال کنید.
از دستور git format-patch
استفاده میکنید تا فایلهایی با فرمت mbox تولید کنید که میتوانید آنها را به فهرست پستی ایمیل کنید — این دستور هر کامیت را به یک پیام ایمیل تبدیل میکند که خط اول پیام کامیت موضوع ایمیل است و بقیه پیام به همراه پچی که کامیت ایجاد کرده، در بدنه ایمیل قرار میگیرد.
نکته خوب این است که اعمال پچی که از ایمیلی تولید شده با format-patch
گرفته شده، تمام اطلاعات کامیت را به درستی حفظ میکند.
$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
دستور format-patch
نام فایلهای پچی که ایجاد میکند را نمایش میدهد.
کلید -M
به گیت میگوید که به دنبال تغییر نام فایلها بگردد.
فایلها به این شکل خواهند بود:
$ cat 0001-add-limit-to-log-function.patch
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
---
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
end
def log(treeish = 'master')
- command("git log #{treeish}")
+ command("git log -n 20 #{treeish}")
end
def ls_tree(treeish = 'master')
--
2.1.0
همچنین میتوانید این فایلهای پچ را ویرایش کنید تا اطلاعات بیشتری برای فهرست پستی اضافه کنید که نمیخواهید در پیام کامیت نمایش داده شود.
اگر متنی بین خط ---
و ابتدای پچ (خط diff --git
) اضافه کنید، توسعهدهندگان آن را میخوانند اما این محتوا توسط فرآیند پچ اعمال نادیده گرفته میشود.
برای ارسال این فایل به فهرست پستی، میتوانید فایل را در برنامه ایمیل خود کپی کنید یا از برنامه خط فرمان استفاده کنید.
کپی کردن متن اغلب باعث مشکلات قالببندی میشود، بهویژه در کلاینتهای “هوشمندتر” که خطوط جدید و فاصلههای سفید را به درستی حفظ نمیکنند.
خوشبختانه، گیت ابزاری برای ارسال پچهای قالببندیشده به صورت صحیح از طریق IMAP فراهم کرده که ممکن است برای شما آسانتر باشد.
ما نحوه ارسال پچ از طریق جیمیل را نشان میدهیم که ما آن را بهتر میشناسیم؛ میتوانید دستورالعملهای دقیق برای چند برنامه ایمیل مختلف را در انتهای فایل Documentation/SubmittingPatches
در کد منبع گیت بخوانید.
ابتدا باید بخش imap را در فایل ~/.gitconfig
خود تنظیم کنید.
میتوانید هر مقدار را به صورت جداگانه با چند دستور git config
تنظیم کنید یا آنها را به صورت دستی اضافه کنید، اما در نهایت فایل پیکربندی شما باید چیزی شبیه به این باشد:
[imap]
folder = "[Gmail]/Drafts"
host = imaps://imap.gmail.com
user = user@gmail.com
pass = YX]8g76G_2^sFbd
port = 993
sslverify = false
اگر سرور IMAP شما از SSL استفاده نمیکند، احتمالاً دو خط آخر لازم نیست و مقدار میزبان به جای imaps://
، imap://
خواهد بود.
وقتی این تنظیمات انجام شد، میتوانید از git imap-send
برای قرار دادن سری پچها در پوشه Drafts سرور IMAP مشخصشده استفاده کنید:
$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done
در این مرحله، باید بتوانید به پوشه Drafts خود بروید، فیلد گیرنده (To) را به فهرست پستی که پچ را به آن ارسال میکنید تغییر دهید، احتمالاً نفر مسئول یا نگهدارنده بخش را CC کنید و ایمیل را ارسال نمایید.
همچنین میتوانید پچها را از طریق سرور SMTP ارسال کنید.
مانند قبل، میتوانید هر مقدار را جداگانه با چند دستور git config
تنظیم کنید یا آنها را به صورت دستی در بخش sendemail فایل ~/.gitconfig
اضافه کنید:
[sendemail]
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = user@gmail.com
smtpserverport = 587
پس از انجام این کار، میتوانید از دستور git send-email
برای ارسال پچهای خود استفاده کنید:
$ git send-email *.patch
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <jessica@example.com>]
Emails will be sent from: Jessica Smith <jessica@example.com>
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y
سپس، گیت برای هر پچی که ارسال میکنید، یک سری اطلاعات لاگ مشابه این را نمایش میدهد:
(mbox) Adding cc: Jessica Smith <jessica@example.com> from
\line 'From: Jessica Smith <jessica@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith <jessica@example.com>
To: jessica@example.com
Subject: [PATCH 1/2] Add limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>
Result: OK
نکته
|
برای دریافت کمک در تنظیم سیستم و ایمیل، نکات و ترفندهای بیشتر، و محیط آزمایشی برای ارسال پچ آزمایشی از طریق ایمیل، به https://git-send-email.io مراجعه کنید. |
خلاصه (Summary)
در این بخش، چندین روند کاری را بررسی کردیم و تفاوتهای کار در یک تیم کوچک روی پروژههای کد بسته را با مشارکت در پروژههای بزرگ و عمومی توضیح دادیم. شما یاد گرفتید که قبل از کامیت کردن، خطاهای فاصله سفید را چک کنید و یک پیام کامیت عالی بنویسید. یاد گرفتید چگونه پچها را قالببندی کرده و آنها را به فهرست پستی توسعهدهندگان ایمیل کنید. پرداختن به ادغامها نیز در چارچوب روندهای کاری مختلف پوشش داده شد. اکنون به خوبی آماده همکاری در هر پروژهای هستید.
در ادامه، خواهید دید چگونه سمت دیگر ماجرا را مدیریت کنید: نگهداری یک پروژه گیت. یاد میگیرید چگونه یک دیکتاتور خیرخواه یا مدیر ادغام باشید.