Das Ziel ist klar: Kein schlecht formatierter Code, keine kaputten Tests und keine vergessenen var_dump()-Aufrufe sollen im Repository landen. Git Hooks sind der Mechanismus, um das durchzusetzen — automatisch, ohne dass jeder Entwickler daran denken muss.
Was sind Git Hooks?
Git Hooks sind ausführbare Skripte im .git/hooks/-Verzeichnis eines Repositories. Git führt sie automatisch bei bestimmten Ereignissen aus:
| Hook | Zeitpunkt | Typischer Einsatz |
|---|---|---|
pre-commit |
Vor dem Commit | Linting, Tests, Code-Formatierung |
commit-msg |
Nach Commit-Message-Eingabe | Conventional Commits prüfen |
pre-push |
Vor dem Push | Test-Suite ausführen |
post-merge |
Nach einem Merge | composer install, npm install |
pre-rebase |
Vor einem Rebase | Warnung bei ungesicherten Änderungen |
Manueller Pre-Commit Hook
#!/bin/bash
# .git/hooks/pre-commit
# PHP CS Fixer auf staged PHP-Dateien anwenden
STAGED_PHP=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.php$')
if [ -n "$STAGED_PHP" ]; then
./vendor/bin/php-cs-fixer fix $STAGED_PHP --no-interaction
git add $STAGED_PHP
fi
Das Problem: .git/hooks/ wird nicht in das Repository eingecheckt, also müssen alle Entwickler den Hook manuell installieren.
Husky: Hooks versionieren
Husky löst dieses Problem: Git Hooks werden im Repository gespeichert und automatisch für alle Entwickler aktiviert.
Installation
# Husky installieren
npm install --save-dev husky
# Husky initialisieren
npx husky init
Das erstellt .husky/pre-commit und fügt "prepare": "husky" zu package.json hinzu. Jetzt wird Husky automatisch beim npm install eingerichtet.
Pre-Commit Hook mit Husky
# .husky/pre-commit
#!/bin/sh
npx lint-staged
lint-staged: Nur geänderte Dateien prüfen
lint-staged führt Linter und Formatter nur auf den für den Commit gestagten Dateien aus — nicht auf dem gesamten Projekt. Das macht Hooks deutlich schneller.
package.json Konfiguration
{
"scripts": {
"prepare": "husky"
},
"lint-staged": {
"*.ts": [
"eslint --fix",
"prettier --write"
],
"*.scss": [
"stylelint --fix"
],
"*.php": [
"./vendor/bin/php-cs-fixer fix --no-interaction"
]
},
"devDependencies": {
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"eslint": "^9.0.0",
"prettier": "^3.0.0"
}
}
PHP CS Fixer konfigurieren
PHP CS Fixer ist das Standard-Tool für PSR-12-konforme PHP-Formatierung in Symfony-Projekten.
.php-cs-fixer.dist.php
<?php
declare(strict_types=1);
$finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests')
->name('*.php')
->exclude('var')
->exclude('vendor');
return (new PhpCsFixer\Config())
->setRules([
'@PSR12' => true,
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'trailing_comma_in_multiline' => true,
'phpdoc_align' => ['align' => 'vertical'],
'declare_strict_types' => true,
'single_quote' => true,
])
->setFinder($finder)
->setCacheFile(__DIR__ . '/.php-cs-fixer.cache');
.php-cs-fixer.cache in .gitignore
.php-cs-fixer.cache
Commit-Message-Validierung
Conventional Commits ist ein Standard für strukturierte Commit-Messages:
type(scope): description
feat(auth): add OAuth2 login support
fix(cart): prevent negative quantities
docs(api): update endpoint documentation
commit-msg Hook
# .husky/commit-msg
#!/bin/sh
npx --no -- commitlint --edit "$1"
commitlint.config.js
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat',
'fix',
'docs',
'style',
'refactor',
'test',
'chore',
'revert',
]],
'subject-max-length': [2, 'always', 100],
'subject-case': [0], // Deutsch/Englisch gemischt erlauben
},
};
npm install --save-dev @commitlint/cli @commitlint/config-conventional
Pre-Push Hook mit PHPUnit
Vor einem Push können Tests ausgeführt werden:
# .husky/pre-push
#!/bin/sh
echo "Running PHPUnit tests..."
vendor/bin/phpunit --testdox --no-coverage
if [ $? -ne 0 ]; then
echo "Tests failed! Push aborted."
exit 1
fi
Für DDEV-Projekte:
# .husky/pre-push
#!/bin/sh
echo "Running PHPUnit tests in DDEV..."
ddev exec vendor/bin/phpunit --no-coverage
if [ $? -ne 0 ]; then
echo "Tests failed! Push aborted."
exit 1
fi
Post-Merge Hook: Dependencies automatisch aktualisieren
Nach einem Merge oder Checkout können Composer- und npm-Pakete automatisch installiert werden:
# .husky/post-merge
#!/bin/sh
# Prüfen ob composer.lock sich geändert hat
CHANGED_FILES=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)
if echo "$CHANGED_FILES" | grep -q "composer.lock"; then
echo "composer.lock changed, running composer install..."
ddev exec composer install
fi
if echo "$CHANGED_FILES" | grep -q "package-lock.json"; then
echo "package-lock.json changed, running npm install..."
ddev exec npm install
fi
Vollständige Konfiguration für ein Symfony-Projekt
{
"scripts": {
"prepare": "husky"
},
"lint-staged": {
"*.php": [
"./vendor/bin/php-cs-fixer fix --no-interaction"
],
"*.{ts,js}": [
"eslint --fix"
],
"*.scss": [
"stylelint --fix"
],
"*.{json,md,yaml,yml}": [
"prettier --write"
]
}
}
.husky/pre-commit:
#!/bin/sh
npx lint-staged
.husky/commit-msg:
#!/bin/sh
npx --no -- commitlint --edit "$1"
Hooks überspringen (nur in Ausnahmefällen!)
# Einzelnen Hook überspringen
git commit --no-verify -m "WIP: work in progress"
# Nur für Notfälle — nie in normalen Workflows!
HUSKY=0 git push
Hinweis: --no-verify sollte im Team-Setup eigentlich nicht möglich sein. CI/CD-Pipelines dienen als letzte Verteidigungslinie, falls jemand Hooks umgeht.
Fazit
Git Hooks mit Husky und lint-staged sind eine der effektivsten Maßnahmen für konsistente Codequalität im Team. Einmal eingerichtet, läuft die Code-Formatierung automatisch — Entwickler müssen nicht mehr an PHP CS Fixer denken. Conventional Commits helfen bei automatischen Changelogs und aussagekräftiger Git-History. Für Symfony-Projekte empfehle ich diese Kombination:
- Husky für Hook-Management
- lint-staged für selektives Linting
- PHP CS Fixer mit PSR-12 + Symfony-Ruleset
- commitlint für Conventional Commits
- Pre-Push PHPUnit für Test-Sicherheit
Kommentare
Kommentare werden von Remark42 bereitgestellt. Beim Laden werden Daten an unseren Kommentar-Server übertragen.