Czy mogę sprawić, by git rozpoznał plik UTF-16 jako tekst?

140

Śledzę plik maszyny wirtualnej Virtual PC (* .vmc) w git i po wprowadzeniu zmiany git zidentyfikował plik jako binarny i nie porównał go dla mnie. Odkryłem, że plik został zakodowany w UTF-16.

Czy można nauczyć git, aby rozpoznawał, że ten plik jest tekstowy i odpowiednio go obsługiwał?

Używam git pod Cygwin, z core.autocrlf ustawionym na false. W razie potrzeby mógłbym użyć mSysGit lub git pod UNIXem.

skiphoppy
źródło

Odpowiedzi:

83

Od jakiegoś czasu zmagam się z tym problemem i właśnie odkryłem (dla mnie) idealne rozwiązanie:

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

git difftoolprzyjmuje te same argumenty, co git diffby zrobił, ale uruchamia wybrany program porównujący zamiast wbudowanego GNU diff. Wybierz więc różnicę obsługującą wiele bajtów (w moim przypadku vimw trybie porównywania) i po prostu użyj git difftoolzamiast git diff.

Znalazłeś „difftool” za długie, aby wpisać? Nie ma problemu:

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

Git rządzi.

Sam Stokes
źródło
1
Nie jest to idealne rozwiązanie (wolałbym raczej mieć przewijany zunifikowany diff), ALE jest to mniejsze zło, biorąc pod uwagę moje wybory i moją niechęć do znalezienia czegoś nowego do zainstalowania. „vimdiff”, to jest! (tak, vim ... i git)
Roboprog
1
Czy działa to również w celu przygotowania i zatwierdzania tylko fragmentów plików UTF16?
Ortwin Gentz
Używam Beyond Compare jako narzędzia do porównywania i scalania. Z .gitconfig <pre> <code> [difftool "bc3"] path = c: / Program Files (x86) / Beyond Compare 3 / bcomp.exe [scaletool "bc3"] path = c: / Program Files (x86) / Beyond Compare 3 / bcomp.exe </code> </pre>
Tom Wilson
@Tom Wilson Przepraszamy, nie można sformatować bloku kodu przez wcięcie 4 spacji !?
Tom Wilson
Mam podstawową wiedzę na temat git i nie jestem pewien, jak obsługuje on zmiany plików. Czy są to zawsze pliki binarne, czy w przypadku tekstu (ASCII) odbywa się specjalne przetwarzanie / wykrywanie zmian?
i486
63

Istnieje bardzo proste rozwiązanie, które działa po wyjęciu z pudełka na Unices.

Na przykład z .stringsplikami Apple po prostu:

  1. Utwórz .gitattributesplik w katalogu głównym repozytorium za pomocą:

    *.strings diff=localizablestrings
    
  2. Dodaj do swojego ~/.gitconfigpliku:

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

Źródło: pliki Diff .strings w Git (i starszy post z 2010 r.).

IlDan
źródło
Zrobiłem to, ale git odmawia ucieczki. Pojawiający się błąd to „zły wiersz 4 pliku konfiguracyjnego w /Users/myusername/.gitconfig”. Użyłem "git config --global --edit", aby otworzyć mój plik gitconfig. Co ciekawe, jeśli usunę dodane linie, wszystko działa dobrze. Jakieś wskazówki?
shshnk
Zgadnę inteligentne cytaty, jeśli skopiujesz / wkleisz. Zredagowałem odpowiedź, aby to naprawić.
Lou Franco,
To działa jak urok, powinno być akceptowaną odpowiedzią ze względu na prostotę i lepszą integrację. Nie rozumiem, jak „użyj innego narzędzia” może być odpowiedzią na pytanie „Czy mogę sprawić, by git rozpoznał plik UTF-16 jako tekst?”
itMaxence
@itMaxence Strictly, iconvjest „kolejnym narzędziem” w taki sam sposób jak Vim lub Beyond Compare (nie jest częścią pakietu git).
Agi Hammerthief,
@AgiHammerthief pewnie po ponownym przeczytaniu zgadzam się, nie wiem, o czym myślałem. FWIW vimdiffi iconvoba są już obecne na macOS, więc nie musisz się zastanawiać, skąd je zdobyć, i wykonują swoją pracę
itMaxence
39

