Versionsverwaltung from Hell



  • Hallo,
    ich habe ein Problem bei dem ihr mir bestimmt helfen könnt...

    Und zwar geht es darum ein neues Versionsverwaltungssystem in unserer Firma einzuführen da das alte, naja sagen wir mal "suboptimal" ist (*hust* VSS *hust*). Erster Ansatz war: na dann stellen wir halt einfach auf Subversion um, das ist verbreitet und wird schon irgendwie für uns geeignet sein, außerdem gibt es ein Migrationstool das tatsächlich fast fehlerfrei funktioniert.

    Leider müssen wir nun feststellen, daß es nicht ganz so einfach ist wie ursprünglich gedacht, und das hat gleich mehrere Gründe. Aber vorab erstmal ein paar Infos zur grundlegenden Struktur mit der wir es zu tun haben (damit ihr versteht wo die Probleme liegen).

    Wir entwickeln und produzieren Messsysteme, diese bestehen üblicherweise aus dem Gerät selbst und der passenden PC-Software um das Gerät zu steuern und die Daten auszuwerten. Das Gerät wiederum besteht seinerseits aus mehreren Komponenten mit jeweils einer eigenen Firmware, die über verschiedene Schnittstellen sowohl untereinander als auch mit de PC-Software kommunizieren. Es ist also alles irgendwie voneinander abhängig, so daß man z.B. die Firmware einer Komponente nicht losgelöst vom Rest des Systems betrachten kann.

    Nun zu den Problemen, die die Migration von VSS auf irgendein anderes Tool deutlich erschweren:

    Shared Files
    Dieses "Feature" von VSS wurde bei uns in den letzten 10 Jahren massiv eingesetzt, allerdings gibt es sowas (vermutlich aus gutem Grund) bei anderen Versionsverwaltungssystemen scheinbar nicht. Wer nicht weiss worum es dabei geht: man kann in VSS eine Datei an mehrere Stellen (unterschiedliche Projekte innerhalb des Repositories) "sharen", und von jeder dieser Stellen aus ganz normal bearbeiten und einchecken (d.h. ich ändere eine gesharte Datei in Projekt1, und wenn ich Projekt2 - das die Datei ebenfalls enthält - abrufe sind die Änderungen ebenfalls vorhanden). Von der Funktionsweise her verhalten sich die Dinger also ein bischen wie Hardlinks auf Dateisystemebene.
    Nun sieht es bei unserer Software/Firmware so aus, daß viele Dateien an viele Stellen geshared wurden. Insbesondere bei der Firmware sind große Codeteile nunmal einfach identisch (z.B. alles was mit der Kommunikation zu tun hat, da das bei allen Komponenten gleich ist), und wurden daher zwischen mehreren Projekten geshared. Dabei sind aber nicht alle Dateien in alle Projekte geshared, sondern selektiv jede Datei einzeln dahin wo sie halt gebraucht wird. Anders ausgedrückt gibt es keinerlei definierte Struktur (wie etwa Gruppen von geshareten Dateien die zusammen als Bibliothek betrachtet werden könnten), und was vielleicht noch schlimmer ist: es gibt diverse gesharete Dateien die unterschiedlich sind, aber den gleichen Namen haben (z.B. gibt es 3 verschiedene CAN.c, die jeweils in unterschiedliche Projekte geshared sind).
    Kurz gesagt: großes Chaos, das inzwischen aber Dimensionen erreicht hat die nicht mehr handhabbar sind (wir reden hier von >700 Dateien die an insgesamt >5000 Stellen geshared wurden, und das ist nur ein relativ kleiner Teil unseres inzwischen >5GB großen Repositories, dazu kommen noch massenhaft ganz normale Source Dateien die nicht geshared sind).

    Migriert man das Repository nach Subversion geht die Information zu den geshareten Dateien verloren, und man erhält viele unabhängige Dateien mit identischem Inhalt und identischer History. Damit können wir so erstmal nichts anfangen, denn der Inhalt der ehemals geshareten Dateien soll natürlich nicht nur am Tag nach der Migration identisch sein, sondern auch noch in einem Jahr. Bei der Suche nach einer Lösung für das Problem sind wir auf die sog. "externals" gestoßen, die auf den ersten Blick genau das ermöglichen was wir brauchen, nämlich den gleichen Code in vielen verschiedenen Projekten zu nutzen. Natürlich müssen die geshareten Dateien dafür erstmal zu Bibliotheken zusammengefasst werden, außerdem ändern sich massenhaft include Pfade, aber das sind Probleme die sich per Script lösen lassen sollten.

    Wir haben also mal ein kleines SVN Repository erstellt um testen zu können wie sich die externals verhalten, und wo es ggf. noch Probleme gibt. Als erstes ist uns aufgefallen, daß alles in ein großes Repository muss wenn wir Änderungen die sowohl externals als auch anderen Code betreffen in einem Changeset haben wollen. Natürlich sollten Bibliotheken prinzipiell erstmal ziemlich unabhängig voneinander und vom restlichen Code sein, aber was wir haben werden kann man noch nicht guten Gewissens als Bibliothek bezeichnen, sondern eher als "Dateien die sich konfliktfrei zu einer Gruppe haben zusammenfassen lassen". Natürlich wollen wir langfristig unabhängige Bibliotheken mit wohldefinierten Schnittstellen haben, aber das können wir nicht sofort alles umbauen.

    Soweit so gut. Wenn die externals innerhalb des gleichen Repositories waren, konnten wir sie aus einem Projekt heraus in dem sie eingebunden waren editieren, und zusammen mit anderen Änderungen aus dem Projekt in einem gemeinsamen Changeset committen. Das klappt also wunderbar (ob man es normalerweise tun sollte ist eine andere Frage)... solange man nicht auf die Idee kommt einen branch anzulegen 😞

    Früher haben wir nicht gebrancht, aus Angst daß uns VSS um die Ohren fliegt falls wir es versuchen. Jetzt sind wir aber an einem Punkt wo klar ist daß wir ein neues Versionskontrollsystem einführen werden, das tatsächlich mit branches umgehen kann. Da entsteht natürlich der Wunsch, den Prozess der Softwareentwicklung besser zu strukturieren, um so z.B. einen definierten Release-Cycle einführen zu können. In so einem Fall bietet es sich an, einen branch für ein neues Release zu erzeugen sobald alle geplanten Features vorhanden sind, und auf diesem branch nurnoch Bugfixes einzubauen.

    Der nächste Versuch mit dem Testrepository war also: was passiert mit den externals, wenn man das gesamte Repository brancht? Und ja, wir wollen das gesamte Repository mit allen Software- und Firmwareprojekten branchen, da alle voneinander abhängig sind und ohnehin gemeinsam released werden. Das Ergebnis war leider wenig erfreulich, denn mit den externals passiert genau garnix. Zwar werden die Verzeichnisse auf die die externals verweisen natürlich mit gebrancht, aber die externals selbst verweisen noch auf trunk (und nicht wie gewünscht auf den neuen branch).

    Daraus haben wir 2 Schlussfolgerungen gezogen:
    1. man darf (in unserem Fall) niemals ein einzelnes Projekt branchen, sondern immer nur trunk (was erstmal kein Problem ist, denn das wollten wir eh so machen)
    2. wenn wir branchen müssen alle externals angepasst werden, und das möglichst automatisch da Fehler sonst vorprogrammiert sind.

    Also bräuchten wir wohl ein pre-commit hook, welches branches unterhalb trunk verhindert und bei erlaubten branches alle externals anpasst......

    Und das war dann der Punkt an dem wir uns gefragt haben, ob das so überhaupt noch ansatzweise einen Sinn ergibt, oder ob svn wegen der external-Problematik vielleicht doch nicht für uns geeignet ist.

    Was meint ihr dazu?
    Wäre git oder mercurial vielleicht die bessere Wahl, oder gäbe es da evtl. noch mehr bzw. andere Probleme?
    Ist der Ansatz mit den externals überhaupt sinnvoll, oder gibt es einen besseren/einfacheren Weg um unser Ziel zu erreichen?

    Ich hoffe ihr könnt uns weiterhelfen, denn uns gehen langsam die Ideen aus.



  • Hmpf schrieb:

    ... solange man nicht auf die Idee kommt einen branch anzulegen 😞

    Früher haben wir nicht gebrancht, aus Angst daß uns VSS um die Ohren fliegt falls wir es versuchen. Jetzt sind wir aber an einem Punkt wo klar ist daß wir ein neues Versionskontrollsystem einführen werden, das tatsächlich mit branches umgehen kann.

    Subversion kann nicht sonderlich gut mit branches umgehen. Das sieht man schon daran, dass die History linear ist und nicht branchen kann, und dass ein "branch" in Subversion einfach ein neuer Ordner ist, in den alles bisherige kopiert wird (wenn auch ein bisschen effizienter implementiert). Wenn du ein Versionskontrollsystem suchst, das wirklich mit Branches umgehen kann, würd ich dir schon eher git empfehlen oder irgendwas anderes, was moderner ist als Subversion.

    "Shared files", wie du sie beschrieben hast, klingt aber nach etwas, das nicht viele Versionskontrollsysteme anbieten. Soweit ich weiß kann auch git das nicht direkt umsetzen. "Shared files" hören sich aber auch sehr gefährlich an, wie du schon gesagt: Chaos, bei dem keiner mehr wirklich durchblickt.

    Vielleicht kommt man nicht darum, im ersten Schritt die shared files in sinnvolle Einheiten zu packen und dann sauber als externe Bibliothek einzubinden.

    Nur damit ich's verstehe: Angenommen es gibt zwei Projekt-Repositories A und B. Repo A enthält A.c, Repo B enthält B.c, und beide Repos enthalten C.c als eine shared file. Wenn nun ein Entwickler in A die beiden Dateien A.c und C.c verändert und einen Commit anlegt mit der (schlechten) Commit-Message "Bugfix in A.c und C.c", wie sieht diese Änderung aus der Sicht von Repository B aus? Der Commit kann ja nicht 1:1 übernommen werden, denn er verändert auch die Datei A.c, die es in B nicht gibt. Aber die Änderung an C.c sollte ja automatisch übernommen werden, weil es ein shared file ist. Wie sieht das aus aus Sicht von B?



  • Nur damit ich's verstehe: Angenommen es gibt zwei Projekt-Repositories A und B. Repo A enthält A.c, Repo B enthält B.c, und beide Repos enthalten C.c als eine shared file. Wenn nun ein Entwickler in A die beiden Dateien A.c und C.c verändert und einen Commit anlegt mit der (schlechten) Commit-Message "Bugfix in A.c und C.c", wie sieht diese Änderung aus der Sicht von Repository B aus? Der Commit kann ja nicht 1:1 übernommen werden, denn er verändert auch die Datei A.c, die es in B nicht gibt. Aber die Änderung an C.c sollte ja automatisch übernommen werden, weil es ein shared file ist. Wie sieht das aus aus Sicht von B?

    Das funktioniert aus zwei Gründen:
    1. Es ist nur ein großes Repository in dem sämtliche Projekte liegen
    2. VSS kennt keine Changesets, jede Datei hat ihre eigene History die unabhängig verwaltet wird

    Wenn ich also Projekt A und Projekt B habe (statt Repo A und B), und die Dateien A.C, B.c und C.c wie von dir beschrieben, dann wird die Änderung von C.c 1:1 so wie sie ist in Projekt B auftauchen.

    Vielleicht kommt man nicht darum, im ersten Schritt die shared files in sinnvolle Einheiten zu packen und dann sauber als externe Bibliothek einzubinden.

    Das wollen und werden wir auch, im ersten Schritt kann dabei aber nichts herauskommen das sich sauber als externe Bibliothek einbinden lässt. Denn das würde voraussetzen, daß es eine wohldefinierte Schnittstelle gibt, und der Code unabhängig von allem anderen ist. Das lässt sich so erstmal nicht erreichen, zumindest nicht kurzfristig.



  • diese "shared files" klingen für mich eher nach dependency management, sprich es gibt eine "library", die von vielen internen "projekten" benutzt wird. sowas würde ich nicht übers VCS regeln sondern über das build management.

    ihr könntet also diese "shared files" in ein eigenes repository auslagern und getrennt vom rest verwalten. ihr braucht dann halt ein build script, dass bei bedarf diese lib im jeweiligen projekt aktualisiert, z.b. indem einfach der aktuelle stabile branch ausgecheckt und ins projekt kopiert wird.

    idealerweise habt ihr ein CI tool, dass bei änderungen in der "shared lib" auch die abhängigen projekte baut und kompiliert. so merkt man frühzeitig, wenn eine änderung in der shared lib irgendwas kaputt macht.



  • SourceGear Vault ist genau für Kunden gemacht die von VSS weg wollen zu was "besserem", aber ein System möchten das man zumindest anfangs gleich verwenden kann:

    http://www.sourcegear.com/vault/

    Die saubere Variante wäre allerdings Libs aus diesen shared Files zu machen.

    lolhehe schrieb:

    ihr könntet also diese "shared files" in ein eigenes repository auslagern und getrennt vom rest verwalten.

    Wieso eigenes Repository? Damit alles komplizierter wird?

    Eigenes Verzeichnis mit trunk/branches/tags drunter und gut.



  • *hust* VSS *hust*

    Gesundheit!



  • Hmpf schrieb:

    Ist der Ansatz mit den externals überhaupt sinnvoll, oder gibt es einen besseren/einfacheren Weg um unser Ziel zu erreichen?

    Ich verstehe sowieso nicht wozu du überhaupt "externals" verwenden willst.
    Wir verwenden zig eigene Libs, aber keine einzige wird über "externals" irgendwo referenziert.

    Zum Entwickeln kommen Lib sowie Projekt aus dem Trunk, und wenn ein Release ansteht werden Projekt-Trunk UND Lib-Trunk in einen Release-Branch zusammenkopiert.

    Auf der Platte liegt dabei z.B. immer folgende Struktur:

    # entwickeln
    
    /src          - normales verzeichnis
    /src/lib1     - working copy von /repo/lib1/trunk
    /src/lib2     - working copy von /repo/lib1/trunk
    /src/project  - working copy von /repo/project/trunk
    
    # release branch
    
    /src          - normales verzeichnis
    /src/lib1     - working copy von /repo/project/branches/release-x/lib1
    /src/lib2     - working copy von /repo/project/branches/release-x/lib2
    /src/project  - working copy von /repo/project/branches/release-x/project
    


  • Erstmal danke für die vielen Antworten 🙂

    diese "shared files" klingen für mich eher nach dependency management, sprich es gibt eine "library", die von vielen internen "projekten" benutzt wird. sowas würde ich nicht übers VCS regeln sondern über das build management.
    ...
    idealerweise habt ihr ein CI tool, dass bei änderungen in der "shared lib" auch die abhängigen projekte baut und kompiliert. so merkt man frühzeitig, wenn eine änderung in der shared lib irgendwas kaputt macht.

    Das wäre schon toll, allerdings betreiben wir bisher nichtmal einfachstes Build management, und von Dingen wie CI sind wir noch Lichtjahre entfernt.

    SourceGear Vault ist genau für Kunden gemacht die von VSS weg wollen zu was "besserem", aber ein System möchten das man zumindest anfangs gleich verwenden kann:

    http://www.sourcegear.com/vault/

    Ja, da bin ich auch schon drüber gestolpert. Aber in diesem Punkt vertreten wir den Ansatz "ganz oder garnicht". Wir wollen ein System das weit verbreitet ist (wie eben svn, mercurial oder git), denn da wissen wir daß es jeweils eine große Community gibt, so daß eventuell auftretende Probleme schnell gelöst werden können. Außerdem möchten wir ja diesen Schritt nutzen, um gleich etwas mehr Struktur in unser Repository zu bringen. Hätten wir aber ein Tool das unser altes Repo 1:1 übernehmen kann, gäbe es sicherlich in absehbarer Zeit keine Gelegenheit mehr zum Aufräumen. Denn unser Chef unterstützt die Umstellung nur, weil VSS nicht mehr supported wird und wir die von MS empfohlene Maximalgröße des Repositories inzwischen überschritten haben (außerdem gibt es genug Horrorstories von Leuten, denen VSS Repos irreparabel gecrasht sind).

    Ich verstehe sowieso nicht wozu du überhaupt "externals" verwenden willst.
    Wir verwenden zig eigene Libs, aber keine einzige wird über "externals" irgendwo referenziert.

    Da gibt es leider ein paar Problemstellen die diesen Ansatz verhindern (oder zumindest erschweren). Ich versuche mal das an einem Beispiel zu erklären, und hoffe daß es halbwegs verständlich ist...

    Angenommen es gäbe 4 Projekte A, B, C und D. In allen Projekten gibt es die Datei shared_1.c, die man evtl. als Bibliothek "lib1" auslagern könnte. Die Datei shared_1.c inkludiert aber vielleicht eine Datei shared_2.h, die allerdings nicht in der gleichen Bibliothek liegen kann wie shared_1.c, und zwar aus folgendem Grund: Es gibt eine shared_2.h die in die Projekte A und B geshared ist, und eine andere shared_2.h (mit anderem Inhalt) für die Projekte C und D.

    Ohne externals (oder ein ähnliches Feature), stimmt der include Pfad in shared_1.c nur für eine der beiden Varianten von shared_2.h. Bindet man aber shared_2.h in den verschiedenen Projekten jeweils unter dem Verzeichnis \lib2 ein, könnte der include Pfad in beiden Fällen identisch sein (auch wenn tatsächlich z.B. einmal auf lib2_X und einmal auf lib2_Y verwiesen wird).

    Anders ausgedrückt: lib1 muss, je nachdem in welchem Projekt sie selbst eingebunden wird, unterschiedliche Varianten von lib2 verwenden.

    Solche Fälle (also Dateien mit gleichem Namen aber unterschiedlichem Inhalt, die in unterschiedliche Projekte geshared werden) haben wir an einigen Stellen im Repository. Wir wissen zwar noch nicht genau wie oft diese Konflikte auftreten, aber es sind sicher mehr als nur eine Handvoll.

    *hust* VSS *hust*

    Gesundheit!

    Danke, hoffentlich finden wir - mit eurer Hilfe - bald ein wirksames Medikament gegen diese Krankheit.



  • Hmpf schrieb:

    Die Datei shared_1.c inkludiert aber vielleicht eine Datei shared_2.h, die allerdings nicht in der gleichen Bibliothek liegen kann wie shared_1.c, und zwar aus folgendem Grund: Es gibt eine shared_2.h die in die Projekte A und B geshared ist, und eine andere shared_2.h (mit anderem Inhalt) für die Projekte C und D.

    Ohne externals (oder ein ähnliches Feature), stimmt der include Pfad in shared_1.c nur für eine der beiden Varianten von shared_2.h. Bindet man aber shared_2.h in den verschiedenen Projekten jeweils unter dem Verzeichnis \lib2 ein, könnte der include Pfad in beiden Fällen identisch sein (auch wenn tatsächlich z.B. einmal auf lib2_X und einmal auf lib2_Y verwiesen wird).

    Anders ausgedrückt: lib1 muss, je nachdem in welchem Projekt sie selbst eingebunden wird, unterschiedliche Varianten von lib2 verwenden.

    Solche Fälle (also Dateien mit gleichem Namen aber unterschiedlichem Inhalt, die in unterschiedliche Projekte geshared werden) haben wir an einigen Stellen im Repository. Wir wissen zwar noch nicht genau wie oft diese Konflikte auftreten, aber es sind sicher mehr als nur eine Handvoll.

    Tjo, das ist doof, und wird vermutlich einiges an Arbeit machen.

    Nur damit ich hier ne Vorstellung bekomme... von wie viel verschiedenen Projekten reden wir?



  • Das ist sogar sehr doof, und wir möchten mittelfristig auch deutlich mehr Ordnung da rein bringen. Nur darf die Umstellung auf ein neues Versionsverwaltungssystem natürlich nicht mehrere Wochen/Monate dauern, sondern muss innerhalb einiger Tage über die Bühne gehen. Daher sind wir gezwungen den alten Mist irgendwie so abzubilden, daß es erstmal funktioniert. Später wollen wir dann parallel zur normalen Entwicklung den ganzen Kram sukzessive neu strukturieren.

    Was die Anzahl der Projekte betrifft, die dürfte wohl so bei 20-30 liegen. Also nicht extrem viele, aber auch mehr als nur eine Handvoll.


Anmelden zum Antworten