Puis-je faire en sorte que git reconnaisse un fichier UTF-16 en tant que texte?

140

Je suis le suivi d'un fichier de machine virtuelle Virtual PC (* .vmc) dans git, et après avoir apporté une modification, git a identifié le fichier comme binaire et ne le différait pas pour moi. J'ai découvert que le fichier était encodé en UTF-16.

Peut-on apprendre à git à reconnaître que ce fichier est du texte et à le gérer de manière appropriée?

J'utilise git sous Cygwin, avec core.autocrlf défini sur false. Je pourrais utiliser mSysGit ou git sous UNIX, si nécessaire.

sauter
la source

Réponses:

83

Je lutte avec ce problème depuis un moment et je viens de découvrir (pour moi) une solution parfaite:

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

git difftoolprend les mêmes arguments que le git diffferait, mais exécute un programme diff de votre choix au lieu du GNU intégré diff. Choisissez donc un diff prenant en charge plusieurs octets (dans mon cas, vimen mode diff) et utilisez simplement à la git difftoolplace de git diff.

Trouver "difftool" trop long pour taper? Aucun problème:

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

Git est génial.

Sam Stokes
la source
1
Pas une solution parfaite (préférerait avoir un diff unifié à défilement), MAIS, c'est le moindre mal étant donné les choix et ma réticence à trouver quelque chose de nouveau à installer. "vimdiff", ça l'est! (yea, vim ... and git)
Roboprog
1
Cela fonctionne-t-il également pour organiser et ne valider que des morceaux de fichiers UTF16?
Ortwin Gentz
J'utilise Beyond Compare comme un outil de comparaison et de fusion. Depuis .gitconfig <pre> <code> [difftool "bc3"] path = c: / Program Files (x86) / Beyond Compare 3 / bcomp.exe [mergetool "bc3"] path = c: / Program Files (x86) / Beyond Compare 3 / bcomp.exe </code> </pre>
Tom Wilson
@Tom Wilson Désolé impossible de formater le bloc de code en indentant 4 espaces !?
Tom Wilson
J'ai des connaissances de base pour git et je ne sais pas comment il gère les changements de fichiers. S'agit-il toujours de fichiers binaires ou pour le texte (ASCII), il y a un traitement / détection spécial des changements?
i486
63

Il existe une solution très simple qui fonctionne hors de la boîte sur Unices.

Par exemple, avec les .stringsfichiers Apple simplement:

  1. Créez un .gitattributesfichier à la racine de votre référentiel avec:

    *.strings diff=localizablestrings
    
  2. Ajoutez ce qui suit à votre ~/.gitconfigfichier:

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

Source: fichiers Diff .strings dans Git (et ancien post de 2010).

IlDan
la source
Je l'ai fait mais git refuse de courir après ça. L'erreur que j'obtiens est "mauvaise ligne de fichier de configuration 4 dans /Users/myusername/.gitconfig". J'ai utilisé "git config --global --edit" pour ouvrir mon fichier gitconfig. Fait intéressant, si je supprime les lignes ajoutées, tout fonctionne bien. Des indices?
shshnk
Je vais deviner les citations intelligentes si vous copiez / collez. J'ai modifié la réponse pour résoudre ce problème.
Lou Franco
Cela fonctionne comme un charme, ce devrait être la réponse acceptée par souci de simplicité et pour une meilleure intégration. Je ne vois pas comment "utiliser un autre outil" peut être la réponse à "Puis-je faire en sorte que git reconnaisse un fichier UTF-16 en tant que texte?"
itMaxence
@itMaxence Strictly, iconvest "un autre outil" de la même manière que Vim ou Beyond Compare (ne fait pas partie de la suite git).
Agi Hammerthief
@AgiHammerthief bien sûr après avoir lu à nouveau, je suis d'accord, je ne sais pas à quoi je pensais. FWIW vimdiffet iconvsont tous deux déjà présents sur macOS, vous n'avez donc pas besoin de vous demander où les obtenir, et ils font le travail
itMaxence
39

Avez-vous essayé de configurer votre .gitattributespour le traiter comme un fichier texte?

par exemple:

*.vmc diff

Plus de détails sur http://www.git-scm.com/docs/gitattributes.html .

Chealion
la source
2
Cela fonctionne, mais par souci d'exactitude, sachez que cela définit deux attributs: setet diff...
OK.
2
Cette solution est la seule acceptable pour moi. Comme pour le commentaire @OK, le "set" n'est pas pertinent ici, juste *.vmc diff, *.sql diffetc. est nécessaire pour définir l'attribut 'diff' pour le chemin spécifié. (Je ne peux pas modifier la réponse). 2 mises en garde cependant: les diffs sont affichés avec un espace entre chaque caractère, et il n'est pas possible de "mettre en scène un morceau" ou de "supprimer un morceau" pour ces fichiers problématiques.
Pac0
30

