webentwicklung-frage-antwort-db.com.de

Lassen Sie git vor dem Festschreiben abschließende Leerzeichen automatisch entfernen

Ich benutze git mit meinem Team und möchte Whitespace-Änderungen aus meinen Diffs, Protokollen, Zusammenführungen usw. entfernen. Ich gehe davon aus, dass der einfachste Weg dies zu tun darin besteht, dass git abschließende Whitespaces (und andere Whitespace-Fehler) automatisch entfernt ) von allen Commits, wie sie angewendet werden.

Ich habe versucht, Folgendes per ~/.gitconfig - Datei hinzuzufügen, aber beim Festschreiben wird nichts ausgeführt. Vielleicht ist es für etwas anderes konzipiert. Was ist die Lösung?

[core]
    whitespace = trailing-space,space-before-tab
[apply]
    whitespace = fix

Ich verwende Ruby für den Fall, dass jemand irgendwelche Ruby spezifische Ideen hat. Die automatische Formatierung des Codes vor dem Festschreiben wäre der nächste Schritt, aber das ist ein schweres Problem und nicht wirklich ein großes Problem verursacht.

210
mloughran

Diese Einstellungen (core.whitespace Und apply.whitespace) Dienen nicht zum Entfernen von nachgestellten Leerzeichen, sondern zum:

  • core.whitespace: Erkennen Sie diese und lösen Sie Fehler aus
  • apply.whitespace: Und entferne sie, aber nur während des Patches, nicht "immer automatisch"

Ich glaube, das git hook pre-commit würde einen besseren Job dafür machen (beinhaltet das Entfernen von nachgestellten Leerzeichen)


Beachten Sie, dass Sie jederzeit festlegen können, dass der Hook pre-commit Nicht ausgeführt werden soll:

  • vorübergehend: git commit --no-verify .
  • permanent: cd .git/hooks/ ; chmod -x pre-commit

Warnung: Standardmäßig hat ein pre-commit - Skript (wie dieses ) nicht eine Funktion zum Entfernen nachgestellter Zeichen, aber eine Warnung. Funktion wie:

if (/\s$/) {
    bad_line("trailing whitespace", $_);
}

Sie können jedoch einen besseren pre-commit - Hook erstellen, insbesondere wenn Sie Folgendes berücksichtigen:

Das Festschreiben in Git mit nur einigen Änderungen, die dem Staging-Bereich hinzugefügt wurden, führt immer noch zu einer "atomaren" Revision, die möglicherweise nie als Arbeitskopie existiert hat und möglicherweise nicht funktioniert.


Zum Beispiel schlägt oldmanin einer anderen Antwort einen pre-commit - Haken vor, der Leerzeichen erkennt und entfernt.
Da dieser Hook den Dateinamen jeder Datei erhält, würde ich empfehlen, bei bestimmten Dateitypen vorsichtig zu sein: Sie möchten abschließende Leerzeichen in .md - Dateien (Markdown) nicht entfernen!

106
VonC

Sie können Git dazu verleiten, das Leerzeichen für Sie zu reparieren, indem Sie Git dazu verleiten, Ihre Änderungen als Patch zu behandeln. Im Gegensatz zu den "Pre-Commit-Hook" -Lösungen fügen diese Lösungen Git Whitespace-Fixing-Befehle hinzu.

Ja, das sind Hacks.


Robuste Lösungen

Die folgenden Git-Aliase stammen aus my ~/.gitconfig .

Mit "robust" meine ich, dass diese Aliase fehlerfrei ausgeführt werden und das Richtige tun, unabhängig davon, ob der Baum oder der Index verschmutzt sind. Sie funktionieren jedoch nicht, wenn bereits ein interaktives git rebase -i Ausgeführt wird. Siehe mein ~/.gitconfig für zusätzliche Überprüfungen, ob Sie sich für diesen Eckfall interessieren, in dem der am Ende beschriebene git add -e - Trick funktionieren sollte.

Wenn Sie sie direkt in der Shell ausführen möchten, ohne einen Git-Alias ​​zu erstellen, kopieren Sie einfach alles in doppelte Anführungszeichen und fügen Sie es ein (vorausgesetzt, Ihre Shell ist Bash-artig).

Korrigieren Sie den Index, aber nicht den Baum

Der folgende fixws Git-Alias ​​behebt alle Whitespace-Fehler im Index, sofern vorhanden, ohne jedoch die Struktur zu berühren:

