namespace cpp

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


vcs:how

Versionskontrolle auf sächsisch

Ich gloobe fest daran, dass e Pfund Rindfleesch eene gute Briehe giebt.
— Uwe Steimle

Versionskontrolle ist ein wichtiges Werkzeug für Programmierer.

Mit Mercurial ist der Einstieg leicht. Der Umgang damit wird so flüssig und quicklebendig, dass dieses Werkzeug seinen Namen und die Abkürzung aus dem chemischen Symbol Hg für Quecksilber herleitet.

Hat man das Prinzip verstanden, ist es leicht zu merken. Das soll hier gezeigt werden. Dabei werden Begriffe wie Repository, Clone, Commit und Merge erklärt.

Warum sächsisch? Mercurial, merge, merke (in sächsischem Dialekt ausgesprochen) liegen lautlich dicht beieinander.

Vorbereitung

Benötigte Software

TortoiseHg integriert Mercurial in das Windows-Betriebssystem. Die Versionskontrolle ist damit sowohl per Konsole als auch als Fensteranwendung (Hg Workbench) verfügbar, am einfachsten über Rechtsklick im Explorer. Mercurial ist nach der Installation sofort benutzbar. Für den Anfang verzichte ich auf weitere Einstellungen.

Auf der grafischen Oberfläche findet man sich schnell zurecht, wenn man das Konzept verstanden hat. Statt "klicke hier" oder "klicke da" werde ich daher die Befehle in der Kommandozeilenversion präsentieren. Auch das ist einfach erlernbar.

Allerdings setze ich voraus, dass Programmierer wissen, wie man eine Konsole öffnet und damit in Verzeichnissen navigiert. Das ist nach 20 Jahren Windows nicht mehr selbstverständlich. Wer damit nicht zurecht kommt, kann sich an den beigefügten Bildchen orientieren, die auf Rechtsklicks im Explorer unter TortoiseHg erscheinen.

Beteiligte

Für das entstehende Beispielprojekt genügen drei Verzeichnisse, die evtl. auf verschiedenen Rechnern liegen und drei Mitwirkenden zugeordnet werden. Nennen wir sie Anton, Bernd und Charlie.

Versionskontrolle als Einzelplatzsystem

Projekt anlegen und erste Versionen

Charlie heißt eigentlich Charlotte, wird aber, für Aussenseiter unverständlich, von ihren Freunden Ingrid gerufen: Charlie plant ein Projekt für ein Kochbuch mit sächsischen Gerichten.

Sie legt einen neuen Ordner für das Projekt an.

md kochbuch
cd kochbuch

Als erfahrene Programmiererin richtet sie sofort Versionskontrolle ein:

hg init

Dadurch entsteht eine Ablage (engl. Repository) im versteckten Unterverzeichnis .hg des Ordners kochbuch. Die Ablage speichert alle entstehenden Versionen des Projektes.

Konsolenprogramme verunsichern manchen Anfänger, weil sie keine geschwätzigen Rückmeldungen "Repository angelegt" liefern. Die Programme beschweren sich nur, wenn ein Fehler aufgetreten ist. No news are good news.

Es ist in Ordnung, dass man den Ablageordner auf der graphischen Oberfläche nicht sieht: Zutritt für Unbefugte verboten. Wenn du ihn sehen solltest, fummel bloß nicht dran rum! Es besteht sonst die Gefahr, das Repository zu ruinieren. Auf der Konsole kann Charlie sich davon überzeugen, dass es angelegt wurde:

dir

Sie interessiert sich aber bewusst nicht für den Inhalt.

Charlie kann nun Dateien erstellen, ändern, hinzufügen und die Änderungen ins Repository übernehmen. Sie legt eine Datei

index.html
TODO  

mit dem Namen index.html an und stellt die Datei mit

hg add

unter Versionskontrolle. Mit

hg status

kann sie nachsehen, welche Änderungen an der Versionierung vorgenommen wurden, und legt die Änderungen mit dem Befehl

hg commit

in die Ablage. Diese Version erhält automatisch die Revisionsnummer 0 in ihrem Repository. Ein Editor öffnet sich. Darin muss eine kurze Beschreibung der Änderungen abgelegt werden:

Projekt Kochbuch angelegt.

Zumindest sollte dies geschehen. Statt dessen wird evtl. der Fehler

abort: no username supplied (see "hg help config")

anzeigt. Die Versionsverwaltung kennt den Nutzernamen nicht. Charlie würde die Konfiguration besser vorher zentral über HgWorkbench erledigen (siehe unten).

Ausnahmsweise greifen wir hier in das Repository ein, um zentrale Einstellungen zu überstimmen. Falls nicht vorhanden, erstellen wir die Datei .hgrc im Repository mit folgendem Inhalt:

.hg/.hgrc
[ui]
username = Charlie

Charlie hat die Datei index.html zu einer minimalen HTML-Datei

index.html
<html>
 <body>
  Rezepte:
 </body>
</html>

ausgebaut. Nach

hg commit -m "Inhaltsverzeichnis geändert"

liegen die ersten beiden Versionen, Revision 0 und Revision 1, im Repository, wovon man sich mit Befehlen wie

hg log
hg locate -r0
hg locate -r1
hg cat -r0 index.html
hg cat -r1 index.html
hg diff -r0 -r1

überzeugen kann. Das grafische Werkzeug Hg Workbench liefert diese Informationen sogar auf einen Mausklick.

So einfach ist Versionskontrolle. Aber wozu ist sie gut?

Versionskontrolle als Zeitmaschine

Die Versionskontrolle ist für Charlie eine Zeitmaschine, mit der sie in der Historie des Projektes zu allen vorhergehenden Ständen springen kann. Sprünge zu künftigen Versionen wie "Projektabschluss" sind leider nicht möglich. (Warum eigentlich nicht?)

Charlie muss noch mal eine frühere Version ihres Projektes testen. Dazu setzt sie die Arbeitskopien unter Angabe der Revisionsnummer

hg update -r0

auf einen früheren Stand zurück. Kein Jonglieren mit Datenträgern, nicht existierenden Backups oder ähnlichem. Sie kann sofort ihren Test durchführen und

hg update

bringt das Arbeitsverzeichnis wieder auf den aktuellen Stand.

Hat Charlie seit dem letzten Commit Änderungen an den Arbeitskopien vorgenommen, die sich nicht bewährt haben, oder Dateien gelöscht, kann sie diese Änderungen mit

hg revert index.html

oder

hg revert --all

einfach rückgängig machen. Die Sicherheitskopien eventuell zurückgenommener Dateien mit der Endung .orig löscht Charlie.

Im Grunde läuft die Arbeit unter Versionskontrolle immer in diesen Schritten:

  1. Verändere dein Projekt.
  2. Teste, ob es funktioniert.
    • Falls ja, sichere die Änderung durch Commit.
    • Falls nein, mache die Änderung rückgängig.
  3. Weiter mit Schritt 1.

Wer Versionskontrolle nur als Einzelplatzsystem nutzen möchte, kann hier aufhören zu lesen. Mehr gibt es dazu nicht zu erklären.

Projekte klonen

Charlie hat eine ganz verrückte Idee zu ihrem Projekt, die sie ausprobieren will. Sie ahnt, dass dieses Experiment etwas längere Zeit in Anspruch nimmt und vermutlich auch mehrere Versionierungsschritte braucht. Sie möchte aber das aktuelle Projekt nicht in Mitleidenschaft ziehen, falls ihr Experiment scheitert. Dazu erzeugt sie eine Kopie (einen Klon) des aktuellen Projekt-Repositories:

cd ..
hg clone kochbuch experimental

Die Versionskontrolle legt dafür das angegebene Verzeichnis an. Der Klon kann nun unabhängig vom Hauptzweig verändert werden. Durch diesen "code fork" entstehen verschiedene Entwicklungslinien (engl. branches). Ist Charlies Idee nicht durchführbar, kann sie das Experimental-Verzeichnis bedenkenlos löschen.

Warum nicht einfach das Verzeichnis kopieren, das geht doch auch? Das Problem liegt nicht beim Kopieren, sondern im Zusammenführen verschiedener Entwicklungslinien. Ohne Versionskontrolle wird es zum Alptraum.

Der Klon speichert die Information, aus welchem Repository er entstanden ist. Ist das Experiment erfolgreich, kann charlie die Experimentalversionen ins übergeordnete Repository übernehmen (push). In umgekehrter Richtung können Änderungen aus dem übergeordneten Repository in den Experimental-Klon eingespielt werden (pull). Die Abläufe dieses Abgleichs mit mehreren, verteilten Repositories gleichen denen in der Teamarbeit.

Teamarbeit

Repository-Server

Charlie stellt das Projekt ihren Mitarbeitern zur Verfügung, zumeist über einen Repository-Server im Web.

In geschützten Umgebungen — wenn man weiß, was man da anrichtet — kann man auch den eigenen Rechner zum Server machen:

hg serve

Dieser ist dann unter http://localhost:8000 für Charlie erreichbar. Ihren Kollegen gibt sie die URL oder die IP-Adresse ihrer Maschine.

Verteiltes Arbeiten

Anton erstellt eine Kopie des Projektes

hg clone http://charliesPC:8000 kochA
cd kochA

auf seinem Rechner. In seinem Verzeichnis kochA stehen nun alle Dateien des Projektes. Im Unterverzeichnis .hg liegt sein lokales Repository. Anton erstellt ein Rezept

abern.txt
Abernmauke
----------
1 Topf voll Kartoffeln waschen, schälen 
in Salzwasser kochen, bis sie weich sind, dann abgießen
unter Zugabe von Milch zerstampfen
mit geriebenem Muskat und etwas Salz abschmecken

und speichert die Version in seinem Repository:

hg add
hg commit

Ähnliches vollzieht Bernd:

hg clone http://charliesPC:8000 kochB
cd kochB

Wieso steht in seinem Verzeichnis kochB nur die Datei index.html von Charlie? Anton hat das "Abern"-Rezept in seinem lokalen Repository abgelegt, nicht in dem von Charlie. Mercurial ist ein verteiltes Versionskontrollsystem. Anton und Bernd können nach dem Klonen ohne Netzverbindung an ihren lokalen Repositories arbeiten. Der Datenverkehr mit der lokalen Festplatte ist wesentlich schneller als über die Netzwerkverbindung.

Bernd erstellt das Rezept

blinsen.txt
Blinsen
-------
1 Liter Milch (frisch oder sauer)
3 Eier + 3 EL Zucker
unter Zugabe von Mehl verrühren,
bis ein dünnflüssiger Teig entsteht
 
dünne Teigschicht in gut geölter Pfanne verteilen
wenden, sobald Unterseite goldbraun ist

und legt ebenfalls seine Version ab:

hg add
hg commit

Habe ich erwähnt, dass auch Anton und Bernd ihre Nutzernamen für Mercurial konfiguriert haben müssen?

Ohne Versionkontrolle ist die jetzige Situation ein Alptraum. Wir haben (zum Glück nur) drei Entwickler und drei völlig verschiedene Projektstände. Wer soll den Überblick behalten und das wieder zusammenführen? Unter verteilter Versionskontrolle übernehmen dies die Entwickler selbst.

Lokale Änderungen veröffentlichen

Anton will seine Änderungen ins Charlies Repository zurückgeben. Zuvor fragt er aber Änderungen im übergeordneten Repository ab:

hg incoming

Weil Charlies Repository unverändert ist, kann Anton seine Änderungen sofort mit

hg push

übertragen. Zuvor hat Charlie Anton und Bernd die Erlaubnis erteilt, die Änderungen auf ihrem Repository-Server abzulegen.

Für einen ersten Test genügt die Ergänzung

.hg/.hgrc
[web]
allow_push = *    
push_ssl = false

in der Konfiguration des Hauptrepositories, das als Server genutzt wird. Damit

  • erfolgt die Übertragung unverschlüsselt mit HTTP und
  • jeder hat Schreibrecht auf das Server-Repository.

Offensichtlich genügt diese Einstellung keinem Sicherheitskonzept: Jeder Schurke weltweit kann das Projekt unterlaufen und ruinieren, wenn der Rechner aus dem Internet erreichbar ist. Ein besseres Konzept wird weiter unten beschrieben.

Eingehende Änderungen einpflegen

Bernd sieht bei hg incoming Antons Änderung, die er mit

hg pull

ins lokale Repository holt. Mit hg update werden beide Entwicklungszweige im Arbeitsverzeichnis verschmolzen. Da nur neue Dateien (abern.txt) hinzu kommen, gibt es dabei keinen Konflikt. Die Verschmelzung beider Revisionen legt Bernd mit

hg commit

in seinem Repository ab und kann sie nun, weil hg incoming keine neuen Änderungen meldet, mit

hg push

ans übergeordnete Repository schicken. Anton übernimmt diese Version mit hg pull wiederum in sein lokales Repository. Nach einem hg update verfügt auch Anton über den aktuellen Stand in seinem Arbeitsverzeichnis.

Konflikte auflösen

Anton und Bernd ändern nun beide die Index-Datei des Kochbuchs:

kochA/index.html
<html>
 <body>
  Rezepte:
  <a href="abern.txt">Kartoffelbrei</a>
 </body>
</html>

