Movazee logo

موازی

خانه
فانوس
مشاغل
مسیرهای یادگیری
دوره‌ها
درباره ما
Movazee logo

موازی

موازی؛ پلتفرم جامع و تخصصی آموزش برنامه‌نویسی و هوش مصنوعی پروژه‌محور برای یادگیری مهارت‌های آینده.

دسترسی سریع

خانهدوره‌هامسیرهای یادگیریدرباره ما
movazee-footer-character
اینماد

تمامی حقوق مادی و معنوی این وب‌سایت متعلق به موازی می‌باشد.

آموزش گیت پروژه محور

اگر آموزش‌های خشک و فهرست‌وار Git باعث شده‌اند دستورها را زود فراموش کنی، این دوره مسیر متفاوتی دارد. در «آموزش گیت پروژه‌محور» قرار نیست فقط چند فرمان را حفظ کنی؛ از همان ابتدا وارد یک پروژه واقعی می

اهداف یادگیری

  • درک مفاهیم پایه کنترل نسخه و نصب و پیکربندی گیت
  • تسلط بر دستورات اصلی گیت (init, add, commit, status, log, diff)
  • مدیریت حرفه‌ای شاخه‌ها (Branching) و ادغام تغییرات (Merging)
  • حل اصولی تعارضات کدی (Merge Conflicts)
  • کار با گیت‌هاب (GitHub)، مخازن ریموت، Push ،Pull و ثبت Pull Request
  • استفاده از ابزارهای پیشرفته مانند Rebase, Stash, Reset, Revert و Cherry-pick
  • کاربرد هوش مصنوعی در مدیریت مخازن گیت و نوشتن Commit پیام‌های استاندارد

پیش‌نیازها

  • آشنایی مقدماتی با کامپیوتر و مفاهیم اولیه برنامه‌نویسی

فصل 1: دعوت هدهد

درس 1: پروژه‌ای که نسخه معتبرش گم شده

چهار فایل نهایی و یک تحویل نزدیک

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

سردرگمی فایل‌های کپی‌شده
simorgh-journey/
    ├── simorgh-journey-final.html← نسخه اولیه؟
    ├── simorgh-journey-final-2.html← اصلاحات هدهد
    ├── simorgh-journey-really-final.html← تغییرات کاتب
    └── simorgh-journey-use-this.html← نسخه تحویلی؟
سه آسیب اصلی روش کپی دستی
تغییرات نامعلوم — نام فایل‌ها به شما نمی‌گوید چه خطی، چه کدی یا چه مطلبی اضافه یا کم شده است.
نویسنده مجهول — مشخص نیست چه کسی، در چه تاریخی و به چه دلیلی فایل را دستکاری کرده است.
کابوس ادغام — اگر دو نفر هم‌زمان روی دو فایل کپی‌شده کار کرده باشند، ترکیب کارهایشان کابوس خواهد بود.
افزودن پسوندهایی مانند final به نام فایل‌ها هیچ تاریخچه‌ای نمی‌سازد. در این دوره به‌جای تکثیر فایل‌ها، همان پروژه واحد را با Git مدیریت می‌کنی.

چرا کپی دستی شکست می‌خورد؟ (منطق سیستم‌های کنترل نسخه)

برای حل ریشه‌ای این مشکل، ابزارهای سیستم کنترل نسخه (Version Control System) به وجود آمدند. منطق این ابزارها بسیار ساده اما فوق‌العاده هوشمندانه است:

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

روش کپی دستی فایل‌ها

تکثیر فایل با اسامی تکراری؛ ایجاد سردرگمی شدید، نبود نویسنده، عدم امکان مقایسه و احتمال بالای نابودی کدها.

منطق مدیریت نسخه با Git

نگهداری یک پوشه واحد، ثبت عکس‌برداری (Snapshot) هوشمند از تغییرات، مشخص بودن دقیق نویسنده و امکان سفر در زمان.

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

تکلیف سه مفهوم: Git، GitHub و نسخه پشتیبان

پیش از شروع کار باید مرز و نقش ابزارها کاملاً شفاف باشد. اشتباه گرفتن Git و GitHub یکی از رایج‌ترین سوءتفاهم‌ها میان تازه‌کاران است:

Git (ابزار محلی - Local)

نرم‌افزاری که روی رایانه تو نصب می‌شود، ۱۰۰٪ آفلاین و بدون نیاز به اینترنت کار می‌کند و تاریخچه تغییرات را ثبت می‌نماید.

GitHub (ابزار ابری - Remote)

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

نکته حیاتی برای کاتب کاروان

وقتی کدی را با Git روی رایانه خود ثبت می‌کنی، این ثبت فقط در حافظه سیستم خودت قرار دارد. تا زمانی که آن را به سرویسی مثل GitHub آپلود (Push) نکنی، به‌صورت آنلاین یا روی سیستم دیگران قرار نمی‌گیرد.

ثبت محلی در Git نیازی به اینترنت ندارد؛ اما برای اشتراک‌گذاری با تیم یا پشتیبان‌گیری در ابر به سرویسی مانند GitHub نیاز داریم.

دعوت هدهد و مأموریت کاتب

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

هست ما را پادشاهی بی‌خلاف
در پس کوهی که هست آن کوه قاف
نام او سیمرغ، سلطان طیور
او به ما نزدیک و ما زو دور دور

نقش تو در این سفر آموزشی
کاتب دیجیتال کاروان — تو در تمام طول دوره، مسئولیت ثبت دقیق تغییرات و تاریخچه پروژه را بر عهده داری.
پروژه واحد simorgh-journey — تنها یک پوشه پروژه وجود دارد و تمام فصول روی همین پروژه توسعه می‌یابد.
اصطلاحات استاندارد صنعت — داستان سفر انگیزه ماست، اما مفاهیمی مثل Repository، Branch و Commit کاملاً واقعی و استاندارد جهانی هستند.
داستان منطق‌الطیر به یادگیری ما روح می‌بخشد، اما ابزارها و کدهایی که اجرا می‌کنی دقیقاً همان ابزارهای برنامه‌نویسان حرفه‌ای در سراسر جهان است.

تحویل‌گرفتن پروژه سفر سیمرغ

فایل‌های همراه دوره در مخزن رسمی آموزش گیت پروژه محور قرار دارند. ابتدا بستهٔ شروع هنرجو را دانلود کن؛ اگر دانلود مستقیم باز نشد، صفحهٔ فایل ZIP را باز کن و Download raw file را بزن.

فقط همین بستهٔ شروع را بگیر؛ Code → Download ZIP کل مخزن رسمی را دانلود می‌کند و بستهٔ تمرین نیست. مخزن رسمی را برای شروع clone یا fork نکن؛ تاریخچهٔ پروژهٔ خودت را در درس‌ها از صفر می‌سازی.

بسته اولیه دوره را استخراج کن و پوشه simorgh-journey را پیدا کن. در آغاز مسیر، تنها یک فایل آماده به نام index.html داخل پوشه قرار دارد:

ساختار اولیه پروژه
simorgh-journey/
    └── index.html← دفتر اصلی پروژه کاروان
این فایل از قبل ساخته شده است و برای ادامه دوره به هیچ دانش قبلی از HTML نیاز نداری! هر جا تغییری لازم باشد، متن دقیق آن را دریافت می‌کنی.
بسته را در پوشه‌ای مستقل مثل Documents استخراج کن، نه داخل پروژه Git دیگری. ساختار آغاز فقط index.html است؛ فایل‌های نمونه با نام final در داستان را نساز. استخراج ZIP را برای فصل‌های بعد تکرار نکن؛ همان پوشه را ادامه می‌دهیم.

دیدن نسخه تحویلی در مرورگر

پیش از انجام هر کاری، باید ظاهر فعلی وب‌سایت پروژه را مشاهده کنی تا نقطه شروع کار مشخص باشد.

مراحل مشاهده خروجی در مرورگر
1
وارد پوشه simorgh-journey شو.
2
روی فایل index.html دو بار کلیک کن تا در مرورگر وب باز شود.
3
صفحه را بررسی کن؛ عنوان «داستان سیمرغ» و متن وضعیت آمادگی کاروان دیده می‌شود.
این صفحه، خروجی زنده پروژه توست. در فصل‌های آینده هر تغییر یا شاخه‌بندی را هم در مرورگر و هم در دیتابیس Git مشاهده خواهی کرد.

بازکردن پروژه در VS Code

اگر VS Code نصب نیست، ابتدا نسخه سیستم‌عامل خودت را از وب‌سایت رسمی code.visualstudio.com دریافت و نصب کن. در رایانه مدیریت‌شده از مسئول سیستم کمک بگیر. افزونه، حساب کاربری یا آشنایی با HTML برای این تمرین لازم نیست.

برای ویرایش کدها و اجرای دستورات ترمینال، باید پوشه پروژه را در محیط VS Code باز کنیم.

مراحل آماده‌سازی ویرایشگر
1
نرم‌افزار VS Code را باز کن.
2
از منوی بالای صفحه مسیر File → Open Folder را انتخاب کن.
3
پوشه simorgh-journey را انتخاب کرده و باز کن.
4
از منوی بالا مسیر Terminal → New Terminal را بزن تا ترمینال داخلی ایجاد شود.
کلیدهای میانبر سریع
باز کردن ترمینال داخلیCtrl + `
نام پوشه simorgh-journey باید در پنل سمت چپ (Explorer) دیده شود. تمام دستورات این دوره در ترمینال همین پوشه اجرا می‌شوند.

اطمینان از پوشه فعلی Terminal

دستورات Git همواره روی پوشه‌ای اثر می‌گذارند که ترمینال در آن باز است. باید مطمئن شوی ترمینال دقیقاً داخل پوشه simorgh-journey قرار دارد.

دستور بررسی مسیر ترمینال
bashmacOS / Linux
pwd
bashWindows PowerShell
Get-Location
انتهای مسیر نمایش داده شده باید عبارت simorgh-journey باشد. اگر مسیر دیگری را می‌بینی، مجدداً پوشه پروژه را با Open Folder باز کن.
متن‌های داخل کادرها یا فرمان ترمینال‌اند یا محتوای فایل؛ عنوان هر کادر را بخوان. متن خروجی نمونه را اجرا نکن. هر فرمان را جدا اجرا و نتیجه را بررسی کن؛ در خطای پیش‌بینی‌نشده به فرمان بعدی نرو. هنگام ساخت فایل، نام و پسوند دقیق باشد و آن را با UTF-8 ذخیره کن.

پیداکردن متن آماده تغییر

فایل index.html را در VS Code باز کن. می‌خواهیم متن وضعیت کاروان را پیدا کرده و نخستین تغییر واقعی پروژه را ایجاد کنیم.

عبارت مورد نظر برای جست‌وجو
htmlخط هدف در index.html
Status: The caravan is preparing to depart.
میانبر جست‌وجو در فایل
جست‌وجو در متن فایلCtrl + F / Cmd + F
عبارت بالا را در فایل پیدا کن. به تگ‌های HTML اطراف آن دست نزن؛ فقط قرار است خود متن انگلیسی را جایگزین کنیم.

ثبت نخستین تغییر قابل‌مشاهده

اکنون متن پیدا شده را تغییر می‌دهیم تا حضور کاتب دیجیتال در پروژه ثبت شود.

متن جدید جایگزین
htmlمتن جایگزین
Status: The caravan has a digital scribe.
1
متن جدید بالا را دقیقاً جایگزین عبارت قبلی کن.
2
فایل را با کلید Ctrl + S (یا Cmd + S) ذخیره کن.
3
صفحه مرورگر را رفرش (F5) کن و تغییر وضعیت کاروان را ببین.
آفرین! اولین تغییر واقعی پروژه با موفقیت در فایل ثبت شد و در خروجی مرورگر به نمایش درآمد.

تغییری که هنوز تاریخچه ندارد

جمله جدید در مرورگر دیده می‌شود و فایل روی دیسک ذخیره شده است. اما هنوز پوشه پروژه به یک Repository تبدیل نشده است.

پرسش تحلیلی چالش

اگر همین حالا فایل index.html به‌طور تصادفی حذف شود یا کد آن خراب گردد، آیا Git می‌تواند نسخه قبلی را بازگرداند؟ چرا؟

ذخیره‌سازی عادی فایل در ویرایشگر (Save)، فقط محتوای روی دیسک را تغییر می‌دهد و هیچ عکس یا ثبت تاریخچه‌ای در Git ایجاد نمی‌کند!

پروژه واقعی روی میز است

خلاصه دستاوردهای درس اول
پوشه واحد simorgh-journey را تحویل گرفتی.
فایل index.html را در مرورگر و VS Code باز کردی.
مسیر صحیح ترمینال را با دستور pwd یا Get-Location تایید کردی.
نخستین تغییر واقعی را اعمال و نتیجه را در مرورگر دیدی.
تفاوت ذخیره‌سازی عادی فایل و ثبت تاریخچه در Git را درک کردی.
در درس بعدی، وجود Git را روی سیستم بررسی می‌کنیم، هویت کاتب را تنظیم کرده و اولین گام‌های رسمی کار با ترمینال Git را برمی‌داریم.

درس 2: ابزار سفر روی رایانه

پر سیمرغ؛ نشانه‌ای که باید آزمود

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

ابتدای کار سیمرغ ای عجب
جلوه‌گر بگذشت بر چین نیم‌شب
در میان چین فتاد از وی پری
لاجرم پُرشور شد هر کشوری

نشانه‌شناسی نرم‌افزار در دنیای برنامه‌نویسی

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

نشانه موفقیت و آمادگی ابزار شما، دریافت یک خروجی شفاف شامل شماره نسخه از فرمان git --version در ترمینال است.

سنجش Git پیش از نصب دوباره

خیلی از اوقات نرم‌افزار Git از قبل روی سیستم‌عامل (مثلاً ابزارهای Xcode در مک یا همراه ابزارهای دیگر) نصب شده است. بنابراین پیش از دانلود فایل‌های سنگین، ابتدا حضور آن را آزمایش می‌کنیم.

دستور سنجش حضور Git
bashترمینال VS Code
git --version
تحلیل خروجی ترمینال
مشاهده نسخه (مانند git version 2.42.0) — تبریک! Git روی سیستم تو آماده است و نیازی به انجام گام بعدی نصب نداری.
خطای command not found یا unrecognized — نرم‌افزار هنوز روی سیستم نصب نشده یا ترمینال مسیر آن را شناسایی نکرده است.
اگر شماره نسخه را مشاهده کردی، مستقیم به گام ۴ (تایید در ترمینال جدید) برو و نیازی به نصب مجدد نداری.

راهنمای جامع نصب Git (متناسب با سیستم‌عامل)

اگر در گام قبلی مشخص شد Git نصب نیست، بر اساس سیستم‌عامل خود تنها یکی از روش‌های زیر را دنبال کن:

دستورالعمل نصب بر اساس سیستم‌عامل
ویندوز (Windows) — دانلود نصب‌کننده رسمی از سایت git-scm.com و ادامه مراحل با تنظیمات پیش‌فرض.
مک (macOS) — اجرای دستور xcode-select --install در ترمینال برای نصب ابزارهای خط فرمان مک.
لینوکس (Linux) — نصب از طریق مدیریت پکیج سیستم‌عامل (APT در اوبونتو یا DNF در فدورا).
bashترمینال مک (macOS)
xcode-select --install
bashلینوکس (Ubuntu / Debian)
sudo apt update && sudo apt install git -y
در سیستم‌های مدرسه، دانشگاه یا محل کار، محدودیت‌های دسترسی مدیر (Administrator) را دور نزن و از مسئول سیستم کمک بگیر.

اثبات نصب در ترمینال تازه (تازه‌سازی محیط Shell)

یکی از نکات مهمی که خیلی از برنامه‌نویسان تازه‌کار را سردرگم می‌کند این است که ترمینال‌های قدیمی، متغیرهای محیطی سیستم را پس از نصب جدید خودکار به‌روزرسانی نمی‌کنند.

گام‌های تازه‌سازی محیط ترمینال
1
تمام پنجره‌های ترمینال باز و نرم‌افزار VS Code را کاملاً ببند.
2
نرم‌افزار VS Code را مجدداً باز کن و از منو یک ترمینال جدید (Ctrl+`) بساز.
3
دستور سنجش نسخه را مجدداً اجرا کن.
bashترمینال جدید
git --version
مشاهده عبارت git version در ترمینال جدید به این معنی است که سیستم‌عامل مسیر فایل اجرایی Git را کاملاً به حافظه سپرده است.
از اینجا به بعد مسیر مشترک فرمان‌ها در ویندوز Git Bash است و در مک و لینوکس zsh یا bash. در ویندوز از منوی کنار دکمه ترمینال، Terminal: Select Default Profile را انتخاب کن، Git Bash را بگذار و ترمینال تازه‌ای داخل پروژه باز کن. Git Bash همراه Git for Windows نصب می‌شود. فرمان‌های sed و ls -la در فصل‌های بعد برای همین محیط‌اند، نه PowerShell.

پیداکردن مسیر فایل اجرایی Git (متغیر PATH)

سیستم‌عامل چگونه می‌داند وقتی دستور git را تایپ می‌کنی، کدام برنامه روی دیسک اجرا شود؟ این کار از طریق متغیری به نام PATH انجام می‌شود.

دستورات پیدا کردن مکان فایل اجرایی
bashWindows PowerShell
Get-Command git
bashmacOS / Linux
command -v git
نمایش یک مسیر معتبر (مانند /usr/bin/git یا C:\Program Files\Git\cmd\git.exe) در خروجی، تایید می‌کند که ترمینال به برنامه Git دسترسی مستقیم دارد.

راهنما و مستندات خودکار Git

هیچ برنامه‌نویسی در دنیا تمام پرچم‌ها و دستورات فرعی Git را حفظ نمی‌کند! Git یک سیستم راهنمای عالی و داخلی همراه خود دارد:

دستورات دریافت راهنمای سریع و کامل
bashفهرست دستورات پرکاربرد
git --help
bashمستندات کامل یک دستور مشخص
git help status
کلیدهای میانبر خروج از صفحه راهنما
خروج از صفحه راهنما (Quit)q
پیمایش صفحه به پایینSpace / Down Arrow
پیمایش صفحه به بالاUp Arrow
هر زمان ساختار یا پرچم یک دستور را فراموش کردی، کافیست عبارت git help را بزنی و با کلید q خارج شوی.

رفع خطای command not found

فرآیند نصب Git به پایان رسیده است، اما با تایپ دستور git در ترمینال هنوز خطای command not found یا unrecognized command ظاهر می‌شود. نخستین بررسی‌های منطقی شما چیست؟

بررسی شرایط چالش
هرگز برای حل خطاهای مسیر، فایل‌های اجرایی ناشناس و مشکوک را از سایت‌های غیررسمی دانلود یا نصب نکنید!

نشانه واقعی ابزار پیدا شد

خلاصه دستاوردهای درس دوم
حضور Git را با دستور git --version بررسی کردی.
روش صحیح نصب متناسب با سیستم‌عامل خود را شناختی.
ضرورت بستن و بازکردن ترمینال پس از نصب را درک کردی.
مسیر دقیق فایل اجرایی را با command -v یا Get-Command تایید کردی.
نحوه دریافت راهنمای داخلی و خروج با کلید q را فرا گرفتی.
در درس بعدی، هویت کاتب کاروان (نام و ایمیل) را در تنظیمات Git ثبت می‌کنیم تا تمام کامیت‌های آینده شناسنامه داشته باشند.

درس 3: نویسنده تاریخچه مشخص می‌شود

هدهد پیامش را بی‌نام نمی‌آورد

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

گفت: ای مرغان! منم بی‌ هیچ ریب
هم برید حضرت و هم پیک غیب
هم ز هر حضرت خبردار آمدم
هم ز فطنت صاحب‌اسرار آمدم

چرا ثبت هویت در Git الزامی است؟

در دنیای توسعه نرم‌افزار، وقتی ده‌ها برنامه‌نویس روی یک پروژه بزرگ کار می‌کنند، هر تغییر (Commit) مثل برگی از شناسنامه پروژه است. اگر تغییری باعث ایجاد باگ شود یا نیاز به قدردانی از سازنده یک ویژگی باشد، سیستم باید بداند چه کسی آن تغییر را ایجاد کرده است.

تغییرات بی‌شناسنامه

عدم امکان پیگیری تغییرات، سردرگمی در عیب‌یابی و بی‌اعتمادی اعضای تیم

ثبت هویت نویسنده

شفافیت کامل، مسئولیت‌پذیری، امکان قدردانی و پیوند با حساب‌های کاربری

نام و ایمیل در Git، اطلاعات توصیفی نویسنده هستند که همراه هر ثبت تاریخچه (Commit) ذخیره می‌شوند تا پروژه همواره زنده و پاسخگو باشد.

ثبت نام سراسری نویسنده (Global Name)

فقط روی حساب شخصی رایانه تنظیمات global را تغییر بده. روی رایانه مشترک این قدم و ثبت ایمیل global را اجرا نکن؛ پس از ساخت مخزن در درس چهارم، قدم سوم، هویت را فقط برای همین پروژه تنظیم می‌کنی. نام و ایمیل نمونه را عیناً کپی نکن.

نخستین قدم برای معرفی خود به سیستم، ثبت نام و نام خانوادگی به صورت سراسری (Global) است. پرچم --global به Git می‌گوید: «این نام را برای تمام پروژه‌های من روی این رایانه به یاد داشته باش.»

فرمان ثبت نام سراسری در ترمینال
bashترمینال VS Code
git config --global user.name "Your Full Name"
پشت صحنه اجرای این دستور

با اجرای این فرمان، Git یک فایل متنی ساده به نام .gitconfig در پوشه اصلی کاربر سیستم‌عامل (Home Directory) می‌سازد یا به‌روزرسانی می‌کند.

نام خود را به زبان انگلیسی و واضح (مثلاً Ali Rezaei) وارد کن تا در تاریخچه پروژه‌ها و سرویس‌های آنلاین مثل GitHub کاملاً خوانا باشد.

ثبت ایمیل سراسری نویسنده (Global Email)

پس از نام، باید ایمیل خود را ثبت کنی. Git از ایمیل شما برای اتصال کامیت‌ها به آواتار شخصی (Gravatar) و پیوند دادن فعالیت‌های شما به حساب پلتفرم‌هایی مانند GitHub یا GitLab استفاده می‌کند.

فرمان ثبت ایمیل سراسری در ترمینال
bashترمینال VS Code
git config --global user.email "your-email@example.com"
حفظ حریم خصوصی در GitHub

اگر تمایل نداری ایمیل واقعی‌ات به صورت عمومی در پروژه‌های متن‌باز نمایش داده شود، می‌توانی از ایمیل محرمانه noreply که GitHub در تنظیمات حساب کاربری‌ات ارائه می‌دهد استفاده کنی.

اگر روی رایانه عمومی (مانند سایت دانشگاه یا سیستم مشترک) کار می‌کنی، اطلاعات شخصی خود را به صورت Global ذخیره نکن.

بازخوانی و بازبینی شناسنامه ثبت‌شده

اگر مسیر رایانه مشترک را انتخاب کرده‌ای، فعلاً فقط مفهوم بازخوانی را بخوان؛ نبود هویت شخصی در global طبیعی است. معیار نهایی، بازخوانی هویت مؤثر پس از تنظیم local در درس چهارم است.

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

دستورات استعلام تنظیمات هویت
bashنمایش نام ثبت‌شده
git config --global user.name
bashنمایش ایمیل ثبت‌شده
git config --global user.email
bashبازخوانی یکجای نام و ایمیل
git config --global --get-regexp '^user[.]'
ارزیابی خروجی ترمینال
مشاهده نام و ایمیل صحیح — همه چیز آماده است؛ از این پس تمام کامیت‌های جدید با این هویت ثبت می‌شوند.
خطای تایپی یا مقدار غلط — کافیست فرمان مرحله قبل را با مقدار درست دوباره اجرا کنی تا مقدار قبلی اورراید شود.
اکنون سیستم شما هویت کامل کاتب کاروان را در حافظه سراسری خود ثبت کرده است.

سطوح سه‌گانه تنظیمات Git و منبع آن‌ها

یکی از قدرت‌های بزرگ Git، مدیریت هوشمندانه تنظیمات در سه سطح مختلف است. هر سطح، فایل کانفیگ مستقل خود را دارد:

سطوح سه‌گانه پیکربندی (Hierarchy Scopes)
سطح System (--system) — تنظیمات کل سیستم‌عامل برای همه کاربران (فایل /etc/gitconfig)
سطح Global (--global) — تنظیمات کاربر فعلی سیستم برای همه پروژه‌ها (فایل ~/.gitconfig)
سطح Local (--local) — تنظیمات اختصاصی همان پروژه (فایل .git/config داخل پروژه)
مشاهده منبع دقیق هر تنظیم روی دیسک
bashنمایش مقادیر همراه با مسیر دقیق فایل
git config --list --show-origin
اولویت تنظیمات همواره به این صورت است: Local > Global > System. یعنی تنظیمات اختصاصی پروژه همیشه بر تنظیمات سراسری ارجحیت دارند.

انتخاب سطح درست برای پروژه کاری

فرض کن در رایانه شخصی خود پروژه‌های شخصیت را با ایمیل شخصی پیش می‌بری، اما یک مخزن پروژه شرکت ساخته‌ای و می‌خواهی نام و ایمیل کاری‌ات فقط در همان مخزن ثبت شود بدون اینکه تنظیم سایر پروژه‌ها تغییر کند.

سوال تعاملی: انتخاب فرمان مناسب

هویت ثبت‌شده، هویت تأییدشده نیست

آیا ثبت نام و ایمیل در Git به این معنی است که هویت شما کاملاً اثبات شده و شخص دیگری نمی‌تواند با نام شما کامیت بزند؟ نام و ایمیل معمولی دقیقاً چه ماهیتی دارند؟

نکته تحلیلی چالش
به تفاوت اطلاعات ادعایی (Metadata) با امضای دیجیتال و کلیدهای رمزنگاری (GPG/SSH Keys) فکر کن.

شناسنامه کاتب آماده شد

خلاصه دستاوردهای درس سوم
هویت نویسنده (Name & Email) را در Git ثبت کردی.
کاربرد پرچم --global و نحوه ذخیره در فایل .gitconfig را درک کردی.
روش‌های بازخوانی و چک کردن تنظیمات را فراگرفتی.
سطوح سه‌گانه (System, Global, Local) و اولویت Local بر Global را شناختی.
تنظیم هویت اختصاصی برای پروژه‌های کاری (Local Config) را فرا گرفتی.
تفاوت هویت توصیفی معمولی را با امضای دیجیتال (Commit Signing) درک کردی.
اکنون همه چیز آماده است! در درس بعدی، پوشه معمولی simorgh-journey را با دستور git init به نخستین مخزن محلی (Repository) سفر تبدیل می‌کنیم.

درس 4: آستانه وادی طلب

خواستن با نخستین قدم واقعی می‌شود

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

چون فرو آیی به وادی طلب
پیشت آید هر زمانی صد تعب
مال اینجا بایدت انداختن
ملک اینجا بایدت باختن

معنای آغاز راه در دنیای نرم‌افزار

در دنیای برنامه‌نویسی، ساخت یک پوشه عادی به تنهایی کاری انجام نمی‌دهد. عمل واقعی در «وادی طلب»، اجرای فرمان git init است که آن پوشه معمولی را به یک مخزن محلی (Local Repository) زنده، هوشمند و قابل ردیابی تبدیل می‌کند.

پوشه معمولی سیستم‌عامل

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

مخزن محلی Git (Repository)

دارای پوشه مدیریتی مخفی git. و توانایی ثبت لحظات مهم پروژه

فرمان git init قرار نیست فایل‌های پروژه شما را تغییر دهد یا چیزی را به اینترنت بفرستد؛ این فرمان فقط ساختار مدیریتی مخزن را در همین پوشه ایجاد می‌کند.

دیدن وضعیت پیش از ساخت Repository

اگر به‌جای خطای «not a git repository» وضعیت مخزن دیگری را دیدی، ادامه نده. پوشه را بیرون از مخزن والد قرار بده و دوباره بررسی کن؛ تاریخچه یا پوشه .git پروژه دیگری را حذف نکن.

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

سنجش وضعیت مخزن در ترمینال
bashترمینال VS Code
git status
تحلیل خروجی ترمینال
خطای fatal: not a git repository — این خروجی کاملاً طبیعی است! ترمینال می‌گوید این پوشه هنوز به مخزن Git تبدیل نشده است.
هدایت ترمینال — مطمئن شو ترمینال تو دقیقاً در مسیر پوشه simorgh-journey باز است.
این پیغام خطای سیستم‌عامل نیست، بلکه اثبات می‌کند پوشه فعلی شما هنوز تحت مدیریت کنترل نسخه Git قرار نگرفته است.

ساخت مخزن محلی پروژه (git init)

اکنون وقت آن است که طلسم پوشه معمولی را بشکنی! در ترمینال و داخل ریشه پروژه simorgh-journey فرمان زیر را اجرا کن:

دستور تبدیل پوشه به مخزن Git
bashترمینال VS Code
git init
تحلیل خروجی موفقیت
bashخروجی ترمینال
Initialized empty Git repository in /path/to/simorgh-journey/.git/
عبارت Initialized — مخزن محلی Git با موفقیت در این پوشه راه‌اندازی شد.
معنای واژه empty — کلمه empty به عدم وجود Commit در تاریخچه اشاره دارد، نه اینکه فایل‌های پروژه پاک شده باشند!
تبریک! پوشه simorgh-journey اکنون به یک مخزن محلی هوشمند کنترل نسخه تبدیل شده است.

اگر رایانه مشترک است یا نمی‌خواهی تنظیمات دیگر پروژه‌ها عوض شود، اکنون که مخزن ساخته شده نام و ایمیل واقعی انتخابی خودت را فقط همین‌جا تنظیم کن:

bash
git config --local user.name "Your Full Name"
git config --local user.email "your-email@example.com"
این دو فرمان فقط .git/config همین مخزن را تغییر می‌دهند؛ هویت را در چک‌لیست پایان فصل بازخوانی کن.

آنچه داخل پوشه مخفی .git می‌گذرد

با اجرای git init، یک پوشه مخفی به نام .git در پوشه پروژه ساخته می‌شود. این پوشه همان مغز و پایگاه داده Git است. فایل اصلی پروژه یعنی index.html بیرون آن در پوشه کاری (Working Tree) قرار دارد:

ساختار درختی مخزن محلی
simorgh-journey/
    ├── .git/← پوشه مخفی مدیریتی Git (مغز مخزن)
        │ ├── HEAD← اشاره‌گر به شاخه فعال فعلی
        │ ├── config← تنظیمات Local اختصاصی این مخزن
        │ ├── objects/← پایگاه داده ذخیره فایل‌ها و کامیت‌ها
        │ └── refs/← محل نگه‌داری آدرس شاخه‌ها و تگ‌ها
    └── index.html← فایل اصلی پروژه در Working Tree
وظیفه اجزای داخلی پوشه .git
objects/ — پایگاه داده فشرده‌شده تمام نسخه فایل‌ها و کامیت‌های آینده
refs/ — اشاره‌گرهای سبک به شاخه‌ها (Branches) و نشان‌ها (Tags)
HEAD — فایلی که نشان می‌دهد هم‌اکنون روی کدام شاخه یا کامیت ایستاده‌ای
config — تنظیمات اختصاصی (Local) همین مخزن
هرگز فایل‌های داخل پوشه .git را دستی دستکاری یا پاک نکن! پاک کردن این پوشه به معنی نابودی کل تاریخچه Git پروژه است.

تحلیل و تفکیک اثر واقعی git init

اکنون که دستور git init را اجرا کردی و ساختار پوشه مخفی .git را دیدی، عمیق‌تر تحلیل کن: این دستور دقیقا چه چیزی روی دیسک ساخته و چه کارهایی را هنوز انجام نداده است؟

نکته چالش تحلیلی
به تفاوت ساخت دیتابیس مخزن (.git) با ثبت کامیت (Commit) و ارسال فایل‌ها به اینترنت (GitHub) فکر کن.

گزارش وضعیت مخزن پس از ساخت (git status)

حالا که مخزن محلی ساخته شده، فرمان git status را دوباره اجرا کن تا تغییر پیام ترمینال را در مقایسه با گام دوم ببینی:

استعلام وضعیت مخزن تازه ساخته‌شده
bashترمینال VS Code
git status
تحلیل خروجی جدید ترمینال
عبارت On branch main (یا master) — تایید می‌کند که ترمینال شما در یک مخزن معتبر است و روی شاخه اصلی پروژه قرار داری.
عبارت No commits yet — نشان می‌دهد که مخزن هنوز هیچ ثبت تاریخچه‌ای (Commit) ندارد.
بخش Untracked files — نشان می‌دهد که فایل index.html توسط Git شناسایی شده اما هنوز ردیابی نمی‌شود.
دستور git status صمیمی‌ترین فرمان برنامه‌نویسان در Git است. هر زمان که در روند پروژه تردید داشتی، اجرای این دستور وضعیت دقیق را به تو نشان می‌دهد.

خواندن نخستین گزارش کوتاه (git status --short)

گاهی اوقات که تعداد فایل‌های پروژه زیاد می‌شود، خروجی کامل git status طولانی خواهد بود. Git یک پرچم فشرده به نام --short ارائه می‌دهد:

دستور مشاهده گزارش خلاصه وضعیت
bashترمینال VS Code
git status --short
تحلیل عبارت ?? index.html
فایل Untracked (علامت ??)

فایل در پوشه کاری هست، اما هنوز به index معرفی نشده و Git آن را ردیابی نمی‌کند.

فایل Tracked (حروف A یا M)

فایل به دیتابیس Git معرفی شده و تغییرات آن تحت کنترل لحظه‌ای است.

علامت ?? یعنی Git فایل index.html را حس می‌کند، اما تا زمانی که دستور git add را نزنی، مسئولیت ردیابی آن را بر عهده نمی‌گیرد.

دیدن همان وضعیت در VS Code

رابط گرافیکی VS Code نیز همین وضعیت را به صورت بصری به شما نشان می‌دهد:

راهنمای مشاهده وضعیت در VS Code
1
از نوار ابزار سمت چپ VS Code روی آیکون Source Control (کلید Ctrl+Shift+G) کلیک کن.
2
در بخش Changes، فایل index.html را مشاهده کن.
3
به حرف U (مخفف Untracked) در کنار اسم فایل دقت کن.
حرف U بنفش یا سبز — معادل همان ?? در ترمینال است و نشان می‌دهد فایل هنوز Stage نشده است.
در این قدم هنوز هیچ دکمه‌ای (مثل دکمه + یا Stage) را در VS Code کلیک نکن؛ در فصل بعد تمام این مراحل را آگاهانه در ترمینال اجرا خواهیم کرد.

چرا Git فایل‌ها را به صورت خودکار کامیت نمی‌کند؟

ممکن است این سوال برایت پیش بیاید که چرا Git پس از git init، فایل index.html را به صورت خودکار وارد تاریخچه نکرد و آن را در حالت Untracked باقی گذاشت؟

دلایل ساختاری و مهندسی گیت
حفظ فایل‌های محرمانه و موقت — پوشه پروژه ممکن است شامل فایل‌های لاگ، تنظیمات شخصی، کلمات عبور یا فایل‌های موقت باشد که هرگز نباید وارد تاریخچه پروژه شوند.
اصل انتخاب آگاهانه (Atomic Commits) — برنامه‌نویسان باید بتوانند فایل‌های هم‌هدف را دسته‌بندی کرده و کامیت‌های تمیز و تفکیک‌شده بسازند.
سیستم‌های همگام‌سازی ابری

ذخیره خودکار مانند Google Docs که تمام خطوط و فایل‌های موقت را بدون حق انتخاب ذخیره می‌کند.

کنترل نسخه هوشمند Git

