تخطَّ إلى المحتوى
GitTree

شروحات Git

حل تعارضات الدمج في Git: شرح merge conflict بسطر الأوامر وبمحرر ثلاثي مرئي

كيف تحل تعارضات الدمج في Git: اقرأ علامات التعارض، وحلّها بأوامر Git المعتادة أو في محرر ثلاثي مرئي، واعرف التعارض قبل أن تدمج. ومعها rerere وإلغاء الدمج بأمان والتراجع عنه.

بقلم Eslam Faisalآخر تحديث قراءة في 10 دقائق
قائمة الفرع في GitTree بخيارات الدمج وإعادة الأساس والتقديم السريع وreset مع ذكر اسمي الفرعين

لحل تعارضات الدمج في Git، افتح كل ملف يذكر Git أنه متعارض، وقرّر الشكل الصحيح للشيفرة بين العلامات <<<<<<< و======= و>>>>>>>، ثم احذف العلامات، وأضِف الملف إلى منطقة التجهيز (staging) بالأمر git add، وأنهِ العملية بالأمر git commit (أو git merge --continue). وإن أردت التراجع بدلًا من ذلك، فنفّذ git merge --abort. يشرح هذا الدليل هذه الدورة بسطر الأوامر أولًا، ثم في محرر ثلاثي مرئي، ويختم بعادات تجعل التعارض أندر والتراجع عنه سهلًا.

تستخدم الأمثلة المرئية GitTree، عميل Git المجاني الذي نطوّره لأنظمة macOS وWindows وLinux (لينكس). يشغّل GitTree نسخة Git المثبّتة على حاسوبك، فيعطي النقر وكتابة الأوامر النتيجة نفسها، وتجد الصورة الأشمل في قائمة ميزات GitTree.

ما هو تعارض الدمج (merge conflict) في Git؟

يدمج Git الملفات سطرًا بسطر اعتمادًا على ثلاث نسخ من كل ملف: الأساس (base، أي آخر إيداع يشترك فيه الفرعان)، ونسختنا (ours، أي فرعك الحالي HEAD)، ونسختهم (theirs، أي الفرع الوارد MERGE_HEAD). إذا غيّر طرف واحد فقط جزءًا من الملف أخذ Git تغييره. أما إذا غيّر الطرفان الأسطر نفسها بطريقتين مختلفتين، فيتوقف Git ويكتب النسختين في الملف ويترك القرار لك.

وقد يحدث الشيء نفسه مع git pull وgit rebase وgit cherry-pick وgit revert وgit stash pop. والحل واحد، ولا يختلف إلا الأمر الأخير. ويصف git status كل مسار متعارض هكذا:

  • both modified: غيّر الفرعان الأسطر نفسها، وهي الحالة الأشيع.
  • deleted by us / deleted by them: حذف أحد الطرفين الملف وعدّله الآخر، فإما أن تحتفظ بالملف المعدّل أو تؤكد الحذف.
  • both added: أنشأ كل فرع ملفًا مختلفًا في المسار نفسه.

كيف تحل تعارضات الدمج بسطر الأوامر

1. اعرف الملفات المتعارضة

يسرد Git التعارضات فور توقف الدمج، ويحتفظ git status بالقائمة حتى تنتهي. أما git diff --name-only --diff-filter=U فيطبع المسارات غير المدموجة وحدها.

$ git merge feature/login
Auto-merging src/auth.ts
CONFLICT (content): Merge conflict in src/auth.ts
Automatic merge failed; fix conflicts and then commit the result.

$ git status
On branch main
You have unmerged paths.

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   src/auth.ts

2. اقرأ علامات التعارض

كل ما هو خارج العلامات دُمج بلا مشكلة. الكتلة التي بعد <<<<<<< HEAD هي فرعك الحالي، والكتلة التي قبل >>>>>>> feature/login هي الفرع الوارد، ويفصل بينهما السطر =======.

export const retries = 3;
<<<<<<< HEAD
export const timeout = 45;
=======
export const timeout = 60;
>>>>>>> feature/login
export const debug = false;

كثيرًا ما لا تكفي نسختان لاتخاذ القرار. اضبط نمط التعارض zdiff3 في Git مرة واحدة، وسيعرض كل تعارض الأساس أيضًا بعد السطر |||||||:

$ git config --global merge.conflictStyle zdiff3

<<<<<<< HEAD
export const timeout = 45;
||||||| 556e564
export const timeout = 30;
=======
export const timeout = 60;
>>>>>>> feature/login

الآن ترى أن الفرع main رفع المهلة من 30 إلى 45 وأن فرع الميزة رفعها إلى 60: فقرّر القيمة التي يحتاجها المنتج، لا الفرع الذي ينتصر.

3. حرّر الملف وعلّمه كمحلول

اكتب الشيفرة التي تريدها (أحد الطرفين، أو الآخر، أو مزيجًا منهما، أو شيئًا جديدًا) واحذف كل أسطر العلامات. ودع Git يبحث عن أي بقايا قبل التجهيز، فسطر ======= منسيّ يُودَع بالخطأ سببٌ كلاسيكي لتعطّل البناء.

$ git diff --check          # lists any leftover conflict markers
$ git add src/auth.ts
$ git commit                # or: git merge --continue

وإن كانت نسخة أحد الطرفين من الملف كله هي الصحيحة، فخذها مباشرة:

$ git checkout --ours -- src/auth.ts     # keep the current branch's version
$ git checkout --theirs -- src/auth.ts   # keep the incoming version
$ git add src/auth.ts

4. أكمل العملية

بعد تجهيز كل الملفات، أكمل العملية التي توقفت: git commit (أو git merge --continue)، أو git rebase --continue، أو git cherry-pick --continue، أو git revert --continue. وقد تتوقف عملية rebase مجددًا عند إيداع لاحق، فكرّر الدورة نفسها.

أوامر Git تفيدك ما دام التعارض مفتوحًا
الأمرماذا يعرض
git diffفرق مجمّع (combined diff) لكل ملف متعارض مقابل الطرفين
git log --merge -p <path>إيداعات كل طرف التي لمست الملف، مع فروقها
git show :1:<path>نسخة الأساس من الملف
git show :2:<path>, git show :3:<path>نسختك (HEAD) والنسخة الواردة (MERGE_HEAD)
git mergetoolيفتح أداة الدمج المضبوطة في Git، ملفًا متعارضًا تلو الآخر
الطرفية المدمجة أسفل مخطط الإيداعات في GitTree، تشغّل الصدفة (shell) الخاصة بك بجوار العرض المرئي.
الطرفية المدمجة أسفل مخطط الإيداعات في GitTree، تشغّل الصدفة (shell) الخاصة بك بجوار العرض المرئي.

حل تعارضات الدمج بواجهة رسومية: محرر ثلاثي لتعارضات Git

تكفي العلامات لتصادم من سطرين. أما مع عشرة تعارضات أو أكثر في ملف طويل، فأداة حل التعارضات الرسومية أسرع بكثير وأقل عرضة للخطأ. والأداة الجيدة لحل merge conflict تعرض النسخ الثلاث معًا، وتتيح لك القرار تعارضًا تعارضًا، وترفض الحفظ ما دامت العلامات باقية.

في GitTree، حين يتوقف الدمج يظهر شريط بعدد التعارضات وقائمة بالملفات المتعارضة. افتح أحدها فيملأ المحرر النافذة كلها: الفرع الحالي والفرع الوارد جنبًا إلى جنب في الأعلى، وعلى كل منهما اسم فرعه، والنتيجة (Output)، أي الملف كما سيُحفظ، في الأسفل.

  • لكل تعارض: خذ الطرف الحالي أو الوارد أو كليهما أو لا شيء منهما، أو اختر أسطرًا مفردة من كل طرف.
  • تحرير النتيجة يدويًا لكل ما لا تغطيه الأزرار.
  • إظهار الأساس لرؤية السلف المشترك (يكتبه Git حين تكون قيمة merge.conflictStyle هي diff3 أو zdiff3).
  • التراجع عن الاختيار للرجوع خطوة بخطوة في قراراتك.
  • الملفات المحذوفة: حين يحذف أحد الطرفين ملفًا عدّله الآخر، احتفظ بملف ذلك الطرف أو أكّد الحذف.

لا يحفظ GitTree نتيجة ما زالت فيها أسطر علامات تعارض. ويستطيع أيضًا فتح الملف في أداة الدمج الخارجية التي تعتمد عليها: VS Code أو Cursor أو بيئات JetBrains أو Beyond Compare أو Meld أو KDiff3 أو P4Merge أو WinMerge أو برنامج مخصّص، أو الأداة التي يحددها إعداد merge.tool في Git. وإذا أُغلقت الأداة دون حل الملف يبقى متعارضًا.

