اولین باری که در یک پروژه تیمی خرابکاری کردم، با git reset --hard سه روز کار خودم را پاک کردم. آن روز یاد گرفتم که Git فقط مجموعه‌ای از دستورات حفظ‌کردنی نیست؛ یک مدل ذهنی است که اگر درست نفهمی، هر دستور می‌تواند به فاجعه تبدیل شود. اگر تازه با Git آشنا می‌شوید، Git در ویکی‌پدیا مرور خوبی از مفاهیم پایه دارد. برای آشنایی کامل با مفاهیم، آموزش Git از صفر نقطه شروع خوبی است.

مدل ذهنی Git: سه ناحیه که باید بشناسید

Git سه ناحیه اصلی دارد: Working Directory که فایل‌های فعلی شما در آن قرار دارند، Staging Area که تغییرات آماده کامیت را نگه می‌دارد، و Repository که تاریخچه کامیت‌ها در آن ذخیره شده. هر دستور Git در یکی از این سه ناحیه عمل می‌کند. اگر این مدل ذهنی را نداشته باشید، دستورات به سحر و جادو تبدیل می‌شوند.

مثال ساده: وقتی git add می‌زنید، تغییر از Working Directory به Staging Area منتقل می‌شود. وقتی git commit می‌زنید، محتوا به Repository می‌رود. وقتی git reset می‌زنید، از Staging به Working Directory برمی‌گردد. بدون درک این سه ناحیه، هر نوع بازگردانی به آزمون و خطا تبدیل می‌شود. برای مدل کامل Git، Git را بازیگوشانه یاد بگیرید نکات خوبی ارائه می‌دهد.

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

دستورات پایه که هر روز استفاده می‌کنید

چند دستور که در هر پروژه‌ای، هر روز با آن‌ها سروکار دارید:

  • git status: نمایش وضعیت فایل‌ها در Working Directory و Staging Area.
  • git add <file>: افزودن فایل به Staging Area.
  • git commit -m "message": ثبت تغییرات در Repository با پیام توضیحی.
  • git log: نمایش تاریخچه کامیت‌ها.
  • git diff: نمایش تفاوت‌های فایل تغییر یافته با آخرین کامیت.
  • git pull: دریافت تغییرات از مخزن راه دور.
  • git push: ارسال تغییرات به مخزن راه دور.

نکته مهم در مورد پیام کامیت: پیام خوب، توضیح می‌دهد چرا این تغییر انجام شد نه فقط چه چیزی تغییر کرد. مثلاً به جای fix bug بنویسید fix: resolve cart total calculation when tax is enabled. این کار بازبینی کد را بسیار ساده‌تر می‌کند. برای آشنایی با پیام‌های استاندارد، اصول کدنویسی تمیز نکات مفیدی دارد.

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

مدیریت شاخه‌ها و جابجایی بین آن‌ها

شاخه‌ها (Branch) قلب کار با Git هستند. هر شاخه یک خط مستقل از توسعه است که می‌تواند به شاخه اصلی merge شود. برای جزئیات کامل، برنچ در Git راهنمای اختصاصی دارد. چند دستور کلیدی:

  • git branch: نمایش شاخه‌های موجود.
  • git branch <name>: ساخت شاخه جدید.
  • git checkout <branch> یا git switch <branch>: جابجایی به شاخه دیگر.
  • git checkout -b <name>: ساخت و جابجایی به شاخه جدید در یک دستور.
  • git branch -d <name>: حذف شاخه.

الگوی نام‌گذاری شاخه‌ها در پروژه‌های حرفه‌ای معمولاً بر اساس نوع کار است: feature/user-login، bugfix/cart-total، hotfix/security-patch. این الگو باعث می‌شود از نام شاخه، هدف آن مشخص باشد. در پروژه‌های وردپرسی که با افزونه‌های مختلف کار می‌کنید، این نظم حیاتی است. برای دیدن این الگو در عمل، گیت در توسعه وردپرس را ببینید.

ادغام تغییرات: merge و rebase

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

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

ویژگیMergeRebase
تاریخچهواقعی و شاخه‌ایخطی و بازنویسی‌شده
ایمنیایمن روی شاخه‌های عمومیخطرناک روی شاخه‌های عمومی
کامیت اضافهیک کامیت merge اضافه می‌شودکامیت اضافه ندارد
مناسب برایادغام نهایی به mainبه‌روزرسانی شاخه شخصی

بازگشت به عقب: undo، reset، revert

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

  • git reset --soft HEAD~1: آخرین کامیت را حذف می‌کند اما تغییرات را در Staging نگه می‌دارد.
  • git reset --mixed HEAD~1: آخرین کامیت را حذف و تغییرات را به Working Directory برمی‌گرداند.
  • git reset --hard HEAD~1: آخرین کامیت و همه تغییرات را حذف می‌کند - خطرناک.
  • git revert <commit>: یک کامیت جدید می‌سازد که اثر کامیت اشتباه را خنثی می‌کند - ایمن روی شاخه‌های عمومی.

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

یک نکته حیاتی: قبل از هر reset --hard، مطمئن شوید تغییری ندارید که از دست برود. اگر شک دارید، اول با git stash تغییرات را ذخیره کنید. برای بازیابی تغییرات پس از reset اشتباه، git reflog نجات‌دهنده است. این دستور، تاریخچه کامل جابجایی‌های HEAD را نشان می‌دهد و می‌توانید به هر کامیت قبلی برگردید.

