Ferramenta simples para 'aceitar a deles' ou 'aceitar a minha' em um arquivo inteiro usando git

399

Eu não quero uma ferramenta de mesclagem visual e também não quero ter que vi o arquivo em conflito e escolher manualmente entre HEAD (meu) e a alteração importada (deles). Na maioria das vezes, eu quero todas as alterações deles ou todas as minhas. Geralmente, isso ocorre porque minha alteração o elevou e está voltando para mim através de um puxão, mas pode ser ligeiramente modificada em vários lugares.

Existe uma ferramenta de linha de comando que se livre dos marcadores de conflito e escolha de uma maneira ou de outra com base na minha escolha? Ou um conjunto de comandos git que eu também posso fazer para cada um.

# accept mine
alias am="some_sequence;of;commands"
alias at="some_other_sequence;of;commands"

Fazer isso é bastante irritante. Para 'aceitar o meu', tentei:

randy@sabotage ~/linus $ git merge test-branch
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
Automatic merge failed; fix conflicts and then commit the result.

randy@sabotage ~/linus $ git checkout Makefile 
error: path 'Makefile' is unmerged

andy@sabotage ~/linus $ git reset --hard HEAD Makefile 
fatal: Cannot do hard reset with paths.

Como devo me livrar desses marcadores de mudança?

Eu posso fazer:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

Mas isso parece bastante complexo, deve haver uma maneira melhor. E, neste momento, não tenho certeza se o git acha que a fusão aconteceu, então não acho que isso funcione necessariamente.

Indo para o outro lado, fazer 'aceitar os deles' é igualmente confuso. A única maneira de descobrir isso é:

git show test-branch:Makefile > Makefile; git add Makefile;

Isso também me dá uma mensagem de confirmação confusa, que contém Conflicts: Makefile duas vezes.

Alguém pode apontar como executar as duas ações acima de uma maneira mais simples? obrigado

nosatalian
fonte
4
Eu tenho que dar a você como um usuário de linha de comando com três anos + git. Acho isso ridiculamente difícil de fazer a partir da memória. Ele realmente deve ser incorporado por padrão.
Mauvis Ledford

Respostas:

602

A solução é muito simples. git checkout <filename>tenta fazer check-out do arquivo do índice e, portanto, falha na mesclagem.

O que você precisa fazer é (por exemplo, finalizar uma confirmação ):

Para fazer o checkout de sua própria versão, você pode usar um dos seguintes:

git checkout HEAD -- <filename>

ou

git checkout --ours -- <filename>

ou

git show :2:<filename> > <filename> # (stage 2 is ours)

Para fazer o checkout da outra versão, você pode usar um dos seguintes:

git checkout test-branch -- <filename>

ou

git checkout --theirs -- <filename>

ou

git show :3:<filename> > <filename> # (stage 3 is theirs)

Você também precisará executar 'add' para marcá-lo como resolvido:

git add <filename>
Jakub Narębski
fonte
31
Eu achei um pouco estranho que --ourse --theirssignifica exatamente o oposto do que eu intuitivamente pensou quando experimentar este comando ...
Joshua Muheim
6
Tenha cuidado ao usar git show- isso ignora a normalização da nova linha.
Cronial
2
Isso é bom para alguns arquivos, mas quando você tem muitos arquivos em conflito (porque a data em um comentário foi alterada!), Como você faz isso?
JhovaniC
4
@ Santos: o --é usado pelo Git para separar revisões (nomes de filiais etc.) dos nomes de caminhos (nomes de arquivos, diretórios). É importante se o Git não puder decidir se um nome é o nome da ramificação ou o nome do arquivo. Isso segue a convenção POSIX (ou GNU) de usar traço duplo para separar opções de argumentos (nomes de arquivos).
Jakub Narębski
3
@ Sammaron @ Joshua Muheim; o theirs/ ourspode aparecer trocado se você estiver resolvendo conflitos no contexto de uma operação de rebase. Como o rebase funciona verificando o ramo de destino, a seleção de cerejas confirmada do "seu" ramo para o destino, a alteração recebida ("deles") é do ramo "seu" e o ramo atual é o ramo de destino ("nosso" )
RJFalconer
93

Tente o seguinte:

Para aceitar as alterações deles: git merge --strategy-option theirs

Para aceitar o seu: git merge --strategy-option ours

Siva Mandadi
fonte
5
Observe que isso manterá suas alterações para TODOS os arquivos conflitantes; portanto, pode ser perigoso se ocorrer um conflito inesperado.
John
3
E você pode usar isso para outros comandos de mesclagem-y, como cherry-pick e rebase.
Idbrii 17/05
50

Com base na resposta de Jakub, você pode configurar os seguintes aliases de git por conveniência:

accept-ours = "!f() { git checkout --ours -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"
accept-theirs = "!f() { git checkout --theirs -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"

