Programma
Possiamo compromettere un’applicazione effettuando il merge di codice problematico, sia per aver integrato per errore del lavoro non finito nel branch principale, sia per aver trascurato un bug critico sfuggito ai test automatici.
In questo articolo ti guiderò nell’uso di git revert per annullare in sicurezza un merge, mantenendo intatta la cronologia dei commit e preservando l’integrità del progetto.
Come funziona git revert
Possiamo pensare a git revert come alla versione Git del comando Annulla. Tuttavia, il comando git revert non elimina i commit e non riporta il branch a uno stato precedente. Invece, crea un nuovo commit che annulla le modifiche di un commit specifico.

La sintassi per revertare un commit con hash <commit_hash> è:
git revert <commit_hash>
Possiamo elencare i commit insieme ai loro identificatori hash usando il comando git log. L’output di git log elenca i commit dal più recente al più vecchio, così:

Per esempio, per revertare il commit che implementa la funzione di sottrazione, useremmo il comando:
git revert 7ba24a3e62d4d37182428ccfaa070baa222b1151
Usando git revert possiamo annullare le modifiche di un commit specifico senza toccare la cronologia dei commit.
Nota che git revert non è magico e, a seconda della cronologia dei commit, può generare conflitti che dovranno essere risolti manualmente.
Vantaggi di git revert rispetto alle modifiche manuali
Perché git revert è utile se potremmo comunque dover risolvere un conflitto a mano? Non sarebbe più semplice annullare le modifiche manualmente? Ecco i suoi vantaggi:
- Preserva la cronologia:
git revertcrea un nuovo commit che annulla le modifiche di un commit specificato preservando l’intera cronologia. Questo aiuta a mantenere una storia trasparente di cambiamenti e ripristini. - Reversal atomico: garantisce che i ripristini siano atomici e coerenti. Quando eliminiamo e committiamo manualmente le modifiche, c’è rischio di errore umano.
- Consapevolezza dei conflitti: ci avvisa tramite conflitti se esistono integrazioni o modifiche dipendenti dal commit originale. Può sembrare scomodo, ma protegge da effetti collaterali indesiderati.
- Metadati: il nuovo commit creato da git revert include metadati e un messaggio che descrive contestualmente cosa è stato annullato, facilitando la comprensione futura. Senza
git revert, questo contesto potrebbe andare perso.
Annullare un merge in diversi scenari
In questa sezione vediamo come annullare un merge. Per l’esempio, supponiamo di fare il merge di un branch chiamato feature nel branch main eseguendo il comando dal branch main:
git merge feature
Quanto impariamo qui si applica a qualsiasi coppia di branch sostituendone i nomi in modo appropriato.
Revertare un merge che non ha un commit associato
Il comando git merge non crea sempre un nuovo commit. Un commit viene creato solo se il branch main è divergente dal branch feature. Poiché git revert richiede un commit su cui operare, in questo caso non possiamo usarlo.
I branch main e feature divergono quando vengono creati nuovi commit su main che non sono antenati del branch feature. In altre parole, sono stati creati nuovi commit su main dopo la creazione di feature.
Se i branch non sono divergenti, quando eseguiamo il comando git merge feature su main, Git userà il fast-forward per effettuare il merge. Questo significa che sposta la HEAD del branch main sulla HEAD del branch feature.

Possiamo osservare che ciò è avvenuto guardando il risultato di git merge:

Per annullare un merge di questo tipo, dobbiamo solo riportare la HEAD del branch main alla posizione precedente. Per farlo:
- Identifica la
HEADprecedente usandogit reflog - Reimposta la
HEADa quella precedente usandogit reset --hard <previous_head>, sostituendo<previous_head>con laHEADprecedente.
L’output di git reflog apparirà più o meno così:

Possiamo identificare la HEAD precedente guardando la riga che dice “checkout: moving from feature to main” (riporta feature e main perché sono i nomi dei nostri branch).
In questo caso, la precedente head è fe59838. Per spostare la HEAD di main indietro a quella e annullare il merge, usiamo quindi il comando:
git reset --hard fe59838

Revertare un merge che ha un commit associato
Se i branch main e feature sono divergenti, quando facciamo il merge dei due branch viene creato un nuovo commit, detto merge commit.

Il merge commit applica le modifiche da un branch a un altro. In questo caso, le modifiche in feature vengono applicate al branch main.
Per annullare le modifiche su main, usiamo git revert sul merge commit. Questo creerà un nuovo commit che annulla le modifiche introdotte su main con il merge, ripristinando di fatto lo stato di main a prima del merge.
Per prima cosa, dobbiamo identificare l’hash del merge commit. Possiamo farlo usando il comando git log:

Poiché il merge commit ha due genitori, la sintassi di git revert è leggermente diversa. Dobbiamo usare l’opzione -m 1 per specificare che vogliamo revertare le modifiche rispetto al branch main:
git revert -m 1 b8dab2c8611e324ed0d273133987415350e6d10d

Risoluzione dei conflitti durante il revert di un commit
A volte possono sorgere conflitti durante il revert di un commit, soprattutto se il commit che stiamo revertando è in conflitto con modifiche successive nel codebase. In questi casi:
- Git mette in pausa il revert: dobbiamo risolvere manualmente i conflitti. Git segnalerà i file in conflitto e richiederà un intervento.
- Risolvi i conflitti: apri ogni file in conflitto, risolvi i blocchi segnalati da Git e salva le modifiche.
- Metti in stage i file risolti:
git add <file-path> - Continua il revert:
git revert --continue
Conclusione
Usare git revert per annullare i merge commit garantisce che ogni modifica e correzione sia documentata nella cronologia dei commit.
Inoltre, capire quando applicare git reset rispetto a git revert ci permette di prendere decisioni migliori, soprattutto considerando i flussi di lavoro collaborativi o le modifiche solo locali.
Puoi approfondire l’argomento nella sezione FAQ qui sotto. Se vuoi imparare di più su Git, ti consiglio queste risorse:
FAQ
Posso revertare un merge già pushato con git revert?
Sì, questo è il principale caso d’uso di git revert sui merge. Se le modifiche sono locali, allora git reset è un modo più semplice per annullare il merge.
Perché gli altri vedano le modifiche, dobbiamo pusharle sul repository remoto. Quindi, per annullare un merge già pushato, facciamo:
git revert -m 1 <merge_commit_hash>
git push origin <branch_name>
Sostituendo <merge_commit_hash> con l’hash del merge commit e <brach_name> con il nome del branch su cui è stato eseguito il merge.
Il merge commit verrà eliminato dopo aver usato git revert?
No. Il merge commit rimane nella cronologia, ma le sue modifiche vengono annullate dal nuovo commit. Il punto di annullare un merge con git revert è proprio preservare la cronologia dei commit.
Ci sono modi diversi da git revert per annullare un merge?
Un modo alternativo per annullare un merge è usare git reset. Questo metodo resetta il branch a uno stato precedente al merge commit. Questo approccio è più invasivo e può causare problemi se altri collaboratori hanno già basato il loro lavoro sui commit che stiamo per resettare. Pertanto è consigliato solo quando siamo sicuri che non influenzerà altri.
git reset --hard <commit_hash>
git push --force
Sostituendo <commit_hash> con l’hash del commit a cui vogliamo tornare.
Quando dovrei usare git reset per annullare un merge?
Consiglio di usare git reset per annullare un merge solo quando il merge è avvenuto in locale e non è ancora stato pushato.


