حول المحتوى:
متى تختار submodule أو subtree، كيفية إعداد كل منهما، إدارة التحديثات والتزامن بين المستودعات، وحلول لمشكلات الشائعة أثناء الاستخدام الفعلي.
Git ليس مجرد أوامر commit و push و pull. في المشاريع المتوسطة والكبيرة ستحتاج غالبًا لإعادة استخدام أكواد من مستودعات أخرى، مثل مكتبات مشتركة، أو موديولات داخلية، أو قوالب جاهزة. هنا يظهر دور Git submodules subtrees كأدوات أساسية لربط عدة مستودعات معًا بشكل منظم.
في هذا الدليل سنشرح:
Git submodule هو طريقة لإضافة مستودع Git داخل مستودع آخر كمرجع (Reference) إلى commit محدد. أي أن المشروع الرئيسي يعرف أن هناك مشروعًا فرعيًا، لكنه لا ينسخ تاريخه بالكامل؛ فقط يحفظ إشارة إلى رقم commit في المستودع الفرعي.
بصيغة أخرى:
هذا مناسب عندما تريد أن يبقى المشروع الفرعي مستقل تمامًا (مثل مكتبة خارجية أو مشروع آخر تستخدمه كما هو).
Git subtree يقوم بدمج مستودع خارجي داخل مشروعك كجزء من شجرة الملفات (tree) الخاصة بك مع إمكانية الاحتفاظ بتاريخ الكوميتات إن رغبت. من وجهة نظر المشروع الرئيسي، ملفات المشروع الفرعي تصبح جزءًا من المستودع، بدون وجود مجلد Git مستقل داخلها.
Git subtree يمنحك شعور أن الكود جزء طبيعي من مشروعك، مع إمكانية التزامن ثنائي الاتجاه مع المستودع الأصلي.
أمثلة:
git submodule update في كل مرة.أمثلة:
| العنصر | Submodule | Subtree |
|---|---|---|
| الاستقلالية | مستودع مستقل داخل المشروع | الكود مدمج في شجرة المشروع |
| سهولة الاستخدام | أوامر إضافية وتحتاج انتباه | أسهل للفريق، تعامل مثل ملفات عادية |
| حجم التاريخ | لا يتم نسخ تاريخ المستودع بالكامل | يمكن دمج التاريخ أو جزء منه |
| مشاركة التغييرات | أصعب قليلاً في التزامن ثنائي الاتجاه | مدعوم عبر subtree pull/push |
لنفترض أن لديك مشروعًا رئيسيًا، وتريد إضافة مكتبة من GitHub كمجلد فرعي:
git submodule add https://github.com/user/lib.git libs/lib
https://github.com/user/lib.git: رابط المستودع الخارجي.libs/lib: المسار الذي سيُضاف فيه المجلد داخل مشروعك.بعد تنفيذ الأمر:
libs/lib وفي داخله مستودع Git مستقل..gitmodules في جذر مشروعك لتخزين إعدادات الـ submodules.لا تنسَ تنفيذ:
git commit -am "Add lib as submodule"
عندما يقوم مطور جديد باستنساخ المشروع، عليه استخدام:
git clone <repo-url>
cd project
git submodule update --init --recursive
الأمر --recursive مفيد إذا كان هناك submodules داخل submodules.
من داخل المشروع الرئيسي:
cd libs/lib
git fetch
git checkout main # أو أي فرع تريده
git pull origin main
cd ../..
git add libs/lib
git commit -m "Update lib submodule to latest main"
يمكنك أيضًا من جذر المشروع:
git submodule update --remote libs/lib
git commit -am "Update lib submodule"
من داخل مجلد الـ submodule:
cd libs/lib
git checkout v1.2.0 # أو commit hash
cd ../..
git add libs/lib
git commit -m "Lock lib submodule to v1.2.0"
الأعراض: المجلدات الفرعية فارغة أو تحتوي فقط على هيكل بدون ملفات حقيقية.
الحل:
git submodule update --init --recursive
.gitmodules مضاف إلى Git ومُدار مع بقية المشروع.git submodule sync
git submodule update --init --recursive
بعد تحديث submodule داخليًا، يجب عليك العودة للمشروع الرئيسي وعمل git add للمجلد الفرعي (المرجع نفسه يتغير)، ثم commit، وإلا سيبقى الفريق على الإصدار القديم.
بعض إصدارات Git تتطلب استخدام git subtree كإضافة، لكن في معظم الإصدارات الحديثة أصبح مدعومًا كأمر مدمج. تأكد من وجود الأمر:
git help subtree
لنفترض أنك تريد إضافة مكتبة في المسار libs/lib:
git remote add lib-remote https://github.com/user/lib.git
git fetch lib-remote
git subtree add --prefix=libs/lib lib-remote main --squash
--prefix=libs/lib: المسار الذي سيوضع فيه الكود داخل مشروعك.lib-remote: اسم الـ remote الذي يشير للمستودع الخارجي.main: الفرع الذي تريد استيراد الكود منه.--squash: يدمج تاريخ المستودع الفرعي في commit واحد لتقليل الحجم (اختياري).بعد ظهور تحديثات في المستودع الخارجي، يمكنك جلبها:
git fetch lib-remote
git subtree pull --prefix=libs/lib lib-remote main --squash
سيتم دمج التحديثات في مشروعك كما لو كنت تعمل بـ merge عادي، بدون الحاجة لأوامر خاصة لباقي أعضاء الفريق.
إذا أجريت تعديلات داخل libs/lib وتريد إرسالها للمستودع الأصلي:
git subtree push --prefix=libs/lib lib-remote main
هذا الأمر سيأخذ التغييرات التي تمت داخل هذا المجلد ويحولها إلى commits تُدفَع إلى الفرع main في lib-remote.
عند تغيير مكان المستودع الخارجي:
git remote set-url lib-remote <new-url>
git fetch lib-remote
قد تظهر تعارضات عندما يتغير نفس الملف في المستودع الخارجي ومشروعك في نفس الوقت. الحل:
git add للملفات المتعارضة بعد التعديل.git commit لإكمال عملية الـ subtree pull.سواء استخدمت submodule أو subtree، اشرح في README أو وثائق المشروع:
بدلاً من تتبع main مباشرة، اربط مشروعك بـ:
v1.0.0 في submodule أو subtree.إذا كنت مهتمًا بتنظيم البنية التحتية والتحديثات بشكل مؤتمت، يمكنك الاطلاع على مقال: GitOps باختصار: كيف تجعل Git مدير البنية التحتية حيث ستجد مبادئ مفيدة لتنسيق عمليات النشر مع Git.
لا تستخدم submodules داخل submodules داخل subtrees إلا إذا كان هناك سبب قوي جدًا. كل طبقة إضافية تزيد من تعقيد إدارة المستودعات، خاصة مع فرق كبيرة أو متوزعة.
الأنسب: Git Submodule
لأنك تريد أن تبقى المكتبة مستقلة، وتستطيع التحديث للإصدار الجديد مباشرة من المستودع الرسمي، وربما تستخدمها في عدة مشاريع.
الأنسب: Git Subtree
يمكنك التعامل مع الكود وكأنه جزء من المشروع، ثم دفع التغييرات إلى المستودع المركزي عند الحاجة، بدون تعقيد submodules على المطورين.
المزج بين الاثنين ممكن:
إتقان Git submodules subtrees يوفر عليك الكثير من العشوائية عند إعادة استخدام الكود بين المشاريع، ويجعل بنية مشاريعك أكثر تنظيمًا وقابلية للصيانة، خاصة مع نمو عدد الخدمات والمكتبات الداخلية في الفريق أو الشركة.
متى تختار submodule أو subtree، كيفية إعداد كل منهما، إدارة التحديثات والتزامن بين المستودعات، وحلول لمشكلات الشائعة أثناء الاستخدام الفعلي.
مساحة اعلانية