Como reverter uma consolidação de mesclagem que já foi enviada para a ramificação remota?

962

git revert <commit_hash>sozinho não vai funcionar. -mdeve ser especificado e estou bastante confuso sobre isso.

Alguém já experimentou isso antes?

Yaz
fonte
3
Dê uma olhada na resposta para esta pergunta: stackoverflow.com/questions/2318777/…
eugen
O link aqui é o melhor exemplo que ilustra revertendo o fundiu cometer: christianengvall.se/undo-pushed-merge-git
SK Venkat
Este é um exemplo de onde o design de gitnão corresponde ao git-flowfluxo de trabalho -ish que todos usam. Se você developfez o check-out, é claro que deseja reverter o ramo de recurso 2-commit que introduziu um bug e não o ramo de desenvolvimento compartilhado há anos. Parece ridículo precisar buscá-lo -m 1.
pkamb
2
Apenas mais uma sugestão que nunca me ocorreu antes - se uma das listas de confirmações das ramificações for pequena, você poderá se sentir mais confortável em reverter confirmações individuais em vez de uma ramificação inteira de confirmações.
Sridhar Sarnobat

Respostas:

1153

A -mopção especifica o número pai . Isso ocorre porque um commit de mesclagem possui mais de um pai, e o Git não sabe automaticamente qual pai era a linha principal e qual pai era o ramo que você deseja desassociar.

Ao visualizar um commit de mesclagem na saída de git log, você verá seus pais listados na linha que começa com Merge:

commit 8f937c683929b08379097828c8a04350b9b8e183
Merge: 8989ee0 7c6b236
Author: Ben James <[email protected]>
Date:   Wed Aug 17 22:49:41 2011 +0100

Merge branch 'gh-pages'

Conflicts:
    README

Nessa situação, git revert 8f937c6 -m 1você obterá a árvore como estava 8989ee0e git revert -m 2restabelecerá a árvore como estava 7c6b236.

Para entender melhor os IDs principais, você pode executar:

git log 8989ee0 

e

git log 7c6b236
Ben James
fonte
127
de dois números 8989ee0, 7c6b236qual deles ir. Como eu entenderia?
Arup Rakshit
12
Após reverter, não acho que seja possível corrigir facilmente o código no ramo de origem e mesclar novamente? kernel.org/pub/software/scm/git/docs/howto/…
IsmailS
10
Enquanto pesquisava em busca de uma explicação melhor, encontrei este artigo que considerava um ótimo trabalho ao analisar os detalhes. Depois de ler, descobri que o que realmente estava procurando era o comando RESET, seguido de um empurrão forçado. Talvez ajude alguém. Você
está em
46
@ArupRakshit se você executar git log 8989ee0e git log 7c6b236, você deve saber a resposta.
BMW
4
git log --merge para ver todas as mesclagens e git log --no-mesclas para ver o histórico sem fusões. Mesclando um ramo traz na história ramo Incorporada alvo e torná-lo difícil de fazer usando git log simples
Alex Punnen
370

Aqui está um exemplo completo, na esperança de que ajude alguém:

git revert -m 1 <commit-hash> 
git push -u origin master

Onde <commit-hash>está o hash de consolidação da mesclagem que você gostaria de reverter e, conforme indicado na explicação desta resposta , -m 1indica que você deseja reverter para a árvore do primeiro pai antes da mesclagem.

A git revert ...linha basicamente confirma suas alterações enquanto a segunda linha as publica, empurrando-as para a ramificação remota.

Saheed
fonte
21
Eu acreditava que o git revertcomando já confirmava o objeto de confirmação criado. Para que isso não aconteça, você teria que introduzir a --no-commitbandeira
Delfic
2
Como o @Delfic mencionou, o commit já é gerenciado pela primeira linha (eu precisava de um: wq para validá-lo), portanto a segunda linha não é necessária.
precisa saber é
1
isso é confuso. existem apenas 2 linhas e nenhum git commit .. alguém pode editar.
Jay Random
176

Ben lhe disse como reverter um commit de mesclagem, mas é muito importante que você perceba que, ao fazer isso

"... declara que você nunca desejará que as alterações na árvore sejam introduzidas pela mesclagem. Como resultado, mesclagens posteriores somente trarão alterações na árvore introduzidas por confirmações que não são ancestrais da mesclagem revertida anteriormente. Isso pode ou não ser o que você quer. " (página de manual do git-merge) .

Uma mensagem de artigo / lista de discussão vinculada a partir da página de manual detalha os mecanismos e considerações envolvidos. Apenas certifique-se de entender que, se você reverter a consolidação de mesclagem, não poderá mesclar a ramificação novamente mais tarde e esperar que as mesmas alterações retornem.

Ryan Stewart
fonte
81
Mas você pode reverter a reversão para recuperá-las, se realmente necessário.
dalore
5
Obrigado. Muito útil como o caso de uso para desfazer uma mesclagem - devido a um erro, digamos - e depois mesclar novamente a ramificação inteira depois que o bug for corrigido, é comum.
Component 10
3
Se você é como eu e depois desejou a mesclagem, pode reverter a reversão ou escolher a alteração que reverteu.
UnitasBrooks
Na minha situação, percebi a necessidade de "reverter a reversão" para obter minhas alterações de volta ao problema. A colheita de cerejas pode ser uma maneira mais organizada? Vou tentar na próxima vez ...
Steven Anderson
79

Você pode seguir estas etapas para reverter as confirmações incorretas ou redefinir sua ramificação remota novamente para corrigir o HEAD / estado.

  1. faça o checkout da filial remota para o repositório local.
    git checkout development
  2. copie o hash de confirmação (ou seja, o ID da confirmação imediatamente antes da confirmação incorreta) do git log git log -n5

    resultado:

    confirmar 7cd42475d6f95f5896b6f02e902efab0b70e8038 "Mesclar ramificação 'commit incorreto' em 'desenvolvimento'"
    confirmar f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 "confirmar incorretamente"
    confirmar 3779ab50e72908a7 "

  3. redefina a ramificação para o hash de confirmação copiado na etapa anterior
    git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)

  4. execute o git statuspara mostrar todas as alterações que fizeram parte do commit errado.
  5. basta executar git reset --hardpara reverter todas essas alterações.
  6. force o envio remoto de sua filial local para o local remoto e observe que seu histórico de consolidação está limpo como antes de ser poluído.
    git push -f origin development
ssasi
fonte
4
e se, enquanto isso, 20 desenvolvedores fizerem a última fusão de desenvolvedores?
Ewoks
2
Eu não forçaria o envio de um ramo de desenvolvimento quando houver 20 desenvolvedores na equipe usando esse ramo. :) Nesse caso, é aconselhável fazer apenas um commit de reversão.
Ssasi
4
Esta é uma solução muito boa quando você está trabalhando sozinho ou tem certeza de que nenhum outro desenvolvedor retirou os commits que você errou #
Kyle B
52
git revert -m 1 <merge-commit>
Neeraj Kumar
fonte
2
Esta resposta carece de muitos detalhes. Talvez seja por isso que é bom.
Gustavo Straube 24/03
30

Para manter o log limpo, como nada aconteceu (com algumas desvantagens dessa abordagem (devido ao push -f)):

git checkout <branch>
git reset --hard <commit-hash-before-merge>
git push -f origin HEAD:<remote-branch>

'commit-hash-before-merge' vem do log (git log) após a mesclagem.

nvd
fonte
Dica: se você estiver fazendo isso na sua empresa, talvez não tenha permissão.
eneski
3
nunca faça um push -frepo compartilhado
Baptiste Mille-Mathias
17

Às vezes, a maneira mais eficaz de reverter é recuar e substituir.

git log

Use o segundo hash de confirmação (hash completo, aquele para o qual você deseja reverter antes do erro listado) e depois renomeie a partir daí.

git checkout -b newbranch <HASH>

Em seguida, exclua o ramo antigo, copie a ramificação nova em seu lugar e reinicie a partir daí.

git branch -D oldbranch
git checkout -b oldbranch newbranch

Se tiver sido transmitido, exclua a ramificação antiga de todos os repositórios, empurre a ramificação refeita para a mais central e puxe-a de volta para todos.

ppostma1
fonte
4
O aviso sobre a transmissão deve ser realmente mais explícito sobre o quão horrível é uma ideia. Isso corromperá a versão de todos da ramificação e será útil apenas se você estiver trabalhando com um repositório remoto (github / bitbucket) ao qual somente você tem acesso.
RobbyD
Não é tão ruim quanto enviar arquivos de configuração modificados para a produção. Ele não corrompe, é apenas uma reformulação de um commit anterior, portanto, é uma maneira geral de mover o ponteiro de branches para uma versão anterior. Esperemos que ele só afeta o repositório local
ppostma1
5

Se você deseja reverter um mergecommit, aqui está o que você deve fazer.

  1. Primeiro, verifique o git logpara encontrar o ID do seu commit de mesclagem. Você também encontrará vários IDs principais associados à mesclagem (veja a imagem abaixo).

insira a descrição da imagem aqui

Anote o ID de confirmação de mesclagem mostrado em amarelo. Os IDs pai são os escritos na próxima linha como Merge: parent1 parent2. Agora...