Czy próbowałeś ustawić .gitattributestraktowanie go jako pliku tekstowego?

na przykład:

*.vmc diff

Więcej szczegółów na http://www.git-scm.com/docs/gitattributes.html .

Chealion
źródło
2
To działa, ale dla poprawności należy pamiętać, że ustawia to dwa atrybuty: seti diff...
OK.
2
To rozwiązanie jest dla mnie jedyne akceptowalne. Zgodnie @OK komentarzu, „set” jest tu bez znaczenia, po prostu *.vmc diff, *.sql diffetc .. jest potrzebne, aby ustawić „diff” atrybut dla podanej ścieżce. (Nie mogę edytować odpowiedzi). Jednak dwa zastrzeżenia: różnice są wyświetlane z odstępami między znakami i nie można ich „wstawić na scenę” ani „odrzucić fragmentu” dla tych problematycznych plików.
Pac0
30

Domyślnie wygląda na to, że gitnie będzie działać dobrze z UTF-16; dla takiego pliku musisz upewnić się, że nie CRLFjest na nim wykonywane żadne przetwarzanie, ale chcesz diffi mergedziałać jak normalny plik tekstowy (to ignoruje, czy twój terminal / edytor obsługuje UTF-16, czy nie).

Ale patrząc na stronę .gitattributespodręcznika , oto atrybut niestandardowy, który jest binary:

[attr]binary -diff -crlf

Wydaje mi się więc, że możesz zdefiniować niestandardowy atrybut na swoim najwyższym poziomie .gitattributesdla utf16(pamiętaj, że dodam tutaj scalanie, aby upewnić się, że jest traktowany jako tekst):

[attr]utf16 diff merge -crlf

Stamtąd możesz określić w dowolnym .gitattributespliku coś takiego:

*.vmc utf16

Pamiętaj również, że nadal powinieneś być w stanie diffplik, nawet jeśli uważasz , gitże jest binarny z:

git diff --text

Edytować

Ta odpowiedź zasadniczo mówi, że różnica GNU z UTF-16 lub nawet UTF-8 nie działa zbyt dobrze. Jeśli chcesz gitużyć innego narzędzia, aby zobaczyć różnice (przez --ext-diff), ta odpowiedź sugeruje Guiffy .

Ale to, czego prawdopodobnie potrzebujesz, to tylko diffplik UTF-16, który zawiera tylko znaki ASCII. Aby to zadziałało, użyj --ext-diffi następującego skryptu powłoki:

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

Zauważ, że konwersja do UTF-8 może działać również w przypadku scalania, musisz tylko upewnić się, że odbywa się to w obu kierunkach.

Jeśli chodzi o dane wyjściowe na terminal podczas przeglądania pliku różnicowego UTF-16:

Próba takiego porównania skutkuje wyrzuceniem binarnych śmieci na ekran. Jeśli git używa GNU diff, wydaje się, że GNU diff nie obsługuje Unicode.

GNU diff tak naprawdę nie dba o unicode, więc kiedy używasz diff --text, po prostu porównuje i wyświetla tekst. Problem polega na tym, że terminal, którego używasz, nie obsługuje emitowanego UTF-16 (w połączeniu ze znakami różnicowymi, które są znakami ASCII).

