{"id":"l-git-4-2","courseId":"git-github","lang":"ar","kind":"lesson","summary":"اركن العمل غير المكتمل جانبًا بـ git stash وأعده لاحقًا، واكتب ملف .gitignore يُبقي الاعتماديات وناتج البناء والأسرار وفوضى نظام التشغيل خارج الـ repository.","html":"<p>أداتان صغيرتان تُبقيان الـ working tree تحت السيطرة. <code dir=\"ltr\">git stash</code> يعطيك درجًا للعمل غير الجاهز للـ commit. و <code dir=\"ltr\">.gitignore</code> يخبر Git بالملفات التي يجب ألّا يتتبّعها أبدًا. وكلتاهما تمنعان النوع نفسه من الحوادث: أن يتسلّل الشيء الخطأ إلى commit.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الـ Stash: درج للعمل غير المكتمل</h2>\n<p>أنت في منتصف نموذج التواصل في الموقع الشخصي على فرع <code dir=\"ltr\">contact-form</code>، حين ينبّهك أحدهم إلى خطأ إملائي في الصفحة الرئيسية المنشورة. تحتاج إلى <code dir=\"ltr\">main</code>، الآن. لكن <code dir=\"ltr\">git switch main</code> يرفض، لأن تعديلاتك على <code dir=\"ltr\">index.html</code> ستُكتب فوقها، ولا تريد تسجيل نموذج نصف مبني في commit.</p>\n<p>خبّئه (stash):</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\"> stash</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> push</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -m</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"contact form: fields done, validation next\"</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> status</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">        # nothing to commit, working tree clean</span></span></code></pre></div>\n<p>تغييراتك، المجهّزة وغير المجهّزة، تُحفظ في مدخل stash وتُزال من الـ working tree. الآن أصلح الخطأ الإملائي:</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\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># fix, commit, push</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>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> pop</span></span></code></pre></div>\n<p>الـ <code dir=\"ltr\">pop</code> يعيد تطبيق أحدث stash ويحذفه. وتعود تمامًا إلى حيث كنت.</p>\n<p>أوامر الـ stash الأخرى التي ستستخدمها:</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\"> stash</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> list</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">                  # stash@{0}: On contact-form: contact form: fields done…</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> show</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -p</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash@{</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">     # the diff inside a stash</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> apply</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash@{</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\">       # reapply but keep the stash</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> drop</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash@{</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\">        # delete one stash</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> stash</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> push</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -u</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -m</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"…\"</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">        # include untracked (new) files too</span></span></code></pre></div>\n<p>الخيار الأخير مهم. افتراضيًا لا يحفظ الـ stash إلا التغييرات على الملفات <strong>المتتبَّعة</strong>. أما <code dir=\"ltr\">contact.js</code> الجديد كليًا فيبقى في مجلدك دون أن يُمسّ، وهذا يفاجئ الناس حين ينفّذون pop ولا «يُستعاد». استخدم <code dir=\"ltr\">-u</code> كلما أنشأت ملفات.</p>\n<p>الـ stash غير مربوط بفرع: تستطيع تنفيذ pop له على أي فرع، وهذا مفيد أحيانًا ومفاجئ أحيانًا. وإن أدركت أن العمل المخبّأ يستحق فرعًا خاصًا به في النهاية، فإن <code dir=\"ltr\">git stash branch contact-validation</code> ينشئ فرعًا عند الـ commit الذي خبّأت عنده، ويطبّق الـ stash هناك ثم يحذفه، فيتجنّب التعارضات مع أي شيء تغيّر منذ ذلك الحين.</p>\n<p>إن اصطدم الـ <code dir=\"ltr\">pop</code> بتعارض، يطبّق Git ما يستطيعه، ويعلّم التعارض كالمعتاد، ويحتفظ بالـ stash بدل حذفه. حُلّ التعارض، ونفّذ <code dir=\"ltr\">git add</code>، وشغّل <code dir=\"ltr\">git stash drop</code> حين تتأكّد.</p>\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>الـ stash للدقائق، لا للأسابيع</span></p>\n<p>الـ stash لا فرع له ولا pull request، والـ stash القديم ذو الرسالة المبهمة لغز. لأي شيء ستتركه حتى الغد، أنشئ commit على فرعك بدلًا من ذلك (<code dir=\"ltr\">git commit -m &quot;WIP: contact validation&quot;</code>) وعدّله بالـ amend أو اضغطه بالـ squash لاحقًا. واستخدم <code dir=\"ltr\">-m</code> مع كل stash لكي يخبرك <code dir=\"ltr\">git stash list</code> بماهية كل واحد منها.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">ملف .gitignore: ملفات يجب ألّا يتتبّعها Git أبدًا</h2>\n<p>بعض الملفات مكانها في مجلد مشروعك، لكن ليس في السجلّ أبدًا:</p>\n<ul>\n<li>الاعتماديات (dependencies) التي تستطيع إعادة تثبيتها، مثل <code dir=\"ltr\">node_modules/</code>؛</li>\n<li>ناتج البناء الذي تستطيع إعادة توليده، مثل <code dir=\"ltr\">dist/</code>؛</li>\n<li>الأسرار والإعدادات الخاصة بالجهاز، مثل <code dir=\"ltr\">.env</code>؛</li>\n<li>فوضى نظام التشغيل والمحرّر، مثل <code dir=\"ltr\">.DS_Store</code> على macOS.</li>\n</ul>\n<p>اسردها في ملف اسمه <code dir=\"ltr\">.gitignore</code> في جذر الـ repository وسجّله في commit، ليتشارك كل من في المشروع القواعد نفسها:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">.gitignore</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># Dependencies\nnode_modules/\n\n# Build output\n/dist/\n\n# Local environment and secrets\n.env\n.env.*\n!.env.example\n\n# OS and editor files\n.DS_Store\n*.log</code></pre></div>\n<p>الملفات المتجاهَلة تختفي من <code dir=\"ltr\">git status</code>، ويتخطّاها <code dir=\"ltr\">git add .</code>.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">كيف تعمل الأنماط</h2>\n<p>كل سطر نمط (pattern)، يُطابق مع المسارات نسبةً إلى مجلد ملف <code dir=\"ltr\">.gitignore</code>:</p>\n<ul>\n<li><code dir=\"ltr\">#</code> تبدأ تعليقًا، لكن فقط في بداية السطر. والأسطر الفارغة تُتجاهل.</li>\n<li><code dir=\"ltr\">*.log</code> يطابق أي ملف ينتهي بـ <code dir=\"ltr\">.log</code>، في أي مجلد.</li>\n<li>الشرطة المائلة <code dir=\"ltr\">/</code> في النهاية تطابق المجلدات فقط: <code dir=\"ltr\">node_modules/</code>.</li>\n<li>الشرطة المائلة <code dir=\"ltr\">/</code> في البداية تثبّت النمط على هذا المجلد: <code dir=\"ltr\">/dist/</code> يتجاهل <code dir=\"ltr\">dist</code> في الجذر لكن لا يتجاهل <code dir=\"ltr\">src/dist</code>.</li>\n<li><code dir=\"ltr\">**/</code> يطابق أي عمق: <code dir=\"ltr\">**/fixtures/*.json</code>.</li>\n<li><code dir=\"ltr\">!</code> تعيد تضمين شيء استبعده سطر سابق: <code dir=\"ltr\">!.env.example</code> يُبقي القالب للمطوّرين الآخرين. لكنها لا تستطيع إعادة تضمين ملف استُبعد <em>المجلد</em> الأب الخاص به، لأن Git لا ينظر أبدًا داخل مجلد متجاهَل.</li>\n</ul>\n<p>حين يكون ملف ما متجاهَلًا ولا تعرف السبب، اسأل Git:</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\"> check-ignore</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -v</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dist/app.js</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>.gitignore:5:/dist/\tdist/app.js</code></pre></div>\n<p>يطبع الملف ورقم السطر والنمط المسؤول.</p>\n<p>وللفوضى التي لا يُنشئها إلا جهازك، لا تُثقل ملف <code dir=\"ltr\">.gitignore</code> في كل مشروع. فـ Git يقرأ أيضًا ملف تجاهل عامًا، <code dir=\"ltr\">~/.config/git/ignore</code>، وهو مكان مناسب لـ <code dir=\"ltr\">.DS_Store</code> ومجلدات المحرّر. ويحتفظ GitHub بمجموعة من قوالب <code dir=\"ltr\">.gitignore</code> الجاهزة لمعظم اللغات، ويعرض عليك واحدًا حين تنشئ repository.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">التجاهل لا يلغي التتبّع</h2>\n<p><code dir=\"ltr\">.gitignore</code> لا يؤثّر إلا في الملفات <strong>غير المتتبَّعة</strong>. إن كان <code dir=\"ltr\">.env</code> قد سُجّل في commit قبل أن تتجاهله، فسيواصل Git تتبّعه. أزله من الـ index وأنشئ commit، وهذا يُبقي ملفك المحلي:</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\"> rm</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --cached</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> .env</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\"> -m</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Stop tracking .env\"</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>تجاهل سرّ بعد رفعه</span></p>\n<p>إن رُفع <code dir=\"ltr\">.env</code> الذي فيه مفتاح API ولو مرة واحدة، فهو في سجلّ الـ repository وفي كل نسخة مستنسخة منه، وإضافته إلى <code dir=\"ltr\">.gitignore</code> لا تغيّر من ذلك شيئًا. تعامل مع المفتاح على أنه مسرَّب: ألغِه لدى المزوّد واستصدر مفتاحًا جديدًا. ثم ألغِ تتبّع الملف. ميزة push protection في GitHub تحجب كثيرًا من صيغ الأسرار المعروفة لحظة الرفع، لكنها شبكة أمان لا خطة؛ اكتب <code dir=\"ltr\">.gitignore</code> قبل أول commit.</p>\n</aside>\n<p>صار الـ working tree نظيفًا، ولا يحتوي الـ repository إلا ما يجب أن يحتويه. في الدرس التالي ستجعل ما يحتويه مقروءًا: رسائل الـ commits، والـ tags، والإصدارات.</p>\n","toc":[{"id":"sec-1","text":"الـ Stash: درج للعمل غير المكتمل","level":2},{"id":"sec-2","text":"ملف .gitignore: ملفات يجب ألّا يتتبّعها Git أبدًا","level":2},{"id":"sec-3","text":"كيف تعمل الأنماط","level":2},{"id":"sec-4","text":"التجاهل لا يلغي التتبّع","level":2}],"takeaways":["<code dir=\"ltr\">git stash push -m &quot;…&quot;</code> يحفظ تغييراتك غير المسجّلة ويُنظّف الـ working tree؛ و <code dir=\"ltr\">git stash pop</code> يعيدها ويحذف الـ stash.","<code dir=\"ltr\">git stash</code> يتخطّى الملفات غير المتتبَّعة ما لم تضف <code dir=\"ltr\">-u</code> (<code dir=\"ltr\">--include-untracked</code>).","<code dir=\"ltr\">.gitignore</code> لا يؤثّر إلا في الملفات غير المتتبَّعة؛ ولإيقاف تتبّع ملف مسجّل، شغّل <code dir=\"ltr\">git rm --cached</code> وأنشئ commit.","في <code dir=\"ltr\">.gitignore</code>، الشرطة المائلة <code dir=\"ltr\">/</code> في النهاية تطابق المجلدات فقط، وفي البداية تثبّت النمط على ذلك المجلد، و <code dir=\"ltr\">!</code> تعيد تضمين ملف.","السرّ الذي سُجّل في commit ورُفع ولو مرة يجب إلغاؤه واستبداله، لأن السجلّ ما زال يحتويه."],"further":[{"title":"git stash reference","url":"https://git-scm.com/docs/git-stash"},{"title":"gitignore pattern format","url":"https://git-scm.com/docs/gitignore"},{"title":"Ignoring files (GitHub Docs)","url":"https://docs.github.com/en/get-started/git-basics/ignoring-files"}],"quiz":[{"q":"<p>أنت في منتصف نموذج التواصل حين يلزم إصلاح خطأ إملائي عاجل على <code dir=\"ltr\">main</code>. يرفض <code dir=\"ltr\">git switch main</code> لأنه سيكتب فوق تغييراتك. ما أسرع طريق نظيف؟</p>","options":[{"text":"احذف تغييراتك بـ <code dir=\"ltr\">git restore .</code>، وأصلح الخطأ، وأعِد بناء النموذج لاحقًا.","why":"هذا يرمي عملًا حقيقيًا لتوفير بضع ثوانٍ. أما الـ stash فيحفظه بأمان."},{"text":"<code dir=\"ltr\">git stash push -m &quot;contact form wip&quot;</code>، ثم انتقل إلى <code dir=\"ltr\">main</code>، وأصلح وسجّل، ثم عُد، ثم <code dir=\"ltr\">git stash pop</code>.","why":"صحيح. يحتفظ الـ stash بعملك غير المكتمل بينما يكون الـ working tree نظيفًا، والـ pop يعيده إلى حيث توقّفت."},{"text":"افرض الانتقال بـ <code dir=\"ltr\">git switch -f main</code>.","why":"<code dir=\"ltr\">-f</code> يرمي تغييراتك المحلية ليُتمّ الانتقال، وهذا بالضبط ما أردت تجنّبه."},{"text":"سجّل النموذج غير المكتمل في commit على <code dir=\"ltr\">main</code> برسالة “WIP”.","why":"ستضع عملًا معطّلًا على <code dir=\"ltr\">main</code>. إن أردت commit من نوع WIP، فأنشئه على فرع الـ feature الخاص بك."}],"answer":1},{"q":"<p>نفّذت stash، وانتقلت بين الفروع، ثم عدت. أعاد <code dir=\"ltr\">git stash pop</code> تعديلاتك على <code dir=\"ltr\">contact.html</code> لكن ملفك الجديد <code dir=\"ltr\">contact.js</code> مفقود. لماذا؟</p>","options":[{"text":"<code dir=\"ltr\">git stash pop</code> يستعيد الملفات بترتيب تعديلها وقد توقّف مبكرًا.","why":"الـ pop يستعيد كل ما في الـ stash دفعة واحدة. والملف لم يكن في الـ stash أصلًا."},{"text":"حُذف الملف بسبب الانتقال بين الفروع.","why":"الانتقال لا يمسّ الملفات غير المتتبَّعة. الـ stash هو الذي تخطّاه، ولهذا قد يكون ما زال في مجلدك."},{"text":"تنتهي صلاحية الـ stash بعد انتقال واحد بين الفروع.","why":"يبقى الـ stash حتى تحذفه أو تنفّذ pop له. و <code dir=\"ltr\">git stash list</code> يعرضه."},{"text":"<code dir=\"ltr\">git stash</code> لا يتضمّن الملفات غير المتتبَّعة ما لم تمرّر <code dir=\"ltr\">-u</code>، فبقي <code dir=\"ltr\">contact.js</code> في الـ working tree.","why":"صحيح. الملفات غير المتتبَّعة لا تُخبّأ افتراضيًا. تفقّد الـ working tree، فالأرجح أنه هناك، واستخدم <code dir=\"ltr\">git stash -u</code> في المرة القادمة."}],"answer":3},{"q":"<p>سجّلت <code dir=\"ltr\">.env</code> في commit الأسبوع الماضي. واليوم تضيف <code dir=\"ltr\">.env</code> إلى <code dir=\"ltr\">.gitignore</code>، لكن <code dir=\"ltr\">git status</code> ما زال يعرض <code dir=\"ltr\">.env</code> على أنه معدّل. لماذا؟</p>","options":[{"text":"<code dir=\"ltr\">.gitignore</code> لا ينطبق إلا على الملفات غير المتتبَّعة، و <code dir=\"ltr\">.env</code> متتبَّع بالفعل؛ شغّل <code dir=\"ltr\">git rm --cached .env</code> وأنشئ commit.","why":"صحيح. <code dir=\"ltr\">--cached</code> يزيله من الـ index (فلا تتضمّنه الـ commits القادمة) ويترك الملف على القرص."},{"text":"يجب أن يكون النمط <code dir=\"ltr\">/.env/</code> لكي يعمل.","why":"الشرطة المائلة في النهاية تجعل النمط يطابق المجلدات فقط، فلن يطابق <code dir=\"ltr\">/.env/</code> الملف أصلًا."},{"text":"تغييرات <code dir=\"ltr\">.gitignore</code> لا تسري إلا بعد الرفع.","why":"قواعد التجاهل تسري فورًا، محليًا. المشكلة أن الملفات المتتبَّعة لا تُتجاهل أبدًا."}],"answer":0},{"q":"<p>أيّ سطر في <code dir=\"ltr\">.gitignore</code> يتجاهل مجلد <code dir=\"ltr\">dist</code> في جذر المشروع دون مجلد <code dir=\"ltr\">src/dist/</code>؟</p>","options":[{"text":"<code dir=\"ltr\">dist</code>","why":"دون شرطة مائلة في البداية أو الوسط، يطابق النمط <code dir=\"ltr\">dist</code> على أي عمق، بما في ذلك <code dir=\"ltr\">src/dist</code>."},{"text":"<code dir=\"ltr\">**/dist/</code>","why":"<code dir=\"ltr\">**/</code> تعني على أي عمق، فهذا يطابق <code dir=\"ltr\">src/dist/</code> أيضًا."},{"text":"<code dir=\"ltr\">/dist/</code>","why":"صحيح. الشرطة المائلة في البداية تثبّت النمط على المجلد الذي يحتوي <code dir=\"ltr\">.gitignore</code>، والشرطة في النهاية تحصره في المجلدات."},{"text":"<code dir=\"ltr\">!dist</code>","why":"<code dir=\"ltr\">!</code> تعيد تضمين مسار سبق تجاهله؛ ووحدها لا تتجاهل شيئًا."}],"answer":2}],"exercise":{"kind":"spot-bug","language":"text","code":"# Dependencies\nnode_modules/\n\n# Build output from the Sass experiment\n*.css\n\n# Local environment (never commit secrets)\n.env\nsecrets.json   # API keys\n!.env.example\n\n# OS and editor files\n/.DS_Store\n.vscode/\n*.log","bugLines":[5,9,13],"title":"أصلح ملف .gitignore في الموقع الشخصي","prompt":"<p>يقع ملف <code dir=\"ltr\">.gitignore</code> هذا في جذر repository الموقع الشخصي، الذي يحتوي <code dir=\"ltr\">index.html</code> و <code dir=\"ltr\">styles.css</code> و <code dir=\"ltr\">projects.html</code>، وملف <code dir=\"ltr\">.env</code> فيه مفتاح API، و <code dir=\"ltr\">secrets.json</code>، وملفات <code dir=\"ltr\">.DS_Store</code> الخاصة بـ macOS في عدة مجلدات. ثلاثة أسطر لا تفعل ما قصده كاتبها. حدّدها.</p>\n","hints":["أحد الأنماط يخفي ملفًا يحتاجه الموقع ليعمل.","التعليقات في <code dir=\"ltr\">.gitignore</code> لا تعمل إلا في موضع واحد من السطر.","ملفات <code dir=\"ltr\">.DS_Store</code> تظهر في كل مجلد فتحه macOS، لا في الجذر فقط."],"explanation":"<p>السطر 5، <code dir=\"ltr\">*.css</code>، يتجاهل كل ملف CSS، بما في ذلك <code dir=\"ltr\">styles.css</code> الذي كتبته بيدك؛ تجاهل مجلد ناتج البناء الحقيقي وحده بدلًا من ذلك (مثلًا <code dir=\"ltr\">/dist/</code>). والسطر 9 فيه تعليق في نهايته، لكن <code dir=\"ltr\">#</code> لا تبدأ تعليقًا إلا في بداية السطر، فيصبح النمط هو النص الحرفي <code dir=\"ltr\">secrets.json   # API keys</code> ولا يُتجاهل <code dir=\"ltr\">secrets.json</code>؛ انقل التعليق إلى سطر مستقل. والسطر 13، <code dir=\"ltr\">/.DS_Store</code>، مثبَّت على الجذر، فتظل ملفات <code dir=\"ltr\">.DS_Store</code> في المجلدات الفرعية تظهر؛ احذف الشرطة المائلة في بدايته (<code dir=\"ltr\">.DS_Store</code>)، أو الأفضل أن تضعه في ملف التجاهل العام. أما السطر <code dir=\"ltr\">!.env.example</code> فسليم: إنه يُبقي ملف القالب الذي يحتاجه المطوّرون الآخرون.</p>\n","checkLabels":{}},"words":687,"readMinutes":4}