Inhalt
- Der Aufstieg von Git
- Inhärente Risiken mit Git Merges
- Der Mann hinter dem Vorhang: Wie funktioniert Git?
- Beispiel: Zusammenführen von Commits
- Konflikte zusammenführen
- Was passiert in der Fusion?
- Der Hai im Wasser: Hilft Git oder tut es weh?
- Beispiel 2: Fehler beim Zusammenführen
- Ein schwer zu findender Fehler
- The Takeaway: Probleme mit Zweigen
- Stammbasierte Entwicklung
- Leistungen
- Das Problem ist nicht Git; So verwenden wir es
Mike Shoemake ist seit 20 Jahren ein erfolgreicher Softwareentwickler, der hochwertige Anwendungen und leistungsstarke Entwicklungsteams erstellt.
Der Aufstieg von Git
Git scheint die Welt zu erobern, was Versionskontrollsysteme betrifft. Vor zehn Jahren war es Subversion, die unser Interesse geweckt hatte (die rote Linie unten). Jetzt hat Git (die blaue Linie) die Kontrolle.
Trotzdem gibt es mit Git viel zu lernen. Viele von uns kamen zu Git, nachdem sie viel Zeit mit Subversion oder einem anderen Konkurrenten verbracht hatten, und die Art und Weise, wie wir Git verwenden, spiegelt unsere Erfahrung mit und unser Verständnis dieser anderen Tools wider. Wir haben gerade genug gelernt, um zu überleben, und sind schnell zu unseren Tagesjobs zurückgekehrt.
Aber Git unterscheidet sich sehr, sehr von jedem anderen Versionskontrollsystem, das Sie möglicherweise verwendet haben. Wenn du Git verstehst, ist es dein bester Freund. Wenn Sie dies nicht tun, können Sie es so verwenden, dass unnötige Risiken für Sie und Ihr Team entstehen. Überraschenderweise können Produktionsprobleme durch die Verwendung von Git in einer Weise entstehen, die nicht für die Verwendung vorgesehen war.
Inhärente Risiken mit Git Merges
Wie wir sehen werden, sind mit Git-Zusammenführungen Risiken verbunden, die über die Entwickler hinausgehen und die Lösung von Zusammenführungskonflikten durcheinander bringen. Dieses Risiko steigt dramatisch an, je länger wir auf die Integration unseres Codes warten. Es stellt sich heraus, dass große Zusammenschlüsse viel riskanter sind als kleine, und dennoch führen viele Entwicklungsteams große Zusammenschlüsse als Lebensform durch. Ihr Verzweigungsmodell wirkt sich möglicherweise negativ auf die Qualität der Produktion aus, und Sie wissen es noch nicht.
Schauen wir uns zunächst an, wie Git-Zusammenführungen funktionieren, und bestimmen dann, wie sie am besten verwendet werden können. In den folgenden Beispielen verwenden wir SourceTree, um zu visualisieren, was passiert.
Der Mann hinter dem Vorhang: Wie funktioniert Git?
Sie können sich Git-Zusammenführungen vorstellen, als würden Sie ein Kartenspiel mischen. Git nimmt zwei separate Kartendecks und verwebt sie zu einem einzigen Deck. Wenn Duplikate gefunden werden, werden Extras geworfen.
Beispiel: Zusammenführen von Commits
Betrachten Sie das folgende Beispiel:
Dies ist eine einfache Groovy-Klassendatei, die nur println () -Aufrufe ausführt. Jetzt haben zwei Entwickler, Jim und Fred, diese Datei gleichzeitig geändert, was eine Zusammenführung erfordert, um sie aufzulösen.
Jeder hat einen Zweig erstellt, der nur ein Name ist, dem eine Reihe von Commits zugeordnet werden können. Der Zweig (dh der Name) ist direkt mit dem letzten Commit verknüpft. Wenn also ein neuer Commit durchgeführt wird, wird der Name des Zweigs in den neuen verschoben. Jedes Commit zeigt auf seine Vorgänger zurück - sie zeigen niemals nach vorne.
Hier ist Jims Commit:
Beachten Sie, dass Jim Zeile 21 bearbeitet hat, um einen Kommentar hinzuzufügen, und Git dies als eine zusammenhängende Änderung ansieht, die ein Entfernen und ein Hinzufügen umfasst.
Und hier ist Freds Commit:
Wichtig ist, dass es hier verschiedene Änderungen gibt - einige überlappen sich und andere sind nur eine Zeile voneinander entfernt.
Um eine Vorschau der Zusammenführung zu erhalten, können wir beide Commits in SourceTree markieren, auf die Quelldatei klicken und den resultierenden Unterschied anzeigen.
Wenn beide Commits ausgewählt sind, sehen wir den Unterschied unten:
Konflikte zusammenführen
Git identifiziert diskrete Änderungen, wobei jede ein zusammenhängender Block von Zeilen ist, die sich geändert haben. Git wird versuchen, die geänderten Blöcke mit den unveränderten Blöcken zu verweben. Wenn zwei geänderte Blöcke nicht mindestens eine unveränderte Zeile dazwischen haben, betrachtet Git dies als Zusammenführungskonflikt.
In diesem Beispiel können wir bereits sehen, wo die Zusammenführungskonflikte liegen werden:
- Zeile 5
- Zeile 21
Da sich die roten und grünen Änderungen im Diff "berühren", weiß Git nicht, welche sie anbringen sollen. Daher werden diese Änderungen als Zusammenführungskonflikte gekennzeichnet und der Entwickler aufgefordert, dies herauszufinden. Da die anderen Änderungen durch gemeinsame Codezeilen getrennt sind, gibt es für Git eine klare Reihenfolge. Aus Gits Sicht geht es darum, wie man Dinge zusammenwebt und sicherstellt, dass sie in der richtigen Reihenfolge ausgeführt werden. Wenn Git nicht sicher ist, werden Sie aufgefordert, das Problem selbst zu beheben.
Was passiert in der Fusion?
Hier ist das Ergebnis der tatsächlichen Zusammenführung:
Wie Sie sehen können, wurden die Zusammenführungskonflikte dort angezeigt, wo sie erwartet wurden. Die Zeilen 29 und 30 werden als eine zusammenhängende Änderung betrachtet und sind daher zusammen im Zusammenführungskonflikt enthalten. Da sich die anderen Änderungen nicht überschneiden, kann Git die richtige Reihenfolge bestimmen und den Rest für Sie erledigen.
Der Hai im Wasser: Hilft Git oder tut es weh?
Git ist definitiv hilfreich, wenn es darum geht, Konflikte für uns zu lösen. Entwickler lieben diese Funktion! Viele von uns stützen sich stark auf die automatische Zusammenführung von Git, als wäre Git ein futuristischer Android mit verrückten Codierungsfähigkeiten, der sich um unsere leichte Arbeit kümmert, damit wir nicht ins Schwitzen kommen müssen. Und doch weiß Git nichts über den Kontext. Es wird keine semantische Analyse durchgeführt und es kann nicht festgestellt werden, ob die Änderungen aus zwei zusammengeführten Quelldateien tatsächlich zusammengehören oder sich gegenseitig ausschließen.
Beispiel 2: Fehler beim Zusammenführen
Betrachten Sie das folgende Beispiel:
Dies ist eine sehr einfache Groovy-Quelldatei mit einer Multiplikationsmethode (int, int) und einer Hauptmethode, die multiply () aufruft. Dies ist eigentlich der Code, der am Anfang des Hauptzweigs unten steht.
Fred hat der multiply () -Methode einen neuen Aufruf hinzugefügt:
Unbekannt von Fred, beschloss Jim, die Multiplikationsmethode einzustellen und sie und die davon abhängigen Methodenaufrufe zu entfernen.
Das Endergebnis der Zusammenführung (siehe unten) enthält einen Aufruf der multiply () -Methode, aber diese Methode ist jetzt nicht mehr vorhanden.
Ein schwer zu findender Fehler
Schlimmer noch, dieser Code wird tatsächlich kompiliert, weil er Groovy und nicht Java ist. Der einzige Weg, um zu wissen, dass dieses Problem besteht, besteht darin, zur Laufzeit eine Ausnahme zu erkennen. Wenn Ihre Fehlerbehandlung nicht besonders gut ist, fällt es Ihnen möglicherweise schwer, dieses Problem aufzuspüren, und Sie werden möglicherweise nie feststellen, dass es durch die automatische Zusammenführung von Git verursacht wurde.
Dynamische Sprachen (z. B. Groovy, Javascript, Python, Ruby, viele andere) sind aufgrund der begrenzten Überprüfung der Kompilierungszeit besonders anfällig für dieses Problem. Während eine solide Abdeckung von Einheiten- / Integrationstests sicherlich hilfreich ist, um Probleme zu identifizieren, die sich daraus ergeben können, verfügen Teams häufig nicht über die Abdeckung, die erforderlich ist, um sie vor Schaden zu schützen.
Während Git die meiste Zeit gute Arbeit bei der Lösung von Konflikten für Sie leistet, besteht die Möglichkeit, dass Fehler auftreten. Wenn Sie dies mit Instanzen von Entwicklern verbinden, die Zusammenführungskonflikte misshandeln, sind Git-Zusammenführungen definitiv mit einem Risiko verbunden. Wie können wir dieses Risiko mindern?
The Takeaway: Probleme mit Zweigen
Viele Entwicklungsteams haben Feature-Zweige als integralen Bestandteil ihres Verzweigungsmodells übernommen. Feature-Zweige sind ein erstklassiger Bürger des beliebten "GitFlow", und viele Organisationen haben benutzerdefinierte Verzweigungsmodelle erstellt, die sich stark auf Feature-Zweige stützen. Mithilfe von Feature-Zweigen können einzelne Entwickler isoliert arbeiten, bis ihr Feature vollständig ist, sodass sie nicht durch Änderungen anderer negativ beeinflusst werden müssen. Einige Organisationen gehen so weit, lange laufende Zweige zu verwenden, um ganze Teams zu isolieren, wenn mehrere Teams in derselben Codebasis arbeiten. Andere verwenden sie, um mehrere Releases zu isolieren, die gleichzeitig erstellt werden.
Letztendlich müssen all diese Dinge, die isoliert gebaut wurden, zusammengeführt werden, um sie in die Produktion zu bringen, und hier tritt das Risiko auf. Wenn zwei Zweige zusammengeführt werden und jeder große Änderungen aufweist, ist es nahezu unmöglich, sicher zu wissen, dass zwischen Git und unserer manuellen Konfliktlösung jede Situation korrekt behandelt wurde. Auch hier weiß Git nichts über Kontext, Zweck oder Semantik. Es geht nur um Ordnung.
Entwicklern wird beigebracht, dass lang laufende Feature-Zweige in Ordnung sind, solange Sie regelmäßig neue Änderungen im Zielzweig (dem Ziel für die bevorstehende Zusammenführung) in Ihrem Feature-Zweig zusammenführen. Dies soll Sie mit allen anderen auf dem Laufenden halten. Leider ist dies ein Trugbild. Wenn 5 Entwickler in einem Team einige Zeit an separaten Feature-Zweigen gearbeitet haben, spielt es keine Rolle, wie oft sie den Zielzweig in ihren jeweiligen Zweigen zusammenführen. Sie sind immer noch nicht integriert. Es ist eine erhebliche Menge an Änderungen aufgetreten, die für niemanden sichtbar sind, da die Benutzer noch nicht bereit sind, sich zusammenzuschließen.
Was ist, wenn nicht 5 Entwickler, sondern 25 oder 75 in derselben Codebasis arbeiten? Diese Zusammenführungen werden gegen Ende des Sprints durchgeführt, und es ist sehr zeitaufwändig, zu überprüfen, ob alles korrekt gehandhabt wurde. Eine verzögerte Integration birgt immer unnötige Risiken und stellt sie häufig genau dann dar, wenn Sie es am wenigsten wollen - wenn Sie einen Sprint oder eine Veröffentlichung abschließen.
Stammbasierte Entwicklung
Betrachten wir nun die trunkbasierte Entwicklung, bei der Entwickler aufgefordert werden, kleine, gut getestete Commits täglich, wenn nicht mehrmals täglich, in einen gemeinsamen Trunk-Zweig mit dem typischen Namen "master" zu verschieben. Wartungszweige werden erstellt, wenn Releases gelöscht werden, aber alle Neuentwicklungen gehen direkt in den Hauptzweig.
Große Features werden in kleine, mundgerechte Blöcke unterteilt, und Entwickler verwenden Feature-Toggle, um ihre Änderungen auszublenden, bis es Zeit ist, live zu gehen. Dies ist eine echte kontinuierliche Integration, die mehrere wichtige Auswirkungen auf uns hat:
- Da es keine Feature-Zweige gibt, erstellt jeder Entwickler den heutigen Code auf dem Code aller von gestern.
- Keine Zusammenführung von Zweigen - nur Änderungen an Stamm- und Wartungszweigen festschreiben und übertragen.
- Fehler, die für einen Wartungszweig bestimmt sind, werden immer zuerst im Stamm behoben und dann in den Wartungszweig gepflückt (um Regressionsprobleme zu vermeiden).
Leistungen
- Die Möglichkeit von Zusammenführungskonflikten wird drastisch reduziert. Da sich weniger Code geändert hat, haben wir wenig Gelegenheit für Konflikte.
- Häufige Verpflichtungen im Kofferraum zwingen Entwickler, die Qualität während des gesamten Bauzyklus zu berücksichtigen, anstatt sie bis zum Ende zu speichern (kleine Änderung, Test, Push; kleine Änderung, Test, Push).
- Konflikte werden eher früh während des Baus als spät zum Zeitpunkt der Zusammenführung erkannt.
- Durch die kontinuierliche Integration wird ein echtes Fenster in den aktuellen Status des Codes, der Version usw. erstellt. Im Schatten verbirgt sich nichts.
Eine verzögerte Integration kann Sie auch dazu zwingen, denselben Code mehrmals zu stabilisieren. Beispielsweise testen und stabilisieren einige Teams Features im Feature-Zweig, damit sie isoliert getestet werden können. Sobald die Zusammenführung erfolgt ist, ist es sehr wahrscheinlich, dass sich die Funktion destabilisiert hat, und jetzt müssen Sie diesen Vorgang erneut durchführen. Oder Sie nehmen an, dass es stabil ist, da es im Feature-Zweig funktioniert, und lassen es einfach so aus der Tür gehen. Dies alles könnte vermieden werden, wenn wir uns nur frühzeitig integrieren.
Das Problem ist nicht Git; So verwenden wir es
Git hat viele fantastische Funktionen. Die Fähigkeit zum Zusammenführen liegt weit über dem, was die Konkurrenten von Git bieten. Diejenigen von uns, die es verwendet haben, haben alle gesehen, wie Git Änderungen ohne unsere Hilfe erfolgreich zusammengeführt hat, was uns in ein falsches Sicherheitsgefühl wiegen kann. Das Problem ist überhaupt nicht Git. So benutzen wir es. Das Klügste, was wir tun können, ist, zu verstehen, was das Werkzeug ist und was nicht. Sobald wir das getan haben, können wir es so verwenden, wie es beabsichtigt war, und aufhören, uns damit zu verletzen.
Dieser Artikel ist genau und nach bestem Wissen des Autors. Der Inhalt dient nur zu Informations- oder Unterhaltungszwecken und ersetzt nicht die persönliche Beratung oder professionelle Beratung in geschäftlichen, finanziellen, rechtlichen oder technischen Angelegenheiten.