Próbuję naprawić zakończenia linii za pomocą git filter-branch, ale bez powodzenia

270

Ugryzł mnie problem zakończenia linii w systemie Windows / Linux w programie git. Wydaje się, za pośrednictwem GitHub, MSysGit i innych źródeł, że najlepszym rozwiązaniem jest skonfigurowanie lokalnych repozytoriów do używania zakończeń linii w stylu linux, ale ustawionych core.autocrlfna true. Niestety nie zrobiłem tego wystarczająco wcześnie, więc teraz za każdym razem, gdy pociągam zmiany, zakończenia linii są przerywane.

Myślałem, że znalazłem tutaj odpowiedź , ale nie mogę zmusić jej do działania. Moja wiedza na temat wiersza poleceń w Linuksie jest w najlepszym wypadku ograniczona, więc nie jestem nawet pewien, co robi wiersz „xargs fromdos” w jego skrypcie. Ciągle otrzymuję wiadomości o braku takiego pliku lub katalogu, a kiedy udaje mi się wskazać istniejący katalog, mówi mi, że nie mam uprawnień.

Próbowałem tego z MSysGit w systemie Windows i za pomocą terminala Mac OS X.

Brian Donahue
źródło
Nie mogę głosować nawet za tym wątkiem. +1 ++ za to, że zapewnia najlepszą odpowiedź w tej sprawie.
sjas,
Zgadzam się z Charlesem. Jednak w moim przypadku (przy użyciu Mac OS X 10.8)> git config core.autocrlf false działał, a nie> git config core.autocrlf input
user1045085

Odpowiedzi:

187

Dokumentacja git dla gitattributes dokumentuje teraz inne podejście do „naprawy” lub normalizacji wszystkich zakończeń linii w twoim projekcie. Oto jego sedno:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

Jeśli jakieś pliki, które nie powinny być znormalizowane, wyświetlają się w stanie git, usuń ich atrybut tekstowy przed uruchomieniem git add -u.

manual.pdf -text

I odwrotnie, w plikach tekstowych, których git nie wykrywa, można ręcznie włączyć normalizację.

weirdchars.txt text

Wykorzystuje to nową --renormalizeflagę dodaną w git v2.16.0, wydanym w styczniu 2018 r. W przypadku starszych wersji git jest jeszcze kilka kroków:

$ echo "* text=auto" >>.gitattributes
$ rm .git/index     # Remove the index to force git to
$ git reset         # re-scan the working directory
$ git status        # Show files that will be normalized
$ git add -u
$ git add .gitattributes
$ git commit -m "Introduce end-of-line normalization"
Russ Egan
źródło
1
Czy możesz mi powiedzieć, jaki jest cel tego git reset, proszę?
crdx
1
zmusza git do przebudowania indeksu, podczas którego skanuje każdy plik, aby zgadnąć, czy jest on binarny. Rm usuwa stary indeks, reset tworzy nowy indeks.
Russ Egan
16
Dzięki, to zadziałało dla mnie. Przydatnym poleceniem po uruchomieniu git statusjest uruchomienie, git diff --ignore-space-at-eolaby upewnić się, że jedynymi zatwierdzanymi przez ciebie zmianami są zakończenia linii.
zelanix
1
Uwaga: jedyną „prawdziwą” różnicą między tym a „starym” rozwiązaniem jest obecność .gitattributes (z odpowiednią zawartością). Bez tego git resetnie wykryje żadnych modyfikacji, a zatem jest bezużyteczny.
Rob
3
Instrukcje dotyczące gitattributes stronie zostały zaktualizowane do skorzystania z --renormalizeflagą dodanej w git v2.16.0 który został wydany w styczniu 2018 roku --renormalizeflaga konsoliduje proces ponownego przetwarzania zakończeń linii dla każdego pliku śledzona do jednego polecenia: git add --renormalize ..
Mike Hill
389

Najprostszym sposobem, aby to naprawić, jest dokonanie jednego zatwierdzenia, który naprawia wszystkie zakończenia linii. Zakładając, że nie masz żadnych zmodyfikowanych plików, możesz to zrobić w następujący sposób.

# From the root of your repository remove everything from the index
git rm --cached -r .

# Change the autocrlf setting of the repository (you may want 
#  to use true on windows):
git config core.autocrlf input

