ramificação mestre e 'origem / mestre' divergiram; como 'undiverge' ramificações '?

965

De alguma forma, meu mestre e minha origem / ramo mestre divergiram.
Na verdade, não quero que eles divergam.

Como posso ver essas diferenças e 'mesclá-las'?

Frank
fonte
2
o que você quer dizer com divergir? você rebase seu mestre depois de empurrá-lo?
hasen
13
Recebo uma mensagem dizendo "Sua filial e 'origem / mestre' divergiram, # e têm 1 e 1 confirmação (ões) diferente cada, respectivamente."
31410 Frank
Atualizei minha resposta para refletir essa mensagem de aviso "divergente".
VonC 16/03/10
A resposta aceita para outra pergunta também pode ser útil na resolução de certos casos em que isso pode entrar em ação (por exemplo, você está tentando mudar seu mestre, mas ele já foi pressionado): stackoverflow.com/questions/2862590/…
lindes
8
A explicação neste blog me ajudou infinitamente mais do que qualquer resposta abaixo: sebgoo.blogspot.com/2012/02/…

Respostas:

1014

Você pode revisar as diferenças com um:

git log HEAD..origin/master

antes de puxá-lo (buscar + mesclar) (consulte também "Como você faz com que o git sempre puxe de um ramo específico?" )


Quando você tem uma mensagem como:

"Sua ramificação e 'origem / mestre' divergiram, # e têm 1 e 1 commit (s) diferente cada, respectivamente."

, verifique se você precisa atualizarorigin . Se originestiver atualizado, algumas confirmações foram enviadas para originoutro repositório enquanto você fazia suas próprias confirmações localmente.

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

Você baseou o commit C no commit A porque esse foi o trabalho mais recente que você buscou do upstream na época.

No entanto, antes de você tentar voltar à origem, alguém empurrou o commit B.
O histórico de desenvolvimento divergiu em caminhos separados.

Você pode mesclar ou refazer o processo. Consulte Pro Git: Ramificação do Git - Rebasing para obter detalhes.

Mesclar

Use o comando git merge:

$ git merge origin/master

Isso diz ao Git para integrar as alterações origin/masterno seu trabalho e criar uma confirmação de mesclagem.
O gráfico da história agora se parece com isso:

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

A nova mesclagem, commit M, tem dois pais, cada um representando um caminho de desenvolvimento que levou ao conteúdo armazenado nesse commit.

Observe que o histórico por trás de M agora não é linear.

Rebase

Use o comando git rebase:

$ git rebase origin/master

Isso diz ao Git para reproduzir o commit C (seu trabalho) como se você o tivesse baseado no commit B em vez de A. Os
usuários do CVS e do Subversion rotineiramente reencaminham suas alterações locais sobre o trabalho upstream quando atualizam antes do commit.
O Git apenas adiciona separação explícita entre as etapas de confirmação e rebase.

O gráfico da história agora se parece com isso:

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

A confirmação C 'é uma nova confirmação criada pelo comando git rebase.
É diferente de C de duas maneiras:

  1. Tem uma história diferente: B em vez de A.
  2. Seu conteúdo é responsável por alterações em B e C; é o mesmo que M do exemplo de mesclagem.

Observe que o histórico por trás de C 'ainda é linear.
Escolhemos (por enquanto) permitir apenas a entrada de histórico linear cmake.org/cmake.git.
Essa abordagem preserva o fluxo de trabalho baseado no CVS usado anteriormente e pode facilitar a transição.
Uma tentativa de enviar C 'para o nosso repositório funcionará (supondo que você tenha permissões e ninguém tenha pressionado enquanto você estava refazendo).

O comando git pull fornece uma maneira abreviada de buscar a origem e refazer o trabalho local:

$ git pull --rebase

Isso combina as etapas de busca e rebase acima em um comando.

VonC
fonte
5
Descobri isso enquanto procurava o mesmo problema. Você pode explicar por que 'git reset --hard HEAD' não resolveu o problema?
Neth
12
@ Neth: porque não se trata de modificações em etapas (ou seja, modificações presentes no índice, mas ainda não confirmadas), mas em confirmações locais (que diferem das confirmações presentes no controle remoto). git reset --hard HEADremoveria apenas qualquer modificação não confirmada indexada local e não faria nada para reconciliar as diferenças entre confirmações local e remota . Somente uma mesclagem ou uma nova reformulação reunirão os dois conjuntos de confirmações (o local e o remoto).
VonC 26/11/2010
4
Uau, obrigado por esta resposta incrível. Acidentalmente, fizemos um "git pull" sem "--rebase", e "git rebase origin / master" foi apenas a correção!
Mrooney
3
Que tal - eu só quero ignorar / despejar minhas alterações locais e estar com minha filial local onde está o controle remoto? Em outras palavras, quero masterapontar para o Bseu exemplo.
CygnusX1
23
@ CygnusX1 que seria um git reset --hard origin/mastercomo mencionado na resposta abaixo: stackoverflow.com/a/8476004/6309
VonC:
726

