Quando si consiglia di utilizzare Git rebase vs. Git merge?
Devo ancora unirmi dopo un rebase riuscito?
Quando si consiglia di utilizzare Git rebase vs. Git merge?
Devo ancora unirmi dopo un rebase riuscito?
Risposte:
Quindi quando usi uno dei due?
init
un nuovo repository, add
il file e commit
. Acquista un nuovo ramo di funzionalità ( checkout -b feature
.) Modifica il file di testo, esegui il commit e ripeti in modo che vi siano due nuovi commit sul ramo di funzionalità. Quindi checkout master
e merge feature
. In log
, vedo il mio commit iniziale sul master, seguito dai due che sono stati uniti dalla funzione. Se si merge --squash feature
, la funzione viene unita in master ma non impegnata, quindi l'unica nuova commit su master sarà quella che si farà da soli.
È semplice. Con rebase dici di usare un altro ramo come nuova base per il tuo lavoro.
Se si dispone, ad esempio, di un ramo master
, si crea un ramo per implementare una nuova funzionalità e si dice di nominarlo cool-feature
, ovviamente il ramo principale è la base per la nuova funzione.
Ora a un certo punto vuoi aggiungere la nuova funzionalità che hai implementato nel master
ramo. Potresti semplicemente passare master
e unire il cool-feature
ramo:
$ git checkout master
$ git merge cool-feature
Ma in questo modo viene aggiunto un nuovo commit fittizio. Se vuoi evitare la storia degli spaghetti puoi rifare :
$ git checkout cool-feature
$ git rebase master
E poi uniscilo in master
:
$ git checkout master
$ git merge cool-feature
Questa volta, poiché il ramo argomento ha gli stessi commit di master più i commit con la nuova funzionalità, l'unione sarà solo un avanzamento veloce.
but this way a new dummy commit is added, if you want to avoid spaghetti-history
- come va?
Sean Schofield
inserisce in un commento: "Rebase è anche bello perché una volta che alla fine unisci le tue cose al master (che è banale come già descritto), le trovi nella parte superiore della cronologia delle tue commit. progetti in cui le funzionalità possono essere scritte ma unite diverse settimane dopo, non si desidera unirle nel master perché vengono "inserite" nel master nella storia. Personalmente mi piace essere in grado di fare git log e vedere quella caratteristica recente proprio in "alto". Nota che le date di commit sono conservate - il rebase non cambia quelle informazioni. "
merge
, rebase
, fast-forward
, etc.) si riferiscono alle manipolazioni specifiche di un grafo orientato aciclico. Diventano più facili da ragionare pensando a quel modello mentale.
Per completare la mia risposta menzionata da TSamper ,
un rebase è spesso una buona idea da fare prima di una fusione, perché l'idea è quella di integrare nel proprio ramo Y
il lavoro del ramo B
su cui vi unirete.
Ma ancora una volta, prima della fusione, risolvi qualsiasi conflitto nel tuo ramo (es: "rebase", come in "rigioca il mio lavoro nel mio ramo a partire da un punto recente dal ramo B
).
Se fatto correttamente, la successiva unione dal tuo ramo a il ramo B
può essere avanzamento veloce.
un'unione influisce direttamente sul ramo di destinazione B
, il che significa che le fusioni dovrebbero essere banali, altrimenti quel ramo B
può essere lungo per tornare a uno stato stabile (tempo per risolvere tutti i conflitti)
il punto di fondersi dopo un rebase?
Nel caso che descrivo, rifaccio B
sul mio ramo, solo per avere l'opportunità di ripetere il mio lavoro da un punto più recente B
, ma rimanendo nel mio ramo.
In questo caso, è ancora necessaria una fusione per portare il mio lavoro "riprodotto" B
.
L'altro scenario ( descritto ad esempio in Git Ready ) è di portare il tuo lavoro direttamente B
attraverso un rebase (che conserva tutti i tuoi bei commit, o ti dà anche l'opportunità di riordinarli attraverso un rebase interattivo).
In quel caso (dove ti ribassi mentre ti trovi nel ramo B), hai ragione: non sono necessarie ulteriori fusioni:
Un albero Git di default quando non ci siamo uniti né rielaborati
otteniamo rifacendo:
Questo secondo scenario riguarda: come posso riportare le nuove funzionalità nel master.
Il mio punto, descrivendo il primo scenario di rebase, è ricordare a tutti che un rebase può anche essere usato come un passo preliminare a quello (che "riporta le nuove funzionalità nel master").
Puoi usare rebase per portare prima il master "nel" ramo delle nuove funzionalità: il rebase riprodurrà i commit delle nuove funzionalità dal HEAD master
, ma ancora nel ramo delle nuove funzionalità, spostando efficacemente il punto di partenza del tuo ramo da un vecchio commit principale a HEAD-master
.
Ciò consente di risolvere eventuali conflitti nel proprio ramo (che significa, in isolamento, consentendo al master di continuare ad evolversi in parallelo se la fase di risoluzione dei conflitti impiega troppo tempo).
Quindi puoi passare a master e unire new-feature
(o rifare new-feature
su master
se vuoi conservare i commit eseguiti nel tuonew-feature
ramo).
Così:
master
.In caso di dubbi, utilizzare unisci.
Le uniche differenze tra un rebase e una fusione sono:
Quindi la risposta breve è scegliere rebase o unire in base a come vuoi che sia la tua storia .
Ci sono alcuni fattori che dovresti considerare quando scegli quale operazione usare.
Se è così, non rifare. Rebase distrugge il ramo e quegli sviluppatori avranno archivi rotti / incoerenti a meno che non lo utilizzinogit pull --rebase
. Questo è un buon modo per turbare rapidamente altri sviluppatori.
Rebase è un'operazione distruttiva. Ciò significa che, se non lo si applica correttamente, si potrebbe perdere il lavoro impegnato e / o interrompere la coerenza dei repository di altri sviluppatori.
Ho lavorato in team in cui tutti gli sviluppatori provenivano da un'epoca in cui le aziende potevano permettersi personale dedicato per gestire le succursali e le fusioni. Quegli sviluppatori non sanno molto di Git e non vogliono sapere molto. In queste squadre non rischierei di raccomandare il rebasing per nessun motivo.
Alcuni team utilizzano il modello di ramo per funzione in cui ogni ramo rappresenta una caratteristica (o correzione di bug, o caratteristica secondaria, ecc.) In questo modello il ramo aiuta a identificare insiemi di commit correlati. Ad esempio, è possibile ripristinare rapidamente una funzione ripristinando l'unione di quel ramo (per essere onesti, questa è un'operazione rara). O diff una funzionalità confrontando due rami (più comuni). Rebase distruggerebbe il ramo e questo non sarebbe semplice.
Ho anche lavorato su team che hanno utilizzato il modello di filiale per sviluppatore (siamo stati tutti lì). In questo caso il ramo stesso non fornisce alcuna informazione aggiuntiva (il commit ha già l'autore). Non ci sarebbe nulla di male nel rifare.
Il ripristino (come nell'annullamento) di un rebase è considerevolmente difficile e / o impossibile (se il rebase ha avuto conflitti) rispetto al ripristino di una fusione. Se pensi che ci sia una possibilità che vorrai ripristinare, allora usa unisci.
Le operazioni di rebase devono essere eseguite con un corrispondente git pull --rebase
. Se lavori da solo potresti essere in grado di ricordare quali dovresti usare al momento opportuno. Se stai lavorando in una squadra, questo sarà molto difficile da coordinare. Questo è il motivo per cui la maggior parte dei flussi di lavoro rebase consiglia di utilizzare rebase per tutte le fusioni (egit pull --rebase
per tutti i pull).
Supponendo di avere la seguente unione:
B -- C
/ \
A--------D
Alcune persone affermeranno che l'unione "distrugge" la cronologia del commit perché se si guardasse il registro del solo ramo master (A - D), si perderebbero gli importanti messaggi di commit contenuti in B e C.
Se questo fosse vero non avremmo domande come questa . Fondamentalmente, vedrai B e C a meno che tu non chieda esplicitamente di non vederli (usando - first-parent). Questo è molto facile da provare da soli.
I due approcci si fondono in modo diverso, ma non è chiaro che uno sia sempre migliore dell'altro e potrebbe dipendere dal flusso di lavoro dello sviluppatore. Ad esempio, se uno sviluppatore tende a impegnarsi regolarmente (ad es. Forse si impegnano due volte al giorno mentre passano dal lavoro a casa), potrebbero esserci molti commit per una determinata filiale. Molti di questi commit potrebbero non assomigliare al prodotto finale (tendo a riformattare il mio approccio una o due volte per funzione). Se qualcun altro stava lavorando su un'area di codice correlata e cercasse di modificare le mie modifiche, potrebbe essere un'operazione abbastanza noiosa.
Se ti piace fare lo pseudonimo rm
di rm -rf
"risparmiare tempo", forse rebase fa per te.
Penso sempre che un giorno mi imbatterò in uno scenario in cui Git rebase è lo strumento fantastico che risolve il problema. Proprio come penso che mi imbatterò in uno scenario in cui Git reflog è uno strumento fantastico che risolve il mio problema. Lavoro con Git da oltre cinque anni. Non è successo
Le storie disordinate non sono mai state un problema per me. Non leggo mai la mia cronologia degli impegni come un romanzo emozionante. La maggior parte delle volte ho bisogno di una storia che userò comunque Git Blame o Git bisect. In tal caso, avere il commit della fusione è effettivamente utile per me, perché se la fusione ha introdotto il problema, sono informazioni significative per me.
Mi sento in dovere di dire che mi sono ammorbidito personalmente sull'uso di rebase, sebbene il mio consiglio generale sia ancora valido. Recentemente ho interagito molto con il progetto Angular 2 Material . Hanno usato rebase per mantenere una cronologia di commit molto pulita. Questo mi ha permesso di vedere facilmente quale commit ha risolto un determinato difetto e se quel commit è stato incluso o meno in una versione. Serve come un ottimo esempio dell'uso corretto di rebase.
Molte risposte qui dicono che la fusione trasforma tutti i tuoi commit in uno, e quindi suggerisce di usare rebase per preservare i tuoi commit. Questo non è corretto E una cattiva idea se hai già spinto i tuoi impegni .
Unire non cancella i tuoi commit. Unisci conserva la storia! (basta guardare gitk) Rebase riscrive la storia, che è una brutta cosa dopo averla spinta .
Usa unisci - non rifare ogni volta che hai già premuto.
Ecco il punto di vista di Linus (autore di Git) (ora ospitato sul mio blog, recuperato dalla Wayback Machine ). È davvero una buona lettura.
Oppure puoi leggere la mia versione della stessa idea di seguito.
Rebasing di un ramo sul master:
Al contrario, unendo un ramo argomento in master:
TLDR: dipende da ciò che è più importante: una storia ordinata o una rappresentazione fedele della sequenza di sviluppo
Se una cronologia ordinata è la più importante, allora ti rifaresti prima e poi uniresti le modifiche, quindi è chiaro esattamente quale sia il nuovo codice. Se hai già spinto il tuo ramo, non effettuare il rebase a meno che tu non possa affrontarne le conseguenze.
Se la vera rappresentazione della sequenza è la più importante, ti uniresti senza rifare.
Unire significa: creare un nuovo nuovo commit che unisce le mie modifiche alla destinazione. Nota: questo nuovo commit avrà due genitori: l'ultimo commit dalla tua serie di commit e l'ultimo commit dell'altro ramo che stai unendo.
Rebase significa: creare una serie completamente nuova di commit, usando il mio set attuale di commit come suggerimenti. In altre parole, calcola come sarebbero state le mie modifiche se avessi iniziato a realizzarle dal punto in cui mi sto rifacendo. Dopo il rebase, pertanto, potrebbe essere necessario testare nuovamente le modifiche e durante il rebase, potrebbero verificarsi alcuni conflitti.
Detto questo, perché dovresti rifare? Giusto per mantenere chiara la storia dello sviluppo. Supponiamo che tu stia lavorando sulla funzione X e quando hai finito, unisci le tue modifiche. La destinazione ora avrà un unico commit che direbbe qualcosa sulla falsariga di "Funzione aggiunta X". Ora, invece di unire, se si è rigenerato e quindi unito, la cronologia di sviluppo della destinazione conterrebbe tutti i singoli commit in un'unica progressione logica. Questo rende la revisione delle modifiche in seguito molto più semplice. Immagina quanto sarebbe difficile rivedere la storia dello sviluppo se 50 sviluppatori unissero varie funzionalità in ogni momento.
Detto questo, se hai già spinto il ramo su cui stai lavorando a monte, non dovresti riformulare, ma unisci invece. Per i rami che non sono stati spinti a monte, rebase, test e unione.
Un'altra volta che potresti voler rifare è quando vuoi sbarazzarti dei commit dalla tua filiale prima di spingere a monte. Ad esempio: commit che introducono un po 'di codice di debug all'inizio e altri commit su quel clean up di quel codice. L'unico modo per farlo è eseguire un rebase interattivo:git rebase -i <branch/commit/tag>
AGGIORNAMENTO: vuoi anche usare rebase quando usi Git per interfacciarti con un sistema di controllo versione che non supporta la cronologia non lineare ( ad esempio Subversion ). Quando si utilizza il bridge git-svn, è molto importante che le modifiche che si ricollegano in Subversion siano un elenco sequenziale di modifiche in cima alle modifiche più recenti nel trunk. Esistono solo due modi per farlo: (1) ricreare manualmente le modifiche e (2) usare il comando rebase, che è molto più veloce.
AGGIORNAMENTO 2: Un altro modo di pensare a un rebase è che consente una sorta di mappatura dal tuo stile di sviluppo allo stile accettato nel repository a cui ti stai impegnando. Diciamo che ti piace impegnarti in piccoli, piccoli pezzi. Hai un commit per correggere un errore di battitura, un commit per eliminare il codice inutilizzato e così via. Quando hai finito quello che devi fare, hai una lunga serie di impegni. Ora diciamo che il repository che stai impegnando incoraggia grandi commit, quindi per il lavoro che stai facendo, uno si aspetterebbe uno o forse due commit. Come prendi la tua serie di commit e li comprimi a ciò che è previsto? Utilizzeresti un rebase interattivo e comprimerai i tuoi piccoli commit in meno blocchi più grandi. Lo stesso vale se fosse necessario il contrario: se il tuo stile era composto da alcuni grandi commit, ma il repository richiedeva lunghe stringhe di piccoli commit. Userai anche un rebase per farlo. Se invece ti fossi unito, ora hai innestato il tuo stile di commit nel repository principale. Se ci sono molti sviluppatori, puoi immaginare quanto sia difficile seguire una storia con diversi stili di commit dopo qualche tempo.
AGGIORNAMENTO 3: Does one still need to merge after a successful rebase?
Sì. Il motivo è che un rebase comporta essenzialmente un "spostamento" di commit. Come ho detto sopra, vengono calcolati questi commit, ma se hai avuto 14 commit dal punto di diramazione, quindi supponendo che nulla vada storto con il tuo rebase, sarai 14 commit in avanti (dal punto su cui ti stai riepilogando) dopo il rebase è fatto. Hai avuto un ramo prima di un rebase. Dopo avrai un ramo della stessa lunghezza. Devi ancora unirti prima di pubblicare le modifiche. In altre parole, rifa tutte le volte che vuoi (di nuovo, solo se non hai spinto le tue modifiche a monte). Unisci solo dopo il rebase.
git merge
supporta l' --no-ff
opzione che lo obbliga a eseguire un commit di unione.
Mentre la fusione è sicuramente il modo più semplice e comune per integrare i cambiamenti, non è l'unico: Rebase è un mezzo alternativo di integrazione.
Comprendere Unisci un po 'meglio
Quando Git esegue un'unione, cerca tre commit:
Avanzamento rapido o Unisci commit
In casi molto semplici, una delle due filiali non ha nuovi commit da quando è avvenuta la ramificazione - il suo ultimo commit è ancora l'antenato comune.
In questo caso, eseguire l'integrazione è semplicissimo: Git può semplicemente aggiungere tutti i commit dell'altro ramo in cima al commit antenato comune. In Git, questa forma di integrazione più semplice è chiamata unione "avanzamento rapido". Entrambi i rami condividono quindi la stessa storia esatta.
In molti casi, tuttavia, entrambi i rami si sono mossi in avanti individualmente.
Per effettuare un'integrazione, Git dovrà creare un nuovo commit che contenga le differenze tra loro: il commit di unione.
Human Commit & Merge Commit
Normalmente, un impegno viene creato con cura da un essere umano. È un'unità significativa che avvolge solo le modifiche correlate e le annota con un commento.
Un commit di unione è un po 'diverso: invece di essere creato da uno sviluppatore, viene creato automaticamente da Git. E invece di racchiudere un insieme di modifiche correlate, il suo scopo è quello di collegare due rami, proprio come un nodo. Se si desidera comprendere un'operazione di unione in un secondo momento, è necessario dare un'occhiata alla cronologia di entrambi i rami e al grafico di commit corrispondente.
Integrazione con Rebase
Alcune persone preferiscono andare senza tali impegni di unione automatica. Invece, vogliono che la storia del progetto appaia come se si fosse evoluta in un'unica linea retta. Nessuna indicazione rimane che fosse stato diviso in più rami ad un certo punto.
Esaminiamo passo dopo passo un'operazione di rebase. Lo scenario è lo stesso degli esempi precedenti: vogliamo integrare le modifiche dal ramo B al ramo A, ma ora usando rebase.
Lo faremo in tre passaggi
git rebase branch-A // Synchronises the history with branch-A
git checkout branch-A // Change the current branch to branch-A
git merge branch-B // Merge/take the changes from branch-B to branch-A
Innanzitutto, Git "annulla" tutti i commit sul ramo A che sono avvenuti dopo che le linee hanno iniziato a diradarsi (dopo il commit dell'antenato comune). Tuttavia, ovviamente, non li scarterà: invece puoi pensare a quei commit come "salvati temporaneamente".
Successivamente, applica i commit della filiale B che vogliamo integrare. A questo punto, entrambi i rami sembrano esattamente uguali.
Nella fase finale, i nuovi commit sul ramo A sono ora riapplicati, ma su una nuova posizione, in cima ai commit integrati del ramo B (sono ricondotti).
Il risultato sembra che lo sviluppo sia avvenuto in linea retta. Invece di un commit di unione che contiene tutte le modifiche combinate, la struttura di commit originale è stata mantenuta.
Infine, ottieni un ramo pulito -ramo A senza commit indesiderati e generati automaticamente.
Nota: tratto dal fantastico post di git-tower
. Gli svantaggi di rebase
è anche una buona lettura nello stesso post.
Prima di unire / rebase:
A <- B <- C [master]
^
\
D <- E [branch]
Dopo git merge master
:
A <- B <- C
^ ^
\ \
D <- E <- F
Dopo git rebase master
:
A <- B <- C <- D' <- E'
(A, B, C, D, E e F sono commit)
Questo esempio e molte altre informazioni ben illustrate su Git sono disponibili in Git The Basics Tutorial .
Questa frase lo ottiene:
In generale, il modo per ottenere il meglio da entrambi i mondi è di riformulare i cambiamenti locali che hai apportato, ma che non hai ancora condiviso, prima di spingerli per ripulire la tua storia, ma non rifare mai nulla che hai spinto da qualche parte .
Questa risposta è ampiamente orientata su Git Flow . Le tabelle sono state generate con il simpatico generatore di tabelle ASCII e gli alberi della storia con questo meraviglioso comando ( aliased as git lg
):
git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'
Le tabelle sono in ordine cronologico inverso per essere più coerenti con gli alberi della storia. Vedi anche la differenza tra git merge
e git merge --no-ff
prima (di solito vuoi usare git merge --no-ff
in quanto rende la tua storia più vicina alla realtà):
git merge
comandi:
Time Branch "develop" Branch "features/foo"
------- ------------------------------ -------------------------------
15:04 git merge features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Risultato:
* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
| Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
| Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge --no-ff
comandi:
Time Branch "develop" Branch "features/foo"
------- -------------------------------- -------------------------------
15:04 git merge --no-ff features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Risultato:
* 1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/ Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge
vs git rebase
Primo punto: unisci sempre le funzionalità allo sviluppo, non rifare mai lo sviluppo dalle funzionalità . Questa è una conseguenza della Regola d'oro del rifacimento :
La regola d'oro di
git rebase
non usarlo mai su rami pubblici .
Non rifare mai nulla che hai spinto da qualche parte.
Aggiungerei personalmente: a meno che non si tratti di un ramo di funzionalità E tu e il tuo team siete consapevoli delle conseguenze .
Quindi la domanda di git merge
vs si git rebase
applica quasi solo ai rami delle caratteristiche (negli esempi seguenti, --no-ff
è sempre stata usata durante l'unione). Si noti che poiché non sono sicuro che esista una soluzione migliore ( esiste un dibattito ), fornirò solo il comportamento di entrambi i comandi. Nel mio caso, preferisco usare git rebase
in quanto produce un albero della storia più bello :)
git merge
comandi:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Risultato:
* c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\ Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | | Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | | Fourth commit - Christophe
* | | 98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \ Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
comandi:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git rebase features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Risultato:
* 7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
develop
a un ramo di funzionalitàgit merge
comandi:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git merge --no-ff develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Risultato:
* 9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\ Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* | 5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | | Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ / Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
comandi:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git rebase develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Risultato:
* b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git cherry-pick
Quando hai solo bisogno di un commit specifico, git cherry-pick
è una buona soluzione (l' -x
opzione aggiunge una riga che dice " (ciliegia selezionata da commit ...) " al corpo del messaggio di commit originale, quindi di solito è una buona idea usarlo - git log <commit_sha1>
per vedere esso):
comandi:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git cherry-pick -x <second_commit_sha1>
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Risultato:
* 50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| | Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git pull --rebase
Non sono sicuro di poterlo spiegare meglio di Derek Gourlay ... Fondamentalmente, usa git pull --rebase
invece di git pull
:) Ciò che manca nell'articolo è che puoi abilitarlo di default :
git config --global pull.rebase true
git rerere
Ancora una volta, ben spiegato qui . In parole semplici, se lo abiliti, non dovrai più risolvere lo stesso conflitto più volte.
Il libro Pro Git ha una spiegazione davvero buona sulla pagina di rebasing .
Fondamentalmente una fusione prenderà due commit e li combinerà.
Un rebase andrà all'antenato comune sui due e applicherà in modo incrementale le modifiche l'una sull'altra. Questo rende la storia "più pulita" e più lineare.
Ma quando ti ribassi, abbandoni gli impegni precedenti e ne crei di nuovi. Quindi non dovresti mai rifare il repository che è pubblico. Le altre persone che lavorano al repository ti odieranno.
Solo per questo motivo mi fondo quasi esclusivamente. Il 99% delle volte le mie filiali non differiscono molto, quindi se ci sono conflitti è solo in uno o due posti.
Git rebase è usato per rendere lineari i percorsi di ramificazione nella storia e la struttura del repository.
Viene anche usato per mantenere privati i rami creati da te, poiché dopo aver riformulato e inviato le modifiche al server, se elimini il tuo ramo, non ci saranno prove del ramo su cui hai lavorato. Quindi la tua filiale è ora la tua preoccupazione locale.
Dopo aver effettuato il rebase, ci sbarazziamo anche di un commit extra che vedevamo se eseguiamo una fusione normale.
E sì, uno deve ancora fare l'unione dopo un rebase riuscito poiché il comando rebase mette il tuo lavoro in cima al ramo che hai menzionato durante rebase, diciamo master, e fa il primo commit del tuo ramo come discendente diretto del ramo master . Ciò significa che ora possiamo eseguire un'unione di avanzamento rapido per portare le modifiche da questo ramo al ramo principale.
Alcuni esempi pratici, in qualche modo collegati allo sviluppo su larga scala in cui Gerrit viene utilizzato per l'integrazione di revisione e consegna:
Mi unisco quando elevo il mio ramo di funzionalità a un nuovo master remoto. Ciò consente un minimo lavoro di sollevamento ed è facile seguire la storia dello sviluppo delle funzionalità, ad esempio Gitk .
git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature
Mi unisco quando preparo un commit di consegna.
git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master
Mi ribasso quando il commit della consegna non riesce a integrarsi per qualsiasi motivo e devo aggiornarlo verso un nuovo master remoto.
git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
È stato spiegato molte volte cosa rebase e che cos'è l'unione, ma quando dovresti usare cosa?
Quando dovresti usare rebase?
Come Git rebase cambia la storia. Pertanto non dovresti usarlo quando qualcun altro sta lavorando sullo stesso ramo / se l'hai spinto. Ma se hai un ramo locale puoi fare un master rebase di unione prima di unire nuovamente il tuo ramo in master per mantenere una cronologia più pulita. In questo modo, dopo esserti unito al ramo principale, non sarà visibile che hai usato un ramo nel ramo principale: la cronologia è "più pulita" poiché non hai generato automaticamente il "fuso ..", ma hai ancora il cronologia completa nella succursale principale senza aver eseguito il commit automatico di "fusione ..".
Assicurati, tuttavia, git merge feature-branch --ff-only
di assicurarti che non vi siano conflitti nella creazione di un singolo commit quando stai riconducendo la tua funzionalità alla pagina principale. Questo è interessante se stai usando i rami delle caratteristiche per ogni attività su cui lavori mentre ottieni la cronologia del ramo delle caratteristiche, ma non un commit "unito .."
Un secondo scenario sarebbe se ti sei ramificato da un ramo e vuoi sapere cosa è cambiato nel ramo principale. Rebase ti fornisce le informazioni in quanto include ogni singolo commit.
Quando utilizzare Merge?
Quando non è necessario o si desidera avere tutta la cronologia di un ramo di funzionalità nel proprio ramo principale o se altri stanno lavorando sullo stesso ramo / è stato eseguito il push. Se vuoi ancora avere la cronologia, basta unire il master nel ramo della funzionalità prima di unire il ramo della funzionalità nel master. Ciò si tradurrà in un'unione di avanzamento rapido in cui hai la cronologia del ramo della funzione nel tuo master (incluso il commit di unione che era nel tuo ramo della funzione perché hai unito il master in esso).
Quando lo uso git rebase
? Quasi mai, perché riscrive la storia. git merge
è quasi sempre la scelta preferibile, perché rispetta ciò che è realmente accaduto nel tuo progetto.