Il comandogit rebase
ha la reputazione di essere magico Git voodoo da cui i principianti dovrebbero stare lontani, ma può effettivamente rendere la vita molto più facile per un team di sviluppo se usato con cura. In questo articolo, confronteremo git rebase
con il relativo comandogit merge
e identificheremo tutte le potenziali opportunità per incorporare il rebasing nel tipico flusso di lavoro Git.,
Panoramica concettuale
La prima cosa da capire su git rebase
è che risolve lo stesso problema di git merge
. Entrambi questi comandi sono progettati per integrare le modifiche da un ramo in un altro ramo—lo fanno solo in modi molto diversi.
Considera cosa succede quando inizi a lavorare su una nuova funzionalità in un ramo dedicato, quindi un altro membro del team aggiorna il ramomaster
con nuovi commit. Ciò si traduce in una cronologia biforcuta, che dovrebbe essere familiare a chiunque abbia utilizzato Git come strumento di collaborazione.,
Ora, diciamo che i nuovi commit inmaster
sono rilevanti per la funzionalità su cui stai lavorando. Per incorporare i nuovi commit nel ramofeature
, hai due opzioni: fusione o rebasing.,
L’Opzione ” Merge
L’opzione più semplice è quello di unire il master
ramo in funzione filiale di utilizzare qualcosa come il seguente:
git checkout feature git merge master
O, è possibile condensare questo per un one-liner:
git merge feature master
crea una nuova “unione di impegnarsi” nel feature
ramo che lega insieme le storie di entrambi i rami, dando un ramo struttura che assomiglia a questo:
la Fusione è bello perché si tratta di un non-operazione distruttiva. I rami esistenti non vengono modificati in alcun modo., Ciò evita tutte le potenziali insidie del rebasing (discusse di seguito).
D’altra parte, questo significa anche che il ramo feature
avrà un commit di unione estraneo ogni volta che è necessario incorporare le modifiche upstream. Se master
è molto attivo, questo può inquinare un po ‘ la cronologia del tuo ramo di funzionalità. Mentre è possibile mitigare questo problema con le opzioni avanzate git log
, può rendere difficile per gli altri sviluppatori comprendere la cronologia del progetto.,
Il Rebase l’Opzione
Come alternativa alla fusione, è possibile rebase feature
ramo su master
ramo utilizzando i seguenti comandi:
git checkout feature git rebase master
Questo si muove l’intera feature
ramo di iniziare la punta di un master
ramo, in modo efficace, che comprende tutti i nuovi depositi nel master
. Ma, invece di utilizzare un commit di unione, rebasing riscrive la cronologia del progetto creando nuovi commit per ogni commit nel ramo originale.,
Il principale vantaggio del rebasing è che si ottiene una cronologia del progetto molto più pulita. Innanzitutto, elimina i commit di unione non necessari richiesti dagit merge
. In secondo luogo, come puoi vedere nel diagramma sopra, il rebasing si traduce anche in una cronologia del progetto perfettamente lineare: puoi seguire il suggerimento di feature
fino all’inizio del progetto senza fork. Ciò semplifica la navigazione del progetto con comandi comegit log
,git bisect
egitk
.,
Ma ci sono due compromessi per questa storia di commit incontaminata: sicurezza e tracciabilità. Se non si segue la regola d’oro del Rebasing, riscrivere la cronologia dei progetti può essere potenzialmente catastrofico per il flusso di lavoro di collaborazione. E, meno importante, il rebasing perde il contesto fornito da un commit di unione: non è possibile vedere quando le modifiche a monte sono state incorporate nella funzionalità.
Rebasing interattivo
Rebasing interattivo ti dà l’opportunità di modificare i commit quando vengono spostati nel nuovo ramo., Questo è ancora più potente di un rebase automatico, poiché offre il controllo completo sulla cronologia di commit del ramo. In genere, questo viene utilizzato per ripulire una cronologia disordinata prima di unire un ramo di funzionalità in master
.,
Per iniziare interattiva riassegnazione di sessione, passare il i
opzione git rebase
comando:
git checkout feature git rebase -i master
si aprirà un editor di testo con l’elenco di tutti i commit che stanno per essere spostati:
pick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
Questo elenco definisce esattamente ciò che il ramo sarà simile dopo il rebase viene eseguita. Modificando il comandopick
e/o riordinando le voci, puoi rendere la cronologia del ramo simile a quella che vuoi., Per esempio, se il 2 ° commettere risolve un piccolo problema nel 1 ° commettere, è possibile condensare in un unico impegnarsi con il fixup
comando:
pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
Quando si salva e chiudi il file, Git eseguire il rebase secondo le vostre istruzioni, con conseguente progetto di storia che è simile al seguente:
Eliminare le insignificante impegna come questo rende la vostra storia molto più facile da capire. Questo è qualcosa chegit merge
semplicemente non può fare.,
La regola d’oro del Rebasing
Una volta capito cos’è il rebasing, la cosa più importante da imparare è quando non farlo. La regola d’oro di git rebase
è di non usarlo mai su rami pubblici.
Ad esempio, pensa a cosa succederebbe se tu rebased master
sul tuo feature
branch:
Il rebase sposta tutti i commit in master
sulla punta di feature
. Il problema è che questo è successo solo nel tuo repository., Tutti gli altri sviluppatori stanno ancora lavorando con l’originale master
. Dal momento che la riassegnazione risultati in nuovissimo impegna, Git pensare che il master
ramo storia si è discostato da chiunque altro.
L’unico modo per sincronizzare i due master
filiali è quello di unire di nuovo insieme, con un conseguente extra unione commit e due set di commit che contengono le stesse modifiche (quelle originali, e quelle dalla tua ribasato ramo). Inutile dire che questa è una situazione molto confusa.,
Quindi, prima di eseguiregit rebase
, chiediti sempre: “Qualcun altro sta guardando questo ramo?”Se la risposta è sì, togli le mani dalla tastiera e inizia a pensare a un modo non distruttivo per apportare le tue modifiche (ad esempio, il comando git revert
). Altrimenti, sei sicuro di riscrivere la storia quanto vuoi.
Forzare
Se si tenta di spingere il ramo rebasedmaster
in un repository remoto, Git ti impedirà di farlo perché è in conflitto con il ramo remotomaster
., Ma, puoi forzare la spinta a passare attraverso il flag --force
, in questo modo:
# Be very careful with this command! git push --force
Questo sovrascrive il ramo remoto master
per abbinare quello rebased dal tuo repository e rende le cose molto confuse per il resto del tuo team. Quindi, fai molta attenzione a usare questo comando solo quando sai esattamente cosa stai facendo.
Una delle uniche volte in cui dovresti forzare è quando hai eseguito una pulizia locale dopo aver spinto un ramo di funzionalità privato in un repository remoto (ad esempio, per scopi di backup)., Questo è come dire: “Oops, non volevo davvero spingere quella versione originale del ramo delle funzionalità. Prendi invece quello attuale.”Ancora una volta, è importante che nessuno stia lavorando ai commit dalla versione originale del ramo delle funzionalità.
Workflow Walkthrough
Il rebasing può essere incorporato nel flusso di lavoro Git esistente tanto o poco quanto il tuo team è a suo agio. In questa sezione, daremo un’occhiata ai vantaggi che rebasing può offrire nelle varie fasi di sviluppo di una funzionalità.,
Il primo passo in qualsiasi flusso di lavoro che sfrutta git rebase
è quello di creare un ramo dedicato per ogni caratteristica. Ciò fornisce la struttura del ramo necessaria per utilizzare in modo sicuro il rebasing:
Pulizia locale
Uno dei modi migliori per incorporare il rebasing nel flusso di lavoro è ripulire le funzionalità locali in corso. Eseguendo periodicamente una rebase interattiva, puoi assicurarti che ogni commit nella tua funzione sia mirato e significativo., Ciò consente di scrivere il codice senza preoccuparsi di suddividerlo in commit isolati: è possibile risolverlo dopo il fatto.
Quando si chiamagit rebase
, si hanno due opzioni per la nuova base: il ramo genitore della funzione (ad esempio,master
) o un commit precedente nella funzione. Abbiamo visto un esempio della prima opzione nella sezione Rebasing interattivo. Quest’ultima opzione è piacevole quando hai solo bisogno di sistemare gli ultimi commit. Ad esempio, il seguente comando inizia una rebase interattiva degli ultimi 3 commit.,
git checkout feature git rebase -i HEAD~3
Specificando HEAD~3
come nuova base, non stai effettivamente spostando il ramo—stai solo riscrivendo interattivamente i 3 commit che lo seguono. Si noti che questo non incorporerà le modifiche upstream nel ramofeature
.
Se si desidera riscrivere l’intera funzionalità utilizzando questo metodo, il comandogit merge-base
può essere utile per trovare la base originale del ramofeature
., Quanto segue restituisce l’ID commit della base originale, che è quindi possibile passare agit rebase
:
git merge-base feature master
Questo uso del rebasing interattivo è un ottimo modo per introdurregit rebase
nel flusso di lavoro, in quanto riguarda solo le filiali locali. L’unica cosa che altri sviluppatori vedranno è il tuo prodotto finito, che dovrebbe essere una cronologia delle filiali di funzionalità pulita e facile da seguire.
Ma ancora una volta, questo funziona solo per rami di funzionalità private., Se stai collaborando con altri sviluppatori tramite lo stesso ramo di funzionalità, quel ramo è pubblico e non ti è permesso riscriverne la cronologia.
Non esiste un’alternativa git merge
per ripulire i commit locali con una rebase interattiva.
Incorporando le modifiche upstream in una Feature
Nella sezione Panoramica concettuale, abbiamo visto come un ramo di feature può incorporare le modifiche upstream damaster
usandogit merge
ogit rebase
., La fusione è un’opzione sicura che conserva l’intera cronologia del repository, mentre la rebasing crea una cronologia lineare spostando il ramo delle funzionalità sulla punta di master
.
Questo uso di git rebase
è simile a una pulizia locale (e può essere eseguita contemporaneamente), ma nel processo incorpora quei commit upstream da master
.
Tieni presente che è perfettamente legale rebase su un ramo remoto invece dimaster
., Ciò può accadere quando si collabora alla stessa funzionalità con un altro sviluppatore ed è necessario incorporare le modifiche nel repository.,
Per esempio, se tu e un altro sviluppatore di nome John ha aggiunto impegna a: feature
ramo, il repository potrebbe apparire come segue dopo il recupero remoto feature
ramo di Giovanni repository:
È possibile risolvere questo bivio allo stesso modo esatto come integrare le modifiche a monte da master
: unire il vostro locale feature
con john/feature
o rebase locale feature
sulla punta del john/feature
.,
Nota che questo rebase non viola la Regola aurea del Rebasing perché solo i tuoi commit feature
locali vengono spostati—tutto prima che sia intatto. Questo è come dire: “aggiungi le mie modifiche a ciò che John ha già fatto.”Nella maggior parte dei casi, questo è più intuitivo della sincronizzazione con il ramo remoto tramite un commit di unione.
Per impostazione predefinita, il comandogit pull
esegue un’unione, ma è possibile forzarlo a integrare il ramo remoto con una rebase passandogli l’opzione--rebase
.,
Revisione di una funzionalità con una richiesta Pull
Se si utilizzano le richieste pull come parte del processo di revisione del codice, è necessario evitare di utilizzaregit rebase
dopo aver creato la richiesta pull. Non appena effettui la richiesta di pull, altri sviluppatori esamineranno i tuoi commit, il che significa che è un ramo pubblico. Riscrivere la sua storia renderà impossibile per Git e i tuoi compagni di squadra tenere traccia di eventuali commit di follow-up aggiunti alla funzione.
Eventuali modifiche da altri sviluppatori devono essere incorporate congit merge
invece digit rebase
.,
Per questo motivo, di solito è una buona idea ripulire il codice con un rebase interattivo prima di inviare la richiesta di pull.
Integrazione di una funzionalità approvata
Dopo che una funzionalità è stata approvata dal tuo team, hai la possibilità di rinominare la funzionalità sulla punta del ramomaster
prima di utilizzaregit merge
per integrare la funzionalità nella base di codice principale.,
Questa è una situazione simile all’incorporazione di modifiche upstream in un ramo di funzionalità, ma poiché non è consentito riscrivere i commit nel ramo master
, è necessario utilizzare alla fine git merge
per integrare la funzionalità. Tuttavia, eseguendo un rebase prima dell’unione, si ha la certezza che l’unione verrà inoltrata rapidamente, risultando in una cronologia perfettamente lineare. Questo ti dà anche la possibilità di schiacciare qualsiasi commit di follow-up aggiunto durante una richiesta di pull.,
Se non sei del tutto a tuo agio congit rebase
, puoi sempre eseguire la rebase in un ramo temporaneo. In questo modo, se accidentalmente incasini la cronologia della tua funzione, puoi controllare il ramo originale e riprovare. Ad esempio:
git checkout feature git checkout -b temporary-branch git rebase -i master # git checkout master git merge temporary-branch
Riepilogo
E questo è tutto ciò che devi sapere per iniziare a rinominare i tuoi rami., Se preferisci una cronologia pulita e lineare priva di commit di unione non necessari, dovresti raggiungere git rebase
invece di git merge
quando integri le modifiche da un altro ramo.
D’altra parte, se si desidera preservare la cronologia completa del progetto ed evitare il rischio di riscrivere commit pubblici, è possibile attenersi a git merge
. Entrambe le opzioni sono perfettamente valide, ma almeno ora hai la possibilità di sfruttare i vantaggi di git rebase
.
Lascia un commento