Par défaut, il semble gitne pas fonctionner correctement avec UTF-16; pour un tel fichier, vous devez vous assurer qu'aucun CRLFtraitement n'est effectué dessus, mais que vous le souhaitez diffet mergequ'il fonctionne comme un fichier texte normal (cela ne tient pas compte du fait que votre terminal / éditeur peut ou non gérer UTF-16).

Mais en regardant la .gitattributespage de manuel , voici l'attribut personnalisé qui est binary:

[attr]binary -diff -crlf

Il me semble donc que vous pourriez définir un attribut personnalisé dans votre niveau supérieur .gitattributespour utf16(notez que j'ajoute la fusion ici pour être sûr qu'il est traité comme du texte):

[attr]utf16 diff merge -crlf

À partir de là, vous pourrez spécifier dans n'importe quel .gitattributesfichier quelque chose comme:

*.vmc utf16

Notez également que vous devriez toujours pouvoir accéder à diffun fichier, même si vous pensez gitqu'il est binaire avec:

git diff --text

Éditer

Cette réponse dit essentiellement que GNU diff avec UTF-16 ou même UTF-8 ne fonctionne pas très bien. Si vous souhaitez gitutiliser un outil différent pour voir les différences (via --ext-diff), cette réponse suggère Guiffy .

Mais ce dont vous avez probablement besoin, c'est simplement d' diffun fichier UTF-16 qui ne contient que des caractères ASCII. Une façon de faire fonctionner cela est d'utiliser --ext-diffet le script shell suivant:

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

Notez que la conversion en UTF-8 peut également fonctionner pour la fusion, il vous suffit de vous assurer que cela se fait dans les deux sens.

Quant à la sortie vers le terminal lors de la recherche d'un diff d'un fichier UTF-16:

Essayer de différer comme ça entraîne des déchets binaires crachés à l'écran. Si git utilise GNU diff, il semblerait que GNU diff ne soit pas compatible avec unicode.

GNU diff ne se soucie pas vraiment de l'unicode, donc quand vous utilisez diff --text, il diffère simplement et produit le texte. Le problème est que le terminal que vous utilisez ne peut pas gérer l'UTF-16 qui est émis (combiné avec les marques de différence qui sont des caractères ASCII).

Jared Oberhaus
la source
Essayer de différer comme ça entraîne des déchets binaires crachés à l'écran. Si git utilise GNU diff, il semblerait que GNU diff ne soit pas compatible avec unicode.
skiphoppy le
1
GNU diff ne se soucie pas vraiment de l'unicode, donc quand vous utilisez diff --text, il diffère simplement et produit le texte. Le problème est que le terminal que vous utilisez ne peut pas gérer l'UTF-16 qui est émis (combiné avec les marques de différence qui sont des caractères ASCII).
Jared Oberhaus
@ jared-oberhaus - existe-t-il un moyen de déclencher ce script uniquement pour certains types de fichiers (c'est-à-dire avec une certaine extension)?
Terry
8

La solution est de filtrer cmd.exe /c "type %1". La fonction typeintégrée de cmd effectuera la conversion, et vous pouvez donc l'utiliser avec la capacité textconv de git diff pour activer la différence de texte des fichiers UTF-16 (devrait également fonctionner avec UTF-8, bien que non testé).

Citation de la page de manuel gitattributes:


Effectuer des différences de texte sur des fichiers binaires

Parfois, il est souhaitable de voir le diff d'une version convertie en texte de certains fichiers binaires. Par exemple, un document de traitement de texte peut être converti en une représentation de texte ASCII et le diff du texte affiché. Même si cette conversion perd certaines informations, la différence résultante est utile pour la visualisation humaine (mais ne peut pas être appliquée directement).

L'option de configuration textconv est utilisée pour définir un programme pour effectuer une telle conversion. Le programme doit prendre un seul argument, le nom d'un fichier à convertir, et produire le texte résultant sur stdout.

Par exemple, pour afficher la différence des informations exif d'un fichier au lieu des informations binaires (en supposant que l'outil exif soit installé), ajoutez la section suivante à votre $GIT_DIR/configfichier (ou $HOME/.gitconfigfichier):

[diff "jpg"]
        textconv = exif

Une solution pour mingw32 , les fans de cygwin devront peut-être modifier l'approche. Le problème est de passer le nom de fichier à convertir en cmd.exe - il utilisera des barres obliques et cmd suppose des séparateurs de répertoire anti-slash.

Étape 1:

Créez le script d'argument unique qui effectuera la conversion en stdout. c: \ chemin \ vers \ certains \ script.sh:

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

Étape 2:

Configurez git pour pouvoir utiliser le fichier de script. Dans votre configuration git ( ~/.gitconfigou .git/configou voir man git-config), mettez ceci:

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

Étape 3:

Indiquez les fichiers auxquels appliquer ce workarond en utilisant des fichiers .gitattributes (voir man gitattributes (5)):

*vmc diff=cmdtype

puis utilisez git diffsur vos fichiers.

