mercoledì 1 ottobre 2025

Git: Annullare le operazioni

Durante lo sviluppo, può capitare di voler annullare una modifica o correggere un errore. Git offre diversi strumenti per gestire queste situazioni, ma è fondamentale utilizzarli con cautela: alcune operazioni di undo non sono reversibili e, se usate in modo improprio, possono comportare la perdita definitiva di lavoro.


Correggere l’ultimo commit con --amend

Uno degli scenari più comuni si verifica quando si effettua un commit troppo presto, dimenticando di includere un file o inserendo un messaggio di commit errato. In questi casi, Git consente di sostituire l’ultimo commit con uno nuovo tramite l’opzione --amend:

git commit --amend

Git prende il contenuto dell’area di staging (index) e lo utilizza per generare un nuovo commit.

Se non sono state aggiunte nuove modifiche dallo scorso commit, verrà riaperto semplicemente l’editor dei messaggi di commit, consentendo di modificarlo.


Esempio pratico

Supponiamo di aver eseguito il commit e di esserci resi conto di aver dimenticato di inserire le modifiche in un file che volevamo aggiungere a questo commit. Possiamo rimediare aggiungendo il file con git add e rieseguire il commit con git commit --amend:

git commit -m "Initial commit"
git add file_dimenticato
git commit --amend

Il repository avrà un solo commit, che include anche file_dimenticato. Il commit precedente viene sovrascritto e sparisce dalla cronologia.


Aspetti importanti da considerare
  • Sostituzione, non modifica 
    - Amend non aggiorna un commit già esistente ma lo sostituisce con un nuovo commit dotato di un nuovo identificativo (SHA-1).
    - Di fatto, è come se il commit precedente non fosse mai esistito.
  • Pulizia della cronologia
    - L’opzione --amend è utile per evitare commit ridondanti.
  • Limitazione: solo commit locali
    - È fortemente sconsigliato eseguire git commit --amend su commit già pubblicati (pushed) in un repository remoto. Infatti, l’operazione riscrive la cronologia e, se accompagnata da un git push --force, può creare conflitti e problemi per gli altri collaboratori.


Tabella di sintesi: git commit --amend

ScenarioComandoRisultato
Modificare solo il messaggio di commitgit commit --amend (senza nuovi file in staging)Sovrascrive il messaggio dell’ultimo commit
Aggiungere file dimenticatigit add file + git commit --amendCrea un nuovo commit che sostituisce il precedente includendo anche file
Commit già pushato sul remotogit commit --amend + git push --forceSconsigliato: riscrive la cronologia e può rompere il lavoro dei collaboratori


Rimuovere un File dall’Area di Staging

Durante il lavoro quotidiano con Git, può capitare di inserire per errore più file nell’area di staging (index) utilizzando un comando troppo generico, come ad esempio:

git add *

In questo caso, se l’obiettivo era committare i file separatamente, è necessario rimuovere uno o più file dallo staging (unstaging), riportandoli allo stato di semplici modifiche nella working directory.

A partire dalla versione 2.23.0, Git ha introdotto il comando git restore, concepito come alternativa più intuitiva e leggibile rispetto a git reset per alcune operazioni di annullamento (undo).

L’obiettivo di questo nuovo comando è semplificare la sintassi e ridurre i rischi legati a usi impropri di git reset, che in passato potevano facilmente portare alla perdita involontaria di dati.

la differenza principale tra git restore e git reset HEAD sta nel loro scopo e nell'ambito d'azione:

  • git restore è un comando moderno e più sicuro, ideato per ripristinare i file nello stato in cui si trovavano in una versione precedente. Agisce sulla working directory (area di lavoro) o sulla staging area (area di "preparazione" prima del commit), ma non modifica la cronologia dei commit. È l'ideale per annullare modifiche locali, che non sono ancora state salvate nella cronologia.
  • git reset HEAD è un comando più vecchio, versatile e potente, che agisce primariamente sulla cronologia dei commit. Sposta il puntatore HEAD a un commit precedente, e di conseguenza, può influenzare l'area di staging e l'area di lavoro a seconda dell'opzione utilizzata (--soft, --mixed, o --hard). È uno strumento più "distruttivo" e va usato con cautela, specialmente su branch condivisi, perché modifica la storia del repository.


Identificare i file in staging con git status

