Git وتدفقات العمل التعاونية

المستودعات البعيدة والتعاون

18 دقيقة الدرس 4 من 28

المستودعات البعيدة والتعاون

كل أمر نفّذته حتى الآن — commit وbranch وmerge وrebase — يعمل بالكامل داخل مستودعك المحلي. في اللحظة التي تحتاج فيها إلى مشاركة العمل مع مهندس آخر أو مع منظومة CI/CD أو خط أنابيب النشر، يجب أن تفهم المستودعات البعيدة (Remotes): ما هي، وكيف يتتبعها Git، والآليات الدقيقة لـfetch وpull وpush. في شركات كـGitHub وShopify وStripe، تتعاون عشرات الفرق على ملايين الـ commits باستخدام هذه الأساسيات بالضبط.

ما هو المستودع البعيد فعلاً

المستودع البعيد ليس سوى عنوان URL مُخزَّن بإسم في .git/config. لا يوجد سحر في Git — origin مجرد اصطلاح وليس كلمة مفتاحية. يمكنك أن لا يكون لديك أي remote، أو واحد، أو عدة. لكل remote اسم (origin أو upstream أو backup) وعنوان URL (HTTPS أو SSH). عند git clone، ينشئ Git تلقائياً remote باسم origin يشير إلى العنوان الذي استنسخت منه.

# عرض كل remotes المضبوطة وعناوينها git remote -v # origin git@github.com:acme/platform.git (fetch) # origin git@github.com:acme/platform.git (push) # إضافة remote ثانٍ (مثلاً نسخة احتياطية أو المستودع الأصلي) git remote add upstream git@github.com:original-org/platform.git # تغيير عنوان remote (مثلاً من HTTPS إلى SSH بعد إعداد المفاتيح) git remote set-url origin git@github.com:acme/platform.git # حذف remote لم تعد بحاجة إليه git remote remove backup # عرض كامل لما يعرفه Git عن remote: عناوين fetch/push، # فروع التتبع، و HEAD git remote show origin

