Git وتدفقات العمل التعاونية

إعادة التأسيس والانتقاء وجراحة السجل

18 دقيقة الدرس 3 من 28

إعادة التأسيس والانتقاء وجراحة السجل

كل مهندس متمرس يواجه في النهاية القرار ذاته: هل أدمج هذا الفرع أم أُعيد تأسيسه؟ لا يؤثر هذا الاختيار على الرسم البياني الذي تراه في git log فحسب — بل يحدد كيف يتتبع فريقك الأعطال، ويبحث عن الانحدارات، ويراجع السجل بعد ستة أشهر. يتخطى هذا الدرس الأساسيات ليعلمك استخدام أدوات Git لمعالجة التاريخ بالطريقة التي يتبعها كبار المهندسين وقادة DevOps على نطاق واسع.

الدمج مقابل إعادة التأسيس: النموذج الذهني

يُسجّل الدمج (Merge) حقيقة أن خطَّي تطوير التقيا في لحظة بعينها، ويحفظ السياق الكامل: يمكنك دائمًا إعادة تكوين ما كان على main حين وصل فرعك. يمتلك دمج الالتزام أبوَين ويُمثّل السجل الدائم لهذا الالتقاء.

أما إعادة التأسيس (Rebase) فتأخذ التزاماتك وتُعيد تطبيقها على قاعدة مختلفة. يظل محتوى الالتزام مطابقًا، لكن يتغير الـ SHA لأن مؤشر الأب يتغير. تبدو النتيجة كما لو أنك بدأت العمل بعد آخر التزام في المصدر — يصبح الفرع خطيًا.

Merge vs Rebase comparison git merge A B C (main) F1 F2 M git rebase A B C (main) F1\' SHA جديد تاريخ خطي — لا يوجد التزام دمج يحفظ السياق الكامل
الدمج يحفظ السياق الكامل؛ إعادة التأسيس تُعيد تطبيق الالتزامات لتاريخ خطي.
القاعدة الذهبية لإعادة التأسيس: لا تُعد تأسيس الالتزامات التي رُفعت إلى فرع مشترك على الخادم البعيد. تُعيد إعادة التأسيس كتابة الـ SHAs — وكل من جلب تلك الالتزامات سيجد تاريخه منفصلًا بصورة يصعب توحيدها.

إعادة التأسيس اليومية: إبقاء الفرع محدثًا

الاستخدام الأكثر شيوعًا لإعادة التأسيس بسيط: تريد أن يجلس فرع الميزة فوق أحدث إصدار من main قبل فتح طلب دمج (Pull Request).

# جلب أحدث التغييرات من المصدر git fetch origin # إعادة تطبيق الالتزامات فوق origin/main git rebase origin/main # عند حدوث تعارض، يتوقف Git ويخبرك بالملف المطلوب إصلاحه. # بعد الحل: git add <conflicted-file> git rebase --continue # إلغاء إعادة التأسيس والعودة للحالة الأصلية: git rebase --abort

إعادة التأسيس التفاعلية: المشرط الجراحي

تتيح لك إعادة التأسيس التفاعلية (git rebase -i) إعادة كتابة التاريخ قبل وصوله إلى الخادم المشترك. في شركات التقنية الكبرى، يُعدّ دمج التزامات العمل قيد التنفيذ في التزامات نظيفة وذرية قبل الرفع ممارسةً معيارية. الأوامر المتاحة لكل التزام هي:

  • pick — احتفظ بالالتزام كما هو
  • reword — احتفظ بالالتزام مع تحرير رسالته
  • edit — توقف بعد التطبيق لتتمكن من التعديل
  • squash — ادمجه مع الالتزام السابق مع دمج الرسائل
  • fixup — مثل squash لكن تجاهل رسالة هذا الالتزام
  • drop — احذف الالتزام كليًا
# إعادة كتابة آخر 4 التزامات بشكل تفاعلي git rebase -i HEAD~4 # يفتح المحرر بقائمة كالتالي: # pick a1b2c3d Add feature skeleton # pick d4e5f6a WIP: half-done # pick 7g8h9i0 Fix typo # pick 1j2k3l4 Add tests and docs # # عدّلها إلى: # pick a1b2c3d Add feature skeleton # squash d4e5f6a WIP: half-done # fixup 7g8h9i0 Fix typo # reword 1j2k3l4 Add tests and docs # # سيطلب Git تحرير الرسالة المدمجة للـ squash ورسالة الـ reword. # النتيجة: التزامان نظيفان.
ممارسة احترافية: اضبط محررك لإعادة التأسيس التفاعلية عبر git config --global core.editor "code --wait" (VS Code) أو vim. كثير من الفرق تستخدم git rebase -i --autosquash جنبًا إلى جنب مع git commit --fixup=<SHA> لترتيب التزامات الإصلاح تلقائيًا أثناء إعادة التأسيس.

