Git 🌙
Chapters â–Ÿ 2nd Edition

8.2 Personnalisation de Git - Attributs Git

Attributs Git

Certains de ces rĂ©glages peuvent aussi s’appliquer sur un chemin, de telle sorte que Git ne les applique que sur un sous-rĂ©pertoire ou un sous-ensemble de fichiers. Ces rĂ©glages par chemin sont appelĂ©s attributs Git et sont dĂ©finis soit dans un fichier .gitattributes dans un rĂ©pertoire (normalement la racine du projet), soit dans un fichier .git/info/attributes si vous ne souhaitez pas que le fichier de description des attributs fasse partie du projet.

Les attributs permettent de spĂ©cifier des stratĂ©gies de fusion diffĂ©rentes pour certains fichiers ou rĂ©pertoires dans votre projet, d’indiquer Ă  Git la maniĂšre de calculer les diffĂ©rences pour certains fichiers non-texte, ou de faire filtrer Ă  Git le contenu avant qu’il ne soit validĂ© ou extrait. Dans ce chapitre, nous traiterons certains attributs applicables aux chemins et dĂ©taillerons quelques exemples de leur utilisation en pratique.

Fichiers binaires

Les attributs Git permettent des trucs cool comme d’indiquer Ă  Git quels fichiers sont binaires (dans les cas oĂč il ne pourrait pas le deviner par lui-mĂȘme) et de lui donner les instructions spĂ©cifiques pour les traiter. Par exemple, certains fichiers peuvent ĂȘtre gĂ©nĂ©rĂ©s par machine et impossible Ă  traiter par diff, tandis que pour certains autres fichiers binaires, les diffĂ©rences peuvent ĂȘtre calculĂ©es. Nous dĂ©taillerons comment indiquer Ă  Git l’un et l’autre.

Identification des fichiers binaires

Certains fichiers ressemblent Ă  des fichiers texte mais doivent en tout Ă©tat de cause ĂȘtre traitĂ©s comme des fichiers binaires. Par exemple, les projets Xcode sous macOS contiennent un fichier finissant en .pbxproj, qui est en fait un jeu de donnĂ©es JSON (format de donnĂ©es en texte JavaScript) enregistrĂ© par l’application EDI pour y sauver les rĂ©glages entre autres de compilation. Bien que ce soit techniquement un fichier texte en ASCII, il n’y a aucun intĂ©rĂȘt Ă  le gĂ©rer comme tel parce que c’est en fait une mini base de donnĂ©es. Il est impossible de fusionner les contenus si deux utilisateurs le modifient et les calculs de diffĂ©rence par dĂ©faut sont inutiles. Ce fichier n’est destinĂ© qu’à ĂȘtre manipulĂ© par un programme. En rĂ©sumĂ©, ce fichier doit ĂȘtre considĂ©rĂ© comme un fichier binaire opaque.

Pour indiquer à Git de traiter tous les fichiers pbxproj comme binaires, ajoutez la ligne suivante à votre fichier .gitattributes :

*.pbxproj binary

À prĂ©sent, Git n’essaiera pas de convertir ou de corriger les problĂšmes des CRLF, ni de calculer ou d’afficher les diffĂ©rences pour ces fichiers quand vous lancez git show ou git diff sur votre projet.

Comparaison de fichiers binaires

Dans Git, vous pouvez utiliser la fonctionnalitĂ© des attributs pour comparer efficacement les fichiers binaires. Pour ce faire, indiquez Ă  Git comment convertir vos donnĂ©es binaires en format texte qui peut ĂȘtre comparĂ© via un diff normal.

Comme c’est une fonctionnalitĂ© vraiment utile et peu connue, nous allons dĂ©tailler certains exemples. PremiĂšrement, nous utiliserons cette technique pour rĂ©soudre un des problĂšmes les plus ennuyeux de l’humanité : gĂ©rer en contrĂŽle de version les documents Word. Tout le monde convient que Word est l’éditeur de texte le plus horrible qui existe, mais bizarrement, tout le monde persiste Ă  l’utiliser. Si vous voulez gĂ©rer en version des documents Word, vous pouvez les coller dans un dĂ©pĂŽt Git et les valider de temps Ă  autre. Mais qu’est-ce que ça vous apporte ? Si vous lancez git diff normalement, vous verrez quelque chose comme :

