{"id":"l-git-3-1","courseId":"git-github","lang":"ar","kind":"lesson","summary":"اربط موقعك الشخصي بـ repository على GitHub، وارفع الفروع مع ضبط تتبّع الـ upstream، وابقَ متزامنًا باستخدام fetch و pull وأنت تفهم ما هو origin/main فعلًا.","html":"<p>حتى الآن يعيش موقعك الشخصي في مكان واحد، حاسوبك المحمول. إن تعطّل القرص ضاع كل شيء، ولا يستطيع أحد غيرك أن يراه أو يراجعه. الـ <strong>remote</strong> (المستودع البعيد) نسخة أخرى من الـ repository في مكان آخر، على GitHub في الغالب. ومهمّة Git أن يُبقي النسختين متزامنتين، وهو يفعل ذلك بأربعة أوامر أساسية: <code dir=\"ltr\">clone</code> و <code dir=\"ltr\">fetch</code> و <code dir=\"ltr\">pull</code> و <code dir=\"ltr\">push</code>.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">ضع الموقع الشخصي على GitHub</h2>\n<p>على github.com، أنشئ repository جديدًا باسم <code dir=\"ltr\">portfolio</code>. اترك “Add a README” وخيارات التهيئة الأخرى دون تحديد: فالـ repository المحلي لديك له سجلّ بالفعل، و commit إضافي على GitHub سيعطيك سجلّين لا علاقة بينهما عليك التوفيق بينهما. بعد ذلك يعرض GitHub الأوامر اللازمة لربط repository موجود. وخلاصتها:</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\"> remote</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> add</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> https://github.com/maya-okafor/portfolio.git</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> remote</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -v</span></span>\n<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\"> -u</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span></code></pre></div>\n<p><code dir=\"ltr\">git remote add</code> يعطي عنوان URL اسمًا قصيرًا. و <strong>origin</strong> مجرد عُرف، الاسم الذي يستخدمه Git تلقائيًا حين تستنسخ، لكن الجميع يستخدمه، فاستخدمه أنت أيضًا. الرفع يحمّل الـ commits الخاصة بك وينشئ <code dir=\"ltr\">main</code> على GitHub. في المرة الأولى سيطلب منك Git المصادقة؛ والدرس التالي يشرح إعدادها كما يجب بمفاتيح SSH أو بمدير بيانات اعتماد.</p>\n<p>وإن كنت تستخدم GitHub CLI، فالأمر <code dir=\"ltr\">gh repo create portfolio --public --source=. --remote=origin --push</code> يفعل كل ذلك دفعة واحدة.</p>\n<p>أما إن بدأت من الجهة الأخرى، بـ repository موجود أصلًا على GitHub، فإنك تستنسخه (clone) بدلًا من ذلك:</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\"> clone</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> https://github.com/maya-okafor/portfolio.git</span></span></code></pre></div>\n<p>الـ clone ينشئ المجلد، وينزّل كل commit، ويضيف remote اسمه <code dir=\"ltr\">origin</code>، وينقلك إلى <code dir=\"ltr\">main</code> مع إعداد التتبّع جاهزًا.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">فروع التتبّع البعيدة: ذاكرة Git عن الـ remote</h2>\n<p>بعد الرفع، يعرض <code dir=\"ltr\">git branch -a</code> شيئًا جديدًا:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"2\"><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>* main\n  remotes/origin/main</code></pre></div>\n<p><code dir=\"ltr\">origin/main</code> <strong>فرع تتبّع بعيد</strong> (remote-tracking branch): ملاحظة يحتفظ بها الـ repository الخاص بك عن مكان <code dir=\"ltr\">main</code> على <code dir=\"ltr\">origin</code> في آخر مرة تواصلت معه. لا تنشئ عليه commits أبدًا. ولا يتحرّك إلا حين تنفّذ fetch أو pull أو push. لهذا يستطيع <code dir=\"ltr\">git status</code> أن يقول “Your branch is up to date with ‘origin/main’” بينما رفع زميلك شيئًا قبل خمس دقائق: لم يسأل Git موقع GitHub منذ ذلك الحين.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 270\" role=\"img\" aria-labelledby=\"l-git-3-1-t1\"><defs><marker id=\"arrow-l-git-3-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-3-1-t1\">يسارًا، حاسوبك: main يشير إلى c3؛ و origin/main، آخر نسخة معروفة لديك، يشير إلى b2. يمينًا، GitHub: main يشير إلى d4، وهو commit رفعه زميل. git fetch ينزّل d4 ويحرّك origin/main؛ ثم يدمجه git pull أيضًا في main لديك؛ و git push يرسل الـ commits الخاصة بك ويحرّك main على GitHub.</title>\n  <rect class=\"d-box\" x=\"20\" y=\"20\" width=\"330\" height=\"230\" rx=\"14\"/>\n  <text class=\"d-label-strong\" x=\"40\" y=\"48\">حاسوبك</text>\n  <circle class=\"d-box\" cx=\"70\" cy=\"130\" r=\"22\"/>\n  <text class=\"d-code\" x=\"70\" y=\"135\" text-anchor=\"middle\">b2</text>\n  <circle class=\"d-box-primary\" cx=\"170\" cy=\"130\" r=\"22\"/>\n  <text class=\"d-code\" x=\"170\" y=\"135\" text-anchor=\"middle\">c3</text>\n  <path class=\"d-line\" d=\"M148 130 L92 130\"/>\n  <rect class=\"d-box-accent\" x=\"135\" y=\"70\" width=\"70\" height=\"30\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"170\" y=\"90\" text-anchor=\"middle\">main</text>\n  <rect class=\"d-box-warn\" x=\"25\" y=\"175\" width=\"100\" height=\"30\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"75\" y=\"195\" text-anchor=\"middle\">origin/main</text>\n  <path class=\"d-line\" d=\"M70 152 L70 175\"/>\n  <text class=\"d-label-muted\" x=\"40\" y=\"236\">origin/main: آخر موضع معروف هو b2</text>\n  <rect class=\"d-box\" x=\"440\" y=\"20\" width=\"240\" height=\"230\" rx=\"14\"/>\n  <text class=\"d-label-strong\" x=\"460\" y=\"48\">GitHub (origin)</text>\n  <circle class=\"d-box\" cx=\"490\" cy=\"130\" r=\"22\"/>\n  <text class=\"d-code\" x=\"490\" y=\"135\" text-anchor=\"middle\">b2</text>\n  <circle class=\"d-box-success\" cx=\"600\" cy=\"130\" r=\"22\"/>\n  <text class=\"d-code\" x=\"600\" y=\"135\" text-anchor=\"middle\">d4</text>\n  <path class=\"d-line\" d=\"M578 130 L512 130\"/>\n  <rect class=\"d-box-accent\" x=\"565\" y=\"70\" width=\"70\" height=\"30\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"600\" y=\"90\" text-anchor=\"middle\">main</text>\n  <text class=\"d-label-muted\" x=\"460\" y=\"200\">d4: رفعه زميل</text>\n  <path class=\"d-arrow\" d=\"M440 110 L354 110\" marker-end=\"url(#arrow-l-git-3-1-1)\"/>\n  <text class=\"d-code\" x=\"397\" y=\"100\" text-anchor=\"middle\">fetch</text>\n  <path class=\"d-arrow\" d=\"M354 160 L440 160\" marker-end=\"url(#arrow-l-git-3-1-1)\"/>\n  <text class=\"d-code\" x=\"397\" y=\"182\" text-anchor=\"middle\">push</text>\n</svg><figcaption>فرعك، وملاحظتك عن الـ remote، والـ remote نفسه</figcaption></figure>\n<h2 id=\"sec-3\" tabindex=\"-1\">الـ upstream و git push -u</h2>\n<p>الخيار <code dir=\"ltr\">-u</code> في <code dir=\"ltr\">git push -u origin main</code> (وصيغته الطويلة <code dir=\"ltr\">--set-upstream</code>) يسجّل أن <code dir=\"ltr\">main</code> المحلي لديك <strong>يتتبّع</strong> <code dir=\"ltr\">origin/main</code>. ومن ثم:</p>\n<ul>\n<li>يعرف <code dir=\"ltr\">git push</code> أو <code dir=\"ltr\">git pull</code> المجرّدان على <code dir=\"ltr\">main</code> إلى أين يذهبان؛</li>\n<li>يعرض <code dir=\"ltr\">git status</code> عبارات مثل “ahead 2” أو “behind 1” مقارنةً بـ <code dir=\"ltr\">origin/main</code>؛</li>\n<li>يسرد <code dir=\"ltr\">git branch -vv</code> كل فرع مع الـ upstream الخاص به وعدد الـ commits المتقدّمة والمتأخّرة.</li>\n</ul>\n<p>كل فرع جديد يحتاجه مرة واحدة، عند رفعه الأول: <code dir=\"ltr\">git push -u origin footer</code>.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">الـ fetch أولًا، ثم الـ pull</h2>\n<p><code dir=\"ltr\">git fetch</code> يسأل الـ remote عمّا هو جديد، وينزّل الـ commits، ويحرّك فروع التتبّع البعيدة لديك. لا يلمس أبدًا فروعك أو ملفاتك، لذا فهو آمن دائمًا:</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\"> fetch</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\">                       # \"Your branch is behind 'origin/main' by 1 commit\"</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main..origin/main</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">   # what's new over there</span></span></code></pre></div>\n<p><code dir=\"ltr\">git pull</code> هو <code dir=\"ltr\">git fetch</code> يتبعه دمج الـ upstream في فرعك الحالي. إن لم تكن لديك commits محلية خاصة بك، فهذا fast-forward. أما إن كان في الطرفين commits جديدة، فعلى Git أن ينفّذ merge أو rebase، والإصدارات الحديثة من Git ترفض التخمين:</p>\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>fatal: Need to specify how to reconcile divergent branches.</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\"> config</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --global</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> pull.rebase</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> true</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # replay my unpushed commits on top (my default)</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># or: git config --global pull.rebase false   (create a merge commit)</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># or: git config --global pull.ff only        (refuse unless it's a fast-forward)</span></span></code></pre></div>\n<p>أنصح بـ <code dir=\"ltr\">pull.rebase true</code>. فهو لا يعيد كتابة إلا الـ commits الخاصة بك التي لم تُرفع بعد، وهذا ما تسمح به القاعدة الذهبية، ويُبقي السجلّ خاليًا من commits من نوع “Merge branch ‘main’ of github.com:…” التي لا تقول شيئًا.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">إيقاع يومي</h2>\n<p>اجمع هذه الأوامر معًا وستبدو كل جلسة عمل على الموقع الشخصي متشابهة في كل مرة. ابدأ بالانتقال إلى <code dir=\"ltr\">main</code> وتنفيذ pull، لتبدأ مما هو موجود فعلًا على GitHub لا مما كان موجودًا الأسبوع الماضي. أنشئ فرعًا للتغيير. أنشئ الـ commits أثناء العمل، وارفع الفرع مبكرًا، حتى لو لم يكتمل: الفرع المرفوع نسخة احتياطية، وهو مرئي لكل من يساعدك. وقبل أن تفتحه للمراجعة، نفّذ fetch مجددًا ثم rebase على <code dir=\"ltr\">origin/main</code> المحدَّث إن كان قد تحرّك. هذه العادة وحدها تمنع معظم عمليات الرفع المرفوضة والتعارضات المفاجئة التي يلوم الناس Git عليها.</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\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x26;&#x26; </span><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> pull</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\"> projects-filter</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># …work and commit…</span></span>\n<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\"> -u</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> projects-filter</span></span></code></pre></div>\n<h2 id=\"sec-6\" tabindex=\"-1\">حين يُرفض الرفع</h2>\n<p>إن رفع أحدهم شيئًا إلى <code dir=\"ltr\">main</code> منذ آخر fetch لك، يُرفض رفعك:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"7\"><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> ! [rejected]        main -&gt; main (fetch first)\nerror: failed to push some refs to 'https://github.com/maya-okafor/portfolio.git'</code></pre></div>\n<p>في الـ remote commits ليست لديك. اسحبها، وحُلّ أي تعارض بالروتين الذي تعلّمته في القسم الثاني، وشغّل فحوصك، ثم ارفع مجددًا.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>تجاوز الرفض بالقوة</span></p>\n<p>الرفض موجود لأن الرفع كان سيرمي commits شخص آخر. و <code dir=\"ltr\">git push --force</code> يفعل ذلك بالضبط، وهو أكثر الطرق شيوعًا لخسارة عمل زميل. على الفرع المشترك، الحلّ دائمًا <code dir=\"ltr\">git pull</code> ثم <code dir=\"ltr\">git push</code>.</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>حين تُحذف الفروع على GitHub بعد دمجها، تبقى ملاحظاتك من نوع <code dir=\"ltr\">origin/footer</code>. الأمر <code dir=\"ltr\">git fetch --prune</code> يزيلها، و <code dir=\"ltr\">git config --global fetch.prune true</code> يجعل كل fetch يفعل ذلك.</p>\n</aside>\n<p>طلب منك رفعك الأول بيانات الاعتماد. في الدرس التالي ستُعدّ المصادقة كما يجب، فيتوقّف Git عن السؤال ويعرف GitHub أنك أنت فعلًا.</p>\n","toc":[{"id":"sec-1","text":"ضع الموقع الشخصي على GitHub","level":2},{"id":"sec-2","text":"فروع التتبّع البعيدة: ذاكرة Git عن الـ remote","level":2},{"id":"sec-3","text":"الـ upstream و git push -u","level":2},{"id":"sec-4","text":"الـ fetch أولًا، ثم الـ pull","level":2},{"id":"sec-5","text":"إيقاع يومي","level":2},{"id":"sec-6","text":"حين يُرفض الرفع","level":2}],"takeaways":["الـ remote عنوان URL مسمّى لنسخة أخرى من الـ repository؛ و <code dir=\"ltr\">origin</code> هو الاسم المتعارف عليه للنسخة التي استنسخت منها أو ترفع إليها.","<code dir=\"ltr\">origin/main</code> هو سجلّك المحلي لمكان <code dir=\"ltr\">main</code> على الـ remote عند آخر fetch؛ ولا يتحرّك إلا حين تنفّذ fetch أو pull أو push.","<code dir=\"ltr\">git push -u origin &lt;branch&gt;</code> يرفع الفرع ويضبطه كـ upstream، فيعرف <code dir=\"ltr\">git push</code> أو <code dir=\"ltr\">git pull</code> المجرّدان لاحقًا إلى أين يذهبان.","<code dir=\"ltr\">git fetch</code> ينزّل دون أن يلمس فروعك؛ و <code dir=\"ltr\">git pull</code> ينفّذ fetch ثم merge أو rebase.","الرفع المرفوض يعني أن في الـ remote commits ليست لديك؛ اسحبها، ثم ارفع مجددًا، ولا تلجأ إلى القوة أبدًا."],"further":[{"title":"Working with Remotes (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes"},{"title":"Remote Branches (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches"},{"title":"Pushing commits to a remote repository","url":"https://docs.github.com/en/get-started/using-git/pushing-commits-to-a-remote-repository"},{"title":"git pull reference","url":"https://git-scm.com/docs/git-pull"}],"quiz":[{"q":"<p>شغّلت <code dir=\"ltr\">git fetch</code> فنزّل commitين جديدين على <code dir=\"ltr\">main</code>. ما الذي تغيّر في الـ working tree لديك؟</p>","options":[{"text":"صارت ملفاتك تتضمّن تغييرات الـ commitين الجديدين.","why":"هذا ما يفعله <code dir=\"ltr\">git pull</code> في خطوته الثانية. أما <code dir=\"ltr\">git fetch</code> وحده فلا يلمس ملفاتك ولا فروعك أبدًا."},{"text":"تقدّم <code dir=\"ltr\">main</code> المحلي لديك بـ commitين.","why":"الـ fetch يحدّث فروع التتبّع البعيدة مثل <code dir=\"ltr\">origin/main</code>، ولا يحدّث <code dir=\"ltr\">main</code> الخاص بك أبدًا."},{"text":"دُمج الـ commitان وأُنشئ merge commit.","why":"لا يحدث أي دمج عند الـ fetch. أنت من يختار متى وكيف يدمج."},{"text":"لا شيء؛ تحرّك <code dir=\"ltr\">origin/main</code> فقط، وملفاتك و <code dir=\"ltr\">main</code> لم تتغيّر.","why":"صحيح. الـ fetch آمن دائمًا. قارن بـ <code dir=\"ltr\">git log main..origin/main</code> قبل أن تقرّر هل تدمج أم تنفّذ rebase."}],"answer":3},{"q":"<p>يفشل <code dir=\"ltr\">git push</code> بالرسالة: <code dir=\"ltr\">Updates were rejected because the remote contains work that you do not have locally.</code> ما الإصلاح الصحيح؟</p>","options":[{"text":"شغّل <code dir=\"ltr\">git pull</code> لتجلب commits الـ remote، وحُلّ أي تعارض، ثم <code dir=\"ltr\">git push</code> مجددًا.","why":"صحيح. في الـ remote commits تنقصك؛ ادمجها أولًا، فيصبح رفعك fast-forward."},{"text":"شغّل <code dir=\"ltr\">git push --force</code> لتفوز نسختك.","why":"هذا سيحذف الـ commits التي رفعها شخص آخر. الرفع بالقوة ليس أبدًا الحلّ حين يكون الفرع المشترك متقدّمًا عليك."},{"text":"احذف الـ repository المحلي واستنسخه من جديد.","why":"ستخسر الـ commits التي لم ترفعها. أما الـ pull فيدمج الطرفين دون خسارة أي شيء."},{"text":"شغّل <code dir=\"ltr\">git push -u origin main</code> لإعادة ضبط التتبّع.","why":"التتبّع ليس المشكلة. الفرع البعيد تقدّم، ورفعك لا يستطيع تقديمه بـ fast-forward."}],"answer":0},{"q":"<p>ماذا يضيف <code dir=\"ltr\">-u</code> إلى <code dir=\"ltr\">git push -u origin footer</code>؟</p>","options":[{"text":"يرفع الملفات غير المتتبَّعة مع الـ commits.","why":"الرفع لا يرسل إلا commits. والملفات غير المتتبَّعة ليست جزءًا من أي commit."},{"text":"يجعل الرفع أسرع بتخطّي التحقّق.","why":"<code dir=\"ltr\">-u</code> اختصار لـ <code dir=\"ltr\">--set-upstream</code>. ولا علاقة له بالسرعة أو الفحوص."},{"text":"يسجّل <code dir=\"ltr\">origin/footer</code> كـ upstream لفرع <code dir=\"ltr\">footer</code> المحلي، فلا يحتاج <code dir=\"ltr\">git push</code> و <code dir=\"ltr\">git pull</code> لاحقًا إلى أي وسائط.","why":"صحيح. تحتاجه مرة واحدة لكل فرع؛ وبعدها يعرض <code dir=\"ltr\">git status</code> أيضًا عدد الـ commits المتقدّمة والمتأخّرة مقارنةً بـ <code dir=\"ltr\">origin/footer</code>."},{"text":"يحدّث كل فرع على الـ remote، لا <code dir=\"ltr\">footer</code> وحده.","why":"لا يُرفع إلا الفرع الذي تسمّيه. ويوجد الخيار <code dir=\"ltr\">--all</code> لرفع كل الفروع."}],"answer":2},{"q":"<p>في <code dir=\"ltr\">main</code> المحلي لديك commit واحد لم ترفعه، وفي <code dir=\"ltr\">origin/main</code> commitان لم تسحبهما. مع ضبط <code dir=\"ltr\">pull.rebase</code> على <code dir=\"ltr\">true</code>، ماذا يفعل <code dir=\"ltr\">git pull</code>؟</p>","options":[{"text":"ينشئ merge commit يجمع الـ commit الخاص بك مع الـ commitين البعيدين.","why":"هذا هو السلوك مع <code dir=\"ltr\">pull.rebase false</code>. أما مع <code dir=\"ltr\">true</code> فينفّذ Git rebase بدل الدمج."},{"text":"ينفّذ fetch، ثم يعيد تشغيل الـ commit غير المرفوع فوق الـ commitين البعيدين.","why":"صحيح. يبقى السجلّ خطيًا، ولا تُعاد كتابة إلا الـ commit الخاص بك الذي لم يُرفع، وهذا يحترم القاعدة الذهبية."},{"text":"يرفض، لأن الفرعين تباعدا.","why":"هذا ما يفعله <code dir=\"ltr\">pull.ff only</code>. أما <code dir=\"ltr\">pull.rebase true</code> فيعالج التباعد بالـ rebase."}],"answer":1}],"exercise":{"kind":"fill","language":"bash","template":"git remote {{0}} origin https://github.com/maya-okafor/portfolio.git\ngit push {{1}} origin main      # first push: also set the upstream\n\n# later: see what's new on GitHub without touching my files\ngit {{2}}\ngit log --oneline main..origin/main","blanks":[{"answers":["add"]},{"answers":["-u","--set-upstream"]},{"answers":["fetch","fetch origin"]}],"title":"اربط الموقع الشخصي وزامِنه","prompt":"<p>املأ الفراغات: سجّل الـ repository الموجود على GitHub باسم <code dir=\"ltr\">origin</code>، وارفع <code dir=\"ltr\">main</code> للمرة الأولى بحيث يتتبّع <code dir=\"ltr\">origin/main</code>، ثم تحقّق لاحقًا من وجود commits جديدة على GitHub دون تغيير فرعك أو ملفاتك.</p>\n","hints":["لدى <code dir=\"ltr\">git remote</code> أمر فرعي يسجّل اسمًا جديدًا وعنوان URL.","خيار الرفع الذي يسجّل الـ upstream حرف واحد، اختصار لكلمة upstream.","أحد أوامر المزامنة الأربعة ينزّل دون أن يدمج."],"explanation":"<p><code dir=\"ltr\">git remote add origin &lt;url&gt;</code> يسمّي عنوان GitHub باسم <code dir=\"ltr\">origin</code>. و <code dir=\"ltr\">git push -u origin main</code> يرفع <code dir=\"ltr\">main</code> ويضبط <code dir=\"ltr\">origin/main</code> كـ upstream له، فيعمل <code dir=\"ltr\">git push</code> أو <code dir=\"ltr\">git pull</code> المجرّد من ذلك الحين. و <code dir=\"ltr\">git fetch</code> يحدّث <code dir=\"ltr\">origin/main</code> دون أن يلمس <code dir=\"ltr\">main</code> أو ملفاتك؛ ثم يسرد <code dir=\"ltr\">git log main..origin/main</code> بالضبط ما ستحصل عليه لو نفّذت pull.</p>\n","checkLabels":{}},"words":744,"readMinutes":4}