In diesem Artikel widmen wir uns den Git Hooks und ihrer Anwendung. Ein Hook ist im Wesentlichen eine Methode, um in den bestehenden Programmablauf einzugreifen, indem eigener Code an bestimmten Events ausgeführt wird.
Die Git Hooks sind also eine Möglichkeit anhand bestimmter Events eigene zusätzliche Anweisungen ausführen zu lassen von Git. Dabei gibt es zwei verschiedene Typen von Hooks zwischen den wir unterschieden. Es gibt die Client seitigen Hooks, diese werden auf deinem System ausgeführt. Es gibt auch serverseitige Hooks, aber diese werden vom Git-Server selbst (z.B. GitHub oder Gitea) dazu verwendet, eigene Events zu triggern. Zum Beispiel Webhooks oder die CI-Pipelines, welche dann die Tests ausführen. Diese Hook-Events kannst und solltest du also ignorieren.
Lokal werden die Git Hooks als Shell Scripte ausgeführt und können alles machen, was du auch über das Terminal deines Computers machen kannst. Theoretisch kannst du dort alles ausführen wie deinen Python Linter, JavaScript Formatter, Java Unit-Tests oder auch nur kleinere Aufgaben wie die Prüfung, dass keine To-do-Kommentare im Repository sind.
Die Git Hooks werden im .git/hooks
Ordner ausgehende von dem Root deiner Repository abgelegt und definiert, wenn du eine Frisches Repository initialisiert hast, findest du dort für jeden möglichen Hook eine *.sample
Datei. Diese beinhaltet ein einfaches Beispiel, um zu zeigen und zu erklären, was mit dem jeweiligen Hook möglich wäre.
Das wichtige ist der .git
Ordner wird nicht über die Versionskontrolle gemanaged, weshalb du eine Strategie benötigst wie du auch die Git Hooks versionieren kannst, dazu später mehr.
Um nun ein Simplen Hook zu erstellen, können wir eine Datei im .git/hooks
Ordner anlegen, zum Beispiel die Datei pre-commit
in dieser können wir jetzt ein Shell Script definieren was passieren soll, wenn ein Commit erstellt wird, diese Geschieht also beim Command git commit
. Ein Beispiel wäre ein „Hello from Pre Commit“ zu Schreiben ins Terminal. Die erste Zeile, die du im Script siehst, ist die Shebang Zeile, diese definiert, mit welchem Interpreter der nachfolgende Code ausgeführt werden soll.
#!/bin/bash
echo "Hello from Pre Commit Hook"
Nachdem du die Datei also unter .git/hooks/pre-commit
abgelegt hast, kannst du einmal einen Commit erstellen, nun wirst du die folgende Ausgabe erhalten.
hint: The '.git/hooks/pre-commit' hook was ignored because it's not set as executable.
hint: You can disable this warning with `git config advice.ignoredHook false`.
Diese liegt daran, dass die Datei nach dem Anlegen standardmäßig nicht ausgeführt werden kann, deshalb ist es wichtig den nachfolgenden Befehl auszuführen, um dafür zu sorgen, dass die Datei die richtige Berechtigung hat, um ausgeführt zu werden.
chmod +x .git/hooks/pre-commit
Anschließend wirst du bei einem erneuten Versuch einen Commit zu erstellen feststellen, dass du nun unseren Text auf der Command Line sehen kannst. Das ist im Prinzip bereits die ganze Magie, um mit Git Hooks arbeiten zu können, innerhalb deines Skriptes kannst du jetzt auf jegliche Linter, Formatter zugreifen, die dir in deinem Projekt zur Verfügung stehen.
Um einen Hook abzubrechen, gibt in der Shell Statuscodes, die mit exit
abgerufen werden können. Dabei bedeutet exit 0
das alles Erfolgreich war und die nachfolgenden Prozesse funktionieren wie gewohnt. Alternative dazu kannst du hinter das exit
auch einen Statuscode von 1 oder höher definieren, das bedeutet für die nachfolgenden, dass etwas nicht in Ordnung ist und der nachfolgenden Prozess wird von Git beendet.
Ein Beispiel könnte zum Beispiel sein zu prüfen, ob ein bestimmter Text im Quelltext vorhanden ist wie "TODO".
#!/bin/sh
echo "Überprüfe Bedingungen..."
# Bedingung die den Fehler verursacht
if [ -n "$(git diff --cached | grep TODO)" ]; then
echo "Fehler: Es gibt TODOs im Code!"
exit 1
fi
echo "Alle Bedingungen erfüllt."
exit 0
Dieses Beispiel schaut über git diff
, ob an einer Stelle ein TODO hinzugekommen ist und bricht dann ab. Bedenken solltest du, dass diese nur eine einfache Prüfung ist und nicht nur auf Kommentare beschränkt ist, so also auch korrekte Verwendungen im Quelltext erkennen würde.
Wie oben bereits erwähnt werden die Dateien aus dem .git/hooks
Ordner nicht gespeichert und mit in der Versionskontrolle versioniert, diese ist häufig nicht das erwartete Verhalten, was man sich wünschen würde. Um dieses Problem zu lösen, gibt es verschiedene Ideen, ein Gedanken, den persönlich interessant finde, weil dafür keine zusätzlichen Tools notwendig sind, ist das Arbeiten mit Makefile und Symlinks.
Mein Lösungsansatz ist also über ein Makefile und einen scripts
Ordner, im scripts
Ordner liegt eine Datei pre-commit.sh
und im Root gibt es ein Makefile. Dieses Makefile hat ein Phony Target (Scheinziel) zu install-pre-commit
um die Einrichtung des Symlinks auszuführen.
.PHONY: install-pre-commit
install-pre-commit:
@chmod +x ./scripts/pre-commit.sh
@ln ./scripts/pre-commit.sh .git/hooks/pre-commit
Anschließende, wenn diese Datei im Root unseres Projektes liegt und wir dort nun make install-pre-commit
ausführen wird, der Hook für uns komplett eingerichtet und kann verwendet werden. Der Vorteil von einem Symlink gegenüber einem Kopieren ist, dass wir jederzeit die Datei editieren können und die Änderungen sofort aktiv sind.
Alternative dazu, sich selber ein Setup ausdenken zu müssen, kann man auch auf gängige Lösungen über Git Hook Manager zurückgreifen.
Ein Git Hook Manager, soll dir dabei helfen, einfach zu definieren, welche Befehle an bestimmten Hooks ausgeführt werden sollen und liefern dir auch eine Möglichkeit einer einfachen Installation. Persönlich sehe ich es so, dass man sich den Einsatz eines Git Hook Managers gut überlegen sollte, den wie oben demonstriert kann, man mit Symlinks und wenigen Zeilen Code vieles Erreichen. Im Zusammenhang mit Cross-Plattform Development können sie helfen, da sie meistens guten Support für alle gängigen Betriebssysteme haben.
Husky ist einer der Populärsten Hook Manager für JavaScript Projekte, der Vorteil bei Husky zum Beispiel ist, dass es einen Ordner .husky
gibt, wo wir einfach unsere Dateien wiederum ablegen können. Beim frischen Installieren unseres Projektes werden die Hooks gleich eingerichtet, es kann nicht so leicht vergessen werden.
Lefthook ist ein Tool, das für jedes Projekt funktionieren kann, den es wird im Root eine lefthook.yml
Datei angelegt. In dieser Datei kann jeder Hook konfiguriert und nach einem kurzen installieren verwendet werden, der Vorteil ist man kann alle Konfigurationen auf einen Blick in einer Datei sehen.
Weitere Git Hook Manager wären zum Beispiel noch:
Kurz: JA. Es ist wichtig, sich immer bewusst zu machen, dass die Git Hooks nur eine zusätzliche Ebene für die Validierung sein sollten und niemals die Quelle der Wahrheit. Den wie du grade gelernt hast, ist ein manueller Schritt notwendig um die Hooks überhaupt zu aktivieren und man kann auch keine Person daran hindern alle Dateien aus dem Hooks Ordner wieder zu löschen.
Zusätzlich hat Git aber auch noch einen Parameter -n
oder --no-verify
um die Hooks auszusetzen, manchmal kann es vorkommen, dass man bewusst die Hooks nicht verwenden möchte. Zum Beispiel, wenn in einem neuen Branch an einem Prototyp gearbeitet wird und ein Kollege schon einmal drüberschauen soll, auch wenn der Quelltext noch Syntaxfehler hat.
Was bedeutet das für den Einsatz in Projekten: Sei dir immer darüber bewusst, dass sie nur eine zusätzliche Validierungs-Ebene sind, alles andere, was für das Projekt wirklich notwendig ist, sollte in entsprechenden Pipelines oder Actions abgedeckt werden.
Persönlich sehe ich es so, dass es wichtig ist, dass im Team eine klare Einheit herrscht darüber, wofür man sie einsetzen will und wofür nicht, denn Git Hooks können auch negativ zur Developer-Experience beitragen, zum Beispiel durch zu lange Laufzeiten.
Persönlich würde ich sagen, dass 20 Sekunden für einen Commit und 30–40 Sekunden für einen Push für mich akzeptable wären, alles darüber hinaus sollte in die Pipelines zum Beispiel beim Mergen auf dem Server ausgelagert werden.
Hier sind einige Anwendungsgebiete, die ich als sinnvoll erachte:
Der Fokus liegt also für mich vorwiegend darauf, Unachtsamkeit zu vermeiden und schon vor dem Veröffentlichen im Remote Repository zu bemerken. In der Recherche für diesen Artikel habe ich bemerkt, dass die Meinung dazu, in welchem Umfang die Git Hooks eingesetzt werden, stark auseinandergehen. Von Aussagen wie am besten gar nicht verwenden und auch umfassenderen Verwendungen, wo eine längere Laufzeit in Kauf genommen wird, war wirklich alles dabei. Ich empfehle euch, euch im Team abzusprechen, welche Ziele ihr mit Git Hooks erreichen möchtet und ob eine gute Pipeline einen besseren Zweck erfüllt oder Hooks das Richtige für das Projekt sind.
Hinterlasse mir gerne einen Kommentar zum Artikel und wie er dir weitergeholfen hat beziehungsweise, was dir helfen würde das Thema besser zu verstehen. Oder hast du einen Fehler entdeckt, den ich korrigieren sollte? Schreibe mir auch dazu gerne ein Feedback!
Es sind noch keine Kommentare vorhanden? Sei der/die Erste und verfasse einen Kommentar zum Artikel "Von Commits bis Push: Git Hooks für optimierte Workflows"!