حل التعارضات
حين يتوقف دمج أو إعادة أساس أو انتقاء أو عكس، يخبرك شريط بما توقّف وبين
ماذا — «دمج feature/x في main»، لا مجرد «تعارض».

لماذا يتعارض هذا
لماذا يتعارض هذا في الترويسة يسرد، لكل جانب، الالتزامات التي مسّت هذا الملف
منذ افترق الفرعان — أي git log --merge، الذي يشحنه git منذ الأزل ولا يعثر عليه
أحد.

العلامات تقول ما الذي يتصادم. وهذا يقول من غيّره ولماذا، وهو عادةً ما يحسم الحل فعلًا. وخلوّه من أي شيء يعني أن أيًا من الجانبين لم يلتزم بتغيير على هذا المسار بالذات — فالتصادم جاء من إعادة تسمية أو نقل.
اللوحات الثلاث
| اللوحة | هي |
|---|---|
| اليمين | جانبنا — الجانب الذي كنت عليه، معنونًا بالتزامه |
| اليسار | جانبهم — الجانب الوارد، معنونًا بالتزامه |
| الوسط | الناتج: قابل للتحرير، بأرقام أسطر، وهو ما يُحضَّر فعلًا |
واللوحات الثلاث كلها قابلة لتغيير الحجم، وتحمل ترويسة الناتج مفتاحَي عرض:
| المفتاح | ما يفعله |
|---|---|
| التفاف | يلفّ الأسطر الطويلة داخل لوحتَي A وB بدل تمريرها. أما لوحة الناتج فتُبقي كل سطر في صفّ واحد — فعلامات الجوانب فيها تعتمد على ذلك — ولذا تتمرّر دائمًا |
| مرتبط | يمرّر A وB والناتج معًا، رأسيًا وأفقيًا. وتختلف أعداد أسطرها، فيُطابَق الموضع الرأسي بالنسبة |
يبدأ الالتفاف معطَّلًا ويبدأ الارتباط مفعَّلًا، ويتذكّر كلاهما حالته.
التنقّل
فتح ملف يهبط بك على أول تعارض فيه، لا على رأس الملف. والسهمان ⌃ / ⌄ في ترويسة الناتج — أو Alt+↑ / Alt+↓ — يخطوان عبر البقية، ويمرّران اللوحات الثلاث كلها إلى كل واحد منها.
الاختيار
لكل سطر، أو لكل قطعة، أو الجانب كله دفعةً واحدة — وتستطيع أن تأخذ جانبَي قطعة معًا حين يكون الجواب «احتفظ بكليهما». ومتنقّل يمشي بك تعارضًا تعارضًا عبر ما تبقّى، فلا يمكنك أن تترك علامة وراءك سهوًا.
مساعدة الذكاء الاصطناعي
مع تفعيل الذكاء الاصطناعي، يقترح حل بالذكاء الاصطناعي دمجًا في لوحة الناتج. وهو لا يطبّق شيئًا من تلقاء نفسه أبدًا: تقرؤه أنت، وتحرّره، وتحضّره. انظر ميزات الذكاء الاصطناعي.
ملفات مشاريع Xcode
يتعارض project.pbxproj أكثر من أي ملف آخر في مستودع iOS، ونادرًا جدًا أن يكون
السبب خلافًا بين شخصين. فهو قاموس مسطّح واحد من الكائنات مفاتيحه معرّفات
ست عشرية من 24 خانة، لذا فإضافة ملف واحد تكتب أربعة مدخلات: PBXBuildFile
وPBXFileReference، وسطرًا في children المجموعة الحاوية، وسطرًا في مرحلة بناء
الهدف. شخصان يضيف كلٌّ منهما ملفًا يكتبان ثمانية مدخلات تقع على البضع سطور
نفسها. يرى Git تصادمًا؛ ولا شيء في الحقيقة يحتاج إلى حل.
عندما يكون الملف المتعارض project.pbxproj، يقرأ محرّر التعارض النسخ الثلاث
بوصفها مشاريع لا نصًّا، ويعرض الدمج حسب البنية: مطابقة الكائنات بمعرّفاتها،
وأخذ كل إضافة من الجهتين، وتوحيد مصفوفتي children وfiles، والتوقف عند ما
اختلف فعلًا. الشريط فوق الألواح يقول ماذا أضافت كل جهة، وماذا بقي لك — إن بقي
شيء.
مثل اقتراح الذكاء الاصطناعي، ينزل في لوح المخرجات ولا يجهّز شيئًا. تقرؤه قبل أن تحفظ.

