Git se fundindo dentro de uma linha

Preâmbulo

Estou usando o git como um sistema de version control para um trabalho que meu laboratório está escrevendo, em LaTeX. Existem várias pessoas colaborando.

Eu estou correndo em git sendo teimoso sobre como se funde. Digamos que duas pessoas fizeram alterações de uma palavra em uma linha e tentaram mesclá-las. Embora git diff –word-diff pareça capaz de EXIBIR a diferença entre os ramos palavra por palavra, o git merge parece incapaz de realizar a mesclagem palavra por palavra e, em vez disso, requer uma mesclagem manual.

Com um documento LaTeX isso é particularmente irritante, já que o hábito comum ao escrever LaTeX é escrever um parágrafo completo por linha e deixar seu editor de texto manipular quebra de linha ao exibir para você. Estamos trabalhando por enquanto adicionando uma nova linha para cada sentença, para que o git possa pelo menos mesclar mudanças em sentenças diferentes dentro de um parágrafo. Mas ele ainda vai ficar confuso sobre várias mudanças dentro de uma frase, e isso faz com que o texto não seja mais bem encheckboxdo, é claro.

A questão

Existe uma maneira de mesclar dois arquivos “palavra por palavra” em vez de “linha por linha”?

Aqui está uma solução na mesma linha que a do Sehe, com algumas mudanças que, esperamos, abordarão seus comentários:

  • Esta solução considera a mesclagem por sentença ao invés de palavra, como você estava fazendo manualmente, somente agora, o usuário verá uma única linha por parágrafo, mas o git verá parágrafos quebrados em sentenças. Isso parece ser mais lógico porque adicionar / remover uma sentença de um parágrafo pode ser compatível com outras alterações no parágrafo, mas é provavelmente mais desejável ter uma mesclagem manual quando a mesma sentença é editada por duas confirmações. Isso também tem o benefício dos instantâneos “limpos” para ainda serem um pouco legíveis para humanos (e latex compilable!).
  • Os filtros são comandos de uma linha, o que deve facilitar a portabilidade para os colaboradores.

Como na solução de saha, faça (ou acrescente).

  *.tex filter=sentencebreak 

Agora, para implementar os filtros limpos e borrados:

  git config filter.sentencebreak.clean "perl -pe \"s/[.]*?(\\?|\\!|\\.|'') /$&%NL%\\n/g unless m/%/||m/^[\\ *\\\\\\]/\"" git config filter.sentencebreak.smudge "perl -pe \"s/%NL%\n//gm\"" 

Eu criei um arquivo de teste com o seguinte conteúdo, observe o parágrafo de uma linha.

  \chapter{Tumbling Tumbleweeds. Intro} A way out west there was a fella, fella I want to tell you about, fella by the name of Jeff Lebowski. At least, that was the handle his lovin' parents gave him, but he never had much use for it himself. This Lebowski, he called himself the Dude. Now, Dude, that's a name no one would self-apply where I come from. But then, there was a lot about the Dude that didn't make a whole lot of sense to me. And a lot about where he lived, like- wise. But then again, maybe that's why I found the place s'durned innarestin'. This line has two sentences. But it also ends with a comment. % here 

Depois de nos comprometermos com o repository local, podemos ver o conteúdo bruto.

  $ git show HEAD:test.tex \chapter{Tumbling Tumbleweeds. Intro} A way out west there was a fella, fella I want to tell you about, fella by the name of Jeff Lebowski. %NL% At least, that was the handle his lovin' parents gave him, but he never had much use for it himself. %NL% This Lebowski, he called himself the Dude. %NL% Now, Dude, that's a name no one would self-apply where I come from. %NL% But then, there was a lot about the Dude that didn't make a whole lot of sense to me. %NL% And a lot about where he lived, like- wise. %NL% But then again, maybe that's why I found the place s'durned innarestin'. This line has two sentences. But it also ends with a comment. % here 

Assim, as regras do filtro limpo são sempre que ele encontra uma sequência de texto que termina com . ou ? ou ! ou '' (essa é a maneira do látex de fazer aspas duplas) então um espaço, ele irá adicionar% NL% e um caractere de nova linha. Mas ignora as linhas que começam com \ (comandos latex) ou contêm um comentário em qualquer lugar (para que os comentários não possam se tornar parte do texto principal).

O filtro de manchas remove% NL% e a nova linha.