کار با مخازن راه دور

در پروژه‌های تیمی، مخزن راه دور (Remote) نقش اصلی را بازی می‌کند. معمولاً یک مخزن اصلی روی GitHub یا GitLab وجود دارد و هر توسعه‌دهنده یک نسخه محلی دارد. دستورات کلیدی برای کار با مخزن راه دور:

  • git remote -v: نمایش مخازن راه دور تنظیم‌شده.
  • git remote add origin <url>: افزودن مخزن راه دور.
  • git fetch: دریافت تغییرات بدون ادغام.
  • git pull: دریافت و ادغام تغییرات.
  • git push origin <branch>: ارسال تغییرات به مخزن راه دور.

تفاوت fetch و pull در این است که fetch فقط تغییرات را دریافت می‌کند اما روی شاخه فعلی اعمال نمی‌کند، در حالی که pull هر دو کار را انجام می‌دهد. در پروژه‌های حساس که می‌خواهید قبل از ادغام تغییرات را ببینید، fetch انتخاب ایمن‌تری است. برای همکاری تیمی روی GitHub، Pull Request در GitHub و GitHub Actions راهنمای کاربردی دارند.

دستورات پیشرفته‌ای که در پروژه‌ها نجات‌دهنده بودند

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

  • git stash: ذخیره تغییرات فعلی بدون کامیت برای جابجایی موقت.
  • git cherry-pick <commit>: اعمال یک کامیت مشخص از شاخه دیگر.
  • git bisect: پیدا کردن کامیتی که یک باگ را معرفی کرده.
  • git blame <file>: نمایش اینکه هر خط فایل در چه کامیتی و توسط چه کسی تغییر کرده.
  • git reflog: تاریخچه کامل جابجایی‌های HEAD - نجات‌دهنده پس از reset اشتباه.
  • git clean -fd: حذف فایل‌های ردیابی‌نشده از Working Directory.

دستور git bisect به ویژه در پروژه‌های بزرگ ارزشمند است. وقتی باگی پیدا می‌شود که معلوم نیست در کدام نسخه معرفی شده، Git می‌تواند به صورت دودویی بین کامیت‌ها جستجو کند و در چند مرحله به کامیت مقصر برسد. این تکنیک در پروژه‌های وردپرسی که چند افزونه با هم تعامل دارند، حیاتی است. برای دیباگ پروژه‌های وردپرسی، دیباگ کردن کدهای سفارشی وردپرس راهنمای کاملی دارد.

یک الگوی حرفه‌ای که در تیم‌ها به آن رسیدم: هر توسعه‌دهنده قبل از شروع کار جدید، git fetch و git rebase روی شاخه شخصی می‌زند تا با آخرین تغییرات همگام شود. این کار احتمال تعارض در مرحله نهایی را به شدت کاهش می‌دهد. برای آشنایی با گردش کار CI/CD که بخشی از این چرخه است، GitHub Actions و CI/CD برای پروژه‌های وردپرسی را ببینید.

در Git، هیچ دستوری بی‌خطر نیست. هر دستور یک تصمیم است و هر تصمیم، پیامد مشخصی دارد که باید بدانید.

پرسش‌های پرتکرار درباره Git

تفاوت git pull و git fetch چیست؟ git fetch فقط تغییرات را دانلود می‌کند اما روی شاخه فعلی اعمال نمی‌کند. git pull علاوه بر دانلود، ادغام هم انجام می‌دهد. برای کنترل بیشتر، fetch و merge جدا بهتر است.

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

چطور یک فایل را از کامیت فراموش شده اضافه کنم؟ با git commit --amend می‌توانید فایل فراموش‌شده را به آخرین کامیت اضافه کنید. اما این دستور کامیت را بازنویسی می‌کند، پس روی شاخه عمومی استفاده نکنید.

چگونه یک شاخه را از مخزن راه دور دریافت کنم؟ با git fetch origin و بعد git checkout <branch-name>. اگر شاخه به صورت محلی وجود ندارد، Git آن را از راه دور می‌گیرد.

چگونه تغییرات یک کامیت خاص را به شاخه فعلی اضافه کنم؟ با git cherry-pick <commit-hash>. این دستور یک نسخه از آن کامیت را روی شاخه فعلی اعمال می‌کند.

برای سایر موضوعات مرتبط با Git، مهاجرت از GitHub به GitLab، GitLab برای تیم‌های DevOps و مدیریت پروژه با GitHub Projects را ببینید. برای آشنایی با ابزارهای مکمل، Visual Studio Code و Jira برای مدیریت پروژه راهنمای کاربردی دارند. اگر با Docker کار می‌کنید، آموزش Docker با مثال‌های واقعی نکات مفیدی ارائه می‌دهد.

آنچه از پروژه‌های واقعی یاد گرفتم

سه چیز بعد از سال‌ها کار با Git در ذهنم جا افتاده. اول، مدل ذهنی مهم‌تر از حفظ کردن دستورات است. دوم، قبل از هر دستور خطرناک، یک بار دیگر از خودتان بپرسید که چه اتفاقی می‌افتد. سوم، git reflog دوست شماست؛ در ۹۰ درصد مواقع می‌تواند کارهای از دست رفته را برگرداند. برای یادگیری Git به صورت سیستماتیک، آموزش Git از صفر و دستورات ضروری Git را ببینید. برای کار با مخازن راه دور، Pull Request در GitHub و GitHub Actions راهنمای کاملی دارند.

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