Unified Diff und Split Diff im Vergleich
(matklad.github.io)- Kleine Änderungen lassen sich sowohl mit Unified Diff als auch mit Split Diff ausreichend gut betrachten, doch bei großen und komplexen Änderungen stößt ein Review, der nur den Diff liest, an Grenzen
- Große Änderungen sollten weniger anhand der Änderungshistorie geprüft werden, sondern anhand des Gesamtzustands der Codebase zu einem bestimmten Zeitpunkt; man sollte sie so erkunden und verifizieren, als würde man den echten Code bearbeiten
- Die ideale Review-Ansicht würde den aktuellen Code auf der Festplatte und den Unified Diff des betreffenden Bereichs nebeneinander zeigen, sodass man Codekontext und Änderungen gleichzeitig verfolgen kann
- Da bestehende Tools dafür zu wenig Unterstützung bieten, wird ein Low-Tech-Workflow genutzt: den PR lokal auschecken, dann die Commits entfernen und nur die Änderungen übrig lassen
- Mit diesem Ansatz kann man geänderte Dateien durchsuchen und geprüfte Hunks markieren, doch die aktuelle Position im Editor und der Diff werden nicht automatisch synchronisiert, sodass man weiterhin manuell eingreifen muss
Den aktuellen Codezustand statt nur den Diff reviewen
- Split Diff und Unified Diff funktionieren beide gut bei kleinen, einfachen Änderungen
- Wenn Änderungen größer und komplexer werden, reicht es nicht mehr aus, einfach nur den Diff zu lesen
- Bei großen Änderungen muss man die Codebase zu einem bestimmten Zeitpunkt gründlich betrachten
- Man sollte besonders auf die zuletzt geänderten Bereiche achten, sie aber grundsätzlich wie bei einem normalen Code Review betrachten
- Tests müssen ausgeführt werden
- Editor-Navigationsfunktionen wie „goto definition“ sollten genutzt werden
- Lokale Änderungen sollten angewendet werden, um zu prüfen, ob eine andere Umsetzung möglich gewesen wäre
- Man sollte Stellen finden, die im größeren Kontext ebenfalls hätten geändert werden müssen
- Man sollte unabhängig von der Änderungshistorie Auffälligkeiten in der aktuellen Codebase entdecken können
Gewünschte Review-Ansicht und lokaler Workaround
- Die ideale Diff-Ansicht würde links den aktuellen Codezustand und rechts den Unified Diff des aktuell sichtbaren Codebereichs gemeinsam anzeigen
- Der Code links sollte dem aktuellen Zustand auf der Festplatte entsprechen
- Änderungen werden dezent am Rand markiert
- Der Diff rechts sollte dem links aktuell sichtbaren Codebereich entsprechen
- Für diese Review-Form fehlt derzeit die Tool-Unterstützung
- Der tatsächliche Workflow besteht darin, den PR mit dem gpr script lokal auszuchecken
- Im letzten Schritt werden die Commits des PR entfernt, sodass nur die Änderungen übrig bleiben
- In diesem Zustand wird der Workflow für Staging und Commits für den Code Review zweckentfremdet
- Mit edamagit kann man die Liste der geänderten Dateien ansehen und im Editor zur nächsten/vorherigen Änderung springen
- Über die Staging Area lassen sich auch bereits reviewte Hunks markieren
- Die verbleibende Unbequemlichkeit ist, dass es keine automatische Synchronisierung zwischen dem magit status buffer und der aktuell im Editor geöffneten Datei gibt
- Um die aktuelle Datei und den Diff nebeneinander zu sehen, muss man den Diff manuell öffnen und selbst bis zur gerade betrachteten Stelle scrollen
- Für Reviews, die dem Code so nahe kommen, muss man sich derzeit provisorische Tools selbst bauen
- Das Hauptziel von Code Reviews ist nicht zwangsläufig nur die Codeprüfung; als verwandter Beitrag kann auch Two Kinds of Code Review gelesen werden
1 Kommentare
Meinungen auf Hacker News
Die Aussage, man wolle bei großen Änderungen nicht ein Diff-Review machen, sondern die Codebasis zu einem bestimmten Zeitpunkt ordentlich reviewen und dabei besonders auf kürzlich geänderte Bereiche achten, passt für mich persönlich nicht so recht zu einem normalen Code-Review.
Das hängt sicher vom Team und vom Ticket ab, aber wenn der Reviewer nicht eine Rolle wie ein Principal Engineer hat, der primär für den gesamten Code verantwortlich ist, dann ist ein Code-Review normalerweise eher eine einfache Plausibilitätsprüfung durch ein zweites Augenpaar.
Wenn die Commits eines PRs gut aufgeräumt oder gesquasht sind, ist es auch vergleichsweise einfach, die inkrementellen Änderungen separat zu prüfen.
Am Ende scheint die Unzufriedenheit eher auf unterschiedliche Arten von Reviews hinauszulaufen. Es gibt die verbreitete Plausibilitätsprüfung und tiefe Architektur-/Feature-Reviews vor dem Merge; Code-Reviews in Web-Tools scheinen größtenteils eher Ersteres zu sein.
Ein gutes Code-Review beginnt mit einem guten PR. Ein PR sollte das Ziel beschreiben und wie dieses Ziel erreicht wurde.
Der erste Punkt eines guten Reviews ist zu prüfen, ob der Code das beschriebene Ziel erreicht; der zweite, ob es Tests gibt, die die behauptete Funktionalität verifizieren; der dritte, ob er den Architekturkonventionen des bestehenden Systems folgt; der vierte, ob der Stil den Erwartungen entspricht; und zuletzt, ob es Vorschläge gibt, die API-Nutzung zu verbessern.
Code-Reviews, die nur Plausibilitätsprüfungen sind, haben wenig Wert, weil CI das ebenfalls erledigen kann. Code-Review ist ein menschlicher Prozess; daher sollte man das, was nur Menschen leisten können, bestmöglich nutzen und den Rest den Maschinen überlassen.
In den obigen Punkten steckt implizit die Frage: „Wird diese Änderung ein gutes Beispiel für künftige Beiträge sein?“
Ich habe Code-Reviews immer aus der Testerperspektive gemacht: den Code bauen und ausführen, schauen, ob er wie beabsichtigt funktioniert, Schwachstellen suchen und versuchen, ihn kaputtzumachen.
Das Diff ist nicht das Ende, sondern der Anfang. Es ist nur ein Hinweis darauf, wo man im Code herumstochern sollte; der Beweis kommt letztlich aus dem tatsächlichen Verhalten.
Man könnte sagen: „Das ist Aufgabe von Testern, nicht von Engineers“, aber wer könnte besser testen als jemand, der den Code versteht und weiß, wo man hinschauen muss?
Mir ist klar, dass diese Haltung in der Minderheit ist. Viele Engineers scheinen eine merkwürdige Abneigung dagegen zu haben, Code selbst auszuführen, sogar dagegen, einen Debugger zu verwenden. Sie bauen riesige Infrastrukturen, um alles zu automatisieren, aber auch Automatisierung ist letztlich nur Code, der Bugs haben kann. Wer testet die Tests — Quis custodiet ipsos custodes?
Code-Review ist, abgesehen von CI, die letzte Verteidigungslinie; in den Teams, in denen ich gearbeitet habe, war die große Richtung, die ein PR einführt, zu diesem Zeitpunkt aber bereits mit mehreren Leuten besprochen worden. Das kann Pairing sein, eine ausführliche Erklärung, ein Coffee Chat — was auch immer der Komplexität angemessen ist.
Dann wird der PR zu einem Ort, an dem nicht die Strategie selbst reviewt wird, etwa das Abtrennen eines neuen Moduls, das Einführen einer großen Dependency oder die Überarbeitung der API-Oberfläche, sondern die Taktik, mit der diese Strategie umgesetzt wurde.
Wenn man ohne vorherige Kommunikation nur einen Teil des Codes isoliert auf Plausibilität prüft, besteht die Gefahr, dass Code-Ownership zersplittert. Dann landet man bei Zuständen wie: „Was macht dieses Modul?“ „Keine Ahnung, frag Bob.“
Unabhängig davon gefällt mir die vom Autor vorgeschlagene Idee für die Diff-Ansicht, egal welcher Review-Modus verwendet wird.
Mit einem Haufen durcheinandergewürfelter roter und grüner Zeilen lässt sich kaum irgendein Review machen. Das ist keine nützliche Ansicht auf Code und zumindest als Standardansicht nicht geeignet.
Trotzdem macht man es zähneknirschend, weil es eben Arbeit ist; aber ich stimme dem Autor zu 100 % zu, dass es keine besonders nützliche Ansicht ist.
Weil es keine grundlegenden Prozesse für Defekte, Features usw. gibt, muss jeder Engineer jedes Mal selbst erfinden, wie ein Feature durch Zusammenarbeit ausgeliefert werden soll.
Die meisten tun am Ende fast gar nichts, und Code-Reviews werden zur einzigen Zusammenarbeit, die auf einer für das Thema sinnvollen Detailebene stattfindet. Deshalb versucht man dann, alle möglichen Eingriffe in diesen einen Moment hineinzupressen.
Als dritte oder vierte Option ist auch difftastic erwähnenswert. Es nutzt kein zeilenbasiertes Diff, sondern ein „strukturelles“ Diff und hebt Änderungen dadurch feiner hervor.
https://github.com/Wilfred/difftastic
Soweit ich mich erinnere, machte es Diffs manchmal, vielleicht in etwa 35 % der Fälle, besser lesbar; normalerweise gab es in 60 % keinen Unterschied, und selten, in 5 %, wurden sie schwerer lesbar.
Ich habe es vor ein paar Jahren benutzt und erinnere mich nicht mehr gut an die konkreten Probleme. Der einzige Grund, warum ich aufgehört habe, war, dass ich für Git-Diffs angefangen habe, magit zu verwenden.
git diff --word-diffhat eine ähnliche Funktion und ist ziemlich gut.Beim Reviewen in Vim fühlt es sich ähnlich an
Ich habe ein kleines Skript, das den PR öffnet und für geänderte Dateien ungefähr etwas wie
vimdiff <(git show baseref:file) fileausführt. Vims Tabs sind im Grunde Views, deshalb ist es praktisch, einzelne Buffer gleichzeitig in unterschiedlichen Zuständen offen halten zu könnenIn der Standardansicht funktioniert Scroll-Lock wie erwartet, und bei Bedarf kann man ihn in einem separaten Tab umgehen
[cund]cspringen zwischen Hunks anhand der im PR vorhandenen Änderungsblöcke, nicht anhand des ArbeitsverzeichnissesMit
dpunddgkann man Hunks in den schreibgeschützten Diff-Buffer pushen bzw. daraus pullen und sie so als „erledigt“ markieren; dann werden sie aus den hervorgehobenen Änderungen im Live-Buffer ausgeblendetÄnderungen im Live-Buffer kann man direkt committen oder als Kommentar pushen
Auch normale Editorfunktionen wie Go to Definition, Build-Integration und Popup-Dokumentation funktionieren im aktuellen Tree-Zustand weiterhin
Diese Arbeitsweise zeigt die Änderungen des PR gegenüber dem Basis-Branch, hält das Arbeitsverzeichnis aber sauber. Das wirkt viel besser als matklads Lösung, Änderungen bei verschmutztem Arbeitsverzeichnis anzusehen
In meinem Arbeitsumfeld ist das besonders praktisch, weil Änderungen oft als
--fixup-Commits ergänzt werden und ein Tool sie beim Merge wieder zusammenführt, indem es per Aufruf vongit-interpret-trailersdie Autoren der Fixup-Commits dem ursprünglichen Commit zuordnet. Außerdem extrahiert es Textkommentare aus dem PR und hängt, wenn passend, einenReviewed-by-Trailer an oder bei einem +1 einenAcked-by-TrailerIn sehr großen, komplexen Codebasen mit vielen beteiligten Engineers ist das ein großes Problem
Code Review ist schwierig, weil Diffs immer plausibel aussehen, Tests immer durchlaufen und die grundlegenden Dinge immer geprüft sind
Aber selbst wenn eine Änderung vernünftig aussieht, ist sie in Wirklichkeit oft falsch
Eine einzige schlecht gemachte Änderung, die plausibel wirkt, kann die gesamte Architektur in eine falsche Richtung treiben
Wie so oft ist das streng genommen kein Tool-Problem, sondern eher ein Problem von Kultur und Wissensaustausch. Bessere Tools allein lösen es nicht
Beim Code Review findet man selten den Bug selbst. Stattdessen schaut man, ob es Tests gibt, die solche Bugs finden würden
Ich habe das Gefühl, etwas zu übersehen. Der Autor sagt, dass Split Diff für ihn nicht passt, sagt aber nicht warum
Der ideale Diff sieht fast genauso aus wie ein Split Diff ohne duplizierten Kontext. Statt dass der Kontext auf beiden Seiten doppelt erscheint, steht er nur links
Ich frage mich, welchen Nutzen es bringt, den duplizierten Kontext im rechten Panel zu entfernen
Die Editoren/IDEs, die ich verwendet habe, unterstützen solche Funktionen innerhalb eines Diffs meist nur rudimentär. Go to Definition funktioniert dann nur innerhalb derselben Datei, Autovervollständigung klappt bei neu hinzugefügten Dingen oft nicht gut, und Refactoring-Funktionen fehlen häufig
So wie ich es verstehe, möchte der Autor den Diff umdrehen. Statt die Änderung selbst zu betrachten, möchte er den Code betrachten und prüfen, ob es dazu relevante Änderungen gibt
Der Artikel hat wenig Details, aber es wirkt so, als wolle er sich zu Implementierungen oder Aufrufstellen navigieren und prüfen, ob dort passende Änderungen vorhanden sind
Diff-Hervorhebungen können erstaunlich ablenken, und wenn man sie ausschaltet, wird der Code manchmal deutlich lesbarer. Auch ich schalte sie im Diff-Viewer gelegentlich aus, um den Code mit frischem Blick zu sehen, aber eine Option, den Unified Diff parallel in einem Split Pane anzuzeigen, gibt es nicht
Wenn man auf GitHub
.drückt, gelangt man in eine vollständige IDE im BrowserBeim Reviewen war das sehr nützlich. Statt nur Ausschnitte zu sehen, konnte ich Änderungen im Kontext der ganzen Datei betrachten und dadurch subtile Designprobleme viel besser erkennen
Übrigens funktioniert diese Tastenkombination auch in GitLab
Ich vermisse einige Funktionen von p4merge. Zum Beispiel gibt es dort eine Funktion, die im Zwischenraum zwischen Split Diffs zeigt, welcher Teil einer Datei welchem Teil der anderen Datei entspricht, und gleichzeitig auf beiden Seiten die Auswirkungen des Diffs darstellt: https://www.perforce.com/manuals/p4merge/Content/P4Merge/dif...
Auch die 3-Way-Merge-Funktion gefällt mir: https://www.perforce.com/manuals/p4merge/Content/P4Merge/dif...
Solche Funktionen sind in verbreiteten webbasierten Diff-Tools nicht angekommen. Ich halte einen 3-Way-Diff für eine gute Methode, um das Ergebnis schwieriger Merges darzustellen
Wenn man GitHub nutzt, kann man auf der Pull-Request-Seite von github.com
.drücken oder die Domain ingithub.devändernDann öffnet sich VSCode im Browser, und der Pull Request wird als Diff-Ansicht angezeigt
Der Vorteil ist, dass man die ganze Datei sehen kann und der Diff-Algorithmus ein anderer ist als auf github.com, wodurch komplexe Diffs manchmal leichter zu lesen sind
Eine Side-by-Side-Diff-Ansicht, die PR-Kommentare als Liste außerhalb der Zeilen anzeigt, könnte sehr nützlich sein, aber es ist schade, dass man dafür eine komplette VSCode-Instanz im Browser starten muss
Eigentlich war das so nützlich, dass GitHub früher so eine Split View hatte und man die Leute nicht in eine Buzzword-Suppe aus AI/ML/crypto/blockchain/cloud-enabled Entwicklungsumgebung drängen musste
Die Idee ist gut, die Umsetzung aber miserabel. Das klingt fast wie eine Zusammenfassung des heutigen GitHub insgesamt
Meld eignet sich für diesen Zweck ziemlich gut
https://meldmerge.org/
Mein Lieblingstool ist diff2html-cli: https://diff2html.xyz/
https://www.npmjs.com/package/diff2html-cli
Damit lassen sich Diffs als HTML anzeigen, unterstützt werden Side-by-side- und Unified-Darstellungen