Software-Entwicklung mit Git: Einführung in Git-Hooks

Git-Hooks sind Skripte, die jedes Mal automatisch ausgeführt werden, wenn in einem Git-Repository ein bestimmtes Ereignis eintritt. Auf diese Weise lässt sich das interne Verhalten von Git anpassen, indem individualisierbare Aktionen an Schlüsselstellen des Entwicklungszyklus' ausgelöst werden.

Git Hooks

Das Vorantreiben einer Commit-Policy, die Veränderung der Projektumgebung in Abhängigkeit vom Status des Repositorys und die Implementierung von Continuous-Integration-Workflows sind gebräuchliche Anwendungsfälle für Git Hooks. Doch da Skripte ungeheuer anpassbar sind, können Hooks dazu dienen, praktisch jeden Aspekte des Entwicklungs-Workflows zu automatisieren oder zu optimieren.

In dieser Artikelserie befassen wir uns zunächst mit einem konzeptionellen Überblick, ehe wir uns einige der beliebtesten Hooks für die Nutzung in lokalen und serverseitigen Repositories ansehen.

Konzeptioneller Überblick

Alle Git-Hooks sind gewöhnliche Scripte, die Git ausführt, wenn im Repo bestimmte Ereignisse eintreten. Dadurch lassen sie sich sehr einfach installieren und konfigurieren.

Hooks können sowohl in lokalen als auch in serverseitigen Repos liegen, und sie werden nur ausgeführt als Reaktion auf Aktionen in diesem Repository. In einem Folgeartikel sehen wir uns Hook-Kategorien genauer an. Die Konfigurationen, die wir jetzt besprechen, gelten für beide Formen: lokale und serverseitige Hooks.

Installation von Hooks

Hooks liegen in jedem Repository im Verzeichnis .git/hooks. Dieses Verzeichnis mit Beispiels-Skripten legt Git automatisch an, wenn wir ein Repo initialisieren. Wenn wir einen Blick hinein werfen, finden wir die folgenden Dateien:

applypatch-msg.sample       pre-push.sample
commit-msg.sample           pre-rebase.sample
post-update.sample          prepare-commit-msg.sample
pre-applypatch.sample       update.sample
pre-commit.sample

Dies sind die meisten der verfügbaren Hooks, aber die Erweiterung .sample verhindert, dass sie standardmäßig ausgeführt werden. Um einen Hook zu 'installieren', müssen wir nur diese Erweiterung entfernen. Wenn wir alternativ ein neues Skript von grundauf schreiben, fügen wir die neue Datei mit einem der passenden Dateinamen ohne .sample-Erweiterung hinzu.

Als Beispiel versuchen wir, einen einfachen prepare-commit-msg-Hook zu installieren. Wir entfernen die Erweiterung .sample und fügen der Datei Folgendes hinzu:

#!/bin/sh

echo "# Bitte fuege eine sinnvolle Commit-Message hinzu!" > $1

Hooks müssen ausführbar sein. Also kann es sein, dass wir die Datei-Berechtigungen des Skripts ändern müssen, wenn wir es ganz neu erstellen. Um z.B. zu gewährleisten, dass prepare-commit-msg ausführbar ist, nutzen wir den folgenden Befehl:

chmod +x prepare-commit-msg

Jedes Mal, wenn wir git commit ausführen, sollten wir nun diese Nachricht anstelle der Standard-Commit-Message sehen. Wie das tatsächlich funktioniert, diskutieren wir später ausführlich. An dieser Stelle soll es vorerst genügen zu wissen, dass wir einige der internen Funktionen von Git anpassen können.

Die nativen Sample-Skripte sind sehr nützliche Referenzen, denn sie dokumentieren die Parameter, die mit jedem Hook übergeben werden. (Sie variieren von Hook zu Hook.)

Skriptsprachen

Die nativen Skripte sind zumeist Shell- und PERL-Skripte, aber wir können jede beliebige Skriptsprache verwenden, solange sie als Executable ausgeführt werden kann. Die Shebang-Zeile (#!/bin/sh) in einem jeden Skript definiert, wie unsere Datei interpretiert werden sollte. Wenn wir also eine andere Sprache nutzen, brauchen wir nur den Pfad unseres Interpreters anzugeben.

Wir können beispielsweise ein ausführbares Python-Skript in der prepare-commit-msg-Datei schreiben, statt Shell-Kommandos zu nutzen. Der folgende Hook tut dasselbe wie das Shell-Skript im vorherigen Abschnitt:

#!/usr/bin/env python

import sys, os

commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'w') as f:
    f.write("# Bitte fuege eine sinnvolle Commit-Message hinzu!")

Die erste Zeile haben wir so verändert, dass sie auf den Python-Interpreter verweist. Und statt $1 zu nutzen, um das erste Argument abzurufen, das dem Skript übergeben wird, verwenden wir sys.argv[1].

Das ist ein sehr mächtiges Feature für Git-Hooks, da wir genau in der Sprache arbeiten können, die uns am besten liegt.

Anwendungsbereich von Hooks

Hooks sind lokal und werden nicht in das neue Repo kopiert, wenn wir git clone ausführen. Und da Hooks lokal sind, kann jeder sie anpassen, der auf das Repository zugreifen kann.

Das hat wichtige Auswirkungen, wenn wir Hooks für ein Team von Entwicklern konfigurieren. Erstens müssen wir einen Weg finden, um sicherzustellen, dass Hooks über die Teammitglieder hinweg aktuell bleiben. Zweitens können wir Entwickler nicht zwingen, Commits zu erstellen, die auf eine bestimmte Art aussehen – wir können sie nur dazu animieren.

Die Pflege von Hooks für ein Entwicklungsteam kann ein bisschen kniffelig sein, weil das Verzeichnis .git/hooks wie gesagt nicht mit dem Rest des Projekts geklont wird. Es fällt auch nicht unter eine Versionskontrolle. Eine einfache Lösung für beide Probleme besteht darin, unsere Hooks im tatsächlichen Projektverzeichnis zu speichern (oberhalb des .git-Verzeichnisses). Dann können wir sie editieren wie alle anderen versionskontrollierten Dateien. Um einen Hook zu installieren, können wir entweder in .git/hooks einen Symlink auf ihn erstellen, oder wir kopieren den Hook einfach immer dann ins Verzeichnis .git/hooks, wann immer er aktualisiert wurde.

Als Alternative bietet Git einen Template-Verzeichnis-Mechanismus, der es vereinfacht, Hooks automatisch zu installieren. Alle Dateien und Verzeichnisse, die in diesem Template-Verzeichnis enthalten sind, werden jedes Mal in das .git-Verzeichnis kopiert, wenn wir git init oder git clone nutzen.

Alle lokalen Hooks, die wir in den folgenden Beiträgen beschreiben, können vom Owner eines Repositorys angepasst oder auch komplett gelöscht werden. Es liegt bei jedem Teammitglied selbst, ob es einen Hook tatsächlich nutzt oder nicht. Mit diesem Gedanken im Hinterkopf denken wir uns Git-Hooks am besten als ein bequemes Entwickler-Tool statt als strikt vorgegebene Entwicklungsrichtlinie.

Gleichwohl ist es möglich, Commits zurückzuweisen, wenn sie nicht gewissen Standards im Hinblick auf serverseitige Hooks entsprechen. Das diskutieren wir in einem kommenden Artikel.

Git und Bitbucket Server effektiv nutzen? Wir sind Ihr Partner!

Kennen Sie Bitbucket Server (vormals Stash), Atlassians Git-Repository-Managementsystem? Bitbucket Server bietet eine zentrale Lösung zum Management des gesamten distributierten Codes: Hier kommen alle Git-Repositories im Unternehmen zusammen, hier finden Entwickler immer die letzte offizielle Version eines Projekts, hier können Projektverantwortliche Berechtigungen kontrollieren, um sicherzustellen, dass die richtigen Nutzer Zugriff auf den richtigen Code haben. Möchten Sie mehr erfahren? Wir sind offizieller Vertriebspartner von Atlassian und einer der größten Atlassian Experts Partner weltweit. Gerne unterstützen wir Sie bei der Evaluierung, Lizenzierung und Adaption von Bitbucket Server.

Übrigens: //SEIBERT/MEDIA bietet auch professionelle Grundlagen- und Aufbau-Workshops zu Git und Bitbucket Server an.

Weiterführende Infos

99 Argumente für Bitbucket Server (Stash) als Git-Repository-Manager
Branch-basierte Git-Workflows adaptieren
Echte Integration: Das Zusammenspiel von JIRA, Stash und Bamboo
Interview: Die Vorteile von Git in der Software-Entwicklung und die Möglichkeiten von Bitbucket Server (Stash)
So funktioniert die Lizenzierung von Atlassian-Produkten