Cours 5 · Tests et outillageLeçon 2 sur 2
Gestion de versions et collaboration
4 h de lecture8 sections Version PDF
Git : dépôt, commit, historique ; branches et fusion ; résolution de conflits ; dépôt distant et travail à plusieurs ; suivi des tâches ; bonnes pratiques de messages de commit.
Un projet d'équipe pose un problème que le code seul ne résout pas : plusieurs personnes modifient les mêmes fichiers en même temps. Sans outil, c'est le chaos — on s'envoie des fichiers par courriel, on écrase le travail de l'autre, on ne sait plus quelle version est la bonne. La gestion de versions règle ce problème, et Git en est l'outil universel. Comme pour les tests (chapitre 8), sa valeur s'éprouve plutôt qu'elle ne se raconte : tant qu'on n'a pas perdu du travail faute de versionnage, ou résolu un conflit dans la panique, on croit pouvoir s'en passer.
Dans votre projet, Git s'utilise dès la première semaine — pas à la fin. C'est l'infrastructure de tout le reste.
Le dépôt, le commit, l'historique
Un dépôt (repository) est un dossier de projet dont Git suit l'histoire complète. À chaque étape stable de votre travail, vous créez un commit : un instantané de l'état du projet, daté, signé, accompagné d'un message qui décrit ce que vous avez changé.
L'ensemble des commits forme l'historique — une suite d'instantanés qui raconte la vie du projet. Cet historique n'est pas décoratif : il permet de revenir en arrière (annuler une modification qui casse tout), de comprendre pourquoi une ligne a été écrite (qui, quand, dans quel commit), et de retrouver l'état exact du projet à n'importe quelle date. Un projet versionné n'a jamais « perdu » de travail : tout état passé est récupérable.
Le message de commit mérite un soin particulier, car il s'adresse au futur — vos coéquipiers, et
vous-même dans six mois. Un bon message dit ce qui a changé et pourquoi, en une ligne claire :
Corrige le calcul de TVA sur les livres vaut infiniment mieux que modif, fix, ou ça marche enfin. L'historique est une documentation gratuite — à condition d'écrire les messages pour qu'on
puisse les relire.
Qu'est-ce qu'un bon message de commit, et pourquoi cela compte-t-il ?
Branches et fusion
Une branche est une ligne de développement parallèle. Plutôt que de travailler tous sur la même version — au risque de se casser mutuellement le code —, chacun crée une branche pour sa tâche : développer une fonctionnalité, corriger un bug, sans perturber le travail des autres ni la version stable.
Quand la tâche est finie et testée, on fusionne (merge) la branche dans la branche principale : Git combine les modifications. La plupart du temps, la fusion est automatique — Git sait réconcilier des changements qui portent sur des lignes ou des fichiers différents. C'est ce qui permet à trois personnes de travailler en parallèle et de réunir leur travail sans effort.
Ce modèle — une branche par tâche, fusion une fois testée — est la manière standard de travailler à plusieurs. Il isole le travail en cours (une fonctionnalité à moitié faite ne casse pas la branche principale) et rend chaque contribution relisable avant d'être intégrée (la revue de code du chapitre 6).
Le conflit, et comment le résoudre
Reste le cas que Git ne peut pas résoudre seul : deux personnes ont modifié la même ligne à partir de la même version. Git ne peut pas deviner laquelle garder — c'est un conflit.
Il faut d'emblée corriger un contresens : un conflit n'est pas une erreur, ni une faute. C'est une situation normale du travail à plusieurs. Git ne bloque que là où il serait dangereux de choisir à votre place ; pour tout le reste, il fusionne seul. Face à un conflit, il affiche les deux versions entre des marqueurs et attend :
<<<<<<< votre version return montant * 1.20;======= return montant * 0.90;>>>>>>> version du coéquipierRésoudre, c'est décider — en humain — de la version finale, puis supprimer les marqueurs. Et décider exige de comprendre les deux intentions : ici, l'un a ajouté la TVA, l'autre une remise ; la bonne résolution n'est ni l'une ni l'autre mais les deux combinées. C'est pourquoi aucun outil ne peut résoudre à votre place : il faudrait comprendre le sens du code.
Deux pièges du débutant, à éviter : paniquer et « prendre le sien » en écrasant le travail de l'autre ; et oublier un marqueur, qui laisse du code qui ne compile pas. La bonne attitude est calme et méthodique : lire les deux versions, comprendre, combiner, nettoyer les marqueurs, tester. L'exercice vous fait vivre exactement cette séquence.
Provoquer un conflit pour l'apprivoiser
C'est le point que le cours insiste à faire vivre en TP. Rencontré pour la première fois en pleine échéance de projet, un conflit affole : on résout n'importe comment, on perd du travail, on accuse l'outil. Provoqué délibérément — deux étudiants modifient volontairement la même ligne, puis fusionnent —, il devient une manipulation banale, apprise à froid.
C'est le même principe que l'exercice de régression du chapitre 8 : la valeur d'une pratique ne se
croit pas, elle s'éprouve. Faites-le une fois en TP, calmement, et le conflit cesse d'être un
épouvantail. On réduit d'ailleurs leur fréquence par de bonnes habitudes — commits petits et
fréquents, communication (« je touche à prix.js cet après-midi »), découpage du travail en zones
distinctes — mais on ne les élimine jamais totalement à plusieurs. Savoir les résoudre est une
compétence, pas un échec.
Deux coéquipiers ont modifié la même ligne ; Git signale un conflit. Quelle est la bonne façon de le résoudre ?
Dépôt distant et collaboration
Jusqu'ici, le dépôt vit sur une machine. Le dépôt distant (sur GitHub, GitLab…) en est une copie partagée, hébergée, qui sert de point de rendez-vous de l'équipe. Chacun envoie (push) ses commits vers le distant et récupère (pull) ceux des autres. Le distant est aussi une sauvegarde : si une machine tombe, le projet survit.
Autour du dépôt, ces plateformes offrent des outils de collaboration essentiels au projet :
- le suivi des tâches et des bugs (issues) : une liste partagée de ce qu'il reste à faire et des problèmes connus — le backlog du chapitre 3, rendu concret ;
- les demandes de fusion (pull/merge requests) : proposer une branche à intégrer, la faire relire par un coéquipier (revue de code, chapitre 6) avant de fusionner ;
- l'historique partagé : qui a fait quoi, quand — précieux pour se coordonner.
Pour votre projet, l'essentiel tient en une routine : committer souvent avec des messages clairs, pousser régulièrement vers le distant, récupérer avant de commencer à travailler. Cette discipline, prise dès la première semaine, évite l'immense majorité des drames de coordination.
À vous
L'exercice fait vivre le moment redouté, mais à froid : deux coéquipiers modifient la même ligne, Git lève un conflit, et vous le résolvez. Vous lisez les marqueurs, vous comprenez les deux intentions — une TVA, une remise —, vous écrivez la version qui les concilie, et vous nettoyez les marqueurs.
C'est exactement la manipulation qui, apprise ici calmement, vous évitera la panique le jour où elle surviendra en pleine échéance de projet. Un conflit résolu proprement est un non-événement.
Deux coéquipiers modifient la même ligne : provoquez le conflit Git, lisez les marqueurs (<<<<, ====, >>>>), puis résolvez-le en combinant les deux intentions et en supprimant les marqueurs. Apprenez à le faire à froid, avant de le rencontrer en pleine échéance.
// Deux membres de l'équipe partent de la MÊME version (la « base ») et // modifient CHACUN la même ligne, sur leur branche. À la fusion, Git ne peut // pas deviner laquelle garder : c'est un CONFLIT. const base = [ "function prix(montant) {", " return montant;", // <- la ligne que les DEUX vont changer "}", ]; // Amina ajoute la TVA : const amina = [ "function prix(montant) {", " return montant * 1.20;", // sa version de la ligne 2 "}", ]; // Bakary ajoute une remise : const bakary = [ "function prix(montant) {", " return montant * 0.90;", // sa version de la ligne 2 "}", ]; // Ce que Git AFFICHE quand il ne peut pas trancher (marqueurs de conflit) : function fusionner(base, a, b) { const res = []; for (let i = 0; i < base.length; i++) { if (a[i] === b[i]) { res.push(a[i]); } // personne n'a changé, ou même changement else if (a[i] === base[i]) { res.push(b[i]); } // seul b a changé -> on prend b else if (b[i] === base[i]) { res.push(a[i]); } // seul a a changé -> on prend a else { // les DEUX ont changé -> CONFLIT res.push("<<<<<<< amina", a[i], "=======", b[i], ">>>>>>> bakary"); } } return res; } console.log("Ce que Git affiche à la fusion :"); console.log(fusionner(base, amina, bakary).join("\n")); // ── À VOUS : résoudre le conflit ──────────────────────────────────────────── // Git ne choisit pas : c'est à un HUMAIN de décider, en COMPRENANT les deux // intentions. Ici, il faut la TVA (Amina) ET la remise (Bakary) : les deux. // Écrivez la version résolue, SANS marqueur de conflit. const resolu = [ "function prix(montant) {", // à compléter : la ligne qui combine les deux intentions "}", ]; console.log("\nVotre résolution :"); console.log(resolu.join("\n")); console.log("\nContient encore des marqueurs de conflit ?", resolu.some((l) => /^(<<<<|====|>>>>)/.test(l)));
Ce que la suite en fait
Vous disposez maintenant de tout l'outillage : spécifier, concevoir, écrire proprement, tester, versionner. Le dernier bloc assemble le tout — car un projet, ce n'est pas la somme de ces techniques, c'est leur conduite dans le temps, à plusieurs.
Le chapitre 10 traite la conduite de projet : constituer l'équipe, découper en tâches, suivre l'avancement, documenter, et présenter le résultat en soutenance. C'est le bloc qui donne son sens à tous les autres — et qui décrit, en réalité, ce que votre projet de semestre a fait vivre depuis la première semaine.
À retenir
Vous avez parcouru les 8 sections.
Marquez-la terminée pour faire avancer votre parcours, ou revenez sur un point avant de passer à la suite.