Ich habe an einigen Stellen gehört, dass einer der Hauptgründe, warum verteilte Versionskontrollsysteme glänzen, das Zusammenführen viel besser ist als bei herkömmlichen Tools wie SVN. Liegt dies tatsächlich an inhärenten Unterschieden in der Funktionsweise der beiden Systeme oder haben bestimmte DVCS-Implementierungen wie Git / Mercurial nur cleverere Zusammenführungsalgorithmen als SVN?
400
Antworten:
Die Behauptung, warum das Zusammenführen in einem DVCS besser ist als in Subversion, beruhte weitgehend darauf, wie das Verzweigen und Zusammenführen in Subversion vor einiger Zeit funktioniert hat. Subversion vor 1.5.0 speicherte keine Informationen darüber, wann Zweige zusammengeführt wurden. Wenn Sie also zusammenführen wollten, mussten Sie angeben, welcher Revisionsbereich zusammengeführt werden musste.
Warum also hat Subversion verschmilzt saugen ?
Denken Sie über dieses Beispiel nach:
Wenn wir wollen fusionieren b1 die Änderungen in den Kofferraum wir den folgenden Befehl ausgeben würde, während auf einem Ordner stehen , die trunk ausgecheckt hat:
… Die versuchen, die Änderungen
b1
in Ihrem lokalen Arbeitsverzeichnis zusammenzuführen. Anschließend übernehmen Sie die Änderungen, nachdem Sie Konflikte gelöst und das Ergebnis getestet haben. Wenn Sie den Revisionsbaum festschreiben, sieht er folgendermaßen aus:Diese Art der Angabe von Revisionsbereichen gerät jedoch schnell außer Kontrolle, wenn der Versionsbaum wächst, da die Subversion keine Metadaten darüber hatte, wann und welche Revisionen zusammengeführt wurden. Überlegen Sie, was später passiert:
Dies ist größtenteils ein Problem des Repository-Designs von Subversion. Um einen Zweig zu erstellen, müssen Sie ein neues virtuelles Verzeichnis im Repository erstellen, in dem eine Kopie des Trunks gespeichert ist, in dem jedoch keine Informationen darüber gespeichert sind, wann und was Dinge wurden wieder zusammengeführt. Das wird manchmal zu bösen Zusammenführungskonflikten führen. Was noch schlimmer war, ist, dass Subversion standardmäßig das bidirektionale Zusammenführen verwendet, was einige lähmende Einschränkungen beim automatischen Zusammenführen aufweist, wenn zwei Zweigköpfe nicht mit ihrem gemeinsamen Vorfahren verglichen werden.
Um diese Subversion zu mildern, werden jetzt Metadaten für die Verzweigung und Zusammenführung gespeichert. Das würde alle Probleme richtig lösen?
Und ach übrigens, Subversion ist immer noch scheiße ...
Auf einem zentralisierten System wie Subversion saugen virtuelle Verzeichnisse . Warum? Weil jeder Zugriff hat, um sie anzusehen… sogar die Müll-Experimental-Experimente. Verzweigung ist gut, wenn Sie experimentieren möchten, aber nicht alle und ihre Tanten experimentieren möchten . Dies ist ernstes kognitives Rauschen. Je mehr Zweige Sie hinzufügen, desto mehr Mist werden Sie sehen.
Je mehr öffentliche Filialen Sie in einem Repository haben, desto schwieriger wird es, die verschiedenen Filialen im Auge zu behalten. Die Frage, die Sie haben werden, ist also, ob sich der Zweig noch in der Entwicklung befindet oder ob er wirklich tot ist, was in einem zentralen Versionskontrollsystem schwer zu sagen ist.
Nach dem, was ich gesehen habe, verwendet eine Organisation die meiste Zeit ohnehin standardmäßig einen großen Zweig. Das ist eine Schande, denn es wird wiederum schwierig sein, die Test- und Release-Versionen im Auge zu behalten, und was auch immer gut ist, kommt von der Verzweigung.
Warum sind DVCS wie Git, Mercurial und Bazaar beim Verzweigen und Zusammenführen besser als Subversion?
Dafür gibt es einen sehr einfachen Grund: Verzweigung ist ein erstklassiges Konzept . Es gibt keine virtuellen Verzeichnisse , und Zweige sind harte Objekte in DVCS, die es sein muss, um einfach mit der Synchronisation von Repositorys (dh Push and Pull ) zu arbeiten.
Das erste, was Sie tun, wenn Sie mit einem DVCS arbeiten, ist das Klonen von Repositorys (Git
clone
,clone
HG und BZRbranch
). Das Klonen ist konzeptionell dasselbe wie das Erstellen eines Zweigs in der Versionskontrolle. Einige nennen dies Gabelung oder Verzweigung (obwohl letztere oft auch für am selben Ort befindliche Zweige verwendet wird), aber es ist genau das Gleiche. Jeder Benutzer führt ein eigenes Repository aus, was bedeutet, dass eine Verzweigung pro Benutzer stattfindet .Die Versionsstruktur ist kein Baum , sondern ein Diagramm . Insbesondere ein gerichteter azyklischer Graph (DAG, dh ein Graph ohne Zyklen). Sie müssen sich wirklich nicht mit den Besonderheiten einer DAG befassen, außer dass jedes Commit eine oder mehrere übergeordnete Referenzen hat (auf denen das Commit basiert). Aus diesem Grund zeigen die folgenden Grafiken die Pfeile zwischen den Revisionen in umgekehrter Reihenfolge.
Ein sehr einfaches Beispiel für das Zusammenführen wäre dies; Stellen Sie sich ein zentrales Repository mit dem Namen
origin
Alice vor, die das Repository auf ihren Computer klont.Während eines Klons passiert, dass jede Revision genau so nach Alice kopiert wird, wie sie war (was durch die eindeutig identifizierbaren Hash-IDs bestätigt wird) und markiert, wo sich die Zweige des Ursprungs befinden.
Alice arbeitet dann an ihrem Repo, legt es in ihrem eigenen Repository fest und beschließt, ihre Änderungen voranzutreiben:
Die Lösung ist ziemlich einfach. Das einzige, was das
origin
Repository tun muss, ist, alle neuen Revisionen aufzunehmen und seinen Zweig auf die neueste Revision zu verschieben (die Git als "Schnellvorlauf" bezeichnet):Der Anwendungsfall, den ich oben dargestellt habe, muss nicht einmal etwas zusammenführen . Das Problem ist also nicht wirklich das Zusammenführen von Algorithmen, da der Drei-Wege-Zusammenführungsalgorithmus zwischen allen Versionskontrollsystemen ziemlich gleich ist. Es geht mehr um Struktur als um irgendetwas .
Wie wäre es, wenn Sie mir ein Beispiel zeigen, das eine echte Verschmelzung hat?
Zugegeben, das obige Beispiel ist ein sehr einfacher Anwendungsfall, also lassen Sie uns einen viel verdrehteren, wenn auch einen häufigeren machen. Erinnerst
origin
du dich, dass das mit drei Revisionen begann? Nun, der Typ, der sie gemacht hat, nennen wir ihn Bob , hat alleine gearbeitet und ein Commit für sein eigenes Repository gemacht:Jetzt kann Bob seine Änderungen nicht direkt in das
origin
Repository übertragen. Das System erkennt dies, indem überprüft wird, ob die Revisionen von Bob direkt von denenorigin
abweichen, was in diesem Fall nicht der Fall ist. Jeder Versuch zu pushen führt dazu, dass das System etwas sagt, das mit " Äh ... ich fürchte, Sie können das nicht tun, Bob ."So Bob muss Einzugs- und dann die Änderungen verschmelzen (mit Git
pull
oder hg istpull
undmerge
oder BZR istmerge
). Dies ist ein zweistufiger Prozess. Zuerst muss Bob die neuen Revisionen abrufen, die sie so kopieren, wie sie aus demorigin
Repository stammen. Wir können jetzt sehen, dass der Graph divergiert:Der zweite Schritt des Pull-Prozesses besteht darin, die divergierenden Spitzen zusammenzuführen und das Ergebnis festzuschreiben:
Hoffentlich kommt es bei der Zusammenführung nicht zu Konflikten (wenn Sie diese vorwegnehmen, können Sie die beiden Schritte manuell in git mit
fetch
und ausführenmerge
). Was später getan werden muss, ist, diese Änderungen erneut auf zu übertragenorigin
, was zu einer schnellen Zusammenführung führt, da das Zusammenführungs-Commit ein direkter Nachkomme der neuesten imorigin
Repository ist:Es gibt eine weitere Option zum Zusammenführen von git und hg, die Rebase , mit der Bobs Änderungen nach den neuesten Änderungen verschoben werden. Da will ich nicht diese Antwort mehr sein ausführliche ich Ihnen die lesen lasse git , Mercurial oder Basar docs darüber statt.
Versuchen Sie als Übung für den Leser herauszufinden, wie es mit einem anderen beteiligten Benutzer funktionieren wird. Dies geschieht ähnlich wie im obigen Beispiel mit Bob. Das Zusammenführen zwischen Repositorys ist einfacher als gedacht, da alle Revisionen / Commits eindeutig identifizierbar sind.
Es gibt auch das Problem, Patches zwischen den einzelnen Entwicklern zu senden. Dies war ein großes Problem bei Subversion, das in git, hg und bzr durch eindeutig identifizierbare Revisionen verringert wird. Sobald jemand seine Änderungen zusammengeführt hat (dh ein Zusammenführungs-Commit durchgeführt hat) und es an alle anderen im Team sendet, um es zu konsumieren, indem er entweder in ein zentrales Repository pusht oder Patches sendet, muss er sich keine Gedanken mehr über die Zusammenführung machen, da dies bereits geschehen ist . Martin Fowler nennt diese Arbeitsweise promiskuitive Integration .
Da sich die Struktur von Subversion unterscheidet und stattdessen eine DAG verwendet wird, können Verzweigungen und Zusammenführungen nicht nur für das System, sondern auch für den Benutzer einfacher durchgeführt werden.
quelle
In der Vergangenheit konnte Subversion nur eine direkte bidirektionale Zusammenführung durchführen, da keine Zusammenführungsinformationen gespeichert wurden. Dazu müssen Sie eine Reihe von Änderungen vornehmen und auf einen Baum anwenden. Selbst bei Zusammenführungsinformationen ist dies immer noch die am häufigsten verwendete Zusammenführungsstrategie.
Git verwendet standardmäßig einen 3-Wege-Zusammenführungsalgorithmus, bei dem ein gemeinsamer Vorfahr für die zusammengeführten Köpfe gefunden und das Wissen genutzt wird, das auf beiden Seiten der Zusammenführung vorhanden ist. Dies ermöglicht es Git, Konflikte intelligenter zu vermeiden.
Git hat auch einen ausgeklügelten Code zum Umbenennen, was ebenfalls hilfreich ist. Es werden keine Änderungssätze oder Tracking-Informationen gespeichert. Es wird lediglich der Status der Dateien bei jedem Commit gespeichert und mithilfe von Heuristiken nach Bedarf umbenannt und umbenannt (der Speicher auf der Festplatte ist komplizierter als dieser, aber die Schnittstelle es präsentiert sich der Logikschicht und legt keine Verfolgung frei).
quelle
Einfach ausgedrückt ist die Merge-Implementierung in Git besser als in SVN . Vor 1.5 zeichnete SVN keine Zusammenführungsaktion auf, sodass zukünftige Zusammenführungen ohne Hilfe des Benutzers nicht möglich waren, der Informationen bereitstellen musste, die SVN nicht aufzeichnete. Mit 1.5 wurde es besser, und tatsächlich ist das SVN-Speichermodell etwas leistungsfähiger als die DAG von Git. Aber SVN hat die Zusammenführungsinformationen in einer ziemlich komplizierten Form gespeichert, sodass Zusammenführungen massiv länger dauern als in Git - ich habe Faktoren von 300 in der Ausführungszeit beobachtet.
Außerdem behauptet SVN, Umbenennungen zu verfolgen, um das Zusammenführen von verschobenen Dateien zu erleichtern. Tatsächlich werden sie jedoch immer noch als Kopie und separate Löschaktion gespeichert, und der Zusammenführungsalgorithmus stößt in Situationen zum Ändern / Umbenennen immer noch über sie, dh wenn eine Datei in einem Zweig geändert und in dem anderen umbenannt wird und diese Zweige es sind zusammengeführt werden. Solche Situationen führen immer noch zu falschen Zusammenführungskonflikten, und bei Umbenennungen von Verzeichnissen führt dies sogar zu einem stillen Verlust von Änderungen. (Die SVN-Leute neigen dann dazu, darauf hinzuweisen, dass die Änderungen noch in der Historie sind, aber das hilft nicht viel, wenn sie nicht in einem Zusammenführungsergebnis sind, wo sie erscheinen sollten.
Git hingegen verfolgt nicht einmal Umbenennungen, sondern findet sie nachträglich heraus (zum Zeitpunkt der Zusammenführung) und tut dies ziemlich magisch.
Die SVN-Zusammenführungsdarstellung weist ebenfalls Probleme auf. In 1.5 / 1.6 konnten Sie automatisch so oft wie gewünscht von Trunk zu Branch zusammenführen, aber eine Zusammenführung in die andere Richtung musste angekündigt werden (
--reintegrate
), und der Zweig wurde in einem unbrauchbaren Zustand belassen. Viel später fanden sie heraus , dass dies tatsächlich nicht der Fall ist, und dass a) das--reintegrate
kann automatisch herausgefunden werden, und b) wiederholt verschmilzt in beiden Richtungen möglich.Aber nach all dem (was meiner Meinung nach ein Unverständnis darüber zeigt, was sie tun) wäre ich (OK, ich bin) sehr vorsichtig, SVN in jedem nicht trivialen Verzweigungsszenario zu verwenden, und würde idealerweise versuchen zu sehen, was Git davon hält das Zusammenführungsergebnis.
Andere Punkte in den Antworten, wie die erzwungene globale Sichtbarkeit von Zweigen in SVN, sind für die Zusammenführungsfunktionen nicht relevant (aber für die Benutzerfreundlichkeit). Auch die "Git speichert Änderungen, während SVN speichert (etwas anderes)" sind meistens falsch. Git speichert jedes Commit konzeptionell als separaten Baum (wie eine TAR- Datei) und verwendet dann einige Heuristiken, um dies effizient zu speichern. Die Berechnung der Änderungen zwischen zwei Commits ist von der Speicherimplementierung getrennt. Was wahr ist, ist, dass Git die Verlaufs-DAG in einer viel einfacheren Form speichert, als SVN seine Mergeinfo macht. Jeder, der versucht, Letzteres zu verstehen, wird wissen, was ich meine.
Kurz gesagt: Git verwendet ein viel einfacheres Datenmodell zum Speichern von Revisionen als SVN und könnte daher viel Energie in die eigentlichen Zusammenführungsalgorithmen stecken, anstatt zu versuchen, mit der Darstellung fertig zu werden => praktisch besseres Zusammenführen.
quelle
Eine Sache, die in den anderen Antworten nicht erwähnt wurde und die wirklich ein großer Vorteil eines DVCS ist, ist, dass Sie sich lokal festlegen können, bevor Sie Ihre Änderungen vornehmen. Wenn ich in SVN eine Änderung hatte, die ich einchecken wollte, und jemand in der Zwischenzeit bereits ein Commit für denselben Zweig durchgeführt hatte, bedeutete dies, dass ich ein Commit durchführen musste,
svn update
bevor ich ein Commit durchführen konnte. Dies bedeutet, dass meine Änderungen und die Änderungen der anderen Person jetzt miteinander vermischt sind und es keine Möglichkeit gibt, die Zusammenführung abzubrechen (wie mitgit reset
oderhg update -C
), da es keine Verpflichtung gibt, zu der ich zurückkehren kann. Wenn die Zusammenführung nicht trivial ist, bedeutet dies, dass Sie nicht weiter an Ihrer Funktion arbeiten können, bevor Sie das Zusammenführungsergebnis bereinigt haben.Aber vielleicht ist das nur ein Vorteil für Leute, die zu dumm sind, um separate Zweige zu verwenden (wenn ich mich richtig erinnere, hatten wir nur einen Zweig, der für die Entwicklung in dem Unternehmen verwendet wurde, in dem ich SVN verwendet habe).
quelle
BEARBEITEN: Dies befasst sich hauptsächlich mit diesem Teil der Frage:
Liegt dies tatsächlich an inhärenten Unterschieden in der Funktionsweise der beiden Systeme, oder haben bestimmte DVCS-Implementierungen wie Git / Mercurial nur cleverere Zusammenführungsalgorithmen als SVN?
TL; DR - Diese spezifischen Tools verfügen über bessere Algorithmen. Die Verteilung hat einige Workflow-Vorteile, ist jedoch orthogonal zu den Vorteilen beim Zusammenführen.
END EDIT
Ich habe die akzeptierte Antwort gelesen. Es ist einfach falsch.
Das Zusammenführen von SVN kann schmerzhaft und auch umständlich sein. Aber ignorieren Sie für eine Minute, wie es tatsächlich funktioniert. Es gibt keine Informationen, die Git aufbewahrt oder ableiten kann, die SVN nicht auch aufbewahrt oder ableiten kann. Noch wichtiger ist, dass es keinen Grund gibt, warum Sie durch das Speichern separater (manchmal teilweiser) Kopien des Versionskontrollsystems aktuellere Informationen erhalten. Die beiden Strukturen sind völlig gleichwertig.
Angenommen, Sie möchten "etwas Kluges" tun, in dem Git "besser" ist. Und dein Ding ist in SVN eingecheckt.
Konvertieren Sie Ihre SVN in das entsprechende Git-Formular, führen Sie sie in Git aus und überprüfen Sie das Ergebnis in einigen zusätzlichen Zweigen, möglicherweise mithilfe mehrerer Commits. Wenn Sie sich einen automatisierten Weg vorstellen können, ein SVN-Problem in ein Git-Problem umzuwandeln, hat Git keinen grundlegenden Vorteil.
Am Ende des Tages lässt mich jedes Versionskontrollsystem
Darüber hinaus ist es zum Zusammenführen auch nützlich (oder kritisch) zu wissen
Mercurial , Git und Subversion (jetzt nativ, zuvor mit svnmerge.py) können alle drei Informationen bereitstellen. Um mit DVC etwas grundlegend Besseres zu demonstrieren, weisen Sie bitte auf eine vierte Information hin, die in Git / Mercurial / DVC verfügbar ist und in SVN / Centralized VC nicht verfügbar ist.
Das heißt nicht, dass sie keine besseren Werkzeuge sind!
quelle
git merge-base
. Mit git können Sie "Zweige a und b bei Revision x teilen" sagen. Aber svn speichert "Dateien wurden von foo nach bar kopiert", daher müssen Sie mithilfe von Heuristiken herausfinden, dass beim Kopieren nach bar ein neuer Zweig erstellt wurde, anstatt Dateien innerhalb eines Projekts zu kopieren. Der Trick besteht darin, dass eine Revision in svn durch die Revisionsnummer und den Basispfad definiert wird . Obwohl es möglich ist, die meiste Zeit von "Stamm" auszugehen, beißt es, wenn es tatsächlich Zweige gibt.SVN - Tracks Dateien während Git Tracks
Inhaltändert. Es ist klug genug, einen Codeblock zu verfolgen, der von einer Klasse / Datei in eine andere umgestaltet wurde. Sie verwenden zwei völlig unterschiedliche Ansätze, um Ihre Quelle zu verfolgen.Ich benutze SVN immer noch stark, aber ich bin sehr zufrieden mit den wenigen Malen, die ich Git verwendet habe.
Eine schöne Lektüre, wenn Sie Zeit haben: Warum ich mich für Git entschieden habe
quelle
Lies einfach einen Artikel in Joels Blog (leider seinen letzten). Hier geht es um Mercurial, aber es geht tatsächlich um die Vorteile verteilter VC-Systeme wie Git.
Lesen Sie den Artikel hier .
quelle