Différences entre les versions de « Monorepo Git »
(étude) |
(++pour les git submodules) |
||
Ligne 3 : | Ligne 3 : | ||
* un multi-repo où certains dossiers serait des repo Git ayant leur historique autonome. | * un multi-repo où certains dossiers serait des repo Git ayant leur historique autonome. | ||
Dans les contraintes | Dans les contraintes imposées, il faut : | ||
* que les solutions potentielles soient possibles en l’état actuel de Git ou, a maximum, un programme Bourne shell d’une taille relativement faible (afin d’être compatible sur un maximum de plate-formes et qu’il reste lisible et compréhensible), | * que les solutions potentielles soient possibles en l’état actuel de Git ou, a maximum, un programme Bourne shell d’une taille relativement faible (afin d’être compatible sur un maximum de plate-formes et qu’il reste lisible et compréhensible), | ||
* il peut y avoir une forme d’équivalence entre deux états "monorepo" et "multirepo" évenutuellement disticts mais switchable avec une commande, | * il peut y avoir une forme d’équivalence entre deux états "monorepo" et "multirepo" évenutuellement disticts mais switchable avec une commande, | ||
* les commandes standard Git devraient fonctionner, au moins dans un certain état "monorepo"/"multirepo" s’ils sont distincts, | * les commandes standard Git devraient fonctionner, au moins dans un certain état "monorepo"/"multirepo" s’ils sont distincts, | ||
* la performance doit rester acceptable <small>(je sais, c’est vague et ça dépend des situations)</small>. | * la performance doit rester acceptable <small>(je sais, c’est vague et ça dépend des situations)</small>. | ||
== Cas d’usage == | |||
Les cas d’usage que je prévoie sont : | Les cas d’usage que je prévoie sont : | ||
* pour Archéo Lex : pouvoir aggréger l’ensemble des lois françaises dans un seul monorepo mais en ayant la possibilité de télécharger seulement des sous-ensembles, par exemples les décrets, | * pour Archéo Lex : pouvoir aggréger l’ensemble des lois françaises dans un seul monorepo (au niveau stockage) mais en ayant la possibilité de télécharger seulement des sous-ensembles, par exemples les décrets, | ||
* pour MediaWiki : pouvoir avoir le logiciel de base (un repo) avec les extensions (chacun dans un repo) où le déploiement est un monorepo comportant la version de base + les extensions déployées dans une certaine version, éventuellement patchée. | * pour MediaWiki : pouvoir avoir le logiciel de base (un repo) avec les extensions (chacun dans un repo) où le déploiement est un monorepo comportant la version de base + les extensions déployées dans une certaine version, éventuellement patchée. | ||
Ligne 16 : | Ligne 18 : | ||
Sous-modules Git (voir gitsubmodules(7)) | Sous-modules Git (voir gitsubmodules(7)) | ||
''alternates'' (voir gitrepository-layout(5)) | |||
Espaces de noms Git (voir gitnamespaces(7)) | Espaces de noms Git (voir gitnamespaces(7)) | ||
Ligne 24 : | Ligne 28 : | ||
Les options de configuration, par exemple "diff.submodule" | Les options de configuration, par exemple "diff.submodule" | ||
== Briques de solution == | |||
== Plusieurs repos indépendants == | |||
Ceci n’est pas une solution au problème exposé car il n’y a jamais de monorepo, au sens que la version globale et unifiée d’un repo et de ses sous-repos n’est jamais présente. C’est toutefois une partie d’une éventuelle solution, intégrée à une autre brique de solution. | |||
Méthode : | |||
* Faire un repo Git « parent » | |||
* Ajouter les repos Git « enfants » avec des `git clone` dans des dossiers du repo parent | |||
* Faire des `git commit` dans chacun des enfants et du parent | |||
… | |||
=== Sous-modules classiques === | |||
Méthode : | |||
* Faire un repo Git « parent » | |||
* Ajouter les repos Git « enfants » | |||
*# Ajouter les sous-modules avec `git submodule add|init|update` | |||
*# Ajouter un repo Git normal dans un dossier, puis, dans le repo parent, faire un git-add(1) (il y a un avertissement, c’est normal), puis git-submodule(1) (absorbgitdirs) sur ce dossier pour transférer le dossier <enfant>/.git dans <parent>/.git/modules/<enfant> | |||
* `git commit` dans le repo parent pour enregistrer la version | |||
* `git submodule deinit` pour retirer le sous-module | |||
Avantages : | |||
* Très standard depuis longtemps, au moins pour l’organisation et les commandes de base | |||
* Gestion de la récursion (enfants d’enfants) | |||
* En local, tout l’historique est dans le dossier <parent>/.git | |||
Inconvénients : | |||
* Les repo enfants ne font pas partie du repo parent : | |||
*# En cas d’accès distant, il faut rendre les repos enfants accessibles | |||
*# En cas de clone du repo parent, seul le repo parent est téléchargé par défaut | |||
*#: Cela peut être changé au cas par cas avec `git clone --recurse-submodules` ou configuré de façon permanente avec "submodule.recurse", "fetch.recurseSubmodules" et "submodule.<name>.fetchRecurseSubmodules" | |||
*# Lors d’un push, il faut pousser aussi les repos enfants | |||
*#: Cela peut être changé au cas par cas avec `git push --recurse-submodules=check|on-demand|no` configuré de façon permanente avec "submodule.recurse" et "push.recurseSubmodules" | |||
*# La compression est donc dégradée par rapport à un object store global | |||
*# La perfomance lors d’un clone est dégradée par rapport à un clone global | |||
* En cas de changement de branche (pour un repo ayant un work tree) : | |||
** il y a des avertissements si les branches source et destination ont des repos enfants ayant des statuts "enregistré dans un commit"/"non-enregistré dans un commit" différents (dans le 2e cas, ça peut être un dossier "non suivi" ou "ignoré") | |||
** il faut synchroniser a posteriori les repos enfants : si on oublie, on est alors dans un état intermédiaire entre deux commits | |||
* L’algorithme de fusion ne fusionne pas à l’intérieur des sous-modules | |||
*: Il faut fusionner chacun des sous-modules puis, lors de la fusion du repo parent, résoudre le conflit de fusion en préférant les commits fusionnés des repos enfants | |||
* Il n’y a pas d’ordre canonique dans le fichier .gitmodules | |||
*: Les commandes `git submodule add` ajoutent à la fin du fichier les nouveaux sous-modules | |||
*: En cas de fusion, même si deux fichiers .gitmodules sont sémantiquement identiques (mêmes sous-modules, mêmes paramètres pour chaque sous-module), il peut y avoir un conflit de fusion sur ce fichier | |||
* Lors du checkout, la référence HEAD des sous-modules pointe vers un commit et non vers la branche indiquée dans le fichier .gitmodules | |||
*: Il est moins aisé de mettre à jour en masse les sous-modules (par exemple dans MediaWiki, dans le principal dépôt d’extensions (Gerrit), les extensions MediaWiki sont dans des branches correspondant à la version majeure de MediaWiki (par exemple REL1_35 pour la branche majeure 1.35) : par défaut il n’est pas possible de faire un `git submodule foreach git fetch` ou `git iterate -- fetch` (programme complémentaire git-iterate) | |||
* Le support des sous-modules évolue (en s’améliorant amha) mais certaines commandes ou options de configuration n’étaient pas disponibles à l’origine | |||
*: Cela amoindrit la facilité d’utilisation pour les anciennes versions de Git |
Version du 19 juillet 2021 à 13:59
Cette page est une étude pour trouver la meilleure solution de faire un monorepo Git, ou plus exactement, idéalement, un repo Git qui serait à la fois :
- un monorepo (qu’on puisse télécharger en une seule fois)
- un multi-repo où certains dossiers serait des repo Git ayant leur historique autonome.
Dans les contraintes imposées, il faut :
- que les solutions potentielles soient possibles en l’état actuel de Git ou, a maximum, un programme Bourne shell d’une taille relativement faible (afin d’être compatible sur un maximum de plate-formes et qu’il reste lisible et compréhensible),
- il peut y avoir une forme d’équivalence entre deux états "monorepo" et "multirepo" évenutuellement disticts mais switchable avec une commande,
- les commandes standard Git devraient fonctionner, au moins dans un certain état "monorepo"/"multirepo" s’ils sont distincts,
- la performance doit rester acceptable (je sais, c’est vague et ça dépend des situations).
Cas d’usage
Les cas d’usage que je prévoie sont :
- pour Archéo Lex : pouvoir aggréger l’ensemble des lois françaises dans un seul monorepo (au niveau stockage) mais en ayant la possibilité de télécharger seulement des sous-ensembles, par exemples les décrets,
- pour MediaWiki : pouvoir avoir le logiciel de base (un repo) avec les extensions (chacun dans un repo) où le déploiement est un monorepo comportant la version de base + les extensions déployées dans une certaine version, éventuellement patchée.
Briques de base
Sous-modules Git (voir gitsubmodules(7))
alternates (voir gitrepository-layout(5))
Espaces de noms Git (voir gitnamespaces(7))
Dépôt distant de type ext (voir git-remote-ext(1))
Ilôts delta (delta islands) (voir git-pack-objects(1))
Les options de configuration, par exemple "diff.submodule"
Briques de solution
Plusieurs repos indépendants
Ceci n’est pas une solution au problème exposé car il n’y a jamais de monorepo, au sens que la version globale et unifiée d’un repo et de ses sous-repos n’est jamais présente. C’est toutefois une partie d’une éventuelle solution, intégrée à une autre brique de solution.
Méthode :
- Faire un repo Git « parent »
- Ajouter les repos Git « enfants » avec des `git clone` dans des dossiers du repo parent
- Faire des `git commit` dans chacun des enfants et du parent
…
Sous-modules classiques
Méthode :
- Faire un repo Git « parent »
- Ajouter les repos Git « enfants »
- Ajouter les sous-modules avec `git submodule add|init|update`
- Ajouter un repo Git normal dans un dossier, puis, dans le repo parent, faire un git-add(1) (il y a un avertissement, c’est normal), puis git-submodule(1) (absorbgitdirs) sur ce dossier pour transférer le dossier <enfant>/.git dans <parent>/.git/modules/<enfant>
- `git commit` dans le repo parent pour enregistrer la version
- `git submodule deinit` pour retirer le sous-module
Avantages :
- Très standard depuis longtemps, au moins pour l’organisation et les commandes de base
- Gestion de la récursion (enfants d’enfants)
- En local, tout l’historique est dans le dossier <parent>/.git
Inconvénients :
- Les repo enfants ne font pas partie du repo parent :
- En cas d’accès distant, il faut rendre les repos enfants accessibles
- En cas de clone du repo parent, seul le repo parent est téléchargé par défaut
- Cela peut être changé au cas par cas avec `git clone --recurse-submodules` ou configuré de façon permanente avec "submodule.recurse", "fetch.recurseSubmodules" et "submodule.<name>.fetchRecurseSubmodules"
- Lors d’un push, il faut pousser aussi les repos enfants
- Cela peut être changé au cas par cas avec `git push --recurse-submodules=check|on-demand|no` configuré de façon permanente avec "submodule.recurse" et "push.recurseSubmodules"
- La compression est donc dégradée par rapport à un object store global
- La perfomance lors d’un clone est dégradée par rapport à un clone global
- En cas de changement de branche (pour un repo ayant un work tree) :
- il y a des avertissements si les branches source et destination ont des repos enfants ayant des statuts "enregistré dans un commit"/"non-enregistré dans un commit" différents (dans le 2e cas, ça peut être un dossier "non suivi" ou "ignoré")
- il faut synchroniser a posteriori les repos enfants : si on oublie, on est alors dans un état intermédiaire entre deux commits
- L’algorithme de fusion ne fusionne pas à l’intérieur des sous-modules
- Il faut fusionner chacun des sous-modules puis, lors de la fusion du repo parent, résoudre le conflit de fusion en préférant les commits fusionnés des repos enfants
- Il n’y a pas d’ordre canonique dans le fichier .gitmodules
- Les commandes `git submodule add` ajoutent à la fin du fichier les nouveaux sous-modules
- En cas de fusion, même si deux fichiers .gitmodules sont sémantiquement identiques (mêmes sous-modules, mêmes paramètres pour chaque sous-module), il peut y avoir un conflit de fusion sur ce fichier
- Lors du checkout, la référence HEAD des sous-modules pointe vers un commit et non vers la branche indiquée dans le fichier .gitmodules
- Il est moins aisé de mettre à jour en masse les sous-modules (par exemple dans MediaWiki, dans le principal dépôt d’extensions (Gerrit), les extensions MediaWiki sont dans des branches correspondant à la version majeure de MediaWiki (par exemple REL1_35 pour la branche majeure 1.35) : par défaut il n’est pas possible de faire un `git submodule foreach git fetch` ou `git iterate -- fetch` (programme complémentaire git-iterate)
- Le support des sous-modules évolue (en s’améliorant amha) mais certaines commandes ou options de configuration n’étaient pas disponibles à l’origine
- Cela amoindrit la facilité d’utilisation pour les anciennes versions de Git