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

برنچ در Git دقیقاً چیست؟

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

مدل ذهنی درست از برنچ این است: شاخه اصلی (main یا master) نسخه پایدار پروژه است. هر قابلیت جدید، تعمیر باگ یا آزمایش، در یک شاخه جدا شروع می‌شود. وقتی کار تمام شد، شاخه به main ادغام می‌شود. این جداسازی باعث می‌شود کارهای نیمه‌کاره و آزمایشی، ثبات main را به هم نزنند. برای درک بهتر مدل ذهنی Git، Git را بازیگوشانه یاد بگیرید نکات ارزشمندی دارد.

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

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

چند دستور که در هر پروژه‌ای مکرر استفاده می‌کنید:

  • git branch: نمایش فهرست شاخه‌های محلی.
  • git branch -a: نمایش شاخه‌های محلی و راه دور.
  • git branch <name>: ساخت شاخه جدید.
  • git checkout <branch> یا git switch <branch>: جابجایی به شاخه دیگر.
  • git checkout -b <name>: ساخت و جابجایی در یک دستور.
  • git branch -d <name>: حذف شاخه پس از ادغام.
  • git branch -D <name>: حذف اجباری شاخه (حتی اگر ادغام نشده باشد).
  • git branch -m <new-name>: تغییر نام شاخه فعلی.

نکته‌ای که در پروژه‌ها به آن رسیدم: استفاده از git switch به جای git checkout در کارهای روزمره توصیه می‌شود چون خواناتر است و کاربرد واضح‌تری دارد. checkout همچنان برای بازگردانی فایل‌ها استفاده می‌شود اما برای جابجایی بین شاخه‌ها، switch انتخاب بهتری است. برای مرور کامل دستورات Git، دستورات پرکاربرد Git راهنمای کاملی دارد.

استراتژی‌های شاخه‌بندی رایج

سه استراتژی اصلی برای شاخه‌بندی وجود دارد و انتخاب هرکدام، به نوع پروژه، اندازه تیم و چرخه انتشار بستگی دارد:

استراتژیمناسب براینقطه قوت
Git Flowپروژه‌های بزرگ با چرخه انتشار مشخصجداسازی دقیق مراحل توسعه
GitHub Flowپروژه‌های SaaS و وب با انتشار مداومسادگی، سرعت انتشار
Trunk-Basedتیم‌های حرفه‌ای با CI/CD قویکاهش تعارض، انتشار سریع

Git Flow از دو شاخه دائمی (main و develop) و چند نوع شاخه موقت (feature، release، hotfix) استفاده می‌کند. این مدل برای پروژه‌هایی که چرخه انتشار دارند و باید چند نسخه را همزمان نگهداری کنند، ایده‌آل است. اما برای تیم‌های کوچک و پروژه‌هایی که روزانه منتشر می‌شوند، پیچیدگی غیرضروری می‌سازد.

GitHub Flow ساده‌تر است: یک شاخه اصلی که همیشه قابل انتشار است، و شاخه‌های feature که از main گرفته می‌شوند و پس از بازبینی به آن برمی‌گردند. برای پروژه‌های وردپرسی که معمولاً با ابزارهایی مثل CI/CD منتشر می‌شوند، این مدل ساده و کارآمد است. برای پیاده‌سازی این چرخه، CI/CD برای پروژه‌های وردپرسی راهنمای عملی دارد.

Trunk-Based Development روی یک شاخه اصلی واحد تمرکز دارد و توسعه‌دهندگان کم‌ترین زمان را در شاخه‌های جانبی می‌گذرانند. این مدل به CI/CD قوی و تست خودکار نیاز دارد چون هر کامیت می‌تواند به سرعت به production برود. در پروژه‌هایی که این زیرساخت را دارند، Trunk-Based بهترین نتیجه را می‌دهد. برای آشنایی با تست خودکار، تست و دیباگ پروژه‌های وردپرس نقطه شروع خوبی است.

الگوی نام‌گذاری شاخه‌ها

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

  • feature/user-login: یک قابلیت جدید.
  • bugfix/cart-total-calculation: تعمیر یک باگ.
  • hotfix/security-patch: تعمیر فوری روی نسخه منتشرشده.
  • refactor/auth-layer: بازنویسی بدون تغییر رفتار.
  • docs/api-reference: تغییرات مستندات.

سه قاعده در نام‌گذاری: اول، همیشه از حروف کوچک و خط تیره استفاده کنید نه فاصله یا خط زیر. دوم، نام باید کوتاه اما معنادار باشد. سوم، اگر با شماره issue کار می‌کنید، آن را در نام شاخه بگذارید مثل feature/ISSUE-123-user-login. برای مدیریت پروژه با issueها، مدیریت پروژه با GitHub Projects راهنمای کاربردی دارد.

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

merge یا rebase؟ تصمیم درست

در ادغام شاخه‌ها دو رویکرد وجود دارد: merge و rebase. تصمیم بین این دو، به ماهیت شاخه و مخاطب آن بستگی دارد.

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

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

حل تعارض هنگام ادغام

