Git unisce master nel ramo delle caratteristiche


1017

Diciamo che abbiamo la seguente situazione in Git:

  1. Un repository creato:

    mkdir GitTest2
    cd GitTest2
    git init
    
  2. Alcune modifiche nel master avvengono e si impegnano:

    echo "On Master" > file
    git commit -a -m "Initial commit"
    
  3. Feature1 branched master e alcuni lavori sono stati eseguiti:

    git branch feature1
    git checkout feature1
    echo "Feature1" > featureFile
    git commit -a -m "Commit for feature1"
    
  4. Nel frattempo, viene rilevato un bug nel codice master e viene stabilito un ramo hotfix:

    git checkout master
    git branch hotfix1
    git checkout hotfix1
    
  5. Il bug viene corretto nel ramo hotfix e riunito nuovamente nel master (forse dopo una richiesta pull / revisione del codice):

    echo "Bugfix" > bugfixFile
    git commit -a -m "Bugfix Commit"
    git checkout master
    git merge --no-ff hotfix1
    
  6. Lo sviluppo su feature1 continua:

    git checkout feature1
    

Supponiamo che abbia bisogno dell'aggiornamento rapido nel mio ramo delle funzionalità, forse perché il bug si verifica anche lì. Come posso raggiungere questo obiettivo senza duplicare gli commit nel ramo delle mie funzioni?

Voglio impedire di ottenere due nuovi commit sul mio ramo di funzionalità che non hanno alcuna relazione con l'implementazione della funzione. Ciò sembra particolarmente importante per me se utilizzo le richieste pull: tutti questi commit saranno inclusi anche nella richiesta pull e dovranno essere rivisti anche se ciò è già stato fatto (poiché l'aggiornamento rapido è già presente nel master).

Non posso fare un git merge master --ff-only: "fatale: impossibile avanzare velocemente, abortire", ma non sono sicuro che questo mi abbia aiutato.


8
Se la filiale feature1è completamente locale, dai un'occhiata a git rebase.
Jokester,

19
Grazie, come un principiante git, mi git rebasesembra una magia nera ...
theomega il

13
se il ramo è una funzione, solo la correzione di bug non dovrebbe avvenire lì (almeno se non è un bug di blocco) poiché lo scopo di questo ramo è mostrare una nuova funzione. Il bug verrà corretto quando unito al master in cui è presente il commit con la correzione.
gipi

21
Probabilmente vale la pena notare per i principianti che in 3. git branch feature1e git checkout feature1potrebbero essere combinati in git checkout -b feature1e 4. potrebbero essere completamente ridotti agit checkout -b hotfix1 master
Naruto Sempai

3
Saresti disposto a tornare e a cambiare la risposta accettata, perché l'attuale risposta accettata è terribile.
Onnipotente il

Risposte:


1218

Come uniamo il ramo principale nel ramo della caratteristica? Facile:

git checkout feature1
git merge master

Non ha senso forzare una fusione in avanti veloce qui, poiché non può essere fatto. È stato eseguito il commit sia nel ramo funzionalità che nel ramo principale. L'avanzamento veloce è impossibile ora.

Dai un'occhiata a GitFlow . È un modello di ramificazione per git che può essere seguito, e inconsciamente lo hai già fatto. È anche un'estensione di Git che aggiunge alcuni comandi per i nuovi passaggi del flusso di lavoro che fanno automaticamente cose che altrimenti dovresti fare manualmente.

Quindi cosa hai fatto nel tuo flusso di lavoro? Hai due rami con cui lavorare, il tuo ramo feature1 è sostanzialmente il ramo "sviluppo" nel modello GitFlow.

È stato creato un ramo hotfix dal master e lo si è unito indietro. E ora sei bloccato.

Il modello GitFlow richiede di unire l'hotfix anche al ramo di sviluppo, che è "feature1" nel tuo caso.

Quindi la vera risposta sarebbe:

git checkout feature1
git merge --no-ff hotfix1

Ciò aggiunge tutte le modifiche apportate all'interno dell'aggiornamento rapido al ramo della funzionalità, ma solo quelle modifiche. Potrebbero entrare in conflitto con altri cambiamenti di sviluppo nel ramo, ma non entreranno in conflitto con il ramo principale se alla fine dovessi unire nuovamente il ramo della funzione al ramo principale.

