Diff
In informatica, diff è un'utilità di confronto file che mostra la differenza tra due file. Questo programma stampa riga per riga le modifiche apportate al file (per i file di testo). Le moderne implementazioni supportano anche binary . L'output dell'utility è chiamato "diff", o, più comunemente, patch perché può essere applicato con il programma patch . L'output di altre utilità di confronto file viene spesso chiamato anche "diff".
Storia
L'utilità diff è stata sviluppata all'inizio degli anni '70 per il sistema operativo Unix , opera di AT&T Bell Labs , a Murray Hill, nel New Jersey. La versione finale, distribuita con Unix 5 nel 1974, è stata scritta interamente da Douglas McIlroy .
Il lavoro di McIlroy è stato preceduto e influenzato dal programma di confronto GECOS di Steve Johnson e dal programma di prova di Mike Lesk. La dimostrazione ha avuto origine anche in Unix e, come diff, ha apportato modifiche riga per riga e persino utilizzato parentesi angolari (">" e "<") per rappresentare inserimenti e cancellazioni di righe nell'output del programma. Tuttavia, l'euristica utilizzata in queste prime applicazioni era considerata inaffidabile. La potenziale utilità dello strumento di confronto ha spinto McIlroy a ricercare e sviluppare uno strumento più robusto che potesse essere utilizzato in una varietà di attività ma che avrebbe funzionato bene entro i limiti di elaborazione e dimensione dell'hardware PDP-11. Il suo approccio al problema è stato il risultato della collaborazione con le persone dei Bell Labs, tra cui Alfred Aho, Elliot Pinson, Jeffrey Ullman e Harold S. Stone.
Algoritmo
L'operazione di diff si basa sulla ricerca della sottosequenza comune più lunga ( problema LCS) . Ad esempio, ci sono due sequenze di elementi:
abcdfghjqz
abcdefgijkrxyz
ed è necessario trovare la sequenza di elementi più lunga presentata in entrambe le sequenze nello stesso ordine. Ciò significa che è necessario trovare una nuova sequenza, che può essere ottenuta dalla prima sequenza eliminando alcuni elementi o dalla seconda sequenza eliminando altri elementi. In questo caso, la sequenza sarà
abcdfgjz
Dopo aver ottenuto la sequenza comune più grande, rimane solo un piccolo passaggio prima di ottenere un output simile a una differenza:
ehikqrxy
+ - + + - + + +
Utilizzo
diff viene chiamato dalla riga di comando con i nomi di due file come argomenti: diff original new . L'output del comando sono le modifiche che devono essere apportate al file di origine originale per ottenere il nuovo file nuovo. Se original e new sono directory, diff verrà automaticamente applicato a ogni file che esiste in entrambe le directory. Tutti gli esempi in questo articolo utilizzano i seguenti due file, original e new :
|
originale: Questa parte del documento
rimasto invariato
da versione a versione. Se una
non c'è cambiamento in lei
non dovrebbe essere visualizzato.
Altrimenti non aiuta
conclusione dell'ottimale
prodotto
i cambiamenti.
Questo paragrafo contiene
testo obsoleto.
Verrà rimosso
presto.
Questo documento
deve essere
controllo ortografico.
D'altra parte, l'errore
in una parola - non la fine del mondo.
Resto del paragrafo
non richiede modifiche.
Nuovo testo può
aggiungi alla fine del documento.
|
nuovo: Questa è una nota importante!
Pertanto dovrebbe
Essere collocato
all'inizio di questo
documento!
Questa parte del documento
rimasto invariato
da versione a versione. Se una
non c'è cambiamento in lei
non dovrebbe essere visualizzato.
Altrimenti non aiuta
conclusione dell'ottimale
la quantità di informazioni.
Questo documento
deve essere
controllo ortografico.
D'altra parte, l'errore
in una parola - non la fine del mondo.
Resto del paragrafo
non richiede modifiche.
Nuovo testo può
aggiungi alla fine del documento.
Questo paragrafo contiene
aggiunte importanti
per questo documento.
|
Il comando diff original new produce il seguente output diff normale : 0a1.6
> Questa è una nota importante!
> Pertanto, dovrebbe
> essere localizzato
> all'inizio di questo
> documento!
>
8.14c14
< volume di prodotto
< modifiche.
<
< Questo paragrafo contiene
< testo obsoleto.
< Verrà cancellato
< nel prossimo futuro.
---
> quantità di informazioni.
17c17
< deve essere fatto
---
> deve essere fatto
24a25.28
>
> Questo paragrafo contiene
> aggiunte importanti
> per questo documento.
|
In questo formato di output tradizionale, a significa aggiunto ( dall'inglese add ), d significa cancellato , c significa modificato . Le lettere a, d o c sono precedute dai numeri di riga del file di origine, seguiti dai numeri di riga del file di destinazione. Ogni riga che è stata aggiunta, rimossa o modificata è preceduta da parentesi angolari .
Per impostazione predefinita, i numeri di riga comuni ai file di origine e di destinazione non sono specificati. Le righe spostate vengono visualizzate come aggiunte nella nuova posizione e rimosse dalla posizione precedente. [uno]
Opzioni
La maggior parte delle implementazioni diff è rimasta apparentemente invariata dal 1975. Le modifiche includono miglioramenti all'algoritmo principale, l'aggiunta di nuove chiavi di comando, nuovi formati di output. L'algoritmo di base è descritto in An O(ND) Difference Algorithm and its Variations di Eugene W. Myers [2] e A File Comparison Program di Webb Miller e Myers [3] . L'algoritmo è stato scoperto e descritto in modo indipendente in Algorithms for Approximate String Matching di E. Ukkonen [4] . Le prime versioni del programma diff sono state progettate per confrontare righe di file di testo utilizzando il carattere di nuova riga come separatore di riga. Negli anni '80, il supporto per i file binari ha portato a cambiamenti nel modo in cui il programma funzionava e veniva implementato.
Modifica script
Lo script di modifica può essere generato dalle versioni moderne di diff con l' opzione -e . Il risultato per il nostro esempio sarà simile a questo:
24 bis Questo paragrafo contiene aggiunte importanti per questo documento. . 17c deve essere . 8.14c la quantità di informazioni. . 0 bis Questa è una nota importante! Pertanto dovrebbe Essere collocato all'inizio di questo documento! .
Per utilizzare lo script risultante per convertire il file originale nel nuovo stato del file , è necessario aggiungere due righe alla fine dello script: una contiene il comando w (scrivi), l'altra - q (chiudi). Ad esempio, così . Qui abbiamo chiamato il file diff mydiff . La trasformazione avverrà quando diamo il comando .
printf "w\nq\n" >> mydiffed -s original < mydiff
Formato del contesto
BSD versione 2.8 (rilasciata nel luglio 1981) ha introdotto il formato del contesto ( -c ) e la capacità di attraversare ricorsivamente l'albero delle directory del filesystem ( -r ).
Nel formato del contesto, le righe modificate vengono visualizzate insieme alle righe inalterate prima e dopo il frammento modificato. L'inserimento di un numero qualsiasi di righe non interessate fornisce il contesto per la patch. Il contesto , che consiste in righe non interessate, funge da riferimento per determinare la posizione del frammento da modificare nel file di destinazione, anche se i numeri di riga delle righe modificate nei file di origine e di destinazione non corrispondono. Il formato del contesto è più leggibile per gli esseri umani e più affidabile quando si applica una patch e l'output viene preso come input per il programma di patch .
Il numero di righe non interessate prima e dopo il frammento modificato può essere impostato dall'utente e può essere pari a zero, ma di solito è impostato su tre righe. Se il contesto delle linee non interessate in un frammento si sovrappone a un frammento adiacente, diff eviterà di copiare le linee non interessate e unirà i frammenti adiacenti in una sola.
L'output del comando diff -c original new è:
*** /percorso/di/originale ''timestamp''
--- /percorso/di/nuovo ''timestamp''
***************
*** 1,3 ****
--- 1.9 ----
+ Questa è una nota importante!
+ Quindi dovrebbe
+ trovarsi
+ all'inizio di questo
+ documento!
+
Questa parte del documento
rimasto invariato
da versione a versione. Se una
***************
*** 5.20 ****
non dovrebbe essere visualizzato.
Altrimenti non aiuta
conclusione dell'ottimale
! volume prodotto
! i cambiamenti.
!
! Questo paragrafo contiene
! testo obsoleto.
! Sarà rimosso
! presto.
Questo documento
! deve essere
controllo ortografico.
D'altra parte, l'errore
in una parola - non la fine del mondo.
--- 11.20 ----
non dovrebbe essere visualizzato.
Altrimenti non aiuta
conclusione dell'ottimale
! la quantità di informazioni.
Questo documento
! deve essere
controllo ortografico.
D'altra parte, l'errore
in una parola - non la fine del mondo.
***************
*** 22.24 ****
--- 22.28 ----
non richiede modifiche.
Nuovo testo può
aggiungi alla fine del documento.
++
Questo paragrafo contiene
importanti aggiunte
+ per questo documento.
Formato universale
Il formato universale (o unidiff ) incorpora i miglioramenti tecnici apportati al formato del contesto, ma rende la differenza tra il vecchio e il nuovo testo in modo più conciso. Il formato universale viene solitamente invocato utilizzando l' opzione della riga di comando " -u " . Questa uscita è spesso usata come patch per i programmi. Molti progetti richiedono specificamente che i "diff" vengano inviati loro in un formato generico, rendendo così il formato generico lo scambio più comune tra gli sviluppatori di software.
Le differenze di contesto universali sono state sviluppate per la prima volta da Wayne Davison nell'agosto 1990 ( l'unidiff appare nel capitolo 14 di comp.sources.misc). Stallman ha aggiunto il supporto del formato universale all'utilità diff del progetto GNU un mese dopo e questa funzionalità ha debuttato in GNU diff 1.15, rilasciato nel gennaio 1991. Da allora GNU diff ha generalizzato il formato del contesto per consentire la formattazione arbitraria delle differenze.
Un file di formato generico inizia con le stesse due righe del formato di contesto, tranne per il fatto che il file originale inizia con " --- " e il nuovo file inizia con " +++ ". Sono seguiti da uno o più frammenti modificati che contengono le modifiche riga per riga ai file. Le righe senza modifiche iniziano con uno spazio, le righe aggiunte iniziano con un segno più, le righe eliminate iniziano con un segno meno.
Il frammento inizia con le informazioni sull'intervallo ed è immediatamente seguito da righe aggiunte, righe eliminate e un numero qualsiasi di righe di contesto. Le informazioni sull'intervallo sono circondate da doppi segni @ e concatenate su un'unica riga, anziché su due righe in ( formato contesto ). Le informazioni sull'intervallo hanno il seguente formato:
@@ -l,s +l,s @@ intestazione di sezione opzionale
Le informazioni sull'intervallo sono composte da due parti. La parte per il file originale inizia con un segno meno e la parte per il nuovo file inizia con un segno più. Ogni parte è nel formato l, s , dove l è il numero della riga con cui iniziamo, ed s è il numero di righe che sono state modificate nel frammento corrente per ciascuno dei file, rispettivamente (cioè, nel nel primo caso, questa è la somma delle righe di output che iniziano con uno spazio e con un meno, nel secondo - righe che iniziano con uno spazio e con un più). In molte versioni di GNU diff, la virgola e la s finale possono essere omesse da ciascun intervallo. In questo caso, s è impostato su 1. Si noti che l'unico valore utile per l solo è il numero di riga del primo intervallo, gli altri valori possono essere calcolati dal diff.
Il frammento di intervallo per il file originale deve essere la somma di tutto il contesto e le righe eliminate (incluse quelle modificate) del frammento. Il frammento di intervallo per il nuovo file deve includere la somma di tutto il contesto e le righe aggiunte (incluse le modifiche) del frammento.
Un frammento di intervallo può essere preceduto dall'intestazione della sezione o funzione di cui fa parte il frammento. Questo di solito è utile per leggere lo snippet stesso. Quando si crea un diff utilizzando GNU, l'intestazione diff è determinata dall'espressione regolare [5] .
Se una riga è stata modificata, viene mostrata sia come rimossa che come aggiunta. Poiché le righe eliminate e aggiunte si trovano in frammenti adiacenti, queste righe vengono mostrate una accanto all'altra [6] . Per esempio:
-controlla questo documento. SU +controlla questo documento. SU
Il comando diff -u original new produrrà il seguente output:
--- /percorso/di/originale ''timestamp''
+++ /percorso/di/nuovo ''timestamp''
@@ -1.3 +1.9 @@
+Questa è una nota importante!
+Quindi, dovrebbe
+essere posizionato
all'inizio +di questo
+documento!
+
Questa parte del documento
rimasto invariato
da versione a versione. Se una
@@ -5.16 +11.10 @@
non dovrebbe essere visualizzato.
Altrimenti non aiuta
conclusione dell'ottimale
- il volume delle
modifiche apportate.
-
-Questo paragrafo contiene
testo obsoleto.
-Sarà rimosso
-nel prossimo futuro.
+ quantità di informazioni.
Questo documento
- deve essere fatto
+ deve essere fatto
controllo ortografico.
D'altra parte, l'errore
in una parola - non la fine del mondo.
@@ -22,3 +22,7 @@
non richiede modifiche.
Nuovo testo può
aggiungi alla fine del documento.
++
Questo paragrafo contiene
+importanti integrazioni
+per questo documento.
Tieni presente che le schede vengono utilizzate per separare correttamente i nomi dei file dai timestamp. Questo è invisibile sullo schermo e può andare perso quando si copia/incolla dalla console.
Esistono diverse modifiche ed estensioni ai formati differenziati che vari programmi utilizzano e comprendono. Ad esempio, alcuni sistemi di controllo della versione , come Subversion , specificano il numero di versione, "copia di lavoro" o qualsiasi altro commento oltre al timestamp nell'intestazione del diff.
Alcuni programmi ti consentono di creare differenze per diversi file e unirli in uno solo, usando un'intestazione per ogni file modificato, che potrebbe assomigliare a questo:
Indice: percorso/per/file.cpp
Il tipo speciale di file che non terminano con una nuova riga non è supportato. Né l'utilità unidiff né lo standard POSIX diff specificano come vengono gestiti tali file (in effetti, i file di questo tipo non sono "testo" nella definizione POSIX [7] ).
Il programma patch non sa nulla dell'implementazione dell'output speciale del comando diff.
Vedi anche
- cmp
- com
- diff3
- Compare
- kdiff3 Archiviato il 29 dicembre 2020 in Wayback Machine
- fondere
- Confronto file Microsoft
- sincronizzare
- tkdiff
- wdiff - wrapper per diff per confrontare i file con le parole
- WinMerge
- xdelta - diff per i file binari
- codifica delta
- Sottosequenza comune massima
- Distanza di Levenshtein
- Sistema di controllo della versione
Note
- ^ David MacKenzie, Paul Eggert e Richard Stallman. Confronto e unione di file con GNU Diff e Patch . — 1997.
- ^ E. Myers. Un algoritmo di differenza O(ND ) e le sue variazioni // Algoritmica : rivista. - 1986. - Vol. 1 , n. 2 . - P. 251-266 .
- ^ Webb Miller e Eugene W. Myers. Un programma di confronto file // Software: pratica ed esperienza. - 1985. - T. 15 , n. 11 . - S. 1025-1040 .
- ^ E. Ukkonen. Algoritmi per la corrispondenza approssimativa di stringhe // Informazioni e calcolo : rivista. - 1985. - Vol. 64 . - P. 100-118 .
- ^ 2.2.3 Mostra in quali sezioni sono presenti differenze Archiviato il 26 maggio 2013 in Internet Archive. , manuale GNU diffutils
- ^ Formato unificato Diff Archiviato il 5 aprile 2013 in Internet Archive . da Guido van Rossum , 14 giugno 2006
- ↑ http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_205 Archiviato il 29 aprile 2013 nella Wayback Machine Sezione 3.205
Collegamenti
- Il pacchetto GNU diffutils Archiviato l'11 giugno 2007 su Wayback Machine include diff. Distribuito sotto la GNU General Public License .
- diffutils per Win32 Archiviato il 12 giugno 2007 su Wayback Machine - parte di GnuWin32
- Interfaccia online per diff Archiviato il 14 settembre 2017 in Wayback Machine (russo)
- Algoritmo diff C# Archiviato il 15 giugno 2007 in Internet Archive . — Codice sorgente per l'algoritmo diff e le sue varianti C#