În calcul, diff este un utilitar de comparare a fișierelor care afișează diferența dintre două fișiere. Acest program imprimă rând cu linie modificările aduse fișierului (pentru fișiere text). Implementările moderne acceptă și binarul . Ieșirea utilitarului se numește „diff”, sau, mai frecvent, un patch deoarece poate fi aplicat cu programul de corecție . Ieșirea altor utilitare de comparare a fișierelor este adesea denumită „dif”.
Utilitarul diff a fost dezvoltat la începutul anilor 1970 pentru sistemul de operare Unix , care a fost opera AT&T Bell Labs , din Murray Hill, New Jersey. Versiunea finală, distribuită cu Unix 5 în 1974, a fost scrisă în întregime de Douglas McIlroy .
Lucrarea lui McIlroy a fost precedată și influențată de programul de comparație GECOS al lui Steve Johnson și de programul de demonstrare al lui Mike Lesk. Proof a apărut, de asemenea, în Unix și, ca și diff, a făcut modificări linie cu linie și chiar a folosit paranteze unghiulare (">" și "<") pentru a reprezenta inserările și ștergerile de linii în ieșirea programului. Cu toate acestea, euristicile utilizate în aceste aplicații timpurii au fost considerate nesigure. Utilitatea potențială a instrumentului de comparație l-a determinat pe McIlroy să cerceteze și să dezvolte un instrument mai robust care ar putea fi utilizat într-o varietate de sarcini, dar care ar funcționa bine în limitele de procesare și dimensiune ale hardware-ului PDP-11. Abordarea sa asupra problemei a fost rezultatul colaborării cu oameni de la Bell Labs, inclusiv Alfred Aho, Elliot Pinson, Jeffrey Ullman și Harold S. Stone.
Funcționarea diff se bazează pe găsirea celei mai lungi subsecvențe comune ( problema LCS) . De exemplu, există două secvențe de elemente:
abcdfghjqz abcdefgijkrxyzși trebuie să găsiți cea mai lungă secvență de elemente care este prezentată în ambele secvențe în aceeași ordine. Aceasta înseamnă că este necesară găsirea unei noi secvențe, care poate fi obținută din prima secvență prin ștergerea unor elemente sau din a doua secvență prin ștergerea altor elemente. În acest caz, secvența va fi
abcdfgjzDupă obținerea celei mai mari secvențe comune, mai rămâne doar un mic pas înainte de a obține o ieșire asemănătoare diferențelor:
ehikqrxy + - + + - + + +diff este apelat din linia de comandă cu numele a două fișiere drept argumente: diff original new . Ieșirea comenzii sunt modificările care trebuie făcute fișierului sursă original pentru a obține noul fișier nou. Dacă originalul și nou sunt directoare, atunci diff va fi aplicat automat fiecărui fișier care există în ambele directoare. Toate exemplele din acest articol folosesc următoarele două fișiere, original și nou :
original: Această parte a documentului ramas neschimbat de la versiune la versiune. În cazul în care un nu este nicio schimbare la ea nu ar trebui să fie afișate. Altfel nu ajută concluzia optimului produs schimbări. Acest paragraf contine text învechit. Va fi eliminat curând. Acest document trebuie sa fie verificare a ortografiei. Pe de altă parte, eroarea într-un cuvânt – nu sfârşitul lumii. Restul paragrafului nu necesita modificari. Textul nou poate adăugați la sfârșitul documentului. |
nou: Aceasta este o notă importantă! Prin urmare ar trebui fi localizat la începutul acestui lucru document! Această parte a documentului ramas neschimbat de la versiune la versiune. În cazul în care un nu este nicio schimbare la ea nu ar trebui să fie afișate. Altfel nu ajută concluzia optimului cantitatea de informații. Acest document trebuie sa fie verificare a ortografiei. Pe de altă parte, eroarea într-un cuvânt – nu sfârşitul lumii. Restul paragrafului nu necesita modificari. Textul nou poate adăugați la sfârșitul documentului. Acest paragraf contine completări importante pentru acest document. |
Comanda diff original new produce următoarea ieșire normală de dif : 0a1.6 > Aceasta este o notă importantă! > Prin urmare, ar trebui > fi localizat > la începutul acestui lucru > document! > 8.14c14 < volumul produs < modificări. < < Acest paragraf contine < text învechit. < Va fi șters < în viitorul apropiat. --- > cantitatea de informatii. 17c17 < trebuie făcut --- > trebuie facut 24a25.28 > > Acest paragraf contine > completări importante > pentru acest document. |
În acest format tradițional de ieșire, a înseamnă adăugat (din engleză add ), d înseamnă șters , c înseamnă schimbat . Literele a, d sau c sunt precedate de numerele de rând ale fișierului sursă, urmate de numerele de rând ale fișierului țintă. Fiecare linie care a fost adăugată, eliminată sau modificată este precedată de paranteze unghiulare .
În mod implicit, numerele de rând comune fișierelor sursă și destinație nu sunt specificate. Rândurile care sunt mutate sunt afișate ca adăugate în noua lor locație și eliminate din locația lor anterioară. [unu]
Cele mai multe implementări diferite au rămas neschimbate din 1975. Modificările includ îmbunătățiri ale algoritmului principal, adăugarea de noi taste de comandă, noi formate de ieșire. Algoritmul de bază este subliniat în An O(ND) Difference Algorithm and its Variations de Eugene W. Myers [2] și A File Comparison Program de Webb Miller și Myers [3] . Algoritmul a fost descoperit și descris în mod independent în Algoritmi pentru potrivirea aproximativă a șirurilor de E. Ukkonen [4] . Primele versiuni ale programului diff au fost concepute pentru a compara liniile de fișiere text folosind caracterul newline ca separator de linii. În anii 1980, suportul pentru fișierele binare a dus la schimbări în modul în care a funcționat și a fost implementat programul.
Scriptul de editare poate fi generat de versiunile moderne ale diff cu opțiunea -e . Rezultatul pentru exemplul nostru va arăta astfel:
24a Acest paragraf contine completări importante pentru acest document. . 17c trebuie sa fie . 8.14c cantitatea de informații. . 0a Aceasta este o notă importantă! Prin urmare ar trebui fi localizat la începutul acestui lucru document! .Pentru a utiliza scriptul rezultat pentru a converti fișierul original în noua stare a fișierului , trebuie să adăugăm două linii la sfârșitul scriptului: una conține comanda w (scriere), cealaltă - q (iesire). De exemplu, deci . Aici am denumit fișierul diff mydiff . Transformarea va avea loc când vom da comanda . printf "w\nq\n" >> mydiffed -s original < mydiff
Versiunea 2.8 BSD (lansată în iulie 1981) a introdus formatul de context ( -c ) și capacitatea de a parcurge recursiv arborele de directoare ale sistemului de fișiere ( -r ).
În format context, liniile modificate sunt afișate împreună cu liniile neafectate înainte și după fragmentul modificat. Inserarea oricărui număr de linii neafectate oferă context pentru patch. Contextul , care constă din linii neafectate, servește ca referință pentru determinarea locației fragmentului care este modificat în fișierul țintă, chiar dacă numerele de linii ale liniilor modificate din fișierele sursă și țintă nu se potrivesc. Formatul contextului este mai lizibil pentru oameni și mai fiabil atunci când se aplică un patch, iar rezultatul este luat ca intrare în programul de corecție .
Numărul de linii neafectate înainte și după fragmentul modificat poate fi setat de utilizator și chiar poate fi zero, dar de obicei este implicit la trei linii. Dacă contextul liniilor neafectate dintr-un fragment se suprapune cu un fragment adiacent, atunci diff va evita copierea liniilor neafectate și va îmbina fragmentele adiacente într-unul singur.
Ieșirea comenzii diff -c original new este:
*** /path/to/original ''timestamp'' --- /path/to/new ''timestamp'' *************** *** 1,3 **** --- 1.9 ---- + Aceasta este o notă importantă! + Așadar, ar trebui să + fie localizat + la începutul acestui + document! + Această parte a documentului ramas neschimbat de la versiune la versiune. În cazul în care un *************** *** 5,20 **** nu ar trebui să fie afișate. Altfel nu ajută concluzia optimului ! volum produs ! schimbări. ! ! Acest paragraf contine ! text învechit. ! El va fi îndepărtat ! curând. Acest document ! trebuie sa fie verificare a ortografiei. Pe de altă parte, eroarea într-un cuvânt – nu sfârşitul lumii. --- 11.20 ---- nu ar trebui să fie afișate. Altfel nu ajută concluzia optimului ! cantitatea de informații. Acest document ! trebuie sa fie verificare a ortografiei. Pe de altă parte, eroarea într-un cuvânt – nu sfârşitul lumii. *************** *** 22.24 **** --- 22.28 ---- nu necesita modificari. Textul nou poate adăugați la sfârșitul documentului. ++ Acest paragraf conține completări importante + pentru acest document.Formatul universal (sau unidiff ) încorporează îmbunătățirile tehnice aduse formatului de context, dar face diferența dintre textul vechi și cel nou într-un mod mai concis. Formatul universal este de obicei invocat folosind opțiunea de linie de comandă „ -u ” . Această ieșire este adesea folosită ca patch pentru programe. Multe proiecte solicită în mod special ca „diferențele” să le fie trimise într-un format generic, făcând astfel formatul generic cel mai comun schimb între dezvoltatorii de software.
Diferențele de context universal au fost dezvoltate pentru prima dată de Wayne Davison în august 1990 ( unidiff apare în capitolul 14 din comp.sources.misc). Stallman a adăugat suport pentru format universal la utilitarul diff al Proiectului GNU o lună mai târziu, iar această funcționalitate a debutat în GNU diff 1.15, lansat în ianuarie 1991. GNU diff a generalizat de atunci formatul de context pentru a permite formatarea arbitrară a diferențelor.
Un fișier în format generic începe cu aceleași două linii ca și formatul context, cu excepția faptului că fișierul original începe cu „ --- ” și noul fișier începe cu „ +++ ”. Acestea sunt urmate de unul sau mai multe fragmente modificate care conțin modificări rând cu linie ale fișierelor. Liniile fără modificări încep cu un spațiu, liniile adăugate încep cu semnul plus, liniile șterse încep cu semnul minus.
Fragmentul începe cu informații despre interval și este urmat imediat de linii adăugate, linii șterse și orice număr de linii de context. Informațiile privind intervalul sunt înconjurate de semne @ duble și concatenate pe o singură linie, spre deosebire de două linii în ( format context ). Informațiile privind intervalul au următorul format:
@@ -l,s +l,s @@ titlu de secțiune opționalInformațiile de gamă sunt formate din două părți. Partea pentru fișierul original începe cu un minus, iar partea pentru fișierul nou începe cu un plus. Fiecare parte este în formatul l, s , unde l este numărul liniei cu care începem și s este numărul de linii care au fost modificate în fragmentul curent pentru fiecare dintre fișiere, respectiv (adică în primul caz, aceasta este suma liniilor de ieșire care încep cu un spațiu și cu un minus, în al doilea - linii care încep cu un spațiu și cu un plus). În multe versiuni de GNU diff, virgulele și cele de sfârșit pot fi omise din fiecare interval. În acest caz, s este implicit 1. Rețineți că singura valoare utilă numai pentru l este numărul rândului din primul interval, celelalte valori pot fi calculate din dif.
Fragmentul de interval pentru fișierul original trebuie să fie suma întregului context și a liniilor șterse (inclusiv modificate) ale fragmentului. Fragmentul de interval pentru noul fișier trebuie să includă suma întregului context și liniile adăugate (inclusiv modificate) ale fragmentului.
Un fragment de interval poate fi precedat de antetul secțiunii sau al funcției din care face parte fragmentul. Acest lucru este de obicei util pentru citirea fragmentului în sine. Când se creează un dif folosind GNU, antetul dif este determinat de expresia regulată [5] .
Dacă o linie a fost modificată, aceasta este afișată atât ca eliminată, cât și ca adăugată. Deoarece liniile șterse și adăugate sunt în fragmente adiacente, aceste linii sunt afișate una lângă alta [6] . De exemplu:
-verificați acest document. Pe +verificați acest document. PeComanda diff -u original new va produce următoarea ieșire:
--- /path/to/original ''timestamp'' +++ /path/to/new ''timestamp'' @@ -1.3 +1.9 @@ +Aceasta este o notă importantă! +Prin urmare, ar trebui să fie + la +începutul acestui +document! + Această parte a documentului ramas neschimbat de la versiune la versiune. În cazul în care un @@ -5.16 +11.10 @@ nu ar trebui să fie afișate. Altfel nu ajută concluzia optimului - volumul modificărilor efectuate. - -Acest paragraf conține text învechit. -Va fi eliminat -în viitorul apropiat. + cantitatea de informații. Acest document - trebuie făcut + trebuie făcut verificare a ortografiei. Pe de altă parte, eroarea într-un cuvânt – nu sfârşitul lumii. @@ -22,3 +22,7 @@ nu necesita modificari. Textul nou poate adăugați la sfârșitul documentului. ++ Acest paragraf conține +adăugiri importante +pentru acest document.Rețineți că filele sunt folosite pentru a separa corect numele fișierelor de marcajele de timp. Acesta este invizibil pe ecran și poate fi pierdut la copierea/lipirea de pe consolă.
Există mai multe modificări și extensii ale formatelor diferite pe care diverse programe le folosesc și le înțeleg. De exemplu, unele sisteme de control al versiunilor , cum ar fi Subversion , specifică numărul versiunii, „copie de lucru” sau orice alt comentariu în plus față de marcajul de timp din antetul diferenței.
Unele programe vă permit să creați diferențe pentru mai multe fișiere diferite și să le îmbinați într-unul singur, folosind un antet pentru fiecare fișier modificat, care ar putea arăta cam așa:
Index: cale/către/fișier.cppTipul special de fișiere care nu se termină cu o linie nouă nu este acceptat. Nici utilitarul unidiff, nici standardul POSIX diff nu specifică modul în care sunt gestionate astfel de fișiere (de fapt, fișierele de acest tip nu sunt „text” în definiția POSIX [7] ).
Programul de corecție nu știe nimic despre implementarea ieșirii speciale a comenzii diff.
Comenzi Unix | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
|
Sisteme de control al versiunilor ( categorie ) | |
---|---|
Doar local | |
Client server | |
Distribuit | |