Eu tive isso e estou confuso com o que causou isso, mesmo depois de ler as respostas acima. Minha solução foi fazer

git reset --hard origin/master

Em seguida, isso redefine minha cópia (local) do mestre (que eu presumo estar estragada) no ponto correto, conforme representado pela origem (remota) / mestre.

AVISO : Você perderá todas as alterações ainda não enviadas para origin/master.

skiphoppy
fonte
21
sim, ele se sente um pouco como a opção manequins, mas se não há perigo real e você está aqui para uma solução rápida - funciona este (pelo menos para mim)
PandaWood
7
Isso precisa estar na ramificação principal antes ("git checkout master").
blueyed
Isso aconteceu comigo uma vez quando recebi as últimas informações de origem, depois alguém fez uma força forçar a origem, o que causou a reversão do commit anterior na origem. Portanto, minha filial local tinha o commit revertido e, quando tentei obter as últimas informações de origem, estava dizendo que tinha que mesclar. Nesse caso, fazia sentido redefinir --hard origin / (branch) porque o problema já havia sido corrigido na origem.
Landon Poch
96
Você provavelmente deve alertar os usuários de que isso vai fazê-los perder todas as alterações ainda não empurrou a origem
Pedro Loureiro
6
Os Commits do @PedroLoureiro são notas realmente perdidas, você ainda pode encontrá- git refloglos ou vê-los gitk --all. Mas, no entanto, é claro que o hard reset é outra coisa que uma recuperação.
sebkraemer
52
git pull --rebase origin/master 

é um comando único que pode ajudá-lo na maior parte do tempo.

Editar: extrai as confirmações da origem / mestre e aplica suas alterações no histórico da ramificação recém-extraída.

asitmoharna
fonte
89
por favor mencione que o comando faz, as pessoas mais pode executá-lo e acabar por estragar
Baz1nga
1
Se não houver nenhum problema, você deverá terminar com o seu mestre, contendo todas as alterações de origem / mestre e todas as confirmações locais serão reproduzidas por cima. Parece bom para mim.
Philipp Claßen
7
Exceto quando existem diferenças reais e isso o deixa em uma nova abortada.
ffledgling
Isso gera um erro: erro: falha ao mesclar as alterações. O patch falhou nos modelos de solicitação e resposta 0024
IgorGanapolsky
32

Eu me encontrei nessa situação quando tentei refazer uma ramificação que estava rastreando uma ramificação remota e tentando refazê-la no mestre. Nesse cenário, se você tentar se refazer, provavelmente encontrará sua ramificação divergente e poderá criar uma bagunça que não é para os git nubees!

Digamos que você esteja no ramo my_remote_tracking_branch, que foi ramificado do master

$ git status

# Na ramificação my_remote_tracking_branch

nada a confirmar (diretório de trabalho limpo)

E agora você está tentando se recuperar do master como:

mestre do rebit do git

PARE AGORA e poupe alguns problemas! Em vez disso, use mesclagem como:

mestre de mesclagem git

Sim, você terminará com confirmações extras em sua filial. Mas, a menos que você esteja disposto a ramificações "não divergentes", esse será um fluxo de trabalho muito mais suave do que a reestruturação. Veja este blog para uma explicação muito mais detalhada.

Por outro lado, se o seu ramo é apenas um ramo local (ou seja, ainda não foi enviado a nenhum controle remoto), você deve fazer uma nova reformulação (e seu ramo não divergirá nesse caso).

Agora, se você está lendo isso porque já está em um cenário "divergente" devido a tal rebase, pode voltar à última confirmação da origem (ou seja, em um estado não divergente) usando:

git reset --hard origin / my_remote_tracking_branch

paneer Tikka
fonte
5
Uma regra prática é usar rebasese a ramificação que você está refazendo não foi publicada (e usada por outras pessoas). Caso contrário, use merge. Se você reformular as ramificações já publicadas (e usadas), precisará coordenar uma conspiração para reescrever o histórico em todos os desenvolvedores que usaram sua ramificação.
Mikko Rantalainen
1
Infelizmente, eu não li esta mensagem antes de fazer o git rebase master...
Vitaly Isaev
Se eu fizer o git rebase master enquanto estiver no ramo 'foobar', tecnicamente o foobar será desviado da origem / foobar até que eu faça um git push -f, certo?
relipse 15/05
1
git reset --hard origin/my_remote_tracking_branché o que realmente funcionou
roothahn
24

No meu caso, aqui está o que eu fiz para causar o mensagem divergente : fiz, git pushmas depois fiz, git commit --amendpara adicionar algo à mensagem de confirmação. Então eu também fiz outro commit.

Então, no meu caso, isso simplesmente significava que a origem / mestre estava desatualizada. Como eu sabia que ninguém mais tocava a origem / mestre, a correção foi trivial: git push -f (onde -fsignifica força)

