{"id":"l-git-1-4","courseId":"git-github","lang":"ar","kind":"lesson","summary":"اقرأ سجلّ الـ repository بـ git log و git show و git diff، وسمِّ أي commit نسبةً إلى الـ HEAD، وتخيّل السجلّ رسمًا بيانيًا من اللقطات المرتبطة بآبائها.","html":"<p>صار في موقعك الشخصي بضعة commits الآن. بعد ستة أشهر سيصبح فيه المئات، والسؤال الذي ستطرحه فعلًا لن يكون «ما هو السجلّ؟» بل شيئًا أدقّ: متى تعطّل الـ footer، وكيف كان هذا الملف قبل إعادة التصميم، وأيّ commit أضاف ذلك اللون. قراءة السجلّ جيدًا هي ما يجعلك تجيب عن هذه الأسئلة في ثوانٍ.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">العرض اليومي</h2>\n<p>الأمر <code dir=\"ltr\">git log</code> وحده يطبع كل commit، الأحدث أولًا، مع المعرّفات الكاملة والتواريخ. إنه شامل ومُتعب. هذه هي الصيغة التي تستحق الحفظ:</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\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --graph</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --all</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>*   e5d81c0 (HEAD -&gt; main) Merge branch 'dark-mode'\n|\\\n| * d4a7f20 (dark-mode) Add dark mode colors\n* | c39be11 Add projects page\n|/\n* b2e04f3 Add navigation bar with base styles\n* a1c9d72 Add home page</code></pre></div>\n<p>اقرأه من الأسفل إلى الأعلى لتتبع الترتيب الزمني. كل <code dir=\"ltr\">*</code> هي commit، والخطوط تبيّن أيّ commit جاء من أيّ commit. وبين الأقواس أسماء دالّة (labels): <code dir=\"ltr\">main</code> و <code dir=\"ltr\">dark-mode</code> فرعان، و <code dir=\"ltr\">HEAD -&gt; main</code> تعني أنك على <code dir=\"ltr\">main</code>. الخيار <code dir=\"ltr\">--all</code> مهم: دونه يعرض Git فقط الـ commits التي يمكن الوصول إليها من مكانك، ولن يبقى خط <code dir=\"ltr\">dark-mode</code> ظاهرًا إلا لأنه دُمج.</p>\n<p>الناتج الطويل يُفتح في أداة تصفّح (pager). استخدم الأسهم أو المسافة للتمرير و <code dir=\"ltr\">q</code> للخروج.</p>\n<p>مرشّحات مفيدة يمكن جمعها كلها: <code dir=\"ltr\">-n 5</code> (آخر خمسة)، و <code dir=\"ltr\">--stat</code> (أيّ الملفات تغيّرت)، و <code dir=\"ltr\">-p</code> (الـ diffs كاملة)، و <code dir=\"ltr\">--author=&quot;Maya&quot;</code>، و <code dir=\"ltr\">--since=&quot;2 weeks ago&quot;</code>، و <code dir=\"ltr\">-- index.html</code> (فقط الـ commits التي لمست ذلك الملف).</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الـ commits تشكّل رسمًا بيانيًا</h2>\n<p>الناتج أعلاه تمثيل لـ <strong>رسم بياني موجَّه غير دوري</strong> (directed acyclic graph)، أو DAG. موجَّه لأن كل commit يشير إلى أبيه. وغير دوري لأنك لا تستطيع تتبّع الآباء والعودة إلى حيث بدأت؛ فلا يمكن لـ commit أن يكون جدًّا لنفسه. معظم الـ commits لها أب واحد. الـ root commit لا أب له. والـ <strong>merge commit</strong> له أبوان.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 250\" role=\"img\" aria-labelledby=\"l-git-1-4-t1\"><defs><marker id=\"arrow-l-git-1-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-1-4-t1\">الـ commitان a1c9 و b2e0 متتاليان، ثم يتفرّع الخط: c39b على خط main و d4a7 على خط dark-mode. كلاهما يقود إلى الـ merge commit ذي المعرّف e5d8، وله أبوان. الاسم main والـ HEAD يشيران إلى e5d8؛ والاسم dark-mode يشير إلى d4a7. الأسهم تتّجه من كل commit إلى أبيه.</title>\n  <circle class=\"d-box\" cx=\"60\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"60\" y=\"115\" text-anchor=\"middle\">a1c9</text>\n  <circle class=\"d-box\" cx=\"190\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"190\" y=\"115\" text-anchor=\"middle\">b2e0</text>\n  <circle class=\"d-box\" cx=\"330\" cy=\"60\" r=\"26\"/>\n  <text class=\"d-code\" x=\"330\" y=\"65\" text-anchor=\"middle\">c39b</text>\n  <circle class=\"d-box\" cx=\"330\" cy=\"170\" r=\"26\"/>\n  <text class=\"d-code\" x=\"330\" y=\"175\" text-anchor=\"middle\">d4a7</text>\n  <circle class=\"d-box-primary\" cx=\"480\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"480\" y=\"115\" text-anchor=\"middle\">e5d8</text>\n  <path class=\"d-arrow\" d=\"M164 110 L90 110\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M305 70 L218 102\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M305 160 L218 120\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M455 100 L360 66\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M455 120 L360 164\" marker-end=\"url(#arrow-l-git-1-4-1)\"/>\n  <rect class=\"d-box-accent\" x=\"560\" y=\"70\" width=\"110\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"615\" y=\"92\" text-anchor=\"middle\">main</text>\n  <rect class=\"d-box-warn\" x=\"560\" y=\"20\" width=\"110\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"615\" y=\"42\" text-anchor=\"middle\">HEAD</text>\n  <path class=\"d-line\" d=\"M615 54 L615 70\"/>\n  <path class=\"d-line\" d=\"M560 92 L508 106\"/>\n  <rect class=\"d-box-accent\" x=\"400\" y=\"200\" width=\"120\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"460\" y=\"222\" text-anchor=\"middle\">dark-mode</text>\n  <path class=\"d-line\" d=\"M400 205 L356 178\"/>\n</svg><figcaption>السجلّ نفسه كرسم بياني للـ commits</figcaption></figure>\n<p>احتفظ بهذه الصورة في ذهنك. في القسم الثاني سترى أن الـ branch ليس أكثر من واحد من هذه الأسماء الدالّة، وأن كل أمر متعلّق بالفروع إما يحرّك اسمًا أو يضيف دائرة.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">تسمية الـ commits</h2>\n<p>تستطيع تسمية أي commit بمعرّفه، ويقبل Git أقصر بادئة لا لبس فيها، وهي عادةً 7 أحرف. المعرّفات hashes مشتقّة من محتوى الـ commit وبياناته الوصفية وأبيه، لذا لا تتغيّر أبدًا، ولا يشترك commitان مختلفان في المعرّف نفسه عمليًا.</p>\n<p>غالبًا ما يكون العدّ انطلاقًا من مكانك أسهل:</p>\n<ul>\n<li><code dir=\"ltr\">HEAD</code> هو الـ commit الذي أنت عليه.</li>\n<li><code dir=\"ltr\">HEAD~1</code> (أو <code dir=\"ltr\">HEAD~</code>) هو أبوه، و <code dir=\"ltr\">HEAD~3</code> هو ثلاثة أجيال إلى الخلف، مع تتبّع الأب الأول في كل مرة.</li>\n<li><code dir=\"ltr\">HEAD^2</code> هو الأب <em>الثاني</em> لـ merge commit؛ وفي <code dir=\"ltr\">e5d81c0</code> أعلاه هو <code dir=\"ltr\">d4a7f20</code>.</li>\n<li>اسم الفرع يصلح في أي مكان يصلح فيه الـ commit: <code dir=\"ltr\">dark-mode~1</code> هو <code dir=\"ltr\">b2e04f3</code>.</li>\n</ul>\n<h2 id=\"sec-4\" tabindex=\"-1\">النظر داخل الـ commits</h2>\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\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> c39be11</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">              # message, author, date and the diff it introduced</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD~2:styles.css</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # the whole file as it was in that snapshot</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 style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> a1c9d72</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">         # everything that changed between two snapshots</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 style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD~3</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> HEAD</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> styles.css</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">   # the same, for one file only</span></span></code></pre></div>\n<p>يقبل <code dir=\"ltr\">git diff</code> أي commitين لأن كلًا منهما لقطة كاملة. ويُحسب الـ diff عند الطلب بمقارنتهما. وكذلك الحال مع الـ diff الذي يطبعه <code dir=\"ltr\">git show</code>: إنه لقطة هذا الـ commit مقارنةً بلقطة أبيه.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">تحقيق حقيقي</h2>\n<p>هكذا تتكامل هذه الأوامر. يخبرك صديق أن الـ footer في صفحة مشاريعك اختفى، وأنت متأكّد أنه كان موجودًا الأسبوع الماضي. ابدأ بتضييق السجلّ إلى الملف المعنيّ:</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\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --since=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"1 week ago\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> projects.html</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"4\"><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 Move project cards into a grid\nc39be11 Add projects page</code></pre></div>\n<p>مشتبهان اثنان. انظر إلى الأحدث بالكامل:</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\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 8f1e2aa</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> projects.html</span></span></code></pre></div>\n<p>يُظهر الـ diff كتلة <code dir=\"ltr\">&lt;footer&gt;</code> باللون الأحمر، أُزيلت مع ترميز القائمة القديمة. هذا هو الجاني، وجدته في أقل من دقيقة. ولتأخذ ترميز الـ footer القديم دون إرجاع أي شيء، اطبع الملف كما كان في الـ 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\"> show</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 8f1e2aa~1:projects.html</span></span></code></pre></div>\n<p>لم يغيّر هذا التحقيق ملفًا واحدًا ولا commitًا واحدًا. قراءة السجلّ آمنة دائمًا، فافعلها بحرية وكثيرًا. معظم «حالات الطوارئ في Git» التي رأيتها كانت في الحقيقة مشكلات قراءة: شخص ما ذُعر قبل أن ينظر إلى الرسم، والجواب كان موجودًا في <code dir=\"ltr\">git log</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>اعثر على الـ commit الذي أضاف شيئًا ما</span></p>\n<p><code dir=\"ltr\">git log -S &quot;dark-mode&quot; --oneline</code> يسرد الـ commits التي أضافت النص <code dir=\"ltr\">dark-mode</code> أو أزالته من أي مكان في الكود. حين تحتاج إلى معرفة متى ولماذا ظهر سطر ما، فهذا البحث (pickaxe) أفضل من التمرير عبر <code dir=\"ltr\">git log -p</code>.</p>\n</aside>\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 log يعرض كل شيء</span></p>\n<p>يبدأ <code dir=\"ltr\">git log</code> من الـ HEAD ويتتبّع الآباء إلى الخلف. الـ commits الموجودة على فروع أخرى لم تُدمج لا يمكن الوصول إليها من هناك، فلا تظهر، ويستنتج الناس أن عملهم ضاع. أضف <code dir=\"ltr\">--all</code>، أو سمِّ الفرع: <code dir=\"ltr\">git log --oneline dark-mode</code>.</p>\n</aside>\n<p>رأيت أسماء مثل <code dir=\"ltr\">main</code> و <code dir=\"ltr\">dark-mode</code> معلّقة على الـ commits. يبدأ القسم الثاني بشرح حقيقة هذه الأسماء، ولماذا يكاد إنشاء واحد منها لا يكلّف شيئًا.</p>\n","toc":[{"id":"sec-1","text":"العرض اليومي","level":2},{"id":"sec-2","text":"الـ commits تشكّل رسمًا بيانيًا","level":2},{"id":"sec-3","text":"تسمية الـ commits","level":2},{"id":"sec-4","text":"النظر داخل الـ commits","level":2},{"id":"sec-5","text":"تحقيق حقيقي","level":2}],"takeaways":["السجلّ رسم بياني موجَّه غير دوري، يشير فيه كل commit إلى أبيه، ويشير الـ merge commit إلى أبوين.","<code dir=\"ltr\">git log --oneline --graph --all</code> يرسم الرسم البياني في الطرفية، بما فيها الفروع التي لست عليها.","<code dir=\"ltr\">git show &lt;commit&gt;</code> يطبع رسالة commit واحد وتغييراته؛ و <code dir=\"ltr\">git show &lt;commit&gt;:&lt;path&gt;</code> يطبع ملفًا كما كان حينها.","<code dir=\"ltr\">HEAD~1</code> هو الـ commit الذي يسبق الـ commit الذي أنت عليه، و <code dir=\"ltr\">HEAD~3</code> ثلاث خطوات إلى الخلف عبر الآباء الأوائل.","<code dir=\"ltr\">git diff A B</code> يقارن أي لقطتين، و <code dir=\"ltr\">-- &lt;path&gt;</code> يحصر المقارنة في ملف واحد."],"further":[{"title":"Viewing the Commit History (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History"},{"title":"git log reference","url":"https://git-scm.com/docs/git-log"},{"title":"gitrevisions (naming commits)","url":"https://git-scm.com/docs/gitrevisions"}],"quiz":[{"q":"<p>الأمر <code dir=\"ltr\">git log</code> على <code dir=\"ltr\">main</code> لا يعرض commit أنشأته على فرعك <code dir=\"ltr\">dark-mode</code>. لماذا؟</p>","options":[{"text":"ضاع الـ commit لأنك انتقلت بين الفروع.","why":"الانتقال بين الفروع لا يحذف أي commit. الـ commit ما زال موجودًا، ويشير إليه <code dir=\"ltr\">dark-mode</code>."},{"text":"<code dir=\"ltr\">git log</code> لا يعرض إلا الـ commits التي يمكن الوصول إليها من الـ HEAD عبر تتبّع الآباء، وهذا الـ commit ليس منها.","why":"صحيح. <code dir=\"ltr\">main</code> لا يقود إليه. الأمر <code dir=\"ltr\">git log --all</code> أو <code dir=\"ltr\">git log dark-mode</code> سيعرضه."},{"text":"<code dir=\"ltr\">git log</code> يخفي الـ commits إلى أن يتم رفعها.","why":"لا يهتمّ Git بالرفع حين يعرض السجلّ المحلي. الـ commits غير المرفوعة تظهر كغيرها تمامًا."},{"text":"تحتاج إلى <code dir=\"ltr\">git log -p</code> لرؤية commits الفروع الأخرى.","why":"<code dir=\"ltr\">-p</code> يضيف الـ diff الخاص بكل commit إلى الناتج. ولا يغيّر الـ commits التي تُعرض."}],"answer":1},{"q":"<p>تريد رؤية <code dir=\"ltr\">styles.css</code> كما كان تمامًا قبل commitين، دون تغيير أي ملف. أيّ أمر يفعل ذلك؟</p>","options":[{"text":"<code dir=\"ltr\">git restore styles.css~2</code>","why":"<code dir=\"ltr\">~2</code> تنطبق على الـ commits لا على أسماء الملفات، و <code dir=\"ltr\">git restore</code> يكتب في الـ working tree، وهذا ما لا تريده."},{"text":"<code dir=\"ltr\">git log -2 styles.css</code>","why":"هذا يسرد آخر commitين لمسا الملف. يعرض الرسائل، لا محتوى الملف."},{"text":"<code dir=\"ltr\">git diff HEAD~2</code>","why":"هذا يعرض التغييرات بين ذلك الـ commit والـ working tree، لا المحتوى الكامل للملف."},{"text":"<code dir=\"ltr\">git show HEAD~2:styles.css</code>","why":"صحيح. الصيغة <code dir=\"ltr\">&lt;commit&gt;:&lt;path&gt;</code> تسمّي ملفًا داخل لقطة، و <code dir=\"ltr\">git show</code> يطبعه دون أن يلمس ملفاتك."}],"answer":3},{"q":"<p>كم أبًا للـ merge commit الذي يدمج <code dir=\"ltr\">dark-mode</code> في <code dir=\"ltr\">main</code>؟</p>","options":[{"text":"اثنان، الـ commit الذي كان عليه <code dir=\"ltr\">main</code> والـ commit الذي كان عليه <code dir=\"ltr\">dark-mode</code>.","why":"صحيح. وجود أبوين هو ما يجعله merge، ولهذا تلتقي الخطوط في الرسم عند تلك النقطة."},{"text":"واحد، الـ commit الذي كان عليه <code dir=\"ltr\">main</code> قبل الدمج.","why":"الأب الواحد صفة الـ commit العادي. أما الـ merge فيسجّل السجلّين اللذين يجمعهما."},{"text":"لا أب له، لأن الـ merge commit يبدأ سجلًّا جديدًا.","why":"الـ commit الأول في الـ repository وحده (الـ root commit) لا أب له."}],"answer":0},{"q":"<p>أيّ أمر يسرد فقط الـ commits التي أضافت تغييراتها النص <code dir=\"ltr\">dark-mode</code> أو أزالته؟</p>","options":[{"text":"<code dir=\"ltr\">git log --grep dark-mode</code>","why":"<code dir=\"ltr\">--grep</code> يبحث في رسائل الـ commits، لا في تغييرات الكود داخلها."},{"text":"<code dir=\"ltr\">git diff dark-mode</code>","why":"هذا يقارن الـ working tree بفرع <code dir=\"ltr\">dark-mode</code>. ولا يسرد أي commits."},{"text":"<code dir=\"ltr\">git log -S dark-mode</code>","why":"صحيح. خيار البحث (pickaxe) <code dir=\"ltr\">-S</code> يجد الـ commits التي غيّرت عدد مرات ظهور النص، وهكذا تتعقّب متى أُضيف شيء أو أُزيل."},{"text":"<code dir=\"ltr\">git show dark-mode</code>","why":"هذا يعرض الـ commit الذي يشير إليه فرع <code dir=\"ltr\">dark-mode</code>، إن وُجد فرع بهذا الاسم."}],"answer":2}],"exercise":{"kind":"choice","multiple":false,"options":["a","b","c","d"],"correct":["c"],"title":"اقرأ الرسم البياني","prompt":"<p>هذا سجلّ الموقع الشخصي مرسومًا كرسم بياني للـ commits، وهو الشكل نفسه الذي يرسمه <code dir=\"ltr\">git log --oneline --graph --all</code> نصًّا في الطرفية. الأسهم تتّجه من كل commit إلى أبيه.</p>\n<p>:::figure رسم الـ commits في الموقع الشخصي\n<svg viewBox=\"0 0 640 230\" role=\"img\" aria-labelledby=\"ex1\"></p>\n  <title id=\"ex1\">خمسة commits. الـ commitان 1a2b و 3c4d متتاليان. من 3c4d يخرج خط إلى 5e6f وآخر إلى 7a8b. الـ merge commit ذو المعرّف 9c0d أبواه 5e6f و 7a8b. الاسم main والـ HEAD يشيران إلى 9c0d؛ والاسم hero يشير إلى 7a8b.</title>\n  <circle class=\"d-box\" cx=\"50\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"50\" y=\"115\" text-anchor=\"middle\">1a2b</text>\n  <circle class=\"d-box\" cx=\"170\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"170\" y=\"115\" text-anchor=\"middle\">3c4d</text>\n  <circle class=\"d-box\" cx=\"300\" cy=\"55\" r=\"26\"/>\n  <text class=\"d-code\" x=\"300\" y=\"60\" text-anchor=\"middle\">5e6f</text>\n  <circle class=\"d-box\" cx=\"300\" cy=\"165\" r=\"26\"/>\n  <text class=\"d-code\" x=\"300\" y=\"170\" text-anchor=\"middle\">7a8b</text>\n  <circle class=\"d-box-primary\" cx=\"440\" cy=\"110\" r=\"26\"/>\n  <text class=\"d-code\" x=\"440\" y=\"115\" text-anchor=\"middle\">9c0d</text>\n  <path class=\"d-arrow\" d=\"M144 110 L80 110\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M275 64 L198 100\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M275 156 L198 120\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M415 100 L330 62\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M415 120 L330 160\" marker-end=\"url(#arrow)\"/>\n  <rect class=\"d-box-accent\" x=\"510\" y=\"93\" width=\"100\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"560\" y=\"115\" text-anchor=\"middle\">main</text>\n  <rect class=\"d-box-warn\" x=\"510\" y=\"40\" width=\"100\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"560\" y=\"62\" text-anchor=\"middle\">HEAD</text>\n  <path class=\"d-line\" d=\"M560 74 L560 93\"/>\n  <rect class=\"d-box-accent\" x=\"370\" y=\"185\" width=\"100\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"420\" y=\"207\" text-anchor=\"middle\">hero</text>\n  <path class=\"d-line\" d=\"M370 195 L328 172\"/>\n</svg>\n:::\n<p>أنت على <code dir=\"ltr\">main</code>. ما الـ commits التي يسردها <code dir=\"ltr\">git log --oneline hero</code>؟</p>\n","hints":["<code dir=\"ltr\">git log &lt;name&gt;</code> يبدأ من الـ commit الذي يشير إليه ذلك الاسم.","من هناك يتتبّع أسهم الآباء فقط. ولا يمشي أبدًا إلى الأمام نحو الأبناء.","ابدأ من الـ commit الذي يحمل الاسم <code dir=\"ltr\">hero</code> وتتبّع الأسهم حتى تنتهي."],"explanation":"<p><code dir=\"ltr\">git log hero</code> يبدأ من <code dir=\"ltr\">7a8b</code>، الـ commit الذي يشير إليه فرع <code dir=\"ltr\">hero</code>، ويتتبّع الآباء: <code dir=\"ltr\">7a8b</code> ← <code dir=\"ltr\">3c4d</code> ← <code dir=\"ltr\">1a2b</code>. الـ merge commit ذو المعرّف <code dir=\"ltr\">9c0d</code> <em>ابن</em> لـ <code dir=\"ltr\">7a8b</code>، و Git لا يمشي أبدًا من الأب إلى الابن، فلا يظهر في القائمة. و <code dir=\"ltr\">5e6f</code> على الجهة الأخرى من التفرّع، وليس من أسلاف <code dir=\"ltr\">hero</code> أيضًا.</p>\n","checkLabels":{},"optionText":{"a":{"text":"الـ commits الخمسة كلها، لأن <code dir=\"ltr\">hero</code> دُمج في <code dir=\"ltr\">main</code>.","why":"الدمج جعل commits الفرع <code dir=\"ltr\">hero</code> قابلة للوصول من <code dir=\"ltr\">main</code>، لا العكس. و <code dir=\"ltr\">git log hero</code> يبدأ من <code dir=\"ltr\">hero</code> ويمشي إلى الخلف فقط."},"b":{"text":"<code dir=\"ltr\">7a8b</code> فقط، الـ commit الذي يشير إليه <code dir=\"ltr\">hero</code>.","why":"<code dir=\"ltr\">git log</code> يسرد الـ commit الذي يبدأ منه وكل أسلافه، لا commitًا واحدًا. أما <code dir=\"ltr\">git show hero</code> فسيطبع <code dir=\"ltr\">7a8b</code> وحده."},"c":{"text":"<code dir=\"ltr\">7a8b</code> و <code dir=\"ltr\">3c4d</code> و <code dir=\"ltr\">1a2b</code>.","why":"صحيح. ابدأ من <code dir=\"ltr\">7a8b</code> وتتبّع الآباء: <code dir=\"ltr\">3c4d</code>، ثم الـ root commit ذو المعرّف <code dir=\"ltr\">1a2b</code>."},"d":{"text":"<code dir=\"ltr\">9c0d</code> و <code dir=\"ltr\">7a8b</code> و <code dir=\"ltr\">3c4d</code> و <code dir=\"ltr\">1a2b</code>.","why":"<code dir=\"ltr\">9c0d</code> ابن لـ <code dir=\"ltr\">7a8b</code>. والـ commits لا تعرف أبناءها، لذا لا يستطيع <code dir=\"ltr\">git log</code> الوصول إليه انطلاقًا من <code dir=\"ltr\">hero</code>."}}},"words":675,"readMinutes":4}