كيف تحل تعارض دمج في GitTree خطوة بخطوة

  1. ابدأ الدمج

    افتح قائمة الفرع الذي تريد جلب تغييراته وادمجه في فرعك الحالي، أو اسحب فرعًا وأفلته على فرع آخر. تذكر القائمة اسمي الفرعين، فلا تضطر إلى تخمين اتجاه الدمج.

  2. اقرأ معاينة التعارض

    قبل أن يتغير أي شيء، يخبرك مربع حوار الدمج أن الفرعين يندمجان بلا تعارضات، أو يسرد الملفات التي سيتوقف عندها. اختر طريقة تسجيل الدمج ثم أكّد.

  3. افتح الملفات المتعارضة

    إذا توقف الدمج، يعدّ الشريط التعارضات وتعرض قائمة الملفات كل ملف متعارض. افتح أولها في المحرر الثلاثي.

  4. احسم كل تعارض

    خذ الطرف الحالي أو الوارد أو كليهما أو لا شيء منهما، أو اختر أسطرًا مفردة، أو حرّر النتيجة. واستخدم «إظهار الأساس» لترى الشيفرة قبل أن يغيّرها أي من الفرعين.

  5. احفظ الملف وعلّمه كمحلول

    اختر «حفظ وتعليم كمحلول» (Save and Mark Resolved). يكتب GitTree الملف ويجهّزه، ثم تنتقل إلى الملف التالي. ويرفض الحفظ ما دامت أسطر علامات التعارض باقية.

  6. أودِع الدمج

    حين تُحل كل الملفات، اختر «إيداع ودمج» (Commit and Merge)، أو «متابعة إعادة التأسيس» في حالة rebase. وإن غيّرت رأيك لاحقًا، فأعِد تعيين الفرع من قائمة الإيداع إلى الإيداع الذي كان عليه قبل الدمج، ويحفظ GitTree لقطة قبل إعادة التعيين، فيستطيع زر التراجع عكسها.

مخطط الإيداعات في GitTree بالسمة الفاتحة مع تحديد إيداع دمج، حيث يلتقي مسارا فرعين.
مخطط الإيداعات في GitTree بالسمة الفاتحة مع تحديد إيداع دمج، حيث يلتقي مسارا فرعين.

توقّع تعارضات الدمج قبل أن تدمج

منذ الإصدار 2.38 من Git، ينفّذ الأمر git merge-tree --write-tree دمجًا في الذاكرة دون أن يقرأ مجلد العمل أو الفهرس (index) أو يكتب فيهما، ودون أن ينشئ إيداعًا. ومع الخيار --name-only يسرد الملفات التي ستتعارض، ويكون رمز خروجه 1 عند وجود تعارضات و0 حين يكون الدمج نظيفًا.

$ git fetch
$ git merge-tree --write-tree --name-only HEAD origin/main
0d708adc4a836d088802870278cb1f3daf06355f
src/auth.ts

Auto-merging src/auth.ts
CONFLICT (content): Merge conflict in src/auth.ts

يُجري GitTree هذه المعاينة نيابةً عنك: يعرض مربع حوار الدمج النتيجة المتوقعة قبل أن تؤكد. وبعد كل جلب (fetch)، يفحص أيضًا فرعك مقابل فرعه المتتبَّع (upstream) والفرع الأساسي (main أو master ما لم تحدد غيره)، ويعرض تحذيرًا بجانب اسم الفرع مثل «سيتعارض مع main في ملفين». ويميّز تلميح ثانٍ الملفات التي تغيّرها وتغيّرها أيضًا فروع أخرى مجلوبة لم تُدمج بعد.

ولا يستخدم الفحصان إلا ما جلبته مسبقًا: لا يُجلب شيء ولا يُدمج ولا يتغير لحسابهما، ولا يغادر شيء حاسوبك. ويمكن إيقاف أي منهما من التفضيلات.

إعادة استخدام حلول التعارض عبر git rerere

كلمة rerere اختصار لعبارة «reuse recorded resolution»، أي إعادة استخدام الحل المسجّل. حين تفعّلها يسجّل Git كل تعارض وطريقة حلك له. وإذا عاد التعارض نفسه تمامًا، كما عند تكرار rebase لفرع طويل العمر أو إعادة دمج سبق أن أجريته، يكتب Git حلك السابق في الملف.

