Due testi che sembrano identici raramente lo sono, e trovare la differenza a occhio è un compito in cui gli umani sono spettacolarmente scarsi. Questo strumento confronta riga per riga e segna cosa è stato aggiunto e cosa rimosso. La parte interessante è che "cosa è cambiato" è una domanda con più di una risposta giusta.
La differenza invisibile che fa sembrare cambiato tutto
Se un diff dichiara che ogni singola riga è cambiata mentre il testo palesemente non lo è, la risposta sono quasi sempre i fine riga. Windows chiude le righe con un ritorno carrello più un avanzamento di riga; Unix, Linux e macOS usano il solo avanzamento. Entrambi non si vedono affatto sullo schermo.
L'effetto è totale. Spezza "uno\ndue\ntre" e "uno\r\ndue\r\ntre" in righe e ottieni ["uno","due","tre"] contro ["uno\r","due\r","tre\r"]: ogni riga ora porta un passeggero invisibile, e solo l'ultima, priva dell'interruzione finale, coincide. Una riga su tre. I testi sono gli stessi; i byte no.
È per questo che git ha le impostazioni autocrlf e per questo i team ci litigano. Se stai confrontando il file di un collega su Windows col tuo su un Mac, normalizza i fine riga prima di cercare un significato nel risultato.
Non esiste un diff corretto e basta
La cosa sorprende chi dà per scontato che un diff sia un fatto. Non lo è: è una risposta tra diverse ugualmente minime. Se una riga viene cancellata da una sequenza di righe identiche, niente nel testo dice quale sia sparita. L'algoritmo sceglie, e algoritmi diversi scelgono diversamente.
Aggiungi una funzione a un file sorgente e il diff può mostrare la tua nuova funzione come aggiunta, oppure può mostrare come aggiunta la tua graffa di chiusura e come rimossa quella della funzione successiva: entrambi sono minimi, uno solo è comprensibile. È esattamente il motivo per cui git offre più algoritmi: il predefinito Myers è veloce, mentre patience e histogram producono diff che si allineano meglio a come un umano vede la modifica. Nessuno è più corretto degli altri. Ottimizzano definizioni diverse di buona risposta.
Perché i confronti grandi sbattono contro un muro
L'algoritmo classico costruisce una tabella di ogni riga di A contro ogni riga di B, il che costa tempo e memoria proporzionali al prodotto delle due lunghezze. Due file da 12.000 righe chiedono una tabella da 144 milioni di celle: oltre un gigabyte di memoria e quasi due secondi di lavoro. Su un telefono è una scheda morta.
La salvezza è che i confronti veri sono raramente casuali. Due versioni di un documento condividono di solito una lunga testa e una lunga coda identiche, e quelle righe non hanno bisogno di nessuna tabella: sono uguali a colpo d'occhio. Toglierle per prime è la prima mossa di ogni implementazione diff seria, e su due file da 12.000 righe che differiscono in un punto porta la tabella da 144 milioni di celle a una manciata.
Questo copre il caso normale. Non aiuta quando due testi grandi sono genuinamente diversi ovunque, perché allora non c'è nessuna testa o coda comune da togliere. Questo strumento traccia lì una linea e te lo dice, invece di provarci e morire: se dopo il taglio il confronto è troppo grande, ricevi un messaggio. È una scelta voluta — uno strumento che ti congela il browser è peggio di uno che ammette i propri limiti.
Le righe sono la granularità sbagliata per la prosa
Questo strumento confronta righe intere, il che va bene per codice, configurazioni e qualsiasi cosa in cui una riga sia un'unità di senso. Va male per la prosa. Cambia una parola in un paragrafo che vive su una sola riga e il diff segnerà l'intero paragrafo come rimosso e riaggiunto: tecnicamente vero e praticamente inutile.
Il rimedio in quella situazione è lavorare con testo che abbia una frase per riga, oppure ricorrere a un diff a livello di parola. Vale la pena sapere quale dei due stai guardando: un diff a righe su un documento con testo a capo automatico ti dice che un paragrafo è cambiato, e niente su cosa sia cambiato dentro.
Domande frequenti
Il mio testo viene inviato a un server?
No. Questo strumento è marcato "client": entrambi i testi sono confrontati nella scheda del tuo browser e nessuno dei due lascia la macchina. Qui conta più del solito, perché confrontare due versioni di qualcosa significa che ne hai due versioni: contratti, bozze, file di configurazione con dentro le credenziali.
Perché dice che ogni riga è cambiata quando sembrano uguali?
I fine riga, quasi certamente — vedi sopra. L'altro candidato sono gli spazi in coda, altrettanto invisibili e altrettanto fatali per un confronto. Se il tuo editor sa mostrare i caratteri invisibili, attivalo e guarda la fine delle righe.
Perché c'è una riga vuota in più alla fine?
Perché il testo finisce con un'interruzione di riga. Spezzare "a\nb\n" sugli a capo produce tre pezzi: "a", "b" e una stringa vuota dopo l'ultima interruzione. Non è un bug del diff; è cosa significa un a capo finale. La convenzione Unix dice che i file di testo dovrebbero finire con uno, quindi è molto comune e di solito non vale la pena inseguirlo.
Può confrontare due file invece di testo incollato?
Non qui: questo è uno strumento da incolla. Per i file, e in particolare per qualsiasi cosa sotto controllo di versione, usa git diff: gestisce i fine riga, offre gli algoritmi migliori citati sopra, e sa mostrare le modifiche a livello di parola dentro una riga. Questo strumento è per il confronto veloce dove aprire un terminale è più fatica di quanta ne meriti la domanda.
Ignora gli spazi?
No. Ogni carattere conta, spazi in coda e indentazione inclusi. È il default giusto per uno strumento generico — a volte gli spazi sono esattamente la modifica che stai cercando — ma significa che un file reindentato risulterà completamente riscritto. Se è la tua situazione, git diff -w ignora gli spazi e ti mostra cosa si è mosso davvero.