برنامه‌نویس با دستور git add آگاهانه تصمیم می‌گیرد کدام فایل‌ها و تغییرات وارد ثبت تاریخچه شوند.

در فصل بعدی (وادی طلب)، با دستور git add اولین گام انتخاب آگاهانه فایل‌ها را در ترمینال تجربه خواهیم کرد.

چک‌لیست فنی پایان فصل

برای اطمینان از سلامت کامل محیط و آمادگی برای فصل بعد، این چهار استعلام ساده را در ترمینال پروژه اجرا کن:

دستورات استعلام سلامت ۴‌گانه فصل اول
bashتست سلامت کامل فصل اول
git --version
git status
git config --get user.name
git config --get user.email
خروجی مورد انتظار چهارگانه
نسخه Git — مشاهده شماره نسخه معتبر (مثلاً git version 2.42.0)
وضعیت مخزن — مشاهده On branch main یا master، عبارت No commits yet و فایل index.html در Untracked؛ نام main را بعد از اولین commit در فصل دوم یکسان می‌کنیم
نام کاتب — مشاهده نام انگلیسی ثبت‌شده شما
ایمیل کاتب — مشاهده ایمیل ثبت‌شده شما
اگر هر ۴ خروجی بالا را با موفقیت دیدی، تمام زیرساخت‌های فصل اول ۱۰۰٪ درست عمل می‌کنند!

نشان پر هدهد

خلاصه دستاوردهای فصل اول (دعوت هدهد)
با فلسفه کنترل نسخه و ابزار Git آشنا شدی.
حضور Git و متغیر PATH را در رایانه تایید کردی.
شناسنامه کاتب (نام و ایمیل سراسری) را ثبت کردی.
پوشه simorgh-journey را با git init به نخستین مخزن محلی تبدیل کردی.
اجزای پوشه مخفی .git و وضعیت Untracked فایل index.html را دیدی.
🏆 نشان «پر هدهد» با موفقیت آزاد شد! در فصل بعدی (وادی طلب)، نخستین کامیت واقعی پروژه را می‌سازیم و جریان سه منطقه‌ای Git (Working Tree, Index, HEAD) را در عمل لمس می‌کنی.

فصل 2: وادی طلب

درس 1: نخستین Snapshot پروژه

ورود به وادی طلب و ضرورت ثبت لحظه‌ها

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

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

ثبت نخستین لحظه تاریخی پروژه

در فصل قبل مخزن خالی Git را ساختیم، اما هنوز هیچ ثبت تاریخی صورت نگرفته است. در این درس، اولین عکس لحظه‌ای Snapshot واقعی پروژه سفر سیمرغ را خلق می‌کنیم.

پوشه کاری Working Tree

تغییرات زنده روی دیسک که هنوز در دیتابیس تاریخچه ثبت رسمی نشده‌اند.

عکس از لحظه Commit Snapshot

نسخه جاودانه و تغییرناپذیر ذخیره‌شده در دیتابیس گیت با شناسه اختصاصی.

در این درس، نخستین Commit رسمی پروژه را می‌سازیم و معماری سه‌منطقه‌ای Git را در عمل لمس می‌کنیم.

معماری سه‌منطقه‌ای Git (Working Tree, Index, HEAD)

برای دنبال کردن تغییرها، سه چیز را از هم جدا کن: Working Tree فایل‌های روی دیسک است؛ Index / Staging Area نسخه انتخاب‌شده برای commit بعدی؛ و Repository محل نگهداری اشیای تاریخچه در مخزن است.

HEAD نام دیتابیس یا منطقه ذخیره‌سازی نیست؛ مرجع موقعیت فعلی توست که معمولاً شاخه فعال را دنبال می‌کند. پس از اولین commit، عبارت «مقایسه با HEAD» یعنی مقایسه با snapshot کامیت فعلی. پیش از اولین commit هنوز چنین snapshotی وجود ندارد.

git add نسخه فعلی فایل را در Index آماده می‌کند و git commit از Index یک snapshot در تاریخچه می‌سازد. خود فایل از پوشه کاری جابه‌جا نمی‌شود.

مشاهده نقطه شروع و شاخه فعلی پروژه

پیش از آنکه نخستین کامیت را ثبت کنیم، وضعیت فعلی ترمینال را در پوشه simorgh-journey بررسی می‌کنیم:

استعلام وضعیت خلاصه در ترمینال
bashترمینال VS Code
git status --short
تحلیل خروجی گزارش خلاصه
علامت ?? index.html — نشان می‌دهد فایل index.html فعلاً فقط در Working Tree است و هنوز به Index معرفی نشده است.
دستور git status --short گزارش خلاصه دو ستونه ارائه می‌دهد؛ ستون چپ مربوط به Index و ستون راست مربوط به Working Tree است.

انتقال فایل به Index با دستور git add

اکنون وقت آن است که فایل اصلی پروژه را برای ثبت در کامیت بعدی آماده Stage کنیم. با اجرای دستور زیر، نسخه فعلی فایل وارد Index می‌شود:

دستور معرفی فایل به Staging Area
bashترمینال VS Code
git add index.html
git status --short
تحلیل خروجی جدید
حرف A سبز در ستون چپ — مخفف Added است و تایید می‌کند که فایل با موفقیت وارد Index شده و آماده کامیت است.
فایل index.html بدون جابه‌جایی روی دیسک، در Index / Staging Area قرار گرفت.

ساخت فایل دوم پروژه و ثبت آن در Index

برای تمرین کار با چند فایل، یک فایل استایل ساده می‌سازیم. در VS Code فایلی به نام style.css بساز و متن زیر را در آن ذخیره کن:

cssمحتوای فایل style.css
body { font-family: Tahoma, sans-serif; }
دستور معرفی فایل دوم به Index
bashترمینال VS Code
git add style.css
git status --short
تحلیل خروجی دو فایلی
حرف A برای index.html — آماده کامیت در Index
حرف A برای style.css — آماده کامیت در Index
اکنون هر دو فایل در Index آماده‌اند تا در نخستین کامیت پروژه قرار گیرند.
style.css در این تمرین فقط یک فایل دوم برای مشاهده Stage است؛ هنوز در index.html به آن پیوند نداده‌ایم و ظاهر صفحه از CSS آماده داخل HTML می‌آید. انتظار تغییر ظاهری با ویرایش style.css نداشته باش.

تفاوت محتوای ثبت‌شده در Index با Working Tree

برای درک عمیق رفتار Index، این آزمایش عملی را انجام بده: به انتهای فایل style.css خط زیر را اضافه و ذخیره کن (اما فعلاً دستور git add را مجدداً اجرا نکن):

cssخط جدید اضافه شده به style.css
h1 { letter-spacing: 0.02em; }
استعلام وضعیت دوگانه فایل
bashترمینال VS Code
git status --short
رمزگشایی خروجی AM style.css
حرف A در ستون چپ Index

نسخه اول فایل (یک خطی) که قبلاً با git add در Index ثبت شده است.

حرف M در ستون راست Working Tree

تغییرات جدید (خط دوم) که روی دیسک ایجاد شده اما هنوز Stage نشده است.

دستور git add اسم فایل را علامت نمی‌زند؛ بلکه عکس همان لحظه محتوای فایل را در Index ثبت می‌کند!

به‌روزرسانی Index با آخرین تغییرات (git add دوباره)

برای اینکه هر دو خط فایل style.css در کامیت نخست قرار گیرند، دستور git add را مجدداً روی این فایل اجرا می‌کنیم:

دستور به‌روزرسانی Index و استعلام وضعیت
bashترمینال VS Code
git add style.css
git status --short
تحلیل خروجی جدید
حرف A سبز بدون M — حرف M ستون راست ناپدید شد؛ یعنی Index با آخرین تغییرات دیسک ۱۰۰٪ هماهنگ گردید.
اکنون Index و Working Tree برای هر دو فایل کاملاً هم‌گام و آماده کامیت هستند.

ثبت نخستین Commit پروژه (git commit)

حالا وقت آن است که پیش‌نویس موجود در Index را به عنوان اولین نقطه ثبت رسمی تاریخچه Commit ذخیره کنیم:

دستور ساخت نخستین کامیت
bashترمینال VS Code
git commit -m "Create initial journey website"
تایید پاک‌شدن Staging Area
bashترمینال VS Code
git status --short
وقتی git status --short هیچ خروجی ارائه نمی‌دهد، یعنی تمام تغییرات Index با موفقیت در HEAD ثبت شده‌اند و Working Tree کاملاً پاکیزه است.

تنظیم شاخه اصلی روی main و استعلام شناسه HEAD

پس از ثبت نخستین کامیت، باید از یکسان بودن نام شاخه اصلی پروژه با استانداردهای مدرن صنعت نرم‌افزار (مانند GitHub و GitLab) مطمئن شویم. سیستم‌های قدیمی گیت ممکن است نام پیش‌فرض را master بگذارند، اما در این دوره نام انتخابی مشترک main است.

مفهوم اشاره‌گر HEAD و شناسنامه کامیت

اشاره‌گر HEAD مثل سنجاق «شما اینجا هستید» روی نقشه است که نشان می‌دهد هم‌اکنون کدام کامیت و شاخه در پوشه کاری شما فعال است. همچنین هر کامیت یک شناسه اختصاصی ۴۰ کاراکتری SHA-1 Hash دریافت می‌کند که نمایش کوتاهش معمولاً حداقل ۷ کاراکتر است؛ طول نمایش کوتاه و الگوریتم هش می‌تواند متفاوت باشد.

پرچم -M در git branch

تغییر نام قطعی شاخه جاری به main جهت یکپارچه‌سازی با استانداردهای گیت‌هاب

پرچم‌های git log -1 --oneline

نمایش فشرده فقط ۱ کامیت اخیر به صورت تک‌خطی همراه با شناسه کوتاه و اشاره‌گرها

دستورات تنظیم شاخه و استعلام لاگ فشرده
bashترمینال VS Code
git branch -M main
git log -1 --oneline
تحلیل بخش به بخش خروجی لاگ
شناسه کوتاه (مانند a1b2c3d) — کد هش SHA-1 Hash اختصاصی و تغییرناپذیر اولین کامیت در دیتابیس گیت
اشاره‌گر HEAD -> main — تایید می‌کند که HEAD روی شاخه main ایستاده و این شاخه به این کامیت اشاره دارد.
پیام کامیت (Create initial...) — توضیحی که هنگام اجرای git commit -m نوشتی
نخستین خشت تاریخچه پروژه شما در دیتابیس Git با هویت شفاف و شاخه main بنا نهاده شد.
از این قدم به بعد نام شاخه اصلی در تمام تمرین‌ها main است. پیش از تغییر نام، مطمئن باش در همین مخزن تازه هستی؛ این فرمان برای تغییر نام شاخه‌های پروژه‌های دیگر نیست.

پیش‌بینی رفتار کامیت در تغییرات دوگانه

اگر برنامه‌نویسی فایلی را ویرایش کند، git add را بزند و سپس خط دیگری به همان فایل اضافه کند اما دوباره Stage نکند؛ هنگام اجرای git commit کدام نسخه ثبت می‌شود و خط دوم چه سرنوشتی دارد؟

نکته چالش فکری
به یاد داشته باش که git commit فقط محتوای فعلی Index را می‌خواند، نه فایل‌های روی دیسک را.

ثبت نخستین نقطه ثبت پروژه

خلاصه دستاوردهای درس اول فصل دوم
معماری ۳ منطقه‌ای Git شامل Working Tree و Index و HEAD را در عمل لمس کردی.
فایل‌های index.html و style.css را با git add وارد Index کردی.
رفتار وضعیت دوگانه AM هنگام ویرایش بعد از Stage را شناختی.
نخستین Commit رسمی پروژه را با git commit -m ثبت کردی.
شاخه اصلی main و اشاره‌گر HEAD را استعلام گرفتی.
در درس بعدی، توسعه صفحات وب‌سایت سفر را ادامه داده و نحوه ثبت و بازبینی تغییرهای آماده با git diff --staged --stat را فرا خواهیم گرفت.

درس 2: گزارش آغاز حرکت

حرکت آگاهانه کاروان در نخستین وادی

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

چون شنودند این سخن مرغان همه
آن زمان گفتند ترک جان همه
عزم ره کردند عزمی بس درست
ره سپردن را باستادند چست

مفهوم کامیت‌های منسجم و آتمیک

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

ویرایش‌های پراکنده و تک‌فایلی

کامیت‌های خرد و بی‌هدف که عیب‌یابی تاریخچه پروژه را در آینده سخت می‌کنند.

کامیت‌های منسجم Atomic Commit

ترکیب تمام فایل‌های مربوط به یک مأموریت (مثلاً کد + لاگ) در یک ثبت واحد.

در این مأموریت، دو فایل مرتبط (تغییر وضعیت وب‌سایت و ساخت فایل لاگ حرکت) را در قالب یک Commit منسجم در تاریخچه گیت ثبت می‌کنیم.

تشخیص وضعیت فایل‌های Modified و Untracked

پیش از آنکه فایل‌ها را به Staging Area / Index بفرستیم، باید نحوه نگاه گیت به تغییرات پوشه کاری Working Tree را بشناسیم:

دو نوع فایل در پوشه کاری
فایل Modified (تغییریافته - M) — فایلی که قبلاً در دیتابیس گیت کامیت شده Tracked و اکنون محتوای آن روی دیسک تغییر کرده است.
فایل Untracked (ردیابی‌نشده - ??) — فایل تازه‌ای که روی دیسک ساخته شده اما هنوز در هیچ کامیت قبلی ثبت نشده است.
1
فایل Modified با تغییر کد روی دیسک ایجاد می‌شود.
2
فایل Untracked با ساخت فایل جدید در پوشه ایجاد می‌شود.
3
دستور git add هر دو نوع فایل را وارد Index می‌کند.
دستور git add هم برای فایل‌های تغییریافته Modified و هم فایل‌های تازه Untracked استفاده می‌شود تا آن‌ها را آماده کامیت کند.

به‌روزرسانی وضعیت صفحه در فایل index.html

در نرم‌افزار VS Code فایل index.html را باز کن و خط زیر را پیدا کن:

htmlمتن قبلی
Status: The caravan has a digital scribe.

همین متن را دقیقاً با جمله جدید زیر جایگزین و فایل را ذخیره کن:

htmlمتن جدید جایگزین
Status: The caravan has started the journey.
استعلام وضعیت فایل Modified
bashترمینال VS Code
git status --short
تحلیل خروجی M index.html
حرف M در ستون راست — نشان می‌دهد که فایل index.html در پوشه کاری Working Tree تغییر کرده ولی هنوز Stage نشده است.
حرف M قرمز در ستون راست تایید می‌کند که این فایل قبلاً کامیت شده ولی محتوای جدید آن هنوز وارد Index نشده است.

مشاهده تغییرات در مرورگر و تایید صحت کارکرد

پیش از Stage کردن، همیشه خروجی زنده پروژه را بررسی می‌کنیم:

مراحل تست خروجی وب‌سایت
1
مرورگر وب را باز کن و صفحه index.html را رفرش (F5) کن.
2
تغییر متن وضعیت کاروان به «The caravan has started the journey» را تایید کن.
3
به ترمینال برگرد تا فایل دوم این مأموریت را بسازیم.
تغییرات ظاهری وب‌سایت در مرورگر اثبات شد. حالا نوبت ساخت فایل لاگ متنی است.

ساخت فایل لاگ متنی (journey-log.txt)

در ریشه پوشه پروژه simorgh-journey فایل جدیدی به نام journey-log.txt بساز و متن زیر را در آن ذخیره کن:

bashمحتوای فایل journey-log.txt
Takeoff confirmed. The caravan has entered the Valley of Quest.
استعلام وضعیت همزمان دو فایل در ترمینال
bashترمینال VS Code
git status --short
تحلیل خروجی دو فایلی
نماد M index.html — فایل قبلی تغییر یافته است Modified
نماد ?? journey-log.txt — فایل جدید اضافه شده است Untracked
اکنون یک تغییر از نوع Modified و یک فایل از نوع Untracked داریم که هر دو به یک مأموریت مربوط می‌شوند.

آموزش کاراکتر نقطه در git add . و کارکرد آن

وقتی چند فایل مرتبط دارید، به جای وارد کردن تک‌تک اسامی، می‌توانی از کاراکتر نقطه (.) استفاده کنی. نقطه در ترمینال به معنای «پوشه جاری و تمام زیرپوشه‌های آن» است:

دستور انتخاب یکجای فایل‌های پوشه جاری
bashترمینال VS Code
git add .
git status --short
تحلیل خروجی پس از git add .
حرف M سبز در ستون چپ — تایید می‌کند که تغییرات index.html وارد Index شد.
حرف A سبز در ستون چپ — تایید می‌کند که فایل جدید journey-log.txt وارد Index شد.
فقط زمانی از git add . استفاده کن که مطمئن باشی هیچ فایل موقت، محرمانه یا نامرتبطی در پوشه کاری Working Tree وجود ندارد!

بازبینی پیش‌نویس آماده ثبت (git diff --staged)

پیش از اجرای کامیت، برنامه‌نویسان حرفه‌ای همواره خلاصه محتوای موجود در Index را با دستور git diff --staged بازبینی می‌کنند:

دستورات بازبینی تغییرات آماده کامیت
bashنمایش خلاصه آمار تغییرات
git diff --staged --stat
تحلیل آمار تغییرات
تغییرات index.html — نمایش ۱ خط اضافه و ۱ خط حذف‌شده (2 +- )
تغییرات journey-log.txt — نمایش ۱ خط اضافه شده (1 +)
خلاصه بازبینی تایید می‌کند که دقیقاً دو فایل مربوط به مأموریت آغاز حرکت در Index قرار گرفته‌اند.

ثبت کامیت دوم پروژه با پیام توصیفی

حالا پیش‌نویس آماده‌شده را با یک پیام شفاف و انگلیسی کامیت می‌کنیم:

دستور ساخت کامیت دوم
bashترمینال VS Code
git commit -m "Record caravan takeoff"
تایید پاکیزه‌شدن Staging Area
bashترمینال VS Code
git status --short
وقتی git status --short خروجی خالی ارائه می‌دهد، یعنی تمام تغییرات با موفقیت در دیتابیس ثبت شده و Working Tree کاملاً پاکیزه است.

استعلام لاگ تاریخچه کامیت‌ها (git log)

برای مشاهده تاریخچه ثبت‌شده و اطمینان از ترتیب زمانی کامیت‌ها، دستور زیر را اجرا کن:

دستور مشاهده لاگ دو کامیت اخیر
bashترمینال VS Code
git log -2 --oneline
تحلیل لاگ دو کامیت
کامیت جدید (بالا) — Record caravan takeoff — وضعیت فعلی HEAD
کامیت قبلی (پایین) — Create initial journey website — نقطه شروع پروژه
تاریخچه زنجیره‌ای پروژه شما اکنون دارای دو نقطه ثبت معتبر، شفاف و تاریخ‌دار است.

تحلیل ریسک استفاده بی‌محابا از git add .

فرض کن در پوشه پروژه ۳ فایل تغییر کرده‌اند: دو فایل مربوط به مأموریت جدید و یک فایل شامل کلمه عبور شخصی یا فایل موقت (.env). چرا استفاده مستقیم از git add . در این شرایط یک خطای بزرگ مهندسی است؟

نکته چالش تحلیلی
به تفاوت انتخاب صریح فایل‌ها با انتخاب نابجای تمام فایل‌های پوشه فکر کن.

ثبت رویداد آغاز حرکت

خلاصه دستاوردهای درس دوم فصل دوم
تفاوت فایل‌های Modified و Untracked را شناختی.
تغییر متنی وب‌سایت را در مرورگر تست و تایید کردی.
فایل لاگ جدید journey-log.txt را ساختی.
کاربرد و ریسک کاراکتر نقطه در git add . را فرا گرفتی.
پیش‌نویس را با git diff --staged بازبینی و کامیت دوم را ثبت کردی.
لاگ ۲ کامیت اخیر را با git log -2 --oneline استعلام گرفتی.
در درس بعدی (میانبر زیر فشار)، میانبر git commit -am را بررسی می‌کنیم و محدودیت‌های آن روی فایل‌های جدید را به چالش می‌کشیم.

درس 3: میانبر زیر فشار

حرکت پیوسته در میانه راه

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

مرد باید کز طلب در انتظار
هر زمانی جان کند در ره نثار
نه زمانی از طلب ساکن شود
نه دمی آسودنش ممکن شود

عطار در حکایت شبلی، طلب را حرکتی پیوسته می‌بیند؛ جست‌وجوگر راه حتی برای لحظه‌ای از توجه به مسیر دست نمی‌کشد. در این گام یک میانبر کاربردی به نام git commit -am را می‌آزمایی و پیش از استفاده می‌فهمی چه فایل‌هایی را وارد Commit می‌کند و چه فایل‌هایی را کنار می‌گذارد.

کالبدشکافی پرچم -am

فرمان git commit -am ترکیب دو پرچم پرکاربرد است:

-a Stage All Tracked — تمام تغییرات و حذف‌های فایل‌هایی که از قبل پیگیری‌شده Tracked هستند را خودکار به محیط آماده‌سازی Staging Area می‌برد.
-m Commit Message — پیام توصیفی مربوط به نقطه داده را مستقیماً در خط فرمان دریافت می‌کند.
مرز حیاتی این میانبر در وضعیت فایل‌هاست: گزینه -a تنها فایل‌های تغییریافته Modified و یا حذف‌شده‌ای را Stage می‌کند که از قبل توسط گیت شناسایی شده‌اند. هرگز فایل‌های پیگیری‌نشده Untracked را وارد Commit نمی‌کند!

مقایسه میانبر با روش دو مرحله‌ای

انتخاب آگاهانه ابزار مناسب
روش دو مرحله‌ای (git add + commit)

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

روش میانبر git commit -am

سرعت بالا در یک خط. تنها برای زمانی مناسب است که همه فایل‌های تغییریافته Tracked مربوط به یک هدف مشخص باشند.

میانبر -am مرحله Staging Area را حذف یا دور نمی‌زند؛ بلکه گیت به صورت پنهانی ابتدا git add را روی فایل‌های Tracked اجرا کرده و سپس اقدام به ایجاد Commit می‌کند.

اعمال تغییر روی فایل Tracked و تست میانبر

در فایل index.html وضعیت فعلی را پیدا کن:

htmlindex.html
Status: The caravan has started the journey.

آن را با متن زیر جایگزین و فایل را ذخیره کن:

htmlindex.html
Status: The caravan is crossing the Valley of Quest.

حالا استعلام وضعیت کوتاه‌نوشت را بگیرید:

bash
git status --short

فایل index.html با علامت قرمز M دیده می‌شود (Modified). حالا میانبر را اجرا کن:

bash
git commit -am "Update journey status for Valley of Quest"
تغییر با موفقیت ثبت شد! گیت فایل پیگیری‌شده index.html را به صورت خودکار Stage کرد و Commit جدید را ساخت.

ساخت فایل جدید و تجربه شکست میانبر

اکنون یک فایل جدید در ریشه پروژه بساز تا رفتار میانبر را روی فایل‌های پیگیری‌نشده Untracked بیازمایی.

فایل تازه supplies.txt را بساز و متن زیر را در آن ذخیره کن:

textsupplies.txt
Water and bread supplies checked.

استعلام وضعیت بگیرید تا علامت ?? را ببینی:

bash
git status --short

اکنون عمداً تلاش کن این فایل تازه را با میانبر ثبت کنی:

bash
git commit -am "Record travel supplies"
پیام گیت را مشاهده کن! هیچ Commit تازه‌ای ساخته نشد و گیت گزارش داد که فایل‌های Untracked وجود دارند اما افزودن آن‌ها نادیده گرفته شده است.

چرا فایل جدید از میانبر جا ماند؟

راز رفتاری پرچم -a
گیت به امنیت داده‌های شما بسیار حساس است. یک فایل جدید ممکن است فایل موقت ویرایشگر، تنظیمات شخصی یا کدهای آزمایشی باشد. به همین دلیل گیت هرگز به صورت خودکار فایل‌های Untracked را به Commit اضافه نمی‌کند.

گزینه -a صرفاً پرونده‌هایی را که قبلاً شناسنامه Tracked گرفته‌اند اسکن می‌کند. برای این که یک فایل تازه وارد این چرخه شود، باید حداقل یک‌بار به صورت صریح با فرمان git add به گیت معرفی گردد.

ثبت صریح فایل جدید

برای حل این مشکل، فایل تازه را به روش صریح دو مرحله‌ای آماده‌سازی و ثبت کن:

bash
git add supplies.txt
git status --short
git commit -m "Record travel supplies"

بررسی کن:

علامت A در git status — نشان‌دهنده ورود فایل به محیط آماده‌سازی Staging Area است.
ثبت Commit — فایل supplies.txt از این پس به یک فایل Tracked تبدیل شد.
اکنون اگر در آینده supplies.txt تغییر کند، استفاده از میانبر git commit -am روی آن کار خواهد کرد!

خطر میانبر در تغییرات هم‌زمان

این قدم سناریوی فکری است؛ فایل‌ها را واقعاً تغییر نده و فرمان نمونه را اجرا نکن. پس از آن، مسیر اصلی همچنان چهار commit و وضعیت تمیز دارد.

سناریویی را تصور کن که در آن هم‌زمان دو فایل index.html و style.css را دستکاری کرده‌ای و هر دو در وضعیت Modified هستند.

اگر کار شما روی index.html تمام شده باشد اما استایل‌ها هنوز نیمی کاره باشند، اجرای ناآگاهانه میانبر زیر چه خطری دارد؟

bash
git commit -am "Fix index html text"
پرچم -a بدون استثنا تمام فایل‌های Tracked تغییریافته را وارد این Commit می‌کند؛ بنابراین استایل‌های نیمه‌کاره شما نیز ناخواسته ثبت می‌شوند!
در این شرایط همواره از git add index.html استفاده کن تا فقط فایل آماده‌شده ثبت گردد.

سنجش دقیق رفتار -am

اگر دو فایل تغییریافته Tracked و یک فایل جدید Untracked در پروژه داشته باشیم و فرمان git commit -am اجرا شود، چه اتفاقی می‌افتد؟

میانبر اکنون قابل‌کنترل است

خلاصه و دستاوردهای این گام
شناخت ترکیب پرچم‌های -a (اماده‌سازی خودکار Trackedها) و -m (پیام توصیفی).
درک این که فایل‌های Untracked هرگز با -am ثبت نمی‌شوند و نیاز به git add دارند.
آگاهی از خطر ثبت ناخواسته چند فایل Tracked تغییریافته به صورت هم‌زمان.

اکنون Repository شما دارای چند نقطه ثبت شده واقعی است. در درس بعدی مسیر طی‌شده را با استعلام لاگ git log پیمایش و جزییات یک Commit را بررسی خواهیم کرد.

درس 4: ردگیری یک تغییر در تاریخچه

راهی که پشت سر مانده

کاروان در وادی طلب چند نشانه واقعی ساخته است: صفحه آغاز سفر، گزارش حرکت و فهرست آذوقه. حالا هدهد از کاتب می‌پرسد: «دقیقاً در کدام نقطه، وضعیت سفر وارد وادی طلب شد؟» پاسخ این پرسش در فایل‌های امروز نیست؛ باید رد پای ثبت‌های گذشته را دنبال کنیم.

گر تو هم روزی فروآیی به راه
عقبهٔ آن ره کنی یک یک نگاه
بازدانی آنچ ایشان کرده‌اند
روشنت گردد که چون خون خورده‌اند

عطار می‌گوید فهم رنج راه با نگاه‌کردن به عقبه‌هایی ممکن می‌شود که مسافر یکی‌یکی پشت سر گذاشته است. تاریخچه گیت هم راه پیموده‌شده پروژه را به ترتیب Snapshotهای ثبت‌شده نشان می‌دهد. در این درس با ابزارهای git log و git show تاریخچه را رمزگشایی می‌کنیم.

معماری و اجزای یک کامیت

شناسنامه چهارگانه هر ثبت historical

هر Commit در دیتابیس گیت یک شیء مستقل و تغییرناپذیر است که از چهار بخش اصلی تشکیل می‌شود:

شناسه هش SHA-1 Hash — کد ۴۰ کاراکتری منحصربه‌فرد که بر اساس محتوای دقیق تغییرات تولید می‌شود (مانند a1b2c3d...).
نویسنده Author — نام و ایمیل فردی که تغییرات را ایجاد و ثبت کرده است.
زمان Date — تاریخ و ساعت دقیق ثبت نقطه داده در سیستم.
پیام توصیفی Commit Message — توضیحات انسانی که دلیل و هدف این ثبت تاریخی را بیان می‌کند.
هر کامیت هم‌چنین اشاره‌گری به کامیت‌های قبلی (والد خود) دارد که این پیوندها زنجیره شکست‌ناپذیر تاریخچه پروژه را می‌سازند.

نقشه فشرده سفر با git log --oneline

برای دیدن تمام ثبت‌های پروژه از جدیدترین به قدیمی‌ترین در یک نمای تک‌خطی و فشرده، دستور زیر را اجرا کن:

bash
git log --oneline --decorate
تحلیل خروجی لاگ
هش کوتاه ۷ کاراکتری — در ابتدای هر سطر جهت ارجاع سریع به کامیت.
برچسب HEAD -> main — نشان‌دهنده جایگاه فعلی شاخه اصلی و اشاره‌گر اصلی گیت.
متن پیام کامیت — عنوان خلاصه ثبت انجام‌شده.

استعلام شناسنامه کامل با git log -1

اگر بخواهی جزییات کامل و شناسنامه تازه‌ترین Commit را مشاهده کنی، از پرچم محدودکننده -1 استفاده کن:

bash
git log -1

خروجی را بررسی کن: کد هش ۴۰ کاراکتری، نام نویسنده، تاریخ کامل و متن پیام نمایش داده می‌شوند.

اگر خروجی لاگ طولانی بود و در صفحه جداگانه باز شد، کلید q را فشار بده تا به محیط ترمینال برگردی.

دیدن گراف خطی با git log --graph

برای مشاهده ساختار درختی و پیوندهای میان Commitها، نمای گرافی لاگ را اجرا کن:

bash
git log --oneline --graph --decorate --all

چون تا این لحظه شاخه فرعی نساخته‌ایم، یک خط مستقیم متصل را می‌بینی که تمام نقاط ثبت‌شده را به ترتیب زمانی نشان می‌دهد و آخرین نقطه با برچسب HEAD -> main علامت خورده است.

محدود کردن تعداد لاگ با پرچم -n

در پروژه‌های بزرگ با هزاران ثبت، دیدن کل لاگ کارآمد نیست. با پرچم -n می‌توانی تعداد سطرها را محدود کنی:

bash
git log --oneline --decorate -n 3

اکنون تنها ۳ کامیت اخیر نمایش داده شده و کامیت‌های قدیمی‌تر پنهان می‌شوند. این پرچم چیزی را پاک نمی‌کند، فقط نمای نمایش را محدود می‌سازد.

نمایش معکوس تاریخچه با git log --reverse

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

bash
git log --oneline --reverse

نخستین کامیت پروژه با پیام Create initial journey website در سطر اول ظاهر می‌شود. هش کوتاه ۷ کاراکتری آن را کپی کن؛ در گام بعدی با آن کار داریم.

ذره‌بین git show و بررسی جزئیات یک کامیت

عبارت COMMIT_HASH جای‌نگهدار است. پیش از اجرا، آن را با شناسه واقعی اولین commit که در قدم قبل یادداشت کردی جایگزین کن؛ خود کلمه COMMIT_HASH نام یک commit نیست.

برای باز کردن یک Commit مشخص و مشاهده کدها و تغییرات دقیق آن، از فرمان git show استفاده کن:

bash
git show COMMIT_HASH
git show --stat COMMIT_HASH
تحلیل خروجی ذره‌بین
خطوط سبز با + — خطوطی که در این کامیت جدید اضافه شده‌اند.
خطوط قرمز با - — خطوطی که در این کامیت حذف یا ویرایش یافته‌اند.
دستور --stat — ارائه آمار خلاصه تغییرات فایل‌ها بدون نمایش خط‌به‌خط کدها.

تفاوت git log و git show

هدهد می‌خواهد اول کامیت درست را پیدا کند و بعد تغییرهای همان کامیت را ببیند. در یک یا دو جمله توضیح بده git log و git show چه نقش متفاوتی در این مسیر دارند.

تحویل سالم وادی طلب

پیش از ترک وادی طلب، وضعیت کلی پروژه و ثبت‌های اخیر را یک‌جا کنترل کن:

bash
git status --short
git log --oneline --decorate -n 4
فرمان اول هیچ خروجی ندارد (پوشه کاری Working Tree تمیز است) و فرمان دوم ۴ کامیت معتبر روی شاخه main را تایید می‌کند.

نشان خواننده تاریخچه و پایان وادی طلب

پایان پیروزمندانه فصل دوم (وادی طلب)
شناخت معماری سه‌منطقه‌ای Working Tree و Index و HEAD.
تسلط بر کامیت‌های منسجم و استفاده از git add . و git diff --staged.
استفاده از میانبر git commit -am و درک مرزهای آن.
استعلام حرفه‌ای لاگ با git log و بررسی کدهای یک کامیت با git show.
با موفقیت از وادی طلب عبور کردی! در فصل سوم وارد «وادی عشق» شده و بررسی و بازگرداندن تغییرها را می‌آموزیم؛ شاخه‌سازی Branching و حل تعارضات مربوط به فصل چهارم است.

فصل 3: وادی عشق

درس 1: آینه پیش از ثبت

ورود به آتش وادی عشق

کاروان از وادی طلب گذشته و به سرزمینی رسیده است که هر تصمیم در آن با شور و شتاب همراه می‌شود. هدهد از کاتب می‌خواهد پیش از ثبت هر خبر، آن را در آینه دقیق ببیند؛ چرا که هیجانِ تغییر می‌تواند یک اشتباه کوچک را هم وارد تاریخچه پروژه کند.

بعد ازین وادی عشق آید پدید
غرق آتش شد کسی کانجا رسید
کس درین وادی به جز آتش مباد
وانک آتش نیست عیشش خوش مباد

دیدن تفاوت‌ها پیش از ثبت تاریخی

عطار وادی عشق را با آتشی آغاز می‌کند که مسافر را بی‌تفاوت نمی‌گذارد. در پروژه نرم‌افزاری هم تغییر لازم است، اما مهندس واقعی پیش از اجرای Commit چشمانش را به تفاوت نسخه‌ها می‌دوزد. در این مأموریت با ابزار git diff یاد می‌گیریم چطور تغییرات پوشه کاری Working Tree را خط‌به‌خط بخوانیم.

کالبدشکافی مرز git diff

فرمان git diff بدون پرچم اضافی، پوشه کاری Working Tree را با محیط آماده‌سازی Index / Staging Area مقایسه می‌کند:

چهار بخش اصلی یک خروجی Patch
هدر فایل diff --git a/file b/file — مشخص‌کننده نام فایل قدیمی و نسخه جدید روی دیسک.
کد هش شاخص index 1234567..89abcdef — شناسنامه محتوایی نسخه قبلی و فعلی در گیت.
هدر تکه تغییرات Hunk @@ -1,5 +1,6 @@ — محدوده دقیق خطوط تغییریافته شامل شماره خط قدیمی و جدید.
خطوط تغییر + و - — خطوط قرمز با علامت - نشان‌دهنده حذف و خطوط سبز با علامت + نشان‌دهنده اضافه شدن هستند.
فایل‌های پیگیری‌نشده Untracked در این مقایسه نشان داده نمی‌شوند؛ زیرا گیت هنوز هیچ نسخه‌ای از آن‌ها را در Index ثبت نکرده است.

رسیدن کاروان به وادی عشق

در نرم‌افزار VS Code فایل index.html را باز کن و خط زیر را پیدا کن:

htmlindex.html
Status: The caravan is crossing the Valley of Quest.

