[linux-l] Versionskontrollen (war: Warum gibt es keine einheitliche Dokumentation?)

Volker Grabsch vog at notjusthosting.com
Di Jan 23 16:23:24 CET 2007


On Sun, Jan 21, 2007 at 03:58:24PM +0100, Steffen Dettmer wrote:
> > Versions-Basiert    |  Changeset-Basiert
> > ------------------------------------------
> > RCS                 |  Darcs
> > CVS                 |  GIT
> > Subversion          |  Mercurial
> >                     |  Arch
> >                     |  Monotone
> 
> (Ja, gut dargestellt: Subversion ist auch nur ein RCS Nachfolger. Mit
> HTTP Unterstützung natürlich :))

Das wollte ich mit dieser Tabelle nicht ausdrücken.

> > Die Herausforderung, der alle Changeset-basierten Versionskontrollen
> > gegenüberstehen, ist folgende: Sie müssen Teilbäume mergen. An
> > irgendeiner Stelle driftet der Baum eventuell mehrmals auseinander,
> > und zwei der Blätter sollen zusammengeführt werden.
> > 
> > Das ist glaubich auch der tiefere Grund, warum sich CVS und auch
> > Subversion so schwer damit tun, "merges" zu automatisieren: Es ist
> > ein konzeptuelles Problem. 
> 
> Versteh ich nicht. CVS tut sich nicht schwer und es ist automatisiert.
> Ich muss nur genau sagen, /was/ ich mergen will. Von einem
> Changeset-basierten SCM erwarte ich, dass es mir da hilft. Wenn ich
> Changesets A, B, C und D in dieser Reihenfolge aufeinander aufbauend
> habe und B gemergt habe, und dann A - D merge, sollte mir so ein SCM das
> B nicht nochmal mergen.

Der Witz in Mercurial & Co. ist: Du brauchst erst gar nicht auf dieser
niedrigen Ebene zu denken.

Du hast zwei Branches (in Mercurial: zwei Repositories), die du mergen
willst.

Die können beide schon zigmal auseinander gegangen, zusammengegangen,
teilweise gemerged, u.s.w. sein. Interessiert dich nicht! Du sagst,
du willst die mergen, und genauo das wird erledigt.

> Bei CVS wäre das B komplett ein Konflikt. Ich
> muss A und dann C-D mergen. CVS weiss davon halt nix. Merge ich Branches
> (also sozusagen das komplette Changeset mit Eltern), das kann ich auch
> inkrementell machen (und bei neuen CVS sogar ohne wanderndes Mergetag),
> hab ich mit CVS aber auch kein Problem.

Du musst einen Überblick über alle Auseinanderdriftungen, teilweisen
Merges etc. haben. In Mercurial & Co. nicht. Das ist natürlich noch
nicht alles, aber ein sehr wesentlicher Punkt.

Du kannst in Mercurial wirklich auf dieser einfachen Ebene denken.
Im Folgenden meine ich mit "arbeiten", dass du mehrere Änderungen
durchführst und jeweils mit "hg commit" lokal eincheckst.

Du hast ein Repository/Arbeitsverzeichnis. Also ein Verzeichnis:

    $ trunk/

Davon willst du einen Branch haben:

    $ cp -r trunk branch-A

Du arbeitest etwas in branch-A/ und im trunk/ herum. Jemand hilft dir
bei branch-A. Du kannst es ihm wieder kopieren, oder er macht das
via "hg clone", das ist etwas effizienter:

    woanders$  hg clone  ssh:.../branch-A  branch-A

Er arbeitet an seinem Branch-A. Dann mergst du seine Arbeit bei dir:

    $ cd branch-A
    $ hg pull -u  ssh:woanders/.../branch-A

Mercurial wird dich warnen, weil ihr beide daran gearbeitet habt,
also musst du mergen, Konflikte beseiten, committen:

    $ hg merge
    ...
    $ hg commit