داخلياً، يُخزِّن .git/config هذه المعلومات كأقسام INI بسيطة. عناوين URL مجرد نصوص — يحلّها Git وقت الاتصال بالشبكة عبر البروتوكول المناسب (SSH أو HTTPS أو بروتوكول git:// غير المصادَق، الذي لا يُستخدم في الإنتاج كاد).

فروع التتبع البعيدة (Remote-Tracking Branches)

بعد عملية fetch، يُخزِّن Git ما تعلمه عن المستودع البعيد في فروع التتبع البعيدة — مراجع للقراءة فقط تحت refs/remotes/origin/. هذه نسخة مؤقتة محلية من حالة المستودع البعيد في آخر مرة نفّذت فيها fetch. لا يمكنك الانتقال إليها مباشرةً؛ وجودها يُمكِّنك من المقارنة والدمج دون الحاجة للاتصال بالشبكة مجدداً.

# سرد كل فروع التتبع البعيدة git branch -r # origin/HEAD -> origin/main # origin/main # origin/feature/payments # upstream/main # سرد الفروع المحلية والبعيدة معاً git branch -a # رؤية آخر commits لفروع التتبع البعيدة git log --oneline origin/main -5 # مقارنة فرعك المحلي بنسخة التتبع البعيدة git diff main origin/main
فروع التتبع البعيدة لقطات، لا عروض مباشرة. يعكس origin/main ما بدا عليه main في المستودع البعيد آخر مرة نفّذت فيها git fetch. دفع زميلك قبل خمس دقائق غير مرئي لك حتى تُنفِّذ fetch من جديد. هذا تصميم مقصود — Git نظام يعمل في وضع عدم الاتصال أولاً.

fetch مقابل pull: الفرق الجوهري

git fetch يُنزِّل الكائنات الجديدة ويُحدِّث فروع التتبع البعيدة. لا يُغيِّر شيئاً في شجرة العمل أو الفروع المحلية. git pull اختصار لـgit fetch متبوعاً فوراً بـgit merge أو git rebase (حسب الإعداد). في كبريات شركات التقنية، يفضّل كثير من المهندسين المخضرمين تنفيذ fetch صريحاً ثم المراجعة ثم الدمج، بدلاً من pull، لأن ذلك يمنحهم فرصة رؤية ما قادم قبل تطبيقه.

# جلب كل remotes (يُحدِّث كل فروع التتبع البعيدة دون تغيير محلي) git fetch --all # جلب remote محدد git fetch origin # جلب وحذف مراجع التتبع المحلية التي لم تعد موجودة على البعيد git fetch --prune origin # نمط المراجعة الآمن الذي يستخدمه المهندسون المتمرسون: git fetch origin git log --oneline --graph origin/main ^main # ماذا قادم؟ git diff main origin/main # ماذا تغيّر؟ git merge origin/main # الدمج يدوياً # الأمر المكافئ في خطوة واحدة (بدون فرصة مراجعة): git pull origin main # Pull مع rebase بدلاً من merge (يحافظ على تاريخ محلي خطي) git pull --rebase origin main # جعل rebase الافتراض الدائم لكل عمليات pull: git config --global pull.rebase true
الإعداد الافتراضي في الإنتاج: دائماً fetch --prune. نفّذ git config --global fetch.prune true مرة واحدة ونسَ الأمر. بذلك ستُنظِّف كل عملية git fetch تلقائياً فروع التتبع البعيدة القديمة (الفروع المحذوفة من البعيد). بدون هذا، يمتلئ git branch -r بمراجع وهمية بمرور الوقت، مما يُربك القادمين الجدد ويُبطئ إكمال الأوامر.

push: إرسال عملك إلى المستودع البعيد

git push يُرفع كائنات محلية إلى البعيد ويطلب منه تقديم مؤشر الفرع. يقبل البعيد فقط إذا كان الـ push يُمثِّل fast-forward على البعيد — أي أن سلسلة الأجداد للـ commit الجديد تتضمن الطرف البعيد الحالي. إذا تقدّم البعيد منذ آخر fetch، يرفض Git الـ push بخطأ non-fast-forward، مُجبِراً إياك على الدمج أولاً.

# Push الفرع الحالي وضبط upstream الخاص به (أول push) git push -u origin feature/payments # -u يضبط علاقة التتبع حتى لا تحتاج الـ push/pull التالية لحجج # Pushes لاحقة على نفس الفرع (upstream مضبوط بالفعل) git push # Push كل الفروع المحلية إلى origin (نادراً ما تريد ذلك — كن محدداً) git push --all origin # Push فرع محلي محدد إلى فرع بعيد بإسم مختلف git push origin local-branch:remote-branch # حذف فرع بعيد (صيغتان مكافئتان) git push origin --delete feature/old-feature git push origin :feature/old-feature # تشغيل تجريبي: رؤية ما سيُرفع دون رفعه فعلاً git push --dry-run origin main # Force push مع lease (أأمن من --force؛ يفشل إذا دفع شخص آخر أولاً) git push --force-with-lease origin feature/payments
لا تستخدم git push --force أبداً على فرع مشترك. يُلغي --force المحتوى البعيد دون شرط، مُدمِّراً commits ربما سحبها زملاؤك بالفعل. استخدم --force-with-lease بدلاً من ذلك: يتضمن فحصاً بأن مرجع التتبع البعيد الخاص بك يطابق الطرف البعيد الفعلي. إذا دفع شخص آخر منذ آخر fetch، يُرفض الـ push — مما يمنع فقدان البيانات الصامت. تُضبط كثير من بيئات استضافة Git لرفض --force على الفروع المحمية.

فروع التتبع بعمق

فرع التتبع (أو الـ upstream branch) هو فرع محلي مضبوط لتتبع فرع تتبع بعيد. هذه العلاقة تُخبر Git بشيئين: أين يدفع افتراضياً، وكم أنت متقدم أو متأخر. يعرض Git هذه المعلومات في git status وفي git branch -vv.

# رؤية علاقات التتبع لكل الفروع المحلية git branch -vv # * feature/payments a3f21bc [origin/feature/payments: ahead 2, behind 1] Add card tokenization # main 9e1c4da [origin/main] Merge PR #412 # ضبط upstream لفرع موجود يدوياً git branch --set-upstream-to=origin/main main # إلغاء upstream (فصل من التتبع البعيد) git branch --unset-upstream feature/old # فحص موقعك نسبةً إلى upstream بدون fetch # (يستخدم مرجع التتبع البعيد المُخزَّن مؤقتاً) git status # On branch feature/payments # Your branch is ahead of 'origin/feature/payments' by 2 commits.
fetch, pull, and push data flows between local and remote Remote (origin) main feature/payments Remote-Tracking Refs (refs/remotes/origin/*) origin/main origin/feature/payments Local Repo main feature/payments Working Tree fetch merge / rebase pull = fetch + merge/rebase push
تدفق البيانات بين المستودع البعيد ومراجع التتبع البعيدة وفروعك المحلية. يُحدِّث fetch الطبقة الوسطى فقط؛ يدمج merge/rebase في الفروع المحلية؛ pull يفعل الاثنين؛ push يُرسل commits محلية إلى المستودع البعيد.

سير عمل Fork والـ Upstream

تستخدم المشاريع مفتوحة المصدر وكثير من البيئات المؤسسية سير عمل Fork: لا تدفع مباشرةً إلى المستودع الأصلي. بدلاً من ذلك تنشئ نسخة (fork) منه على منصة الاستضافة، تستنسخ نسختك، ثم ترسل طلبات pull/merge إلى المستودع الأصلي. هذا هو النموذج القياسي على GitHub وGitLab وBitbucket لأي مشروع لا تملك فيه صلاحية الكتابة المباشرة.

المفتاح هو تضبيط remote-ين في نسختك المحلية المستنسخة: origin يشير إلى نسختك (حيث تدفع)، وupstream يشير إلى المستودع الأصلي (حيث تجلب التحديثات).

# 1. Fork على GitHub (من واجهة الويب)، ثم استنسخ نسختك أنت git clone git@github.com:yourname/kubernetes.git cd kubernetes # 2. أضف المستودع الأصلي بإسم "upstream" git remote add upstream git@github.com:kubernetes/kubernetes.git # تحقق: الآن لديك remote-ان git remote -v # origin git@github.com:yourname/kubernetes.git (fetch) # origin git@github.com:yourname/kubernetes.git (push) # upstream git@github.com:kubernetes/kubernetes.git (fetch) # upstream git@github.com:kubernetes/kubernetes.git (push) # 3. أبقِ نسختك متزامنة مع upstream (افعل هذا قبل كل فرع جديد) git fetch upstream git switch main git merge upstream/main # أو: git rebase upstream/main git push origin main # حافظ على main في نسختك محدَّثة أيضاً # 4. أنشئ فرع ميزة من قاعدة محدَّثة git switch -c feature/add-hpa-scaling # 5. ادفع إلى نسختك وافتح Pull Request إلى upstream git push -u origin feature/add-hpa-scaling
Fork and upstream collaboration workflow Canonical Repo kubernetes/kubernetes (upstream) Your Fork yourname/kubernetes (origin) Local Clone ~/kubernetes remotes: origin + upstream fetch upstream push origin Pull Request
سير عمل Fork: تجلب من upstream (المستودع الأصلي)، وتدفع إلى origin (نسختك)، وتفتح Pull Request إلى المستودع الأصلي.

أنماط الفشل الشائعة

  • النسيان في المزامنة قبل إنشاء الفرع. إذا أنشأت فرعاً من main قديمة دون جلب upstream أولاً، فرع ميزتك مبني على كود قديم. نفّذ دائماً git fetch upstream && git merge upstream/main قبل قطع فرع جديد.
  • رفض non-fast-forward. error: failed to push some refs تعني أن البعيد لديه commits لم تحصل عليها محلياً. الحل دائماً نفسه: git fetch origin، ادمج أو أعد الأساس على العمل الوارد، ثم ادفع مجدداً.
  • فروع التتبع البعيدة القديمة. بعد أن يحذف زميل فرعاً على البعيد، لا يزال git branch -r يُظهره حتى تنفّذ git fetch --prune. فعِّل الحذف التلقائي عالمياً: git config --global fetch.prune true.
  • الدفع إلى remote خاطئ. في سير عمل Fork، تحقق دائماً من git remote -v قبل الدفع. الدفع إلى upstream بدلاً من origin سيحاول الكتابة إلى مستودع قد لا تملك صلاحيته، أو ربما تملكه لكن لا ينبغي تلويثه مباشرةً.
  • تباعد main في نسختك عن upstream. إذا تباعد main في نسختك عن upstream (بسبب commit مباشر خاطئ)، استخدم git reset --hard upstream/main ثم force push إلى main في نسختك لإعادة المحاذاة. هذه هي الحالة الوحيدة التي يكون فيها force push مقبولاً — لأنك تعيد ضبط main في نسختك الخاصة فحسب.
مفاتيح SSH لا كلمات مرور. اضبط دائماً مصادقة مفاتيح SSH لـ remotes Git في خطوط أنابيب CI/CD الإنتاجية ومحطات عمل المطورين. HTTPS مع رموز الوصول الشخصية (PATs) مقبول لكنه يتطلب تدوير الرموز. مفاتيح deploy SSH المقيّدة بمستودع واحد هي النهج الأنظف لعمّال CI. خزِّن المفاتيح الخاصة في مدير الأسرار (Vault أو AWS Secrets Manager أو GitHub Actions Secrets) — لا تُرمِّزها أبداً في الكود.

ES
Edrees Salih
منذ ساعة

We are still cooking the magic in the way!