Fai molta attenzione al rebasing. Rifare solo se le modifiche apportate sono rimaste locali nel proprio repository, ad esempio se non si è spostato alcun ramo in un altro repository. Rebasing è un ottimo strumento per te per organizzare i tuoi impegni locali in un ordine utile prima di spingerlo fuori nel mondo, ma in seguito il rebasing rovinerà le cose per i principianti git come te.


7
No. Il commit che corregge il bug appare solo una volta nel ramo hotfix, anche se il nome del ramo viene eliminato una volta unito ai rami master e di sviluppo. Il commit di unione mostra solo le modifiche introdotte dall'unione, che sembra un commit duplicato. Ma è così che funziona git: diramare e ricollegarsi. Il vero lavoro di sviluppo ha luogo solo in commit non-merge e la fusione è accettata solo se il risultato funziona con un software.
Sven,

42
Questa dovrebbe essere la risposta accettata. Funziona bene anche con la funzione di richiesta pull di GitHub.
Nostalg.io

125
Penso che valga la pena notare che un git merge mastersi fonderà dalla tua copia locale del master, quindi anche se hai fatto un git pullnel ramo della tua funzione dopo che qualcun altro ha unito un ramo diverso nel master, dovrai git checkout master, quindi git pull, quindi di git checkout feature1nuovo e THEN git merge master.
damick,

50
@damick O semplicemente git fetchegit merge origin/master
Yngvar Kristiansen il

20
@damick @ yngvar-kristiansen git pull origin mastersi unirà automaticamente orgin/masterall'attuale filiale
L422Y

613

Dovresti essere in grado di rifare il tuo ramo sul master:

git checkout feature1
git rebase master

Gestisci tutti i conflitti che si presentano. Quando arrivi ai commit con le correzioni dei bug (già in master), Git dirà che non ci sono state modifiche e che forse sono già state applicate. Quindi continui il rebase (saltando i commit già nel master) con

git rebase --skip

Se esegui un comando git logsul ramo della tua funzione, vedrai apparire il commit dei bug solo una volta e nella parte principale.

Per una discussione più dettagliata, dai un'occhiata alla documentazione del libro Git su git rebase( https://git-scm.com/docs/git-rebase ) che tratta questo esatto caso d'uso.

================ Modifica per contesto aggiuntivo ====================

Questa risposta è stata fornita specificamente per la domanda posta da @theomega, tenendo conto della sua situazione particolare. Nota questa parte:

Voglio impedire [...] il commit nel mio ramo di funzionalità che non ha alcuna relazione con l'implementazione della funzione.

Rebasing del suo ramo privato su master è esattamente ciò che produrrà quel risultato. Al contrario, la fusione di master nel suo ramo farebbe esattamente ciò che in particolare non vuole accadere : aggiungere un commit che non è correlato all'implementazione delle funzionalità su cui sta lavorando tramite il suo ramo.

Per indirizzare gli utenti che leggono il titolo della domanda, saltare il contenuto e il contesto effettivi della domanda, quindi leggere solo la risposta principale alla cieca supponendo che si applichi sempre al loro (diverso) caso d'uso, permettimi di elaborare:

  • rifare solo le filiali private (ovvero che esistono solo nel repository locale e non sono state condivise con altri). Rebasing delle filiali condivise "spezzerebbe" le copie che altre persone potrebbero avere.
  • se vuoi integrare le modifiche da un ramo (sia esso master o un altro ramo) in un ramo che è pubblico (ad es. hai spinto il ramo per aprire una richiesta pull, ma ora ci sono conflitti con il master e devi aggiornare il tuo ramo per risolvere quei conflitti) dovrai fonderli (ad es. con git merge mastercome nella risposta di @ Sven).
  • puoi anche unire le filiali nelle tue filiali private locali se questa è la tua preferenza, ma tieni presente che si tradurrà in impegni "esteri" nella tua filiale.

Infine, se non sei soddisfatto del fatto che questa risposta non sia la migliore per la tua situazione anche se era per @theomega, aggiungere un commento qui sotto non sarà particolarmente utile: non controllo quale risposta è selezionata, fa solo @theomega.