Du werkelst weiter. Okay, fertig. Dein branch-A geht in den Trunk:

    $ cd ../trunk
    $ hg pull -u  ../branch-A
    $ hg merge

Dein Branch kann jetzt weg:

    $ rm -r ../branch-A

Genaugenommen verschwindet der Branch nicht. All seine Versions-
Informationen sind ja nun in trunk/.

Du arbeitest weiter im Trunk. Plötzlich kommt dein Helfer und sagt
dir, dass er in seinem Branch-A noch etwas getan hat. Na? Wird's
unübersichtlich? Egal! Du holst seine Änderungen einfach mit in den
Trunk:

    $ hg pull -u  ssh:woanders/.../branch-A
    $ hg merge

Und all das ohne Tags, Revisions-Nummern, oder ähnliches. Du hast
zwei Repositories/Branches, und sagst: Hole alle Änderungen von A,
bzw. schiebe all meine Änderungen nach B. Mergen, und gut ist's.

Diese Arbeitsweise ist mit CVS sicherlich auch möglich. Aber genauso
komfortabel? Den ganzen Ballast, den die Versionskontrolle eh weiß,
brauchst du nicht mehr im Kopf zu haben. Es gibt ja nur Den Einen[tm]
Weg, das richtig zu mergen, und den findet Mercurial heraus und macht
es .. und präsentiert dir zwischendurch eventuelle Konflikte.

Die Graphenstruktur musst du nicht in deinem Kopf ablegen, sondern
sie ist da, wo sie hingehört: In der Versionskontrolle.

> > > Ich find es i.d.R. nicht so gut, wenn man so tut, als ob komplexe
> > > Probleme wie Versionsmanagement einfach wären. Als ob es einen
> > > Unterschied macht, 10 Kommandos zu lernen.
> > 
> > Ja, das ist ein Unterschied. Für ständig benutzte Befehle (update,
> > commit) mag das Befehl-Lernen sinnvoll sein.
> > 
> > Die seltener benutzten Befehle hingegen schlägt man eh ständig nach.
> > Diese aus einem Menü auswählen zu dürfen ist definitiv benutzer-
> > freundlicher.
> 
> Nein, man lässt dass einen Kollegen machen. Muss nicht (SOLL nicht)
> jeder alles machen.

Stimmt auch wieder. Obwohl *ich* wahrscheinlich eben dieser Kollege
wäre. Zumindest habe ich mich bei bisherigen Projekten sehr oft um
Versionskontrolle, Branches, Releases etc. gekümmert. Macht mir einfach
Spaß, und wenn's die anderen nicht machen wollen, bitte sehr. Dann "code"
ich erstmal nicht, sondern "maintaine" ein bisschen.  :-)

> > Ein "man hg", Befehl raussuchen, "hg <befehl> ..." kann man natürlich
> > auch als Menü-Auswahl interpretieren. ;-)
> > 
> > Nee, höchsten ein "hg <tab><tab>" ... und dann die Kommandos per
> > Autovervollständigung, inkl. Beschreibung. *Das* ist benutzer-
> > freundlich. BTW, ich müsste meine Shell mal entsprechend konfigurieren.
> 
> Ich würde bei "hg" erwarten, dass es eine Usage ausgibt. Brauchste gar
> kein TAB :)

Ja klar, aber was macht denn die Autovervollständigung? Die gängigen
Konfigurationen verarbeiten das Tab bei "hg", "svn" & Co. gerade
dadurch, dass sie das Teil mit "--help" aufrufen und dir die Ausgabe
präsentieren. Ist sehr angenehm sowas. Spart einem das Eingeben eines
extra Befehls für die Hilfsausgabe.

(genauso wie einem die klassische Tab-Vervollständigung einen extra
"ls" erspart ... man kann stattdessen direkt seinen längeren Befehl
weiter tippen)

