¿Puedo hacer que git reconozca un archivo UTF-16 como texto?

140

Estoy rastreando un archivo de máquina virtual de PC virtual (* .vmc) en git, y después de hacer un cambio, git identificó el archivo como binario y no me diferenció. Descubrí que el archivo estaba codificado en UTF-16.

¿Se puede enseñar a git a reconocer que este archivo es texto y manejarlo adecuadamente?

Estoy usando git en Cygwin, con core.autocrlf establecido en falso. Podría usar mSysGit o git en UNIX, si es necesario.

skiphoppy
fuente

Respuestas:

83

He estado luchando con este problema por un tiempo, y acabo de descubrir (para mí) una solución perfecta:

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

git difftooltoma los mismos argumentos que lo git diffharía, pero ejecuta un programa diff de su elección en lugar del GNU incorporado diff. Por lo tanto, elija un diff multibyte-aware (en mi caso, vimen modo diff) y simplemente use en git difftoollugar degit diff .

¿Encuentra "difftool" demasiado largo para escribir? No hay problema:

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

Git rocas.

Sam Stokes
fuente
1
No es una solución perfecta (preferiría tener un diff unificado de desplazamiento), PERO, es el mal menor dadas las opciones y mi falta de voluntad para encontrar algo nuevo para instalar. "vimdiff", lo es! (sí, vim ... y git)
Roboprog
1
¿Esto también funciona para organizar y comprometer solo fragmentos de archivos UTF16?
Ortwin Gentz
Yo uso Beyond Compare como una herramienta de diferencias y fusión. Desde .gitconfig <pre> <code> [difftool "bc3"] ruta = c: / Archivos de programa (x86) / Beyond Compare 3 / bcomp.exe [mergetool "bc3"] ruta = c: / Archivos de programa (x86) / Beyond Compare 3 / bcomp.exe </code> </pre>
Tom Wilson
@Tom Wilson Lo sentimos, no se puede formatear el bloque de código al sangrar 4 espacios.
Tom Wilson
Tengo conocimientos básicos para git y no estoy seguro de cómo maneja los cambios de archivo. ¿Es esto siempre como archivos binarios o para texto (ASCII) hay un procesamiento / detección especial de cambios?
i486
63

Hay una solución muy simple que funciona de forma inmediata en Unices.

Por ejemplo, con los .stringsarchivos de Apple solo:

  1. Cree un .gitattributesarchivo en la raíz de su repositorio con:

    *.strings diff=localizablestrings
    
  2. Agregue lo siguiente a su ~/.gitconfigarchivo:

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

Fuente: archivos Diff .strings en Git (y una publicación anterior de 2010).

IlDan
fuente
Hice esto pero git se niega a correr después de esto. El error que obtengo es "línea de archivo de configuración incorrecta 4 en /Users/myusername/.gitconfig". Usé "git config --global --edit" para abrir mi archivo gitconfig. Curiosamente, si elimino las líneas agregadas, todo funciona bien. Alguna pista ?
shshnk
Voy a adivinar las citas inteligentes si copia / pega. Edité la respuesta para arreglar eso.
Lou Franco
Esto funciona de maravilla, debería ser la respuesta aceptada en aras de la simplicidad y para una mejor integración. No veo cómo "usar otra herramienta" puede ser la respuesta a "¿Puedo hacer que git reconozca un archivo UTF-16 como texto?"
itMaxence
@itMaxence Estrictamente, iconves "otra herramienta" de la misma manera que lo es Vim o Beyond Compare (no es parte del paquete git).
Agi Hammerthief
@AgiHammerthief seguro después de leer de nuevo, estoy de acuerdo, no sé en qué estaba pensando. FWIW vimdiffy iconvambos ya están presentes en macOS, por lo que no necesita molestarse en preguntarse dónde conseguirlos, y hacen el trabajo
itMaxence
39

¿Has intentado configurar tu .gitattributes para tratarlo como un archivo de texto?

p.ej:

*.vmc diff

Más detalles en http://www.git-scm.com/docs/gitattributes.html .

Chealion
fuente
2
Esto funciona, pero para ser correcto, tenga en cuenta que esto establece dos atributos: sety diff...
OK.
2
Esta solución es la única aceptable para mí. Según el comentario de @OK, el "conjunto" es irrelevante aquí, solo *.vmc diff, *.sql diffetc. se necesita para establecer el atributo 'diff' para la ruta especificada. (No puedo editar la respuesta). Sin embargo, hay dos advertencias: las diferencias se muestran con un espacio entre cada personaje y no es posible "poner en escena" o "descartar trozo" para esos archivos problemáticos.
Pac0
30

