2012-09-07 25 views
13

Das Internet ist übersät mit falschen und nicht idealen Antworten auf diese Frage. Das ist bedauerlich, weil Sie denken würden, dass dies eine allgemeine Sache wäre, die Sie tun möchten.Testen, was in einem Pre-Commit Hook begangen werden soll

Das Problem: Wenn ein Hook pre-commit ausgeführt wird, ist das Repository möglicherweise nicht sauber. Wenn du deine Tests also naiv durchführst, werden sie nicht gegen das sein, was du verpflichtest, sondern egal, welcher Dreck gerade in deinem Arbeitsbaum ist.

Die offensichtliche Sache zu tun ist zu git stash --keep-index --include-untracked am Anfang der pre-commit und git pop am Ausgang. Auf diese Weise testen Sie gegen den (reinen) Index, was wir wollen.

Leider generiert dies Merge-Konfliktmarker, wenn Sie git add --patch verwenden (insbesondere wenn Sie hunks bearbeiten), da der Inhalt von [email protected]{0} möglicherweise nicht mit dem Arbeitsbaum nach dem Festschreiben übereinstimmt.

Eine weitere gängige Lösung besteht darin, das Repository zu klonen und die Tests in einem neuen temporären Test auszuführen. Es gibt zwei Probleme damit: Einer ist, dass wir noch nicht festgeschrieben haben, also können wir nicht einfach eine Kopie des Repositorys in dem Zustand bekommen, den wir begehen wollen (ich bin mir sicher, dass es einen Weg dafür gibt) , aber ich bin nicht interessiert, weil :). Zweitens sind meine Tests empfindlich auf den Speicherort des aktuellen Arbeitsverzeichnisses. Zum Beispiel wegen der lokalen Umgebungskonfiguration.

Also: Wie kann ich meinen Arbeitsbaum in den Zustand vor der git stash --keep-index --include-untracked wiederherstellen, ohne Merge-Konflikt-Marker einzuführen, und ohne die Post-Commit HEAD zu ändern?

+0

Der pre-commit Skript empfängt die Daten als Eingabe begangen werden. Warum musst du etwas anderes suchen? Vielleicht, was Sie versuchen, zu tun, würde am besten in etwas anderem als einem Vorbereitungshaken getan werden. Welche Art von Tests möchten Sie durchführen, die Zugriff auf das vollständige Repository benötigen? –

+0

@WilliamPursell: Was meinen Sie mit "die Daten werden übertragen?". Das Vorab-Commit-Skript wird in meinem Arbeitsbaum (d. H. Der Basis des Quell-Repositorys) ausgeführt. Das Problem ist, dass wenn Sie einige Änderungen am Repository vornehmen und nur einige von ihnen inszenieren (zB fügen Sie einige Dateien hinzu, aber keine anderen), dann werden Sie das Commit nicht testen bevor es passiert (was ich machen möchte), Sie würden alles was Sie in Ihrem Arbeitsverzeichnis haben testen. – pwaller

+0

Der Patch, den Sie committen, ist auf der Standardeingabe für den Pre-Commit Hook verfügbar. Was testen Sie, wenn nicht der Patch, der ausgeführt wird? Der Zweck des Vorbereitungs-Hooks besteht darin, das Patch zu verifizieren. –

Antwort

2

Wenn das Klonen des gesamten Repos zu teuer ist, brauchen Sie vielleicht nur eine Kopie des Arbeitsverzeichnisses. Das Erstellen einer Kopie wäre einfacher als der Versuch, mit Konflikten umzugehen. Zum Beispiel:

#!/bin/sh -e 

trap 'rm -rf $TMPD' 0 
mkdir ${TMPD=$PWD/.tmpdir} 
git ls-tree -r HEAD | while read mod type sha name; do 
    if test "$type" = blob; then 
     mkdir -p $TMPD/$(dirname "$name") 
     git show $sha > $TMPD/"$name"; 
     chmod $mod $TMPD/"$name" 
    fi 
done 
cd $TMPD 
git diff --cached HEAD | patch 
# Run tests here 

