الوحدة 3 · التعاون على GitHub
الـ Remotes: الـ Push والـ Fetch والـ Pull والفروع المتتبِّعة
16 دقراءة 4 داملأ الفراغات
الدرس في لمحة
اربط موقعك الشخصي بـ repository على GitHub، وارفع الفروع مع ضبط تتبّع الـ upstream، وابقَ متزامنًا باستخدام fetch و pull وأنت تفهم ما هو origin/main فعلًا.
- 1ضع الموقع الشخصي على GitHub
- 2فروع التتبّع البعيدة: ذاكرة Git عن الـ remote
- 3الـ upstream و git push -u
- 4الـ fetch أولًا، ثم الـ pull
- 5إيقاع يومي
- 6حين يُرفض الرفع
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
حتى الآن يعيش موقعك الشخصي في مكان واحد، حاسوبك المحمول. إن تعطّل القرص ضاع كل شيء، ولا يستطيع أحد غيرك أن يراه أو يراجعه. الـ remote (المستودع البعيد) نسخة أخرى من الـ repository في مكان آخر، على GitHub في الغالب. ومهمّة Git أن يُبقي النسختين متزامنتين، وهو يفعل ذلك بأربعة أوامر أساسية: clone و fetch و pull و push.
ضع الموقع الشخصي على GitHub
على github.com، أنشئ repository جديدًا باسم portfolio. اترك “Add a README” وخيارات التهيئة الأخرى دون تحديد: فالـ repository المحلي لديك له سجلّ بالفعل، و commit إضافي على GitHub سيعطيك سجلّين لا علاقة بينهما عليك التوفيق بينهما. بعد ذلك يعرض GitHub الأوامر اللازمة لربط repository موجود. وخلاصتها:
git remote add origin https://github.com/maya-okafor/portfolio.git
git remote -v
git push -u origin maingit remote add يعطي عنوان URL اسمًا قصيرًا. و origin مجرد عُرف، الاسم الذي يستخدمه Git تلقائيًا حين تستنسخ، لكن الجميع يستخدمه، فاستخدمه أنت أيضًا. الرفع يحمّل الـ commits الخاصة بك وينشئ main على GitHub. في المرة الأولى سيطلب منك Git المصادقة؛ والدرس التالي يشرح إعدادها كما يجب بمفاتيح SSH أو بمدير بيانات اعتماد.
وإن كنت تستخدم GitHub CLI، فالأمر gh repo create portfolio --public --source=. --remote=origin --push يفعل كل ذلك دفعة واحدة.
أما إن بدأت من الجهة الأخرى، بـ repository موجود أصلًا على GitHub، فإنك تستنسخه (clone) بدلًا من ذلك:
git clone https://github.com/maya-okafor/portfolio.gitالـ clone ينشئ المجلد، وينزّل كل commit، ويضيف remote اسمه origin، وينقلك إلى main مع إعداد التتبّع جاهزًا.
فروع التتبّع البعيدة: ذاكرة Git عن الـ remote
بعد الرفع، يعرض git branch -a شيئًا جديدًا:
* main
remotes/origin/mainorigin/main فرع تتبّع بعيد (remote-tracking branch): ملاحظة يحتفظ بها الـ repository الخاص بك عن مكان main على origin في آخر مرة تواصلت معه. لا تنشئ عليه commits أبدًا. ولا يتحرّك إلا حين تنفّذ fetch أو pull أو push. لهذا يستطيع git status أن يقول “Your branch is up to date with ‘origin/main’” بينما رفع زميلك شيئًا قبل خمس دقائق: لم يسأل Git موقع GitHub منذ ذلك الحين.
الـ 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، ويحرّك فروع التتبّع البعيدة لديك. لا يلمس أبدًا فروعك أو ملفاتك، لذا فهو آمن دائمًا:
git fetch
git status # "Your branch is behind 'origin/main' by 1 commit"
git log --oneline main..origin/main # what's new over theregit pull هو git fetch يتبعه دمج الـ upstream في فرعك الحالي. إن لم تكن لديك commits محلية خاصة بك، فهذا fast-forward. أما إن كان في الطرفين commits جديدة، فعلى Git أن ينفّذ merge أو rebase، والإصدارات الحديثة من Git ترفض التخمين:
fatal: Need to specify how to reconcile divergent branches.اتّخذ القرار مرة واحدة:
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 عليها.
git switch main && git pull
git switch -c projects-filter
# …work and commit…
git push -u origin projects-filterحين يُرفض الرفع
إن رفع أحدهم شيئًا إلى main منذ آخر fetch لك، يُرفض رفعك:
! [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 ليست لديك؛ اسحبها، ثم ارفع مجددًا، ولا تلجأ إلى القوة أبدًا.