Mein Lieblings-Git-Commit (2019)
(dhwthompson.com)- Der Commit „Convert template to US-ASCII to fix error“ von Dan Carley aus der Arbeit an GOV.UK zeigt, dass selbst kleine Codeänderungen durch detaillierte Commit-Messages zu einer langfristigen Dokumentation der Codebasis werden können
- Gute Messages halten nicht nur fest, was geändert wurde, sondern warum; dieses Beispiel dokumentiert konkret den Fehler
invalid byte sequence in US-ASCII, der beim Ausführen vonbundle exec rakeauftrat, sowie die Bedingungen zur Reproduktion - Da sowohl die Fehlermeldung als auch der Untersuchungsprozess festgehalten wurden, kann später jemand mit demselben Problem per
git log --grepoder GitHub-Commit-Suche frühere Lösungsaufzeichnungen finden - Die Message enthält auch den Einsatz von Unix-Werkzeugen wie
find -exec,file --mimeundiconv, sodass Reviewer und spätere Leser den Ablauf der Fehlersuche nachvollziehen können - Nicht jeder Commit muss so lang sein, aber Messages, die Kontext, Untersuchungsprozess und sogar Emotionen festhalten, helfen dabei, ein gemeinsames Verständnis der Codebasis und Vertrauen im Team aufzubauen
Ein Commit, der aus einer kleinen Änderung langfristige Dokumentation machte
- Git-Commit-Messages können, wenn sie gut geschrieben sind, ein starkes Dokumentationswerkzeug sein, das über die gesamte Lebensdauer einer Codebasis erhalten bleibt
- Der hier genannte Commit stammt aus der Zeit, als an GOV.UK beim Government Digital Service gearbeitet wurde
- Der Autor ist Dan Carley, und der Titel lautet „Convert template to US-ASCII to fix error“
- Dank der offenen Entwicklungsweise von GDS lassen sich auch solche internen Beispiele nach außen teilen
Wichtiger als die Länge ist der Kontext
- Dieser Commit hat im Verhältnis zur Codeänderung eine lange Message, aber der eigentliche Wert liegt nicht in der Länge selbst, sondern im nützlichen Kontext
- Dieselbe Änderung hätte anderswo vielleicht nur als
change whitespaceoderfix bugfestgehalten werden können - Dan hinterließ eine ausführliche Aufzeichnung, damit Kollegen und spätere Leser die Ursache des Problems und den Weg zur Lösung verstehen können
Nicht nur was geändert wurde, sondern warum
- Gute Commit-Messages erklären nicht nur, was geändert wurde, sondern auch warum
- Dieser Commit hält fest, dass nach dem Hinzufügen eines Tests auf dem Feature-Branch, der den Inhalt von
/etc/nginx/router_routes.confabgleicht, je nach Ausführungsweise unterschiedliche Ergebnisse auftraten- Bei Ausführung mit
bundle exec rake specoderbundle exec rspec modules/router/specfunktionierte alles normal - Bei Ausführung mit
bundle exec rakeschlug jedershould-Block fehl - Der Fehler war
ArgumentError: invalid byte sequence in US-ASCII
- Bei Ausführung mit
- Ohne diese Erklärung hätte man vermutlich raten müssen, bei welchem Werkzeug welcher Parsing-Fehler auftrat
- Der ursprüngliche Arbeitskontext geht leicht verloren, wenn Menschen ihn vergessen, das Team wechseln oder die Organisation verlassen
Durchsuchbare Fehlerhistorie
- Gleich am Anfang der Commit-Message steht unverändert die Fehlermeldung, die den Anlass für die Änderung gab
- Wer auf denselben Fehler stößt, kann auf folgende Weise frühere Spuren in der Codebasis finden
git log --grep "invalid byte sequence"- GitHub commit search
- Den Suchergebnissen zufolge haben mehrere Personen nach diesem Fehler gesucht; man konnte auch sehen, wer zuerst auf das Problem stieß und welche Maßnahmen ergriffenen wurden
Den Lösungsweg als Geschichte festhalten
- Die Message ist so aufgebaut, dass man nachvollziehen kann, wie sich das Problem zeigte, in welcher Reihenfolge es untersucht wurde und wie es schließlich behoben wurde
- Als Beispiel wird beschrieben, dass der Fehler verschwand, wenn der Matcher
.with_content(//)entfernt wurde, dass sich in der Spec-Datei keine seltsamen Zeichen fanden und dass sich das Problem reproduzieren ließ, wenn Puppet im selben Interpreter perrequiregeladen wurde - Eine Commit-Message dokumentiert nicht nur eine bestimmte Datei, Funktion oder einzelne Codezeile, sondern die Änderung selbst und ist damit ein guter Ort, um die Reise der Codebasis festzuhalten
Nebeneffekt: Teamwissen verbreiten
- Dan dokumentierte die in jedem Untersuchungsschritt ausgeführten Befehle, und das kann im Team ein leichter Weg sein, Wissen zu verbreiten
- Leser können allein aus der Commit-Message etwas über Unix-Werkzeuge lernen
- Mit dem Argument
-execbeifindkann man auf jede gefundene Datei einen Befehl ausführen - Hängt man am Ende des Befehls
\+an, werden statt eines Aufrufs pro Datei mehrere Dateinamen an einen einzigenfile-Befehl übergeben file --mimezeigt den MIME-Typ einer Datei an- Es gibt ein Werkzeug namens
iconv
- Mit dem Argument
- Nicht nur Personen, die die Änderung reviewen, sondern auch jene, die den Commit später finden, erhalten dieselben Informationen
- Mit genug Zeit und genügend Commits können solche Messages zu einem Wissensverstärker für das Team werden
Aufzeichnungen, die Empathie und Vertrauen schaffen
- Am Ende des Commits steht der Satz: „Now the tests work! One hour of my life I won't get back..“
- Dieser Satz vermittelt die Frustration eines Entwicklers, der eine Stunde lang einem heimtückischen Bug nachgegangen ist, ebenso wie die Befriedigung, ihn gelöst zu haben
- Selbst wenn kurzfristige Hacks oder Prototyp-Code in die Produktion gelangt sind und dort Wurzeln geschlagen haben, erinnern solche Messages daran, dass hinter jeder Änderung jemand stand, der mit den damaligen Informationen die bestmögliche Entscheidung getroffen hat
Nicht jeder Commit muss lang sein
- Dieses Beispiel ist ein Extremfall, und man muss gerade bei Commits dieser Größe nicht überall denselben Detaillierungsgrad erwarten
- Trotzdem ist es ein gutes Beispiel dafür, den Kontext hinter einer Änderung zu erklären, anderen Lernmöglichkeiten zu geben und zum gemeinsamen mentalen Modell der Codebasis im Team beizutragen
- Wenn dich gute Commit-Messages und Werkzeuge zur Strukturierung von Änderungen interessieren, sieh dir diese Materialien an
- Telling stories through your commits by Joel Chippindale
- A branch in time by Tekin Süleyman
1 Kommentare
Hacker-News-Kommentare
Ob gut oder schlecht: Aus der Sicht eines GitHub-Mitgründers und Autors von Git-Büchern wie Pro Git sind Git-Commit-Messages ein ungewöhnlicher Weg zur Codedokumentation, aber sehr ineffizient.
Das Kernproblem ist, dass Git, GitHub und die meisten anderen Tools normalerweise nur die erste Zeile anzeigen. Selbst der Beispiel-Commit zeigt nur eine generische Einzeile wie „US-ASCII error“, und den übrigen Inhalt, den der Artikel lobt, sieht in modernen Tools fast niemand.
Git hat Commit-Messages als E-Mail-Textkörper entworfen, den das ganze Projekt liest, aber heute erfüllen sie diese Rolle meistens nicht mehr. Wenn etwas nicht als Patch-Serie auf einer Mailingliste diskutiert wird, liest fast niemand mehr als die ersten 50 Zeichen der Betreffzeile.
Selbst wenn man in
git blamenachverfolgt, ob es-w -C -C -Coder zwei-Cwaren, um die relevante Message zu finden, sieht man nur die erste Zeile; am Ende muss man die SHA nachschlagen undgit showausführen, um die lange Message zu sehen. Dass Informationen selbst bei gut geschriebenen Messages so schwer wiederzufinden sind, ist eine meiner größten Beschwerden über Git.Wenn man sich die Historie des Git-Projekts ansieht, sind die Commit-Messages fast immer großartig, aber selbst wenn man auf einer einzelnen Datei
blameausführt, ist es sehr schwer, dem Kontext einer Funktionsimplementierung zu folgen. Die Dokumentation, die Jeff King in den letzten zehn Jahren hinterlassen hat, ist geradezu genial, und es ist schrecklich, dass sie fast niemand zu Gesicht bekommt.Ich kenne die genaue Lösung nicht, aber in den meisten Communities ist es leider fast eine Zeitverschwendung, großartige Dokumentation in Commit-Messages zu schreiben. Sie ist einfach zu schwer auffindbar.
git blame,git logundgit show, und einer Dateihistorie zu folgen ist leicht; mitgit log -Gdauert es nur Sekunden, den Zeitpunkt von Ergänzungen oder Löschungen zu finden.Schwierig wird es erst, wenn man den Commit gefunden hat und die Message nur „bleh“ oder „add a thing“ lautet. Dabei hätte der Entwickler nur 60 Sekunden investieren müssen, um aufzuschreiben, warum das so gemacht wurde.
Umgekehrt freue ich mich jedes Mal, wenn ich eine Commit-Message finde, die detailliert erklärt, warum eine Änderung vorgenommen wurde. Eine einzige gute Commit-Message spart Stunden oder Tage an Arbeit.
GitHub trägt ebenfalls zum Problem schlechter Commit-Messages bei. Mit etwas Glück stehen Details in der PR-Beschreibung, aber nicht direkt neben dem Commit-Log, und meistens führt die PR zu einem Jira-Link, Jira zu einem Slack-Gespräch und Slack zu einem Google-Dokument.
Unsere Branche ist wirklich schlecht in Dokumentation, aber Leute wie Jeff King kämpfen den guten Kampf. Am Ende ist es kein technisches, sondern ein menschliches Problem. Dokumentation bringt keinen unmittelbaren Lohn, wirkt wie zusätzliche Arbeit, und der Nutzen zeigt sich erst Tage, Wochen oder Monate später.
Bitte schreibt gute Commit-Messages. Wenn ihr auch nur eine Minute investiert, um das Warum festzuhalten, muss nicht jeder Commit zur verdammten Chesterton’s-Fence-Erklärung werden. Wenn man es in eine leicht auffindbare Commit-Message schreibt, werden euer zukünftiges Ich und ich euch dankbar sein.
Und nebenbei: Ich stimme auch der Behauptung nicht zu, Commit-Messages seien schwer zu finden. Ich habe fast nie Schwierigkeiten, die Historie einer Codezeile, Funktion oder Datei nachzuverfolgen, und Commit-Messages haben selbst dann Wert, wenn sie nie wieder gelesen werden. Wer gute Messages schreibt, prüft dabei auch, ob der tatsächlich geschriebene Code der eigenen Absicht entspricht, und für Code-Reviewer ist das ebenfalls wertvoll.
Auch die Klage,
git blame-Optionen seien schwer zu finden, klingt für mich wie ein Witz. Gute IDEs zeigen die Blame-Informationen an jeder Zeile an und bieten per Knopfdruck den Diff dazu; von dort aus sollte man rekursiv Blame auf Kontextzeilen oder gelöschte Zeilen anwenden können. Tools wie Tig können genau das.Dass GitHub es schwer macht, Commit-Messages anzusehen, räume ich ein.
Dokumentation an Commits hängt nicht aus Spaß dort. Sie dient dazu, das Risiko zu verringern, Patches von Leuten zu übernehmen, die später vielleicht nicht mehr da sind, und sie macht die zugrunde liegenden Überlegungen sichtbar, damit andere darauf aufbauen können. Wenn man Gedanken verbirgt, entsteht exklusive Besitzerschaft am Code, und das ist meistens nichts Gutes. Commit-Messages dienen auch als Arbeitsnachweis, was wichtig werden kann, wenn es zu viele Patches gibt. In kommerziellen Projekten mag ein Teil dieser Bedeutung geringer sein.
Einen anderen guten Ort für solche Dokumentation gibt es eigentlich nicht. In Code-Kommentare gehört sie nicht. Aber inzwischen gibt es eine Entwicklerschaft, die denkt, Git sei gleich GitHub und der einzige Zweck von Git bestehe darin, Änderungen auf GitHub hochzuladen.
Git ist nicht großartig, aber im Vergleich zu den Alternativen deutlich weniger schlecht. Wir sollten wieder zu den Grundlagen zurückkehren und den eigentlichen Zweck von Versionskontrolle verstehen.
Deshalb hilft es der aktuellen Community vielleicht nicht besonders, aber für jemanden, der in ein paar Jahren einen Bug debuggt, ist es nützlich.
Ich stimme der allgemeinen Stoßrichtung zu, aber ein konkreteres BLUF am Anfang wäre besser. Zum Beispiel so etwas wie „Fix test issues caused by non-breaking space character \xa0“.
So versteht man sofort, worum das Problem geht, und wer mehr wissen will, kann den Rest lesen.
Daher reicht für die Durchsuchbarkeit der Historie eine gute erste Zeile völlig aus. Die kurze Geschichte darüber, wie man bis nach Narnia gereist ist und zurück, um die eigentliche Ursache des Bugs zu finden, halte ich für wenig relevant. Ich habe allerdings nichts dagegen, so etwas zur Frustbewältigung in die PR-Beschreibung oder in die ausführliche Commit-Nachricht zu schreiben.
Ich kenne dieses Gefühl von Stolz, wenn man eine hervorragende Commit-Nachricht schreibt, bin mir aber weniger sicher, welchen Wert das für andere hat. Wenn jemand auf eine seltsame Fehlermeldung stößt oder ein neues Feature ergänzt, oder eigentlich in fast jedem Fall, durchsuchen die meisten meiner Meinung nach keine Commit-Nachrichten.
Es ist ein wenig traurig, aber ich vermute zunehmend, dass schöne Commit-Nachrichten eher eine Form von Eitelkeit unter Programmierern sind. Am meisten bewundert sie meist der Autor selbst, während andere sie gar nicht bemerken.
Manchmal ist Platz für solche ästhetischen Verzierungen, aber ich bin nicht überzeugt, dass ihr praktischer Nutzen groß ist. Wenn jemand anders nur „fix whitespace issue“ commitet, stört mich das inzwischen nicht mehr besonders, und ich glaube, das macht mich zu einem besseren Kollegen.
Bei Projekten wie Git oder Linux mit riesigen verteilten Teams und unzähligen Commits mag das anders sein. Die Projekte, mit denen ich vertraut bin, haben 1 bis 100 Beitragende, und die meisten gehören derselben Organisation an.
Wenn ich bei Google oder Stack Overflow keine Antwort finde, suche ich auf GitHub manchmal nach ähnlichen Inhalten in PRs oder Commit-Nachrichten. Das hat mir unzählige Male geholfen, auch in privaten Repositories, auf die ich Zugriff hatte. Gute Commit-Nachrichten sind ganz sicher mehr als Eitelkeit und haben echten Wert. Wenn viele Entwickler sie nicht ansehen, ist das ihr Verlust, und ich hoffe, dass sie das mit wachsender Erfahrung irgendwann erkennen. Man kann Junior-Entwicklern beibringen, wie man sucht, oder ihnen den ursprünglichen Artikel schicken.
Seitdem versuche ich, Commit-Nachrichten so zu schreiben, dass mein zukünftiges Ich sich wenigstens wieder daran erinnern kann, warum die Änderung nötig war.
git blamebenutzt, bevor man eine Codezeile ändert, macht man es falsch.Ich wollte schon mehrmals einen offensichtlichen Bug beheben, habe dann direkt vor dem Commit den vorherigen Commit angesehen und festgestellt, dass dieser „Bug“ absichtlich eingebaut worden war und dass das verknüpfte JIRA-Ticket sehr gut erklärte, warum meine offensichtliche Änderung einen zwei Jahre alten Bug wieder hervorgerufen hätte.
git blame-Funktion der IDE finde ich oft heraus, was passiert ist. Wenn ich dann auf eine gute Commit-Nachricht stoße, bin ich dankbar.Die erste Zeile einer Commit-Nachricht ist am wichtigsten, weil
git logdamit Chestertons Zaun behandeln kann. In diesem Fall hat der Autor meiner Meinung nach danebengeschlagen.Entscheidend ist, in die erste Zeile nicht zu schreiben, was man getan hat, sondern warum man es getan hat. Wer wissen will, was geändert wurde, kann sich den Code oder den Diff ansehen.
Zum Beispiel könnte dort „nginx .conf files must be in us-ascii“ stehen, gefolgt von „changed blahblah.erb to remove nonbreaking space character“, und danach kann die ansonsten ziemlich gute restliche Commit-Nachricht kommen.
Man sollte es wie einen Nachrichtenartikel betrachten. Man muss davon ausgehen, dass der Leser an jeder Stelle aufhören kann zu lesen, und deshalb in absteigender Wichtigkeit und mit zunehmendem Detailgrad schreiben.
Auch bei Nachrichtenartikeln erklärt die Überschrift eher das Was als das Warum.
In diesem Fall passt die erste Zeile des Originals genau. Wenn man
git logüberfliegt, kann man daraus schließen, dass dieser Commit wahrscheinlich nicht das funktionale Verhalten der Tests geändert hat, und weitergehen.„nginx .conf files must be in us-ascii“ mag ein guter Bug- oder PR-Titel sein, aber es kann zu mehreren Commits gehören, die unterschiedliche Dinge tun, und sagt nicht, was tatsächlich passiert ist. Man weiß nicht, ob Dateien nach US-ASCII konvertiert wurden, ob ein Konvertierungstool gebaut wurde, ob die Dokumentation aktualisiert wurde, ob Tests erstellt wurden oder ob es eine Kombination davon ist. Man sollte mit dem Was beginnen, nicht mit dem Warum, um diese Verwirrung zu verringern.
Ob man nun „The files must be in us-ascii“ oder „Changed the files to us-ascii“ schreibt, ist nicht besonders wichtig. Beides macht klar, dass die Dateien auf US-ASCII umgestellt wurden.
Ein Nachteil dieser Art der Dokumentation ist, dass man eine einmal geschriebene Commit-Message praktisch nicht mehr ändern kann
Natürlich geht das mit
rebase, aber ob man wirklich etwas ändern will, das vor einem Jahr geschrieben und bereits inmaingemergt wurde, und damit allen die Feature-Branches kaputtmacht, ist fraglichIm Vergleich zu Dokumentation in
.md-Dateien, Wikis oder Confluence kann ich Inhalte von Kollegen verbessern, und andere Kollegen können wiederum verbessern, was ich geschrieben habeIn diesem Fall ist der Bug behoben und taucht vielleicht nie wieder auf. Aber wenn ich einen bestimmten Bestandteil committe, ist die Versuchung groß, dabei auch das Design zu erklären — das vermeide ich inzwischen. Was passiert, wenn sich diese Komponente später wegen geänderter Business-Anforderungen verändert? Beschreibt die Commit-Dokumentation dann nur noch Diffs? Dann muss ein neues Teammitglied mehrere Commit-Messages lesen und sie im Kopf zusammenführen, um das System zu verstehen
Der Vergleich mit
.md-Dateien, Wikis oder Confluence ist wie der Vergleich eines Fahrrads mit einer GansDie Überlegungen und Trade-offs bei der Erstellung einer Komponente — oder auch die Tatsache, dass es keine gab — sind oft nützlich, um ursprüngliche Mängel, spätere Entwicklungen und Defekte zu verstehen, die durch veränderte Use Cases entstanden sind
Wenn neue Teammitglieder das System verstehen sollen, kann man separat eine „aktuelle“ Dokumentation pflegen. Diese muss nicht auch noch Implementierungs-Trade-offs oder die Tatsache abdecken, dass ein erster Entwurf unter Zeitdruck geschrieben wurde
Squashen verbessert den Verlauf als Geschichte einer Feature-Ergänzung, Merging bewahrt das unveränderliche Log der tatsächlichen Codeänderungen, und Reordering macht von beidem ein bisschen
Es gibt keinen Grund, nicht beides zu haben. Wir programmieren Computer, also können wir sie so bauen, wie wir wollen
Wenn ich Git neu schreiben würde, würde ich Commits in zwei Teile aufspalten. Die Änderungshistorie bliebe wie in Git unveränderlich in einer Struktur wie einem Merkle-DAG, und die Commit-Messages würde ich in einem separaten zugeordneten Datenspeicher halten, als vernünftiges und editierbares Log zur Beschreibung des Workflows. Ich würde Commits anhand von Feature-Tags gruppieren, Tippfehler korrigieren und Messages beliebig umschreiben können, während das Diff-Log darunter — also „was sich im Code tatsächlich geändert hat“ — unverändert erhalten bliebe
git noteskann man etwas ergänzen. Aber bei so langen Messages ist es eher unwahrscheinlich, dass das jemand bemerktIn einem Punkt widerspreche ich. Gemeint ist die Stelle „Ich erwarte nicht von jedem Commit, insbesondere nicht von einem Commit dieser Größe, dieses Maß an Detail“
Meiner Erfahrung nach brauchen gerade kleine und harmlos wirkende Änderungen oft relativ längere Erklärungen
Gestern habe ich drei Absätze darüber geschrieben, warum ich
gh pr listum--limit=999erweitert habe. Es ist verwirrend. Im--jq-Argument gibt es bereits einlimit(, und wenn man so tut, als gäbe es unendlich viele PRs, führt ein höherer Wert im Endergebnis tatsächlich zu einem kleineren ResultatIch habe auch einen Codekommentar geschrieben und wahrscheinlich mehr Zeit mit Nachdenken und Feinschliff verbracht als mit dem eigentlichen Schreiben. Wenn jemand andeutet, diese Arbeit bestünde nur darin, wahllos Code herauszuhauen, soll er sich das bitte als nächstes Beispiel merken
„This was a non-ascii whitespace character that caused
ArgumentError: invalid byte sequence in US-ASCIIwhen runningbundle exec rake“ reicht völlig aus. Das enthält die Stichwörter, nach denen man später bei ähnlichen Problemen suchen würde, benennt die eigentliche Ursache und ist nicht so lang, dass Leute es wahrscheinlich überspringenIch halte das nicht für einen großartigen Git-Commit
Angesichts all des Texts könnte die erste Zeile „Convert template to US-ASCII to fix error“ besser sein. Man müsste nur noch mit ein paar Wörtern ergänzen, welches Whitespace-Zeichen den Fehler ausgelöst hat und welcher Fehler es war. Diese Erklärung zusammen mit dem Diff liefert genug nötigen Kontext
Ehrlich gesagt ist der Rest fast nutzlos. Er schadet nicht, hat aber auch wenig Wert. Der Autor hat seinen Weg bei der Fehlersuche dokumentiert — und ich frage mich, wen das interessieren soll
Im Artikel gibt es auch Suchergebnis-Links zu mehreren Commits von Leuten, die aus diesem Fix gelernt haben
Diese Commit-Message ist ein Wissensschatz
Wenn man großartige Commit-Messages sehen will, sollte man in die Git-Historie des Linux-Kernels schauen. Dort ist das Standard
In der ersten Zeile wird immer das betroffene Subsystem genannt, gefolgt von einer einzeiligen Zusammenfassung im Imperativ. Danach werden drei Fragen so ausführlich wie möglich beantwortet
Ein Beispiel wäre etwa: „Der aktuelle Code macht X. Beim Ausführen des Testfalls T wurde das unerwartete Verhalten U beobachtet. Das liegt an Grund R. Behoben wird es durch F“
Ein neuerer Mitwirkender sagte mir kürzlich, mein Ansatz bei Git-Nachrichten, also meine Anforderungen, seien „eigenwillig“. Offenbar zeigt sich da mein Linux-Kernel-Hintergrund, aber all meine Commit-Messages sehen so aus wie die hier gezeigten
Wenn es um bestehenden Code geht, sollten Anmerkungen zusammen mit dem Code stehen. Prozessbezogene Inhalte, zum Beispiel entfernter Code oder Code, der nicht funktionierte, gehören in den Commit
Am wichtigsten ist, dass eine Commit-Message der Bequemlichkeit halber zwar auf ein Issue verweisen kann, aber die zentralen Details unbedingt wiedergeben muss. GitHub ist vergänglich, Git-Nachrichten sind es nicht
Commit-Messages voller Kontext gibt es hier[1]
Das passiert so häufig, dass ein Maintainer diesen Text geschrieben hat[2]
[1] https://github.com/git/git/commit/d70f554cdf38b0b05cfaa8e8eb...
[2] https://lore.kernel.org/git/xmqqedevo8ps.fsf@gitster.g/