{"id":"l-git-2-4","courseId":"git-github","lang":"ar","kind":"lesson","summary":"نفّذ rebase لفرع feature على main لتُبقي السجلّ خطيًا، وتعامل مع التعارضات في منتصف الـ rebase، ورتّب الـ commits تفاعليًا، واعرف متى تقول القاعدة الذهبية إن عليك الدمج بدلًا من ذلك.","html":"<p>في الدرسين السابقين، كان جمع الفروع يعني الدمج، وكان السجلّ المتباعد يكسب merge commit. هذا صادق وآمن، لكن في repository مزدحم يبدأ <code dir=\"ltr\">git log --graph</code> بالظهور كخريطة قطارات. الـ rebase (إعادة التأسيس) هو الطريقة الأخرى لجمع العمل: بدل أن يصل خطّين، ينقل خطك بحيث لا يبقى إلا خط واحد.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">ما الذي يفعله الـ rebase</h2>\n<p>انفصل فرعك <code dir=\"ltr\">projects-grid</code> عن <code dir=\"ltr\">main</code> عند <code dir=\"ltr\">b2</code>. ومنذ ذلك الحين كسب <code dir=\"ltr\">main</code> الـ commit ذا المعرّف <code dir=\"ltr\">c3</code>، وفي فرعك <code dir=\"ltr\">d4</code> و <code dir=\"ltr\">e5</code>. من الفرع، شغّل:</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\"> projects-grid</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> rebase</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span></code></pre></div>\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>Successfully rebased and updated refs/heads/projects-grid.</code></pre></div>\n<p>وجد Git الـ commits الموجودة على فرعك وليست على <code dir=\"ltr\">main</code> (<code dir=\"ltr\">d4</code> و <code dir=\"ltr\">e5</code>)، ووضعها جانبًا، ونقل فرعك إلى طرف <code dir=\"ltr\">main</code>، ثم أعاد تشغيل كل تغيير فوقه، commitًا تلو الآخر.</p>\n<p>الـ commits المُعاد تشغيلها <strong>commits جديدة</strong>. التغييرات والرسائل نفسها، لكن الآباء مختلفون، فالمعرّفات مختلفة. هذه ليست تفصيلة؛ إنها القصة كلها لمعرفة متى يكون الـ rebase آمنًا. معرّف الـ commit يتضمّن أباه، لذا لا تستطيع نقل commit، بل تصنع نسخة منه في مكان آخر.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 280\" role=\"img\" aria-labelledby=\"l-git-2-4-t1\"><defs><marker id=\"arrow-l-git-2-4-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-4-t1\">قبل: للـ commit ذي المعرّف b2 ابنان، c3 على main، و d4 ثم e5 على projects-grid. بعد الـ rebase: ما زال main ينتهي عند c3؛ وتلي c3 على خط مستقيم commits جديدة هي d4' و e5'، ويشير projects-grid إلى e5'. أما d4 و e5 القديمان فباهتان بلا مرجع.</title>\n  <text class=\"d-label-strong\" x=\"20\" y=\"28\">قبل</text>\n  <circle class=\"d-box\" cx=\"50\" cy=\"100\" r=\"22\"/>\n  <text class=\"d-code\" x=\"50\" y=\"105\" text-anchor=\"middle\">b2</text>\n  <circle class=\"d-box\" cx=\"150\" cy=\"60\" r=\"22\"/>\n  <text class=\"d-code\" x=\"150\" y=\"65\" text-anchor=\"middle\">c3</text>\n  <circle class=\"d-box\" cx=\"150\" cy=\"140\" r=\"22\"/>\n  <text class=\"d-code\" x=\"150\" y=\"145\" text-anchor=\"middle\">d4</text>\n  <circle class=\"d-box\" cx=\"240\" cy=\"140\" r=\"22\"/>\n  <text class=\"d-code\" x=\"240\" y=\"145\" text-anchor=\"middle\">e5</text>\n  <path class=\"d-line\" d=\"M130 68 L70 92\"/>\n  <path class=\"d-line\" d=\"M130 132 L70 108\"/>\n  <path class=\"d-line\" d=\"M218 140 L172 140\"/>\n  <text class=\"d-code\" x=\"180\" y=\"55\">main</text>\n  <text class=\"d-code\" x=\"180\" y=\"185\">projects-grid</text>\n  <text class=\"d-label-strong\" x=\"350\" y=\"28\">بعد git rebase main</text>\n  <circle class=\"d-box\" cx=\"380\" cy=\"100\" r=\"22\"/>\n  <text class=\"d-code\" x=\"380\" y=\"105\" text-anchor=\"middle\">b2</text>\n  <circle class=\"d-box\" cx=\"470\" cy=\"100\" r=\"22\"/>\n  <text class=\"d-code\" x=\"470\" y=\"105\" text-anchor=\"middle\">c3</text>\n  <circle class=\"d-box-primary\" cx=\"560\" cy=\"100\" r=\"22\"/>\n  <text class=\"d-code\" x=\"560\" y=\"105\" text-anchor=\"middle\">d4'</text>\n  <circle class=\"d-box-primary\" cx=\"650\" cy=\"100\" r=\"22\"/>\n  <text class=\"d-code\" x=\"650\" y=\"105\" text-anchor=\"middle\">e5'</text>\n  <path class=\"d-line\" d=\"M448 100 L402 100\"/>\n  <path class=\"d-line\" d=\"M538 100 L492 100\"/>\n  <path class=\"d-line\" d=\"M628 100 L582 100\"/>\n  <text class=\"d-code\" x=\"450\" y=\"70\">main</text>\n  <text class=\"d-code\" x=\"600\" y=\"148\">projects-grid</text>\n  <circle class=\"d-box d-dashed\" cx=\"470\" cy=\"210\" r=\"22\"/>\n  <text class=\"d-label-muted\" x=\"470\" y=\"215\" text-anchor=\"middle\">d4</text>\n  <circle class=\"d-box d-dashed\" cx=\"560\" cy=\"210\" r=\"22\"/>\n  <text class=\"d-label-muted\" x=\"560\" y=\"215\" text-anchor=\"middle\">e5</text>\n  <path class=\"d-line d-dashed\" d=\"M448 202 L395 120\"/>\n  <path class=\"d-line d-dashed\" d=\"M538 210 L492 210\"/>\n  <text class=\"d-label-muted\" x=\"380\" y=\"262\">commits قديمة: لا فرع يشير إليها الآن</text>\n</svg><figcaption>الـ rebase يعيد تشغيل commits فرعك على قاعدة جديدة</figcaption></figure>\n<p>الآن صار <code dir=\"ltr\">main</code> سلفًا لفرعك، فإدخال العمل يتمّ بـ fast-forward:</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\"> projects-grid</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # Fast-forward</span></span></code></pre></div>\n<p>السجلّ خط مستقيم، كأنك بدأت العمل على الشبكة (grid) بعد تصحيح الخطأ الإملائي.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">التعارضات أثناء الـ rebase</h2>\n<p>لأن الـ rebase يعيد تشغيل الـ commits واحدًا تلو الآخر، فإن التعارض يوقفه عند commit بعينه. الروتين هو نفسه الذي تعلّمته في الدمج، مع كلمات مختلفة في النهاية:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"3\"><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:#6E7781;--shiki-dark:#8B949E\"># Git stops: CONFLICT (content): Merge conflict in projects.html</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># edit projects.html, remove the markers, check it</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> add</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> projects.html</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> rebase</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --continue</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">      # finish this commit, replay the next</span></span></code></pre></div>\n<p><code dir=\"ltr\">git rebase --abort</code> يعيد الفرع تمامًا إلى حيث كان قبل أن تبدأ. و <code dir=\"ltr\">git rebase --skip</code> يُسقط الـ commit المتعارض، ونادرًا ما يكون هذا ما تريده.</p>\n<p>مفاجأة واحدة: أثناء الـ rebase يتبادل “ours” و “theirs” موقعيهما. أنت تعيد تشغيل الـ commits الخاصة بك فوق <code dir=\"ltr\">main</code>، لذا فالـ HEAD (النصف العلوي من التعارض) هو طرف <code dir=\"ltr\">main</code>، والـ commit الخاص بك هو النصف السفلي. اقرأ الأسماء الدالّة، لا حدسك.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">الترتيب بالـ rebase التفاعلي</h2>\n<p>قبل أن تشارك فرعًا، تستطيع إعادة كتابة الـ commits الخاصة به. يفتح <code dir=\"ltr\">git rebase -i</code> قائمة مهام في محرّرك:</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\"> rebase</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -i</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"5\"><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>pick 4b1e2c0 Add projects grid\npick 9a3d7f1 fix typo\npick c2e8b44 Grid gap on mobile</code></pre></div>\n<p>غيّر الكلمة التي أمام الـ commit واحفظ. <code dir=\"ltr\">reword</code> يعدّل رسالته، و <code dir=\"ltr\">squash</code> أو <code dir=\"ltr\">fixup</code> يطويه في الـ commit الذي فوقه (مع الاحتفاظ برسالته أو التخلّص منها)، و <code dir=\"ltr\">drop</code> يحذفه، وإعادة ترتيب الأسطر تعيد ترتيب الـ commits. تغيير <code dir=\"ltr\">pick 9a3d7f1</code> إلى <code dir=\"ltr\">fixup 9a3d7f1</code> يحوّل ثلاثة commits إلى اثنين نظيفين. هكذا يُبقي المحترفون commits من نوع “fix typo” خارج الـ pull request.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">القاعدة الذهبية</h2>\n<p>الـ rebase يستبدل الـ commits بنسخ منها. هذا غير ضارّ حين تكون الوحيد الذي يملكها. لكنه فوضى حين يكون شخص آخر قد بنى على الأصلية: سجلّه ما زال يحتوي <code dir=\"ltr\">d4</code> و <code dir=\"ltr\">e5</code>، وسجلّك يحتوي <code dir=\"ltr\">d4'</code> و <code dir=\"ltr\">e5'</code>، والـ pull التالي ينتج commits مكرّرة وتعارضات محيّرة.</p>\n<p>إذن: <strong>لا تنفّذ rebase على commits موجودة خارج الـ repository الخاص بك وقد يكون الناس بنوا عليها عملًا.</strong> وعمليًا:</p>\n<ul>\n<li>نفّذ rebase بحرية على فروعك الخاصة قبل أن ترفعها أو قبل أن يلمسها أحد غيرك.</li>\n<li>لا تنفّذ rebase أبدًا على <code dir=\"ltr\">main</code> أو على أي فرع يسحب منه آخرون.</li>\n<li>تنفيذ rebase على فرع الـ pull request الخاص بك بعد رفعه أمر شائع ومقبول إن لم يكن أحد غيرك ينشئ commits عليه. سيرفض Git الرفع العادي لأن السجلّ أُعيدت كتابته، فاستخدم:</li>\n</ul>\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\"> push</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --force-with-lease</span></span></code></pre></div>\n<p><code dir=\"ltr\">--force-with-lease</code> يكتب فوق الفرع البعيد فقط إن كان ما زال يشير إلى حيث رأيته آخر مرة. وإن رفع زميل شيئًا في الأثناء، يرفض بدل أن يمحو عمله.</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>اللجوء إلى git push --force</span></p>\n<p><code dir=\"ltr\">--force</code> المجرّد يكتب فوق الفرع البعيد دون أي شرط. إن رفع مراجِع إصلاحًا إلى فرعك قبل ساعة، فقد ضاع، ولا شيء على جهازك يحذّرك. اجعل <code dir=\"ltr\">--force-with-lease</code> طريقتك الوحيدة للرفع بالقوة، ولا ترفع بالقوة إلى <code dir=\"ltr\">main</code> أبدًا. وفي القسم الرابع ستجعل GitHub يرفض مثل هذه العمليات كليًا.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">الـ merge أم الـ rebase؟</h2>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th></th>\n<th>Merge</th>\n<th>Rebase</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>السجلّ</td>\n<td>سجلّ صادق، مع merge commits</td>\n<td>خطّي، كأن العمل جرى بالتتابع</td>\n</tr>\n<tr>\n<td>معرّفات الـ commits</td>\n<td>لا تتغيّر</td>\n<td>تُعاد كتابتها</td>\n</tr>\n<tr>\n<td>آمن على الفروع المشتركة</td>\n<td>نعم</td>\n<td>لا</td>\n</tr>\n<tr>\n<td>التعارضات</td>\n<td>تُحلّ مرة واحدة</td>\n<td>قد تتكرّر مع كل commit يُعاد تشغيله</td>\n</tr>\n</tbody>\n</table></div><p>خيار افتراضي معقول: نفّذ rebase لفرع الـ feature الخاص بك على <code dir=\"ltr\">main</code> لتبقى محدَّثًا ولترتّبه قبل المراجعة؛ وادمجه (غالبًا عبر pull request) لإيصاله. وكثير من الفرق تضبط أيضًا <code dir=\"ltr\">git pull</code> ليعيد تأسيس الـ commits التي لم تُرفع بعد، وهذا ما ستضبطه في القسم التالي، حين يحصل موقعك الشخصي أخيرًا على remote.</p>\n","toc":[{"id":"sec-1","text":"ما الذي يفعله الـ rebase","level":2},{"id":"sec-2","text":"التعارضات أثناء الـ rebase","level":2},{"id":"sec-3","text":"الترتيب بالـ rebase التفاعلي","level":2},{"id":"sec-4","text":"القاعدة الذهبية","level":2},{"id":"sec-5","text":"الـ merge أم الـ rebase؟","level":2}],"takeaways":["<code dir=\"ltr\">git rebase main</code> يعيد تشغيل commits فرعك فوق طرف <code dir=\"ltr\">main</code>، فينشئ commits جديدة بمعرّفات جديدة.","بعد الـ rebase، يصبح دمج الفرع في <code dir=\"ltr\">main</code> عملية fast-forward، فيبقى السجلّ خطًا مستقيمًا.","أثناء تعارض الـ rebase، أصلح الملف، ونفّذ <code dir=\"ltr\">git add</code> له، ثم <code dir=\"ltr\">git rebase --continue</code>؛ و <code dir=\"ltr\">git rebase --abort</code> يلغي الـ rebase بكامله.","القاعدة الذهبية ألّا تنفّذ rebase أبدًا على commits ربما بنى عليها آخرون؛ نفّذه فقط على عملك الخاص غير المشارَك.","إن اضطررت إلى تحديث فرع رفعته بالفعل، فاستخدم <code dir=\"ltr\">git push --force-with-lease</code>، ولا تستخدم <code dir=\"ltr\">--force</code> المجرّد أبدًا."],"further":[{"title":"Rebasing (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Branching-Rebasing"},{"title":"git rebase reference","url":"https://git-scm.com/docs/git-rebase"},{"title":"About Git rebase (GitHub Docs)","url":"https://docs.github.com/en/get-started/using-git/about-git-rebase"}],"quiz":[{"q":"<p>في فرعك <code dir=\"ltr\">projects-grid</code> الـ commitان <code dir=\"ltr\">d4</code> و <code dir=\"ltr\">e5</code>. تشغّل عليه <code dir=\"ltr\">git rebase main</code>. ماذا يحدث لـ <code dir=\"ltr\">d4</code> و <code dir=\"ltr\">e5</code>؟</p>","options":[{"text":"يُنقلان، محتفظين بمعرّفاتهما، ليقعا بعد طرف <code dir=\"ltr\">main</code>.","why":"لا يمكن نقل الـ commits، لأن معرّف الـ commit يتضمّن أباه. أب جديد يعني commit جديدًا."},{"text":"يُدمجان في commit واحد على <code dir=\"ltr\">main</code>.","why":"لا يضغط الـ rebase الـ commits افتراضيًا ولا يلمس <code dir=\"ltr\">main</code>. إنه يعيد كتابة الفرع الذي أنت عليه."},{"text":"ينشئ Git commits جديدة بالتغييرات نفسها فوق <code dir=\"ltr\">main</code>؛ ويشير الفرع إليها، وتصبح الأصلية بلا مرجع.","why":"صحيح. الـ commits المُعاد تشغيلها (وتُكتب غالبًا <code dir=\"ltr\">d4'</code> و <code dir=\"ltr\">e5'</code>) لها معرّفات جديدة. وتبقى الأصلية في الـ reflog لفترة."},{"text":"يُحذفان وعليك إعادة تسجيل العمل.","why":"يعيد الـ rebase تشغيل تغييراتك تلقائيًا. لا شيء يحتاج إلى إعادة يدوية ما لم يقع تعارض."}],"answer":2},{"q":"<p>في منتصف الـ rebase، يبلّغ Git عن تعارض في <code dir=\"ltr\">projects.html</code>. حرّرت الملف حتى صار سليمًا. ماذا تشغّل بعد ذلك؟</p>","options":[{"text":"<code dir=\"ltr\">git commit -m &quot;Fix conflict&quot;</code>","why":"أثناء الـ rebase، ينشئ Git الـ commits بنفسه. إنشاء commit يدويًا يضيف commit زائدًا ويربك إعادة التشغيل."},{"text":"<code dir=\"ltr\">git add projects.html</code>، ثم <code dir=\"ltr\">git rebase --continue</code>","why":"صحيح. التجهيز يعلّم التعارض بأنه حُلّ؛ و <code dir=\"ltr\">--continue</code> ينهي ذلك الـ commit ويعيد تشغيل البقية."},{"text":"<code dir=\"ltr\">git merge --continue</code>","why":"أنت في rebase، لا في merge. لكل عملية أمر الإكمال الخاص بها."},{"text":"<code dir=\"ltr\">git rebase main</code> مجددًا من البداية","why":"هناك rebase قيد التنفيذ أصلًا، فيرفض Git بدء آخر. أكمل الحالي أو ألغِه."}],"answer":1},{"q":"<p>أيّ الحالات التالية تخرق القاعدة الذهبية للـ rebase؟</p>","options":[{"text":"تنفيذ rebase لفرعك المحلي <code dir=\"ltr\">footer</code> الذي لم يُرفع قط على <code dir=\"ltr\">main</code>.","why":"لا أحد غيرك يملك هذه الـ commits، فإعادة كتابتها لا تؤثّر إلا فيك. هذا هو الاستخدام المثالي للـ rebase."},{"text":"استخدام <code dir=\"ltr\">git rebase -i HEAD~3</code> لضغط إصلاحات الأخطاء الإملائية قبل فتح pull request.","why":"ترتيب الـ commits الخاصة بك التي لم تشاركها هو بالضبط الغرض من الـ rebase التفاعلي."},{"text":"تشغيل <code dir=\"ltr\">git pull --rebase</code> لوضع الـ commits التي لم ترفعها بعد فوق الـ commits الجديدة لزملائك.","why":"هذا يعيد كتابة الـ commits المحلية التي لم تُرفع فقط، لذا فهو آمن وشائع."},{"text":"تنفيذ rebase لفرع <code dir=\"ltr\">main</code> المشترك ورفعه بالقوة (force-push) بينما لدى زملائك عمل مبني عليه.","why":"صحيح. commits زملائك تقع فوق المعرّفات القديمة التي استبدلتها للتو، فينتج عن الـ pull التالي لديهم commits مكرّرة وتعارضات محيّرة."}],"answer":3},{"q":"<p>نفّذت rebase لفرع الـ pull request الخاص بك، والآن يُرفض <code dir=\"ltr\">git push</code> لأنه ليس fast-forward. ما الطريقة الآمنة لتحديثه؟</p>","options":[{"text":"<code dir=\"ltr\">git push --force-with-lease</code>","why":"صحيح. يكتب فوق الفرع البعيد فقط إن كان ما زال حيث رأيته آخر مرة، فلن تمحو بصمت commit رفعه شخص آخر منذ آخر fetch لك."},{"text":"<code dir=\"ltr\">git push --force</code>","why":"هذا يكتب فوق الـ remote مهما كان، بما في ذلك commits رفعها مراجِع منذ آخر fetch لك."},{"text":"<code dir=\"ltr\">git pull</code> ثم الرفع.","why":"الـ pull يدمج الـ commits القديمة مرة أخرى بجانب نسخها المُعاد تأسيسها، فتبقى commits مكرّرة في الفرع."}],"answer":0}],"exercise":{"kind":"fill","language":"bash","template":"git switch projects-grid\ngit {{0}} main                  # replay my commits on top of main\n# CONFLICT in projects.html: edit it, then\ngit add projects.html\ngit rebase {{1}}               # replay the remaining commits\n\n# this branch was already pushed for review, so update it safely\ngit push {{2}}","blanks":[{"answers":["rebase"]},{"answers":["--continue"]},{"answers":["--force-with-lease"]}],"title":"نفّذ rebase لشبكة المشاريع","prompt":"<p>فرعك <code dir=\"ltr\">projects-grid</code> متأخّر عن <code dir=\"ltr\">main</code>، وتريد سجلًّا خطيًا. املأ الفراغات لتنفّذ rebase له، وتتجاوز تعارضًا واحدًا، ثم تحدّث الفرع الذي رفعته أصلًا للمراجعة دون أن تخاطر بـ commits أي شخص آخر.</p>\n","hints":["الأمر الذي تحتاجه يحمل اسم العملية نفسها.","بعد <code dir=\"ltr\">git add</code>، يحتاج الـ rebase الجاري إلى كلمة أخرى ليواصل.","هناك خيار للرفع بالقوة يتحقّق من أن الـ remote لم يتغيّر منذ آخر fetch لك. إنه طويل، ويتضمّن كلمة lease."],"explanation":"<p><code dir=\"ltr\">git rebase main</code> يعيد تشغيل commits الفرع فوق طرف <code dir=\"ltr\">main</code>. وحين يتعارض commit مُعاد تشغيله، تصلح الملف، وتنفّذ <code dir=\"ltr\">git add</code> له، وتشغّل <code dir=\"ltr\">git rebase --continue</code> (أو <code dir=\"ltr\">--abort</code> لتستسلم). الـ commits المُعاد تأسيسها لها معرّفات جديدة، لذا يُرفض الرفع العادي؛ و <code dir=\"ltr\">git push --force-with-lease</code> يستبدل الفرع البعيد فقط إن لم يرفع إليه أحد منذ آخر مرة نظرت فيها. هذا مقبول على فرع المراجعة الخاص بك، وغير مقبول أبدًا على <code dir=\"ltr\">main</code>.</p>\n","checkLabels":{}},"words":677,"readMinutes":4}