Uno tra i comandi più utili in Git è Rebase: vediamone le differenze rispetto al merging e le operazioni che è possibile effettuare con esso.
Differenze tra rebasing e merging
In Git il rebasing (o rifondazione) è una procedura molto simile al merging che presenta però delle differenze di carattere concettuale oltre a basarsi su comandi diversi. Partendo dalle caratteristiche che accomunano questi due strumenti possiamo affermare che il rebasing, tramite l'istruzione git rebase
, svolge la medesima funzionalità del merging attraverso il comando git merge
, consente cioè di includere le modifiche eseguite a carico di un ramo in un altro ramo.
Detto questo, è importante sottolineare che il rebasing e il merging assolvono questo compito in modo differente. Si ipotizzi per esempio di essere coinvolti in un progetto collaborativo e di creare un nuovo ramo da dedicare all'introduzione di una feature addizionale, potrebbe succedere che mentre stiamo lavorando all'implementazione di quest'ultima un altro componente del nostro team esegua degli interventi sul ramo master
ed effettui dei commit per confermare tali aggiornamenti.
Come potremmo procedere per fare in modo che i commit a carico del master
vengano inclusi anche nel ramo generato per sviluppare la nuova feature? Per risolvere questo problema possiamo ricorrere al merging, che già conosciamo, ma il rebasing ci fornisce un'ulteriore alternativa.
Merging e cronologia dei rami
Se scegliessimo di operare con il merging, potremmo fondere il master
nell'altro ramo, chiamato ad esempio "fbk-6", utilizzando le seguenti istruzioni:
$ git checkout fbk-6
$ git merge master
Ritornando all'esempio del nostro progetto "app-html.it", procediamo quindi alla generazione del nuovo ramo:
Figura 1. Generazione del nuovo ramo.
Per poi procedere all'applicazione di una modifica a carico del master
ed effettuare il commit necessario per la conferma dell'aggiornamento:
Figura 2. Modifica del master e commit.
E concludere con il merging del master da "fbk-6":
Figura 3. Fusione dei rami.
Una volta spostato il puntatore su "fbk-6" si può quindi procedere con il merging in modo da sincronizzare i commit (merge commit). Con il merging non viene effettuata alcuna modifica ai rami disponibili, ciò rappresenta un vantaggio ma nel caso in cui il master
dovesse essere aggiornato di frequente si dovrà ricorrere spesso al merging nel ramo "fbk-6", con il possibile insorgere di problemi a carico della cronologia di quest'ultimo che potrebbe diventare difficilmente comprensibile.
Ricordiamoci infatti che nel caso specifico stiamo partecipando ad un progetto di sviluppo collaborativo in cui sono coinvolti tutti i membri di un team.
Rebasing dei rami
Ora proviamo a risolvere lo stesso problema con il rebasing:
$ git checkout fbk-6
$ git rebase master
Operazione che nel caso del nostro progetto "app-html.it" potrebbe svolgersi in questo modo:
Figura 4. Rebasing dei rami.
L'istruzione git rebase master
sposta il ramo "fbk-6" all'estremità del master
(la rappresentazione del suo stato corrente), in questo modo tutti i nuovi commit del master
vengono inclusi in esso. Sostanzialmente possiamo affermare che il rebasing, si occupa di riscrivere la cronologia di un progetto, in pratica genera dei nuovi commit per ciascun commit effettuato per il ramo rifondato.
A differenza del merging il rebasing non crea delle sovrapposizioni tra rami, questo significa che la cronologia stessa risulterà più comprensibile anche nel caso in cui vi sia la necessità di incorporare frequentemente i commit di un ramo nell'altro.
Il rebasing offre quindi la possibilità di accedere ad una cronologia più lineare in quanto, come accade invece nel merging, permette di non incorporare nel ramo di destinazione commit non necessari. Quanto detto non significa però che il rebasing rappresenti sempre una soluzione ideale.
I limiti del rebasing
Il rebasing presenta delle controindicazioni che lo rendono uno strumento da adottare con estrema attenzione, a tal proposito è bene ricordarsi di non utilizzarlo su rami pubblici. Nell'esempio precedente abbiamo rifondato il ramo "fbk-6" sul master
, ora ipotizziamo di effettuare l'operazione inversa.
In questo caso l'effetto del rebasing è lo spostamento dei commit del master
all'estremità dei "fbk-6". Ora però la cronologia del master
su cui stiamo lavorando non è più la stessa disponibile per gli altri componenti del team, che hanno invece come riferimento il master
originale. Il master
ha quindi due cronologie diverse, una per chi ha eseguito il rebasing e l'altra per gli altri sviluppatori, perché il rebasing ha avuto effetto soltanto sul repository di chi lo ha effettuato.
Se si ha anche il solo sospetto che un altro componente del team stia lavorando allo stesso ramo è sempre opportuno evitare il rebasing. Se per un errore di valutazione il rebasing ha portato alla duplicazione del master
, la soluzione più adeguata per evitare ulteriori danni è quella di sottoporre i due master
risultanti ad un merging. In questo modo però si viene a verificare una situazione in cui la cronologia verrà inquinata dalla presenza di commit riguardanti i medesimi aggiornamenti, cioè quelli applicati inizialmente e quelli ereditati dal ramo rifondato, il tutto a danno della comprensibilità della cronologia.