Warum Merge-Konflikte Entwickler abschrecken (und warum das nicht sein muss)
Wenn Sie Git schon eine Weile benutzen, sind Sie mit ziemlicher Sicherheit schon auf den gefürchteten Merge-Konflikt gestoßen. Der Moment, in dem Git Ihnen mitteilt, dass es Ihre Änderungen nicht automatisch zusammenführen kann, fühlt sich an, als würde man gegen eine Wand laufen. Viele Entwickler, vor allem am Anfang ihrer Karriere, betrachten Merge-Konflikte als etwas, das schiefgelaufen ist. Aber die Wahrheit ist einfacher: Merge-Konflikte sind ein normaler Bestandteil der kollaborativen Entwicklung. Man braucht nur den richtigen Ansatz, um sie effizient zu lösen.
Was ist ein Merge-Konflikt?
Ein Merge-Konflikt entsteht, wenn Git Änderungen aus zwei verschiedenen Branches nicht automatisch zusammenführen kann. Das passiert typischerweise, wenn zwei Personen (oder sogar dieselbe Person auf verschiedenen Branches) die gleichen Zeilen in derselben Datei bearbeiten.
Wenn Git einen Konflikt erkennt, fügt es Konfliktmarkierungen direkt in die betroffenen Dateien ein:
<<<<<<< HEAD
const timeout = 5000;
=======
const timeout = 10000;
>>>>>>> feature/update-timeout
Alles zwischen <<<<<<< HEAD und ======= ist Ihre Version (oft als "ours" bezeichnet). Alles zwischen ======= und >>>>>>> ist die eingehende Version ("theirs"). Ihre Aufgabe ist es, zu entscheiden, wie das Endergebnis aussehen soll, die Markierungen zu entfernen und Git mitzuteilen, dass der Konflikt gelöst ist.
Der traditionelle Weg: Konfliktmarkierungen von Hand bearbeiten
Der einfachste Ansatz ist, die konfliktbehaftete Datei in einem Texteditor zu öffnen, jede Gruppe von Konfliktmarkierungen zu finden und den Code manuell zu bearbeiten. Bei einem einzelnen Konflikt in einer kleinen Datei funktioniert das gut. Aber es wird schnell mühsam, wenn Sie es mit Folgendem zu tun haben:
- Mehrere Konflikte, die über eine einzelne Datei verstreut sind
- Mehrere konfliktbehaftete Dateien im selben Merge
- Komplexe Änderungen, bei denen Sie Teile beider Versionen kombinieren müssen
- Verschachtelte oder benachbarte Konflikte, bei denen die Markierungen schwer zu lesen sind
Das manuelle Bearbeiten von Markierungen ist fehleranfällig. Es passiert leicht, dass man versehentlich eine Markierung stehen lässt, den falschen Block löscht oder einen subtilen Fehler einführt, weil man den Überblick verloren hat, was jede Version bezwecken sollte. Es muss einen besseren Weg geben.
Der visuelle Weg: Einen 3-Wege-Merge-Editor verwenden
Was ist ein 3-Wege-Merge?
Ein 3-Wege-Merge zeigt Ihnen drei Versionen der Datei gleichzeitig:
- Base -- der gemeinsame Vorfahre, also die Version der Datei, bevor einer der Branches Änderungen vorgenommen hat
- Ours -- die Version der Datei in Ihrem Branch
- Theirs -- die Version der Datei im eingehenden Branch
Alle drei Versionen gleichzeitig sehen zu können, ist ein entscheidender Vorteil. Anstatt auf Konfliktmarkierungen zu starren und zu versuchen, nachzuvollziehen, was passiert ist, können Sie genau sehen, was jede Seite im Vergleich zum Original geändert hat. Dieser Kontext macht es wesentlich einfacher zu entscheiden, wie jeder Konflikt gelöst werden soll.
So funktioniert es in GitSquid
GitSquid enthält einen integrierten 3-Wege-Merge-Editor, der die Konfliktlösung unkompliziert macht, selbst wenn Sie noch nie einen Konflikt gelöst haben. So sieht der Workflow aus:
1. Alle konfliktbehafteten Dateien auf einen Blick sehen. Wenn ein Merge Konflikte erzeugt, listet das Konflikt-Panel jede betroffene Datei auf. Sie wissen sofort, welchen Umfang die Lösung haben wird.
2. Den 3-Wege-Editor öffnen. Klicken Sie auf eine beliebige konfliktbehaftete Datei, um sie im Merge-Editor zu öffnen. Sie sehen drei Spalten nebeneinander: Base links, Ours in der Mitte und Theirs rechts. Jede Spalte zeigt den gesamten Dateiinhalt mit Syntaxhervorhebung, sodass Sie den Code im Kontext lesen können.
3. Konflikte Block für Block lösen. Jeder Konflikt ist hervorgehoben und als eigenständiger Block dargestellt. Für jeden Konflikt gibt es klare Aktionsschaltflächen:
- Use Ours -- Ihre Version des konfliktbehafteten Codes beibehalten
- Use Theirs -- stattdessen die eingehende Version übernehmen
- Use Both -- beide Änderungen nacheinander einfügen
Ein Klick und der Konflikt ist gelöst. Kein Suchen nach Markierungen, kein Risiko, versehentlich die falschen Zeilen zu löschen.
4. Das Ergebnis feinjustieren. Unterhalb der drei Spalten zeigt ein Ergebnispanel die zusammengeführte Ausgabe in einem vollständigen Code-Editor, der von CodeMirror 6 mit Syntaxhervorhebung betrieben wird. Wenn keine der Ein-Klick-Optionen genau das liefert, was Sie brauchen, können Sie das Ergebnis direkt bearbeiten. Das ist nützlich, wenn Sie Teile beider Versionen auf individuelle Weise kombinieren müssen, zum Beispiel eine Funktionssignatur aus einem Branch beibehalten, aber die Implementierung aus einem anderen übernehmen möchten.
5. Als gelöst markieren. Sobald Sie mit dem Ergebnis zufrieden sind, markieren Sie die Datei als gelöst und fahren mit der nächsten konfliktbehafteten Datei fort. Wenn alle Dateien gelöst sind, können Sie den Merge abschließen.
Der entscheidende Vorteil ist, dass Sie nie den Kontext verlieren. Sie können jederzeit sehen, wie der Code aussah, bevor einer der Branches ihn verändert hat, was jeder Branch geändert hat und wie das Endergebnis aussehen wird. Diese Klarheit beseitigt den größten Teil der Angst vor Merge-Konflikten.
Tipps zur Vermeidung von Merge-Konflikten
Selbst mit guten Werkzeugen ist es immer besser, Konflikte von vornherein zu vermeiden, als sie lösen zu müssen. Hier sind einige praktische Gewohnheiten, die helfen:
- Regelmäßig pull ausführen. Je länger Sie warten, Änderungen aus dem Haupt-Branch zu integrieren, desto mehr divergiert Ihr Code von der Arbeit Ihrer Teamkollegen. Regelmäßiges Pullen hält den Abstand klein und verringert die Wahrscheinlichkeit überlappender Bearbeitungen.
- Branches kurzlebig halten. Ein Feature-Branch, der zwei Wochen existiert, sammelt weit mehr potenzielle Konflikte an als einer, der nur zwei Tage lebt. Teilen Sie große Features in kleinere, merge-fähige Schritte auf.
- Mit dem Team kommunizieren. Wenn zwei Personen an derselben Datei oder demselben Modul arbeiten müssen, kann ein kurzer Hinweis später viel Zeit sparen. Koordination muss nicht formell sein; eine kurze Nachricht reicht oft aus.
- Feature flags verwenden. Anstatt einen langlebigen Branch für ein Feature zu pflegen, das noch nicht bereit zum Ausliefern ist, mergen Sie es hinter einem Feature-Flag. Der Code wird kontinuierlich integriert, aber das Feature bleibt verborgen, bis es fertig ist.
Merge-Konflikte sind normal
Merge-Konflikte sind kein Zeichen dafür, dass etwas schiefgelaufen ist. Sie sind eine natürliche Konsequenz davon, dass mehrere Personen an derselben Codebasis arbeiten -- und genau dafür ist Versionskontrolle konzipiert. Der Unterschied zwischen einer frustrierenden und einer reibungslosen Erfahrung liegt meist an den verwendeten Werkzeugen.
Ein visueller 3-Wege-Merge-Editor beseitigt das Rätselraten. Anstatt Konfliktmarkierungen in einem Texteditor zu entziffern, sehen Sie jede Version des Codes, wählen die passende Lösung und machen weiter. Mit dem integrierten Merge-Editor von GitSquid wird die Konfliktlösung einfach ein weiterer Teil des Workflows, anstatt etwas, vor dem man sich fürchten muss.