$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 88839c4..4afcb7c 100644
Binary files a/chapter1.docx and b/chapter1.docx differ

Vous ne pouvez pas comparer directement les versions Ă  moins de les extraire et de les parcourir manuellement. En fait, vous pouvez faire la mĂȘme chose plutĂŽt bien en utilisant les attributs Git. Ajoutez la ligne suivante dans votre fichier .gitattributes :

*.docx diff=word

Cette ligne indique Ă  Git que tout fichier correspondant au patron (.docx) doit utiliser le filtre word pour visualiser le diff des modifications. Qu’est-ce que le filtre « word » ? Nous devons le dĂ©finir. Vous allez indiquer Ă  Git d’utiliser le programme docx2txt qui a Ă©tĂ© Ă©crit spĂ©cifiquement pour extraire le texte d’un document MS Word, qu’il pourra comparer correctement.

Installez dĂ©jĂ  docx2text. Vous pouvez le tĂ©lĂ©charger depuis https://docx2txt.sourceforge.net. Suivez les instruction dans le fichier INSTALL pour le placer Ă  un endroit oĂč votre shell peut le trouver. Ensuite, Ă©crivons un script qui convertit la sortie dans le format que Git comprend. CrĂ©ez un fichier dans votre chemin d’exĂ©cution appelĂ© docx2txt et ajoutez ce contenu :

#!/bin/bash
docx2txt.pl $1 -

N’oubliez pas de faire un chmod a+x sur ce fichier. Finalement, vous pouvez configurer Git pour qu’il utilise ce script :

$ git config diff.word.textconv docx2txt

À prĂ©sent, Git sait que s’il essaie de faire un diff entre deux instantanĂ©s et qu’un des fichiers finit en .docx, il devrait faire passer ces fichiers par le filtre word dĂ©fini comme le programme docx2txt. Cette mĂ©thode fait effectivement des jolies versions texte de vos fichiers Word avant d’essayer de les comparer.

Voici un exemple. J’ai mis le chapitre 1 de ce livre dans Git, ajoutĂ© du texte Ă  un paragraphe et sauvegardĂ© le document. Puis, j’ai lancĂ© git diff pour visualiser ce qui a changé :

$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 0b013ca..ba25db5 100644
--- a/chapter1.docx
+++ b/chapter1.docx
@@ -2,6 +2,7 @@
 This chapter will be about getting started with Git. We will begin at the beginning by explaining some background on version control tools, then move on to how to get Git running on your system and finally how to get it setup to start working with. At the end of this chapter you should understand why Git is around, why you should use it and you should be all setup to do so.
 1.1. About Version Control
 What is "version control", and why should you care? Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. For the examples in this book you will use software source code as the files being version controlled, though in reality you can do this with nearly any type of file on a computer.
+Testing: 1, 2, 3.
 If you are a graphic or web designer and want to keep every version of an image or layout (which you would most certainly want to), a Version Control System (VCS) is a very wise thing to use. It allows you to revert files back to a previous state, revert the entire project back to a previous state, compare changes over time, see who last modified something that might be causing a problem, who introduced an issue and when, and more. Using a VCS also generally means that if you screw things up or lose files, you can easily recover. In addition, you get all this for very little overhead.
 1.1.1. Local Version Control Systems
 Many people's version-control method of choice is to copy files into another directory (perhaps a time-stamped directory, if they're clever). This approach is very common because it is so simple, but it is also incredibly error prone. It is easy to forget which directory you're in and accidentally write to the wrong file or copy over files you don't mean to.

Git m’indique succinctement que j’ai ajoutĂ© la chaĂźne « Testing: 1, 2, 3. », ce qui est correct. Ce n’est pas parfait – les modifications de formatage n’apparaissent pas – mais c’est efficace.

Un autre problĂšme intĂ©ressant concerne la comparaison de fichiers d’images. Une mĂ©thode consiste Ă  faire passer les fichiers image Ă  travers un filtre qui extrait les donnĂ©es EXIF, les mĂ©ta-donnĂ©es enregistrĂ©es avec la plupart des formats d’image. Si vous tĂ©lĂ©chargez et installez le programme exiftool, vous pouvez l’utiliser pour convertir vos images en texte de mĂ©ta-donnĂ©es de maniĂšre que le diff puisse au moins montrer une reprĂ©sentation textuelle des modifications pratiquĂ©es. Mettez la ligne suivante dans votre fichier .gitattributes :

*.png diff=exif

Configurez Git pour utiliser cet outil :

$ git config diff.exif.textconv exiftool

Si vous remplacez une image dans votre projet et lancez git diff, vous verrez ceci :

diff --git a/image.png b/image.png
index 88839c4..4afcb7c 100644
--- a/image.png
+++ b/image.png
@@ -1,12 +1,12 @@
 ExifTool Version Number         : 7.74
-File Size                       : 70 kB
-File Modification Date/Time     : 2009:04:21 07:02:45-07:00
+File Size                       : 94 kB
+File Modification Date/Time     : 2009:04:21 07:02:43-07:00
 File Type                       : PNG
 MIME Type                       : image/png
-Image Width                     : 1058
-Image Height                    : 889
+Image Width                     : 1056
+Image Height                    : 827
 Bit Depth                       : 8
 Color Type                      : RGB with Alpha

Vous pouvez réaliser rapidement que la taille du fichier et les dimensions des images ont changé.

Expansion des mots-clés

L’expansion de mots-clĂ©s dans le style de CVS ou de SVN est souvent une fonctionnalitĂ© demandĂ©e par les dĂ©veloppeurs qui y sont habituĂ©s. Le problĂšme principal de ce systĂšme avec Git est que vous ne pouvez pas modifier un fichier avec l’information concernant le commit aprĂšs la validation parce que Git calcule justement la somme de contrĂŽle sur son contenu. Cependant, vous pouvez injecter des informations textuelles dans un fichier au moment oĂč il est extrait et les retirer avant qu’il ne soit ajoutĂ© Ă  un commit. Les attributs Git vous fournissent deux maniĂšres de le faire.

PremiĂšrement, vous pouvez injecter automatiquement la somme de contrĂŽle SHA-1 d’un blob dans un champ $Id$ d’un fichier. Si vous positionnez cet attribut pour un fichier ou un ensemble de fichiers, la prochaine fois que vous extrairez cette branche, Git remplacera chaque champ avec le SHA-1 du blob. Il est Ă  noter que ce n’est pas le SHA du commit mais celui du blob lui-mĂȘme. Mettez la ligne suivante dans votre fichier .gitattributes :

*.txt ident

Ajoutez une référence $Id$ à un fichier de test :

$ echo '$Id$' > test.txt

À la prochaine extraction de ce fichier, Git injecte le SHA du blob :

$ rm test.txt
$ git checkout -- test.txt
$ cat test.txt
$Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $

NĂ©anmoins, ce rĂ©sultat n’a que peu d’intĂ©rĂȘt. Si vous avez utilisĂ© la substitution avec CVS ou Subversion, il est possible d’inclure la date. Le code SHA n’est pas des plus utiles car il ressemble Ă  une valeur alĂ©atoire et ne vous permet pas de distinguer si tel SHA est plus rĂ©cent ou plus ancien que tel autre.

Il apparaĂźt que vous pouvez Ă©crire vos propres filtres pour rĂ©aliser des substitutions dans les fichiers lors des validations/extractions. Ces filtres s’appellent « clean » et « smudge ». Dans le fichier .gitattributes, vous pouvez indiquer un filtre pour des chemins particuliers puis crĂ©er des scripts qui traiteront ces fichiers avant qu’ils soient extraits (« smudge », voir Le filtre « smudge » est lancĂ© lors d’une extraction.) et juste avant qu’ils soient validĂ©s (« clean », voir Le filtre « clean » est lancĂ© lorsque les fichiers sont indexĂ©s.). Ces filtres peuvent servir Ă  faire toutes sortes de choses attrayantes.

