Existe uma versão “deles” do “git merge -s our”?


876

Ao mesclar a ramificação de tópico "B" em "A" usando git merge, recebo alguns conflitos. Eu sei que todos os conflitos podem ser resolvidos usando a versão em "B".

Eu estou ciente git merge -s ours. Mas o que eu quero é algo parecido git merge -s theirs.

Por que isso não existe? Como posso obter o mesmo resultado após a fusão conflitante com os gitcomandos existentes ? ( git checkouttodo arquivo não imerso de B)

ATUALIZAÇÃO: A "solução" de descartar qualquer coisa do ramo A (o ponto de confirmação de mesclagem para a versão B da árvore) não é o que estou procurando.


1
Veja também esta resposta: stackoverflow.com/questions/928646/... - é trivial para mudar o exemplo para a versão uso B em vez de A.
Robie Basak

8
Consulte o comando SO answer git para criar um ramo como outro para todas as formas possíveis atuais de simulargit merge -s their .
VonC 13/03/11

4
Então você não está realmente à procura de um git merge -s theirs(que pode ser alcançado facilmente com git merge -s ourse uma filial temporária), uma vez que o nosso -s ignora completamente as mudanças do merge-de galho ...
Nicolò Martini

21
@Torek - faça o Git devs realmente encontrá-lo que ofensiva para fornecer theirsalém ours??? Este é um sintoma de um dos problemas de engenharia e design de alto nível no Git: inconsistência.
jww 15/09/16

3
@jww O problema aqui não é sobre "o git merge -s our" ser ofensivo, mas sobre ser contra-intuitivo. Você pode ver na pergunta do OP que se esse recurso fosse adicionado, ele o usaria por engano quando o que ele realmente quer fazer é um "git merge -s recursive -X dele". É comum querer mesclar outro ramo substituindo conflitos com a versão no outro ramo, mas sobrescrever completamente o ramo atual por outro ramo descartando completamente as alterações atuais é realmente um caso de exceção.
ThomazMoura 15/05

Respostas:


1020

Adicione a -Xopção a theirs. Por exemplo:

git checkout branchA
git merge -X theirs branchB

Tudo se fundirá da maneira desejada.

A única coisa que vi causar problemas é se os arquivos foram excluídos do branchB. Eles aparecem como conflitos se algo diferente de git fez a remoção.

A correção é fácil. Basta executar git rmcom o nome de qualquer arquivo que foi excluído:

git rm {DELETED-FILE-NAME}

Depois disso, o -X theirsdeve funcionar como esperado.

Obviamente, fazer a remoção real com o git rmcomando impedirá que o conflito aconteça em primeiro lugar.


Nota : Também existe uma opção de formulário mais longa.

Para usá-lo, substitua:

-X theirs

com:

--strategy-option=theirs

6
Veja outra resposta abaixo para obter uma solução melhor (Paul Pladijs) para a pergunta original.
Malcolm

204
Vale ressaltar que isso não é o mesmo que "estratégia de mesclagem theirs". -Xtheirs é a opção de estratégia aplicada à estratégia recursiva . Isso significa que a estratégia recursiva ainda mesclará tudo o que puder e só voltará à lógica "deles" em caso de conflitos. Enquanto isso é o que se precisa na maioria dos casos, como acima, não é o mesmo que "basta pegar tudo do ramo B". De qualquer maneira, ele faz a mesclagem real.
Timur

2
Também funciona com outros comandos. Acabei de fazer um 'git cherry-pick sha1 -X deles'. Obrigado!
Max Hohenegger

48
Esta é uma resposta terrível, porque parece correta, mas está realmente errada. "git merge -X deles" não faz o que "git merge -s deles" faria. Ele não substitui a ramificação atual pelo conteúdo da ramificação mesclada. Prefere mudanças "deles", mas apenas em caso de conflito. Portanto, a consolidação resultante pode ser bem diferente da ramificação "deles". Não é isso que o cartaz da pergunta tinha em mente.
Ivan Krivyakov