Opcionalmente, eles usam um ou vários caminhos de arquivos para resolver e o padrão para resolver tudo no diretório atual, se nenhum for fornecido.

Adicione-os à [alias]seção do seu ~/.gitconfigou execute

git config --global alias.accept-ours '!f() { git checkout --ours -- "${@:-.}"; git add -u "${@:-.}"; }; f'
git config --global alias.accept-theirs '!f() { git checkout --theirs -- "${@:-.}"; git add -u "${@:-.}"; }; f'
kynan
fonte
11
Não está funcionando para mim ... São para bash ou algum outro shell?
User456584
Esses são aliases do git, adicione-os à [alias]seção no seu ~.gitconfigou use git config --global accept-ours "...". Editei minha resposta.
Kynan
2
Você não tem idéia de quanto tempo esse pseudônimo me salvou. Afirmativo!
Adam Parkin
11
@hakre Certifique-se de citar o alias, caso contrário, seu shell tentará interpretá-lo. Ou apenas edite manualmente o seu ~/.gitconfig.
Kynan
11
Sintaxe do shell para valores padrão:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
jthill
17

Com base na resposta de kynan, aqui estão os mesmos aliases, modificados para que possam lidar com espaços e traços iniciais nos nomes de arquivos:

accept-ours = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --ours -- \"$@\"; git add -u -- \"$@\"; }; f"
accept-theirs = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --theirs -- \"$@\"; git add -u -- \"$@\"; }; f"
Dar
fonte
0

A situação ideal para a resolução de conflitos é quando você sabe antecipadamente como deseja resolvê-los e pode passar as opções da estratégia de mesclagem recursiva -Xoursou -Xtheirs. Fora disso, posso ver três cenários:

  1. Você deseja apenas manter uma única versão do arquivo (isso provavelmente só deve ser usado em arquivos binários não substituíveis, pois os arquivos conflitantes e não conflitantes podem ficar fora de sincronia).
  2. Você quer simplesmente decidir todos os conflitos em uma direção específica.
  3. Você precisa resolver alguns conflitos manualmente e depois resolver todo o resto em uma direção específica.

Para resolver esses três cenários, você pode adicionar as seguintes linhas ao seu .gitconfigarquivo (ou equivalente):

[merge]
  conflictstyle = diff3
[mergetool.getours]
  cmd = git-checkout --ours ${MERGED}
  trustExitCode = true
[mergetool.mergeours]
  cmd = git-merge-file --ours ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keepours]
  cmd = sed -I '' -e '/^<<<<<<</d' -e '/^|||||||/,/^>>>>>>>/d' ${MERGED}
  trustExitCode = true
[mergetool.gettheirs]
  cmd = git-checkout --theirs ${MERGED}
  trustExitCode = true
[mergetool.mergetheirs]
  cmd = git-merge-file --theirs ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keeptheirs]
  cmd = sed -I '' -e '/^<<<<<<</,/^=======/d' -e '/^>>>>>>>/d' ${MERGED}
  trustExitCode = true

A get(ours|theirs)ferramenta apenas mantém a respectiva versão do arquivo e joga fora todas as alterações da outra versão (para que não haja mesclagem).

A merge(ours|theirs)ferramenta refaz a fusão de três maneiras das versões local, base e remota do arquivo, optando por resolver conflitos na direção especificada. Isso tem algumas ressalvas, especificamente: ignora as opções diff que foram passadas para o comando mesclar (como manipulação de algoritmo e espaço em branco); faz a mesclagem limpa dos arquivos originais (para que sejam descartadas quaisquer alterações manuais no arquivo, o que pode ser bom ou ruim); e tem a vantagem de que não pode ser confundido por marcadores diff que deveriam estar no arquivo.

A keep(ours|theirs)ferramenta simplesmente edita os marcadores diff e as seções fechadas, detectando-os por expressão regular. Isso tem a vantagem de preservar as opções diff do comando mesclar e permitir que você resolva alguns conflitos manualmente e depois resolva automaticamente o restante. Tem a desvantagem de que, se houver outros marcadores de conflito no arquivo, ele poderá ficar confuso.

Todos são usados ​​executando git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]onde, se <filename>não for fornecido, processa todos os arquivos em conflito.

De um modo geral, supondo que você saiba que não há marcadores diff para confundir a expressão regular, as keep*variantes do comando são as mais poderosas. Se você deixar a mergetool.keepBackupopção desabilitada ou verdadeira, após a mesclagem, poderá diferenciar o *.origarquivo do resultado da mesclagem para verificar se faz sentido. Como exemplo, executei o seguinte após mergetoolapenas para inspecionar as alterações antes de confirmar:

for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done

Nota : Se merge.conflictstylenão for diff3, o /^|||||||/padrão na sedregra precisará ser /^=======/.

Parakleta
fonte