W3docs

Introduzione

Scopri come Git riscrive la cronologia con git commit --amend, git reflog e git rebase — cosa fa ogni comando, quando usarlo e come operare in sicurezza.

In Git, la cronologia è la sequenza di commit che registra come il tuo progetto è cambiato nel tempo. Ogni commit è identificato da un hash univoco e punta al commit (o ai commit) che lo ha preceduto. La maggior parte delle volte ti limiti ad aggiungere elementi a quella catena. Ma a volte hai bisogno di modificare ciò che è già presente — correggere un errore di battitura in un messaggio di commit, unire diversi commit disordinati in un unico commit pulito, o recuperare un lavoro che pensavi di aver perso.

Ecco a cosa servono gli strumenti di riscrittura della cronologia di Git. I tre che utilizzerai più spesso sono:

  • git commit --amend — sostituisce il commit più recente.
  • git reflog — mostra ogni posizione a cui ha puntato HEAD, così puoi recuperare i commit "persi".
  • git rebase — riproduce una serie di commit su una nuova base, modificandoli facoltativamente lungo il percorso.

Questa pagina ti offre una panoramica pratica di ciascuno. I capitoli più approfonditi collegati sopra illustrano l'insieme completo di opzioni.

La regola d'oro della riscrittura della cronologia: non riscrivere mai i commit che hai già inviato a un branch condiviso. La riscrittura modifica gli hash dei commit, quindi chiunque abbia scaricato i vecchi commit riceverà conflitti. Riscrivi liberamente sui tuoi branch locali; lascia intatta la cronologia condivisa.

Modificare l'ultimo commit con git commit --amend

È facile dimenticare di mettere in stage un file, lasciare un errore di battitura in un messaggio di commit, o fare il commit troppo presto. Invece di aggiungere un commit "fix" separato, git commit --amend ti permette di sostituire l'ultimo commit con uno corretto.

Per correggere solo il messaggio del commit più recente:

git commit --amend -m "Add login validation"

Per aggiungere un file dimenticato all'ultimo commit, mettilo in stage prima, poi esegui l'amend senza cambiare il messaggio:

git add forgotten-file.js
git commit --amend --no-edit

Il flag --no-edit mantiene il messaggio esistente e lo riutilizza. Dietro le quinte, amend non modifica il vecchio commit sul posto — crea un commit completamente nuovo (con un nuovo hash) e sposta il branch in modo che punti ad esso. Il commit originale rimane indietro, senza riferimenti.

Poiché l'amend produce un nuovo hash, si applica la stessa regola d'oro: esegui l'amend solo su un commit che non hai inviato a un branch condiviso con altri.

Per una guida completa, vedi git commit --amend.

Recuperare i commit persi con git reflog

Il reflog (reference log) registra ogni modifica alla punta dei tuoi branch e a HEAD — ogni commit, checkout, reset, merge e rebase. Questo è il tuo salvagente: anche dopo una riscrittura o un hard reset, i vecchi commit sono ancora nel repository e il reflog ricorda dove si trovavano.

Visualizza il log con:

git reflog

Un output tipico si presenta così:

a1b2c3d HEAD@{0}: commit: Add login validation
9f8e7d6 HEAD@{1}: reset: moving to HEAD~1
4c3b2a1 HEAD@{2}: commit: Work in progress

Ogni voce mostra un hash del commit, un riferimento HEAD@{n} (quanti spostamenti fa) e cosa è accaduto. Se hai accidentalmente rimosso un commit con un reset, trovalo nel reflog e ripristinalo:

git reset --hard HEAD@{2}

Il comando git reflog dispone anche di sottocomandi come git reflog show, git reflog expire e git reflog delete. Da notare che le voci del reflog sono locali al tuo repository e scadono nel tempo (90 giorni per impostazione predefinita per le voci raggiungibili), quindi si tratta di uno strumento di recupero, non di un backup permanente. Vedi git reflog e il capitolo correlato git reset per i dettagli.

Eseguire il rebase su una nuova base con git rebase

git rebase prende una serie di commit e li riproduce sopra un commit base diverso. Il suo principale vantaggio è una cronologia lineare e leggibile: invece di un merge commit che collega due branch, il tuo lavoro appare come una linea pulita di commit.

Esistono due modalità:

  • La modalità standard applica i commit del tuo branch corrente sopra la punta di un altro branch. È l'alternativa al merge quando si integrano aggiornamenti da main:
git rebase main
  • La modalità interattiva ti permette di riordinare, modificare, combinare o eliminare i commit man mano che vengono riprodotti. Aggiungi l'opzione -i e passa il commit su cui vuoi fare il rebase:
git rebase -i <base-branch>

Un obiettivo interattivo comune è "gli ultimi N commit", ad esempio gli ultimi tre:

git rebase -i HEAD~3

Per una trattazione più approfondita, vedi git rebase e git rebase interactive.

Modificare i commit durante un rebase interattivo

Quando avvii un rebase interattivo, Git apre un editor che elenca i commit selezionati, ciascuno preceduto da un'azione. Cambi la parola chiave dell'azione per controllare cosa succede:

  • pick — mantieni il commit così com'è (il default).
  • reword (r) — metti in pausa per riscrivere il messaggio di quel commit.
  • squash (s) — unisci questo commit nel precedente e combina i loro messaggi in un editor.
  • fixup (f) — come squash, ma scarta il messaggio di questo commit e mantieni solo quello del precedente (nessun prompt dell'editor).
  • edit (e) — metti in pausa in modo da poter modificare il contenuto del commit.
  • drop (d) — rimuovi il commit completamente.

Un tipico elenco di operazioni di rebase si presenta così:

pick a1b2c3d Add login form
squash 9f8e7d6 Fix typo in label
fixup 4c3b2a1 Adjust spacing

Qui il secondo e il terzo commit vengono incorporati nel primo, lasciando un unico commit ordinato.

Perché i commit riscritti ottengono nuovi ID

Il rebase non sposta i commit originali — crea commit nuovi con le stesse modifiche ma genitori diversi, e quindi hash diversi. I vecchi commit diventano privi di riferimenti (ancora recuperabili tramite il reflog per un certo periodo). Anche i commit contrassegnati con pick ottengono nuovi ID se un commit precedente nella sequenza è stato modificato, perché il loro genitore è cambiato.

Unire commit durante un rebase interattivo

Questo è esattamente il motivo per cui la regola d'oro è importante: nuovi hash significano una cronologia divergente per chiunque avesse i vecchi commit.

Esercizio

Pratica
Quali delle seguenti affermazioni sono vere riguardo ai meccanismi per riscrivere la cronologia in Git?
Quali delle seguenti affermazioni sono vere riguardo ai meccanismi per riscrivere la cronologia in Git?
Was this page helpful?