git-diff, aby zignorować ^ M

473

W projekcie, w którym niektóre pliki zawierają ^ M jako separatory nowego wiersza. Zróżnicowanie tych plików jest pozornie niemożliwe, ponieważ git-diff widzi je, ponieważ cały plik jest tylko jedną linią.

Jak się różni od poprzedniej wersji?

Czy istnieje opcja typu „traktuj ^ M jak nowy wiersz przy różnicowaniu”?

prompt> git-diff "HEAD^" -- MyFile.as 
diff --git a/myproject/MyFile.as b/myproject/MyFile.as
index be78321..a393ba3 100644
--- a/myproject/MyFile.cpp
+++ b/myproject/MyFile.cpp
@@ -1 +1 @@
-<U+FEFF>import flash.events.MouseEvent;^Mimport mx.controls.*;^Mimport mx.utils.Delegate
\ No newline at end of file
+<U+FEFF>import flash.events.MouseEvent;^Mimport mx.controls.*;^Mimport mx.utils.Delegate
\ No newline at end of file
prompt>

AKTUALIZACJA:

teraz napisałem skrypt Ruby, który sprawdza najnowsze 10 wersji i konwertuje CR na LF.

require 'fileutils'

if ARGV.size != 3
  puts "a git-path must be provided"
  puts "a filename must be provided"
  puts "a result-dir must be provided"
  puts "example:"
  puts "ruby gitcrdiff.rb project/dir1/dir2/dir3/ SomeFile.cpp tmp_somefile"
  exit(1)
end

gitpath = ARGV[0]
filename = ARGV[1]
resultdir = ARGV[2]

unless FileTest.exist?(".git")
  puts "this command must be run in the same dir as where .git resides"
  exit(1)
end

if FileTest.exist?(resultdir)
  puts "the result dir must not exist"
  exit(1)
end
FileUtils.mkdir(resultdir)

10.times do |i|
  revision = "^" * i
  cmd = "git show HEAD#{revision}:#{gitpath}#{filename} | tr '\\r' '\\n' > #{resultdir}/#{filename}_rev#{i}"
  puts cmd 
  system cmd
end
neoneye
źródło
7
być może chciałeś git diff -b- pokazałem to na stackoverflow.com/a/46265081/58794
Jason Pyeron
6
Z Git 2.16 (Q1 2018) będziesz mieć git diff --ignore-cr-at-eol. Zobacz moją odpowiedź poniżej .
VonC
7
@JasonPyeron i dla przyszłych Googlersów: Musiałem spojrzeć w górę, co git diff -bjest identyczne git diff --ignore-space-change.
Gogowitsch

Odpowiedzi:

392

GitHub sugeruje , abyś używał \ n jako znaku nowej linii w repozytoriach obsługiwanych przez git. Istnieje opcja automatycznej konwersji:

$ git config --global core.autocrlf true

Oczywiście mówi się, że konwertuje crlf na lf, podczas gdy chcesz przekonwertować crlf na lf. Mam nadzieję, że to nadal działa…

A następnie przekonwertuj swoje pliki:

# Remove everything from the index
$ git rm --cached -r .

# Re-add all the deleted files to the index
# You should get lots of messages like: "warning: CRLF will be replaced by LF in <file>."
$ git diff --cached --name-only -z | xargs -0 git add

# Commit
$ git commit -m "Fix CRLF"

core.autocrlf jest opisany na stronie manuala .

nes1983
źródło
1
Nie, oczywiście, że nie, gdy ustawienie już tam będzie, po cichu nastąpi konwersja po zatwierdzeniu. Jeśli wszystko działa tak, jak myślę, to znaczy…
nes1983,
1
Problem polega na tym, że mam już niektóre pliki w repozytorium, które mają zakończenia CRLF, a inne nie. Podejrzewam, że Adobe Flash dodaje CRLF, mimo że używam wersji Mac. Muszę porównać ze starszymi wersjami tych plików. Konwersja zakończeń linii od teraz nie rozwiązuje problemu w starszych wersjach: - /
neoneye
65
Nie pracujesz tutaj z plikami CRLF, przynajmniej nie w opublikowanym przykładzie. To jest plik mac w starym stylu (tylko używa \ r dla EOL). Dlatego różnicę wyświetla się w jednym wierszu. Plik korzystający z dos EOL pokazałby każdą linię wyraźnie z końcowym ^ M, który można rozpoznać za pomocą git config core.whitespace cr-at-eol.
jamessan,
12
Próbuję tego, ale wciąż otrzymuję warning: LF will be replaced by CRLFzamiast tego warning: CRLF will be replaced by LFi jestem w systemie Linux. Masz pomysł, dlaczego? Chcę, żeby wszystko skończyło się na LF, a nie CRLF!
trusktr
5
@trusktr, stało się tak samo ze mną. W Linuksie, z przypadkowym CRLF, użyj git config --global core.autocrlf input, wykonaj kroki w tej odpowiedzi (rm, add, commit), a dostaniesz warning: CRLF will be replaced by LF. The file will have its original line endings in your working directory.. Usuń pliki (ponieważ mają oryginalny, niewłaściwy CRLF) i sprawdź je ponownie od ostatniego zatwierdzenia „Fix CRLF”.
jmmut,
370

Podczas programowania w systemie Windows napotkałem ten problem podczas używania git tfs. Rozwiązałem to w ten sposób:

git config --global core.whitespace cr-at-eol

To w zasadzie mówi Gitowi, że CR na końcu linii nie jest błędem. W rezultacie, te irytujące ^Mpostacie pojawiają się już na końcu wierszy git diff, git showitp

Wygląda na to, że pozostawia inne ustawienia bez zmian; na przykład dodatkowe spacje na końcu linii nadal są wyświetlane jako błędy (podświetlone na czerwono) w pliku różnic.

(Nawiązały do ​​tego inne odpowiedzi, ale powyższe dotyczy dokładnie ustawienia. Aby ustawić ustawienie tylko dla jednego projektu, pomiń --global.)

EDYCJA :

Po wielu zadaniach kończących linię miałem szczęście, pracując w zespole .NET, z tymi ustawieniami:

  • Brak ustawienia core.eol
  • BEZ ustawienia podstawowego. Białego miejsca
  • BEZ ustawienia core.autocrlf
  • Podczas uruchamiania instalatora Git dla systemu Windows dostępne są następujące trzy opcje:
    • Kasy w stylu Windows, zatwierdzenia zakończeń linii w stylu Unix <- wybierz ten
    • Kwestia jak jest, zatwierdza zakończenia linii w stylu uniksowym
    • Kasa jak jest, zatwierdza jak jest

Jeśli chcesz użyć ustawienia białych znaków, prawdopodobnie powinieneś włączyć je tylko dla poszczególnych projektów, jeśli chcesz wchodzić w interakcje z TFS. Po prostu pomiń --global:

git config core.whitespace cr-at-eol

Jeśli chcesz usunąć niektóre podstawowe ustawienia. *, Najprostszym sposobem jest uruchomienie tego polecenia:

git config --global -e

Spowoduje to otwarcie globalnego pliku .gitconfig w edytorze tekstu i łatwe usuwanie linii, które chcesz usunąć. (Lub możesz umieścić „#” przed nimi, aby je skomentować.)

Ryan Lundy
źródło
30
Dla tych, którzy znajdują to teraz, warto zauważyć, że kasa w stylu Windows, zatwierdza automatyczne ustawianie zakończeń linii w stylu uniksowymcore.autocrlf natrue
K. Carpenter
14
Pamiętaj, że linia git config --global core.whitespace cr-at-eolwyłączy inne domyślne ustawienia. Istnieją trzy wartości domyślne: blank-at-eol, blank-at-eof i spacja-przed-tab. Aby włączyć cr-at-eol, zachowując te, których będziesz potrzebować git config --global core.whitespace blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol.
Zitrax,
2
W przypadku mojego projektu (było to kasy w systemie Windows, a przeglądam go w systemie Linux), cr-at-eolpozbyłem się ^Mna końcu linii w git diffporządku, ale GIT nadal pokazywał te linie jako różne, chociaż zakończenie linii było jedyną różnicą.
Jānis Elmeris
SourceInsight wciąż naciska znak ^ M, a git nadal pokazuje różnicę na końcach linii. @ Polecenie Zitrax jest właściwą odpowiedzią na mój przypadek, git diff pokazuje ładne i czyste wyjście.
Lê Quang Duy,
3
Myślę, że git potrzebuje trochę więcej złożoności, kilku bardziej sprzecznych ustawień dla końca linii. Myślę, że git powinien bardziej martwić się o moje białe znaki. Na przykład zgłasza niepowiązany błąd krytyczny i pozostawia repozytorium w stanie uszkodzonym, gdy napotyka zakończenia linii Mac na komputerze z systemem Windows (ale nie Linux). Chodzi mi o to, dlaczego miałbym używać VCS, który miałby coś przeciwko temu biznesowi i pozwolić mi używać dowolnych zakończeń linii, jakie chcę? Widzę, że próbują, ale powinni wprowadzić jeszcze kilka zachowań kończących linię, aby rozwiązać problem, który nie istnieje. Już prawie są! Tak trzymaj.
Rolf
124

Spróbuj git diff --ignore-space-at-eol, albo git diff --ignore-space-change, albo git diff --ignore-all-space.

Jakub Narębski
źródło
22
Nic z tego tak naprawdę nie wpływa na znak identyfikujący nowy wiersz.
nes1983,
4
Próbowałem też z „-w”, ale bez powodzenia, nadal traktuję to jako pojedynczą linię. Następny projekt, o którym muszę pamiętać, aby nigdy nie dostać CR do kodu źródłowego.
neoneye,
3
Pamiętaj tylko o git config --global core.autocrlf true lub o błędzie ludzi z git, dopóki nie
ustawią
10
To rozwiązało mój problem bez konieczności zmiany autocrlfustawień. Dzięki!
nneonneo
11
te flagi nie mają dla mnie żadnego efektu ... nadal pokazuje ^ M jako różnice
Magnus
103

Zobacz także:

core.whitespace = cr-at-eol

lub równoważnie

[core]
    whitespace = cr-at-eol

gdzie whitespacepoprzedza znak tabulacji .

Vladimir Panteleev
źródło
4
Tak, to sprawiło, że narzędzie git diff (używane również w git show) przestało mnie denerwować o ^Ms na zmienionych liniach! :)
Rijk,
2
z jakiegokolwiek powodu to nie działało dla mnie. Próbowałem zarówno ze znakiem =, jak i bez =. git diffnadal pokazuje ^ M znaków.
Dennis
6
Można to zrobić na dwa sposoby: po pierwsze, dodaj wiersz powyżej dosłownie do pliku .gitconfig w .git / config lub ~ / .gitconfig; dwa, git config --global core.whitespace cr-at-eol(gdzie --global jest opcjonalny, jeśli chcesz go tylko na repozytorium, na którym jesteś)
K. Carpenter
To działało dla mnie w systemie Windows 7, chociaż po prostu podłożyłem go, [core]aby móc zastąpić core.prefiks znakiem TAB.
Rufflewind
Ta kwestia była powyżej, jak ukryć ^Msię git diff, a nie o tym, jak nie umieścić w ^ M w pierwszej kolejności. Oznacza to, że zaakceptowana zmiana core.autocrlfnie jest najlepsza, ponieważ cicho zmienia pliki bez potwierdzenia użytkownika.
deddebme
45

Dlaczego masz takie ^Mw twojej git diff?

W moim przypadku pracowałem nad projektem opracowanym w systemie Windows i korzystałem z systemu OS X. Kiedy zmieniłem trochę kodu, zobaczyłem ^Mna końcu wierszy, które dodałem git diff. Myślę, że ^Mbyły wyświetlane, ponieważ były innymi zakończeniami linii niż reszta pliku. Ponieważ reszta pliku została opracowana w systemie Windows, używała ona CRkońcówek linii, aw OS X używa LFkońcówek linii.

Najwyraźniej programista Windows nie użył opcji „ Kasa w stylu Windows, zatwierdzania zakończeń linii w stylu Unix ” podczas instalacji Gita.

Co więc powinniśmy z tym zrobić?

Możesz poprosić użytkowników Windows o ponowną instalację git i skorzystanie z opcji „ Kasa w stylu Windows, zatwierdzanie zakończeń linii w stylu Unix ”. Tak wolałbym, ponieważ widzę Windows jako wyjątek w postaci znaków kończących wiersz, a Windows rozwiązuje w ten sposób swój problem.