Jared Oberhaus
źródło
Próba takiego porównania skutkuje wyrzuceniem binarnych śmieci na ekran. Jeśli git używa GNU diff, wydaje się, że GNU diff nie obsługuje Unicode.
skiphoppy
1
GNU diff tak naprawdę nie dba o unicode, więc kiedy używasz diff --text, po prostu porównuje i wyświetla tekst. Problem polega na tym, że terminal, którego używasz, nie obsługuje emitowanego UTF-16 (w połączeniu ze znakami różnicowymi, które są znakami ASCII).
Jared Oberhaus
@ jared-oberhaus - czy istnieje sposób na wywołanie tego skryptu tylko dla określonych typów plików (tj. z określonym rozszerzeniem)?
Terry
8

Rozwiązaniem jest przefiltrowanie cmd.exe /c "type %1". Wbudowany program cmd typewykona konwersję, więc możesz go użyć ze zdolnością textconv git diff, aby włączyć tekstowe porównywanie plików UTF-16 (powinno działać również z UTF-8, chociaż nie zostało przetestowane).

Cytowanie ze strony podręcznika gitattributes:


Wykonywanie różnic tekstowych plików binarnych

Czasami pożądane jest zobaczenie różnicy wersji przekonwertowanych na tekst niektórych plików binarnych. Na przykład dokument edytora tekstu można przekonwertować na reprezentację tekstową ASCII i różnicę wyświetlanego tekstu. Mimo że ta konwersja powoduje utratę niektórych informacji, wynikowa różnica jest przydatna do oglądania przez ludzi (ale nie można jej zastosować bezpośrednio).

Opcja textconv config służy do zdefiniowania programu do wykonania takiej konwersji. Program powinien pobrać pojedynczy argument, nazwę pliku do konwersji i wygenerować wynikowy tekst na standardowe wyjście.

Na przykład, aby wyświetlić różnice w informacjach exif pliku zamiast informacji binarnych (zakładając, że masz zainstalowane narzędzie exif), dodaj następującą sekcję do $GIT_DIR/configpliku (lub $HOME/.gitconfigpliku):

[diff "jpg"]
        textconv = exif

Rozwiązanie dla mingw32 , fani cygwin mogą wymagać zmiany podejścia. Problem polega na przekazaniu nazwy pliku do konwersji do cmd.exe - będzie on używał ukośników, a cmd zakłada separatory katalogów z ukośnikiem odwrotnym.

Krok 1:

Utwórz skrypt z jednym argumentem, który dokona konwersji na standardowe wyjście. c: \ ścieżka \ do \ jakiś \ skrypt.sh:

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

Krok 2:

Skonfiguruj git, aby móc używać pliku skryptu. Wewnątrz git config ( ~/.gitconfiglub .git/configlub zobaczyć man git-config), umieścić w tym:

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

Krok 3:

Wskaż pliki, do których należy zastosować to obejście, wykorzystując pliki .gitattributes (zobacz man gitattributes (5)):

*vmc diff=cmdtype

następnie użyj git diffna swoich plikach.

Gilles 'SO- przestań być zły'
źródło
Prawie jak Tony Kuneck, ale bez „c: /path/to/some/script.sh” entropy.ch/blog/Developer/2010/04/15/…
Alexey Shumkin
Mam problem ze skryptem, jak pokazano powyżej z Git dla Windows, ale znalazłem następujący jest w porządku, a także radzi sobie ze spacjami w ścieżce: cmd //c type "${1//\//\\}" .
patthoyts
To zadziała bez konieczności tworzenia skryptu:textconv = powershell -NoProfile -Command \"& {Get-Content \\$args[0]}\"
Jakub Berezanski
5

git zaczął ostatnio rozumieć kodowanie, takie jak utf16. Zobacz dokumentację gitattributes , wyszukajworking-tree-encoding

[Upewnij się, że Twoja strona podręcznika pasuje, ponieważ jest to całkiem nowe!]

Jeśli (powiedzmy) plik jest w formacie UTF-16 bez BOM na komputerze z systemem Windows, dodaj go do swojego .gitattributespliku

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

Jeśli UTF-16 (z bom) na * nix to:

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

(Wymień *.vmcsię *.whateverdla whateverplików typu trzeba uchwytem)