ما يرفض فعله
لا يخمّن أبدًا إعدادًا حرّكتماه معًا. إن ضبطت MARKETING_VERSION على 1.1
وضبطوها على 2.0 فتلك قرار، ويُذكر في الشريط باسمه — الإعداد، وقيمتك، وقيمتهم —
بدل أن يُحسم من وراء ظهرك. والكائن الذي تعذّر حسمه يبقى على نسختك أنت تمامًا، كي
لا يصل إلى القرص دمجٌ مطبَّق نصفه.
يرفض الملف كله إذا تعذّر تحليل أي من النسخ الثلاث. ملف project.pbxproj لا
يفتحه Xcode أغلى من دمج يدوي، فما لا يستطيع قراءته بيقين يبقى تعارض نص عاديًا،
ويقول ذلك صراحة.
لا يكتشف معرّفَين صيغا لكائنين مختلفين. هذا نادر لأن Xcode يختارها عشوائيًا، لكن حين يقع فإن أخذ أي من الجهتين يُسقط ملف أحدهم بصمت، فيُبلَّغ عنه بدل أن يُدمج.
ليس merge=union
العلاج الشائع لهذا هو *.pbxproj merge=union في
.gitattributes. تجنّبه. يعمل الاتحاد ما دامت التغييرات
إضافات مستقلة، وما إن يحرّر شخصان الإعداد نفسه حتى يُخرج السطرين معًا ويُنتج ملفًا
يرفض Xcode فتحه — في اللحظة التي يقلّ فيها احتمال قراءتك للفروق بتمعّن. الدمج
البنيوي يمنحك اليسر نفسه بلا هذا العطب.
ملفات القفل
ملفات Podfile.lock وPackage.resolved وyarn.lock وأخواتها تسجّل رسمًا
بيانيًا للاعتماديات سبق لمُحلِّل ما أن حلّه. وخياطة نصف حلٍّ إلى نصف حلٍّ آخر تعطي
رسمًا لم يحلّه أحد: قد لا يُثبَّت، وإن تثبّت فسيثبّت شيئًا لم يختبره أيٌّ من
الفرعين.
لذا حين يكون الملف المتعارض ملف قفل، يذكر الشريط الأداة التي تملكه، ويعرض في موضعه أبقِ ما لدينا وأبقِ ما لديهم، ويمنحك الأمر الذي يعيد توليده بعد ذلك. اختيار جهة هنا ليس حلًّا وسطًا — إنه المنهج كلّه، وإعادة التوليد هي ما يجعله صحيحًا.

تبقى الألواح الثلاثة متاحة، فبين الحين والآخر تودّ فعلًا أن تقرأ ما تغيّر: بصمة تعرفها، أو إصدارًا كنت تتوقعه. أمّا ما يحاول هذا أن يثنيك عنه فهو تحريرها يدويًا.
تجنّبها من الأصل
يخبرك رادار التعارضات أي الفروع ستتعارض قبل أن تدمج أيًا منها.
أن تدع git يتذكر (rerere)
أعِد أساس فرع طويل العمر وستلاقي التعارض نفسه في كل مرة. وrerere — أي إعادة
استخدام الحل المسجَّل — هو جواب git: فهو يحفظ كيف سوّيت تعارضًا ويعيد تشغيل ذلك
الجواب حين يظهر التعارض المطابق مرة أخرى.
الإعدادات ← عام ← تذكّر حلول التعارضات. وهو يكتب rerere.enabled في إعداد
git العام لديك، فيتصرف سطر الأوامر بالطريقة نفسها.
وحين يكون git قد أجاب نيابةً عنك، تقول أداة الحل ذلك بدل أن تعرض شاشة فارغة تقول «لا توجد علامات تعارض»، وتعرض انسَ هذا الحل — وهو ما يُسقط الذاكرة ويعيد التعارض، كي تسوّيه على نحو مختلف.
وشيئان يستحقان المعرفة:
الحل المعاد تشغيله لا يُحضَّر ما لم تفعّل تحضير الحل المعاد تشغيله تلقائيًا. اترك ذلك معطَّلًا: فالغاية من الوقفة هي أن جوابًا محفوظًا قد يكون خاطئًا لهذا الدمج بالذات، والتحضير دون نظر هو الطريق الذي يوصله إلى التزام.
ولهذا يبقى الملف المعاد تشغيله في الملفات المتعارضة: فقد كتب git المحتوى لكن الفهرس ما زال يحمله كغير مدموج، ولا يسوّي ذلك إلا التحضير. وتحضير كما هو في أداة الحل، أو تعليم الكل كمحلول في القائمة، هو ما ينقله.
rerere لا يفهم كل تعارض. فتعارضات الإضافة/الإضافة والحذف/التعديل لا تحصل على صورة سابقة، ولذا تعود خامًا دائمًا. والعدد في الإعدادات هو ما يحمله فعلًا، وانسَ الكل يفرغه.
انظر أيضًا: رادار التعارضات · الدمج وإعادة الأساس