> > > Wenn man dank GUI denkt, es wäre einfach, benutzt
> > > man es, als wäre es einfach. Da kommt dann oft was "einfaches" bei raus
> > > (vorsichtig ausgedrückt).
> > 
> > Die GUI darf die Konzepte nicht verstecken.
> 
> Tortoise versteckt das, finde ich. Beispielsweise wird gern rekursiv
> eingecheckt, weil da ja so ein Zeichen am Ordner ist etc.

Aber es zeigt kurz vor dem Commit alle gänderten Dateien nochmal an.
Hat man ausversehen zu viele, wählt man nur diejenigen davon an, die
man wirklich in dieses Commit hinein nehmen wollte.

Ich finde diesen der GUI durchaus okay.

> > Ja, svn macht das genauso.
> > 
> > Mercurial nicht, aber ein "hg up *" oder ähnliches löst das Problem
> > genauso.
> 
> Wir waren ja bei "atomar": ist "hg up *" atomar? 

Nein. Davon abgesehen: Einen *wirklich* atomaren Commit über mehrere
Repositories hinweg, die im Netzwerk auf verschiedenen Rechnern liegen,
stelle ich mir auch technisch unmöglich vor. Ich meine, das Protokoll
müsste doch so aussehen:

Client -> Server1   commit, Daten
Client -> Server2   commit, Daten
Client -> Server3   commit, Daten
Client -> Server4   commit, Daten

... 5min später ...

Server3 -> Client   ok, warte auf Bestätigung
Server2 -> Client   ok, warte auf Bestätigung
Server4 -> Client   ok, warte auf Bestätigung
Server1 -> Client   ok, warte auf Bestätigung

Client -> Server1   bestätigt
Client -> Server2   bestätigt

... Netz bricht ab ...

Client -> Server3   bestätigt  [kommt nicht an]
Client -> Server4   bestätigt  [kommt nicht an]

Server1 und Server2 haben einen Commit, die anderen beiden nicht.
Trotz Vorkehrungen.

> Aber wie gesagt, ist IMHO nicht sooooo wichtig.

ACK.

> > > > Ich wüsste nicht, wieso ich in solch einem Szenario
> > > > modulübergreifend mergen wollte.
> > > 
> > > Na, Du hast einen release branch, machst einen Fix (der Änderungen in
> > > drei Repos braucht) und willst das halt in den trunk mergen.
> > 
> > Will man das wirklich? Alles auf einmal?
> 
> Ja, natürlich, wie sonst?!
[...]

Okay, ich verstehe deine Ausführungen. Danke für die Erläuterungen.

Es gibt also Änderungen, die man in das Modul zurückfließen lassen
möchte, daher braucht das Modul sein eigenes Repo.

Es gibt andererseits auch Änderungen, die nicht zurückfließen
sollen und eng mit dem Hauptprojekt verwoben sind (Hacks). Das
späche dafür, beides (für das Release) in ein einziges Repository
zu packen.

Das Modul kann also innerhalb des Projektes sowohl Modul-spezifische
als auch Projekt-spezifische Änderungen haben. Die Projekt-spezfischen
müssen aber leider auch in das Modul eingecheckt werden. Dieser
Missstand wird durch ein atomares Commit in beide Repositories
erträglich gemacht.

Bei CVS und Subversion wüsste ich auch nicht, was man da machen
kann. Mit Mercurial & Co. hingegen würde ich das relativ elegant
lösen können:

Module haben eigene Repositories mit jeweis genau einem
Unterverzeichnis:

    modul1-meinbranch/
        modul1/
            [Modul1-code]

Das Projekt hat ein anderes Repository das erstmal nur Projekt-Code hat:

    projekt-meinbranch/
        [Projekt-Code]

Die benötigten Module werden via "hg pull" reingeholt:

    cd projekt-meinbranch/
    hg pull -u  ../modul1-meinbranch/
    hg pull -u  ../modul2-meinbranch/
    hg pull -u  ../modul3-meinbranch/