Zobacz: Obsługa kodowania drzewa roboczego „UTF-16LE-BOM” .


Dodano później

Po @Hackslash można stwierdzić, że to nie wystarczy

 *.vmc text working-tree... 

Aby uzyskać ładne różnice tekstowe, potrzebujesz

 *.vmc diff working-tree...

Umieszczenie obu działa również

 *.vmc text diff working-tree... 

Ale to prawdopodobnie

  • Nadmiarowy - eol=...implikujetext
  • Pełne - duży projekt może z łatwością zawierać dziesiątki różnych typów plików tekstowych

Problem

Git ma atrybut makro, binary który oznacza -text -diff. Odwrotność +text +diffnie jest dostępna jako wbudowana, ale git daje narzędzia (chyba!) Do syntezy

Rozwiązanie

Git pozwala zdefiniować nowe atrybuty makr.

Zaproponowałbym ten szczyt .gitattributespliku, który masz

 [attr]textfile text diff

Następnie dla wszystkich ścieżek, które muszą być tekstowe i różnicowe

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

Zauważ, że w większości przypadków chcielibyśmy domyślnego kodowania (utf-8) i domyślnego eol (natywnego), więc mogą zostać odrzucone.

Większość linii powinna wyglądać

textfile *.c
textfile *.py
Etc

Dlaczego po prostu nie użyć diff?

Praktyczne: w większości przypadków potrzebujemy natywnego eol. Co oznacza nie eol=.... Więc textnie będzie dorozumiany i musi być wyraźnie określony.

Koncepcyjne: binarny tekst Vs jest podstawowym rozróżnieniem. eol, kodowanie, różnicowanie itp. to tylko niektóre aspekty tego.

Zrzeczenie się

Ze względu na dziwne czasy, w których żyjemy, nie mam maszyny z działającym dupkiem. Dlatego w tej chwili nie mogę sprawdzić najnowszego dodatku. Jeśli ktoś znajdzie coś nie tak, poprawię / usunę.

Rusi
źródło
Aby mój plik UTF-16LE-BOM działał, musiałem użyć*.vmc diff working-tree-encoding=UTF-16LE-BOM eol=CRLF
HackSlash
@HackSlash: Dzięki za ostrzeżenie. Myślę, że mówisz textsam, że nie dostałeś ładnych różnic tekstowych? Czy możesz to sprawdzić z obydwoma text i diffwszystko działa dobrze? W takim razie przedstawię inną rekomendację
Rusi
Prawidłowo, textsamo wyniki w porównaniu binarnym. Mogę zrobić difflub text diffi to działa. Musiałem dodać -BOMtylko dlatego, że mój plik miał BOM, YMMV.
HackSlash
@HackSlash Uwzględniłem twoje odkrycie. Byłoby wspaniale, gdybyś mógł to sprawdzić!
Rusi
Dzięki @Rusi, ma to dla mnie sens.
HackSlash
4

Napisałem mały sterownik git-diff to-utf8, który powinien ułatwić porównywanie plików zakodowanych w formacie innym niż ASCII / UTF-8. Możesz go zainstalować, korzystając z instrukcji tutaj: https://github.com/chaitanyagupta/gitutils#to-utf8 ( to-utf8skrypt jest dostępny w tym samym repozytorium).

Zauważ, że ten skrypt wymaga , aby w systemie były dostępne obie komendy filei iconv.

Chaitanya Gupta
źródło
2

Niedawno wystąpił ten problem w systemie Windows, a pojemniki dos2unixi unix2dosdostarczane z git dla systemu Windows załatwiły sprawę. Domyślnie znajdują się w C:\Program Files\Git\usr\bin\. Zauważ, że zadziała to tylko wtedy, gdy twój plik nie musi być w formacie UTF-16. Na przykład ktoś przypadkowo zakodował plik Pythona jako UTF-16, gdy nie było to konieczne (w moim przypadku).

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

i

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
źródło