{"id":"l-git-2-2","courseId":"git-github","lang":"ar","kind":"lesson","summary":"ادمج فرعًا مكتملًا في main، وتوقّع هل سينفّذ Git عملية fast-forward أم سينشئ merge commit، واختر بينهما عن قصد بـ --no-ff و --ff-only.","html":"<p>لديك فرع <code dir=\"ltr\">footer</code> مكتمل، وفرع <code dir=\"ltr\">main</code> لا يحتويه بعد. الدمج (merge) هو الطريقة التي ينضمّ بها العمل على خط من السجلّ إلى خط آخر. لدى Git طريقتان لفعل ذلك، والطريقة التي تحدث ليست عشوائية: تستطيع توقّعها بالنظر إلى الرسم البياني قبل أن تكتب أي شيء.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">قاعدة الاتجاه</h2>\n<p>أولًا، القاعدة التي تجنّبك أكبر قدر من الارتباك: <strong>أنت تدمج فرعًا آخر في الفرع الذي أنت عليه.</strong> الأمر <code dir=\"ltr\">git merge footer</code> يعني «أدخِل عمل <code dir=\"ltr\">footer</code> إلى فرعي الحالي». لا يتحرّك إلا الفرع الحالي. أما الفرع الذي سمّيته فيبقى تمامًا حيث كان.</p>\n<p>إذن الروتين دائمًا: انتقل إلى الفرع الذي يجب أن يستقبل العمل، ثم ادمج.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span></span></code></pre></div>\n<h2 id=\"sec-2\" tabindex=\"-1\">الحالة الأولى: fast-forward</h2>\n<p>منذ أنشأت <code dir=\"ltr\">footer</code>، لم ينشئ أحد أي commit على <code dir=\"ltr\">main</code>. كل commit على <code dir=\"ltr\">main</code> موجود أصلًا في سجلّ <code dir=\"ltr\">footer</code>. لا يحتاج Git إلى جمع أي شيء؛ إنه ينزلق بـ <code dir=\"ltr\">main</code> إلى الأمام حتى آخر commit في <code dir=\"ltr\">footer</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Updating c39be11..f1a3b07\nFast-forward\n index.html |  8 ++++++++\n styles.css | 12 ++++++++++++\n 2 files changed, 20 insertions(+)</code></pre></div>\n<p>هذا هو الـ <strong>fast-forward</strong> (التقديم السريع): لا commit جديد، بل مؤشّر يتحرّك على commits موجودة. ويبقى السجلّ خطًا مستقيمًا، كأنك عملت على <code dir=\"ltr\">main</code> طوال الوقت.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">الحالة الثانية: دمج حقيقي</h2>\n<p>والآن الحالة الأكثر إثارة. بينما كنت تبني <code dir=\"ltr\">dark-mode</code>، صحّحت أيضًا خطأً إملائيًا مباشرة على <code dir=\"ltr\">main</code>. في كل فرع commits يفتقدها الآخر؛ لقد <strong>تباعدا</strong> (diverged). لا يوجد خط مستقيم يمكن الانزلاق عليه.</p>\n<p>ينفّذ Git <strong>دمجًا ثلاثيًا</strong> (three-way merge). يجد الـ <strong>merge base</strong>، وهو أحدث commit يتشاركه الفرعان، ويقارن طرف كل فرع به. التغيير الذي جرى على طرف واحد فقط يُؤخذ. والتغييرات التي جرت على الطرفين في مواضع مختلفة تُؤخذ كلها. أما إن غيّر الطرفان الأسطر نفسها، فيتوقّف Git ويسألك؛ هذا هو التعارض (conflict)، وله الدرس التالي كاملًا.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dark-mode</span></span></code></pre></div>\n<p>يفتح Git محرّرك بالرسالة <code dir=\"ltr\">Merge branch 'dark-mode'</code>. احفظ وأغلق، وسترى:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Merge made by the 'ort' strategy.\n styles.css | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)</code></pre></div>\n<p><code dir=\"ltr\">ort</code> هو اسم خوارزمية الدمج الافتراضية في Git؛ ولا تحتاج إلى اختيارها. والنتيجة <strong>merge commit</strong>: commit له أبوان، طرف <code dir=\"ltr\">main</code> السابق وطرف <code dir=\"ltr\">dark-mode</code>. ولقطته تحتوي خطَّي العمل كليهما.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 300\" role=\"img\" aria-labelledby=\"l-git-2-2-t1\"><defs><marker id=\"arrow-l-git-2-2-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-git-2-2-t1\">في الأعلى: main يشير إلى b2e0 و footer متقدّم بـ commitين على الخط نفسه، فينزلق main عند الدمج إلى f1a3 دون commit جديد. في الأسفل: لكل من main و dark-mode commit جديد بعد b2e0، فينشئ الدمج الـ merge commit ذا المعرّف m7c1 وله أبوان، ويتحرّك main إليه.</title>\n  <text class=\"d-label-strong\" x=\"20\" y=\"28\">Fast-forward: لم يتحرّك main</text>\n  <circle class=\"d-box\" cx=\"60\" cy=\"80\" r=\"22\"/>\n  <text class=\"d-code\" x=\"60\" y=\"85\" text-anchor=\"middle\">b2e0</text>\n  <circle class=\"d-box\" cx=\"160\" cy=\"80\" r=\"22\"/>\n  <text class=\"d-code\" x=\"160\" y=\"85\" text-anchor=\"middle\">e9d2</text>\n  <circle class=\"d-box\" cx=\"260\" cy=\"80\" r=\"22\"/>\n  <text class=\"d-code\" x=\"260\" y=\"85\" text-anchor=\"middle\">f1a3</text>\n  <path class=\"d-line\" d=\"M138 80 L82 80\"/>\n  <path class=\"d-line\" d=\"M238 80 L182 80\"/>\n  <text class=\"d-code\" x=\"300\" y=\"70\">footer</text>\n  <text class=\"d-code\" x=\"300\" y=\"92\">main (بعد)</text>\n  <text class=\"d-label-muted\" x=\"40\" y=\"128\">كان main (قبل) هنا، على b2e0</text>\n  <path class=\"d-arrow d-dashed\" d=\"M80 110 C140 125 220 125 250 106\" marker-end=\"url(#arrow-l-git-2-2-1)\"/>\n  <text class=\"d-label-strong\" x=\"20\" y=\"175\">Merge commit: تحرّك الفرعان كلاهما</text>\n  <circle class=\"d-box\" cx=\"60\" cy=\"235\" r=\"22\"/>\n  <text class=\"d-code\" x=\"60\" y=\"240\" text-anchor=\"middle\">b2e0</text>\n  <circle class=\"d-box\" cx=\"170\" cy=\"200\" r=\"22\"/>\n  <text class=\"d-code\" x=\"170\" y=\"205\" text-anchor=\"middle\">c39b</text>\n  <circle class=\"d-box\" cx=\"170\" cy=\"272\" r=\"22\"/>\n  <text class=\"d-code\" x=\"170\" y=\"277\" text-anchor=\"middle\">d4a7</text>\n  <circle class=\"d-box-primary\" cx=\"290\" cy=\"235\" r=\"22\"/>\n  <text class=\"d-code\" x=\"290\" y=\"240\" text-anchor=\"middle\">m7c1</text>\n  <path class=\"d-line\" d=\"M148 207 L80 228\"/>\n  <path class=\"d-line\" d=\"M148 265 L80 242\"/>\n  <path class=\"d-line\" d=\"M268 228 L192 207\"/>\n  <path class=\"d-line\" d=\"M268 242 L192 265\"/>\n  <text class=\"d-code\" x=\"325\" y=\"240\">main</text>\n  <text class=\"d-code\" x=\"200\" y=\"296\">dark-mode</text>\n  <text class=\"d-label-muted\" x=\"420\" y=\"225\">للـ commit m7c1 أبوان:</text>\n  <text class=\"d-label-muted\" x=\"420\" y=\"248\">c39b (main) و d4a7</text>\n</svg><figcaption>الـ fast-forward مقابل الـ merge commit</figcaption></figure>\n<h2 id=\"sec-4\" tabindex=\"-1\">الاختيار عن قصد</h2>\n<p>النتيجتان تحتويان الملفات النهائية نفسها. الاختلاف في طريقة قراءة السجلّ، وللفرق آراء في ذلك.</p>\n<ul>\n<li><strong>الـ fast-forward</strong> يُبقي السجلّ خطيًا وهادئًا. جيد للتغييرات الصغيرة، لكنك لاحقًا لا تستطيع أن ترى أيّ الـ commits كانت تنتمي إلى أيّ feature.</li>\n<li><strong>الـ merge commits</strong> تُبقي كل feature مجمّعة بشكل ظاهر، وتسجّل متى وصلت. ويصبح الرسم أكثر ازدحامًا.</li>\n</ul>\n<p>خياران يسمحان لك بالاختيار بدل ترك الرسم يقرّر:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --no-ff</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">     # always create a merge commit, even if fast-forward is possible</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --ff-only</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">   # fast-forward or refuse; never create a merge commit</span></span></code></pre></div>\n<p>خياري الافتراضي لموقع شخصي يعمل عليه شخص واحد: دع Git ينفّذ fast-forward للفروع الصغيرة، ولا ترهق نفسك بالتفكير في الأمر. أما في الفريق، فاتبع عُرف الفريق، وهو عادةً يُضبط على GitHub ولا يُكتب يدويًا. والأهمّ من السياسة بكثير أن يحمل كل فرع قطعة عمل واحدة متماسكة، فأيًّا كان الشكل الذي يتّخذه السجلّ، تكون كل خطوة فيه مفهومة.</p>\n<p><code dir=\"ltr\">--ff-only</code> عادة أمان جيدة حين لا تتوقّع إلا اللحاق بالتحديثات، مثلًا عند تحديث <code dir=\"ltr\">main</code> المحلي. إن رفض Git، فهذا خبر بحدّ ذاته: شيء ما تباعد دون علمك. وعلى GitHub ستقابل الاختيار نفسه في صورة طرق الدمج (merge methods) في الـ pull request في القسم الثالث.</p>\n<p>تستطيع فحص الـ merge base بنفسك قبل الدمج، وهذا يخبرك كم ابتعد الفرعان:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge-base</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dark-mode</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main..dark-mode</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # commits on dark-mode that main lacks</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dark-mode..main</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # and the reverse</span></span></code></pre></div>\n<p>إن كان ناتج أمر الـ log الثاني فارغًا، فـ <code dir=\"ltr\">main</code> لم يتحرّك وسيكون الدمج fast-forward.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>الدمج في الاتجاه الخطأ</span></p>\n<p>أنت على <code dir=\"ltr\">dark-mode</code>، وتشغّل <code dir=\"ltr\">git merge main</code>، ثم تتساءل لماذا لم يحصل <code dir=\"ltr\">main</code> على الثيم الداكن. الدمج ذهب إلى الفرع الذي كنت عليه. شغّل <code dir=\"ltr\">git branch</code> (أو انظر إلى الـ prompt في الطرفية) قبل كل دمج، وانتقل إلى الفرع المستقبِل، وادمج من هناك. دمج <code dir=\"ltr\">main</code> في فرع feature تقنية حقيقية للحاق بالتحديثات، لذا لن يوقفك Git.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">بعد الدمج</h2>\n<p>بعد دمج <code dir=\"ltr\">footer</code>، يكون اسم الفرع قد أدّى مهمّته. احذفه:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"6\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> branch</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span></span></code></pre></div>\n<p>ينجح <code dir=\"ltr\">-d</code> لأن الـ commits قابلة للوصول من <code dir=\"ltr\">main</code>. الـ commits نفسها تبقى في السجلّ إلى الأبد؛ والذي يذهب هو الورقة اللاصقة فقط. نظّف الفروع المدموجة أولًا بأول، فيبقى ناتج <code dir=\"ltr\">git branch</code> قائمة قصيرة بالعمل الجاري فعلًا.</p>\n<p>حتى الآن مرّ كل دمج بسلاسة لأن الفرعين لمسا أسطرًا مختلفة. في الدرس التالي ستجعلهما يتصادمان عن قصد، وتحلّ التعارض خطوة بخطوة.</p>\n","toc":[{"id":"sec-1","text":"قاعدة الاتجاه","level":2},{"id":"sec-2","text":"الحالة الأولى: fast-forward","level":2},{"id":"sec-3","text":"الحالة الثانية: دمج حقيقي","level":2},{"id":"sec-4","text":"الاختيار عن قصد","level":2},{"id":"sec-5","text":"بعد الدمج","level":2}],"takeaways":["أنت تدمج دائمًا فرعًا آخر في الفرع الذي أنت عليه؛ ولا يتحرّك إلا الفرع الحالي.","إن لم يتحرّك الفرع الحالي منذ بدأ الفرع الآخر، يقدّم Git المؤشّر إلى الأمام (fast-forward) ولا ينشئ commit جديدًا.","إن كان في الفرعين commits جديدة، ينفّذ Git دمجًا ثلاثيًا (three-way merge) انطلاقًا من سلفهما المشترك، ويسجّل merge commit له أبوان.","<code dir=\"ltr\">--no-ff</code> يفرض إنشاء merge commit ليُبقي الـ feature مجمّعة؛ و <code dir=\"ltr\">--ff-only</code> يرفض أي شيء غير الـ fast-forward."],"further":[{"title":"Basic Branching and Merging (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging"},{"title":"git merge reference","url":"https://git-scm.com/docs/git-merge"},{"title":"git merge-base reference","url":"https://git-scm.com/docs/git-merge-base"}],"quiz":[{"q":"<p>أنت على <code dir=\"ltr\">main</code>. منذ أنشأت <code dir=\"ltr\">footer</code> منه، لم يكسب <code dir=\"ltr\">main</code> أي commit، بينما كسب <code dir=\"ltr\">footer</code> ثلاثة. ماذا يفعل <code dir=\"ltr\">git merge footer</code>؟</p>","options":[{"text":"يقدّم <code dir=\"ltr\">main</code> إلى آخر commit في <code dir=\"ltr\">footer</code> دون إنشاء commit جديد.","why":"صحيح. <code dir=\"ltr\">main</code> سلف لـ <code dir=\"ltr\">footer</code>، لذا يستطيع Git تقديم المؤشّر (fast-forward). ويبقى السجلّ خطًا مستقيمًا."},{"text":"ينشئ merge commit له أبوان.","why":"هذا يحدث فقط حين يتحرّك الفرعان كلاهما، أو حين تطلبه صراحة بـ <code dir=\"ltr\">--no-ff</code>."},{"text":"ينسخ الـ commits الثلاثة إلى <code dir=\"ltr\">main</code> بمعرّفات جديدة.","why":"هذا وصف لعملية rebase أو cherry-pick. أما الـ merge فيعيد استخدام الـ commits الموجودة."},{"text":"يُرجع <code dir=\"ltr\">footer</code> إلى حيث يقف <code dir=\"ltr\">main</code>.","why":"الـ merge لا يحرّك أبدًا الفرع الذي تسمّيه، بل الفرع الذي أنت عليه فقط."}],"answer":0},{"q":"<p>كنت تقصد إدخال <code dir=\"ltr\">dark-mode</code> إلى <code dir=\"ltr\">main</code>، لكنك كنت على <code dir=\"ltr\">dark-mode</code> وشغّلت <code dir=\"ltr\">git merge main</code>. ما الذي حدث؟</p>","options":[{"text":"لا شيء؛ يكتشف Git أنك قصدت الاتجاه الآخر فيعكسهما.","why":"يفعل Git بالضبط ما طلبته. اتجاه الدمج يحدّده الفرع الذي أنت عليه."},{"text":"فشل الأمر لأنه لا يمكن دمج <code dir=\"ltr\">main</code> في فرع feature.","why":"يمكن ذلك، والفرق تفعله لتحديث فرع طويل العمر. إنه مسموح، لكنه ليس ما أردته هنا."},{"text":"صار <code dir=\"ltr\">main</code> يحتوي عمل الوضع الداكن.","why":"<code dir=\"ltr\">main</code> لم يتحرّك. وحده الفرع الذي أنت عليه، <code dir=\"ltr\">dark-mode</code>، يمكن أن يتغيّر أثناء الدمج."},{"text":"كسب <code dir=\"ltr\">dark-mode</code> الـ commits الجديدة من <code dir=\"ltr\">main</code>؛ و <code dir=\"ltr\">main</code> لم يتغيّر.","why":"صحيح. انتقل إلى <code dir=\"ltr\">main</code> بـ <code dir=\"ltr\">git switch main</code> وشغّل <code dir=\"ltr\">git merge dark-mode</code> لتدمج في الاتجاه الذي قصدته."}],"answer":3},{"q":"<p>أيّ commit يستخدمه Git نقطة انطلاق حين ينفّذ دمجًا ثلاثيًا لـ <code dir=\"ltr\">dark-mode</code> في <code dir=\"ltr\">main</code>؟</p>","options":[{"text":"أول commit في الـ repository.","why":"الـ root commit غالبًا أقدم بكثير مما يلزم. يريد Git أحدث نقطة مشتركة، ليقارن فقط ما غيّره كل طرف."},{"text":"أحدث commit يتشاركه الفرعان، أي الـ merge base الخاص بهما.","why":"صحيح. يقارن Git طرف كل فرع بالـ merge base، ويأخذ التغييرات التي جرت على أي من الطرفين، وينبّه إلى المواضع التي غيّر فيها الطرفان الأسطر نفسها."},{"text":"طرف الفرع صاحب الطابع الزمني الأقدم.","why":"الطوابع الزمنية لا تقرّر شيئًا في الدمج. الرسم البياني هو الذي يقرّر."}],"answer":1},{"q":"<p>يريد فريقك أن تظهر كل feature في سجلّ <code dir=\"ltr\">main</code> على شكل merge commit واحد، حتى حين يكون الـ fast-forward ممكنًا. أيّ خيار يحقّق ذلك؟</p>","options":[{"text":"<code dir=\"ltr\">git merge --ff-only feature</code>","why":"هذه هي السياسة المعاكسة. إنها تسمح بالـ fast-forward فقط وترفض إنشاء merge commits."},{"text":"<code dir=\"ltr\">git merge --squash feature</code>","why":"هذا يجهّز التغييرات المجمّعة دون إنشاء commit أو تسجيل أب ثانٍ، فيضيع الرابط بالفرع."},{"text":"<code dir=\"ltr\">git merge --no-ff feature</code>","why":"صحيح. يسجّل دائمًا merge commit، فتبقى commits الـ feature مجمّعة على خطها الخاص في الرسم."},{"text":"<code dir=\"ltr\">git merge --all feature</code>","why":"لا يوجد خيار <code dir=\"ltr\">--all</code> في <code dir=\"ltr\">git merge</code>."}],"answer":2}],"exercise":{"kind":"choice","multiple":false,"options":["a","b","c","d"],"correct":["b"],"title":"توقّع نتيجة الدمج","prompt":"<p>هذا هو الموقع الشخصي الآن. الأسهم تتّجه من كل commit إلى أبيه.</p>\n<p>:::figure قبل الدمج\n<svg viewBox=\"0 0 640 220\" role=\"img\" aria-labelledby=\"ex1\"></p>\n  <title id=\"ex1\">الـ commit ذو المعرّف a1 ثم b2. من b2 يكمل main إلى c3، ويشير إليه main والـ HEAD. ومن b2 أيضًا، في فرع projects-grid الـ commit ذو المعرّف d4 ثم e5، ويشير projects-grid إلى e5.</title>\n  <circle class=\"d-box\" cx=\"50\" cy=\"110\" r=\"24\"/>\n  <text class=\"d-code\" x=\"50\" y=\"115\" text-anchor=\"middle\">a1</text>\n  <circle class=\"d-box\" cx=\"160\" cy=\"110\" r=\"24\"/>\n  <text class=\"d-code\" x=\"160\" y=\"115\" text-anchor=\"middle\">b2</text>\n  <circle class=\"d-box\" cx=\"290\" cy=\"55\" r=\"24\"/>\n  <text class=\"d-code\" x=\"290\" y=\"60\" text-anchor=\"middle\">c3</text>\n  <circle class=\"d-box\" cx=\"290\" cy=\"165\" r=\"24\"/>\n  <text class=\"d-code\" x=\"290\" y=\"170\" text-anchor=\"middle\">d4</text>\n  <circle class=\"d-box\" cx=\"410\" cy=\"165\" r=\"24\"/>\n  <text class=\"d-code\" x=\"410\" y=\"170\" text-anchor=\"middle\">e5</text>\n  <path class=\"d-arrow\" d=\"M136 110 L78 110\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M267 64 L188 100\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M267 156 L188 120\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M386 165 L318 165\" marker-end=\"url(#arrow)\"/>\n  <rect class=\"d-box-accent\" x=\"340\" y=\"38\" width=\"90\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"385\" y=\"60\" text-anchor=\"middle\">main</text>\n  <rect class=\"d-box-warn\" x=\"450\" y=\"38\" width=\"90\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"495\" y=\"60\" text-anchor=\"middle\">HEAD</text>\n  <path class=\"d-line\" d=\"M450 55 L430 55\"/>\n  <path class=\"d-line\" d=\"M340 55 L318 55\"/>\n  <rect class=\"d-box-accent\" x=\"460\" y=\"148\" width=\"150\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"535\" y=\"170\" text-anchor=\"middle\">projects-grid</text>\n  <path class=\"d-line\" d=\"M460 165 L438 165\"/>\n</svg>\n:::\n<p>لمس الفرعان ملفات مختلفة. تشغّل <code dir=\"ltr\">git merge projects-grid</code>. كيف يبدو الرسم بعد ذلك؟</p>\n","hints":["على أيّ فرع يقف الـ HEAD؟ هذا هو الفرع الوحيد الذي يمكن أن يتحرّك.","هل كسب <code dir=\"ltr\">main</code> أي commits منذ انفصل عنه <code dir=\"ltr\">projects-grid</code> عند <code dir=\"ltr\">b2</code>؟","حين يكون في الطرفين commits جديدة، يصبح الـ fast-forward مستحيلًا."],"explanation":"<p>الـ HEAD على <code dir=\"ltr\">main</code>، إذن <code dir=\"ltr\">main</code> هو الفرع الذي يتحرّك. في الفرعين commits منذ الـ merge base المشترك <code dir=\"ltr\">b2</code> (<code dir=\"ltr\">c3</code> على <code dir=\"ltr\">main</code>؛ و <code dir=\"ltr\">d4</code> و <code dir=\"ltr\">e5</code> على <code dir=\"ltr\">projects-grid</code>)، لذا فالـ fast-forward مستحيل. ينفّذ Git دمجًا ثلاثيًا انطلاقًا من <code dir=\"ltr\">b2</code> ويسجّل merge commit جديدًا أبواه <code dir=\"ltr\">c3</code> و <code dir=\"ltr\">e5</code>. يتحرّك <code dir=\"ltr\">main</code> إليه؛ ويبقى <code dir=\"ltr\">projects-grid</code> على <code dir=\"ltr\">e5</code> حتى تحذفه.</p>\n","checkLabels":{},"optionText":{"a":{"text":"يتقدّم <code dir=\"ltr\">main</code> بـ fast-forward إلى <code dir=\"ltr\">e5</code>؛ ولا يُنشأ أي commit جديد.","why":"الـ fast-forward يتطلّب أن يكون <code dir=\"ltr\">main</code> سلفًا لـ <code dir=\"ltr\">e5</code>. لكن <code dir=\"ltr\">c3</code> ليس في سجلّ <code dir=\"ltr\">projects-grid</code>، فلا يستطيع Git أن ينزلق بـ <code dir=\"ltr\">main</code> على خط مستقيم."},"b":{"text":"يظهر merge commit جديد أبواه <code dir=\"ltr\">c3</code> و <code dir=\"ltr\">e5</code>؛ يتحرّك <code dir=\"ltr\">main</code> إليه ويبقى <code dir=\"ltr\">projects-grid</code> على <code dir=\"ltr\">e5</code>.","why":"صحيح. تحرّك الطرفان منذ <code dir=\"ltr\">b2</code>، فينشئ Git commit له أبوان على الفرع الحالي وحده."},"c":{"text":"يظهر merge commit جديد ويتحرّك <code dir=\"ltr\">main</code> و <code dir=\"ltr\">projects-grid</code> كلاهما إليه.","why":"لا يتحرّك أثناء الدمج إلا الفرع الذي أنت عليه. ويظل <code dir=\"ltr\">projects-grid</code> يشير إلى <code dir=\"ltr\">e5</code>."},"d":{"text":"يُنسخ <code dir=\"ltr\">d4</code> و <code dir=\"ltr\">e5</code> بعد <code dir=\"ltr\">c3</code> بمعرّفات جديدة، فيبقى السجلّ خطيًا.","why":"هذا ما يفعله الـ rebase. أما الـ merge فيحتفظ بالـ commits الأصلية ويصل الخطّين بـ merge commit."}}},"words":641,"readMinutes":4}