← Torna al blog

Git rebase vs merge: quando usare quale

tutorial git

Due modi per integrare le modifiche

Quando lavori con i branch di Git, arriva il momento in cui devi portare le modifiche da un branch all'altro. Git ti offre due strategie principali: merge e rebase. Entrambe raggiungono lo stesso risultato finale -- il tuo branch ha tutte le ultime modifiche -- ma lo fanno in modi fondamentalmente diversi, e scegliere quello giusto è importante per la storia del tuo progetto e il workflow del tuo team.

Come funziona Merge

Un merge prende due branch e li combina creando un nuovo merge commit. Questo merge commit ha due genitori: la punta del tuo branch attuale e la punta del branch che stai integrando.

git checkout feature/login
git merge main

Dopo questo, il tuo branch di funzionalità contiene tutte le modifiche da main, unite da un merge commit. La storia di entrambi i branch è preservata esattamente come è avvenuta. Se guardi il grafo dei commit, vedrai le due linee di sviluppo convergere nel punto di merge.

Le caratteristiche chiave del merge:

  • Non distruttivo. Nessun commit esistente viene alterato. La storia è esattamente ciò che è accaduto.
  • Crea un merge commit. Questo commit aggiuntivo unisce le due storie.
  • Preserva il contesto. Puoi sempre vedere quando un branch è stato creato e quando è stato riunito.

Merge fast-forward

Se il tuo branch non ha diverged dal target (cioè non ci sono nuovi commit sul target da quando hai creato il branch), Git esegue un merge fast-forward per impostazione predefinita. Sposta semplicemente il puntatore del branch in avanti. Non viene creato alcun merge commit perché non è necessario. Puoi forzare un merge commit anche in questo caso con:

git merge --no-ff feature/login

Questo è utile quando vuoi preservare il fatto che il lavoro è stato svolto su un branch separato, anche se il merge era triviale.

Come funziona Rebase

Rebase adotta un approccio diverso. Invece di creare un merge commit, riproduce i tuoi commit sopra un altro branch. Il risultato è una storia lineare che sembra come se avessi iniziato il tuo lavoro dall'ultimo punto del branch di destinazione.

git checkout feature/login
git rebase main

Cosa succede dietro le quinte:

  • Git trova l'antenato comune dei due branch.
  • Rimuove temporaneamente i commit del tuo branch.
  • Sposta il puntatore del tuo branch alla punta di main.
  • Riproduce i tuoi commit uno alla volta sopra.

La storia risultante è perfettamente lineare. Nessun merge commit, nessuna linea che si biforca e converge nel grafo. Sembra come se avessi scritto tutto il tuo codice dopo le ultime modifiche su main, anche se non è quello che è realmente successo.

Le caratteristiche chiave del rebase:

  • Crea una storia lineare. Il grafo dei commit è pulito e facile da leggere.
  • Riscrive gli hash dei commit. I commit riprodotti ottengono nuovi hash SHA, anche se il loro contenuto è lo stesso.
  • Nessun merge commit. Il branch si integra in modo pulito senza commit aggiuntivi.

Quando usare Merge

Merge è la scelta più sicura e diretta in diverse situazioni:

  • Branch condivisi. Quando più persone lavorano sullo stesso branch, merge preserva la storia di tutti senza riscrivere commit da cui altri dipendono.
  • Preservare la storia è importante. Nei progetti in cui devi tracciare esattamente quando e come le modifiche sono state integrate, merge fornisce un registro completo e inalterato.
  • Integrazione di branch di lunga durata. Quando fai merge di un branch di release in main, o combini due branch che hanno diverged significativamente, un merge commit segna chiaramente il punto di integrazione.
  • Vuoi semplicità. Merge è concettualmente più facile. Ci sono meno cose che possono andare storte, specialmente per sviluppatori che sono nuovi a Git.

Quando usare Rebase

Rebase eccelle quando vuoi una storia del progetto pulita e lineare:

  • Aggiornare un branch di funzionalità. Prima di fare merge della tua funzionalità in main, fare rebase sul più recente main ti dà un insieme pulito di commit che si applicano direttamente senza merge commit.
  • Mantenere una storia pulita. Una storia lineare è più facile da navigare con git log, più facile da bisezionare quando si cercano bug, e più facile da revisionare nelle pull request.
  • Pulizia locale. Se hai fatto piccoli commit incrementali mentre lavoravi, puoi usare rebase per riordinare prima di condividere il tuo branch.

La regola d'oro del Rebase

Non fare mai rebase di commit che sono stati pushati su un branch condiviso.

Questa è la regola più importante da ricordare. Poiché rebase riscrive gli hash dei commit, chiunque abbia basato il proprio lavoro sui commit originali avrà problemi. La loro storia non corrisponde più alla storia rebasata, portando a commit duplicati, conflitti e confusione.

La regola è semplice: se altre persone potrebbero aver fatto pull dei tuoi commit, non fare rebase. Usa rebase solo sui tuoi branch locali, o su branch dove sei l'unico contributore.

Se accidentalmente fai rebase di un branch condiviso, gli altri sviluppatori del team dovranno gestire la storia divergente. Questo tipicamente significa fare un force-push (che sovrascrive il remote) e far fare reset delle copie locali a tutti. È disruptivo ed evitabile.

Rebase interattivo: riscrivere la storia con precisione

Il rebase interattivo (git rebase -i) va oltre il semplice spostamento di commit. Ti permette di modificare la storia dei commit stessa:

git rebase -i HEAD~5

Questo apre una lista degli ultimi 5 commit, dove puoi:

  • pick -- mantenere il commit così com'è
  • reword -- cambiare il messaggio del commit
  • edit -- fare una pausa per modificare il commit
  • squash -- combinare con il commit precedente
  • fixup -- come squash, ma scartando il messaggio del commit
  • drop -- rimuovere il commit completamente
  • reorder -- cambiare l'ordine riorganizzando le righe

Il rebase interattivo è potente per pulire un branch disordinato prima del merge. Puoi fare squash dei commit "correzione typo" nel loro genitore, riformulare messaggi poco chiari e riordinare i commit in modo che raccontino una storia logica.

Un workflow pratico

Molti team usano un approccio combinato che ottiene il meglio di entrambe le strategie:

  1. Creare un branch di funzionalità da main.
  2. Durante il lavoro, fare periodicamente rebase del branch di funzionalità su main per restare aggiornati.
  3. Prima di aprire una pull request, usare il rebase interattivo per pulire i commit.
  4. Fare merge del branch di funzionalità in main (spesso con un merge commit per segnare il punto di integrazione).

Questo ti dà una storia pulita e lineare sul branch di funzionalità e un chiaro punto di merge su main. È un compromesso pragmatico che funziona bene per la maggior parte dei team.

Rebase e Merge in GitSquid

GitSquid supporta sia merge che rebase direttamente dall'interfaccia. Puoi fare merge dei branch dal menu contestuale su qualsiasi etichetta di branch nel grafo dei commit. Per il rebase, GitSquid include un editor visuale di rebase interattivo che ti permette di riordinare, squash, fixup, reword e drop commit trascinando e cliccando invece di modificare un file di testo nel tuo terminale. Questo rende il rebase interattivo accessibile anche se trovi la versione da riga di comando intimidatoria.

Riepilogo

Merge Rebase
Storia Non lineare, preserva i branch Lineare, pulita
Hash dei commit Invariati Riscritti
Merge commit Sì (salvo fast-forward) No
Sicuro per branch condivisi No
Ideale per Integrare lavoro condiviso Pulire branch di funzionalità

Né merge né rebase è universalmente migliore. Sono strumenti per situazioni diverse. Comprendi cosa ognuno fa alla tua storia, segui la regola d'oro del rebase e scegli quello che si adatta al contesto. Il tuo io futuro che legge git log te ne sarà grato.