Git et dépendances de projet

Nicola Paolucci
Nicola Paolucci
Retour à la liste

Posez-vous les questions suivantes. Comment gérer les dépendances de projet avec Git ? Notre projet est composé de plusieurs dépôts interdépendants. Actuellement, ils sont gérés avec svn:externals. Quelle est la meilleure façon de les gérer avec Git ? Comment diviser un dépôt très volumineux en plusieurs petits composants avec Git ? Vous trouverez ci-dessous quelques-unes des questions les plus fréquemment posées lors de la tournée européenne récente Getting Git Right.

Le sujet est un réel problème pour de nombreuses équipes de développement ayant adopté Git. Par conséquent, cet article vise à faire la lumière sur ce point.

Manifestement, les dépendances de projet et les infrastructures de builds sont deux sujets étroitement liés. Nous avons d'ailleurs entamé un grand débat chez Atlassian concernant « l'avenir des builds ».

Le fait d'avoir des dépôts séparés au lieu d'un dépôt unique peut compliquer les choses. C'est toutefois une étape relativement naturelle (et parfois obligatoire) dans l'évolution d'un projet de logiciel pour au moins deux raisons essentielles : améliorer les temps de build et avoir des dépendances partagées entre les projets.

Généralités : Directives et solutions non optimales

Revenons-en à nos moutons : Comment suivre et gérer les dépendances de projet avec Git ?

Si possible, ne le faites pas !

Blague à part, je vais d'abord vous donner quelques informations générales, puis j'entrerai dans les détails. Sachez qu'il n'existe pas de solution magique (que ce soit dans Git ou ailleurs) qui résoudra tous les problèmes liés aux dépendances de projet.

Une fois qu'un projet a atteint un certain volume, il est recommandé de le diviser en plusieurs composants logiques, mais n'attendez pas d'avoir 100 millions de lignes de code supplémentaires dans un seul dépôt avant de le faire. Les conseils qui suivent vous aideront à trouver la bonne approche.

Premier choix : utiliser un outil de gestion des dépendances/builds approprié plutôt que Git

Je recommande d'utiliser un outil de gestion des dépendances pour gérer les difficultés croissantes et les temps de build des projets volumineux.

Conservez vos modules séparément dans des dépôts individuels et gérez leur interdépendance à l'aide d'un outil spécifique. Il en existe un pour (presque) toutes les technologies. Quelques exemples :

  • Maven (ou Gradle) si vous utilisez Java
  • Npm pour les applications Node
  • Bower, Component.io, etc. si vous utilisez Javascript (mis à jour !)
  • Pip et les fichiers requirements.txt si vous utilisez Python
  • RubyGems et Bundler si vous utilisez Ruby
  • NuGet pour .NET
  • Ivy (ou certaines actions personnalisées sur CMake) pour C++ (mis à jour !)
  • CocoaPods pour les applications Cocoa sous iOS
  • Composer ou Phing pour PHP (ajouté !)
  • Dans Go, l'infrastructure de builds/dépendances est d'une certaine manière intégrée au langage (même si certains développeurs travaillent sur une solution plus complète, voir godep). Pour notre serveur Git Bitbucket Server, nous utilisons Maven et Bower. Au moment du build, l'outil choisi extraira les bonnes versions des dépendances, et vous pourrez ainsi développer votre projet master. Si certains de ces outils ont des limites et que leurs hypothèses ne sont pas toujours optimales, ils ont fait leurs preuves et sont viables.

Diviser un projet, quelle corvée !

En résumé, au début d'un projet, tous les éléments sont contenus dans un build. Plus le projet évolue, plus le build risque de perdre de la vitesse, d'où l'intérêt d'effectuer une mise en cache afin de gérer les dépendances. Cela signifie par ailleurs que les submodules (voir ci-dessous) se prêtent particulièrement bien aux langages dynamiques, par exemple. Je pense que dans la plupart des cas, les temps de build peuvent devenir préoccupants à un moment ou un autre, c'est pourquoi vous devez utiliser un outil de gestion des dépendances.

Répartir les composants dans des dépôts séparés présente des inconvénients. En voici quelques-uns :

  • Modifier un composant nécessite une livraison
  • Perte de temps et échec possible pour des tas de raisons débiles
  • Pas nécessaire en cas de changements mineurs
  • Il faut configurer manuellement les nouveaux builds pour chaque composant.
  • Entrave la détectabilité des dépôts
  • Refactoring nécessaire lorsque le code source n'est pas entièrement disponible dans un dépôt unique
  • Dans certaines configurations (comme la nôtre), la mise à jour des API nécessite une livraison pilote du produit, puis du plug-in et à nouveau du produit. Nous avons probablement oublié quelque chose, mais vous comprenez où nous voulons en venir. C'est loin d'être la panacée.

Deuxième option : Utiliser git submodule

Si vous ne pouvez ou ne voulez pas utiliser un outil de gestion des dépendances, Git comporte une fonctionnalité pour gérer les submodules. Les submodules peuvent s'avérer pratiques, surtout pour les langages dynamiques. Cependant, ils n'amélioreront pas forcément les temps de build. J'ai déjà rédigé quelques consignes et astuces les concernant, et j'ai aussi exploré quelques solutions alternatives. La plupart des internautes avancent des arguments opposés.

1:1 match between svn:external and git

ATTENTION ! Si vous cherchez une correspondance parfaite entre svn:externals et git,, vous utiliserez volontiers submodules pour veiller à ce que les submodules (sous-modules) suivent uniquement des branches de livraison, et non des commits aléatoires.

Troisième choix : utiliser d'autres outils de build et de gestion des dépendances cross-stack

Vous n'aurez pas toujours la chance de travailler sur un projet complètement uniforme, que vous pouvez développer et assembler au moyen d'un seul outil. Par exemple, pour certains projets mobiles, il vous faudra jongler entre des dépendances développées en Java et en C++, ou utiliser des outils propriétaires pour créer des actifs. Dans ces situations plus complexes, vous pouvez enrichir Git d'une couche supplémentaire sur le dessus. Un bel exemple du genre est le dépôt Android.

Autres outils de build qui valent la peine d'être examinés :

Conclusions et publications à consulter

Charles O'Farrell recommande d'autres articles (en anglais) sur l'infrastructure de builds (et Maven), qui donnent matière à réflexion :

Je souhaiterais maintenant conclure avec une excellente citation extraite de ce dernier article. Même si elle fait référence à Maven, elle s'applique aussi à d'autres outils de gestion des builds et des dépendances.

Un cache se contente d'accélérer les choses. Vous pourriez supprimer un cache intégralement et le système environnant fonctionnerait de la même manière, mais moins vite. Un cache n'a aucun effet secondaire. Peu importe ce que vous avez fait avec un cache par le passé, une requête spécifique sur le cache renverra exactement la même valeur que la même requête soumise ultérieurement.

L'expérience Maven est très différente de ma description ! Les dépôts Maven sont utilisés comme des caches, mais ils n'en possèdent pas les propriétés. Lorsque vous souhaitez récupérer des éléments dans un dépôt Maven, les opérations réalisées précédemment sont très importantes. Le dépôt renvoie l'élément ajouté le plus récemment. Il peut même échouer si vous voulez récupérer un élément avant de l'y ajouter.

Merci à Charles O'Farrell pour son précieux feedback lors de la rédaction de cet article. J'espère que vous avez apprécié votre lecture. Suivez-moi sur @durdn et @AtlDevtools pour encore plus d'astuces sur Git.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant