الدمج أو الإدماج — هو عملية دمج فرعين في Git، وجمع التغييرات من فرع إلى آخر. في التطوير الحديث، الدمج هو الطريقة القياسية لدمج فرع الميزة في الفرع الرئيسي للمشروع. وفقاً لتقرير GitHub Octoverse 2024، يتم تنفيذ أكثر من 15 مليون عملية دمج يومياً. الدمج هو آلية رئيسية للعمل الجماعي، تتيح جمع جهود عدة مطورين في منتج واحد.
النقاط الرئيسية
الدمج في Git هو عملية دمج تاريخين أو أكثر من تاريخ التطوير في تاريخ واحد. عندما يدمج المطور فرعاً، يجد Git تلقائياً السلف المشترك (base commit) وينشئ commit دمج جديد يتضمن التغييرات من كلا الفرعين. Three-way merge هو الخوارزمية القياسية التي تقارن ثلاث حالات: السلف المشترك، الفرع الأول، والفرع الثاني.
تبدأ عملية الدمج بأمر git merge. يحدد Git نقطة تباعد الفروع ويطبق التغييرات بالتسلسل من الفرع المصدر على الفرع الهدف. إذا لم تتعارض التغييرات، يقوم Git بتنفيذ fast-forward أو إنشاء merge commit حسب الإعدادات. Fast-forward هو سيناريو حيث يتحرك الفرع الهدف ببساطة إلى commits الفرع المصدر.
# التبديل إلى الفرع الهدف والدمج
git checkout main
git merge feature/payment-module
# الدمج مع no-fast-forward صريح
git merge --no-ff feature/payment-module
# إلغاء الدمج إذا كانت النزاعات معقدة جداً
git merge --abort
العلامة --no-ff (no fast-forward) تفرض إنشاء merge commit حتى عندما يكون fast-forward ممكناً. هذا يحافظ على معلومات أن التغييرات تمت في فرع منفصل. تفضل العديد من الفرق هذا النهج للحفاظ على تاريخ التفرع بشكل صريح.
لدى Git ثلاث استراتيجيات رئيسية لدمج الفروع، كل منها مناسبة لسيناريو محدد. يعتمد اختيار الاستراتيجية على ثقافة الفريق ومتطلبات نظافة تاريخ المشروع.
| الاستراتيجية | النتيجة | متى تستخدم |
|---|---|---|
| Standard merge | merge commit + التاريخ الكامل | الفرق التي تقدر التاريخ الكامل |
| Squash merge | commit واحد، التاريخ مضغوط | فروع الميزات ذات commits صغيرة متعددة |
| Rebase merge | تاريخ خطي، بدون merge commit | فروع الميزات الشخصية، قبل إنشاء PR |
Standard merge ينشئ merge commit مع أبوين اثنين. يتم الحفاظ على التاريخ الكامل، لكن رسم بياني التفرع يصبح أكثر تعقيداً. Squash merge يجمع كل commits فرع الميزة في commit واحد ويطبقه على الفرع الهدف — يصبح التاريخ خطياً ونظيفاً، لكن تُفقد معلومات المراحل الوسيطة.
Rebase، على الرغم من أنه ليس دمجاً كاملاً، يحقق نفس النتيجة — يتم نقل تغييرات فرع واحد فوق آخر. الفرق هو أن التاريخ يُعاد كتابته: يتم إعادة إنشاء commits فرع الميزة فوق آخر commit للفرع الهدف. هذا يعطي تاريخاً خطياً تماماً، لكنه يتطلب force push عند الرفع.
ينشأ نزاع الدمج عندما يتم تغيير نفس الأسطر من ملف في فرعين. لا يستطيع Git تحديد الإصدار الذي يجب الاحتفاظ به تلقائياً ويحتاج إلى تدخل المطور. تظهر النزاعات في الملفات باستخدام علامات خاصة: <<<<<<< و ======= و >>>>>>>.
تتضمن عملية حل النزاع عدة خطوات. أولاً، يفتح المطور الملف المتنازع عليه ويختار يدوياً التغييرات المطلوبة. من المهم ليس فقط اختيار إحدى النسختين، بل فهم منطق كلا التغييرين واتخاذ قرار صحيح. بعد تحرير الملف، تُحذف علامات النزاع وتُضاف التغييرات إلى staging area عبر git add.
# عرض قائمة الملفات المتعارضة
git status
# بدء mergetool (مثل VS Code، IntelliJ)
git mergetool
# بعد حل جميع النزاعات
git add .
git merge --continue
# أو إلغاء الدمج بالكامل
git merge --abort
استخدام أدوات الدمج المرئية يسرع بشكل كبير حل النزاعات. توفر VS Code و IntelliJ IDEA و GitKraken واجهات بثلاث لوحات: الفرع الحالي، الفرع الوارد، والنتيجة. أداة git mergetool تفتح تلقائياً المحرر المُهيأ لكل ملف متعارض.
أفضل طريقة لتجنب النزاعات المعقدة هي المزامنة المنتظمة لفرع الميزة مع الفرع الرئيسي. إذا قام المطور بدمج main في فرعه مرة واحدة يومياً، ستكون النزاعات صغيرة وسهلة الحل. تراكم التغييرات لمدة أسبوع يضمن نزاعات معقدة مع خطر كبير للأخطاء.
Rebase و merge هما طريقتان لدمج التغييرات، وغالباً ما يثير الاختيار بينهما جدلاً في الفرق. Rebase ينقل commits من فرع فوق آخر، مع إعادة كتابة التاريخ. Merge ينشئ commit دمج جديد، محافظاً على تاريخ التفرع. لكل نهج مزاياه وقيوده.
Rebase مناسب عندما يعمل المطور على فرع ميزة محلي ويريد تاريخاً خطياً نظيفاً قبل إنشاء Pull Request. بعد rebase، يتم ترتيب جميع commits بالتسلسل دون commits دمج غير ضرورية. ومع ذلك، يتطلب rebase force push ولا ينطبق على الفروع التي يعمل عليها عدة أشخاص في وقت واحد.
القاعدة الذهبية في Git: لا تستخدم rebase على commits تم دفعها بالفعل إلى المستودع المشترك. هذا يضمن بقاء التاريخ في الفرع المشترك دون تغيير وعدم مواجهة المطورين الآخرين لـ commits مكررة أو مفقودة. لدمج فرع الميزة في الفرع الرئيسي، استخدم merge عبر Pull Request.
عملية الدمج الصحيحة هي أساس التطوير المستقر. في العمل الجماعي الحديث، يتم الدمج ليس عبر وحدة التحكم، بل عبر Pull Request على GitHub أو Merge Request في GitLab. يمر PR بمراجعة الكود، والفحوصات التلقائية CI، وبعد ذلك فقط يتم دمجه في الفرع الرئيسي.
الممارسة الأولى — الدمج فقط بعد اجتياز جميع الفحوصات. يجب أن يقوم خط أنابيب CI ببناء المشروع، وتشغيل الاختبارات، والتحقق من جودة الكود. إذا فشل فحص واحد على الأقل، يتم حظر الدمج. المنصات الحديثة (GitHub، GitLab) لديها حماية مدمجة: branch protection rules تمنع الدمج تلقائياً عند فشل CI.
الممارسة الثانية — عدم دمج الكود المعطل أبداً. قبل الدمج، يجب على المطور التأكد من أن تغييراته لا تعطل البناء ولا تسبب تراجعاً في الوظائف الحالية. لهذا توجد اختبارات تلقائية ومراجعة الكود.
الممارسة الثالثة — تنظيف فروع الميزات بعد الدمج. يجب حذف الفرع الذي تم دمجه بالفعل. هذا يمنع الارتباك والفوضى في المستودع. يعرض GitHub تلقائياً حذف الفرع بعد دمج PR، ويمكن تكوين إعدادات المستودع للحذف التلقائي.
الأسئلة الشائعة
الدمج (merge) هو دمج فرعين من Git في فرع واحد. يتم نقل التغييرات من فرع إلى آخر عبر الدمج ثلاثي الاتجاهات (three-way merge). يتم تسجيل النتيجة في commit دمج جديد له commitان أبوان. Merge commit يحتفظ بمعلومات حول أي الفروع تم دمجها.
Merge ينشئ commit دمج جديد، محافظاً على تاريخ التفرع. Rebase يعيد كتابة التاريخ عن طريق نقل commits فوق فرع آخر دون إنشاء merge commit. Rebase يعطي تاريخاً خطياً لكنه يتطلب force push. Merge أكثر أماناً للفروع المشتركة، rebase أفضل للفروع الشخصية.
افتح الملف المتعارض، ابحث عن العلامات <<<<<<< و ======= و >>>>>>>، اختر التغييرات المطلوبة واحذف العلامات. أضف الملف عبر git add وأكمل الدمج عبر git merge --continue. استخدم git mergetool للحل المرئي في VS Code أو IntelliJ IDEA.
Pull Request (أو Merge Request) إلزامي عند دمج فرع الميزة في الفرع الرئيسي للمشروع. يمر PR بمراجعة كود الزملاء والفحوصات التلقائية CI. هذا هو معيار التطوير الحديث. الدفع المباشر إلى الفرع الرئيسي محظور في معظم المشاريع.
Squash merge يجمع كل commits فرع الميزة في commit واحد قبل الدمج. هذا يعطي تاريخاً نظيفاً للفرع الرئيسي بدون commits وسيطة مسودة. استخدم squash merge عندما يحتوي فرع الميزة على العديد من commits الخدمية (wip, fixes) ولا تحتاج إلى الاحتفاظ بجميع الخطوات الوسيطة في التاريخ.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.