48 votes

Sous-modules imbriqués et dépendances Git

Disons que j'ai quatre projets nommés Core , A , B , Super . L'arbre de dépendance se présente comme suit :

Super ---> Core
       |-> A -> Core
       |-> B -> Core

Je veux que chaque projet soit autonome, c'est-à-dire que je veux pouvoir extraire et compiler chaque projet seul (chacun avec ses dépendances bien sûr).

J'ai pensé à faire correspondre chaque projet à un référentiel et à référencer les dépendances avec des submodules, mais je vois les problèmes suivants avec cette approche :

  1. Lors de la vérification Super avec toutes ses dépendances, je me retrouverais avec trois copies de Core .
  2. Puisque les sous-modules sont totalement indépendants, chacune de ces trois copies pourrait pointer vers différentes révisions de Core et ce serait le bordel.

Alors... J'ai raté quelque chose ? Ai-je mal compris les submodules git ou les ai-je mal utilisés ? Existe-t-il une autre solution à ce problème (autre que le recours aux dépendances binaires) ?

15voto

VonC Points 414372

Vous venez de découvrir l'absence de dépendances surchargées avec les submodules Git :

Si Super dépend de Core, sa dépendance à l'égard de Core devrait "écraser" celles que A et B ont avec Core.

La seule façon d'imiter cela serait de créer votre Super projet comme vous l'avez fait,
y pour supprimer le sous-module Core de A et B.
(ce qui signifie que Super dépend maintenant de A' et B', A' étant A sans Core, B' étant B sans Core)

7voto

Igor Zevaka Points 32586

Les dépôts git doivent être assez atomiques dans le sens où chaque dépôt est une entité autonome dans un but spécifique. Quel est le but du super projet autre que de combiner les projets A et B ? S'il n'y a rien d'unique (c'est-à-dire des fichiers qui ne sont ni dans A, ni dans B, ni dans Core), alors il est plutôt redondant.

EDIT : Parce que les submodules git sont particulièrement pénibles à un endroit où je travaillais, nous avons mis en place notre propre système de dépendance qui suit les dépôts dépendants via des fichiers texte. Nous l'avons configuré pour qu'il suive toujours la tête d'une branche, et non un commit particulier.

Nous avons pu configurer tous nos projets comme s'ils faisaient partie du Super projet comme ceci :

Super
|-A
|-B
|-Core

Les projets se référeront les uns aux autres en utilisant des chemins relatifs, par exemple. ../A/include.h . Extraire le repo A ne fonctionnera pas, vous devrez créer un autre "super" repo pour travailler uniquement sur A :

AWorking
|-A
|-Core

EDITAR Une autre raison de ce comportement dans git est qu'il ne peut pas suivre les choses qui sont au-dessus du répertoire Root repo (c'est-à-dire au-dessus du dossier contenant le dossier .git), ce qui serait certainement nécessaire si vous voulez que vos super-projets et sous-projets se réfèrent aux mêmes dépôts.

5voto

Charlie Points 139

Je pense que le problème ici est une inadéquation entre la conception de Git et le problème que vous cherchez à résoudre.

Git permet de garder la trace de Trees. Les relations de dépendance entre les projets peuvent former (et forment probablement) un graphe. Un arbre est un graphique mais un graphique n'est pas nécessairement un arbre. Puisque votre problème est de savoir comment représenter efficacement un graphique, un arbre n'est pas le meilleur outil pour ce travail.

Voici une approche qui pourrait fonctionner :

Un projet git possède un répertoire .gitmodules dans lequel il enregistre des "indices" indiquant les projets dont un commit peut dépendre, où ils peuvent être trouvés et à quel chemin dans le projet ils sont censés être insérés. ( http://osdir.com/ml/git/2009-04/msg00746.html )

Vous pourriez ajouter un script qui lit ces informations à partir d'un ensemble de projets, fait correspondre les indices trouvés dans le fichier .gitmodules de chaque projet aux emplacements sur le système de fichiers où ces projets ont réellement été placés, puis ajoute des liens symboliques à partir des chemins où git s'attend à extraire des submodules vers les emplacements réels sur le système de fichiers des projets respectifs.

Cette approche utilise des liens symboliques pour sortir du moule de l'arbre et construire un graphique. Si nous enregistrons les liens directement dans les dépôts git, nous aurions des chemins relatifs spécifiques à notre configuration locale enregistrés dans les projets individuels, et les projets ne seraient pas " totalement indépendants " comme vous le souhaitiez. D'où le script pour construire dynamiquement les liens symboliques.

Je pense que cette approche pourrait interférer avec git de manière indésirable, puisque nous avons pris des chemins où il s'attend à trouver une chose, et mis autre chose à la place. Peut-être que nous pourrions .gitignore les chemins des liens symboliques. Mais maintenant nous écrivons ces chemins deux fois et violons DRY. À ce stade, nous nous sommes également éloignés de l'idée d'utiliser des submodules. Nous pourrions enregistrer les dépendances ailleurs dans chaque projet, et laisser le fichier .gitmodules pour les choses que git attend. Nous allons donc créer notre propre fichier, disons, .dependencies, et chaque projet pourra y indiquer ses dépendances. Notre script regardera là et ira ensuite construire ses liens symboliques.

Hmm, je pense que je viens de décrire un système de gestion de paquets ad-hoc, avec son propre format de paquet léger :)