Le filtre « smudge » est lancĂ© lors d’une extraction.
Figure 143. Le filtre « smudge » est lancĂ© lors d’une extraction.
Le filtre « clean » est lancé lorsque les fichiers sont indexés.
Figure 144. Le filtre « clean » est lancé lorsque les fichiers sont indexés.

Le message de validation d’origine pour cette fonctionnalitĂ© donne un exemple simple permettant de passer tout votre code C par le programme indent avant de valider. Vous pouvez le faire en rĂ©glant l’attribut filter dans votre fichier .gitattributes pour filtrer les fichiers *.c avec le filtre « indent » :

*.c filter=indent

Ensuite, indiquez à Git ce que le filtre « indent » fait sur smudge et clean :

$ git config --global filter.indent.clean indent
$ git config --global filter.indent.smudge cat

Dans ce cas, quand vous validez des fichiers qui correspondent Ă  *.c, Git les fera passer par le programme indent avant de les valider et les fera passer par le programme cat avant de les extraire sur votre disque. Le programme cat ne fait rien : il se contente de rĂ©gurgiter les donnĂ©es telles qu’il les a lues. Cette combinaison filtre effectivement tous les fichiers de code source C par indent avant leur validation.

Un autre exemple intĂ©ressant fournit l’expansion du mot-clĂ© $Date$ dans le style RCS. Pour le rĂ©aliser correctement, vous avez besoin d’un petit script qui prend un nom de fichier, calcule la date de la derniĂšre validation pour le projet et l’insĂšre dans le fichier. Voici un petit script Ruby qui le fait :

#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')

Tout ce que le script fait, c’est rĂ©cupĂ©rer la date de la derniĂšre validation Ă  partir de la commande git log, la coller dans toutes les chaĂźnes $Date$ qu’il trouve et rĂ©Ă©crire le rĂ©sultat. Ce devrait ĂȘtre simple dans n’importe quel langage avec lequel vous ĂȘtes Ă  l’aise. Appelez ce fichier expand_date et placez-le dans votre chemin. À prĂ©sent, il faut paramĂ©trer un filtre dans Git (appelons le dater) et lui indiquer d’utiliser le filtre expand_date en tant que smudge sur les fichiers Ă  extraire. Nous utiliserons une expression Perl pour nettoyer lors d’une validation :

$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'

Cette commande Perl extrait tout ce qu’elle trouve dans une chaĂźne $Date$ et la rĂ©initialise. Le filtre prĂȘt, on peut le tester en Ă©crivant le mot-clĂ© $Date$ dans un fichier, puis en crĂ©ant un attribut Git pour ce fichier qui fait rĂ©fĂ©rence au nouveau filtre et en crĂ©ant un fichier avec votre mot-clĂ© $Date$ :

date*.txt filter=dater
$ echo '# $Date$' > date_test.txt

Si vous validez ces modifications et extrayez le fichier à nouveau, vous remarquez le mot-clé correctement substitué :

$ git add date_test.txt .gitattributes
$ git commit -m "Testing date expansion in Git"
$ rm date_test.txt
$ git checkout date_test.txt
$ cat date_test.txt
# $Date: Tue Apr 21 07:26:52 2009 -0700$

Vous pouvez voir Ă  quel point cette technique peut ĂȘtre puissante pour des applications personnalisĂ©es. Il faut rester nĂ©anmoins vigilant car le fichier .gitattributes est validĂ© et inclus dans le projet tandis que le gestionnaire (ici, dater) ne l’est pas. Du coup, ça ne marchera pas partout. Lorsque vous crĂ©ez ces filtres, ils devraient pouvoir avoir un mode dĂ©gradĂ© qui n’empĂȘche pas le projet de fonctionner.

Export d’un dĂ©pĂŽt

Les donnĂ©es d’attribut Git permettent aussi de faire des choses intĂ©ressantes quand vous exportez une archive du projet.

export-ignore

Vous pouvez dire Ă  Git de ne pas exporter certains fichiers ou rĂ©pertoires lors de la gĂ©nĂ©ration d’archive. S’il y a un sous-rĂ©pertoire ou un fichier que vous ne souhaitez pas inclure dans le fichier archive mais que vous souhaitez extraire dans votre projet, vous pouvez indiquer ce fichier via l’attribut export-ignore.

