-
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)
3.6 انشعابگیری در گیت (Git Branching) - بازپایهگذاری (Rebasing)
بازپایهگذاری (Rebasing)
در گیت، دو روش اصلی برای ادغام تغییرات از یک شاخه به شاخه دیگر وجود دارد: دستور merge
و دستور rebase
.
در این بخش خواهید آموخت که بازپایهگذاری چیست، چگونه انجام میشود، چرا این ابزار بسیار کاربردی است و در چه مواردی بهتر است از آن استفاده نکنید.
بازپایهگذاری پایه (The Basic Rebase)
اگر به مثالی که در بخش ادغام پایهای (Basic Merging) آوردیم رجوع کنید، میبینید که کارتان را از هم جدا کردهاید و تغییراتی را روی دو شاخه متفاوت ایجاد کردهاید.

سادهترین روش برای ادغام شاخهها، همانطور که پیشتر توضیح دادیم، دستور merge
است.
این دستور یک ادغام سهطرفه بین دو آخرین تصویر شاخهها (C3
و C4
) و آخرین جد مشترک آنها (C2
) انجام میدهد و یک تصویر (commit) جدید ایجاد میکند.

اما راه دیگری نیز وجود دارد: میتوانید تغییراتی را که در C4
ایجاد شدهاند برداشته و مجدداً روی C3
اعمال کنید.
در گیت به این کار «بازپایهگذاری» گفته میشود.
با دستور rebase
میتوانید تمام تغییراتی که در یک شاخه ثبت شدهاند را گرفته و دوباره روی شاخهای دیگر اجرا کنید.
برای این مثال، ابتدا شاخه experiment
را چکاوت میکنید و سپس آن را روی شاخه master
بازپایهگذاری میکنید، به این صورت:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
این عملیات با رفتن به جد مشترک دو شاخه (شاخه فعلی و شاخهای که میخواهید بازپایهگذاری کنید)، گرفتن تفاوتهایی که هر کامیت روی شاخه فعلی ایجاد کرده، ذخیره این تفاوتها در فایلهای موقتی، ریست کردن شاخه فعلی به همان کامیتی که شاخه مقصد دارد، و در نهایت اعمال هر تغییر به ترتیب انجام میشود.

C4
onto C3
در این مرحله، میتوانید دوباره به شاخه master
برگردید و یک ادغام fast-forward انجام دهید.
$ git checkout master
$ git merge experiment

master
branchاکنون تصویری که شاخه C4'
به آن اشاره میکند دقیقاً همان است که در the merge example شاخه C5
به آن اشاره داشت.
در نهایت محصول ادغام تفاوتی ندارد، اما بازپایهگذاری تاریخچهای تمیزتر و منظمتر ایجاد میکند.
اگر لاگ شاخهای که بازپایهگذاری شده را بررسی کنید، تاریخچه به صورت خطی دیده میشود؛ یعنی انگار همه کارها به صورت سری انجام شدهاند، حتی اگر در ابتدا به طور موازی بودند.
اغلب این کار را برای اطمینان از اینکه کامیتهای شما بهصورت تمیز روی شاخه ریموت اعمال میشوند انجام میدهید — مثلاً در پروژهای که میخواهید در آن مشارکت کنید اما مسئولیت نگهداری آن را ندارید.
در این حالت، کارتان را در یک شاخه انجام میدهید و وقتی آماده ارسال تغییرات به پروژه اصلی شدید، کارتان را روی origin/master
بازپایهگذاری میکنید.
به این ترتیب، مسئول نگهداری پروژه نیازی به انجام ادغام دستی ندارد — فقط یک fast-forward یا اعمال تمیز تغییرات خواهد بود.
توجه داشته باشید که اسنپشاتی که توسط آخرین کامیتی که در نهایت به آن میرسید اشاره میشود، چه آخرین کامیتهای بازبیس شده در یک بازبیس باشند یا کامیت نهایی ادغام بعد از یک مرج، همان اسنپشات است — تنها تاریخچه است که متفاوت است. بازبیس کردن تغییرات را به ترتیبی که معرفی شدهاند، از یک شاخه کاری روی شاخه دیگری بازپخش میکند، در حالی که مرج گرفتن نقاط انتهایی شاخهها را گرفته و آنها را با هم ادغام میکند.
بازپایهگذاریهای جالبتر (More Interesting Rebases)
شما همچنین میتوانید بازبیس خود را روی چیزی غیر از شاخه هدف بازبیس اجرا کنید.
برای مثال، یک تاریخچه مثل A history with a topic branch off another topic branch را در نظر بگیرید.
شما یک شاخه موضوعی به نام server
ایجاد کردید تا عملکردهای سمت سرور را به پروژهتان اضافه کنید و یک کامیت انجام دادید.
سپس، از آن شاخه انشعاب گرفتید تا تغییرات سمت کلاینت (client
) را ایجاد کنید و چندین بار کامیت کردید.
در نهایت، به شاخه server
برگشتید و چند کامیت دیگر انجام دادید.

فرض کنید تصمیم گرفتهاید تغییرات سمت کلاینت را برای انتشار به شاخه اصلی خود مرج کنید، اما میخواهید تغییرات سمت سرور را تا زمان آزمایش بیشتر نگه دارید.
شما میتوانید تغییرات روی شاخه client
که روی server
نیستند (C8
و C9
) را با گزینه --onto
در دستور git rebase
روی شاخه master
بازپخش کنید:
$ git rebase --onto master server client
این اساساً میگوید: «شاخه client
را بگیر، پچهایی را که از زمانی که از شاخه server
جدا شدهاند پیدا کن، و این پچها را در شاخه client
طوری بازپخش کن که گویی مستقیماً روی شاخه master
پایهگذاری شدهاند.»
این کمی پیچیده است، اما نتیجه بسیار جالب است.

حالا میتوانید شاخه master
را بهصورت fast-forward جلو ببرید (نگاه کنید به Fast-forwarding your master
branch to include the client
branch changes):
$ git checkout master
$ git merge client

master
branch to include the client
branch changesفرض کنید تصمیم میگیرید شاخه server
را هم وارد کنید.
میتوانید شاخه server
را بدون نیاز به چکاوت کردن قبلی، روی شاخه master
بازبیس کنید با اجرای دستور git rebase <basebranch> <topicbranch>
— که شاخه موضوع (در اینجا server
) را برای شما چکاوت میکند و روی شاخه پایه (master
) بازپخش میکند:
$ git rebase master server
این کار، تغییرات شاخه server
را روی تغییرات شاخه master
بازپخش میکند، همانطور که در Rebasing your server
branch on top of your master
branch نشان داده شده است.

server
branch on top of your master
branchسپس میتوانید شاخه پایه (master
) را fast-forward کنید:
$ git checkout master
$ git merge server
میتوانید شاخههای client
و server
را حذف کنید چون تمام کارها ادغام شده و دیگر به آنها نیازی ندارید، و تاریخچه شما برای کل این فرایند مشابه Final commit history خواهد بود.
$ git branch -d client
$ git branch -d server

خطرات بازپایهگذاری (The Perils of Rebasing)
آه، اما لذت بازبیس کردن بدون معایب نیست، که میتوان آن را در یک جمله خلاصه کرد:
کامیتهایی را که خارج از مخزن شما وجود دارند و ممکن است دیگران روی آنها کار کرده باشند، بازبیس نکنید.
اگر این دستورالعمل را رعایت کنید، مشکلی نخواهید داشت. اگر رعایت نکنید، دیگران از شما متنفر خواهند شد و دوستان و خانواده شما را تحقیر خواهند کرد.
هنگامی که بازبیس میکنید، شما کامیتهای موجود را رها کرده و کامیتهای جدیدی میسازید که شبیه آنها اما متفاوت هستند.
اگر کامیتهایی را جایی پوش کنید و دیگران آنها را کلون و روی آنها کار کنند، سپس شما با git rebase
آن کامیتها را بازنویسی کرده و دوباره پوش کنید، همکاران شما مجبور خواهند بود کارهایشان را دوباره ادغام کنند و وقتی بخواهید تغییراتشان را به کار خودتان برگردانید، اوضاع پیچیده و نامرتب خواهد شد.
بیایید مثالی از مشکلات بازبیس کردن تغییراتی که عمومی کردهاید را بررسی کنیم. فرض کنید از یک سرور مرکزی کلون کردهاید و سپس روی آن کارهایی انجام دادهاید. تاریخچه کامیتهای شما به این شکل است:

