Publié par
Il y a 5 mois · 9 minutes · Craft

git essentials – 5 – visualiser les changements

Ceci est le cinquième article d’une série consacrée aux commandes de Git, le système de gestion de révisions décentralisé. Le sujet de cet article est la visualisation des changements, au travers de la commande diff.

Retrouvez les précédents articles de la série :

Dans cet article, nous allons voir comment visualiser les modifications dans le code versionné, à différentes étapes dans le flux de travail sous Git, et de différentes manières. Les sujets abordés seront :

  • visualisation des différences entre le code de la dernière version et le code modifié avant commit ;
  • visualisation des différences entre le code de la dernière version et le code indexé (aussi appelé staged ou « en zone pré-commit ») ;
  • visualisation des différences entre deux révisions (commits) ;
  • les options utiles pour faciliter la visualisation.

Différences entre la dernière version et le code modifié avant commit

Vous avez écrit du code et, avant de faire un commit, vous souhaitez jeter un coup d’œil aux changements apportés par vos modifications, c’est-à-dire les différences entre HEAD et l’état du code dans votre copie de travail :

$ git diff

Voici un exemple de sortie, tiré du projet Spark, comme les exemples qui suivront :

Il s’agit du même format de diff que l’on peut trouver dans d’autres outils :

  • les informations entre @@ indiquent la portion de code affectée
  • les lignes préfixées par + sont les lignes de code modifiées, dans leur nouvelle version (ici, votre nouveau code)
  • les lignes préfixées par - sont les lignes de code modifiées, dans leur ancienne version (ici, le code du dernier commit)
  • les lignes sans préfixe sont là pour fournir le contexte

N.B. : une ligne modifiée apparaitra donc deux fois, même si un seul caractère a été modifié. Nous verrons plus loin comment rendre cela plus concis et lisible.

Différences entre la dernière version et le code indexé

Après avoir terminé votre travail, mais avant le commit, vous sélectionnez vos modifications à ajouter au commit avec la commande add. Si vous ajoutez toutes vos modifications avec add et que vous lancez la commande diff de la section précédente, vous obtiendrez une sortie vide. En effet, vos modifications sont à présent enregistrées dans une zone spéciale, appelée index ou staging area.

Si vous souhaitez voir les modifications entre le dernier commit (HEAD) et les modifications enregistrées dans cette zone, il faut utiliser l’option --cached (équivalente de --staged) :

$ git diff --cached

Différences entre deux révisions

Forme de base

Pour visualiser les différences entre deux révisions A et B (où A et B sont des identifiants de commits, des tags ou des branches) la forme de base est la suivante :

$ git diff A B

Différences entre une révision et son parent

Pour les changements apportés par un commit, c’est-à-dire entre un commit et son parent, on peut utiliser la forme abrégée suivante :

$ git diff A^!

qui est équivalent à :

$ git diff A~1 A

ou encore :

$ git diff A^ A

Donc si vous souhaitez visualiser rapidement les changements apportés par le dernier commit sur votre branche (HEAD), ce sera :

$ git diff HEAD^!

Pour plus d’informations à ce sujet, vous pouvez consulter la documentation expliquant les différentes manières de spécifier les intervalles de révisions.

Les options de sortie utiles

Voir un résumé des modifications

Lorsque l’on souhaite visualiser un ensemble de changements, il peut être utile de commencer par avoir une vue d’ensemble : savoir quels fichiers ont été modifiés. La commande diff propose l’option --name-status qui change la sortie normale de la commande pour afficher à la place une liste des fichiers modifiés, avec la nature de la modification :

git diff --name-status

Ce qui donne :

Les lettres en tête de ligne représentent la nature de la modification :

  • M pour modifié
  • A pour ajouté (nouveau fichier)
  • D pour effacé (fichier supprimé)

N.B. : les trois types de modifications mentionnées ci-dessus sont les plus fréquentes, mais il existe d’autres lettres ayant d’autres significations : R (renommé), C (copié), U (non-fusionné, ou unmerged)…

Ignorer les différences d’espacement

L’option -w impose à diff d’ignorer toutes les différences liées à des caractères d’espacement, ce qui est bien utile pour simplifier la lecture quand une partie des modifications est une réindentation ou des ajouts et suppressions de lignes vides :

Exemple de sortie sans cette option :

$ git diff

 

Le même diff, cette fois avec l’option de filtrage des différences d’espacement :

On s’aperçoit ainsi que ces modifications consistaient en fait en une grosse réindentation dans laquelle état noyée une modification plus importante : l’inversion des sorties true et false (je parie que vous l’aviez manquée dans la capture précédente).

Modifier le nombre de lignes de contexte

Par défaut, git diff donne 3 lignes de contexte (si possible) avant et après chaque ligne (ou groupe de lignes proches). Parfois, vous souhaiterez en avoir plus (pour mieux comprendre les modifications apportées) ou moins, par exemple si les lignes environnantes n’apportent aucune information utile à ce moment.

