Git Hooks werden bei bestimmten Git Aktionen ausgelöst. Ein Hook ist ein Skript, dass einen vordefinierten Dateinamen verwenden muss und sich im Verzeichnis .git/hooks befindet. Die Skriptsprache wird durch den Shebang am Anfang des Skriptes festgelegt (z.B. #!/bin/bash).

Liste der verfügbaren Hooks: (Git Version 1.7.0.2)

applypatch-msg
commit-msg
post-commit
post-receive
post-update
pre-applypatch
pre-commit
pre-rebase
pre-receive
prepare-commit-msg
update

Die meisten Hooks fallen in einer von zwei Kategorien:

  • pre Hook Ein Pre Hook wird immer vor der eigentlichen Aktion ausgeführt (z.B. vor dem Commit). Dieser kann z.B. verwendet werden, um eine Aktion zu verifizieren (ablehnen oder annehmen). Ein Pre Hook endet mit einem Rückgabewert ungleich 0, wenn die Aktion abgebrochen wurde.

  • post Hook Ein Post Hook wird immer nach der eigentlich Aktion ausgeführt. Ein Anwendungszweck wäre z.B. das Versenden einer Benachrichtigungs-Mail. Der Rückgabewert eines Post Hook wird ignoriert, da er keinen Einfluss auf den weiteren Verlauf hat.

Werden Git Hooks eingesetzt, sollte man über folgende Punkte klar sein:

  • Hooks werden nicht über das Repository repliziert, sondern sind immer an das aktuelle Repository gebunden
  • die Geschwindigkeit kann dadurch verlangsamt werden
  • man ändert das Verhalten von Git
  • durch einen Fehler im Hook Skript wird sich diser auf das ganze Projekt und die Produktivität auswirken

Installieren eines Hooks

Ein Hook ist aktiv, wenn eine Datei mit einem genau festgelegten Namen im Verzeichnis .git/hooks existiert und diese Datei ausführbar ist. Die Liste alle möglichen Hooks kann oben entnommen werden. In vielen Distributionen werden Beispiele mitgeliefert, denen ein .sample angehängt ist. Will man einen Beispiel-Hook aktivieren, benennt man z.B. die Datei post-receive.sample in post-receive um und macht diese Datei ausführbar:

mv .git/hooks/post-receive.sample .git/hooks/post-receive
chmod +x .git/hooks/post-receive

Beispiele aus der Praxis

Source Code Check auf Syntaxfehler

Über einen pre-commit Hook überprüft man den Quellcode, ob Syntaxfehler bestehen. Falls ja, wird der Commit nicht angenommen. Um alle PHP Dateien auf Sytaxfehler zu überprüfen, legt man die Datei .git/hooks/pre-commit mit folgenden Inhalt an:

#!/usr/bin/php
<?php

$output = array();
$rc     = 0;
exec('git rev-parse --verify HEAD 2> /dev/null', $output, $rc);
if ($rc == 0)  $against = 'HEAD';
else 	       $against = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';

exec('git diff-index --cached --name-only '. $against, $output);

$needle 	   = '/(\.php|\.module|\.install)$/';
$exit_status = 0;

foreach ($output as $file) {
	if (!preg_match($needle, $file)) {
		// only check php files
		continue;
	}

	$lint_output = array();
	$rc 		 = 0;
	exec('php -l '. escapeshellarg($file), $lint_output, $rc);
	if ($rc == 0) {
		continue;
	}
	# echo implode("\n", $lint_output), "\n";
	$exit_status = 1;
}

exit($exit_status);

Damit das funktioniert, muss PHP CLI auf dem Rechner vorhanden sein. Weiterhin wäre denkbar, dass man über einen pre-commit Hook auch die Coding Standards überprüft.

Überprüfung, ob Ticket Nummer in der Commit Message verwendet wurde

Oft wird in einem Projekt ein Ticketsystem eingesetzt. Die Commit Nachrichten zum Source-Code sollten dabei die Ticket Nummer beinhalten. Dies kann man über einen Regulären Ausdruck in einen pre-commit oder pre-receive Hook lösen. Hier ein Beispiel, wie man mit dem pre-receive Hook das Vorhandensein der Ticket Nummer überprüft und zusätzlich noch überprüft, ob mindestens 3 Worte als Commit-Message verwendet wurden. Dabei muss folgende Syntax eingehalten werden: Issue, CR, Defect oder Resolved mit Ticketnummer und nachfolgenden Doppelpunkt. (Beispiel: “Issue 33: drupal core hacked for fun.”)

#! /usr/bin/perl

my $errors = 0;
while (<>) {
  chomp;
  next unless my($old,$new) =
    m[ ^ ([0-9a-f]+) \s+   # old SHA-1
         ([0-9a-f]+) \s+   # new SHA-1
         refs/heads/master # ref
       \s* $ ]x;

  chomp(my @commits = `git rev-list $old..$new`);
  if ($?) {
    warn "git rev-list $old..$new failed\n";
    ++$errors, next;
  }

  foreach my $sha1 (@commits) {
    my $msg = `git cat-file commit $sha1`;
    if ($?) {
      warn "git cat-file commit $sha1 failed";
      ++$errors, next;
    }

    # check issue number
    $msg =~ s/\A.+? ^$ \s+//smx;
    unless ($msg =~ /([Ii]ssue|CR|[Dd]efect|[Rr]esolved)\s\d+\:\s/) {
      warn "No Issue, Defect, CR or Resolve number in $sha1:\n\n" . $msg . "\nE.g. usage: \"Issue 33: drupal core hacked for fun.\"\n";
      ++$errors, next;
    }

    # check amount of words
    my %count_of;
    foreach my $word (split /\s+/, $msg) {
      $count_of++;
    }
    if ($count_of < 6) {
      warn "Commit message is to short in $sha1:\n\n" . $msg . "\nUse at least 3 words in your commit message.\n";
      ++$errors, next;
    }
  }
}
exit $errors == 0 ? 0 : 1;

Überprüfung, ob aktuelle Commit Message von der vorherigen Commit Message abweicht

Eine Commit Nachricht sollte immer individuell sein. Um zu verhindern dass immer die gleiche Meldung verwendet wird (es soll tatsächlich Entwickler geben, die das aus Bequemlichkeit machen;)), kann man einen pre-commit Hook einsetzen, der die aktuell angegebenen Message mit der vorherigen Message vergleicht.

