ohne Schuldzuweisung
git blame wird in vielen Teams als Werkzeug zur Schuldzuweisung missverstanden, dabei liefert es wertvollen Kontext: wer eine Zeile wann und in welchem Zusammenhang geändert hat. Dieser Artikel zeigt praxisnah, wie ihr mit Flags wie -w, -C und -M präzisere Ergebnisse erhaltet, Massen-Reformatierungen sauber ausblendet und blame-Ausgaben in PhpStorm und der Kommandozeile richtig interpretiert.
Inhaltsverzeichnis
- 1. Warum git blame nicht für Schuldzuweisungen gedacht ist
- 2. Grundsyntax und die Blame-Ausgabe richtig lesen
- 3. Mit -w Whitespace-Rauschen aus der Historie filtern
- 4. Verschobenen und kopierten Code mit -C aufspüren
- 5. Mit -M verschobene Zeilen im selben Commit erkennen
- 6. Mit --since den Blame zeitlich eingrenzen
- 7. Massen-Reformatierungen mit .git-blame-ignore-revs ausblenden
- 8. Blame-Ausgaben lesen: PhpStorm-Gutter vs. Kommandozeile
- 9. Wenn git blame nicht mehr ausreicht: git log -p und --follow
- 10. Zusammenfassung
- 11. FAQ
1. Warum git blame nicht für Schuldzuweisungen gedacht ist
Der Name git blame ist eine der unglücklichsten Entscheidungen in der gesamten Git-Terminologie, denn er suggeriert genau das Gegenteil dessen, wofür der Befehl tatsächlich gedacht ist. Historisch aus CVS und SVN annotate übernommen, zeigt git blame zu jeder Zeile einer Datei den Commit, den Autor und den Zeitpunkt der letzten Änderung an. Das ist Code-Archäologie, kein Tribunal: Die Ausgabe beantwortet die Frage "in welchem Zusammenhang ist diese Zeile entstanden", nicht "wer hat hier Mist gebaut".
In Teams mit gesunder Code-Review-Kultur wird git blame vor allem genutzt, um die richtige Ansprechperson für eine Änderung zu finden, jemand, der die ursprüngliche Anforderung, den Bugfix oder die Design-Entscheidung hinter einer Zeile kennt. Wer git blame stattdessen als Werkzeug zur öffentlichen Bloßstellung einsetzt, vergiftet die Zusammenarbeit und sorgt dafür, dass Entwickler künftig lieber unsauberen Code liegen lassen, als eine sichtbare Änderung mit ihrem Namen zu riskieren. Manche Teams legen sich bewusst einen Alias wie git config alias.context blame zu, um diese Denkweise auch sprachlich zu verankern.
Entscheidend ist die Perspektive, mit der die Ausgabe gelesen wird. Eine Zeile, die vor drei Jahren von jemandem geschrieben wurde, der das Unternehmen längst verlassen hat, ist kein Vorwurf, sondern ein Hinweis darauf, dass die zugehörige Commit-Nachricht, der verlinkte Pull Request oder das Ticket-System jetzt die einzige verlässliche Kontextquelle sind. git blame ist damit der Einstiegspunkt einer Recherche, nicht ihr Endpunkt.
2. Grundsyntax und die Blame-Ausgabe richtig lesen
Der einfachste Aufruf git blame datei.php zeigt für jede Zeile vier Kerninformationen: den abgekürzten Commit-Hash, den Autorennamen, Datum und Uhrzeit der Änderung sowie die Zeilennummer, gefolgt vom eigentlichen Zeileninhalt. Standardmäßig sortiert Git nach Zeilennummer der aktuellen Dateiversion, nicht nach Commit-Reihenfolge, was die Ausgabe bei häufig bearbeiteten Dateien auf den ersten Blick unübersichtlich wirken lässt. Ein Griff zum jeweiligen Hash über git show <hash> liefert sofort den vollständigen Commit inklusive Message und Diff.
Für tiefere Analysen lohnt sich git blame -e, das E-Mail-Adressen statt Namen anzeigt und bei mehreren Autoren mit identischem Namen Verwechslungen vermeidet, sowie git blame -s, das Autor und Datum komplett ausblendet und nur Hash und Zeileninhalt zeigt, praktisch, wenn ausschließlich die Änderungshistorie interessiert, nicht die Person dahinter. Das folgende Beispiel zeigt eine typische Ausgabe für eine PHP-Klasse in einem Magento-Modul, eingegrenzt mit -L auf einen konkreten Zeilenbereich.
$ git blame -L 10,18 src/app/code/Mironsoft/SeoSuite/Model/MetaGenerator.php
^a1b2c3d4 (Jane Miller 2024-03-11 14:22:07 +0100 10) class MetaGenerator
a1b2c3d4 (Jane Miller 2024-03-11 14:22:07 +0100 11) {
f9e8d7c6 (Tom Weber 2024-06-02 09:15:41 +0200 12) public function __construct(
f9e8d7c6 (Tom Weber 2024-06-02 09:15:41 +0200 13) private readonly StoreManagerInterface $storeManager,
3c4d5e6f (Jane Miller 2025-01-19 16:40:12 +0100 14) private readonly ScopeConfigInterface $scopeConfig,
3c4d5e6f (Jane Miller 2025-01-19 16:40:12 +0100 15) ) {
7a8b9c0d (Tom Weber 2025-11-04 11:03:55 +0100 16) }
7a8b9c0d (Tom Weber 2025-11-04 11:03:55 +0100 17)
a1b2c3d4 (Jane Miller 2024-03-11 14:22:07 +0100 18) public function generate(): string
# ^a1b2c3d4: the caret marks the commit that first created the file (boundary commit)
3. Mit -w Whitespace-Rauschen aus der Historie filtern
Massen-Commits, die Einrückungen von Tabs auf Spaces umstellen, eine PHP-CS-Fixer- oder Prettier-Regel nachträglich anwenden oder Zeilenenden normalisieren, sind der häufigste Grund, warum git blame scheinbar falsche Autoren anzeigt. Ohne Gegenmaßnahme landet jede betroffene Zeile beim Reformat-Commit, selbst wenn sich der inhaltliche Code seit Jahren nicht geändert hat. Die Flag -w weist Git an, reine Whitespace-Änderungen bei der Zeilenzuordnung zu ignorieren und stattdessen zur letzten inhaltlichen Änderung weiterzublättern.
git blame -w datei.php vergleicht dabei jede Version wie gewohnt, überspringt aber Commits, in denen sich eine Zeile nur durch Leerzeichen, Tabs oder Zeilenumbrüche vom Vorgänger unterscheidet. Das Ergebnis ist eine deutlich aussagekräftigere Historie, besonders in Legacy-Modulen, die mehrfach durch automatische Formatierungstools gelaufen sind. Kombiniert mit -C für verschobenen Code liefert -w die Grundlage für eine Blame-Ausgabe, die tatsächlich etwas über Autorenschaft aussagt statt nur über den letzten Formatierungslauf.
# Without -w: a whitespace-only reformat commit dominates the blame
$ git blame -L 40,42 app/code/Mironsoft/Core/Helper/Data.php
9f1a2b3c (Tom Weber 2026-02-14 08:00:00 +0100 40) if ($value === null) {
9f1a2b3c (Tom Weber 2026-02-14 08:00:00 +0100 41) return $default;
9f1a2b3c (Tom Weber 2026-02-14 08:00:00 +0100 42) }
# Commit 9f1a2b3c only re-indented the file, it did not write this logic
# With -w: whitespace-only commit is skipped, the real author surfaces
$ git blame -w -L 40,42 app/code/Mironsoft/Core/Helper/Data.php
4d5e6f7a (Jane Miller 2023-08-30 12:11:03 +0200 40) if ($value === null) {
4d5e6f7a (Jane Miller 2023-08-30 12:11:03 +0200 41) return $default;
4d5e6f7a (Jane Miller 2023-08-30 12:11:03 +0200 42) }
4. Verschobenen und kopierten Code mit -C aufspüren
Wird eine Methode aus einer Klasse in eine neue, dedizierte Service-Klasse extrahiert, zeigt git blame ohne weitere Flags standardmäßig genau diesen Extraktions-Commit als Ursprung jeder verschobenen Zeile an, selbst wenn die Logik seit Jahren unverändert ist. Die Flag -C löst dieses Problem, indem sie Git anweist, auch nach kopiertem oder verschobenem Code aus anderen Dateien im selben Commit zu suchen. Dabei gibt es drei Eskalationsstufen: Ein einfaches -C durchsucht nur Dateien, die im selben Commit ebenfalls geändert wurden.
-C -C (auch -CC) erweitert die Suche auf alle Dateien, die im Commit neu hinzugefügt wurden, auch wenn sie sonst nicht angefasst wurden. -C -C -C (-CCC) geht am weitesten und durchsucht den gesamten Commit-Verlauf nach dem Ursprung, unabhängig davon, ob die Quelldatei überhaupt noch existiert, die gründlichste, aber auch rechenintensivste Variante. Für große Magento-Module mit häufigen Refactorings ist -C -C -C oft der einzige Weg, die tatsächliche Autorenschaft über Datei- und Klassengrenzen hinweg zu rekonstruieren.
# A method was extracted into a new service class, find its real origin
$ git blame -C -C -C -L 5,7 app/code/Mironsoft/SeoSuite/Service/SchemaBuilder.php
e2f3a4b5 app/code/Mironsoft/SeoSuite/Model/MetaGenerator.php (Jane Miller 2024-03-11 5) public function buildProductSchema(
e2f3a4b5 app/code/Mironsoft/SeoSuite/Model/MetaGenerator.php (Jane Miller 2024-03-11 6) ProductInterface $product
e2f3a4b5 app/code/Mironsoft/SeoSuite/Model/MetaGenerator.php (Jane Miller 2024-03-11 7) ): array
# Original file and author of the logic are preserved across the extraction,
# instead of showing the refactoring commit as the "author" of the logic
5. Mit -M verschobene Zeilen im selben Commit erkennen
Während -C Code über Dateigrenzen hinweg verfolgt, kümmert sich -M um den einfacheren, aber ebenso häufigen Fall: Zeilen, die innerhalb derselben Datei nach oben oder unten verschoben wurden, etwa weil eine Methode neu sortiert oder eine Konstante an den Dateianfang gezogen wurde. Ohne -M markiert Git jede verschobene Zeile als im aktuellen Commit "neu geschrieben", was speziell bei größeren Aufräum-Commits die Blame-Historie unbrauchbar macht.
Die Erkennung basiert auf einem konfigurierbaren Ähnlichkeitsschwellenwert, standardmäßig 20 alphanumerische Zeichen: Ein Codeblock, der diese Mindestlänge unterschreitet, wird nicht als Verschiebung erkannt, sondern weiterhin als neue Zeile gewertet. Über -M<n>, etwa -M10, lässt sich dieser Schwellenwert für kurze, aber inhaltsgleiche Zeilen wie einzelne Array-Einträge oder kurze Bedingungen absenken. In der Praxis kombiniert man -M fast immer mit -C -C -C zu git blame -M -C -C -C, um sowohl innerhalb der Datei verschobene als auch über Dateigrenzen kopierte Zeilen korrekt zuzuordnen.
6. Mit --since den Blame zeitlich eingrenzen
Nicht jede Blame-Anfrage braucht die vollständige Historie einer Datei bis zum ersten Commit. Wenn die eigentliche Frage lautet "wer hat diese Zeile im letzten Quartal geändert" oder "ist diese Regression Teil des letzten Release-Zyklus", liefert git blame --since=<datum> ein zeitlich begrenztes Ergebnis, das ältere Änderungen ignoriert und stattdessen bei der jeweils jüngsten Änderung im gewählten Zeitraum stehen bleibt. Zulässige Werte reichen von absoluten Daten wie --since=2026-01-01 bis zu relativen Angaben wie --since="3 months ago".
Eine verwandte, oft übersehene Alternative ist die Revisionsbereichs-Syntax git blame <alt>..<neu> -- datei.php, die den Blame explizit auf Commits zwischen zwei Referenzpunkten begrenzt, etwa zwischen dem letzten stabilen Tag und HEAD. Das ist besonders bei der Fehlersuche nach einem Deployment wertvoll: Statt die komplette Historie zu durchsuchen, zeigt der eingegrenzte Blame ausschließlich Zeilen, die tatsächlich im verdächtigen Release-Fenster verändert wurden, und reduziert die Zahl der Kandidaten-Commits drastisch.
7. Massen-Reformatierungen mit .git-blame-ignore-revs ausblenden
Die Flag -w hilft gegen reine Whitespace-Änderungen, versagt aber bei Commits, die zusätzlich echte Formatierungsentscheidungen treffen, etwa einen Zeilenumbruch bei langen Methodenaufrufen durch PHP-CS-Fixer oder eine komplette Umstellung auf PSR-12. Für genau diesen Fall bietet Git seit Version 2.23 die Datei .git-blame-ignore-revs im Repository-Root: eine einfache Liste von Commit-Hashes, die git blame bei der Zeilenzuordnung vollständig überspringt, unabhängig davon, was sie inhaltlich verändert haben.
Damit Git diese Datei automatisch berücksichtigt, muss sie über die Konfiguration blame.ignoreRevsFile registriert werden, einmal pro Klon, oder projektweit über eine versionierte .gitconfig-Empfehlung im README. GitHub und GitLab erkennen .git-blame-ignore-revs inzwischen ebenfalls automatisch und blenden die dort gelisteten Commits in der Web-Oberfläche aus. Wichtig: Jeder neue Massen-Reformat-Commit muss der Datei manuell hinzugefügt werden, idealerweise als fester Schritt in der Pull-Request-Checkliste direkt nach dem Merge.
# .git-blame-ignore-revs (lives in the repository root)
# Commits listed here are skipped by git blame and by GitHub/GitLab blame view.
# Add the full commit hash plus a comment explaining why it is ignored.
# Applied PSR-12 formatting across the entire app/code directory
a3f9c21e8b4d6f0912ab34cd56ef7890a1b2c3d4
# Migrated indentation from tabs to 4 spaces (editorconfig change)
7b2e4f61c9a80d3e5f6178902b3c4d5e6f7a8b9c
# Ran php-cs-fixer with the new house style ruleset
c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0
# Register the ignore file once per local clone
$ git config blame.ignoreRevsFile .git-blame-ignore-revs
# From then on, git blame automatically skips the listed commits
$ git blame -L 1,5 app/code/Mironsoft/Core/Helper/Data.php
# Verify the config value was actually set
$ git config --get blame.ignoreRevsFile
.git-blame-ignore-revs
8. Blame-Ausgaben lesen: PhpStorm-Gutter vs. Kommandozeile
PhpStorm zeigt Blame-Informationen standardmäßig als Annotate-Spalte am linken Rand des Editors: Ein Rechtsklick auf den Zeilennummern-Rand mit "Annotate" blendet Autor, Datum und eine farbliche Alterskennzeichnung ein, wobei jüngere Änderungen wärmer eingefärbt werden. Ein Klick auf den jeweiligen Eintrag öffnet direkt den vollständigen Commit im integrierten VCS-Log inklusive Commit-Message, Diff und verknüpftem Pull Request, sofern die Git-Hosting-Integration konfiguriert ist. Für den schnellen Kontext beim Lesen von Fremdcode ist das kaum zu schlagen.
Die Kommandozeile spielt ihre Stärken dagegen bei Automatisierung und gezielten Abfragen aus. git blame -L 120,145 datei.php beschränkt die Ausgabe auf einen konkreten Zeilenbereich und ist bei tausend Zeilen langen Dateien deutlich schneller als das Warten auf eine vollständige IDE-Annotation. Für Tooling und Skripte ist --porcelain beziehungsweise --line-porcelain vorgesehen: ein maschinenlesbares Format mit einem Datenblock pro Zeile, das Commit-Metadaten wie Autor, E-Mail und Zeitstempel als eigene Schlüssel-Wert-Paare ausgibt und sich zuverlässig parsen lässt, ohne fragile Regex-Auswertung der menschenlesbaren Standardausgabe.
9. Wenn git blame nicht mehr ausreicht: git log -p und --follow
git blame zeigt nur die aktuell existierenden Zeilen einer Datei und ihren jeweils letzten Änderungs-Commit, gelöschter Code taucht in der Ausgabe grundsätzlich nicht auf. Wenn die eigentliche Frage lautet "warum wurde diese Funktion vor drei Monaten entfernt und jetzt wieder gebraucht", hilft nur git log -p -- datei.php, das die vollständige Commit-für-Commit-Diff-Historie einer Datei ausgibt, inklusive aller gelöschten und später wiederhergestellten Abschnitte.
Wurde eine Datei umbenannt oder in einen anderen Ordner verschoben, verliert git blame ohne -C-Flags typischerweise den Anschluss an die Historie vor der Umbenennung. git log --follow -- datei.php löst genau dieses Problem für die Commit-Liste, indem es Umbenennungen automatisch erkennt und die Historie über den Rename-Punkt hinweg fortsetzt. Die Kombination git log --follow -p liefert damit die vollständige Änderungshistorie einer Datei über alle Umbenennungen hinweg, während git blame stets nur eine Momentaufnahme der aktuellen Zeilen zeigt. Die folgende Tabelle stellt beide Ansätze für typische Szenarien gegenüber.
| Szenario | Weniger hilfreich | Empfohlener Ansatz |
|---|---|---|
| Whitespace-lastige Historie | einfaches git blame |
git blame -w |
| Massen-Reformat-Commit | Blame zeigt den Reformat-Commit | blame.ignoreRevsFile |
| Gezielte Zeilenabfrage | Blame auf die ganze Datei | git blame -L für den Zeilenbereich |
| Gelöscht und neu erstellt | Blame allein reicht nicht | git log -p / --follow kombinieren |
| Grundhaltung im Team | Blame als "wer ist schuld" | Blame zur Suche nach der richtigen Ansprechperson |
In der Praxis ergänzen sich die Werkzeuge: git blame beantwortet schnell "wer hat diese Zeile zuletzt angefasst", während git log -p und --follow die Lücken füllen, die eine reine Zeilen-Momentaufnahme systembedingt nicht abdecken kann. Wer beide Werkzeuge routiniert kombiniert, findet in Minuten Kontext, für den früher ein Rückfrage-Thread im Team-Chat nötig war.
Mironsoft
Git-Workflows, Code-Reviews und Entwicklerprozesse für Magento- und Hyvä-Teams
Saubere Git-Historie und Code-Reviews im Team etablieren?
Wir unterstützen Magento- und Hyvä-Teams beim Aufbau nachvollziehbarer Git-Workflows, von Commit-Konventionen über Blame-Ignore-Konfiguration bis zu Code-Review-Prozessen, die Kontext statt Schuldzuweisung in den Mittelpunkt stellen.
Git-Workflow-Audit
Commit-Historie, Branching-Strategie und Blame-Hygiene eures Repositories analysieren
Onboarding & Schulung
Teams im produktiven Einsatz von git blame, log und Reviews schulen
CI/CD & Tooling
blame.ignoreRevsFile, Hooks und Review-Automatisierung für Magento-Projekte einrichten
10. Zusammenfassung
git blame löst ein Kernproblem im Entwickleralltag: den Kontext hinter einer Codezeile schnell zu verstehen, ohne dass eine Frage im Team-Chat nötig wird. Die Flag -w filtert Whitespace-Rauschen, -C in seinen drei Eskalationsstufen verfolgt kopierten und verschobenen Code über Dateigrenzen hinweg, -M erkennt Verschiebungen innerhalb derselben Datei, und --since grenzt die Suche zeitlich ein. Zusammen verwandeln diese Flags eine oberflächlich unbrauchbare Blame-Ausgabe in ein präzises Werkzeug für Code-Archäologie.
Massen-Reformatierungen gehören über .git-blame-ignore-revs und die Konfiguration blame.ignoreRevsFile konsequent aus der Historie ausgeblendet, statt sie bei jeder Blame-Abfrage erneut mühsam zu überspringen. PhpStorms Annotate-Ansicht und die Kommandozeile mit -L und --porcelain ergänzen sich je nach Anwendungsfall, und wo git blame an seine Grenzen stößt, etwa bei gelöschtem oder umbenanntem Code, liefern git log -p und --follow die fehlende Tiefe. Der wichtigste Grundsatz bleibt dabei unabhängig von jeder Flag: git blame ist ein Werkzeug für Kontext, keine Waffe für Schuldzuweisungen.
git blame richtig einsetzen, Das Wichtigste auf einen Blick
Kontext statt Schuldzuweisung
git blame beantwortet "warum ist das so", nicht "wer war das". Die richtige Ansprechperson finden, nicht bloßstellen.
-w, -C, -M gezielt kombinieren
Whitespace ignorieren, verschobenen/kopierten Code über -C -C -C verfolgen, Zeilenverschiebungen mit -M erkennen.
Reformat-Commits sauber ausblenden
.git-blame-ignore-revs plus blame.ignoreRevsFile registrieren, damit Massen-Formatierungen die Historie nicht verfälschen.
IDE und CLI kombinieren
PhpStorm-Annotate für schnellen Kontext, git blame -L und --porcelain für gezielte Abfragen und Tooling.