Difundir e mesclar é feito nos arquivos ‘limpos’, portanto as mudanças nos parágrafos são mescladas frase a frase. Esse é o comportamento desejado.

O bom é que o arquivo de látex deve ser compilado no estado limpo ou borrado, então há alguma esperança de que os colaboradores não precisem fazer nada. Finalmente, você poderia colocar os comandos git config em um shell script que faz parte do repo para que um colaborador tenha que executá-lo na raiz do repository para ser configurado.

  #!/bin/bash git config filter.sentencebreak.clean "perl -pe \"s/[.]*?(\\?|\\!|\\.|'') /$&%NL%\\n/g unless m/%/||m/^[\\ *\\\\\\]/\"" git config filter.sentencebreak.smudge "perl -pe \"s/%NL%\n//gm\"" fileArray=($(find . -iname "*.tex")) for (( i=0; i<${#fileArray[@]}; i++ )); do perl -pe "s/%NL%\n//gm" < ${fileArray[$i]} > temp mv temp ${fileArray[$i]} done 

Essa última parte é um hack porque quando este script é executado pela primeira vez, o branch já está com check-out (na forma limpa) e não fica borrado automaticamente.

Você pode adicionar este script e o arquivo .gitattributes ao repo, então os novos usuários só precisam clonar e executar o script na raiz do repository.

Eu acho que este script ainda roda no windows git se for feito no git bash.

Desvantagens:

  • Isso não manipula linhas com comentários de maneira inteligente, apenas as ignora.
  • % NL% é meio feio
  • Os filtros podem estragar algumas equações (não tenho certeza sobre isso).

Você poderia tentar isso:

em vez de trocar um mecanismo de mesclagem ( hard ), você pode fazer algum tipo de ‘normalização’ (canonicalização, se quiser). Eu não falo Latex, mas deixe-me ilustrar da seguinte forma:

Digamos que você tenha input como test.raw

 curve ball well received {misfit} whatever proprietary format extinction {benefit}. 

Você quer que ele faça diff / merge palavra por palavra. Adicione o seguinte arquivo .gitattributes

 *.raw filter=wordbyword 

Então

 git config --global filter.wordbyword.clean /home/username/bin/wordbyword.clean git config --global filter.wordbyword.smudge /home/username/bin/wordbyword.smudge 

Uma implementação minimalista dos filtros seria

/home/username/bin/wordbyword.clean

 #!/usr/bin/perl use strict; use warnings; while (<>) { print "$_\n" foreach (m/(.*?\s+)/go); print '#@#DELIM#@#' . "\n"; } 

/home/username/bin/wordbyword.smudge

 #!/usr/bin/perl use strict; use warnings; while (<>) { chomp; '#@#DELIM#@#' eq $_ and print "\n" or print; } 

Depois de confirmar o arquivo, inspecione o conteúdo bruto do blob confirmado com `git show

 HEAD:test.raw`: curve ball well received {misfit} whatever #@#DELIM#@# proprietary format extinction {benefit}. #@#DELIM#@# 

Depois de alterar o conteúdo de test.raw para

 curve ball welled repreived {misfit} whatever proprietary extinction format {benefit}. 

A saída do git diff --patch-with-stat provavelmente será o que você queria:

  test.raw | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test.raw b/test.raw index b0b0b88..ed8c393 100644 --- a/test.raw +++ b/test.raw @@ -1,14 +1,14 @@ curve ball -well -received +welled +repreived {misfit} whatever #@#DELIM#@# proprietary -format extinction +format {benefit}. #@#DELIM#@# 

Você pode ver como isso funcionaria magicamente para mesclagens, resultando em uma combinação de palavras por palavra. QED

( Espero que você goste do meu uso criativo de atributos .git. Se não, eu gostei de fazer este pequeno exercício )

Acredito que o algoritmo de git merge do git merge é bastante simples (mesmo que você possa fazer com que ele trabalhe mais com a estratégia de mesclagem “paciência”).
Seu item de trabalho permanecerá na linha.

Mas a idéia geral é delegar qualquer mecanismo de resolução de detecção refinado para uma ferramenta de terceiros que você possa configurar com o git config mergetool .
Se algumas palavras dentro de uma linha longa diferirem, essa ferramenta externa ( KDiff3 , DiffMerge , …) será capaz de pegar essa mudança e apresentá-la a você.