ملخص المقال: القاعدة الذهبية للتاريخ المنشور، والفرق بين reset المحلي وrevert القائم للأمام، والأنواع الثلاثة للreset، وتنظيف الالتزامات بـrebase التفاعلي، ونقل القرارات بـcherry-pick، والاسترجاع عبر reflog.
طريقة القراءة: ابدأ بفهم المشكلة، ثم راجع القرار أو الخطوات العملية في الأقسام التالية، واستفد من المخطط لتصور الأجزاء المرتبطة ببعضها.
ماذا ستخرج به من هذا المقال؟
التاريخ في Git ليس سجلًّا ثابتًا يجب قبوله كما هو؛ بل أداة قراءة تتحدث عن القرارات وتساعد الفريق — أو تضلله. هذا المقال يعلّمك الإصلاح الآمن: متى تعيد كتابة التاريخ ومتى لا، والفرق بين تغيير رسالة commit وإعادة ترتيب سلسلة كاملة، وreset بالأنواع الثلاثة، وrebase التفاعلي، وcherry-pick لنقل القرارات بدقة، دون حرق عمل زملائك أو تمزيق الفرع البعيد.
ستتعلم قاعدة النجاح الأولى قبل أي أمر: لا تعيد كتابة التاريخ الذي نشره غيرك فعلًا. وبعدها ستفهم متى يكون الحل بالرجوع للأمام (revert) وهو الأأمن للفروع المشتركة، ومتى يكون بإعادة الكتابة المحلية المحصورة.
قبل أي أمر: القاعدة الذهبية للتاريخ
Git يرى الالتزامات كسلسلة من الأقراص: تغيير التزام محلي يعيد بناء من بعده. المشكلة تظهر فقط عندما يكون التاريخ المكتوب قد نُشر على فرع بعيد وحرّكه آخرون. القاعدة:
خطأ شائع يكلف الفريق يومًا
إعادة كتابة تاريخ فرع مشترك بـ git push --force بعد دمج أحدهم. من تلقّى الالتزام القديم يلقى عند السحب صراعًا لا يفهمه؛ والمشكلة ليست في الأمر بل في عدم سؤالك: هل كتب أحدٌ بعدي؟ القاعدة: التاريخ المنشور يُخاطب بـ revert لا بإعادة الكتابة.
قبل أي إعادة كتابة اسأل ثلاثًا: هل الفرض محلي ولم أدفعه؟ هل النسخ المشتركة من النادر تسحب؟ هل أنا وحدي من يعمل عليه؟ إن كان أي جواب «لا»، فالحل الصحيح هو إلغاء للأمام لا إعادة كتابة.
الفرق الجوهري: reset مقابل revert
الأمران يبدوان متشابهين ويختلفان في الجوهر:
git reset: يحرك المؤشر وينقل الالتزامات — يعيد كتابة التاريخ محليًا، ولا يصلح للفروع المنشورة.git revert: يضيف التزامًا جديدًا يعكس السابق — التاريخ يُبنى للأمام، والفرع البعيد يبقى آمنًا وكل من يسحب يتحاور بسلام.
# قبل الضغط: إصلاح آخر التزام محلي
git reset --soft HEAD~1 # أبقِ التغييرات في الفهرس
git reset --hard HEAD~1 # احذف نهائيًا (بحرص)
# بعد الضغط: إلغاء للأمام آمن
git revert HEAD # التزام جديد يعكس الأخيرالأنواع الثلاثة لـ reset ومتى يستخدم كل منها
| الوضع | الفهرس | مساحة العمل | متى تستخدم |
|---|---|---|---|
--soft | يبقى | يبقى | دمج التزامات دون فقدان التغييرات |
| --mixed (افتراضي) | يُرسب | يبقى | رفع التغييرات من الفهرس إلى العمل قبل إعادة الالتزام |
--hard | يُحذف | يُحذف | التنظيف النهائي لمسار خاطئ (منطقة أصلية لا بعيدة) |
قاعدة السلامة
قبل --hard أنشئ عنصرًا احتياطيًا: git branch backup أو git stash. الالتزام المحذوف يبقى في reflog إن كان قبلها، لكن إن لم تكن نسخة فلا عودة.
الجمع التصاعدي: squash وrebase التفاعلي
عند العمل على ميزة، عادة ما تنتج عشرين التزامًا صغيرًا: «fix typo» و«wip» و«oops». قبل الدمج تُنظّف السلسلة إلى التزام واحد ذي معنى عبر rebase التفاعلي:
git rebase -i main
# في المحرر:
# pick 3a2f91 feat: المنفجر
# fixup d7e0aa fix typo
# fixup 9cbb11 oops
# reword 3a2f91 feat: إعادة هيكلة الفواتيرالأوامر التي ستجدها وتحتاجها بصورة متكررة:
pick: إبقاء الالتزام.reword: تغيير الرسالة فقط.fixup: دمج داخل السابق مع تجاهل رسالته.squash: دمج وتحرير الرسالة النهائية.drop: حذف الالتزام.
نقل قرار واحد: cherry-pick
عندما يهبط إصلاح على فرع ثم تكتشف أن فرعًا آخر (مثل hotfix/x) يحتاجه أيضًا، ينقل cherry-pick الالتزام بمعرّفه دون سحب الفرع كله:
git checkout hotfix/payment
git cherry-pick a1b2c3d4 # نقل إصلاح الدفع
git push origin hotfix/paymentنصائح الاستخدام: التزام واحد بأثر ذري، ثم اختبار فوري بعد النقل، وإن نشب صراع فحُلّه بفهم السياق لا بتعطيله. وثقّ كل إصلاح منقول في الوصف (channel) لإرجاع قابلة للنقاش.
التاريخ المفقود ليس محتومًا: reflog
git reflog يسجل كل حركة رأس، حتى التي بدت مُدمرة — فتحذير --hard لا يبطل قدرة الاسترجاع إذا أردت:
git reflog
# 3a2f91 (HEAD) HEAD@{0}: commit: feat: …
# b7c001 HEAD@{1}: reset: moving to HEAD~1
git reset --hard b7c001 # العودة قبل الحذفأفضل ممارسة
عدّ reflog صندوق أمان ماديًا: أي خطأ يُصحح بالوصول إليه ما دام السجل هذا حيًا (عمومًا 90 يومًا). لا تعتمد عليه للتخزين الدائم لكنه يمنحك مساحة زمن لتصحيح الأخطاء.
استراتيجيات للدفع بعد إعادة الكتابة
عندما تعيد كتابة فرع محلي وتحب إرساله، استخدم git push --force-with-lease — الأمان الإضافي: يتجاهل بأمان إذا تغيّر الفرع البعيد منذ آخر سحب:
git push --force-with-lease origin feature/checkoutهذه ليست حلًا سحريًا: مع فرع مشترك، الإجراء الصحيح هو التنسيق المسبق ثم الدفع الآمن، أو اللجوء إلى revert الخالص لإلغاء للأمام. وأبقِ قاعدة: لا دفعات مفروضة على فروع يحضرها أكثر من شخص.
قائمة تحقق إصلاح التاريخ
- هل التاريخ المنشور؟ إن نعم →
revertلا إعادة كتابة. - قبل
--hardأنشأت نسخة احتياطية (branch/stash). rebase -iتُركت بعناية والرسائل النهائية صريحة.cherry-pickبمعرّف واحد مع اختبار فورًا بعد النقل.- الدفع الآمن
--force-with-leaseلا--forceالعاري. - أدركت أن reflog مفتاح للاسترجاع خلال عمره.
الأسئلة الشائعة
لماذا بعض الفرق تجنب إعادة كتابة التاريخ أصلًا؟
لأن أمان الفريق أسهل من انضباطه: قاعدة «لا إعادة كتابة بعد الضغط» تمحو فئة كاملة من الأخطاء، وتفرض الوصف للأمام على الجميع. بعض الفرق تقبلها كسياسة شاملة.
هل يمكنني إعادة ترتيب التزامات بصدد إعادة الكتابة؟
نعم في rebase -i اكتب بترتيب جديد عبر تحريك السطور؛ لكن انتبه أن كل إعادة ترتيب قد تسبب صراعات إذا كانت الالتزامات تعتمد تسلسلًا.
ماذا أفعل بعد دمج فرع خطأ؟
حافظ على التاريخ: git revert للالتزام الدامج أو للعمل المدمج كلًا على حدة، ولا تحاول فك الدمج بإعادة الكتابة على فرع مشترك.
الخلاصة
ما يجب أن يبقى معك
- القاعدة الذهبية: التاريخ المنشور يُعاجل بـ revert لا بإعادة كتابة.
- reset يكتب محليًا، revert يكتب للأمام؛ لا تخلط بينهما.
- rebase -i أداة التنظيف الوحيدة قبل الضغط، وضمنها فقط.
- cherry-pick ينقل القرار ولا ينسخ الفرع.
- أعرف أن reflog و force-with-lease هما حبالك للأمان.
الخطوة التالية
الآن تكتب الآن وتراجع وتصلح التاريخ بنفسك، لكن هذه الأدوات وحدها لا تُظهر حركة الفريق. في المقال القادم نغوص في GitHub Flow: إدارة fork، والعلامات (releases)، والمساهمات الخارجية، وكل ما يكلل تكاملك في منظومة GitHub كاملة.
مخطط يربط عناصر الموضوع
flowchart LR
A["إيداع الشيفرة"]
B["اختبارات آلية"]
C["بناء نسخة"]
D["نشر متدرج"]
E["مراقبة وتراجع"]
A --> B
B --> C
C --> D
D --> E
التسلسل: إيداع الشيفرة ← اختبارات آلية ← بناء نسخة ← نشر متدرج ← مراقبة وتراجع.
سيناريو تطبيقي ومراجعة
افترض أنك مسؤول عن تطبيق ما ورد في هذا المقال داخل فريق عمل. ابدأ بتحديد الهدف الذي يقيس النجاح في موضوع إصلاح تاريخ Git بأمان: revert وreset وrebase وcherry-pick، ثم اكتب المدخلات المطلوبة والقيود والصلاحيات، وجرب حالة طبيعية وأخرى استثنائية قبل الاعتماد.
- حدد الافتراضات التي تحتاج إلى تأكيد في بيئتك.
- حوّل القرارات الأساسية الواردة أعلاه إلى خطوات أو اختبارات قابلة للتنفيذ.
- وثّق طريقة التراجع أو التصحيح إذا فشل أحد الاختبارات.
أسئلة للمراجعة
كيف أبدأ بتطبيق هذا الموضوع؟
اختر حالة استخدام واحدة، وطبّق أصغر جزء يحقق قيمة قابلة للقياس؛ ثم قارن النتيجة بالمتطلبات.
كيف أتحقق من جودة الحل؟
اختبر المسار المعتاد ومسار الخطأ، وراجع الأمان وسلامة البيانات والأداء وفق طبيعة الموضوع، ثم أعد الاختبار بعد أي تعديل.
الرد على التعليق