La suggestion de megamic me semble être une bonne utilisation des submodules git. Il ne s'agit ici que de garder la trace d'un ensemble plutôt que d'un graphique, et un ensemble s'intègre facilement dans un arbre. Un arbre d'un niveau de profondeur est essentiellement un nœud parent et un ensemble de nœuds enfants.

Comme vous l'avez souligné, cela ne résout pas complètement le problème énoncé dans votre question. Nous pouvons distinguer deux types distincts d'informations "ceci fonctionne avec cela" qui sont susceptibles de nous intéresser : 1. Une déclaration d'une version d'un projet (vraisemblablement par l'auteur du projet) disant "J'ai besoin de la version X du projet Y". 2. Une déclaration utilisée par votre propre configuration de construction disant "J'ai testé avec succès notre système entier en utilisant cet ensemble de versions du projet".

La réponse de megamic a résolu (2) mais pour (1) nous voulons toujours que les projets nous disent quelles sont leurs dépendances. Ensuite, nous pouvons utiliser les informations de (1) pour calculer ces ensembles de versions que nous finirons par enregistrer comme (2). C'est un problème suffisamment complexe pour justifier son propre outil, ce qui nous ramène aux systèmes de gestion de paquets :)

Pour autant que je sache, la plupart des bons outils de gestion de paquets sont conçus pour les utilisateurs d'un langage ou d'un système d'exploitation spécifique. Voir Bundler pour les paquets 'gem' dans le monde ruby et apt pour les paquets '.deb' dans le monde Debian.

Si quelqu'un connaît une bonne solution, neutre en termes de langue et de système d'exploitation, qui soit adaptée aux besoins des utilisateurs "polyglottes" ( http://blog.heroku.com/archives/2011/8/3/polyglot_platform/ ) des projets de programmation, je serais très intéressé ! Je devrais poster cela comme une question.

2voto

MarcH Points 1868

Je pense que vous pouvez gérer la cohérence de la manière suivante : définissez une branche ou une série de balises "de référence" avec le(s) même(s) nom(s) dans toutes vos bibliothèques "Core" (note : il n'y a qu'une seule bibliothèque "Core" dans votre exemple). Ensuite, demandez aux développeurs des sous-projets (A, B,...) de mettre régulièrement à jour vers la version de référence de "Core" dès qu'ils le peuvent.

Avant d'exécuter un build, vérifiez facilement que "Core(s)" est utilisé de manière cohérente dans A, B, C,... en exécutant ces trois commandes dans un "Super" checkout propre, récursif et de haut niveau :

# 1.  Switch to the reference version (= "Force" consistency where need be)
git submodule foreach --recursive 'git checkout [origin/]reference || true'

# 2a. Show which inconsistencies you just forced; terse output
git status -s; git submodule foreach --recursive git status -s 2>/dev/null

# 2b. Same but verbose output
git submodule; git submodule foreach --recursive git submodule

# 3. Switch back to versions individually defined by sub-projects 
git submodule update --recursive

La commande "Terse output" 2a ci-dessus met en évidence le(s) sous-projet(s) qui n'utilisent pas la version "de référence" de Core.

Vous pouvez facilement étendre cette approche pour afficher les différences, forcer les mises à niveau ou faire tout ce que vous voulez.

2voto

Darwin Points 547

Une petite tâche utilitaire transformant les submodules partagés en clones à l'aide de liens durs pourrait fonctionner.

Vous pouvez lire ma solution complète ici : https://stackoverflow.com/a/10265084/84283

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X