L’option -U permet d’ajuster le nombre de lignes de contexte. Prenons un exemple :

$ git diff

donne :

Le contexte n’apporte pas ici de véritable valeur : dans le premier morceau de code, il s’agit :

  • d’une modification d’imports : pas besoin d’avoir les autres imports en contexte pour comprendre
  • d’un ajout de test : de même, pas besoin d’avoir les autres tests à côté, avec le nom de la classe (en haut), on sait déjà que l’on se trouve dans une classe de tests.

En supprimant toutes les lignes de contexte, on peut rendre le tout plus concis et toujours aussi compréhensible (dans ce cas) :

$ git diff -U0

donne :

Réduire la granularité

Par défaut, git diff affiche la première version de la ligne, suivie de sa deuxième version, et ce même si un seul caractère a été modifié.

Prenons par exemple :

Dans ce cas, seuls quelques éléments on été modifiés sur chaque ligne. Il serait appréciable que cela soit mis en évidence afin de faciliter la lecture. diff propose l’option --color-words, qui permet de mettre en évidence les mots modifiés au sein d’une ligne :

$ git diff --color-words

 

Les modifications sont ainsi plus évidentes.

N.B. : par défaut, le séparateur de mot utilisé est l’espace. Ce ne sera pas adapté pour tous les types de lignes, c’est pourquoi il est possible de définir plus finement la notion de mot.

Définir précisément la granularité

Comme évoqué plus haut, il est possible de définir précisément ce que Git doit considérer comme un mot (une unité sémantique). On peut utiliser pour ce faire une expression régulière :

$ git diff --color-words='[[:alnum:]]+|[^[:space:]]'

Explication :

  • [[:alnum:]]+ correspond à un enchainement de caractères alphanumériques (un mot au sens usuel du terme ou un nom de variable)
  • | est un OU logique
  • [^[:space:]] tout ce qui n’est pas un espace. Est placé en seconde positon après le OU logique pour donner la priorité à la recherche d’un mot, sinon chaque caractère serait considéré comme un mot à part entière.

Comparaison entre la sortie avec le simple --color-words de la section précédente :

et avec le --color-words='[[:alnum:]]+|[^[:space:]]' :

On observe ici que le découpage est plus fin et précis qu’avec --color-words, ce qui facilite la lecture et la compréhension des modifications.

Détecter les déplacements de code plus agressivement

Git est capable de détecter seul les renommages, même si un renommage s’accompagne de modifications dans le même commit. Pour ce faire, il utilise un indice de similarité, qui est la quantité de lignes ajoutées/supprimées par rapport à la taille du fichier. Par défaut, le seuil de similarité est fixé à 50 %, ce qui veut dire qu’au-delà, Git considèrera qu’un fichier a été supprimé et qu’un nouveau a été créé. Dans ce cas, la visualisation des différences ne pourra pas se faire.

Il est toutefois possible de modifier la valeur du seuil de similarité pour détecter plus agressivement les renommages, et ainsi faciliter les comparaisons, grâce à l’option M qui prend en argument une valeur qui est le seuil de similarité.

Exemple sur un listing des fichiers modifiés :

git diff --name-status revision_A revision_B

 

Et avec l’option :

git diff --name-status -M20% revision_A revision_B

 

On note qu’avec l’option, on a pu détecter le renommage, avec modifications conséquentes, du fichier BlockStatusListener.scala en LiveEntitySuite.scala. La visualisation du diff sera donc possible sur ce fichier, là où sans l’option, git diff aurait présenté le contenu comme entièrement supprimé dans sa première version, puis entièrement ajouté dans sa seconde version.

Par souci de concision, je vous montre le résultat sur un résumé de la liste des fichiers modifiés (--name-status). Mais cela implique aussi que si vous visualisez les différences de code avec cette option, que ce soit en ligne de commande avec diff ou avec un visualiseur de différences graphique, les anciennes/nouvelles versions vous seront présentées à côté, et non pas comme deux fichiers différents, l’un entièrement supprimé, l’autre entièrement nouveau.

N.B. : Cette option est également proposée sur la commande log.

Conclusion

Nous avons vu comment visualiser rapidement en ligne de commande les changements apportés dans le code. Cela reste un outil pertinent en conjonction avec les outils graphiques, car cela permet :

  • une visualisation rapide des changements depuis n’importe quelle machine (y compris à distance en SSH),
  • un ajustement fin des options de visualisation pour ne conserver que l’information utile, et ainsi mieux repérer les changements-clés, voire des anomalies.

Certaines options sont difficiles à retenir ou retaper, donc n’hésitez pas à vous faire des alias !

Bastien Bonnet
Bastien est un développeur disposant de 4 ans d'expérience. Il est passionné par le développement de logiciel de qualité (code clair, facile à maintenir, robuste face aux régression (tests automatisés)). Agiliste convaincu, il s'inscrit parfaitement dans le mouvement du software craftsmanship. Il est convaincu et investi dans le partage de connaissance pour améliorer le niveau technique et les compétences de son équipe.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *