Der Versuch, Zeilenenden mit Git-Filter-Zweig zu reparieren, hat aber kein Glück

270

Ich bin von dem Windows / Linux-Problem mit Git-Ending gebissen worden. Über GitHub, MSysGit und andere Quellen scheint es die beste Lösung zu sein, Ihre lokalen Repos so einzustellen, dass sie Zeilenenden im Linux-Stil verwenden, aber core.autocrlfauf true. Leider habe ich das nicht früh genug gemacht, so dass jetzt jedes Mal, wenn ich Änderungen ziehe, die Zeilenenden verzerrt sind.

Ich dachte, ich hätte hier eine Antwort gefunden, aber ich kann sie nicht für mich arbeiten lassen. Meine Linux-Kommandozeilenkenntnisse sind bestenfalls begrenzt, daher bin ich mir nicht einmal sicher, was die Zeile "xargs fromdos" in seinem Skript bewirkt. Ich erhalte immer wieder Nachrichten darüber, dass keine solche Datei oder kein solches Verzeichnis vorhanden ist. Wenn ich es schaffe, auf ein vorhandenes Verzeichnis zu verweisen, wird mir mitgeteilt, dass ich keine Berechtigungen habe.

Ich habe dies mit MSysGit unter Windows und über das Mac OS X-Terminal versucht.

Brian Donahue
quelle
Ich kann diesen Thread nicht annähernd genug bewerten. +1 ++ dafür liefert die beste Antwort auf die Angelegenheit.
Sjas
Stimme Charles zu. In meinem Fall (unter Mac OS X 10.8)> git config core.autocrlf false funktionierte jedoch nicht> git config core.autocrlf input
user1045085

Antworten:

187

Die Git-Dokumentation für Gitattributes dokumentiert jetzt einen anderen Ansatz zum " Korrigieren " oder Normalisieren aller Zeilenenden in Ihrem Projekt. Hier ist der Kern davon:

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

Wenn Dateien, die nicht normalisiert werden sollen, im Git-Status angezeigt werden, deaktivieren Sie das Textattribut, bevor Sie git add -u ausführen.

manual.pdf -text

Umgekehrt kann bei Textdateien, die Git nicht erkennt, die Normalisierung manuell aktiviert werden.

weirdchars.txt text

Dies nutzt ein neues --renormalizeFlag, das in git v2.16.0, veröffentlicht im Januar 2018, hinzugefügt wurde. Für ältere Versionen von git gibt es einige weitere Schritte:

$ 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
quelle
1
Können Sie mir git resetbitte sagen, was der Zweck des ist?
Crdx
1
zwingt git, den Index neu zu erstellen, wobei jede Datei gescannt wird, um zu erraten, ob sie binär ist. Der rm löscht den alten Index, das Zurücksetzen erstellt den neuen Index.
Russ Egan
16
Danke, das hat bei mir funktioniert. Ein nützlicher Befehl nach dem Ausführen git statusist das Ausführen git diff --ignore-space-at-eol, um sicherzustellen, dass die einzigen Änderungen, die Sie vornehmen, die Zeilenenden sind.
Zelanix
1
Hinweis: Der einzige "echte" Unterschied zwischen dieser und der "alten" Lösung besteht im Vorhandensein von .gitattributes (mit dem entsprechenden Inhalt). Ohne dies git resetwerden keine Änderungen erkannt und sind somit unbrauchbar.
Rob
3
Die Anweisungen auf der Seite gitattributes wurden aktualisiert, um das --renormalizein git v2.16.0 hinzugefügte Flag zu nutzen, das im Januar 2018 veröffentlicht wurde. Das --renormalizeFlag konsolidiert den Prozess der erneuten Verarbeitung von Zeilenenden für jede verfolgte Datei in einem einzigen Befehl : git add --renormalize ..
Mike Hill
389

Der einfachste Weg, dies zu beheben, besteht darin, ein Commit durchzuführen, das alle Zeilenenden behebt. Angenommen, Sie haben keine geänderten Dateien, können Sie dies wie folgt tun.

# 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
quelle
7
PS Ich habe den Jungs von github.com Ihr Update empfohlen und sie haben ihre Hilfe aktualisiert, um Ihre Lösung zu verwenden (zuvor wurde nur ein neuer Klon und ein Hard-Reset empfohlen, bei dem anscheinend nicht alle Dateien abgerufen wurden.
Brian Donahue
31
Danke ... das ist eine großartige Lösung. Fand es auf GitHub.
PHLAK
4
Möglicherweise möchten Sie auch config.safecrlf überprüfen, um sicherzustellen, dass Sie crlfs nicht in Nicht-Textdateien (z. B. Binärdateien) ändern. Überprüfen Sie es in den Dokumenten kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88
4
@ vrish88: Wenn Sie sich jedoch in dieser Situation befinden, leiden Sie wahrscheinlich unter gemischten Endungen, und core.safecrlf kann Sie tatsächlich daran hindern, das zu tun, was Sie tun müssen. Es ist wahrscheinlich einfacher, safecrlf nicht zu verwenden. Bei git wird die Erkennung von Binärdateien nicht oft falsch angezeigt. Wenn dies der Fall ist, können Sie sie manuell mit einem .gitattribute als binär markieren und die richtige Version aus dem vorherigen Commit wiederherstellen.
CB Bailey
26
Die neuere Lösung, die in der Antwort von Russ Egan unten empfohlen wird, ist einfacher und beinhaltet keine beängstigenden Dinge wie das Löschen Ihres gesamten Quellcodes. Daher würde ich den Leuten wirklich empfehlen, diese zu verwenden, obwohl diese alte Lösung zehnmal so viele Stimmen hat!
Porculus
11

Mein Verfahren für den Umgang mit den Zeilenenden ist wie folgt (Kampf auf vielen Repos getestet):

Beim Erstellen eines neuen Repos:

  • Setzen Sie .gitattributesdas allererste Commit zusammen mit anderen typischen Dateien als .gitignoreund einREADME.md

Beim Umgang mit einem bestehenden Repo:

  • .gitattributesEntsprechend erstellen / ändern
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n (--no-verify ist es, Pre-Commit-Hooks zu überspringen)
    • Ich muss es oft genug tun, um es als Alias ​​zu definieren alias fixCRLF="..."
  • Wiederholen Sie den vorherigen Befehl
    • Ja, es ist Voodoo, aber im Allgemeinen muss ich den Befehl zweimal ausführen, beim ersten Mal werden einige Dateien normalisiert, beim zweiten Mal noch mehr Dateien. Im Allgemeinen ist es wahrscheinlich am besten zu wiederholen, bis kein neues Commit erstellt wird :)
  • Gehen Sie einige Male zwischen dem alten (kurz vor der Normalisierung) und dem neuen Zweig hin und her. Nach dem Wechseln des Zweigs findet git manchmal noch mehr Dateien, die neu normalisiert werden müssen!

In .gitattributesIch erkläre alle Textdateien explizit als LF EOL, da Windows-Tools im Allgemeinen mit LF kompatibel sind, während Nicht-Windows-Tools nicht mit CRLF kompatibel sind (selbst viele Befehlszeilentools von nodejs setzen LF voraus und können daher die EOL in Ihren Dateien ändern).

Inhalt von .gitattributes

Mein .gitattributessieht normalerweise so aus:

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

Um herauszufinden, welche unterschiedlichen Erweiterungen von git im aktuellen Repo verfolgt werden, klicken Sie hier

Probleme nach der Normalisierung

Sobald dies erledigt ist, gibt es noch eine weitere Einschränkung.

Angenommen, Sie mastersind bereits auf dem neuesten Stand und normalisiert, und dann checken Sie aus outdated-branch. Sehr oft markiert git viele Dateien direkt nach dem Auschecken dieses Zweigs als geändert.

Die Lösung besteht darin, ein falsches Commit ( git add -A . && git commit -m 'fake commit') durchzuführen und dann git rebase master. Nach dem Rebase sollte das gefälschte Commit verschwinden.