همین متن را دقیقاً با جمله جدید زیر جایگزین و فایل را ذخیره کن:

htmlindex.html
Status: The caravan has entered the Valley of Love.

صفحه را در مرورگر وب Refresh کن. با دیدن وضعیت تازه مطمئن می‌شوی تغییر روی دیسک در پوشه کاری Working Tree درست ذخیره شده است.

ساخت گزارش قابل‌ردگیری سفر

محتوای فعلی فایل journey-log.txt را با گزارش ساختاریافته زیر جایگزین و ذخیره کن:

textjourney-log.txt
SIMORGH JOURNEY LOG
Departure: Confirmed
Guide: Hudhud
Destination: Mount Qaf
Map: Draft
Current valley: Love
Weather: Clear
Water: Checked
Bread: Checked
Camp: Ready
Watch: Active
Formation: Stable
Supplies: Checked
Song: Draft
Next checkpoint: Unknown
فایل را ذخیره کن، اما فعلاً دستور git add را اجرا نکن. هر دو تغییر باید فقط در Working Tree باقی بمانند.

استعلام وضعیت و نماد M قرمز

وضعیت فشرده ترمینال را بررسی کن:

bash
git status --short
تحلیل وضعیت فایل‌ها
نماد M قرمز index.html — تغییر یافته در Working Tree اما هنوز Stage نشده است.
نماد M قرمز journey-log.txt — تغییر یافته در Working Tree اما هنوز Stage نشده است.
حرف M در ستون راست (قرمز) نشان می‌دهد که محتوای فایل‌ها روی دیسک با نسخه موجود در Index تفاوت دارد.

خواندن Patch واقعی با git diff

برای دیدن تغییرات خط‌به‌خط، فرمان زیر را اجرا کن:

bash
git diff
تحلیل خروجی آینه diff

به خطوط قرمز با علامت - و خطوط سبز با علامت + توجه کن. بخش‌های با علامت @@ همان Hunk یا تکه تغییرات هستند.

اگر خروجی لاگ یا diff در محیط Pager باز شد، کلید q را فشار بده تا خارج شوی.

تمرکز روی صفحه اصلی با git diff -- filename

وقتی چند فایل تغییر کرده‌اند، می‌توانی با اضافه کردن جداکننده -- و نام فایل، فقط تغییرات همان فایل را ایزوله کنی:

bash
git diff -- index.html

علامت -- به گیت می‌گوید عبارت بعدی نام مسیر فایل است، نه نام شاخه Branch یا Commit.

اندازه مأموریت در یک نگاه (git diff --stat)

برای دیدن آمار خلاصه‌شده تغییرات بدون نمایش متن کامل کدها، از پرچم --stat استفاده کن:

bash
git diff --stat
تحلیل خروجی آمار
تعداد خطوط اضافه شده با + — تعداد سطرهایی که در پوشه کاری جدید اضافه شده‌اند.
تعداد خطوط حذف شده با - — تعداد سطرهایی که پاک یا ویرایش یافته‌اند.

تغییری که در آینه دیده نمی‌شود

کاتب فایل تازه‌ای ساخته و در git status --short آن را با علامت ?? می‌بیند، اما git diff هیچ متنی برای آن نشان نمی‌دهد. چرا؟

تغییرها دیده شدند، نه ثبت

خلاصه دستاوردهای این گام
یادگیری مقایسه غیرآماده پوشه کاری Working Tree با Index.
شناخت اجزای خروجی Patch و ساختار تکه‌تکه Hunk.
تمرکز روی یک فایل خاص با git diff -- filename.
مشاهده آمار فشرده با git diff --stat.
در درس بعدی، آینه را به سوی Index می‌گیریم و تفاوت‌های آماده‌شده برای کامیت را بررسی می‌کنیم.

درس 2: انتخاب تکه‌های هم‌هدف

ماهی میان دو سوی تغییر

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

می‌تپد پیوسته در سوز و گداز
تا بجای خود رسد ناگاه باز
ماهی از دریا چو بر صحرا فتد
می‌تپد تا بوک در دریا فتد

عطار بی‌قراری ماهی برای بازگشت به جای خود را چنین تصویر می‌کند. تغییرهای پروژه نیز ممکن است در Working Tree یا Index قرار داشته باشند. در این درس با سه مقایسه جای دقیق هر تغییر را پیدا می‌کنی و دو اصلاح مستقل در یک فایل را به دو Commit منسجم و شفاف تقسیم می‌نمایی.

سه پنجره برای سه مرز diff

مقایسه سه حالت متغیر در گیت

برای آن که بدانی کدها در چه مرحله‌ای از چرخه گیت هستند، سه دستور اصلی مقایسه وجود دارد:

git diff (غیرآماده) — مقایسه پوشه کاری Working Tree با محیط آماده‌سازی Index. فقط تغییرات Stage نشده را نشان می‌دهد.
git diff --staged (آماده کامیت) — مقایسه محیط آماده‌سازی Index با آخرین کامیت دیتابیس HEAD. کدهایی که قرار است وارد کامیت بعدی شوند.
git diff HEAD (کل تغییرات) — مقایسه مستقیم پوشه کاری Working Tree با آخرین کامیت HEAD. کل تغییرات آماده‌شده و آماده‌نشده را یک‌جا نشان می‌دهد.
عبارت --cached کاملاً هم‌معنای پرچم --staged است و هر دو تغییرات موجود در Index را نسبت به HEAD می‌سنجند.

مفهوم آماده‌سازی تکه‌ای (git add -p)

جداسازی تغییرات چندگانه در یک فایل

گاهی در طول یک روز کاری، چند بخش مختلف از یک فایل را دستکاری می‌کنی (مثلاً هم یک باگ را برطرف می‌کنی و هم استایل جدید می‌نویسی). آیا باید همه را در یک کامیت آشفته ثبت کنی؟

ثبت یکباره فایل با git add

تمام تغییرات فایل یکجا Stage می‌شوند، حتی اگر مربوط به دو موضوع کاملاً نامرتبط باشند.

ثبت تکه‌ای با پرچم پچ git add -p

گیت تغییرات فایل را به تکه‌های کوچک Hunk تقسیم کرده و اجازه می‌دهد هر تکه را جداگانه Stage کنی.

این قابلیت به شما امکان می‌دهد کامیت‌های تمیز، منسجم و تک‌هدفه Atomic Commit بسازید، حتی اگر تمام تغییرات در یک فایل رخ داده باشند.

فرستادن فقط صفحه به Index و تست مرزها

از دو فایل تغییرکرده، فعلاً فقط فایل index.html را Stage کن و دو مقایسه را پشت سر هم ببین:

bash
git add index.html
git diff -- index.html
git diff --staged -- index.html
تحلیل دو مقایسه
خروجی git diff اول — خالی است؛ زیرا نسخه index.html در Working Tree و Index کاملاً یکسان شد.
خروجی git diff --staged دوم — تغییر آماده‌شده صفحه را نسبت به HEAD نشان می‌دهد (آماده ثبت در کامیت).

دیدن هر دو سوی پروژه با --stat

حالا مرزها را در مقیاس کل پروژه و به صورت خلاصه‌شده مقایسه کن:

bash
git diff --stat
git diff --staged --stat
git diff HEAD --stat
تفسیر آمار سه فرمان

دستور اول فقط journey-log.txt را نشان می‌دهد (غیرآماده)، دستور دوم فقط index.html را (آماده) و دستور سوم هر دو فایل را به طور هم‌زمان نمایش می‌دهد.

پیداکردن جای هر تغییر در معماری سه منطقه‌ای

پس از Stage شدن index.html و Stage نشدن journey-log.txt، چرا git diff فقط گزارش سفر را نشان می‌دهد ولی git diff HEAD هر دو فایل را می‌بیند؟

ثبت ورود کاروان به وادی عشق

گزارش سفر را هم Stage کن، عکس کامل آماده‌شده را بازبینی کرده و ثبت کن:

bash
git add journey-log.txt
git diff --staged --stat
git commit -m "Record arrival in Valley of Love"
git status --short
هر دو فایل مربوط به مأموریت ورود به وادی عشق بودند و اکنون در یک Commit منسجم جای گرفتند. خروجی خالی دستور آخر پاکیزگی Working Tree را تایید می‌کند.

ایجاد دو تغییر مستقل در یک فایل

در فایل journey-log.txt دو جای مختلف و دور از هم را ویرایش کن:

۱. خط Map: Draft را به Map: Reviewed تغییر بده.

۲. خط Song: Draft را به Song: Final تغییر بده و فایل را ذخیره کن.

سپس تفاوت متنی را مشاهده کن:

bash
git diff -- journey-log.txt

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

انتخاب تعاملی تکه‌ها با git add -p

برای اینکه تنظیمات شخصی نمایش diff دو تکه دور از هم را یکی نکند، در همین فرمان فاصله نمایشی را مشخص می‌کنیم؛ این گزینه‌های -c تنظیمات دائمی تو را تغییر نمی‌دهند.

حالا حالت تعاملی پچ را برای این فایل فعال کن:

bash
git -c diff.context=3 -c diff.interHunkContext=0 add -p journey-log.txt
پاسخ به پرسش‌های تعاملی گیت
کلید y جهت تأیید Yes — برای Hunk مربوط به Map کلید y و سپس Enter را بزن تا این تکه Stage شود.
کلید n جهت رد کردن No — برای Hunk مربوط به Song کلید n و سپس Enter را بزن تا در Working Tree باقی بماند.
کلید y تغییر را وارد Index می‌کند و کلید n تغییر را در Working Tree نگه‌می‌دارد.

یک فایل در دو وضعیت و ثبت کامیت‌های جداگانه

محتوای دو سوی فایل را جداگانه بازبینی کن:

bash
git diff --staged -- journey-log.txt
git diff -- journey-log.txt

دستور اول فقط تغییر Map را در Index نشان می‌دهد و دستور دوم تغییر Song را در Working Tree می‌بیند. حالا کامیت اول را ثبت کن:

bash
git commit -m "Review caravan map"

سپس تغییر باقی‌مانده را Stage کرده و کامیت دوم را بساز:

bash
git add journey-log.txt
git commit -m "Finalize caravan song"
git status --short
آفرین! یک فایل با دو هدف مجزا را به دو کامیت تک‌هدفه و استاندارد Atomic Commit تبدیل کردی.

Index به ابزار هوشمند انتخاب تبدیل شد

خلاصه دستاوردهای این درس
تسلط بر مرزهای ۳ گانه git diff و git diff --staged و git diff HEAD.
درک این که یک فایل می‌تواند هم‌زمان بخش Staged و Unstaged داشته باشد.
استفاده از فرمان تعاملی git add -p برای تفکیک Hunkهای نامرتبط.
خلق کامیت‌های پاکیزه و منسجم Atomic Commit.
در درس بعدی، طوطی کاروان رازهای محلی را حفظ می‌کند! یاد می‌گیریم چطور فایل‌های محرمانه و موقت را با فایل .gitignore از چشم گیت پنهان نگه داریم.

درس 3: رازهای محلی کاروان

طوطی و راز آب حیات

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

طوطی آمد با دهان پر شکر
در لباس فستقی با طوقِ زر
من در این زندانِ آهن مانده باز
زآرزوی آب خضرم در گداز

عطار ظاهر زیبا و آرزوی پنهان طوطی را کنار هم می‌آورد. این حکایت در پروژه یادآور فایل‌های محلی و تنظیمات شخصی است؛ فایل‌هایی که وجودشان روی رایانه شما لازم است، اما نباید وارد دیتابیس عمومی Repository شوند.

فیلتر انتخاب، نه گاوصندوق

کارکرد واقعی فایل .gitignore

فایل .gitignore متنی شامل الگوهای متنی Ignore Pattern است که نحوه تعامل گیت با فایل‌های نادیده‌گرفته‌شده را مشخص می‌کند:

پنهان‌سازی از گزارش status — فایل‌های نادیده‌گرفته‌شده با دستور git status دیگر در لیست زرد/قرمز نشان داده نمی‌شوند.
محافظت در برابر git add . — اجرای دستور git add . به صورت خودکار فایل‌های پنهان‌شده را نادیده گرفته و Stage نمی‌کند.
عدم تأثیر روی فایل‌های گذشته — اگر فایلی قبلاً کامیت شده Tracked باشد، افزودن نام آن به .gitignore آن را خودکار از دیتابیس پاک نمی‌کند.
توجه: فایل .gitignore یک ابزار امنیتی یا رمزنگاری نیست! اگر کلید محرمانه یا کلمه عبوری را اشتباهاً کامیت کرده‌اید، تنها نادیده گرفتن آن کافی نیست و باید بلافاصله آن کلید را باطل و جایگزین کنید.

ساخت دو فایل محلی و موقت

در ریشه پروژه فایل .env.local را بساز و مقدار نمایشی زیر را در آن ذخیره کن:

bash.env.local
API_TOKEN=demo-only-do-not-use

فایل دوم را با نام cache.tmp بساز و متن زیر را قرار بده:

textcache.tmp
local preview cache
این دو فایل نمونه‌هایی از تنظیمات شخصی محیط توسعه و خروجی‌های موقت سیستم هستند که نباید وارد تاریخچه گیت شوند.

دیدن فایل‌های ناخواسته پیش از فیلتر

پیش از تعریف قوانین پنهان‌سازی، استعلام وضعیت ترمینال را بررسی کن:

bash
git status --short
تحلیل خروجی
علامت ?? .env.local — فایل ردیابی‌نشده Untracked آماده ورود به Index
علامت ?? cache.tmp — فایل موقت Untracked آماده ورود به Index
در این حالت اگر دستور git add . بدون بازبینی اجرا شود، هر دو فایل ناخواسته وارد Staging Area خواهند شد.

نوشتن نخستین قوانین Ignore

در ریشه پوشه پروژه فایلی به نام .gitignore بساز و این دو الگو را در آن ذخیره کن:

bash.gitignore
.env.local
*.tmp
مفهوم الگوها
خط اول (.env.local) — نام .env.local را در ریشه و زیرپوشه‌ها نادیده می‌گیرد؛ برای محدود کردن به ریشه از /.env.local استفاده می‌شود.
خط دوم (*.tmp) — هر فایلی با پسوند tmp. را در تمام پوشه‌های پروژه پنهان می‌کند.

پیداکردن قانونی که فایل را پنهان کرد با git check-ignore

گزارش وضعیت و دلیل دقیق پنهان شدن فایل‌ها را با ابزار git check-ignore عیب‌یابی کن:

bash
git status --short
git check-ignore -v .env.local cache.tmp
تحلیل خروجی عیب‌یابی

در خروجی دستور اول، فقط خود فایل .gitignore با علامت ?? دیده می‌شود. خروجی دستور دوم نام فایل قوانین، شماره خط و الگوی دقیق نادیده‌گرفته‌شدن هر فایل را نشان می‌دهد.

قانون ویژه پوشه موقت ریشه (/temp/)

این خط جدید را به انتهای فایل .gitignore اضافه کن:

bash.gitignore
/temp/

سپس در ریشه پروژه پوشه‌ای به نام temp بساز و فایلی به نام route.txt داخل آن ایجاد کن:

bash
git check-ignore -v temp/route.txt
اسلش آغازین / الگو را به ریشه مخزن محدود می‌کند و اسلش پایانی نشان می‌دهد این قانون مخصوص یک پوشه Directory است.

آزمون امن git add .

حالا با خیال راحت دستور انتخاب همه‌جانبه را اجرا کرده و ورود فایل‌ها را بررسی کن:

bash
git status --short
git add .
git status --short
مشاهده می‌کنی که پس از اجرای دستور دوم، فقط A .gitignore وارد Index شده است و فایل‌های محلی نادیده‌گرفته‌شده روی دیسک باقی مانده اما وارد Stage نشدند!

رازی که قبلاً وارد تاریخچه شده

فایل config.env قبلاً کامیت شده و اکنون نامش به .gitignore اضافه شده است. چرا گیت هنوز تغییرات آن را ردیابی می‌کند و برای یک راز واقعی چه کارهایی لازم است؟

ثبت قوانین مشترک پروژه

محتوای آماده‌شده فایل قوانین را بازبینی و در یک Commit ثبت کن:

bash
git diff --staged -- .gitignore
git commit -m "Add ignore rules for local files"
git status --short
فایل .gitignore خود بخشی از کد پروژه است که با سایر اعضای تیم به اشتراک گذاشته می‌شود. اکنون Working Tree کاملاً پاکیزه است.

رازها از دفتر مشترک بیرون ماندند

خلاصه دستاوردهای این درس
فهم کارکرد فیلترکننده .gitignore برای فایل‌های محلی و موقت.
استفاده از الگوی پسوندی (*.tmp) و الگوی پوشه‌ای /temp/،
عیب‌یابی قوانین با دستور کاربردی git check-ignore -v.
درک تفاوت نادیده‌گرفتن فایل‌های Untracked با پاک‌سازی فایل‌های از قبل Trackedشده.
در درس بعدی، اشتباه روی یک فایل ردیابی‌شده اتفاق می‌افتد. یاد می‌گیریم چطور با دستور git restore پوشه کاری را به نسخه امن بازگردانیم.

درس 4: بازگرداندن آواز درست

بلبل و دل‌بستگی ناپایدار

بلبل چنان شیفته گل شده بود که هیچ راه دیگری را نمی‌دید. هدهد به او گفت زیبایی گل ماندگار نیست و مسافر باید بتواند انتخابی را که از مسیر دورش می‌کند کنار بگذارد.

هدهدش گفت: ای به صورت مانده باز!
بیش از این در عشق رعنایی مناز
گل اگر چه هست بس صاحب‌جمال
حسن او در هفته‌ای گیرد زوال

پیوند داستان با ابزار کنترل نسخه

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

در این مأموریت پیش از هر بازیابی، تغییر را با Diff می‌بینی. سپس فقط وقتی مطمئن شدی، Working Tree را به نسخه سالم موجود در Index برمی‌گردانی.

منبع و مقصد git restore

ساختار عملکرد فرمان git restore

دستور git restore PATH دارای دو جهت مشخص در معماری گیت است:

منبع پیش‌فرض (Source) — محیط آماده‌سازی Index یا همان Staging Area است.
مقصد بازیابی (Destination) — پوشه کاری روی دیسک یا Working Tree است.

اگر فایلی هنوز Stage نشده باشد، محتوای Index با آخرین کامیت HEAD یکسان است؛ در نتیجه بازیابی، فایل را به حالت آخرین Commit برمی‌گرداند.

توجه: اجرای git restore تغییرات محلی ذخیره‌نشده در پوشه کاری را کاملاً جایگزین می‌کند و قابل بازگشت نیست. همچنین فایل‌های Untracked نسخه‌ای در Index ندارند و با این دستور دستکاری نمی‌شوند.

ساخت یک پیام آشکارا اشتباه

برای تجربه عملکرد بازسازی، ابتدا یک تغییر اشتباه محلی در پوشه کاری ایجاد می‌کنیم.

موقعیت فعلی در فایل index.html
htmlindex.html
Status: The caravan has entered the Valley of Love.
ویرایش ناخواسته

عبارت بالا را با پیام اشتباه زیر جایگزین کرده و فایل را ذخیره کن:

htmlindex.html
Status: ROUTE DATA LOST.
1
فایل index.html را در VS Code باز کن.
2
متن وضعیت را با عبارت Status: ROUTE DATA LOST. جایگزین کن.
3
فایل را ذخیره کن (Ctrl + S یا Cmd + S).
نکته: فایل را فقط ذخیره کن و دستور git add را اجرا نکن تا تغییر صرفاً در Working Tree باقی بماند.

دیدن چیزی که قرار است حذف شود

پیش از بازگرداندن فایل، همواره باید تغییرات ایجادشده را عیب‌یابی و بررسی کنی تا محتوای باارزشی را اشتباهاً پاک نکنی.

دستورهای استعلام وضعیت و تغییرات
bashترمینال اصلی
git status --short
git diff -- index.html
تحلیل خروجی دستورها
علامت M index.html — نشان می‌دهد فایل در Working Tree تغییر کرده اما Stage نشده است.
خروجی git diff — سطر قرمز با علامت - متن قبلی و سطر سبز با علامت + پیام اشتباه را نشان می‌دهد.
استفاده از علامت -- در دستور diff، مسیر فایل را از آرگومان‌ها و گزینه‌های گیت تفکیک می‌کند.

پیش‌بینی نسخه بازسازی‌شده

هیچ تغییری Stage نشده و محتوای Index با HEAD یکسان است.

اگر اکنون دستور git restore index.html اجرا شود، کدام نسخه وارد Working Tree می‌شود و چه بلایی سر پیام اشتباه می‌آید؟

منبع پیش‌فرض Restore برای پوشه کاری، نسخه موجود همان مسیر در Index است.

بازیابی صفحه و تأیید در مرورگر

پس از اطمینان از اشتباه‌بودن تغییر، با دستور زیر فایل را به حالت سالم Index بازگردان:

دستور اجرای بازسازی
bashترمینال اصلی
git restore index.html
git status --short
1
دستور git restore index.html را در ترمینال اجرا کن.
2
دستور git status --short را بزن؛ خروجی باید کاملاً خالی باشد.
3
صفحه index.html را در مرورگر Refresh کن.
جمله Status: The caravan has entered the Valley of Love. دوباره در مرورگر ظاهر شد و تغییر ناخواسته پاک گردید.

بازسازی فایل حذف‌شده

دستور git restore تنها برای لغو ویرایش خطوط نیست؛ اگر فایلی که در گیت Tracked است به طور کامل از روی دیسک حذف شود نیز می‌توان آن را بازسازی کرد.

حذف آزمایشی فایل style.css

فایل style.css را از پنل فایل‌های VS Code یا ترمینال حذف کن، سپس وضعیت را استعلام کرده و آن را بازیابی کن:

bashترمینال اصلی
git status --short
git restore style.css
git status --short
حالت اول (پس از حذف) — دستور status عبارت D style.css را نمایش می‌دهد.
حالت دوم (پس از restore) — فایل style.css دوباره روی دیسک ظاهر شده و خروجی status خالی می‌شود.
چرا فایل برگشت؟ چون نسخه‌ای کامل از style.css در Index ذخیره شده بود و Git توانست آن را روی دیسک بازنویسی کند.

دامنه نقطه در دو فایل

وقتی در چندین فایل تغییرات آزمایشی ایجاد کرده‌ای، می‌توانی همه آن‌ها را یکجا با پرچم نقطه . لغو کنی.

ایجاد تغییر در دو فایل

در journey-log.txt عبارت Weather: Clear را به Weather: Storm تغییر بده و کل محتوای supplies.txt را با عبارت زیر جایگزین کن:

textsupplies.txt
Supplies unavailable.
بررسی خلاصه تغییرات و بازسازی یکباره
bashترمینال اصلی
git diff --stat
git restore .
git status --short
دستور git diff --stat — آماری فشرده از تعداد خطوط تغییریافته در هر فایل ارائه می‌دهد.
دستور git restore . — تمام فایل‌های Tracked تغییریافته زیر پوشه جاری را یکجا بازسازی می‌کند.
نکته مهم: علامت نقطه . فایل‌های نادیده‌گرفته‌شده (Ignored) یا پیگیری‌نشده (Untracked) را حذف نمی‌کند؛ بلکه صرفاً مسیرهای قابل‌بازسازی از Index را هدف می‌گیرد.

فایل تازه بدون نسخه پشتیبان

این پرسش فرضی است؛ draft.txt را نساز و فرمان خطادار را اجرا نکن. تمرین عملی قبلی تمام شده و وضعیت پروژه باید تمیز بماند.

فرض کن فایلی جدید به نام draft.txt در پروژه ساخته‌ای که هنوز Untracked است و هرگز به گیت معرفی نشده است.

پرسش: اجرای git restore draft.txt چه نتیجه‌ای دارد؟
خطای گیت: error: pathspec draft.txt did not match any file(s) known to git
دلیل خطا: برای اینکه Git بتواند فایلی را بازسازی کند، باید نسخه‌ای از آن در Index یا Commit وجود داشته باشد. فایل‌های Untracked هیچ پشتیبانی در گیت ندارند.

بازگشت اکنون یک تصمیم آگاهانه است

خلاصه دستاوردهای این درس
مشاهده و تحلیل تغییرات ناخواسته با git diff پیش از بازگرداندن
بازیابی ویرایش‌های محلی فایل‌های Tracked با دستور git restore
بازسازی کامل فایل‌های حذف‌شده از دیسک از طریق نسخه موجود در Index
لغو یکباره تمام تغییرات محلی پوشه کاری با دستور git restore .
درک تفاوت فایل‌های Tracked و Untracked در فرایند بازیابی
در آخرین مأموریت این فصل، می‌بینیم که اگر تغییری به اشتباه Stage شده باشد، چطور با پرچم git restore --staged آن را از Index خارج کنیم بدون اینکه ویرایش فایل روی دیسک از دست برود.

درس 5: پس‌گرفتن انتخاب شتاب‌زده

برگه‌ای که زود روی میز رفت

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

داستان آخرین منزل وادی عشق

این بار قرار نیست نوشته روی برگه را پاک کنیم. هدف این است که برگه را از روی میز آماده‌سازی Staging Area برداریم، محتوا را روی دیسک Working Tree نگه داریم و پس از بازبینی کامل درباره Commit تصمیم بگیریم.

در این مأموریت یاد می‌گیریم چطور انتخاب Stage را لغو کنیم بدون اینکه حتی یک کاراکتر از فایل روی دیسک پاک شود.

دو Restore با دو مقصد

مقایسه دو فرم دستور git restore

فرمان git restore بر اساس پرچم ورودی، دو مقصد کاملاً متفاوت در معماری گیت دارد:

git restore PATH

مقصد: Working Tree
منبع: Index
تغییرات محلی روی دیسک را پاک کرده و فایل را از Index بازنویسی می‌کند.

git restore --staged PATH

مقصد: Index
منبع: HEAD
انتخاب Stage را لغو کرده اما فایل روی دیسک را دست‌نخورده نگه می‌دارد.

در گذشته دستور git reset HEAD PATH برای این کار استفاده می‌شد، اما معرفی پرچم --staged در دستور جدید git restore قصد دقیق مهندس را شفاف‌تر نشان می‌دهد.

نکته ایمنی: پرچم --staged هرگز محتوای فایل شما در Working Tree را دستکاری یا پاک نمی‌کند؛ صرفاً آن را از صف ثبت خارج می‌سازد.

نوشتن مقصد بعدی

ابتدا تغییر واقعی مقصد کاروان را در گزارش سفر ایجاد می‌کنیم.

موقعیت فعلی در فایل journey-log.txt
textjourney-log.txt
Next checkpoint: Unknown
ویرایش و تعیین مقصد جدید

سطر بالا را با عبارت مقصد وادی بعدی جایگزین کرده و فایل را ذخیره کن:

textjourney-log.txt
Next checkpoint: Valley of Knowledge
1
فایل journey-log.txt را در VS Code باز کن.
2
عبارت Next checkpoint: Unknown را پیدا کرده و به Next checkpoint: Valley of Knowledge تغییر بده.
3
فایل را ذخیره کن (Ctrl + S یا Cmd + S).
این تغییر واقعی پروژه است، اما پیش از ثبت نهایی باید چرخه Stage و Unstage را روی آن تمرین کنیم.

دیدن نسخه‌ای که زود آماده شد

فایل را به محیط آماده‌سازی اضافه کن و وضعیت Stage آن را بررسی نما:

افزودن تغییر به Staging Area و بررسی
bashترمینال اصلی
git add journey-log.txt
git status --short
git diff --staged -- journey-log.txt
تحلیل خروجی دستورها
حرف M در ستون چپ — نشان‌دهنده این است که تغییرات فایل با موفقیت وارد Index یا همان Staging Area شده است.
خروجی git diff --staged — تغییرات آماده ثبت برای کامیت بعدی را خط‌به‌خط نشان می‌دهد.
پرچم --staged به گیت می‌گوید فقط تغییرات موجود در Index را مقایسه کن، نه تغییرات غیرآماده پوشه کاری را.

برداشتن فایل از Index

فرض کن تصمیم گرفته‌ای این تغییر هنوز وارد کامیت نشود. فقط انتخاب Stage را پس بگیر:

فرمان پس‌گرفتن انتخاب (Unstage)
bashترمینال اصلی
git restore --staged journey-log.txt

پشت صحنه چه اتفاقی افتاد؟ Git نسخه journey-log.txt موجود در Index را از آخرین کامیت HEAD بازسازی کرد. اما فایل روی دیسک در Working Tree دست‌نخورده باقی ماند.

فایل با موفقیت از Index خارج شد بدون اینکه حتی یک کاراکتر از نوشته جدید روی دیسک پاک شود.

اثبات باقی‌ماندن نوشته

برای اینکه با چشمان خودت ببینی نوشته روی دیسک باقی مانده، هر دو سوی تغییر را استعلام کن:

اثبات ماندگاری تغییر در Working Tree
bashترمینال اصلی
git status --short
git diff --staged -- journey-log.txt
git diff -- journey-log.txt
حرف M در ستون راست — دستور status نشان می‌دهد فایل اکنون به حالت Unstaged برگشته است.
خروجی git diff --staged — کاملاً خالی است؛ یعنی هیچ تغییری در Index برای کامیت آماده نیست.
خروجی git diff ساده — تغییر مقصد را هنوز در Working Tree روی دیسک نشان می‌دهد.
اکنون مطمئن شدیم که Unstage کردن، فقط انتخاب گیت را لغو کرده و ویرایش محلی را کاملاً حفظ نموده است.

کدام لایه تغییر می‌کند؟

فرق حیاتی git restore journey-log.txt با git restore --staged journey-log.txt چیست و کدام‌یک می‌تواند نوشته روی دیسک را از بین ببرد؟

مقصد فرمان اول Working Tree و مقصد فرمان دوم Index است.

Unstage کردن چند مسیر با نقطه

وقتی چندین فایل را اشتباهاً Stage کرده‌ای، می‌توانی همه آن‌ها را یکجا با پرچم نقطه . Unstage کنی.

ویرایش آزمایشی در فایل دوم

در فایل supplies.txt سطر فعلی را موقتاً با متن زیر جایگزین کن:

textsupplies.txt
Water and bread supplies packed too early.
Stage گروهی و Unstage هم‌زمان با نقطه
bashترمینال اصلی
git add journey-log.txt supplies.txt
git restore --staged .
git status --short
دستور git add ... — هر دو فایل را وارد Index می‌کند.
دستور git restore --staged . — تمام فایل‌های Stageشده را یکجا به Working Tree برمی‌گرداند.
علامت نقطه . تمام فایل‌های Stageشده در پوشه جاری و زیرپوشه‌ها را از Index خارج می‌کند، اما هیچ تغییری را از روی دیسک پاک نمی‌کند.

ثبت مقصد و کنارگذاشتن شتاب

اکنون تغییر موقت آذوقه را از دیسک پاک کن، اما تغییر مقصد واقعی را Stage و کامیت کن:

پاک‌سازی تغییر آزمایشی و کامیت تغییر واقعی
bashترمینال اصلی
git restore supplies.txt
git add journey-log.txt
git diff --staged -- journey-log.txt
git commit -m "Set next checkpoint to Valley of Knowledge"
1
با git restore supplies.txt تغییر آزمایشی آذوقه را از دیسک پاک کن.
2
با git add journey-log.txt فقط گزارش مقصد واقعی را Stage کن.
3
با git diff --staged -- journey-log.txt صحت تغییر آماده ثبت را تأیید کن.
4
با دستور git commit پیام کامیت را ثبت نما.
تغییر آزمایشی پاک شد، تغییر واقعی Stage گردید و کامیت مقصد جدید با موفقیت در تاریخچه گیت ثبت شد.

تحویل پاک وادی عشق

پیش از پایان فصل سوم، صحت شاخه، وضعیت پوشه کاری و آخرین کامیت‌های پروژه را استعلام کن:

تحویل نهایی پروژه و استعلام پاکیزگی
bashترمینال اصلی
git branch --show-current
git status --short
git log --oneline --decorate -n 5
شاخه جاری — باید روی شاخه اصلی main باشی.
وضعیت ترمینال — دستور status نباید هیچ خروجی داشته باشد (Working Tree تمیز).
تاریخچه کامیت‌ها — کامیت Set next checkpoint to Valley of Knowledge باید در بالای تاریخچه قرار داشته باشد.
پروژه فصل سوم کاملاً پاکیزه، منظم و آماده ورود به فصل چهارم است.

نشان نگهبان تغییرها

پایان وادی عشق و اعطای نشان «نگهبان تغییرها»

وادی عشق را با موفقیت پشت سر گذاشتی! اکنون می‌دانی چطور تغییرات را دقیق پیش از ثبت بخوانی، فایل‌های محلی را نادیده بگیری و تغییرات Staging Area را پس بگیری.

بعد از آن بنمایدت پیش نظر
معرفت را وادیی بی پا و سر
هیچ ره دروی نه هم آن دیگرست
سالک تن، سالک جان، دیگرست

دستاوردهای فصل سوم
خواندن دقیق تغییرات خط‌به‌خط با git diff پیش از هر ثبت
تعریف قوانین نادیده‌گیری فایل‌های محلی و موقت با .gitignore و عیب‌یابی با git check-ignore
بازگرداندن تغییرات و فایل‌های حذف‌شده پوشه کاری با git restore
پس‌گرفتن انتخاب‌های شتاب‌زده از Staging Area با git restore --staged
تحویل مخزن کاملاً تمیز و آماده‌سازی برای ساخت شاخه‌های جدید
در فصل چهارم وارد Valley of Knowledge می‌شویم و یاد می‌گیریم چطور با استفاده از Branch در مسیرهای موازی کدنویسی کنیم بدون اینکه شاخه اصلی main دچار اختلال شود.

فصل 4: وادی معرفت

درس 1: راهی که هنوز حرکت نکرده

نقشه‌ای با راه‌های بسیار

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

بعد از آن بنمایدت پیش نظر
معرفت را وادیی بی پا و سر
هیچ ره دروی نه هم آن دیگرست
سالک تن، سالک جان، دیگرست

پیوند داستان با شاخه‌های موازی

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

در این درس ابتدا یک نام تازه به نام Branch روی آخرین Commit می‌گذاریم و سپس به‌طور جداگانه به آن شاخه می‌رویم (Switch). تفکیک این دو عمل، کلید فهم رفتار Branch و HEAD است.

اطمینان از نقطه شروع

پیش از بازکردن راه تازه و شاخه‌بندی، ابتدا وضعیت فعلی پروژه، شاخه فعال و سه کامیت اخیر را استعلام کن:

استعلام نقطه شروع کاروان
bashترمینال اصلی
git branch --show-current
git status --short
git log --oneline --decorate -n 3
تحلیل خروجی دستورها
شاخه جاری — دستور اول باید نام شاخه اصلی main را چاپ کند.
وضعیت پوشه کاری — دستور دوم نباید هیچ خروجی داشته باشد (Working Tree کاملاً تمیز).
آخرین کامیت — دستور سوم نشان می‌دهد کامیت ثبت مقصد Valley of Knowledge در نوک تاریخچه قرار دارد.
اکنون مطمئن هستیم که مأموریت شاخه‌بندی را از یک نقطه مشخص، سالم و بدون تغییرات معلق آغاز می‌کنیم.

Branch، نامی روی یک Commit

مفاهیم بنیادی Branch و HEAD

در کنترل نسخه گیت، دو مفهوم کلیدی وجود دارد که اغلب با کپی فیزیکی فایل‌ها اشتباه گرفته می‌شوند:

Branch (شاخه)

یک اشاره‌گر سبک و نام‌دار (در حد یک فایل متنی ۴۱ بایتی) است که صرفاً به شناسه یک Commit خاص اشاره می‌کند.

HEAD (سربرگ)

یک اشاره‌گر نمادین (Symbolic Reference) است که نشان می‌دهد ترمینال و دیسک در حال حاضر کدام شاخه را دنبال می‌کنند.

