Post-Merge-Hooks: Aufgaben nach jedem Merge automatisieren
AI generated
git
HEAD
Git
Post-Merge-Hooks
Wiederkehrende Aufgaben nach jedem Merge automatisieren

Wer nach jedem Pull vergisst, Composer zu aktualisieren, verliert Zeit mit kryptischen Fehlermeldungen. Der post-merge Hook erledigt solche Routineaufgaben automatisch, ganz ohne daran denken zu müssen.

10 Min. Lesezeit Git Hooks Automatisierung

1. Was der post-merge Hook ist

Der post-merge Hook ist ein clientseitiges Skript, das Git automatisch ausführt, nachdem ein Merge erfolgreich abgeschlossen wurde. Das schließt sowohl explizite git merge Aufrufe als auch git pull ein, da pull intern aus fetch plus merge besteht.

Anders als serverseitige Hooks läuft post-merge ausschließlich lokal, auf der Maschine des Entwicklers, der den Merge durchgeführt hat. Es gibt keine Möglichkeit, ihn zentral für alle Beteiligten auf dem Server zu erzwingen.

Das Skript liegt standardmäßig unter .git/hooks/post-merge und muss ausführbar sein, damit Git es überhaupt aufruft. Ohne Ausführungsrecht wird der Hook stillschweigend übersprungen, ohne Fehlermeldung.


# Hook-Datei anlegen und ausführbar machen
touch .git/hooks/post-merge
chmod +x .git/hooks/post-merge

# Minimaler Test-Hook
echo '#!/bin/sh
echo "Merge abgeschlossen, führe Nacharbeiten aus"' > .git/hooks/post-merge

2. Typische Anwendungsfälle

Am häufigsten wird post-merge genutzt, um Abhängigkeiten automatisch zu aktualisieren. Ändert sich die composer.lock oder package-lock.json durch einen Merge, läuft im Hintergrund direkt ein passender Installationsbefehl, ohne dass der Entwickler manuell daran denken muss.

Ein zweiter verbreiteter Anwendungsfall ist das Leeren lokaler Caches. In Magento-Projekten führt ein Merge oft zu Änderungen an Layout-XML, Konfiguration oder Klassenstruktur, sodass ein automatischer Hinweis oder ein direkter Cache-Clean-Aufruf viele kleine Folgefehler vermeidet.

Auch das Anstoßen von Datenbank-Migrationen oder zumindest ein deutlicher Hinweis darauf, dass neue Migrationen vorliegen, lässt sich über den Hook automatisieren, auch wenn das eigentliche Ausführen aus Sicherheitsgründen meist manuell bestätigt werden sollte.


#!/bin/sh
# Nur ausführen, wenn sich die Composer-Lockdatei geändert hat
if git diff --name-only ORIG_HEAD HEAD | grep -q "composer.lock"; then
    echo "composer.lock geändert, führe composer install aus"
    composer install
fi

3. Grundstruktur und Parameter des Skripts

Der post-merge Hook erhält einen einzigen Parameter: eine 1, wenn der Merge im Squash-Modus durchgeführt wurde, andernfalls eine 0. Damit lässt sich unterschiedliches Verhalten für normale Merges und Squash-Merges definieren, falls das im Team relevant ist.

Der Rücksprungwert des Skripts wird von Git nicht ausgewertet, um den Merge selbst rückgängig zu machen, der Merge ist zu diesem Zeitpunkt bereits vollständig abgeschlossen. Ein fehlgeschlagener Hook kann den vorherigen Merge also nicht mehr verhindern, sondern nur eine Warnung ausgeben.

Weil der Hook nach jedem Merge läuft, auch nach sehr kleinen, sollte er möglichst schnell entscheiden, ob überhaupt eine Aktion nötig ist, bevor er teure Befehle wie eine vollständige Abhängigkeitsinstallation auslöst.


#!/bin/sh
SQUASH_FLAG="$1"

if [ "$SQUASH_FLAG" = "1" ]; then
    echo "Squash-Merge erkannt, überspringe automatische Aktionen"
    exit 0
fi

echo "Regulärer Merge, prüfe auf relevante Änderungen"

