فروع 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 إلى ثلاث لقطات مهمة:

  1. Merge base حيث كان الفرعان يشتركان في السجل.

  2. طرف الفرع الحالي.

  3. طرف الفرع الذي يجري دمجه.

إذا أمكن الجمع بين التغييرات تلقائيًا، ينشئ 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:.

  • إلغاء الدمج وإعادة تشغيله عندما تصبح عملية الحل غير موثوقة.

لا تتابع العمل على ملف لم تعد تفهم مصدر محتواه.

مسار حل التعارض

  1. نفّذ git status.

  2. حدد كل المسارات غير المدمجة.

  3. افتح كل تعارض وافهم هدف الطرفين.

  4. راجع الاختبارات والمتطلبات والالتزامات المرتبطة وزملاء الفريق عند الحاجة.

  5. عدّل الملف إلى الحالة المدمجة الصحيحة.

  6. أزل جميع علامات التعارض.

  7. نفّذ git add path لتحديد المسار كمحلول.

  8. كرر ذلك إلى أن تختفي كل المسارات غير المدمجة.

  9. شغّل الاختبارات والفحوص الثابتة.

  10. راجع النتيجة المجهزة.

  11. أكمل الدمج باستخدام git merge --continue أو git commit.

  12. افحص الرسم النهائي وسلوك التطبيق.

فحص عملية دمج جارية

تشمل الأوامر المفيدة:

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، ونشر العلامات بأمان.

المراجع الرسمية ومواد إضافية للقراءة