ساختن Branch جدید، اشاره‌گر HEAD را به‌طور خودکار جابه‌جا نمی‌کند؛ ساخت شاخه و جابه‌جایی به آن دو دستور کاملاً مستقل هستند.

وقتی کامیت تازه‌ای روی یک شاخه ثبت می‌شود، تنها اشاره‌گر همان شاخه فعال به کامیت جدید جلو می‌رود و سایر شاخه‌ها روی جای قبلی خود باقی می‌مانند.

گذاشتن یک نام تازه روی تاریخچه

برای مأموریت آماده‌سازی فهرست پرندگان، یک شاخه جدید به نام feature/bird-roster بساز:

دستور ساخت شاخه جدید
bashترمینال اصلی
git branch feature/bird-roster

در گیت، سکوت ترمینال پس از اجرای دستور یعنی عملیات با موفقیت انجام شده است.

این دستور صرفاً یک اشاره‌گر تازه به نام feature/bird-roster روی همان کامیت فعلی قرار داده است؛ اما هنوز وارد آن شاخه نشده‌ای!

دو نام، یک نقطه آغاز

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

مشاهده فهرست شاخه‌ها و شناسه‌ها
bashترمینال اصلی
git branch -v
تحلیل خروجی دستور
علامت ستاره کنار main — نشان می‌دهد HEAD هنوز شاخه اصلی main را دنبال می‌کند.
شناسه کامیت یکسان — هر دو شاخه main و feature/bird-roster دقیقاً شناسه کامیت یکسانی را نشان می‌دهند.
پرچم -v خلاصه پیام و شناسه آخرین کامیت هر شاخه را نمایش می‌دهد.

شاخه ساخته شد، جای ما عوض نشد

نام شاخه فعال فعلی را بدون اطلاعات اضافی بخوان:

استعلام شاخه فعال فعلی
bashترمینال اصلی
git branch --show-current

خروجی هنوز main است.

توجه: دستور git branch NAME فقط برچسب شاخه را می‌سازد و موقعیت HEAD را تغییر نمی‌دهد. برای وارد شدن به شاخه جدید باید دستور دیگری اجرا کنی.

Commit بعدی کدام نام را جلو می‌برد؟

کاتب شاخه feature/bird-roster را ساخته، اما هنوز روی شاخه main ایستاده است.

اگر همین حالا فایلی را تغییر داده و Commit کند، کدام Branch به جلو حرکت می‌کند و چرا؟

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

ورود به راه فهرست پرندگان

اکنون وقت آن است که با دستور switch به شاخه مأموریت منتقل شوی:

دستور جابه‌جایی به شاخه جدید
bashترمینال اصلی
git switch feature/bird-roster

پیام گیت جابه‌جایی به شاخه را تأیید می‌کند: Switched to branch 'feature/bird-roster'

از این لحظه به بعد، HEAD شاخه feature/bird-roster را دنبال می‌کند و کامیت‌های بعدی فقط روی این شاخه ثبت خواهند شد.

دیدن جای دقیق HEAD

موقعیت جدید خود را با دو دستور مکمل بررسی کن:

استعلام موقعیت جدید HEAD و شاخه‌ها
bashترمینال اصلی
git branch --show-current
git log -1 --oneline --decorate
خروجی دستور اول — عبارت feature/bird-roster را به عنوان شاخه فعال چاپ می‌کند.
خروجی دستور دوم — عبارت (HEAD -> feature/bird-roster, main) را نشان می‌دهد که اثبات می‌کند HEAD اکنون به شاخه جدید وصل شده است.
هر دو شاخه هنوز به یک کامیت اشاره دارند، اما با اولین کامیت روی شاخه جدید، مسیر این دو از هم جدا خواهد شد.

قطب‌نمای راه‌های موازی

خلاصه دستاوردهای این درس
درک مفهوم Branch به عنوان یک اشاره‌گر سبک و متحرک روی کامیت‌ها
درک نقش HEAD به عنوان قطب‌نمای تعیین‌کننده شاخه فعال
تفکیک کامل فرمان ساخت شاخه (git branch) از جابه‌جایی به شاخه (git switch)
استعلام وضعیت شاخه‌ها با دستورات git branch -v و git log --decorate
پروژه تمیز است و اکنون روی شاخه feature/bird-roster ایستاده‌ایم. در درس بعدی، نخستین فایل مستقل این شاخه را می‌سازیم و تفاوت واقعی دو مسیر را روی دیسک مشاهده خواهیم کرد.

درس 2: دو گروه، دو مأموریت

هر گروه به اندازه مأموریتش

هدهد کاروان را به دو گروه تقسیم می‌کند. بلبل و طوطی باید فهرست همراهان را بنویسند و شاهین باید یادداشت‌های مسیر را آماده کند؛ هر گروه مسئول یک خروجی روشن است.

سیر هر کس تا کمال وی بود
قرب هر کس حسب حال وی بود
گر بپرد پشه چندانی که هست
کی کمال صرصرش آید بدست

پیوند داستان با مأموریت شاخه‌ها

عطار می‌گوید سیر هر کس با اندازه و حال خودش پیوند دارد. در پروژه نرم‌افزاری هم Branchها زمانی مفیدند که هرکدام مأموریتی مشخص داشته باشند، نه اینکه بی‌هدف زیاد شوند.

اکنون اولین گروه روی شاخه خودش (feature/bird-roster) فایل می‌سازد. سپس به main برمی‌گردی تا با چشم خودت ببینی فایلِ ثبت‌شده روی یک شاخه، الزاماً روی شاخه دیگر وجود ندارد.

وقتی HEAD مسیر را عوض می‌کند

مکانیزم هماهنگ‌سازی git switch

دستور git switch TARGET_BRANCH شاخه‌ای را که HEAD دنبال می‌کند تغییر می‌دهد. گیت هنگام جابه‌جایی، لایه‌های خود را هماهنگ می‌سازد:

به‌روزرسانی Index و Working Tree — فایل‌های Tracked روی دیسک تغییر می‌کنند تا دقیقاً Snapshot شاخه مقصد را نشان دهند.
مدیریت تغییرات محلی Uncommitted — اگر تغییرات محلی با فایل‌های شاخه مقصد تعارضی نداشته باشند، همراه شما منتقل می‌شوند.
جلوگیری از پاک شدن داده‌ها — اگر جابه‌جایی باعث بازنویسی یا پاک شدن تغییر محلی شود، Git جلوی جابه‌جایی را می‌گیرد.
اگر گیت جابه‌جایی را متوقف کرد، پیام خطای Your local changes to the following files would be overwritten by checkout را نشان می‌دهد تا داده‌های شما آسیب نبینند.

نوشتن فهرست همراهان

روی شاخه feature/bird-roster فایلی به نام bird-roster.txt بساز و دقیقاً محتوای زیر را در آن قرار بده:

محتوای فایل bird-roster.txt
textbird-roster.txt
CARAVAN BIRD ROSTER
Hudhud - Guide
Nightingale - Singer
Parrot - Keeper
Falcon - Scout
ذخیره و استعلام وضعیت
bashترمینال اصلی
git status --short
1
فایل bird-roster.txt را در ریشه پروژه بساز.
2
متن فهرست همراهان را داخل آن ذخیره کن.
3
دستور git status --short را بزن؛ باید علامت ?? bird-roster.txt را ببینی.
علامت ?? نشان می‌دهد فایل هنوز Untracked است و گیت آن را ردیابی نکرده است.

ثبت خروجی گروه اول

فهرست ساخته‌شده را Stage کرده، صحت تغییر را بررسی کن و نخستین کامیت این شاخه را ثبت نما:

دستورهای ثبت کامیت در شاخه مأموریت
bashترمینال اصلی
git add bird-roster.txt
git diff --staged -- bird-roster.txt
git commit -m "Add caravan bird roster"
این کامیت فقط اشاره‌گر شاخه feature/bird-roster را جلو می‌برد. شاخه اصلی main همچنان روی کامیت قبلی باقی مانده است.

دیدن نخستین انشعاب

تاریخچه همه شاخه‌ها را به صورت نمودار گرافیکی در ترمینال ببین:

مشاهده نمودار انشعاب شاخه‌ها
bashترمینال اصلی
git log --oneline --graph --decorate --all -n 5
موقعیت HEAD — اشاره‌گر HEAD و feature/bird-roster کنار کامیت جدید قرار دارند.
موقعیت main — شاخه main یک کامیت عقب‌تر روی نقطه انشعاب اولیه ایستاده است.
استفاده هم‌زمان از پرچم‌های --graph و --all تصویر انشعاب واقعی شاخه‌ها را ترسیم می‌کند.

بازگشت به نسخه پایدار

اکنون به شاخه اصلی پروژه برگرد و وضعیت فایل‌ها روی دیسک را کنترل کن:

بازگشت به شاخه اصلی
bashترمینال اصلی
git switch main
git status --short

به پوشه پروژه نگاه کن؛ فایل bird-roster.txt دیگر دیده نمی‌شود!

فایل پاک یا نابود نشده است؛ بلکه گیت پوشه کاری روی دیسک را مطابق با Snapshot شاخه main به‌روز کرده است که آن فایل در آن وجود ندارد.

میان‌بُر آخرین شاخه

برای رفت و برگشت سریع بین دو شاخه اخیر، از پرچم dash (-) استفاده کن:

جابه‌جایی سریع بین دو شاخه اخیر
bashترمینال اصلی
git switch -
git branch --show-current
git switch main

وقتی روی شاخه مأموریت برمی‌گردی، فایل bird-roster.txt بلافاصله روی دیسک ظاهر می‌شود.

پرچم git switch - دقیقا مانند کلید ترکیبی Alt+Tab در سیستم‌عامل، شما را بین دو آخرین شاخه جابه‌جا می‌کند.

ساخت و ورود در یک فرمان

برای گروه دوم (یادداشت‌های مسیر)، شاخه جدیدی بساز و در همان لحظه وارد آن شو:

فرمان ترکیبی ساخت و جابه‌جایی
bashترمینال اصلی
git switch -c feature/route-notes

گزینه -c (مخفف create) ابتدا شاخه جدید را از کامیت فعلی می‌سازد و سپس HEAD را به آن منتقل می‌کند.

چون این شاخه را از روی main ساختیم، فایل bird-roster.txt در آن وجود ندارد و این شاخه مستقل است.

ثبت یادداشت‌های مسیر

فایل route-notes.txt را با محتوای زیر بساز و مأموریت گروه دوم را کامیت کن:

محتوای فایل route-notes.txt
textroute-notes.txt
ROUTE NOTES
Northern pass: Open
Water stop: Marked
Night camp: Ready
Stage و Commit مأموریت دوم
bashترمینال اصلی
git add route-notes.txt
git commit -m "Add route notes"
اکنون دو شاخه فرعی مستقل داری که هرکدام کامیت خاص خود را روی پایه مشترک main ثبت کرده‌اند.

آیا تغییر محلی همیشه مانع Switch است؟

یکی از هم‌تیمی‌ها می‌گوید: «اگر Working Tree تمیز نباشد، Git همیشه اجازه switch نمی‌دهد.»

این جمله چه ایرادی دارد و تصمیم امن پیش از جابه‌جایی چیست؟

گیت بیشتر نگران بازنویسی و از دست رفتن تغییرات محلی است، نه صرفاً وجود هر تغییری در پوشه کاری.

دو مسیر آماده بازگشت

خلاصه دستاوردهای این درس
ساخت دو شاخه فرعی مستقل feature/bird-roster و feature/route-notes
ثبت کامیت‌های جداگانه روی هر شاخه و مشاهده ناپدید/ظاهر شدن فایل‌ها روی دیسک با git switch
استفاده از میان‌بر git switch - برای پرش بین دو شاخه اخیر
استفاده از پرچم git switch -c برای ساخت و جابه‌جایی یکباره شاخه
درک نحوه رفتار گیت هنگام وجود تغییرات محلی Uncommitted در زمان جابه‌جایی
در درس بعدی، هر دو خروجی را از سمت main ادغام می‌کنیم و تفاوت Fast-forward با 3-way Merge را در تاریخچه واقعی مشاهده خواهیم کرد.

درس 3: بازگشت دو گروه به main

دو راه با اندازه‌های متفاوت

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

لاجرم بس ره که پیش آمد پدید
هر یکی بر حد خویش آمد پدید
کی تواند شد درین راه جلیل
عنکبوت مبتلا هم‌سیر پیل

پیوند داستان با تکنیک‌های ادغام در گیت

عطار در وادی معرفت یادآوری می‌کند که راه‌ها و توان رهروان هم‌اندازه نیست. در کنترل نسخه گیت هم شکل Merge به موقعیت شاخه‌ها در درخت کامیت‌ها بستگی دارد؛ یک فرمان یکسان می‌تواند دو خروجی کاملاً متفاوت بسازد.

در این درس نخست شاخه‌ای را ادغام می‌کنیم که main می‌تواند مستقیم تا نوک آن جلو برود (Fast-forward). سپس شاخه دوم را ادغام می‌کنیم که نیاز به ساخت یک Merge Commit خواهد داشت.

جهت Merge و دو شکل تاریخچه

دو استراتژی اصلی ادغام شاخه‌ها

در اجرای دستور git merge SOURCE_BRANCH، شاخه‌ای که روی آن ایستاده‌ای مقصد ادغام است. همیشه جهت حرکت را مشخص کن:

Fast-forward Merge

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

3-way Merge

اگر هر دو شاخه کامیت مستقل داشته باشند، گیت ۳ نقطه را مقایسه کرده و یک Merge Commit با دو والد می‌سازد.

قبل از اجرای دستور merge همواره با git branch --show-current مطمئن شو که روی شاخه مقصد (مانند main) ایستاده‌ای.

ادغام مستقیم فهرست

ابتدا به شاخه اصلی برگرد و شاخه فهرست پرندگان را به صورت مستقیم ادغام کن:

ادغام صریح و مستقیم Fast-forward
bashترمینال اصلی
git switch main
git merge --ff-only feature/bird-roster

خروجی ترمینال عبارت Fast-forward را نشان می‌دهد.

گیت هیچ کامیت جدیدی نساخت؛ صرفاً برچسب main را تا نوک شاخه feature/bird-roster جلو برد. پرچم --ff-only تضمین می‌کند که اگر Fast-forward ممکن نباشد، عملیات متوقف شود.

اثبات Fast-forward

تاریخچه کامیت‌ها و وضعیت پوشه کاری را بررسی کن:

استعلام و اثبات ادغام Fast-forward
bashترمینال اصلی
git log --oneline --graph --decorate --all -n 6
git status --short
موقعیت شاخه‌ها — هر دو شاخه main و feature/bird-roster کنار یک کامیت قرار گرفته‌اند.
حضور فایل در main — فایل bird-roster.txt اکنون بخشی از Snapshot شاخه اصلی است.
در ادغام Fast-forward هیچ گره یا انشعاب جدیدی در تاریخچه ایجاد نمی‌شود و مسیر خطی باقی می‌ماند.

ادغام سه‌طرفه مسیر

حالا شاخه یادداشت‌های مسیر را از سمت همین main ادغام کن:

اجرای ادغام سه‌طرفه (3-way Merge)
bashترمینال اصلی
git merge --no-edit feature/route-notes

این بار main کامیت فهرست را دارد و feature/route-notes کامیت مسیر را؛ هیچ‌کدام جد مستقیم دیگری نیستند.

چون فایل‌ها با هم برخوردی ندارند، گیت یک ادغام سه‌طرفه خودکار انجام داده و یک Merge Commit ایجاد کرد. پرچم --no-edit پیام پیش‌فرض کامیت را پذیرفت.

Merge Commit با دو والد

شکل تاریخچه و کالبدشناسی کامیت ادغام را بررسی کن:

کالبدشکافی کامیت ادغام دو والدی
bashترمینال اصلی
git log --oneline --graph --decorate --all -n 8
git show --no-patch --pretty=raw HEAD
نمودار دو شاخه‌ای — در گراف ترمینال، دو خط انشعاب به کامیت ادغام وصل می‌شوند.
دو والد در خروجی raw — دستور دوم دو سطر parent را نشان می‌دهد: یکی والد main و دیگری والد feature/route-notes.
کامیت‌های معمولی فقط ۱ والد دارند، اما Merge Commit ناشی از ادغام سه‌طرفه دارای ۲ والد است.

چرا نتیجه دو Merge فرق داشت؟

هر دو بار دستور git merge را روی شاخه main اجرا کردی.

چرا ادغام نخست Fast-forward بود، اما ادغام دوم یک Merge Commit ساخت؟

به رابطه نیایی کامیت فعلی main با نوک هر Branch در لحظه ادغام نگاه کن.

کدام راه‌ها به مقصد رسیده‌اند؟

فهرست شاخه‌هایی که کامیت‌های آن‌ها وارد تاریخچه main شده است را بررسی کن:

استعلام شاخه‌های ادغام‌شده
bashترمینال اصلی
git branch --merged

هر دو شاخه مأموریت باید در این لیست دیده شوند.

این بررسی پیش از حذف شاخه نشان می‌دهد که تمام کامیت‌های آن شاخه در main ثبت شده‌اند و حذف آن کاملاً امن است.

جمع‌کردن تابلوهای تمام‌شده

اشاره‌گرهای شاخه‌های تمام‌شده را با روش حذف امن پاک کن:

حذف امن برچسب‌های شاخه
bashترمینال اصلی
git branch -d feature/bird-roster feature/route-notes
git branch -d NAME

حذف امن — اگر شاخه کاملاً ادغام نشده باشد، گیت عملیات را متوقف کرده و هشدار می‌دهد.

git branch -D NAME

حذف اجباری — محافظ ادغام را دور می‌زند و ممکن است دسترسی به کامیت‌ها را از بین ببرد.

اشاره‌گرهای اضافی شاخه‌ها با موفقیت حذف شدند بدون اینکه هیچ کدی از دست برود.

تحویل دو خروجی روی main

فهرست نهایی شاخه‌ها و وضعیت پوشه کاری را بررسی نما:

استعلام وضعیت نهایی مخزن
bashترمینال اصلی
git branch -v
git status --short
تنها شاخه باقی‌مانده — فقط شاخه اصلی main در لیست وجود دارد.
حضور هر دو فایل — هر دو فایل bird-roster.txt و route-notes.txt روی دیسک حاضر هستند.
حذف شاخه‌ها، فایل‌های ادغام‌شده را پاک نکرده است؛ زیرا محتوای آنها اکنون در کامیت‌های main حفظ شده است.

نقشه‌خوان ادغام

خلاصه دستاوردهای این درس
تشخیص دقیق جهت Merge از سمت شاخه مقصد (main)
تجربه ادغام مستقیم Fast-forward با پرچم --ff-only
تجربه ادغام سه‌طرفه 3-way Merge و کالبدشکافی کامیت ادغام دووالدی
بررسی امنیت ادغام شاخه‌ها با دستور git branch --merged
حذف امن برچسب‌های اضافی شاخه با پرچم git branch -d
در درس بعدی، سناریوی حساس تعارض (Merge Conflict) را بررسی می‌کنیم؛ زمانی که دو گروه یک خط از یک فایل یکسان را به دو شکل متفاوت تغییر داده‌اند.

درس 4: دو روایت روی یک خط

وقتی دو راه به یک نقطه می‌رسند

پس از بازگشت دو گروه، درباره آرایش شبانه کاروان اختلافی پیش می‌آید. بلبل می‌خواهد خط آواز را هدایت کند و شاهین می‌خواهد خط دیده‌بانی را؛ هر دو پیشنهاد درست‌اند، اما کاتب هرکدام را جداگانه روی همان خط از فایل گزارش ثبت می‌کند.

هیچ کس نبود که او این جایگاه
مختلف گردد ز بسیاری راه
هیچ ره دروی نه هم آن دیگرست
سالک تن، سالک جان، دیگرست

پیوند داستان با تعارض ادغام

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

در این مأموریت عمداً یک تعارض واقعی (Merge Conflict) می‌سازی. هدف حفظ‌کردن چند دستور نیست؛ باید بتوانی بفهمی هر روایت از کدام Branch آمده و نتیجه درست را خودت بنویسی.

Conflict یعنی Git انتخاب نمی‌کند

چرا تعارض ایجاد می‌شود؟

گیت برای ادغام ۳ نسخه را می‌سنجد: مبنای مشترک، نوک شاخه فعلی و نوک شاخه ورودی. اگر هر دو مسیر یک بخش از فایل را به دو شکل ناسازگار تغییر داده باشند، انتخاب خودکار ممکن است کد صحیح را نابود کند.

توقف ادغام — عملیات Merge متوقف می‌شود و هیچ کامیت ادغامی ساخته نمی‌شود.
وضعیت UU — فایل متعارض در گزارش وضعیت با علامت UU (Unmerged) مشخص می‌شود.
فرمان لغو اضطراری — در صورت نیاز به انصراف، دستور git merge --abort مخزن را به حالت قبل از ادغام برمی‌گرداند.
بروز Conflict خرابی مخزن نیست؛ بلکه یک توقف هوشمندانه است تا انسان یا ابزار ادغام خروجی نهایی را انتخاب کند.

بازکردن مسیر بلبل

از شاخه اصلی main یک شاخه جدید برای پیشنهاد بلبل بساز و شاخه فعال را تأیید کن:

ساخت و جابه‌جایی به شاخه بلبل
bashترمینال اصلی
git switch -c feature/nightingale-route
git branch --show-current

خروجی فرمان دوم باید feature/nightingale-route باشد.

هر دو روایت بعدی از Snapshot مشترک همین لحظه آغاز می‌شوند.

روایت بلبل از آرایش

در فایل journey-log.txt سطر زیر را پیدا کن:

textjourney-log.txt
Formation: Stable
ویرایش سطر آرایش کاروان در شاخه بلبل

فقط همان سطر را با پیشنهاد بلبل جایگزین کن:

textjourney-log.txt
Formation: Nightingale leads the song line
ثبت کامیت روی شاخه بلبل
bashترمینال اصلی
git diff -- journey-log.txt
git commit -am "Let Nightingale lead the formation"
پیشنهاد بلبل با موفقیت روی شاخه feature/nightingale-route ثبت شد.

روایت شاهین روی main

به شاخه اصلی main برگرد. در این Snapshot سطر آرایش هنوز نسخه قدیمی Formation: Stable است:

جابه‌جایی به main
bashترمینال اصلی
git switch main
ویرایش همان سطر در شاخه اصلی main

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

textjourney-log.txt
Formation: Falcon leads the scouting line
ثبت کامیت روی شاخه اصلی
bashترمینال اصلی
git commit -am "Let Falcon lead the formation"
اکنون هر دو شاخه، دقیقاً یک سطر از یک فایل را به دو شکل متفاوت تغییر داده‌اند و آمادگی ایجاد تعارض را دارند.

دیدن دو نوک جدا

پیش از اجرای ادغام، انشعاب متعارض دو شاخه را در گراف تاریخچه ببین:

مشاهده نمودار دو انشعاب متعارض
bashترمینال اصلی
git log --oneline --graph --decorate --all -n 7
نوک main — شامل کامیت آرایش شاهین است.
نوک feature/nightingale-route — شامل کامیت آرایش بلبل است.
هر دو شاخه به کامیت ادغام درس قبل می‌رسند که مبنای مشترک این تعارض محسوب می‌شود.

ساخت تعارض واقعی

پیشنهاد بلبل را از سمت شاخه main ادغام کن تا بروز تعارض را تجربه کنی:

اجرای ادغام و بروز تعارض (Conflict)
bashترمینال اصلی
git merge feature/nightingale-route
git status --short
پیام گیت: CONFLICT (content): Merge conflict in journey-log.txt
دستور status عبارت UU journey-log.txt را نشان می‌دهد.
علامت UU (Unmerged) یعنی هر دو سوی ادغام این مسیر را تغییر داده‌اند و تا زمانی که تعارض آن را حل نکنی، کامیت ادغام ثبت نمی‌شود.

خواندن سه نشانگر تعارض

این بلوک نمونه خروجی Git است، نه متنی برای افزودن به فایل. اگر نمایش تعارض diff3 فعال باشد، یک بخش مبنای مشترک با نشانگر ||||||| هم می‌بینی؛ در حل نهایی همه نشانگرها و نسخه‌های رقیب را با سطر ترکیبی قدم بعد جایگزین کن.

فایل journey-log.txt را در VS Code باز کن. گیت دو روایت را میان نشانگرهای سه‌گانه زیر محصور کرده است:

نشانگرهای سه‌گانه تعارض در فایل
textjourney-log.txt
<<<<<<< HEAD
Formation: Falcon leads the scouting line
=======
Formation: Nightingale leads the song line
>>>>>>> feature/nightingale-route
<<<<<<< HEAD — بخش بالا نشان‌دهنده تغییرات شاخه فعلی شما (main) است.
======= — خط جداکننده دو روایت متعارض.
feature/nightingale-route — بخش پایین نشان‌دهنده تغییرات شاخه ورودی است.
این نشانگرها صرفاً برای راهنمایی شما اضافه شده‌اند و نباید در خروجی نهایی فایل باقی بمانند.

چرا Git یکی را برنده نکرد؟

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

گیت تفاوت Snapshotها را می‌فهمد، اما هدف و معنای تصمیم منطقی تیم را نمی‌داند.

نوشتن روایت ترکیبی

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

نتیجه ترکیبی و نهایی سطر
textjourney-log.txt
Formation: Nightingale sings while Falcon scouts
بررسی خطای فاصله و Stage کردن حل تعارض
bashترمینال اصلی
git diff --check
git add journey-log.txt
git diff --staged -- journey-log.txt
1
نشانگرهای تعارض را کاملاً پاک کرده و متن ترکیبی بالا را ذخیره کن.
2
دستور git diff --check را بزن تا از نبود خطای فاصله مطمئن شوی.
3
دستور git add journey-log.txt را اجرا کن تا حل تعارض به گیت اعلام شود.
اجرای git add روی فایل متعارض، به گیت اعلام کرد که تعارض این مسیر با موفقیت حل شده است.

بستن Merge و پاک‌سازی راه

نتیجه حل‌شده را کامیت کن و شاخه فرعی تمام‌شده را پاک کن:

ثبت کامیت نهایی ادغام و پاک‌سازی
bashترمینال اصلی
git commit -m "Resolve formation route conflict"
git branch -d feature/nightingale-route
git status --short
git log --oneline --graph --decorate -n 7
کامیت ادغام با موفقیت ثبت شد و شاخه فرعی تمام‌شده بدون هیچ خطایی حذف گردید.

نشان راهبر مسیرهای موازی

پایان فصل چهارم و اعطای نشان «راهبر مسیرهای موازی»

نشان «راهبر مسیرهای موازی» را به دست آوردی! اکنون شاخه‌ها و HEAD را دقیق می‌شناسی، مسیرهای مستقل می‌سازی، دو شکل Merge را تشخیص می‌دهی و Conflict را با خواندن نشانگرها حل می‌کنی.

بعد ازین وادی استغنا بود
نه درو دعوی و نه معنی بود
می‌جهد از بی‌نیازی صرصری
می‌زند بر هم به یک دم کشوری

دستاوردهای فصل چهارم
ساخت و مدیریت شاخه‌های سبک با دستورات git branch و git switch
درک نقش اشاره‌گر نمادین HEAD در ردیابی شاخه جاری
اجرای ادغام مستقیم Fast-forward و ادغام سه‌طرفه 3-way Merge
خواندن، تحلیل و حل نشانگرهای تعارض Merge Conflict
استعلام امنیت ادغام با git branch --merged و حذف امن شاخه‌ها
در فصل پنجم وارد Valley of Independence (وادی استغنا) می‌شویم و یاد می‌گیریم چطور با ابزار git stash تغییرات ناتمام را موقتاً کنار بگذاریم.

فصل 5: وادی استغنا

درس 1: یادداشت نیمه‌تمام و پیام فوری

باد ناگهانی در میانه کار

وقفه ناگهانی در سفر

طوطی هنوز گزارش مسیر را کامل نکرده که پیامی فوری از گروه آذوقه می‌رسد. گزارش نیمه‌تمام برای Commit آماده نیست، اما تغییر فوری هم نمی‌تواند منتظر بماند؛ پاک‌کردن نوشته یا ثبت یک Commit ناقص هر دو انتخاب‌های اشتباهی هستند.

کاروان وارد وادی استغنا شده است؛ جایی که بادِ عطار نظم آشنای یک سرزمین را در یک دم برهم می‌زند. مسئله این درس هم مدیریت همین وقفه است: کار نیمه‌تمام را موقتاً در کشوی مخفی کنار می‌گذاری تا مسیر اصلی برای مأموریت فوری تمیز شود.

بعد ازین وادی استغنا بود
نه درو دعوی و نه معنی بود
می‌جهد از بی‌نیازی صرصری
می‌زند بر هم به یک دم کشوری

فرمان git stash دقیقاً برای چنین وقفه‌هایی طراحی شده است. این ابزار تغییرهای Staged و Unstaged شما را موقتاً در پشته‌ای صفی‌مانند ذخیره کرده، Working Tree را به وضعیت تمیز آخرین Commit برمی‌گرداند و اجازه می‌دهد پس از انجام کار فوری، به همان نوشته‌های نیمه‌کاره بازگردید.

کشوی موقت گیت (Stash): دستور git stash مثل یک کشوی ابزار کشویی در کارگاه است. وسایل نیمه‌کاره روی میز را برمی‌داری، توی کشو می‌گذاری تا میز برای کار اضطراری خالی شود.

Stash چه چیزی را کنار می‌گذارد؟

قوانین و رفتار Stash

دستور git stash به‌صورت پیش‌فرض تغییرات فایل‌های Tracked (فایل‌های ردیابی‌شده در گیت) را ذخیره کرده و Working Tree را به وضعیت HEAD بازمی‌گرداند. این ذخیره یک Commit عادی روی شاخه نیست و در git log معمولی دیده نمی‌شود.

فایل‌های Tracked — تغییرات فایل‌های موجود که در Git شناخته‌شده هستند، به‌طور خودکار Stash می‌شوند.
فایل‌های Untracked — فایل‌های تازه‌ای که هنوز git add نشده‌اند، به‌طور پیش‌فرض Stash نمی‌شوند! برای شمول آن‌ها باید از -u یا --include-untracked استفاده کنید.
فایل‌های Ignored — فایل‌هایی که در .gitignore هستند، حتی با -u هم کنار گذاشته نمی‌شوند مگر از پرچم -a استفاده کنید.
بازیابی و مدیریت پشته (Stash Stack)
git stash apply

تغییرات را روی فایل‌ها اعمال می‌کند، اما مدخل Stash را در پشته باقی می‌گذارد.

git stash pop

تغییرات را اعمال کرده و در صورت موفقیت، مدخل مربوطه را از پشته حذف می‌کند.

هشدار درباره Drop و Clear: فرمان‌های drop (حذف یک Stash مشخص) و clear (پاک کردن تمام پشته) تغییرات ذخیره‌شده را بدون اعمال روی کد شما پاک می‌کنند. همیشه قبل از پاک کردن پشته، از عدم نیاز به آن مطمئن شوید.

شروع از میز خالی

بررسی وضعیت اولیه مخزن

قبل از شروع تمرین، ابتدا وضعیت شاخه، تغییرات فعلی و پشته Stash را بررسی کنید تا مطمئن شوید روی یک نقطه تمیز قرار دارید:

1
نام شاخه جاری را بازبینی کنید:
bashبررسی شاخه فعلی
git branch --show-current
2
وضعیت فایل‌های Working Tree را چک کنید:
bashبررسی تغییرات
git status --short
3
فهرست Stashهای موجود را مشاهده کنید:
bashمشاهده پشته Stash
git stash list
نتیجه متناسب: شما باید روی شاخه main باشید و دو فرمان دوم و سوم هیچ خروجی نشان ندهند. این محیط تمیز حاصل پایان موفق فصل چهارم است.

بازبینی گذرگاه شمالی

ایجاد تغییر نیمه‌تمام روی فایل Tracked

در فایل route-notes.txt که از قبل ردیابی می‌شود (Tracked)، وضعیت گذرگاه شمالی را به‌صورت نیمه‌کاره تغییر دهید:

1
در فایل route-notes.txt خط زیر را پیدا کنید:
textمحتوای قبلی
Northern pass: Open
2
آن را با عبارت زیر جایگزین و ذخیره کنید:
textمحتوای جدید نیمه‌کاره
Northern pass: Under review
3
تفاوت ایجادشده را مشاهده کنید:
bashمشاهده Diff فایل
git diff -- route-notes.txt
تغییر Tracked و Unstaged: این تغییر روی یک فایل ردیابی‌شده قرار دارد، اما هنوز Stage یا Commit نشده است. این نمونه عالی برای استفاده از Stash است.

پیش‌نویس تازه دیده‌بانی

ایجاد یک فایل جدید Untracked

اکنون یک فایل گزارش تازه به نام scout-report.txt ایجاد کنید تا رفتار Stash را با فایل‌های ردیابی‌نشده (Untracked) آزمایش کنیم:

1
فایل جدید scout-report.txt را بسازید و متن زیر را در آن بنویسید:
textمحتوای گزارش جدید
SCOUT REPORT
Detachment pass: Draft
Signal point: Unknown
2
خلاصه وضعیت مخزن را مشاهده کنید:
bashمشاهده status خلاصه
git status --short
دو جنس مختلف در Status: خروجی status باید M route-notes.txt (تغییر Tracked) و ?? scout-report.txt (فایل Untracked) را نشان دهد.

اولین بسته، فقط برای فایل Tracked

اجرای Stash معمولی بدون پرچم -u

حالا فرمان git stash push را همراه با یک پیام توضیحی روشن اجرا کنید و وضعیت مخزن را ببینید:

1
ذخیره تغییرات با پیام مشخص:
bashStash تغییرات
git stash push -m "WIP: review detachment pass"
2
بررسی status پس از Stash:
bashبررسی وضعیت Working Tree
git status --short
3
مشاهده لیست Stashها:
bashفهرست پشته Stash
git stash list
مشاهده نتیجه Stash معمولی: تغییر route-notes.txt از Working Tree کنار رفته و تمیز شده، اما ?? scout-report.txt همچنان باقی مانده است! چرا که Stash معمولی فایل‌های Untracked را برنمی‌دارد.

چرا گزارش تازه جا ماند؟

چالش: تحلیل رفتار Stash پیش‌فرض

چرا Stash در مرحله قبل تغییر route-notes.txt را ذخیره و پاک کرد، اما فایل scout-report.txt همچنان در git status باقی ماند؟ برای کنارگذاشتن این فایل تازه چه گزینه‌ای لازم است؟

بسته دوم با فایل تازه

ذخیره فایل‌های Untracked با پرچم -u

اکنون گزارش دیده‌بانی تازه را هم با پرچم -u به پشته Stash اضافه کنید:

1
ذخیره فایل Untracked در Stash:
bashStash با پرچم -u
git stash push -u -m "WIP: scout report"
2
بررسی وضعیت تمیز شده:
bashبررسی وضعیت
git status --short
3
مشاهده فهرست پشته Stash:
bashلیست Stashها
git stash list
ساختار پشته (LIFO): اکنون status کاملاً تمیز است. آخرین Stash ساخته‌شده در stash@{0} (گزارش دیده‌بانی) و Stash قبلی در stash@{1} (بازبینی گذرگاه) قرار گرفته است. عضو جدیدتر همیشه در بالای پشته (اندیس 0) قرار می‌گیرد.

ثبت ورود و وضعیت آذوقه

دو سطر مربوط به وادی در journey-log.txt کنار هم نیستند. هرکدام را در جای فعلی خودش تغییر بده؛ کل فایل یا خط‌های میان آن‌ها را با کادر دوخطی جایگزین نکن.

انجام مأموریت فوری و ثبت Commit

میز کار شما اکنون کاملاً تمیز است! حالا می‌توانید مأموریت فوری گروه آذوقه را انجام دهید و ثبت کنید:

1
در فایل supplies.txt متن را با این گزارش جایگزین کنید:
textبه‌روزرسانی supplies.txt
Water and bread supplies checked for the detachment pass.
2
در فایل journey-log.txt خطوط مربوط به وادی را به‌روز کنید:
textبه‌روزرسانی journey-log.txt
Current valley: Detachment
Next checkpoint: Valley of Unity
3
تغییرات را بازبینی و یک Commit تمیز بسازید:
bashثبت Commit فوری
git diff -- supplies.txt journey-log.txt
git commit -am "Record arrival and verify detachment supplies"
تفکیک کامل وظایف: این Commit فوری کاملاً مستقل و تمیز بدون دخالت دادن کارهای نیمه‌کاره ثبت شد. کارهای نیمه‌کاره شما هنوز امن و سالم در پشته Stash منتظر شما هستند!

دیدن تفاوت Apply و Pop

مقایسه عملی git stash apply و git stash pop

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

1
اعمال Stash قدیمی‌تر (stash@{1}) با apply:
bashاجرای apply
git stash apply "stash@{1}"
2
مشاهده پشته (می‌بینید stash@{1} هنوز در لیست هست):
bashمشاهده لیست
git stash list
3
برگرداندن فایل به حالت قبل برای تست pop:
bashریست تغییر روی دیسک
git restore route-notes.txt
4
بازگرداندن همان Stash با pop (که آن را از پشته پاک می‌کند):
bashاجرای pop روی stash@{1}
git stash pop "stash@{1}"
5
بازگرداندن Stash بعدی (گزارش دیده‌بانی):
bashاجرای pop روی stash@{0}
git stash pop "stash@{0}"
6
بررسی وضعیت نهایی فایل‌ها:
bashوضعیت فعلی فایل‌ها
git status --short
نکته جابه‌جایی اندیس‌ها در Pop: وقتی stash@{1} را pop کردید، حذف شد و عضو باقی‌مانده همچنان اندیس stash@{0} گرفت! به همین دلیل pop دوم روی stash@{0} اجرا شد.

تکمیل گزارش و خالی‌کردن پشته

نهایی‌سازی کارهای نیمه‌کاره و ثبت Commit

اکنون هر دو کار نیمه‌کاره برگردانده شده‌اند. فایل‌ها را به نسخه نهایی تغییر دهید و یک Commit تمیز بسازید:

1
در فایل route-notes.txt عبارت Under review را به Confirmed تغییر دهید.
textتغییر نهایی route-notes.txt
Northern pass: Confirmed
2
محتوای scout-report.txt را به نسخه نهایی زیر تغییر دهید:
textتغییر نهایی scout-report.txt
SCOUT REPORT
Detachment pass: Safe
Signal point: Marked
3
تغییرات را Stage کرده، بازبینی کنید و Commit بزنید:
bashثبت Commit نهایی
git add route-notes.txt scout-report.txt
git diff --staged --check
git commit -m "Add detachment route report"
4
پاک بودن status و پشته Stash را چک کنید:
bashتأیید تمیزی مخزن
git status --short
git stash list
مخزن کاملاً تمیز: هر دو فرمان آخر بدون خروجی هستند؛ یعنی هم Working Tree و هم پشته Stash کاملاً پاک و مرتب شدند.

مدیر وقفه‌های کاروان

جمع‌بندی و چک‌لیست مهارت‌ها

شما در این درس یاد گرفتید چگونه وقفه‌های ناگهانی را بدون نامرتب کردن تاریخچه Commitها مدیریت کنید:

استفاده از git stash push -m "..." برای ذخیره موقت تغییرات فایل‌های Tracked.
اضافه کردن پرچم -u برای ذخیره همزمان فایل‌های جدید و Tracked نشده.
درک رفتار پشته‌ای (LIFO) و تغییر اندیس Stashها هنگام حذف یا اضافه شدن.
تفاوت کلیدی بین apply (حفظ در پشته) و pop (حذف پس از اعمال).
ثبت Commitهای مستقل و ایزوله برای کارهای اضطراری و کارهای نیمه‌کاره.

در درس بعدی، سراغ تغییرات موقت نمی‌رویم؛ بلکه خودِ اشاره‌گر شاخه و تاریخچه local را جابه‌جا کرده و اثر پرچم‌های soft، mixed و hard فرمان git reset را روی سه لایه Git بررسی می‌کنیم.

درس 2: یک Commit عقب، نوشته‌ها سر جایشان

اندازه اشتباه در پهنه تاریخچه

اصلاح تاریخچه محلی بدون از دست دادن تغییرات

یکی از کاتبان یک یادداشت درست را با پیام مبهم ثبت کرده است. قرار نیست برای اصلاح یک Commit محلی، کل کار انجام‌شده پاک شود؛ اما کاتب باید بداند با عقب‌بردن اشاره‌گر شاخه، کدام لایه‌ها دست‌نخورده باقی می‌مانند.

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

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

با پرچم git reset --soft فقط نوک تاریخچه شاخه را عقب می‌برید و کدهای تغییریافته را در Index (بخش Staged) نگه می‌دارید. سپس با حالت --mixed می‌توانید همان تغییر را از حالت Staged بیرون بیاورید، بی‌آنکه فایل‌های Working Tree شما دستخوش تغییر شوند.

هدف این درس: درک تفاوت دقیق دو حالت --soft و --mixed در فرمان git reset و مشاهده رفتار آن‌ها روی ۳ لایه گیت.

سه لایه و یک مقصد

تحلیل رفتار Git Reset روی سه لایه گیت

در حالت معمول، HEAD نام شاخه فعال شما را دنبال می‌کند. اجرای git reset TARGET اشاره‌گر شاخه فعال را به Commit مقصد می‌برد، و پرچم انتخابی تعیین می‌کند که Index و Working Tree چه وضعیتی پیدا کنند:

حالت --soft — فقط اشاره‌گر Branch را جابه‌جا می‌کند. لایه‌های Index و Working Tree دست‌نخورده می‌مانند (تغییرات Staged می‌مانند).
حالت --mixed (پیش‌فرض) — اشاره‌گر Branch را جابه‌جا کرده و Index را هم با Commit مقصد هماهنگ می‌کند. تغییرات در Working Tree باقی می‌مانند اما Unstaged می‌شوند.
حالت --hard — Branch، Index و تمام فایل‌های ردیابی‌شده در Working Tree را همزمان با Commit مقصد هماهنگ می‌کند (تغییرات کاملاً پاک می‌شوند!).
مفهوم HEAD~1: عبارت HEAD~1 یعنی «یک Commit قبل از HEAD فعلی». قبل از هر Reset همیشه تاریخچه را با git log چک کنید.

ساخت آزمایشگاه بازنویسی

ایجاد شاخه آزمایشی sandbox

برای اینکه تمرین‌های این درس آسیبی به شاخه اصلی نزند، یک شاخه آزمایشی جدید بسازید:

1
ساخت و سوئیچ به شاخه آزمایشی جدید:
bashایجاد شاخه sandbox
git switch -c sandbox/reset-lab
2
تأیید نام شاخه جاری:
bashبررسی نام شاخه
git branch --show-current
3
تأیید تمیز بودن محیط:
bashبررسی وضعیت
git status --short
تفکیک محیط آزمایشی: تمام آزمایش‌های بازنویسی تاریخچه در این درس روی sandbox/reset-lab انجام می‌شود تا شاخه اصلی main سالم بماند.

روشن‌کردن فانوس اردو

ثبت Commit پایه‌ای (مقصد امن)

ابتدا یک تغییر معتبر و دقیق در فایل route-notes.txt اعمال و Commit کنید. این نقطه، مقصد امن ما در Resetهای بعدی خواهد بود:

1
در فایل route-notes.txt عبارت Night camp: Ready را پیدا کرده و به عبارت زیر تغییر دهید:
textمحتوای جدید
Night camp: Lanterns ready
2
تغییر را Commit کنید:
bashثبت Commit پایه‌ای
git commit -am "Confirm lanterns at night camp"
نقطه اتکا (Safe Destination): این Commit سالم و دقیق همان HEAD~1 بعدی ما خواهد بود.

ثبت شتاب‌زده شمارش آذوقه

ثبت یک Commit با پیام مبهم

اکنون تغییر جدیدی در supplies.txt ایجاد کرده و آن را با یک پیام ضعیف و شتاب‌زده ثبت کنید:

1
محتوای supplies.txt را به عبارت زیر تغییر دهید:
textتغییر جدید
Water and bread supplies rechecked at sunset.
2
با پیام مبهم آن را Commit کنید:
bashCommit شتاب‌زده
git commit -am "Update notes"
3
تاریخچه ۳ Commit اخیر را بررسی کنید:
bashمشاهده تاریخچه
git log --oneline --decorate -n 3
مشاهده وضعیت تاریخچه: Commit مبهم در نوک Branch قرار گرفته و Commit فانوس دقیقاً در موقعیت HEAD~1 است.

عقب‌بردن نرم Branch

اجرای git reset --soft HEAD~1

حالا شاخه آزمایشی را با پرچم --soft یک Commit به عقب برگردانید:

1
عقب بردن اشاره‌گر شاخه با پرچم soft:
bashاجرای Reset Soft
git reset --soft HEAD~1
2
بررسی مجدد تاریخچه Commitها:
bashمشاهده log پس از Reset
git log --oneline --decorate -n 3
نتیجه اجرای Soft: نوک شاخه اکنون دوباره به Commit فانوس اشاره می‌کند. Commit شتاب‌زده از log شاخه غیب شده است، اما تغییرات فایل supplies.txt کجاست؟ در مرحله بعد ببینید!

نوشته باقی‌مانده در Index

اثبات حفظ تغییرات در Index (Staged)

برای اینکه ببینید تغییرات Commit حذف‌شده از بین نرفته‌اند، status و staged diff را چک کنید:

1
بررسی status خلاصه:
bashبررسی status
git status --short
2
بررسی Diff تغییرات Stage شده:
bashمشاهده Diff آماده برای Commit
git diff --staged -- supplies.txt
سبز بودن وضعیت: در status حرف M در ستون سمت چپ (رنگ سبز) قرار دارد. این یعنی --soft Commit را پاک کرد اما تغییرات فایل را به‌صورت Staged تحویل شما داد تا پیامش را اصلاح کنید!

Mixed، فقط برای خالی‌کردن Index

اجرای git reset --mixed HEAD

اکنون بدون پرچم (یا با پرچم --mixed)، فرمان reset را روی همین HEAD جاری اجرا کنید:

1
خارج کردن تغییرات از Stage با mixed reset:
bashاجرای Mixed Reset
git reset --mixed HEAD
2
بررسی مجدد status:
bashبررسی status
git status --short
قرمز شدن وضعیت (Unstaged): حرف M به ستون سمت راست (رنگ قرمز) منتقل شد. فایل همچنان تغییریافته است اما دیگر در Stage نیست. دستور git reset HEAD دقیقاً همین کار را انجام می‌دهد.

کنارگذاشتن آزمایش شتاب‌زده

پاکسازی Working Tree با git restore

تغییر Unstaged را مشاهده کرده و سپس آن را ریست کنید تا شاخه پاک شود:

1
مشاهده تغییرات Unstaged فایل:
bashDiff فایل supplies
git diff -- supplies.txt
2
بازگرداندن فایل به حالت Commit اخیر:
bashاجرای git restore
git restore supplies.txt
3
تأیید تمیزی کامل شاخه:
bashتأیید status تمیز
git status --short
پایان آزمایش: اکنون شاخه شما روی Commit سالم فانوس باقی مانده و تغییرات آزمایشی کلاً پاک شدند.

آیا Soft واقعاً بی‌خطر است؟

چالش: تحلیل رفتار Soft و Mixed

چرا نام --soft به این معنا نیست که فرمان همیشه بی‌خطر است؟ همچنین توضیح دهید چرا git reset --mixed HEAD در این تمرین اشاره‌گر شاخه را جابه‌جا نکرد، اما وضعیت Stage را تغییر داد؟

نقشه‌خوان سه لایه

جمع‌بندی و چک‌لیست مهارت‌ها

در این درس رفتار علمی Reset روی ۳ لایه گیت را یاد گرفتید:

تفاوت پرچم --soft (حفظ Index و Working Tree) و --mixed (حفظ فقط Working Tree).
مفهوم HEAD~1 برای اشاره به Commit والد (یک نود قبل‌تر در تاریخچه).
استفاده از Soft Reset برای اصلاح پیام یا اضافه/کم کردن تغییرات یک Commit محلی.
استفاده از Mixed Reset برای Unstage کردن تغییرات بدون پاک شدن کدها از روی دیسک.
تمیز نگه داشتن شاخه‌های اصلی با انجام آزمایش‌های Reset در شاخه‌های sandbox/*.

در درس بعد، حالت --hard را بررسی می‌کنیم و می‌بینیم چگونه گیت فایل‌ها را با Commit مقصد یکسان می‌کند و چطور می‌توان با reflog Commitهای ظاهراً نابودشده را نجات داد!

درس 3: پرتگاه Hard و طناب نجات

پیش از وزیدن باد، نشانی بگذار

شناخت خطر Hard Reset و طناب نجات گیت

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

عطار در وادی استغنا از جهانی می‌گوید که حتی افلاک و ستارگان در آن همچون برگی کوچک می‌شوند. فرمان git reset --hard هم می‌تواند تغییرات روی میز کار را در یک لحظه از نمای فعلی بردارد؛ پس نباید بی‌مقدمه و بدون احتیاط اجرا شود.

گر بریخت افلاک و انجم لخت لخت
در جهان کم گیر برگی از درخت
گر ز ماهی در عدم شد تا به ماه
پای مور لنگ شد در قعر چاه

در این درس یک Commit مفید می‌سازیم، یک شاخه پشتیبان (Backup Branch) روی آن قرار می‌دهیم و سپس Hard Reset را اجرا می‌کنیم. خواهید دید که تغییرات Commit‌شده از طناب نجات برمی‌گردند، اما ویرایش‌های ثبت‌نشده روی دیسک چنین تضمینی ندارند!

قانون طلایی Hard Reset: تغییرات Commit نشده (Uncommitted changes) در صورت Hard Reset به‌طور دائمی پاک می‌شوند و قابل بازیابی نیستند. فقط تغییراتی که حداقل یک بار Commit شده باشند شانس نجات دارند.

دامنه دقیق Hard Reset

رفتار فرمان git reset --hard

اجرای git reset --hard TARGET سه لایه شاخه فعال، Index و فایل‌های Tracked در Working Tree را همزمان با Snapshot مقصد هماهنگ می‌کند. تمام ویرایش‌های Staged و Unstaged آن فایل‌ها حذف خواهند شد.

حذف ویرایش‌های Commit‌نشده — فایل‌های تغییریافته ردیابی‌شده کاملاً به حالت Commit مقصد برمی‌گردند و تغییرات ثبت‌نشده شما پاک می‌شوند.
بازیابی Commitها با Reflog — اگر تغییرات را قبلاً Commit کرده باشید، خود Commit از پایگاه داده گیت پاک نمی‌شود و با git reflog قابل بازیابی است.
نقش شاخه پشتیبان — ساخت یک شاخه موقت مثل backup/xyz پیش از Reset، ساده‌ترین و امن‌ترین راه نجات Commitها است.
تفاوت Reflog و Backup Branch: ابزار git reflog دفترچه ثبت تمام جابه‌جایی‌های محلی HEAD است، اما یک شاخه پشتیبان نشانی روشن‌تر و مطمئن‌تری برای نجات ارائه می‌دهد.

کنترل لبه پرتگاه

بررسی وضعیت قبل از شروع تمرین

پیش از ادامه، نام شاخه، وضعیت فایل‌ها و آخرین Commitها را کنترل کنید:

1
بررسی نام شاخه جاری:
bashنام شاخه فعلی
git branch --show-current
2
تأیید تمیز بودن status:
bashبررسی وضعیت
git status --short
3
مشاهده ۳ Commit اخیر:
bashتاریخچه اخیر
git log --oneline --decorate -n 3
شرایط ورود: باید روی شاخه sandbox/reset-lab باشید، وضعیت تمیز باشد و Commit فانوس در بالای log دیده شود.

ثبت پناهگاه اضطراری

ایجاد و ثبت Commit پناهگاه اضطراری

در انتهای فایل route-notes.txt یک خط جدید اضافه کنید و آن را Commit نمایید:

1
افزودن خط زیر به انتهای route-notes.txt:
textخط جدید
Emergency shelter: Marked
2
ثبت Commit جدید:
bashثبت Commit
git commit -am "Mark emergency shelter"
3
مشاهده Commit ثبت‌شده:
bashتأیید Commit جدید
git log -1 --oneline --decorate
تغییر Commit‌شده: این Commit قرار است موقتاً در اثر Hard Reset ناپدید شود و سپس آن را نجات دهیم.

بستن طناب به Commit سالم

ایجاد شاخه پشتیبان روی Commit فعلی

پیش از اجرای Hard Reset، یک اشاره‌گر یا شاخه پشتیبان روی همین Commit بسازید:

1
ایجاد شاخه پشتیبان بدون سوئیچ کردن:
bashساخت شاخه backup
git branch backup/before-hard-reset
2
مشاهده شاخه‌ها و موقعیت آن‌ها:
bashفهرست دقیق شاخه‌ها
git branch -v
طناب نجات وصل شد! هر دو شاخه sandbox/reset-lab و backup/before-hard-reset اکنون دقیقاً به یک شناسه Commit اشاره می‌کنند.

یادداشت محلی بدون پشتیبان

ایجاد تغییر Commit‌نشده روی supplies.txt

محتوای supplies.txt را به‌صورت زیر تغییر دهید اما آن را Commit نکنید:

1
تغییر متن supplies.txt به عبارت زیر:
textتغییر Uncommitted
Water and bread supplies need another count.
2
مشاهده diff تغییر ثبت‌نشده:
bashDiff تغییرات Working Tree
git diff -- supplies.txt
تغییر بدون طناب نجات: این ویرایش فقط در Working Tree قرار دارد و شاخه پشتیبان فقط Commitها را نگه می‌دارد، نه تغییرات ثبت‌نشده روی دیسک را!

چه چیزی برمی‌گردد و چه چیزی نه؟

چالش: پیش‌بینی اثر Hard Reset

اگر اکنون git reset --hard HEAD~1 اجرا شود، چه اتفاقی برای شاخه آزمایشی، خط پناهگاه Commit‌شده، تغییر Commit‌نشده آذوقه و شاخه پشتیبان می‌افتد؟

اجرای بازگشت سخت کنترل‌شده

اجرای git reset --hard HEAD~1

اکنون فرمان Hard Reset را اجرا کرده و پاک شدن تغییرات را در Working Tree مشاهده کنید:

1
اجرای Hard Reset به یک Commit قبل:
bashاجرای Reset Hard
git reset --hard HEAD~1
2
بررسی وضعیت Working Tree:
bashبررسی status
git status --short
نتایج Hard Reset: status کاملاً تمیز است. خط پناهگاه از route-notes.txt پاک شد و فایل supplies.txt بدون هیچ ردی به حالت قبلی برگشت! ویرایش ثبت‌نشده آذوقه برای همیشه نابود شد.

رد پا در Reflog

بررسی Reflog و شاخه پشتیبان

رد حرکت HEAD را در Reflog و موقعیت شاخه پشتیبان ببینید:

1
مشاهده ۶ رخداد اخیر در Reflog:
bashبررسی reflog
git reflog -n 6
2
مشاهده آخرین Commit شاخه پشتیبان:
bashبررسی شاخه پشتیبان
git log --oneline backup/before-hard-reset -n 1
Reflog و Backup: فرمان Reflog تمام قدم‌های گیت شما را ثبت کرده است. همچنین شاخه پشتیبان هنوز مستقیماً به Commit پناهگاه اشاره می‌کند.

کشیدن Commit از طناب نجات

بازیابی Commit ناپدیدشده با شاخه پشتیبان

اکنون شاخه آزمایشی را به کمک شاخه پشتیبان به موقعیت Commit پناهگاه بازگردانید و سپس شاخه پشتیبان را پاک کنید:

1
بازگرداندن شاخه به موقعیت Backup:
bashبازیابی Commit با Reset Hard
git reset --hard backup/before-hard-reset
2
حذف شاخه پشتیبان موقت:
bashحذف شاخه پشتیبان
git branch -d backup/before-hard-reset
3
تأیید تمیزی وضعیت:
bashتأیید وضعیت final
git status --short
بازیابی موفق Commit: خط Emergency shelter: Marked دوباره برگردانده شد! اما یادداشت Commit نشده آذوقه برنگشت؛ زیرا فقط تغییرات Commit‌شده شانس بازیابی دارند.

نگهبان مقصدهای خطرناک

جمع‌بندی و چک‌لیست مهارت‌ها

در این درس کنترل کامل بر Hard Reset و راه‌های نجات کد را کسب کردید:

درک رفتار تخریبی git reset --hard روی فایل‌های ردیابی‌شده در Working Tree.
تفاوت سرنوشت تغییرات Commit‌شده (قابل بازیابی) و Commit‌نشده (نابودی دائمی).
نحوه ساخت شاخه پشتیبان (Backup Branch) به عنوان طناب نجات قبل از تغییرات خطرناک.
استفاده از git reflog برای پیدا کردن شناسه Commitهای ناپدیدشده.
بازیابی Commitهای از دست رفته با Reset کردن شاخه به شناسه Reflog یا شاخه پشتیبان.

در درس بعدی و پایانی این فصل، کارهای سالم انجام‌شده را به شاخه اصلی main منتقل کرده و نحوه لغو ایمن Commitهای عمومی را با دستور git revert فرا خواهیم گرفت.

درس 4: اصلاحیه‌ای که گذشته را پاک نمی‌کند

خبر اشتباه میان همه پرندگان

اصلاح تاریخچه عمومی بدون بازنویسی آن

خبر نادرستی درباره بسته‌بودن پناهگاه در دفتر اصلی ثبت شده و همه گروه‌ها آن را دیده‌اند. اگر کاتب اکنون تاریخچه را با Reset عقب بکشد، نسخه‌ای که دیگران دارند با روایت او متفاوت می‌شود؛ راه بهتر نوشتن یک اصلاحیه شفاف در ادامه همان دفتر است.

در وادی استغنا، عطار حادثه‌های بسیار بزرگ را در مقیاسی دیگر کوچک می‌بیند. کاتب هم قرار نیست برای یک خبر اشتباه تمام گذشته را جابه‌جا کند؛ فقط اثر همان تغییر را با یک Commit تازه خنثی می‌کند.

گر دو عالم شد همه یک بارنیست
در زمین ریگی همان انگار نیست
گر نماند از دیو وز مردم اثر
از سر یک قطره باران در گذر

فرمان git revert تفاوت‌های یک Commit مشخص را معکوس کرده و نتیجه را در یک Commit جدید ثبت می‌کند. به همین دلیل برای تاریخچه‌ای که قبلاً Push شده یا به اشتراک گذاشته شده است، روشی بسیار ایمن‌تر و حرفه‌ای‌تر از Reset محسوب می‌شود.

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

Reset جابه‌جا می‌کند، Revert اضافه

مقایسه راهبردی Reset و Revert

آگاهی از زمان مناسب برای استفاده از این دو ابزار کلیدی گیت، مرز بین یک توسعه‌دهنده تازه‌کار و حرفه‌ای است:

git reset

اشاره‌گر شاخه را به گذشته برمی‌گرداند و تاریخچه را بازنویسی می‌کند. مناسب برای شاخه‌های محلی (Local) قبل از Push.

git revert

تاریخچه را دست‌نخورده نگه می‌دارد و یک Commit جدید برای خنثی‌سازی می‌سازد. مناسب برای شاخه‌های عمومی (Shared/Remote).

احتمال بروز Conflict در Revert: اگر پس از Commit مورد نظر، تغییرات دیگری روی همان خطوط انجام شده باشد، اجرای Revert ممکن است باعث تعارض (Conflict) شود که باید آن را دستی حل کنید.

بازگرداندن دستاورد آزمایشگاه به main

ادغام Fast-forward شاخه آزمایشی به main

کارهای سالم انجام‌شده در شاخه آزمایشی sandbox/reset-lab را به شاخه اصلی منتقل کنید:

1
سوئیچ به شاخه اصلی main:
bashسوئیچ به main
git switch main
2
ادغام مستقیم و سریع (Fast-forward):
bashادغام FF به main
git merge --ff-only sandbox/reset-lab
3
حذف شاخه آزمایشی پایان‌یافته:
bashحذف شاخه sandbox
git branch -d sandbox/reset-lab
ادغام موفق: تمام دستاوردهای فانوس اردو و پناهگاه اضطراری اکنون بخشی از تاریخچه اصلی شاخه main هستند.

تحویل سالم پیش از اصلاحیه

بررسی وضعیت پایدار main

قبل از ثبت تغییر اشتباه، وضعیت شاخه اصلی را کنترل کنید:

1
بررسی لیست شاخه‌ها:
bashلیست شاخه‌ها
git branch -v
2
تأیید status تمیز:
bashبررسی وضعیت
git status --short
3
مشاهده ۵ Commit اخیر:
bash۵ Commit اخیر
git log --oneline --decorate -n 5
تأیید محیط: فقط شاخه main باید وجود داشته باشد و Commit پناهگاه در بالای تاریخچه قرار گرفته باشد.

ثبت عمدی خبر بسته‌بودن پناهگاه

ثبت یک Commit اشتباه (شبیه‌سازی خبر منتشرشده)

در فایل route-notes.txt وضعیت پناهگاه را عمداً به اشتباه تغییر دهید و Commit کنید:

1
در فایل route-notes.txt عبارت Emergency shelter: Marked را به عبارت زیر تغییر دهید:
textتغییر اشتباه
Emergency shelter: Closed
2
تغییر را با یک پیام مشخص Commit کنید:
bashثبت Commit اشتباه
git commit -am "Close emergency shelter by mistake"
فرض کنید این تغییر Push شده است: این Commit را تغییری فرض کنید که دیگران آن را دریافت کرده‌اند؛ بنابراین حق استفاده از Reset برای پاک کردن آن را نداریم.

شناخت دقیق Commit هدف

بازبینی دقیق Commit هدف قبل از Revert

قبل از خنثی‌سازی، جزییات و Diff دقیق Commit اخیر را بررسی کنید:

1
مشاهده جزییات Commit در نوک شاخه:
bashمشاهده جزییات HEAD
git show --stat --oneline HEAD
2
مشاهده Diff دقیق بین یک Commit قبل و HEAD:
bashمشاهده Diff
git diff HEAD~1 HEAD -- route-notes.txt
تحلیل Diff: تغییر وضعیت از Marked به Closed کاملاً مشخص است.

ساخت Commit اصلاحیه

اجرای git revert --no-edit HEAD

اکنون اثر Commit اخیر را با فرمان git revert معکوس کنید:

1
اجرای Revert با پذیرش پیام پیش‌فرض:
bashاجرای git revert
git revert --no-edit HEAD
ساخت Commit اصلاحی جدید: گیت یک Commit جدید ساخت و وضعیت پناهگاه را به Marked برگرداند. Commit اشتباه هنوز در تاریخچه هست و این Commit اصلاحی بعد از آن قرار گرفته است.

اثبات باقی‌ماندن هر دو روایت

تأیید نهایی تاریخچه و وضعیت فایل‌ها

حالت فایل‌ها، Log شاخه و تمیزی کلی مخزن را بررسی کنید:

1
مشاهده ۵ Commit اخیر شاخه:
bashمشاهده تاریخچه پنج‌تایی
git log --oneline --decorate -n 5
2
مقایسه Diff وضعیت قبل از خطا و وضعیت فعلی:
bashمقایسه Diff (خالی)
git diff HEAD~2 HEAD -- route-notes.txt
3
تأیید تمیز بودن status، stash و branches:
bashکنترل تمیزی مخزن
git status --short
git stash list
git branch -v
اثبات علمی Revert: خروجی log هم Commit اشتباه و هم Revert را نشان می‌دهد. Diff بین HEAD~2 و HEAD کاملاً خالی است (یعنی اثر تغییر خنثی شد).

اگر Revert وسط راه متوقف شد

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

برای ادامه: فایل‌های متعارض را اصلاح کن، PATH را با مسیر واقعی جایگزین کن و سپس:

bash
git add PATH
git revert --continue

یا اگر تصمیم به انصراف از همان عملیات ناتمام داری، به‌جای ادامه:

bash
git revert --abort
continue و abort دو انتخاب جایگزین‌اند؛ آن‌ها را پشت‌سرهم برای یک عملیات اجرا نکن.

Reset یا Revert برای تاریخچه مشترک؟

چالش: انتخاب بین Reset و Revert

یک Commit اشتباه قبلاً به هم‌تیمی‌ها رسیده و Push شده است. چرا معمولاً git revert COMMIT از عقب‌بردن main با Reset انتخاب مناسب‌تری است و چه محدودیتی همچنان دارد؟

نشان نگهبان تاریخچه

پایان فصل پنجم: کسب نشان نگهبان تاریخچه

تبریک! شما فصل پنجم را با موفقیت پشت سر گذاشتید و نشان نگهبان تاریخچه را کسب نمودید.

مدیریت کارهای نیمه‌کاره و وقفه‌های ناگهانی با git stash.
تسلط بر رفتار ۳ پرچم --soft، --mixed و --hard در git reset.
روش‌های ایمن‌سازی کد قبل از Reset سخت (ساخت Backup Branch و استفاده از reflog).
اصلاح تاریخچه عمومی و اشتراکی به کمک git revert بدون بازنویسی گذشته.

کاروان اکنون سبک‌بار و با یک تاریخچه واحد از وادی استغنا بیرون می‌آید. عطار در آغاز منزل بعد (وادی توحید)، چهره‌های بسیار را رو به یک افق می‌بیند. در فصل ششم، با ابزار git tag نقاط مهم این تاریخچه سالم را نشان‌گذاری خواهیم کرد.

بعد از این وادی توحید آیدت
منزل تفرید و تجرید آیدت
رویها چون زین بیابان درکنند
جمله سر از یک گریبان برکنند

فصل 6: وادی توحید

درس 1: مهری برای یک نسخهٔ قابل اعتماد

نشانی که باید پس از عبور هم پیدا شود

مهر تأیید و برچسب‌گذاری نسخه‌های رسمی

کاروان به نقطه‌ای رسیده که گزارش مسیر، آذوقه، دیده‌بانی و پناهگاه همگی درست و سنجیده شده‌اند. اگر فردا چند تغییر تازه به دفتر پروژه اضافه شود، عبارت «نسخه خوب دیروز» دیگر نشانی دقیق و قابل استنادی نیست؛ کاتب باید همین Commit تأییدشده را با یک مهر ثابت و دائمی نام‌گذاری کند.

کاروان وارد وادی توحید شده است؛ جایی که راه‌های بسیار به یک نشان مشترک و مقصدی واحد می‌رسند. برچسب (Tag) نیز نامی خوانا و انسانی برای یک نقطه مشخص از تاریخچه ارائه می‌دهد، بی‌آنکه شاخه تازه‌ای برای ادامه کار بسازد.

بعد از این وادی توحید آیدت
منزل تفرید و تجرید آیدت
روی‌ها چون زین بیابان درکنند
جمله سر از یک گریبان برکنند

مأموریت این درس ساخت یک برچسب شناسنامه‌دار (Annotated Tag) به نام v0.7.0 روی نسخه سالم و سپس اثبات عملی ثابت‌ماندن نشانی آن با پیشروی شاخه به سمت جلو است.

چرا به Tag نیاز داریم؟ شاخه‌ها (Branch) با هر Commit جدید جلو می‌روند، اما Tag مانند سنجاقی روی یک Commit خاص در تاریخچه ثابت می‌ماند و هرگز خودبه‌خود جابه‌جا نمی‌شود.

نامی خوانا برای یک شیء مشخص

برچسب (Tag) چیست و چه تفاوتی با Branch دارد؟

هر Commit با یک کد هش ۴۰ کاراکتری SHA-1 شناسایی می‌شود؛ اما استفاده از این کدهای پیچیده برای گفتگو درباره نسخه‌های انتشار (Releases) در تیم غیرعملی است. برچسب (Tag) یک اشاره‌گر ثابت با نامی خوانا مانند v0.7.0 است که در مسیر refs/tags/ ذخیره می‌شود.

شاخه (Branch)

اشاره‌گری متحرک است. با هر Commit جدید روی شاخه، اشاره‌گر به خودی خود جلو می‌رود تا نوک توسعه را نشان دهد.

برچسب (Tag)

اشاره‌گری ثابت است. روی یک Commit مشخص می‌ماند و حتی با ثبت Commitهای بعدی روی شاخه، جابه‌جا نمی‌شود.

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

آیا این نقطه آمادهٔ مهر انتشار است؟

بررسی سلامت Working Tree قبل از برچسب‌گذاری

پیش از ساخت Tag و ممهور کردن نسخه، ابتدا باید از سلامت کامل و تمیز بودن Working Tree و شاخه فعال اطمینان حاصل کنیم:

1
تأیید نام شاخه فعلی:
bashشاخه فعلی
git branch --show-current
2
تأیید تمیز بودن وضعیت مخزن (status):
bashبررسی وضعیت
git status --short
3
بررسی نبود فاصله اضافی یا خطای کاراکتری در تغییرات:
bashبررسی خطاهای ساختاری
git diff --check
4
مشاهده ۵ Commit اخیر در تاریخچه لاگ:
bashتاریخچه اخیر
git log --oneline --decorate -n 5
شرایط آماده‌سازی: نام شاخه باید main باشد و دو دستور میانی (status و diff) هیچ خروجی متنی نشان ندهند که به معنای پاک بودن کامل مخزن است.

بازبینی خود نسخه، نه فقط وضعیت آن

بازبینی محتوای گزارش‌های سه‌گانه پروژه

نسخه‌ای که قرار است نام v0.7.0 بگیرد باید دارای داده‌های معتبر و نهایی باشد. پیش از صدور مهر انتشار، گزارش‌های اصلی پروژه را بازخوانی و بازبینی کنید:

1
خواندن گزارش دفترچه سفر:
textمحتوای journey-log.txt
sed -n '1,20p' journey-log.txt
2
خواندن یادداشت‌های مسیر:
textمحتوای route-notes.txt
sed -n '1,20p' route-notes.txt
3
خواندن گزارش دیده‌بانی:
textمحتوای scout-report.txt
sed -n '1,20p' scout-report.txt
تأیید محتوا: مقصد بعدی باید Valley of Unity، پناهگاه Marked و گذرگاه Safe باشد. Tag جای آزمون را نمی‌گیرد؛ بلکه نقطه تأییدشده را برای همیشه مهر و موم می‌کند.

دو نوع برچسب برای دو نوع نیاز

مقایسه Lightweight Tag و Annotated Tag

در Git دو نوع برچسب (Tag) وجود دارد که بر اساس میزان حساسیت و کاربرد پروژه انتخاب می‌شوند:

برچسب سبُک (Lightweight Tag) — فقط یک اشاره‌گر ساده است که مستقیماً به کد هش یک Commit اشاره می‌کند؛ بدون پیام، تاریخ یا نام نویسنده. برای نشانه‌های موقت یا شخصی کاربرد دارد.
برچسب شناسنامه‌دار (Annotated Tag) — یک شیء مستقل (Tag Object) در دیتابیس Git است که شامل نام برچسب‌زننده، ایمیل، تاریخ، پیام توضیحی کامل و امضای دیجیتال است. استاندارد اصلی برای انتشار نسخه (Release).
توصیه مهندسی: همیشه برای انتشار نسخه‌های رسمی پروژه از Annotated Tag استفاده کنید تا تاریخچه و شناسنامه انتشار شفاف بماند.

ثبت نسخهٔ ۰٫۷ روی Commit تأییدشده

ساخت Annotated Tag به نام v0.7.0

چون HEAD اکنون روی Commit تأییدشده قرار دارد، برچسب شناسنامه‌دار را بدون نیاز به وارد کردن کد هش و مستقیماً روی نقطه فعلی می‌سازیم:

1
اجرای دستور ساخت Annotated Tag:
bashساخت Tag شناسنامه‌دار
git tag -a v0.7.0 -m "Verified journey milestone"
توضیح پرچم‌ها: پرچم -a مشخص می‌کند که Tag از نوع شناسنامه‌دار (Annotated) است و پرچم -m پیام توضیحی برچسب را در دیتابیس Git ثبت می‌کند.

خواندن شناسنامهٔ مهر

مشاهده فهرست و جزییات شناسنامه Tag

پس از ساخت برچسب، وجود آن در فهرست Tagها و اطلاعات شناسنامه‌ای ثبت‌شده در آن را بررسی کنید:

1
فیلتر کردن و نمایش لیست Tagها:
bashلیست Tagهای سری 0.7
git tag --list "v0.7.*"
2
مشاهده جزییات شناسنامه برچسب بدون نمایش تغییرات فایل‌ها:
bashمشاهده شناسنامه Tag
git show --no-patch v0.7.0
جزییات شناسنامه: خروجی دستور دوم شامل Tagger (نام و ایمیل شما)، تاریخ و زمان دقیق ساخت، پیام Tag و کد هش Commit اشاره‌شده خواهد بود.

اثبات این‌که نام و HEAD به یک نقطه می‌رسند

تحلیل ارجاع Tag به Commit با rev-parse

یک Annotated Tag ابتدا به یک Tag Object اشاره می‌کند و آن شیء به Commit اصلی وصل است. با علامت ^{} می‌توانید کد هش Commit پشت این Tag را استخراج و با HEAD مقایسه کنید:

1
استخراج کد هش HEAD جاری:
bashهش HEAD
git rev-parse HEAD
2
استخراج کد هش Commit مربوط به Tag:
bashهش Commit مربوط به v0.7.0
git rev-parse 'v0.7.0^{}'
تطابق کامل: هر دو کد هش ۴۰ کاراکتری کاملاً یکسان هستند، که نشان می‌دهد Tag دقیقاً روی همان Commit قرار گرفته است.

یک قدم پس از نسخهٔ نشانه‌گذاری‌شده

دو سطر کادر در دو جای جدا از journey-log.txt قرار دارند. هر سطر را جداگانه در محل فعلی ویرایش کن و تمام خطوط بین آن‌ها را نگه دار.

ثبت یک Commit جدید پس از Tag زدن

اکنون کاروان یک قدم به جلو می‌رود. فایل journey-log.txt را به‌روز کرده و Commit جدیدی ثبت کنید تا رفتار برچسب در برابر پیشروی شاخه را مشاهده کنیم:

1
در فایل journey-log.txt این دو خط را پیدا کنید:
textمتن قبلی
Current valley: Detachment
Next checkpoint: Valley of Unity
2
آن‌ها را با وضعیت ورود به وادی توحید جایگزین کنید:
textمتن جدید
Current valley: Unity
Next checkpoint: Valley of Wonder
3
تغییرات را بازبینی کرده و Commit بزنید:
bashثبت Commit جدید
git diff -- journey-log.txt
git commit -am "Enter the Valley of Unity"
تغییر پس از Tag: این Commit جدید بعد از نسخه برچسب‌گذاری‌شده ثبت شده است.

Tag ماند و main جلو رفت

اثبات ثابت ماندن Tag و پیشروی شاخه main

حالا موقعیت شاخه main و برچسب v0.7.0 را در تاریخچه لاگ مقایسه کنید:

1
مشاهده لاگ با نمایش موقعیت Tag و Branch:
bashمشاهده لاگ تزئین‌شده
git log --oneline --decorate -n 4
2
مشاهده آمار تغییرات بین Tag و main:
bashDiff بین Tag و main
git diff --stat v0.7.0..main
3
شمارش تعداد Commitهای جلوتر شاخه main:
bashتعداد Commit پیش‌افتاده
git rev-list --count v0.7.0..main
ثابت ماندن Tag: شاخه main یک Commit جلو رفته است اما برچسب v0.7.0 دقیقاً روی Commit قبلی باقی مانده است. عدد 1 نشان‌دهنده یک Commit تفاوت است.

اگر نسخهٔ منتشرشده اشتباه نشانه خورد

تحلیل آسیب‌شناسی جابه‌جایی یا اصلاح Tag منتشرشده

تیم Tag نسخه v0.7.0 را در مخزن مرکزی منتشر کرده و چند عضو تیم آن را در رایانه خود دریافت کرده‌اند. سپس مشخص می‌شود Commit واقعی انتشار، یک قدم جلوتر بوده است. چرا جابه‌جا کردن بی‌خبر همان برچسب خطرناک است و راهکار مهندسی و شفاف‌تر چیست؟

هشدار مهندسی: بازنویسی یک Tag منتشرشده باعث ایجاد دو ناهماهنگی در تیم می‌شود: کد هش‌های متفاوت برای یک نام نسخه واحد! همواره اصلاح را با ساخت نسخه جدید (مانند v0.7.1) انجام دهید.

یک مهر، دو نقطهٔ روشن

جمع‌بندی و چک‌لیست مهارت‌ها

در این درس نحوه نشانه‌گذاری ثابت نقاط تاریخچه پروژه را به طور کامل فرا گرفتید:

درک تفاوت بنیادین Branch (متحرک) و Tag (ثابت).
تفاوت Lightweight Tag (فقط نام) و Annotated Tag (شناسنامه‌دار با پیام و تاریخ).
نحوه ساخت Annotated Tag با دستور git tag -a NAME -m "MSG".
روش استخراج کد هش Commit پشت Tag با git rev-parse 'TAG^{}'.
مقایسه فاصله شاخه و برچسب با دستورهای git diff TAG..BRANCH و git rev-list.

در درس بعدی، مسئله انتخاب یک نقطه نیست؛ بلکه یک شاخه آزمایشی دارای چند Commit است و ما می‌خواهیم فقط یک Commit مشخص از آن را با ابزار git cherry-pick جدا کرده و به شاخه اصلی پیوند بزنیم!

گام بعدی: باتسلط بر برچسب‌گذاری، اکنون آماده‌اید تا در درس بعدی تکنیک انتخاب تک‌تک کامیت‌ها (Cherry-Pick) را بیاموزید.

درس 2: نجات یک پیام از شاخهٔ آزمایشی

یک پیام لازم میان دو تغییر

جداسازی کامیت‌های مفید از کارهای نیمه‌کاره

یک گروه آزمایشی در پروژه، هم راهنمای علامت‌های فانوس را نوشته و هم پیش‌نویس بنری را ساخته که هنوز رنگ و طرح آن معلوم نیست. مسیر اصلی پروژه (main) فقط به راهنمای کاربردی نیاز دارد؛ ادغام کامل این شاخه (Merge)، پیش‌نویس ناتمام را هم وارد تاریخچه اصلی پروژه می‌کند.

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

گر بسی بینی عدد، گر اندکی
آن یکی باشد درین ره در یکی
چون بسی باشد یک اندر یک مدام
آن یک اندر یک، یکی باشد تمام

برای حل این مسئله، اثر Commit راهنما را با فرمان git cherry-pick روی شاخه اصلی main بازسازی می‌کنیم؛ در حالی که شاخه آزمایشی و پیش‌نویس ناتمام، دست‌نخورده سر جای خود باقی می‌مانند.

راهکار Cherry-pick: این دستور به شما اجازه می‌دهد به‌جای ادغام کل یک شاخه (Merge)، تنها یک یا چند Commit مشخص را برداشته و روی شاخه فعلی بازسازی کنید.

Cherry-pick دقیقاً چه چیزی را برمی‌دارد؟

نحوه کارکرد دقیق فرمان git cherry-pick

دستور git cherry-pick COMMIT_HASH ابتدا تغییرات معرفی‌شده توسط Commit منتخب را نسبت به والدش محاسبه کرده، آن را روی HEAD فعلی اعمال می‌کند و یک Commit تازه می‌سازد:

ایجاد کامیت جدید — کامیت جدید در شاخه مقصد دارای کد هش (SHA-1 Hash) جدیدی خواهد بود، هرچند محتوای تغییرات و پیام آن کاملاً یکسان است.
استقلال شاخه منبع — این عملیات شاخه منبع را ادغام، جابه‌جا یا حذف نمی‌کند؛ فقط اثر یک کامیت را در شاخه مقصد تکرار می‌نماید.
احتمال تعارض (Conflict) — اگر فایل‌های شاخه مقصد تغییراتی مغایر با کامیت منتخب داشته باشند، ممکن است تعارض ایجاد شود که باید حل گردد.
استعاره نام‌گذاری: دستچین کردن (Cherry-pick) یعنی چیدن فقط همان میوه رسیده از روی شاخه درخت، بدون بریدن کل شاخه!

جداکردن آزمایش از مسیر اصلی

ایجاد شاخه آزمایشی experiment/signal-kit

ابتدا از وضعیت تمیز شاخه اصلی main یک شاخه آزمایشی جدید می‌سازیم تا تغییرات جدید را در محیطی ایزوله ثبت کنیم:

1
تأیید تمیز بودن Working Tree:
bashبررسی وضعیت
git status --short
2
ساخت و سوئیچ به شاخه آزمایشی:
bashایجاد شاخه آزمایشی
git switch -c experiment/signal-kit
محیط ایزوله: از این لحظه هر کارهای آزمایشی فقط روی شاخه experiment/signal-kit ثبت می‌شوند و شاخه main دست‌نخورده می‌ماند.

ثبت راهنمای قابل‌استفاده

ایجاد فایل راهنمای فانوس و ثبت Commit اول

فایل جدید signal-guide.txt را ایجاد کرده و محتوای راهنمای فانوس را در آن ثبت کنید:

1
فایل signal-guide.txt را ساخته و متن زیر را در آن قرار دهید:
textمحتوای signal-guide.txt
SIGNAL GUIDE
One lantern: Stop
Two lanterns: Move
2
تغییرات را Stage کرده، بازبینی کنید و Commit بزنید:
bashثبت Commit اول
git add signal-guide.txt
git diff --staged -- signal-guide.txt
git commit -m "Add lantern signal guide"
کامیت آماده: این Commit شامل یک راهنمای کامل و قابل‌استفاده برای پروژه است که آماده دستچین شدن می‌باشد.

ثبت پیش‌نویسی که هنوز آماده نیست

ایجاد فایل پیش‌نویس ناتمام و ثبت Commit دوم

در همان شاخه آزمایشی، فایل draft-banner.txt را برای بنر ناتمام ایجاد و ثبت کنید:

1
فایل draft-banner.txt را بسازید و متن زیر را در آن قرار دهید:
textمحتوای draft-banner.txt
DRAFT BANNER
Color: Undecided
2
تغییر را به عنوان کار ناتمام ثبت کنید:
bashثبت Commit دوم
git add draft-banner.txt
git commit -m "Draft an undecided signal banner"
دو کامیت متفاوت: اکنون شاخه آزمایشی دو Commit دارد: کامیت اول (راهنما) آماده است و کامیت دوم (بنر) ناتمام و غیرقابل قبول برای main است.

انتخاب Commit از روی محتوا

شناسایی کد هش دقیق کامیت منتخب

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

1
مشاهده لیست کامیت‌های جدید این شاخه به ترتیب زمانی:
bashلیست کامیت‌ها
git log --oneline --reverse main..HEAD
2
مشاهده کامیت‌های مرتبط با فایل راهنما در لاگ:
bashلاگ فایل راهنما
git log --oneline -- signal-guide.txt
3
مشاهده جزییات و diff دقیق کامیت راهنما (HEAD~1):
bashبازبینی جزییات کامیت
git show --stat HEAD~1
git show HEAD~1 -- signal-guide.txt
یادداشت کد هش: کد هش کوتاه کامیت Add lantern signal guide را یادداشت کنید تا در دستور Cherry-pick استفاده شود.

ایستادن روی مقصد پیش از انتخاب

بازگشت به شاخه اصلی main قبل از Cherry-pick

دستور git cherry-pick همواره تغییرات را روی HEAD فعلی اعمال می‌کند. بنابراین باید ابتدا روی شاخه مقصد (main) بایستید:

1
سوئیچ به شاخه اصلی main:
bashسوئیچ به main
git switch main
2
تأیید نام شاخه فعلی:
bashبررسی شاخه فعلی
git branch --show-current
3
تأیید عدم وجود دو فایل جدید در شاخه main:
bashبررسی نبود فایل‌ها
git ls-tree --name-only HEAD signal-guide.txt draft-banner.txt
هشدار جدی: اگر Cherry-pick را در حالی که روی شاخه آزمایشی ایستاده‌اید اجرا کنید، ممکن است عملیات به‌علت تغییر خالی متوقف شود؛ شاخه main بی‌تغییر باقی خواهد ماند!

بازسازی فقط Commit راهنما

اجرای git cherry-pick COMMIT_HASH

اکنون کد هش یادداشت‌شده کامیت راهنما را جلوی دستور git cherry-pick قرار داده و اجرا کنید:

1
اجرای دستور cherry-pick با کد هش واقعی:
bashاجرای Cherry-pick
git cherry-pick COMMIT_HASH
اعمال موفق: Git تغییرات کامیت راهنما را روی main پیاده کرد و کامیت جدیدی با همان پیام و محتوا ساخت.

اثبات این‌که پیش‌نویس وارد نشد

تأیید عدم ورود فایل پیش‌نویس ناتمام

برای اثبات اینکه فایل پیش‌نویس draft-banner.txt وارد شاخه اصلی نشده، درخت فایل‌ها و لاگ گرافیکی را چک کنید:

1
بررسی وجود فایل‌ها در شاخه main:
bashچک کردن لیست فایل‌ها
git ls-tree --name-only HEAD signal-guide.txt draft-banner.txt
2
مشاهده گراف تاریخچه تمام شاخه‌ها:
bashگراف کامل history
git log --graph --oneline --decorate --all -n 8
3
تأیید تمیز بودن وضعیت مخزن (status):
bashتأیید status
git status --short
نتیجه بازبینی: فقط فایل signal-guide.txt در main قرار گرفته است. تغییر راهنما روی شاخه آزمایشی و main دیده می‌شود. اگر والد، محتوا و تمام فراداده‌ها یکسان باشند، حتی شناسه commit ممکن است همان بماند؛ تفاوت هش شرط موفقیت این تمرین نیست.

وقتی انتخاب وسط راه متوقف می‌شود

این قدم فقط راهنمای موقعیت تعارض است؛ در اجرای موفق فعلی هیچ‌یک از فرمان‌های ادامه یا لغو را اجرا نکن. پس از حل تعارض، continue را انتخاب می‌کنی؛ یا پیش از پایان عملیات، برای انصراف abort را.
مدیریت Conflict احتمالی در Cherry-pick

اگر تغییرات کامیت منتخب با فایل‌های شاخه مقصد تداخل داشته باشد، Git پروسه را متوقف می‌کند تا تعارض را حل کنید:

1
پس از حل دستی فایل تعارض‌دار، آن را Stage کنید:
bashStage کردن فایل حل‌شده
git add PATH
2
ادامه پروسه Cherry-pick:
bashادامه Cherry-pick
git cherry-pick --continue
3
انصراف کامل و بازگشت به حالت قبل:
bashلغو Cherry-pick
git cherry-pick --abort
پرچم no-commit: با استفاده از git cherry-pick -n COMMIT_HASH تغییرات فقط در Working Tree و Index اعمال می‌شوند اما کامیت خودکار ساخته نمی‌شود.

انتخاب یک Commit یا پذیرفتن یک مسیر؟

تحلیل مقایسه‌ای کاربرد Merge و Cherry-pick

شاخه یک هم‌تیمی دارای پنج کامیت است که هر پنج تغییر برای محصول نهایی آماده‌اند. آیا Cherry-pick کردن تک‌تک آن‌ها بهترین انتخاب است؟ چه زمانی Merge و چه زمانی Cherry-pick مناسب‌تر است؟

توصیه معماری: برای ادغام کامل یک شاخه آماده حتماً از Merge استفاده کنید تا پیوند تاریخچه حفظ شود. Cherry-pick را برای دستچین کردن کامیت‌های مستقل و جداگانه نگه دارید.

کنارگذاشتن Branch آزمایشی با آگاهی

حذف شاخه آزمایشی حاوی کامیت ادغام‌نشده با -D

شاخه آزمایشی هنوز کامیت پیش‌نویس ادغام‌نشده دارد و مأموریتش تمام شده است. با آگاهی کامل آن را با پرچم -D حذف کنید:

1
بررسی کامیت‌های باقی‌مانده در شاخه آزمایشی:
bashکامیت‌های ادغام‌نشده
git log --oneline main..experiment/signal-kit
2
حذف اجباری شاخه آزمایشی:
bashحذف شاخه با -D
git branch -D experiment/signal-kit
3
تأیید لیست شاخه‌ها و status تمیز:
bashتأیید نهایی
git branch -v
git status --short
تفاوت -d و -D: پرچم -d اگر شاخه کامیت ادغام‌نشده داشته باشد اجازه حذف نمی‌دهد؛ اما پرچم -D حذف را به‌صورت اجباری انجام می‌دهد.

پیام مفید رسید، بار اضافه نرسید

جمع‌بندی و چک‌لیست مهارت‌ها

در این درس یاد گرفتید چگونه فقط تغییرات آماده را بدون وارد کردن کارهای نیمه‌کاره دستچین کنید:

درک دقیق کارکرد git cherry-pick COMMIT_HASH در بازسازی تغییرات روی HEAD.
نحوه استخراج کد هش کامیت از طریق git log و git show.
ضرورت ایستادن روی شاخه مقصد قبل از اجرای Cherry-pick.
مدیریت تعارض‌های احتمالی با --continue و --abort.
حذف شاخه‌های آزمایشی حاوی کارهای ناتمام با git branch -D.

در درس بعدی، دو کامیت محلی را روی یک پایه تازه بازپخش خواهیم کرد. آنجا تغییر کد هش‌ها را قبل و بعد از git rebase مقایسه می‌کنیم!

گام بعدی: با تسلط بر Cherry-pick، اکنون آماده‌اید تا در درس بعدی جادوی بازپخش لاگ با git rebase را فرابگیرید.

درس 3: صف تازه برای گزارش رسیدن

گزارشی که از کاروان عقب مانده است

هم‌صف کردن شاخه‌های توسعه با git rebase

کاتب روی یک شاخه جدید، دو بخش از گزارش رسیدن کاروان را می‌نویسد. در همان زمان، دفتر اصلی پروژه روی main یادداشت تازه‌ای دریافت می‌کند؛ حالا شاخه گزارش بر پایه یک Commit قدیمی‌تر ایستاده است.

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

نیست آن یک کان احد آید ترا
زان یکی کان در عدد آید ترا
چون برونست از احد وین از عدد
از ازل قطع نظر کن وز ابد

مأموریت این درس، بازپخش کامیت‌های شاخه خصوصی با دستور git rebase روی پایه جدید شاخه main، درک تفاوت Rebase و Merge و علت تغییر کد هش کامیت‌هاست.

هدف Rebase: تغییر دادن نقطه شروع (Base) یک شاخه به‌طوری که تمام کامیت‌های جدید آن روی جدیدترین کامیت شاخه اصلی (main) بازنویسی شوند.

بازپخش روی پایهٔ تازه

نحوه عملکرد git rebase main

وقتی روی شاخه ویژگی ایستاده‌اید و دستور git rebase main را اجرا می‌کنید، گیت کامیت‌های این شاخه را موقتاً کنار گذاشته، نوک شاخه را روی main جدید قرار می‌دهد و کامیت‌ها را به ترتیب بازپخش می‌کند:

ساخت کامیت‌های تازه — گیت کامیت‌های قبلی را واقعاً «جابه‌جا» نمی‌کند؛ بلکه کامیت‌های جدیدی با والد (Parent) جدید می‌سازد.
تغییر کد هش (SHA-1 Hash) — حتی اگر محتوای فایل‌ها تغییر نکند، به دلیل تغییر والد و زمان، کد هش کامیت‌ها کاملاً عوض می‌شود.
ثابت ماندن شاخه اصلی — خود شاخه main در طی عملیات Rebase جلو نمی‌رود؛ فقط شاخه فعلی به بالای main منتقل می‌شود.
مقایسه بصری: در نمودار درس می‌بینید که چگونه کامیت‌های دو شاخه موازی به یک خط راست و هموار تبدیل می‌شوند.

مرز امن بازنویسی تاریخچه

قانون طلایی Rebase (The Golden Rule of Rebase)

دستور Merge ساختار دو خط تاریخچه را حفظ می‌کند؛ اما Rebase هویت کامیت‌های بازپخش‌شده را تغییر می‌دهد. بنابراین قانون طلایی Rebase این است:

شاخه خصوصی (Local/Private)

کامیت‌هایی که هنوز Push نشده‌اند یا کس دیگری روی آن‌ها کار نمی‌کند. Rebase عالی، تمیزکننده و ایمن است.

شاخه عمومی (Public/Shared)

کامیت‌هایی که Push شده و اعضای تیم کدهای خود را روی آن بنا کرده‌اند. هرگز Rebase نکنید!

خطر Rebase روی شاخه‌های عمومی: بازنویسی کامیت‌های عمومی باعث سردرگمی همکاران، ایجاد کامیت‌های تکراری و خرابی تاریخچه مشترک می‌شود.

ساخت Branch خصوصی گزارش

ایجاد شاخه feature/arrival-report

از وضعیت تمیز شاخه اصلی main، شاخه گزارش رسیدن را ایجاد کنید:

1
بررسی تمیز بودن وضعیت مخزن (status):
bashبررسی وضعیت
git status --short
2
ساخت و سوئیچ به شاخه جدید:
bashایجاد شاخه arrival-report
git switch -c feature/arrival-report
شاخه خصوصی: این شاخه هنوز به هیچ جا Push نشده و محیطی کاملاً ایمن برای استفاده از Rebase است.

بخش اول گزارش رسیدن

ایجاد arrival-report.txt و ثبت کامیت اول

فایل جدید arrival-report.txt را با متن اولیه زیر بسازید و Commit کنید:

1
ایجاد فایل arrival-report.txt با محتوای زیر:
textمحتوای گزارش اولیه
ARRIVAL REPORT
Valley: Unity
2
Stage کردن و ثبت کامیت اول:
bashثبت کامیت اول
git add arrival-report.txt
git commit -m "Start the Unity arrival report"
کامیت اول: بخش اولیه گزارش با موفقیت روی شاخه جدید ثبت شد.

بخش دوم گزارش رسیدن

تکمیل گزارش و ثبت کامیت دوم

خط جدیدی به انتهای فایل اضافه کرده و Commit دوم را بسازید:

1
افزودن خط زیر به انتهای arrival-report.txt:
textخط جدید در گزارش
Formation: Unified
2
بازبینی Diff و ثبت کامیت دوم:
bashثبت کامیت دوم
git diff -- arrival-report.txt
git commit -am "Complete the Unity arrival report"
دو کامیت آماده: اکنون این شاخه ۲ کامیت اختصاصی برای گزارش رسیدن دارد.

عکس قبل از بازنویسی

ساخت Tag موقت before-rebase برای مقایسه

برای اینکه تغییر کد هش‌ها را پس از Rebase لمس کنید، یک Tag موقت روی نوک فعلی شاخه بگذارید:

1
ساخت Tag موقت before-rebase روی HEAD فعلی:
bashساخت Tag موقت
git tag before-rebase HEAD
2
مشاهده کد هش کوتاه کامیت‌های جدید:
bashمشاهده Hash قبل از Rebase
git log --format="%h %s" main..HEAD
یادداشت Hashها: کدهای هش کوتاه کامیت‌ها را به خاطر بسپارید تا پس از Rebase تغییر آن‌ها را بررسی کنیم.

جلو رفتن مستقل main

ایجاد کامیت جدید روی شاخه main (ایجاد واگرایی)

به شاخه main برگردید و Commit جدیدی بسازید تا شاخه اصلی یک قدم جلو برود:

1
سوئیچ به شاخه اصلی:
bashسوئیچ به main
git switch main
2
ساخت فایل journey-notes.txt با محتوای زیر:
textمحتوای journey-notes.txt
JOURNEY NOTES
Unity checkpoint: Confirmed
3
Stage کردن و ثبت کامیت رو شاخه main:
bashثبت کامیت روی main
git add journey-notes.txt
git commit -m "Confirm the Unity checkpoint"
وقوع واگرایی (Divergence): شاخه main یک کامیت جلو رفت در حالی که شاخه feature/arrival-report بر پایه کامیت قبلی main ساخته شده بود.

دیدن واگرایی پیش از تصمیم

مشاهده انشعاب تاریخچه در log --graph

گراف انشعاب شاخه‌ها را قبل از اجرای Rebase بازبینی کنید:

1
مشاهده گراف تاریخچه ۱۰ کامیت اخیر:
bashگراف واگرایی شاخه‌ها
git log --graph --oneline --decorate --all -n 10
تحلیل گراف: شاخه main یک مسیر و شاخه feature/arrival-report مسیر دیگری را طی کرده‌اند. اکنون زمان هم‌صف کردن آن‌ها با Rebase است.

بازپخش دو Commit روی main

اجرای git rebase main

روی شاخه گزارش سوئیچ کرده و دستور git rebase main را اجرا کنید:

1
سوئیچ به شاخه گزارش:
bashسوئیچ به arrival-report
git switch feature/arrival-report
2
اجرای Rebase روی main:
bashاجرای Rebase
git rebase main
بازپخش موفق: کامیت‌های شما بدون هیچ تعارضی روی جدیدترین کامیت main بازنویسی و منتقل شدند.

هویت تازه، محتوای آشنا

اثبات تغییر کد هش‌ها و حفظ محتوا

حالا کد هش‌های جدید را با Tag موقت before-rebase مقایسه کنید:

1
مشاهده کد هش‌ها و والدین جدید کامیت‌ها:
bashمشاهده Hashهای جدید
git log --format="%h %p %s" main..HEAD
2
مقایسه شاخه فعلی و Tag موقت:
bashمقایسه قبل و بعد
git log --left-right --oneline before-rebase...HEAD
3
تأیید یکسان بودن محتوای فایل‌ها (Diff خالی):
bashDiff محتوای فایل
git diff before-rebase HEAD -- arrival-report.txt
نتیجه علمی: خروجی Diff کاملاً خالی است؛ یعنی محتوا ۱۰۰٪ حفظ شده، اما کد هش کامیت‌ها به دلیل تغییر والد کاملاً عوض شده است!

تحویل خطی بدون Merge Commit

ادغام Fast-forward شاخه Rebase شده به main

چون شاخه گزارش اکنون کاملاً خطی و جلوی main قرار دارد، آن را با --ff-only ادغام کرده و شاخه‌ها و تگ‌های موقت را پاک کنید:

1
سوئیچ به main:
bashسوئیچ به main
git switch main
2
ادغام Fast-forward:
bashادغام FF-only
git merge --ff-only feature/arrival-report
3
حذف شاخه و Tag موقت:
bashپاک‌سازی شاخه و تگ موقت
git branch -d feature/arrival-report
git tag -d before-rebase
تاریخچه کاملاً خطی: ادغام بدون هیچ کامیت اضافه Merge انجام شد و تاریخچه خط مستقیم دارد.

ممیزی پایان وادی

تأیید تمیزی کامل مخزن

وضعیت نهایی مخزن را ممیزی کنید:

1
استعلام وضعیت (status) و شاخه‌ها:
bashبررسی status و شاخه
git status --short
git branch -v
2
استعلام لیست Tagها:
bashبررسی Tagها
git tag --list
3
مشاهده ۱۰ کامیت اخیر در لاگ گرافیکی:
bashگراف نهایی پروژه
git log --graph --oneline --decorate -n 10
تأیید نهایی: فقط شاخه main و Tag رسمی v0.7.0 باقی مانده‌اند و مخزن کاملاً منظم است.

آیا هر Branch منتشرشده ممنوع است؟

تحلیل کاربرد Rebase روی Remote Branch شخصی

یک Branch شخصی را دیروز Push کرده‌اید، اما هیچ‌کس آن را دریافت نکرده یا کارش را بر کامیت‌هایش نساخته است. آیا Rebase مطلقاً ممنوع است؟ معیار تصمیم و خطر اصلی چیست؟

معیار اصلی: عدم وابستگی دیگران به تاریخچه شاخه است. اگر کسی کدهایش را روی شاخه شما بنا کرده باشد، Rebase ممنوع است.

نشان نگهبان یک تاریخچهٔ روشن

پایان فصل ششم: کسب نشان تاریخچه روشن

تبریک! شما فصل ششم را با موفقیت پشت سر گذاشتید و با سه ابزار قدرتمند Git برای کنترل تاریخچه آشنا شدید:

ثبت برچسب‌های ثابت نسخه با git tag -a.
دستچین کردن کامیت‌های منفرد با git cherry-pick.
هم‌صف کردن و ساخت تاریخچه خطی با git rebase.
درک قانون طلایی Rebase و پرهیز از بازنویسی تاریخچه‌های عمومی.

کاروان اکنون آماده ورود به وادی هفتم (وادی حیرت) است، جایی که ارتباط مخزن محلی روی سیستم با مخزن ابری در GitHub برپا خواهد شد!

بعد ازین وادی حیرت آیدت
کار دایم درد و حسرت آیدت
هر نفس اینجا چو تیغی باشدت
هر دمی اینجا دریغی باشدت

ورود به وادی بعدی: با تسلط بر ابزارهای تاریخچه، آماده‌اید تا در فصل بعدی اتصال مخزن محلی به GitHub و دنیای آنلاین را فرابگیرید.

فصل 7: وادی حیرت

درس 1: نشانی آشیانه، نه خود آشیانه

دفتر محلی و آشیانه‌ای که هنوز خالی است

مفهوم مخزن ابری (Remote Repository)

پروژه simorgh-journey اکنون روی رایانه کاتب تاریخچه‌ای کامل دارد؛ اما اگر سیستم آسیب ببیند یا هم‌تیمی بخواهد آن را ببیند، این دفتر محلی به تنهایی کافی نیست. کاروان به یک Remote Repository احتیاج دارد تا Git بتواند با آن داده رد و بدل کند.

این نخستین قدم در وادی حیرت است: یک پروژه می‌تواند در چند مکان مختلف تاریخچه داشته باشد، اما هیچ نسخه‌ای خودبه‌خود و بدون فرمان ما از نسخه دیگر باخبر نمی‌شود! باید آدرس مقصد را تعریف و سپس آگاهانه Fetch یا Push کنیم.

بعد ازین وادی حیرت آیدت
کار دایم درد و حسرت آیدت
هر نفس اینجا چو تیغی باشدت
هر دمی اینجا دریغی باشدت

هدف این درس: ثبت آدرس GitHub با نام قرارداد origin و تست دسترسی اولیه، بدون ارسال هیچ کامیت یا برچسبی.

Local و Remote دو مخزن مستقل‌اند

تفاوت و استقلال مخزن محلی و مخزن ابری

مخزن محلی (Local Repository) شامل Working Tree و دیتابیس .git روی سیستم شماست. مخزن روی GitHub مخزنی کاملاً جداگانه است؛ می‌تواند Commitهایی داشته باشد که نسخه محلی ندارد و برعکس.

عبارت Remote در تنظیمات Git محلی، فقط یک نام کوتاه برای URL مخزن دیگر و قواعد دریافت داده از آن است. افزودن Remote هیچ فایل یا کامیتی را منتقل نمی‌کند؛ بلکه فقط راه ارتباطی را تعریف می‌نماید.

مخزن محلی (Local Repository)

مخزن روی سیستم شما با دیتابیس .git و تمام شاخه‌ها و کامیت‌های محلی.

مخزن ابری (Remote Repository)

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

تعریف Remote: افزودن Remote فقط ذخیره‌سازی نام و URL در کانفیگ گیت است و هیچ فایلی را خودبه‌خود منتقل نمی‌کند.

تحویل تمیز فصل قبل

بررسی وضعیت اولیه و تمیز مخزن محلی

پیش از اتصال به Remote، مطمئن شوید که روی شاخه اصلی main قرار دارید و وضعیت مخزن کاملاً تمیز است:

1
بررسی شاخه فعلی، وضعیت status، لیست برچسب‌ها و لاگ اخیر:
bashبررسی وضعیت محلی
git branch --show-current
git status --short
git tag --list
git log --oneline --decorate -n 6
شاخه جاری — باید روی شاخه main باشید.
وضعیت تمیز — خروجی دستور status باید کاملاً خالی باشد.
برچسب رسمی — تگ v0.7.0 در لیست برچسب‌ها دیده شود.
آخرین کامیت — کامیت گزارش رسیدن در بالای تاریخچه لاگ قرار داشته باشد.
آمادگی مخزن: مخزن محلی کاملاً آماده و تمیز است تا به مقصد آنلاین متصل شود.

ساخت مقصد خالی در GitHub

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

ایجاد مخزن تازه و خالی در GitHub

در GitHub یک Repository تازه با نام دقیق simorgh-journey بسازید. برای ادامه ساده دوره، تنظیمات را روی حالت Public بگذارید:

1
وارد اکانت GitHub شوید، روی آیکن + در بالا راست کلیک کرده و New repository را بزنید.
2
نام مخزن را دقیقاً simorgh-journey بگذارید.
3
گزینه Public را انتخاب کنید.
4
هیچ‌کدام از گزینه‌های ساخت خودکار README، فایل .gitignore یا License را تیک نزنید!
5
روی دکمه Create repository کلیک کرده و لینک HTTPS آن را کپی کنید.
نکته بسیار مهم: مخزن آنلاین باید کاملاً خالی ایجاد شود؛ زیرا پروژه محلی شما از قبل تاریخچه کامل دارد و نباید تعارض اولیه ایجاد گردد.
اگر مخزنی با همین نام از قبل داری، آن را حذف یا بازنویسی نکن. یک مخزن خالی با نام آزاد انتخاب کن و از اینجا به بعد URL واقعی همان مخزن را در clone، remote و نشانی سایت جایگزین کن. تمرین روی مخزن شخصی با اجازه نوشتن انجام می‌شود، نه مخزن سازمانی با قواعد اجباری.

یک نام کوتاه برای یک مقصد دور

چرا نام origin را انتخاب می‌کنیم؟

نام origin قرارداد رایج در Git برای Remote اصلی است، اما واژه‌ای جادویی یا اجباری نیست. در هر Repository محلی می‌توانید چند Remote با نام‌های متفاوت داشته باشید.

فلش‌های نمودار زیر امکان ارتباط را نشان می‌دهند. تا زمانی که فرمانی مانند Fetch یا Push اجرا نشود، دو Repository کاملاً مستقل می‌مانند.

چند ریموت هم‌زمان: اگر با چند سرور کار کنید، می‌توانید ریموت‌های دیگری مثل upstream یا backup نیز تعریف کنید.

ثبت origin با URL واقعی

در این قدم، origin باید به مخزن simorgh-journey در حساب خودت اشاره کند. مخزن رسمی فایل‌های دوره فقط محل دریافت بستهٔ شروع است؛ URL آن را جای آدرس نمونهٔ فرمان زیر نگذار.

افزودن Remote origin با دستور git remote add

ابتدا عدم وجود Remote را بررسی کرده، سپس URL واقعی اکانت خود را جای عبارت نمونه بگذارید:

1
بررسی لیست ریموت‌های موجود و ثبت origin:
bashافزودن Remote origin
git remote
git remote add origin https://github.com/YOUR_GITHUB_USERNAME/simorgh-journey.git
بررسی ریموت موجود: اگر دستور اول نامی به شما نشان داد، ریموت دیگری نسازید؛ با git remote -v آن را بررسی کرده و در صورت نیاز URL را ویرایش کنید.

دیدن URL دریافت و ارسال

مشاهده آدرس‌های Fetch و Push ثبت‌شده

آدرس ذخیره‌شده را از دو راه مختلف بررسی کنید:

1
مشاهده آدرس‌های کامل ثبت‌شده در ریموت origin:
bashمشاهده آدرس‌های ثبت‌شده
git remote -v
git remote get-url origin
git remote get-url --push origin
URL دریافت (Fetch) — آدرسی که داده‌ها و شاخه‌ها از آن خوانده می‌شوند.
URL ارسال (Push) — آدرسی که کامیت‌های محلی شما به آن فرستاده می‌شوند.
تنظیمات پیش‌فرض: در حالت عادی URL دریافت و ارسال یکسان هستند. این دستورات فقط صحت کانفیگ محلی را نشان می‌دهند و داده‌ای منتقل نکرده‌اند.

پشت صحنهٔ Remote در تنظیمات Git

خواندن مستقیم تنظیمات Remote از فایل کانفیگ

دو بخش مهم تنظیم محلی Remote را مستقیماً از فایل تنظیمات Git بخوانید:

1
خواندن مستقیم کلیدهای تنظیمات از کانفیگ گیت:
bashخواندن مستقیم تنظیمات Git
git config --get remote.origin.url
git config --get-all remote.origin.fetch
مفهوم RefSpec: عبارت +refs/heads/*:refs/remotes/origin/* مشخص می‌کند که شاخه‌های مقصد هنگام Fetch در کدام فضای محلی ثبت شوند.

آزمون دسترسی بدون انتقال پروژه

تست ارتباط با مقصد با دستور git ls-remote

از Git بخواهید ارجاعات (References) مقصد را بدون دریافت داده فهرست کند:

1
اجرای تست دسترسی و ارتباط شبکه با مخزن آنلاین:
bashتست ارتباط با ls-remote
git ls-remote origin
تست ارتباط: برای یک مخزن کاملاً خالی خروجی متنی خاصی دیده نمی‌شود، اما عدم نمایش خطای اتصال نشان‌دهنده دسترسی موفق است.

چرا Remote هنوز پشتیبان نیست؟

تحلیل تفاوت تعریف Remote و ارسال داده

کاتب فرمان git remote add origin URL را اجرا کرده و سپس سیستمش خاموش و فایل‌های محلی آسیب می‌بینند. چرا فقط اضافه کردن origin نسخه پشتیبان نساخته بود؟

نتیجه تحلیلی: ثبت ریموت فقط پل ارتباطی را تعریف می‌کند. تا زمانی که Push موفق صورت نگیرد، هیچ داده‌ای در مقصد ذخیره نمی‌شود.

پل ساخته شد، نامه هنوز اینجاست

جمع‌بندی و چک‌لیست مهارت‌ها

اکنون پروژه محلی یک Remote به نام origin با URL واقعی دارد و دسترسی آن را بدون انتقال تاریخچه آزموده‌اید:

مفهوم Remote Repository و استقلال مخزن محلی و ابری را درک کردید.
یک Repository خالی در GitHub ساختید و لینک HTTPS آن را کپی نمودید.
ریموت origin را ثبت و با دستورهای git remote -v و ls-remote تست کردید.
آماده شدید تا در درس بعدی نخستین ارسال کامیت‌ها و تگ‌ها را انجام دهید.
گام بعدی: در درس آینده، نخستین ارسال (Push) کامیت‌ها و برچسب نسخه v0.7.0 را به GitHub انجام خواهیم داد!

درس 2: اولین تحویل به GitHub

نامه‌ای که باید مقصد دقیق داشته باشد

تنظیم مقصد و Upstream Tracking در نخستین ارسال

نشانی آشیانه ثبت شده، اما Git هنوز نمی‌داند کدام شاخه محلی باید با کدام شاخه دوردست جفت شود. نخستین ارسال باید هم شاخه main را منتقل کند و هم رابطه پیگیری (Upstream Tracking) آن را مشخص سازد.

در وادی حیرت، هر قدم پرسشی تازه می‌سازد. اینجا هم موفق‌شدن یک Push پایان بررسی نیست؛ باید بدانیم چه ارجاعاتی (References) فرستاده شده‌اند، چه چیزهایی مثل Tag جا مانده‌اند و Upstream دقیقاً چه کمکی می‌کند.

هر نفس اینجا چو تیغی باشدت
هر دمی اینجا دریغی باشدت
آه باشد، درد باشد، سوز هم
روز و شب باشد، نه شب نه روز هم

چرخه آموزش این درس: ارسال نخست با -u، بررسی Upstream، ارسال صریح Tag، ایجاد کامیت جدید محلی، مشاهده ahead 1 و در نهایت Push کوتاه پایانی.

Push چه چیزی را به‌روز می‌کند؟

نحوه کارکرد git push -u origin main

فرمان git push آبجکت‌های لازم را فرستاده و از Remote می‌خواهد ارجاع (Reference) مقصد را به‌روز کند. در دستور git push -u origin main، مقصد ریموت origin و شاخه منبع main است.

سوئیچ -u یا --set-upstream رابطه پیگیری میان main محلی و origin/main را در تنظیمات محلی ثبت می‌کند؛ بنابراین فرمان‌های بعدی مقصد پیش‌فرض و وضعیت ahead/behind را می‌شناسند.

انتقال Commitها — دستور git push تمام کامیت‌ها و شیءهای جدید محلی را به دیتابیس آنلاین فرستاده و اشاره‌گر origin/main را به‌روز می‌کند.
تنظیم Upstream (--set-upstream) — پرچم -u رابطه پیگیری دائمی بین main محلی و origin/main ریموت ایجاد می‌کند.
شناسایی وضعیت ahead/behind — با ثبت Upstream، گیت در دستور status عینا به شما می‌گوید چند کامیت از سرور جلوتر یا عقب‌تر هستید.
نتیجه اصلی: پس از یک بار اجرای git push -u origin main، در دفعات بعدی فقط کافیست دستور کوتاه git push را اجرا کنید.

بازبینی پیش از نخستین ارسال

بازبینی وضعیت مخزن محلی پیش از نخستین ارسال

شاخه، وضعیت status و آدرس ریموت را پیش از اولین انتقال دوباره بررسی کنید:

1
تأیید شاخه فعال، تمیز بودن status، لیست ریموت‌ها و لاگ اخیر:
bashبازبینی پیش از ارسال
git branch --show-current
git status --short
git remote -v
git log --oneline --decorate -n 5
شاخه فعال — باید روی شاخه main باشید.
وضعیت تمیز — هیچ تغییر Unstaged یا Staged مانده‌ای نباید در status باشد.
ریموت درست — آدرس origin باید به اکانت خودتان در GitHub اشاره کند.
آمادگی ارسال: همه چیز برای نخستین Push به GitHub آماده است.

فرستادن main و ساخت Upstream

ارسال شاخه main و تنظیم Upstream

نخستین ارسال شاخه اصلی را همراه با پرچم -u انجام دهید:

1
اجرای دستور ارسال اولیه و ثبت Upstream:
bashارسال شاخه main و تنظیم Upstream
git push -u origin main
احراز هویت امن: در صورت درخواست GitHub، ورود را از طریق مرورگر یا Personal Access Token انجام دهید؛ هرگز رمز عبور عادی اکانت را در ترمینال وارد نکنید.

دیدن رابطهٔ پیگیری

بررسی رابطه Upstream و وضعیت هماهنگی

پس از Push موفق، جفت‌شدن شاخه‌ها را از چند زاویه بررسی کنید:

1
استعلام وضعیت پیگیری شاخه و مقادیر کانفیگ محلی:
bashبررسی رابطه Upstream
git branch -vv
git status -sb
git config --get branch.main.remote
git config --get branch.main.merge
هماهنگی کامل: خروجی ## main...origin/main نشان می‌دهد شاخه محلی شما عینا با ریموت هماهنگ است و هیچ کامیت جلوتر یا عقب‌تری ندارد.

Tag با Push شاخه سفر نکرد

ارسال صریح برچسب رسمی v0.7.0 به GitHub

فرمان git push عادی شاخه‌ها، الزاماً Tagهای محلی را نمی‌فرستد! ابتدا Tag محلی را مشاهده کرده و سپس همان نسخه رسمی را صریحاً ارسال کنید:

1
مشاهده لیست تگ‌های محلی و ارسال صریح تگ v0.7.0:
bashارسال صریح تگ v0.7.0
git tag --list
git push origin v0.7.0
ارسال تگ‌ها: می‌توانید از git push --tags استفاده کنید، اما ارسال دانه به دانه تگ‌های رسمی مانند v0.7.0 دقت و امنیت بیشتری دارد.

بازبینی تحویل در GitHub

بازبینی تحویل فایل‌ها و تگ در GitHub

صفحه Repository خود را در GitHub تازه کنید. فایل‌های پروژه را باز کرده و سپس بخش Tags یا Releases را ببینید:

1
وارد مرورگر شوید و صفحه ریپازیتوری GitHub را رفرش کنید.
2
مطمئن شوید فایل‌های پروژه و کامیت‌ها ظاهر شده‌اند.
3
تب Tags را باز کنید و وجود v0.7.0 را تأیید نمایید.
4
کد کوتاه Hash آخرین کامیت محلی را با دستور git rev-parse --short HEAD گرفته و با GitHub مقایسه کنید.
تأیید تصویری: تمام تغییرات محلی و برچسب نسخه روی GitHub قابل مشاهده و دسترسی هستند.

یک چک‌لیست برای ارسال‌های بعدی

ایجاد فایل چک‌لیست اشتراک‌گذاری در پروژه

در ریشه پروژه فایل چک‌لیست sharing-checklist.txt را بسازید:

1
فایل sharing-checklist.txt را ساخته و متن زیر را در آن قرار دهید:
textمحتوای sharing-checklist.txt
SHARING CHECKLIST
Review changes
Commit locally
Pull before push
Verify remote
2
تغییر را Stage کرده و کامیت بزنید:
bashکامیت کردن چک‌لیست اشتراک‌گذاری
git add sharing-checklist.txt
git diff --staged -- sharing-checklist.txt
git commit -m "Add the sharing checklist"
کامیت محلی جدید: چک‌لیست در دیتابیس محلی ذخیره شد، اما هنوز روی GitHub قرار نگرفته است.

Local یک Commit جلوتر است

مشاهده وضعیت ahead 1 در status

پیش از Push دوم، اختلاف قابل دسترسی بودن Commitها را مشاهده کنید:

1
مشاهده خلاصه status و لاگ کامیت‌های فرستاده‌نشده:
bashمشاهده وضعیت ahead
git status -sb
git log --oneline origin/main..main
مفهوم ahead 1: عبارت [ahead 1] نشان می‌دهد دیتابیس محلی شما دقیقاً ۱ کامیت جدید دارد که هنوز روی سرور آنلاین فرستاده نشده است.

Push کوتاه با مقصد شناخته‌شده

ارسال با دستور کوتاه git push

به کمک Upstream تنظیم‌شده در قدم ۴، اکنون اجرای فرمان کوتاه کافی است:

1
اجرای push کوتاه و استعلام وضعیت نهایی:
bashPush کوتاه
git push
git status -sb
git log --oneline origin/main..main
ارسال موفق: نشانه ahead حذف شد و هر دو مخزن محلی و ابری عینا روی یک کامیت هم‌نقطه شدند.

چرا Push ممکن است رد شود؟

تحلیل خطای non-fast-forward در git push

هم‌تیمی شما کامیت تازه‌ای روی origin/main فرستاده و Local شما آن را ندارد. چرا یک Push عادی ممکن است با خطای non-fast-forward رد شود و قدم امن بعدی چیست؟

پاسخ ایمن: در این شرایط هرگز نباید Force Push کرد! روش صحیح اجرای Fetch/Pull، بررسی کدهای هم‌تیمی و ادغام یا Rebase آگاهانه است.

دو سوی مسیر هم‌نقطه شدند

جمع‌بندی و چک‌لیست مهارت‌ها

شاخه main و Tag رسمی روی GitHub قرار گرفته‌اند، Upstream تنظیم شده و چک‌لیست اشتراک‌گذاری نیز با Push کوتاه به مقصد رسیده است:

ارسال شاخه اصلی همراه با تنظیم Upstream با git push -u origin main.
درک علت عدم ارسال خودکار تگ‌ها و ارسال صریح تگ v0.7.0.
مشاهده و درک مفهوم ahead 1 هنگام ایجاد کامیت محلی جدید.
استفاده از git push کوتاه به لطف Upstream.
گام بعدی: در درس آینده، نحوه دریافت آخرین تغییرات آنلاین (Fetch و Pull) از GitHub را فرا خواهیم گرفت!

درس 3: خبر رسید، دفتر تکان نخورد

دیدن خبر پیش از پذیرفتن آن

مفهوم جداسازی دانلود داده از ادغام با git fetch

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

در وادی حیرت، ندانستن با نگاه دقیق پاسخ می‌گیرد، نه با حدس! Fetch همین فاصله امن را می‌دهد: خبر Remote به Repository محلی می‌رسد، اما شاخه جاری و Working Tree هنوز تصمیم نگرفته‌اند آن را بپذیرند.

مرد حیران چون رسد این جایگاه
در تحیر مانده و گم کرده راه
هرچ زد توحید بر جانش رقم
جمله گم گردد ازو، گم نیز هم

هدف این درس: درک تفاوت حیاتی دانلود داده‌ها (Fetch) با تغییر دادن فایل‌های جاری (Working Tree) در خروجی واقعی Git.

Fetch نقشهٔ محلی Remote را تازه می‌کند

نحوه کارکرد دقیق فرمان git fetch origin

فرمان git fetch origin آبجکت‌ها و ارجاعات (References) اعلام‌شده از مقصد را دریافت می‌کند. طبق RefSpec معمول، شاخه main دوردست در مرجع محلی origin/main بازتاب پیدا می‌کند.

شاخه origin/main خودِ شاخه زنده روی GitHub نیست؛ بلکه آخرین وضعیت شناخته‌شده آن در دیتابیس محلی شماست. Fetch آن را تازه می‌کند، ولی main، Index و Working Tree سیستم شما را جابه‌جا نمی‌کند.

دریافت شیءها — دستور git fetch origin تمام کامیت‌ها و شیءهای جدید را از سرور دانلود کرده و در دیتابیس محلی قرار می‌دهد.
به‌روزرسانی origin/main — اشاره‌گر origin/main محلی به جدیدترین کامیت سرور منتقل می‌شود تا آخرین وضعیت آنلاین را بازتاب دهد.
امنیت کامل Working Tree — شاخه فعال main، فایل‌های روی دیسک و Working Tree شما کاملاً دست‌نخورده و بدون تغییر می‌مانند.
مفهوم کلیدی: اشاره‌گر origin/main شاخه زنده سرور نیست؛ بلکه آخرین عکس ثبت‌شده از سرور در دیتابیس محلی شماست.

ثبت وضعیت شناخته‌شده پیش از خبر

برای دیدن تفاوت «قبل از fetch» و «بعد از fetch»، دریافت خودکار Git در VS Code و ابزارهای دیگر را برای مدت آزمایش غیرفعال نگه دار؛ دکمه Sync را هم نزن. اگر Git: Autofetch فعال است، وضعیتش را یادداشت و موقتاً غیرفعال کن. پس از فصل می‌توانی تنظیم قبلی را برگردانی.
ثبت وضعیت اولیه و هم‌نقطه بودن شاخه‌ها

پیش از ساخت تغییر دوردست، هم‌نقطه بودن دو Reference و محتوای فایل را بررسی کنید:

1
استعلام کد هش محلی و ریموت، status و خواندن فایل:
bashثبت وضعیت همگام اولیه
git rev-parse --short main
git rev-parse --short origin/main
git status -sb
sed -n '1,20p' journey-notes.txt
تطابق اولیه: هر دو کد هش محلی و ریموت یکسان هستند و فایل فقط محتوای قبلی را در بر دارد.

ساخت یک Commit در سمت GitHub

ایجاد کامیت جدید در سمت GitHub (شبیه‌سازی هم‌تیمی)

در صفحه GitHub فایل journey-notes.txt را باز و ویرایش کنید. این خط را به انتهای آن بیفزایید:

1
افزودن این خط به انتهای فایل در وب‌سایت GitHub:
textخط جدید در GitHub
Remote checkpoint: Reviewed
2
ثبت مستقیم تغییر روی شاخه main با پیام Review checkpoint from GitHub.
شبیه‌سازی تغییر آنلاین: این کامیت جدید روی سرور ثبت شده است، اما رایانه شما هنوز از وجود آن کاملاً بی‌اطلاع است.

Local هنوز از خبر بی‌اطلاع است

بررسی بی‌اطلاعی گیت محلی پیش از اجرای Fetch

بدون اجرای Fetch، همان فرمان‌های محلی را تکرار کنید تا بی‌اطلاعی Git ثابت شود:

1
تکرار استعلام status، هش ریموت محلی و محتوای فایل:
bashبررسی قبل از Fetch
git status -sb
git rev-parse --short origin/main
sed -n '1,20p' journey-notes.txt
عدم پایش خودکار: Git محلی تا زمانی که دستور صریح صادر نشود، سرور را پایش نمی‌کند و وضعیت را همچنان همگام نشان می‌دهد.

دریافت خبر بدون ادغام

دریافت تغییرات آنلاین با دستور git fetch origin

اکنون داده تازه را با دستور Fetch از مقصد دریافت کنید:

1
اجرای دستور fetch و استعلام خلاصه وضعیت status:
bashدریافت تغییرات با Fetch
git fetch origin
git status -sb
نتیجه اجرای Fetch: وضعیت اکنون عبارت [behind 1] را نشان می‌دهد؛ یعنی داده دریافت شده اما Working Tree هنوز دست‌نخورده مانده است.

سه Reference، سه نقش متفاوت

مقایسه سه Reference کلیدی HEAD و main و origin/main

موقعیت شاخه جاری (main)، اشاره‌گر ریموت محلی (origin/main) و آخرین Commit را کنار هم ببینید:

1
مشاهده آمار شاخه‌های پیگیری‌کننده و استخراج کد هش‌ها:
bashمقایسه سه Reference
git branch -vv
git rev-parse --short HEAD
git rev-parse --short main
git rev-parse --short origin/main
HEAD & main — به کامیت محلی قبلی اشاره می‌کنند و جابه‌جا نشده‌اند.
origin/main — به کامیت جدید دریافت‌شده از GitHub اشاره دارد.
تفکیک مسئولیت: main و HEAD روی جای قبلی ایستاده‌اند، در حالی که origin/main کد هش جدید سرور را دارد.

خواندن Commitی که فقط Remote دارد

بازبینی کامیت و تغییرات موجود در origin/main

پیش از هرگونه ادغام، Commit و تغییر فایل را در لاگ و diff بازبینی کنید:

1
مشاهده لاگ اختصاصی، diff تغییرات فایل و جزئیات کامیت ریموت:
bashبازبینی کامیت دریافت شده
git log --oneline main..origin/main
git diff main..origin/main -- journey-notes.txt
git show --stat origin/main
پیش‌نمایش ایمن: شما بدون تغییر دادن یک خط از فایل‌های محلی خود، عینا دیدید چه تغییراتی در سرور ایجاد شده است.

دیدن Branchهای شناخته‌شدهٔ Remote

فهرست کردن شاخه‌های ردیابی ریموت با git branch -r

ارجاعات ردیابی ریموت (Remote-tracking References) را در سیستم محلی فهرست کنید:

1
مشاهده لیست شاخه‌های ریموت شناخته‌شده در سیستم محلی:
bashلیست شاخه‌های ریموت محلی
git branch -r
git for-each-ref --format="%(refname:short) %(objectname:short)" refs/remotes/
مفهوم شاخه ریموت محلی: مرجع‌های origin/* فقط‌خواندنی هستند و کار روزمره و کامیت‌های شما روی شاخه محلی main انجام می‌شود.

Fetch برای چند مقصد و پاک‌سازی نقشه

مدیریت چند ریموت و پاک‌سازی مرجع‌ها با --prune

فرمان git fetch --all همه Remoteهای تعریف‌شده را دریافت می‌کند. همچنین git fetch --prune origin مرجع‌های ردیابی که شاخه‌شان در سرور حذف شده را پاک می‌نماید.

پاک‌سازی با prune: پرچم --prune باعث می‌شود شاخه‌های ریموت محلی که از روی سرور پاک شده‌اند، از نقشه محلی شما نیز حذف شوند.

چرا فایل پس از Fetch تغییر نکرد؟

تحلیل علت عدم تغییر فایل‌ها پس از Fetch

Fetch موفق بوده و origin/main جلو رفته است، اما فایل journey-notes.txt روی دیسک هنوز خط تازه را ندارد. آیا داده دانلود نشده است؟ توضیح دهید چه چیزی تغییر کرده و چه چیزی نه.

پاسخ تحلیلی: دانلود داده‌ها (Fetch) مجزا از پیوند دادن یا به‌روزرسانی Working Tree است. برای ورود کامل تغییرات، بعداً باید ادغام صورت گیرد.

خبر در دسترس است، تصمیم هنوز نه

جمع‌بندی و چک‌لیست مهارت‌ها

کامیت دوردست اکنون در دیتابیس Local و پشت نام origin/main قابل مشاهده است؛ با لاگ و Diff آن را خواندید، اما main و فایل‌های جاری عمداً دست‌نخورده ماندند:

تفاوت حیاتی Fetch (دریافت داده) با ادغام یا تغییر Working Tree را آموختید.
مفهوم شاخه ردیابی محلی origin/main را عینا مشاهده کردید.
با git log main..origin/main و git diff تغییرات آنلاین را پیش از اعمال بررسی کردید.
آماده شدید تا در درس بعدی این تغییرات را به شاخه محلی خود وارد کنید.
گام بعدی: در درس آینده، نحوه ادغام (Merge) یا به‌روزرسانی مستقیم (Pull) تغییرات ریموت در شاخه محلی را فرامی‌گیریم!

درس 4: وقتی هر دو سوی دفتر جلو رفته‌اند

خبر دیده شده؛ حالا نوبت تصمیم است

مدیریت هم‌گام‌سازی و تاریخچه‌های واگرا با git pull

کاتب Commit تازه GitHub را با Fetch دیده و Diff آن را خوانده است. چون main محلی تغییر مستقلی ندارد، می‌توان خبر را بدون ساخت Merge Commit پذیرفت؛ اما مرحله بعد آسان‌تر نیست، چون هر دو سمت به صورت مستقل جلو خواهند رفت.

در وادی حیرت، مرز «این سو» و «آن سو» روشن نمی‌ماند. در Git نیز واگرایی (Divergence) یعنی Local و Remote هر کدام Commitی دارند که دیگری ندارد؛ راه حل از روی شکل تاریخچه و قرارداد تیم انتخاب می‌شود.

در میانی یا برونی از میان
بر کناری یا نهانی یا عیان
فانیی یا باقیی یا هر دوی
یا نهٔ هر دو توی یا نه توی

نقشه آموزشی این درس: تمرین دو حالت کلیدی: پذیرش Fast-forward امن تغییرات، و سپس حل واگرایی با git pull --rebase روی کامیت محلی.

Pull یعنی Fetch و سپس یکپارچه‌سازی

معادله دقیق git pull

فرمان git pull ابتدا Fetch می‌کند و سپس شاخه دریافت‌شده را با شاخه جاری یکپارچه می‌سازد. روش یکپارچه‌سازی می‌تواند Merge، Rebase یا فقط Fast-forward باشد.

پس فرمول ساده «Pull = Fetch + Merge» همیشه دقیق نیست! پیش از اجرای Pull باید بدانید روی کدام شاخه هستید، Upstream چیست و در صورت واگرایی کدام سیاست را می‌خواهید.

گام اول: Fetch — ابتدا آخرین داده‌ها و شیءهای جدید را از سرور دانلود کرده و origin/main را به روز می‌کند.
گام دوم: Integration — سپس براساس سیاست انتخابی (Fast-forward، Merge یا Rebase)، تغییرات را وارد شاخه محلی می‌نماید.
نتیجه کلیدی: فرمول ساده «Pull = Fetch + Merge» همیشه دقیق نیست! شما می‌توانید سیاست یکپارچه‌سازی را مشخص کنید.

پذیرفتن خبر فقط با Fast-forward

پذیرش تغییرات فقط با Fast-forward

در پایان درس قبل main محلی فقط عقب بود. Pull را طوری اجرا کنید که اگر این فرض غلط بود، Merge Commit ناخواسته ساخته نشود:

1
تأیید شاخه فعلی و اجرای pull با پرچم ff-only:
bashپذیرش تغییرات فقط با Fast-forward
git branch --show-current
git pull --ff-only origin main
پیشروی موفق: اشاره‌گر main محلی بدون ساخت کامیت اضافه پیش برده شد.

اثبات ورود خبر به Working Tree

تأیید Fast-forward در Working Tree

محتوا و هم‌نقطه بودن دو سمت را در Working Tree و دیتابیس گیت بررسی کنید:

1
خواندن فایل، استعلام status و استخراج کد هش‌ها:
bashتأیید Fast-forward در Working Tree
sed -n '1,20p' journey-notes.txt
git status -sb
git rev-parse --short main
git rev-parse --short origin/main
نتیجه Fast-forward: خط Remote checkpoint: Reviewed اکنون در فایل قرار دارد و هر دو اشاره‌گر هم‌نقطه‌اند.

سه پاسخ برای تاریخچهٔ واگرا

سه سیاست مختلف هنگام مواجهه با واگرایی

اگر هر دو سمت جلو رفته باشند، پرچم --ff-only متوقف می‌شود. پرچم --no-rebase با Merge تاریخچه‌ها را پیوند می‌دهد و پرچم --rebase کامیت‌های محلی را بازپخش می‌نماید.

git pull --no-rebase

یک کامیت ادغام (Merge Commit) می‌سازد و تاریخچه دو شاخه را پیوند می‌دهد.

git pull --rebase

کامیت‌های محلی شما را موقتاً برداشته، ریموت را اعمال کرده و کامیت‌های شما را روی آن بازپخش می‌کند (تاریخچه خطی).

توصیه تیم‌های مدرن: استفاده از git pull --rebase باعث جلوگیری از کامیت‌های تکراری ادغام و حفظ تاریخچه تمیز و خطی می‌شود.

ساخت خبر دوم در Remote

ایجاد کامیت دوم در سمت GitHub

در GitHub و روی شاخه main فایل تازه remote-observation.txt را بسازید:

1
فایل remote-observation.txt را با محتوای زیر در GitHub ایجاد کنید:
textمحتوای remote-observation.txt
REMOTE OBSERVATION
The distant nest is ready.
2
آن را با پیام Add an observation from the remote nest مستقیم روی سرور کامیت کنید.
تغییر سرور: سرور اکنون یک کامیت جدید دارد که هنوز روی سیستم محلی دانلود نشده است.

ساخت Commit مستقل در Local

ایجاد کامیت مستقل محلی و ساخت واگرایی واقعی

در نسخه محلی این خط را به انتهای journey-notes.txt اضافه کنید:

1
افزودن این خط به انتهای فایل محلی:
textخط جدید در فایل محلی
Local note: Ready for the distant nest
2
ثبت کامیت محلی و ساخت تگ موقت جهت مقایسه کد هش:
bashکامیت محلی و ساخت تگ موقت
git commit -am "Add a local note before syncing"
git tag before-pull-rebase HEAD
ایجاد واگرایی: اکنون هم سرور و هم سیستم محلی هر کدام ۱ کامیت مستقل و جداگانه دارند.

دیدن واگرایی پیش از Pull

مشاهده وضعیت ahead 1, behind 1 در لاگ

نقشه Remote را با Fetch تازه کنید و میزان واگرایی را اندازه بگیرید:

1
اجرای fetch، استعلام status، گراف لاگ و شمارش چپ‌وراست کامیت‌ها:
bashمشاهده واگرایی تاریخچه‌ها
git fetch origin
git status -sb
git log --graph --oneline --decorate --all -n 8
git rev-list --left-right --count main...origin/main
تأیید واگرایی: خروجی ## main...origin/main [ahead 1, behind 1] نشان می‌دهد هر دو سمت ۱ کامیت جلوتر از نقطه‌انشعاب مشترک هستند.

Pull با بازپخش Commit محلی

اجرای git pull --rebase origin main

چون کامیت محلی خصوصی است و دو تغییر روی فایل‌های متفاوت‌اند، Pull مبتنی بر Rebase را اجرا کنید:

1
اجرای دستور pull --rebase:
bashاجرای pull --rebase
git pull --rebase origin main
یکپارچه‌سازی خطی: کامیت آنلاین دانلود شد و کامیت محلی شما بالای آن بازپخش گردید.

همان تغییر با Hash تازه

بررسی تغییر کد هش کامیت بازپخش‌شده

نتیجه Rebase را با Tag موقت مقایسه کنید و سپس نشانه آزمایش را حذف نمایید:

1
مقایسه لاگ چپ‌وراست، diff فایل، status و حذف تگ موقت:
bashبررسی تغییر Hash بعد از Rebase
git log --left-right --oneline before-pull-rebase...HEAD
git diff before-pull-rebase HEAD -- journey-notes.txt
git status -sb
git tag -d before-pull-rebase
نتیجه Rebase: کد هش کامیت محلی به دلیل تغییر والد عوض شد، اما محتوا عینا حفظ گردید و وضعیت روی [ahead 1] قرار گرفت.

فرستادن نتیجهٔ یکپارچه

ارسال نهایی کامیت‌های یکپارچه‌شده با git push

نتیجه خطی یکپارچه‌شده را به GitHub بفرستید و هم‌نقطه بودن دو سوی مسیر را کنترل کنید:

1
اجرای push نهایی و استعلام status و لاگ اختلافی:
bashPush نهایی کامیت‌های یکپارچه‌شده
git push
git status -sb
git log --oneline origin/main..main
همگامی کامل: تغییرات با موفقیت به سرور ارسال شدند و هر دو سمت عینا روی یک کامیت قرار گرفتند.

اگر Pull وسط Conflict متوقف شد

تحلیل مدیریت Conflict هنگام اجرای git pull

در یک پروژه دیگر، git pull --rebase روی Conflict متوقف شده است. پس از حل فایل چه فرمان‌هایی لازم‌اند و اگر بخواهید کل عملیات را لغو کنید چه می‌کنید؟

تفکیک دستورها: برای pull بستر rebase از git rebase --continue یا --abort و برای pull بستر merge از git merge --abort استفاده کنید.

دو تاریخچه دوباره هم‌مسیر شدند

جمع‌بندی و چک‌لیست مهارت‌ها

یک بار main را فقط Fast-forward کردید و بار دوم واگرایی واقعی را با بازپخش کامیت خصوصی حل نمودید. سپس نتیجه را Push کردید تا هر دو سمت دوباره هم‌نقطه شوند:

استفاده از git pull --ff-only برای پذیرش امن تغییرات بدون ساخت کامیت اضافی.
درک مفهوم واگرایی تاریخچه‌ها (ahead 1, behind 1).
حل واگرایی محلی با git pull --rebase برای ایجاد تاریخچه خطی و تمیز.
حذف تگ موقت و ارسال نهایی تغییرات یکپارچه‌شده به GitHub.
گام بعدی: در درس بعدی، مهار تکنیک‌های کلون‌سازی و ایجاد مخزن همکار را فرامی‌گیریم!

درس 5: یک دستگاه دیگر، همان سفر

دفتر تازه‌ای که گذشته را هم می‌شناسد

پرنده‌ای تازه می‌خواهد از دستگاه دیگری به سفر بپیوندد. دانلود چند فایل آخر کافی نیست؛ او باید Commitها، Branchهای قابل‌مشاهده، Tag نسخه و نشانی Repository اصلی را هم داشته باشد.

در پایان حیرت، سالک هم دل پرعشق دارد و هم خود را تهی می‌بیند. Clone نیز نسخه‌ای مستقل می‌سازد که در آغاز با مقصد هم‌نقطه است، اما پس از آن می‌تواند راه محلی خودش را طی کند.

عاشقم اما ندانم بر کیم
نه مسلمانم نه کافر، پس چیم
لیکن از عشقم ندارم آگهی
هم دلی پرعشق دارم هم تهی

این درس Clone را مانند یک Working Copy واقعی به کار می‌گیرد: در نسخه دوم Commit می‌سازی و نسخه اول آن را از Remote دریافت می‌کند.

Clone چه چیزی می‌سازد؟

فرمان git clone URL DIRECTORY یک پوشه تازه، Working Tree شاخه آغازین و Repository محلی با دیتابیس .git می‌سازد. Remote مبدأ معمولاً خودکار با نام origin ثبت می‌شود.

Clone عادی تمام تاریخچه لازم و Remote-tracking References را دریافت می‌کند، اما از هر شاخه دوردست یک شاخه محلی قابل-Commit نمی‌سازد؛ فقط شاخه آغازین Checkout می‌شود.

کلون کردن، شبیه‌سازی کامل تاریخچه و دیتابیس پروژه است، نه فقط دانلود چند فایل ساده!

انتخاب آزمایشگاه بیرون از پروژه

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

bashساخت پوشه آزمایش خارج از ریپازیتوری فعلی
mkdir simorgh-clone-lab
cd simorgh-clone-lab
pwd
ls -la
اگر این نام از قبل وجود دارد، نام تازه‌ای انتخاب کن؛ پوشه موجود را حذف نکن. خروجی باید نشان دهد در محیطی جدا از Repository اصلی هستی.

ساخت Working Copy دوم

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

نشانی URL واقعی پروژه را جایگزین و Clone را در پوشه مشخص بساز:

bashکلون کردن پروژه در fresh-copy
git clone https://github.com/YOUR_GITHUB_USERNAME/simorgh-journey.git fresh-copy
cd fresh-copy
از این قدم تا زمان بازگشت صریح به ترمینال اول، فرمان‌ها داخل پوشه fresh-copy اجرا می‌شوند.

ممیزی اجزای Clone

وجود Working Tree، دیتابیس و تنظیم Remote را در نسخه کلون‌شده بررسی کن:

bashممیزی اجزای Clone
git status -sb
git branch -vv
git branch -r
git remote -v
git tag --list
test -d .git
شاخه اصلی — باید main تمیز و در حال پیگیری origin/main باشد.
تگ‌ها — تگ v0.7.0 باید به طور کامل منتقل شده باشد.
دیتابیس .git — فولدر .git باید به صورت یک دایرکتوری معتبر وجود داشته باشد.
با اجرای این فرمان‌ها مطمئن می‌شوی که Clone کامل و بدون نقص انجام شده است.
Clone تنظیمات هویت local مخزن اول را کپی نمی‌کند. اگر هویت را فقط برای پروژه اول تنظیم کردی یا روی دستگاه دیگری هستی، پیش از commit درس، داخل fresh-copy هویت خودت را با دو فرمان local درس اول، درس چهارم، قدم سوم تنظیم و با git config --get user.name و git config --get user.email بازخوانی کن. URL ریموت به معنی داشتن هویت commit یا مجوز ورود نیست.

تطبیق تاریخچه با نسخهٔ دور

نوک Local و Remote-tracking Reference و فایل‌های اصلی را مقایسه کن:

bashتطبیق تاریخچه کلون
git rev-parse --short HEAD
git rev-parse --short origin/main
git log --oneline --decorate -n 8
ls
مقادیر Hash در HEAD محلی و origin/main باید دقیقاً یکسان و برابر با نسخه سرور باشند.

چرا ZIP جای Clone را نمی‌گیرد؟

دانلود ZIP فقط یک عکس لحظه‌ای (Snapshot) از فایل‌ها می‌دهد، اما Repository محلی، تاریخچه کامیت‌ها، شاخه‌ها و Remote آماده ندارد.

دانلود ZIP

فقط فایل‌های پروژه بدون هیچ تاریخچه کامیت یا پوشه .git یا آدرس remote.

git clone

تاریخچه کامل کامیت‌ها، تمام تگ‌ها، تنظیمات origin و دیتابیس .git کامل.

گزینه --depth 1 یک Shallow Clone با تاریخچه محدود می‌سازد، اما برای پروژه‌های عادی Clone معمولی بهترین گزینه است.

ثبت حضور دستگاه دوم

داخل پوشه fresh-copy فایل clone-note.txt را بساز:

textمحتوای clone-note.txt
CLONE NOTE
Second working copy: Ready

تغییر را بازبینی و کامیت کن:

bashکامیت کردن یادداشت کلون
git add clone-note.txt
git diff --staged -- clone-note.txt
git commit -m "Confirm the second working copy"
اکنون دستگاه دوم اولین Commit محلی خود را روی شاخه main ایجاد کرده است.

فرستادن Commit از Clone دوم

چون Clone رابطه Upstream را ساخته است، کامیت را با فرمان کوتاه بفرست:

bashPush از نسخه دوم
git status -sb
git push
git status -sb
پیش از Push باید ahead 1 و پس از آن وضعیت همگام دیده شود. این Push، ریپازیتوری دستگاه اول را خودکار تغییر نمی‌دهد.

دیدن خبر دستگاه دوم در نسخهٔ اول

به پنجره ترمینال Repository اصلی (simorgh-journey) برگرد. بدون Pull، فقط نقشه Remote را تازه و کامیت دستگاه دوم را بررسی کن:

bashمشاهده تغییر نسخه دوم در نسخه اول
git fetch origin
git status -sb
git log --oneline main..origin/main
git show --stat origin/main
نسخه اول باید یک کامیت عقب باشد ([behind 1]) و فایل clone-note.txt هنوز در Working Tree آن ظاهر نشده باشد.

بستن حلقهٔ همکاری میان دو نسخه

چون نسخه اول کامیت محلی تازه‌ای ندارد، خبر را فقط با Fast-forward بپذیر:

bashهمگام‌سازی نسخه اول با Fast-forward
git pull --ff-only
git status -sb
sed -n '1,20p' clone-note.txt
git log --oneline --decorate -n 5
اکنون هر دو نسخه محلی و Remote روی یک کامیت یکسان قرار گرفته‌اند!
ادامه فصل هشتم فقط در مخزن اصلی simorgh-journey است. fresh-copy را نگه دار، اما روی آن تغییر تازه نساز. فایل‌های ignored مثل .env.local و cache.tmp از clone منتقل نشده‌اند؛ برای ادامه درس‌ها نیز لازم نیست آن‌ها را دوباره بسازی.

Clone کدام Branchها را محلی می‌کند؟

Remote پنج Branch دارد. پس از Clone، چرا معمولاً فقط Branch آغازین در git branch دیده می‌شود، اما نام‌های بیشتری در git branch -r هستند؟

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

نشان نگهبان دو سوی سفر

از یک Repository محلی به یک چرخه واقعی همکاری رسیدی: مقصد را تعریف کردی، Branch و Tag را فرستادی، خبر Remote را بی‌خطر دیدی، واگرایی را حل کردی و از Clone دوم Commitی به نسخه اول رساندی.

پروژه اصلی روی main تمیز و با origin/main هم‌نقطه است. کاروان اکنون آماده همکاری گسترده‌تر با Fork و Pull Request است.

بعد ازین وادی فقرست و فنا
کی بود اینجا سخن گفتن روا
عین وادی فراموشی بود
لنگی و کری و بیهوشی بود

درک کامل تفاوت کلون با فایل ZIP و ممیزی اجزای git clone.
ایجاد تغییر و Push از نسخه دوم (fresh-copy).
دریافت تغییرات نسخه دوم در نسخه اصلی با git fetch و git pull --ff-only.
اتمام موفقیت‌آمیز فصل هفتم و آمادگی برای فصل هشتم (همکاری و Pull Request).
تبریک! فصل هفتم با موفقیت به پایان رسید. اکنون با مفاهیم کامل Remote، Clone، Fetch، Push و Pull آشنا شدی.

فصل 8: وادی فقر و فنا

درس 1: یک دفتر، دو راه مشارکت

در آستانه آخرین وادی

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

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

بعد ازین وادی فقرست و فنا
کی بود اینجا سخن گفتن روا
عین وادی فراموشی بود
لنگی و کری و بیهوشی بود

خروجی این فصل: یک درخواست ادغام بررسی‌شده، نسخه برچسب‌خورده با SemVer و نشانی سایت آنلاین دفتر سفر روی GitHub Pages است. پروژه simorgh-journey را ادامه می‌دهیم.

پیش از باز کردن دفتر

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

bashبررسی مسیر و ریموت فعلی
git rev-parse --show-toplevel
git status --short
git branch --show-current
git remote -v
مسیر باید پروژه تو و origin مخزن حساب خودت در GitHub باشد. خروجی وضعیت باید خالی باشد؛ فایل ناتمام را آگاهانه commit یا stash کن.

شروع از آخرین main

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

bashهمگام‌سازی شاخه main با origin/main
git switch main
git fetch origin
git merge --ff-only origin/main
git status -sb
اگر --ff-only شکست خورد، تاریخچه‌ها واگرا هستند؛ از reset یا force push استفاده نکن. اگر main محلی جلوتر است، با git push origin main آن را منتشر کن.

کدام نسخه، کجا؟

Fork یک مخزن مرتبط روی اکانت سرور می‌سازد، Clone مخزن را همراه دیتابیس و تنظیمات به سیستم محلی می‌آورد، و Branch نامی جابه‌جاشونده برای یک کامیت در همان مخزن است.

برای مخزن خودت، شاخه و Pull Request در همان مخزن کافی است. برای مخزنی که اجازه Push مستقیم نداری، فورک می‌سازی و از شاخه فورک به مخزن مبدأ PR می‌فرستی.

Fork

کپی آنلاین مخزن دیگری در حساب GitHub تو برای مشارکت مستقل.

Clone

دانلود مخزن و دیتابیس .git به رایانه محلی برای کار روزمره.

در مسیر اصلی این فصل، فورک یا حساب دوم لازم نیست و همه‌چیز در مخزن خودت تمرین می‌شود.

نقشه ریموت خودت را بخوان

تنظیمات Remote و شاخه محلی خودت را چک کن:

bashبررسی نام ریموت و upstream
git remote -v
git branch -vv
origin نام قراردادی ریموت توست. عبارت upstream دو کاربرد دارد: نام ریموت مبدأ فورک، و شاخه دنبال‌شونده مثل origin/main.

اگر مهمان پروژه دیگری بودی

برای این مسیر اختیاری، ابتدا مخزن عمومی شخص دیگری را در GitHub فورک کن؛ نام آزاد مثل simorgh-contribution انتخاب و فورک خودت را در یک پوشه جدا clone کن. origin در آن clone باید فورک خودت باشد. سپس URL مبدأ واقعی را جای ORIGINAL_OWNER بگذار. بدون این آماده‌سازی، الگوی زیر را اجرا نکن. پس از مطالعه به مخزن اصلی دوره برگرد.

این مسیر اختیاری برای مشارکت در پروژه‌های متن‌باز دیگران است (در مخزن اصلی اجرا نکن):

bashالگوی همگام‌سازی فورک با upstream
git remote add upstream https://github.com/ORIGINAL_OWNER/simorgh-journey.git
git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin main
مشارکت بدون دسترسی مستقیم Push از طریق Fork و Upstream ممکن است.

قانون کوچک مشارکت

در ریشه پروژه فایل CONTRIBUTING.md را بساز:

textمحتوای CONTRIBUTING.md
# Contributing

- Use a focused feature branch.
- Describe the change and how you checked it.
- Keep unrelated files out of the pull request.
- Never commit passwords or access tokens.
- Review the diff before merging.
این قرارداد ساده، بازبینی را از سلیقه شخصی به معیار قابل‌بررسی تبدیل می‌کند.

اول ببین، بعد ثبت کن

تغییر فایل را بازبینی و کامیت کن:

bashبازبینی staged و کامیت قوانین مشارکت
git add CONTRIBUTING.md
git diff --cached
git commit -m "docs: define contribution rules"
فقط قرارداد مشارکت باید در این Commit قرار داشته باشد.

قرارداد به لانه دور می‌رسد

کامیت را به GitHub ارسال کن و وضعیت را بررسی کن:

bashPush تغییر به origin main
git push origin main
git status -sb
صفحه مخزن خودت را در GitHub باز کن و فایل CONTRIBUTING.md را ببین.

برای هر در، کلید خودش

مالک یک مخزن هستی؛ در مخزن دوم فقط اجازه خواندن داری. برای هرکدام مسیر Branch تا PR را توضیح بده. آیا Clone به تنهایی دسترسی Push ایجاد می‌کند؟

همواره یادداشت کن که دسترسی دستکاری مخزن اصلی با مجوزهای دسترسی حساب GitHub کنترل می‌شود، نه با کلون کردن ساده.

درس 2: گزارش سی‌امین مسافر

گزارشی که باید به جمع برسد

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

عطار رسیدن اندک مسافران را پس از راهی بس دشوار روایت می‌کند. تمرین ما ثبت یک گزارش کوتاه است.

عاقبت از صد هزاران تا یکی
بیش نرسیدند آنجا اندکی

فایل caravan-registry.txt را می‌سازیم و پیش از ادغام، برایش یک Pull Request ایجاد می‌کنیم.

پیشنهاد، نه ادغام خودکار

Pull Request یا PR صفحه گفت‌وگو، بازبینی و بررسی تفاوت دو شاخه است. base شاخه مقصد و compare شاخه پیشنهاددهنده تغییرات است.

باز کردن PR هنوز شاخه مقصد را تغییر نمی‌دهد و push هم معادل ادغام نیست.

base: main

شاخه اصلی و مقصد نهایی که تغییرات قرار است به آن اضافه شود.

compare: feature/arrival-registry

شاخه ویژگی که شامل کامیت‌های پیشنهادی شماست.

در بخش Files changed در GitHub باید دقیقاً فایل مربوط به این پیشنهاد را ببینی.

شاخه گزارش را بساز

با وضعیت محلی تمیز، یک شاخه ویژگی جدید بساز:

bashساخت و انتقال به شاخه feature/arrival-registry
git status --short
git switch -c feature/arrival-registry
شاخه فعال اکنون feature/arrival-registry است.

یک گزارش سه‌خطی

در ریشه پروژه فایل تازه caravan-registry.txt را با این محتوا بساز (عمداً نقش آخرین مسافر را نمی‌نویسیم تا بعداً بازبینی انجام شود):

textمحتوای اولیه caravan-registry.txt
CARAVAN REGISTRY
Arrivals: 30
Last traveler: Mount Qaf Pilgrim
هنوز index.html را تغییر نده؛ این درخواست فقط مربوط به ثبت گزارش متنی کاروان است.

بسته تغییر را وارسی کن

تغییرات را stage کرده و قبل از کامیت diff آن را دقیقاً چک کن:

bashبررسی تغییرات آماده کامیت
git status --short
git add caravan-registry.txt
git diff --cached -- caravan-registry.txt
باید فقط همین فایل تازه با سه خط دیده شود.

ثبت و ارسال شاخه

تغییر را کامیت کن و شاخه جدید را به origin بفرست:

bashکامیت و Push شاخه ویژگی با -u
git commit -m "docs: record caravan arrival"
git push -u origin feature/arrival-registry
گزینه -u شاخه دنبال‌شونده را تنظیم می‌کند؛ شاخه اصلی main هنوز تغییر نکرده است.

درخواست با مقصد مشخص

در مخزن خودت بخش Pull requests و سپس New pull request را باز کن. base را main و compare را feature/arrival-registry قرار بده. Files changed باید فقط گزارش سه‌خطی جدید را نشان بدهد.

هنوز Create pull request را نزن؛ در قدم بعد عنوان و شرح را وارد و درخواست را ایجاد می‌کنی.

شرحی که بازبین بتواند آزمایش کند

عنوان و شرح مناسبی برای PR خود بنویس:

textنمونه عنوان و شرح Pull Request
Title: Add initial caravan arrival registry

Description:
Adds caravan-registry.txt to record the arrival of 30 pilgrims.
Verified locally by reading the file diff.
یک شرح خوب به بازبین می‌گوید چه چیزی تغییر کرده و چگونه تست شده است.
عنوان و شرح را وارد کن، Create pull request را بزن و شماره یا URL درخواست را نگه دار. درخواست باز بماند؛ پیش از درس بازبینی آن را ادغام نکن.

فرق شاخه‌ها را محلی ببین

تفاوت دو شاخه را روی سیستم خودت هم بررسی کن:

bashمقایسه تفاضلی main و شاخه ویژگی
git diff main...feature/arrival-registry -- caravan-registry.txt
این همان متنی است که در تب Files changed روی GitHub به بازبین نشان داده می‌شود.
سه نقطه تغییرهای شاخه پیشنهاد را از مبنای مشترک نشان می‌دهد. مقایسه دو نوک با دو نقطه، وقتی main مستقل جلو رفته باشد، الزاماً همان Files changed درخواست نیست.

درخواست باز است؛ چرا سایت عوض نشد؟

یک Pull Request باز کرده‌ای و تغییرات روی GitHub مشخص است؛ اما چرا هنوز شاخه اصلی main و سایت این تغییر را ندارند؟

همواره به یاد داشته باش که تا زمان کلیک روی Merge Pull Request یا اجرای Merge، شاخه اصلی دست‌نخورده می‌ماند.

درس 3: بازبینی با یک اصلاح واقعی

دوباره به نوشته نگاه کن

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

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

چون نگه کردند آن سی مرغ زود
بی‌شک این سی مرغ آن سیمرغ بود

درخواست باز درس قبل را اصلاح و سپس با روش Merge Commit ادغام می‌کنیم.

چه کسی چه اختیاری دارد؟

Code Review بررسی تغییر است؛ Comment نظر، Approve تأیید بازبین و Request changes درخواست اصلاح است.

تأیید به تنهایی نه ادغام می‌کند و نه تضمین می‌کند قواعد مخزن رعایت شده‌اند.

Comment & Review

ثبت نظر یا درخواست تغییر روی خطوط کد پیشنهادی.

Approve & Merge

تأیید نهایی و ادغام شاخه ویژگی در شاخه اصلی main.

خودبازبینی تمرین مفیدی است، اما در پروژه‌های تیمی جای بازبین مستقل را نمی‌گیرد.
اگر تنها هستی، مسیر تمرین خودبازبینی با Comment است؛ نمی‌توانی PR خودت را Approve کنی. اگر هم‌تیمی داری، او می‌تواند مستقل بازبینی کند. اگر قواعد مخزن تأیید فرد دیگری را لازم دارد، همان فرد مجاز باید اقدام کند؛ برای عبور از تمرین قواعد دسترسی را دور نزن.

نظر دقیق و محترمانه

در بخش Files changed درخواست، کنار خط Last traveler یک کامنت ثبت کن:

textمتن کامنت بازبینی
In caravan-registry.txt, please add "Role: Route recorder" after the last traveler. This makes the traveler responsibility explicit. Check that the other three lines stay unchanged.
نظر بازبینی باید محل دقیق، تغییر لازم و روش بررسی را روشن کند.
اگر نظر به‌صورت pending review باقی ماند، Submit review را با گزینه Comment بزن تا ارسال شود. در صورت نبود نظر خطی، همین متن را همراه نام فایل در گفت‌وگوی PR ثبت کن.

همان شاخه، اصلاح کوچک

روی سیستم محلی به همان شاخه ویژگی برو:

bashبازگشت به شاخه feature/arrival-registry
git switch feature/arrival-registry
git status --short

با وضعیت تمیز، خط زیر را به انتهای caravan-registry.txt اضافه کن (سه خط قبلی حفظ شوند):

textخط اضافه شده
Role: Route recorder
bashبررسی diff محلی
git diff -- caravan-registry.txt

پاسخ به بازبینی را ارسال کن

اصلاح جدید را کامیت و push کن تا همان PR به‌روز شود:

bashکامیت و push اصلاح جدید روی همان شاخه
git add caravan-registry.txt
git diff --cached
git commit -m "docs: clarify traveler role"
git push
برای این اصلاح نیازی به ساخت PR تازه نیست؛ همان PR روی GitHub خودکار کامیت جدید را نشان می‌دهد.

قبل از ادغام، روش را انتخاب کن

Merge commit دو کامیت شاخه ویژگی را نگه می‌دارد و یک کامیت ادغام با دو والد می‌سازد.

Squash and merge تغییرها را در یک کامیت جدید روی مقصد جمع می‌کند. Rebase and merge کامیت‌ها را خطی بازاعمال می‌کند.

Create a merge commit

حفظ تمام کامیت‌های شاخه ویژگی + یک کامیت Merge شفاف.

Squash / Rebase

ترکیب کامیت‌ها در یک کامیت واحد یا بازپخش خطی آن‌ها روی main.

برای این تمرین گزینه Create a merge commit را انتخاب می‌کنیم.

بازبینی نهایی درخواست

در صفحه PR روی GitHub، بخش Files changed را بررسی کن تا مطمئن شوی ۴ خط فایل caravan-registry.txt کامل و تمیز دیده می‌شوند.

تعداد کامیت‌ها — باید دو کامیت دیده شود.
محتوای فایل — شامل ۴ خط کامل گزارش کاروان.

ادغام روی سرور

روی دکمه Merge pull request کلیک کن، گزینه Create a merge commit را انتخاب کرده و Confirm merge را بزن.

پیام Pull request successfully merged and closed ظاهر می‌شود. اکنون می‌توانی دکمه Delete branch را بزنی تا شاخه ریموت پاک شود.
پیش از تأیید، روش ادغام را از فهرست کنار دکمه انتخاب کن. اگر Create a merge commit مجاز نیست یا بررسی اجباری ناموفق است، ابتدا مانع را روشن کن؛ نتیجه تاریخچه را نمی‌توان مستقل از روش انتخابی تضمین کرد.

نسخه محلی را برسان

به شاخه main محلی برگرد و تغییرات ادغام‌شده را از سرور بگیر:

bashهمگام‌سازی main محلی با origin/main
git switch main
git fetch origin
git merge --ff-only origin/main
git status -sb
اکنون شاخه main محلی شامل فایل caravan-registry.txt و کامیت Merge است.

اکنون که merge commit روی main دریافت شده، شاخه محلی تمام‌شده را هم جمع کن. حذف شاخه در GitHub، شاخه محلی را حذف نمی‌کند:

bash
git branch --merged
git branch -d feature/arrival-registry
git fetch --prune origin
برای شاخه محلی از -d استفاده کن. اگر هشدار ادغام‌نشدن دیدی، ابتدا روش ادغام و محتوای main را بررسی کن و خودکار سراغ -D نرو.

چرا درخواست تازه نسازیم؟

چرا برای اعمال نظر بازبین، کامیت جدید را روی همان شاخه push کردیم و یک Pull Request تازه نساختیم؟

نگهداری سابقه نظرات و اصلاحات روی یک Pull Request شفافیت تاریخچه توسعه را افزایش می‌دهد.

درس 4: آزمایش یک چرخه انتشار

پیش از سپردن دفتر

کاروان گزارش نهایی را آماده کرده، اما هدهد دو کار را جدا می‌کند: جمع کردن نوشته‌های تازه و آماده کردن نسخه‌ای که دیگران خواهند خواند.

جدا کردن کارها زمانی ارزش دارد که آشفتگی را کمتر کند. صرفاً با نام شاخه‌ها درباره کیفیتشان قضاوت نمی‌کنیم.

این به صورت هر دو یکسان باشدت
در صفت فرق فراوان باشدت

در این درس یک چرخه محلی از الگوی Git Flow را تمرین می‌کنیم.

این نقشه برای هر تیم نیست

Git Flow یک قرارداد مدیریت شاخه‌هاست: شاخه feature از develop جدا می‌شود؛ شاخه release برای آماده‌سازی نسخه جدا شده و در نهایت هم به main و هم به develop ادغام می‌شود.

develop

شاخه اصلی توسعه که ویژگی‌های جدید در آن جمع می‌شوند.

release & main

شاخه‌های آماده‌سازی انتشار و نسخه پایدار نهایی.

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

دو خط کار محلی

این تمرین از main تمیز آغاز می‌شود. اگر شاخه develop یا feature/release-notes از اجرای قبلی وجود دارد، وضعیتش را بررسی کن؛ نام موجود را حذف یا بازنویسی نکن. در اجرای اول هر دو نام تازه‌اند.

ابتدا مطمئن شو روی شاخه main و با وضعیت تمیز هستی، سپس شاخه‌های develop و feature را بساز:

bashساخت شاخه‌های develop و feature/release-notes
git switch main
git status --short
git switch -c develop
git switch -c feature/release-notes
اکنون HEAD به شاخه feature/release-notes اشاره می‌کند.

یک ویژگی کوچک اما واقعی

فایل release-notes.txt را با محتوای اولیه بساز:

textمحتوای پیش‌نویس release-notes.txt
RELEASE NOTES
Target: 1.0.0
Included: Reviewed caravan registry
Status: Draft
bashکامیت کردن پیش‌نویس انتشار
git add release-notes.txt
git diff --cached
git commit -m "docs: draft release notes"
این کامیت تازه روی شاخه feature قرار دارد.

ویژگی به محل جمع شدن کار می‌رسد

شاخه feature را به develop ادغام کن و شاخه موقت را حذف کن:

bashادغام --no-ff در develop و حذف شاخه feature
git switch develop
git merge --no-ff feature/release-notes -m "merge: integrate release notes"
git branch -d feature/release-notes
گزینه --no-ff باعث ثبت شفاف مرز شاخه ویژگی در تاریخچه می‌شود.

آماده‌سازی نسخه جدا می‌شود

شاخه release برای نسخه 1.0.0 بساز و Status را آماده انتشار کن:

bashساخت شاخه release/1.0.0
git switch -c release/1.0.0

در فایل release-notes.txt عبارت Status: Draft را به Status: Ready تغییر بده:

bashثبت آمادگی انتشار روی release/1.0.0
git diff -- release-notes.txt
git add release-notes.txt
git commit -m "docs: mark release notes ready"
تغییر وضعیت به Ready علامت آمادگی نسخه برای انتشار نهایی است.

نسخه آماده به main می‌رود

شاخه release آماده را به main ادغام کن:

bashادغام release در main
git switch main
git merge --no-ff release/1.0.0 -m "merge: release version 1.0.0"
شاخه اصلی main اکنون شامل نسخه آماده است.

اصلاح انتشار گم نشود

تغییرات انجام‌شده در release (مثل تغییر Status به Ready) را به develop هم برگردان (Backport):

bashادغام release در develop و پاک‌سازی شاخه release
git switch develop
git merge --no-ff release/1.0.0 -m "merge: backport release 1.0.0 fixes to develop"
git branch -d release/1.0.0
اکنون هر دو شاخه main و develop دارای اصلاح نهایی release هستند.

پیش از ادامه، نسخه فایل در هر دو شاخه را بخوان و مطمئن شو اصلاح آماده‌سازی به هر دو رسیده است:

bash
git show main:release-notes.txt
git show develop:release-notes.txt
git diff main develop -- release-notes.txt
هر دو نسخه Status: Ready دارند و diff آخر خالی است. develop شاهد محلی آزمایش می‌ماند؛ ادامه درس روی main خواهد بود.

آزمایش را جمع کن

تغییرات آماده‌شده روی main را به origin ارسال کن:

bashPush تغییرات main به origin
git switch main
git push origin main
git status -sb
نسخه آماده با موفقیت به سرور آنلاین رسید.

یک اصلاح جا مانده

چرا اصلاح انجام‌شده روی شاخه release/1.0.0 را علاوه بر main، به شاخه develop هم ادغام (Backport) کردیم؟

بازگرداندن اصلاحات (Backporting) از بروز تعارضات مجدد در چرخه‌های توسعه بعدی جلوگیری می‌کند.

درس 5: نسخه‌ای با نام روشن

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

پرندگان روبه‌روی آینه می‌ایستند. دفترشان دیگر وعده‌ای برای آینده نیست؛ گزارش رسیدن و اصلاح‌هایی است که واقعاً ثبت کرده‌اند. هدهد می‌خواهد این لحظه نامی مشخص داشته باشد تا بتوان دوباره به آن برگشت.

خویش را دیدند سیمرغ تمام
بود خود سیمرغ سی مرغ مدام

پیش از ساخت تگ رسمی v1.0.0، متن وضعیت صفحه و معرفی پروژه را با خروجی واقعی هماهنگ می‌کنیم.

نام نسخه دقیقاً چه می‌گوید؟

Tag نامی ثابت برای یک کامیت مشخص در تاریخچه است. تگ annotated پیام، نویسنده و تاریخ دارد.

در Semantic Versioning (نسخه‌گذاری معنایی)، فرمت MAJOR.MINOR.PATCH نشان‌دهنده میزان و نوع تغییرات است.

Annotated Tag (git tag -a)

دارای پیام، نویسنده و تاریخ ثبت (مناسب نسخه رسمی).

Lightweight Tag

فقط یک اشاره‌گر ساده به یک کامیت بدون متادیتای اضافی.

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

وضعیت صفحه با سفر هماهنگ شود

فقط متن داخل پاراگراف status در index.html را جایگزین کن؛ تگ‌های HTML را نگه دار. در journey-log.txt دو سطر نام‌برده را جداگانه در محل فعلی تغییر بده، نه اینکه کل گزارش را با دو خط جایگزین کنی.

در شاخه main محلی، فایل index.html و journey-log.txt را به‌روزرسانی کن:

در index.html متن status را به خط زیر تغییر بده:

textمتن status جدید
Status: The caravan has reached Mount Qaf.

در journey-log.txt مقادیر زیر را تنظیم کن:

textمقادیر جدید در journey-log.txt
Current valley: Poverty and Annihilation
Next checkpoint: Journey complete

معرفی صادقانه پروژه

در ریشه پروژه فایل README.md را بساز:

textمحتوای README.md
# Simorgh Journey

A small Git learning project inspired by Attar.

## What is included
- A static journey page in index.html.
- A reviewed arrival registry.
- Text records of the journey and release preparation.

## Open locally
Open index.html in a browser. No build step is required.

## Records
- [Arrival registry](caravan-registry.txt)
- [Journey log](journey-log.txt)
- [Release notes](release-notes.txt)

## Learning scope
This is an educational project, not a production application.

پیش از نام‌گذاری آزمایش کن

تغییرات را قبل از کامیت بازبینی کن:

bashبررسی diff قبل از ثبت نسخه نهایی
git diff --check
git diff -- index.html journey-log.txt
git status --short
مطمئن شو همه لینک‌ها و متن‌ها کاملاً درست و تمیز هستند.
index.html را محلی در مرورگر باز کن و متن رسیدن به Mount Qaf را ببین. گزارش caravan-registry.txt باید دقیقاً چهار خط، از جمله Role داشته باشد؛ release-notes.txt باید Ready باشد. هر سه پیوند نسبی README را به فایل موجود تطبیق بده. نبود خروجی diff --check به‌تنهایی آزمون سالم بودن صفحه نیست.

خروجی نهایی را ثبت کن

تغییرات نهایی را کامیت کرده و به origin فرست:

bashکامیت و push نهایی main
git add index.html journey-log.txt README.md
git diff --cached
git commit -m "docs: finalize journey milestone"
git push origin main
git status -sb
شاخه main اکنون کاملاً به‌روز و همگام با سرور آنلاین است.

تگ را روی جای درست بگذار

ابتدا با git tag --list v1.0.0 بررسی کن این نام وجود نداشته باشد. در اجرای تکراری تگ قبلی را حذف یا جابه‌جا نکن؛ اگر همان نسخه است فقط بازبینی کن و اگر انتشار متفاوتی است نام نسخه جدید را در تمام قدم‌های بعد یکسان به کار ببر.

یک تگ رسمی annotated به نام v1.0.0 روی آخرین کامیت بساز:

bashساخت تگ v1.0.0
git tag -a v1.0.0 -m "Release 1.0.0: Caravan reaches Mount Qaf"
git tag -n
تگ v1.0.0 با پیام رسمی ساخته شد.

مقصد تگ را با commit نهایی مقایسه کن:

bash
git rev-parse HEAD
git rev-parse "v1.0.0^{commit}"
دو شناسه در این لحظه باید برابر باشند. پس از افزودن لینک سایت در درس بعد، main جلو می‌رود ولی این تگ ثابت می‌ماند.

ارسال فقط همین تگ

تگ ساخته‌شده را صریحاً به origin بفرست:

bashPush تگ v1.0.0 به سرور
git push origin v1.0.0
تگ رسمی به آنلاین فرستاده شد.

صفحه انتشار با تگ موجود

در GitHub وارد بخش Releases شو و یک Release جدید بر اساس تگ v1.0.0 بساز:

1
وارد تب Releases در GitHub شو.
2
روی دکمه Draft a new release کلیک کن.
3
تگ v1.0.0 موجود را انتخاب کن.
4
عنوان را Simorgh Journey 1.0.0 بگذار.
5
متن release-notes.txt را در توضیحات قرار بده و دکمه Publish release را بزن.
انتشار نسخه ۱.۰.۰ در صفحه GitHub Release ثبت و عمومی شد.

اشتباه بعد از انتشار

اگر بعد از انتشار v1.0.0 متوجه غلط املایی در README شدی، آیا باید تگ v1.0.0 را حذف یا جابه‌جا کنی؟ راه درست چیست؟

تگ‌های عمومی ثابت هستند؛ هرگونه تغییر یا اصلاح باید با ساخت تگ نسخه جدید مانند v1.0.1 منتشر شود.

درس 6: دفتر سفر روی وب

دفتر از دست کاروان بیرون می‌رود

کاروان به پایان راه رسیده و حالا می‌خواهد دیگران هم دفتر سفر را بخوانند. هدهد می‌گوید فرستادن نشانی کافی نیست؛ باید از نگاه خواننده‌ای که فایل‌های محلی ما را ندارد، صفحه را امتحان کنیم.

محو او گشتند آخر بر دوام
سایه در خورشید گم شد والسلام

سایت را از شاخه main روی GitHub Pages منتشر می‌کنیم.

میزبانی با نسخه‌گذاری فرق دارد

GitHub Pages فایل‌های ایستا مانند HTML، CSS و تصاویر را روی وب میزبانی می‌کند. مخزن و Release به خودی خود سایت زنده نیستند.

GitHub Pages

سایت آنلاین زنده وب برای نمایش فایل‌های ایستا به کاربران.

Git Tags & Releases

نقاط ثبت‌شده در تاریخچه برای دانلود سورس‌کد و نسخه‌ها.

این تمرین از شاخه main و پوشه ریشه (root) منتشر می‌شود.

فایل ورودی روی سرور حاضر است؟

مطمئن شو فایل index.html روی main سرور با حروف کوچک وجود دارد:

bashبررسی وجود index.html در main
git switch main
git status -sb
git ls-tree --name-only HEAD
git log -1 --oneline
فایل index.html در ریشه مخزن روی main ثبت شده است.
فرمان ls-tree فقط مخزن محلی را می‌خواند. برای اثبات حضور روی سرور، صفحه GitHub را روی شاخه main باز کن و وجود index.html و شناسه آخرین commit را با خروجی محلی تطبیق بده.

منبع انتشار را تعیین کن

در مخزن GitHub وارد Settings → Pages شو و تنظیمات زیر را اعمال کن:

1
در بخش Build and deployment منبع را روی Deploy from a branch بگذار.
2
شاخه را main و پوشه را /(root) انتخاب کن.
3
روی دکمه Save کلیک کن.
اگر Pages برای حساب یا مخزن تو در دسترس نیست، محدودیت طرح یا سیاست سازمان را بررسی کن؛ تنظیم امنیتی را دور نزن. می‌توانی خروجی محلی را بررسی کنی، اما بخش انتشار اینترنتی را انجام‌نشده ثبت کن.

منتظر نتیجه ساخت، نه حدس

وارد تب Actions شو و وضعیت انتشار GitHub Pages را ببین:

در حال اجرا — مدت build و deploy ثابت نیست؛ اجرای مربوط به آخرین commit main را دنبال کن.
موفقیت‌آمیز — تیک سبز رنگ کنار Pages deployment ظاهر می‌شود.
لینک آنلاین سایت در صفحه Settings → Pages ظاهر خواهد شد.
در وضعیت Failed، جزئیات اجرای ناموفق را بخوان و منبع main، پوشه root و index.html را بررسی کن. تنها پس از موفقیت، URL اعلام‌شده در Pages را بردار؛ ذخیره تنظیمات به‌تنهایی اثبات انتشار نیست.

از چشم خواننده باز کن

لینک انتشار (مانند https://USERNAME.github.io/simorgh-journey/) را در یک پنجره Incognito مرورگر باز کن.

صفحه وب داستان سیمرغ، همراهان و وضعیت رسیدن به قاف بدون نیاز به لود فایل‌های محلی باز می‌شود.

دفتر تحویل را کامل کن

نشانی نمونه را عیناً کپی نکن؛ آن را با URL واقعی که در قدم قبل باز کردی جایگزین کن. اگر انتشار اینترنتی در دسترس نبود، این قدم افزودن URL و commit آن را اجرا نکن و همان محدودیت را در تحویل بنویس.

آدرس سایت وب خودت را در انتهای README.md بیفزا:

markdownافزودن لینک سایت به README
## Live Site
https://YOUR_GITHUB_USERNAME.github.io/simorgh-journey/
bashکامیت و push آدرس سایت
git add README.md
git commit -m "docs: add live website URL"
git push origin main
این commit بعد از v1.0.0 است. main اکنون یک commit جلوتر و تگ قبلی همچنان ثابت است؛ به‌روزرسانی Pages مربوط به این commit را هم بررسی کن. نشانی سایت در README نسخه v1.0.0 وجود ندارد و این طبیعی است.

پنجره‌ای اختیاری به jj

آشنایی کوتاه با سیستم‌های مدرن‌تر کنترل نسخه مثل Jujutsu (jj):

jj ابزاری مدرن است که مفاهیم کامیت، شاخه و Working Copy را با سادگی بیشتر و مدیریت خودکار کامیت‌های پیش‌فرض پیاده‌سازی می‌کند.

در آینده و پروژه‌های پیچیده‌تر، jj می‌تواند جایگزینی سریع برای برخی جریان‌های کاری Git باشد.

سایت جدید، نسخه قدیمی

اگر یک کامیت جدید روی main بزنید و push کنید، آیا سایت GitHub Pages خودکار به‌روز می‌شود؟ اگر تگ جدید بزنید چطور؟

هر push جدید به شاخه اصلی main موجب اجرای خودکار فرایند انتشار در GitHub Pages می‌شود.

نشان سیمرغ با شاهد

برای تحویل، به‌جای تکیه به پیام موفقیت، شاهدها را جمع کن: URL درخواست با وضعیت Merged، صفحه Release متصل به v1.0.0 و URL واقعی سایت که در پنجره خصوصی باز شده است. هر بخش در دسترس نبوده را صریحاً انجام‌نشده بنویس.

bash
git branch --show-current
git status --short
git stash list
git show v1.0.0:caravan-registry.txt
git show develop:release-notes.txt
git rev-list --count v1.0.0..main

شاخه main و وضعیت و stash تمیز باشند؛ گزارش نسخه چهارخطی و یادداشت develop دارای Ready باشد. اگر قدم افزودن URL را انجام دادی، عدد آخر ۱ است؛ اگر به‌علت محدودیت انتشار آن را انجام ندادی، ۰ است. در هیچ‌یک تگ را جابه‌جا نکن.

نشان سیمرغ یعنی بتوانی یک تغییر را ثبت، بررسی، اصلاح و با شاهد تحویل بدهی. پایان دوره آغاز تمرین مستقل توست، نه ادعای تسلط بر تمام Git.