EduFlow
Git و GitHub
⁦0%⁩

الوحدة 2 · الفروع والدمج

الـ Rebase مقابل الـ Merge، والقاعدة الذهبية

17 دقراءة 4 داملأ الفراغات

الدرس في لمحة

نفّذ rebase لفرع feature على main لتُبقي السجلّ خطيًا، وتعامل مع التعارضات في منتصف الـ rebase، ورتّب الـ commits تفاعليًا، واعرف متى تقول القاعدة الذهبية إن عليك الدمج بدلًا من ذلك.

  1. 1ما الذي يفعله الـ rebase
  2. 2التعارضات أثناء الـ rebase
  3. 3الترتيب بالـ rebase التفاعلي
  4. 4القاعدة الذهبية
  5. 5الـ merge أم الـ rebase؟

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

في الدرسين السابقين، كان جمع الفروع يعني الدمج، وكان السجلّ المتباعد يكسب merge commit. هذا صادق وآمن، لكن في repository مزدحم يبدأ git log --graph بالظهور كخريطة قطارات. الـ rebase (إعادة التأسيس) هو الطريقة الأخرى لجمع العمل: بدل أن يصل خطّين، ينقل خطك بحيث لا يبقى إلا خط واحد.

ما الذي يفعله الـ rebase

انفصل فرعك projects-grid عن main عند b2. ومنذ ذلك الحين كسب main الـ commit ذا المعرّف c3، وفي فرعك d4 و e5. من الفرع، شغّل:

Terminal
git switch projects-grid
git rebase main
Text
Successfully rebased and updated refs/heads/projects-grid.

وجد Git الـ commits الموجودة على فرعك وليست على main (d4 و e5)، ووضعها جانبًا، ونقل فرعك إلى طرف main، ثم أعاد تشغيل كل تغيير فوقه، commitًا تلو الآخر.

الـ commits المُعاد تشغيلها commits جديدة. التغييرات والرسائل نفسها، لكن الآباء مختلفون، فالمعرّفات مختلفة. هذه ليست تفصيلة؛ إنها القصة كلها لمعرفة متى يكون الـ rebase آمنًا. معرّف الـ commit يتضمّن أباه، لذا لا تستطيع نقل commit، بل تصنع نسخة منه في مكان آخر.

قبل: للـ commit ذي المعرّف b2 ابنان، c3 على main، و d4 ثم e5 على projects-grid. بعد الـ rebase: ما زال main ينتهي عند c3؛ وتلي c3 على خط مستقيم commits جديدة هي d4' و e5'، ويشير projects-grid إلى e5'. أما d4 و e5 القديمان فباهتان بلا مرجع. قبل b2 c3 d4 e5 main projects-grid بعد git rebase main b2 c3 d4' e5' main projects-grid d4 e5 commits قديمة: لا فرع يشير إليها الآن
الـ rebase يعيد تشغيل commits فرعك على قاعدة جديدة

الآن صار main سلفًا لفرعك، فإدخال العمل يتمّ بـ fast-forward:

Terminal
git switch main
git merge projects-grid    # Fast-forward

السجلّ خط مستقيم، كأنك بدأت العمل على الشبكة (grid) بعد تصحيح الخطأ الإملائي.

التعارضات أثناء الـ rebase

لأن الـ rebase يعيد تشغيل الـ commits واحدًا تلو الآخر، فإن التعارض يوقفه عند commit بعينه. الروتين هو نفسه الذي تعلّمته في الدمج، مع كلمات مختلفة في النهاية:

Terminal
# Git stops: CONFLICT (content): Merge conflict in projects.html
# edit projects.html, remove the markers, check it
git add projects.html
git rebase --continue      # finish this commit, replay the next

git rebase --abort يعيد الفرع تمامًا إلى حيث كان قبل أن تبدأ. و git rebase --skip يُسقط الـ commit المتعارض، ونادرًا ما يكون هذا ما تريده.

مفاجأة واحدة: أثناء الـ rebase يتبادل “ours” و “theirs” موقعيهما. أنت تعيد تشغيل الـ commits الخاصة بك فوق main، لذا فالـ HEAD (النصف العلوي من التعارض) هو طرف main، والـ commit الخاص بك هو النصف السفلي. اقرأ الأسماء الدالّة، لا حدسك.

الترتيب بالـ rebase التفاعلي

قبل أن تشارك فرعًا، تستطيع إعادة كتابة الـ commits الخاصة به. يفتح git rebase -i قائمة مهام في محرّرك:

Terminal
git rebase -i main
Text
pick 4b1e2c0 Add projects grid
pick 9a3d7f1 fix typo
pick c2e8b44 Grid gap on mobile

