git rebase
Questo documento presenta un'analisi approfondita del comando git rebase
. Il comando di riassegnazione è stato illustrato anche nelle pagine dedicate alla configurazione di un repository e alla riscrittura della cronologia. In questa pagina analizzeremo più nei dettagli la configurazione e l'esecuzione di git rebase
e i casi d'uso e le insidie più comuni del comando di riassegnazione.
La riassegnazione è una delle due utility Git specializzate nell'integrazione delle modifiche da un branch all'altro. L'altra utilità di integrazione delle modifiche è git merge
. Il merge è un record delle modifiche che si muove in avanti. Invece, la riassegnazione include efficaci funzioni di riscrittura della cronologia. Per un confronto dettagliato tra merge e riassegnazione, consulta la nostra guida al confronto tra il merge e la riassegnazione. La riassegnazione prevede 2 modalità principali: modalità" manuale" e modalità "interattiva". Di seguito le analizzeremo in modo più dettagliato.
Cos'è git rebase?
La riassegnazione è il processo di spostamento o di unione di una sequenza di commit in un nuovo commit di base. La riassegnazione è molto utile e facilmente visualizzabile nel contesto di un flusso di lavoro di creazione dei branch di funzioni. L'intero processo può essere visualizzato come segue:
Dal punto di vista dei contenuti, la riassegnazione cambia la base del branch da un commit all'altro facendo sembrare come se il branch fosse stato creato da un commit diverso. Internamente, Git ottiene questo risultato creando nuovi commit e applicandoli alla base specificata. È molto importante capire che anche se il branch ha lo stesso aspetto, è composto da commit completamente nuovi.
Utilizzo
Il motivo principale della riassegnazione è quello di mantenere una cronologia del progetto lineare. Ad esempio, considera una situazione in cui il branch principale è avanzato da quando hai iniziato a lavorare su un branch di funzioni. Vuoi applicare gli ultimi aggiornamenti del branch principale al branch di funzioni, ma vuoi mantenere pulita la cronologia del branch in modo che sembri che tu abbia lavorato sull'ultima versione del branch principale. Ciò offre il vantaggio di un merge ordinato del branch di funzioni nel branch principale. Perché vogliamo mantenere una cronologia "pulita"? I vantaggi di avere una cronologia pulita diventano tangibili quando si eseguono operazioni Git per indagare sull'introduzione di una regressione. Uno scenario più reale sarebbe:
- Viene identificato un bug nel branch principale. Una funzione ha smesso di funzionare.
- Uno sviluppatore esamina la cronologia del branch principale usando
git log
. Grazie alla cronologia "pulita", lo sviluppatore è in grado di ragionare rapidamente sulla cronologia del progetto. - Lo sviluppatore non è in grado di individuare il momento in cui è stato introdotto il bug usando
git log
, quindi esegue un comandogit bisect
. - Dal momento che la cronologia di git è pulita,
git bisect
può mettere a confronto un set perfezionato di commit durante la ricerca della regressione. Lo sviluppatore trova rapidamente il commit che ha introdotto il bug ed è in grado di agire di conseguenza.
Scopri di più su git log e git bisect consultando le pagine singole corrispondenti.
Hai due opzioni per integrare la funzione nel branch principale: eseguire direttamente il merge oppure eseguire prima la riassegnazione e poi il merge. La prima opzione si traduce in un merge a 3 vie e in un commit di merge, mentre la seconda si traduce in un merge con avanzamento rapido e in una cronologia perfettamente lineare. Il diagramma seguente mostra come la riassegnazione sul branch principale faciliti i merge con avanzamento rapido.
La riassegnazione è un modo comune per integrare le modifiche upstream nel repository locale. Il pull delle modifiche upstream con git merge si traduce in un commit di merge superfluo ogni volta che desideri vedere l'avanzamento del progetto. D'altra parte, eseguire la riassegnazione è come affermare di voler basare le proprie modifiche su ciò che hanno già fatto gli altri membri del team.
Non eseguire la riassegnazione della cronologia pubblica
Come abbiamo discusso in precedenza nella sezione sulla riscrittura della cronologia, non dovresti mai eseguire la riassegnazione dei commit una volta che sono stati inviati a un repository pubblico. La riassegnazione sostituirebbe i commit meno recenti con quelli nuovi dando l'impressione che la parte della cronologia correlata a questi ultimi sia svanita improvvisamente.
Confronto tra git rebase standard e git rebase interattivo
Si parla di git rebase interattivo quando git rebase accetta un argomento -- i
, che sta per "interattivo". Senza argomenti, il comando viene eseguito in modalità standard. In entrambi i casi, supponiamo di aver creato un branch di funzioni separato.
# Create a feature branch based off of main
git checkout -b feature_branch main
# Edit files
git commit -a -m "Adds new feature"
Git rebase in modalità standard prenderà automaticamente i commit nel branch di lavoro corrente e li applicherà alla base del branch inviato.
git rebase <base>
This automatically rebases the current branch onto <base>
, which can be any kind of commit reference (for example an ID, a branch name, a tag, or a relative reference to HEAD
).
L'esecuzione di git rebase
con il flag -i
avvia una sessione di riassegnazione interattiva. Invece di spostare ciecamente tutti i commit nella nuova base, la riassegnazione interattiva ti dà l'opportunità di modificare i singoli commit durante il processo. Ciò consente di ripulire la cronologia rimuovendo, dividendo e modificando una serie esistente di commit. È come il comando Git commit --amend
, ma con una marcia in più.
git rebase --interactive <base>
This rebases the current branch onto <base>
but uses an interactive rebasing session. This opens an editor where you can enter commands (described below) for each commit to be rebased. These commands determine how individual commits will be transferred to the new base. You can also reorder the commit listing to change the order of the commits themselves. Once you've specified commands for each commit in the rebase, Git will begin playing back commits applying the rebase commands. The rebasing edit commands are as follows:
pick 2231360 some old commit
pick ee2adc2 Adds new feature
# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
Comandi rebase aggiuntivi
Come descritto nei dettagli nella pagina relativa alla riscrittura della cronologia, la riassegnazione può essere utilizzata per modificare commit meno recenti e duplicati, file sottoposti a commit e messaggi duplicati. Sebbene queste siano le applicazioni più comuni, git rebase
dispone inoltre di opzioni di comando aggiuntive che possono essere utili in applicazioni più complesse.
git rebase -- d
significa che durante la riproduzione il commit verrà scartato dal blocco di commit unito finale.git rebase -- p
lascia il commit invariato. Non modificherà il messaggio o il contenuto del commit, che verrà comunque indicato come commit singolo nella cronologia dei branch.git rebase -- x
durante la riproduzione esegue uno script della shell della riga di comando su ogni commit contrassegnato. Un esempio utile potrebbe essere quello di eseguire la suite di test della base di codice su commit specifici, per facilitare l'individuazione delle regressioni durante una riassegnazione.
Riepilogo
La riassegnazione interattiva dà il controllo completo sull'aspetto della cronologia del progetto. Ciò offre molta libertà agli sviluppatori, poiché consente loro di eseguire il commit di una cronologia "disordinata", mentre sono concentrati sulla scrittura di codice, e di tornare indietro e ripulirla quando portano a termine il lavoro.
La maggior parte degli sviluppatori preferisce utilizzare la riassegnazione interattiva per perfezionare un branch di funzioni prima di eseguirne il merge alla base di codice principale. Questo dà loro l'opportunità di eseguire lo squash dei commit insignificanti, eliminare quelli obsoleti e assicurarsi che tutto il resto sia in ordine prima di eseguire il commit nella cronologia "ufficiale" del progetto. A tutti gli altri, sembrerà che l'intera funzione sia stata sviluppata in un'unica serie di commit ben pianificati.
Il vero potere della riassegnazione interattiva è evidente osservando la cronologia del branch principale risultante. A tutti gli altri, sembrerà che tu sia uno sviluppatore brillante che ha implementato la nuova funzione al primo colpo utilizzando il numero perfetto di commit. Ecco come la riassegnazione interattiva è in grado di mantenere pulita e pertinente la cronologia di un progetto.
Opzioni di configurazione
Ci sono alcune proprietà di riassegnazione che possono essere impostate usando git config
. Queste opzioni modificheranno l'aspetto dell'output di git rebase
.
rebase.stat
: un valore booleano impostato su false per impostazione predefinita. L'opzione attiva o disattiva la visualizzazione del contenuto diffstat visivo che mostra cosa è cambiato dall'ultima riassegnazione.
rebase.autoSquash:
un valore booleano che attiva o disattiva il comportamento--autosquash
.
rebase.missingCommitsCheck:
può essere impostato su più valori che cambiano il comportamento di riassegnazione in caso di commit mancanti.
warn | Stampa l'output di avviso in modalità interattiva per avvertire della rimozione dei commit |
| Arresta la riassegnazione e stampa i messaggi di avviso relativi ai commit rimossi |
| Configurato per impostazione predefinita, ignora eventuali avvisi di commit mancanti |
rebase.instructionFormat:
una stringa di formatogit log
che verrà utilizzata per formattare la visualizzazione della riassegnazione interattiva
Applicazione avanzata di riassegnazione
L'argomento della riga di comando --onto
può essere inviato a git rebase
. Nella modalità --onto
di git rebase, il comando si espande in:
git rebase --onto <newbase> <oldbase>
Il comando --onto
abilita un modulo o una riassegnazione più efficace che consente di inviare riferimenti specifici da usare come punte di una riassegnazione.
Supponiamo di avere un repository di esempio con branch come il seguente:
o---o---o---o---o main
\
o---o---o---o---o featureA
\
o---o---o featureB
featureB si basa su featureA, tuttavia, ci rendiamo conto che featureB non dipende da nessuna delle modifiche in featureA e potrebbe essersi semplicemente distaccato dal branch principale.
git rebase --onto main featureA featureB
featureA is the <oldbase>
. main
becomes the <newbase>
and featureB is reference for what HEAD
of the <newbase>
will point to. The results are then:
o---o---o featureB
/
o---o---o---o---o main
\
o---o---o---o---o featureA
Conoscere i pericoli della riassegnazione
Un avvertimento da considerare quando si usa git rebase è che i conflitti di merge possono diventare più frequenti durante un flusso di lavoro di riassegnazione. Ciò si verifica se si dispone di un branch di lunga durata che si è allontanato da quello principale. Alla fine, sarà necessario eseguire la riassegnazione sul branch principale che in quel momento potrebbe contenere molti nuovi commit con cui le modifiche apportate al branch potrebbero entrare in conflitto. È possibile risolvere facilmente questo problema eseguendo spesso la riassegnazione del branch su quello principale ed eseguendo commit più frequenti. Gli argomenti della riga di comando --continue
e --abort
possono essere inviati a git rebase
per far avanzare o ripristinare la riassegnazione durante la gestione dei conflitti.
Una precisazione più importante sulla riassegnazione riguarda i commit persi in seguito alla riscrittura interattiva della cronologia. Se vengono eseguiti la riassegnazione in modalità interattiva e i sottocomandi come lo squash o il drop, i commit verranno rimossi dal log immediato del branch. A prima vista, potrebbe sembrare che i commit siano stati eliminati definitivamente. Tramite git reflog
, è possibile ripristinare questi commit e annullare l'intera riassegnazione. Per maggiori informazioni sull'uso di git reflog
per trovare i commit persi, consulta la nostra pagina di documentazione di Git reflog.
Il comando git rebase in sé non è seriamente pericoloso. I veri casi di pericolo sorgono quando si esegue la riscrittura della cronologia con riassegnazioni interattive e si forza il push dei risultati su un branch remoto condiviso da altri utenti. Questo è uno schema da evitare in quanto ha la capacità di sovrascrivere il lavoro di altri utenti remoti quando eseguono il pull.
Ripristino dalla riassegnazione upstream
Se un altro utente ha eseguito la riassegnazione e forzato il push sul branch in cui stai eseguendo il commit, il comando git pull
sovrascriverà tutti i commit che hai basato su quel branch precedente con la punta di cui è stato forzato il push. Fortunatamente, tramite git reflog
puoi ottenere il log di riferimento del branch remoto, in cui puoi trovare il riferimento risalente a prima della riassegnazione. Potrai quindi eseguire la riassegnazione del branch in base a tale riferimento remoto utilizzando l'opzione --onto
, come illustrato in precedenza nella sezione Applicazione avanzata di riassegnazione.
Riepilogo
In questo articolo abbiamo trattato l'utilizzo di git rebase
. Abbiamo illustrato casi d'uso di base e avanzati e analizzato esempi più avanzati. Alcuni argomenti chiave sono:
- Confronto tra git rebase standard e le modalità interattive
- Opzioni di configurazione di git rebase
- git rebase --onto
- Commit persi di git rebase
Abbiamo esaminato l'utilizzo di git rebase
con altri strumenti come git reflog
, git fetch
e git push
. Visita le pagine corrispondenti per ulteriori informazioni.