تعارض (Conflict) وقتی رخ می‌دهد که دو شاخه یک بخش از کد را متفاوت تغییر داده باشند. Git نمی‌تواند تصمیم بگیرد کدام نسخه درست است و این تصمیم را به شما می‌سپارد. راهنمای کامل در حل تعارض در Git آمده است. چند نکته از تجربه:

  • قبل از هر merge، از شاخه فعلی یک بکاپ بگیرید یا مطمئن شوید که می‌توانید به راحتی برگردید.
  • تعارض‌های کوچک را همان لحظه حل کنید، نه اینکه به عقب بیندازید.
  • اگر تعارض خیلی پیچیده است، با git merge --abort merge را لغو کنید و استراتژی را تغییر دهید.
  • از ابزارهای گرافیکی مثل VS Code یا GitKraken برای حل تعارض استفاده کنید؛ کار را بسیار ساده‌تر می‌کنند.

یک نکته مهم در تیم‌ها: بعد از حل تعارض، حتماً تست کنید که همه‌چیز درست کار می‌کند. تعارض‌های حل‌شده اما تست‌نشده، منبع برخی از بدترین باگ‌های production هستند. اگر با پروژه‌های وردپرسی کار می‌کنید، تست را روی محیط staging انجام دهید نه روی production. برای تست، تست و دیباگ پروژه‌های وردپرس راهنمای کاربردی دارد.

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

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

پاکسازی و نگهداری شاخه‌ها

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

  • git branch -d <name>: حذف شاخه‌ای که ادغام شده.
  • git fetch --prune: حذف شاخه‌های راه دوری که در origin وجود ندارند.
  • git branch --merged: نمایش شاخه‌هایی که ادغام شده‌اند و قابل حذف هستند.
  • git branch --no-merged: نمایش شاخه‌هایی که هنوز ادغام نشده‌اند.

عادت ماهانه‌ای که در پروژه‌ها توصیه می‌کنم: یک مرور روی شاخه‌ها. هر شاخه‌ای که بیش از ۳۰ روز از آخرین کامیتش گذشته و ادغام نشده، بررسی کنید. اگر کار روی آن رها شده، حذفش کنید. اگر ادامه دارد، با main همگامش کنید. این مرور ماهانه، مخزن را از شاخه‌های زامبی پاک می‌کند. برای مدیریت کل مخزن، مهاجرت از GitHub به GitLab نکات کاربردی دارد.

در ادغام شاخه‌ها، pull request ابزار مهمی است. هر pull request باید یک بازبین داشته باشد که کد و تغییرات را بررسی کند. قبل از تأیید، CI باید تست‌ها را پاس کند. این چرخه، کیفیت کد را بالا می‌برد و از ورود باگ به main جلوگیری می‌کند. برای راه‌اندازی این چرخه، Pull Request در GitHub راهنمای کاملی دارد.

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

تفاوت git branch و git checkout -b چیست؟ git branch <name> فقط شاخه می‌سازد اما شما در شاخه فعلی می‌مانید. git checkout -b <name> هم شاخه می‌سازد هم شما را به آن منتقل می‌کند.

چگونه یک شاخه حذف‌شده را بازیابی کنم؟ با git reflog آخرین کامیت‌های شاخه حذف‌شده را پیدا کنید و با git branch <name> <commit-hash> آن را بازسازی کنید.

آیا می‌توانم شاخه فعلی را تغییر نام دهم؟ بله، با git branch -m <new-name>. اگر می‌خواهید نام شاخه دیگری را عوض کنید، اول به آن شاخه سوییچ کنید و بعد دستور را بزنید.

تفاوت git branch -d و git branch -D چیست؟ -d فقط شاخه‌های ادغام‌شده را حذف می‌کند و اگر شاخه ادغام نشده باشد، خطا می‌دهد. -D بدون توجه به ادغام، شاخه را حذف می‌کند و خطرناک است.

چند شاخه در یک پروژه معمولی وجود دارد؟ در مدل GitHub Flow، معمولاً فقط main به‌عنوان شاخه دائمی و چند شاخه feature فعال. در Git Flow، دو شاخه دائمی و چند شاخه موقت. تعداد به استراتژی شما بستگی دارد نه به محدودیت فنی.

برای یادگیری عمیق‌تر Git، آموزش Git از صفر و دستورات ضروری Git را ببینید. برای کار با مخازن راه دور، Pull Request در GitHub و GitHub Actions راهنمای کاربردی دارند. اگر با GitLab کار می‌کنید، GitLab برای تیم‌های DevOps نکات ارزشمندی ارائه می‌دهد.

برای پروژه‌های وردپرسی که با Git مدیریت می‌شوند، گیت در توسعه وردپرس راهنمای کاملی دارد. برای ساختاردهی پروژه، اصول کدنویسی تمیز و ساختار استاندارد کدنویسی را ببینید. برای ابزارهای مکمل، Visual Studio Code و Jira راهنمای کاربردی دارند. برای مطالعه مقالات مرتبط با تیم‌های توسعه، تجربه‌های تیمی در پروژه‌های وردپرسی را ببینید.

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

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

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