modulo 04 / lezione 17
Riscrivere la storia: rebase, amend, cherry-pick
git rebase: spostare commit su un'altra base
Capire cosa fa il rebase davvero e perché può produrre conflitti uno per commit.
git rebase: spostare commit su un’altra base
Rebase significa prendere una serie di commit e riprodurli sopra un’altra base. Non integra due storie come un merge: crea nuovi commit equivalenti su un punto diverso del grafo.
È uno strumento potente per mantenere una storia lineare, ma va usato con disciplina perché riscrive hash.
Perché questa lezione conta
Molti sviluppatori usano rebase come incantesimo prima della PR. Funziona finché non compare un conflitto, un force push o un collega che aveva basato lavoro sul branch vecchio.
Capire il replay dei commit permette di usare rebase senza paura e senza danneggiare branch condivisi.
Obiettivo operativo
Alla fine della lezione devi saper fare rebase di un branch personale su main, risolvere conflitti per commit, abortire se necessario e capire quando preferire merge.
Prima e dopo
Prima:
C -- D feat
/
A -- B -- E -- F main
feat è partito da B, ma main è andato avanti.
git switch feat
git fetch origin
git rebase origin/main
Dopo:
A -- B -- E -- F -- C' -- D' feat
C' e D' sono nuovi commit. Hanno contenuto simile a C e D, ma hash diverso perché hanno parent diverso.
Rebase non è merge
Merge conserva la divergenza:
C -- D
/ \
A -- B -- E -- M
Rebase riscrive la linea:
A -- B -- E -- C' -- D'
La storia diventa più lineare, ma perdi la forma originale della divergenza.
Conflitti durante rebase
Se un commit non si applica:
git status
Risolvi i file, poi:
git add file-risolto
git rebase --continue
Per saltare un commit:
git rebase --skip
Usalo con molta cautela: stai decidendo che quel commit non serve più.
Per annullare tutto:
git rebase --abort
Torna allo stato prima del rebase.
Quando usare rebase
Usalo per:
- aggiornare branch personale su
main; - preparare una PR lineare;
- evitare merge commit locali inutili;
- ripulire storia prima della condivisione.
Evitalo su:
- branch condivisi da più persone;
- branch di release;
- storia già usata come base da altri;
- situazioni in cui preservare il merge è informazione importante.
Rebase e push
Dopo rebase, il push normale può fallire:
git push
Perché il remoto ha la storia vecchia. Se il branch è tuo e il force push è previsto:
git push --force-with-lease
Mai usare --force per abitudine. --force-with-lease protegge dal sovrascrivere lavoro remoto che non hai visto.
Laboratorio guidato
In un repo demo:
- crea
maincon commitAeB; - crea branch
featcon commitCeD; - torna a
maine aggiungiE; - fai rebase di
featsumain; - osserva hash prima/dopo;
- crea un conflitto e risolvilo con
--continue; - ripeti e usa
--abort.
Output atteso
Un diagramma prima/dopo e una regola:
Rebase branch personale per linearità.
Merge branch condiviso per preservare storia.
Force push solo con --force-with-lease e solo se previsto.
Errori comuni
Pensare che rebase sposti gli stessi commit. Crea commit nuovi.
Rebasare branch condivisi. Obbliga altri a risincronizzare manualmente.
Usare --skip senza capire. Può eliminare modifiche necessarie.
Risoluzione conflitti frettolosa. Durante rebase il conflitto può ripresentarsi per commit successivi.
Checklist di verifica
- so disegnare rebase prima/dopo;
- so usare
--continue,--abort,--skip; - so perché cambiano gli hash;
- so quando serve
--force-with-lease; - so quando preferire merge.
Esercizi
Base · facile
Riporta il tuo branch sopra l’ultima versione di main con un rebase.
Soluzione
git switch feature
git rebase main # riapplica i commit di feature sopra l'attuale main
Il rebase “ricuce” i tuoi commit sopra la punta aggiornata di main, come se li avessi appena scritti. Risultato: una storia lineare, senza commit di merge.
Intermedio · medio
Spiega la differenza pratica tra git merge main e git rebase main su un branch feature, in termini di storia risultante.
Soluzione
merge main → crea un commit di merge; la storia mostra il punto di unione (non lineare)
rebase main → riscrive i commit di feature sopra main; storia lineare, niente commit di merge
merge preserva la storia reale (quando i rami si sono incontrati) ma la rende più intricata. rebase produce una sequenza lineare, più facile da leggere, al prezzo di riscrivere i commit (nuovi hash). Scelta tipica: rebase per pulire un branch personale prima della PR, merge per integrare preservando la traccia storica.
Sfida · difficile
Un rebase entra in conflitto a metà: mostra come risolverlo, proseguire o annullare tutto. Spiega perché il rebase può chiedere di risolvere lo stesso tipo di conflitto più volte.
Soluzione
# durante il rebase, su un conflitto:
# 1. risolvi i file, poi:
git add file-risolto.txt
git rebase --continue
# per saltare un commit problematico:
git rebase --skip
# per annullare tutto e tornare allo stato iniziale:
git rebase --abort
Il rebase riapplica i commit uno alla volta: se più commit toccano la stessa zona di codice, lo stesso conflitto può ripresentarsi a ogni commit riapplicato. Risolvi, git add, git rebase --continue per ciascuno; --abort riporta tutto al punto di partenza in sicurezza. (git rerere può ricordare risoluzioni ripetute, ma va abilitato.)
Collegamenti
- Modulo: Riscrivere la storia: rebase, amend, cherry-pick — lezione 17 del corso.
- Il rebase interattivo della prossima lezione usa lo stesso principio di riscrittura.