Choisir entre un ou plusieurs projets dans un référentiel git?

223

Dans un gitenvironnement où nous avons modularisé la plupart des projets, nous sommes confrontés à un projet par référentiel ou à plusieurs projets par problème de conception de référentiel . Considérons un projet modularisé:

myProject/
   +-- gui
   +-- core
   +-- api
   +-- implA
   +-- implB

Aujourd'hui, nous avons un projet par référentiel . Cela donne la liberté de

  • release composants individuels
  • tag composants individuels

Mais les branchcomposants sont également encombrants , car la ramification apinécessite souvent des branches équivalentes dans core, voire d'autres composants.

Étant donné que nous voulons releasedes composants individuels, pouvons-nous obtenir la même flexibilité en utilisant plusieurs projets par conception de référentiel .

Quelles sont les expériences existantes et comment / pourquoi avez-vous abordé ces problèmes?

Johan Sjöberg
la source
1
J'ai un problème très similaire en ce moment. Je dois publier différentes versions d'un projet afin qu'ils soient placés dans des référentiels différents. C'est un cauchemar à gérer cependant. Ce serait bien s'il y avait un moyen de créer des branches seulement des sous-répertoires.
Andrew T Finnell
1
Chaque module doit avoir des numéros de version distincts. Et nous utilisons git-describe.
linquize
Je suis surpris de voir que Bit ( bitsrc.io ) et Lerna ( github.com/lerna/lerna ) ne sont pas mentionnés! Vous pouvez en apprendre plus ici: hackernoon.com/…
Yoni

Réponses:

199

one project per repositoryLa façon dont vous l'avez décrite ci-dessus présente trois inconvénients majeurs . Celles-ci sont moins vraies s’il s’agit de projets véritablement distincts, mais les changements qui en résonnent nécessitent souvent d’être modifiés, ce qui peut réellement exagérer ces problèmes:

  1. Il est plus difficile de découvrir quand les bugs ont été introduits. Des outils tels que ceux-ci git bisectdeviennent beaucoup plus difficiles à utiliser lorsque vous répartissez votre référentiel en sous-référentiels. C'est possible, ce n'est tout simplement pas aussi facile, ce qui signifie que la recherche de bogues en temps de crise est d'autant plus difficile.
  2. Il est beaucoup plus difficile de suivre l’ensemble de l’historique d’une fonctionnalité. Les commandes qui traversent l’historique, telles git logque, tout simplement, ne produisent pas l’historique de manière aussi significative avec des structures de référentiel fracturées. Vous pouvez obtenir des résultats utiles avec des sous - modules ou des sous-arbres, ou via d'autres méthodes scriptables, mais ce n'est pas la même chose que de taper tig --grep=<caseID>ou d' git log --grep=<caseID>analyser tous les commits qui vous intéressent. Votre histoire devient plus difficile à comprendre, ce qui la rend moins utile lorsque vous en avez vraiment besoin.
  3. Les nouveaux développeurs passent plus de temps à apprendre la structure du contrôle de version avant de pouvoir commencer à coder. Chaque nouveau travail nécessite des procédures de ramassage, mais la fragmentation d'un référentiel de projet signifie qu'ils doivent récupérer la structure de VC en plus de l'architecture du code. D'après mon expérience, cela est particulièrement difficile pour les développeurs débutants venant de magasins plus traditionnels et centralisés utilisant un seul référentiel.

En fin de compte, c'est un calcul du coût d'opportunité. Chez un ancien employeur, notre application principale était divisée en 35 sous-répertoires différents. De plus, nous avons utilisé un ensemble compliqué de scripts pour effectuer une recherche dans l'historique, nous assurer que l'état (c'est-à-dire la production par rapport aux branches de développement) était identique, et les déployer individuellement ou en masse.

C'était trop; trop pour nous au moins. Les frais généraux de gestion ont rendu nos fonctionnalités moins souples, rendu les déploiements beaucoup plus difficiles, obligé à enseigner trop longtemps aux nouveaux développeurs et, à la fin, nous pouvions à peine nous souvenir de la raison pour laquelle nous avions divisé le référentiel. Un beau jour de printemps, j'ai dépensé 10 dollars pour un après-midi de calcul en cluster dans EC2. J'ai réparé les pensions avec une douzaine d' git filter-branchappels. Nous n'avons jamais regardé en arrière.

Christopher
la source
7
En dehors du sujet, il n’ya guère de choses plus agréables en tant que gestionnaire de référentiel que de gagner du temps sur un système capable de faire en deux heures ce que votre ordinateur portable ne pourrait pas faire en 20, pour moins que le prix du déjeuner. Parfois, j'aime beaucoup Internet.
Christopher
2
Comment publieriez-vous ces projets individuels en tant que versions séparées? Ou avez-vous jamais besoin de faire ça? C'est le problème que j'ai. Avec si vous avez besoin de créer une V1 du projet A et une V2 du projet B.
Andrew T Finnell
5
Pour vous déplacer entre le "projet par référent" et les "relocations multiples", considérez git-subtree (bonne explication à stackoverflow.com/a/17864475/15585 )
déterperi
1
J'ai écrit un script pour automatiser cela pour les cas d'utilisation courants: github.com/Oakleon/git-join-repos
chrishiestand
Qu'est-ce qu'une "structure VC?"
Robert Harvey
60

Christopher a très bien énuméré les inconvénients d'un modèle à un projet par dépôt. J'aimerais discuter de certaines des raisons pour lesquelles vous pourriez envisager une approche à référentiel multiple. Dans de nombreux environnements dans lesquels j'ai travaillé, une approche multi-référentiels constituait une solution raisonnable, mais il n’était pas toujours facile de décider du nombre de référentiels à disposer et du lieu où opérer les réductions.

Dans mon poste actuel, j'ai migré un énorme dépôt CVS avec un dépôt unique avec plus de dix ans d’histoire dans un certain nombre de dépôts git. Depuis cette décision initiale, le nombre de référentiels a augmenté (grâce aux actions d'autres équipes), au point que je soupçonne que nous en avons plus que ce qui serait optimal. Certains nouveaux employés ont suggéré de fusionner les référentiels, mais j’ai plaidé contre. Le projet Wayland a une expérience similaire. Dans une conversation que j'ai récemment vue, ils avaient, à un moment donné, plus de 200 dépôts git, pour lesquels le responsable s'est excusé. En regardant leur site Web , je vois maintenant qu'ils sont à 5 heures, ce qui semble raisonnable. Il est important de noter que la jonction et la division de référentiels est une tâche gérable, et qu'il est acceptable d'expérimenter (dans des limites raisonnables).

Alors, quand voulez-vous plusieurs référentiels?

  1. Un seul référentiel serait trop volumineux pour être efficace.
  2. Vos référentiels sont faiblement couplés ou découplés.
  3. Un développeur n'a généralement besoin que d'un ou d'un petit sous-ensemble de ses référentiels à développer.
  4. Vous souhaitez généralement développer les référentiels de manière indépendante et ne les synchroniser que de temps en temps.
  5. Vous voulez encourager plus de modularité.
  6. Différentes équipes travaillent sur différents référentiels.

Les points 2 et 3 ne sont significatifs que si le point 1 tient. En scindant nos référentiels, j'ai considérablement réduit les délais de nos collègues hors site, réduit la consommation de disques et amélioré le trafic réseau.

4 et 5 sont plus subtiles. Lorsque vous séparez les dépôts d'un client et d'un serveur, cela rend plus onéreuse la coordination des modifications entre le code client et le code serveur. Cela peut être positif car cela encourage une interface découplée entre les deux.

Même avec les inconvénients des projets multi-référentiels, de nombreux travaux respectables sont effectués de cette manière - on pense à Wayland et à boost. Je ne crois pas qu'un consensus sur les meilleures pratiques ait encore évolué et qu'il faut faire preuve de jugement. Des outils permettant de travailler avec plusieurs référentiels (git-sous-arbre, git-sous-module et autres) sont encore en cours de développement et d'expérimentation. Mon conseil est d'expérimenter et d'être pragmatique.

Spacemoose
la source
7
Cette réponse serait encore plus utile avec une référence à l’appui de l’affirmation: "la jonction et la scission de référentiels est une tâche gérable."
Wildcard
3
Les mises en pension multiples peuvent également nuire à la modularité, car elles compliquent la modification du code partagé. Les dépendances entre référentiels rendent l'intégration plus difficile, peuvent rompre le code plus facilement (même si vous disposez de bons outils pour le vérifier) ​​et la menace de rupture du code sans référentiel décourage les interfaces de refactoring, qui est l'un de vos outils les plus puissants pour rendre les choses plus efficaces. plus modulaire.
Curt J. Sampson
Tout ce qui concerne MicroServices et la conception DDD est valable ici. Vous devriez minimiser le code partagé.
Arwin le
49