4. Fast-Forward- von echten Merges unterscheiden

Nicht jeder Merge erzeugt einen eigenen Merge-Commit. Bei einem Fast-Forward-Merge wird der Zeiger des aktuellen Branches einfach vorwärtsbewegt, ohne dass ein neuer Commit entsteht. Der post-merge Hook wird in beiden Fällen ausgeführt, unabhängig davon, ob ein echter Merge-Commit entstanden ist.

Um Änderungen zu erkennen, die durch den Merge hinzugekommen sind, eignet sich der Vergleich zwischen der Umgebungsvariable ORIG_HEAD, die Git vor dem Merge setzt, und dem aktuellen HEAD. Diese Methode funktioniert gleichermaßen für Fast-Forward- und echte Merges.

Ein häufiger Fehler ist die Annahme, es gebe immer einen zweiten Elternteil zum Vergleichen. Bei einem Fast-Forward-Merge existiert kein zweiter Merge-Parent, weshalb ein Vergleich über HEAD^2 in diesem Fall fehlschlägt und stattdessen ORIG_HEAD verwendet werden sollte.


#!/bin/sh
# Robuster Vergleich, funktioniert bei Fast-Forward und echtem Merge
CHANGED_FILES=$(git diff --name-only ORIG_HEAD HEAD)

if echo "$CHANGED_FILES" | grep -q "^composer.lock$"; then
    composer install
fi

5. Team-weite Verteilung von Hooks

Dateien im Verzeichnis .git/hooks/ werden von Git grundsätzlich nicht versioniert und deshalb auch nicht automatisch mit dem Repository geteilt. Jeder Entwickler müsste den Hook manuell in seine lokale Kopie kopieren, was in der Praxis schnell vergessen wird.

Die sauberste Lösung ist die Konfigurationsoption core.hooksPath, mit der sich ein alternatives, versioniertes Verzeichnis für Hooks festlegen lässt. Liegt dieses Verzeichnis im Repository selbst, etwa unter .githooks/, wird der Hook automatisch mit jedem Clone und jedem Pull mitgeliefert.

Alternative Werkzeuge wie Husky lösen dasselbe Problem für Node-basierte Projekte, indem sie core.hooksPath automatisch beim Installieren der Abhängigkeiten setzen. Für reine PHP- oder Magento-Projekte reicht in der Regel die manuelle Konfiguration über ein Setup-Skript vollkommen aus.


# Versioniertes Hook-Verzeichnis anlegen und aktivieren
mkdir -p .githooks
git mv .git/hooks/post-merge .githooks/post-merge
git config core.hooksPath .githooks

# Im README oder Setup-Skript dokumentieren, damit jeder es ausführt
echo "git config core.hooksPath .githooks" >> setup.sh

6. Sicherheit und Performance beachten

Weil post-merge Skripte mit den vollen Rechten des jeweiligen Benutzers ausgeführt werden, sollte der Inhalt eines geteilten Hook-Verzeichnisses beim Code-Review genauso ernst genommen werden wie jede andere Änderung am Repository. Ein manipulierter Hook könnte beliebige Befehle ausführen.

Lange laufende Aktionen im Hook, etwa eine vollständige Neuinstallation aller Abhängigkeiten bei jedem Merge unabhängig vom tatsächlichen Inhalt, frustrieren das Team schnell. Ein Hook sollte nur dann aktiv werden, wenn die relevanten Dateien sich tatsächlich geändert haben.

Ein Hook sollte grundsätzlich nicht blockierend fehlschlagen, wenn eine Aktion aus einem nicht kritischen Grund misslingt, etwa weil kein Internetzugang besteht. Eine klare Warnmeldung ist meist besser als ein abgebrochener Merge-Vorgang, der den Entwickler unnötig ausbremst.


#!/bin/sh
if ! composer install 2>/tmp/composer-error.log; then
    echo "Warnung: composer install fehlgeschlagen, bitte manuell prüfen"
    cat /tmp/composer-error.log
fi

7. Zusammenspiel mit post-checkout und post-rewrite

