Versionsverwaltung GIT
Willemers Informatik-Ecke

Aufgabe und Prinzip einer Versionsverwaltung

Geschichtsschreibung

Git ist eine Versionskontrolle. Eine Versionskontrolle ermöglicht es, Snapshots von Dateizuständen zu sichern und diese alten Versionen wieder zurückholen zu können. Dazu speichern die meisten Versionsverwaltungen die Unterschiede zwischen den Versionen, die in der Regel kleiner sind als eine komplette neue Version der Datei. Darum sind Versionsverwaltungen besonders effizient bei reinen Textdateien wie Programmquelltexten, HTML- oder XML-Dateien, LaTeX-Dokumenten und anderen rohen Textdateien.

Die Möglichkeit, die Unterschiede zwischen den Versionen ermitteln zu können, entfallen bei binären Dateien wie Musik, Bildern, aber auch Textverarbeitungsdokumenten wie beispielsweise Word-Dateien. Darum sind Versionsverwaltungen kein idealer Ort, um Binärdaten zu verwalten.

Gemeinschaftspflege

Besonders nützlich ist eine Versionsverwaltung, wenn mehrere Programmierer an einem gemeinsamen Projekt arbeiten. Die Versionsverwaltung verwaltet den gemeinsamen Stand und kann nicht nur die Historie verwalten, sondern auch nachvollziehen, wer welche Änderungen gemacht hat.

Startschuss

Ein Projekt wird in einem Repository verwaltet. Soll es ein Gemeinschaftsprojekt werden, wird zunächst ein zentrales Repository angelegt und jeder Entwickler zieht sich davon eine Kopie, ein sogenanntes Clone. Bei git hat also jeder Arbeitsplatz eine eigene Versionsverwaltung, die mit dem zentralen Repository synchronisiert werden kann.

Anlegen eines lokalen Repository

Sie müssen aber nicht zwingend mit einem zentralen Repository arbeiten. Sie können mit dem Befehl git init jederzeit ein lokales Repository erzeugen.
$ cd src/java/adr
$ git init
Initialisierte leeres Git-Repository in /home/arnold/src/java/adr/.git/
Damit existiert ein lokales Repository. Ein Verzeichnis, das unter einer Versionskontrolle von Git steht, enthält ein lokales Verzeichnis namens .git. Darin werden die Historien verwaltet.

Sollte es aus irgendeinem Grund erforderlich sein, dass Repository wieder zu entfernen, kann dies einfach durch Löschen des Verzeichnisses .git erreicht werden.

$ rm -rf .git

Clone eines zentralen Repositories

Wenn Sie mit einem zentralen Repository arbeiten, muss irgendjemand zunächst auf dem zentralen Server ein Repository mit git init angelegen. Das ist oft nicht so augenfällig, weil die Initialisierung des Repository unter den grafischen Oberflächen von GitLab oder GitHub beim Klick auf einen Button erfolgt, der ein Repository anlegt.

Um sich an einem zentralen Repository zu beteiligen, erzeugt man einen Clone zu erzeugen. Dazu benötigt man die URL des Repositories, also die Adresse, über die es zu erreichen ist.

Von dem externen Repository wird mit dem Befehl git clone ein lokales Repository gebildet:
git clone /pfad/projekt
git clone git@server:projekt
git clone http://server/projekt
Es entsteht anschließend ein vollwertiges, lokales Repository, auf dem Sie völlig unabhängig vom zentralen Repository arbeiten können, bis Sie später einmal synchronisieren wollen.

Das Ursprungs-Repository kann von Git über den Namen origin angesprochen werden.

Einrichten der Umgebung

Wenn Sie mit einem zentralen Repository arbeiten, ist Ihre Identität wichtig. Dazu konfigurieren Sie mindestens Ihren Namen und Ihre E-Mail-Adresse für die Git-Zugriffe.
git config --global user.name "Ihr Name"
git config --global user.email ihre@emailadresse.de

Erzeugen eines Schlüsselpaares für SSH

Falls Sie das Protokoll SSH verwenden, benötigen Sie auch einen SSH-Key. Dazu rufen Sie in der Shell den Befehl ssh-keygen auf. Wenn Sie alle Rückfragen mit einer leeren Zeile quittieren, befindet sich der private Schlüssel in der Datei id_rsa im Unterverzeichnis .ssh des Benutzerverzeichnisses. Dieser sollte dort auch bleiben. Die Datei id_rsa.pub ist öffentlich, kann verteilt werden und sollte auch dem Git-Server mitgegeben werden.

Dateizustände

Die Dateien im Arbeitsverzeichnis haben zunächst nichts mit dem Repository zu tun. Sie müssen explizit in einen Status gebracht werden, dass sie übernommen werden.

In der Anzeige der Dateien durch git status erscheinen sie als unversionierte Dateien.

