Quando usi Git rebase invece di Git merge?


1549

Quando si consiglia di utilizzare Git rebase vs. Git merge?

Devo ancora unirmi dopo un rebase riuscito?




6
Un problema con le persone a cui piace usare rebase è che impedisce loro di spingere il loro codice regolarmente. Quindi, desiderare una cronologia pulita impedisce loro di condividere il proprio codice, che ritengo sia più importante.
static_rtti,

9
@static_rtti: non è vero. Stai utilizzando un flusso basato su rebase sbagliato se ti impedisce di inviare regolarmente le modifiche.
juzzlin,

5
È un vero peccato che la risposta di Andrew Arnott e la risposta di Pace non siano state pubblicate prima, poiché rispondono a questa domanda in modo più completo rispetto alle risposte precedenti che avevano già accumulato molti voti.
Mark Booth,

Risposte:


1136

Versione breve

  • Unisci accetta tutte le modifiche in un ramo e le unisce in un altro ramo in un commit.
  • Rebase dice che voglio che il punto in cui mi sono ramificato si sposti su un nuovo punto di partenza

Quindi quando usi uno dei due?

Merge

  • Supponiamo che tu abbia creato un ramo allo scopo di sviluppare una singola funzione. Quando vuoi riportare quelle modifiche al master, probabilmente vuoi unire (non ti interessa mantenere tutti i commit temporanei).

rebase

  • Un secondo scenario sarebbe se hai iniziato a fare qualche sviluppo e poi un altro sviluppatore ha apportato una modifica non correlata. Probabilmente si desidera estrarre e quindi rifare la base per basare le modifiche dalla versione corrente dal repository.

105
@Rob ha menzionato il mantenimento degli commit temporanei durante l'unione. Per impostazione predefinita, credo che la fusione del ramo B (un ramo di funzionalità su cui hai lavorato) nel ramo M (il ramo principale) creerà un commit in M ​​per ciascun commit effettuato in B da quando i due sono divergenti. Ma se si unisce usando l'opzione --squash, tutti i commit effettuati sul ramo B verranno "raggruppati insieme" e uniti come un unico commit sul ramo M, mantenendo il registro sul ramo principale bello e pulito. Schiacciare è probabilmente quello che vuoi se hai numerosi sviluppatori che lavorano in modo indipendente e si fondono nuovamente in master.
spaaarky21,

19
Credo che l'ipotesi di @ spaaarky21 sulla fusione non sia corretta. Se unisci un ramo B nel master M, ci sarà un solo commit su M (anche se B ha più commit), indipendentemente dal fatto che tu usi una fusione semplice o --squash. Quello che farà --squash è eliminare il riferimento a B come genitore. Una buona visualizzazione è qui: syntevo.com/smartgithg/howtos.html?page=workflows.merge
jpeskin

14
@jpeskin Non è quello che sto vedendo. Ho appena fatto un test rapido per verificarlo. Creare una directory con un file di testo, initun nuovo repository, addil 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 mastere 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.
spaaarky21,

21
@ spaaarky21 Sembra che siamo entrambi a metà ragione. Quando è possibile una fusione in avanti veloce (come nel tuo esempio), git per impostazione predefinita includerà tutti i commit nel ramo funzione B (o come suggerisci, puoi usare --squash per combinare in un singolo commit). Ma nel caso in cui ci siano due rami divergenti M e B che stai unendo, git non includerà tutti i singoli commit dal ramo B se uniti in M ​​(indipendentemente dal fatto che tu usi o meno --squash).
jpeskin,

6
Perché il "(non ti interessa mantenere tutti i commit temporanei)" a parte questa risposta? Non aveva senso nel '09 e non ha senso ora. Inoltre, sicuramente vorresti rifare solo se un altro sviluppatore ha apportato le modifiche correlate di cui avevi bisogno - se hanno apportato modifiche non correlate, il ramo della tua funzionalità dovrebbe fondersi facilmente senza conflitti e la tua cronologia verrebbe mantenuta.
Mark Booth,

372

È 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 masterramo. Potresti semplicemente passare mastere unire il cool-featureramo:

$ 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.


31
but this way a new dummy commit is added, if you want to avoid spaghetti-history- come va?
ア レ ッ ク ス

6
Inoltre, la bandiera --no-ff di merge è molto utile.
Aldo 'xoen' Giambelluca,

3
@ ア レ ッ ク ス come l'utente lo Sean Schofieldinserisce 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. "
Adrien Be