Darren Cook
fonte
8
+1 para git push -fsubstituir as alterações confirmadas anteriormente e enviadas para a origem. Também tenho certeza de que ninguém mais tocou no repositório.
Zacharydl
4
Comando muito arriscado. Por favor, escreva uma breve informação sobre o fator de risco do comando.
J4cK
1
@ Malandro: Eu já havia descrito o risco: "como eu sabia que ninguém mais estava tocando origem / mestre". Eu acredito que, nesse caso, este não é um comando arriscado.
Darren Cook
1
Se alguém comete no mestre e, em seguida, uma pessoa executar o impulso de comando git -f então é comando de alto risco
J4cK
11

No meu caso, enviei alterações para origin/master e depois percebi que não deveria fazê-lo :-( Isso foi complicado pelo fato de as alterações locais estarem em uma subárvore. Então, voltei ao último bom commit antes do local "ruim" alterações (usando SourceTree) e então recebi a "mensagem de divergência".

Depois de consertar minha bagunça localmente (os detalhes não são importantes aqui), eu queria "voltar no tempo" a origin/masterramificação remota para que ela estivesse sincronizada com a local masternovamente. A solução no meu caso foi:

git push origin master -f

Observe o -finterruptor (força). Isso excluiu as "alterações incorretas" que foram enviadas origin/masterpor engano e agora as ramificações local e remota estão sincronizadas.

Lembre-se de que esta é uma operação potencialmente destrutiva, portanto, execute-a apenas se tiver 100% de certeza de que "voltar" o mestre remoto no tempo está correto.

Laringe Decidua
fonte
Sempre útil, mas certamente não responde à pergunta.
Thibault D.
1
@ThibaultD. mesmo se não, é exatamente isso que eu estava procurando.
Neil Chowdhury
Estou recebendo You are not allowed to force push code to a protected branch on this project.. Estou tentando empurrar para o meu garfo.
BanAnanas 11/03
Eu tive que remover a proteção no repo gitlab stackoverflow.com/questions/32246503/…
BanAnanas
5

Sei que há muitas respostas aqui, mas acho que git reset --soft HEAD~1merece alguma atenção, porque isso permite que você mantenha as alterações no último commit local (não pressionado) enquanto resolve o estado divergente. Eu acho que essa é uma solução mais versátil do que rebaseusar, porque o commit local pode ser revisado e até movido para outro ramo.

A chave está usando --soft, em vez de dura --hard. Se houver mais de 1 confirmação, uma variação de HEAD~xdeverá funcionar. Então, aqui estão todas as etapas que resolveram minha situação (eu tinha 1 confirmação local e 8 confirmações no controle remoto):

1) git reset --soft HEAD~1 para desfazer a consolidação local. Para as próximas etapas, usei a interface no SourceTree, mas acho que os seguintes comandos também devem funcionar:

2) git stash para esconder as alterações de 1). Agora todas as mudanças estão seguras e não há mais divergências.

3) git pull para obter as alterações remotas.

4) git stash pop ou git stash applypara aplicar as últimas alterações ocultas, seguidas de uma nova confirmação, se desejado. Esta etapa é opcional, juntamente com 2) , quando você deseja descartar as alterações na confirmação local. Além disso, quando desejar confirmar com outra ramificação, essa etapa deve ser realizada após a mudança para a desejada.

Bianca Daniciuc
fonte
Na verdade, hoje em dia, o pull --rebaseesconderijo seria automaticamente. stackoverflow.com/a/30209750/6309
VonC
4

Para visualizar as diferenças:

git difftool --dir-diff master origin/master

Isso exibirá as alterações ou diferenças entre os dois ramos. Em araxis (Meu favorito), ele é exibido no estilo diff de pasta. Mostrando cada um dos arquivos alterados. Posso clicar em um arquivo para ver os detalhes das alterações no arquivo.

C Johnson
fonte
1
Uso interessante de git-dir: +1
VonC 28/04
3

No meu caso, isso foi causado por não cometer minha resolução de conflitos.

O problema foi causado pela execução do git pullcomando. Alterações na origem levaram a conflitos com meu repositório local, que resolvi. No entanto, eu não os cometi. A solução neste momento é confirmar as alterações ( git commito arquivo resolvido)

Se você também modificou alguns arquivos desde que resolveu o conflito, o git statuscomando mostrará as modificações locais como modificações locais sem estágio e mesclará a resolução como modificações locais com etapas. Isso pode ser resolvido adequadamente, confirmando as alterações desde a mesclagem primeiro git commite adicionando e confirmando as alterações não faseadas como de costume (por exemplo, por git commit -a).

Mohammad Reza Esmaeilzadeh
fonte
0

Eu tinha a mesma mensagem quando estava tentando editar a última mensagem de confirmação, de confirmação já enviada, usando: git commit --amend -m "New message" Quando enviei as alterações usando, git push --force-with-lease repo_name branch_name não havia problemas.

LoveForDroid
fonte
0

Conheci esse problema quando criei uma ramificação baseada na ramificação A por

git checkout -b a

e, em seguida, defino o fluxo up da ramificação A para originar a ramificação B por

git branch -u origin/B

Então recebi a mensagem de erro acima.

Uma maneira de resolver esse problema para mim foi:

  • Exclua a ramificação a
  • Crie um novo ramo b por
git checkout -b b origin/B
YanQing
fonte