لماذا الفروع؟
main عندك يعمل وموقعك منشور منه. الآن تريد بناء صفحة تسجيل دخول تحتاج ثلاثة أيام. لو عملت على main مباشرة، فطوال الأيام الثلاثة سيكون المشروع نصف جاهز: لا تستطيع إصلاح خطأ عاجل ونشره دون نشر نصف الميزة معه. الفرع (Branch) خطّ عمل منفصل: تبني فيه الميزة كما تشاء، وmain يبقى كما هو حتى تقرّر الدمج.
ما هو الفرع فعلًا؟
الفكرة التي تجعل الفروع في Git سهلة: الفرع ليس نسخة من المشروع، بل مؤشّر (Pointer) إلى commit. حرفيًّا، ملف صغير فيه رقم commit:
cat .git/refs/heads/feature/login
# f3007b5898f5b95af4a771632fbdadccbe44deb2
لهذا إنشاء فرع فوري مهما كبر المشروع. وحين تعمل commit جديدًا، يتقدّم مؤشّر الفرع الحالي إليه تلقائيًّا.
و**HEAD** مؤشّر خاص يقول "أنت تقف هنا": غالبًا يشير إلى اسم فرع (ref: refs/heads/main)، فيعرف Git أي فرع يتقدّم مع الـ commit القادم.
feature/login
↓
A ── B ── C ──────────── E
↑
main ← HEAD
إنشاء فرع والانتقال إليه
git switch -c feature/login # أنشئ وانتقل (الأشيع)
Switched to a new branch 'feature/login'
أو على خطوتين:
git branch feature/login # أنشئ فقط، وابقَ مكانك
git switch feature/login # انتقل
الفرع الجديد يبدأ من الـ commit الذي تقف عليه الآن. لتبدأه من مكان آخر: git switch -c hotfix main.
switch أم checkout؟
ستجد في الشروحات القديمة git checkout -b feature/login. checkout أمر قديم يفعل شيئين مختلفين تمامًا: الانتقال بين الفروع، واستبدال ملفات بنسخ أخرى. منذ Git 2.23 انقسم إلى أمرين أوضح: switch للفروع و**restore** للملفات (درس التراجع). checkout ما زال يعمل، لكن switch لا يمسح تعديلاتك أبدًا إلا إن طلبت ذلك صراحة.
رؤية الفروع
git branch # الفروع المحلية، و* بجانب الحالي
git branch -v # مع آخر commit في كل فرع
git branch -a # مع الفروع البعيدة (remotes/origin/...)
* feature/login f3007b5 Add login form
main cb566b2 Add header
والرسم الأوضح لكل الفروع معًا:
git log --oneline --graph --all
الانتقال وفي يدك تعديلات
ماذا يحدث للتعديلات غير المحفوظة حين تنتقل لفرع آخر؟
- إن لم تتعارض مع الفرع الهدف، تنتقل معك كما هي. هي ليست "ملك" أي فرع حتى تعمل commit.
- إن كان الفرع الهدف يغيّر نفس الملف، يرفض Git الانتقال حتى لا يمسح عملك:
error: Your local changes to the following files would be overwritten by checkout:
app.txt
Please commit your changes or stash them before you switch branches.
Aborting
الحل: احفظ عملك بـ commit، أو خبّئه مؤقّتًا بـ git stash ثم انتقل.
detached HEAD: ليس خطأ
git switch --detach a8b6e0e # أو: git checkout a8b6e0e
HEAD is now at a8b6e0e Initial page
انتقلت إلى commit قديم مباشرة لا إلى فرع، فأصبح HEAD يشير لـ commit لا لاسم فرع. تستطيع التجوّل وتشغيل الكود القديم بأمان. لكن أي commit تعمله هنا لا يتبع أي فرع، وحين تنتقل بعيدًا يصبح بلا اسم يدلّ عليه. إن أعجبك ما فعلت، أعطه فرعًا قبل أن تغادر:
git switch -c experiment # الآن صار لعملك اسم
وإن غادرت ونسيت، فما زال يمكن استرجاعه لفترة عبر reflog.
حذف فرع وإعادة تسميته
git branch -d feature/login # احذف فرعًا مدموجًا
git branch -D feature/login # احذف حتى لو لم يُدمج (بحذر)
git branch -m old-name new-name # أعد التسمية
-d تحميك: إن كان في الفرع commits لم تُدمج في فرعك الحالي، ترفض:
error: the branch 'feature/login' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature/login'
-D تحذف على أي حال، وcommits الفرع تصبح بلا اسم. لا تستخدمها إلا وأنت متأكّد أنك لا تحتاج ذلك العمل.
تسمية الفروع
الأسماء تشرح الغرض، والبادئة تصنّف:
feature/login-page
fix/cart-total-nan
docs/api-readme
لا مسافات، وكلمات بأحرف صغيرة مفصولة بشرطات. وفرع لكل مهمة واحدة: فرع "تعديلات-متنوعة" يعيد كل مشاكل العمل على main مباشرة.
أخطاء شائعة
- العمل على
mainمباشرة ثم اكتشاف أنك تحتاج فرعًا:git switch -c new-branchالآن تأخذ تعديلاتك غير المحفوظة معها. - commits في detached HEAD ثم المغادرة: أنشئ فرعًا أولًا.
-Dكعادة: تحذف عملًا لم يُدمج بلا تحذير.- فرع طويل العمر لا يُدمج لأسابيع: يبتعد عن
mainوتتراكم التعارضات. ادمج كثيرًا. - الخلط بين
git branch nameوgit switch -c name: الأول لا ينقلك، فتستمر الـ commits على الفرع القديم.
بعد أن تنهي عملك في الفرع، الخطوة التالية إعادته إلى main.
🎯 التالي: دمج الفروع (Merge).