برای درک واقعی نحوهی شاخهبندی در گیت، باید یک قدم به عقب برداریم و بررسی کنیم گیت دادههای خود را چگونه ذخیره میکند.
همانطور که ممکن است از ch01-getting-started.asc به یاد داشته باشید، گیت دادهها را به صورت یک سری تغییرات یا تفاوتها ذخیره نمیکند، بلکه به صورت یک سری تصویر لحظهای ذخیره میکند.
وقتی شما یک کامیت میزنید، گیت یک شیء کامیت ذخیره میکند که شامل اشارهگری به تصویر لحظهای محتوایی است که آماده کردهاید. این شیء همچنین شامل نام و ایمیل نویسنده، پیامی که تایپ کردهاید، و اشارهگرهایی به کامیت یا کامیتهایی است که مستقیماً قبل از این کامیت آمدهاند (والد یا والدین آن): صفر والد برای کامیت اولیه، یک والد برای کامیت معمولی، و چند والد برای کامیتی که نتیجه ادغام دو یا چند شاخه است.
برای تجسم این موضوع، فرض کنید دایرکتوریای دارید که شامل سه فایل است و همه را آماده کرده و کامیت میکنید. آماده کردن فایلها یک مقدار چکسام برای هر فایل محاسبه میکند (هش SHA-1 که در ch01-getting-started.asc ذکر شد)، آن نسخه از فایل را در مخزن گیت ذخیره میکند (گیت به آنها blob میگوید) و آن چکسام را به منطقه آمادهسازی اضافه میکند:
$ git add README test.rb LICENSE
$ git commit -m 'Initial commit'وقتی با اجرای دستور git commit کامیت را ایجاد میکنید، گیت چکسام هر زیرشاخه (در این مورد فقط دایرکتوری اصلی پروژه) را محاسبه کرده و آنها را به شکل یک شیء درختی (tree) در مخزن گیت ذخیره میکند. سپس گیت یک شیء کامیت ایجاد میکند که شامل فراداده و اشارهگری به درخت ریشه پروژه است تا بتواند آن تصویر لحظهای را در صورت نیاز بازسازی کند.
اکنون مخزن گیت شما شامل پنج شیء است: سه blob (هر کدام نمایانگر محتوای یکی از سه فایل)، یک tree که محتوای دایرکتوری را لیست میکند و مشخص میکند کدام نام فایل به کدام blob ذخیره شده است، و یک commit با اشارهگری به آن درخت ریشه و تمام فرادادههای کامیت.
اگر تغییراتی ایجاد کنید و دوباره کامیت بزنید، کامیت بعدی اشارهگری به کامیتی که بلافاصله قبل از آن آمده است ذخیره میکند.
یک شاخه در گیت صرفاً یک اشارهگر سبک و قابل انتقال به یکی از این کامیتها است. نام پیشفرض شاخه در گیت master است. وقتی شروع به ایجاد کامیت میکنید، شاخه master به آخرین کامیتی که زدهاید اشاره دارد. هر بار که کامیت میزنید، اشارهگر شاخه master به طور خودکار جلو میرود.
|
Note
|
شاخه “master” در گیت شاخهای ویژه نیست. این دقیقاً مانند هر شاخه دیگری است. تنها دلیل اینکه تقریباً هر مخزنی یکی دارد این است که دستور |
وقتی یک شاخه جدید ایجاد میکنید چه اتفاقی میافتد؟ خب، این کار یک اشارهگر جدید برای شما ایجاد میکند تا جابجا کنید. فرض کنید میخواهید شاخه جدیدی به نام testing بسازید. این کار را با دستور git branch انجام میدهید:
$ git branch testingاین اشارهگر جدیدی به همان کامیتی که در حال حاضر روی آن هستید ایجاد میکند.
چگونه گیت میداند شما الان روی کدام شاخه هستید؟ گیت یک اشارهگر ویژه به نام HEAD نگه میدارد. توجه داشته باشید که این بسیار متفاوت از مفهوم HEAD در سایر سیستمهای کنترل نسخه مانند Subversion یا CVS است. در گیت، این اشارهگری به شاخه محلی است که در حال حاضر روی آن هستید. در این مورد، شما هنوز روی master هستید. دستور git branch فقط یک شاخه جدید ایجاد کرد — ولی به آن شاخه سوئیچ نکرد.
شما میتوانید این موضوع را با اجرای دستور ساده git log ببینید که نشان میدهد اشارهگرهای شاخه کجا هستند. این گزینه --decorate نام دارد.
$ git log --oneline --decorate
f30ab (HEAD -> master, testing) Add feature #32 - ability to add new formats to the central interface
34ac2 Fix bug #1328 - stack overflow under certain conditions
98ca9 Initial commitشما میتوانید شاخههای master و testing را ببینید که درست کنار کامیت f30ab قرار دارند.
برای تغییر به یک شاخه موجود، دستور git checkout را اجرا کنید. بیایید به شاخه جدید testing سوئیچ کنیم:
$ git checkout testingاین باعث میشود HEAD به شاخه testing اشاره کند.
اهمیت این چیست؟ خب، بیایید یک کامیت دیگر بزنیم:
$ vim test.rb
$ git commit -a -m 'Make a change'این جالب است، چون اکنون شاخه testing جلو رفته اما شاخه master هنوز به کامیتی اشاره دارد که هنگام اجرای git checkout روی آن بودید. بیایید برگردیم به شاخه master:
$ git checkout master|
Note
|
git log doesn’t show all the branches all the timeاگر همین الان دستور شاخه ناپدید نشده؛ گیت فقط نمیداند شما علاقهمند به آن شاخه هستید و سعی دارد چیزی را نشان دهد که فکر میکند شما علاقه دارید. به عبارت دیگر، به طور پیشفرض، برای نمایش تاریخچه کامیتهای شاخه مورد نظر باید صراحتاً آن را مشخص کنید: |
آن دستور دو کار انجام داد. اشارهگر HEAD را برگرداند تا به شاخه master اشاره کند و فایلهای دایرکتوری کاری شما را به تصویر لحظهایای که master اشاره دارد برگرداند. این همچنین یعنی تغییراتی که از این نقطه به بعد انجام میدهید از نسخه قدیمیتر پروژه انحراف خواهد داشت. در واقع کارهایی که در شاخه testing انجام داده بودید را عقب برد تا بتوانید مسیر متفاوتی بروید.
|
Note
|
Switching branches changes files in your working directory
مهم است بدانید وقتی در گیت بین شاخهها جابجا میشوید، فایلهای دایرکتوری کاری شما تغییر خواهند کرد. اگر به شاخهای قدیمیتر سوئیچ کنید، دایرکتوری کاری شما بازمیگردد تا شبیه آخرین باری شود که روی آن شاخه کامیت زده بودید. اگر گیت نتواند این کار را به صورت تمیز انجام دهد، اصلاً اجازه نمیدهد سوئیچ کنید. |
بیایید چند تغییر ایجاد کنیم و دوباره کامیت بزنیم:
$ vim test.rb
$ git commit -a -m 'Make other changes'اکنون تاریخچه پروژه شما انحراف پیدا کرده است
ببینید)Divergent history).
شما یک شاخه ساختید و روی آن کار کردید، سپس برگشتید به شاخه اصلی و کار دیگری انجام دادید. هر دو تغییر در شاخههای جداگانه ایزوله شدهاند: میتوانید بین شاخهها جابجا شوید و وقتی آماده بودید آنها را ادغام کنید. و همه این کارها را با دستورات سادهی branch، checkout و commit انجام دادید.
شما همچنین میتوانید این موضوع را به راحتی با دستور git log ببینید. اگر دستور git log --oneline --decorate --graph --all را اجرا کنید، تاریخچه کامیتها را چاپ میکند و نشان میدهد اشارهگرهای شاخه کجا هستند و تاریخچه شما چگونه انحراف یافته است.
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) Make other changes
| * 87ab2 (testing) Make a change
|/
* f30ab Add feature #32 - ability to add new formats to the central interface
* 34ac2 Fix bug #1328 - stack overflow under certain conditions
* 98ca9 Initial commit of my project| چون یک شاخه در گیت در واقع یک فایل ساده است که مقدار چکسام 40 حرفی SHA-1 کامیتی که به آن اشاره دارد را نگه میدارد، ایجاد و حذف شاخه بسیار کم هزینه است. ساختن یک شاخه جدید همانقدر سریع و ساده است که 41 بایت (40 کاراکتر و یک خط جدید) را در یک فایل بنویسیم.
این برخلاف روش اکثر ابزارهای کنترل نسخه قدیمیتر است که برای ایجاد شاخه باید همه فایلهای پروژه را در دایرکتوری دوم کپی کنند. این کار ممکن است چند ثانیه یا حتی چند دقیقه طول بکشد، بسته به اندازه پروژه، در حالی که در گیت این فرایند همیشه فوری است. همچنین چون ما والدین را هنگام کامیت ثبت میکنیم، یافتن پایه مناسب برای ادغام خودکار انجام شده و معمولاً بسیار آسان است. این ویژگیها توسعهدهندگان را تشویق میکند تا اغلب شاخه بسازند و استفاده کنند.
بیایید ببینیم چرا باید این کار را انجام دهید
|
Note
|
Creating a new branch and switching to it at the same time
معمولاً وقتی یک شاخه جدید ایجاد میکنید، میخواهید همزمان روی آن سوئیچ کنید — این کار با یک عملیات با دستور git checkout -b <newbranchname> انجام پذیر است. |
|
Note
|
از نسخه 2.23 گیت به بعد میتوانید از
|








