Posso fazer o git reconhecer um arquivo UTF-16 como texto?

140

Estou rastreando um arquivo de máquina virtual do Virtual PC (* .vmc) no git e, depois de fazer uma alteração, o git identificou o arquivo como binário e não o difere para mim. Descobri que o arquivo estava codificado em UTF-16.

O git pode ser ensinado a reconhecer que esse arquivo é texto e manipulá-lo adequadamente?

Estou usando o git no Cygwin, com core.autocrlf definido como false. Eu poderia usar mSysGit ou git no UNIX, se necessário.

skiphoppy
fonte

Respostas:

83

Estou lutando com esse problema há um tempo e acabei de descobrir (para mim) uma solução perfeita:

$ git config --global diff.tool vimdiff      # or merge.tool to get merging too!
$ git difftool commit1 commit2

git difftoolusa os mesmos argumentos que o git difffaria, mas executa um programa diff de sua escolha em vez do GNU embutido diff. Portanto, escolha um diff com reconhecimento de multibyte (no meu caso, vimno modo diff) e use apenas em git difftoolvez de git diff.

Encontre "difftool" muito tempo para digitar? Sem problemas:

$ git config --global alias.dt difftool
$ git dt commit1 commit2

Git rochas.

Sam Stokes
fonte
1
Não é uma solução perfeita (preferiria um diff unificado de rolagem), MAS, é o mal menor, dadas as opções e minha falta de vontade de encontrar algo novo para instalar. "vimdiff", é! (sim, vim ... e git)
Roboprog
1
Isso também funciona para preparar e confirmar apenas blocos de arquivos UTF16?
Ortwin Gentz
Eu uso o Beyond Compare como uma ferramenta de comparação e mesclagem. De .gitconfig <pre> <code> [difftool "bc3"] caminho = c: / Arquivos de programas (x86) / Além da comparação 3 / bcomp.exe [mergetool "bc3"] caminho = c: / Arquivos de programas (x86) / Além da comparação 3 / bcomp.exe </code> </pre>
Tom Wilson
@ Tom Wilson Desculpe por não conseguir formatar o bloco de código recuando 4 espaços !?
Tom Wilson
Eu tenho conhecimentos básicos para o git e não tenho certeza de como ele lida com alterações de arquivos. Isso sempre é como arquivos binários ou para texto (ASCII) existe processamento / detecção especial de alterações?
I486
63

Existe uma solução muito simples que funciona imediatamente no Unices.

Por exemplo, com os .stringsarquivos da Apple apenas:

  1. Crie um .gitattributesarquivo na raiz do seu repositório com:

    *.strings diff=localizablestrings
    
  2. Adicione o seguinte ao seu ~/.gitconfigarquivo:

    [diff "localizablestrings"]
    textconv = "iconv -f utf-16 -t utf-8"
    

Fonte: arquivos Diff .strings no Git (e post anterior de 2010).

IlDan
fonte
Eu fiz isso, mas o git se recusa a correr atrás disso. O erro que recebo é "linha 4 do arquivo de configuração incorreta em /Users/myusername/.gitconfig". Eu usei "git config --global --edit" para abrir meu arquivo gitconfig. Curiosamente, se eu remover as linhas adicionadas, tudo funcionará bem. Alguma pista?
shshnk
Vou adivinhar as aspas inteligentes se você copiar / colar. Eu editei a resposta para corrigir isso.
Lou Franco
Isso funciona como um encanto, deve ser a resposta aceita por uma questão de simplicidade e por uma melhor integração. Não vejo como "usar outra ferramenta" possa ser a resposta para "Posso fazer o git reconhecer um arquivo UTF-16 como texto?"
itMaxence
@itMaxence Estritamente, iconvé "outra ferramenta" da mesma maneira que o Vim ou Beyond Compare (não faz parte do pacote git).
Agi Hammerthief
@AgiHammerthief com certeza depois de ler novamente, eu concordo, não sei o que estava pensando. FWIW vimdiffe iconvsão ambos já presente no MacOS, assim você não precisa se preocupar perguntando onde consegui-los, e eles fazem o trabalho
itMaxence
39

Você já tentou definir o seu .gitattributestratamento como um arquivo de texto?

por exemplo:

*.vmc diff

Mais detalhes em http://www.git-scm.com/docs/gitattributes.html .

Chealion
fonte
2
Isso funciona, mas, para correção, lembre-se de que isso define dois atributos: sete diff...
OK.
2
Esta solução é a única aceitável para mim. Como por comentário @OK, o "set" é irrelevante aqui, apenas *.vmc diff, *.sql diffetc .. é necessário para definir o atributo 'diff' para o caminho especificado. (Não consigo editar a resposta). No entanto, existem duas ressalvas: as diferenças são mostradas com um espaço entre cada caractere e não é possível "encenar o pedaço" ou "descartar o pedaço" para esses arquivos problemáticos.
Pac0
30

Por padrão, parece que gitnão funcionará bem com UTF-16; para esse arquivo, você precisa garantir que nenhum CRLFprocessamento seja feito, mas deseja diffe mergefuncione como um arquivo de texto normal (isso é ignorar se o seu terminal / editor pode ou não lidar com UTF-16).

Mas, olhando para a página de .gitattributesmanual , aqui está o atributo personalizado que é binary:

[attr]binary -diff -crlf

Portanto, parece-me que você pode definir um atributo personalizado em seu nível superior .gitattributespara utf16(observe que eu adiciono mesclagem aqui para garantir que seja tratado como texto):

[attr]utf16 diff merge -crlf

A partir daí, você poderá especificar em qualquer .gitattributesarquivo algo como:

*.vmc utf16

Observe também que você ainda pode conseguir diffum arquivo, mesmo que gitpense que ele é binário com:

git diff --text

Editar

Essa resposta basicamente diz que o GNU diff com UTF-16 ou mesmo UTF-8 não funciona muito bem. Se você quiser gitusar uma ferramenta diferente para ver as diferenças (via --ext-diff), essa resposta sugere Guiffy .

Mas o que você provavelmente precisa é apenas para diffum arquivo UTF-16 que contenha apenas caracteres ASCII. Uma maneira de fazer isso funcionar é usar --ext-diffo seguinte script de shell:

#!/bin/bash
diff <(iconv -f utf-16 -t utf-8 "$1") <(iconv -f utf-16 -t utf-8 "$2")

Observe que a conversão para UTF-8 também pode funcionar para mesclagem, você só precisa garantir que isso seja feito nas duas direções.

Quanto à saída para o terminal ao examinar um diff de um arquivo UTF-16:

Tentar diferir dessa maneira resulta em lixo binário espalhado na tela. Se o git estiver usando o GNU diff, parece que o GNU diff não é compatível com unicode.

O diff do GNU realmente não se importa com o unicode, portanto, quando você usa o diff --text, ele apenas difere e gera o texto. O problema é que o terminal que você está usando não suporta o UTF-16 emitido (combinado com as marcas diff que são caracteres ASCII).

Jared Oberhaus
fonte
Tentar diferir dessa maneira resulta em lixo binário espalhado na tela. Se o git estiver usando o GNU diff, parece que o GNU diff não é compatível com unicode.
Skiphoppy
1
O diff do GNU realmente não se importa com o unicode, portanto, quando você usa o diff --text, ele apenas difere e gera o texto. O problema é que o terminal que você está usando não suporta o UTF-16 emitido (combinado com as marcas diff que são caracteres ASCII).
Jared Oberhaus
@ jared-oberhaus - existe uma maneira de acionar esse script apenas para certos tipos de arquivos (ou seja, dada extensão)?
Terry
8

A solução é filtrar cmd.exe /c "type %1". O typebuiltin do cmd fará a conversão e, portanto, você pode usá-lo com a capacidade de conversão de texto do git diff para ativar a difusão de texto dos arquivos UTF-16 (deve funcionar com o UTF-8 também, embora não tenha sido testado).

Citando a página de manual gitattributes:


Executando diferenças de texto de arquivos binários

Às vezes, é desejável ver o diff de uma versão convertida em texto de alguns arquivos binários. Por exemplo, um documento do processador de texto pode ser convertido em uma representação de texto ASCII e no diff do texto mostrado. Mesmo que essa conversão perca algumas informações, o diff resultante é útil para visualização humana (mas não pode ser aplicado diretamente).

A opção de configuração textconv é usada para definir um programa para realizar essa conversão. O programa deve usar um único argumento, o nome de um arquivo a ser convertido e produzir o texto resultante no stdout.

Por exemplo, para mostrar o diff das informações exif de um arquivo em vez das informações binárias (supondo que você tenha a ferramenta exif instalada), adicione a seguinte seção ao seu $GIT_DIR/configarquivo (ou $HOME/.gitconfigarquivo):

[diff "jpg"]
        textconv = exif

Uma solução para os fãs do mingw32 , cygwin, pode ter que alterar a abordagem. O problema é passar o nome do arquivo para converter em cmd.exe - ele estará usando barras invertidas e o cmd assume separadores de diretório de barra invertida.

Passo 1:

Crie o script de argumento único que fará a conversão em stdout. c: \ caminho \ para \ algum \ script.sh:

#!/bin/bash
SED='s/\//\\\\\\\\/g'
FILE=\`echo $1 | sed -e "$SED"\`
cmd.exe /c "type $FILE"

Passo 2:

Configure o git para poder usar o arquivo de script. Dentro de sua git config ( ~/.gitconfigou .git/configou ver man git-config), coloque isso:

[diff "cmdtype"]
textconv = c:/path/to/some/script.sh

Etapa 3:

Aponte os arquivos aos quais aplicar esta solução alternativa utilizando arquivos .gitattributes (consulte man gitattributes (5)):

*vmc diff=cmdtype

depois use git diffem seus arquivos.

Gilles 'SO- parar de ser mau'
fonte
Quase como o de Tony Kuneck, mas sem "c: /path/to/some/script.sh
Alexey Shumkin
Eu tenho algum problema com o script, como mostrado acima com o Git para Windows, mas eu achei o seguinte é bom e também pode lidar com espaços no caminho: cmd //c type "${1//\//\\}" .
patthoyts
Isso funcionará sem a necessidade de criar um arquivo de script:textconv = powershell -NoProfile -Command \"& {Get-Content \\$args[0]}\"
Jakub Berezanski
5

O git começou recentemente a entender codificações como utf16. Consulte os documentos gitattributes , pesquise porworking-tree-encoding

[Verifique se a sua página de manual corresponde, pois é uma novidade!]

Se (digamos) o arquivo for UTF-16 sem BOM na máquina Windows, adicione-o ao .gitattributesarquivo

*.vmc text working-tree-encoding=UTF-16LE eol=CRLF

Se UTF-16 (com bom) em * nix, faça-o:

*.vmc text working-tree-encoding=UTF-16-BOM eol=LF

(Substitua *.vmccom *.whateverpara whateverarquivos do tipo que você precisa para lidar com)

Consulte: Suporte à codificação de árvore de trabalho "UTF-16LE-BOM" .


Adicionado mais tarde

Seguindo @Hackslash, pode-se achar que isso é insuficiente

 *.vmc text working-tree... 

Para obter boas diferenças de texto, você precisa

 *.vmc diff working-tree...

Colocando ambos os trabalhos também

 *.vmc text diff working-tree... 

Mas é sem dúvida

  • Redundante - eol=...implicatext
  • Detalhado - um projeto grande pode facilmente ter dezenas de tipos diferentes de arquivos de texto

O problema

Git tem um atributo macro, o binary que significa -text -diff. O oposto +text +diffnão está disponível, mas o git fornece as ferramentas (eu acho!) Para sintetizá-lo

A solução

O Git permite definir novos atributos de macro.

Eu proporia que o topo do .gitattributesarquivo que você tem

 [attr]textfile text diff

Então, para todos os caminhos que precisam ser texto e diff

 path textfile working-tree-encoding= eol=...

Observe que, na maioria dos casos, queremos a codificação padrão (utf-8) e o eol padrão (nativo), podendo ser descartados.

A maioria das linhas deve parecer

textfile *.c
textfile *.py
Etc

Por que não usar apenas diff?

Prático: Na maioria dos casos, queremos eol nativo. O que significa que não eol=.... Portanto text, não será implícito e precisa ser explicitamente explicado.

Conceitual: Texto versus binário é a distinção fundamental. eol, codificação, diff etc são apenas alguns aspectos.

aviso Legal

Devido aos tempos bizarros em que vivemos, não tenho uma máquina com um git atual. No momento, não consigo verificar a última adição. Se alguém encontrar algo errado, eu o emendarei / removerei.

Rusi
fonte
Para que meu arquivo UTF-16LE-BOM funcionasse, eu tive que usar*.vmc diff working-tree-encoding=UTF-16LE-BOM eol=CRLF
HackSlash 20/07
@HackSlash: Obrigado pelo aviso. Eu acho que você está dizendo que, textsozinho, você não obteve boas diferenças de texto? Você pode verificar isso com ambos text e difftudo funciona bem? Nesse caso, farei uma recomendação diferente
Rusi
Correto, textsozinho , resulta em comparação binária. Eu posso fazer diffou text diffe funciona. Eu precisava adicionar -BOMsimplesmente porque meu arquivo tinha uma BOM, YMMV.
HackSlash
@HackSlash Incorporei sua descoberta. Seria ótimo se você pudesse conferir!
Rusi
Obrigado @Rusi, faz sentido para mim.
HackSlash
4

Eu escrevi um pequeno driver git-diff to-utf8, que deve facilitar a difusão de arquivos codificados que não sejam ASCII / UTF-8. Você pode instalá-lo usando as instruções aqui: https://github.com/chaitanyagupta/gitutils#to-utf8 (o to-utf8script está disponível no mesmo repositório).

Note que esse script exige tanto filee iconvcomandos para estar disponível no sistema.

Chaitanya Gupta
fonte
2

Recentemente, tive esse problema no Windows e as caixas dos2unixe unix2dosfornecidas com o git for windows fizeram o truque. Por padrão, eles estão localizados C:\Program Files\Git\usr\bin\. Observe que isso só funcionará se seu arquivo não precisar ser UTF-16. Por exemplo, alguém acidentalmente codificou um arquivo python como UTF-16 quando não precisava (no meu caso).

PS C:\Users\xxx> dos2unix my_file.py
dos2unix: converting UTF-16LE file my_file.py to ANSI_X3.4-1968 Unix format...

e

PS C:\Users\xxx> unix2dos my_file.py
unix2dos: converting UTF-16LE file my_file.py to ANSI_X3.4-1968 DOS format...
Matt Messersmith
fonte