$ git config --global rerere.enabled true
$ git rerere forget src/auth.ts   # drop a recorded resolution that was wrong

ويترك Git النتيجة المُعاد استخدامها خارج منطقة التجهيز كي تراجعها بنفسك، ما لم تضبط rerere.autoUpdate. وفي GitTree تضم إعدادات المستودع مفتاحَي إعادة استخدام الحلول المسجَّلة (rerere) وتجهيز الحلول المُعاد استخدامها لخيارَي Git هذين، ويميّز عرض التعارض الحل المُعاد استخدامه، ويحذف خيار نسيان الحلّ تسجيلًا خاطئًا.

كيف تلغي الدمج بأمان

إذا كان التعارض أكبر من وقتك المتاح، فتراجع بأمر الإلغاء المناسب:

$ git merge --abort
$ git rebase --abort
$ git cherry-pick --abort
$ git revert --abort

يحذّر توثيق Git نفسه من أن git merge --abort لا يستطيع دائمًا استعادة التغييرات غير المودَعة التي كانت لديك عند بدء الدمج. فأودِع تغييراتك أو خزّنها مؤقتًا (stash) قبل الدمج، ولن يبقى أمام الإلغاء إلا عمل مودَع يستعيده.

في GitTree، يشرح زر إيقاف الدمج (Abort Merge)، ومثله إيقاف rebase أو cherry-pick أو revert، قبل التنفيذ أن Git سيعيد الفرع والفهرس ونسخة العمل كما كانت قبل العملية، وأن الحلول التي أنجزتها ستُهمل. ويحفظ GitTree كل ما يُسقطه الإلغاء، مجهّزًا كان أو غير مجهّز، في لقطات الاسترداد، فيستطيع التراجع إعادته.

كيف تتراجع عن دمج بعد إيداعه

يتوقف ما تفعله على ما إذا كنت قد دفعت (push) الدمج أم لا:

# Not pushed yet, right after the merge:
$ git reset --hard ORIG_HEAD     # also discards uncommitted changes
$ git reset --merge ORIG_HEAD    # keeps uncommitted changes

# Already pushed:
$ git revert -m 1 <merge-commit> # a new commit that reverses it

يحفظ الدمج الرأس السابق لفرعك في ORIG_HEAD، فإعادة الضبط إليه تزيل الدمج، ما لم يحرّك ORIG_HEAD أمرُ reset أو rebase لاحق. وإذا وصل الدمج إلى الآخرين فاستخدم revert بدلًا من ذلك. وعكس الدمج يخبر Git أنك لا تريد تلك التغييرات أبدًا: فدمج الفرع نفسه لاحقًا لن يعيدها ما لم تعكس عملية العكس نفسها.

في GitTree، حدّد في المخطط الأب الأول لإيداع الدمج (أي الإيداع الذي كان عليه فرعك قبله)، واختر من قائمة الإيداع إعادة تعيين ناعمة (soft) أو مختلطة (mixed) أو كاملة (hard) إليه، وبجانب كل منها ما تحتفظ به. ويحفظ GitTree لقطة قبل تنفيذ تسعة عشر نوعًا من العمليات، منها إعادة التعيين، فيستطيع زر التراجع عكسها، ويستعيد عرض سجل المراجع (reflog) الحالات الأقدم. ويتناول دليل التراجع عن reset وrebase والإيداع الاسترداد بالتفصيل.

كيف تتجنب معظم تعارضات الدمج

  • اجعل الفروع صغيرة وقصيرة العمر. كلما طال افتراق فرعين زادت الأسطر التي يلمسها كلاهما.
  • ادمج الفرع الأساسي في فرعك كثيرًا، لا مرة واحدة في النهاية.
  • افصل التنسيق عن المنطق. اتفقوا على أداة تنسيق واحدة، وأدخلوا إعادة تنسيق الملفات الكاملة وحدها.
  • أعد توليد ملفات القفل (lockfile) بدل دمجها يدويًا. خذ أي طرف، ثم شغّل مدير الحزم مجددًا.
  • فعّل zdiff3 وrerere مرة واحدة. أمران فقط وفائدة دائمة.
  • أودِع أو خزّن مؤقتًا قبل الدمج، ليكون كل إلغاء وكل تراجع دقيقًا.

ما زلت توازن بين الطرفية والواجهة الرسومية؟ يقارن مقال واجهة Git الرسومية أم سطر الأوامر بينهما بإنصاف.