# Logic:
#
# The 'git stash save' fails if the tree is clean (instead of
# creating an empty stash :P). So, we only 'stash' and 'pop' if
# the tree is dirty.
#
# The 'git rebase --whitespace=fix HEAD~' throws away the commit
# if it's empty, and adding '--keep-empty' prevents the whitespace
# from being fixed. So, we first check that the index is dirty.
#
# Also:
# - '(! git diff-index --quiet --cached HEAD)' is true (zero) if
#   the index is dirty
# - '(! git diff-files --quiet .)' is true if the tree is dirty
#
# The 'rebase --whitespace=fix' trick is from here:
# https://stackoverflow.com/a/19156679/470844
fixws = !"\
  if (! git diff-files --quiet .) && \
     (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git stash save FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~ && \
    git stash pop && \
    git reset --soft HEAD~ ; \
  Elif (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git rebase --whitespace=fix HEAD~ && \
    git reset --soft HEAD~ ; \
  fi"

Die Idee ist, git fixws Vor git commit Auszuführen, wenn der Index Leerzeichen enthält.

Korrigieren Sie den Index und den Baum

Der folgende Git-Alias ​​fixws-global-tree-and-index Behebt alle Whitespace-Fehler im Index und in der Baumstruktur, falls vorhanden:

# The different cases are:
# - dirty tree and dirty index
# - dirty tree and clean index
# - clean tree and dirty index
#
# We have to consider separate cases because the 'git rebase
# --whitespace=fix' is not compatible with empty commits (adding
# '--keep-empty' makes Git not fix the whitespace :P).
fixws-global-tree-and-index = !"\
  if (! git diff-files --quiet .) && \
     (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git add -u :/ && \
    git commit -m FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~2 && \
    git reset HEAD~ && \
    git reset --soft HEAD~ ; \
  Elif (! git diff-files --quiet .) ; then \
    git add -u :/ && \
    git commit -m FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~ && \
    git reset HEAD~ ; \
  Elif (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git rebase --whitespace=fix HEAD~ && \
    git reset --soft HEAD~ ; \
  fi"

Führen Sie Folgendes aus, um auch Leerzeichen in nicht versionierten Dateien zu korrigieren

git add --intent-to-add <unversioned files> && git fixws-global-tree-and-index

Einfache aber nicht robuste Lösungen

Diese Versionen sind einfacher zu kopieren und einzufügen, aber sie tun nicht das Richtige, wenn ihre Nebenbedingungen nicht erfüllt sind.

Korrigieren Sie den im aktuellen Verzeichnis verwurzelten Teilbaum (aber setzen Sie den Index zurück, wenn er nicht leer ist)

Verwenden Sie git add -e, Um die Patches mit dem Identitätseditor : Zu "bearbeiten":

(export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .) && git checkout . && git reset

Korrigieren und erhalten Sie den Index (schlägt jedoch fehl, wenn der Baum verschmutzt oder der Index leer ist)

git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset --soft HEAD~

Korrigieren Sie den Baum und den Index (aber setzen Sie den Index zurück, wenn er nicht leer ist)

git add -u :/ && git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset HEAD~

Erklärung des Tricks export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .

Bevor ich den git rebase --whitespace=fix - Trick durch diese Antwort kennengelernt habe, habe ich überall den komplizierteren git add - Trick verwendet.

Wenn wir es manuell gemacht haben:

  1. Setzen Sie apply.whitespace Auf fix (Sie müssen dies nur einmal tun):

    git config apply.whitespace fix
    

    Dies weist Git an, Leerzeichen in Patches zu korrigieren.

  2. Überzeugen Sie Git, Ihre Änderungen als Patch zu behandeln:

    git add -up .
    

    Schlagen a+enterum alle Änderungen für jede Datei auszuwählen. Sie erhalten eine Warnung, dass Git Ihre Whitespace-Fehler behebt.
    (git -c color.ui=auto diff An dieser Stelle zeigt, dass Ihre nicht indizierten Änderungen genau die Leerzeichenfehler sind).

  3. Entfernen Sie die Whitespace-Fehler aus Ihrer Arbeitskopie:

    git checkout .
    
  4. Bringen Sie Ihre Änderungen zurück (wenn Sie nicht bereit sind, sie festzuschreiben):

    git reset
    