Por defecto, parece gitque no funcionará bien con UTF-16; para dicho archivo, debe asegurarse de que no CRLFse realice ningún procesamiento en él, pero desea diffymerge trabajar como un archivo de texto normal (esto ignora si su terminal / editor puede manejar UTF-16 o no).

Pero mirando la página de .gitattributesmanual , aquí está el atributo personalizado que es binary:

[attr]binary -diff -crlf

Entonces, me parece que podría definir un atributo personalizado en su nivel superior .gitattributespara utf16(tenga en cuenta que agrego fusionar aquí para asegurarme de que se trate como texto):

[attr]utf16 diff merge -crlf

Desde allí, podría especificar en cualquier .gitattributesarchivo algo como:

*.vmc utf16

También tenga en cuenta que aún debe poder crear diffun archivo, incluso si gitcree que es binario con:

git diff --text

Editar

Esta respuesta básicamente dice que la diferencia de GNU con UTF-16 o incluso UTF-8 no funciona muy bien. Si desea gitutilizar una herramienta diferente para ver las diferencias (vía --ext-diff), esa respuesta sugiere Guiffy .

Pero lo que probablemente necesite es solo diffun archivo UTF-16 que contenga solo caracteres ASCII. Una forma de hacer que eso funcione es usar --ext-diffel siguiente script de shell:

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

Tenga en cuenta que la conversión a UTF-8 también podría funcionar para la fusión, solo debe asegurarse de que se realice en ambas direcciones.

En cuanto a la salida al terminal cuando se mira una diferencia de un archivo UTF-16:

Intentar diferir así da como resultado basura binaria arrojada a la pantalla. Si git usa GNU diff, parecería que GNU diff no es compatible con Unicode.

GNU diff realmente no se preocupa por unicode, por lo que cuando usa diff --text solo difiere y genera el texto. El problema es que el terminal que está utilizando no puede manejar el UTF-16 que se emite (combinado con las marcas de diferencias que son caracteres ASCII).

Jared Oberhaus
fuente
Intentar diferir así da como resultado basura binaria arrojada a la pantalla. Si git usa GNU diff, parecería que GNU diff no es compatible con Unicode.
skiphoppy
1
GNU diff realmente no se preocupa por unicode, por lo que cuando usa diff --text solo difiere y genera el texto. El problema es que el terminal que está utilizando no puede manejar el UTF-16 que se emite (combinado con las marcas de diferencias que son caracteres ASCII).
Jared Oberhaus
@ jared-oberhaus: ¿hay alguna forma de activar este script solo para ciertos tipos de archivos (es decir, dada cierta extensión)?
Terry
8

La solución es filtrar a través cmd.exe /c "type %1". cmd'stype builtin realizará la conversión, por lo que puede usar eso con la capacidad textconv de git diff para habilitar la diferenciación de texto de los archivos UTF-16 (también debería funcionar con UTF-8, aunque no probado).

Citando de la página del manual de gitattributes:


Realizar diferencias de texto de archivos binarios

A veces es deseable ver la diferencia de una versión convertida por texto de algunos archivos binarios. Por ejemplo, un documento de procesador de textos se puede convertir a una representación de texto ASCII y mostrar la diferencia del texto. Aunque esta conversión pierde algo de información, la diferencia resultante es útil para la visualización humana (pero no se puede aplicar directamente).

La opción de configuración textconv se utiliza para definir un programa para realizar dicha conversión. El programa debe tomar un solo argumento, el nombre de un archivo para convertir, y producir el texto resultante en stdout.

Por ejemplo, para mostrar la diferencia de la información exif de un archivo en lugar de la información binaria (suponiendo que tenga instalada la herramienta exif), agregue la siguiente sección a su $GIT_DIR/configarchivo (o $HOME/.gitconfigarchivo):

[diff "jpg"]
        textconv = exif

Una solución para mingw32 , los fanáticos de pueden tener que alterar el enfoque. El problema es pasar el nombre del archivo para convertirlo a cmd.exe: utilizará barras diagonales y cmd asume separadores de directorio de barra invertida.

Paso 1:

Cree el script de argumento único que realizará la conversión a stdout. c: \ ruta \ a \ algunos \ script.sh:

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

Paso 2:

Configure git para poder usar el archivo de script. Dentro de su git config ( ~/.gitconfigo .git/configni ver man git-config), poner esto:

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

Paso 3:

Señale los archivos a los que se debe aplicar este trabajo utilizando archivos .gitattributes (consulte man gitattributes (5)):

*vmc diff=cmdtype

luego úselo git diffen sus archivos.