História curta:

  1. Alterne para a ramificação na qual a mesclagem foi feita. Em seguida, basta fazer o git revert <merge commit id> -m 1que abrirá um viconsole para inserir a mensagem de confirmação. Escreva, salve, saia, pronto!

Longa história:

  1. Alterne para a ramificação na qual a mesclagem foi feita. No meu caso, é o testramo e estou tentando remover o feature/analytics-v3ramo.

  2. git reverté o comando que reverte qualquer confirmação. Mas há um truque desagradável ao reverter um mergecommit. Você precisa inserir a -mbandeira, caso contrário ela falhará. A partir daqui, você precisa decidir se deseja reverter sua ramificação e fazer com que pareça exatamente como estava em parent1ou parent2via:

git revert <merge commit id> -m 1(reverte para parent2)

git revert <merge commit id> -m 2 (reverte para parent1)

Você pode registrar esses pais para descobrir para onde deseja seguir e essa é a raiz de toda a confusão.

saran3h
fonte
5

Todas as respostas já cobriram a maioria das coisas, mas adicionarei meus 5 centavos. Em resumo, reverter um commit de mesclagem é bastante simples:

git revert -m 1 <commit-hash>

Se você tiver permissão, poderá enviá-lo diretamente para o ramo "mestre", caso contrário, basta enviá-lo para o ramo "reverter" e criar uma solicitação de recebimento.

Você pode encontrar informações mais úteis sobre este assunto aqui: https://itcodehub.blogspot.com/2019/06/how-to-revert-merge-in-git.html

xproph
fonte
1

Eu descobri que a criação de um patch reverso entre dois pontos finais conhecidos e a aplicação desse patch funcionariam. Isso pressupõe que você criou instantâneos (tags) fora de sua ramificação principal ou até mesmo um backup da ramificação principal, como master_bk_01012017.

Digamos que o ramo de código que você mesclou no master fosse mycodebranch.

  1. Mestre de checkout.
  2. Crie um patch reverso binário completo entre o mestre e seu backup. git diff --binary master..master_bk_01012017 > ~/myrevert.patch
  3. Verifique seu patch git apply --check myrevert.patch
  4. Aplicar patch com aprovação git am --signoff < myrevert.patch
  5. Se você precisar inserir esse código novamente depois de corrigido, precisará ramificar o mestre revertido e efetuar o checkout do ramo de correção git branch mycodebranch_fix git checkout mycodebranch_fix
  6. Aqui você precisa encontrar a chave SHA para reverter e reverter git revert [SHA]
  7. Agora você pode usar o mycodebranch_fix para corrigir os problemas, confirmar e reconfigurar no master uma vez concluído.
alexplain
fonte
1

A resposta marcada corretamente funcionou para mim, mas tive que gastar algum tempo para determinar o que estava acontecendo. Então, decidi adicionar uma resposta com etapas simples e diretas para casos como o meu.

Digamos que obtivemos as ramificações A e B .. Você mesclou a ramificação A na ramificação B e empurrou a ramificação B para si mesma, agora a mesclagem faz parte dela. Mas você deseja voltar ao último commit antes da mesclagem .. O que fazer Você faz?

  1. Vá para a pasta raiz do git (normalmente a pasta do projeto) e use git log
  2. Você verá o histórico de confirmações recentes - as confirmações têm propriedades de confirmação / autor / data, enquanto as mesclagens também têm uma propriedade de mesclagem -, assim você as vê assim:

    commit: <commitHash> Merge: <parentHashA> <parentHashB> Author: <author> Date: <date>

  3. Use git log <parentHashA>e git log <parentHashB>- você verá os históricos de consolidação desses ramos principais - os primeiros commit na lista são os mais recentes

  4. Pegue o <commitHash>commit que você deseja, vá para a pasta raiz do git e use git checkout -b <newBranchName> <commitHash>- que criará um novo ramo a partir do último commit que você escolheu antes da mesclagem .. Voila, pronto!
Божидар Йовчев
fonte
0

Também enfrentei esse problema em um PR que foi mesclado ao ramo principal de um repositório do GitHub.

Desde que eu só queria modificar alguns arquivos modificados mas não toda a mudança do PR trouxe, eu tive que amendo merge commitcom git commit --am.

Passos:

  1. Vá para o ramo em que deseja alterar / reverter alguns arquivos modificados
  2. Faça as alterações desejadas de acordo com os arquivos modificados
  3. correr git add *ougit add <file>
  4. corre git commit --am e validar
  5. corre git push -f

Por que é interessante:

  • Mantém inalterado o autor do PR
  • Não quebra a árvore do git
  • Você será marcado como confirmador (o autor da confirmação de mesclagem permanecerá inalterado)
  • O Git age como se você tivesse resolvido conflitos, ele removerá / alterará o código nos arquivos modificados, como se você dissesse manualmente ao GitHub para não mesclá-lo como está.
