
Entwickler, die wissen und wissen, wie man mit Git arbeitet, sind in letzter Zeit um eine Größenordnung gewachsen. Sie gewöhnen sich an die Geschwindigkeit der Befehlsausführung. Sie gewöhnen sich an die Bequemlichkeit von Zweigen und das einfache Zurücksetzen von Änderungen. Konfliktlösung ist so alltäglich, dass Programmierer es gewohnt sind, Konflikte heldenhaft dort zu lösen, wo sie nicht sein sollten.
Unser Team bei Directum entwickelt ein Entwicklungstool für Plattformlösungen. Wenn Sie 1C gesehen haben, können Sie sich das Arbeitsumfeld unserer "Kunden" - Anwendungsentwickler - grob vorstellen. Mit diesem Entwicklungstool erstellt ein Anwendungsentwickler eine Anwendungslösung für Kunden.
Unser Team stand vor der Aufgabe, das Leben unserer Bewerber zu vereinfachen. Wir werden mit modernen Chips von Visual Studio, ReSharper und IDEA verwöhnt. Die Antragsteller forderten, dass wir git out of the box in das Tool integrieren.
Das ist die Schwierigkeit. Im Tool für jeden Entitätstyp (Vertrag, Bericht, Verzeichnis, Modul) kann eine Sperre vorhanden sein. Ein Entwickler begann mit der Bearbeitung des Entitätstyps und blockierte ihn, bis die Änderungen abgeschlossen und auf den Server übertragen wurden. Andere Entwickler sehen derzeit dieselbe Art von Entität schreibgeschützt an. Die Entwicklung erinnerte ein wenig an die Arbeit in SVN oder das Senden eines Word-Dokuments per E-Mail zwischen mehreren Benutzern. Ich will alles auf einmal, aber vielleicht nur einen.
Jeder Entitätstyp kann über viele Handler verfügen (Öffnen eines Dokuments, Validierung vor dem Speichern, Schreiben in die Datenbank), in die Sie Code schreiben möchten, der mit einer bestimmten Instanz der Entität funktioniert. Sperren Sie beispielsweise die Schaltflächen, zeigen Sie dem Benutzer eine Nachricht an oder erstellen Sie eine neue Aufgabe für die Darsteller. Der gesamte Code im Rahmen der von der Plattform bereitgestellten API. Handler sind Klassen, in denen viele Methoden liegen. Wenn zwei Personen dieselbe Datei mit dem Code reparieren mussten, war dies nicht möglich, da die Plattform den gesamten Entitätstyp zusammen mit dem abhängigen Code blockierte.
Unsere Praktizierenden gingen den ganzen Weg. Sie gaben sich leise eine "illegale" Kopie unserer Entwicklungsumgebung, kommentierten den blockierenden Teil aus und verschmolzen unsere Verpflichtungen mit sich selbst. Der Anwendungscode wurde unter dem Git gespeichert und über Tools von Drittanbietern (Git Bash, SourceTree und andere) festgeschrieben. Wir haben unsere Schlussfolgerungen gezogen:
- Unser Team hat die Bereitschaft der Anwendungsentwickler unterschätzt, sich in die Plattform einzufügen. Riesiger Respekt und Ehre!
- Die von ihnen vorgeschlagene Lösung ist nicht für die Produktion geeignet. Mit git werden die Hände einer Person gelöst und sie kann alles erschaffen. Um die ganze Vielfalt zu unterstützen, wird es dumm sein, nicht zu entführen. Darüber hinaus müssen Plattformkunden geschult werden. Das Dokumentieren aller Git-Befehle für die Plattform würde das Dokumentationsteam verrückt machen.

Was du von Git willst
Es ist also nicht gut, mit einem Git-Out für die Produktion auszugeben. Wir haben uns entschlossen, die Logik der Hauptoperationen irgendwie zusammenzufassen und ihre Anzahl zu begrenzen. Zumindest für die erste Veröffentlichung. Die Liste der Mannschaften wurde so weit wie möglich reduziert und blieb:
- Status
- begehen
- ziehen
- drücken
- zurücksetzen - hart auf HEAD
- Auf das letzte "Server" -Commit zurücksetzen
Für die erste Veröffentlichung beschlossen sie, sich zu weigern, mit Filialen zu arbeiten. Nicht, dass es sehr schwierig wäre, nur das Team hat die Zeitressource nicht erreicht.
In regelmäßigen Abständen senden unsere Partner ihre Anwendungsentwicklung und fragen: "Bei uns funktioniert etwas nicht. Was machen wir falsch?". In diesem Fall lädt sich die Anwendung selbst mit der Entwicklung eines anderen und untersucht den Code. Das hat früher so funktioniert:
- Der Entwickler hat das Archiv mit der Entwicklung entfernt;
- Die lokale Datenbank in Konfigurationen wurde geändert.
- Goss die Entwicklung eines anderen in seine Basis;
- Debugged, gefundene Fehler;
- Herausgegebene Empfehlungen;
- Er gab seine Entwicklung zurück.
Die neue Methodik passte nicht zum alten Ansatz. Ich musste meinen Kopf zerschlagen. Das Team schlug zwei Ansätze zur Lösung dieses Problems vor:
- Speichern Sie die gesamte Entwicklung in einem Git-Repository. Arbeiten Sie bei Bedarf mit der Entscheidung einer anderen Person zusammen, eine temporäre Verzweigung zu erstellen.
- Speichern Sie die Entwicklung verschiedener Teams in verschiedenen Repositories. Verschieben Sie die Einstellungen der in die Umgebung geladenen Ordner in die Konfigurationsdatei.
Wir beschlossen, dem zweiten Weg zu folgen. Der erste schien schwieriger zu implementieren zu sein, und außerdem war es einfacher, sich mit einem Astwechsel in den Fuß zu schießen.
Aber der zweite ist auch nicht süß. Die oben beschriebenen Befehle sollten nicht nur innerhalb desselben Repositorys funktionieren, sondern mit mehreren gleichzeitig. Gibt es eine Änderung der Entitätstypen aus verschiedenen Repositorys? Wir zeigen sie in einem Fenster. Dies ist für den Anwendungsentwickler bequemer und transparenter. Durch Drücken der Schaltfläche "Festschreiben" schreibt das Tool Änderungen an jedem der Repositorys fest. Dementsprechend arbeiten die Pull / Push / Reset-Befehle "unter der Haube" mit physikalisch unterschiedlichen Repositorys.

Libgit2sharp
Um mit Git zu arbeiten, haben wir zwei Optionen ausgewählt:
- Arbeiten Sie mit auf dem System installiertem Git, ziehen Sie es durch Process.Start und analysieren Sie die Ausgabe.
- Verwenden Sie libgit2sharp, das über Pinvoke die libgit2-Bibliothek abruft.
Es schien uns, dass die Verwendung einer vorgefertigten Bibliothek eine vernünftige Lösung ist. Vergebens. Ich werde dir etwas später sagen warum. Die Bibliothek gab uns zunächst die Möglichkeit, schnell einen funktionierenden Prototyp herauszubringen.
Erste Entwicklungsiteration
Die Implementierung war in etwa einem Monat möglich. Tatsächlich war das Verschrauben des Gits schnell und die meiste Zeit versuchten wir, die geöffneten Wunden zu heilen, weil wir den alten Mechanismus zum Speichern der Quelldateien herausgeschnitten hatten. Alles, was der git status
zurückgegeben hat, wurde an die Schnittstelle zurückgegeben. Wenn Sie auf jede Datei klicken, wird diff angezeigt. Es sah aus wie eine Git-GUI-Oberfläche.
Zweite Entwicklungsiteration
Die erste Option war zu informativ. Jedem Entitätstyp sind viele Dateien gleichzeitig zugeordnet. Diese Dateien verursachten Rauschen und es wurde unklar, welche Arten von Entitäten sich geändert haben und was genau.
Gruppierte Dateien nach Entitätstyp. Jede Datei erhielt einen für Menschen lesbaren Namen, genau wie in der GUI. Metadaten vom Entitätstyp werden in JSON beschrieben. Sie mussten auch in einem für Menschen lesbaren Format präsentiert werden. Die Analyse der Änderungen in den json-Versionen "vorher" und "nachher" wurde unter Verwendung der jsondiffpatch-Bibliothek gestartet, und dann haben sie ihre eigene Implementierung des JSON-Vergleichs geschrieben (im Folgenden werde ich jsondiff nennen). Wir führen die Vergleichsergebnisse durch Analysegeräte, die für Menschen lesbare Aufzeichnungen erstellen. Viele Dateien wurden ausgeblendet und hinterließen einen einfachen Eintrag im Änderungsbaum.
Das Endergebnis ist wie folgt:

Probleme mit libgit2
Libgit2 sorgte für eine große Anzahl unerwarteter Überraschungen. Der Umgang mit einigen war jenseits der Macht einer vernünftigen Zeit. Ich werde dir sagen, woran ich mich erinnere.
Unerwartet und schwer zu reproduzieren fällt auf einige Standardoperationen. "Kein Fehler von der nativen Bibliothek bereitgestellt" teilt uns den Wrapper mit. Großartig. Sie fluchen, Sie erstellen die native Bibliothek beim Debuggen neu, Sie wiederholen einen zuvor gelöschten Fall, aber er stürzt im Debug-Modus nicht ab. In Release neu erstellen und wieder fallen.
Wenn ein Drittanbieter-Tool, z. B. SourceTree, parallel zu libgit2sharp ausgeführt wird, werden beim Festschreiben möglicherweise einige Dateien nicht festgeschrieben. Oder friert ein, wenn Unterschiede in einigen Dateien angezeigt werden. Sobald Sie versuchen zu debuggen, können Sie nicht mehr reproduzieren.
Bei einer unserer Anwendungen dauerte die Implementierung des git status
Analogons 40 Sekunden. Vierzig Carl! Gleichzeitig funktionierte der von der Konsole aus gestartete Git für eine Sekunde so, wie er sollte. Ich habe ein paar Tage damit verbracht, das zu klären. Bei der Suche nach Änderungen überprüft Libgit2 die Dateiattribute von Ordnern und vergleicht sie mit dem Eintrag im Index. Wenn die Änderungszeit unterschiedlich ist, hat sich im Ordner etwas geändert, und Sie müssen in die Dateien schauen und / oder in sie schauen. Und wenn sich nichts geändert hat, sollten Sie nicht hineinklettern. Diese Optimierung ist anscheinend auch in Console Git. Ich weiß nicht aus welchem Grund, aber nur für eine Person im Git-Index hat sich die Zeit geändert. Aus diesem Grund überprüfte git jedes Mal den Inhalt ALLER Dateien im Repository auf Änderungen.
Kurz vor der Veröffentlichung gab unser Team den Wünschen des Anwendungsteams nach und ersetzte git pull
durch fetch + rebase + autostash
. Und dann kamen eine Reihe von Fehlern zu uns, darunter "Kein Fehler von der nativen Bibliothek bereitgestellt".
Status, Pull und Rebase funktionieren viel länger als das Aufrufen von Konsolenbefehlen.
Automatische Zusammenführung
Dateien in der Entwicklung werden in zwei Typen unterteilt:
- Dateien, die die Anwendung im Entwicklungstool sieht. Zum Beispiel Code, Bilder, Ressourcen. Solche Dateien müssen wie git zusammengeführt werden.
- JSON-Dateien, die von der Entwicklungsumgebung erstellt werden, aber vom Anwendungsentwickler nur in Form einer GUI angezeigt werden. Sie müssen Konflikte automatisch lösen.
- Generierte Dateien, die bei der Arbeit mit dem Entwicklungstool automatisch neu erstellt werden. Diese Dateien gelangen nicht in das Repository. Das Tool legt sofort .gitignore sorgfältig ab.
Mit einer neuen Methode konnten zwei verschiedene Applikatoren denselben Entitätstyp ändern.
Beispielsweise ändert Sasha die Informationen zum Speichern des Entitätstyps in der Datenbank und schreibt einen Handler für das Sicherungsereignis, und Sergey formatiert die Darstellung der Entität. Aus Git-Sicht wird dies kein Konflikt sein und beide Änderungen werden ohne Komplexität zusammengeführt.
Und dann hat Sasha die Property1-Eigenschaft geändert und einen Handler dafür festgelegt. Sergey hat die Property2-Eigenschaft erstellt und den Handler festgelegt. Wenn Sie die Situation von oben betrachten, stehen ihre Änderungen nicht in Konflikt, obwohl aus Sicht von git dieselben Dateien betroffen sind.
Ich wollte, dass das Instrument diese Situation selbst lösen kann.
Ein Beispielalgorithmus zum Zusammenführen von zwei JSON im Konfliktfall:
Download von der JSON Base Git.
Download von unserer JSON Gita.
Herunterladen ihrer JSON von einem Git.
Mit jsondiff bilden wir base-> unsere Software-Patches und wenden diese an. Der resultierende JSON heißt P1.
Mit jsondiff bilden wir base-> ihre Software-Patches und wenden sie auf unsere an. Der resultierende JSON heißt P2.
Idealerweise nach dem Anwenden der Patches P1 === P2. Wenn ja, schreiben Sie P1 auf die Festplatte.
- In einem unvollständigen Fall (wenn tatsächlich ein Konflikt besteht) empfehlen wir dem Benutzer, zwischen P1 und P2 zu wählen, um mit seinen Händen fertig zu werden. Wir schreiben die Auswahl auf die Festplatte.
Nach dem Zusammenführen prüfen wir, ob der Status ohne Validierungsfehler erreicht ist. Wenn Sie noch nicht angekommen sind, brechen Sie die Zusammenführung ab und bitten Sie den Benutzer, sie zu wiederholen. Dies ist nicht die beste Lösung, garantiert jedoch zumindest, dass die Fusion ab dem zweiten oder dritten Versuch ohne unangenehme Folgen erfolgt.
Zusammenfassung
- Metzger sind froh, dass sie legal verwenden können.
- Die Einführung von Git beschleunigte die Entwicklung.
- Automatische Fusionen sehen im Allgemeinen wie Magie aus.
- Wir legen die zukünftige Ablehnung von libgit2 zugunsten des Aufrufs des Git-Prozesses fest.