GitTree مجاني اليوم، بما في ذلك المستودعات الخاصة، ويحتاج إلى حساب مجاني وإلى Git بالإصدار 2.39 أو أحدث. حمّله لأجهزة ماك (Mac) أو ويندوز (Windows) أو لينكس (Linux)، أو اعثر على كل ملفات التثبيت في صفحة تحميل GitTree. وعلى أوبونتو (Ubuntu) 22.04، حدّث Git أولًا. وإن علقت عند تعارض عنيد، اسأل دعم GitTree.

الأسئلة الشائعة

ما سبب حدوث تعارض الدمج في Git؟

يحدث تعارض الدمج حين يغيّر فرعان الأسطر نفسها في الملف نفسه بطريقتين مختلفتين، أو حين يحذف أحد الفرعين ملفًا عدّله الآخر. يجمع Git تلقائيًا التغييرات التي تمس أجزاء مختلفة من الملف، لكنه لا يستطيع الاختيار بين تعديلين على الأسطر نفسها، فيتوقف ويطلب منك القرار.

هل إلغاء الدمج الذي فيه تعارضات آمن؟

نعم، ما دمت قد أودعت عملك أو خزّنته مؤقتًا قبل البدء. يعيد git merge --abort الفرع والفهرس ومجلد العمل إلى ما كانت عليه قبل الدمج، ويتخلص من الحلول التي أنجزتها. أما التعديلات غير المودَعة التي سبقت الدمج فقد لا تعود كما كانت، ولهذا يهم الإيداع أو التخزين المؤقت أولًا. ويحفظ GitTree لقطة لكل ما يُسقطه الإلغاء ليستعيده التراجع.

ماذا تعني ours وtheirs في تعارضات Git؟

أثناء الدمج، تعني ours الفرع الذي تعمل عليه (HEAD)، وتعني theirs الفرع الوارد الذي تدمجه في فرعك. وأثناء إعادة التأسيس (rebase) تنقلب الأدوار: تصبح ours الفرع الذي تنقل عملك فوقه، وتصبح theirs إيداعك أنت الذي يُعاد تطبيقه. ولهذا قد يفاجئك الأمر git checkout --ours في منتصف rebase. ويضع GitTree على كل طرف اسم فرعه ودوره.

كيف أتراجع عن دمج أودعته بالفعل؟

إن لم تدفعه بعد، فنفّذ git reset --hard ORIG_HEAD مباشرة بعد الدمج، أو git reset --merge ORIG_HEAD للاحتفاظ بتغييراتك غير المودَعة. وإن كان الدمج قد وصل إلى فرع مشترك، فنفّذ git revert -m 1 <merge-commit> لعكسه دون إعادة كتابة التاريخ. وفي GitTree، أعِد تعيين الفرع من قائمة الإيداع إلى الأب الأول لإيداع الدمج، ويحفظ GitTree لقطة قبل إعادة التعيين، فيستطيع زر التراجع عكسها إن غيّرت رأيك.

ما أفضل أداة لحل تعارضات الدمج في Git؟

ابحث عن أداة لحل تعارضات الدمج تعرض ثلاث نسخ معًا (نسختك والنسخة الواردة والأساس المشترك)، وتتيح لك القرار تعارضًا تعارضًا، وترفض حفظ ملف ما زالت فيه علامات تعارض. يتضمن GitTree محررًا ثلاثيًا بهذه المواصفات، ويستطيع أيضًا تسليم الملف إلى أداة الدمج التي تستخدمها أصلًا. وفي كل الأحوال، فعّل نمط zdiff3.

هل يستطيع Git حل التعارض نفسه تلقائيًا في المرة القادمة؟

نعم، عبر rerere. نفّذ git config --global rerere.enabled true فيسجّل Git طريقة حلك لكل تعارض. وحين يظهر التعارض نفسه تمامًا مرة أخرى، كما عند تكرار rebase، يكتب Git حلك السابق في الملف. وتبقى المراجعة والتجهيز عليك ما لم تفعّل rerere.autoUpdate. ويبيّن GitTree متى أُعيد استخدام حل.

المصادر

تحققنا من المعلومات عن المنتجات الأخرى من صفحاتها الرسمية بتاريخ 6 أكتوبر 2026. الأسعار والميزات تتغير، وصفحات الشركات هي المرجع.