Git Rebase vs Merge: Quando Usare Cosa
Una delle decisioni più discusse in Git è: usare rebase o merge? Entrambi integrano cambiamenti da un branch all'altro, ma in modi radicalmente diversi. Capire le differenze, i vantaggi e gli svantaggi di ciascuno è essenziale per mantenere una storia Git pulita e collaborare efficacemente in team.
🎯 Cosa Imparerai
- Differenze fondamentali tra rebase e merge
- Quando usare rebase e quando merge
- Fast-forward merge vs merge commit
- La regola d'oro del rebase
Come Funziona il Merge
Git merge crea un nuovo merge commit che unisce le storie di due branch. È un'operazione non distruttiva: i branch originali non vengono modificati.
# Situazione iniziale
main: A---B---C
\
feature: D---E
# Merge di feature in main
git checkout main
git merge feature
# Risultato
main: A---B---C-------M
\ /
feature: D---E---
# M è il merge commit che ha due genitori: C ed E
# Passa al branch di destinazione
git checkout main
# Mergia il feature branch
git merge feature
# Git crea automaticamente un merge commit
# Messaggio default: "Merge branch 'feature' into main"
Come Funziona il Rebase
Git rebase riscrive la storia spostando i commit di un branch su una nuova base. Invece di creare un merge commit, i commit vengono riapplicati uno per uno sulla punta del branch di destinazione.
# Situazione iniziale
main: A---B---C
\
feature: D---E
# Rebase di feature su main
git checkout feature
git rebase main
# Risultato
main: A---B---C
\
feature: D'---E'
# D' ed E' sono NUOVI commit con contenuto uguale a D ed E
# ma hash diversi (perché il parent è cambiato)
# Passa al feature branch
git checkout feature
# Rebase su main
git rebase main
# I commit di feature vengono riapplicati su main
# Se ci sono conflitti, risolvili e:
git add .
git rebase --continue
# Ora puoi fare fast-forward merge su main
git checkout main
git merge feature # Fast-forward (nessun merge commit)
Fast-Forward Merge
Un fast-forward merge accade quando il branch di destinazione non ha commit nuovi rispetto al branch da mergeare. Git semplicemente "sposta avanti" il puntatore senza creare merge commit.
# Situazione dopo rebase
main: A---B---C
\
feature: D'---E'
# Fast-forward merge
git checkout main
git merge feature
# Risultato (nessun merge commit)
main: A---B---C---D'---E'
|
feature:
Per evitare fast-forward e forzare un merge commit:
git merge --no-ff feature
Differenze Chiave
Merge:
- Storia ramificata: Mostra quando i branch hanno diverged
- Non distruttivo: Storia originale preservata
- Tracciabile: Vedi esattamente quando è avvenuto il merge
- Sicuro: Nessuna riscrittura della storia
- Crea merge commit aggiuntivi
Rebase:
- Storia lineare: Come se avessi lavorato sequenzialmente
- Distruttivo: Riscrive commit (nuovi hash)
- Pulito: Nessun merge commit rumoroso
- Rischioso: Mai fare rebase di branch pubblici!
- Storia più facile da leggere
Quando Usare Merge
✅ Usa Merge quando:
- Integri branch pubblici (main, develop)
- Vuoi preservare la storia esatta di quando i branch hanno diverged
- Lavori in team e altri hanno già pullato il branch
- Vuoi tracciare milestone (feature completate, release)
- Segui Git Flow (usa
--no-ffper preservare struttura)
# Feature branch completato
git checkout main
git merge --no-ff feature/user-auth
# Merge commit preserva la storia della feature
# Chiaro quando la feature è stata integrata
# Altri sviluppatori vedono la struttura completa
Quando Usare Rebase
✅ Usa Rebase quando:
- Lavori su branch locali privati
- Vuoi storia lineare pulita
- Stai aggiornando il tuo feature branch con gli ultimi cambiamenti da main
- Vuoi evitare merge commit "rumorosi"
- Prima di creare una Pull Request (per storia pulita)
# Il tuo feature branch è indietro rispetto a main
git checkout feature/payment
git rebase main
# Ora il tuo branch ha gli ultimi cambiamenti da main
# Storia lineare, facile da revieware nella PR
# Risolvi conflitti se necessario
# git add .
# git rebase --continue
La Regola d'Oro del Rebase
⚠️ REGOLA D'ORO
Mai fare rebase di commit che sono stati pushati su branch pubblici condivisi!
Il rebase riscrive la storia cambiando gli hash dei commit. Se altri sviluppatori hanno già basato il loro lavoro su quei commit, il loro repository diventa inconsistente e il prossimo pull causerà conflitti e duplicazioni.
# MALE! Altri hanno già pullato main
git checkout main
git rebase feature
# Questo riscrive main, causando problemi per tutti
# BENE! feature è solo tuo, nessuno l'ha pullato
git checkout feature
git rebase main
# Aggiorna il tuo branch privato con gli ultimi cambiamenti
Workflow Consigliato
Ecco un workflow che combina il meglio di entrambi:
# 1. Crea feature branch da main
git checkout -b feature/new-feature main
# 2. Lavora sulla feature
git commit -m "feat: add component"
git commit -m "feat: add tests"
# 3. Nel frattempo, main è andato avanti
# Aggiorna il tuo branch con rebase (branch privato, OK!)
git fetch origin
git rebase origin/main
# 4. Pulizia della storia con interactive rebase (opzionale)
git rebase -i origin/main
# Squash commit WIP, fixup typos, ecc.
# 5. Push del feature branch
git push origin feature/new-feature
# 6. Crea Pull Request su GitHub/GitLab
# 7. Dopo code review, mergia in main CON merge commit
git checkout main
git merge --no-ff feature/new-feature
# Risultato: storia main pulita con milestone chiare
Storia Lineare vs Ramificata
Storia con Merge:
* Merge feature B
|\
| * Feature B commit 2
| * Feature B commit 1
* | Main commit 3
* | Main commit 2
|/
* Main commit 1
Pro: Vedi esattamente quando le feature sono state integrate
Con: Può diventare caotica con molti merge
Storia con Rebase:
* Feature B commit 2
* Feature B commit 1
* Main commit 3
* Main commit 2
* Main commit 1
Pro: Lineare, facile da seguire
Con: Perdi informazione su quando i branch hanno diverged
Conflitti: Merge vs Rebase
Entrambi possono avere conflitti, ma si gestiscono diversamente:
# Conflitti durante MERGE
git merge feature
# Risolvi conflitti
git add .
git commit # Completa il merge
# Conflitti durante REBASE
git rebase main
# Risolvi conflitti per ogni commit in conflitto
git add .
git rebase --continue # Vai al prossimo commit
# Ripeti fino alla fine
# Abortire rebase se necessario
git rebase --abort
Preferenze di Team
Diversi team hanno preferenze diverse. Alcuni esempi:
- Linux Kernel: Solo merge (preserva storia completa)
- Rails: Rebase per feature branch, merge per integrazioni importanti
- Google: Storia lineare con rebase (trunk-based development)
Conclusione
Non esiste una scelta "migliore" universale tra merge e rebase. Merge preserva
la storia ed è sicuro per branch pubblici. Rebase crea storia lineare pulita
ma riscrive commit. La strategia migliore è spesso ibrida: usa rebase per mantenere puliti i
tuoi feature branch locali, e merge con --no-ff per integrare in branch pubblici.
🎯 Punti Chiave
- Merge = non distruttivo, crea merge commit, storia ramificata
- Rebase = riscrive storia, nessun merge commit, storia lineare
- Mai fare rebase di branch pubblici condivisi
- Usa rebase per feature branch privati, merge per integrazioni pubbliche
- Fast-forward merge = nessun merge commit quando possibile







