Chapitre 10 - Git

Le travail collaboratif
Temps de lecture : 5 minutes


C’est qui le *!#@? qui a supprimé mon code ??? <un dev de l’an 2000>

Comment des milliers de développeurs arrivent à travailler sur les mêmes projets sans se marcher sur les pieds ?

Imagine 10 personnes dans une pièce. Chacune va recevoir une copie d'un discours de 500 lignes. Isolées dans leur coin, elles doivent corriger le discours et en reformuler des parties. À la fin, il faut combiner le travail de tout le monde pour générer un discours unique, avec un ton homogène, qui a du sens et qui fonctionne bien.

C'est mission impossible sans un outil qui permet de :

  • Voir les ajouts et suppressions facilement.
  • Garder en mémoire les différentes versions du texte.
  • Combiner les éléments tout en permettant de résoudre les conflits : deux personnes qui modifient la même ligne.

Le code n'est rien d'autre que du texte. Les développeurs sont donc quotidiennement confrontés à ce problème. Plus l'équipe est grande, plus le besoin de contrôle est élevé.

Chaque demande de nouvelle fonctionnalité peut engendrer un conflit avec un autre développement en cours. Sur des petites équipes, on essaie de désamorcer la situation au maximum en amont en définissant une architecture cible et des étapes pour l'atteindre. Bien souvent, on tente même de supprimer totalement le problème en négociant un délai sur l'une des fonctionnalités.

Mais certains conflits restent inévitables !

Git

En 1972, pour répondre à tous ces besoins, Marc Rochkind, un ingénieur de Bell Labs, a créé le logiciel Source Code Control System (SCCS).
D'autres ont suivi, apportant chacun une vision différente et leur lot de fonctionnalités : vitesse de travail, compression des données, gratuité, sécurité de l'historique des versions, décentralisation des dépôts etc...

Insatisfait des solutions déjà existantes pour le développement du noyau Linux, Linus Torvalds a lancé en 2005 la création de Git. C'est depuis devenu le leader incontesté du contrôle de versions. Il est nativement intégré dans des dizaines de logiciels lui apportant encore plus de fonctionnalités, notamment des interfaces graphiques facilitant la visualisation. (Pour la suite on va rester en lignes de commande, sinon c'est pas drôle 🤭.)

Git est disponible pour tous les systèmes d'exploitation. Après son installation, il te suffit d'aller dans un dossier existant et de le transformer en dépôt Git. Un dépôt, repository en anglais, est un espace géré par Git qui gardera en mémoire toutes les modifications.

git init

Git est un système décentralisé, c'est-à-dire que chacun possède son propre dépôt sur sa machine avec une copie complète de tout l'historique. On peut travailler sans avoir besoin de se connecter à un dépôt centralisé. Dans les faits, en entreprise, il y a quand même presque systématiquement un dépôt central servant au partage des modifications entre développeurs.

On l'utilise majoritairement pour des fichiers texte, mais il fonctionne sur tout type de fichier. Si tu es par exemple graphiste, tu peux ainsi versionner tes PNG.

On peut faire des trucs de fou avec Git mais les bases sont très simples et se résument en quelques actions principales.
Partons directement sur un exemple : le discours à modifier est composé de 3 fichiers représentant respectivement l'introduction, le corps et la conclusion.

introduction.txt
Salut les rockstars !
corps.txt
Comment appelle-t-on un hamster dans l'espace ?

Un hamstéroïde 😂.
conclusion.txt
Bisoux <3

Il y a une faute, je veux la corriger. Je remplace "Bisoux" par "Bisous".

1 - Je fais ma modification sur Word ou n'importe quel autre éditeur de texte.

2 - J'ajoute le fichier à la liste des modifications que je souhaite sauvegarder.

git add conclusion.txt

3 - J'engage cette version des modifications. En gros, je sauvegarde l'état courant de mon dépôt local dans mon historique Git.

git commit -m "Un message pour expliquer le changement"

4 - J'envoie ce commit (cette version) sur le dépôt central pour que mon changement soit accessible pour mes collaborateurs.

git push

5 - Mes collaborateurs ont, eux aussi, travaillé sur les fichiers. Au moment de push, Git leur indique qu'il y a eu un changement et qu'ils doivent se mettre à jour avant de pouvoir partager leur travail.

git pull

C'est là que les ennuis commencent. Soit, par chance, nous n'avons pas modifié les mêmes lignes et Git assemble tout seul les différentes parties. Soit, nous nous retrouvons dans un état instable avec un conflit qu'il va falloir résoudre manuellement. Facile quand c'est sur un mot comme ici, beaucoup plus dur quand ça concerne des centaines de lignes 😅.

6 - Chacun peut parcourir l'historique et analyser les différentes versions.

git log

Les branches

J'ai une fonctionnalité à développer. Elle va me prendre plusieurs jours, pendant lesquels mon travail sera instable et non finalisé. Pendant ce temps, je pourrais en plus avoir une urgence, quelque chose à corriger rapidement sur la version du code actuellement en production. Dans cette situation, il faudrait que je reparte sur une base saine et que j'applique juste la correction. J'ai ainsi besoin de travailler en parallèle sur plusieurs versions du code.

C'est là qu'intervient le concept de "branches".

T'as forcément déjà vu un film avec des univers parallèles. Un point dans le temps a créé une divergence provoquant deux réalités. Créer une branche dans Git, c'est créer une divergence dans l'historique. Les deux branches peuvent évoluer complètement différemment.

git checkout -b ma_nouvelle_branche
--(commit 1)------(commit 2A)---
                      \
                       \____(commit 2B)___

La communauté a mis en place des méthodologies pour uniformiser les pratiques concernant la gestion des branches. La principale : GitFlow.

En version simplifiée :

  • La branche principale, "master", représente l'état actuel de la production.
  • Quand un développeur commence une fonctionnalité, il crée une branche dédiée.
  • S'il doit intervenir sur une urgence, il peut revenir sur master et créer une troisième branche dédiée à sa correction.

Le vrai GitFlow a quelques subtilités en plus, je te laisse approfondir ça par toi-même. Ici, je vais rester simple en ne parlant que de master.

À tout moment, le développeur peut vérifier les différences entre le master et sa branche :

git diff master

Une fois le travail terminé, ces différences peuvent être appliquées sur le master. Ça signifie qu'elles sont prêtes à être déployées lors de la prochaine mise en production. On appelle ça un "merge".

git checkout master
git merge ma_nouvelle_branche

Imagine que tu essaies de combiner deux univers avec des historiques différents. Dans l'un, Zidane a tiré sa Panenka à côté. Évidemment ça ne se passe pas bien, il va y avoir des conflits. Il faut réécrire la version de l'histoire qu'on souhaite devenir réalité. Une fois les conflits résolus, ce nouvel historique est prêt à être poussé pour tout le monde.

C'est l'un des problèmes majeurs du GitFlow, les branches durent trop longtemps et provoquent souvent des conflits.

De plus en plus de développeurs, dont moi, adoptent ainsi le "trunk based development". Le but est de raccourcir au maximum la vie des branches pour que les développements soient le plus rapidement mis à disposition sur le tronc commun : master. Dans cette vision, on accentue le côté itératif des développements et on accepte de passer en production des choses non finalisées, du moment évidemment que ça n'impacte pas négativement l'expérience client.

Comment on s'assure de la qualité des livraisons ?

GitHub / GitLab

Dans tout projet sérieux, le code ne passe pas en production et n'est même pas fusionnable sur master sans passer par une série de vérifications : tests automatisés, vérification de syntaxe, analyse de qualité. C'est ce qu'on appelle un pipeline de Continuous Integration (CI).

Différents pipelines sont automatiquement déclenchés selon le cycle de vie des branches sur le dépôt central : création, demande de fusion avec le master, fusion exécutée.

Dans les étapes avancées, les pipelines peuvent inclure la livraison automatique du code sur les environnements de préproduction, voire de production. C'est ce qu'on appelle la Continuous Delivery (CD).

Des outils, les plus connus étant GitHub et GitLab, servent à héberger des projets utilisant Git et à organiser les phases post-développement : configuration et exécution des pipelines CI/CD, meilleure visualisation de l'historique et revue de code.

La revue de code

Le développeur envoie sa branche sur le dépôt central. Il se connecte sur GitHub ou GitLab et ouvre une "demande de fusion". C'est-à-dire qu'il demande à ce que son code soit merge sur master. Sur GitHub, cette demande de fusion est appelée "Pull Request" (PR) et sur GitLab "Merge Request" (MR).

On a vu que l'ouverture d'une demande de fusion provoque automatiquement l'exécution de pipelines de CI. C'est aussi à ce moment qu'intervient l'étape tant redoutée de la revue de code !

La revue de code, c'est l'étape pendant laquelle les autres développeurs de l'équipe vont étudier ce que tu as produit. Dans GitHub et GitLab, l'interface graphique affiche la différence entre les deux branches et permet d'ajouter des commentaires. La revue de code n'est pas une science exacte, chacun a sa façon de s'organiser : revue à deux, facultative vs obligatoire, organisation d'ateliers etc...

Une revue mal organisée peut vite se transformer en guerre d'égo et créer des tensions 💣.