Git Pre-Commit-Hook: Geänderte / hinzugefügte Dateien

71

Ich schreibe einen Pre-Commit-Hook. Ich möchte rennenphp -l gegen alle Dateien mit der Erweiterung .php laufen. Ich stecke jedoch fest.

Ich muss eine Liste der neuen / geänderten Dateien erhalten, die bereitgestellt werden. gelöschte Dateien sollten ausgeschlossen werden.

Ich habe versucht, git diffund zu verwenden git ls-files, aber ich denke, ich brauche hier eine Hand.

igorw
quelle
Das ist ziemlich nett Teilweise bereitgestellte Dateien werden jedoch nicht verarbeitet. Siehe meinen Kommentar zur Antwort von @ LarryH.
igorw

Antworten:

50

git diff --cached --name-status zeigt eine Zusammenfassung der Inszenierungen an, sodass Sie entfernte Dateien einfach ausschließen können, z.

M       wt-status.c
D       wt-status.h

Dies zeigt an, dass wt-status.c geändert und wt-status.h im Staging-Bereich (Index) entfernt wurde. So überprüfen Sie nur Dateien, die nicht entfernt wurden:

steve@arise:~/src/git <master>$ git diff --cached --name-status | awk '$1 != "D" { print $2 }'
wt-status.c
wt-status.h

Sie müssen durch zusätzliche Rahmen springen, um mit Dateinamen mit Leerzeichen umzugehen (-z Option zum Git-Diff und einige interessantere Analyse).

araqnid
quelle
Danke, das ist ein guter Anfang. Wenn ich jedoch eine Datei ändere, ohne sie bereitzustellen, wird sie weiterhin angezeigt. Ich verwende die Git-Version 1.7.0.1.147.g6d84b (aktueller benutzerdefinierter Build). Nicht sicher, ob dies beabsichtigtes Verhalten ist.
igorw
Das klingt seltsam. Mit dem Schalter "--cached" sollten nur Dateien angezeigt werden, die bereitgestellt wurden: Obwohl ich dies mit 1.6.5 teste, scheint es überraschend, dass sich dies geändert hätte ... "git diff --cached" auf seinem eigene zeigen die nicht inszenierten Änderungen?
Araqnid
Nach einigem Debuggen konnte ich es auf eine andere Ursache zurückführen. Vielen Dank!
igorw
@igorw, ich wäre interessiert, aber der Link ist tot.
Simon
Nur um zu beachten, ob das einzige, was gewünscht wird, der Name der Datei ist, gibt es - nur Name statt - Name-Status. Könnte den extra awk Reifen schneiden.
LP
94

Ein etwas übersichtlicherer Weg, um dieselbe Liste zu erhalten, ist:

git diff --cached --name-only --diff-filter=ACM

Dies gibt die Liste der Dateien zurück, die überprüft werden müssen.

Es ist jedoch php -lmöglicherweise nicht richtig, nur Ihre Arbeitskopie zu verwenden. Wenn Sie ein partielles Commit durchführen, dh nur eine Teilmenge der Unterschiede zwischen Ihrem aktuellen Arbeitssatz und dem HEAD für das Commit auswählen, wird der Test für Ihren Arbeitssatz ausgeführt, es wird jedoch ein Commit zertifiziert, das auf Ihrem Commit noch nie vorhanden war Scheibe.

Um es richtig zu machen, sollten Sie das gesamte inszenierte Bild in einen temporären Bereich extrahieren und dort den Test durchführen.

rm -rf $TEMPDIR
mkdir -p $TEMPDIR
git checkout-index --prefix=$TEMPDIR/ -af
git diff --cached --name-only --diff-filter=ACM | xargs -n 1 -I '{}' \bin\echo TEMPDIR/'{}' | grep \\.php | xargs -n 1 php -l

Siehe Erstellen eines besseren Pre-Commit-Hooks für Git weitere Implementierung finden .

