Rebaseando uma consolidação de mesclagem do Git

183

Tome o seguinte caso:

Tenho algum trabalho em uma ramificação de tópicos e agora estou pronto para voltar ao master:

* eb3b733 3     [master] [origin/master]
| * b62cae6 2   [topic]
|/  
* 38abeae 1

Realizo a mesclagem do mestre, resolvo os conflitos e agora tenho:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | eb3b733 3                     [origin/master]
|/  
* 38abeae 1

Agora, a mesclagem levou algum tempo, então eu faço outra busca e percebo que a ramificação principal remota tem novas alterações:

*   8101fe3 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
| | * e7affba 4                   [origin/master]
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Se eu tentar 'git rebase origin / master' do master, sou forçado a resolver todos os conflitos novamente e também perco a confirmação de mesclagem:

* d4de423 2       [master]
* e7affba 4       [origin/master]
* eb3b733 3
| * b62cae6 2     [topic]
|/  
* 38abeae 1

Existe uma maneira limpa de refazer a consolidação de mesclagem para que eu termine com um histórico como o que mostro abaixo?

*   51984c7 Merge branch 'topic'  [master]
|\  
| * b62cae6 2                     [topic]
* | e7affba 4                     [origin/master]
* | eb3b733 3
|/  
* 38abeae 1
jipumarino
fonte
74
TL; DR:git rebase --preserve-merges origin/master
Ilia K.
6
Com relação a ter que resolver novamente conflitos, você pode dar uma olhada no git rerere .
Parker Coates
git config --global pull.rebase preserve para sempre preservar os commits mesclagem durante uma rebase
galath
4
Aviso: a partir do Git 2.18 (segundo trimestre de 2018, 5 anos depois), git --rebase-mergessubstituirá o antigo git --preserve-merges. Veja o que exatamente o “ rebase --preserve-merges” do Git faz (e por quê?)
VonC
1
--preserve-mergesestá obsoleto. Usegit rebase --rebase-merges origin/master
Arjun Sreedharan

Respostas:

126

Existem duas opções aqui.

Uma é fazer uma reformulação interativa e editar a confirmação de mesclagem, refazer a mesclagem manualmente e continuar a reformulação.

Outra é usar a --rebase-mergesopção on git rebase, que é descrita a seguir no manual: "Por padrão, um rebase simplesmente remove commits de mesclagem da lista de tarefas e coloca os commits rebased em um único ramo linear. Com --rebase- mescladas, a rebase tentará preservar a estrutura de ramificação dentro das confirmações a serem rebaseadas, recriando as confirmações de mesclagem. Quaisquer conflitos de mesclagem resolvidos ou alterações manuais nessas confirmações de mesclagem terão que ser resolvidos / reaplicados manualmente ".

siride
fonte
16
Eu tentei a opção -p, e ela realmente deixa o histórico de consolidação como eu queria, mas me obriga a resolver os conflitos novamente, mesmo em arquivos que não foram editados na origem / mestre. Como sua primeira sugestão, qual seria a sequência precisa de comandos?
Jipumarino
2
@ jipumarino: git rebase -i (diga para editar o commit de mesclagem), quando ele chegar ao commit de mesclagem, git reset --hard HEAD ^, git merge, consertar conflitos, git commit, git rebase --continue. Você também pode querer olhar para o git rerere, que deve ajudar com esse tipo de coisa (mas eu nunca usei, então não posso oferecer nenhum conselho ou ajuda).
siride
2
Obrigado. Eu ativei o rerere e tentei com rebase -p e está funcionando como deveria.
Jipumarino
3
Aqui está um excelente post que descreve esta situação exata: rebasing Compromete mesclagem no Git
Kynan
1
O rere não é a solução, pois você ainda precisa resolver as mesclagens manualmente na primeira vez.
Flimm
29

Ok, essa é uma pergunta antiga e ela já aceitou resposta @siride, mas essa resposta não foi suficiente no meu caso, pois --preserve-mergesobriga você a resolver todos os conflitos pela segunda vez. Minha solução é baseada na ideia, @Tobi Bmas com comandos passo a passo exatos

Então, começaremos nesse estado com base no exemplo da pergunta:

*   8101fe3 Merge branch 'topic'  [HEAD -> master]
|\  
| * b62cae6 2                     [topic]
| |
| | * f5a7ca8 5                   [origin/master]
| | * e7affba 4
| |/  
|/|   
* | eb3b733 3
|/  
* 38abeae 1

Observe que temos 2 commits à frente do master, portanto, a escolha da cereja não funcionaria.

  1. Primeiro de tudo, vamos criar o histórico correto que queremos:

    git checkout -b correct-history # create new branch to save master for future
    git rebase --strategy=ours --preserve-merges origin/master
    

    Usamos --preserve-mergespara salvar nosso commit de mesclagem no histórico. Usamos --strategy=oursignorar todos os conflitos de mesclagem, pois não nos importamos com o conteúdo desse commit de mesclagem, precisamos apenas de um bom histórico agora.

    A história será assim (ignorando o mestre):

    *   51984c7 Merge branch 'topic'  [HEAD -> correct-history]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
  2. Vamos obter o índice correto agora.

    git checkout master # return to our master branch
    git merge origin/master # merge origin/master on top of our master
    

    Podemos obter alguns conflitos adicionais de mesclagem aqui, mas isso seria apenas conflitos de arquivos alterados entre 8101fe3e f5a7ca8, mas não inclui conflitos já resolvidos detopic

    O histórico ficará assim (ignorando o histórico correto):

    *   94f1484 Merge branch 'origin/master'  [HEAD -> master]
    |\  
    * | f5a7ca8 5                   [origin/master]
    * | e7affba 4
    | *   8101fe3 Merge branch 'topic'
    | |\  
    | | * b62cae6 2                     [topic]
    |/ /
    * / eb3b733 3
    |/  
    * 38abeae 1
    
  3. A última etapa é combinar nossa ramificação com o histórico correto e ramificar com o índice correto

    git reset --soft correct-history
    git commit --amend
    

    Usamos reset --softpara redefinir nosso ramo (e histórico) para o histórico correto, mas deixamos o índice e a árvore de trabalho como estão. Em seguida, commit --amendreescrevemos nosso commit de mesclagem, que costumava ter um índice incorreto, com nosso bom índice do master.

    No final, teremos esse estado (observe outro ID do top commit):

    *   13e6d03 Merge branch 'topic'  [HEAD -> master]
    |\  
    | * b62cae6 2                     [topic]
    * | f5a7ca8 5                     [origin/master]
    * | e7affba 4
    * | eb3b733 3
    |/  
    * 38abeae 1
    
Ivan Naydonov
fonte
Isso é incrível e ajudou muito! Mas não entendi o truque com o commit --amend: você poderia adicionar mais informações sobre isso? O que exatamente acontece depois que você o executa - notei que o SHA do commit foi alterado - mas por quê? Ou o que acontece se você não executar isso?
ZenJ
1
O @ZenJ git commit --amendadiciona as alterações ao último commit (HEAD, neste caso, o commit de mesclagem). Como o conteúdo da confirmação é alterado, o hash é atualizado.
Dries Staelens
1
Para pessoas que não têm o 're-redirecionamento' ativado antes de corrigir conflitos, esta solução é ótima porque evita que você precise corrigir os conflitos novamente. Obrigado!
Shackleford
6

Como perdi um dia tentando descobrir isso e realmente encontrei uma solução com a ajuda de um colega de trabalho, achei que deveria entrar em cena.

Temos uma grande base de código e temos que lidar com 2 ramificações sendo fortemente modificadas ao mesmo tempo. Existe um ramo principal e um ramo secundário, se você qual.

Enquanto mesclo o ramo secundário no ramo principal, o trabalho continua no ramo principal e, quando termino, não posso enviar minhas alterações porque elas são incompatíveis.

Portanto, preciso "refazer" minha "mesclagem".

Foi assim que finalmente fizemos:

1) anote o SHA. ex .: c4a924d458ea0629c0d694f1b9e9576a3ecf506b

git log -1

2) Crie o histórico adequado, mas isso interromperá a mesclagem.

git rebase -s ours --preserve-merges origin/master

3) anote o SHA. ex .: 29dd8101d78

git log -1

4) Agora redefina para onde você estava antes

git reset c4a924d458ea0629c0d694f1b9e9576a3ecf506b --hard

5) Agora mescle o mestre atual em seu ramo de trabalho

git merge origin/master
git mergetool
git commit -m"correct files

6) Agora que você possui os arquivos corretos, mas o histórico errado, obtenha o histórico correto em cima de suas alterações com:

git reset 29dd8101d78 --soft

7) E então - altere os resultados no seu commit de mesclagem original

git commit --amend

Voila!

Claude Peloquin
fonte
1

Parece que o que você deseja fazer é remover sua primeira mesclagem. Você pode seguir o seguinte procedimento:

git checkout master      # Let's make sure we are on master branch
git reset --hard master~ # Let's get back to master before the merge
git pull                 # or git merge remote/master
git merge topic

Isso daria o que você quer.

Antoine Pelisse
fonte
4
Com o rerere ativado, isso parece fornecer o mesmo resultado que a solução rebase -p fornecida acima pelo siride.
Jipumarino
0
  • Do seu commit de mesclagem
  • Escolha a nova mudança que deve ser fácil
  • copie suas coisas
  • refaça a mesclagem e resolva os conflitos apenas copiando os arquivos da sua cópia local;)
Tobi B
fonte
1
Esta resposta parece boa, mas seria mais útil se você deu os comandos reais git, no caso de um usuário é novo para git
Louise Davies