Software-Entwicklung mit Git: Lokale Git-Hooks (Teil 2)

Hooks sind Skripte, die ausgeführt werden, wenn in Git bestimmte Ereignisse eintreten (siehe die Einführung ins Thema). Dabei wird zwischen lokalen und serverseitigen Git-Hooks unterschieden. Erstere beschäftigen uns in dieser Artikelserie. Nachdem wir im ersten Beitrag die Skripte pre-commit und prepare-commit-msg näher angesehen haben, wollen wir uns nun den Hooks commit-msg und post-commit widmen.

Commit-Message

Der Hook commit-msg ist dem prepare-commit-msg-Hook sehr ähnlich, jedoch wird er aufgerufen, nachdem der Nutzer eine Commit-Nachricht eingegeben hat. Er eignet sich dazu, die Entwickler zu warnen, wenn die Commit-Message nicht den Standards des Teams entspricht.

Das einzige Argument, das diesem Hook übergeben wird, ist der Name der Datei, die die Nachricht enthält. Wenn dem Hook die Commit-Message nicht gefällt, kann er diese Datei abändern oder den Commit durch einen Exit mit einem Nicht-Null-Status verwerfen.

Das folgende Skript führt eine Überprüfung durch, um sicherzustellen, dass der Nutzer den String ISSUE-[#] nicht gelöscht hat, der automatisch vom Hook prepare-commit-msg generiert wurde:

#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# Collect the parameters
commit_msg_filepath = sys.argv[1]

# Figure out which branch we're on
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
print "commit-msg: On branch '%s'" % branch

# Check the commit message if we're on an issue branch
if branch.startswith('issue-'):
    print "commit-msg: Oh hey, it's an issue branch."
    result = re.match('issue-(.*)', branch)
    issue_number = result.group(1)
    required_message = "ISSUE-%s" % issue_number

    with open(commit_msg_filepath, 'r') as f:
        content = f.read()
        if not content.startswith(required_message):
            print "commit-msg: ERROR! The commit message must start with '%s'" % required_message
            sys.exit(1)

Dieses Skript wird jedes Mal ausgeführt, wenn wir einen Commit erstellen. Mehr zu tun, als die Commit-Message zu prüfen, sollten wir vermeiden. Wenn wir andere Dienste benachrichtigen müssen, dass ein Snapshot committet wurde, hilft uns stattdessen der Hook post-commit.

Nach dem Commit

Unmittelbar nach dem commit-msg-Hook wird der Hook post-commit aufgerufen. Er kann das Ergebnis der git commit-Operation nicht verändern und dient damit vor allem Benachrichtigungszwecken.

Das Skript holt keine Parameter und sein Exit-Status beeinflusst den Commit in keiner Weise. Bei den meisten post-commit-Skripten möchten wir auf den Commit zugreifen, der gerade erstellt wurde. Wir können git rev-parse HEAD nutzen, um den SHA1-Hash des neuen Commit zu erhalten, oder mit git log -l HEAD all seine Informationen bekommen.

Wenn wir beispielsweise möchten, dass unser Chef jedes Mal eine E-Mail erhält, wenn wir einen Snapshot committen (was für die meisten Workflows wohl eher nicht die beste Idee ist 😉 ), können wir den folgenden post-commit-Hook hinzufügen:

#!/usr/bin/env python

import smtplib
from email.mime.text import MIMEText
from subprocess import check_output

# Get the git log --stat entry of the new commit
log = check_output(['git', 'log', '-1', '--stat', 'HEAD'])

# Create a plaintext email message
msg = MIMEText("Look, I'm actually doing some work:\n\n%s" % log)

msg['Subject'] = 'Git post-commit hook notification'
msg['From'] = 'mary@example.com'
msg['To'] = 'boss@example.com'

# Send the message
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587

session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo()
session.login(msg['From'], 'secretPassword')

session.sendmail(msg['From'], msg['To'], msg.as_string())
session.quit()

Es ist möglich, post-commit als Trigger für ein lokales Continuous-Integration-System zu nutzen, aber in den meisten Fällen werden wir dafür den post-receive-Trigger heranziehen wollen. Dieser läuft auf dem Server statt auf unserer lokalen Maschine und er wird auch jedes Mal ausgeführt, wenn ein Entwickler seinen Code pusht. Damit ist er deutlich besser für unseren CI-Prozess geeignet.

Im dritten Artikel zu lokalen Git-Hooks geht es dann um die Skripte post-checkout und pre-rebase.

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