git – les bonnes pratiques







Dans ce guide, nous répondrons à la question de savoir quelles sont les meilleures pratiques à respecter lorsqu'on utilise l’outil de contrôle de version git. Ce guide prend pour acquis que vous connaissez les bases de git. Si ce n'est pas le cas, nous vous invitons à vous familiariser avec les commandes de bases de git avant de consulter ce guide.
Le site web w3school offre un tutoriel assez complet disponible ici.
Pourquoi dit-on que git est un outil de «contrôle de versions»? Parce que dans le développement de logiciels, git permet de sauvegarder différentes versions du même logiciel afin de pouvoir les redéployer au besoin.
Dans le cadre de projet en sciences des données cependant, git sert d'abord et avant tout à deux choses :

  1. Sauvegarder l'évolution du projet afin de permettre le retour en arrière.
  2. Travailler en équipe sur le même projet.

Dans ce guide, nous allons nous intéresser :

  1. au fichier .gitignore;
  2. à la fréquence à laquelle pull, commit et push;
  3. aux messages des commits;
  4. aux branches (pourquoi en faire, et quand les merge);

le fichier .gitignore

On veut généralement que notre répertoire git soit léger en terme de mémoire. Pour ce faire, il est conseiller de seulement suivre les fichiers qui contienent le code. Étant donné que le repertoire qui contient notre projet sur notre ordinateur contient généralement aussi autre chose que le code (les données par exemple), il est nécessaire de dire à git d'ignorer certains répertoires et certains types de fichiers. C'est ce à quoi sert le fichier .gitignore. Ce fichier doit être placé à la racine du projet pour que git le détecte.

On conseiller de mettre les fichiers et les répertoires suivants dans le fichier .gitignore :

  • le code compilé (exécutables) – ex : fichiers en .o, .pyc, et .class;
  • les fichiers binaires et les images – ex : .jpeg, .db;
  • les caches des dépendances – ex : le contenu de /node_modules ou /packages;
  • les répertoires des fichiers exécutables – ex : /bin, /out, ou /target;
  • les fichiers logs ou temporaires – ex: .log, .lock, ou .tmp;
  • les fichiers systèmes cachés – ex : .DS_Store or Thumbs.db;
  • les fichiers configs d'environnements : – ex : idea/workspace.xml;
  • les répertoires de données : – ex : /data
  • les données : – ex : les fichiers .csv


Voici un exemple de fichier .gitignore :

Pour aller un peu plus loin, nous vous conseillons ce guide où il est, entre autres, question des caractères spéciaux que l’on peut utiliser pour spécifier à git d’ignorer plusieurs fichiers en même temps grâce à une seule ligne dans notre fichier .gitignore. Le caractère le plus important est le « * » qui est un caractère de remplacement (wildcard) qui permet, par exemple, de spécifier qu’on veut ignorer tous les fichiers d’un certain type. Par exemple « *.csv » signifie que l’on veut ignorer tous les fichiers csv.

La fréquence à laquelle pull commit et push

Un des objectifs de cette section est de proposer une méthode de travail qui permet le plus possible d'éviter d'avoir à gérer de gros conflits. Vous le savez sans doute si vous lisez ce guide, lorsqu'on travaille avec git en équipe sur un même projet, il est presqu'impossible qu'on ait jamais à gérer des conflits. Un conflit a lieu lorsque deux coéquipiers ont modifier le même fichier de code. Il est relativement aisé de gérer un conflit sur un fichier de code. Mais lorsque plusieurs fichiers ont plusieurs conflits, et que certaines fichiers sont interdépendants les uns avec les autres, cela peut rapidement devenir un cauchemar. Pour éviter cela, c'est assez simple, il faut pull commit et push souvent.

Le pull

Lorsqu'on travaille en équipe, il est généralement recommandé de faire un pull chaque fois qu'on commence une journée de travail sur un projet. On s'assure ainsi de commencer à travailler sur un projet à jour. Modifier un fichier qui n'est pas à jour avec notre répertoire distant est la recette parfaite pour créer des conflits.

Le commit et le push

Il est recommandé de faire des commits relativement souvent (quelques fois par journée de travail) et de push à chaque fois. Faire des commits à court interval permet de retourner en arrière de manière plus précise, sans risquer de perdre plusieurs heures de travail. Cela permet aussi à l'équipe de retracer quand a été fait chaque changement (si les commits sont bien nommés). Cela rend aussi la gestion des conflits plus facile. Si tous les membres de l'équipe font des commits et des pushs fréquents, on sera plus en mesure de détecter rapidement lorsqu'il y a conflit. Les conflits seront moins difficiles à gérer.

La règle générale que vous devriez suivre pourrait être énoncée de la manière suivante : faites un commit chaque fois que vous avez complété une tâche. Évitez de faire des commits où plusieurs tâches ont été faites. La question se pose de savoir ce que représente «une tâche». Il est difficile de tracer le frontière de ce que représente «une tâche» mais on peut penser à :

  • écrire une fonction;
  • corriger un bug;
  • documenter une fonction;
  • réusiner un script particulier;
  • etc.

Une tâche doit pouvoir être décrite par un message simple. Ces considérations nous renvoies à la prochaine section, mais nous croyons que le fait de commit après la complétion d'une tâche, rendra le travail de rédaction des messages associés aux commits beaucoup plus simple.

Les messages des commits

Vous avez probablement été déjà confronté à une panne d'inspiration lorsqu'est venu le temps d'écrire le message descriptif d'un de vos commits. Ce mème exprime, possiblement, assez bien ce à quoi ont déjà ressemblé vos messages de commits (l'auteur de ces lignes s'avoue coupable) :