Gilles 'SO- deja de ser malvado'
fuente
Casi como Tony Kuneck pero sin "c: /path/to/some/script.sh" entropy.ch/blog/Developer/2010/04/15/…
Alexey Shumkin
Tengo un problema con el script como se ha mostrado anteriormente con Git para Windows, pero me encontré con la siguiente es fina y también se puede tratar con espacios en la ruta: cmd //c type "${1//\//\\}" .
Patthoyts
Esto funcionará sin la necesidad de crear un archivo de script:textconv = powershell -NoProfile -Command \"& {Get-Content \\$args[0]}\"
Jakub Berezanski
5

git recientemente ha comenzado a comprender codificaciones como utf16. Ver gitattributes docs, buscarworking-tree-encoding

[¡Asegúrese de que su página de manual coincida ya que esto es bastante nuevo!]

Si (por ejemplo) el archivo es UTF-16 sin BOM en la máquina Windows, agréguelo a su .gitattributesarchivo

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

Si UTF-16 (con bom) en * nix, hágalo:

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

(Reemplazar *.vmccon *.whateverpara whateverlos ficheros del tipo que necesita para manejar)

Consulte: Soporte de codificación de árbol de trabajo "UTF-16LE-BOM" .


Agregado luego

Después de @Hackslash, uno puede encontrar que esto es insuficiente

 *.vmc text working-tree... 

Para obtener buenas diferencias de texto necesitas

 *.vmc diff working-tree...

Poniendo ambas obras también

 *.vmc text diff working-tree... 

Pero es discutible

  • Redundante - eol=...implicatext
  • Detallado: un proyecto grande podría tener fácilmente docenas de diferentes tipos de archivos de texto

El problema

Git tiene un macro-atributo binary que significa -text -diff. Lo contrario +text +diffno está disponible incorporado, pero git proporciona las herramientas (¡creo!) Para sintetizarlo

La solución

Git le permite a uno definir nuevos atributos de macro.

Propondría que la parte superior de la .gitattributes archivo que tienes

 [attr]textfile text diff

Luego, para todas las rutas que deben ser text y diff do

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

Tenga en cuenta que en la mayoría de los casos nos gustaría la codificación predeterminada (utf-8) y la eol predeterminada (nativa) y así lo se pueden eliminar.

La mayoría de las líneas deberían verse como

textfile *.c
textfile *.py
Etc

¿Por qué no solo usar diff?

Práctico: en la mayoría de los casos queremos eol nativo. Lo que significa que noeol=... . Por textlo tanto , no se implicará y debe expresarse explícitamente.

Conceptual: el texto V binario es la distinción fundamental. eol, codificación, diff, etc. son solo algunos aspectos.

Descargo de responsabilidad

Debido a los tiempos extraños en que vivimos, no tengo una máquina con un git de trabajo actual. Por lo tanto, en este momento no puedo verificar la última incorporación. Si alguien encuentra algo mal, lo enmendaré / eliminaré.

Rusi
fuente
Para que mi archivo UTF-16LE-BOM funcione, tuve que usar*.vmc diff working-tree-encoding=UTF-16LE-BOM eol=CRLF
HackSlash
@HackSlash: Gracias por el aviso. ¿Supongo que estás diciendo textsolo que no obtuviste buenas diferencias de texto? ¿Puedes comprobar eso con ambos text y difftodo funciona bien? En cuyo caso haré una recomendación diferente
Rusi
Correcto, textsolo da como resultado una comparación binaria. Puedo hacer diffo text diffy funciona. Necesitaba agregar -BOMsimplemente porque mi archivo tenía una lista de materiales, YMMV.
HackSlash
@HackSlash He incorporado tu hallazgo. ¡Sería genial si pudieras echarle un vistazo!
Rusi hace
Gracias @Rusi, tiene sentido para mí.
HackSlash
4

He escrito un pequeño controlador git-diff to-utf8, que debería facilitar la difusión de cualquier archivo codificado que no sea ASCII / UTF-8. Puede instalarlo siguiendo las instrucciones aquí: https://github.com/chaitanyagupta/gitutils#to-utf8 (elto-utf8 script está disponible en el mismo repositorio).

Tenga en cuenta que este script requiere que tanto los comandos filecomo los iconvcomandos estén disponibles en el sistema.

Chaitanya Gupta
fuente
2

Tenido este problema en Windows recientemente, y las dos2unixy unix2doslos contenedores que se suministran con git para ventanas resolvieron el problema. Por defecto están ubicados en C:\Program Files\Git\usr\bin\. Observe que esto solo funcionará si su archivo no necesita ser UTF-16. Por ejemplo, alguien codificó accidentalmente un archivo de Python como UTF-16 cuando no era necesario (en mi caso).

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

y

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
fuente