GIT_EDITOR=: Bedeutet, : Als Editor zu verwenden, und als Befehl ist : Die Identität.

43
ntc2

Ich habe einen git Pre-Commit-Hook, der nachfolgende Leerzeichen entfernt gefunden.

#!/bin/sh

if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
   against=HEAD
else
   # Initial commit: diff against an empty tree object
   against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -r 's/:[0-9]+:.*//' | uniq` ; do
   # Fix them!
   sed -i 's/[[:space:]]*$//' "$FILE"
   git add "$FILE"
done
exit
29
cmcginty

Unter Mac OS (oder wahrscheinlich einem BSD) müssen sich die sed-Befehlsparameter geringfügig unterscheiden. Versuche dies:

#!/bin/sh

if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
   against=HEAD
else
   # Initial commit: diff against an empty tree object
   against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -E 's/:[0-9]+:.*//' | uniq` ; do
    # Fix them!
    sed -i '' -E 's/[[:space:]]*$//' "$FILE"
    git add "$FILE"
done

Speichern Sie diese Datei als .git/hooks/pre-commit - oder suchen Sie die Datei, die bereits vorhanden ist, und fügen Sie den unteren Teil irgendwo hinein. Und denken Sie daran, es auch zu chmod a+x.

Oder für die globale Verwendung (über Git-Commit-Hooks - globale Einstellungen ) können Sie es in $GIT_PREFIX/git-core/templates/hooks Einfügen (wobei GIT_PREFIX/usr oder/usr/local oder/usr/share oder/opt ist/local/share) und führen Sie git init in Ihren vorhandenen Repos aus.

Nach git help init:

Das Ausführen von git init in einem vorhandenen Repository ist sicher. Es werden keine Dinge überschrieben, die bereits vorhanden sind. Der Hauptgrund für die erneute Ausführung von git init besteht darin, neu hinzugefügte Vorlagen zu übernehmen.

19
AlexChaffee

Ich überlasse diese Aufgabe lieber Ihrem Lieblingsredakteur.

Setzen Sie einfach einen Befehl, um nachgestellte Leerzeichen beim Speichern zu entfernen.

10
Giacomo

Bitte versuchen Sie meine Pre-Commit Hooks , es kann automatisch abschließende Leerzeichen erkennen und entferne es. Vielen Dank!

es kann unter GitBash(windows), Mac OS X and Linux arbeiten!


Schnappschuss:

$ git commit -am "test"
auto remove trailing whitespace in foobar/main.m!
auto remove trailing whitespace in foobar/AppDelegate.m!
[master 80c11fe] test
1 file changed, 2 insertions(+), 2 deletions(-)
9
oldman

Ich habe diesen Pre-Commit-Hook geschrieben, der nur den nachgestellten Leerraum aus den von Ihnen geänderten/hinzugefügten Zeilen entfernt, da die vorherigen Vorschläge dazu neigen, unlesbare Commits zu erstellen, wenn die Zieldateien zu viel nachgestellten Leerraum haben.

#!/bin/sh

if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
   against=HEAD
else
   # Initial commit: diff against an empty tree object
   against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

IFS='
'

files=$(git diff-index --check --cached $against -- | sed '/^[+-]/d' | Perl -pe 's/:[0-9]+:.*//' | uniq)
for file in $files ; do
    diff=$(git diff --cached $file)
    if test "$(git config diff.noprefix)" = "true"; then
        prefix=0
    else
        prefix=1
    fi
    echo "$diff" | patch -R -p$prefix
    diff=$(echo "$diff" | Perl -pe 's/[ \t]+$// if m{^\+}')
    out=$(echo "$diff" | patch -p$prefix -f -s -t -o -)
    if [ $? -eq 0 ]; then
        echo "$diff" | patch -p$prefix -f -t -s
    fi
    git add $file
done
9
urandom

Verwenden von Git-Attributen und Filtern, die mit Git Config eingerichtet wurden

OK, dies ist ein neuer Ansatz zur Lösung dieses Problems. Mein Ansatz ist, keine Hooks zu verwenden, sondern Filter und Git-Attribute zu verwenden. Damit können Sie auf jedem Computer, auf dem Sie entwickeln, eine Reihe von Filtern einrichten, die zusätzliche Leerzeichen und Leerzeilen am Ende der Dateien entfernen, bevor Sie sie festschreiben. Richten Sie dann eine .gitattributes-Datei ein, in der angegeben ist, auf welche Dateitypen der Filter angewendet werden soll. Die Filter bestehen aus zwei Phasen: clean, die beim Hinzufügen von Dateien zum Index angewendet werden, und smudge, die beim Hinzufügen zum Arbeitsverzeichnis angewendet werden.

