Git mesclar com substituição forçada

100

Eu tenho uma filial chamada demo que preciso fundir com o masterbranch. Posso obter o resultado desejado com os seguintes comandos:

git pull origin demo
git checkout master
git pull origin master
git merge demo
git push origin master

Minha única preocupação é, se houver algum problema de mesclagem , quero dizergit para substituir as alterações no masterbranch sem me dar um prompt de mesclagem. Então, basicamente, as alterações no demobranch devem substituir automaticamente as alterações no masterbranch.

Eu olhei ao redor, havia várias opções, mas não quero me arriscar com a fusão.

Pilha aberta
fonte
git push -f origin master
MD XF de
4
@MDXF: Posso estar errado, mas não deveria estar usando -fopção com mergecomando e não com pushcomando
OpenStack
Você pode tentar os dois e ver o que funciona para você
MD XF
Veja o link abaixo para uma solução de substituição de força: stackoverflow.com/a/42454737/984471
Manohar Reddy Poreddy

Respostas:

99

Realmente não relacionado a esta resposta, mas eu abandonaria git pull, o que apenas é executado git fetchseguido por git merge. Você está fazendo três mesclagens, o que fará seu Git executar três operações de busca, quando uma busca é tudo que você precisa. Conseqüentemente:

git fetch origin   # update all our origin/* remote-tracking branches

git checkout demo         # if needed -- your example assumes you're on it
git merge origin/demo     # if needed -- see below

git checkout master
git merge origin/master

git merge -X theirs demo   # but see below

git push origin master     # again, see below

Controlando a mesclagem mais complicada

A parte mais interessante aqui é git merge -X theirs. Como root545 observou , as -Xopções são passadas para a estratégia de mesclagem, e tanto a recursiveestratégia padrão quanto a alternativa resolvetomam -X oursou -X theirs(uma ou outra, mas não ambas). Para entender o que eles fazem, no entanto, você precisa saber como o Git encontra e trata os conflitos de mesclagem .

Um conflito de mesclagem pode ocorrer dentro de algum arquivo 1 quando a versão base difere da versão atual (também chamada local, HEAD ou --ours) e da outra versão (também chamada remota ou --theirs) desse mesmo arquivo. Ou seja, a fusão identificou três revisões (três commits): base, nossa e deles. A versão "base" é a partir da base de mesclagem entre nosso commit e seu commit, conforme encontrado no gráfico de commit (para muito mais sobre isso, veja outras postagens do StackOverflow). O Git então encontrou dois conjuntos de mudanças: "o que fizemos" e "o que eles fizeram". Essas alterações são (em geral) encontradas linha a linha, puramente textuaisbase. Git não tem uma compreensão real do conteúdo do arquivo; é apenas comparar cada linha do texto.

Essas mudanças são o que você vê na git diffsaída e, como sempre, elas também têm contexto . É possível que as coisas que mudamos estejam em linhas diferentes das coisas que eles mudaram, de modo que as mudanças parecem que não iriam colidir, mas o contexto também mudou (por exemplo, devido à nossa mudança estar perto da parte superior ou inferior do arquivo, para que o arquivo se esgote na nossa versão, mas na deles, eles também adicionaram mais texto na parte superior ou inferior).

Se as mudanças acontecerem em linhas diferentes - por exemplo, mudamos colorpara coloura linha 17 e eles mudam fredpara barneya linha 71 - então não há conflito: o Git simplesmente aceita as duas mudanças. Se as mudanças acontecem nas mesmas linhas, mas são mudanças idênticas , o Git pega uma cópia da mudança. Somente se as mudanças estiverem nas mesmas linhas, mas forem mudanças diferentes, ou aquele caso especial de contexto interferente, você obterá um conflito de modificar / modificar.

As opções -X ourse -X theirsinformam ao Git como resolver esse conflito, escolhendo apenas uma das duas alterações: a nossa ou a deles. Como você disse que está mesclando os demo(deles) com os master(os nossos) e deseja as alterações de demo, é o que você deseja -X theirs.

Aplicar às cegas -X, entretanto, é perigoso. Só porque nossas mudanças não entraram em conflito linha por linha não significa que elas não entrem em conflito! Um exemplo clássico ocorre em linguagens com declarações de variáveis. A versão base pode declarar uma variável não utilizada:

int i;

Em nossa versão, excluímos a variável não usada para fazer um aviso do compilador desaparecer - e em sua versão, eles adicionam um loop algumas linhas depois, usando icomo contador de loop. Se combinarmos as duas alterações, o código resultante não será mais compilado. A -Xopção não ajuda aqui, pois as alterações são em linhas diferentes .

Se você tiver um conjunto de testes automatizado, a coisa mais importante a fazer é executar os testes após a fusão. Você pode fazer isso após a confirmação e consertar as coisas mais tarde, se necessário; ou você pode fazer isso antes de confirmar, adicionando --no-commitao git mergecomando. Vamos deixar os detalhes de tudo isso para outras postagens.


1 Você também pode obter conflitos em relação às operações de "todo o arquivo", por exemplo, talvez consertemos a grafia de uma palavra em um arquivo (para que tenhamos uma mudança), e eles apaguem o arquivo inteiro (para que tenham um excluir). O Git não resolverá esses conflitos por conta própria, independentemente dos -Xargumentos.


Fazer menos mesclagens e / ou mesclagens mais inteligentes e / ou usar rebase

Existem três fusões em ambas as nossas sequências de comando. A primeira é trazer origin/demopara o local demo(o seu uso git pullque, se o Git for muito antigo, não será atualizado, origin/demomas produzirá o mesmo resultado final). O segundo é trazer para origin/masterdentro master.

Não está claro para mim quem está atualizando demoe / ou master. Se você escreve seu próprio código em seu próprio demobranch, e outros estão escrevendo código e empurrando-o para o demobranch origin, então esta fusão de primeira etapa pode ter conflitos ou produzir uma fusão real. Na maioria das vezes, é melhor usar rebase, em vez de mesclar, para combinar trabalho (reconhecidamente, isso é uma questão de gosto e opinião). Em caso afirmativo, você pode querer usar git rebase. Por outro lado, se você nunca faz nenhum de seus próprios commits demo, você nem mesmo precisa de umdemo branch. Alternativamente, se você deseja automatizar muito isso, mas ser capaz de verificar cuidadosamente quando há commits que você e outros fizeram, você pode querer usargit merge --ff-only origin/demo: isso irá acelerar o seu, se possível, e simplesmente falhará completamente se não (nesse ponto você pode inspecionar os dois conjuntos de alterações e escolher uma fusão real ou um rebase, conforme apropriado).demo para corresponder ao atualizado,origin/demo

Esta mesma lógica se aplica master, embora você está fazendo a fusão em master , então você definitivamente precisa de um master. No entanto, é ainda mais provável que você deseje que a mesclagem falhe se não puder ser feita como uma não mesclagem de avanço rápido, então isso provavelmente também deveria ser git merge --ff-only origin/master.

Digamos que você nunca faça seus próprios commits no demo. Neste caso, podemos descartar o nome demointeiramente:

git fetch origin   # update origin/*

git checkout master
git merge --ff-only origin/master || die "cannot fast-forward our master"

git merge -X theirs origin/demo || die "complex merge conflict"

git push origin master

Se você está fazendo seus próprios democommits de branch, isso não é útil; você também pode manter a fusão existente (mas talvez adicionar --ff-onlydependendo do comportamento que você deseja), ou alternar para fazer um rebase. Observe que todos os três métodos podem falhar: mesclar pode falhar com um conflito, mesclar com --ff-onlypode não ser capaz de avançar e rebase pode falhar com um conflito (rebase funciona, em essência, com commits seletivamente, que usa a fusão máquinas e, portanto, pode obter um conflito de fusão).

