{"id":"l-git-4-1","courseId":"git-github","lang":"ar","kind":"lesson","summary":"اختر طريقة التراجع المناسبة لأي خطأ (restore أو amend أو revert أو reset بأوضاعه الثلاثة)، واستخدم الـ reflog لاسترجاع commits ظننت أنها ضاعت.","html":"<p>كل شخص يكسر شيئًا في Git عاجلًا أو آجلًا. تنشئ commit على الفرع الخطأ، أو ترفع خطأً برمجيًا، أو تمحو عمل صباح كامل بـ reset. والخبر الجيد: نادرًا ما يرمي Git البيانات، ولمعظم الأخطاء طريقة تراجع نظيفة. الفخّ هو أن تلجأ إلى أي أمر تراجع تتذكّره نصف تذكّر. اختر بطرح سؤال واحد: <strong>أين يعيش الخطأ؟</strong></p>\n<h2 id=\"sec-1\" tabindex=\"-1\">اختر الأداة بحسب المكان</h2>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>أين الخطأ</th>\n<th>الأمر</th>\n<th>هل يعيد كتابة السجلّ؟</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>تعديلات غير مجهّزة في ملف</td>\n<td><code dir=\"ltr\">git restore &lt;file&gt;</code></td>\n<td>لا (لكنه يرمي التعديلات)</td>\n</tr>\n<tr>\n<td>مجهّز، لم يُسجَّل في commit</td>\n<td><code dir=\"ltr\">git restore --staged &lt;file&gt;</code></td>\n<td>لا</td>\n</tr>\n<tr>\n<td>آخر commit، لم يُرفع</td>\n<td><code dir=\"ltr\">git commit --amend</code></td>\n<td>نعم، الـ commit المحلي فقط</td>\n</tr>\n<tr>\n<td>commits محلية، لم تُرفع</td>\n<td><code dir=\"ltr\">git reset</code></td>\n<td>نعم، محليًا فقط</td>\n</tr>\n<tr>\n<td>commits رُفعت وتمّت مشاركتها</td>\n<td><code dir=\"ltr\">git revert &lt;commit&gt;</code></td>\n<td>لا</td>\n</tr>\n</tbody>\n</table></div><p>يتبع الجدول قاعدة واحدة: حين يصبح commit ما لدى آخرين، تضيف فوقه commits جديدة بدل أن تغيّره.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الـ Amend: أصلح آخر commit</h2>\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\"> add</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> contact.css</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> commit</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --amend</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --no-edit</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">              # same message, file included</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> commit</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --amend</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -m</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Add contact form\"</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">  # or a new message</span></span></code></pre></div>\n<p>الـ amend لا يعدّل الـ commit؛ بل يستبدله بآخر جديد بمعرّف جديد. هذا مقبول محليًا، ومشكلة إن كنت قد رفعت، للسبب نفسه الذي يجعل الـ rebase مشكلة.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">الـ Revert: التراجع علنًا</h2>\n<p>دمجت في <code dir=\"ltr\">main</code> commit عطّل قائمة التنقّل على الجوال. وقد رُفع، وسحبه زملاؤك. لا تُعِد كتابة أي شيء. أضف commit يعكسه:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"1\"><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\"> revert</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">          # if it's the latest commit</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> revert</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 8f1e2aa</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">       # or any commit by ID</span></span></code></pre></div>\n<p>ينشئ Git commit جديدًا، <code dir=\"ltr\">Revert &quot;Move nav into header&quot;</code>، يطبّق التغييرات المعاكسة تمامًا. ويظل السجلّ يُظهر الخطأ وإصلاحه، وهذا صادق وآمن: يحصل الجميع على الـ revert بـ pull عادي. وللتراجع عن merge commit، أخبر Git أيّ الأبوين يُعتمد خطًا رئيسيًا (mainline)، وهو الأول عادةً: <code dir=\"ltr\">git revert -m 1 &lt;merge-commit&gt;</code>.</p>\n<p>أحيانًا يكون جزء فقط من الـ commit خاطئًا. إن كان نقل قائمة التنقّل سليمًا باستثناء <code dir=\"ltr\">styles.css</code>، فلا داعي للتراجع عن الـ commit كله. أعِد ذلك الملف وحده إلى ما كان عليه قبل الـ commit، وافحصه، وسجّل النتيجة في commit إصلاح عادي:</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\"> restore</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --source=8f1e2aa~1</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> styles.css</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> diff</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> commit</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -am</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Restore mobile nav styles\"</span></span></code></pre></div>\n<p>هذا ما زال «إضافة إلى السجلّ لا إعادة كتابة له»، لكن بنطاق أصغر.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">الـ Reset: حرّك الفرع، بثلاث طرق</h2>\n<p><code dir=\"ltr\">git reset &lt;commit&gt;</code> يحرّك الفرع الحالي إلى commit آخر. وما يفعله بالـ index والـ working tree يعتمد على الوضع (mode)، وهنا يتأذّى الناس.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 260\" role=\"img\" aria-labelledby=\"l-git-4-1-t1\"><defs><marker id=\"arrow-l-git-4-1-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-4-1-t1\">جدول من ثلاثة صفوف. reset --soft يحرّك الفرع فقط؛ ويحتفظ الـ index والـ working tree بالتغييرات، وتصبح مجهّزة. reset --mixed، وهو الافتراضي، يحرّك الفرع ويعيد ضبط الـ index؛ ويحتفظ الـ working tree بالتغييرات، وتصبح غير مجهّزة. reset --hard يحرّك الفرع ويعيد ضبط الـ index والـ working tree كليهما، فتُرمى التغييرات.</title>\n  <text class=\"d-label-strong\" x=\"190\" y=\"30\" text-anchor=\"middle\">الفرع (HEAD)</text>\n  <text class=\"d-label-strong\" x=\"380\" y=\"30\" text-anchor=\"middle\">Index</text>\n  <text class=\"d-label-strong\" x=\"570\" y=\"30\" text-anchor=\"middle\">Working tree</text>\n  <text class=\"d-code\" x=\"20\" y=\"82\">--soft</text>\n  <rect class=\"d-box-primary\" x=\"120\" y=\"55\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"190\" y=\"82\" text-anchor=\"middle\">يتحرّك</text>\n  <rect class=\"d-box-success\" x=\"310\" y=\"55\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"380\" y=\"82\" text-anchor=\"middle\">يُحتفظ به</text>\n  <rect class=\"d-box-success\" x=\"500\" y=\"55\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"570\" y=\"82\" text-anchor=\"middle\">يُحتفظ به</text>\n  <text class=\"d-code\" x=\"20\" y=\"142\">--mixed</text>\n  <rect class=\"d-box-primary\" x=\"120\" y=\"115\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"190\" y=\"142\" text-anchor=\"middle\">يتحرّك</text>\n  <rect class=\"d-box-primary\" x=\"310\" y=\"115\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"380\" y=\"142\" text-anchor=\"middle\">يُعاد ضبطه</text>\n  <rect class=\"d-box-success\" x=\"500\" y=\"115\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"570\" y=\"142\" text-anchor=\"middle\">يُحتفظ به</text>\n  <text class=\"d-code\" x=\"20\" y=\"202\">--hard</text>\n  <rect class=\"d-box-primary\" x=\"120\" y=\"175\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"190\" y=\"202\" text-anchor=\"middle\">يتحرّك</text>\n  <rect class=\"d-box-primary\" x=\"310\" y=\"175\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"380\" y=\"202\" text-anchor=\"middle\">يُعاد ضبطه</text>\n  <rect class=\"d-box-warn\" x=\"500\" y=\"175\" width=\"140\" height=\"44\" rx=\"8\"/>\n  <text class=\"d-label-strong\" x=\"570\" y=\"202\" text-anchor=\"middle\">يُكتب فوقه</text>\n  <text class=\"d-label-muted\" x=\"350\" y=\"248\" text-anchor=\"middle\">--mixed هو الافتراضي حين لا تحدّد وضعًا</text>\n</svg><figcaption>أوضاع الـ reset الثلاثة والمناطق الثلاث</figcaption></figure>\n<p>ثلاث حالات، وثلاثة أوضاع:</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:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> reset</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --soft</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD~2</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # squash: the last two commits' changes become staged, ready for one commit</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> reset</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD~1</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">           # un-commit (--mixed): changes return as unstaged edits to rework</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> reset</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --hard</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD~1</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # discard: the commit and its changes are gone from the branch and files</span></span></code></pre></div>\n<p>استخدام كلاسيكي: أنشأت commit على <code dir=\"ltr\">main</code> بينما كنت تقصد أن تكون على فرع. سجّل أي تعديلات غير مسجّلة أو خبّئها أولًا (فـ <code dir=\"ltr\">--hard</code> سيمحوها)، ثم أنشئ الفرع حيث أنت، ثم أرجِع <code dir=\"ltr\">main</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\"> branch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> contact-form</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">        # the branch keeps your commit</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> reset</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --hard</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin/main</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">   # main goes back to match GitHub</span></span>\n<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\"> contact-form</span></span></code></pre></div>\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>استخدام reset على commits مرفوعة</span></p>\n<p>تنفيذ <code dir=\"ltr\">git reset</code> على فرع رفعته بالفعل يجعل فرعك المحلي مختلفًا عن GitHub، والطريقة الوحيدة لنشره رفع بالقوة يعيد كتابة سجلّ الجميع. إن كان الـ commit مشارَكًا، فاستخدم <code dir=\"ltr\">git revert</code>. واحتفظ بالـ <code dir=\"ltr\">reset</code> للـ commits التي لم تغادر جهازك قط.</p>\n</aside>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>ضع علامة قبل أي خطوة محفوفة بالمخاطر</span></p>\n<p>قبل الـ reset، أو rebase كبير، أو أي أمر لست متأكّدًا منه، شغّل <code dir=\"ltr\">git branch backup-nav</code>. لا يكلّف شيئًا، وإن لم تكن النتيجة كما أردت، يعيدك <code dir=\"ltr\">git reset --hard backup-nav</code> مباشرة إلى حيث كنت. احذف العلامة بـ <code dir=\"ltr\">git branch -D backup-nav</code> حين تطمئن.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">الـ Reflog: الصندوق الأسود في Git</h2>\n<p>هذه هي شبكة الأمان التي تجعل كل ما سبق أقل إخافة. في كل مرة يتحرّك فيها الـ HEAD (commit، أو switch، أو reset، أو rebase، أو merge)، يكتب Git سطرًا في الـ <strong>reflog</strong>:</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\"> reflog</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"6\"><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>2b7f0c1 (HEAD -&gt; main) HEAD@{0}: reset: moving to HEAD~3\n9e4a1d5 HEAD@{1}: commit: Add contact form validation\n4c8b2e7 HEAD@{2}: commit: Add contact form\na71d3f0 HEAD@{3}: checkout: moving from footer to main</code></pre></div>\n<p>بعد ذلك الـ <code dir=\"ltr\">git reset --hard HEAD~3</code> العرَضي، لم تعد الـ commits الثلاثة على أي فرع، لكنها ما زالت موجودة، و <code dir=\"ltr\">HEAD@{1}</code> يشير إلى أحدثها. استعدها:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"7\"><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\"> reset</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --hard</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD@{</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">          # put main back where it was</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># or, more cautiously, keep them on a new branch to inspect first</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -c</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> rescue</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 9e4a1d5</span></span></code></pre></div>\n<p>الـ reflog محلي (لا يُرفع ولا يُستنسخ)، وسطوره تنتهي صلاحيتها: افتراضيًا بعد 90 يومًا، أو 30 يومًا للـ commits التي لا يصل إليها أي فرع. وهذا وقت أكثر من كافٍ لملاحظة الخطأ.</p>\n<p>ما لا يستطيع الـ reflog إنقاذه هو العمل الذي لم يُسجَّل قط. فـ <code dir=\"ltr\">git restore</code> و <code dir=\"ltr\">git reset --hard</code> يكتبان فوق التعديلات غير المسجّلة في ملفاتك، ولا يعرف أي سجلّ عنها شيئًا. لذا فعادة الأمان الحقيقية بسيطة: أنشئ commits مبكرًا وكثيرًا على فرعك. تستطيع دائمًا ضغط الـ commits قبل المراجعة؛ لكنك لا تستطيع استعادة تعديلات لم تسجّلها قط.</p>\n<p>لكن بعض العمل الجاري ليس جاهزًا ليُسجَّل في commit. في الدرس التالي ستركنه جانبًا بـ <code dir=\"ltr\">git stash</code>، وتمنع Git من تتبّع ملفات ما كان يجب أن يتتبّعها أصلًا.</p>\n","toc":[{"id":"sec-1","text":"اختر الأداة بحسب المكان","level":2},{"id":"sec-2","text":"الـ Amend: أصلح آخر commit","level":2},{"id":"sec-3","text":"الـ Revert: التراجع علنًا","level":2},{"id":"sec-4","text":"الـ Reset: حرّك الفرع، بثلاث طرق","level":2},{"id":"sec-5","text":"الـ Reflog: الصندوق الأسود في Git","level":2}],"takeaways":["اختر طريقة التراجع بحسب مكان الخطأ (الـ working tree أو الـ index أو commit محلي أو commit مشارَك)، لا بحسب الأمر الذي تتذكّره.","<code dir=\"ltr\">git revert &lt;commit&gt;</code> يضيف commit جديدًا يلغي commit قديمًا، وهي الطريقة الآمنة للتراجع عن أي شيء رُفع بالفعل.","<code dir=\"ltr\">git reset</code> يحرّك الفرع الحالي: <code dir=\"ltr\">--soft</code> يُبقي التغييرات مجهّزة، و <code dir=\"ltr\">--mixed</code> يُبقيها غير مجهّزة، و <code dir=\"ltr\">--hard</code> يرميها.","<code dir=\"ltr\">git commit --amend</code> يستبدل آخر commit، لذا استخدمه قبل الرفع فقط.","<code dir=\"ltr\">git reflog</code> يسرد الأماكن التي مرّ بها الـ HEAD، فيمكن العثور على commits ضاعت بسبب reset أو rebase واستعادتها لأسابيع بعد ذلك."],"further":[{"title":"Undoing Things (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things"},{"title":"Reset Demystified (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified"},{"title":"git revert reference","url":"https://git-scm.com/docs/git-revert"},{"title":"git reflog reference","url":"https://git-scm.com/docs/git-reflog"}],"quiz":[{"q":"<p>أنشأت commit باسم “Add contact form” قبل دقيقة، ولم ترفعه، ثم أدركت أنك نسيت تجهيز <code dir=\"ltr\">contact.css</code>. ما الإصلاح الأنظف؟</p>","options":[{"text":"<code dir=\"ltr\">git revert HEAD</code>، ثم أنشئ commit من جديد بالملفين.","why":"هذا يترك commitين زائدين (الأصلي وعكسه) في السجلّ بسبب خطأ لم يره أحد غيرك."},{"text":"<code dir=\"ltr\">git reset --hard HEAD~1</code> وأعِد العمل.","why":"<code dir=\"ltr\">--hard</code> يرمي التغييرات المسجّلة من ملفاتك. ستعيد كتابة النموذج بيدك."},{"text":"<code dir=\"ltr\">git add contact.css</code>، ثم <code dir=\"ltr\">git commit --amend --no-edit</code>.","why":"صحيح. الـ amend يستبدل آخر commit غير مرفوع بآخر يتضمّن الملف؛ و <code dir=\"ltr\">--no-edit</code> يُبقي الرسالة."},{"text":"<code dir=\"ltr\">git restore --staged contact.css</code>","why":"هذا يلغي تجهيز ملف. و <code dir=\"ltr\">contact.css</code> ليس مجهّزًا بعد، وسيظل الـ commit يفتقده."}],"answer":2},{"q":"<p>بالأمس دمجت في <code dir=\"ltr\">main</code> commit عطّل قائمة التنقّل على الجوال. وقد رُفع وسحبه زملاؤك. كيف تتراجع عنه؟</p>","options":[{"text":"<code dir=\"ltr\">git revert &lt;commit&gt;</code> وارفع الـ commit الجديد.","why":"صحيح. الـ revert يسجّل commit جديدًا يطبّق التغيير المعاكس، فلا تُعاد كتابة سجلّ أحد، ويحصل الجميع على الإصلاح بـ pull عادي."},{"text":"<code dir=\"ltr\">git reset --hard &lt;commit&gt;~1</code> ثم <code dir=\"ltr\">git push --force</code>.","why":"هذا يعيد كتابة السجلّ المشترك. زملاؤك ما زال لديهم الـ commit السيئ، ورفعهم التالي سيعيده أو يُحدث فوضى."},{"text":"<code dir=\"ltr\">git commit --amend</code> لتعديل الـ commit السيئ وإزالته.","why":"الـ amend لا يلمس إلا آخر commit لديك، وينشئ معرّفًا جديدًا، وسيحتاج أيضًا إلى رفع بالقوة إلى فرع مشترك."},{"text":"<code dir=\"ltr\">git restore --source=HEAD~1 .</code> ثم الرفع دون commit.","why":"استرجاع الملفات يغيّر الـ working tree فقط. ولا يُرفع شيء حتى يُسجَّل في commit."}],"answer":0},{"q":"<p>تشغّل <code dir=\"ltr\">git reset --soft HEAD~2</code> على commitين غير مرفوعين. أين تغييراتهما الآن؟</p>","options":[{"text":"حُذفت من ملفاتك.","why":"هذا ما يفعله <code dir=\"ltr\">--hard</code>. أما <code dir=\"ltr\">--soft</code> فلا يلمس الـ index ولا الـ working tree أبدًا."},{"text":"في الـ working tree لكن غير مجهّزة.","why":"هذا هو الوضع الافتراضي <code dir=\"ltr\">--mixed</code>، الذي يعيد ضبط الـ index أيضًا."},{"text":"ما زالت في الـ commitين، وقد صارا على فرع جديد.","why":"الـ reset لا ينشئ فروعًا. إنه يحرّك مؤشّر الفرع الحالي."},{"text":"مجهّزة في الـ index، جاهزة لتُسجَّل من جديد في commit واحد.","why":"صحيح. تراجع الفرع commitين لكن الـ index والملفات احتفظت بكل شيء، وهي طريقة سريعة لضغط commits محلية."}],"answer":3},{"q":"<p>شغّلت <code dir=\"ltr\">git reset --hard HEAD~3</code> بالخطأ فاختفت ثلاثة commits من <code dir=\"ltr\">git log</code>. كيف تستعيدها؟</p>","options":[{"text":"ضاعت؛ فـ <code dir=\"ltr\">--hard</code> يحذف الـ commits نهائيًا.","why":"الـ reset يحرّك مؤشّرًا. والـ commits ما زالت موجودة، والـ reflog يتذكّر أين كان الـ HEAD."},{"text":"شغّل <code dir=\"ltr\">git reflog</code>، وجِد السطر السابق للـ reset، ثم <code dir=\"ltr\">git reset --hard HEAD@{1}</code>.","why":"صحيح. <code dir=\"ltr\">HEAD@{1}</code> يعني مكان الـ HEAD قبل حركة واحدة، أي قبل الـ reset مباشرة."},{"text":"شغّل <code dir=\"ltr\">git revert HEAD~3</code>.","why":"الـ revert ينشئ commit جديدًا يتراجع عن تغيير. ولا يعيد commits ضائعة إلى فرعك."},{"text":"شغّل <code dir=\"ltr\">git pull</code>، فهو يستعيد أي commits مفقودة.","why":"فقط إن كانت قد رُفعت. الـ pull يجلب ما هو موجود على الـ remote؛ أما الـ commits غير المرفوعة فموجودة في الـ repository المحلي وحده."}],"answer":1}],"exercise":{"kind":"choice","multiple":false,"options":["a","b","c","d"],"correct":["b"],"title":"اختر طريقة التراجع","prompt":"<p>أحدث commit لديك على <code dir=\"ltr\">main</code>، وهو <code dir=\"ltr\">8f1e2aa Move nav into header</code>، عطّل قائمة الجوال. رفعته قبل ساعة، وسحبه زميلان بالفعل. ويعرض <code dir=\"ltr\">git status</code> working tree نظيفًا.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"0\"><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>8f1e2aa (HEAD -&gt; main, origin/main) Move nav into header\nc39be11 Add projects page\nb2e04f3 Add navigation bar with base styles</code></pre></div>\n<p>أيّ أمر يجب أن تشغّله قبل أن ترفع مجددًا؟</p>\n","hints":["اسأل أين يعيش الخطأ: في ملفاتك، أم في الـ index، أم في commit محلي، أم في commit مشارَك؟","أي شيء يغيّر <code dir=\"ltr\">8f1e2aa</code> نفسه سيحتاج إلى رفع بالقوة إلى فرع سحبه آخرون.","تريد أن تضيف إلى السجلّ، لا أن تعيد كتابته."],"explanation":"<p>الـ commit السيئ مشارَك بالفعل، لذا فإعادة كتابته (بالـ amend أو الـ reset) ستجبر الجميع على إصلاح سجلّاتهم. أما <code dir=\"ltr\">git revert HEAD</code> فيضيف commit جديدًا يطبّق عكس <code dir=\"ltr\">8f1e2aa</code> تمامًا؛ ارفعه بشكل عادي فيحصل زملاؤك على الإصلاح بـ pull عادي. ولاحقًا تستطيع إصلاح قائمة التنقّل كما يجب على فرع، وإعادة النقل عبر pull request.</p>\n","checkLabels":{},"optionText":{"a":{"text":"<code dir=\"ltr\">git reset --hard HEAD~1</code>، ثم <code dir=\"ltr\">git push --force</code>","why":"هذا يحذف commit يملكه زملاؤك بالفعل. رفعهم التالي قد يعيده، والرفع بالقوة قد يمحو أي شيء رُفع في الأثناء."},"b":{"text":"<code dir=\"ltr\">git revert HEAD</code>، ثم <code dir=\"ltr\">git push</code>","why":"صحيح. الـ commit الجديد يلغي <code dir=\"ltr\">8f1e2aa</code> دون إعادة كتابة السجلّ المشترك."},"c":{"text":"<code dir=\"ltr\">git restore index.html</code>","why":"الـ working tree نظيف، فلا شيء يُسترجع. الخطأ داخل commit، ولا يستطيع التراجع عنه علنًا إلا commit جديد."},"d":{"text":"<code dir=\"ltr\">git commit --amend</code>","why":"الـ amend يستبدل آخر commit بمعرّف جديد، وهذه إعادة كتابة لـ commit مرفوع وستحتاج إلى رفع بالقوة."}}},"words":671,"readMinutes":4}