Come annullare in modo sicuro commit locali e remoti in Git

Padroneggia le tecniche essenziali di controllo delle revisioni Git per gestire e correggere in sicurezza errori locali e remoti. Questa guida illustra le differenze tra `git reset` (per riscrivere la cronologia locale utilizzando le modalità soft, mixed o hard) e `git revert` (per annullare in modo sicuro commit condivisi). Scopri come sfruttare `git reflog` come tua rete di sicurezza locale definitiva e comprendi le best practice per il force push.

38 visualizzazioni

Come annullare in sicurezza commit locali e remoti in Git

Quando si lavora con Git, la capacità di correggere errori è fondamentale per un flusso di lavoro di sviluppo fluido. Sia che tu abbia eseguito il commit troppo presto, incluso accidentalmente dati sensibili, o che tu abbia semplicemente bisogno di tornare indietro su una serie di modifiche, capire come annullare in sicurezza le operazioni locali e remote è fondamentale. Questa guida demistifica gli strumenti principali per la pulizia del controllo della revisione: git reset, git revert e git reflog. Padroneggiare questi comandi ti consente di gestire la tua cronologia con sicurezza, assicurandoti di poter cancellare o invertire le modifiche senza perdere lavoro prezioso.

È fondamentale distinguere tra la modifica della cronologia locale (che è generalmente sicura) e la riscrittura della cronologia remota condivisa (che può causare problemi significativi ai collaboratori). Ci concentreremo sui metodi più sicuri per entrambi gli scenari.

Comprensione degli strumenti principali per l'annullamento delle modifiche

Git offre diversi meccanismi per gestire i commit che si desidera rimuovere o modificare. La scelta dello strumento dipende interamente dal fatto che il commit sia stato inviato a un repository condiviso e se si desidera cancellare completamente il commit dalla cronologia o introdurre un nuovo commit che ne annulli gli effetti.

1. git reset: Riscrivere la cronologia locale

git reset è lo strumento più potente (e potenzialmente pericoloso) per manipolare la cronologia dei commit locali. Sposta il puntatore del branch corrente (HEAD) a un commit diverso. L'aspetto critico di git reset è come gestisce l'area di staging e la directory di lavoro, controllato dalle opzioni --soft, --mixed e --hard.

Modalità di git reset

Modalità Effetto su HEAD Effetto sull'area di staging (Index) Effetto sulla directory di lavoro
--soft Sposta il puntatore HEAD. Invariato. Invariato.
--mixed (Predefinito) Sposta il puntatore HEAD. Ripristina per corrispondere al nuovo HEAD. Invariato.
--hard Sposta il puntatore HEAD. Ripristina per corrispondere al nuovo HEAD. Ripristina per corrispondere al nuovo HEAD (ATTENZIONE: le modifiche non committate vengono perse).

Caso d'uso: Usa git reset quando hai eseguito commit di modifiche localmente che ti sei reso conto non avrebbero dovuto essere committate, o se vuoi rimuovere dallo staging modifiche mantenendole nella tua directory di lavoro.

Esempi pratici per git reset