Maxime Lafarie
fonte
0

Eu encontrei uma boa explicação para Como reverter a mesclagem neste link e copiei e colei a explicação abaixo e seria útil apenas no caso de o link abaixo não funcionar.

Como reverter uma fusão defeituosa Alan ([email protected]) disse:

Eu tenho um ramo mestre. Temos uma ramificação disso em que alguns desenvolvedores estão trabalhando. Eles afirmam que está pronto. Nós o fundimos no ramo mestre. Ele quebra algo, então revertemos a mesclagem. Eles fazem alterações no código. eles chegam a um ponto em que dizem que está tudo bem e nos fundimos novamente. Quando examinados, descobrimos que as alterações de código feitas antes da reversão não estão no ramo mestre, mas as alterações de código depois estão no ramo mestre. e pediu ajuda para se recuperar dessa situação.

A história imediatamente após a "reversão da mesclagem" ficaria assim:

---o---o---o---M---x---x---W
              /
      ---A---B

onde A e B estão do lado do desenvolvimento que não foi tão bom, M é a mesclagem que traz essas alterações prematuras para a linha principal, x são alterações não relacionadas ao que o ramo lateral fez e já fez na linha principal e W é o " reverter a mesclagem M "(W não parece M de cabeça para baixo?). IOW, "diff W ^ .. W" é semelhante a "diff -RM ^ .. M".

Essa "reversão" de uma mesclagem pode ser feita com:

$ git revert -m 1 M Depois que os desenvolvedores do ramo lateral corrigem seus erros, o histórico pode ficar assim:

---o---o---o---M---x---x---W---x
              /
      ---A---B-------------------C---D

onde C e D devem corrigir o que foi quebrado em A e B, e você já pode ter outras alterações na linha principal após W.

Se você mesclar a ramificação lateral atualizada (com D na ponta), nenhuma das alterações feitas em A ou B estará no resultado, porque elas foram revertidas por W. Foi isso que Alan viu.

Linus explica a situação:

A reversão de um commit regular apenas efetivamente desfaz o que o commit fez e é bastante direta. Porém, a reversão de uma consolidação de mesclagem também desfaz os dados alterados pela consolidação, mas não faz absolutamente nada com os efeitos no histórico que a mesclagem teve. Portanto, a mesclagem ainda existirá, e ainda será vista como a união dos dois ramos, e as futuras mesclagens verão essa mesclagem como o último estado compartilhado - e a reversão que reverteu a mesclagem trazida não afetará nada disso. Portanto, uma "reversão" desfaz as alterações de dados, mas é muito nãoum "desfazer" no sentido de que não desfaz os efeitos de uma confirmação no histórico do repositório. Então, se você pensa em "reverter" como "desfazer", sempre sentirá falta dessa parte dos reverts. Sim, desfaz os dados, mas não, não desfaz o histórico. Em tal situação, você desejaria primeiro reverter a reversão anterior, o que tornaria a história assim:

---o---o---o---M---x---x---W---x---Y
              /
      ---A---B-------------------C---D

onde Y é a reversão de W. Essa "reversão da reversão" pode ser feita com:

$ git revert W Este histórico (ignorando possíveis conflitos entre o que W e W..Y mudaram) seria equivalente a não ter W ou Y no histórico:

---o---o---o---M---x---x-------x----
              /
      ---A---B-------------------C---D

e mesclar a ramificação lateral novamente não terá conflitos decorrentes de uma reversão e reversão anteriores da reversão.

---o---o---o---M---x---x-------x-------*
              /                       /
      ---A---B-------------------C---D

É claro que as alterações feitas em C e D ainda podem entrar em conflito com o que foi feito por qualquer um dos x, mas esse é apenas um conflito de mesclagem normal.

MK446
fonte
-2

Como Ryan mencionou, git revertpode dificultar a fusão no caminho, por isso git revertpode não ser o que você deseja. Eu descobri que usar o git reset --hard <commit-hash-prior-to-merge>comando é mais útil aqui.

Depois de fazer a parte de redefinição física, você pode forçar o envio para a ramificação remota, ou seja git push -f <remote-name> <remote-branch-name>, onde <remote-name>é frequentemente chamado origin. A partir desse ponto, você pode mesclar novamente, se desejar.

Harry Wang
fonte
4
Qualquer coisa que envolva força é uma má ideia, a menos que você seja o único a usar o repositório e saiba exatamente o que está fazendo. Revertendo com git revert e, em seguida, possivelmente revertendo com git revert (se você precisar trazer as coisas de volta novamente) é uma alternativa muito mais segura.
Oyvind