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:
1 2 4 6 8
trunk o-->o-->o---->o---->o
\
\ 3 5 7
b1 +->o---->o---->o
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:
svn merge -r 2:7 {link to branch b1}
… 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:
1 2 4 6 8 9
trunk o-->o-->o---->o---->o-->o "the merge commit is at r9"
\
\ 3 5 7
b1 +->o---->o---->o
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:
12 14
trunk …-->o-------->o
"Okay, so when did we merge last time?"
13 15
b1 …----->o-------->o
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 BZR branch
). 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.
a… b… c…
origin o<---o<---o
^master
|
| clone
v
a… b… c…
alice o<---o<---o
^master
^origin/master
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:
a… b… c…
origin o<---o<---o
^ master
"what'll happen after a push?"
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
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):
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
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:
a… b… c… f…
bob o<---o<---o<---o
^ master
^ origin/master
"can Bob push his changes?"
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
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 denen origin
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 ist pull
und merge
oder BZR ist merge
). Dies ist ein zweistufiger Prozess. Zuerst muss Bob die neuen Revisionen abrufen, die sie so kopieren, wie sie aus dem origin
Repository stammen. Wir können jetzt sehen, dass der Graph divergiert:
v master
a… b… c… f…
bob o<---o<---o<---o
^
| d… e…
+----o<---o
^ origin/master
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
Der zweite Schritt des Pull-Prozesses besteht darin, die divergierenden Spitzen zusammenzuführen und das Ergebnis festzuschreiben:
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
^ origin/master
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ühren merge
). Was später getan werden muss, ist, diese Änderungen erneut auf zu übertragen origin
, was zu einer schnellen Zusammenführung führt, da das Zusammenführungs-Commit ein direkter Nachkomme der neuesten im origin
Repository ist:
v origin/master
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
v master
a… b… c… f… 1…
origin o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
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.