Torek
fonte
19

Eu tive um problema semelhante, onde precisei substituir efetivamente qualquer arquivo que tinha alterações / conflitos por um branch diferente.

A solução que encontrei foi usar git merge -s ours branch.

Observe que a opção é -se não -X. -sdenota o uso de ourscomo uma estratégia de mesclagem de nível superior, -Xestaria aplicando a oursopção à recursiveestratégia de mesclagem, que não é o que eu (ou nós) queremos neste caso.

Etapas, onde oldbranchestá o branch que você deseja substituir newbranch.

  • git checkout newbranch verifica o branch que você deseja manter
  • git merge -s ours oldbranch funde-se no ramo antigo, mas mantém todos os nossos arquivos.
  • git checkout oldbranch verifica o branch que você deseja substituir
  • get merge newbranch funde-se no novo ramo, substituindo o antigo ramo
Niall Mccormack
fonte
Nota: nenhuma das outras soluções aqui funcionou para mim, portanto, estou postando minha resposta.
Niall Mccormack
1
Não funcionou para mim. Ele resolveu o conflito (resolveu os arquivos em conflito), mas o arquivo não foi mesclado. E não pode mesclar nenhum.
c-an
Se alguém ficar travado onde for solicitado "Digite uma mensagem de confirmação para explicar por que essa mesclagem é necessária": Digite sua mensagem, pressione a tecla ESC no teclado, digite: wq e pressione ENTER para sair do prompt.
topherPedersen
17

Esta abordagem de mesclagem irá adicionar um commit no topo do masterqual cola o que estiver dentro feature, sem reclamar de conflitos ou outras porcarias.

insira a descrição da imagem aqui

Antes de tocar em qualquer coisa

git stash
git status # if anything shows up here, move it to your desktop

Agora prepare mestre

git checkout master
git pull # if there is a problem in this step, it is outside the scope of this answer

Se featurevestir bem

git checkout feature
git merge --strategy=ours master

Vá para a matança

git checkout master
git merge --no-ff feature
William Entriken
fonte
1
git push to finish
Kochchy
git-scm.com/docs/git-merge#Documentation/git-merge.txt-ours. Funcionou quando os commits não foram mesclados corretamente. Mas essa abordagem nem sempre funcionará, para citar a fonte Changes from the other tree that do not conflict with our side are reflected in the merge result. Não funcionou para mim, pois eu tinha commits de fusão limpa em meu branch que estava sendo sobrescrito. Existe alguma outra maneira?
NiharGht
11

Você pode tentar a opção "nossa" no git merge,

git merge branch -X nosso

Esta opção força os pedaços conflitantes a serem resolvidos automaticamente de forma limpa, favorecendo nossa versão. As alterações da outra árvore que não conflitam com o nosso lado são refletidas no resultado da mesclagem. Para um arquivo binário, todo o conteúdo é obtido do nosso lado.

Manuj
fonte
3
Isso seria ao contrário, pois o OP disse que deseja que a demoversão seja preferida, embora ele esteja se fundindo com master. Também há -X theirs, mas não está claro se isso é o que o OP realmente deseja.
Torek de
Você não leu todo o caminho. Sua nota descreve o que oursfaz ao usar no back-end com a -sopção. Ao usar ourscom -X: Descarta tudo o que a outra árvore fez, declarando que nossa história contém tudo o que aconteceu nela. fonte
jimasun
2

Quando tentei usar -X theirsoutras opções de comando relacionadas, continuei recebendo um commit de mesclagem. Provavelmente não estava entendendo direito. Uma alternativa fácil de entender é simplesmente excluir o branch e rastreá-lo novamente.

git branch -D <branch-name>
git branch --track <branch-name> origin/<branch-name>

Isso não é exatamente uma "fusão", mas é o que eu procurava quando me deparei com essa pergunta. No meu caso, eu queria extrair alterações de um branch remoto que foram empurradas à força.

Matt
fonte