Comme nous utilisons GitHub, nous avons en réalité plusieurs projets dans un même dépôt, mais nous nous assurons que ces projets / modules sont correctement modularisés (nous utilisons les conventions -api et -core + Maven +, ainsi que les vérifications statiques et d'exécution et pouvons même aller à OSGi un jour pour démarrer). .

Qu'est-ce que cela économise? Nous n’avons pas à émettre plusieurs demandes d’extraction si nous modifions quelque chose de petit dans plusieurs projets. Les problèmes et le wiki sont centralisés, etc.

Nous traitons toujours chaque module / projet comme un projet indépendant approprié et les construisons et les intégrons séparément dans notre serveur CI, etc.

Martijn Verburg
la source
1
Très intéressant. Je soupçonne que ceci est un modèle commun sur github. Si vous êtes confronté à des versions de composants individuelles, utilisez-vous quelque chose comme submodulesou libérez-vous / étiquetez-vous le référentiel entier?
Johan Sjöberg
sous-modules si nous devons mais pour l'instant nous version du parent vers le bas.
Martijn Verburg
Chez mon employeur actuel, nous utilisons une stratégie similaire et regroupons les métadonnées relatives au dernier engagement dans un projet dans les différents fichiers de manifeste d'artefacts (c'est-à-dire les résultats de git log -1 -- <project_dir>). C'est vraiment génial. Cette réponse mérite plus de votes positifs.
Christopher
22

Pour moi, la principale différence en utilisant un ou plusieurs référentiels réside dans les réponses aux questions suivantes:

  • Les multiples pièces développées par la même équipe, ont-elles le même cycle de publication, le même client? Ensuite, il y a moins de raisons de scinder le référentiel.
  • Les multiples parties sont-elles fortement dépendantes les unes des autres? Par conséquent, la division du modèle, du contrôleur et de l’UI (même s’il s’agit de pièces différentes) n’est pas très judicieuse, en raison de la forte dépendance les uns des autres. Mais si 2 parties ont seulement une petite dépendance, qui est implémentée par une interface stable qui ne change que quelques années, il serait donc sage de diviser les 2 parties en 2 référentiels.

À titre d’exemple, j’ai une petite application (client seulement) qui vérifie la "qualité" d’un référentiel Subversion. Il y a l'implémentation principale, qui pourrait être lancée à partir de la ligne de commande et fonctionne bien avec Java 6. Mais j'ai commencé à implémenter une interface utilisateur, qui utilise JavaFX dans le cadre de Java 8. J'ai donc divisé le 2, et créé un second référentiel (avec un second processus de compilation), avec un planning différent, ...

J'aime les réponses ci-dessus (votées), mais je pense qu'elles ne sont pas toute l'histoire vraie. Je voulais donc ajouter les arguments en faveur du fractionnement des référentiels. Donc, la vraie réponse (quand diviser) peut être quelque part au milieu ...

mliebelt
la source
0

D'après votre exemple, les référentiels devraient être configurés en fonction de leur interdépendance. Tous les raisonnements sur la conception de MicroServices et de la conception par domaine s'appliquent ici: dans certains cas, le code en double est acceptable, travaillez avec des interfaces, ne coupez pas la compatibilité à moins que vous n'ayez vraiment à le faire, etc.

À mon avis, une interface utilisateur devrait être indépendante du backend. Ainsi, un référentiel de projet d'interface utilisateur doit généralement contenir le code d'interface utilisateur et le contrôleur de client. Le contrôleur client se connecte aux contrôleurs de service de manière abstraite. Ils utiliseront une abstraction client / API de service qui est versionnée séparément du service, de sorte qu'un service puisse être mis à jour sans rompre le ou les clients (il peut y avoir plusieurs clients différents).

Ainsi, un service lui-même devrait être son propre référentiel. À mon avis, le service n’est qu’une empaquetage d’une logique de gestion basée sur un seul point de vérité. La logique métier doit donc généralement être distincte de la technologie de service qui l'héberge. D'autre part, la mise en œuvre du référentiel est généralement si étroitement liée à la logique métier qu'elle pourrait être intégrée dans le même référentiel. Mais même là, votre kilométrage peut varier.

Bien sûr, les projets simples qui ne risquent pas de changer beaucoup en termes de technologie ou de prise en charge de plusieurs piles, où toutes les interfaces utilisateur peuvent être hébergées à partir de la même source que le serveur principal et les services principaux étant généralement utilisés uniquement par le même client, peuvent en bénéficier davantage. référentiels étroitement intégrés.

Dans ce cas, il vous suffirait probablement de disposer de la totalité de la verticale dans un référentiel et de vous assurer que vos domaines fonctionnels sont correctement autonomes dans leur propre référentiel. Vous disposez alors toujours de la plupart des avantages des référentiels plus petits et des frais généraux minimes dans le cas contraire.

Arwin
la source