# 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 "Fixed crlf issue"

# If you're doing this on a Unix/Mac OSX clone then optionally remove
# the working tree and re-check everything out with the correct line endings.
git ls-files -z | xargs -0 rm
git checkout .
CB Bailey
źródło
7
PS Poleciłem waszą poprawkę chłopakom na github.com, a oni zaktualizowali swój przewodnik pomocy, aby użyć waszego rozwiązania (wcześniej zalecał świeżego klonowanie i twardy reset, który nie wydawał się uzyskać wszystkich plików.) Help.github. com / radzenie sobie z liniami
Brian Donahue
31
Dzięki ... to świetna poprawka. Znaleziono go na GitHub.
PHLAK
4
Możesz także sprawdzić config.safecrlf, aby upewnić się, że nie zmieniasz crlfs w plikach nietekstowych (takich jak binarne). Sprawdź to w dokumentacji kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88
4
@ vrish88: Jeśli jednak znajdujesz się w takiej sytuacji, prawdopodobnie cierpisz na mieszane zakończenia z podszewką i rdzeń.safecrlf może faktycznie uniemożliwić ci wykonanie tego, co musisz zrobić. Prawdopodobnie łatwiej jest nie używać safecrlf. git często błędnie wykrywa pliki binarne, a jeśli tak, możesz ręcznie oznaczyć go jako plik binarny za pomocą .gitattribute i odzyskać poprawną wersję z poprzedniego zatwierdzenia.
CB Bailey,
26
Nowsze rozwiązanie zalecane w poniższej odpowiedzi Russa Egana jest prostsze i nie wiąże się ze strasznymi rzeczami, takimi jak usunięcie całego kodu źródłowego , więc naprawdę polecam ludziom korzystanie z niego, nawet jeśli to stare rozwiązanie ma 10 razy więcej głosów!
Porculus,
11

Moja procedura postępowania z zakończeniami linii jest następująca (przetestowana na wielu repozytoriach):

Podczas tworzenia nowego repozytorium:

  • wstaw .gitattributespierwsze zatwierdzenie wraz z innymi typowymi plikami jako .gitignoreiREADME.md

W przypadku istniejącego repozytorium:

  • Utwórz / zmodyfikuj .gitattributesodpowiednio
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n( --no-verifyma pominąć haczyki poprzedzające zatwierdzenie)
    • Muszę to robić wystarczająco często, by zdefiniować to jako alias alias fixCRLF="..."
  • powtórz poprzednie polecenie
    • tak, to voodoo, ale generalnie muszę uruchomić polecenie dwa razy, za pierwszym razem normalizuje niektóre pliki, za drugim razem jeszcze więcej plików. Generalnie najlepiej chyba powtarzać, dopóki nie zostanie utworzone nowe zatwierdzenie :)
  • przejdź kilka razy między starą (tuż przed normalizacją) a nową gałęzią. Po zmianie gałęzi czasami git znajdzie jeszcze więcej plików, które wymagają ponownej normalizacji!

W .gitattributesoświadczam jawnie, że wszystkie pliki tekstowe mają LOL EOL, ponieważ ogólnie narzędzia Windows są kompatybilne z LF, podczas gdy narzędzia inne niż Windows nie są kompatybilne z CRLF (nawet wiele narzędzi wiersza poleceń nodejs zakłada LF i dlatego może zmieniać EOL w twoich plikach).

Zawartość .gitattributes

Mój .gitattributeszwykle wygląda jak:

*.html eol=lf
*.js   eol=lf
*.json eol=lf
*.less eol=lf
*.md   eol=lf
*.svg  eol=lf
*.xml  eol=lf

Aby dowiedzieć się, jakie różne rozszerzenia są śledzone przez git w bieżącym repozytorium, spójrz tutaj

Problemy po normalizacji

Po wykonaniu tej czynności istnieje jeszcze jedno wspólne zastrzeżenie.

Powiedz, że Twój masterjest już aktualny i znormalizowany, a następnie do kasy outdated-branch. Dość często zaraz po sprawdzeniu tej gałęzi, git zaznacza wiele plików jako zmodyfikowanych.

Rozwiązaniem jest wykonanie fałszywego commit ( git add -A . && git commit -m 'fake commit'), a następnie git rebase master. Po zmianie bazy fałszywe zatwierdzenie powinno zniknąć.

