« Utiliser diff et patch » : différence entre les versions

De Le Wiki du Forum-Debian.fr
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
m (Mise en forme)
Ligne 1 : Ligne 1 :
Qui n'a jamais eu besoin de comparer deux fichiers très semblables, ou bien de mémoriser ''uniquement'' les modifications apportées à un fichier pour pouvoir ensuite les réappliquer à volonté&nbsp;?<br>
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é ?


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


Heureusement, Linux dispose comme très souvent d'outils tous prêts pour régler ce souci&nbsp;: '''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.


Bien que ces outils soient le plus souvent associés à la programmation (pour gérer les fameux «&nbsp;patches&nbsp;» 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).<br>
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''').<br />
Ou tout simplement, comparer deux fichiers ('''diff''' uniquement).


<br>
= Installer les paquets nécessaires =


= Installer les paquets nécessaires<br>  =
Les deux paquets à installer sont '''[[Paquet::diffutils]]''' et '''[[Paquet::patch]]''' :  
 
<pre>
Les deux paquets à installer sont '''[[Paquet::diffutils]]''' et '''[[Paquet::patch]]'''&nbsp;:  
# aptitude install diffutils patch
<pre># aptitude install diffutils patch
</pre>
</pre>


= Comparer deux fichiers avec diff<br>  =
= Comparer deux fichiers avec diff =


La commande '''[[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).<br>
La commande '''[[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&nbsp;:<br>
Comme exemple, nous prendrons les deux fichiers suivants :
<pre>$ cat ancien-fichier
<pre>
$ cat ancien-fichier
ceci
ceci
n'est
n'est
Ligne 28 : Ligne 29 :
grand
grand
intérêt
intérêt
 
</pre>
<pre>
$ cat nouveau-fichier
$ cat nouveau-fichier
ceci
ceci
Ligne 39 : Ligne 41 :
intérêt
intérêt
</pre>  
</pre>  
Et la comparaison ('''diff''') des deux,&nbsp;qui permet de visualiser très rapidement et de manière extrêmement claire les différences&nbsp;:<br>  
Et la comparaison ('''diff''') des deux, qui permet de visualiser très rapidement et de manière extrêmement claire les différences :<br />
<pre>$ diff -u ancien-fichier nouveau-fichier
<pre>
$ diff -u ancien-fichier nouveau-fichier
--- ancien-fichier      2010-04-27 23:53:41.948385552 +0200
--- ancien-fichier      2010-04-27 23:53:41.948385552 +0200
+++ nouveau-fichier    2010-04-27 23:54:12.552109974 +0200
+++ nouveau-fichier    2010-04-27 23:54:12.552109974 +0200
Ligne 56 : Ligne 59 :
  intérêt
  intérêt
</pre>  
</pre>  
L'option '''-u''' permet d'obtenir ce qu'on appelle un ''diff unifié'', le format le plus couramment utilisé.<br>
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, c'est les signes + et - (ou leur absence) en début de lignes. Votre intuition ne vous trompe pas&nbsp;: 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).<br>
Ce qui saute immédiatement aux yeux, ce sont les signes + et - (ou leur absence) en début de lignes.


Deuxième point remarquable&nbsp;: 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&nbsp;dans '''ancien-fichier''' la ligne précédent immédiatement la première ligne de contexte (ici, «&nbsp;''ceci''&nbsp;» qui se trouve être la première ligne du fichier, c'est à dire la ligne n° 0).<br>
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).


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.<br>
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.<br />
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).


= Sauvegarder des modifications pour usage ultérieur (créer un «&nbsp;patch&nbsp;»)<br>  =
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.


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&nbsp;:<br>
= Sauvegarder des modifications pour usage ultérieur (créer un « patch ») =
<pre>$ diff -u ancien-fichier nouveau-fichier &gt; fichier.diff
 
La commande diff affiche par défaut son résultat dans la console.<br />
Si l'on veut sauvegarder ce résultat sur le disque dur, il suffit donc de rediriger la sortie vers un fichier :
<pre>
$ diff -u ancien-fichier nouveau-fichier &gt; fichier.diff
</pre>  
</pre>  
= Appliquer des modifications (un «&nbsp;patch&nbsp;») à un fichier  =


À quoi sert de sauvegarder un jeu de modifications si on ne le réutilise pas après&nbsp;?...<br>
= Appliquer des modifications (un « patch ») à un fichier  =
<pre>$ patch -i fichier.diff troisieme-fichier
 
À quoi sert de sauvegarder un jeu de modifications si on ne le réutilise pas après ?
<pre>
$ patch -i fichier.diff troisieme-fichier
</pre>  
</pre>  
ou bien, en utilisant les redirections du shell (exemple simplifié avec '''cat''', uniquement pour montrer la syntaxe '''patch''' correspondante)&nbsp;:<br>
ou bien, en utilisant les redirections du shell (exemple simplifié avec '''cat''', uniquement pour montrer la syntaxe '''patch''' correspondante) :
<pre>$ cat fichier.diff | patch troisieme-fichier
<pre>
$ cat fichier.diff | patch troisieme-fichier
</pre>  
</pre>  
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.<br>
Ceci va tout simplement appliquer les modifications contenues dans '''fichier.diff''' au fichier '''troisieme-fichier'''.<br />
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.


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


Si par malheur '''patch''' n'arrive pas à ''réconcilier'' les différentes versions, il&nbsp;:<br>  
*vous en préviendra<br />
*créera un fichier '''troisieme-fichier.orig''' contenant l'original de '''troisieme-fichier''' avant toute modification<br />  
*appliquera tout de même le maximum de modifications possible<br />
*créera un fichier '''troisieme-fichier.rej''' contenant le ''diff unifié'' des modifications ayant été ''rejetées'' pour cause de ''conflit''


*vous en préviendra<br>
À 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.
*créera un fichier '''troisieme-fichier.orig''' contenant l'original de '''troisieme-fichier''' avant toute modification<br>
*appliquera tout de même le maximum de modifications possible<br>
*créera un fichier '''troisieme-fichier.rej''' contenant le ''diff unifié'' des modifications ayant été ''rejetées'' pour cause de ''conflit''<br>


À partir de là, charge à vous de revenir à la bonne vieille méthode consistant à comparer et corriger les différences «&nbsp;manuellement&nbsp;», 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.<br>
= Annuler les modifications contenues dans un « patch » =


= Annuler les modifications contenues dans un «&nbsp;patch&nbsp;»<br>  =
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''' :
 
<pre>
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'''&nbsp;:<br>
$ cp nouveau-fichier ancien-fichier
<pre>$ cp nouveau-fichier ancien-fichier


$ patch -R -i fichier.diff ancien-fichier
$ patch -R -i fichier.diff ancien-fichier
</pre>  
</pre>  
= Plus d'options  =
= 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&nbsp;:  
'''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''').<br />
<pre>$ man diff
Pour tout savoir :  
<pre>
$ man diff


$ man patch
$ man patch
</pre>  
</pre>  
= Exemple pratique (scénario de gestion des fichiers de configuration)<br>  =


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.<br>
= 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&nbsp;:<br>
Première étape, en faire une copie de sauvegarde :
<pre># cd /etc/
<pre>
# cd /etc/


# cp foobar foobar~backup</pre>  
# 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)&nbsp;: <br>
</pre>  
<pre># diff -u foobar~backup foobar &gt; foobar.diff
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) :
<pre>
# diff -u foobar~backup foobar &gt; foobar.diff
</pre>  
</pre>  
À ce stade, la copie de sauvegarde n'est plus nécessaire, on peut la supprimer&nbsp;:<br>
À ce stade, la copie de sauvegarde n'est plus nécessaire, on peut la supprimer :
<pre># rm foobar~backup
<pre>
# rm foobar~backup
</pre>  
</pre>  
<br> 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.<br>


<br>  
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).<br />
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é&nbsp;:  
Pour vérifier les différences entre ma version et la nouvelle version, vous l'aurez deviné :  
<pre># cd /etc/
<pre>
# cd /etc/


# diff -u foobar foobar.dpkg-new
# diff -u foobar foobar.dpkg-new
</pre>  
</pre>  
Pour appliquer mes modifications personnelles à la nouvelle version (toujours en faisant une copie de sauvegarde avant...)&nbsp;:  
Pour appliquer mes modifications personnelles à la nouvelle version (toujours en faisant une copie de sauvegarde avant...) :  
<pre># cp foobar foobar~backup.working
<pre>
# cp foobar foobar~backup.working


# cp foobar.dpkg-new foobar
# cp foobar.dpkg-new foobar
Ligne 134 : Ligne 155 :
# patch -i foobar.diff foobar
# patch -i foobar.diff foobar
</pre>  
</pre>  
Sortir ensuite du shell ('''exit''') puis sélectionner l'option '''apt''' «&nbsp;conserver mon fichier de configuration et ignorer celui du paquet&nbsp;».
Sortir ensuite du shell ('''exit''') puis sélectionner l'option '''apt''' « conserver mon fichier de configuration et ignorer celui du paquet ».


<br>


[[Utilisateur:Syam]]
[[Utilisateur:Syam]]

Version du 16 octobre 2016 à 13:17

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