7
@ user3338098: sim, você está certo. Reli a pergunta, e ela também não é tão boa. Infelizmente, a causa raiz da confusão não é a resposta e nem mesmo a pergunta. É a escolha do design dos autores do git atribuir o mesmo nome 'nosso' a uma estratégia de mesclagem e a uma opção da estratégia de mesclagem 'recursiva'. A confusão neste caso é praticamente inevitável.
Ivan Krivyakov 02/09/19

218

Uma solução possível e testada para mesclar branchB em nosso branch de check-out A:

# in case branchA is not our current branch
git checkout branchA

# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours branchB    

# make temporary branch to merged commit
git branch branchTEMP         

# get contents of working tree and index to the one of branchB
git reset --hard branchB

# reset to our merged commit but 
# keep contents of working tree and index
git reset --soft branchTEMP

# change the contents of the merged commit
# with the contents of branchB
git commit --amend

# get rid off our temporary branch
git branch -D branchTEMP

# verify that the merge commit contains only contents of branchB
git diff HEAD branchB

Para automatizá-lo, você pode envolvê-lo em um script usando branchA e branchB como argumentos.

Essa solução preserva o primeiro e o segundo pai do commit de mesclagem, exatamente como você esperaria git merge -s theirs branchB.


P: Por que você precisa do branchTEMP? Você não poderia apenas git reset --soft branchA?
cdunn2001

4
@ cdunn2001: De alguma forma, pensei a mesma coisa, mas não. Observe que as git reset --hardalterações às quais os branchApontos de confirmação são confirmados .
Tsuyoshi Ito

5
desculpe-me por fazer uma pergunta estúpida, mas por que esse método é melhor para -xtheirs? Quais são as vantagens / desvantagens
chrispepper1989

9
@ chrispepper1989 -x deles afeta apenas conflitos , se um pedaço nosso puder ser aplicado de maneira limpa, ele será direcionado à mesclagem resultante. Nesse caso, como mostra o último comando, estamos obtendo uma mesclagem perfeitamente idêntica à branchB, independentemente de haver ou não conflitos.
UncleZeiv 5/05

2
@UncleZeiv, obrigado pela explicação, então, basicamente, fazer um "deles" manteria alterações e arquivos sem conflito resultando em código int que não é idêntico ao código extraído.
Chrispepper1989

89

As versões mais antigas do git permitiam usar a estratégia de mesclagem "deles":

git pull --strategy=theirs remote_branch

Mas isso já foi removido, como explicado nesta mensagem por Junio ​​Hamano (o mantenedor do Git). Conforme observado no link, você faria o seguinte:

git fetch origin
git reset --hard origin

Cuidado, porém, que isso é diferente de uma mesclagem real. Sua solução é provavelmente a opção que você está realmente procurando.


7
Realmente não entendo a explicação de Junio ​​Hamano. Como é git reset --hard originuma solução para uma fusão de estilos deles? Se eu quisesse mesclar o BranchB ao BranchA (como na resposta de Alan W. Smith), como faria isso usando o resetmétodo?
James McMahon

3
@ James McMahon: O ponto de Junio ​​C Hamano não git reset --hardé uma fusão no estilo “deles”. Obviamente, git reset --hardnão cria nenhuma confirmação de mesclagem ou qualquer confirmação para esse assunto. Seu argumento é que não devemos usar uma mesclagem para substituir o que estiver no HEAD por outra coisa. Eu não necessariamente concordo, no entanto.
Tsuyoshi Ito

11
É uma pena que isso tenha theirssido removido porque a justificativa estava incompleta. Ele falhou em permitir aquelas instâncias em que o código é bom; é apenas que o upstream é mantido em uma base filosófica diferente; portanto, nesse sentido, é "ruim"; portanto, quer-se manter atualizado com o upstream, mas ao mesmo tempo, retenha um bom código de "correções" [git & msysgit têm parte desse "conflito" por causa das filosofias de suas diferentes plataformas de destino]]
Philip Oakley

