Come applicare una patch a un file (e creare patch) in Linux

Il comando `patch` in ambiente Linux è uno strumento essenziale per trasferire modifiche da un insieme di file sorgente a un altro, in modo rapido e affidabile. Scopriamo insieme come utilizzarlo in maniera efficiente.

`patch` e `diff`: Strumenti complementari

Supponiamo di avere un file di testo sul tuo computer e di ricevere da un collaboratore una versione modificata dello stesso file. Come trasferire velocemente tutte le modifiche dal file modificato a quello originale? Qui entrano in gioco i comandi `patch` e `diff`, disponibili in Linux e in altri sistemi operativi Unix-like, come macOS.

Il comando `diff` analizza due varianti di un file, individuando e segnalando le discrepanze. Queste differenze possono essere salvate in un file, spesso chiamato “file patch”.

Il comando `patch`, a sua volta, interpreta il file patch come una serie di istruzioni. Seguendo queste indicazioni, le modifiche presenti nella versione aggiornata vengono riprodotte nel file originale.

Immagina ora di dover applicare queste operazioni a un’intera cartella di file di testo, contemporaneamente. Ecco la potenza del comando `patch`.

A volte, invece di inviare l’intero insieme di file modificati, si preferisce condividere solo il file patch. Questo approccio è molto utile quando si tratta di numerosi file, perché consente di inviare un unico file, semplificando il download e la condivisione.

Come si utilizza concretamente il file patch per aggiornare i file? Nonostante possa suonare complicato, ti guideremo attraverso questo processo.

Il comando `patch` è frequentemente utilizzato da chi lavora con codice sorgente, ma si rivela efficace anche con qualsiasi tipo di file di testo, indipendentemente dal loro scopo.

Caso pratico: La nostra dimostrazione

Nello scenario di esempio, abbiamo una directory chiamata `lavoro`, che contiene a sua volta due cartelle: `working` e `latest`. La directory `working` contiene una serie di file di codice sorgente, mentre `latest` contiene la versione più recente di questi file, alcuni dei quali sono stati modificati.

La directory `working` rappresenta una copia di backup della versione corrente dei file. Non è l’unica copia esistente, ma una sicurezza in più.

Individuare le differenze tra file con `diff`

Il comando `diff` identifica le divergenze tra due file, mostrando le righe modificate direttamente nel terminale.

Consideriamo un file denominato `slang.c`. Confronteremo la versione presente nella directory `working` con quella in `latest`.

L’opzione `-u` (unificata) istruisce `diff` a includere anche le righe di testo non modificate, sia precedenti che successive alle sezioni modificate. Queste righe, chiamate “righe di contesto”, sono utili per permettere a `patch` di individuare con precisione la posizione in cui apportare una modifica nel file originale.

Specifichiamo i nomi dei file in modo che `diff` sappia quali confrontare. Il file originale viene indicato per primo, seguito dal file modificato. Ecco il comando da digitare:

diff -u working/slang.c latest/slang.c

L’output di `diff` mostra le differenze tra i file. Se i file fossero identici, non sarebbe visualizzato alcun output. La presenza di output da `diff` conferma l’esistenza di modifiche tra le due versioni e la necessità di applicare una patch al file originale.

Creazione del file patch

Per salvare le differenze in un file patch, usa il comando seguente. È lo stesso di prima, ma l’output di `diff` viene reindirizzato in un file chiamato `slang.patch`:

diff -u working/slang.c latest/slang.c > slang.patch

Per fare in modo che `patch` agisca sul file patch e modifichi il file `working/slang.c`, usa il comando seguente. L’opzione `-u` (unificata) indica a `patch` che il file patch contiene righe di contesto unificate. In altre parole, se si usa l’opzione `-u` con `diff`, la si deve usare anche con `patch`.

patch -u working/slang.c -i slang.patch

Se tutto è andato bene, otterrai un output che indica che `patch` sta correggendo il file.

Backup del file originale

Si può istruire `patch` a creare una copia di backup dei file prima di modificarli utilizzando l’opzione `-b` (backup). L’opzione `-i` (input) indica a `patch` il nome del file patch da usare:

patch -u -b working/slang.c -i slang.patch

Il file viene patchato come prima, senza differenze evidenti nell’output. Tuttavia, all’interno della cartella `working`, noterai la creazione di un file chiamato `slang.c.orig`. La data e l’ora di creazione dei file dimostrano che `slang.c.orig` è il file originale, mentre `slang.c` è il nuovo file generato da `patch`.

Uso di `diff` con directory

È possibile usare `diff` per creare un unico file patch che racchiuda tutte le differenze tra i file di due directory. Successivamente, si potrà utilizzare questo file patch con `patch` per applicare le modifiche ai file nella cartella `working` con un solo comando.

Le opzioni che utilizzeremo con `diff` sono `-u` (contesto unificato), già vista prima; `-r` (ricorsiva) per indicare a `diff` di esplorare anche le sottocartelle; e `-N` (nuovo file).

L’opzione `-N` indica a `diff` come gestire i file presenti nella directory `latest` ma non in `working`. Questa opzione forza `diff` a includere istruzioni nel file patch per fare in modo che `patch` crei anche i file mancanti in `working`, ma presenti in `latest`.

Puoi raggruppare le opzioni insieme, usando un solo trattino (-).

Nota che forniamo solo i nomi delle directory, senza specificare a `diff` di guardare file specifici:

diff -ruN working/ latest/ > slang.patch

La parte iniziale del file mostra le differenze tra le due versioni di `slang.c`.

Scorrendo il file patch, possiamo vedere anche le modifiche in un altro file chiamato `structs.h`. Ciò conferma che il file patch contiene le differenze tra diverse versioni di più file.

Controllo preliminare

Applicare una patch a una vasta collezione di file può essere rischioso. Per questo, useremo l’opzione `–dry-run` per verificare che tutto sia corretto prima di procedere con le modifiche.

L’opzione `–dry-run` indica a `patch` di eseguire tutte le operazioni, ad eccezione delle modifiche effettive. In questo modo, `patch` effettuerà tutti i controlli preliminari sui file e segnalerà eventuali problemi, senza alterare nulla.

Se non vengono segnalati problemi, possiamo ripetere il comando senza l’opzione `–dry-run` e applicare con sicurezza le patch.

L’opzione `-d` (directory) indica a `patch` la cartella in cui operare.

Notiamo che non stiamo utilizzando l’opzione `-i` (input) per specificare a `patch` il file patch da cui ricavare le istruzioni. Invece, stiamo reindirizzando il file patch a `patch` usando il simbolo `<`.

patch --dry-run -ruN -d working < slang.patch

Nell’intera directory, `diff` ha individuato due file da patchare. Le istruzioni relative a queste modifiche sono state verificate da `patch`, e non sono stati segnalati problemi.

I controlli preliminari sono a posto; siamo pronti a procedere.

Applicazione delle patch alla directory

Per applicare effettivamente le patch ai file, eseguiamo il comando precedente senza l’opzione `–dry-run`.

patch -ruN -d working < slang.patch

Questa volta, ogni riga di output non inizia con “controllo”, ma con “patch”.

Non vengono segnalati problemi. Ora possiamo compilare il nostro codice sorgente e lavorare sull’ultima versione del software.

Risolvi le tue discrepanze

Questo è di gran lunga il modo più semplice e sicuro per usare le patch. Copiare i file di destinazione in una cartella e applicare la patch a quella cartella. Ri-copiarli quando sei sicuro che il processo di patch sia stato completato senza errori.