Bitten Sie Ihren Git, nach einer globalen Attributdatei zu suchen

Weisen Sie zuerst Ihre globale Konfiguration an, eine globale Attributdatei zu verwenden:

git config --global core.attributesfile ~/.gitattributes_global

Erstellen Sie globale Filter

Erstellen Sie jetzt den Filter:

git config --global filter.fix-eol-eof.clean fixup-eol-eof %f
git config --global filter.fix-eol-eof.smudge cat
git config --global filter.fix-eol-eof.required true

Fügen Sie die sed scripting Magie hinzu

Schließlich setzen Sie die fixup-eol-eof Skript irgendwo auf Ihrem Pfad, und machen Sie es ausführbar. Das Skript verwendet sed, um einige Änderungen im Handumdrehen vorzunehmen (Leerzeichen und Leerzeichen am Zeilenende und überflüssige Leerzeichen am Ende der Datei entfernen).

fixup-eol-eof sollte so aussehen:

#!/bin/bash
sed -e 's/[  ]*$//' -e :a -e '/^\n*$/{$d;N;ba' -e '}' $1

mein Kern

Teilen Sie git mit, auf welche Dateitypen der neu erstellte Filter angewendet werden soll

Zuletzt erstellen oder öffnen Sie ~/.gitattributes_global in Ihrem Lieblingseditor und fügen Sie folgende Zeilen hinzu:

pattern attr1 [attr2 [attr3 […]]]

Wenn wir also das Whitespace-Problem beheben möchten, fügen wir für alle unsere c-Quelldateien eine Zeile hinzu, die wie folgt aussieht:

*.c filter=fix-eol-eof

Diskussion des Filters

Der Filter hat zwei Phasen, die Bereinigungsphase, die angewendet wird, wenn Dinge zum Index hinzugefügt oder eingecheckt werden, und die Verschmierungsphase, wenn git Dinge in Ihr Arbeitsverzeichnis legt. In diesem Fall führt unser Smudge lediglich den Inhalt über den Befehl cat aus, der sie unverändert lassen sollte, mit der Ausnahme, dass möglicherweise ein nachgestelltes Zeilenumbruchzeichen hinzugefügt wird, falls am Ende der Datei kein vorhanden war. Der Befehl clean ist die Whitespace-Filterung, die ich aus Notizen unter http://sed.sourceforge.net/sed1line.txt zusammengesetzt habe. Es scheint, dass es in ein Shell-Skript eingefügt werden muss. Ich konnte nicht herausfinden, wie der sed-Befehl eingefügt werden soll, einschließlich der Bereinigung der überflüssigen zusätzlichen Zeilen am Ende der Datei direkt in die git-config-Datei. (Sie [~ # ~] können [~ # ~] abschließende Leerzeichen entfernen, ohne dass ein separates sed-Skript erforderlich ist das filter.fix-eol-eofzu etwas wie sed 's/[ \t]*$//' %f bei dem die \t ist eine aktuelle Registerkarte, indem Sie die Tabulatortaste drücken.)

Mit require = true wird ein Fehler ausgelöst, wenn etwas schief geht, um Probleme zu vermeiden.

Bitte verzeihen Sie mir, wenn meine Sprache in Bezug auf Git ungenau ist. Ich glaube, ich habe ein ziemlich gutes Verständnis für die Konzepte, lerne aber immer noch die Terminologie.

8
zbeekman

Hier ist eine Ubuntu + Mac OS X kompatible Version:

#!/bin/sh
#

# A git hook script to find and fix trailing whitespace
# in your commits. Bypass it with the --no-verify option
# to git-commit
#

if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
  against=HEAD