Das Aktualisieren funktioniert auf exakt dem selben Wege.
Das Projektverzeichnis sieht dann so aus:

    projekt-meinbranch/

        [Projekt-Code]

        modul1/
            [Modul1-code]

        modul2/
            [Modul2-code]

        modul3/
            [Modul3-code]

Gibt es eine Änderung in modul1, die alle Projekte betrifft,
wird sie im Modul1-Repository erledigt:

    modul1-meinbranch/
        modul1/
            [neuer Modul1-code]

und via "hg pull" wie gehabt in das Projekt geholt:

    projekt-meinbranch/
        [Projekt-Code]
        modul1/
            [neuer Modul1-code]
        modul2/
            [Modul2-code]
        modul3/
            [Modul3-code]

Gibt es hingegen in modul1 eine projektspezifische Änderung,
wird sie im Projekt-Repository erledigt, und das Modul1-Repository
wird davon niemals etwas mitbekommen:

    projekt-meinbranch/
        [Projekt-Code]
        modul1/
            [Modul1-code mit projektspez. Änderungen]
        modul2/
            [Modul2-code]
        modul3/
            [Modul3-code]

Schick, oder? Deine Hacks bleiben in einem Repository, in einem
Branch, zusammenhängen in einem commit (changeset). Die richtigen
Modul-Änderungen hingegen passieren in den Modul-Repositories.

Der Kunde macht lediglich ein "hg pull" von

Der einzige Trick hierbei ist, dass man "hg pull" nur in ein
Richtung anwendet (vom Modul zum Projekt) und niemals andersrum!

Du willst dein Projekt vom einen Modul-Branch zu einem anderen
migrieren (neuere Lib-Version)? Kein Problem. Das ist nur ein
"hg pull" mit anderer Quelle:

    hg pull -u  ../modul1-version2/

Natürlich musst du jetzt sämtlich Konflikte zugunsten von version2
lösen, aber das kann auch positiv sein, wenn du z.B. deine Hacks
am alten modul1-meinbranch behalten willst. Einfaches Rüberbügeln
geht jedenfalls ohne  großen Aufwand.

Ist natürlich nur eine Möglichkeit von vielen, ich wollte nur mal
die Power von Mercurial & Co. demonstrieren.

Ach ja, man kann natürlich Default-Werte für "hg pull" und "hg push"
setzen, versteht sich. Für das Prokekt-Repository nicht so interessant,
wohl aber für den Kunden. :-)

> In der Praxis muss man evtl. auch mal was dreckiges in so einem Branch
> machen. Stört nicht weiter, wenn im trunk des modul1 das schon durch
> andere Massnahmen (Refakturierung oder so) gelöst ist (sprich: in -Fixes
> branches kann man notfalls hacken, wenn's nie gemergt werden wird,
> macht's kaum was). Gut, mein Beispiel war natürlich gerade ein anderes,
> merk ich gerade :)
> 
> Jedenfalls geht schön, schnell und elegant mit CVS finde ich.

Was hältst du von dem Weg, den ich demonstriert habe? Ist doch auch
recht fix und elegant, oder? Und es ist auch dann sicher, wenn man
keine atomischen Repo-übergreifenden Commits hat. :-)

> > Was in CVS als Sandbox-Muster bezeichnet wird, ist genau das, was in
> > Darcs/Mercurial/GIT/... bereits ein Repository *ist*. Insbesondere kann
> > sich dort jeder selbst beliebig viele "Sandboxes" anlegen. 
> 
> Aber das sind doch Eigenschaften von dezentralen Systemen, nicht von
> Changeset-basierten, oder? Auch wenn vermutlich viele Tools beides oder
> keins davon implementieren...
> 
> Kann ich denn in hg nicht so arbeiten, als ob ich ein zentrales Repo
> hätte, sprich, in einer per Konvention ausgewählte Sandbox immer alles
> reinmergen?

Klar, sowas das macht man i.d.R. auch.