1
Também está faltando o caso de uso com o qual estou preso agora, em que estou compartilhando uma ramificação com outra pessoa e, por acaso, ambos enviamos alterações (em arquivos diferentes) ao mesmo tempo. Então, eu quero pegar todas as versões antigas que tenho localmente e usar as mais novas. Ele quer fazer o mesmo pelos arquivos que atualizei. Não há nada para 'redefinir'. E a solução para fazer o check-out de cada arquivo individual não é prática quando há ~ 20 arquivos.
Szeitlin 24/09/2015

2
Eu realmente não entendo a filosofia. Acabei de usar theirsporque alterei acidentalmente alguns arquivos em duas ramificações separadas e queria descartar as alterações de uma sem precisar fazer manualmente para cada arquivo.
Sudo

65

Não está totalmente claro qual é o resultado desejado, por isso há alguma confusão sobre a maneira "correta" de fazê-lo nas respostas e nos comentários deles. Eu tento dar uma visão geral e ver as três opções a seguir:

Tente mesclar e use B para conflitos

Esta não é a "versão deles para git merge -s ours", mas a "versão deles para git merge -X ours" (que é a abreviação de git merge -s recursive -X ours):

git checkout branchA
# also uses -s recursive implicitly
git merge -X theirs branchB

É isso que, por exemplo, a resposta de Alan W. Smith faz.

Use somente conteúdo de B

Isso cria uma consolidação de mesclagem para os dois ramos, mas descarta todas as alterações branchAe mantém apenas o conteúdo branchB.

# Get the content you want to keep.
# If you want to keep branchB at the current commit, you can add --detached,
# else it will be advanced to the merge commit in the next step.
git checkout branchB

# Do the merge an keep current (our) content from branchB we just checked out.
git merge -s ours branchA

# Set branchA to current commit and check it out.
git checkout -B branchA

Observe que a mesclagem confirma o primeiro pai agora é de branchBe somente o segundo é de branchA. Isto é o que, por exemplo, a resposta de Gandalf458 faz.

Use apenas o conteúdo de B e mantenha a ordem dos pais correta

Esta é a verdadeira "versão deles para git merge -s ours". Ele tem o mesmo conteúdo da opção anterior (ou seja, somente a partir de branchB), mas a ordem dos pais está correta, ou seja, a primeira mãe vem branchAe a segunda parte branchB.

git checkout branchA

# Do a merge commit. The content of this commit does not matter,
# so use a strategy that never fails.
# Note: This advances branchA.
git merge -s ours branchB

# Change working tree and index to desired content.
# --detach ensures branchB will not move when doing the reset in the next step.
git checkout --detach branchB

# Move HEAD to branchA without changing contents of working tree and index.
git reset --soft branchA

# 'attach' HEAD to branchA.
# This ensures branchA will move when doing 'commit --amend'.
git checkout branchA

# Change content of merge commit to current index (i.e. content of branchB).
git commit --amend -C HEAD

É o que a resposta de Paul Pladijs faz (sem exigir uma ramificação temporária).


Uma versão mais limpa da opção 3:git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit
jthill

@jthill: Embora sua versão seja mais concisa, ela também usa o comando de baixo nível ("encanamento") com o read-treequal o usuário git médio pode não estar acostumado (pelo menos eu não ;-)). As soluções na resposta usam apenas os comandos de alto nível ("porcelana") que a maioria dos usuários do git deve saber. Eu prefiro eles, mas obrigado por sua versão!
siegi 21/05

Você pode explicar o penúltimo passo (checkout branchA)? Parece que não deveria estar lá.
Patrick

@ Patrick, esta etapa é necessária. Se você pular, obterá o mesmo commit, mas você não teria um ramo apontando para esse commit. Portanto, assim que você alternar para outro commit / branch, o commit que você fez com o último comando ( …commit --amend…) será "perdido". Estou planejando adicionar algumas explicações gráficas para esta resposta assim que eu tiver tempo para fazê-las ... ;-)
Siegi

62

Eu usei a resposta de Paul Pladijs desde agora. Descobri que você pode fazer uma mesclagem "normal", ocorrer conflitos, e você

git checkout --theirs <file>

para resolver o conflito usando a revisão da outra ramificação. Se você fizer isso para cada arquivo, terá o mesmo comportamento que você esperaria de