Il comando git status non solo mostra lo stato della working directory e dell’area di staging, ma fornisce anche indicazioni utili su come annullare operazioni indesiderate.

Esempio pratico:

Per individuare il file da rimuovere all'area di staging

git status

git status
FIG 1 - git status

Come si può notare da FIG 1 , Git suggerisce direttamente il comando corretto per rimuovere un file dallo staging.

git restore --staged <file>

Nel nostro caso, per rimuovere il file main.cs dallo staging il comando sarà

git restore --staged main.cs

git restore --staged
FIG 2 - git restore --staged


Lo stesso risultato lo si ottiene con 

git reset HEAD <file>

Il comando git reset è spesso considerato pericoloso, poiché con opzioni come --hard può portare alla perdita definitiva di modifiche locali. Tuttavia, in questo scenario, l’uso di git reset HEAD è sicuro perché non modifica i contenuti della working directory ma agisce esclusivamente sull’indice, rimuovendo il file dallo staging.


Tabella di sintesi: Comandi per gestire lo staging

ComandoEffetto
git add fileAggiunge un file all’area di staging
git reset HEAD fileRimuove il file dallo staging, mantenendo le modifiche nella working directory
git restore --staged fileAlternativa moderna a git reset HEAD file, con la stessa funzione
git reset --hardPericoloso: annulla anche le modifiche nella working directory


 

Annullare le modifiche a un File

Può capitare di accorgersi che le modifiche apportate a un file non sono più necessarie e che si desidera ripristinare il contenuto all’ultima versione tracciata da Git (ossia l’ultima presente nell’area di staging o nel commit più recente).

In questi casi, Git mette a disposizione due comandi storicamente equivalenti, ma con differenze importanti: git restore (introdotto in Git ≥ 2.23.0) e git checkout (metodo tradizionale).


Esempio pratico con git restore

Supponiamo di aver modificato il file main.cs. Eseguiamo il comando

git status

git status
FIG 3 - git status

Per annullare le modifiche non salvate:

git restore main.cs

Dopo il comando, il file torna allo stato dell’ultimo commit o staging, eliminando definitivamente i cambiamenti locali.

git restore
FIG 4 - git restore


Esempio pratico con git checkout

git checkout -- main.cs

Anche in questo caso il file viene ripristinato, cancellando le modifiche locali.

git checkout
FIG 5 - git checkout



Differenze tra git restore e git checkout

Caratteristicagit restoregit checkout
DisponibilitàIntrodotto in Git ≥ 2.23.0Presente in tutte le versioni di Git
ObiettivoProgettato specificamente per annullare modifiche ai file (working directory e staging)Comando multifunzione (cambio branch, ripristino file, navigazione)
Chiarezza sintatticaPiù leggibile e autoesplicativo (restore indica chiaramente lo scopo)Sintassi più generica e meno intuitiva
Rischio di confusioneRidotto: il comando è dedicato solo al ripristinoElevato: git checkout gestisce operazioni molto diverse tra loro
Best PracticeConsigliato nei workflow moderni per annullare modifiche localiAncora valido, ma da usare con cautela per evitare ambiguità


Attenzione

  • Entrambi i comandi cancellano in modo irreversibile le modifiche locali non ancora salvate in un commit.
  • Prima di utilizzarli, è opportuno chiedersi se le modifiche possano essere utili in futuro.
  • In alternativa, si può ricorrere a git stash o a un nuovo branch, salvando temporaneamente i cambiamenti senza perderli.

È importante ricordare che soltanto ciò che è stato effettivamente sottoposto a commit può essere recuperato; le modifiche non salvate andranno invece perse in maniera definitiva.

In Git, i dati oggetto di commit risultano quasi sempre recuperabili, anche qualora appartengano a rami successivamente eliminati oppure siano stati sovrascritti tramite l’opzione --amend. Tutte le modifiche non ancora committate e andate perse non potranno, con ogni probabilità, essere ripristinate.


Best Practice

  • Usare git restore nei progetti moderni: più chiaro e meno soggetto a errori.
  • Evitare git checkout -- <file> se non strettamente necessario: mantiene la retrocompatibilità, ma può confondere.
  • Ricorrere a git stash o ai branch per soluzioni temporanee: ideale se non si vuole perdere il lavoro fatto.