Différences entre les versions de « Monorepo Git »

De Wiki Seb35
Aller à la navigation Aller à la recherche
(é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 que j’impose, il faut :
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 »
    1. Ajouter les sous-modules avec `git submodule add|init|update`
    2. 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 :
    1. En cas d’accès distant, il faut rendre les repos enfants accessibles
    2. 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"
    3. 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"
    4. La compression est donc dégradée par rapport à un object store global
    5. 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