GIT | Ripristinare un precedente commit sul branch master
Come tornare a un commit precedente su master, scegliendo tra git revert (sicuro, mantiene la storia) e git reset --hard con push forzato (riscrive la storia), e perché su un branch condiviso conviene usare --force-with-lease.
Prima o poi capita: sul branch principale finisce del codice che non doveva arrivarci e serve riportare tutto a un commit precedente. Git offre due strade molto diverse, e sceglierla in base a chi altro lavora su quel branch evita un bel po’ di guai.
Serve in entrambi i casi l’identificativo del commit a cui vuoi tornare. Per trovarlo:
git log --oneline
Strada sicura: git revert
Se il branch è condiviso — cioè se qualcun altro l’ha già scaricato — questa è la strada da preferire. revert non cancella nulla: crea un nuovo commit che annulla le modifiche di quelli indesiderati, lasciando intatta la storia.
Per annullare un singolo commit:
git checkout master
git revert <hash_del_commit>
git push origin master
Per annullare tutti i commit successivi a un certo punto, indica l’intervallo (^.. include l’estremo iniziale):
git revert --no-commit <hash_del_commit_buono>..HEAD
git commit -m "Ripristino dello stato al commit <hash_del_commit_buono>"
git push origin master
Il vantaggio pratico è che non serve nessun push forzato: chi ha già il branch in locale riceve la correzione con un normale git pull, senza dover fare niente di speciale. Resta traccia sia dell’errore sia della correzione, che in un repository di lavoro è spesso un pregio, non un difetto.
Strada distruttiva: reset e push forzato
Se il branch è tuo e non l’ha ancora scaricato nessuno, puoi riscrivere la storia e far finta che quei commit non siano mai esistiti:
git checkout master
git reset --hard <hash_del_commit>
git push --force-with-lease origin master
Due precisazioni importanti su questi comandi.
git reset --hard scarta le modifiche non committate presenti nella working directory, senza chiedere conferma e senza possibilità di recupero. Se hai lavoro in corso che vuoi conservare, mettilo da parte prima con git stash.
--force-with-lease è preferibile al classico --force: rifiuta il push se sul remoto è arrivato qualcosa che tu non hai ancora scaricato. Con --force secco, i commit che un collega ha pushato nel frattempo vengono sovrascritti e vanno perduti; con --force-with-lease il push si interrompe e ti accorgi del problema. Costa un carattere in più e salva una giornata.
Molti repository proteggono il branch principale lato server (GitHub, GitLab, Azure DevOps): in quel caso il push forzato viene rifiutato a prescindere. È una protezione sensata — se la incontri, la risposta giusta è quasi sempre passare a
git revert.
Se hai già fatto reset e te ne penti
Finché il commit non è stato rimosso dal garbage collector, è recuperabile. git reflog elenca tutte le posizioni in cui HEAD è passato:
git reflog
git reset --hard HEAD@{1}
HEAD@{1} è la posizione immediatamente precedente all’ultima operazione; nell’elenco del reflog trovi l’hash esatto a cui tornare.
Una nota su master e main
Negli esempi il branch si chiama master, ma dal 2020 i repository creati su GitHub usano main come nome predefinito. Se i comandi restituiscono un errore del tipo pathspec 'master' did not match any file(s) known to git, sostituisci master con main. Per sapere come si chiama il branch corrente:
git branch --show-current
Leggi anche: