EduFlow
Git و GitHub
⁦0%⁩

الوحدة 3 · التعاون على GitHub

الـ Remotes: الـ Push والـ Fetch والـ Pull والفروع المتتبِّعة

16 دقراءة 4 داملأ الفراغات

الدرس في لمحة

اربط موقعك الشخصي بـ repository على GitHub، وارفع الفروع مع ضبط تتبّع الـ upstream، وابقَ متزامنًا باستخدام fetch و pull وأنت تفهم ما هو origin/main فعلًا.

  1. 1ضع الموقع الشخصي على GitHub
  2. 2فروع التتبّع البعيدة: ذاكرة Git عن الـ remote
  3. 3الـ upstream و git push -u
  4. 4الـ fetch أولًا، ثم الـ pull
  5. 5إيقاع يومي
  6. 6حين يُرفض الرفع

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

حتى الآن يعيش موقعك الشخصي في مكان واحد، حاسوبك المحمول. إن تعطّل القرص ضاع كل شيء، ولا يستطيع أحد غيرك أن يراه أو يراجعه. الـ remote (المستودع البعيد) نسخة أخرى من الـ repository في مكان آخر، على GitHub في الغالب. ومهمّة Git أن يُبقي النسختين متزامنتين، وهو يفعل ذلك بأربعة أوامر أساسية: clone و fetch و pull و push.

ضع الموقع الشخصي على GitHub

على github.com، أنشئ repository جديدًا باسم portfolio. اترك “Add a README” وخيارات التهيئة الأخرى دون تحديد: فالـ repository المحلي لديك له سجلّ بالفعل، و commit إضافي على GitHub سيعطيك سجلّين لا علاقة بينهما عليك التوفيق بينهما. بعد ذلك يعرض GitHub الأوامر اللازمة لربط repository موجود. وخلاصتها:

Terminal
git remote add origin https://github.com/maya-okafor/portfolio.git
git remote -v
git push -u origin main

git remote add يعطي عنوان URL اسمًا قصيرًا. و origin مجرد عُرف، الاسم الذي يستخدمه Git تلقائيًا حين تستنسخ، لكن الجميع يستخدمه، فاستخدمه أنت أيضًا. الرفع يحمّل الـ commits الخاصة بك وينشئ main على GitHub. في المرة الأولى سيطلب منك Git المصادقة؛ والدرس التالي يشرح إعدادها كما يجب بمفاتيح SSH أو بمدير بيانات اعتماد.

وإن كنت تستخدم GitHub CLI، فالأمر gh repo create portfolio --public --source=. --remote=origin --push يفعل كل ذلك دفعة واحدة.

أما إن بدأت من الجهة الأخرى، بـ repository موجود أصلًا على GitHub، فإنك تستنسخه (clone) بدلًا من ذلك:

Terminal
git clone https://github.com/maya-okafor/portfolio.git

الـ clone ينشئ المجلد، وينزّل كل commit، ويضيف remote اسمه origin، وينقلك إلى main مع إعداد التتبّع جاهزًا.

فروع التتبّع البعيدة: ذاكرة Git عن الـ remote

بعد الرفع، يعرض git branch -a شيئًا جديدًا:

Text
* main
  remotes/origin/main

origin/main فرع تتبّع بعيد (remote-tracking branch): ملاحظة يحتفظ بها الـ repository الخاص بك عن مكان main على origin في آخر مرة تواصلت معه. لا تنشئ عليه commits أبدًا. ولا يتحرّك إلا حين تنفّذ fetch أو pull أو push. لهذا يستطيع git status أن يقول “Your branch is up to date with ‘origin/main’” بينما رفع زميلك شيئًا قبل خمس دقائق: لم يسأل Git موقع GitHub منذ ذلك الحين.

يسارًا، حاسوبك: main يشير إلى c3؛ و origin/main، آخر نسخة معروفة لديك، يشير إلى b2. يمينًا، GitHub: main يشير إلى d4، وهو commit رفعه زميل. git fetch ينزّل d4 ويحرّك origin/main؛ ثم يدمجه git pull أيضًا في main لديك؛ و git push يرسل الـ commits الخاصة بك ويحرّك main على GitHub. حاسوبك b2 c3 main origin/main origin/main: آخر موضع معروف هو b2 GitHub (origin) b2 d4 main d4: رفعه زميل fetch push
فرعك، وملاحظتك عن الـ remote، والـ remote نفسه

الـ upstream و git push -u

الخيار -u في git push -u origin main (وصيغته الطويلة --set-upstream) يسجّل أن main المحلي لديك يتتبّع origin/main. ومن ثم:

  • يعرف git push أو git pull المجرّدان على main إلى أين يذهبان؛
  • يعرض git status عبارات مثل “ahead 2” أو “behind 1” مقارنةً بـ origin/main؛
  • يسرد git branch -vv كل فرع مع الـ upstream الخاص به وعدد الـ commits المتقدّمة والمتأخّرة.

كل فرع جديد يحتاجه مرة واحدة، عند رفعه الأول: git push -u origin footer.