Emailversand bei neuem/verändertem Inhalt

Wenn ein Git Repository von mehreren Personen genutzt wird, kann man über einen post-receive Hook eine Email mit Commit Informationen verschicken. Ein Beispiel-Skript dafür ist unter den meisten Linux Distributionen (Ubuntu und Debian auf jeden Fall) in der Datei /usr/share/doc/git-core/contrib/hooks/post-receive-email zu finden.

Automatisches Erstellen von Archivdateien bei Tags

Wird ein Tag gepusht, wird automatisch eine Archivdatei und eine Prüfsummendatei dazu erstellt. Das Skript wird für das Aegir hosting system eingesetzt und kann auf der koumbit.net Webseite bezogen werden.

Auslösen eines automatischen Builds (Continuous Integration)

Ein automatischer Build wird meist in Verbindung mit einem gemeinsam genutzten Repository eingesetzt. Durch den Einsatz eines post-receive Hooks kann z.B. ein Jenkins oder Cruisecontrol Prozess angestoßen werden.

Automatisches aktualisieren einer Testumgebung

Durch den Einsatz eines post-receive Hook kann mit jedem push auf ein Repository eine Testumgebung aktualisiert werden. Dazu legt man die Datei .git/hooks/post-receive an und macht sie ausführbar:

#!/bin/sh

GIT=/usr/bin/git
REPO=/www/devdays
DATE_STAMP=$(date +%Y%m%d%H%M)
LOG=/var/log/git_devdays.log

echo "Push at $DATE_STAMP $1 $2 $3" >>$LOG
cd $REPO && $GIT pull 2>>$LOG

In diesem Beispiel befindet sich eine Arbeitskopie des Repositories im Verzeichnis /www/devdays. Es macht natürlich nur Sinn ein Repository zu aktualisieren, welches durch den Einsatz von git clone aus dem Repository erzeugt wurde, welches den Hook ausführt. Des weiteren wird die Ausführung des Hooks in die Datei /var/log/git_devdays.log protokolliert. Wichtig ist hierbei, dass diese Datei von dem Benutzer beschreibbar ist, welcher den git Prozess ausführt.

Das Beispiel kann so ausgebaut werden, dass über eine SSH Verbindung auf einen anderen Server ein pull ausgeführt wird:

#!/bin/sh

GIT=/usr/bin/git
SSH=/usr/bin/ssh
REPO=/www/devdays
DATE_STAMP=$(date +%Y%m%d%H%M)
LOG=/var/log/git_devdays.log

echo "Push at $DATE_STAMP $1 $2 $3" >>$LOG
$SSH -i ~/.ssh/externer-testing-server.id_rsa git@externer-testing-server.de "cd $REPO && $GIT pull 2>>$LOG"

Wichtig ist hierbei, dass der private SSH Schlüssel für den Zugriff des externen Servers auf dem Git Repository Server vorhanden ist und dieser über den SSH Parameter -i angegeben wird. Auf dem Testingserver muss der Benutzer git vorhanden sein und der öffentliche Schlüssel in der Datei authorized_keys dieses Benutzers gelistet sein. Der Benutzer git muss für dieses Beispiel auf dem Testingserver Schreibrechte auf das Verzeichnis des Repositories besitzen, auf dem der pull ausgeführt werden soll. Hey, und keiner hält Euch davon ab, einen pull auf mehrerer Rechner auszulösen. Dazu wird die letzte Zeile des Beispiels einfach kopiert und die SSH Anweisung entsprechend angepasst.

Ressourcen

Projektmanagement Services, Consulting und Coaching

Unser Projektmanagement Team aus München ist hervorragend ausgebildet und verfügt über die nötige Expertise, Erfahrung und das Augenmaß, wenn es darum geht in Kundenprojekten die richtigen Tools einzuführen, zu konfigurieren und bestehende Prozesse zu optimieren. Unser Ziel ist es für einen reibungslosen Projektverlauf zu sorgen. Kontaktieren Sie uns unverbindlich.

Aktualisiert: