Utiliser diff et patch

De Le Wiki du Forum-Debian.fr
Aller à la navigation Aller à la recherche

Qui n'a jamais eu besoin de trouver, ou bien de mémoriser uniquement les modifications apportées à un fichier pour pouvoir ensuite les réappliquer à volonté ?

Heureusement, Linux dispose comme très souvent d'outils tous prêts pour régler ce souci : diff et patch

Bien que ces outils soient le plus souvent associés à la programmation (pour gérer les fameux « patches » sur le code source), avec un tout petit peu d'imagination ils peuvent également être mis à profit pour simplifier l'utilisation quotidienne de votre machine.

Un exemple serait de conserver des modifications apportées à un fichier de configuration (diff), pour pouvoir les réappliquer à la nouvelle version de ce fichier après une mise à jour (patch).
Ou tout simplement, comparer deux fichiers (diff uniquement).

Installer les paquets nécessaires

Les deux paquets à installer sont diffutils et patch :

# aptitude install diffutils patch

Comparer deux fichiers avec diff

La commande diff, comme son nom l'indique, permet d'afficher les différences entre deux fichiers semblables (généralement entre deux versions d'un même fichier).

Comme exemple, nous prendrons les deux fichiers suivants :

$ cat ancien-fichier
ceci
n'est
qu'un
exemple
sans
grand
intérêt
$ cat nouveau-fichier
ceci
est
un
exemple
ayant
un
grand
intérêt

Et la comparaison (diff) des deux, qui permet de visualiser très rapidement et de manière extrêmement claire les différences :

$ diff -u ancien-fichier nouveau-fichier
--- ancien-fichier      2010-04-27 23:53:41.948385552 +0200
+++ nouveau-fichier     2010-04-27 23:54:12.552109974 +0200
@@ -1,7 +1,8 @@
 ceci
-n'est
-qu'un
+est
+un
 exemple
-sans
+ayant
+un
 grand
 intérêt

L'option -u permet d'obtenir ce qu'on appelle un diff unifié, le format le plus couramment utilisé.

Ce qui saute immédiatement aux yeux, ce sont les signes + et - (ou leur absence) en début de lignes.

Votre intuition ne vous trompe pas : les signes - marquent les lignes ayant été supprimées lors du passage de ancien-fichier à nouveau-fichier, et les signes + les lignes ayant été rajoutées. Les lignes commençant par un espace n'ont tout simplement pas été modifiées, et sont incluses pour fournir un minimum de contexte aussi bien aux humains qui liraient ce résultat, qu'à la commande patch chargée d'appliquer automatiquement ces modifications à un troisième fichier, même s'il est légèrement différent des deux autres (dans une certaine limite, bien sûr).

Deuxième point remarquable : les lignes commençant par @@ indiquent les numéros de lignes correspondant au bloc de modifications qui se trouve à la suite, pour aider la commande patch à s'y retrouver.
Leur format n'a pas grande importance pour un être humain, tout ce qu'il y a à savoir c'est que le premier chiffre (dans cet exemple, -1) identifie dans ancien-fichier la ligne précédant immédiatement la première ligne de contexte (ici, « ceci » qui se trouve être la première ligne du fichier, c'est à dire la ligne n° 0).

Dernière chose, un diff unifié commence toujours par les deux lignes --- et +++ qui indiquent le nom et l'heure de modification des deux fichiers comparés.

Sauvegarder des modifications pour usage ultérieur (créer un « patch »)

La commande diff affiche par défaut son résultat dans la console.
Si l'on veut sauvegarder ce résultat sur le disque dur, il suffit donc de rediriger la sortie vers un fichier :

$ diff -u ancien-fichier nouveau-fichier > fichier.diff

Appliquer des modifications (un « patch ») à un fichier

À quoi sert de sauvegarder un jeu de modifications si on ne le réutilise pas après ?…

$ patch -i fichier.diff troisieme-fichier

ou bien, en utilisant les redirections du shell (exemple simplifié avec cat, uniquement pour montrer la syntaxe patch correspondante) :

$ cat fichier.diff | patch troisieme-fichier

Ceci va tout simplement appliquer les modifications contenues dans fichier.diff au fichier troisieme-fichier.
Si, comme on est en droit de s'y attendre dans le monde réel, troisieme-fichier a subi des modifications par rapport à ancien-fichier, la commande patch dispose d'un algorithme pour résoudre, dans la mesure du possible, les conflits entre les différentes versions.

Si par malheur patch n'arrive pas à réconcilier les différentes versions, il :

  • vous en préviendra
  • créera un fichier troisieme-fichier.orig contenant l'original de troisieme-fichier avant toute modification
  • appliquera tout de même le maximum de modifications possible
  • créera un fichier troisieme-fichier.rej contenant le diff unifié des modifications ayant été rejetées pour cause de conflit

À partir de là, charge à vous de revenir à la bonne vieille méthode consistant à comparer et corriger les différences « manuellement », avec tout de même l'avantage de disposer du .diff (en l'occurrence, le .rej est plus utile) pour vous guider dans vos recherches.

Annuler les modifications contenues dans un « patch »

Si pour une raison quelconque vous souhaitez faire le chemin inverse (obtenir ancien-fichier alors que vous n'avez plus que nouveau-fichier et fichier.diff) il suffit d'inverser le patch à l'aide de l'option -R :

$ cp nouveau-fichier ancien-fichier

$ patch -R -i fichier.diff ancien-fichier

Plus d'options

diff et patch proposent une flopée d'options supplémentaires permettant de contrôler de nombreux aspects des algorithmes de comparaison (diff) et de fusion (patch).
Pour tout savoir :

$ man diff

$ man patch

Exemple pratique (scénario de gestion des fichiers de configuration)

Je viens d'installer le paquet foobar qui fournit le fichier de configuration /etc/foobar. Et bien entendu j'ai besoin de personnaliser ce dernier.

Première étape, en faire une copie de sauvegarde :

# cd /etc/

# cp foobar foobar~backup

J'édite /etc/foobar à ma convenance, et une fois la configuration validée je crée le patch pour conserver soigneusement ces modifications (et uniquement ces modifications) :

# diff -u foobar~backup foobar > foobar.diff

À ce stade, la copie de sauvegarde n'est plus nécessaire, on peut la supprimer :

# rm foobar~backup

Le temps passe, et vient le moment où le paquet foobar est mis à jour et où apt me demande quoi faire à propos du fichier de configuration modifié (conserver / écraser / examiner la situation en shell).
Afin de m'assurer de conserver mes modifications tout en bénéficiant également des nouvelles modifications distribuées avec la mise à jour, je vais aller examiner la situation en shell et faire la fusion avec mes petites mains à moi.

Pour vérifier les différences entre ma version et la nouvelle version, vous l'aurez deviné :

# cd /etc/

# diff -u foobar foobar.dpkg-new

Pour appliquer mes modifications personnelles à la nouvelle version (toujours en faisant une copie de sauvegarde avant...) :

# cp foobar foobar~backup.working

# cp foobar.dpkg-new foobar

# patch -i foobar.diff foobar

Sortir ensuite du shell (exit) puis sélectionner l'option apt « conserver mon fichier de configuration et ignorer celui du paquet ».


Utilisateur:Syam Ce tutoriel a un niveau de difficulté : Expert