136
No, non è sicuro: se rinnovi, stai cambiando la cronologia del ramo, che influenzerà gli sviluppatori che hanno estratto il ramo. infatti, git non ti permetterà di spingere un ramo riformulato di default: devi forzare l'aggiornamento con -fquando premi per sovrascrivere il ramo con la versione ridimensionata. Stai attento!
David Sulc,

17
In che modo i team di professionisti che utilizzano git gestiscono questo problema? Fai solo attenzione, pensa attentamente e poi fai un -f? O il mio flusso di lavoro completo è imperfetto perché ho bisogno di un -f?
theomega,

30
Bene, mi sarei avventurato la regola "sacra" se non ti rifare (o altrimenti cambi la cronologia di commit) sul codice che è stato condiviso: è solo per il tuo codice locale. Fondamentalmente, dovresti rifare le modifiche per "ripulire" prima di condividerle. Nel tuo caso, puoi spingere un nuovo ramo reimpostato (con un nome diverso) e chiedere ai colleghi di basare le loro modifiche su quel ramo (cioè rifacendo il loro ramo locale da quello nuovo, come sopra). Quindi, elimina feature1da Github.
David Sulc,

19
La maggior parte dei team professionali su cui ho lavorato non usa quasi mai rebase: fondono semplicemente tutto per impostazione predefinita, in modo che non avvenga mai alcuna modifica della cronologia. Questo è il mio modo di lavorare preferito. D'altra parte, alcune squadre usano rebase per "ripulire" gli impegni prima di spingerli (ma mai dopo aver spinto).
Jonathan Hartley,

11
Sì, non devi MAI rifare le filiali pubbliche. Tuttavia, la domanda di OP sembrava riguardare l'integrazione di nuovi impegni effettuati masterin una filiale privata (menziona la "sua" filiale locale). In quel caso, rebaseva bene ed è lo stesso caso d'uso del "ripulire" di cui parli.
David Sulc,

69

Sulla base di questo articolo , dovresti:

  • creare un nuovo ramo basato sulla nuova versione di master

    git branch -b newmaster

  • unisci il tuo vecchio ramo di funzionalità in uno nuovo

    git checkout newmaster

  • risolvere i conflitti sul nuovo ramo di funzionalità

I primi due comandi possono essere combinati a git checkout -b newmaster .

In questo modo la cronologia rimane chiara perché non hai bisogno di fusioni posteriori. E non devi essere così cauto poiché non devi fare un rebase di Git.


7
sarebbe bello se si fa in modo che il relativo comando git segua ogni punto. Altrimenti mi sembra che questa sia davvero l'opzione più sicura e pulita.
VirgileD

@zimi Che succede se abbiamo una filiale remota? Ricreamo di nuovo il nuovo ramo delle funzionalità di aggiornamento? O possiamo semplicemente impostare l'upstream remoto?
BILL

@VirgileD Ho appena pubblicato la mia risposta con maggiori dettagli, inclusi i relativi comandi git.
jkdev,

29

git merge

puoi seguire i passaggi seguenti

1. unione origin/masterramo a featureramo

# step1: change branch to master, and pull to update all commits
$ git checkout master
$ git pull

# step2: change branch to target, and pull to update commits
$ git checkout feature
$ git pull

# step3: merge master to feature(⚠️ current is feature branch)
$ git merge master

2. merge featureramo in origin/masterramo

origin/masterè il ramo principale remoto, mentre masterè il ramo principale locale

$ git checkout master
$ git pull origin/master

$ git merge feature
$ git push origin/master




Sembra che rebase sia pubblicizzato! Buona vecchia fusione :)!
sempre il

27

La risposta di Zimi descrive questo processo in generale. Ecco i dettagli:

  1. Crea e passa a un nuovo ramo. Assicurarsi che il nuovo ramo sia basato in mastermodo da includere gli aggiornamenti rapidi recenti.

    git checkout master
    git branch feature1_new
    git checkout feature1_new
    
    # Or, combined into one command:
    git checkout -b feature1_new master
    
  2. Dopo essere passati al nuovo ramo, unisci le modifiche dal ramo di funzionalità esistente. Ciò aggiungerà i tuoi commit senza duplicare i commit di hotfix.

    git merge feature1
    
  3. Nel nuovo ramo, risolvere eventuali conflitti tra la funzionalità e il ramo principale.

Fatto! Ora usa il nuovo ramo per continuare a sviluppare la tua funzionalità.


2
Il problema è che uno sviluppatore perde tempo generando costantemente nuove filiali quando ha bisogno di aggiornare contro master. Faremmo moltissime filiali, probabilmente 3 volte al giorno durante il lavoro attivo. Dovresti scrivere istruzioni su come pulire tutti i rami dei rifiuti locali e su come sbarazzartene anche sul telecomando. Abbiamo anche bisogno di consigli su come nominare tutti questi rami in modo da non confonderci. Senza quel bit, questo trasformerà un sistema di diramazione in caos.
pauljohn32,

4
Hai ragione, questo non dovrebbe essere fatto sempre. Solo quando (1) le modifiche sul master sono necessarie per la tua funzione o (2) stai per unire il tuo ramo con il master e potrebbero esserci conflitti. E per evitare disordine, puoi eliminare il tuo ramo dopo che è stato unito.
jkdev,

11

Ecco uno script che puoi utilizzare per unire il tuo ramo principale al ramo corrente.

Lo script procede come segue:

  • Passa al ramo principale
  • Tira il ramo principale
  • Torna al ramo corrente
  • Unisce il ramo principale nel ramo corrente

Salvare questo codice come file batch (.bat) e posizionare lo script in qualsiasi punto del repository. Quindi fai clic su di esso per eseguirlo e sei pronto.

:: This batch file pulls current master and merges into current branch

@echo off

:: Option to use the batch file outside the repo and pass the repo path as an arg
set repoPath=%1
cd %repoPath%

FOR /F "tokens=*" %%g IN ('git rev-parse --abbrev-ref HEAD') do (SET currentBranch=%%g)

echo current branch is %currentBranch%
echo switching to master
git checkout master
echo.
echo pulling origin master
git pull origin master
echo.
echo switching back to %currentBranch%
git checkout %currentBranch%
echo.
echo attemting merge master into %currentBranch%
git merge master
echo.
echo script finished successfully
PAUSE

10

Potresti essere in grado di eseguire una "selezione" per estrarre i commit esatti di cui hai bisogno nel ramo della funzione.

Fare un git checkout hotfix1per ottenere il ramo hotfix1. Quindi fai un git logper ottenere l'hash SHA-1 (grande sequenza di lettere e numeri casuali che identifica in modo univoco un commit) del commit in questione. Copia quello (oi primi 10 caratteri circa).

Quindi, git checkout feature1per tornare al ramo delle funzionalità.

Poi, git cherry-pick <the SHA-1 hash that you just copied>

Questo attirerà quel commit, e solo quel commit, nel tuo ramo di funzionalità. Quel cambiamento sarà nel ramo - lo hai semplicemente "selezionato". Quindi, riprendi il lavoro, modifica, impegna, spingi, ecc. Al contenuto del tuo cuore.

Quando, alla fine, esegui un'altra unione da un ramo al ramo della tua funzione (o viceversa), Git riconoscerà che ti sei già unito a quel particolare commit, sapendo che non è necessario farlo di nuovo, e semplicemente "salta".


Non lo considero una buona idea. Quindi, IMO, il commit dell'aggiornamento rapido apparirà davvero nella cronologia del ramo delle funzionalità, che in pratica non vuoi.
Martin Pecka,

1
"Quando, alla fine, esegui un'altra unione da un ramo al ramo della tua funzione (o viceversa), git riconoscerà che hai già unito [...]" - è così che funziona? Non penso che git mergefunzioni in questo "commit di replay", a cui sembra che tu stia suggerendo ("e saltaci sopra"). Mescolare la raccolta e la fusione della ciliegia può apparentemente causare problemi; vedi: news.ycombinator.com/item?id=3947950
Guildenstern

0

Sono nel ramo delle funzionalità e ho effettuato i refactoring. Voglio unire le modifiche principali ora al mio ramo di funzionalità. Sono molto indietro. Nota Non voglio trasferire le modifiche principali al mio locale perché il mio ramo di funzionalità ha i moduli spostati da una posizione all'altra. Ho scoperto che esibirsi di seguito senza pull non funziona. dice "Già aggiornato."

 //below does not get the latest from remote master to my local feature branch without git pull
    git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge master

Questo sotto funziona, nota usa git merge origin / master:

 git checkout master 
    git fetch 
    git checkout my-feature-branch 
    git merge origin/master
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.