Zentrale Ansätze sind ja nur degenerierte Spezialfälle der dezentralen.

> > Weil das so einfach ist, ermöglicht es eine Weiter-Entwicklung des
> > Sandbox-Musters: Micro-Branches. Häufige, private Branches, die
> > ständiges Merging erfahren, dafür sind Mercurial/Darcs/GIT/... super
> > geeignet, und das ist eine *sehr* angenehme Art der Entwicklung.
> 
> Ob so ein Branch zentral ist oder nicht, sollte man ja gar nicht merken.
> Blöd wäre nur, wenn bei dezentral der gleiche Branchname zweimal
> verwendet wird. Was macht hg in so einem Fall eigentlich?

Keine Ahnung. Vielleicht ein Konflikt in der ".hgtags". Ist auch eher
unwichtig, wie Rocco bereits ausführte: Mercurial kenn auch lokale
Tags, die werden nicht eingecheckt, sondern sind spezifisch für dein
Repository.

> > Dass Subversion jedoch ein auf HTTP basiertes Protokoll spricht, *hat*
> > handfeste Vorteile: Du kannst das Repository über einen Webserver
> > verfügbar machen. Kein zusätzlicher Port, kein extra abzudichtener
> > SSH-Zugang.
> 
> lol
> 
> Man hat doch SSH sowieso, aber kein HTTP in ernstzunehmenden Umgebungen,
> weil HTTP einfach viiiiiiiiiiiiiiiieeeeeeeeeeel zu viel kann.
> Beispielsweise das Tunneln beliebiger Daten. Ohne vernünftige eingebaute
> Authentifizierung wie bei SSH :)
> 
> Also, wer mit HTTP und "nicht extra abzudichten" kommt... naja.
> Vielleicht Geschmackssache.

Ich finde einen Webserver mit CGI-Script leichter abzudichten als einen
SSH-Zugang. Bei ersterem kannst du "per se" erstmal nur so viel, wie
dein CGI-Script an Sicherheitslücken zu bieten hat. Bei SSH hingegen
kannst du erstmal alles.

> > Konkret in Mercurial hast du in deinem Arbeitsverzeichnis ein
> > Unterverzeichnis ".hg". Im Gegensatz zu SVN und CVS hast du das
> > nur einmal, nicht in jedem Unterordner (".svn/" bzw. "CVS/").
> 
> das .hg hab ich nur einmal?

Einmal pro Arbeitskopie=Repository, ja. Also nicht extra in jedem
Unterverzeichnis deines Repositories.

> Aber was ich wie und wohin auschecke, ist
> doch nur meine lokale Sicht?

Es gibt kein "auschecken". Man Clont/Kopiert ein anderes Repository.
(siehe Beispiele oben)

> Geht das mit HG nicht? hum, dass wäre ja
> eine Einschränkungen...

Finde ich nicht. *Innerhalb* eines Repositories kannst du natürlich
zu jeder beliebigen älteren Version "springen"  (hg update -r ...).

Mit dem .hg-Verzeichnis hat das aber erstmal nichts zu tun. Ich meinte
nur, es gibt nur ein solches Verzeichnis, in dem *alles* drin steckt,
statt die Versionsinfos sinnlos auf Unterverzeichnisse aufzuspalten.

Hat natürlich den "Nachteil", dass man sein Repository nicht
verzeichnisweise "auseinanderreißen" kann. Vielleicht kann GIT das,
aber Mercurial AFAIK nicht. Sehe ich aber nicht als wesentliche
Einschränkung an. Man braucht hier ja keine Tags und Branches wie
in Subversion durch getrennt ausgecheckte Unterverzeichnisse zu
simulieren.

In Subversion würde ich das vermissen. Im Mercurial aber ganz sicher
nicht. Hab's nie gebraucht.


Viele Grüße,

    Volker

-- 
Volker Grabsch
---<<(())>>---
Administrator
NotJustHosting GbR



Mehr Informationen über die Mailingliste linux-l