Nel mondo dello sviluppo frontend moderno, la collaborazione e la gestione del codice sono fondamentali. Git è lo standard de facto per il controllo di versione, ma padroneggiarne i flussi di lavoro più avanzati può fare la differenza tra un progetto ordinato e uno caotico. Uno strumento potente, spesso temuto ma incredibilmente utile, è git rebase --interactive
. Se hai mai desiderato una history dei commit più pulita o hai sudato freddo di fronte a un conflitto di merge complesso, questa guida fa per te.
Impareremo cos’è git rebase --interactive
, quando è opportuno usarlo (e quando non farlo), e soprattutto come sfruttarlo per riscrivere la storia dei tuoi commit locali e risolvere conflitti in modo chirurgico, mantenendo il tuo branch di sviluppo snello e comprensibile. Preparati a portare le tue skill di Git al livello successivo!
Se sei nuovo a Git o vuoi rinfrescare le basi, ti consiglio di dare un’occhiata alla nostra guida definitiva a Git & GitHub per team frontend.
Cos’è git rebase
? Una breve introduzione
Prima di tuffarci nella modalità interattiva, capiamo il concetto base di git rebase
. In parole semplici, rebase
(ribasare) permette di prendere una serie di commit da un branch e “riapplicarli” sopra un altro commit (solitamente la punta di un altro branch, come main
o develop
).
Immagina di aver creato un branch feature/nuova-interfaccia
partendo da main
. Mentre lavoravi sulla tua feature, altri sviluppatori hanno aggiunto nuovi commit a main
.
A---B---C feature/nuova-interfaccia
/
D---E---F---G main
Usando git rebase main
mentre sei sul branch feature/nuova-interfaccia
, Git prenderà i commit A
, B
, e C
, li metterà temporaneamente da parte, aggiornerà il tuo branch all’ultimo commit di main
(G
), e poi riapplicherà A
, B
, e C
uno alla volta sopra G
.
A'--B'--C' feature/nuova-interfaccia
/
D---E---F---G main
Il risultato è una history lineare: sembra che tu abbia iniziato a lavorare sulla tua feature dopo l’ultimo aggiornamento di main
. Questo contrasta con git merge
, che creerebbe un “merge commit” per unire le due storie:
A---B---C --------- M feature/nuova-interfaccia
/ /
D---E---F---G ----------- main
Il rebase produce una history più pulita e facile da seguire, ma attenzione: riscrive la storia dei commit (nota come A'
, B'
, C'
sono nuovi commit, anche se contengono le stesse modifiche di A
, B
, C
).
Entra in scena git rebase --interactive
(o git rebase -i
)
La vera magia inizia con l’opzione --interactive
(abbreviata in -i
). Mentre un git rebase
standard riapplica semplicemente i commit, git rebase --interactive
ti dà il controllo prima che i commit vengano riapplicati. Ti permette di modificare la serie di commit che stai per ribasare.
Quando esegui un comando come git rebase -i HEAD~3
(ribasa interattivamente gli ultimi 3 commit) o git rebase -i main
(ribasa interattivamente il tuo branch corrente su main
), Git apre il tuo editor di testo predefinito con un file che elenca i commit coinvolti e le azioni che puoi intraprendere per ciascuno:
pick f7f3f6d Aggiunta funzione X
pick 310154e Correzione bug Y
pick a5f4a0d Refactor componente Z
# Rebase 12a3b4c..a5f4a0d onto 12a3b4c (3 commands)
#
# Comandi:
# p, pick <commit> = usa il commit
# r, reword <commit> = usa il commit, ma modifica il messaggio
# e, edit <commit> = usa il commit, ma fermati per fare modifiche
# s, squash <commit> = usa il commit, ma fondilo nel commit precedente
# f, fixup <commit> = come "squash", ma scarta il messaggio di questo commit
# x, exec <command> = esegui un comando di shell
# b, break = fermati qui (continua poi con 'git rebase --continue')
# d, drop <commit> = rimuovi il commit
# l, label <label> = assegna un nome a questo punto nella history
# t, reset <label> = resetta HEAD a un label precedente
# m, merge [-C <commit> | -c <commit>] <branch> = crea un merge commit
#
# Questi commit vengono riapplicati uno alla volta sulla base del commit originale.
# Se rimuovi una riga qui, QUEL COMMIT ANDRÀ PERSO.
# Tuttavia, se rimuovi tutto, il rebase verrà annullato.
#
Questo ti dà un potere enorme per:
- Riordinare i commit: Cambia l’ordine delle righe
pick
. - Modificare i messaggi: Cambia
pick
inreword
(r
). - Unire commit: Cambia
pick
insquash
(s
) ofixup
(f
).squash
ti permette di combinare i messaggi,fixup
scarta il messaggio del commit che stai fondendo. Utile per unire piccole correzioni (“fix typo”, “WIP”) in commit più significativi. - Dividere commit: Usa
edit
(e
) per fermarti a un certo commit, fare modifiche (es.git reset HEAD^
, poigit add -p
egit commit
multipli), e infinegit rebase --continue
. - Rimuovere commit: Cancella la riga o usa
drop
(d
). Fai attenzione! - Modificare il codice di un commit: Usa
edit
(e
), apporta le modifiche, faigit add .
e poigit commit --amend
, infinegit rebase --continue
.
Quando e Perché Usare git rebase -i
?
L’uso principale di git rebase -i
è pulire la tua history di commit locale prima di condividerla (es. pushando su un repository remoto o aprendo una Pull Request).
Scenari ideali:
- Preparare una Pull Request: Prima di chiedere una revisione, puoi usare
git rebase -i
per:- Riordinare i commit in modo logico.
- Unire piccoli commit di fix o WIP in commit più grandi e significativi.
- Riscrivere messaggi di commit poco chiari.
- Assicurarti che la tua feature branch sia aggiornata rispetto al branch principale (
main
odevelop
) con una history lineare.
- Correggere errori: Ti accorgi di un errore in un commit precedente (ma non ancora pushato)? Puoi usare
edit
per correggerlo. - Organizzare il lavoro: Hai fatto diversi commit disordinati durante lo sviluppo?
rebase -i
ti aiuta a presentarli in modo professionale.
La Regola d’Oro del Rebase:
Non ribasare MAI commit che sono già stati pushati su un branch condiviso (usato da altri).
Riscrivere la storia di un branch pubblico causa enormi problemi ai tuoi collaboratori, che avranno copie della “vecchia” storia. Quando faranno git pull
, Git vedrà storie divergenti e creerà confusione. Per integrare modifiche da un branch condiviso nel tuo lavoro, usa git merge
o git pull
(che spesso fa un merge per default). Il rebase è principalmente per la tua storia locale.
Risolvere Conflitti durante git rebase --interactive
Ecco il cuore della questione. I conflitti durante un rebase (interattivo o meno) accadono quando Git tenta di riapplicare un tuo commit che modifica le stesse linee di codice cambiate nei commit “sottostanti” (quelli del branch su cui stai ribasando, es. main
).
Supponiamo di avere questa situazione:
A---B---C feature/mia-feature (Tu hai modificato il file `style.css` nel commit B)
/
D---E---F---G main (Qualcun altro ha modificato `style.css` nel commit F)
Decidi di ribasare la tua feature su main
:
git checkout feature/mia-feature
git rebase -i main
Git prova a riapplicare i commit A
, B
, C
sopra G
. Quando arriva a riapplicare il tuo commit B
(che ha modificato style.css
), si accorge che anche il commit F
(ora parte della storia sottostante) ha modificato le stesse righe in style.css
. Conflitto!
Git si ferma e ti informa:
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
error: could not apply B... Descrizione del commit B
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add <conflicted_file>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply B... Descrizione del commit B
Cosa fare? Ecco la procedura passo-passo:
Identifica i file in conflitto: Il messaggio di Git te lo dice (CONFLICT (content): Merge conflict in style.css
). Puoi anche usare git status
:
git status
Output (esempio):
interactive rebase in progress; onto abc1234
Last command done (1 command done):
pick A Descrizione commit A
Next command to do (1 remaining command):
pick B Descrizione commit B
You are currently rebasing branch 'feature/mia-feature' on 'abc1234'.
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: style.css
no changes added to commit (use "git add" and/or "git commit -a")
La sezione Unmerged paths
elenca i file con conflitti.
Apri il file in conflitto: Apri style.css
nel tuo editor preferito. Troverai dei marcatori speciali inseriti da Git:
/* Stili per il bottone principale */
.button-primary {
<<<<<<< HEAD
background-color: #007bff; /* Modifica da main (commit F) */
color: white;
=======
background-color: #0056b3; /* Tua modifica (commit B) */
color: #ffffff;
padding: 10px 20px; /* Hai aggiunto anche padding */
>>>>>>> B... Descrizione del commit B
border: none;
border-radius: 5px;
}
<<<<<<< HEAD
: Indica l’inizio delle modifiche provenienti dal branch su cui stai ribasando (in questo caso, main
, rappresentato da HEAD
durante il rebase).
=======
: Separa le due versioni conflittuali.
>>>>>>> B... Descrizione commit B
: Indica la fine delle modifiche provenienti dal tuo commit che Git sta cercando di riapplicare.
Risolvi manualmente il conflitto: Questo è il passaggio cruciale e richiede la tua logica di sviluppatore. Devi decidere quale codice tenere, se combinare le modifiche, o se scrivere qualcosa di completamente nuovo. Devi rimuovere i marcatori <<<<<<<
, =======
, >>>>>>>
e lasciare solo il codice finale desiderato.
- Scenario 1: Tieni solo le tue modifiche: Rimuovi la parte tra
<<<<<<< HEAD
e=======
, e i marcatori.
- Scenario 2: Tieni solo le modifiche da
main
: Rimuovi la parte tra=======
e>>>>>>> ...
, e i marcatori.
- Scenario 3: Combina le modifiche: Modifica il codice per integrare entrambe le versioni in modo sensato, poi rimuovi i marcatori.
/* Stili per il bottone principale */
.button-primary {
/* Combiniamo: usiamo il colore di main, ma teniamo il padding aggiunto */
background-color: #007bff; /* Modifica da main (commit F) */
color: white;
padding: 10px 20px; /* Tua modifica (commit B) */
border: none;
border-radius: 5px;
}
Aggiungi il file risolto allo staging: Una volta che sei soddisfatto della risoluzione, devi comunicarlo a Git:
git add style.css
Se ci sono più file in conflitto, risolvili tutti e fai git add
per ciascuno.
Continua il rebase: Ora che hai risolto i conflitti per questo specifico commit, puoi dire a Git di continuare a riapplicare i commit successivi:
git rebase --continue
Ripeti se necessario: Il rebase potrebbe fermarsi di nuovo se anche i commit successivi (nel nostro esempio, il commit C
) generano conflitti con le modifiche presenti su main
o con le risoluzioni appena fatte. Ripeti i passaggi 1-5 per ogni conflitto.
Completamento: Una volta che tutti i commit sono stati riapplicati con successo (con eventuali conflitti risolti), Git completerà il rebase e il tuo branch feature/mia-feature
sarà ribasato su main
con una history lineare e pulita.
Opzioni alternative durante un conflitto:
git rebase --abort
: Se ti senti perso o hai fatto un errore, puoi annullare l’intero rebase e tornare allo stato precedente all’avvio del comando. È un’ancora di salvezza!git rebase --skip
: Usare con estrema cautela. Questo comando salta completamente l’applicazione del commit che ha causato il conflitto. Perderai le modifiche introdotte da quel commit. Utile solo se ti rendi conto che quel commit è diventato obsoleto o ridondante.
Padroneggiare la risoluzione dei conflitti durante un git rebase interactive
è essenziale per mantenere un flusso di lavoro pulito ed efficiente.
Problemi Comuni e Come Evitarli
Anche i developer esperti possono incontrare difficoltà con git rebase -i
. Ecco alcuni problemi comuni:
- Ribasare branch pubblici/condivisi:
- Problema: Viola la Regola d’Oro. Causa divergenze nella history per gli altri collaboratori.
- Soluzione: Non farlo. Usa
git merge
per integrare modifiche da branch condivisi. Usarebase
solo su branch locali o feature branch prima che vengano condivise/mergiate. Se hai assolutamente bisogno di correggere un commit già pushato (e sei sicuro che nessun altro ci stia lavorando), comunica chiaramente col team e preparati a usaregit push --force-with-lease
(più sicuro di--force
).
- Perdere commit o modifiche:
- Problema: Usare
drop
accidentalmente, fare errori durante losquash
/fixup
, o risolvere male un conflitto e poi faregit rebase --skip
. - Soluzione: Lavora con attenzione nell’editor interattivo. Prima di un rebase complesso, puoi creare un branch di backup (
git branch backup-prima-rebase
). Se perdi qualcosa,git reflog
è il tuo migliore amico: mostra la history di tutte le azioni diHEAD
e ti permette di recuperare commit “persi”.
- Problema: Usare
- Rebase infiniti o molto complessi:
- Problema: Tentare di ribasare un branch con moltissimi commit su un altro branch che è cambiato radicalmente. Potresti dover risolvere conflitti per decine di commit.
- Soluzione: Fai rebase più spesso! Integra le modifiche dal branch principale (
main
/develop
) nel tuo feature branch frequentemente (git pull --rebase origin main
ogit fetch origin && git rebase origin/main
). Questo mantiene le differenze piccole e gestibili. Se un rebase diventa troppo complesso,git rebase --abort
e considera unmerge
.
- Dimenticare
git rebase --continue
:- Problema: Risolvi i conflitti, fai
git add
, ma ti dimentichi di continuare. Lo stato del repository rimane “in rebase”. - Soluzione:
git status
ti ricorderà che sei in mezzo a un rebase. Eseguigit rebase --continue
.
- Problema: Risolvi i conflitti, fai
- Committare i marcatori di conflitto:
- Problema: Modifichi il file ma dimentichi di rimuovere i marcatori
<<<<<<<
,=======
,>>>>>>>
prima di faregit add
egit rebase --continue
. - Soluzione: Controlla sempre il file risolto prima di fare
git add
. Se te ne accorgi dopo, puoi usaregit commit --amend
(se il rebase è ancora fermo su quel commit) o un altrogit rebase -i
per correggere il commit successivo. Potrebbe essere utile anche uno strumento di merge grafico (come quello integrato in VS Code, WebStorm, ogit mergetool
).
- Problema: Modifichi il file ma dimentichi di rimuovere i marcatori
git rebase -i
vs git merge
: Quale scegliere?
Non c’è una risposta unica, dipende dal contesto e dalle preferenze del team.
git rebase -i
(prima di condividere):- Pro: History lineare e pulita, facile da leggere e navigare. Ogni commit sulla feature branch rappresenta un passo logico. Ideale per preparare Pull Request. Permette di “correggere” la storia locale.
- Contro: Riscrive la storia (pericoloso su branch condivisi). I conflitti vanno risolti commit per commit, il che può essere tedioso se sono tanti. Non preserva il contesto esatto di quando il lavoro è stato fatto rispetto al branch principale.
git merge
:- Pro: Non riscrive la storia, preservando il contesto originale. È sicuro da usare su qualsiasi branch. I conflitti vengono risolti una sola volta, al momento del merge.
- Contro: Crea “merge commit” extra che possono rendere la history più complessa e difficile da leggere (soprattutto con molti branch paralleli). La history diventa un grafo, non una linea retta.
Molti team adottano un approccio ibrido:
- Gli sviluppatori usano
git rebase -i
sui loro feature branch locali per tenerli aggiornati conmain
/develop
e per pulire la history prima di una Pull Request. - Quando la feature è pronta e approvata, viene integrata nel branch principale usando
git merge --no-ff
(per preservare il contesto della feature branch e creare comunque un merge commit esplicito) oppure con l’opzione “Squash and merge” offerta da piattaforme come GitHub/GitLab (che prende tutti i commit della feature, li unisce in uno solo, e lo applica sumain
).
Per approfondire la gestione dei progetti frontend con Git e GitHub, leggi la nostra guida su come usare GitHub per gestire progetti frontend.
Conclusione: Abbraccia il Potere del Rebase Interattivo
git rebase --interactive
è uno strumento incredibilmente potente nel toolkit di ogni sviluppatore frontend moderno. Anche se all’inizio può sembrare intimidatorio, specialmente quando si presentano conflitti, padroneggiarlo porta a enormi benefici: una history dei commit pulita, Pull Request più facili da revisionare e una migliore comprensione del flusso di lavoro di Git.
Ricorda la Regola d’Oro, fai rebase spesso sui tuoi branch locali, e non aver paura di affrontare i conflitti passo dopo passo. Con un po’ di pratica, scoprirai che git rebase interactive
non è un nemico da temere, ma un prezioso alleato per mantenere il tuo codice e la tua collaborazione organizzati ed efficienti. Buona riscrittura della storia (locale)!