Gilles 'SO- arrête d'être méchant'
la source
Presque comme Tony Kuneck mais sans "c: /path/to/some/script.sh" entropy.ch/blog/Developer/2010/04/15
Alexey Shumkin
J'ai un problème avec le script comme montré plus haut Git pour Windows mais j'ai trouvé ce qui suit est très bien et peut également traiter des espaces dans le chemin: cmd //c type "${1//\//\\}" .
patthoyts
Cela fonctionnera sans qu'il soit nécessaire de créer un fichier de script:textconv = powershell -NoProfile -Command \"& {Get-Content \\$args[0]}\"
Jakub Berezanski
5

git a récemment commencé à comprendre les encodages tels que utf16. Consultez la documentation de gitattributes , recherchezworking-tree-encoding

[Assurez-vous que votre page de manuel correspond, car c'est assez nouveau!]

Si (disons) le fichier est UTF-16 sans BOM sur une machine Windows, ajoutez-le à votre .gitattributesfichier

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

Si UTF-16 (avec bom) sur * nix, faites-le:

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

(Remplacez *.vmcpar *.whateverpour whateverles fichiers de type que vous devez gérer)

Voir: Prise en charge de l'encodage de l'arbre de travail "UTF-16LE-BOM" .


Ajouté plus tard

Suite à @Hackslash, on peut trouver que c'est insuffisant

 *.vmc text working-tree... 

Pour obtenir de belles différences de texte dont vous avez besoin

 *.vmc diff working-tree...

Mettre les deux fonctionne aussi bien

 *.vmc text diff working-tree... 

Mais c'est sans doute

  • Redondant - eol=...impliquetext
  • Verbose - un grand projet pourrait facilement avoir des dizaines de types de fichiers texte différents

Le problème

Git a un macro-attribut binary qui signifie -text -diff. Le contraire +text +diffn'est pas disponible intégré mais git donne les outils (je pense!) Pour le synthétiser

La solution

Git permet de définir de nouveaux attributs de macro.

Je proposerais que le haut du .gitattributesfichier que vous avez

 [attr]textfile text diff

Ensuite, pour tous les chemins qui doivent être du texte et des différences, faites

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

Notez que dans la plupart des cas, nous souhaitons l'encodage par défaut (utf-8) et l'eol par défaut (natif) et peuvent donc être supprimés.

La plupart des lignes devraient ressembler à

textfile *.c
textfile *.py
Etc

Pourquoi ne pas simplement utiliser diff?

Pratique: dans la plupart des cas, nous voulons des eol natifs. Ce qui veut dire non eol=.... Donc text, ne sera pas implicite et doit être mis explicitement.

Conceptuel: Texte Vs binaire est la distinction fondamentale. eol, encoding, diff, etc. n'en sont que quelques-uns.

Avertissement

En raison des moments bizarres dans lesquels nous vivons, je n'ai pas de machine avec un git de travail actuel. Je suis donc incapable pour le moment de vérifier le dernier ajout. Si quelqu'un trouve quelque chose qui ne va pas, j'effacerai / supprimerai.

Rusi
la source
Pour que mon fichier UTF-16LE-BOM fonctionne, je devais utiliser*.vmc diff working-tree-encoding=UTF-16LE-BOM eol=CRLF
HackSlash
@HackSlash: Merci pour le heads-up. Je suppose que vous dites qu'avec textseul vous n'avez pas eu de jolis textes diffs? Pouvez-vous s'il vous plaît vérifier que les deux text et que difftout fonctionne bien? Dans ce cas, je ferai une recommandation différente
Rusi
Correct, textseul entraîne une comparaison binaire. Je peux faire diffou text diffet ça marche. J'avais besoin d'ajouter -BOMsimplement parce que mon fichier avait une nomenclature, YMMV.
HackSlash le
@HackSlash J'ai incorporé votre découverte. Ce serait génial si vous pouviez le vérifier!
Rusi le
Merci @Rusi, cela a du sens pour moi.
HackSlash le
4

J'ai écrit un petit pilote git-diff to-utf8, qui devrait faciliter la diffusion de tous les fichiers encodés non ASCII / UTF-8. Vous pouvez l'installer en utilisant les instructions ici: https://github.com/chaitanyagupta/gitutils#to-utf8 (le to-utf8script est disponible dans le même référentiel).

Notez que ce script nécessite que les commandes fileet iconvsoient disponibles sur le système.

Chaitanya Gupta
la source
2

J'ai eu ce problème sur Windows récemment, et les bins dos2unixet unix2dosfournis avec git pour Windows ont fait l'affaire. Par défaut, ils se trouvent dans C:\Program Files\Git\usr\bin\. Notez que cela ne fonctionnera que si votre fichier n'a pas besoin d'être UTF-16. Par exemple, quelqu'un a accidentellement encodé un fichier python en UTF-16 alors qu'il n'en avait pas besoin (dans mon cas).

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

et

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
la source