داخليات Git: الكائنات والمراجع
داخليات Git: الكائنات والمراجع
يستخدم معظم المهندسين Git يومياً دون أن ينظروا تحت السطح. لكن المهندسين الذين يتقنون Git حقاً — أولئك الذين ينقذون المستودعات التالفة، ويصممون استراتيجيات التفرع على نطاق واسع، ويبنون خطوط CI/CD التي لا تفقد أي عمل — يفهمون نموذج الكائنات تحت الغطاء. يزيل هذا الدرس السحر ويريك بالضبط ما هو Git: نظام ملفات يعتمد على المحتوى مع طبقة رقيقة للتحكم في الإصدار فوقه.
مخزن الكائنات: أربعة أنواع تشرح كل شيء
كل قطعة بيانات يتتبعها Git تعيش في .git/objects/. يخزن Git أربعة أنواع من الكائنات، يُعرَّف كل منها بتجزئة SHA-1 (أو SHA-256 في المستودعات الأحدث) لمحتوياته. التجزئة هي الهوية — غيّر بايتاً واحداً، تحصل على كائن مختلف تماماً. هذا هو نموذج العنونة بالمحتوى.
- Blob — محتويات الملف الخام، لا شيء آخر. لا اسم ملف، لا صلاحيات. ملفان بمحتويات متطابقة يتشاركان blob واحداً.
- Tree — لقطة دليل: قائمة من إدخالات (mode، اسم، SHA) تشير إلى blobs وأشجار أخرى. تمثل دليلاً واحداً في لحظة واحدة.
- Commit — مؤشر إلى شجرة جذر، وصفر أو أكثر من SHAs للccommits الوالدة، وبيانات وصفية للمؤلف/الموفر، ورسالة. الcommit هو ما يمنح Git رسم تاريخه.
- Tag — كائن وسم مشروح: يشير إلى أي كائن (عادةً commit) ويضيف هوية المُوسِّم وتاريخاً وتوقيعاً PGP لسلامة الإصدارات.
git commit --amend لا يحرر الcommit القديم؛ بل يكتب كائن commit جديداً تماماً بـSHA جديدة ويحرك مؤشر الفرع. الcommit القديم لا يزال موجوداً حتى يُجمع كمهملات.
تشريح الـ DAG
يُشكّل تاريخ الcommits رسماً بيانياً موجهاً لا دوري (DAG). يشير كل commit للخلف نحو والده (والديه). يملك commit الدمج والدَين. هذه البنية تجعل التفرع والدمج رخيصَين — لا تُنسخ بيانات، تُكتب مؤشرات فحسب.
استكشاف مخزن الكائنات مباشرةً
كل ما يلي قابل للتشغيل في أي مستودع Git. استخدم git cat-file — سكين الجيش السويسري للتحقيق في الكائنات الخام.
كل ملف ضمن .git/objects/ يستخدم أول حرفين سداسيين كاسم دليل والـ38 المتبقية كاسم ملف. الكائنات مضغوطة بـzlib. Packfiles (في .git/objects/pack/) تجمع كائنات كثيرة معاً للكفاءة — ستراها في أي مستودع مستنسخ.
المراجع (Refs): أسماء تشير إلى SHAs
المرجع هو ببساطة ملف يحتوي على SHA. هذا كل شيء. refs/heads/main ملف بحجم 41 بايت يحمل SHA لأحدث commit على main. عندما تنفذ git commit، يكتب Git كائن الcommit الجديد، ثم يعيد كتابة ذلك الملف بالـSHA الجديدة.
refs/heads/*— الفروع المحليةrefs/remotes/*— فروع التتبع عن بُعد (لقطات للقراءة فقط لما كان عليه الريموت آخر مرة جلبت)refs/tags/*— وسوم خفيفة (مجرد ملف SHA) أو وسوم مشروحة (تشير إلى كائن tag)HEAD— مرجع رمزي يشير إلى الفرع المُسحب حالياً، أو SHA مجردة عند الانفصال
git reset --hard أو push قسري عرضي حالة من الذعر بسبب commits "مفقودة"، فإن reflog يُنقذك في معظم الأحيان. تُحتفظ بإدخالات reflog لمدة 90 يوماً افتراضياً (gc.reflogExpire). على الريموتات المشتركة (GitHub، GitLab)، لا يكون reflog مكشوفاً — لكن محلياً يمكنك الاسترداد دائماً قبل تشغيل git gc.
كيف تُمكّن الحزم ورسم الكائنات التوسع
في المستودعات الكبيرة (فكر في Chromium بـ900 ألف commit، أو Linux بـ1.1 مليون)، سيكون مخزن الكائنات الفردية غير قابل للإدارة. يستخدم Git packfiles وضغط الدلتا: بدلاً من تخزين كل إصدار لملف، يخزن نسخة كاملة واحدة وفروقات ثنائية (deltas) بين blobs المتشابهة. git gc (جمع المهملات) يُشغّل الحزم. على GitHub، كل git push يُشغّل إعادة حزم جانب الخادم.
git gc --aggressive على ريموت مشترك نشط. يعيد كتابة جميع سلاسل دلتا الحزمة وقد يستغرق ساعات على المستودعات الكبيرة. على المنصات المُدارة (GitHub، GitLab، Bitbucket)، دع المنصة تتولى إعادة الحزم — تشغّلها بشكل غير متزامن مع ضمان استمرارية خدمة الاستنساخ. على Gitea المستضاف ذاتياً أو المستودعات المجردة، جدوّل git gc في فترات انخفاض حركة المرور.
نموذج العنونة بالمحتوى في الإنتاج
فهم أن Git يعتمد على المحتوى له تداعيات مباشرة على عمل DevOps:
- بنيات قابلة للاستنساخ: تثبيت اعتمادية على SHA لcommit في Git (لا على اسم فرع) يكون حتمياً — نفس SHA يعني دائماً نفس الشجرة بالضبط. هذا سبب إشارة مستودعات manifests Kubernetes ووحدات Terraform ووحدات Go جميعها إلى SHAs.
- التحقق من السلامة: يستطيع
git fsckالكشف عن تلف البيانات من أعطال الأقراص. أي تغيير في بت يغير SHA ويُشير فوراً إلى خطأ. - الاستنساخات الضحلة في CI:
git clone --depth=1يجلب commit الطرف وشجرته فقط — لا تاريخ. هذا سبب قدرة GitHub Actions على استنساخ مستودع بحجم 5 جيجابايت في 3 ثوانٍ لمهمة بناء. المقايضة: لاgit log، لاgit bisect. - الاستنساخات الجزئية:
git clone --filter=blob:none(الفحص المتفرق) يجلب commits والأشجار لكن يحمّل blobs عند الطلب بشكل كسول. هذه الطريقة التي تعمل بها فرق المستودعات الأحادية الكبيرة على نطاق Google مع أجزاء من مستودع دون تحميل تيرابايتات.
git init --object-format=sha256). لم تهاجر GitHub بعد، لكن المنصات الداخلية الكبرى تُقيّمها. نموذج الكائنات متطابق — دالة التجزئة فقط تتغير. في الوقت الحالي، جميع مستودعات الإنتاج التي ستواجهها تستخدم SHA-1. التصادم المعروف لـSHA-1 (SHAttered، 2017) مُخفَّف في Git عبر مكتبة كشف التصادم؛ الهجمات الفعلية على مستودعات Git تبقى نظرية.
بهذا الأساس — blobs والأشجار والcommits والمراجع والـDAG — يصبح كل سلوك آخر في Git قابلاً للتنبؤ. التفرع مجرد كتابة ملف بـ41 بايت. الدمج هو إنشاء commit بوالدَين. إعادة الأساس هي إعادة كتابة كائنات commit بـSHAs والدة جديدة. لديك الآن النموذج الذهني لتشخيص أي مشكلة في Git على مستوى الكائنات.