4
Credo sopporta ripetere qui - ricordare che tutti questi termini ( 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.
Roy Tinker,

10
@Aldo Non c'è niente di "pulito" o "ordinato" in una storia rinnovata. È generalmente sporco e IMHO orribile perché non hai idea di cosa sia realmente successo. La storia di Git "più pulita" è quella che si è effettivamente verificata. :)
Marnen Laibow-Koser

269

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 Yil lavoro del ramo Bsu 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 Bpuò essere avanzamento veloce.

  • un'unione influisce direttamente sul ramo di destinazione B, il che significa che le fusioni dovrebbero essere banali, altrimenti quel ramo Bpuò 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 Bsul 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 Battraverso 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

rebase1

otteniamo rifacendo:

rebase3

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-featuresu masterse vuoi conservare i commit eseguiti nel tuonew-feature ramo).

Così:

  • "rebase vs. merge" può essere visto come due modi per importare un lavoro, diciamo, master.
  • Ma "rebase then merge" può essere un flusso di lavoro valido per risolvere prima il conflitto in isolamento, quindi riportare indietro il lavoro.

17
l'unione dopo rebase è un banale avanzamento veloce senza dover risolvere i conflitti.
obecalp,

4
@obelcap: In effetti, questa è una specie di idea: prendi tutto il conflitto di problemi nel tuo ambiente (rebase master all'interno del tuo ramo di nuove funzionalità), quindi co-master, unisci new-feature: 1 pico-second (fast- avanti) se il maestro non ha avuto evoluzioni
VonC

27
Rebase è anche bello perché una volta che alla fine unisci le tue cose al master (che è banale come già descritto), le hai inserite nella parte superiore della tua cronologia di commit. Sui progetti più grandi 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 recente funzionalità proprio nella parte superiore. Si noti che le date di commit vengono conservate - rebase non modifica tali informazioni.
Sean Schofield,

3
@Joe: mentalmente stai dicendo "replay qualsiasi mio cambiamento (fatto in modo isolato nel mio ramo privato) sopra l'altro ramo, ma lasciami nel mio ramo privato una volta che il rebase è terminato". Questa è una buona opportunità per ripulire la storia locale, evitando "commit del checkpoint", bisect rotto e risultati di colpa errati. Vedi "Git workflow": sandofsky.com/blog/git-workflow.html
VonC

4
@scoarescoare la chiave è vedere come le modifiche locali sono compatibili in cima all'ultimo ramo upstream. Se uno dei tuoi commit introduce un conflitto, lo vedrai immediatamente. Una fusione introduce un solo commit (unito), che potrebbe innescare molti conflitti senza un modo semplice per vedere quale, tra i tuoi commit locali, ha aggiunto detto conflitto. Quindi, oltre ad una storia più pulita, si ottiene una visione più precisa dei cambiamenti che si presento, impegnarsi per commit (riprodotto dal rebase), al contrario di tutte le modifiche introdotte dal ramo di monte (scaricati in un unico merge).
VonC

229

TL; DR

In caso di dubbi, utilizzare unisci.

Risposta breve

Le uniche differenze tra un rebase e una fusione sono:

  • La struttura ad albero risultante della cronologia (generalmente evidente solo quando si guarda un grafico di commit) è diversa (uno avrà rami, l'altro no).
  • L'unione generalmente crea un commit aggiuntivo (ad es. Nodo nella struttura).
  • Unisci e rebase gestirà i conflitti in modo diverso. Rebase presenterà conflitti un commit alla volta in cui unione li presenterà tutti in una volta.

Quindi la risposta breve è scegliere rebase o unire in base a come vuoi che sia la tua storia .

Risposta lunga

Ci sono alcuni fattori che dovresti considerare quando scegli quale operazione usare.

Il ramo da cui stai ricevendo le modifiche viene condiviso con altri sviluppatori esterni al tuo team (ad esempio open source, pubblico)?

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.

Quanto è competente il tuo team di sviluppo?

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.

Il ramo stesso rappresenta informazioni utili?

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.

Potresti voler ripristinare l'unione per qualche motivo?

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.

Lavori in una squadra? In tal caso, sei disposto ad adottare un approccio tutto o niente su questo ramo?

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).

Miti comuni

Unisci distrugge la storia (le zucca si impegnano)

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.

Rebase consente fusioni più sicure / più semplici

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.

Rebase è più cool / più sexy / più professionale

Se ti piace fare lo pseudonimo rmdi rm -rf"risparmiare tempo", forse rebase fa per te.

I miei due centesimi

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.

Aggiornamento (4/2017)

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.


5
Dovrebbe essere la risposta convalidata.
Mik378,

Questa è sicuramente la risposta migliore. Soprattutto con l'osservazione chiarita a nell'ultimo aggiornamento. Aiuta a mantenere la cronologia git pulita e chiara, ma usala in modo sicuro.
zquintana,

5
Adoro soprattutto questa risposta. Ma: Rebase non crea una storia "pulita". Rende una storia più lineare, ma non è affatto la stessa cosa, dal momento che chissà molto "sporcizia" che ogni commit si nasconde? La storia di Git più pulita e chiara è quella che mantiene la branca e l'integrità.
Marnen Laibow-Koser

3
"Mito comune, vedi commette B e C": Non necessariamente !! In realtà vedi B e C solo se l'unione è stata un'unione di avanzamento rapido e ciò è possibile solo se non ci sono stati conflitti. Se ci sono conflitti, ottieni un unico commit! Tuttavia: puoi unire prima il master nella funzione e risolvere i conflitti lì e se poi unisci la funzionalità al master, ottieni i commit B e C e un singolo commit X dalla (prima) unione dal master alla funzionalità nella tua cronologia.
Jeremy Benks,

ottima spiegazione! Deve essere votato di più!
dimmer

185

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:

  • fornisce un'idea errata di come sono stati creati i commit
  • inquina il master con un sacco di commit intermedi che potrebbero non essere stati ben testati
  • potrebbe effettivamente introdurre interruzioni di build su questi commit intermedi a causa delle modifiche apportate al master tra quando è stato creato il ramo dell'argomento originale e quando è stato riformulato.
  • rende difficile trovare buoni posti in master per il checkout.
  • Fa sì che i timestamp su commit non si allineino con il loro ordine cronologico nella struttura. Quindi vedresti che il commit A precede il commit B nel master, ma il commit B è stato creato per primo. (Che cosa?!)
  • Produce più conflitti, poiché i singoli commit nel ramo dell'argomento possono comportare conflitti di fusione che devono essere risolti individualmente (ulteriori bugie nella storia di ciò che è accaduto in ciascun commit).
  • è una riscrittura della storia. Se il ramo da ridisegnare è stato spinto ovunque (condiviso con chiunque tranne te stesso), hai rovinato tutti gli altri che hanno quel ramo da quando hai riscritto la storia.

Al contrario, unendo un ramo argomento in master:

  • conserva la cronologia di dove sono stati creati i rami dell'argomento, comprese eventuali fusioni dal master al ramo dell'argomento per mantenerlo aggiornato. Hai davvero un'idea precisa del codice con cui lo sviluppatore stava lavorando durante la creazione.
  • master è un ramo costituito principalmente da fusioni e ognuno di questi commit di tipo merge è in genere un "buon punto" nella storia che è sicuro verificare, perché è lì che il ramo di argomento era pronto per essere integrato.
  • vengono conservati tutti i singoli commit del ramo argomento, incluso il fatto che si trovavano in un ramo argomento, quindi isolare tali modifiche è naturale e si può eseguire il drill-through dove richiesto.
  • i conflitti di unione devono essere risolti una sola volta (nel punto di unione), quindi le modifiche intermedie al commit apportate nel ramo dell'argomento non devono essere risolte in modo indipendente.
  • può essere fatto più volte senza problemi. Se integri periodicamente il ramo tematico, le persone possono continuare a sviluppare il ramo tematico e continuare a fondersi in modo indipendente.

3
Inoltre, git merge ha l'opzione "--no-ff" (nessun avanzamento rapido) che ti permette di ripristinare tutte le modifiche introdotte da una certa unione in modo molto semplice.
Tiago,

3
Renderlo più chiaro: fai riferimento alla situazione "ogni volta che hai già spinto" - questo dovrebbe essere audace. Il link Link to Linus è eccezionale, tra l'altro, lo chiarisce.
honzajde,

2
ma non è buona prassi "aggiornare" dal master al ramo argomento, prima di unire il ramo argomento al master tramite PR (per risolvere i conflitti nel ramo, non il master)? Lo stiamo facendo in questo modo, quindi la maggior parte dei rami di argomento ha come ultimo commit "unisci master ramo in argomento -..." ma qui questo è elencato come una "caratteristica" di rebasing e nessuno lo menziona per la fusione ...?
ProblemsOfSumit

2
@AndrewArnott "La maggior parte dei settori dovrebbe essere in grado di fondersi senza conflitti nei rami di destinazione" Come dovrebbe essere possibile quando 20 sviluppatori lavorano su 30 rami? Ci saranno delle fusioni mentre lavori sul tuo, quindi ovviamente devi aggiornare il tuo argomento dalla destinazione prima di creare un PR ... no?
ProblemsOfSumit

3
Di solito, @Sumit. Git può fondere bene entrambe le direzioni anche se sono state apportate modifiche a uno o entrambi i rami. Solo quando le stesse righe di codice (o molto vicine) vengono modificate su due rami si ottengono conflitti. Se ciò accade frequentemente in qualsiasi team, il team dovrebbe ripensare il modo in cui distribuisce il lavoro poiché la risoluzione dei conflitti è una tassa e li rallenta.
Andrew Arnott,

76

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.


1
Un'unione con il master potrebbe comportare un avanzamento veloce. In un ramo di funzionalità potrebbero esserci alcuni commit, che hanno bug minori o non si compilano nemmeno. Se si eseguono solo test di unità in un ramo di funzionalità, alcuni errori di integrazione possono essere visualizzati. Prima di fondersi con il master, sono richiesti test di integrazione che possono mostrare alcuni bug. Se questi sono corretti, la funzione potrebbe essere integrata. Dato che non si desidera impegnare il codice buggy da padroneggiare, un rebase sembra necessario al fine di prevenire un avanzamento rapido di tutti i commit.
mbx,

1
@mbx git mergesupporta l' --no-ffopzione che lo obbliga a eseguire un commit di unione.
Gavin S. Yancey,

63

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:

  • (1) Commesso antenato comune. Se segui la storia di due rami in un progetto, hanno sempre almeno un commit in comune: a questo punto, entrambi i rami avevano lo stesso contenuto e quindi si sono evoluti in modo diverso.
  • (2) + (3) Endpoint di ciascun ramo. L'obiettivo di un'integrazione è combinare gli stati attuali di due rami. Pertanto, le rispettive ultime revisioni sono di particolare interesse. La combinazione di questi tre commit comporterà l'integrazione a cui aspiriamo.

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.

Inserisci qui la descrizione dell'immagine

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.

Inserisci qui la descrizione dell'immagine

In molti casi, tuttavia, entrambi i rami si sono mossi in avanti individualmente.

Inserisci qui la descrizione dell'immagine

Per effettuare un'integrazione, Git dovrà creare un nuovo commit che contenga le differenze tra loro: il commit di unione.

Inserisci qui la descrizione dell'immagine

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.

Inserisci qui la descrizione dell'immagine

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.

Inserisci qui la descrizione dell'immagine

Lo faremo in tre passaggi

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. 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".

Inserisci qui la descrizione dell'immagine

Successivamente, applica i commit della filiale B che vogliamo integrare. A questo punto, entrambi i rami sembrano esattamente uguali.

Inserisci qui la descrizione dell'immagine

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.

Inserisci qui la descrizione dell'immagine

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.


+1 per diagrammi molto interessanti. Ho sempre voluto essere in grado di illustrare un esempio di flusso git allo stesso modo senza fortuna.
Mikayil Abdullayev

60

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 .


30

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 .

Fonte: 3.6 Git Branching - Rebasing, Rebase vs. Merge


25

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 mergee git merge --no-ffprima (di solito vuoi usare git merge --no-ffin 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 rebasenon usarlo mai su rami pubblici .

In altre parole :

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 mergevs si git rebaseapplica 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 rebasein quanto produce un albero della storia più bello :)

Tra i rami delle caratteristiche

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

Da developa 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

Note a margine

git cherry-pick

Quando hai solo bisogno di un commit specifico, git cherry-pickè una buona soluzione (l' -xopzione 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 --rebaseinvece 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.


15

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.


1
Le fusioni non uniscono i commit: sarebbe riscrivere la storia. Rebase lo fa.
kellyfj,

4

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.


4

Se sei solo uno sviluppatore, puoi utilizzare rebase invece di unire per avere una cronologia chiara

inserisci qui la descrizione dell'immagine


3

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

3

È stato spiegato molte volte cosa rebase e che cos'è l'unione, ma quando dovresti usare cosa?

Quando dovresti usare rebase?

  • quando non hai premuto il ramo / nessun altro ci sta lavorando
  • vuoi la storia completa
  • vuoi evitare tutti i messaggi di commit "uniti .." generati automaticamente

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-onlydi 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 hai spinto il ramo / anche altri ci stanno lavorando
  • non hai bisogno della storia completa
  • semplicemente la fusione è abbastanza buona per te

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).


-4

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.


1
@benjaminhull Grazie! Tranne spero che la mia risposta sia basata sui fatti. L'opinione dell'IMHO ha poco spazio in questo genere di cose: è un dato di fatto che perdere la tua storia reale rende la vita più difficile in seguito.
Marnen Laibow-Koser,

1
Essere d'accordo. La fusione non condurrà mai alla storia corrotta, ecc. (Quando rinnovi i tuoi commit spinti)
surfrider
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.