LarryH
quelle
4
Es ist tatsächlich möglich, den Dateiinhalt an weiterzuleiten php -l. Und damit sind wir gelandet. Siehe hier: github.com/phpbb/phpbb3/blob/develop-olympus/git-tools/hooks/…
igorw
2
Sie können die Syntax einer bereitgestellten Datei überprüfen git show :FILENAME | php -l.
Aad Mathijssen
6
--diff-filter sollte wahrscheinlich "ACMR" sein, da umbenannte Dateien (R) ebenfalls Änderungen aufweisen können.
Droopycom
14

Keine der Antworten hier unterstützt Dateinamen mit Leerzeichen. Der beste Weg dafür ist, das -zFlag in Kombination mit hinzuzufügenxargs -0

git diff --cached --name-only --diff-filter=ACM -z | xargs -0 ...

Dies wird von git in integrierten Beispielen angegeben (siehe .git / hooks / pre-commit.sample ).

Eddygeek
quelle
13

Folgendes verwende ich für meine Perl-Checks:

#!/bin/bash

while read st file; do
    # skip deleted files
    if [ "$st" == 'D' ]; then continue; fi

    # do a check only on the perl files
    if [[ "$file" =~ "(.pm|.pl)$" ]] && ! perl -c "$file"; then
        echo "Perl syntax check failed for file: $file"
        exit 1
    fi
done < <(git diff --cached --name-status)

für PHP wird es so aussehen:

#!/bin/bash

while read st file; do
    # skip deleted files
    if [ "$st" == 'D' ]; then continue; fi
    # do a check only on the php files
    if [[ "$file" =~ ".php$" ]] && ! php -l "$file"; then
        echo "PHP syntax check failed for file: $file"
        exit 1
    fi
done < <(git diff --cached --name-status)
Marian HackMan Marinov
quelle
2
Ziemlich gut, funktioniert aber nicht für teilweise bereitgestellte Dateien, da die gesamte Datei gelesen wird.
igorw
Vielen Dank ! Ich habe Ihren Code angepasst und <<< $ (git diff --cached --name-status) nach dem Fertigstellen stattdessen mit einer Pipe gesetzt, sodass keine Subshell in der Schleife gestartet wird. Es ermöglicht die Verwendung der Variablenaktualisierung in der Schleife. Senden einer Aktualisierung der Antwort zur Überprüfung. Best
lcetinsoy
Ich kann meinen Kommentar nicht noch einmal bearbeiten, so dass Synthax tatsächlich '<< $ (Befehl) wie stackoverflow.com/a/7390610/5203829
lcetinsoy
0

git diff --cached ist nicht ausreichend, wenn der Festschreibungsaufruf mit dem Flag -a angegeben wurde, und es gibt keine Möglichkeit festzustellen, ob dieses Flag in den Hook geworfen wurde. Es wäre hilfreich, wenn die zu feststellenden Argumente dem Haken zur Prüfung zur Verfügung stehen sollten.

mpersico
quelle
git diff - cached DOES scheint ausreichend zu sein. Ich glaube jedoch, dass, wenn Sie git status --porcelain in Ihrem Hook ausführen, alle Dateien, die verarbeitet werden, kein Leerzeichen oder ein? in der ersten Position des Ausgangs. Ich habe es noch nicht vollständig getestet, aber bisher hat es unter allen Bedingungen, die ich in meinem Repo habe, eine Mischung aus neuen, hinzugefügten, geänderten Dateien, in denen ich versuche, explizite Dateien festzuschreiben, die Standarddateien, - ein für alles. Warum also git status anstelle von git diff verwenden? Ich denke, es ist einfacher zu analysieren.
Mpersico
git status --porcelain | grep -E -v '^[? ]'
Mpersico
git status --porcelain | perl -ane 'print $F[1],qq(\n) if m/^[ACM] /' ist eine bessere Antwort. Es hat den Vorteil, dass eine --porcelain-Option verwendet wird, die sich garantiert nie ändert. Verwenden Sie Ihren eigenen Parser, wenn Perl zu schwer für Sie ist.
Mpersico