• Contenu
  • Bas de page
logo ouidoulogo ouidoulogo ouidoulogo ouidou
  • Qui sommes-nous ?
  • Offres
    • 💻 Applications métier
    • 🤝 Collaboration des équipes
    • 🛡️ Sécurisation et optimisation du système d’information
    • 🔗 Transformation numérique
  • Expertises
    • 🖥️ Développement logiciel
    • ♾️ DevSecOps
    • ⚙️ Intégration de logiciels et négoce de licences
      • Atlassian : Jira, Confluence, Bitbucket…
      • Plateforme monday.com
      • GitLab
      • SonarQube
    • 📚​ Logiciel de CRM et de gestion
    • 🎨 UX/UI design
    • 🌐 Accessibilité Numérique
    • 🗂️​ Démarches simplifiées
    • 📝 Formations Atlassian
  • Références
  • Carrières
    • 🧐 Pourquoi rejoindre Ouidou ?
    • ✍🏻 Nous rejoindre
    • 👨‍💻 Rencontrer nos collaborateurs
    • 🚀 Grandir chez Ouidou
  • RSE
  • Ressources
    • 🗞️ Actualités
    • 🔍 Articles techniques
    • 📖 Livres blancs
    • 🎙️ Interviews Clients
Nous contacter
✕
Rails 6.1 et ses nouveautés notables
Rails 6.1 et ses nouveautés notables
12 février 2021
Gardons le cap avec Kubernetes
Gardons le cap avec Kubernetes
25 février 2021
Ressources > Articles techniques > Un meilleur rendu de Git diff pour Ruby

Un meilleur rendu de Git diff pour Ruby

Article écrit par François Vantomme

Si vous utilisez Git en ligne de commande, vous connaissez bien la sortie diff qui se décompose en morceaux (hunks) comme ci-dessous :

@@ -231,7 +231,7 @@ class TimeCalc        case        when from.class == Date          [coerce_date(from, to), to] -      when to.class == Date +      when to.instance_of?(Date)          [from, coerce_date(to, from)]        else          [from, to.public_send("to_#{from.class.downcase}")] 

La première ligne (commençant par @@) est connue sous le nom de « hunk header » et est là pour nous aider à comprendre où a eu lieu le changement. Elle nous donne les numéros de ligne pour le changement (les numéros entre les paires d’arobases), mais aussi une description textuelle du contexte dans lequel le changement s’est produit, dans cet exemple class TimeCalc. Git essaie de comprendre ce contexte, qu’il s’agisse d’une fonction, d’un module ou d’une définition de classe. Pour les langages de type C, il est assez bon à ce niveau. Mais pour l’exemple Ruby ci-dessus, il n’a pas réussi à nous montrer le contexte immédiat, qui est en fait une méthode appelée coerce_classes. C’est parce que Git n’est pas capable de reconnaître la syntaxe Ruby pour une définition de méthode, qui ici serait def coerce_classes.

Ainsi, ce que nous voulons vraiment voir, c’est :

@@ -270,7 +270,7 @@ def coerce_classes(from, to)        case        when from.class == Date          [coerce_date(from, to), to] -      when to.class == Date +      when to.instance_of?(Date)          [from, coerce_date(to, from)]        else          [from, to.public_send("to_#{from.class.downcase}")] 

Et il n’y a pas qu’avec Ruby où Git a du mal à trouver le bon contexte de clôture. De nombreux autres langages de programmation et formats de fichiers sont également perdants en ce qui concerne le contexte de l’en-tête.

Heureusement, il est non seulement possible de configurer une expression rationnelle personnalisée spécifique à votre langage pour aider Git à mieux s’orienter, mais il existe même un ensemble de modèles prédéfinis pour de nombreux langages et formats dans Git. Tout ce que nous avons à faire est de dire à Git quels modèles utiliser pour nos extensions de fichiers.

Nous pouvons le faire en définissant un fichier .gitattributes à l’intérieur de notre dépôt. Dans ce fichier, on associera les extensions de fichiers Ruby au modèle de diff prédéfini pour Ruby :

*.gemspec diff=ruby *.rake    diff=ruby *.rb      diff=ruby 

Certains projets open source définissent leur propre fichier .gitattributes. Il y en a un dans Rails. Il y en a même un dans les sources Git qui défini les modèles de diff pour Perl et Python.

Configurer un fichier .gitattributes global

Au lieu d’ajouter un fichier .gitattributes à chaque dépôt, nous pouvons en configurer global. Il suffit de créer ce fichier dans votre répertoire personnel, de le remplir avec tous les formats de fichiers qui vous intéressent et de le déclarer dans votre configuration globale de Git :

❯ git config --global core.attributesfile ~/.gitattributes 

On pourrait se demander pourquoi, s’il existe des modèles de diff prédéfinis, doit-on les déclarer nous-même ? Ça semble être une étape superflue dont on se passerait bien !

Oui mais voilà, tout n’est pas si simple ! Si vous avez dans votre dépôt un fichier *.c, faut-il lui appliquer le modèle de diff du langage C ou de C++ ? Si c’est un fichier *.m, est-ce de l’Objective-C ou du Matlab ? Ah, vous voyez, vous aussi vous êtes perdu ! Eh bien c’est parce qu’on ne peut pas se fier à la seule extension d’un fichier pour définir le modèle à appliquer, que les développeurs de Git ont décidé de ne pas arbitrer à notre place. Et si vous voulez revivre cette intense discussion, vous la retrouverez sur ce fil.

Modèle de diff pour Rspec

Comme annoncé plus tôt, il est aussi possible de définir un modèle de diff personnalisé. Cela peut être utile pour Rspec par exemple, de manière à prendre en considération son propre DSL.

