Confronto

Git merge vs rebase: quale scegliere per il proprio team

Aggiornato: · Skillaria.Top

Sia merge sia rebase integrano le modifiche di due branch, ma trattano la cronologia in modo diverso: merge la conserva così com'è e aggiunge un commit di merge, rebase riscrive i vostri commit sopra la nuova base e rende la cronologia lineare. Regola pratica: rebase per mettere ordine nei propri commit non ancora pubblicati, merge per integrare il lavoro finito nei branch condivisi.

In breve: la differenza tra merge e rebase

Il dibattito «git merge vs rebase» in realtà non riguarda quale comando sia migliore, ma quale cronologia del progetto volete vedere. Entrambi i comandi portano allo stesso codice finale. Cambia il percorso che resta registrato nel log.

  • Merge unisce due linee di cronologia e registra onestamente che il lavoro è proceduto in parallelo. I commit esistenti non cambiano: si aggiunge un merge commit con due genitori.
  • Rebase riscrive i vostri commit come se aveste iniziato a lavorare dalla versione più recente del branch di base. La cronologia appare come una linea retta, ma i commit ottengono nuovi hash.

La meccanica di ciascun comando (flag, risoluzione dei conflitti, modalità interattiva) è trattata in articoli dedicati. Qui parliamo solo della scelta della strategia.

Come appare la cronologia nei due casi

Situazione di partenza: avete creato feature, fatto due commit, e nel frattempo su main sono arrivati due commit di altri.

          D---E        feature
         /
A---B---C---F---G      main

Dopo git merge feature su main:

          D---E
         /     \
A---B---C---F---G---M  main

Dopo git rebase main su feature e il fast-forward di main:

A---B---C---F---G---D'---E'   main

Nel primo caso la cronologia mostra che la feature è stata sviluppata in parallelo e quando è stata integrata. Nel secondo la cronologia si legge come un racconto sequenziale, ma il fatto che il lavoro sia stato parallelo sparisce: D' ed E' sono commit nuovi, creati al momento del rebase.

Tabella di confronto git merge vs rebase

Criteriogit mergegit rebase
Commit esistentiNon cambianoVengono ricreati con nuovi hash
Forma della cronologiaGrafo ramificato con merge commitLineare
Sicurezza sui branch condivisiSempre sicuroPericoloso per i commit già pubblicati
Serve un force pushNoSì, se il branch è già sul server
ConflittiSi risolvono una volta, nel merge commitPossono comparire su ogni commit spostato
Contesto «da dove arriva»ConservatoPerso
Annullare un'intera featureUn solo git revert -m 1 del merge commitRevert di ogni commit o di un intervallo
Comodità di git bisect e git logGrafo più difficile da leggerePiù semplice: una sola linea retta
Curva di apprendimentoBassaPiù alta, bisogna capire la riscrittura della cronologia
Pulizia dei commit (squash, reword)Solo --squash in bloccoFlessibile con rebase -i

Pro e contro di merge

Pro

  • Non distrugge la cronologia. Tutto ciò che è stato pubblicato resta al suo posto. I colleghi non dovranno sistemare nulla dopo la vostra operazione.
  • Il conflitto si risolve una volta sola. Anche se il branch ha 30 commit, lavorate sullo stato finale e non su ogni singolo passo.
  • Il contesto resta. Dal merge commit si vede quale attività è entrata nel branch principale e quando, ed è facile annullarla per intero.

Contro

  • Grafo rumoroso. Con sincronizzazioni frequenti la cronologia si riempie di commit «Merge branch 'main' into feature» e git log --graph diventa una mappa ferroviaria.
  • I commit di bozza finiscono nella cronologia. «wip», «fix», «altro fix» restano per sempre nel branch principale, se non li comprimete.

Pro e contro di rebase

Pro

  • Cronologia lineare e leggibile. Ogni commit è al suo posto, git log si legge come un elenco di modifiche e git bisect trova più facilmente il commit con il bug.
  • Possibilità di riordinare i commit prima della review: unirli, rinominarli, cambiarne l'ordine.
  • Niente merge commit tecnici dovuti alla sincronizzazione con il branch principale.

Contro

  • Riscrive la cronologia. Il rebase di un branch condiviso già pubblicato rompe il lavoro di chiunque lo abbia scaricato.
  • Richiede un force push per i branch sul server, quindi disciplina e --force-with-lease.
  • Conflitti ripetuti. Lo stesso blocco di codice può andare in conflitto su più commit di fila.
  • I commit intermedi potrebbero non compilare. Dopo lo spostamento sulla nuova base, nessuno ha testato i vecchi commit nel nuovo contesto.

Quando usare l'uno o l'altro

Rebase per il proprio lavoro locale

Aggiornate il vostro branch prima di inviarlo: git pull --rebase. Preparate il branch per la review e volete eliminare i commit di bozza: git rebase -i. Il vostro feature branch personale è rimasto indietro rispetto a main: git rebase origin/main. In tutti questi casi riscrivete commit che avete visto solo voi (o che appartengono solo a voi).

Merge per i branch condivisi e l'integrazione

Una feature finita viene integrata in main o develop. Sul branch lavorano più persone. Bisogna riportare un branch di release in quello principale. Qui è importante non toccare i commit già pubblicati, e merge è l'unica opzione corretta.

Una regola semplice

Se i commit li avete solo voi, potete fare rebase. Se qualcuno li ha già scaricati, solo merge.

Flussi di lavoro tipici per il team

SchemaCome funzionaPer chi è adatto
Solo mergeLe feature si integrano con --no-ff, la sincronizzazione con git merge mainChi è alle prime armi con Git, team per cui contano una cronologia completa e rollback semplici
Rebase + mergePrima dell'integrazione il branch viene ribasato su main, poi un merge commit con --no-ffTeam che vogliono sia una cronologia pulita sia i confini delle feature
Rebase + fast-forwardIl branch viene ribasato e integrato in fast-forward, la cronologia è del tutto lineareTeam piccoli con commit ordinati e atomici
Squash mergeOgni pull request diventa un solo commit su mainTeam con task brevi, dove la cronologia dettagliata del branch non serve

Su GitHub e GitLab questi schemi corrispondono ai pulsanti «Create a merge commit», «Rebase and merge» e «Squash and merge». Conviene abilitare nelle impostazioni del repository solo l'opzione scelta, così la cronologia non diventa un miscuglio di stili.

Impostazioni che fissano la scelta

# pull senza merge commit superflui
git config --global pull.rebase true

# oppure: pull solo in fast-forward, altrimenti errore e decidete voi
git config --global pull.ff only

# sempre un merge commit quando si integrano le feature
git config --global merge.ff false

Attenzione a merge.ff false: vale anche per git pull in modalità merge. Molti preferiscono non impostarlo a livello globale e scrivere esplicitamente --no-ff quando integrano una feature.

Esempio: lo stesso task in due stili

Vediamo uno scenario reale. Da tre giorni sviluppate la feature dei pagamenti nel branch feature/payment, e nel frattempo su main sono stati integrati cinque task di altri. Prima della pull request dovete portarvi dentro il main aggiornato e verificare che tutto funzioni.

Variante con merge

git switch feature/payment
git fetch origin
git merge origin/main
# conflitti risolti una volta sola, git add, git commit
git push

Il branch ha ricevuto il commit «Merge remote-tracking branch 'origin/main' into feature/payment». Il push è normale e i colleghi che seguivano il branch non notano nulla. Se sincronizzate così ogni giorno, nel branch ci saranno tre commit del genere, e finiranno su main insieme alla feature se integrate senza squash.

Variante con rebase

git switch feature/payment
git fetch origin
git rebase origin/main
# i conflitti si risolvono strada facendo, commit per commit: git add, git rebase --continue
git rebase -i origin/main      # facoltativo: unire i commit di bozza
git push --force-with-lease

Il branch sembra creato stamattina dal main aggiornato, non ci sono commit superflui e chi fa la review legge le modifiche passo per passo più facilmente. Il prezzo è il push forzato e la necessità di assicurarsi che nessun altro faccia commit su quel branch.

Cosa vedrà alla fine main

Con l'integrazione tramite pull request la differenza rimane. Lo stile merge produce un grafo con diramazioni e merge di sincronizzazione, lo stile rebase una catena ordinata di commit della feature, che si può integrare in fast-forward o con un unico merge commit. Il codice sarà identico, a patto che i conflitti siano stati risolti allo stesso modo.

Come decidere in team

Rispondete ad alcune domande. Più persone lavorano sullo stesso branch? Se sì, il rebase per quel branch è escluso. Quanto è esperto il team con Git? Se ci sono spesso nuovi arrivati, partite da merge e squash merge: perdonano gli errori. Serve una cronologia completa per audit e analisi degli incidenti? Allora i merge commit delle feature sono utili. Contano un git log leggibile e un uso frequente di git bisect? Allora vince il rebase con la cronologia lineare.

Scrivete la decisione nel CONTRIBUTING o nel README del repository e attivate la protezione del branch principale: niente force push, pull request obbligatoria, metodo di merge consentito. Una regola fissata nelle impostazioni funziona meglio di qualsiasi accordo verbale.

Errori di valutazione comuni

  • «Rebase è roba da esperti, merge è per principianti». No: sono strumenti diversi per compiti diversi. Gli sviluppatori esperti li usano entrambi.
  • «I merge commit sono spazzatura». Quelli di sincronizzazione spesso sì, sono rumore. Ma il merge commit che integra una feature è un punto utile: mostra e permette di annullare un intero task.
  • «Con rebase ci sono meno conflitti». I conflitti sono gli stessi, perché cambiano le stesse righe. Rebase li distribuisce sui singoli commit, merge li risolve tutti insieme.
  • «Rebase perde i commit». I vecchi commit restano in git reflog e si possono recuperare finché il garbage collector non li elimina.

Conclusioni

Scegliere tra git merge vs rebase significa scegliere tra una cronologia fedele e una cronologia lineare e leggibile. Merge è sicuro e conserva il contesto, rebase offre pulizia ma riscrive i commit. La pratica più affidabile è combinarli: ribasare e ripulire il proprio branch finché è solo vostro, e integrare il lavoro finito nei branch condivisi con merge o squash secondo le regole del team. L'essenziale è accordarsi su uno schema e fissarlo nelle impostazioni del repository. Entrambe le strategie e il lavoro in team con i branch sono trattati in pratica nel corso su Git.

Domande frequenti

Per git pull è meglio merge o rebase?

Per aggiornare il branch locale è più comodo rebase: i vostri commit non pubblicati si posizionano sopra quelli remoti aggiornati, senza merge commit superflui. Si attiva con git config --global pull.rebase true.

Si possono mescolare merge e rebase nello stesso progetto?

Sì, ed è l'approccio più diffuso: rebase per i propri branch, merge per quelli condivisi. L'importante è che la regola sia nota a tutto il team.

Cosa conviene scegliere a un principiante?

Iniziate con merge: non riscrive la cronologia ed è più difficile rompere qualcosa. Imparate rebase sui vostri branch locali, una volta presa confidenza con reflog e force push.

La strategia scelta influisce sul codice finale?

No, se i conflitti sono risolti allo stesso modo lo stato finale dei file coincide. Cambia solo la cronologia dei commit registrata.

Che differenza c'è tra squash merge e rebase?

Lo squash merge comprime l'intero branch in un unico nuovo commit sul branch principale, mentre rebase sposta ogni commit singolarmente. Entrambi producono una cronologia lineare, ma lo squash perde il dettaglio dei singoli passi.

Impara l’argomento in video

Questo argomento è trattato in modo approfondito, con pratica, nel corso «Git da zero a sviluppatore sicuro».

Vai al corso