Git و GitHub
حل تعارضات الدمج
حل تعارضات الدمج
تعارضات الدمج هي جزء طبيعي من التطوير التعاوني. تحدث عندما لا يستطيع Git دمج التغييرات من فروع مختلفة تلقائياً. في هذا الدرس، ستتعلم كيفية فهم وحل ومنع تعارضات الدمج بثقة.
ما هي تعارضات الدمج؟
يحدث تعارض الدمج عندما يكون فرعان قد أجريا تغييرات مختلفة على نفس الجزء من الملف، ولا يستطيع Git تحديد التغيير الذي يجب الاحتفاظ به.
تذكر: التعارضات ليست أخطاء - إنها Git يطلب منك اتخاذ قرار حول التغييرات التي يجب الاحتفاظ بها.
السيناريوهات الشائعة التي تسبب التعارضات
1. نفس السطر، تغييرات مختلفة
الفرع A: $price = 100;
الفرع B: $price = 150;
→ تعارض! Git لا يعرف أي سعر يستخدم
2. واحد يحذف، واحد يعدّل
الفرع A: يحذف الوظيفة
الفرع B: يعدّل نفس الوظيفة
→ تعارض! Git لا يعرف ما إذا كان يجب الحذف أو الاحتفاظ
3. الملفات المنقولة
الفرع A: يعيد تسمية الملف إلى new-name.php
الفرع B: يحرر old-name.php
→ تعارض! قد يفقد Git تتبع التغييرات
4. الملفات الثنائية
الفرع A: يحدّث logo.png
الفرع B: يحدّث logo.png بشكل مختلف
→ تعارض! لا يمكن دمج الملفات الثنائية تلقائياً
التعرف على تعارض الدمج
سيخطرك Git عند حدوث تعارض:
$ git merge feature/new-checkout
Auto-merging app/Http/Controllers/CheckoutController.php
CONFLICT (content): Merge conflict in app/Http/Controllers/CheckoutController.php
Automatic merge failed; fix conflicts and then commit the result.
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: app/Http/Controllers/CheckoutController.php
مهم: لا تصب بالذعر عندما ترى التعارضات! هذا طبيعي. خذ نفساً عميقاً واتبع خطوات الحل.
شرح علامات التعارض
يضع Git علامات على التعارضات في ملفاتك بعلامات خاصة:
<<<<<<< HEAD
$taxRate = 0.08; // الفرع الحالي (main)
=======
$taxRate = 0.10; // الفرع الوارد (feature/new-checkout)
>>>>>>> feature/new-checkout
تفصيل علامة التعارض:
<<<<<<< HEAD
↓ إصدار فرعك الحالي (ما لديك الآن)
=======
↓ فاصل بين الإصدارات
>>>>>>> branch-name
↓ إصدار الفرع الوارد (ما تقوم بدمجه)
حل التعارضات يدوياً
عملية حل التعارض خطوة بخطوة:
الخطوة 1: تحديد الملفات المتعارضة
$ git status
Unmerged paths:
both modified: app/Http/Controllers/CheckoutController.php
both modified: resources/views/checkout.blade.php
الخطوة 2: افتح كل ملف وابحث عن علامات التعارض
ابحث عن علامات <<<<<<< و ======= و >>>>>>>
الخطوة 3: قرر أي التغييرات يجب الاحتفاظ بها
الخيارات:
أ) احتفظ بإصدار الفرع الحالي (HEAD)
ب) احتفظ بإصدار الفرع الوارد
ج) احتفظ بكلا التغييرين
د) اكتب كوداً جديداً يجمع بين الاثنين
الخطوة 4: احذف علامات التعارض
احذف أسطر <<<<<<< و ======= و >>>>>>>
احتفظ فقط بالكود النهائي الذي تريده
الخطوة 5: ضع علامة كمحلول
$ git add app/Http/Controllers/CheckoutController.php
الخطوة 6: أكمل الدمج
$ git commit -m "Merge feature/new-checkout, resolve tax rate conflict"
أمثلة على حل التعارضات
مثال 1: اختر جانباً واحداً
// قبل (مع التعارض)
<<<<<<< HEAD
$shippingCost = 5.00;
=======
$shippingCost = 7.50;
>>>>>>> feature/update-shipping
// بعد (محلول - احتفظ بالتغيير الوارد)
$shippingCost = 7.50;
مثال 2: احتفظ بكلا التغييرين
// قبل
<<<<<<< HEAD
use App\Models\Order;
=======
use App\Models\Payment;
>>>>>>> feature/add-payment
// بعد (احتفظ بكلا الاستيرادين)
use App\Models\Order;
use App\Models\Payment;
مثال 3: ادمج المنطق
// قبل
<<<<<<< HEAD
if ($order->total > 100) {
$discount = 10;
}
=======
if ($order->isPremium()) {
$discount = 15;
}
>>>>>>> feature/premium-discount
// بعد (دمج الشروط)
if ($order->total > 100 && $order->isPremium()) {
$discount = 15;
} elseif ($order->total > 100) {
$discount = 10;
}
نصيحة احترافية: بعد الحل، اختبر كودك دائماً! يمكن أن تؤدي التعارضات إلى أخطاء خفية إذا لم يتم حلها بعناية.
استخدام محرر الدمج في VS Code
يوفر VS Code واجهة مرئية لحل التعارضات:
واجهة التعارض في VS Code:
عند فتح ملف متعارض، سترى:
┌─────────────────────────────────────────┐
│ Accept Current Change | Accept Incoming │
│ Accept Both Changes | Compare Changes │
├─────────────────────────────────────────┤
│ <<<<<<< HEAD (Current Change) │
│ $price = 100; │
│ ======= │
│ $price = 150; // (Incoming Change) │
│ >>>>>>> feature/update-pricing │
└─────────────────────────────────────────┘
خيارات النقر:
• Accept Current Change → احتفظ بإصدار HEAD
• Accept Incoming Change → احتفظ بإصدار الفرع
• Accept Both Changes → احتفظ بكليهما (واحد بعد الآخر)
• Compare Changes → مقارنة جنباً إلى جنب
نصيحة: محرر الدمج ثلاثي الاتجاهات في VS Code (متوفر في الإصدارات الحديثة) يعرض القاعدة والحالي والوارد جنباً إلى جنب لتسهيل المقارنة.
استخدام أدوات Git GUI
أدوات الدمج الشائعة:
1. GitKraken
- حل تعارضات مرئي
- واجهة السحب والإفلات
- رائع للمبتدئين
2. Sourcetree
- مجاني من Atlassian
- أداة دمج مدمجة
- يعرض سياق التعارض
3. Beyond Compare
- أداة diff/merge احترافية
- عرض دمج ثلاثي الاتجاهات
- مدفوعة ولكنها قوية
4. P4Merge (Perforce)
- أداة دمج ثلاثية الاتجاهات مجانية
- واجهة نظيفة
- متعددة المنصات
تكوين Git لاستخدام أداة خارجية:
# إعداد P4Merge كأداة دمج
git config --global merge.tool p4merge
git config --global mergetool.p4merge.path "/path/to/p4merge"
# تشغيل أداة الدمج للتعارضات
git mergetool
إلغاء الدمج
إذا كنت تريد إلغاء الدمج والبدء من جديد:
# إلغاء الدمج (قبل الالتزام)
$ git merge --abort
# العودة إلى الحالة قبل بدء الدمج
# لم يتم الالتزام بأي تغييرات، تمت استعادة دليل العمل
متى تلغي:
- تعارضات كثيرة جداً لحلها الآن
- تم دمج الفرع الخطأ
- تحتاج إلى المناقشة مع الفريق أولاً
- تريد سحب أحدث التغييرات أولاً
تحذير:
git merge --abort يعمل فقط قبل إكمال التزام الدمج. بعد الالتزام، استخدم git reset بدلاً من ذلك (ولكن كن حذراً!).
التعامل مع التعارضات أثناء إعادة الأساس
يتم حل تعارضات إعادة الأساس بشكل مماثل ولكن لها أوامر مختلفة:
$ git rebase main
CONFLICT (content): Merge conflict in app/Services/PaymentService.php
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
حل تعارض إعادة الأساس:
# 1. حل التعارضات في الملفات (نفس الدمج)
# 2. تجهيز الملفات المحلولة
$ git add app/Services/PaymentService.php
# 3. متابعة إعادة الأساس (وليس الالتزام!)
$ git rebase --continue
# 4. كرر لكل التزام متعارض
أوامر خاصة بإعادة الأساس:
git rebase --continue # الانتقال إلى الالتزام التالي
git rebase --skip # تخطي الالتزام الحالي
git rebase --abort # إلغاء إعادة الأساس
منع تعارضات الدمج
أفضل الممارسات لتقليل التعارضات:
1. تواصل مع الفريق
- ناقش من يعمل على ماذا
- تجنب تحرير نفس الملفات في وقت واحد
- استخدم Slack/Discord للتنسيق
2. اسحب بشكل متكرر
- git pull origin main يومياً
- ابقَ متزامناً مع الفرع الرئيسي
- اختلافات أصغر = تعارضات أقل
3. احتفظ بالفروع قصيرة العمر
- ادمج فروع الميزات بسرعة
- لا تدع الفروع تنجرف بعيداً جداً
- التطوير القائم على الجذع يساعد
4. قسّم التغييرات الكبيرة إلى PRs أصغر
- أسهل للدمج بشكل تدريجي
- التعارضات أصغر ومعزولة
- مراجعات أسرع
5. استخدم أدوات تنسيق الكود
- PHP CS Fixer و Prettier وما إلى ذلك
- التنسيق المتسق يقلل من التعارضات
- شغّل قبل الالتزام
6. بنية كود معيارية
- افصل المخاوف في ملفات مختلفة
- يقلل من فرصة تحرير نفس الملف
- فصل أفضل للمسؤوليات
7. أعد أساس فروع الميزات بانتظام
- حافظ على تحديث فرع الميزة مع main
- حل التعارضات بشكل تدريجي
- أسهل من تعارض كبير واحد في النهاية
git checkout feature/my-feature
git fetch origin
git rebase origin/main
نصيحة احترافية: إذا كنت تعمل على ملف يحرره شخص آخر بنشاط، ففكر في البرمجة الزوجية أو التناوب لتجنب التعارضات تماماً.
الاختبار بعد حل التعارض
تحقق دائماً من أن كودك المدمج يعمل بشكل صحيح:
قائمة التحقق من الاختبار بعد الدمج:
☐ تشغيل الاختبارات التلقائية
php artisan test
☐ التحقق من أن التطبيق لا يزال يعمل
php artisan serve
زيارة الصفحات المتأثرة في المتصفح
☐ اختبار الوظيفة المحددة التي تم تغييرها
اختبر عملية الدفع إذا قمت بدمج كود الدفع
☐ التحقق من أخطاء البناء
php artisan route:list (فحص Laravel)
php -l file.php (فحص البناء)
☐ مراجعة الكود المدمج النهائي
git diff HEAD~1
تأكد من أنه منطقي
☐ اطلب من عضو الفريق التحقق البسيط
مراجعة سريعة لحل التعارض
متقدم: استخدام git rerere
rerere = "إعادة استخدام الحل المسجل"
# تمكين rerere عالمياً
git config --global rerere.enabled true
كيف يعمل:
- يسجل Git كيف حللت التعارضات
- إذا ظهر نفس التعارض مرة أخرى، يحله Git تلقائياً
- مفيد عند إعادة الأساس أو الاختيار الانتقائي بشكل متكرر
مثال:
1. حل التعارض في file.php
2. يسجل Git حلك
3. لاحقاً، يحدث نفس التعارض أثناء إعادة الأساس
4. يطبق Git تلقائياً حلك السابق
5. أنت فقط تتحقق وتستمر
التواصل أثناء التعارضات
متى تتحدث مع الفريق:
❌ لا تحل كل شيء بصمت
- إذا كنت غير متأكد، اسأل المؤلف الأصلي
- تعارضات المنطق المعقدة تحتاج إلى نقاش
- لا تحذف كود شخص ما دون أن تسأل
✅ تواصل جيد:
"مرحباً @john، أقوم بدمج فرعي وحصلت على تعارض
في PaymentController. لقد غيرت calculateTax() لاستخدام
$rate * 1.1، لكنني غيرتها لاستخدام $rate * 1.15.
أيهما صحيح، أم يجب أن ندمجهما؟"
✅ التوثيق في رسالة الالتزام:
"دمج feature/tax-update، حل التعارض في
calculateTax() من خلال الاحتفاظ بمضاعف 1.15 كما
تم الاتفاق عليه مع @john في Slack"
تمرين عملي:
السيناريو: أنت تدمج فرع ميزة وتواجه هذا التعارض:
<<<<<<< HEAD
public function calculateDiscount($total) {
if ($total > 100) {
return $total * 0.10;
}
return 0;
}
=======
public function calculateDiscount($total, $user) {
if ($user->isPremium()) {
return $total * 0.15;
}
return 0;
}
>>>>>>> feature/premium-users
المهام:
- ما الذي تفعله النسختان بشكل مختلف؟
- كيف ستحل هذا التعارض؟
- ما الاختبارات التي ستجريها بعد الحل؟
الحل:
- الاختلافات: إصدار HEAD يمنح خصماً 10% للطلبات التي تزيد عن 100 دولار. الإصدار الوارد يمنح خصماً 15% للمستخدمين المميزين (بغض النظر عن الإجمالي).
- الحل: ادمج كلا الشرطين:
public function calculateDiscount($total, $user = null) { // المستخدمون المميزون يحصلون على خصم 15% if ($user && $user->isPremium()) { return $total * 0.15; } // المستخدمون العاديون يحصلون على 10% للطلبات التي تزيد عن 100 دولار if ($total > 100) { return $total * 0.10; } return 0; }
- الاختبارات: - اختبار مستخدم غير مميز مع طلب 50 دولاراً (توقع 0 دولار) - اختبار مستخدم غير مميز مع طلب 150 دولاراً (توقع 15 دولاراً) - اختبار مستخدم مميز مع طلب 50 دولاراً (توقع 7.50 دولار) - اختبار مستخدم مميز مع طلب 150 دولاراً (توقع 22.50 دولار)
الملخص
في هذا الدرس، تعلمت:
- تحدث تعارضات الدمج عندما لا يستطيع Git دمج التغييرات تلقائياً
- علامات التعارض (<<<<<<<, =======, >>>>>>>) تعرض الإصدارات المتنافسة
- حل التعارضات من خلال اختيار إصدار واحد، الاحتفاظ بكليهما، أو دمجهما
- استخدم محرر الدمج في VS Code أو أدوات GUI لحل التعارضات المرئية
- اختبر الكود دائماً بعد حل التعارضات
- امنع التعارضات من خلال التواصل، السحب المتكرر، والفروع قصيرة العمر
- استخدم
git merge --abortلإلغاء دمج إشكالي
التالي: في الدرس التالي، سنستكشف خطافات Git لأتمتة فحوصات الجودة وفرض المعايير!