Formatierungs-Commits aus der Blame-Historie ausblenden
Ein einzelner großflächiger Formatierungs-Commit reicht aus, um git blame für eine ganze Datei nutzlos zu machen, weil jede Zeile plötzlich auf diesen einen Commit zeigt statt auf die eigentliche fachliche Änderung. Die Datei .git-blame-ignore-revs löst genau dieses Problem, ohne die Commit-Historie selbst anzufassen.
Inhaltsverzeichnis
- 1. Wie Formatierungs-Commits die Blame-Historie zerstören
- 2. Grundlagen: die Datei .git-blame-ignore-revs
- 3. Dauerhafte Konfiguration mit blame.ignoreRevsFile
- 4. Unterstützung in PhpStorm und VS Code
- 5. Unterstützung in GitHub und GitLab
- 6. Grenzen: git log, bisect und Rebase
- 7. Workflow: einen neuen Formatierungs-Commit eintragen
- 8. Automatisierung: CI-Check auf Aktualität der Datei
- 9. Alternative Strategien zur Vermeidung des Problems
- 10. Zusammenfassung
- 11. FAQ
1. Wie Formatierungs-Commits die Blame-Historie zerstören
Sobald ein Team einen automatischen Formatter wie Prettier, PHP-CS-Fixer oder Black einführt, entsteht meist ein einziger grosser Commit, der den kompletten bestehenden Code auf einen Schlag umformatiert. Das ist inhaltlich sinnvoll, hat aber einen unangenehmen Nebeneffekt: git blame zeigt für praktisch jede Zeile im Projekt anschließend genau diesen Formatierungs-Commit an, unabhängig davon, wer die Zeile fachlich zuletzt geändert hat und warum.
Damit verliert git blame genau dort seinen Nutzen, wo er am wichtigsten ist, nämlich bei der Frage, wer eine bestimmte Zeile aus welchem Grund eingeführt hat. Wer nach der Formatierung eine Zeile untersucht, landet immer wieder beim selben Formatierungs-Commit und muss sich mühsam mit git log -L oder wiederholtem blame auf den Elter-Commit durch die eigentliche Historie durchhangeln.
2. Grundlagen: die Datei .git-blame-ignore-revs
Seit Git 2.23 unterstützt git blame die Option --ignore-revs-file, die eine Liste von Commit-Hashes entgegennimmt, die bei der Blame-Berechnung übersprungen werden sollen. Trifft eine Zeile auf einen ignorierten Commit, sucht Git automatisch im Elter-Commit weiter, bis eine Zeile bei einem nicht ignorierten Commit landet, der die fachliche Änderung tatsächlich repräsentiert.
Die Konvention hat sich etabliert, diese Liste in einer Datei namens .git-blame-ignore-revs im Wurzelverzeichnis des Repositories zu pflegen, mit einem Commit-Hash pro Zeile und optionalen Kommentaren, die mit einer Raute beginnen. Diese Datei wird ganz normal mitversioniert und wächst mit jedem weiteren großflächigen, rein mechanischen Commit.
# .git-blame-ignore-revs
# Einführung von Prettier über das gesamte Projekt
a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
# PHP-CS-Fixer Regelsatz auf PSR-12 aktualisiert
9f8e7d6c5b4a9f8e7d6c5b4a9f8e7d6c5b4a9f8e
# Einmalige manuelle Anwendung mit dieser Datei
git blame --ignore-revs-file=.git-blame-ignore-revs pfad/zur/datei.php
3. Dauerhafte Konfiguration mit blame.ignoreRevsFile
Den Pfad bei jedem git blame Aufruf manuell anzugeben, ist unpraktisch, deshalb erlaubt Git, die Datei über git config blame.ignoreRevsFile dauerhaft zu hinterlegen. Danach berücksichtigt jeder git blame Aufruf im Repository automatisch die Liste, ohne dass zusätzliche Flags nötig sind.
Diese Konfiguration ist lokal und muss von jedem Entwickler einmal gesetzt werden, weshalb sich ein Hinweis in der README oder ein automatisiertes Setup-Skript anbietet, ähnlich wie bei anderen lokalen git config Einstellungen. Alternativ kann ein Team die Einstellung auch projektweit über eine gemeinsame .gitconfig mit include.path verteilen.
git config blame.ignoreRevsFile .git-blame-ignore-revs
# Danach reicht der normale Aufruf
git blame pfad/zur/datei.php
4. Unterstützung in PhpStorm und VS Code
Sowohl PhpStorm als auch Visual Studio Code respektieren die blame.ignoreRevsFile Konfiguration, sofern sie über git config lokal gesetzt ist, weil beide Editoren letztlich das systemweite Git-Binary mit der lokalen Konfiguration aufrufen. Das bedeutet, dass die Annotate- beziehungsweise Blame-Ansicht im Editor automatisch die ignorierten Commits überspringt, sobald die Konfiguration einmal gesetzt wurde, ganz ohne editor-spezifische Einstellung.
In der Praxis lohnt es sich trotzdem, nach einem Editor-Update oder einer Neuinstallation kurz zu prüfen, ob die Blame-Ansicht die erwarteten Commits überspringt, da manche Editor-Versionen die Konfiguration erst nach einem Neustart oder einer expliziten Cache-Invalidierung übernehmen.
5. Unterstützung in GitHub und GitLab
GitHub berücksichtigt eine Datei namens .git-blame-ignore-revs im Wurzelverzeichnis automatisch in der Web-Oberfläche der Blame-Ansicht, ohne dass eine zusätzliche Konfiguration nötig ist, solange die Datei exakt diesen Namen trägt. GitLab bietet eine vergleichbare Unterstützung über dieselbe Konvention, sodass ein Team, das die Datei einmal korrekt pflegt, von konsistentem Verhalten in Editor, Terminal und Web-Oberfläche profitiert.
Wichtig ist, dass beide Plattformen ausschließlich den Dateinamen im Wurzelverzeichnis erkennen und keine alternative Konfiguration über git config respektieren, weil sie schließlich nicht auf der lokalen Konfiguration eines einzelnen Entwicklers basieren können. Die Datei ist damit der einzige portable Ort, um diese Information plattformübergreifend zu teilen.
6. Grenzen: git log, bisect und Rebase
Die Ignore-Liste wirkt ausschließlich auf git blame und die davon abgeleiteten Annotate-Ansichten, nicht auf git log, git bisect oder andere Befehle, die die Commit-Historie durchsuchen. Ein Formatierungs-Commit taucht also weiterhin ganz normal in git log auf und kann bei einer git bisect Sitzung als regulärer Commit getestet werden, was gelegentlich zu falsch-positiven Ergebnissen führt, wenn der Formatierungs-Commit selbst versehentlich Verhalten verändert hat.
Bei einem interaktiven Rebase, der Commits vor dem Formatierungs-Commit verändert, ändern sich zwangsläufig auch dessen Hash und der aller nachfolgenden Commits, wodurch die Einträge in .git-blame-ignore-revs ungültig werden. Für Branches, die regelmässig rebased werden, lohnt es sich deshalb, die Ignore-Liste erst nach dem endgültigen Merge in den Hauptbranch zu pflegen, wo die Hashes stabil bleiben.
7. Workflow: einen neuen Formatierungs-Commit eintragen
Ein bewährter Ablauf besteht darin, den Formatierungs-Commit isoliert zu erstellen, ohne fachliche Änderungen im selben Commit, damit er später sauber ignoriert werden kann, ohne versehentlich echte Änderungen mit zu verstecken. Direkt nach dem Commit wird dessen Hash mit git log -1 --format=%H ermittelt und mit einem kurzen Kommentar in die Datei eingetragen.
Sinnvoll ist zusätzlich, diesen Schritt in die Pull-Request-Checkliste für großflächige, rein mechanische Änderungen aufzunehmen, damit er nicht vom individuellen Erinnerungsvermögen abhängt. Manche Teams automatisieren den Eintrag sogar über ein kleines Skript, das nach einem Formatter-Lauf automatisch den letzten Commit-Hash anhängt.
#!/usr/bin/env bash
# scripts/track-formatting-commit.sh
set -euo pipefail
HASH="$(git log -1 --format=%H)"
MESSAGE="$(git log -1 --format=%s)"
{
echo ""
echo "# ${MESSAGE}"
echo "${HASH}"
} >> .git-blame-ignore-revs
echo "Commit ${HASH} zu .git-blame-ignore-revs hinzugefügt."
8. Automatisierung: CI-Check auf Aktualität der Datei
Damit die Ignore-Liste nicht schleichend veraltet, lohnt sich eine automatisierte Erinnerung im Pull-Request-Prozess: Ein CI-Job kann prüfen, ob ein Commit ausschließlich Whitespace- oder Formatierungs-Änderungen enthält, etwa über git diff --ignore-all-space im Vergleich zu einem regulären Diff, und in diesem Fall eine Warnung ausgeben, falls der Commit-Hash noch nicht in .git-blame-ignore-revs auftaucht.
Ein solcher Check ersetzt keine menschliche Entscheidung, weil nicht jeder grosse Diff automatisch ein reiner Formatierungs-Commit ist, aber er verhindert zuverlässig, dass ein Team schlicht vergisst, den Eintrag zu ergänzen, was in der Praxis der häufigste Grund für eine veraltete Ignore-Liste ist.
9. Alternative Strategien zur Vermeidung des Problems
Statt das Problem nachträglich zu lösen, lässt es sich teilweise von vornherein vermeiden, indem Formatierungs-Änderungen in kleinere, thematisch abgegrenzte Commits aufgeteilt werden, etwa pro Verzeichnis oder Modul statt für das gesamte Repository auf einmal. Das reduziert zwar nicht die Anzahl der betroffenen Zeilen, verteilt aber die Ignore-Einträge feiner und macht jeden einzelnen Commit leichter nachvollziehbar.
Eine ergänzende Strategie ist, Formatierungsregeln von Anfang an über einen Pre-Commit-Hook durchzusetzen, sodass gar keine grossen nachträglichen Formatierungs-Commits mehr nötig werden, weil jeder Commit bereits beim Erstellen automatisch korrekt formatiert ist. Für bereits bestehende Repositories bleibt .git-blame-ignore-revs aber der pragmatischste Weg, weil er die Vergangenheit nicht anfasst und sofort wirkt.
| Werkzeug | Beachtet .git-blame-ignore-revs | Konfigurationsweg | Wirkt auf git log |
|---|---|---|---|
| git blame (Terminal) | Ja | blame.ignoreRevsFile | Nein |
| PhpStorm Annotate | Ja | Lokale git config | Nein |
| VS Code GitLens/Blame | Ja | Lokale git config | Nein |
| GitHub Web-Blame | Ja | Dateiname im Root | Nein |
| GitLab Web-Blame | Ja | Dateiname im Root | Nein |
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
.git-blame-ignore-revs
Mechanismus
git blame --ignore-revs-file springt zum Elter-Commit
Konfiguration
git config blame.ignoreRevsFile .git-blame-ignore-revs
Plattformen
GitHub und GitLab erkennen die Datei automatisch im Root
Grenze
Wirkt nicht auf git log, bisect oder die Historie selbst