الـ fetch أولًا، ثم الـ pull

git fetch يسأل الـ remote عمّا هو جديد، وينزّل الـ commits، ويحرّك فروع التتبّع البعيدة لديك. لا يلمس أبدًا فروعك أو ملفاتك، لذا فهو آمن دائمًا:

Terminal
git fetch
git status                       # "Your branch is behind 'origin/main' by 1 commit"
git log --oneline main..origin/main   # what's new over there

git pull هو git fetch يتبعه دمج الـ upstream في فرعك الحالي. إن لم تكن لديك commits محلية خاصة بك، فهذا fast-forward. أما إن كان في الطرفين commits جديدة، فعلى Git أن ينفّذ merge أو rebase، والإصدارات الحديثة من Git ترفض التخمين:

Text
fatal: Need to specify how to reconcile divergent branches.

اتّخذ القرار مرة واحدة:

Terminal
git config --global pull.rebase true    # replay my unpushed commits on top (my default)
# or: git config --global pull.rebase false   (create a merge commit)
# or: git config --global pull.ff only        (refuse unless it's a fast-forward)

أنصح بـ pull.rebase true. فهو لا يعيد كتابة إلا الـ commits الخاصة بك التي لم تُرفع بعد، وهذا ما تسمح به القاعدة الذهبية، ويُبقي السجلّ خاليًا من commits من نوع “Merge branch ‘main’ of github.com:…” التي لا تقول شيئًا.

إيقاع يومي

اجمع هذه الأوامر معًا وستبدو كل جلسة عمل على الموقع الشخصي متشابهة في كل مرة. ابدأ بالانتقال إلى main وتنفيذ pull، لتبدأ مما هو موجود فعلًا على GitHub لا مما كان موجودًا الأسبوع الماضي. أنشئ فرعًا للتغيير. أنشئ الـ commits أثناء العمل، وارفع الفرع مبكرًا، حتى لو لم يكتمل: الفرع المرفوع نسخة احتياطية، وهو مرئي لكل من يساعدك. وقبل أن تفتحه للمراجعة، نفّذ fetch مجددًا ثم rebase على origin/main المحدَّث إن كان قد تحرّك. هذه العادة وحدها تمنع معظم عمليات الرفع المرفوضة والتعارضات المفاجئة التي يلوم الناس Git عليها.

Terminal
git switch main && git pull
git switch -c projects-filter
# …work and commit…
git push -u origin projects-filter

حين يُرفض الرفع

إن رفع أحدهم شيئًا إلى main منذ آخر fetch لك، يُرفض رفعك:

Text
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/maya-okafor/portfolio.git'

في الـ remote commits ليست لديك. اسحبها، وحُلّ أي تعارض بالروتين الذي تعلّمته في القسم الثاني، وشغّل فحوصك، ثم ارفع مجددًا.

طلب منك رفعك الأول بيانات الاعتماد. في الدرس التالي ستُعدّ المصادقة كما يجب، فيتوقّف Git عن السؤال ويعرف GitHub أنك أنت فعلًا.

أهم النقاط

  • الـ remote عنوان URL مسمّى لنسخة أخرى من الـ repository؛ و origin هو الاسم المتعارف عليه للنسخة التي استنسخت منها أو ترفع إليها.
  • origin/main هو سجلّك المحلي لمكان main على الـ remote عند آخر fetch؛ ولا يتحرّك إلا حين تنفّذ fetch أو pull أو push.
  • git push -u origin <branch> يرفع الفرع ويضبطه كـ upstream، فيعرف git push أو git pull المجرّدان لاحقًا إلى أين يذهبان.
  • git fetch ينزّل دون أن يلمس فروعك؛ و git pull ينفّذ fetch ثم merge أو rebase.
  • الرفع المرفوض يعني أن في الـ remote commits ليست لديك؛ اسحبها، ثم ارفع مجددًا، ولا تلجأ إلى القوة أبدًا.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

شغّلت git fetch فنزّل commitين جديدين على main. ما الذي تغيّر في الـ working tree لديك؟

السؤال 2 من 4

يفشل git push بالرسالة: Updates were rejected because the remote contains work that you do not have locally. ما الإصلاح الصحيح؟

السؤال 3 من 4

ماذا يضيف -u إلى git push -u origin footer؟

السؤال 4 من 4

في main المحلي لديك commit واحد لم ترفعه، وفي origin/main commitان لم تسحبهما. مع ضبط pull.rebase على true، ماذا يفعل git pull؟

تدريب

املأ الفراغات

اربط الموقع الشخصي وزامِنه

املأ الفراغات: سجّل الـ repository الموجود على GitHub باسم origin، وارفع main للمرة الأولى بحيث يتتبّع origin/main، ثم تحقّق لاحقًا من وجود commits جديدة على GitHub دون تغيير فرعك أو ملفاتك.

git remote origin https://github.com/maya-okafor/portfolio.git git push origin main # first push: also set the upstream # later: see what's new on GitHub without touching my files git git log --oneline main..origin/main