Changed

Änderungen an Dateien, auch solchen, die bereits im Repository stehen, werden zwar von Git bemerkt, führen aber nicht dazu, dass die Änderungen beim nächsten Abgleich automatisch in das Repository übernommen werden. Der Status der Datei ist changed.

Sollen die Änderungen in das Repository gelangen, müssen sie zunächst in die Staged Area geschafft werden.

Staged Area

Eine Datei muss vor der Übernahme in das Repository mit dem Befehl git add in die Staged Area aufgenommen werden.
$ git add copyright.txt
Damit ist sie nur für Übernahme markiert.
$ git status
Der Status-Befehl gibt Ihnen Auskunft über den Stand Ihrer Dateien im Arbeitsverzeichnis. Dateien, die hier nicht auftauchen, fühlen sich bereits im Repository wohl und brauchen keine Pflege. Es erscheinen alle Dateien, die geändert wurden oder sich in der Staged Area befinden.

Übernahme ins lokale Repository: Commit

Alle Dateien der Staged Area werden mit dem Befehl git commit in das lokale Repository übernommen. Für diese Übernahme erwartet das System einen Kommentar, der in einem Editor eingegeben werden muss. Alternativ kann er auch mit der Option -m gleich im Commit-Befehl eingebaut werden.
$ git commit -m "Copyright für ein wichtiges Programm"
[master (Basis-Commit) da2828f] Copyright für ein wichtiges Programm
 1 file changed, 2 insertions(+)
 create mode 100644 copyright.txt
$

Sie können nach dem Status Ihres Arbeitsverzeichnis fragen:

$ git status
Auf Branch master
nichts zu committen, Arbeitsverzeichnis unverändert
$
Wird nach dem Commit an der Datei geändert, wird dies zwar von git bemerkt, führt aber erst dann zur Übernahme beim nächsten Commit, wenn die Datei mit einem erneuten Befehl git add in den Zustand staged gebracht wird. Das sorgt dafür, dass keine unfertigen Zwischenstände versehentlich ins Repository geraten.

Übersicht der Dateizustände

Dadurch ergeben sich für die in einem Arbeitsverzeichnis stehenden Dateien verschiedene möglichen Dateizustände. Welchen Zustand die Dateien im aktuellen Verzeichnis haben, ermitteln Sie mit dem Befehl git status.
modified
Die Datei wurde im Arbeitsverzeichnis gegenüber dem Zustand im lokalen Git-Repository geändert. Diese Änderungen bewirken nicht automatisch eine Übernahme in das Repository.
staged
Erst wenn die lokale Änderung in die Versionskontrolle einfließen soll, wird die Datei datei.txt mit dem folgenden Befehl für die Übernahme vorbereitet.
git add datei
Damit befindet sich die Datei in dem staged Zustand. Nur Dateiänderungen, die so angemeldet sind, werden beim Befehl git commit in das lokale Git-Repository übernommen.

committed
Durch den Befehl git commit werden alle Dateien, die sich im staged-Zustand befinden, in die lokale Versionskontrolle übernommen. Dazu wird ein Commit-Kommentar eingefordert, der mit dem Commit gespeichert wird.
Arbeiten Sie mit einem zentralen Repository, benötigen Sie noch den Befehl git push, um den Stand im lokalen Repository auf das zentrale Repository abzubilden. Dies wird weiter unten näher betrachtet.

Protokolle

Der Befehl git log zeigt die Liste der Commits an. Dabei werden folgende Informationen angezeigt:

Der Befehl git log erlaubt weitere Optionen.

Rückzieher

Mit dem Befehl git reset können Sie eine Datei aus dem Zustand staged wieder zurückholen, damit sie nicht beim nächsten Commit-Befehl mitgenommen wird.

Sollen eine Datei aus dem Zustand staged wieder in den Zustand changed gebracht werden, wird der folgende Befehl eingestetzt:

$ git reset copyright.txt
Lokale Änderungen können auf den Repository-Stand durch den Befehl git checkout zurückgesetzt werden. Dazu muss der Dateiname als Argument angegeben werden.

$ git checkout copyright.txt
Soll der Stand eines entfernten Repository alle lokalen Änderungen überschreiben, verwendet man die folgenden Befehle:
git fetch origin
git reset --hard origin/master

Datei entfernen und umbenennen

Eine Datei wird aus dem Repository mit dem Befehl git rm entfernt.
$ git rm copyright.txt
Im Anschluss wurde die Datei copyright.txt aus dem Arbeitsbereich gelöscht und der Löschvorgang in die Staged Area übernommen. Erst mit einem Commit wird die Datei auch aus dem Repository-Stand entfernt. Analog wird für das Umbenennen der Befehl git mv verwendet:
$ git mv datei.txt neuname.txt

Abgestempelt: Tag