Dadurch wird der Zustand des Baumes Dump, wie es nach dem Commit in $ TMPD sein wird, so dass Sie Ihre Tests dort ausführen können. Sie sollten ein temporäres Verzeichnis in einer sichereren Weise als hier erhalten, aber damit das finale Diff funktioniert (oder um das Skript und die CD früher zu vereinfachen), muss es ein Kind des Arbeitsverzeichnisses sein.

-1

Ich habe endlich die Lösung gefunden, nach der ich gesucht habe. Nur der Status des Index vor dem Festschreiben wird überprüft, und der Index und der Arbeitsbaum bleiben genauso wie vor dem Festschreiben.

Wenn Sie irgendwelche Probleme oder einen besseren Weg sehen, antworten Sie bitte, entweder als Kommentar oder Ihre eigene Antwort.

Dies setzt voraus, dass nichts anderes versuchen wird, das Git-Repository oder den Arbeitsbaum während der Ausführung zu speichern oder anderweitig zu ändern. Dies kommt ohne Garantie, könnte falsch sein und Ihren Code in den Wind werfen. VERWENDUNG MIT VORSICHT.

# pre-commit.sh 
REPO_PATH=$PWD 
git stash save -q --keep-index --include-untracked # ([email protected]{1}) 
git stash save -q         # ([email protected]{0}) 

# Our state at this point: 
# * clean worktree 
# * [email protected]{0} contains what is to be committed 
# * [email protected]{1} contains everything, including dirt 

# Now reintroduce the changes to be committed so that they can be tested 
git stash apply [email protected]{0} -q 

git_unstash() { 
    G="git --work-tree \"$REPO_PATH\" --git-dir \"$REPO_PATH/.git\"" 
    eval "$G" reset -q --hard    # Clean worktree again 
    eval "$G" stash pop -q [email protected]{1}  # Put worktree to original dirty state 
    eval "$G" reset -q [email protected]{0} .  # Restore index, ready for commit 
    eval "$G" stash drop -q [email protected]{0}  # Clean up final remaining stash 
} 
trap git_unstash EXIT 

... tests against what is being committed go here ... 
+0

Wie in den Kommentaren in dem folgenden Beitrag erwähnt, wird dies nicht korrekt funktionieren, wenn ein Commit geändert wird oder wenn Sie keinen schmutzig arbeitenden Baum haben. http://codeinthehole.com/writing/tips-for-using-a-git-pre-commit-hook/ – pwaller

2

Wenn Sie ein temporäres Verzeichnis verwenden leisten können (. Dh eine vollständige Kopie der aktuellen Kasse machen) Sie ein temporäres Verzeichnis wie so verwenden können:

tmpdir=$(mktemp -d) # Or put it wherever you like 
git archive HEAD | tar -xf - -C "$tmpdir" 
git diff --staged | patch -p1 -d "$tmpdir" 
cd "$tmpdir" 
... 

Dies ist im Grunde William Pursell der Lösung nutzt aber git archive Vorteil, die den Code einfacher macht, und ich erwarte, dass schneller sein wird.

Alternativ kann durch wechseln mit cd zuerst:

cd somewhere 
git -C path/to/repo archive HEAD | tar -xf - 
git -C path/to/repo diff --staged | patch -p1 
... 

git -C erfordert Git 1.8.5.

+0

Ich habe dich gewählt, aber es ist nicht "bleib in $ PWD beim Testen", was für einige Systeme notwendig ist, wie gehen Bibliotheken. – pwaller

+0

Abgesehen von Pwallers Bedenken glaube ich, dass diese Lösung nicht funktioniert, wenn Sie eine 'git commit -a' ausführen, da Ihr Skript davon ausgeht, dass wir nur gestaffelte Dateien übertragen. – TanguyP

1

git write-tree ist nützlich in pre-commit Haken. Er schreibt einen Baum in den Repo des Indexes (dieser Baum wird wiederverwendet, wenn der Commit abgeschlossen ist.)

Sobald der Baum in den Repo geschrieben wurde, können Sie git archive | tar -x verwenden, um den Baum in ein temporäres Verzeichnis zu schreiben .

Z. B .:

#!/bin/bash 

TMPDIR=$(mktemp -d) 
TREE=$(git write-tree) 
git archive $TREE | tar -x -C $TMPDIR 

# Run tests in $TMPDIR 

RESULT=$? 
rm -rf "$TMPDIR" 
exit $RESULT