0%
Tutorial 9 of 10
90% complete

Git Submodules and Subtrees

Master the techniques for managing external dependencies and multi-module projects with Git.

Git Submodules and Subtrees

When your projects grow more complex and depend on other Git repositories, submodules and subtrees become essential. This tutorial will teach you how to manage multi-repository projects effectively with Git.

Introduction to Git Dependencies

Common Challenges

Projet PrincipalBibliothèque CommuneModule UIAPI ClientUtilitaires

Challenges:

  • Sharing code between projects
  • Managing dependency versions
  • Maintaining consistency
  • Avoiding duplication

Git Solutions

  1. Submodules: References to other repositories
  2. Subtrees: Integration of external code
  3. Alternatives: Package managers, monorepos

Git Submodules

Concept

A submodule is a Git repository embedded within another Git repository, maintaining its own history and references.

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

Adding a Submodule

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

# Vérifier l'ajout
cat .gitmodules

Contents of .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"

Cloning a Project with Submodules

# 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

Working with Submodules

Updating a Submodule

# 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"

Updating All Submodules

# 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

Modifying a Submodule

# 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"

Advanced Configuration

Specific Branches

# 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

Relative URLs

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

Ignoring Changes

# 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

Removing a Submodule

# 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

Concept

A subtree integrates another repository’s code directly into your repository, without maintaining an external reference.

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

Adding a Subtree

# 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/

Updating a Subtree

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

Contributing to a Subtree

# 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

Extracting a Subtree

# 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 vs. Subtrees

Comparison Table

AspectSubmodulesSubtrees
ComplexityHighMedium
HistorySeparateIntegrated
Repository sizeSmallLarger
CloningExtra stepsStandard
ModificationsComplexSimple
VersionsExplicitImplicit
CollaborationDifficultEasier

When to Use Which

Submodules:

  • Stable dependencies
  • Strict version control
  • Separate teams
  • Large external projects

Subtrees:

  • Active development
  • Frequent collaboration
  • Simplicity as a priority
  • Small libraries

Practical Use Cases

Project with a Shared Library

# 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 Theme with Submodules

# 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

Microservices with Shared Dependencies

# 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

Hands-On Exercises

Exercise 1: Basic 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"

Exercise 2: Subtrees with Contributions

# 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

Exercise 3: Version Management with 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"

Best Practices

For Submodules

  1. Clear documentation

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

    Mise à jour des sous-modules

    git submodule update --remote
  2. Automation scripts

    #!/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

For 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. Documenting sources

    # 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

Modern Alternatives

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 Managers

// 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

Troubleshooting

Common Submodule Issues

# 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"

Subtree Issues

# 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

Additional Resources

Tools

  • Git Subrepo: An alternative to submodules
  • Lerna: JavaScript monorepo management
  • Nx: Monorepo development tools
  • Bazel: Build system for monorepos

Documentation

Conclusion

Submodules and subtrees are powerful tools for managing complex projects with external dependencies. The choice between the two depends on your specific needs in terms of simplicity, control, and collaboration.

In the next tutorial, we will explore GitHub Actions to automate your development and deployment workflows.


Estimated reading time: 25 minutes
Level: Advanced
Previous step: Advanced Git Workflows
Next step: GitHub Actions and CI/CD