الانتقاء (Cherry-Pick): زرع التزامات بعينها

أحيانًا تحتاج إلى إصلاح محدد من فرع ما على فرع آخر، دون دمج الفرع كله. يطبّق git cherry-pick الفرق (diff) لالتزام واحد أو أكثر على HEAD الحالية.

# تطبيق التزام واحد من فرع آخر git cherry-pick 4f5a6b7 # تطبيق نطاق من الالتزامات (لا يشمل البداية، يشمل النهاية) git cherry-pick a1b2c3d..f6g7h8i # انتقاء دون إنشاء التزام (مرحلة فقط للمراجعة) git cherry-pick --no-commit 4f5a6b7 # سيناريو شائع: نقل إصلاح أمني إلى فرع إصدار git checkout release/2.4 git cherry-pick 9d1e2f3 # التزام الإصلاح الأمني من main git push origin release/2.4
الانتقاء يُنشئ التزامًا مكررًا بـ SHA جديد. إذا اندمج الفرعان لاحقًا، سيحاول Git تطبيق التغيير مرتين — عادةً لا تأثير له إذا كان المحتوى متطابقًا، لكنه قد يُسبب تعارضات مُربكة. وثّق عمليات الانتقاء في رسالة الالتزام (مثلًا: Cherry-picked from main: 9d1e2f3) وتتبعها في ملاحظات الإصدار.

Reflog: شبكة الأمان الطارئة

الـ reflog هو شبكة الأمان الخفية في Git. في كل مرة يتحرك فيها HEAD — التزام أو إعادة تأسيس أو إعادة ضبط أو تبديل فرع — يُسجّل Git الموضع القديم في .git/logs/HEAD. يُحتفظ بهذا السجل 90 يومًا افتراضيًا وهو محلي فقط (لا يُرفع). هذا يعني أن لا شيء تقريبًا يضيع حقًا على جهازك.

# عرض الـ reflog الكامل لـ HEAD git reflog # يبدو الناتج هكذا: # 4a5b6c7 HEAD@{0}: rebase finished: returning to refs/heads/feature/auth # 1d2e3f4 HEAD@{1}: rebase: Apply rate limiting middleware # 9g8h7i6 HEAD@{2}: rebase: Apply JWT validation # a3b4c5d HEAD@{3}: checkout: moving from main to feature/auth # ... # سيناريو: أجريت rebase تفاعليًا وحدث خطأ. # ابحث عن الـ SHA قبل إعادة التأسيس: git reflog | grep "checkout:" # استعد تلك الحالة في فرع جديد git checkout -b recovery/before-rebase a3b4c5d # أو أعد ضبط فرعك الحالي بقوة (مُدمِّر — استخدمه بحذر) git reset --hard HEAD@{3}

أدوات أخرى لمعالجة التاريخ

يُعيد git commit --amend كتابة الالتزام الأخير — مفيد لتصحيح خطأ إملائي في الرسالة أو إضافة ملف منسي. مثل إعادة التأسيس، يُغيّر الـ SHA، لذا استخدمه قبل الرفع فقط.

يُحرّك git reset مؤشر الفرع للخلف. يحتفظ --soft بتغييراتك مرحلية؛ يلغي --mixed (الافتراضي) مرحلتها؛ ويتجاهلها --hard كليًا. استخدم --hard فقط حين تكون متأكدًا تمامًا من رغبتك في التخلي عن العمل.

يُعدّ git bisect الأداة الشريكة للتاريخ النظيف: يجعل التاريخ الخطي والذري البحث الثنائي عن الانحدارات أسرع بكثير. هذا هو سبب اشتراط الفرق "تغيير منطقي واحد لكل التزام" — ليس للجمالية، بل لكفاءة الاستجابة للحوادث.

متى تدمج ومتى تُعيد التأسيس: معظم فرق التقنية الكبرى تُعيد تأسيس فروع الميزات محليًا قبل مراجعة طلب الدمج (تاريخ نظيف للمراجعين)، ثم تستخدم دمجًا بالضغط (squash merge) أو التزام دمج عادي في main حسب استراتيجية التفرع. السياسة المحدد أقل أهمية من تطبيقه باتساق وعدم إعادة كتابة الالتزامات العامة أبدًا.