git merge <branch> -s theirs

Enfim, o esforço é mais do que seria com a estratégia de mesclagem! (Isso foi testado com a versão 1.8.0 do git)


1
seria ótimo se a lista de arquivos pudesse ser recuperada. Por exemplo, git ls-files --modified | xargs git addeu queria fazer isso para adicionado de ambos os lados se fundem: /
andho

Na verdade, o git retorna os arquivos adicionados nos dois lados com o git ls-files --modifiedque eu acho que essa também é uma solução viável.
andho

1
Obrigado por adicionar isso também. Mais frequentemente, não é necessário arquivo por arquivo. Além disso, o status do git mostra "Caminhos não imersos" na parte inferior. Para obter a lista de caminhos não resolvidos, eu uso este alias:git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"'
Melvyn

qual é o significado -Xe qual a diferença entre -Xe -s? não encontra nenhum documento.
Lei Yang

21

Eu resolvi meu problema usando

git checkout -m old
git checkout -b new B
git merge -s ours old

um ramo "B" do ramo "antigo"
elmarco 22/02

13

Ao mesclar a ramificação de tópico "B" em "A" usando a mesclagem de git, recebo alguns conflitos. Eu sei que todos os conflitos podem ser resolvidos usando a versão em "B".

Estou ciente da fusão do git - é nossa. Mas o que eu quero é algo como git merge> -s deles.

Suponho que você tenha criado uma ramificação fora do mestre e agora queira voltar ao mestre, substituindo qualquer um dos itens antigos do mestre. Era exatamente o que eu queria fazer quando me deparei com este post.

Faça exatamente o que você deseja fazer, exceto mesclar primeiro um ramo ao outro. Eu fiz isso e funcionou muito bem.

git checkout Branch
git merge master -s ours

Em seguida, faça o checkout master e mescle sua filial (ela funcionará sem problemas agora):

git checkout master
git merge Branch

1
Obrigado. Essa deve ser a resposta aceita, de longe a mais simples e a mais correta. Se sua ramificação estiver atrasada / entrar em conflito com o mestre, é o trabalho da ramificação a ser mesclado e fazer com que tudo funcione antes de mesclar tudo novamente ao mestre.
StackHola 14/03

Isso funcionou muito bem, obrigado.
Kodie Grantham

12

Se você estiver no ramo A, faça:

git merge -s recursive -X theirs B

Testado na versão 1.7.8 do git


8

Para realmente fazer uma mesclagem adequada, que recebe apenas a entrada da ramificação que você está mesclando, você pode fazer

git merge --strategy=ours ref-to-be-merged

git diff --binary ref-to-be-merged | git apply --reverse --index

git commit --amend

Não haverá conflitos em nenhum cenário que eu conheça, você não precisa criar ramificações adicionais e ele age como um commit de mesclagem normal.

Isso não funciona bem com submódulos no entanto.


O que o -R faz aqui?
P. Myer Nore

Dessa forma, você perde o histórico dos arquivos "movidos e alterados". Estou certo?
elysch

1
O -R é - reverso, eu atualizei a resposta para que seja mais auto-documentada.
Elias Lynn

Isso não parece funcionar se os arquivos que você está tentando mesclar forem excluídos na ramificação recebida.
precisa saber é o seguinte

7

Por que isso não existe?

Embora eu mencione no " comando git para criar um ramo como outro " como simular git merge -s theirs, observe que o Git 2.15 (quarto trimestre de 2017) agora é mais claro:

A documentação para ' -X<option>' para mesclagens foi escrita enganosamente para sugerir que " -s theirs" existe, o que não é o caso.

Veja commit c25d98b (25 de setembro de 2017) por Junio ​​C Hamano ( gitster) .
(Mesclado por Junio ​​C Hamano - gitster- in commit 4da3e23 , 28 de setembro de 2017)

estratégias de mesclagem: evite sugerir que " -s theirs" existe

