ramo principale e 'origine / maestro' sono divergenti, come 'separare' rami '?


965

In qualche modo il mio maestro e la mia origine / ramo principale sono divergenti.
In realtà non voglio che divergano.

Come posso visualizzare queste differenze e "unirle"?


2
cosa intendi per divergenza? rinnovi il tuo padrone dopo averlo spinto?
hasen

13
Ricevo un messaggio che dice "Il tuo ramo e 'origine / master' sono divergenti, # e hanno 1 e 1 commit diversi ciascuno, rispettivamente."
Frank,

Ho aggiornato la mia risposta per riflettere quel messaggio di avviso "divergente".
VonC

La risposta accettata a un'altra domanda può anche essere utile per risolvere alcuni casi in cui ciò potrebbe entrare in gioco (ad esempio, stai cercando di spostare il tuo master, ma è già stato spinto): stackoverflow.com/questions/2862590/…
lindes

8
La spiegazione di questo blog mi ha aiutato infinitamente più di qualsiasi risposta qui sotto: sebgoo.blogspot.com/2012/02/…

Risposte:


1014

Puoi rivedere le differenze con un:

git log HEAD..origin/master

prima di estrarlo (recupera + unisci) (vedi anche "Come fai ad estrarre Git da un ramo specifico?" )


Quando hai un messaggio come:

"La tua filiale e 'origine / master' sono divergenti, # e hanno 1 e 1 commit diversi ciascuno, rispettivamente."

, controlla se devi aggiornareorigin . Se originè aggiornato, alcuni commit sono stati trasferiti originda un altro repository mentre sono stati effettuati i propri commit localmente.

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \
                    C  master (your work)

Hai basato il commit C sul commit A perché quello era l'ultimo lavoro che avevi recuperato da upstream in quel momento.

Tuttavia, prima di provare a riportare indietro all'origine, qualcun altro ha spinto il commit B.
La storia dello sviluppo si è divisa in percorsi separati.

È quindi possibile unire o riformulare. Vedi Pro Git: Git Branching - Rebasing per i dettagli.

Merge

Usa il comando git merge:

$ git merge origin/master

Questo dice a Git di integrare le modifiche dal origin/mastertuo lavoro e creare un commit di unione.
Il grafico della storia ora appare così:

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \      \
                    C ---- M  master (your work)

La nuova unione, commit M, ha due genitori, ognuno dei quali rappresenta un percorso di sviluppo che ha portato al contenuto archiviato in quel commit.

Si noti che la storia dietro M è ora non lineare.

rebase

Usa il comando git rebase:

$ git rebase origin/master

Questo dice a Git di riprodurre il commit C (il tuo lavoro) come se lo avessi basato sul commit B anziché su A. Gli
utenti di CVS e Subversion sistematicamente cambiano le loro modifiche locali in cima al lavoro a monte quando eseguono l'aggiornamento prima del commit.
Git aggiunge semplicemente una separazione esplicita tra i passaggi commit e rebase.

Il grafico della storia ora appare così:

... o ---- o ---- A ---- B  origin/master (upstream work)
                          \
                           C'  master (your work)

Commit C 'è un nuovo commit creato dal comando git rebase.
È diverso da C in due modi:

  1. Ha una storia diversa: B invece di A.
  2. Il suo contenuto spiega le modifiche sia in B che in C; è uguale a M dall'esempio di unione.

Si noti che la storia dietro C 'è ancora lineare.
Abbiamo scelto (per ora) di consentire solo la cronologia lineare in cmake.org/cmake.git.
Questo approccio preserva il flusso di lavoro basato su CVS utilizzato in precedenza e potrebbe facilitare la transizione.
Un tentativo di spingere C 'nel nostro repository funzionerà (supponendo che tu abbia i permessi e nessuno ha spinto mentre stavi eseguendo il rebasing).

Il comando git pull fornisce un modo abbreviato per recuperare dall'origine e rifare il lavoro locale su di esso:

