
فروع Git وعمليات الدمج: دليل عملي شامل
تتيح الفروع للمطورين فصل مسار عمل عن آخر. يمكن تطوير ميزة جديدة من دون تغيير الشيفرة المستقرة مباشرة، ويمكن إعداد إصلاح عاجل بصورة مستقلة، كما يمكن التخلي عن التجارب من دون الإضرار بمسار التطوير الرئيسي.
يجعل Git إنشاء الفروع عملية خفيفة، لأن الفرع ليس نسخة كاملة مكررة من مجلد المشروع، بل هو مرجع متحرك يشير إلى التزام. ومع إنشاء التزامات جديدة، يتحرك مرجع الفرع الحالي إلى الأمام.
يشرح هذا الدليل نموذج الفروع أولًا، ثم يحوله إلى مسار عمل عملي متكامل. ستنشئ الفروع وتنتقل بينها، وتفحص تباعدها، وتنفذ عمليات دمج من نوع fast-forward وthree-way merge، وتحل التعارضات، وتلغي عمليات الدمج غير الآمنة، وتعيد تسمية الفروع وتحذفها، وتتحقق من دمج العمل قبل تنظيف الفروع.
ما الذي ستتعلمه؟
ما الذي يمثله فرع Git فعليًا.
كيف ترتبط الفروع والالتزامات و
HEADببعضها.كيفية عرض الفروع وإنشائها والانتقال بينها وإعادة تسميتها ونسخها وحذفها.
كيفية بدء فرع من التزام أو فرع آخر.
كيفية مقارنة الفروع قبل دمجها.
الفرق بين fast-forward وthree-way merge.
كيف تؤثر الخيارات
--ff-onlyو--no-ffو--no-commitفي عملية الدمج.كيفية فهم تعارضات الدمج وحلها.
كيفية إلغاء عملية الدمج أو متابعتها بأمان.
كيفية فحص الفروع المدمجة وغير المدمجة قبل حذفها.
الفرق بين الفروع المحلية وفروع remote-tracking.
أخطاء الفروع الشائعة وقواعد العمل العملية داخل الفرق.
ما فرع Git؟
الفرع مرجع مسمى يشير إلى التزام، ويمثل الطرف الحالي لمسار تطوير معين.
افترض أن المستودع يحتوي على ثلاثة التزامات:
A---B---C main
يشير الفرع المسمى main إلى الالتزام C.
عندما تنشئ فرعًا باسم feature/search، ينشئ Git مرجعًا آخر عند الالتزام نفسه:
A---B---C main
feature/search
لم تُنسخ أي ملفات ولم يُنشأ التزام جديد. يشير الاسمان إلى الالتزام نفسه.
بعد الانتقال إلى feature/search وإنشاء التزام، يتحرك هذا الفرع فقط:
A---B---C main
\
D feature/search
إذًا الفرع تسمية متحركة، وليس نسخة احتياطية من مجلد.
الفروع مراجع خفيفة
يُعد إنشاء فرع في Git عملية منخفضة التكلفة، لأن Git ينشئ مرجعًا جديدًا بصورة أساسية، بينما تكون لقطات المشروع والالتزامات موجودة بالفعل في قاعدة بيانات الكائنات.
يجعل ذلك الفروع قصيرة العمر عملية للاستخدام في:
الميزات.
إصلاح الأخطاء.
التجارب.
تحديثات التوثيق.
التحضير للإصدارات.
الإصلاحات العاجلة.
إعادة الهيكلة.
كون الفروع خفيفة لا يعني استخدامها بإهمال. فقد يصبح المستودع صعب الفهم عندما تكون الفروع سيئة التسمية، أو تبقى إلى أجل غير محدد، أو تُدمج من دون مراجعة.
ما HEAD؟
يحدد HEAD الموقع الذي جرى checkout له حاليًا داخل شجرة العمل.
في مسار العمل المعتاد، يشير HEAD إلى فرع، ويشير الفرع إلى التزام:
HEAD -> feature/search -> D
عند إنشاء التزام جديد، يتحرك الفرع الحالي إلى الالتزام الجديد:
قبل:
HEAD -> feature/search -> D
بعد إنشاء الالتزام E:
HEAD -> feature/search -> E
لا تتحرك الفروع الأخرى تلقائيًا.
إنشاء مستودع للتدرب
تستخدم الأمثلة مشروعًا صغيرًا باسم Task Notes.
على macOS أو Linux أو Git Bash
mkdir git-branches-demo
cd git-branches-demo
git init -b main
cat > app.txt <<'EOF'
Task Notes
التخزين: محلي
EOF
cat > README.md <<'EOF'
# Task Notes
مشروع صغير للتدرب على فروع Git وعمليات الدمج.
EOF
git add app.txt README.md
git commit -m "إنشاء مشروع ملاحظات المهام"
على PowerShell
New-Item -ItemType Directory git-branches-demo
Set-Location git-branches-demo
git init -b main
@"
Task Notes
التخزين: محلي
"@ | Set-Content app.txt
@"
# Task Notes
مشروع صغير للتدرب على فروع Git وعمليات الدمج.
"@ | Set-Content README.md
git add app.txt, README.md
git commit -m "إنشاء مشروع ملاحظات المهام"
تحقق من المستودع الأولي:
git status
git branch --show-current
git log --oneline --decorate
عرض الفروع المحلية
نفّذ:
git branch
يُشار إلى الفرع الحالي بعلامة النجمة:
* main
اعرض أحدث التزام في كل فرع:
git branch -v
اعرض معلومات إضافية عن العلاقات، بما في ذلك حالة upstream عند ضبطها:
git branch -vv
اعرض الفروع المحلية وفروع remote-tracking:
git branch -a
اعرض فروع remote-tracking فقط:
git branch -r
ستبقى قوائم الفروع البعيدة فارغة في مستودع التدريب المحلي إلى أن تضيف remote وتنفذ fetch.
إنشاء فرع من دون الانتقال إليه
أنشئ فرعًا عند الالتزام الحالي:
git branch feature/search
اعرض الفروع:
git branch
البنية المتوقعة:
feature/search
* main
الفرع الجديد موجود، لكن HEAD ما يزال يشير إلى main.
تحقق من الرسم:
git log --oneline --decorate --all
يجب أن يظهر اسما الفرعين بجوار الالتزام نفسه.
الانتقال إلى فرع موجود
استخدم:
git switch feature/search
تحقق:
git branch --show-current
git status
يحدّث الانتقال شجرة العمل وindex لتطابق الفرع المحدد، ما دام ذلك لا يكتب فوق تغييرات محلية غير آمنة.
أمر checkout الأقدم
سترى غالبًا:
git checkout feature/search
يمكن لـ git checkout الانتقال بين الفروع واستعادة المسارات وفصل HEAD. ويوفر Git الحديث git switch للتنقل بين الفروع وgit restore لاستعادة الملفات، ما يجعل الهدف أوضح.
قد تستمر المشاريع والسكربتات القديمة في استخدام checkout، لذلك يجب على المطورين معرفة الصيغتين.
إنشاء فرع والانتقال إليه بأمر واحد
أنشئ فرعًا جديدًا وانتقل إليه:
git switch -c feature/export
الصيغة القديمة المكافئة:
git checkout -b feature/export
ينشئ الخيار -c الفرع فقط عندما لا يكون موجودًا.
أما صيغة الإنشاء الإجباري فهي:
git switch -C feature/export
يمكن لـ -C إعادة تعيين فرع موجود إلى نقطة بداية جديدة. استخدمه بحذر، لأن نقل مرجع فرع موجود قد يجعل بعض الالتزامات غير قابلة للوصول من ذلك الفرع.
إنشاء فرع من نقطة بداية محددة
يبدأ الفرع الجديد عادة من HEAD الحالي. ويمكنك اختيار نقطة بداية مختلفة:
git switch -c hotfix/login main
أنشئ فرعًا من tag:
git switch -c support/v1.0 v1.0.0
أنشئ فرعًا من التزام:
git switch -c investigation 7a93c2e
أنشئه من دون الانتقال إليه:
git branch investigation 7a93c2e
تحدد نقطة البداية أول التزام يمكن الوصول إليه من طرف الفرع الجديد.
إنشاء أول التزام للميزة
انتقل إلى فرع البحث عند الحاجة:
git switch feature/search
أنشئ ملف الميزة.
على macOS أو Linux أو Git Bash
cat > search.txt <<'EOF'
البحث عن المهام باستخدام كلمة مفتاحية.
EOF
git add search.txt
git commit -m "إضافة وصف البحث بالكلمة المفتاحية"
على PowerShell
"البحث عن المهام باستخدام كلمة مفتاحية." | Set-Content search.txt
git add search.txt
git commit -m "إضافة وصف البحث بالكلمة المفتاحية"
افحص الرسم:
git log --oneline --graph --decorate --all
مفاهيميًا:
A main
\
B feature/search
تحرك فرع الميزة إلى الأمام، بينما بقي main عند الالتزام الأصلي.
الانتقال مع وجود تغييرات غير مسجلة
قد يسمح Git بالانتقال إلى فرع آخر عندما يمكن الحفاظ على التغييرات المحلية بأمان، ويرفض الانتقال عندما يؤدي إلى الكتابة فوق التغييرات أو إنشاء حالة غير آمنة.
قبل الانتقال بين الفروع، نفّذ:
git status
ثم اختر أحد الإجراءات التالية:
تسجيل تغيير منطقي مكتمل.
وضع العمل غير المكتمل داخل stash عند الحاجة.
التخلص من التغييرات غير المطلوبة بصورة مقصودة.
البقاء على الفرع الحالي إلى أن يُنظم العمل.
لا تستخدم خيارات force لمجرد تجاوز التحذير. فقد يكون Git يحمي عملًا غير مسجل.
العودة إلى الفرع السابق
استخدم:
git switch -
ينقلك هذا إلى الفرع أو الموقع الذي جرى checkout له سابقًا.
تُمثل صيغة revision المكافئة داخليًا غالبًا بواسطة:
@{-1}
يفيد هذا الاختصار عند الانتقال المتكرر بين فرع ميزة وفرع الدمج الخاص به.
Detached HEAD
عندما تنتقل مباشرة إلى التزام بدل فرع، قد يصبح HEAD في وضع detached:
git switch --detach HEAD~1
في هذه الحالة يشير HEAD مباشرة إلى التزام:
HEAD -> B
لا يتحرك أي اسم فرع تلقائيًا عند إنشاء التزام جديد.
يفيد وضع detached في:
فحص إصدار قديم.
تشغيل اختبارات على التزام تاريخي.
استكشاف نقطة أخرى في السجل بصورة مؤقتة.
إذا أنشأت التزامات مهمة أثناء هذا الوضع، فاحفظها بإنشاء فرع:
git switch -c recovery/experiment
للعودة من دون حفظ التزامات detached الجديدة:
git switch main
لا يُعد detached HEAD خطأ بحد ذاته، لكن الالتزامات المنشأة في هذا الوضع قد تصبح صعبة العثور بعد المغادرة ما لم يحفظها فرع أو tag.
إعادة تسمية فرع
أعد تسمية الفرع الحالي:
git branch -m feature/task-search
أعد تسمية فرع محلي آخر:
git branch -m old-name new-name
استخدم إعادة التسمية الإجبارية عندما يكون الاسم الهدف موجودًا:
git branch -M new-name
استخدم force بحذر، لأنه قد يستبدل مرجع الفرع الهدف.
لا تؤدي إعادة التسمية المحلية إلى إعادة تسمية الفرع تلقائيًا على منصة الاستضافة البعيدة. يتطلب نشر الفرع البعيد وتنظيفه عمليات push منفصلة، وسيجري تناولها في مقالات التعاون مع remote.
نسخ مرجع فرع
يمكن لـ Git نسخ فرع وإعداداته:
git branch -c feature/task-search backup/task-search
انسخه إجباريًا باستخدام:
git branch -C feature/task-search backup/task-search
نسخ اسم فرع أقل شيوعًا من إنشاء فرع عادي عند الالتزام نفسه، لكنه يفيد عندما تريد نسخ إعدادات الفرع وسلوك reflog المرتبط به عمدًا.
مقارنة الفروع قبل الدمج
لا تدمج فرعًا لمجرد أن اسمه يبدو مكتملًا. افحص التزاماته ومحتواه.
عرض الالتزامات الموجودة في فرع الميزة فقط
git log --oneline main..feature/task-search
عرض الالتزامات الموجودة في main فقط
git log --oneline feature/task-search..main
عرض الالتزامات الفريدة في الطرفين
git log --left-right --graph --oneline main...feature/task-search
مقارنة تغييرات الميزة منذ التباعد
git diff --stat main...feature/task-search
git diff main...feature/task-search
عرض السلف المشترك
git merge-base main feature/task-search
يمثل merge base أفضل سلف مشترك يمكن استخدامه لفهم التغييرات التي أُجريت بعد تباعد الفرعين.
ما الذي يفعله git merge؟
يدمج git merge تغييرات التزام أو فرع آخر داخل الفرع الحالي.
اتجاه الدمج مهم جدًا.
لدمج ميزة البحث داخل main:
git switch main
git merge feature/task-search
يستقبل الفرع الحالي تغييرات الفرع الآخر. أما تنفيذ الأمر وأنت على فرع الميزة، فسيحاول دمج main داخل فرع الميزة.
قبل كل عملية دمج، تحقق من:
git branch --show-current
git status
دمج Fast-Forward
يكون fast-forward ممكنًا عندما لا يملك الفرع الحالي التزامات جديدة بعد تفرع فرع الميزة عنه.
قبل الدمج:
A---B main
\
C---D feature
وبصورة أدق، يشير main إلى B، ويكون فرع الميزة امتدادًا مباشرًا له:
A---B---C---D feature
^
main
بعد الدمج:
A---B---C---D main, feature
ينقل Git مرجع main إلى الأمام فقط. ولا يحتاج إلى merge commit لأنه لا يوجد سجل متباعد في الفرع الحالي يحتاج إلى الجمع.
نفّذ الدمج:
git switch main
git merge feature/task-search
قد تشير النتيجة إلى تنفيذ تحديث fast-forward.
السماح بـ Fast-Forward فقط
استخدم:
git merge --ff-only feature/task-search
ينجح هذا الأمر فقط عندما يستطيع Git تحديث الفرع الحالي من دون إنشاء merge commit.
إذا كان السجل قد تباعد، يتوقف الأمر بدل اختيار استراتيجية دمج.
يفيد هذا في مسارات العمل التي تتطلب سجلًا خطيًا وتريد معالجة أي تباعد غير متوقع بصورة صريحة.
فرض Merge Commit باستخدام --no-ff
يمكنك طلب merge commit حتى عندما يكون fast-forward ممكنًا:
git merge --no-ff feature/task-search
يصبح السجل:
A---B-------M main
\ /
C---D feature
يحافظ merge commit على حد تكامل واضح حول فرع الميزة.
قد تشمل الفوائد:
إظهار الميزة المكتملة كوحدة واحدة داخل السجل.
توفير رسالة دمج تتضمن سياق التكامل.
تسهيل تحديد الميزة كاملة للتراجع عنها لاحقًا.
وقد تشمل التكاليف:
زيادة عدد merge commits.
رسمًا بيانيًا أكثر تعقيدًا.
ضوضاء إضافية عندما يحصل كل فرع صغير على merge commit.
يعتمد الخيار الصحيح على سياسة الفريق في إدارة السجل.
Three-Way Merge
نحتاج إلى three-way merge عندما يحتوي كلا الفرعين على التزامات بعد السلف المشترك.
مثال:
C---D feature
/
A---B---E---F main
ينظر Git إلى ثلاث لقطات مهمة:
Merge base حيث كان الفرعان يشتركان في السجل.
طرف الفرع الحالي.
طرف الفرع الذي يجري دمجه.
إذا أمكن الجمع بين التغييرات تلقائيًا، ينشئ Git نتيجة دمج ويسجل عادة merge commit:
C---D
/ \
A---B---E---F---M main
يملك merge commit أبوين: الطرف السابق للفرع الحالي وطرف الفرع المدموج.
إنشاء مثال على Three-Way Merge
انطلاقًا من main نظيف، أنشئ فرعًا للتقارير:
git switch -c feature/report
أضف ملف تقرير:
على macOS أو Linux أو Git Bash
cat > report.txt <<'EOF'
عرض تقرير بالمهام المكتملة.
EOF
git add report.txt
git commit -m "إضافة وصف تقرير المهام المكتملة"
على PowerShell
"عرض تقرير بالمهام المكتملة." | Set-Content report.txt
git add report.txt
git commit -m "إضافة وصف تقرير المهام المكتملة"
انتقل إلى main ونفّذ تغييرًا مستقلًا:
git switch main
على macOS أو Linux أو Git Bash
cat >> README.md <<'EOF'
## التخزين
تُخزن المهام محليًا.
EOF
git add README.md
git commit -m "توثيق التخزين المحلي للمهام"
على PowerShell
@"
## التخزين
تُخزن المهام محليًا.
"@ | Add-Content README.md
git add README.md
git commit -m "توثيق التخزين المحلي للمهام"
تباعد الفرعان الآن. ادمج فرع الميزة:
git merge feature/report
بما أن التغييرات تؤثر في ملفات منفصلة، يجب أن يتمكن Git من جمعها تلقائيًا وإنشاء merge commit.
افحص النتيجة:
git log --oneline --graph --decorate --all
git show --stat --summary HEAD
فحص الدمج قبل إنشاء الالتزام
استخدم:
git merge --no-commit --no-ff feature/report
ينفذ هذا الأمر الدمج، لكنه يتوقف قبل إنشاء merge commit، ما يتيح فحص النتيجة وتعديلها.
راجع:
git status
git diff --staged
git diff --check
شغّل الاختبارات، ثم أنشئ الالتزام:
git commit
لماذا نضيف --no-ff؟ لا ينشئ تحديث fast-forward أي merge commit، لذلك لا يستطيع --no-commit التوقف قبل التزام غير موجود. يضمن الجمع بين الخيارين توقف التكامل للمراجعة عندما يكون merge commit مقصودًا.
التحقق مسبقًا من وجود Merge Base
افحص السلف المشترك:
git merge-base main feature/report
تحقق مما إذا كان أحد الفرعين سلفًا للآخر:
git merge-base --is-ancestor feature/report main
يعبر الأمر عن النتيجة من خلال exit status:
النجاح يعني أن revision الأولى سلف للثانية.
القيمة غير الصفرية تعني أنها ليست سلفًا لها.
يفيد ذلك داخل السكربتات وفحوص الإصدارات.
ما تعارض الدمج؟
يحدث merge conflict عندما لا يستطيع Git تحديد نتيجة موحدة وآمنة تلقائيًا.
تشمل الأسباب الشائعة:
تعديل الفرعين للأسطر نفسها بطرق مختلفة.
حذف أحد الفرعين ملفًا عدّله الفرع الآخر.
إضافة الفرعين ملفين مختلفين في المسار نفسه.
إعادة تسمية ملف أو مجلد بطرق غير متوافقة.
تعديل ملفات binary في الفرعين.
التعارض ليس تلفًا. فقد حفظ Git المعلومات المتنافسة ويطلب من المطور تحديد النتيجة المطلوبة.
إنشاء تعارض دمج متحكم به
ابدأ من main نظيف، ثم أنشئ فرعًا:
git switch -c feature/cloud-storage
غيّر app.txt إلى:
Task Notes
التخزين: سحابي
جهّز وسجل التغيير:
git add app.txt
git commit -m "استخدام التخزين السحابي للمهام"
عُد إلى main:
git switch main
غيّر السطر نفسه بصورة مختلفة:
Task Notes
التخزين: قاعدة بيانات محلية مشفرة
أنشئ الالتزام:
git add app.txt
git commit -m "استخدام تخزين محلي مشفر للمهام"
ادمج الآن:
git merge feature/cloud-storage
يجب أن يتوقف Git ويبلغ عن تعارض داخل app.txt.
قراءة حالة التعارض
نفّذ:
git status
يحدد Git المسارات غير المدمجة ويوفر أوامر المتابعة أو الإلغاء.
قد تعرض الحالة المختصرة:
UU app.txt
تعني UU أن الطرفين عدّلا الملف وأن التعارض ما يزال دون حل.
وقد تمثل رموز تعارض أخرى حالات حذف وتعديل، أو إضافة وإضافة، أو مجموعات أخرى.
فهم علامات التعارض
افتح الملف المتعارض. قد يبدو التعارض النصي كما يلي:
Task Notes
<<<<<<< HEAD
التخزين: قاعدة بيانات محلية مشفرة
=======
التخزين: سحابي
>>>>>>> feature/cloud-storage
تعني الأقسام:
<<<<<<< HEAD: المحتوى القادم من الفرع الحالي.=======: الفاصل.>>>>>>> feature/cloud-storage: المحتوى القادم من الفرع الذي يجري دمجه.
يجب أن يحتوي الملف النهائي على المحتوى المقصود فعليًا، وليس علامات التعارض.
حل التعارض يدويًا
افترض أن قرار المنتج الصحيح هو دعم الوضعين. عدّل الملف إلى:
Task Notes
التخزين: قاعدة بيانات محلية مشفرة مع مزامنة سحابية اختيارية
ثم ضع الملف في منطقة التجهيز لتحديده كمحلول:
git add app.txt
تحقق من الملفات غير المحلولة:
git status
git diff --check
راجع النتيجة المدمجة:
git diff --staged
أكمل الدمج:
git commit
أو استخدم:
git merge --continue
يكمل git merge --continue عملية الدمج بعد حل التعارضات وتجهيز الملفات. وقد يفتح Git المحرر لرسالة الدمج.
استخدام مراحل Index أثناء التعارض
خلال عملية دمج غير محلولة، يمكن أن يخزن index عدة مراحل للمسار المتعارض:
المرحلة 1: نسخة merge base.
المرحلة 2: نسخة الفرع الحالي.
المرحلة 3: نسخة الفرع الآخر.
افحصها:
git ls-files -u
اعرض نسخة base:
git show :1:app.txt
اعرض نسخة الطرف الحالي:
git show :2:app.txt
اعرض نسخة الطرف الآخر:
git show :3:app.txt
قد تكون هذه الطريقة أدق من قراءة علامات التعارض وحدها.
Ours وTheirs أثناء الدمج العادي
أثناء الدمج العادي:
Ours يشير إلى الفرع الحالي الذي كان checkout عند بدء الدمج.
Theirs يشير إلى الفرع أو الالتزام الذي يجري دمجه.
خذ النسخة الحالية لملف متعارض:
git checkout --ours app.txt
git add app.txt
خذ النسخة القادمة:
git checkout --theirs app.txt
git add app.txt
يمكن لصيغة الاستعادة الحديثة أيضًا اختيار مراحل index في مسارات العمل المدعومة:
git restore --ours app.txt
git restore --theirs app.txt
لا تستخدم هذه الأوامر تلقائيًا. فهي تستبدل ملف العمل بأحد الطرفين وقد تحذف تغييرات مهمة من الطرف الآخر. راجع النتيجة قبل التجهيز.
لاحظ أيضًا أن معنى “ours” و“theirs” قد يصبح غير متوقع أثناء rebase لأن منظور العملية مختلف. تحقق دائمًا من العملية الحالية ومحتوى الملف.
استخدام أداة دمج
بعد ضبط أداة دمج مرئية أو طرفية مدعومة، نفّذ:
git mergetool
يمكن لأداة الدمج عرض:
السلف المشترك.
نسخة الفرع الحالي.
النسخة القادمة.
النتيجة المقترحة.
بعد انتهاء الأداة، افحص الملفات وحالة المستودع. تترك بعض الأدوات ملفات احتياطية يجب حذفها أو تجاهلها وفق سياسة المشروع.
إلغاء عملية الدمج
عندما يجب ألا يستمر الدمج:
git merge --abort
يحاول Git إعادة الحالة التي كانت موجودة قبل بدء الدمج.
يكون الإلغاء أكثر أمانًا عندما كانت شجرة العمل نظيفة قبل بدء العملية. وقد تجعل التغييرات غير المسجلة السابقة استعادة الحالة أكثر تعقيدًا.
لهذا السبب يُنصح بشدة ببدء التكامل من شجرة عمل نظيفة.
حل بديل يظهر في مسارات العمل القديمة
قد تصادف:
git reset --merge
يمكن استخدامه في حالات استعادة مرتبطة، لكن git merge --abort يوضح المقصد بصورة أفضل أثناء عملية دمج نشطة.
إعادة بدء حل التعارض
إذا عُدّل التعارض بصورة خاطئة لكن لا يجب إلغاء الدمج كاملًا، فأعد عرض حالة التعارض بحذر.
يمكن إعادة المراحل غير المحلولة للملف من خلال حالة index المتوفرة أثناء الدمج، لكن السلوك يعتمد على ما جرى تجهيزه أو الكتابة فوقه بالفعل.
بالنسبة إلى المبتدئين، تكون الخيارات الأكثر أمانًا عادة:
استخدام إجراءات المحرر المدركة لنظام التحكم بالإصدارات قبل التجهيز.
فحص المراحل باستخدام
git show :1:و:2:و:3:.إلغاء الدمج وإعادة تشغيله عندما تصبح عملية الحل غير موثوقة.
لا تتابع العمل على ملف لم تعد تفهم مصدر محتواه.
مسار حل التعارض
نفّذ
git status.حدد كل المسارات غير المدمجة.
افتح كل تعارض وافهم هدف الطرفين.
راجع الاختبارات والمتطلبات والالتزامات المرتبطة وزملاء الفريق عند الحاجة.
عدّل الملف إلى الحالة المدمجة الصحيحة.
أزل جميع علامات التعارض.
نفّذ
git add pathلتحديد المسار كمحلول.كرر ذلك إلى أن تختفي كل المسارات غير المدمجة.
شغّل الاختبارات والفحوص الثابتة.
راجع النتيجة المجهزة.
أكمل الدمج باستخدام
git merge --continueأوgit commit.افحص الرسم النهائي وسلوك التطبيق.
فحص عملية دمج جارية
تشمل الأوامر المفيدة:
git status
git diff
git diff --staged
git diff --check
git ls-files -u
أثناء التعارضات:
يعرض
git diffمعلومات combined diff غير المحلولة.يعرض
git diff --stagedالمحتوى المحلول والمجهز لـ merge commit.يعرض
git ls-files -uإدخالات index غير المدمجة.يساعد
git diff --checkعلى اكتشاف أخطاء المسافات وعلامات التعارض المتبقية في الأسطر المضافة.
رسائل الدمج
عند إنشاء merge commit، يقترح Git رسالة. ويمكنك تمرير رسالة مباشرة:
git merge --no-ff feature/report -m "دمج ميزة تقارير المهام المكتملة"
أو السماح بفتح المحرر:
git merge --no-ff --edit feature/report
يمكن لرسالة دمج مفيدة أن تشرح:
الميزة أو الإصلاح الذي جرى دمجه.
سبب حل تعارض غير واضح بطريقة معينة.
اعتبارات مهمة تتعلق بالتوافق أو النشر.
مراجع issue أو المراجعات المرتبطة وفق قواعد الفريق.
لا تستخدم رسالة الدمج بديلًا عن الالتزامات الواضحة وسجل المراجعة.
استراتيجية الدمج وخياراتها
يدعم Git استراتيجيات دمج تُحدد باستخدام -s، وخيارات خاصة بالاستراتيجية تُمرر باستخدام -X.
في عمليات الدمج العادية ذات الطرفين، صُممت استراتيجية Git الافتراضية للتكامل المعتاد بين الفروع، وتتعامل مع إعادة التسمية ومع كثير من عمليات الجمع التلقائي.
من الأمثلة التي قد تصادفها:
git merge -s ort feature/report
git merge -Xours feature/report
git merge -Xtheirs feature/report
لا تعني خيارات -Xours و-Xtheirs حذف الفرع الآخر كاملًا أو حذف الفرع الحالي. بل تؤثر في الطرف المفضل عند حل الأجزاء المتعارضة، مع استمرار دمج التغييرات غير المتعارضة.
لا تستخدم خيارات الاستراتيجية لإخفاء التعارضات إلا إذا كانت السياسة مقصودة والنتيجة خضعت للاختبار.
استراتيجية ours مختلفة
ينفذ الأمر التالي:
git merge -s ours obsolete-branch
دمجًا يستخدم شجرة الفرع الحالي كنتيجة، مع تسجيل سجل الفرع الآخر على أنه مدمج.
تختلف هذه الاستراتيجية جذريًا عن -Xours. تتجاهل استراتيجية ours محتوى الشجرة الأخرى في النتيجة. وهي عملية متقدمة على السجل ولا ينبغي استخدامها كاختصار اعتيادي لحل التعارضات.
دمج أكثر من فرع
يمكن لـ Git تنفيذ octopus merge بعدة أطراف في الحالات المناسبة الخالية من التعارضات:
git merge branch-a branch-b branch-c
ينشئ ذلك merge بأكثر من أبوين عندما تستطيع الاستراتيجية حله تلقائيًا.
تُستخدم عمليات octopus merge عادة للجمع بين عدة فروع مستقلة جرى اختبارها مسبقًا. ولا تناسب حل التعارضات المعقدة، لأن فهم تعارض يضم عددًا كبيرًا من الآباء يصبح صعبًا.
العثور على الفروع المدمجة بالفعل
اعرض الفروع المحلية التي يمكن الوصول إلى أطرافها من HEAD:
git branch --merged
اعرض الفروع غير المدمجة بالكامل داخل HEAD:
git branch --no-merged
تحقق نسبة إلى فرع محدد:
git branch --merged main
git branch --no-merged main
يفيد ذلك قبل تنظيف الفروع.
ظهور فرع ضمن --merged main يعني أن طرفه يمكن الوصول إليه من main. ولا يثبت أن الميزة دُمجت باستخدام merge commit؛ فقد تؤثر عمليات fast-forward أو rebase أو التغييرات المكافئة في طريقة تفسير النتيجة.
العثور على الفروع التي تحتوي على التزام
اعرض الفروع التي تحتوي على التزام محدد:
git branch --contains <commit>
اعرض الفروع التي لا تحتوي عليه:
git branch --no-contains <commit>
يساعد ذلك على الإجابة عن أسئلة مثل:
هل وصل إصلاح الأمان إلى فرع الإصدار؟
ما فروع الصيانة التي تحتوي على هذا التصحيح؟
هل يمكن حذف هذا الفرع المؤقت من دون فقدان طرفه؟
حذف فرع محلي بأمان
احذف الفرع فقط عندما لا تكون موجودًا عليه:
git branch -d feature/report
ينفذ الخيار الصغير -d فحص أمان، ويرفض عادة حذف فرع لم يُدمج بالكامل في upstream مناسب أو في السجل الحالي.
للحذف الإجباري:
git branch -D feature/report
يعادل -D حذفًا إجباريًا، ويزيل مرجع الفرع حتى عندما لا تكون بعض الالتزامات قابلة للوصول من مكان آخر.
قبل الإجبار، نفّذ:
git log --oneline main..feature/report
git branch --contains feature/report
git reflog show feature/report
إذا كان العمل قد يظل مهمًا، فأنشئ tag أو فرعًا احتياطيًا بدل الحذف العشوائي.
حذف الفرع لا يحذف الالتزامات فورًا
اسم الفرع مرجع. ويؤدي حذفه إلى إزالة المرجع، وليس بالضرورة إلى حذف كائنات الالتزامات الأساسية مباشرة.
تبقى الالتزامات متاحة ما دامت قابلة للوصول من فرع أو tag أو مرجع remote-tracking أو أي مرجع آخر.
وقد تبقى أطراف الفروع المحلية المحذوفة حديثًا قابلة للاكتشاف من خلال reflog لمدة محدودة. لكن الكائنات غير القابلة للوصول قد تُحذف لاحقًا بواسطة prune.
يُعد reflog وسيلة استعادة، وليس أرشيفًا دائمًا للفروع.
الفروع المحلية وفروع Remote-Tracking
الفرع المحلي هو فرع يمكنك checkout له وإنشاء التزامات عليه، مثل:
main
feature/search
يسجل فرع remote-tracking آخر حالة معروفة لفرع موجود على remote بعد تنفيذ fetch:
origin/main
origin/feature/search
لا تنشئ التزامات مباشرة على origin/main عادة، فهو يتحرك عند جلب معلومات remote.
أنشئ فرعًا محليًا اعتمادًا على فرع remote-tracking:
git switch -c feature/search --track origin/feature/search
وفي حالات شائعة كثيرة، يستطيع Git استنتاج الاسم المحلي:
git switch feature/search
سيجري تناول نشر الفروع البعيدة وضبط upstream وعمليات push وfetch وحذف الفروع البعيدة بصورة منفصلة، لأنها تتعلق بالتعاون وحالة الخادم.
ما فرع Upstream؟
يمكن ضبط فرع محلي لتتبع upstream branch. تساعد هذه العلاقة الأوامر على الإبلاغ عما إذا كان الفرع المحلي ahead أو behind، وتوفر قيمًا افتراضية لعمليات مثل pull وpush وفق الإعدادات.
افحص معلومات التتبع:
git branch -vv
اضبط upstream لفرع محلي موجود:
git branch --set-upstream-to=origin/feature/search feature/search
أزله:
git branch --unset-upstream feature/search
لا يُعد upstream التزامًا أبًا أو merge base، بل هو إعداد فرع يصف فرعًا محليًا أو بعيدًا مرتبطًا به.
الفرق العام بين Merge وRebase
يجمع merge السجلات وقد ينشئ التزامًا بعدة آباء. أما rebase فيعيد تشغيل الالتزامات فوق base جديد وينشئ التزامات بديلة.
Merge:
C---D
/ \
A---B---E---F---M
Rebase بصورة مفاهيمية:
A---B---E---F---C'---D'
قد ينتج rebase سجلًا خطيًا، لكنه يعيد كتابة معرّفات الالتزامات. أما merge فيحافظ على الالتزامات الموجودة ويسجل التكامل صراحة.
تركز هذه المقالة على الدمج. ويجب تعلم rebase بصورة منفصلة لأن منظور التعارض وأمان السجل المشترك وأساليب الاستعادة تختلف.
قواعد تسمية الفروع
تصف الأسماء المفيدة الغرض:
feature/task-search
fix/login-timeout
hotfix/payment-callback
docs/local-setup
refactor/order-service
release/2.4
تجنب أسماء مثل:
test
new
final
branch2
john-work
changes
القواعد الجيدة:
مختصرة بما يكفي للكتابة والقراءة.
محددة بما يكفي لمعرفة الغرض.
متسقة داخل الفريق.
متوافقة مع قواعد المستودع والأتمتة.
خالية من المعلومات الخاصة أو الحساسة.
تضيف بعض الفرق معرّفات التذاكر:
feature/APP-142-task-search
يظل الاتساق والوضوح أهم من النمط الدقيق.
مسار عملي لفرع ميزة
1. ابدأ من فرع دمج محدث
git switch main
git status
عند استخدام remote، نفّذ fetch وحدّث الفرع وفق مسار عمل الفريق قبل إنشاء الفرع.
2. أنشئ فرع الميزة
git switch -c feature/task-search
3. أنشئ التزامات محددة الهدف
git add -p
git diff --staged
git commit -m "إضافة مطابقة الكلمات المفتاحية للمهام"
4. افحص الميزة
git log --oneline main..feature/task-search
git diff main...feature/task-search
5. شغّل الاختبارات
# استخدم أمر الاختبار الحقيقي للمشروع
php vendor/bin/phpunit
6. ادمج داخل الفرع الهدف
git switch main
git merge --no-ff feature/task-search
استخدم نمط الدمج الذي يطلبه الفريق. تفضّل بعض الفرق التكامل بنمط fast-forward فقط، بينما تحافظ فرق أخرى على merge commits.
7. تحقق من النتيجة المدمجة
git status
git log --oneline --graph --decorate --all
شغّل مجموعة الاختبارات الكاملة ذات الصلة على النتيجة المدمجة، لا على فرع الميزة فقط.
8. احذف الفرع المحلي المكتمل
git branch -d feature/task-search
مسار عملي للإصلاح العاجل
افترض أن فرع ميزة ما يزال غير مكتمل عند ظهور مشكلة عاجلة في الإنتاج.
1. احفظ عمل الميزة غير المكتمل
أنشئ التزام checkpoint متماسكًا أو استخدم stash وفق سياسة الفريق.
2. انتقل إلى قاعدة الإنتاج
git switch main
3. أنشئ فرع Hotfix
git switch -c hotfix/login-timeout
4. نفّذ الإصلاح واختبره
git add -p
git commit -m "زيادة مهلة مزود تسجيل الدخول"
5. ادمج الإصلاح
git switch main
git merge --no-ff hotfix/login-timeout
6. انقل الإصلاح إلى مسارات العمل النشطة الأخرى
بحسب مسار المستودع، ادمج base المحدث داخل فرع الميزة أو طبّق الإصلاح بطريقة تكامل أخرى معتمدة.
git switch feature/large-change
git merge main
يمنع ذلك فرع الميزة النشط من إعادة السلوك القديم لاحقًا.
تطبيق يدوي: دمج Fast-Forward
السجل الأولي
A main
إنشاء فرع
git switch -c feature/a
إنشاء التزامين
A---B---C feature/a
^
main
العودة إلى Main
git switch main
الدمج
git merge feature/a
النتيجة
A---B---C main, feature/a
لا حاجة إلى merge commit، إذ ينتقل مؤشر الفرع الحالي إلى C.
تطبيق يدوي: Three-Way Merge
السجل الأولي
A---B main
عمل الميزة
C---D feature
/
A---B main
عمل مستقل على Main
C---D feature
/
A---B---E---F main
الدمج أثناء الوجود على Main
git switch main
git merge feature
النتيجة
C---D
/ \
A---B---E---F---M main
يملك merge commit الجديد M طرف main القديم وطرف الميزة بوصفهما أبوين.
تطبيق يدوي: تعارض وحل
الملف الأساسي
التخزين: محلي
تعديله في Main إلى
التخزين: قاعدة بيانات محلية مشفرة
تعديله في Feature إلى
التخزين: سحابي
محاولة الدمج
git switch main
git merge feature/cloud-storage
حالة التعارض
<<<<<<< HEAD
التخزين: قاعدة بيانات محلية مشفرة
=======
التخزين: سحابي
>>>>>>> feature/cloud-storage
قرار المنتج بعد الحل
التخزين: قاعدة بيانات محلية مشفرة مع مزامنة سحابية اختيارية
تحديده كمحلول والمتابعة
git add app.txt
git diff --staged
git merge --continue
التحقق
git status
git log --oneline --graph --decorate --all
أخطاء شائعة في استخدام الفروع
إنشاء فرع من نقطة بداية خاطئة
افحص دائمًا الفرع الحالي والسجل القريب قبل إنشاء فرع جديد.
نسيان الفرع الحالي
نفّذ:
git branch --show-current
git status
قبل إنشاء الالتزامات أو تنفيذ الدمج.
التعامل مع الفرع بوصفه نسخة من مجلد
الفرع مرجع إلى السجل. ويؤدي الانتقال بين الفروع إلى تحديث شجرة العمل لتطابق مسار سجل آخر.
ترك تغييرات غير مسجلة قبل الدمج
تزيد التغييرات السابقة من تعقيد حل التعارضات وسلوك الإلغاء.
الدمج في الاتجاه الخطأ
يغير git merge other الفرع الحالي. انتقل أولًا إلى الفرع الهدف المقصود.
حل التعارض بالإبقاء على النسخة التي تعمل فقط
قد تكون النتيجة صحيحة من ناحية الصياغة لكنها تخالف المتطلبات أو تحذف سلوكًا بصمت. افهم التغييرين وشغّل الاختبارات.
استخدام ours أو theirs من دون مراجعة
قد يؤدي اختيار ملف كامل من أحد الطرفين إلى حذف عمل صحيح من الطرف الآخر.
حذف فرع غير مدمج باستخدام -D
قد يزيل الحذف الإجباري أسهل مرجع يصل إلى التزامات فريدة.
الاحتفاظ بالفروع المكتملة إلى الأبد
تضيف الفروع القديمة ضوضاء وتجعل التمييز بين العمل النشط والبقايا التاريخية أصعب.
استخدام --no-ff أو --ff-only من دون سياسة فريق
يؤثر الخياران في شكل السجل. استخدمهما بصورة مقصودة ومتسقة.
الخلط بين الفرع المحلي وorigin/branch
يسجل فرع remote-tracking حالة remote التي جرى جلبها. وليس هو الفرع المحلي المعتاد الذي تنشئ عليه الالتزامات.
أفضل الممارسات للفروع وعمليات الدمج
أنشئ الفروع من base صحيح ومحدث.
استخدم أسماء فروع واضحة ومتسقة.
اجعل لكل فرع غرضًا رئيسيًا واحدًا.
أنشئ التزامات محددة الهدف قبل التكامل.
تحقق من الفرع الحالي قبل الدمج.
ابدأ الدمج من شجرة عمل نظيفة.
افحص الالتزامات الفريدة وdiff ذي النقاط الثلاث قبل التكامل.
شغّل الاختبارات على فرع الميزة وعلى النتيجة المدمجة.
حل التعارضات وفق السلوك المقصود، لا وفق تفضيل نصي فقط.
راجع حلول التعارضات قبل إكمال الدمج.
استخدم الحذف الإجباري فقط بعد التأكد من عدم الحاجة إلى العمل الفريد.
احذف الفروع قصيرة العمر المكتملة لتقليل الفوضى.
وثّق ما إذا كان الفريق يستخدم fast-forward أو merge commits أو squash merge أو rebase.
مرجع أوامر الفروع
| الأمر | الغرض |
|---|---|
git branch | عرض الفروع المحلية |
git branch -vv | عرض أطراف الفروع وعلاقات upstream |
git branch name | إنشاء فرع من دون الانتقال إليه |
git switch name | الانتقال إلى فرع موجود |
git switch -c name | إنشاء فرع والانتقال إليه |
git switch - | العودة إلى موقع checkout السابق |
git switch --detach revision | فحص revision في وضع detached HEAD |
git branch -m old new | إعادة تسمية فرع محلي |
git branch -d name | حذف فرع محلي مدمج بأمان |
git branch -D name | حذف فرع محلي إجباريًا |
git branch --merged main | عرض الفروع المدمجة في main |
git branch --no-merged main | عرض الفروع غير المدمجة في main |
git branch --contains commit | عرض الفروع التي تحتوي على التزام |
مرجع أوامر الدمج
| الأمر | الغرض |
|---|---|
git merge branch | دمج فرع داخل الفرع الحالي |
git merge --ff-only branch | السماح بتحديث fast-forward فقط |
git merge --no-ff branch | إنشاء merge commit حتى عندما يكون fast-forward ممكنًا |
git merge --no-commit --no-ff branch | تحضير merge commit والتوقف للفحص |
git merge --continue | إكمال الدمج بعد حل التعارضات |
git merge --abort | محاولة العودة إلى حالة ما قبل الدمج |
git merge-base A B | عرض أفضل سلف مشترك |
git mergetool | فتح أداة حل الدمج المضبوطة |
git ls-files -u | عرض إدخالات index غير المدمجة |
git diff --check | فحص أخطاء المسافات وعلامات التعارض داخل التغييرات |
قائمة التحقق قبل الدمج
الفرع الحالي هو هدف الدمج المقصود.
شجرة العمل وindex نظيفان.
يحتوي فرع الميزة على الالتزامات المطلوبة.
جرى فحص diff الخاص بالفرع.
نجحت الاختبارات ذات الصلة قبل التكامل.
نمط الدمج المطلوب معروف: fast-forward أو merge commit أو طريقة أخرى معتمدة.
لا توجد تغييرات محلية غير مرتبطة ستتداخل مع العملية.
توجد خطة استعادة للتكامل عالي المخاطر.
قائمة التحقق بعد الدمج
لا توجد مسارات غير محلولة.
لا توجد علامات تعارض داخل الملفات المتتبعة.
اجتازت الشيفرة المدمجة الاختبارات والفحوص الثابتة.
يظهر merge commit أو تحديث fast-forward داخل السجل.
يعمل التطبيق بصورة صحيحة مع مجموعتي التغييرات.
يمكن حذف الفرع المكتمل بأمان.
تصل الإصلاحات المهمة إلى الفروع طويلة العمر الأخرى عند الحاجة.
الأسئلة الشائعة
هل يؤدي إنشاء فرع إلى نسخ المشروع كاملًا؟
لا. ينشئ Git مرجعًا خفيفًا يشير إلى التزام. وتتغير شجرة العمل عند الانتقال بين الفروع.
ما الفرق بين git branch وgit switch -c؟
ينشئ git branch name الفرع من دون الانتقال إليه، بينما ينشئ git switch -c name الفرع وينتقل إليه.
لماذا لا يستطيع Git الانتقال بين الفروع؟
قد تُكتب تغييراتك المحلية بواسطة الفرع الهدف. سجل التغييرات أو ضعها في stash أو تخلص منها أو نظمها بأمان قبل الانتقال.
أي فرع يستقبل الدمج؟
يستقبل الفرع الذي جرى checkout له حاليًا الفرع المحدد في git merge.
ما دمج Fast-Forward؟
هو نقل مرجع الفرع الحالي إلى الأمام لأن الفرع الآخر امتداد مباشر له، ولا توجد التزامات متباعدة في الفرع الحالي تحتاج إلى الدمج.
هل ينشئ --no-ff دائمًا Merge Commit؟
يمنع fast-forward في عملية دمج فرع عادية غير تافهة ويطلب merge commit. وينبغي مراجعة التوثيق الرسمي للحالات الخاصة وشروط الأمر الأخرى.
لماذا لم يوقف --no-commit عملية الدمج؟
لا ينشئ تحديث fast-forward merge commit حتى يتوقف الأمر قبله. استخدم --no-ff --no-commit عندما تحتاج إلى merge commit قابل للمراجعة.
هل Merge Conflict خطأ في Git؟
لا. يعني أن Git لا يستطيع اختيار المحتوى المدمج بأمان بصورة تلقائية ويحتاج إلى قرار بشري.
هل يمكن إلغاء التعارض؟
نعم، استخدم git merge --abort. تكون الاستعادة أكثر موثوقية عندما كانت شجرة العمل نظيفة قبل الدمج.
هل يجب اختيار Ours أو Theirs دائمًا؟
لا. تتطلب معظم التعارضات الحقيقية نتيجة مركبة. وقد يؤدي اختيار طرف كامل إلى حذف سلوك مطلوب.
هل يمكن حذف الفرع بعد الدمج؟
نعم. استخدم git branch -d name بعد التأكد من أن عمل الفرع قابل للوصول من الهدف المقصود.
ماذا يحدث للالتزامات بعد حذف الفرع؟
تبقى ما دامت قابلة للوصول من مرجع آخر. وقد تُحذف الالتزامات الفريدة غير القابلة للوصول لاحقًا، رغم أن reflog المحلي قد يتيح فرص استعادة مؤقتة.
ما الفرق بين Branch وUpstream؟
الفرع مرجع متحرك يشير إلى التزام. أما upstream فهو إعداد يربط فرعًا محليًا بفرع آخر لتوفير معلومات الحالة وسلوك المزامنة الافتراضي.
هل أستخدم Merge أم Rebase؟
يدمج الأسلوبان مسارات التطوير بطرق مختلفة. يحافظ merge على الالتزامات الموجودة ويمكنه تسجيل التكامل بواسطة merge commit. أما rebase فينشئ التزامات بديلة فوق base جديد. استخدم مسار العمل الذي يحدده الفريق وتجنب إعادة كتابة السجل المشترك بلا داعٍ.
الخلاصة
فروع Git مراجع خفيفة تتيح للتطوير الاستمرار في مسارات مستقلة. وهي تجعل عزل الميزات والتجارب والإصلاحات والإصدارات أسهل من دون نسخ المشروع أو تغيير الشيفرة المستقرة فورًا.
تعيد عملية الدمج جمع هذه المسارات. ينقل fast-forward مرجع الفرع عندما لا يوجد تباعد. ويجمع three-way merge التغييرات باستخدام السلف المشترك وطرفي الفرعين. وعندما لا يستطيع Git تحديد المحتوى المدمج الصحيح، يحفظ النسخ المتنافسة ويطلب حلًا مقصودًا للتعارض.
يبدأ مسار التكامل الآمن قبل أمر الدمج: استخدم شجرة عمل نظيفة، وتحقق من الفرع الهدف، وراجع الالتزامات الفريدة والفروقات، وشغّل الاختبارات. أثناء التعارضات، افهم هدف الطرفين، وجهّز النتيجة المركبة الصحيحة، وتحقق من التطبيق النهائي بدل اختيار طرف واحد تلقائيًا.
بعد التكامل، افحص الرسم البياني، وتحقق من أن طرف الفرع قابل للوصول من الهدف المقصود، واحذف الفروع قصيرة العمر المكتملة. يجب أن تكون النتيجة شيفرة تعمل وسجلًا يستطيع المطورون المستقبليون فهمه.
ستركز المقالة التالية على Git stash وtags: وضع العمل جانبًا بصورة مؤقتة، واستعادة التغييرات المخزنة، وإنشاء علامات للإصدارات، ومقارنة lightweight tags وannotated tags، ونشر العلامات بأمان.
المراجع الرسمية ومواد إضافية للقراءة

