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.
Inhaltsverzeichnis
- 1. Was der post-merge Hook ist
- 2. Typische Anwendungsfälle
- 3. Grundstruktur und Parameter des Skripts
- 4. Fast-Forward- von echten Merges unterscheiden
- 5. Team-weite Verteilung von Hooks
- 6. Sicherheit und Performance beachten
- 7. Zusammenspiel mit post-checkout und post-rewrite
- 8. Praxisbeispiel für ein Magento-Projekt
- 9. Debugging und Deaktivieren von Hooks
- 10. Zusammenfassung
- 11. FAQ
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