$ git pull --rebase

Questo combina i passaggi sopra recuperati e rebase in un solo comando.


5
Ho trovato questo mentre cercavo lo stesso problema, puoi spiegare perché 'git reset --hard HEAD' non ha risolto il problema?
Neth,

12
@Neth: perché non si tratta di modifiche graduali (ovvero modifiche presenti nell'indice ma non ancora impegnate), ma di commit locali (che differiscono dai commit presenti sul telecomando). git reset --hard HEADeliminerebbe solo qualsiasi modifica non impegnata indicizzata locale e non farebbe nulla per riconciliare le differenze tra commit locali e remoti . Solo un'unione o un rebase riunirà i due set di commit (quello locale e quello remoto) insieme.
VonC,

4
Caspita, grazie per questa fantastica risposta. Avevamo accidentalmente fatto un "git pull" senza "--rebase", e "git rebase origin / master" era solo la soluzione!
mrooney

3
Che ne dici - Voglio solo ignorare / scaricare le mie modifiche locali ed essere con la mia filiale locale dove si trova il telecomando? In altre parole, voglio masterindicare il Btuo esempio.
CygnusX1

23
@ CygnusX1 che sarebbe un git reset --hard origin/mastercome accennato nella risposta appena sotto: stackoverflow.com/a/8476004/6309
VonC

726

Ho avuto questo e sono sconcertato da ciò che l'ha causato, anche dopo aver letto le risposte di cui sopra. La mia soluzione era fare

git reset --hard origin/master

Quindi questo reimposta la mia copia (locale) del master (che presumo sia rovinata) nel punto corretto, come rappresentato da (remoto) origin / master.

ATTENZIONE : perderai tutte le modifiche non ancora inviate origin/master.


21
sì, sembra un po 'come l'opzione dei manichini, ma se non c'è alcun pericolo reale e sei qui per una soluzione rapida - questo funziona (per me comunque)
PandaWood,

7
Questo richiede di essere sul ramo master prima ("git checkout master").
bluastra

Questo è successo a me una volta quando sono arrivato più tardi dalle origini, quindi qualcun altro ha spinto la forza verso l'origine, causando il ripristino del precedente commit in origine. Quindi la mia filiale locale ha avuto il commit ripristinato, e quando ho cercato di estrarre le ultime origini mi diceva che dovevo unirmi. In questo caso aveva senso ripristinare --hard origin / (branch) perché il problema era già stato risolto in origine.
Landon Poch,

96
Probabilmente dovresti avvertire gli utenti che questo li farà perdere tutte le modifiche non ancora trasferite all'origine
Pedro Loureiro

6
@PedroLoureiro Gli commit sono davvero persi, puoi ancora trovare gli commit git reflogo vederli gitk --all. Ma, naturalmente, l'hard reset è un'altra cosa che un rebase.
sebkraemer,

52
git pull --rebase origin/master 

è un singolo comando che può aiutarti nella maggior parte dei casi.

Modificare: estrae i commit dall'origine / master e applica le modifiche sulla cronologia del ramo appena estratto.


89
per favore menziona cosa fa il comando, altrimenti la gente potrebbe eseguirlo e finire per
incasinare

1
Se non ci sono problemi, dovresti finire con il tuo master contenente tutte le modifiche origine / master più tutti i tuoi commit locali verranno riprodotti sopra di esso. Mi sembra buono.
Philipp Claßen,

7
Tranne quando ci sono differenze reali e ti lascia in un rebase interrotto.
Sfogliando il

Questo produce un errore: errore: impossibile unire le modifiche. Patch non riuscita sui modelli di richiesta e risposta 0024
IgorGanapolsky,

32

Mi sono trovato in questa situazione quando ho provato a rebase un ramo che stava rintracciando un ramo remoto e stavo cercando di rifarlo su master. In questo scenario, se provi a rebase, molto probabilmente troverai il tuo ramo divergente e può creare un disastro che non è per git cloudes!

Supponiamo che tu sia sul ramo my_remote_tracking_branch, che è stato ramificato dal master

$ git status

# Sul ramo my_remote_tracking_branch

niente da impegnare (directory di lavoro pulita)

E ora stai cercando di tornare dal master come:

git rebase master

FERMATI ORA e risparmia qualche problema! Invece, usa unisci come:

git merge master

Sì, finirai con ulteriori impegni sulla tua filiale. Ma a meno che tu non sia interessato a rami "non divergenti", questo sarà un flusso di lavoro molto più agevole rispetto al rebasing. Vedi questo blog per una spiegazione molto più dettagliata.

D'altra parte, se il tuo ramo è solo un ramo locale (cioè non è ancora stato inviato a nessun telecomando) dovresti assolutamente fare un rebase (e il tuo ramo non divergerà in questo caso).

Ora, se stai leggendo questo perché già sono in una "divergevano" scenario a causa di tale rebase, si può tornare all'ultimo commit di origine (cioè in uno stato non discostato) utilizzando:

git reset --hard origin / my_remote_tracking_branch


5
Una regola empirica è quella di utilizzare rebasese il ramo che stai riassegnando non è stato pubblicato (e utilizzato da altre persone). Altrimenti, usa merge. Se rifatti i rami già pubblicati (e utilizzati), devi coordinare una cospirazione per riscrivere la cronologia di tutti gli sviluppatori che hanno utilizzato il tuo ramo.
Mikko Rantalainen,

1
Purtroppo non ho letto questo messaggio prima di fare il git rebase master...
Vitaly Isaev l'

Se eseguo git rebase master mentre sono sul ramo 'foobar', tecnicamente il foobar è divergente dall'origine / foobar fino a quando non eseguo un git push -f, giusto?
ricaduta


1
git reset --hard origin/my_remote_tracking_branchè quello che ha funzionato davvero
roothahn

24

Nel mio caso, ecco cosa ho fatto per causare il messaggio divergente : l'ho fatto git pushma poi ho fattogit commit --amend aggiunto qualcosa al messaggio di commit. Poi ho anche fatto un altro commit.

Quindi nel mio caso ciò significava semplicemente che origin / master era obsoleto. Poiché sapevo che nessun altro stava toccando origin / master, la correzione era banale: git push -f (dove -fsignifica forza)


8
+1 per git push -fsovrascrivere le modifiche precedentemente impegnate e trasferite all'origine. Sono anche sicuro che nessun altro abbia toccato il repository.
Zacharydl,

4
Comando molto rischioso. Scrivi una breve informazione sul fattore di rischio del comando.
J4cK,

1
@Trickster: avevo già descritto il rischio: "come sapevo che nessun altro stava toccando l'origine / il padrone". Credo, in tal caso, questo non è un comando rischioso.
Darren Cook,

1
Se qualcuno si impegna sul master e quindi una persona esegue il comando git push -f, allora è un comando ad alto rischio
J4cK,

11

Nel mio caso ho apportato modifiche a origin/master e poi ho capito che non avrei dovuto farlo :-( Questo era complicato dal fatto che le modifiche locali erano in una sottostruttura. Quindi sono tornato all'ultimo commit buono prima del "cattivo" locale cambia (usando SourceTree) e poi ho ricevuto il "messaggio di divergenza".

Dopo aver risolto il mio disordine localmente (i dettagli non sono importanti qui) volevo "tornare indietro nel tempo" il origin/masterramo remoto in modo che fosse nuovamente sincronizzato con il locale master. La soluzione nel mio caso era:

git push origin master -f

Notare l' -finterruttore (force). Ciò ha eliminato le "modifiche errate" che erano state spinte per origin/mastererrore e ora le filiali locali e remote sono sincronizzate.

Tieni presente che si tratta di un'operazione potenzialmente distruttiva, quindi eseguila solo se sei sicuro al 100% che "tornare indietro" in tempo il master remoto è OK.


Sempre utile ma sicuramente non risponde alla domanda.
Thibault D.

1
@ThibaultD. anche se non lo fosse, questo è esattamente quello che stavo cercando.
Neil Chowdhury,

Sto ottenendo You are not allowed to force push code to a protected branch on this project.. Sto cercando di spingere verso la mia forchetta.
BanAnanas,

Ho dovuto rimuovere la protezione su gitlab repo stackoverflow.com/questions/32246503/...
BanAnanas

5

So che ci sono molte risposte qui, ma penso che git reset --soft HEAD~1meriti qualche attenzione, perché ti consente di mantenere i cambiamenti nell'ultimo commit locale (non spinto) mentre risolvi lo stato divergente. Penso che questa sia una soluzione più versatile di quella con cui interagire rebase, perché il commit locale può essere rivisto e persino spostato in un altro ramo.

La chiave sta usando --soft, invece del duro --hard. Se è presente più di 1 commit, una variante di HEAD~xdovrebbe funzionare. Quindi, ecco tutti i passaggi che hanno risolto la mia situazione (avevo 1 commit locale e 8 commit nel telecomando):

1) git reset --soft HEAD~1 per annullare il commit locale. Per i passaggi successivi, ho usato l'interfaccia in SourceTree, ma penso che dovrebbero funzionare anche i seguenti comandi:

2) git stash per nascondere le modifiche da 1). Ora tutti i cambiamenti sono al sicuro e non c'è più divergenza.