حالا، شخص دیگری کار بیشتری انجام میدهد که شامل یک ادغام (merge) است و آن کار را به سرور مرکزی ارسال میکند. شما آن را دریافت میکنید و شاخهی جدید راه دور را با کار خود ادغام میکنید، به طوری که تاریخچهی شما چیزی شبیه به این میشود:

بعداً، همان شخصی که کار ادغامشده را ارسال کرده است تصمیم میگیرد به عقب برگردد و کار خود را مجدداً بازبیس (rebase) کند؛ او با دستور git push --force
تاریخچهی سرور را بازنویسی میکند.
سپس شما از آن سرور دریافت میکنید و کمیتهای جدید را میآورید.

حالا هر دوی شما در وضعیتی دشوار قرار دارید.
اگر دستور git pull
اجرا کنید، یک کمیت ادغام (merge commit) ایجاد میشود که هر دو خط تاریخچه را شامل میشود و مخزن شما به این شکل در میآید:

اگر وقتی تاریخچهتان به این شکل است دستور git log
بزنید، دو کمیت با همان نویسنده، تاریخ و پیام خواهید دید که باعث سردرگمی میشود.
علاوه بر این، اگر این تاریخچه را دوباره به سرور بفرستید، تمام کمیتهای بازبیسشده را دوباره به سرور مرکزی وارد میکنید که ممکن است باعث سردرگمی بیشتر شود.
به طور منطقی میتوان فرض کرد توسعهدهندهی دیگر نمیخواهد کمیتهای C4
و C6
در تاریخچه باشند؛ به همین دلیل است که ابتدا کارش را بازبیس کرده بود.
بازپایهگذاری هنگام بازپایهگذاری (Rebase When You Rebase)
اگر در چنین وضعیتی قرار گرفتید، گیت جادوهای دیگری هم دارد که شاید به شما کمک کند. اگر کسی در تیم شما تغییراتی را با force push ارسال کند که کاری را که شما بر اساس آن کار کردهاید بازنویسی کند، چالش شما این است که بفهمید چه چیزی مال شماست و چه چیزی توسط آنها بازنویسی شده است.
گیت علاوه بر محاسبهی شناسه SHA-1 کمیت، یک شناسهی دیگری هم بر اساس تغییرات وارد شده در کمیت محاسبه میکند که به آن “patch-id” میگویند.
اگر شما کاری را که بازنویسی شده دریافت کنید و آن را روی کمیتهای جدید همتیمیتان بازبیس کنید، گیت اغلب میتواند تشخیص دهد که چه تغییراتی مختص شماست و آنها را دوباره روی شاخه جدید اعمال کند.
برای مثال، در سناریوی قبلی، اگر به جای ادغام در مرحلهی Someone pushes rebased commits, abandoning commits you’ve based your work on دستور git rebase teamone/master
را اجرا کنیم، گیت این کارها را انجام میدهد:
* تشخیص میدهد چه کارهایی منحصر به شاخهی ماست (C2
، C3
، C4
، C6
، C7
)
* تشخیص میدهد کدام کمیتها ادغام نیستند (C2
، C3
، C4
)
* تشخیص میدهد کدامها هنوز در شاخهی هدف بازنویسی نشدهاند (فقط C2
و C3
، چون C4
همان تغییر C4'
است)
* آن کمیتها را روی رأس teamone/master
اعمال میکند
پس به جای نتیجهای که در You merge in the same work again into a new merge commit دیدیم، چیزی شبیه به Rebase on top of force-pushed rebase work خواهیم داشت.

