Quand plusieurs projets partagent du code commun, Git propose plusieurs approches. Chacune a ses compromis — il n’y a pas de solution universelle.
Git Submodules : projets imbriqués avec pointeurs
Un submodule est un dépôt Git imbriqué dans un autre. Le dépôt parent stocke un pointeur vers un commit spécifique du sous-dépôt.
# Ajouter un submodule
git submodule add https://github.com/sphinx-digital/shared-components.git components/shared
git commit -m "Add shared-components as submodule"
# Cloner un repo avec ses submodules
git clone --recurse-submodules https://github.com/sphinx-digital/main-app.git
# Ou après un clone classique
git submodule init
git submodule update
# Mettre à jour un submodule vers son dernier commit
cd components/shared
git pull origin main
cd ../..
git add components/shared
git commit -m "Update shared-components to latest"
Avantages : chaque projet a son historique indépendant, ses propres branches, sa propre CI. Versionning explicite (vous choisissez exactement quel commit du sous-projet utiliser).
Inconvénients : workflow complexe, facile d’oublier de git submodule update, impossible de modifier le sous-projet et le projet parent dans le même commit.
Git Subtree : fusion d’historiques
# Ajouter un sous-projet comme subtree
git subtree add --prefix=components/shared https://github.com/sphinx-digital/shared-components.git main --squash
# Mettre à jour depuis le repo source
git subtree pull --prefix=components/shared https://github.com/sphinx-digital/shared-components.git main --squash
# Contribuer des modifications en upstream
git subtree push --prefix=components/shared https://github.com/sphinx-digital/shared-components.git feature/my-fix
Avantages : plus simple que les submodules (un seul clone, pas de submodule update), l’historique est fusionné.
Inconvénients : l’historique du sous-projet se mélange avec le projet principal, les push upstream sont complexes.
Monorepo : tout dans le même dépôt
Un monorepo place tous les projets dans le même dépôt Git, avec des outils de build intelligents qui ne rebuilident que ce qui a changé.
monorepo/
├── apps/
│ ├── api/
│ ├── frontend/
│ └── workers/
├── packages/
│ ├── shared-types/
│ ├── ui-components/
│ └── utils/
├── tools/
│ └── scripts/
└── package.json # Workspace root (npm, pnpm, yarn workspaces)
// package.json racine (npm workspaces)
{
"name": "sphinx-digital",
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint"
}
}
# Turborepo : ne rebuilder que ce qui a changé
# turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"], # construire les dépendances d'abord
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"],
"inputs": ["src/**", "tests/**"]
}
}
}
Quelle approche choisir ?
| Contexte | Recommandation |
|---|---|
| Bibliothèque réutilisée par plusieurs équipes indépendantes | Submodules ou package manager |
| Projets liés mais déployés séparément, petite équipe | Monorepo |
| Open source avec contributions externes | Dépôts séparés |
| Applications microservices fortement couplées | Monorepo |
Notre formation Git couvre les workflows multi-projets avec des exercices pratiques.