3) git pull per ottenere le modifiche remote.

4) git stash pop oppure git stash applyapplicare le ultime modifiche nascoste, seguite da un nuovo commit, se lo si desidera. Questo passaggio è facoltativo, insieme a 2) , quando si desidera eliminare le modifiche nel commit locale. Inoltre, quando si desidera eseguire il commit in un altro ramo, questo passaggio deve essere eseguito dopo il passaggio a quello desiderato.


In realtà, in questi giorni, la cosa si pull --rebasemetterebbe automaticamente comunque. stackoverflow.com/a/30209750/6309
VonC

4

Per visualizzare le differenze:

git difftool --dir-diff master origin/master

Questo mostrerà i cambiamenti o le differenze tra i due rami. In araxis (il mio preferito) lo visualizza in uno stile diff cartella. Mostra ciascuno dei file modificati. Posso quindi fare clic su un file per vedere i dettagli delle modifiche nel file.


1
Uso interessante di git-dir: +1
VonC

3

Nel mio caso ciò è stato causato dal fatto di non aver commesso la mia risoluzione del conflitto.

Il problema è stato causato dall'esecuzione del git pullcomando. I cambiamenti nell'origine hanno portato a conflitti con il mio repository locale, che ho risolto. Tuttavia, non li ho commessi. La soluzione a questo punto è impegnare le modifiche ( git commitil file risolto)

Se sono stati modificati anche alcuni file dalla risoluzione del conflitto, il git statuscomando mostrerà le modifiche locali come modifiche locali non in scena e unirà la risoluzione come modifiche locali in fasi. Ciò può essere risolto correttamente eseguendo prima le modifiche dall'unione git commit, quindi aggiungendo e eseguendo le modifiche non messe in scena come di consueto (ad esempio da git commit -a).


0

Avevo lo stesso messaggio quando stavo cercando di modificare l'ultimo messaggio di commit, di commit già inviato, usando: git commit --amend -m "New message" Quando ho inviato le modifiche usando git push --force-with-lease repo_name branch_name non c'erano problemi.


0

Ho riscontrato questo problema quando ho creato un ramo basato sul ramo A di

git checkout -b a

e quindi ho impostato il flusso up del ramo a sul ramo B di origine

git branch -u origin/B

Quindi ho ricevuto il messaggio di errore sopra.

Un modo per risolvere questo problema per me era,

  • Elimina il ramo a
  • Crea un nuovo ramo b di
git checkout -b b origin/B
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.