Jeśli wybierzesz tę opcję, powinieneś jednak naprawić bieżące pliki (ponieważ nadal używają CRkońcówek linii). Zrobiłem to, wykonując następujące kroki:

  1. Usuń wszystkie pliki z repozytorium, ale nie z systemu plików.

    git rm --cached -r .
    
  2. Dodaj .gitattributesplik, który zmusza niektóre pliki do użycia LFjako zakończenia linii. Umieść to w pliku:

    *.ext text eol=crlf
    

    Zamień .extna rozszerzenia plików, które chcesz dopasować.

  3. Dodaj wszystkie pliki ponownie.

    git add .
    

    Spowoduje to wyświetlenie takich wiadomości:

    warning: CRLF will be replaced by LF in <filename>.
    The file will have its original line endings in your working directory.
    
  4. Możesz usunąć .gitattributesplik, chyba że masz upartych użytkowników systemu Windows, którzy nie chcą używać opcji „ Kasa w stylu systemu Windows, zatwierdzaj zakończenia linii w stylu uniksowym ”.

  5. Zaangażuj się i pchnij wszystko.

  6. Usuń i pobierz odpowiednie pliki ze wszystkich systemów, w których są używane. W systemach Windows upewnij się, że teraz używają opcji „ Kasa w stylu Windows, zatwierdzaj zakończenia linii w stylu Unix ”. Powinieneś to również zrobić w systemie, w którym wykonałeś te zadania, ponieważ po dodaniu plików git powiedział:

    The file will have its original line endings in your working directory.
    

    Możesz zrobić coś takiego, aby usunąć pliki:

    git ls | grep ".ext$" | xargs rm -f
    

    A następnie, aby odzyskać je z poprawnymi zakończeniami linii:

    git ls | grep ".ext$" | xargs git checkout
    

    Oczywiście zastępując .extje wybranym rozszerzeniem.

Teraz twój projekt używa tylko LFznaków na końcu linii, a paskudne CRpostacie nigdy nie wrócą :).

Inną opcją jest wymuszenie zakończenia linii w stylu Windows. Możesz również użyć .gitattributesdo tego pliku.

Więcej informacji: https://help.github.com/articles/dealing-with-line-endings/#platform-all

gitaarik
źródło
4
Aby naprawić wszystkie zakończenia linii w określonym pliku, jeśli używasz Sublime Text, możesz przejść do View-> Line Endingsi kliknąć Unix.
Topher Hunt
Co to dokładnie ^Mznaczy? Czy to nowa linia w systemie Windows lub Linux? Czy jest to po prostu „inna” nowa linia w porównaniu do innych nowych linii w pliku?
buhtz
Dobry, myślę, że to po prostu „inna” nowa linia (inna niż większość innych)
gitaarik
-1, ponieważ ponowna instalacja git do osiągnięcia git config --global core.autocrlf truejest przesadą, a anty-Windows / anty- CRkampania wydaje się styczna do pytania.
RJFalconer
41

Czy istnieje opcja typu „traktuj ^ M jak nowy wiersz przy różnicowaniu”?

Będzie jeden z Gitem 2.16 (Q1 2018), ponieważ diffrodzina poleceń nauczyła się ignorować różnice w zwrocie karetki na końcu wiersza.

Zobacz commit e9282f0 (26 października 2017 r.) Autor: Junio ​​C Hamano ( gitster) .
Pomocnik: Johannes Schindelin ( dscho) .
(Połączone przez Junio ​​C Hamano - gitster- w commit 10f65c2 , 27 listopada 2017)

diff: --ignore-cr-at-eol

Nowa opcja --ignore-cr-at-eolinformuje maszynę różnicową, aby traktowała powrót karetki na końcu (kompletnej) linii, jakby nie istniała.

Podobnie jak inne --ignore-*opcje ignorowania różnych rodzajów białych spacji, pomoże to w sprawdzeniu prawdziwych zmian, które wprowadziłeś, bez rozpraszania się fałszywą CRLF<->LFkonwersją dokonaną przez Twój edytor.

VonC
źródło
@kaartic Dziękujemy za edycję odpowiedzi i odniesienie do właściwego zatwierdzenia!
VonC
3
Chociaż ogólnie dobrą praktyką jest ustawianie git config --global core.autocrlf truezgodnie z przyjętą odpowiedzią, odpowiada to bardziej bezpośrednio na pytanie PO: „Czy istnieje opcja typu„ traktuj ^ M jak nowy wiersz, gdy się różni ”?”.
drkvogel
1
Począwszy od Gita 2.20, nie ukrywa to ^ M
użytkownika1944491
@ user1944491 Nie zauważyłem żadnej regresji, co oznacza, że ​​nadal ignoruje eol, gdy różni się od tej opcji w Git 2.26.
VonC
@VonC Użycie tego argumentu w komendzie git diff nie zadziałało. Nie ustawiłem też mojej wartości core.whitespace, git version 2.20.1 (Apple Git-117)ale naprawiłem ją, dodając odpowiedź core.pager Jasona Pyerona. YMMV oczywiście.
user1944491
26

TL; DR

Zmienić core.pagersię "tr -d '\r' | less -REX", a nie w kodzie źródłowym

Dlatego

Te nieznośne ^ M pokazane są jako artefakt koloryzacji i pager. wprowadź opis zdjęcia tutaj Jest to spowodowane less -Rdomyślną opcją git pager. (domyślny pager gita to less -REX)

Pierwszą rzeczą, na którą należy zwrócić uwagę, jest to, że git diff -bnie pokaże zmian w białych spacjach (np. \ R \ n vs \ n)

Ustawiać:

git clone https://github.com/CipherShed/CipherShed
cd CipherShed

Szybki test, aby utworzyć plik unix i zmienić zakończenia linii, nie pokaże żadnych zmian za pomocą git diff -b:

echo -e 'The quick brown fox\njumped over the lazy\ndogs.' > test.txt
git add test.txt
unix2dos.exe test.txt
git diff -b test.txt

Zauważamy, że wymuszenie mniejszej rury nie pokazuje ^ M, ale włącza kolor i less -R:

git diff origin/v0.7.4.0 origin/v0.7.4.1 | less
git -c color.ui=always diff origin/v0.7.4.0 origin/v0.7.4.1 | less -R

Poprawka jest pokazana za pomocą potoku, aby usunąć \ r (^ M) z wyjścia:

git diff origin/v0.7.4.0 origin/v0.7.4.1
git -c core.pager="tr -d '\r' | less -REX"  diff origin/v0.7.4.0 origin/v0.7.4.1

Rozsądną alternatywą jest użycie less -r, ponieważ przejdzie ona przez wszystkie kody kontrolne, a nie tylko kody kolorów.

Jeśli chcesz bezpośrednio edytować plik konfiguracyjny git, jest to wpis do aktualizacji / dodania:

[core]
        pager = tr -d '\\r' | less -REX
Jason Pyeron
źródło
Miałem ten problem w repozytorium, w którym niektóre pliki miały \r\nzakończenia linii, a niektóre \nzakończenia linii (nie wiem, czy to istotne); diffs tego pierwszego pokazał ^Mw zmodyfikowanych liniach (czyli +liniach). core.autocrlfzostał ustawiony na true. Bieganie git config core.pager "tr -d '\r' | less -REX"pozbyło się nieznośnych ^M. Dzięki!
labreuer
5
Dzięki za to. Jest to jedyna odpowiedź, jeśli musisz pracować z różnymi zakończeniami linii w repozytorium (repozytoriach) - np. Korzystasz z kasy w niezmienionej formie, zatwierdzasz niezmiennie, celowo.
Mike
git diff -btego szukałem, ale doceniam dokładne wyjaśnienie.
Martin Burch,
Oto odpowiedź! Dziękuję Ci. flaga -b nie działała dla mnie.
Chris
Tak! Spośród wszystkich odpowiedzi na to pytanie modyfikacja [core]sekcji pliku git „config” poprzez dodanie pager = tr -d '\\r' | less -REXbyła jedyną odpowiedzią, która działała dla mnie. Dziękuję Ci!
Rashiki
13

Długo zmagałem się z tym problemem. Zdecydowanie najłatwiejszym rozwiązaniem jest nie martwić się o znaki ^ M i po prostu użyć wizualnego narzędzia do różnicowania, które może je obsłużyć.

Zamiast pisać:

git diff <commitHash> <filename>

próbować:

git difftool <commitHash> <filename>
Ian Wojtowicz
źródło
1
Dzięki! Również uruchomiłem „git difftool” i zasadniczo porównałem wszystkie zmienione pliki w pętli
Bhanuprakash D
2

Jak zauważył VonC, zostało to już uwzględnione w git 2.16+. Niestety nazwa opcji ( --ignore-cr-at-eol) różni się od nazwy używanej przez GNU diff, do której jestem przyzwyczajony ( --strip-trailing-cr).

Kiedy miałem do czynienia z tym problemem, moim rozwiązaniem było wywołanie diff GNU zamiast wbudowanego diff gita, ponieważ mój git jest starszy niż 2.16. Zrobiłem to za pomocą tego wiersza poleceń:

GIT_EXTERNAL_DIFF='diff -u --strip-trailing-cr "$2" "$5";true;#' git diff --ext-diff

Pozwala to na użycie --strip-trailing-cri dowolne inne opcje GNU diff.

Jest też inny sposób:

git difftool -y -x 'diff -u --strip-trailing-cr'

ale nie używa skonfigurowanych ustawień pagera, dlatego wolę ten pierwszy.

Pedro Gimeno
źródło
Ciekawa alternatywa dla mojej odpowiedzi. Pozytywne.
VonC