Der post-merge Hook steht nicht isoliert. post-checkout läuft nach einem Branch-Wechsel und ist der richtige Ort für Aktionen, die beim Wechsel zwischen Branches mit unterschiedlichen Abhängigkeiten relevant sind, unabhängig von einem Merge.

post-rewrite greift bei Operationen, die Commits umschreiben, etwa git rebase oder git commit --amend. Wer dieselbe Nacharbeit sowohl nach einem Merge als auch nach einem Rebase durchführen möchte, sollte die eigentliche Logik in ein gemeinsames Skript auslagern, das von beiden Hooks aufgerufen wird.

Diese Trennung vermeidet doppelten Code und stellt sicher, dass ein Update der Logik nur an einer Stelle erfolgen muss, unabhängig davon, über welchen Git-Vorgang die Änderung letztlich ausgelöst wurde.


#!/bin/sh
# post-merge und post-rewrite rufen dasselbe gemeinsame Skript auf
DIR="$(git rev-parse --show-toplevel)"
sh "$DIR/.githooks/lib/sync-dependencies.sh"

8. Praxisbeispiel für ein Magento-Projekt

In einem Magento-Projekt ändern sich nach einem Merge häufig composer.lock, Modul-Konfigurationsdateien oder db_schema.xml Definitionen. Ein Hook kann bei jeder dieser Änderungen eine passende, klar formulierte Empfehlung ausgeben, statt stillschweigend alles automatisch auszuführen.

Eine vollständig automatische setup:upgrade Ausführung direkt im Hook ist in den meisten Projekten riskant, da der Befehl potenziell länger läuft und bei paralleler Nutzung der Datenbank zu Konflikten führen kann. Ein deutlicher Hinweis im Terminal ist hier der sicherere Weg.

Kombiniert mit core.hooksPath stellt ein solcher Hook sicher, dass kein Teammitglied nach einem Merge vergisst, notwendige Nacharbeiten durchzuführen, ohne dabei kritische Befehle unkontrolliert auszuführen.


#!/bin/sh
CHANGED=$(git diff --name-only ORIG_HEAD HEAD)

echo "$CHANGED" | grep -q "composer.lock" && echo "Hinweis: composer install ausführen"
echo "$CHANGED" | grep -q "db_schema.xml" && echo "Hinweis: bin/magento setup:upgrade ausführen"

9. Debugging und Deaktivieren von Hooks

Anders als pre-commit oder pre-push gibt es für post-merge keine Möglichkeit, ihn über --no-verify zu umgehen, da diese Option ausschließlich für Hooks gedacht ist, die einen Vorgang verhindern können. Ein Merge ist zu diesem Zeitpunkt bereits abgeschlossen.

Zum gezielten Testen lässt sich der Hook direkt aufrufen, ohne einen echten Merge durchzuführen. So lassen sich Logikfehler schnell finden, ohne den Repository-Zustand tatsächlich zu verändern.

Für eine dauerhafte Deaktivierung reicht es, die Datei nicht ausführbar zu machen oder core.hooksPath auf ein leeres Verzeichnis zu setzen. Detailliertes Tracing der Hook-Ausführung lässt sich zusätzlich über gängige Shell-Debugging-Optionen im Skript selbst aktivieren.


# Hook manuell aufrufen, ohne echten Merge
sh .git/hooks/post-merge 0

# Shell-internes Tracing im Hook aktivieren
#!/bin/sh
set -x
Hook Zeitpunkt Blockierend Typische Aktion
post-merge Nach abgeschlossenem Merge oder Pull Nein Abhängigkeiten aktualisieren, Cache leeren
post-checkout Nach Branch-Wechsel Nein Umgebung an neuen Branch anpassen
post-rewrite Nach Rebase oder Amend Nein Gleiche Nacharbeit wie post-merge auf umgeschriebene Commits
pre-push Vor dem Übertragen zum Remote Ja Tests und Linting vor dem Push erzwingen

Mironsoft

Git-Workflows, Branching-Strategien und CI-Hooks

Chaotische Git-Historie und unklare Branching-Regeln im Team?

Wir richten saubere Git-Workflows ein, klären Branching-Strategien fürs Team und automatisieren Qualitätschecks über Git-Hooks und CI-Pipelines, damit die Historie nachvollziehbar bleibt.

Workflow-Audit

Bestehende Branching-Strategie und Merge-Praxis auf Schwachstellen prüfen.

Hook-Automatisierung

Pre-Commit- und Pre-Push-Hooks für Linting, Tests und Commit-Konventionen einrichten.

Team-Schulung

Rebase, Cherry-Pick und Konfliktauflösung im Team praxisnah vermitteln.

10. Zusammenfassung

Post-Merge-Hooks

Ort

.git/hooks/post-merge, ausführbar erforderlich

Parameter

1 bei Squash-Merge, sonst 0

Verteilung

core.hooksPath für versioniertes Hook-Verzeichnis

Vergleich

ORIG_HEAD gegen HEAD deckt Fast-Forward und echte Merges ab

11. FAQ: Post-Merge-Hooks

1Wann genau wird der post-merge Hook ausgeführt?
Unmittelbar nachdem ein Merge erfolgreich abgeschlossen wurde, sowohl bei explizitem git merge als auch bei git pull, da pull intern aus fetch und merge besteht.
2Kann der post-merge Hook einen Merge rückgängig machen?
Nein, der Merge ist zu dem Zeitpunkt, an dem der Hook läuft, bereits vollständig abgeschlossen. Der Hook kann nur nachträglich reagieren und Warnungen ausgeben, aber keine Änderung mehr blockieren.
3Warum wird mein post-merge Hook nicht ausgeführt?
Am häufigsten fehlt das Ausführungsrecht auf der Skriptdatei. Ohne chmod +x auf die Datei überspringt Git den Hook stillschweigend, ohne eine Fehlermeldung anzuzeigen.
4Wie kann ich post-merge Hooks im ganzen Team teilen?
Über die Konfigurationsoption core.hooksPath lässt sich ein versioniertes Verzeichnis im Repository als Hook-Quelle festlegen. So wird der Hook automatisch mit jedem Clone und jedem Pull an alle Teammitglieder verteilt.
5Was bedeutet der Parameter, den Git an den Hook übergibt?
Der einzige übergebene Parameter ist eine 1, wenn der Merge im Squash-Modus lief, andernfalls eine 0. Damit lässt sich im Skript unterschiedliches Verhalten für normale und Squash-Merges definieren.
6Wie erkenne ich im Hook, welche Dateien sich durch den Merge geändert haben?
Der Vergleich zwischen der Variable ORIG_HEAD, die Git vor dem Merge setzt, und dem aktuellen HEAD liefert die Liste der geänderten Dateien, unabhängig davon, ob es sich um einen Fast-Forward oder einen echten Merge handelte.
7Ist ein post-merge Hook auch bei einem Fast-Forward-Merge aktiv?
Ja, der Hook läuft in beiden Fällen. Da bei einem Fast-Forward-Merge kein zweiter Merge-Parent existiert, sollte für den Dateivergleich ORIG_HEAD statt eines Parent-Commits verwendet werden.
8Wie unterscheidet sich post-merge von post-checkout?
post-merge läuft nach einem abgeschlossenen Merge oder Pull, post-checkout dagegen nach jedem Branch-Wechsel, unabhängig davon, ob dabei gemergt wurde. Beide lassen sich für gemeinsame Nacharbeiten kombinieren.
9Kann ich den post-merge Hook mit --no-verify umgehen?
Nein, --no-verify wirkt nur bei Hooks, die einen Vorgang verhindern können, etwa pre-commit oder pre-push. Der post-merge Hook läuft nach einem bereits abgeschlossenen Vorgang und kennt diese Option nicht.
10Welche Risiken bringen automatisierte Aktionen im post-merge Hook mit sich?
Ein manipulierter, geteilter Hook wird mit den vollen Rechten des jeweiligen Benutzers ausgeführt und sollte deshalb im Code-Review genauso behandelt werden wie jede andere Änderung. Lange laufende Aktionen ohne Bedingungsprüfung können zudem das Team unnötig ausbremsen.