Dans git, comment faire du versioning pour une douzaine de bibliothèques toutes travaillées en parallèle

11

Nous faisons des projets, mais nous réutilisons beaucoup de code entre les projets et avons beaucoup de bibliothèques qui contiennent notre code commun. Au fur et à mesure que nous implémentons de nouveaux projets, nous trouvons plus de façons de factoriser le code commun et de le mettre dans les bibliothèques. Les bibliothèques dépendent les unes des autres et les projets dépendent des bibliothèques. Chaque projet et toutes les bibliothèques utilisées dans ce projet doivent utiliser la même version de toutes les bibliothèques auxquelles ils se réfèrent. Si nous publions un logiciel, nous devrons corriger des bugs et peut-être ajouter de nouvelles fonctionnalités pendant de nombreuses années, parfois pendant des décennies. Nous avons environ une douzaine de bibliothèques, les changements se répartissent souvent sur plus de deux et plusieurs équipes travaillent sur plusieurs projets en parallèle, apportant des modifications simultanées à toutes ces bibliothèques.

Nous avons récemment basculé vers git et mis en place des référentiels pour chaque bibliothèque et chaque projet. Nous utilisons stash comme référentiel commun, faisons de nouvelles choses sur les branches de fonctionnalités, puis faisons des demandes d'extraction et ne les fusionnons qu'après examen.

Bon nombre des problèmes que nous devons traiter dans les projets nous obligent à apporter des modifications à plusieurs bibliothèques et au code spécifique du projet. Il s'agit souvent de modifications des interfaces de bibliothèque, dont certaines sont incompatibles. (Si vous pensez que cela semble louche: nous nous connectons avec du matériel et cachons du matériel spécifique derrière des interfaces génériques. Presque chaque fois que nous intégrons du matériel d'un autre fournisseur, nous rencontrons des cas que nos interfaces actuelles n'anticipaient pas, et nous devons donc les affiner.) Pour exemple, imaginez un projet en P1utilisant les bibliothèques L1, L2et L3. L1utilise également L2et L3, et L2utilise L3également. Le graphique des dépendances ressemble à ceci:

   <-------L1<--+
P1 <----+  ^    |
   <-+  |  |    |
     |  +--L2   |
     |     ^    |
     |     |    |
     +-----L3---+

Imaginez maintenant qu'une fonctionnalité de ce projet nécessite des modifications dans P1et L3qui modifient l'interface de L3. Ajoutez maintenant des projets P2et P3dans le mix, qui font également référence à ces bibliothèques. Nous ne pouvons pas nous permettre de les passer tous à la nouvelle interface, d'exécuter tous les tests et de déployer le nouveau logiciel. Alors, quelle est l'alternative?

  1. implémenter la nouvelle interface dans L3
  2. faire une demande d'extraction L3et attendre l'examen
  3. fusionner le changement
  4. créer une nouvelle version de L3
  5. commencer à travailler sur la fonctionnalité en la P1faisant référence à L3la nouvelle version de, puis implémenter la fonctionnalité sur P1la branche de fonctionnalité de
  6. faire une demande d'extraction, la faire examiner et fusionner

(Je viens de remarquer que j'ai oublié de passer L1et L2de la nouvelle version. Je ne sais même pas où coller cela dans, car il devrait être fait en parallèle avec P1...)

Il s'agit d'un processus fastidieux, sujet aux erreurs et très long pour implémenter cette fonctionnalité, il nécessite des examens indépendants (ce qui le rend beaucoup plus difficile à examiner), ne se modifie pas du tout et est susceptible de nous mettre hors service parce que nous être tellement embourbé dans le processus que nous ne faisons rien.

Mais comment utiliser la ramification et le balisage afin de créer un processus qui nous permet d'implémenter de nouvelles fonctionnalités dans de nouveaux projets sans trop de frais généraux?

sbi
la source
1
Changer votre outillage ne devrait pas trop affecter les processus que vous avez en place. Alors, comment gérez-vous ce problème avant de passer à git?
Bart van Ingen Schenau
Est-il possible d'ajouter simplement une nouvelle méthode à l'interface sans casser celle existante quand trop de bibliothèques en dépendent? Normalement, ce n'est pas la meilleure idée, mais au moins cela vous permettrait de "continuer" à implémenter la nouvelle fonctionnalité et vous pouvez déconseiller correctement l'ancienne méthode chaque fois qu'il y a un moment libre. Ou ces interfaces sont-elles trop dynamiques pour que des "interfaces parallèles" comme celle-ci fonctionnent?
Ixrec
1
@Ixrec: On m'a dit que "ce n'est pas la bonne façon de faire". Tout le monde utilise des référentiels individuels pour des projets individuels, il a donc été décidé de le faire également.
sbi
2
Je dirais que ce ne sont pas des projets distincts s'ils doivent souvent être modifiés en tandem. Les frontières entre les "projets" devraient toujours avoir une sorte de garantie de compatibilité ascendante à long terme imo.
Ixrec
1
@Ixrec: L'intégration de plus de matériel est similaire au portage de code vers plus de plates-formes: plus vous en avez fait, moins vous devez changer pour un autre matériel / plate-forme. Donc à long terme, le code se stabilisera. Cependant, en ce moment, nous devrons trouver un processus qui nous permettra de rester sur le marché assez longtemps pour y arriver.
sbi

Réponses:

5

Sorte de l'évidence ici, mais ça vaut peut-être la peine de le mentionner.

Habituellement, les git repos sont personnalisés par lib / projet car ils ont tendance à être indépendants. Vous mettez à jour votre projet et ne vous souciez pas du reste. Les autres projets qui en dépendent mettront simplement à jour leur bibliothèque quand ils le jugeront bon.

Cependant, votre cas semble fortement dépendant des composants corrélés, de sorte qu'une caractéristique affecte généralement bon nombre d'entre eux. Et le tout doit être conditionné sous forme de bundle. Étant donné que la mise en œuvre d'une fonctionnalité / modification / bogue nécessite souvent d'adapter plusieurs bibliothèques / projets différents à la fois, il est peut-être judicieux de les mettre tous dans le même référentiel.

Il existe de forts avantages / inconvénients à cela.

Avantages:

  • Traçabilité: la branche montre tout ce qui a changé dans chaque projet / bibliothèque lié à cette fonctionnalité / bogue.
  • Regroupement: choisissez simplement une balise et vous obtiendrez toutes les bonnes sources.

Désavantages:

  • Fusion: ... c'est parfois déjà difficile avec un seul projet. Avec différentes équipes travaillant sur des branches partagées, soyez prêt à vous préparer à l'impact.
  • Facteur dangereux "oups": si un employé gâche le référentiel en faisant une erreur, cela peut avoir un impact sur tous les projets et équipes.

A vous de savoir si le prix en vaut la peine.

ÉDITER:

Cela fonctionnerait comme ceci:

  • La fonction X doit être implémentée
  • Créer une branche feature_x
  • Tous les développeurs impliqués travaillent sur cette branche et y travaillent en parallèle, probablement dans des répertoires dédiés liés à leur projet / lib
  • Une fois terminé, revoyez-le, testez-le, emballez-le, peu importe
  • Fusionnez-le dans le maître ... et cela peut être la partie difficile depuis entre-temps feature_yet feature_zpeut-être aussi ajouté. Cela devient une fusion "inter-équipes". C'est pourquoi c'est un sérieux inconvénient.

juste pour mémoire: je pense que dans la plupart des cas, c'est une mauvaise idée et que cela doit être fait avec prudence car l'inconvénient de la fusion est généralement plus élevé que celui que vous obtenez grâce à la gestion des dépendances / au suivi des fonctionnalités.

dagnelies
la source
Merci, nous examinons actuellement la question. Ce que je ne comprends pas à la lumière de cela, c'est comment nous ferions des branchements avec git. Dans SVN, les branchements signifient copier un sous-arbre (dans le référentiel) vers un autre endroit (dans le référentiel). Avec cela, il est facile de créer des branches de n'importe quel sous-arbre dans votre référentiel, donc si vous avez de nombreux projets dedans, vous pouvez dériver chacun d'eux individuellement. Y a-t-il quelque chose comme ça dans git, ou serions-nous toujours en mesure de créer un repo entier?
sbi
@sbi: vous branchez tout le repo. Vous ne pouvez pas travailler avec des sous-arbres dans différentes branches, ce qui pourrait en quelque sorte vaincre le point dans votre cas. Cependant, Git ne "copiera" rien, il suivra simplement les changements dans la branche sur laquelle vous travaillez.
dagnelies
Cela nécessite donc que quelqu'un qui crée une branche de fonctionnalité pour une bibliothèque fusionne également toutes les autres lors de la fusion ou du rebasage. C'est un vrai inconvénient. (BTW, SVN ne fait également que des copies paresseuses.)
sbi
@sbi: voir edit
dagnelies
1
Eh bien, actuellement, la plupart d'entre nous ne sont pas à l'aise. :-/De plus, même ceux qui sont (et qui ont poussé à passer à git), ne savent pas comment adapter notre processus de développement à git. Soupir. Ça va être quelques mois difficiles, je le crains, jusqu'à ce que les choses commencent à s'améliorer. Merci de toute façon, la vôtre est la réponse la plus / seule utile à ce jour.
sbi
4

La solution que vous recherchez est un outil de gestion des dépendances en coordination avec les sous-modules git

Des outils tels que:

  • Maven
  • Fourmi
  • Compositeur

Vous pouvez utiliser ces outils pour définir les dépendances d'un projet.

Vous pouvez exiger qu'un sous-module soit au moins version > 2.xx ou indiquer une gamme de versions qui sont compatibles = 2.2. * Ou moins qu'une version particulière <2.2.3

Chaque fois que vous publiez une nouvelle version de l'un des packages, vous pouvez la marquer avec le numéro de version, de cette façon, vous pouvez insérer cette version spécifique du code dans tous les autres projets

Patrick
la source
Mais la gestion des dépendances n'est pas notre problème, c'est résolu. Nous apportons actuellement régulièrement des modifications à de nombreuses bibliothèques et devons minimiser les frais généraux liés à la création de nouvelles versions tout en maintenant des versions de projet stables. Votre réponse ne semble pas apporter de solution à cela.
sbi
@sbi Cela permettrait de gérer les frais généraux liés à la création de nouvelles versions et au maintien de versions de projet stables. Comme vous pouvez dicter que le projet x repose sur le projet y version 2.1.1, vous pouvez créer de nouvelles versions du projet y qui n'affecteront pas le projet x.
Patrick
Encore une fois, la déclaration des dépendances n'est pas notre problème. Nous pouvons déjà le faire. Le problème est de savoir comment gérer efficacement les changements dans plusieurs projets / bibliothèques. Votre réponse ne l'explique pas.
sbi
@sbi: quel est donc exactement votre problème? Vous apportez vos modifications, augmentez la version, mettez à jour les dépendances si nécessaire et le tour est joué. Ce que vous avez décrit dans votre message initial est typique de maven & co. des trucs. Chaque distribution est basée sur des bibliothèques versionnées clairement définies. Comment pourrait-il être plus clair?
dagnelies
@arnaud: Les délais d'exécution d'un tel processus pour des changements (actuellement assez courants) traversant trois couches ou plus nous tueraient. Je pensais que ma question le décrivait.
sbi
0

Sous-modules

Vous devriez essayer de créer des sous - modules , comme suggéré dans un commentaire.

Lorsque le projet P1fait référence aux trois sous L1- modules , L2et L3, il stocke en fait une référence à des validations particulières dans les trois référentiels: ce sont les versions de travail de chaque bibliothèque pour ce projet .

Ainsi, plusieurs projets peuvent fonctionner avec plusieurs sous-modules: P1peuvent faire référence à l'ancienne version de la bibliothèque L1tandis que le projet P2utilise la nouvelle version.

Que se passe-t-il lorsque vous livrez une nouvelle version de L3?

  • implémenter une nouvelle interface dans L3
  • commit, test , make pull request, review, merge, ... (vous ne pouvez pas l'éviter)
  • assurer les L2travaux avec L3, valider, ...
  • assurer des L1travaux avec de nouveaux L2, ...
  • assurer le P1fonctionnement avec les nouvelles versions de toutes les bibliothèques:
    • à P1l' intérieur de la copie de travail locale de L1, L2et L3, récupérez les changements qui vous intéressent.
    • valider les modifications, git add L1 L2 L3valider la nouvelle référence aux modules
    • pull request pour P1, tester, réviser, pull request, merge ...

Méthodologie

Il s'agit d'un processus fastidieux, sujet aux erreurs et très long pour implémenter cette fonctionnalité, il nécessite des examens indépendants (ce qui le rend beaucoup plus difficile à examiner), ne se modifie pas du tout et est susceptible de nous mettre hors service parce que nous être tellement embourbé dans le processus que nous ne faisons rien.

Oui, cela nécessite des examens indépendants, car vous changez:

  • la bibliothèque
  • bibliothèques qui en dépendent
  • projets qui dépendent de plusieurs bibliothèques

Seriez-vous mis hors service parce que vous livrez de la merde? (Peut-être pas, en fait). Si oui, vous devez effectuer des tests et revoir les modifications.

Avec les outils git appropriés (même gitk), vous pouvez facilement voir quelles versions des bibliothèques chaque projet utilise et vous pouvez les mettre à jour indépendamment en fonction de vos besoins. Les sous-modules sont parfaits pour votre situation et ne ralentiront pas votre processus.

Vous pouvez peut-être trouver un moyen d' automatiser une partie de ce processus, mais la plupart des étapes ci-dessus nécessitent des cerveaux humains. Le moyen le plus efficace de gagner du temps serait de vous assurer que vos bibliothèques et vos projets sont faciles à faire évoluer. Si votre base de code peut gérer les nouvelles exigences avec élégance, les révisions de code seront plus simples et prendront peu de temps.

(Modifier) une autre chose qui pourrait vous aider est de regrouper les revues de code associées. Vous validez toutes les modifications et attendez de les propager vers toutes les bibliothèques et tous les projets qui les utilisent avant de sumbitting les demandes de tirage (ou avant de les prendre en charge). Vous finissez par faire un examen plus approfondi de toute la chaîne de dépendance. Cela peut peut-être vous aider à gagner du temps si chaque changement local est faible.

coredump
la source
Vous décrivez comment résoudre le problème de dépendance (que, comme je l'ai déjà dit, nous avons réglé) et niez le problème même que nous rencontrons. Pourquoi vous dérangez-vous? (FWIW, nous écrivons un logiciel qui pilote les centrales électriques. Un code propre, sécurisé et soigneusement révisé est une caractéristique principale.)
sbi
@sbi Que sont les sous-modules si ce n'est un cas particulier de branchement et de marquage? Vous pensez que les sous-modules concernent la gestion des dépendances, car ils assurent également le suivi des dépendances. Mais bien sûr, réinventez les sous-modules avec des balises si vous le souhaitez, cela ne me dérange pas. Je ne comprends pas votre problème: si le code révisé est une fonctionnalité principale, vous devez prévoir du temps pour les révisions. Vous n'êtes pas enlisé, vous allez le plus vite possible avec les contraintes qui vous sont imposées.
coredump
Les avis sont très importants pour nous. C'est l'une des raisons pour lesquelles nous sommes préoccupés par le fait qu'un problème (et ses avis) soit divisé en plusieurs avis pour plusieurs modifications dans plusieurs référentiels. De plus, nous ne voulons pas nous enliser dans l'administration de nous à mort sur un problème, c'est que nous préférons passer le temps à écrire et à réviser le code. Concernant les sous-modules: jusqu'à présent, la seule chose que j'ai entendue à leur sujet est "ne vous embêtez pas, ce n'est pas comme ça". Eh bien, étant donné que nos exigences semblent si uniques, nous devrions peut-être revoir cette décision ...
sbi
0

Donc, ce que je comprends, c'est que pour P1, vous voulez changer l'interface L3 mais vous voulez que les autres P2 et P3 qui dépendent de l'interface L3 changent tout de suite. Il s'agit d'un cas typique de compatibilité descendante. Il y a un bel article sur cette préservation de la compatibilité descendante

Il existe plusieurs façons de résoudre ce problème:

  • Vous devez créer de nouvelles interfaces à chaque fois, ce qui peut étendre les anciennes interfaces.

OU

  • Si vous souhaitez retirer l'ancienne interface après un certain temps, vous pouvez avoir plusieurs versions d'interfaces et une fois tous les projets dépendants déplacés, vous supprimez les anciennes interfaces.
Uday Shankar
la source
1
Non, la compatibilité descendante est assurée par les branches de publication et ce n'est pas notre problème. Le problème est que nous nous asseyons sur une base de code en évolution rapide, qui veut maintenant se séparer en bibliothèques, malgré le fait que les interfaces sont encore dans la phase où elles changent souvent. Je sais comment gérer de telles bêtes dans SVN, mais je ne sais pas comment faire ça dans git sans être noyé dans l'administration.
sbi
0

Si je comprends bien votre problème:

  • vous avez 4 modules interdépendants, P1 et L1 à L3
  • vous devez modifier P1, ce qui affectera finalement L1 à L3
  • cela compte comme un échec du processus si vous devez changer les 4 ensemble
  • cela compte comme un échec du processus si vous devez les changer tous 1 par 1.
  • cela compte comme un échec de processus si vous devez identifier à l'avance les morceaux dans lesquels des modifications doivent être apportées.

Donc, l'objectif est que vous puissiez faire P1 et L1 en une fois, puis un mois plus tard, faire L2 et L3 dans un autre.

Dans le monde Java, c'est trivial, et peut-être la façon de travailler par défaut:

  • tout se passe dans un seul référentiel sans utilisation appropriée de branchement
  • les modules sont compilés + liés ensemble par maven sur la base des numéros de version, et non du fait que tous se trouvent dans la même arborescence de répertoires.

Vous pouvez donc avoir le code sur votre disque local pour L3 qui ne se compilerait pas s'il était en cours de compilation avec la copie du P1 dans l'autre répertoire de votre disque; heureusement, ce n'est pas le cas. Java peut le faire directement parce que les contes de compilation / liaison sont placés contre les fichiers jar compilés, pas le code source.

Je ne connais pas de solution préexistante largement utilisée à ce problème pour le monde C / C ++, et j'imagine que vous ne voulez guère changer de langue. Mais quelque chose pourrait facilement être piraté avec des fichiers de création qui ont fait la même chose:

  • bibliothèques installées + en-têtes vers des répertoires connus avec des numéros de version intégrés
  • chemins de compilation modifiés par module dans le répertoire pour les numéros de version appropriés

Vous pourriez même utiliser le support C / C ++ dans maven , bien que la plupart des développeurs C vous regarderaient étrangement si vous le faisiez ...

Soru
la source
"cela compte comme un échec de processus si vous devez changer les 4 ensemble" . En fait, ce ne serait pas le cas. En fait, c'est exactement ce que nous avons fait en utilisant SVN.
sbi
Dans ce cas, je suppose qu'il n'y a aucun problème à simplement mettre tous les projets dans le référentiel.
soru
Nous évaluons actuellement la possibilité de placer les bibliothèques dans seulement deux référentiels. C'est toujours plus d'un, mais beaucoup moins que "un pour chaque projet", et les bibliothèques peuvent très bien être divisées en deux groupes. Merci pour votre contribution!
sbi
PS: "J'imagine que vous voulez à peine changer de langue." Ce sont des trucs intégrés. :)
sbi
-1

Il existe une solution simple: couper les branches des versions sur l'ensemble du référentiel, fusionner tous les correctifs à toutes les versions activement expédiées (c'est facile en clair, cela devrait être possible dans git).

Toutes les alternatives créeront un horrible gâchis au fil du temps et avec la croissance du projet.

zzz777
la source
Pourriez-vous s'il vous plaît développer? Je ne sais pas trop ce que vous proposez.
OSMŒ
En clair, vous définissez un point de branche comme branche de base et horodatage. Vous avez la hiérarchie suivante: branche de base -> branche de développement-version -> branche de développement privé. Tout le développement est effectué sur des branches privées, puis fusionné dans la hiérarchie. Les branches de publication client sont rompues. Je ne connais pas très bien git, mais il semble que la chose la plus proche soit de clarifier le cas parmi les systèmes de contrôle de source libre.
zzz777
Une lecture attentive de ma question aurait dû vous montrer que nous avons des problèmes avec les frais généraux impliqués dans la propagation des modifications entre les référentiels. Cela n'a rien à voir avec votre réponse.
sbi
@sbi Je suis désolé d'avoir mal compris votre question. Et je crains que vous fassiez face à un horrible gâchis tôt ou tard.
zzz777