else
  # Initial commit: diff against an empty tree object
  against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | (sed -r 's/:[0-9]+:.*//' > /dev/null 2>&1 || sed -E 's/:[0-9]+:.*//') | uniq` ; do
  # Fix them!
  (sed -i 's/[[:space:]]*$//' "$FILE" > /dev/null 2>&1 || sed -i '' -E 's/[[:space:]]*$//' "$FILE")
  git add "$FILE"
done

# Now we can commit
exit

Habe Spaß

6
sdepold

Habe heute darüber nachgedacht. Das ist alles, was ich für ein Java Projekt getan habe:

egrep -rl ' $' --include *.Java *  | xargs sed -i 's/\s\+$//g'
5
Grant Murphy

Für Erhabener Text Benutzer.

Stellen Sie Folgendes in Ihrer Setting-User Konfiguration richtig ein.

"trim_trailing_white_space_on_save": true

2
Haris Krajina

die for-Schleife für Dateien verwendet die Shell-Variable $ IFS. Im angegebenen Skript werden Dateinamen mit einem darin enthaltenen Zeichen, das sich auch in der $ IFS-Variablen befindet, als zwei verschiedene Dateien in der for-Schleife angezeigt. Dieses Skript behebt das Problem: Der Multiline-Modus-Modifikator, wie er in sed-manual angegeben ist, scheint auf meiner Ubuntu-Box nicht standardmäßig zu funktionieren, daher habe ich nach einer anderen Implementierung gesucht und diese mit einem iterierenden Label gefunden letzte Zeile der Datei, wenn ich es richtig verstanden habe.

#!/bin/sh
#

# A git hook script to find and fix trailing whitespace
# in your commits. Bypass it with the --no-verify option
# to git-commit
#

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

SAVEIFS="$IFS"
# only use new-line character as seperator, introduces EOL-bug?
IFS='
'
# Find files with trailing whitespace
for FILE in $(
    git diff-index --check --cached $against -- \
    | sed '/^[+-]/d' \
    | ( sed -r 's/:[0-9]+:.*//' || sed -E 's/:[0-9]+:.*//' ) \
    | uniq \
)
do
# replace whitespace-characters with nothing
# if first execution of sed-command fails, try second one( MacOSx-version)
    (
        sed -i ':a;N;$!ba;s/\n\+$//' "$FILE" > /dev/null 2>&1 \
        || \
        sed -i '' -E ':a;N;$!ba;s/\n\+$//' "$FILE" \
    ) \
    && \
# (re-)add files that have been altered to git commit-tree
#   when change was a [:space:]-character @EOL|EOF git-history becomes weird...
    git add "$FILE"
done
# restore $IFS
IFS="$SAVEIFS"

# exit script with the exit-code of git's check for whitespace-characters
exec git diff-index --check --cached $against --

[1] sed-subsition pattern: Wie kann ich eine neue Zeile (\ n) mit sed ersetzen? .

2
imme

Dies entfernt nicht das Leerzeichen automatisch vor einem Commit, aber es ist ziemlich einfach zu bewirken. Ich habe das folgende Perl-Skript in eine Datei mit dem Namen git-wsf (git whitespace fix) in ein Verzeichnis in $ PATH geschrieben, damit ich:

git wsf | Sch

und es entfernt alle Leerzeichen nur aus Zeilen von Dateien, die git als Diff ausgibt.

#! /bin/sh
git diff --check | Perl -x $0
exit

#! /usr/bin/Perl

use strict;

my %stuff;
while (<>) {
    if (/trailing whitespace./) {
        my ($file,$line) = split(/:/);
        Push @{$stuff{$file}},$line;
    }
}

while (my ($file, $line) = each %stuff) {
    printf "ex %s <<EOT\n", $file;
    for (@$line) {
        printf '%ds/ *$//'."\n", $_;
    }
    print "wq\nEOT\n";
}
1
davidc

Verwenden Sie ed, um abschließende Leerzeichen am Zeilenende einer Datei portabel zu löschen:

test -s file &&
   printf '%s\n' H ',g/[[:space:]]*$/s///' 'wq' | ed -s file
0
nat

Etwas spät aber da dies jemandem da draußen helfen könnte, geht es los.

Öffnen Sie die Datei in VIM. Geben Sie Folgendes in die vim-Befehlszeile ein, um Tabulatoren durch Leerzeichen zu ersetzen

:%s#\t#    #gc

Andere nachgestellte Leerzeichen entfernen

:%s#\s##gc

Das hat es so ziemlich für mich getan. Es ist mühsam, wenn Sie viele Dateien bearbeiten müssen. Aber ich fand es einfacher, als Hooks vorab festzuschreiben und mit mehreren Editoren zu arbeiten.

0
hriddle