غيّر الكلمة التي أمام الـ commit واحفظ. reword يعدّل رسالته، و squash أو fixup يطويه في الـ commit الذي فوقه (مع الاحتفاظ برسالته أو التخلّص منها)، و drop يحذفه، وإعادة ترتيب الأسطر تعيد ترتيب الـ commits. تغيير pick 9a3d7f1 إلى fixup 9a3d7f1 يحوّل ثلاثة commits إلى اثنين نظيفين. هكذا يُبقي المحترفون commits من نوع “fix typo” خارج الـ pull request.

القاعدة الذهبية

الـ rebase يستبدل الـ commits بنسخ منها. هذا غير ضارّ حين تكون الوحيد الذي يملكها. لكنه فوضى حين يكون شخص آخر قد بنى على الأصلية: سجلّه ما زال يحتوي d4 و e5، وسجلّك يحتوي d4' و e5'، والـ pull التالي ينتج commits مكرّرة وتعارضات محيّرة.

إذن: لا تنفّذ rebase على commits موجودة خارج الـ repository الخاص بك وقد يكون الناس بنوا عليها عملًا. وعمليًا:

  • نفّذ rebase بحرية على فروعك الخاصة قبل أن ترفعها أو قبل أن يلمسها أحد غيرك.
  • لا تنفّذ rebase أبدًا على main أو على أي فرع يسحب منه آخرون.
  • تنفيذ rebase على فرع الـ pull request الخاص بك بعد رفعه أمر شائع ومقبول إن لم يكن أحد غيرك ينشئ commits عليه. سيرفض Git الرفع العادي لأن السجلّ أُعيدت كتابته، فاستخدم:
Terminal
git push --force-with-lease

--force-with-lease يكتب فوق الفرع البعيد فقط إن كان ما زال يشير إلى حيث رأيته آخر مرة. وإن رفع زميل شيئًا في الأثناء، يرفض بدل أن يمحو عمله.

الـ merge أم الـ rebase؟

Merge Rebase
السجلّ سجلّ صادق، مع merge commits خطّي، كأن العمل جرى بالتتابع
معرّفات الـ commits لا تتغيّر تُعاد كتابتها
آمن على الفروع المشتركة نعم لا
التعارضات تُحلّ مرة واحدة قد تتكرّر مع كل commit يُعاد تشغيله

خيار افتراضي معقول: نفّذ rebase لفرع الـ feature الخاص بك على main لتبقى محدَّثًا ولترتّبه قبل المراجعة؛ وادمجه (غالبًا عبر pull request) لإيصاله. وكثير من الفرق تضبط أيضًا git pull ليعيد تأسيس الـ commits التي لم تُرفع بعد، وهذا ما ستضبطه في القسم التالي، حين يحصل موقعك الشخصي أخيرًا على remote.

أهم النقاط

  • git rebase main يعيد تشغيل commits فرعك فوق طرف main، فينشئ commits جديدة بمعرّفات جديدة.
  • بعد الـ rebase، يصبح دمج الفرع في main عملية fast-forward، فيبقى السجلّ خطًا مستقيمًا.
  • أثناء تعارض الـ rebase، أصلح الملف، ونفّذ git add له، ثم git rebase --continue؛ و git rebase --abort يلغي الـ rebase بكامله.
  • القاعدة الذهبية ألّا تنفّذ rebase أبدًا على commits ربما بنى عليها آخرون؛ نفّذه فقط على عملك الخاص غير المشارَك.
  • إن اضطررت إلى تحديث فرع رفعته بالفعل، فاستخدم git push --force-with-lease، ولا تستخدم --force المجرّد أبدًا.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

في فرعك projects-grid الـ commitان d4 و e5. تشغّل عليه git rebase main. ماذا يحدث لـ d4 و e5؟

السؤال 2 من 4

في منتصف الـ rebase، يبلّغ Git عن تعارض في projects.html. حرّرت الملف حتى صار سليمًا. ماذا تشغّل بعد ذلك؟

السؤال 3 من 4

أيّ الحالات التالية تخرق القاعدة الذهبية للـ rebase؟

السؤال 4 من 4

نفّذت rebase لفرع الـ pull request الخاص بك، والآن يُرفض git push لأنه ليس fast-forward. ما الطريقة الآمنة لتحديثه؟

تدريب

املأ الفراغات

نفّذ rebase لشبكة المشاريع

فرعك projects-grid متأخّر عن main، وتريد سجلًّا خطيًا. املأ الفراغات لتنفّذ rebase له، وتتجاوز تعارضًا واحدًا، ثم تحدّث الفرع الذي رفعته أصلًا للمراجعة دون أن تخاطر بـ commits أي شخص آخر.

git switch projects-grid git main # replay my commits on top of main # CONFLICT in projects.html: edit it, then git add projects.html git rebase # replay the remaining commits # this branch was already pushed for review, so update it safely git push