A. Annullare un commit (mantenendo le modifiche nell'area di staging):
Se hai eseguito un commit ma ti sei reso conto di dover aggiungere altri file prima di inviarlo, usa --soft:

# Sposta HEAD indietro di un commit, ma mantiene le modifiche nell'area di staging (pronte per essere aggiunte al prossimo commit)
git reset --soft HEAD~1

B. Rimuovere dallo staging e mantenere le modifiche localmente:
Se hai eseguito un commit e ora vuoi rimuovere tutto dallo staging ma mantenere le modifiche ai file nella tua directory di lavoro:

# Sposta HEAD e rimuove dallo staging le modifiche, ma mantiene i file modificati nel tuo spazio di lavoro
git reset --mixed HEAD~1
# oppure semplicemente:
git reset HEAD~1

C. Cancellazione completa (PERICOLOSO per commit recenti):
Se vuoi scartare completamente l'ultimo commit e scartare tutte le modifiche locali apportate da quel commit (tornando allo stato del commit precedente):

# ATTENZIONE: Questo scarta tutto il lavoro dal commit specificato.
git reset --hard HEAD~1

⚠️ Avviso sulla migliore pratica: Non usare mai git reset --hard su commit che sono già stati inviati a un repository remoto condiviso, a meno che tu non sia assolutamente sicuro che nessun altro abbia basato il proprio lavoro su quei commit. Riscrivere la cronologia condivisa causa mal di testa ai collaboratori.

2. git revert: Annullare in sicurezza i commit inviati

git revert è il metodo preferito per annullare le modifiche che sono già state condivise pubblicamente. Invece di riscrivere la cronologia, git revert crea un nuovo commit che annulla specificamente le modifiche introdotte da un commit precedente specifico. La cronologia rimane intatta, rendendola compatibile con la collaborazione.

Caso d'uso: Usa git revert quando è necessario annullare modifiche già presenti sul server remoto (ad esempio, una funzionalità che ha introdotto un bug).

Esempio pratico per git revert

Supponiamo che il commit hash a1b2c3d4 abbia introdotto un bug. Per creare un commit che annulli i suoi effetti:

# Crea un nuovo commit che inverte le modifiche introdotte in a1b2c3d4
git revert a1b2c3d4

# Se non vuoi aprire un editor per il messaggio del commit di revert:
git revert -n a1b2c3d4
# Quindi esegui manualmente il commit delle modifiche
git commit -m "Revert: Corretto problema introdotto da a1b2c3d4"

3. git reflog: La rete di sicurezza

Cosa succede se esegui un git reset --hard distruttivo e ti rendi conto di aver appena cancellato ore di lavoro? È qui che entra in gioco git reflog. Il Reference Log (reflog) tiene traccia di ogni modifica a HEAD nel tuo repository locale: ogni commit, reset, merge e checkout. È la tua cronologia di annullamento locale.

Caso d'uso: Recuperare commit persi a causa di un git reset aggressivo o navigare verso uno stato temporaneo visitato in precedenza.

Visualizzazione e recupero con git reflog

Innanzitutto, visualizza la cronologia dei movimenti di HEAD:

$ git reflog

a1b2c3d HEAD@{0}: reset: moving to HEAD~2
4f5e6d7 HEAD@{1}: commit: Finita feature X
b8a9c0d HEAD@{2}: commit: Avviata implementazione feature X
...

Se per errore hai eseguito un reset oltre il commit 4f5e6d7 (che era HEAD@{1}), puoi facilmente ripristinarlo:

# Esegui il reset allo stato in cui ti trovavi un passo prima dell'azione distruttiva
git reset --hard HEAD@{1}

Suggerimento: Le voci di git reflog vengono solitamente conservate per 90 giorni localmente. È la rete di sicurezza locale definitiva per operazioni che coinvolgono reset o l'eliminazione di branch.

Annullamento delle modifiche remote (Push forzati)

Se hai usato git reset per rimuovere commit che erano già stati inviati al repository remoto, la tua cronologia locale divergerà dalla cronologia remota. Git impedirà un git push standard perché comporta aggiornamenti non fast-forward.

Per sincronizzare il repository remoto con la tua nuova cronologia locale riscritta, devi utilizzare un push forzato.

# Usa --force-with-lease per un'alternativa più sicura a --force
git push origin <nome-branch> --force-with-lease

Perché usare --force-with-lease?

--force-with-lease è più sicuro dell'opzione grezza --force. Controlla per assicurarsi che nessuno abbia inviato nuovi commit al branch remoto da quando hai fatto l'ultimo pull. Se qualcuno ha aggiornato il repository nel frattempo, il push verrà rifiutato, impedendoti di cancellare inconsapevolmente il loro lavoro.

Riepilogo su quando usare ciascun comando

La scelta dello strumento giusto dipende dallo stato e dalla destinazione dei tuoi commit:

  1. Commit locali, non inviati: Usa git reset (soft, mixed o hard) per regolare l'area di staging o cancellare completamente la cronologia.
  2. Commit inviati, condivisi: Usa git revert per creare un commit opposto, preservando la cronologia pubblica.
  3. Perdita accidentale di cronologia: Usa git reflog per trovare e ripristinare stati HEAD persi in precedenza.
  4. Forzare aggiornamenti remoti: Usa git push --force-with-lease solo dopo aver riscritto in sicurezza la cronologia localmente utilizzando git reset su commit inviati.