jakub.g
źródło
1
Myślałem, że wariuję, dopóki nie przeczytałem twojego postu, ponieważ musiałem kilka razy uruchomić określoną sekwencję poleceń. Wiara w czary! ;)
Sean Fausett
W wersji git 2.7.0.windows.1użyłem następujących opcji: git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n
Sean Fausett
4
git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

Wyjaśnienie:

  • git status --short

    Wyświetla każdą linię, której git jest i nie jest świadomy. Pliki, które nie są kontrolowane przez git są oznaczone na początku linii znakiem „?”. Pliki, które są modyfikowane, są oznaczone literą M.

  • grep "^ *M"

    To odfiltrowuje tylko te pliki, które zostały zmodyfikowane.

  • awk '{print $2}'

    Pokazuje tylko nazwę pliku bez żadnych znaczników.

  • xargs fromdos

    Pobiera nazwy plików z poprzedniego polecenia i uruchamia je za pomocą narzędzia „fromdos” w celu konwersji końcówek linii.

Lloyd Moore
źródło
To jest niesamowite. Dziękuję Ci. Dla każdego, kto szuka rozwiązania korzystającego z Homebrew dos2unixzamiast fromdos.
Almir Sarajčić
4

Oto jak naprawiłem wszystkie zakończenia linii w całej historii za pomocą git filter-branch. ^MPostać musi być wprowadzona za pomocą CTRL-V+ CTRL-M. Kiedyś dos2unixkonwertowałem pliki, ponieważ automatycznie pomija to pliki binarne.

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'
pfrenssen
źródło
3

„| Xargs fromdos” odczytuje ze standardowego wejścia ( findznalezione pliki ) i używa go jako argumentu polecenia fromdos, które konwertuje zakończenia linii. (Czy fromdos jest standardem w tych środowiskach? Jestem przyzwyczajony do dos2unix). Zauważ, że możesz uniknąć używania xargs (szczególnie przydatne, jeśli masz wystarczającą liczbę plików, aby lista argumentów była zbyt długa dla xargs):

find <path, tests...> -exec fromdos '{}' \;

lub

find <path, tests...> | while read file; do fromdos $file; done

Nie jestem całkowicie pewien twoich komunikatów o błędach. Z powodzeniem przetestowałem tę metodę. Jaki program produkuje każdy? Do jakich plików / katalogów nie masz uprawnień? Oto jednak próba zgadnięcia, co to może być:

Jednym prostym sposobem na uzyskanie błędu „nie znaleziono pliku” dla skryptu jest użycie ścieżki względnej - użyj bezwzględnej. Podobnie możesz otrzymać błąd uprawnień, jeśli skrypt nie jest wykonywalny (chmod + x).

Dodaj komentarze, a postaram się pomóc.

Cascabel
źródło
Widziałem inny przykład z dos2unix i pomyślałem, że to jakoś kopiowanie plików do folderu o nazwie tak, ale teraz rozumiem. Wow, wydaje się teraz oczywiste. Dzięki za pomoc!
Brian Donahue
1

ok ... w cygwinie nie mamy łatwo dostępnych fromdos, a ta awk podmuch pojawia się na twojej twarzy, jeśli masz jakieś spacje w ścieżkach do zmodyfikowanych plików (które mieliśmy), więc musiałem to zrobić nieco inaczej:

git status --short | grep "^ *M" | sed 's/^ *M//' | xargs -n 1 dos2unix

podziękowania dla @lloyd za większość tego rozwiązania

Anton K
źródło
-2

Wykonaj następujące kroki, jeśli żadna inna odpowiedź nie działa dla Ciebie:

  1. Jeśli korzystasz z systemu Windows, zrób git config --global core.autocrlf true; jeśli korzystasz z Uniksa, zróbgit config core.autocrlf input
  2. Biegać git rm --cached -r .
  3. Usuń plik .gitattributes
  4. Biegać git add -A
  5. Biegać git reset --hard

W takim razie twój lokal powinien być teraz czysty.

zs2020
źródło
4
Naprawdę? Usunięcie .gitattributespliku jest rozwiązaniem problemu z zakończeniami linii?
Aleksandr M
Tak proszę zająć komentarz przez @AleksandrM
Mr_and_Mrs_D