Mon commit Git préféré (2019)
(dhwthompson.com)- Le commit de Dan Carley intitulé « Convert template to US-ASCII to fix error », réalisé pendant son travail sur GOV.UK, montre que même un petit changement de code peut devenir une documentation durable pour la base de code grâce à un message de commit détaillé
- Un bon message n’indique pas seulement ce qui a changé, mais surtout pourquoi cela a changé ; cet exemple consigne précisément l’erreur
invalid byte sequence in US-ASCIIsurvenant lors de l’exécution debundle exec rake, ainsi que les conditions de reproduction - Comme la chaîne d’erreur et la démarche d’investigation sont conservées ensemble, toute personne confrontée plus tard au même problème peut retrouver la trace d’une résolution antérieure via
git log --grepou la recherche de commits sur GitHub - Le message inclut aussi l’utilisation d’outils Unix comme
find -exec,file --mimeeticonv, ce qui permet au reviewer comme aux lecteurs ultérieurs de suivre le cheminement de résolution - Tous les commits n’ont pas besoin d’être aussi longs, mais un message qui conserve le contexte, l’enquête et même l’émotion aide à construire une compréhension partagée de la base de code et de la confiance au sein de l’équipe
Un commit qui transforme un petit changement en documentation durable
- Les messages de commit Git, lorsqu’ils sont bien rédigés, peuvent devenir un puissant outil de documentation qui accompagne toute la vie d’une base de code
- Le commit pris en exemple correspond à une modification faite à l’époque où l’auteur travaillait sur GOV.UK au Government Digital Service
- Son auteur est Dan Carley, et son titre est « Convert template to US-ASCII to fix error »
- Grâce à la manière ouverte dont GDS développe ses projets, ce type d’exemple interne à l’organisation peut aussi être partagé à l’extérieur
Le contexte compte plus que la longueur
- Ce commit est long par rapport à la taille du changement de code, mais sa valeur essentielle ne tient pas à sa longueur en soi : elle réside dans le contexte utile qu’il apporte
- Ailleurs, la même modification aurait pu être résumée par
change whitespaceoufix bug - Dan a laissé une trace détaillée pour que ses collègues immédiats comme les lecteurs ultérieurs puissent comprendre l’origine du problème et la manière dont il a été résolu
Expliquer pourquoi, pas seulement ce qui change
- Un bon message de commit explique non seulement ce qui a changé, mais aussi pourquoi cela a changé
- Ce commit consigne qu’après avoir ajouté sur une feature branch un test comparant le contenu de
/etc/nginx/router_routes.conf, le résultat différait selon la manière d’exécuter la suite- L’exécution via
bundle exec rake specoubundle exec rspec modules/router/specfonctionnait normalement - L’exécution via
bundle exec rakefaisait échouer chaque blocshould - L’erreur était
ArgumentError: invalid byte sequence in US-ASCII
- L’exécution via
- Sans cette explication, il aurait probablement fallu deviner quel outil produisait quelle erreur de parsing
- Le contexte initial d’un travail peut facilement disparaître quand on oublie, qu’on change d’équipe ou qu’on quitte l’organisation
Un historique d’erreurs facile à retrouver
- Dès le début du message de commit, on retrouve telle quelle la chaîne d’erreur qui a déclenché la modification
- Toute personne rencontrant la même erreur peut retrouver des traces antérieures dans la base de code de la manière suivante
git log --grep "invalid byte sequence"- GitHub commit search
- Les résultats de recherche montraient que plusieurs personnes avaient cherché cette erreur, et permettaient aussi de voir qui l’avait rencontrée en premier et quelles mesures avaient été prises
Raconter la résolution comme une histoire
- Le message est structuré de façon à permettre de suivre comment le problème s’est manifesté, dans quel ordre il a été investigué, puis finalement corrigé
- Par exemple, il y est indiqué que l’erreur disparaissait lorsqu’on retirait le matcher
.with_content(//), qu’aucun caractère étrange n’apparaissait dans le fichier spec, et qu’il était possible de reproduire le problème en important Puppet dans le même interpréteur - Le message de commit documente non pas un fichier, une fonction ou une ligne de code précise, mais bien la modification elle-même ; c’est donc un excellent endroit pour conserver la trace du parcours de la base de code
Un effet secondaire utile : diffuser le savoir de l’équipe
- Dan a noté les commandes exécutées à chaque étape de son investigation, ce qui peut devenir un moyen léger de diffuser des connaissances dans l’équipe
- Rien qu’en lisant le message de commit, on peut apprendre à utiliser des outils Unix
- Avec l’argument
-execdefind, on peut exécuter une commande sur chaque fichier trouvé - En ajoutant
\+à la fin de la commande, on transmet plusieurs noms de fichiers à une seule commandefileau lieu de l’exécuter une fois par fichier file --mimepermet de connaître le type MIME d’un fichier- L’outil
iconvexiste
- Avec l’argument
- Ces informations profitent non seulement à la personne qui relit le changement, mais aussi à celle qui retrouvera ce commit plus tard
- Avec suffisamment de temps et de commits, ce type de message peut devenir un mécanisme d’amplification des connaissances pour l’équipe
Des traces qui créent empathie et confiance
- À la fin du commit, on trouve la phrase : « Now the tests work! One hour of my life I won't get back.. »
- Cette phrase transmet à la fois la frustration d’un développeur ayant traqué un bug sournois pendant une heure et la satisfaction ressentie une fois le problème résolu
- Même lorsque du code issu d’un hack rapide ou d’un prototype finit par entrer en production et à y prendre racine, ce type de message rappelle que derrière chaque changement se trouvait quelqu’un qui a pris la meilleure décision possible avec les informations du moment
Tous les commits n’ont pas besoin d’être longs
- Cet exemple est un cas extrême, et il ne faut pas s’attendre à ce que tous les commits de cette taille atteignent le même niveau de détail
- Il reste néanmoins un bon exemple de message qui explique le contexte d’un changement, permet aux autres d’apprendre et contribue à un modèle mental partagé de la base de code de l’équipe
- Si le sujet des bons messages de commit et des outils de structuration des changements vous intéresse, voici quelques ressources
- Telling stories through your commits by Joel Chippindale
- A branch in time by Tekin Süleyman
1 commentaires
Avis sur Hacker News
Pour le meilleur ou pour le pire, du point de vue d’un cofondateur de GitHub et auteur de livres sur Git comme Pro Git, les messages de commit Git sont une voie singulière de documentation du code, mais très inefficace.
Le problème central est que la plupart des outils, Git, GitHub et autres, n’affichent généralement que la première ligne. Même dans l’exemple cité, on ne voit qu’une ligne générique comme « US-ASCII error », et presque personne ne voit le reste du contenu salué dans l’article avec les outils modernes.
Git a été conçu pour que le message de commit soit le corps d’un e-mail lu par tout le projet, mais aujourd’hui il ne joue généralement plus ce rôle. À moins que cela ne soit discuté sous forme de série de patchs sur une liste de diffusion, presque rien n’est lu au-delà des 50 premiers caractères du titre.
Même quand on cherche le message associé dans
git blame, qu’il faille utiliser-w -C -C -Cou deux-C, on ne voit que la première ligne ; au final, il faut retrouver le SHA puis fairegit showpour lire le message long. Même quand ils sont bien rédigés, le fait que l’information soit si difficile à retrouver est l’un de mes plus gros reproches à Git.Si l’on regarde l’historique du projet Git, les messages de commit sont presque toujours excellents, mais même en lançant
blamesur un fichier, il est très difficile de suivre le contexte de l’implémentation d’une fonction. La documentation laissée par Jeff King ces dix dernières années est d’un niveau génial, et c’est terrible que presque personne ne puisse l’apprécier.Je ne connais pas la solution exacte, mais dans la plupart des communautés, écrire une excellente documentation dans les messages de commit est malheureusement presque une perte de temps. Parce qu’elle est trop difficile à trouver.
git blame,git logetgit showdans le terminal ; suivre l’historique d’un fichier est facile, et avecgit log -G, il suffit de quelques secondes pour trouver quand quelque chose a été ajouté ou supprimé.Ce qui est pénible, c’est de trouver le commit et de n’avoir comme message que « bleh » ou « add a thing ». Il aurait suffi que le développeur prenne 60 secondes pour expliquer pourquoi il l’a fait.
À l’inverse, quand on trouve un message de commit qui explique en détail pourquoi un changement a été fait, c’est un vrai bonheur. Un bon message de commit peut économiser des heures, voire des jours de travail.
GitHub contribue aussi au problème des mauvais messages de commit. Avec de la chance, la description de la PR contient les détails, mais elle n’est pas juste à côté du log de commits ; le plus souvent, la PR pointe vers Jira, Jira vers une conversation Slack, et Slack vers un document Google.
Le secteur est vraiment mauvais en documentation, mais des gens comme Jeff King mènent le bon combat. Au final, je pense que ce n’est pas un problème technique, mais humain. Rédiger de la documentation n’apporte pas de récompense immédiate, donc cela ressemble à du travail en plus, et le bénéfice n’apparaît que des jours, des semaines ou des mois plus tard.
S’il vous plaît, écrivez de bons messages de commit. Même en prenant une minute pour expliquer le pourquoi, chaque commit n’aura pas à devenir une fichue énigme de clôture de Chesterton. Si vous mettez cela dans un message de commit facile à retrouver, votre futur vous — et moi — vous remercierons.
J’ajouterai que je ne suis pas non plus d’accord avec l’idée que les messages de commit sont difficiles à trouver. Je n’ai presque aucune difficulté à suivre l’histoire d’une ligne de code, d’une fonction ou d’un fichier ; et même si un message de commit n’est jamais relu, il a de la valeur au moment de sa rédaction. Écrire un bon message me force à vérifier que j’ai bien écrit le code que j’avais l’intention d’écrire, et c’est aussi utile pour le reviewer.
Dire qu’il est difficile de trouver les options de
git blameressemble aussi à une plaisanterie. Un bon IDE devrait afficher les informations de blame sur chaque ligne, montrer le diff avec un bouton, et permettre, depuis ce diff, de faire récursivement un blame sur des lignes de contexte ou des lignes supprimées. Des outils comme Tig font exactement cela.J’admets que GitHub rend les messages de commit difficiles à consulter.
La documentation attachée aux commits n’est pas écrite pour le plaisir. Elle sert à réduire le risque d’accepter un patch envoyé par quelqu’un qui ne sera peut-être plus là plus tard, et à exposer les réflexions associées pour que d’autres puissent travailler dessus. Si l’on cache sa pensée, une forme de propriété exclusive sur le code s’accumule, et ce n’est généralement pas une bonne chose. Les messages de commit servent aussi de preuve de travail, ce qui peut devenir important quand il y a trop de patchs. Dans les projets commerciaux, une partie de cette importance peut être moindre.
En réalité, il n’y a pas vraiment d’autre endroit où mettre ce type de documentation. Elle n’a pas sa place dans les commentaires du code. Mais il existe désormais une génération de développeurs pour qui Git est GitHub, et dont l’idée est que le seul but de Git est de pousser des changements sur GitHub.
Git n’est pas génial, mais il l’est beaucoup moins mal que les autres solutions. Il faut revenir aux fondamentaux et comprendre le véritable objectif du contrôle de version.
Donc cela n’aide peut-être pas beaucoup la communauté actuelle, mais cela aidera la personne qui déboguera dans quelques années.
Je suis d’accord avec l’intention générale, mais il vaudrait mieux mettre un BLUF plus concret au début. Par exemple : « Fix test issues caused by non-breaking space character \xa0 »
On comprend immédiatement quel est le problème, tout en gardant la liberté de lire la suite si l’on veut en savoir plus.
Donc, pour rechercher dans l’historique, une bonne première ligne de message suffit. La petite histoire du voyage jusqu’à Narnia et retour pour trouver la cause racine du bug ne me paraît pas très pertinente. Cela dit, je ne suis pas contre le fait de l’écrire pour se défouler dans la description de la PR ou dans le message étendu du commit.
J’ai déjà ressenti la fierté d’écrire un excellent message de commit, mais je suis moins convaincu de la valeur que cela apporte aux autres. Quand on tombe sur un message d’erreur étrange, qu’on ajoute une nouvelle fonctionnalité, ou dans presque n’importe quel cas, je pense que la plupart des gens ne cherchent pas dans les messages de commit.
C’est un peu triste, mais je soupçonne de plus en plus que les beaux messages de commit relèvent en partie de la vanité du programmeur. La personne qui les admire le plus est surtout leur auteur, tandis que les autres passent à côté sans les remarquer.
Il y a parfois une place pour ce genre d’ornement esthétique, mais je ne suis pas convaincu que sa valeur pratique soit si grande. Si quelqu’un committe « fix whitespace issue », cela ne me dérange plus beaucoup, et je pense être devenu un meilleur collègue grâce à cela.
C’est peut-être différent dans des projets comme Git ou Linux, avec d’immenses équipes distribuées et un très grand nombre de commits. Les projets auxquels je suis habitué comptent entre 1 et 100 contributeurs, la plupart appartenant à la même organisation.
Quand Google ou Stack Overflow ne donnent pas de réponse, il m’arrive de chercher sur GitHub si quelque chose de similaire apparaît dans une PR ou un message de commit. Cela m’a aidé d’innombrables fois, y compris dans des dépôts privés auxquels j’ai accès. Un bon message de commit a clairement une valeur qui dépasse la vanité. Si beaucoup de développeurs ne les consultent pas, tant pis pour eux ; avec l’expérience, j’espère qu’ils finiront par s’en rendre compte. On peut aussi apprendre aux développeurs juniors à chercher, ou leur envoyer l’article original.
Depuis, j’essaie d’écrire mes messages de commit de façon à ce qu’au minimum, mon moi futur puisse se rappeler pourquoi la modification était nécessaire.
git blameavant de modifier une ligne de code, vous vous y prenez mal.Il m’est arrivé plusieurs fois de vouloir corriger un bug évident, puis de regarder le commit précédent juste avant de committer, pour découvrir que ce « bug » avait été introduit intentionnellement, et que la tâche JIRA associée expliquait très bien pourquoi ma modification pourtant évidente aurait recréé un bug vieux de deux ans.
git blamede l’IDE pour comprendre ce qui s’est passé. Quand je tombe sur un bon message de commit, je l’apprécie vraiment.La première ligne d’un message de commit est la plus importante, car elle permet à
git logde gérer la barrière de Chesterton. Dans ce cas, je pense que l’auteur a manqué sa cible.L’essentiel est de mettre dans la première ligne pourquoi on l’a fait, et non ce qu’on a fait. Ce qui a changé, les personnes intéressées peuvent le voir dans le code ou le diff.
Par exemple, on pourrait écrire « nginx .conf files must be in us-ascii », puis « changed blahblah.erb to remove nonbreaking space character », et ensuite poursuivre avec le reste du message de commit, qui est plutôt bon.
Il faut penser comme pour un article de presse : partir du principe que le lecteur peut s’arrêter à n’importe quel moment, et écrire dans un ordre où l’importance diminue tandis que le niveau de détail augmente.
Les articles de presse aussi expliquent dans leur titre le quoi, pas le pourquoi.
Dans ce cas, la première ligne originale est parfaitement adaptée. En parcourant
git log, on peut assez probablement se dire que ce commit n’a pas modifié le comportement fonctionnel des tests et passer à la suite.« nginx .conf files must be in us-ascii » peut être un bon titre de bug ou de PR, mais cela peut aussi correspondre à plusieurs commits faisant des choses différentes, et cela n’indique pas ce qui s’est réellement passé. Est-ce qu’on convertit les fichiers en US-ASCII, est-ce qu’on crée un outil de conversion, est-ce qu’on met à jour la documentation, est-ce qu’on crée des tests, ou une combinaison de tout cela ? Commencer par le quoi plutôt que par le pourquoi réduit cette confusion.
Que ce soit « The files must be in us-ascii » ou « Changed the files to us-ascii », cela ne change pas grand-chose. Les deux indiquent clairement que les fichiers sont passés en US-ASCII.
L’inconvénient de cette manière de documenter, c’est qu’un message de commit déjà écrit ne peut pratiquement plus être modifié.
Bien sûr, c’est possible avec
rebase, mais a-t-on vraiment envie de modifier quelque chose écrit il y a un an et déjà fusionné dansmain, au risque de faire souffrir les branches de fonctionnalités de tout le monde ?Par rapport à une documentation stockée dans des fichiers
.md, un wiki ou Confluence, je peux améliorer ce qu’un collègue a écrit, et un autre collègue peut améliorer ce que j’ai écrit.Dans ce cas précis, le bug a peut-être été corrigé et ne réapparaîtra jamais. Mais quand on commite un composant donné, on est tenté d’en expliquer la conception, et désormais j’évite de le faire. Que se passera-t-il si ce composant change plus tard, par exemple à cause d’une évolution des besoins métier ? La documentation dans les commits finira-t-elle par ne décrire que les différences ? Dans ce cas, pour comprendre le système, un nouveau membre de l’équipe devra lire plusieurs messages de commit et les fusionner mentalement.
Comparer cela à des fichiers
.md, à un wiki ou à Confluence, c’est comme comparer un vélo et une oie.Les considérations et compromis au moment de la création d’un composant, ou le fait qu’il n’y en avait pas, sont souvent utiles pour comprendre les défauts d’origine, les défauts apparus au fil de l’évolution et ceux liés à des changements de cas d’usage.
Si un nouveau membre de l’équipe doit comprendre le système, il suffit de maintenir une documentation « actuelle » séparée. Cette documentation n’a pas besoin d’aborder les compromis d’implémentation ni le fait qu’une ébauche a été rédigée sous contrainte de temps.
Squasher des commits améliore l’historique en tant que récit de l’ajout d’une fonctionnalité ; le merge préserve le journal immuable des changements réels du code ; le rebase fait un peu des deux.
Il n’y a aucune raison de ne pas pouvoir avoir les deux. Nous programmons des ordinateurs, donc nous pouvons construire ce que nous voulons.
Si je réécrivais Git, je diviserais les commits en deux parties. L’historique des changements resterait immuable, dans une structure de type Merkle DAG comme Git, et les messages de commit seraient stockés dans un magasin de données associé séparé, pour former un journal raisonnable et éditable décrivant le flux de travail. On pourrait regrouper les commits autour de tags de fonctionnalités, corriger les fautes de frappe et modifier les messages comme on le souhaite, tout en conservant intact le journal des diffs sous-jacent, c’est-à-dire « ce qui a réellement changé dans le code ».
git notes. Cela dit, si le message est aussi long, il y a peu de chances que quelqu’un le remarque.Il y a un point avec lequel je ne suis pas d’accord : le passage disant « je ne m’attends pas à ce niveau de détail dans tous les commits, surtout dans des commits de cette taille ».
D’expérience, ce sont au contraire souvent les petites choses apparemment anodines qui nécessitent des explications relativement plus longues.
Hier, j’ai écrit trois paragraphes pour expliquer pourquoi j’avais ajouté
--limit=999àgh pr list. Parce que c’est déroutant. Il y a déjà unlimit(dans l’argument--jq, et si l’on suppose qu’il existe un nombre infini de PR au total, plus la valeur est élevée, plus le résultat final est en réalité faible.J’ai aussi écrit un commentaire dans le code, et j’ai probablement passé plus de temps à réfléchir et à le peaufiner qu’à l’écrire. J’aimerais qu’on s’en souvienne comme exemple la prochaine fois que quelqu’un sous-entend que ce travail consiste à produire du code à la chaîne.
Quelque chose comme « This was a non-ascii whitespace character that caused
ArgumentError: invalid byte sequence in US-ASCIIwhen runningbundle exec rake» suffit. Il y a des mots-clés pour rechercher un problème similaire plus tard, la cause racine est incluse, et ce n’est pas si long que les gens risquent de passer à côté.Je ne pense pas que ce soit un excellent commit Git.
Vu toute cette quantité de texte, la première ligne « Convert template to US-ASCII to fix error » pourrait être meilleure. Il suffirait d’ajouter quelques mots sur le caractère d’espacement qui a provoqué l’erreur et sur la nature de cette erreur. Avec cette explication et le diff, le contexte nécessaire serait suffisant.
Honnêtement, le reste est presque inutile. Ce n’est pas nuisible, mais cela n’a pas non plus une grande valeur. L’auteur a documenté son parcours pour traquer ce bug ; qui s’en soucie vraiment ?
L’article contient aussi un lien vers des résultats de recherche montrant plusieurs commits laissés par des personnes qui ont appris quelque chose grâce à ce correctif.
Ce message de commit est une mine de connaissances.
Pour voir d’excellents messages de commit, il suffit de regarder l’historique Git du noyau Linux. Là-bas, c’est la norme.
La première ligne mentionne toujours le sous-système concerné par le changement, puis donne un résumé impératif en une ligne. Ensuite, le message répond aussi précisément que possible à trois questions :
Un exemple pourrait être : « Le code actuel fait X. Lorsqu’on exécute le cas de test T, on observe le comportement inattendu U. Cela est dû à la raison R. On corrige en faisant F. »
Un contributeur récent m’a dit que mon approche des messages Git — autrement dit mes exigences — était « unique ». Mon passé lié au noyau Linux doit transparaître, mais tous mes messages de commit ressemblent à ce qui est décrit ici.
Quand il s’agit du code existant, les commentaires doivent rester avec le code. Les éléments liés au processus, par exemple du code supprimé ou du code qui ne fonctionnait pas, doivent figurer dans le commit.
Le plus important, c’est qu’un message de commit puisse faire référence à une issue par commodité, mais qu’il doive absolument reprendre les détails essentiels. GitHub est éphémère, pas les messages Git.
Voici un message de commit rempli de contexte[1].
Ce genre de situation est si fréquent que le mainteneur a écrit ce billet[2].
[1] https://github.com/git/git/commit/d70f554cdf38b0b05cfaa8e8eb...
[2] https://lore.kernel.org/git/xmqqedevo8ps.fsf@gitster.g/