A -Xoursopção descrição da mesclagem possui uma nota entre parênteses que informa aos leitores que é muito diferente do -s oursque é correto, mas a descrição -Xtheirsque segue segue descuidadamente diz "isso é o oposto de ours", dando uma falsa impressão de que os leitores também precisam ser avisado de que é muito diferente do -s theirsque na realidade nem existe.

-Xtheirsé uma opção de estratégia aplicada à estratégia recursiva. Isso significa que a estratégia recursiva ainda mesclará tudo o que puder e " theirs" voltará à lógica em caso de conflito.

Esse debate sobre a pertinência ou não de uma theirsestratégia de mesclagem foi trazido de volta recentemente neste segmento de setembro de 2017 .
Ele reconhece threads mais antigos (2008)

Em resumo, a discussão anterior pode ser resumida em "não queremos -s theirs", pois incentiva o fluxo de trabalho errado ".

Menciona o apelido:

mtheirs = !sh -c 'git merge -s ours --no-commit $1 && git read-tree -m -u $1' -

Yaroslav Halchenko tenta defender mais uma vez essa estratégia, mas Junio ​​C. Hamano acrescenta :

A razão pela qual os nossos e os deles não são simétricos é porque você é você e não eles - o controle e a propriedade de nossa história e a história deles não são simétricos.

Depois de decidir que o histórico deles é a linha principal, você prefere tratar sua linha de desenvolvimento como uma ramificação lateral e fazer uma fusão nessa direção, ou seja, o primeiro pai da mesclagem resultante é um commit no histórico e no segundo pai é o último mau da sua história. Então você acabaria usando " checkout their-history && merge -s ours your-history" para manter a primeira paternidade sensata.

E nesse ponto, o uso de " -s ours" não é mais uma solução alternativa por falta de " -s theirs".
É uma parte apropriada da semântica desejada, ou seja, do ponto de vista da linha de história canônica sobrevivente, você deseja preservar o que fez, anulando o que a outra linha da história fez .

Junio ​​acrescenta, como comentado por Mike Beaton :

git merge -s ours <their-ref>afirma efetivamente 'marcar commits feitos <their-ref>em suas filiais como commits para serem permanentemente ignorados';
e isso é importante porque, se você mesclar posteriormente os estados posteriores de suas ramificações, as alterações posteriores serão trazidas sem que as alterações ignoradas sejam trazidas .


1
Esta é uma resposta útil, mas não menciona um ponto-chave que eu acabei de entender da resposta de Junio ​​C. Hamano: git merge -s ours <their-ref>efetivamente diz 'mark commits feitos para <their-ref> em suas filiais, pois confirma ser permanentemente ignorado '; e isso é importante porque, se você posteriormente fusão dos estados posteriores do seu ramo, suas alterações posteriores serão trazidos sem as alterações ignorados nunca ser trazido.
MikeBeaton

1
@MikeBeaton Obrigado. Incluímos seu comentário na resposta para obter mais visibilidade.
VonC 12/09/19

5

Veja a resposta amplamente citada de Junio ​​Hamano : se você deseja descartar conteúdo comprometido, apenas descarte os commits ou, de qualquer forma, mantenha-o fora da história principal. Por que incomodar a todos no futuro lendo mensagens de confirmação que não têm nada a oferecer?

Mas, às vezes, existem requisitos administrativos, ou talvez algum outro motivo. Para aquelas situações em que você realmente precisa registrar confirmações que não contribuem com nada, você deseja:

(edit: uau, eu consegui entender isso errado antes. Este funciona.)

git update-ref HEAD $(
        git commit-tree -m 'completely superseding with branchB content' \
                        -p HEAD -p branchB    branchB:
)
git reset --hard

3

Este usa uma árvore de leitura do comando git plumbing, mas contribui para um fluxo de trabalho geral mais curto.

git checkout <base-branch>

git merge --no-commit -s ours <their-branch>
git read-tree -u --reset <their-branch>
git commit

# Check your work!
git diff <their-branch>

0

Isso mesclará seu newBranch na baseBranch existente

git checkout <baseBranch> // this will checkout baseBranch
git merge -s ours <newBranch> // this will simple merge newBranch in baseBranch
git rm -rf . // this will remove all non references files from baseBranch (deleted in newBranch)
git checkout newBranch -- . //this will replace all conflicted files in baseBranch