Par exemple, disons que vous avez des fichiers de test dans le sous-rĂ©pertoire test/ et que ce n’est pas raisonnable de les inclure dans l’archive d’export de votre projet. Vous pouvez ajouter la ligne suivante dans votre fichier d’attribut Git :

test/ export-ignore

À prĂ©sent, quand vous lancez git archive pour crĂ©er une archive tar de votre projet, ce rĂ©pertoire ne sera plus inclus dans l’archive.

export-subst

Quand vous exportez des fichiers pour un dĂ©ploiement, vous pouvez appliquer le formatage de git log et l’expansion de mot-clĂ©s Ă  des portions choisies de fichiers marquĂ©es avec l’attribut export-subst.

Par exemple, si vous voulez inclure un fichier appelé LAST_COMMIT dans votre projet et y injecter automatiquement la date de derniÚre validation lorsque git archive est lancé, vous pouvez définir vos fichiers .gitattributes et LAST_COMMIT comme ceci :

LAST_COMMIT export-subs
$ echo 'Last commit date: $Format:%cd by %aN$' > LAST_COMMIT
$ git add LAST_COMMIT .gitattributes
$ git commit -am 'adding LAST_COMMIT file for archives'

Quand vous lancez git archive, le contenu de ce fichier inclus dans l’archive ressemblera à ceci :

$ git archive HEAD | tar xCf ../test-deploiement -
$ cat ../test-deploiement/LAST_COMMIT
Last commit date: Tue Apr 21 08:38:48 2009 -0700 by Scott Chacon

Les substitutions peuvent inclure par exemple le message de validation et n’importe quelle note git, et git log peut faire du simple retour à la ligne :

$ echo '$Format:Last commit: %h by %aN at %cd%n%+w(76,6,9)%B$' > LAST_COMMIT
$ git commit -am 'export-subst uses git log'\''s custom formatter

git archive uses git log'\''s `pretty=format:` processor
directly, and strips the surrounding `$Format:` and `$`
markup from the output.
'
$ git archive @ | tar xfO - LAST_COMMIT
Last commit: 312ccc8 by Jim Hill at Fri May 8 09:14:04 2015 -0700
       export-subst uses git log's custom formatter

         git archive uses git log's `pretty=format:` processor directly, and
         strips the surrounding `$Format:` and `$` markup from the output.

L’archive rĂ©sultante est appropriĂ©e pour le travail de dĂ©ploiement, mais comme n’importe quelle archive exportĂ©e, elle n’est pas appropriĂ©e pour continuer un travail de dĂ©veloppement.

Stratégies de fusion

Vous pouvez aussi utiliser les attributs Git pour indiquer Ă  Git d’utiliser des stratĂ©gies de fusion diffĂ©renciĂ©es pour des fichiers spĂ©cifiques dans votre projet. Une option trĂšs utile est d’indiquer Ă  Git de ne pas essayer de fusionner des fichiers spĂ©cifiques quand ils rencontrent des conflits mais plutĂŽt d’utiliser prioritairement votre version du fichier.

C’est trĂšs utile si une branche de votre projet a divergĂ© ou s’est spĂ©cialisĂ©e, mais que vous souhaitez pouvoir fusionner les modifications qu’elle porte et vous voulez ignorer certains fichiers. Supposons que vous avez un fichier de paramĂštres de base de donnĂ©es appelĂ© database.xml diffĂ©rent sur deux branches et vous voulez les fusionner dans votre autre branche sans corrompre le fichier de base de donnĂ©es. Vous pouvez dĂ©clarer un attribut comme ceci :

database.xml merge=ours

Et dĂ©finir une bĂȘte stratĂ©gie de fusion ours avec :

$ git config --global merge.ours.driver true

Si vous fusionnez dans une autre branche, plutÎt que de rencontrer des conflits de fusion avec le fichier database.xml, vous verrez quelque chose comme :

$ git merge topic
Auto-merging database.xml
Merge made by recursive.

Dans ce cas, database.xml reste dans l’état d’origine, quoi qu’il arrive.

scroll-to-top