0%
الدرس 9 من 10
اكتمل 90%

الوحدات الفرعية والأشجار الفرعية في Git

أتقن تقنيات إدارة الاعتماديات الخارجية والمشاريع متعددة الوحدات باستخدام Git.

الوحدات الفرعية والأشجار الفرعية في Git

عندما تصبح مشاريعك معقدة وتعتمد على مستودعات Git أخرى، تصبح الوحدات الفرعية (submodules) والأشجار الفرعية (subtrees) أمرًا أساسيًا. سيعلّمك هذا الدرس كيفية إدارة المشاريع متعددة المستودعات باستخدام Git بكفاءة.

مقدمة إلى اعتماديات Git

المشكلات الشائعة

Projet PrincipalBibliothèque CommuneModule UIAPI ClientUtilitaires

التحديات:

  • مشاركة الشيفرة بين المشاريع
  • إدارة إصدارات الاعتماديات
  • الحفاظ على الاتساق
  • تجنب التكرار

حلول Git

  1. الوحدات الفرعية (Submodules): مراجع تشير إلى مستودعات أخرى
  2. الأشجار الفرعية (Subtrees): دمج الشيفرة الخارجية داخل المستودع
  3. بدائل: مديرو الحزم، والمستودعات الأحادية (monorepos)

وحدات Git الفرعية (Submodules)

المفهوم

الوحدة الفرعية هي مستودع Git مدمج داخل مستودع Git آخر، ويحتفظ بتاريخه ومراجعه الخاصة.

projet-principal/
├── src/
├── lib/
│   └── shared-utils/     # <- Sous-module
│       ├── .git          # Dépôt Git séparé
│       └── utils.js
├── .gitmodules           # Configuration des sous-modules
└── README.md

إضافة وحدة فرعية

# Ajouter un sous-module
git submodule add https://github.com/user/shared-utils.git lib/shared-utils

# Vérifier l'ajout
cat .gitmodules

محتوى الملف .gitmodules:

[submodule "lib/shared-utils"]
    path = lib/shared-utils
    url = https://github.com/user/shared-utils.git
# Commiter l'ajout du sous-module
git add .gitmodules lib/shared-utils
git commit -m "Add shared-utils submodule"

استنساخ مشروع يحتوي على وحدات فرعية

# Méthode 1 : Clone avec initialisation automatique
git clone --recurse-submodules https://github.com/user/projet-principal.git

# Méthode 2 : Clone puis initialisation
git clone https://github.com/user/projet-principal.git
cd projet-principal
git submodule init
git submodule update

# Méthode 3 : Commande combinée
git submodule update --init --recursive

العمل مع الوحدات الفرعية

تحديث وحدة فرعية

# Aller dans le sous-module
cd lib/shared-utils

# Récupérer les dernières modifications
git fetch
git merge origin/main

# Ou directement
git pull origin main

# Revenir au projet principal
cd ../..

# Commiter la nouvelle référence
git add lib/shared-utils
git commit -m "Update shared-utils to latest version"

تحديث جميع الوحدات الفرعية

# Mettre à jour tous les sous-modules
git submodule update --remote

# Avec merge automatique
git submodule update --remote --merge

# Avec rebase
git submodule update --remote --rebase

تعديل وحدة فرعية

# Aller dans le sous-module
cd lib/shared-utils

# Créer une branche
git checkout -b feature/new-utility

# Développer
echo "export function newUtil() { return 'new'; }" >> utils.js
git add utils.js
git commit -m "Add new utility function"

# Pousser vers le dépôt du sous-module
git push origin feature/new-utility

# Revenir au projet principal
cd ../..

# Le sous-module pointe maintenant vers le nouveau commit
git add lib/shared-utils
git commit -m "Update shared-utils with new utility"

الإعدادات المتقدمة

فروع محددة

# Configurer un sous-module pour suivre une branche
git config -f .gitmodules submodule.lib/shared-utils.branch develop

# Mettre à jour vers cette branche
git submodule update --remote

العناوين النسبية

# .gitmodules avec URL relative
[submodule "lib/shared-utils"]
    path = lib/shared-utils
    url = ../shared-utils.git

تجاهل التعديلات

# Ignorer les modifications non commitées dans le sous-module
git config submodule.lib/shared-utils.ignore dirty

# Ignorer tous les changements
git config submodule.lib/shared-utils.ignore all

حذف وحدة فرعية

# 1. Désactiver le sous-module
git submodule deinit lib/shared-utils

# 2. Supprimer du dépôt
git rm lib/shared-utils

# 3. Supprimer le dossier .git/modules
rm -rf .git/modules/lib/shared-utils

# 4. Commiter la suppression
git commit -m "Remove shared-utils submodule"

أشجار Git الفرعية (Subtrees)

المفهوم

تدمج الشجرة الفرعية شيفرة مستودع آخر مباشرةً داخل مستودعك، دون الاحتفاظ بمرجع خارجي.