allerdings in unterschiedlicher Weise:

kochB/index.html
<html>
 <body>
  Rezepte:
  <a href="blinsen.txt">Eierkuchen</a>
 </body>
</html>

Beide übernehmen die Änderungen in ihr lokales Repository (hg commit). Anton legt seine Änderung zuerst im Zentralrepository:

hg incoming   # ergibt keine Neuigkeiten
hg push

Bernd findet die Änderungen und übernimmt sie ins sein Repository

hg incoming   # zeigt Änderungen an
hg pull

Da dieselbe Datei bearbeitet wurde, liegt ein Versionskonflikt vor. Mercurial schlägt ein Zusammenführen

hg merge

vor. Veränderungen in unterschiedlichen Abschnitten der Textdatei kann das Versionskontrollsystem ohne Zutun automatisch im Arbeitsverzeichnis zusammenführen. Da aber beide Autoren die selbe Zeile, aber mit unterschiedlichem Inhalt eingefügt haben, lässt sich der Konflikt nur manuell auflösen. Dazu öffnet sich automatisch ein Vergleichswerkzeug (kdiff3).

Merge mit kdiff3

Bernd muss entscheiden, ob

  • nur Antons Änderungen,
  • nur seine eigenen,
  • beide, wenn ja, in welcher Reihenfolge,
  • oder etwas ganz anderes

übernommen wird. Nach dem Beenden gilt der Konflikt als gelöst. Die zusammengeführte Version wird von Bernd abgelegt und vom lokalen ins übergeordnete Repository überführt:

hg commit
hg incoming  # nicht neues
hg push

Anton holt sich die Änderungen wiederum in sein Verzeichnis:

hg incoming  # Bernds Zusammenführung
hg pull
hg update

Charlie kommt nun vom Außeneinsatz zurück und betrachtet mit Hg Workbench ihr Repository.

Sie bemerkt, dass Anton und Bernd während ihrer Abwesenheit zwei Rezepte, abern.txt und blinsen.txt, und das Inhaltsverzeichnis ergänzt haben:

charlie/index.html
<html>
 <body>
  Rezepte:
  <a href="abern.txt">Kartoffelbrei</a>
  <a href="blinsen.txt">Eierkuchen</a>
 </body>
</html>

Charlie aktualisiert ihr Arbeitsverzeichnis mit hg update. So macht Teamarbeit Spaß!

Bitbucket als Repository-Server

Repository-Server mit hohen Sicherheitsansprüchen aufzusetzen, erfordert Administrator-Kenntnisse, die den Rahmen des Aufsatzes sprengen würden. Statt dessen wird hier ein kostenfreies Angebot zum Source-Code-Hosting beschrieben, das für Privatanwender, kleine Projekte und Open-Source-Entwicklungen ausreichend erscheint.

BitBucket bietet seit Herbst 2010 kostenlose Mercurial-Repositories an. Dabei können je angemeldetem Nutzer beliebig viele öffentliche (open source) und private Repositories angelegt werden. Nur maximal 5 BitBucket-Nutzer dürfen Schreibzugriff auf die Repositories eines Nutzers haben, bei einer größeren Anzahl werden kostenpflichtige Nutzungsmodelle angeboten.

Anmelden, Einrichten von Repositories und Freigabe erfolgen über den Webbrowser (siehe Abbildungen). Je Projekt können Wiki und Issue-Tracking genutzt werden. Nur einladene Nutzer ("Freunde") sehen private Repositories.

Anmelden Repository anlegen Repository ansehen

Für offene Repositories sind gar keine Schreibrechte notwendig. Jeder Bitbucket-Nutzer kann sich von anderen die Versionen eines Projektes in sein lokales Repository einpflegen und die Zusammenführung dann wieder unter seinem eigenen Account veröffentlichen. Dies wird als "pull-only"-Versionskontrolle bezeichnet und von einigen großen Open-Source-Projekten, z.B. dem Linux-Kernel-Projekt mit git, so praktiziert.

pull-only DVCS

Einstellungen in TortoiseHg

Um Commits ausführen zu können, muss zumindest der Nutzername festgelegt worden sein. Dies geschieht am einfachsten über Hg Workbench unter Datei|Einstellungen (engl. File|Settings) unter Übernehmen|Benutzername (engl. Commit|Username).

Die globalen Einstellungen können durch projektbezogene überstimmt werden. Eine englische Beschreibung ist hier zu finden.

vcs/how.txt · Zuletzt geändert: 2015-03-13 12:47 (Externe Bearbeitung)