Mit einem Tag können Stände markiert werden. Gern werden dafür Versionsnummern verwendet. Um einen Stand mit einem Tag zu versehen, verwendet man den folgenden Befehl:
$ git tag v1.4 -m 'my version 1.4'
Mit dem Befehl git tag ohne weitere Parameter erhält man eine Liste aller Tags in alphabetischer Reihenfolge.

Sie können einen alten Commit mit einem Tag versehen, indem Sie die Commit-ID hinten anhängen.

$ git tag v1.6 4b491290521630bdddb4280b20fddc18b6b4cc40
Tags wandern nur dann vom lokalen in das zentrale Repository, wenn das Tag als Argument angegeben wird.
$ git push origin v1.5
Sollen alle Tags übernommen werden, wird dem Push-Befehl als Option --tags mitgegeben.

Abzweigung: Branch

Mit einem Branch wird eine Weiterentwicklung des Projekts angelegt, die zunächst unabhängig vom Hauptstrang verfolgt wird. Erst wenn die Weiterentwicklung produktreif ist, wird sie wieder in den Hauptstrang integriert. So können Erweiterungen entwickelt werden und dennoch jederzeit stabile Versionen an den Kunden ausgeliefert werden.

Der Hauptzweig trägt den Namen master. Der aktuelle Branch wird durch den Namen HEAD bezeichnet und zeigt anfangs auf master.

Der folgende Befehl erzeugt einen Branch namens testbranch.

$ git branch testbranch
Eine Übersicht über alle angelegten Branches zeigt der Befehl git branch ohne Argumente.

Umsetzen des Branches

Um mit dem Branch normal arbeiten zu können, wird HEAD auf diesen Branch gesetzt.
$ git checkout testbranch
Sie können das Erzeugen und Wechseln zu einem Branch auch zusammenfassen, indem Sie dem Checkout-Befehl die Option -b hinzufügen.
$ git checkout -b testbranch
Dieser Befehl erzeugt den Brach testbranch und wechselt sofort dorthin.

Nun wirken Commits auf den Branch testbranch, während der Hauptzweig master unverändert bleibt. Soll wieder auf den Hauptzweig verwiesen werden, wird HEAD wieder auf den Zweig master gesetzt.

$ git checkout master

Familienzusammenführung

Um den Zweig testbranch in den Hauptzweig einzufädeln, wird zunächst der Hauptzweig zum HEAD gesetzt und anschließend der Branch mit dem Befehl git merge eingepflegt.
git checkout master
git merge testbranch
War eine Zusammenführung nicht konfliktfrei möglich, liefert der Merge-Befehl entsprechende Fehlermeldungen. Der Befehl git status zeigt den Konflikt auf.

Für die Konfliktbereinigung kann der Befehl git mergetool aufgerufen werden.

Abknicken

Hat die Zusammenführung geklappt, kann der Branch testbranch wieder gelöscht werden.
git branch -d testbranch

Verbindung zum zentralen Repository

Clone des zentralen Repositories

Wenn ein zentrales Repository besteht, holt sich jeder Arbeitsplatz einen Clone davon. Dadurch entsteht eine Beziehung zwischen den beiden. Das Ursprungs-Repository wird danach automatisch als origin angesprochen.

git clone git@gitserver:meinRepository
Der Entwickler kann sein lokales Repository verwenden, ohne mit dem entfernten Repository Kontakt zu haben. Mit dem Befehl git push wird der aktuelle Stand im lokalen Repository auf den zentralen Server geschrieben werden.
git push
Um Änderungen der Kollegen vom zentralen Repository in das lokale Repository zu übernehmen wird der Befehl git pull aufgerufen. Es empfielt sich, dies häufig zu tun, um die Stände nicht zu sehr auseinander laufen zu lassen.
git pull
Bei beiden Befehlen wird Git als remote Repository origin verwenden, so lange ihm keine weiteren Angaben als Argument geliefert werden.

Bezug zu einem anderen Repository definieren

Mit dem Befehl git remote add wird die Verbindung zu einem entfernten Repository definiert. Das entfernte Repository erhält einen Namen, hier origin. Sie können mit git remove namen die Definition wieder entfernen.
$ git remote add origin git@debian:/var/git/testprojekt
Nun werden mit dem Befehl push über den Namen (oder alternativ direkt über die URL) in das Remote Repository geschoben werden.
$ git push origin master
git@debian's password: 
Zähle Objekte: 9, Fertig.
Delta compression using up to 8 threads.
Komprimiere Objekte: 100% (9/9), Fertig.
Schreibe Objekte: 100% (9/9), 6.50 KiB | 0 bytes/s, Fertig.
Total 9 (delta 0), reused 0 (delta 0)
To git@debian:/var/git/testprojekt
 * [new branch]      master -> master

Links