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. Für Open-Source-Projekte dominiert heute die Versionskontrolle mit Git und Github als externem Quellcodespeicher. Mit GitExtensions ist der Einstieg auf Windows-Systemen leichter. 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.

Vorbereitung

Benötigte Software

Git for Windows bildet die Grundlage. Die Versionskontrolle ist damit sowohl per Konsole als über Rechtsklick im Explorer (Git-Bash) nutzbar.

Auf der grafischen Oberfläche von GitExtensions 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. Dabei setze ich voraus, dass Programmierer wissen, wie man eine Konsole öffnet und damit in Verzeichnissen navigiert. Das ist nach 30 Jahren Windows nicht mehr selbstverständlich. Wer damit nicht zurecht kommt, kann sich an den beigefügten Bildchen orientieren, die auf Rechtsklicks im Explorer 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. Zunächst muss jeder Git-Nutzer einige Dinge einstellen, die später wichtig sind: (Charlie hat schon einen Github-Account und möchte die eigene Mailadresse nicht dort veröffentlicht sehen.)

git config --global user.name charlies-username
git config --global user.email CharliesPrivateNoReplyGithubAddress

Einzelplatzsystem

Projekt anlegen und versionieren

Charlie heißt eigentlich Charlotte, wird aber (für Nicht-Sachsen 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:

git init

Dadurch entsteht eine Ablage (engl. Repository = Lager, Depot) im versteckten Unterverzeichnis .git 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

TODO  

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

git add index.html

unter Versionskontrolle. Mit

git status

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

git commit

in die Ablage (commit = engl. bekennen). Ein Editor öffnet sich. Darin muss eine kurze Beschreibung der Änderungen abgelegt werden:

Projekt Kochbuch angelegt

Zumindest sollte dies geschehen. Charlie hat die Datei index.html zu einer minimalen HTML-Datei

<html>
 <body>
  Rezepte:
 </body>
</html>

ausgebaut. Nach einem weiteren

git add index.html

der den jetzigen Arbeitsstand zwischenspeichert, in Git "staging" genannt, und

git commit -m "Inhaltsverzeichnis geändert"

liegen die ersten beiden Versionen im Repository, wovon man sich mit Befehlen wie

git show
git status
git log --pretty=oneline

überzeugen kann. Jede Version bekommt eine ziemlich lange Nummer (Hashwert):

723872d7480b99cc5cd015901d6b758a6fcbb340 (HEAD -> master) Inhaltsverzeichnis angelegt
bd0e0a1562f2e069706562bf796b82e7739c7180 Projekt Kochbuch angelegt

Das grafische Werkzeug GitExtensions liefert diese Informationen sogar auf einen Mausklick:

So einfach ist Versionskontrolle. Aber wozu ist sie gut?

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 des Hashwertes

git checkout bd0e0a1562f2e069706562bf796b82e7739c7180

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

git checkout master

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

git checkout index.html

einfach rückgängig machen.

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 ..
git 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 übergeben ("push" = engl. drücken). In umgekehrter Richtung können Änderungen aus dem übergeordneten Repository in den Experimental-Klon eingespielt werden ("pull" = engl. ziehen). Die Abläufe dieses Abgleichs mit mehreren, verteilten Repositories ähneln denen in der Teamarbeit.

Charlie stellt das Projekt ihren Mitarbeitern zur Verfügung, zumeist über einen Repository-Server im Web, nehmen wir z.B. an über Github. Sie legt ein neues Repository auf Github an und befolgt die dort angegebenen Hinweise für ein existierendes Projekt:

git remote add origin https://github.com/<charlies-username>/kochbuch.git
git push -u origin master

Teamarbeit

Verteiltes Arbeiten

Anton erstellt eine Kopie des Projektes

git clone https://github.com/<charlies-username>/kochbuch.git
cd kochbuch

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

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:

git add abern.txt
git commit

Ähnliches vollzieht Bernd:

git clone https://github.com/<charlies-username>/kochbuch.git
cd kochbuch

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

Bernd erstellt das Rezept

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:

git add blinsen.txt
git commit

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

Ohne Versionskontrolle 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:

git fetch

Weil das Github-Repository unverändert ist, kann Anton seine Änderungen sofort mit

git push

übertragen.

Eingehende Änderungen einpflegen

Bernd sieht bei git fetch Antons Änderung. Mit

git merge

werden beide Entwicklungszweige im Arbeitsverzeichnis verschmolzen. Da nur neue Dateien (abern.txt) hinzu kommen, gibt es dabei keine Konflikte. Die Verschmelzung beider Revisionen kann Bernd nun mit

git push

ans übergeordnete Repository schicken. Anton übernimmt diese Version mit

git pull

(das ist git fetch & git merge in einem) wiederum in sein lokales Repository. Nun verfügt auch Anton über den aktuellen Stand in seinem Arbeitsverzeichnis.

Konflikte

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

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

allerdings in unterschiedlicher Weise:

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

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

git push

Bernd findet die Änderungen und übernimmt sie in sein Repository:

git pull

Da dieselbe Datei bearbeitet wurde, liegt ein Versionskonflikt vor:

Mergekonflikt

Veränderungen in unterschiedlichen Abschnitten der Textdatei kann das Versionskontrollsystem ohne Zutun automatisch im Arbeitsverzeichnis zusammenführen. Da beide Autoren die selbe Zeile, aber mit unterschiedlichem Inhalt eingefügt haben, lässt sich der Konflikt nur manuell im Editor auflösen (Klick auf das rote Feld rechts unten: Resolve…). Ein Vergleichswerkzeug wie kdiff3, das sich (nach Installation und Konfiguration) über die grafische Oberfläche von GitExtensions aufrufen lässt, erleichtert das.

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 Speichern und Beenden gilt der Konflikt als gelöst. Evtl. muss noch index.html.orig gelöscht werden. Die zusammengeführte Version wird von Bernd abgelegt und vom lokalen ins übergeordnete Repository überführt:

git add .	
git commit
git push

Anton holt sich die Änderungen wiederum mit git pull in sein Verzeichnis. Als Charlie vom Außeneinsatz zurückkommt, aktualisiert ihr Arbeitsverzeichnis mit git pull und betrachtet 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>

So macht Teamarbeit Spaß!

Repository-Server

Repository-Server mit hohen Sicherheitsansprüchen aufzusetzen erfordert Administrator-Kenntnisse, die den Rahmen dieses Textes sprengen würden. Github bietet seit 2019 auch private Repositories. Anmelden, Einrichten von Repositories und Freigabe erfolgen über den Webbrowser. Je Projekt können Wiki und Issue-Tracking genutzt werden. Nur einladene Nutzer ("Freunde") sehen private Repositories und dürfen evtl. dort ändern.

Für öffentliche Repositories sind keine Schreibrechte notwendig. Jeder Nutzer kann Projekte anderer klonen, von anderen "Pull-Requests" in sein lokales Repository einpflegen und die Zusammenführung dann wieder unter seinem eigenen Account veröffentlichen. Diese "pull-only"-Versionskontrolle wird von großen Open-Source-Projekten praktiziert.

pull-only DVCS

Installation

Der hier beschriebene Ablauf basiert auf folgender Software:

Installiere Git, konfiguriere user.name und user.email. Optional installiere ein mergetool, gib das mergetool / difftool in GitExtensions an.

Nie wieder geht ein Quelltext verloren…

vcs/how.txt · Zuletzt geändert: 2020-05-31 15:51 von rrichter