Sugiro adicionar git commit --amendno final do seu exemplo, ou os usuários poderão ver a mesclagem confirmada git loge assumir que a operação está concluída.
Michael R

0

Eu acho que o que você realmente quer é:

git checkout -B mergeBranch branchB
git merge -s ours branchA
git checkout branchA
git merge mergeBranch
git branch -D mergeBranch

Isso parece desajeitado, mas deve funcionar. A única coisa que eu realmente não gosto nesta solução é que o histórico do git será confuso ... Mas pelo menos o histórico será completamente preservado e você não precisará fazer algo especial para os arquivos excluídos.


0

O equivalente (que mantém a ordem dos pais) a 'git merge -s its branchB'

Antes da mesclagem:insira a descrição da imagem aqui

!!! Certifique-se de estar em estado limpo !!!

Faça a mesclagem:

git commit-tree -m "take theirs" -p HEAD -p branchB 'branchB^{tree}'
git reset --hard 36daf519952 # is the output of the prev command

O que fizemos ? Criamos um novo commit, no qual dois pais são nossos e o deles e o conteúdo do commit é branchB - deles

Após a mesclagem:insira a descrição da imagem aqui

Mais precisamente:

git commit-tree -m "take theirs" -p HEAD -p 'SOURCE^{commit}' 'SOURCE^{tree}'

0

Esta resposta foi dada por Paul Pladijs. Eu apenas peguei seus comandos e criei um apelido git por conveniência.

Edite seu arquivo .gitconfig e adicione o seguinte:

[alias]
    mergetheirs = "!git merge -s ours \"$1\" && git branch temp_THEIRS && git reset --hard \"$1\" && git reset --soft temp_THEIRS && git commit --amend && git branch -D temp_THEIRS"

Então você pode "git merge -s his A" executando:

git checkout B (optional, just making sure we're on branch B)
git mergetheirs A

-1

Recentemente, eu precisava fazer isso para dois repositórios separados que compartilham um histórico comum. Comecei com:

  • Org/repository1 master
  • Org/repository2 master

Eu queria que todas as alterações repository2 masterfossem aplicadas repository1 master, aceitando todas as alterações que o repository2 faria. Nos termos do git, essa deve ser uma estratégia chamada -s theirsMAS, não existe. Tenha cuidado, pois -X theirsé nomeado como seria o que você deseja, mas NÃO é o mesmo (o mesmo diz na página do manual).

O jeito que resolvi isso foi repository2criar um novo ramo repo1-merge. Nesse ramo, eu corri git pull git@gitlab.com:Org/repository1 -s ourse ele se funde bem, sem problemas. Eu o empurro para o controle remoto.

Então volto repository1e faço um novo ramo repo2-merge. Nesse ramo, eu corro, git pull git@gitlab.com:Org/repository2 repo1-mergeque será concluído com problemas.

Finalmente, você precisaria emitir uma solicitação de mesclagem repository1para torná-la o novo mestre ou apenas mantê-la como uma ramificação.


-2

Uma maneira simples e intuitiva de fazê-lo em duas etapas é

git checkout branchB .
git commit -m "Picked up the content from branchB"

Seguido por

git merge -s ours branchB

(que marca as duas ramificações como mescladas)

A única desvantagem é que ele não remove os arquivos que foram excluídos no branchB do seu branch atual. Uma diferença simples entre os dois ramos depois mostrará se existem esses arquivos.

Essa abordagem também deixa claro, no log de revisão, o que foi feito - e o que foi planejado.


isso pode estar correto para a pergunta original; no entanto, o OP atualizou a pergunta em 2008 de forma que esta resposta esteja incorreta.
precisa saber é o seguinte

1
@ user3338098 Sim, uma pena que eles deixaram o título enganador e apenas bateu a atualização não é tão visível no final do corpo pergunta .... porque estas respostas não responder corretamente à pergunta do título.
RomainValeri

Concordo plenamente com @RomainValeri
AppleCiderGuy
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.