Git merge vs rebase: differenze e quando usare quale
Merge e rebase a confronto: cosa fanno davvero alla storia del progetto, quando conviene l'uno e quando l'altro, e la regola d'oro da non violare mai.
È una delle poche domande su Git capaci di generare discussioni accese nei team. In realtà non c'è una risposta universale: fanno cose diverse, con conseguenze diverse sulla storia del progetto. Quello che esiste è una regola da non violare mai, e a parte quella è una scelta di stile. In questo articolo ti spiego cosa succede davvero in entrambi i casi.
Il problema che risolvono entrambi
Hai creato un ramo per una funzionalità. Mentre ci lavoravi, il ramo principale è andato avanti: altri hanno aggiunto commit.
main: A---B---C---F
\
funzione: D---E
Adesso vuoi che il tuo lavoro (D, E) faccia parte del ramo principale. Merge e rebase sono due modi diversi di ottenerlo. Se il concetto di ramo ti è ancora poco chiaro, parti da cos'è il version control.
Cosa fa il merge
Il merge crea un nuovo commit che unisce le due linee di sviluppo, conservandole entrambe.
main: A---B---C---F---M
\ /
funzione: D---E-/
Il commit M ha due genitori: è il punto in cui le storie si ricongiungono.
Cosa ottieni: la storia registra esattamente com'è andata. Si vede che è esistito un ramo, quando è nato e quando è stato riunito. Nessun commit viene modificato: D ed E restano identici a come li hai creati.
Il costo: su un progetto con molti rami, il grafico della storia diventa intricato — quello che viene chiamato "a spaghetti".
git switch main
git merge funzione
Cosa fa il rebase
Il rebase riscrive i tuoi commit come se fossero stati fatti a partire dallo stato attuale del ramo principale.
main: A---B---C---F
\
funzione: D'---E'
Nota gli apici: D' ed E' non sono D ed E. Sono commit nuovi, con lo stesso contenuto ma identificatori diversi, ricostruiti su una base diversa.
Cosa ottieni: una storia lineare, che si legge come una sequenza ordinata di cambiamenti.
Il costo: la storia non racconta più com'è andata davvero. Sembra che tu abbia lavorato a partire da F, cosa che non è vera. E i commit originali non esistono più.
git switch funzione
git rebase main
Il confronto
| Merge | Rebase | |
|---|---|---|
| Storia | Fedele alla realtà | Lineare e pulita |
| Commit esistenti | Intatti | Riscritti |
| Grafico | Ramificato | Rettilineo |
| Conflitti | Si risolvono una volta | Possibili a ogni commit |
| Su rami condivisi | Sicuro | Pericoloso |
| Tracciabilità del ramo | Conservata | Persa |
Un punto pratico spesso sottovalutato: con il rebase i conflitti si possono ripresentare commit per commit, perché ogni commit viene riapplicato singolarmente. Su un ramo con quindici commit e modifiche sovrapposte, può diventare laborioso.
La regola d'oro
Non fare mai rebase su commit che hai già condiviso con altri.
È l'unica regola non negoziabile dell'articolo, e il motivo è concreto.
Il rebase riscrive i commit dando loro nuovi identificatori. Se un collega ha già scaricato i commit originali e tu li sostituisci, la sua copia e la tua raccontano storie incompatibili. Nel tentativo di riallinearsi, il suo Git ricreerà i vecchi commit accanto ai nuovi — con il risultato di duplicati, confusione e lavoro potenzialmente perso.
La formulazione pratica: il rebase va bene sui tuoi rami locali, prima di condividerli. Mai su main o su un ramo su cui lavora anche qualcun altro.
Se ti accorgi di aver fatto un rebase su un ramo condiviso, la cosa peggiore che puoi fare è forzare l'invio senza avvisare nessuno. Parlane con il team prima.
Quando usare cosa
Usa il rebase quando:
- Vuoi aggiornare il tuo ramo di lavoro con le novità del principale, prima di proporre le modifiche
- Vuoi riordinare i tuoi commit locali prima di condividerli
- Il ramo è solo tuo e non l'hai ancora inviato
Usa il merge quando:
- Unisci un ramo completato nel principale
- Il ramo è condiviso
- Vuoi che resti traccia dell'esistenza del ramo
- Hai il minimo dubbio: il merge è l'opzione sicura
Il flusso che usano molti team
Nella pratica non si sceglie l'uno o l'altro: si usano entrambi, ciascuno dove ha senso.
- Lavori sul tuo ramo
- Ogni tanto rebase dal principale, per restare aggiornato — il ramo è tuo, nessun problema
- Prima di proporre le modifiche, rebase interattivo per riordinare i commit
- Merge del ramo nel principale
Il risultato è una storia leggibile che conserva comunque la traccia dei rami.
Il rebase interattivo
Merita una menzione, perché è la funzione più utile del rebase e la meno conosciuta:
git rebase -i HEAD~5
Ti apre un elenco degli ultimi cinque commit e permette di riordinarli, unirli, riscriverne i messaggi o eliminarli.
L'uso tipico: hai lavorato tutto il giorno producendo commit come "wip", "prova", "fix del fix". Prima di condividere, li unisci in due o tre commit sensati con messaggi decenti.
Vale la stessa regola: solo su commit non ancora condivisi.
Il terzo modo: squash
Molte piattaforme offrono, al momento di integrare le modifiche, l'opzione di comprimere tutti i commit del ramo in uno solo.
Il vantaggio: un commit per funzionalità nel ramo principale, storia pulitissima e facile da annullare.
Il costo: si perde il dettaglio di come ci sei arrivato.
È la scelta più diffusa nei progetti dove il ramo principale deve leggersi come un elenco ordinato di funzionalità.
Cosa conta davvero
Una nota di realismo, perché su questo si spreca parecchia energia nei team.
Merge e rebase producono lo stesso codice. La differenza è solo su come si legge la storia. Nessuna delle due scelte rende il software migliore.
Quello che conta davvero è:
- Che il team faccia tutti la stessa cosa, qualunque essa sia
- Che i messaggi di commit siano comprensibili
- Che nessuno faccia rebase su rami condivisi
Se il tuo team ha una convenzione, seguila. Se non ce l'ha, sceglierne una qualsiasi e metterla per iscritto vale più di qualunque discussione su quale sia superiore.
Vale lo stesso principio dei messaggi di commit: la coerenza conta più della scelta specifica, come spiego in conventional commits.
In sintesi
Il merge unisce due linee di sviluppo creando un commit di unione: conserva la storia reale, al prezzo di un grafico ramificato. Il rebase riscrive i tuoi commit su una base aggiornata: ottieni una storia lineare, al prezzo di perdere la traccia di com'è andata davvero.
La regola non negoziabile è una: mai rebase su commit già condivisi. Il rebase va bene sui tuoi rami locali, il merge è sempre sicuro.
Il flusso più diffuso li usa entrambi: rebase per tenere aggiornato il proprio ramo e riordinare i commit, merge per integrarlo nel principale.
E la cosa da tenere presente sopra tutte: producono lo stesso codice. È una scelta di leggibilità, non di qualità — quindi conta più che il team sia coerente di quale delle due sia "giusta".
Se i comandi ti sono ancora poco familiari, il quadro pratico è in comandi Git essenziali.