این فقط زمانی جواب میدهد که C4
و C4'
که همتیمی شما ساخته تقریباً همان اصلاحات یکسان باشند.
در غیر این صورت، بازبیس نمیتواند تشخیص دهد که این یک نسخهی تکراری است و یک تغییر شبیه C4
دیگر اضافه خواهد کرد (که احتمالاً به درستی اعمال نمیشود، چون تغییرات قبلاً تا حدی وجود دارند).
شما همچنین میتوانید این کار را با اجرای دستور git pull --rebase
به جای git pull
معمولی سادهتر کنید.
یا میتوانید به صورت دستی ابتدا git fetch
بگیرید و سپس دستور git rebase teamone/master
را اجرا کنید.
اگر میخواهید git pull
به طور پیشفرض با گزینه --rebase
اجرا شود، میتوانید مقدار pull.rebase
را با دستور git config --global pull.rebase true
تنظیم کنید.
اگر کمیتهایی را بازبیس میکنید که هرگز از کامپیوتر خودتان خارج نشدهاند، هیچ مشکلی نخواهید داشت. اگر کمیتهایی را بازبیس میکنید که قبلاً ارسال شدهاند، اما هیچ کس دیگری بر اساس آنها کار نکرده است، باز هم مشکلی پیش نمیآید. اما اگر کمیتهایی را بازبیس کنید که قبلاً به صورت عمومی ارسال شدهاند و دیگران بر اساس آنها کار کردهاند، ممکن است با مشکلات ناامیدکنندهای روبرو شوید و همکارانتان از شما ناراضی شوند.
اگر شما یا همتیمیتان در جایی مجبور شدید این کار را انجام دهید، مطمئن شوید همه میدانند که باید از دستور git pull --rebase
استفاده کنند تا دردسرهای بعدی کمی کمتر شود.
بازپایهگذاری در مقابل ادغام (Rebase vs. Merge)
حالا که بازبیس و ادغام را در عمل دیدهاید، شاید بپرسید کدام بهتر است؟ قبل از پاسخ دادن، کمی عقبتر میرویم و دربارهی مفهوم تاریخچه صحبت میکنیم.
یک دیدگاه این است که تاریخچهی کمیتهای مخزن شما، ثبت آنچه واقعاً اتفاق افتاده است. این یک سند تاریخی است که ارزش خاص خود را دارد و نباید دستکاری شود. از این زاویه، تغییر تاریخچهی کمیتها تقریباً یک عمل ناپسند است؛ انگار دربارهی آنچه واقعاً رخ داده دروغ میگویید. پس اگر یک سری کمیت ادغام نامرتب وجود داشته باشد، این همانا همان چیزی است که اتفاق افتاده و مخزن باید این را برای آیندگان حفظ کند.
دیدگاه مخالف این است که تاریخچه کامیتها، داستان چگونگی ساخته شدن پروژه شما است.
شما اولین پیشنویس یک کتاب را منتشر نمیکنید، پس چرا کار درهموبرهم خود را نشان دهید؟
وقتی روی پروژهای کار میکنید، ممکن است به رکورد تمام اشتباهات و مسیرهای بنبست خود نیاز داشته باشید، اما وقتی زمان نمایش کار به جهان میرسد، شاید بخواهید داستان منسجمتری درباره چگونگی رسیدن از نقطه A به B تعریف کنید.
افراد این دسته از ابزارهایی مانند rebase
و filter-branch
برای بازنویسی کامیتهایشان قبل از ادغام در شاخه اصلی استفاده میکنند.
آنها از این ابزارها برای بیان داستان به شکلی که برای خوانندگان آینده بهتر باشد بهره میبرند.
حالا درباره این سؤال که ادغام (merge) بهتر است یا بازپایهگذاری (rebase): امیدوارم متوجه شده باشید که پاسخ سادهای ندارد. گیت ابزاری قدرتمند است و به شما امکان انجام کارهای زیادی روی تاریخچه میدهد، اما هر تیم و هر پروژهای متفاوت است. حالا که میدانید هر دو روش چگونه کار میکنند، انتخاب بهترین گزینه برای وضعیت خاص خودتان بر عهده شماست.
میتوانید بهترینهای هر دو را داشته باشید: تغییرات محلی را قبل از ارسال با rebase مرتب کنید تا کارتان تمیز شود، اما هرگز چیزی را که جایی ارسال کردهاید دوباره بازپایهگذاری نکنید.