source : https://xkcd.com/1296/

Si se conformer aux bonnes pratiques exige que l'on fasse souvent des commits, cela exige aussi que l'on écrive des messages descriptifs informatifs pour chacun de ces commits. Comme on l'a vu dans le précédente section, le fait d'être en mesure d'écrire de courts messages informatifs est étroitement lié au contenu de notre commit. Il sera difficile de décrire en quelques mots ce qu'on a fait pendant des journées entières de travail. Il sera, au contraire, assez aisé de décrire en quelques mots une modification qu'on a apporté à une fonction particulière qui nous a pris moins d'une heure de travail. Si la fréquence à laquelle on commit est motivée par le désir d'écrire des messages informatifs, la tâche deviendra automatiquement plus simple.

L'auteur de ce billet de blog propose de précéder chaque message d'un étiquette nous informant du type de modification qui a été fait par le commit, ce que nous semble une excellente idée :

  • [fonc] pour l’ajout d’une fonctionnalité;
  • [modif] pour la modification d’une fonctionnalité;
  • [supp] pour la suppression d’une fonctionnalité ou fichier;
  • [corr] pour la correction d’un bug;
  • [reus] pour le réunisage ( refactor ) du code;
  • [autr] quand aucun des tags précédant ne correspond à la tâche;

Gestion de branches

Dans un petit projet, il est souvent non-nécessaire de faire des branches.

Ce qu'il faut savoir c'est que créer une branche génère un danger, et on l'évite en s'abstenant de faire des branches. Le danger est le suivant :

Le merge conflict.

Plus longtemps deux personnes travaillent sur deux branches différentes, plus la différence entre les deux versions du projet augmente, et plus la possibilité de devoir gérer un énorme conflit augmente. Cela peut faire en sorte qu'on se retrouve avec deux versions parallèles de notre projet qui sont tellement difficile à merge qu'on abandonnera tout simplement le projet de le faire. On veut éviter cela à tout prix

Quoi faire en cas de merge conflict?

Pas de panique! Résoudre un conflit signifie simplement qu’on devra sélectionner ce qu’on veut conserver dans les deux versions des fichiers en conflits. Vous pouvez faire cela à la main (modifier directement le fichier dans lequel il y a un conflit). Nous vous conseillons ce guide pour en savoir plus. Il existe sinon plusieurs outils avec des interfaces graphiques qui permettent de gérer les conflits de manière plus conviviale. On pense, par exemple, à Sourcetree mais aussi à certains environnements de développement ou des éditeurs de code qui offrent ce service comme Pycharm ou VScode.

Pourquoi faire une branche?

Pourquoi faire une branche si cette pratique semble dangereuse?

Faire une branche est nécessaire lorsqu'on veut faire des modifications au code qui risque de briser des parties du code avec lesquelles d'autres membres de l'équipe doivent travailler.

Si notre code n'est pas susceptible de briser quoi que ce soit pour les autres, il n'est pas nécessaire de faire une branche.

La méthode gitflow

La motivation

La méthode gitflow est très utilisée dans le monde du développement de logiciels. Nous ne croyons pas qu'elle soit nécessaire pour la majorité des projets en sciences des données. Cette méthode vise essentiellement deux choses :

  1. Permettre à plusieurs personnes de travailler en parallèle sur le développement de différentes fonctionnalités d'un logiciel sans nuire au travail des autres.
  2. Garder distincte une version stable du projet (qui peut être utilisée par les utilisateurs du logiciel) et une version en développement, qu'on peut se permettre de briser, et sur laquelle on teste les nouvelles fonctionnalités.

Nous vous laissons répondre à la question de savoir si votre projet est suffisamment complexe pour bénéficier de cette méthode.

La méthode

Dans la méthode gitflow on utilise 4 types de branche.

  • Master : qui contient la version stable du projet.
  • Development : qui est la branche sur laquelle les modifications sont faites pendant la phase active de développement du logiciel.
  • Features : qui sont toutes les branches qui sont créées pour développer une nouvelle fonctionnalité (une branche par fonctionnalité).
  • Release : qui sont des branches qu'on crée uniquement afin de merge les changements de la branche Development dans la branche Master (pour ne pas directement merge Development dans Master).

Le développement de nouvelles fonctionnalités se fait de la manière suivante :

  1. Lorsqu'une personne veut ajouter une nouvelle fonctionnalité, elle crée une nouvelle branche au nom de la fonctionnalité qu'elle veut développer à partir de la branche Development
  2. Lorsque la fonctionnalité est prête, elle merge sa branche dans la branche Development.
  3. Des tests sont faits pour s'assurer que la nouvelle fonctionnalité (ou l'intéraction des nouvelles fonctionnalités) n'a pas brisé le logiciel.
  4. Lorsque le logiciel est stable dans la branche Developement, et est prêt à être déployé, on crée une branche Release à partir de la branche Development.
  5. On merge la branche Release dans Master.

Pour une explication plus détaillé (en anglais) de la méthode gitflow, voir ce guide

Tutoriels

https://webpick.info/les-bonnes-pratiques-de-git-pour-un-developpeur/

https://medium.com/@pilloud.anthony/git-les-bonnes-pratiques-b0f19c3eef47

http://www2.ift.ulaval.ca/~eude/Gif-1003/Initiation_a_git/Labo%20sur%20Git_fichiers/Git%20-Bonnes_pratiques.pdf

Ce contenu a été mis à jour le 7 août 2023 à 16 h 07 min.