jakub.g
quelle
1
Ich dachte, ich würde verrückt, bis ich Ihren Beitrag gelesen habe, weil ich die angegebene Befehlsfolge auch mehrmals ausführen musste. Voodoo! ;)
Sean Fausett
Mit Git-Version habe 2.7.0.windows.1ich Folgendes verwendet: 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

Erläuterung:

  • git status --short

    Dies zeigt jede Zeile an, die git kennt und nicht kennt. Dateien, die nicht unter Git-Kontrolle stehen, sind am Zeilenanfang mit einem '?' Gekennzeichnet. Geänderte Dateien sind mit einem M gekennzeichnet.

  • grep "^ *M"

    Dadurch werden nur die Dateien herausgefiltert, die geändert wurden.

  • awk '{print $2}'

    Dies zeigt nur den Dateinamen ohne Markierungen.

  • xargs fromdos

    Dadurch werden die Dateinamen aus dem vorherigen Befehl übernommen und über das Dienstprogramm 'fromdos' ausgeführt, um die Zeilenenden zu konvertieren.

Lloyd Moore
quelle
Das ist fantastisch. Danke dir. Für alle, die nach einer Lösung mit Homebrew suchen, verwenden Sie dos2unixstatt fromdos.
Almir Sarajčić
3

Das "| xargs fromdos" liest aus der Standardeingabe (die Dateien findfinden) und verwendet sie als Argumente für den Befehl fromdos, der die Zeilenenden konvertiert. (Ist fromdos Standard in diesen Umgebungen? Ich bin an dos2unix gewöhnt). Beachten Sie, dass Sie die Verwendung von xargs vermeiden können (besonders nützlich, wenn Sie über genügend Dateien verfügen, sodass die Argumentliste für xargs zu lang ist):

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

oder

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

Ich bin mir über Ihre Fehlermeldungen nicht ganz sicher. Ich habe diese Methode erfolgreich getestet. Welches Programm produziert jeweils? Für welche Dateien / Verzeichnisse haben Sie keine Berechtigungen? Hier ist jedoch ein Versuch zu erraten, was es sein könnte:

Eine einfache Möglichkeit, einen Fehler "Datei nicht gefunden" für das Skript zu erhalten, besteht darin, einen relativen Pfad zu verwenden - verwenden Sie einen absoluten. Ebenso kann ein Berechtigungsfehler auftreten, wenn Sie Ihr Skript nicht ausführbar gemacht haben (chmod + x).

Fügen Sie Kommentare hinzu und ich werde versuchen, Ihnen dabei zu helfen, es herauszufinden!

Cascabel
quelle
Ich habe ein anderes Beispiel mit dos2unix gesehen und dachte, dies würde irgendwie Dateien in einen Ordner mit dem Namen kopieren, aber jetzt verstehe ich es. Wow, scheint jetzt offensichtlich. Danke für Ihre Hilfe!
Brian Donahue
1

okay ... unter cygwin haben wir fromdos nicht leicht verfügbar, und dieser awk-Substeb explodiert in deinem Gesicht, wenn du Leerzeichen in Pfaden zu modifizierten Dateien hast (die wir hatten), also musste ich das etwas anders machen:

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

Ein großes Lob an @lloyd für den Großteil dieser Lösung

Anton K.
quelle
-2

Befolgen Sie diese Schritte, wenn keine der anderen Antworten für Sie funktioniert:

  1. Wenn Sie unter Windows arbeiten, tun Sie git config --global core.autocrlf trueFolgendes: Wenn Sie unter Unix arbeiten, tun Sie diesgit config core.autocrlf input
  2. Lauf git rm --cached -r .
  3. Löschen Sie die Datei .gitattributes
  4. Lauf git add -A
  5. Lauf git reset --hard

Dann sollte Ihr Einheimischer jetzt sauber sein.

zs2020
quelle
4
"Ja wirklich?" Das Löschen einer .gitattributesDatei ist die Lösung für das Problem mit den Zeilenenden?
Aleksandr M
Ja, bitte adressieren Sie den Kommentar von @AleksandrM
Mr_and_Mrs_D