Cela se fait en deux étapes. Premièrement, nous allons ajouter une ligne spécifique à nos fichiers de tests Rspec dans le fichier .gitattributes :

*.rb diff=ruby *._spec.rb diff=rspec 

Ensuite, il nous faut définir ce nouveau modèle dans $GIT_DIR/config au sein de notre projet ou dans $HOME/.gitconifg :

[diff "rspec"]   xfuncname = "^[ t]*((RSpec|describe|context|it|before|after|around|feature|scenario)[ t].*)$" 

On utilise dans l’exemple ci-dessus l’instruction xfuncname introduite plus récemment que funcname et qui, contrairement à son ainée, supporte les expressions rationnelles étendues ; ce qui n’est pas pour nous déplaire tant la syntaxe résultante s’en trouve épurée ! Et si vous n’êtes pas à l’aise avec les regex, on a quelques articles sur le sujet 😉

Un contexte étendu

Ainsi configuré, une option méconnue de git diff dévoile alors tout son potentiel ; il s’agit de -W ou --function-context. C’est une option similaire à celle de git grep et qui élargit le contexte des hunks de manière à montrer toute la fonction environnante. Ce contexte que l’on qualifierait de « naturel » peut permettre de mieux comprendre les changements effectués.

Si on reprend notre exemple, ça donnerait ceci :

❯ git diff -W 
@@ -269,10 +269,10 @@ def zone(tm)      def coerce_classes(from, to)        case        when from.class == Date # not is_a?(Date), it will catch DateTime          [coerce_date(from, to), to] -      when to.class == Date +      when to.instance_of?(Date)          [from, coerce_date(to, from)]        else          [from, to.public_send("to_#{from.class.downcase}")].then(&method(:coerce_zones))        end      end 

Les plus attentifs d’entre vous auront repéré un petit couac…

À l’épreuve des balles ?

Pas tout à fait… il peut arriver qu’un faux se glisse dans les résultats. C’est notamment le cas, si l’on veut prendre un exemple, de cet extrait :

❯ git diff 
@@ -268,9 +268,9 @@ def zone(tm)       def coerce_classes(from, to)        case -      when from.class == Date # not is_a?(Date), it will catch DateTime +      when from.instance_of?(Date)          [coerce_date(from, to), to] -      when to.class == Date +      when to.instance_of?(Date)          [from, coerce_date(to, from)]        else          [from, to.public_send("to_#{from.class.downcase}")] 

On se serait attendu à voir affiché def coerce_classes(from, to) ou class Diff plutôt que def zone(tm), puisque notre changement n’a pas lieu au sein de cette méthode. On peut néanmoins obtenir le résultat escompté en jouant sur le nombre de lignes présentées autour des lignes modifiées grâce à l’option -U ou --unified.

❯ git diff -U1 
@@ -270,5 +270,5 @@ def coerce_classes(from, to)        case -      when from.class == Date # not is_a?(Date), it will catch DateTime +      when from.instance_of?(Date)          [coerce_date(from, to), to] -      when to.class == Date +      when to.instance_of?(Date)          [from, coerce_date(to, from)] 

On comprend alors que Git recherche le contexte autour du hunk et non pas strictement autour des lignes modifiées. Malgré cela l’utilisation de modèles de diff reste néanmoins utile et pertinente dans la grande majorité des cas !

Ressources

  • Git attributes (en)
  • Better Git diff output for Ruby, Python, Elixir, Go and more (en)
  • Git attributes examples (en)
  • Where does the excerpt in the git diff hunk header come from ? (en)

À lire aussi

Fresque numérique miniature image
16 avril 2025

Fresque du Numérique

Lire la suite

intelligence artificielle Ouicommit miniature image
17 mars 2025

Ouicommit – L’intelligence artificielle en entreprise, on y est ! 

Lire la suite

Image miniature Hackathon Women in Tech
13 mars 2025

Hackathon Women in Tech :  un engagement pour une tech plus inclusive 

Lire la suite

image miniature les nouveautés Atlassian
26 février 2025

Les nouveautés Atlassian en 2025

Lire la suite

Articles associés

Fresque numérique miniature image
16 avril 2025

Fresque du Numérique


Lire la suite
intelligence artificielle Ouicommit miniature image
17 mars 2025

Ouicommit – L’intelligence artificielle en entreprise, on y est ! 


Lire la suite
Image miniature Hackathon Women in Tech
13 mars 2025

Hackathon Women in Tech :  un engagement pour une tech plus inclusive 


Lire la suite

À propos

  • Qui sommes-nous ?
  • Références
  • RSE
  • Ressources

Offres

  • Applications métier
  • Collaboration des équipes
  • Sécurisation et optimisation du système d’information
  • Transformation numérique

Expertises

  • Développement logiciel
  • DevSecOps
  • Intégration de logiciels et négoce de licences
  • Logiciel de CRM et de gestion
  • UX/UI design
  • Accessibilité Numérique
  • Démarches simplifiées
  • Formations Atlassian

Carrières

  • Pourquoi rejoindre Ouidou ?
  • Nous rejoindre
  • Rencontrer nos collaborateurs
  • Grandir chez Ouidou

SIEGE SOCIAL
70-74 boulevard Garibaldi, 75015 Paris

Ouidou Nord
165 Avenue de Bretagne, 59000 Lille

Ouidou Rhône-Alpes
4 place Amédée Bonnet, 69002 Lyon

Ouidou Grand-Ouest
2 rue Crucy, 44000 Nantes

Ouidou Grand-Est
7 cour des Cigarières, 67000 Strasbourg

  • Linkedin Ouidou
  • GitHub Ouidou
  • Youtube Ouidou
© 2024 Ouidou | Tous droits réservés | Plan du site | Mentions légales | Déclaration d'accessibilité
    Nous contacter