projet-principal/
├── src/
├── lib/
│   └── shared-utils/     # <- Code intégré
│       └── utils.js      # Pas de .git séparé
└── README.md

إضافة شجرة فرعية

# Ajouter un sous-arbre
git subtree add --prefix=lib/shared-utils \
                https://github.com/user/shared-utils.git \
                main --squash

# Vérifier l'ajout
ls lib/shared-utils/

تحديث شجرة فرعية

# Récupérer les mises à jour
git subtree pull --prefix=lib/shared-utils \
                 https://github.com/user/shared-utils.git \
                 main --squash

المساهمة في شجرة فرعية

# Modifier le code du sous-arbre
echo "export function newFeature() { return 'feature'; }" >> lib/shared-utils/utils.js
git add lib/shared-utils/utils.js
git commit -m "Add new feature to shared utils"

# Pousser les modifications vers le dépôt original
git subtree push --prefix=lib/shared-utils \
                 https://github.com/user/shared-utils.git \
                 main

استخراج شجرة فرعية

# Créer un nouveau dépôt depuis un sous-dossier
git subtree split --prefix=lib/shared-utils -b shared-utils-branch

# Créer un nouveau dépôt
mkdir ../shared-utils-extracted
cd ../shared-utils-extracted
git init
git pull ../projet-principal shared-utils-branch

مقارنة بين Submodules وSubtrees

جدول المقارنة

الجانبSubmodulesSubtrees
التعقيدمرتفعمتوسط
التاريخمنفصلمدمج
حجم المستودعصغيرأكبر
الاستنساخخطوات إضافيةقياسي
التعديلاتمعقدةبسيطة
الإصداراتصريحةضمنية
التعاونصعبأسهل

متى نستخدم كل واحدة؟

Submodules:

  • اعتماديات مستقرة
  • تحكم صارم في الإصدارات
  • فرق منفصلة
  • مشاريع خارجية كبيرة

Subtrees:

  • تطوير نشط
  • تعاون متكرر
  • الأولوية للبساطة
  • مكتبات صغيرة

حالات استخدام عملية

مشروع بمكتبة مشتركة

# Structure du projet
mkdir monorepo-example
cd monorepo-example
git init

# Créer la bibliothèque commune
mkdir -p shared/utils
echo "export function formatDate(date) { 
    return date.toISOString().split('T')[0]; 
}" > shared/utils/date.js

git add shared/
git commit -m "Add shared utilities"

# Ajouter comme sous-module dans d'autres projets
cd ../web-app
git submodule add ../monorepo-example/shared shared/utils

قالب WordPress بوحدات فرعية

# Thème principal
mkdir wp-theme
cd wp-theme
git init

# Ajouter un framework CSS
git submodule add https://github.com/twbs/bootstrap.git assets/bootstrap

# Ajouter une bibliothèque JS
git submodule add https://github.com/jquery/jquery.git assets/jquery

# Structure finale
# wp-theme/
# ├── assets/
# │   ├── bootstrap/    # Sous-module
# │   └── jquery/       # Sous-module
# ├── style.css
# └── index.php

الخدمات المصغّرة ذات الاعتماديات المشتركة

# Service utilisateur
mkdir user-service
cd user-service
git init

# Ajouter les utilitaires communs
git subtree add --prefix=lib/common \
                https://github.com/company/common-lib.git \
                main --squash

# Service commande
cd ../order-service
git init
git subtree add --prefix=lib/common \
                https://github.com/company/common-lib.git \
                main --squash

تمارين عملية

التمرين 1: أساسيات Submodules

# 1. Créer un dépôt de bibliothèque
mkdir math-lib
cd math-lib
git init
echo "export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }" > math.js
git add math.js
git commit -m "Initial math library"

# Simuler un dépôt distant
cd ..
git clone --bare math-lib math-lib.git

# 2. Créer le projet principal
mkdir calculator-app
cd calculator-app
git init
echo "# Calculator App" > README.md
git add README.md
git commit -m "Initial commit"

# 3. Ajouter le sous-module
git submodule add ../math-lib.git lib/math
git commit -m "Add math library submodule"

# 4. Utiliser la bibliothèque
echo "import { add, multiply } from './lib/math/math.js';
console.log('2 + 3 =', add(2, 3));
console.log('4 * 5 =', multiply(4, 5));" > app.js
git add app.js
git commit -m "Add calculator app using math library"

التمرين 2: Subtrees مع المساهمة

# 1. Créer une bibliothèque d'utilitaires
mkdir utils-lib
cd utils-lib
git init
echo "export function capitalize(str) { 
    return str.charAt(0).toUpperCase() + str.slice(1); 
}" > string-utils.js
git add string-utils.js
git commit -m "Add string utilities"

# 2. Créer le projet principal
cd ..
mkdir blog-app
cd blog-app
git init

# 3. Ajouter comme sous-arbre
git subtree add --prefix=utils ../utils-lib main --squash

# 4. Modifier et contribuer
echo "export function slugify(str) { 
    return str.toLowerCase().replace(/\s+/g, '-'); 
}" >> utils/string-utils.js
git add utils/string-utils.js
git commit -m "Add slugify function to string utils"

# 5. Pousser la contribution
git subtree push --prefix=utils ../utils-lib main

التمرين 3: إدارة الإصدارات باستخدام Submodules

# 1. Dans le dépôt de bibliothèque
cd math-lib
git tag v1.0.0

# Ajouter une nouvelle fonction
echo "export function subtract(a, b) { return a - b; }" >> math.js
git add math.js
git commit -m "Add subtract function"
git tag v1.1.0

# 2. Dans le projet principal
cd ../calculator-app

# Mettre à jour vers une version spécifique
cd lib/math
git fetch --tags
git checkout v1.1.0
cd ../..

git add lib/math
git commit -m "Update math library to v1.1.0"

# 3. Utiliser la nouvelle fonction
echo "import { subtract } from './lib/math/math.js';
console.log('10 - 3 =', subtract(10, 3));" >> app.js
git add app.js
git commit -m "Use subtract function"

أفضل الممارسات

بالنسبة إلى Submodules

  1. توثيق واضح

    # README.md
    ## Installation
    
    ```bash
    git clone --recurse-submodules https://github.com/user/project.git

    Mise à jour des sous-modules

    git submodule update --remote
  2. سكربتات الأتمتة

    #!/bin/bash
    # scripts/update-submodules.sh
    
    echo "Updating all submodules..."
    git submodule update --remote --merge
    
    echo "Committing updates..."
    git add .
    git commit -m "Update submodules to latest versions"
  3. خطافات Git (Hooks)

    #!/bin/bash
    # .git/hooks/post-checkout
    
    # Mettre à jour les sous-modules après checkout
    git submodule update --init --recursive

بالنسبة إلى Subtrees

  1. أسماء Git المستعارة (Aliases)

    git config alias.sba 'subtree add --prefix'
    git config alias.sbu 'subtree pull --prefix'
    git config alias.sbp 'subtree push --prefix'
  2. توثيق المصادر

    # SUBTREES.md
    
    ## lib/shared-utils
    - Source: https://github.com/company/shared-utils.git
    - Branch: main
    - Last update: 2024-01-15
    
    ### Update command:
    ```bash
    git subtree pull --prefix=lib/shared-utils \
                     https://github.com/company/shared-utils.git \
                     main --squash

بدائل حديثة

Git Worktrees

# Créer des arbres de travail séparés
git worktree add ../feature-branch feature/new-feature
git worktree add ../hotfix-branch hotfix/critical-fix

# Lister les arbres de travail
git worktree list

# Supprimer un arbre de travail
git worktree remove ../feature-branch

مديرو الحزم

// package.json avec dépendances Git
{
  "dependencies": {
    "shared-utils": "git+https://github.com/company/shared-utils.git#v1.2.0"
  }
}

المستودعات الأحادية (Monorepos)

# Structure monorepo
project/
├── packages/
│   ├── shared-utils/
│   ├── web-app/
│   └── mobile-app/
├── tools/
└── package.json

استكشاف الأخطاء وإصلاحها

مشكلات شائعة مع Submodules

# Submodule vide après clone
git submodule update --init --recursive

# Conflit de sous-module
git reset --hard HEAD
git submodule update --init --recursive

# Sous-module détaché
cd submodule-path
git checkout main
cd ..
git add submodule-path
git commit -m "Fix detached submodule"

مشكلات مع Subtrees

# Erreur "Working tree has modifications"
git stash
git subtree pull --prefix=lib/utils origin main --squash
git stash pop

# Historique pollué
# Utiliser --squash pour éviter l'historique complet
git subtree add --prefix=lib/utils origin main --squash

موارد إضافية

الأدوات

  • Git Subrepo: بديل عن submodules
  • Lerna: إدارة المستودعات الأحادية في JavaScript
  • Nx: أدوات تطوير للمستودعات الأحادية
  • Bazel: نظام بناء للمستودعات الأحادية

التوثيق

الخلاصة

الوحدات الفرعية والأشجار الفرعية أدوات قوية لإدارة المشاريع المعقدة ذات الاعتماديات الخارجية. ويعتمد الاختيار بينهما على احتياجاتك المحددة من حيث البساطة والتحكم والتعاون.

في الدرس القادم، سنستكشف GitHub Actions لأتمتة مسارات عملك في التطوير والنشر.


وقت القراءة المقدّر: 25 دقيقة
المستوى: متقدم
المرحلة السابقة: مسارات عمل Git المتقدمة
المرحلة التالية: GitHub Actions وCI/CD