26.05.2013
Git-Tutorial
Dieser Artikel beschreibt, wie man das Versionskontrollsystem (VCS) Git verwenden kann, um verschiedene Versionsstände von Dateien zu verwalten. In diesem Artikel steht die Benutzung von lokalen Repositories auf dem eigenen Rechner im Vordergrund, wobei aber auch die Benutzung von remote Repositories kurz erklärt wird. Das Ziel des Artikels ist, dass man bei lokalen Projekten eine zuverlässige Möglichkeit hat, zu einen vorherigen Version zurückzukehren. Bei der Verwendung von Git ist man nicht darauf beschränkt, dass Sourcecode in das Repository abgelegt wird. Es ist beispielsweise problemlos möglich Word- oder Excel-Dokumente mit Git zu verwalten.
Veraussetzungen
Ich gehe im Folgenden davon aus, dass Git installiert ist und von der Kommandozeile aus aufgerufen werden kann.
Um das zu überprüfen kann man folgendes Kommando in einem Terminal ausführen:
git --version
Als Antwort sollte die Versionsnummer von Git zu sehen sein:
git version 1.8.1.3
Beispiel
In den nächsten Schritten wird an einem Beispiel gezeigt, wie man mit Git arbeitet. Dazu wird ein Beispielprojekt angelegt, es werden ein paar Dateien eingecheckt, verändert, wieder zurückgerollt. Außerdem wird ein Feature-Branch angelegt und das Mergen von Branches erklärt. Konkret sind das folgende Schritte:
- Git-Repository anlegen (git init)
- Status abfragen (git status)
- Dateien ausschließen
- Dateien vorbereiten (git add)
- Dateien einchecken (git commit)
- Überblick über commits (git log)
- Zwischen Versionen wechseln (git checkout)
- Einen Branch anlegen (git checkout -b)
- Versionen vergleichen (git diff)
- Branches zusammenführen (git merge)
- Branches löschen (git checkout -d)
- Merge Konflikte
- Tags verwenden (git tag)
- Ein remote Repository benutzen (git clone)
- Ein lokales mit einem remote Repository abgleichen (git pull)
- Änderungen im remote Repository speichern (git push)
Git-Repository anlegen
Zum Anlegen eines neuen lokalen Repositories wechselt man in das Verzeichnis, in dem sich die Daten befinden, die im Git abgelegt werden sollen. Beispielsweise: /Users/hameister/Documents/workspace/TCXViewer
.
Anschließend ruft man folgenden Befehl auf:
git init
Und erhält als Antwort, dass ein leeres Git Repository initialisiert wurde.
Initialized empty Git repository in /Users/hameister/Documents/workspace/TCXViewer/.git/
Status abfragen
Um sich einen Überblick zu verschaffen in welchem Zustand die Dateien in dem Verzeichnis sind wird folgender Befehl verwendet:
git status
Für das Beispiel von oben, könnte die Ausgabe so aussehen:
# On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .classpath # .project # .settings/ # 04.05.13 09-04-29.tcx # 12.05.13 09-32-26.tcx # 19.05.13 09-06-59.tcx # 25.04.13 16-43-38.tcx # bin/ # build.fxbuild # src/ nothing added to commit but untracked files present (use "git add" to track)
Die Ausgabe sagt, dass man sich im master
-Branch befindet und sich eine Reihe von Dateien noch in Zustand untracked
befinden. D.h. nicht unter Versionskontrolle von Git stehen.
Dateien ignorieren
Da oft nicht alle Dateien in einem Verzeichnis/Projekt unter Versionskontrolle gestellt werden sollen, bietet Git die Möglichkeit mit der Datei .gitignore
gezielt Dateien auszuschließen.
Mit dem Kommando touch
kann man unter Unix/Mac eine Datei .gitignore
anlegen.
touch .gitignore
Um die neue Datei zu editieren, kann vi
oder jeder andere Texteditor verwendet werden.
vi .gitignore
In dem Beispiel soll das Verzeichnis bin
und einige tcx
-Dateien ignoriert werden. Um das zu erreichen werden diese Dateien und das Verzeichnis in der Datei .gitignore
ergänzt. (Verzeichnisse mit '/' abschließen.)
bin/ 04.05.13 09-04-29.tcx 19.05.13 09-06-59.tcx 12.05.13 09-32-26.tcx 25.04.13 16-43-38.tcx
Dateien für den commit vorbereiten
Vor dem eigentlich commit
in das Repository werden die Dateien in eine sogenannte Staging Area verschoben. Dies passiert mit dem Kommando add
. In dem Staging-Area befinden sich alle Dateien, die beim nächsten commit
in das Repository eingecheckt werden sollen.
Für das Beispiel könnte das Verschieben in die Staging-Areas folgendermaßen aussehen:
git add .classpath git add .gitignore git add .project git add .settings/ git add build.fxbuild git add src/
Es lassen sich also einzelne Dateien und auch ganze Verzeichnisse mit git add
verschieben.
Nachdem die Dateien verschoben sind, kann das Ergebnis mit git status
überprüft werden.
git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached..." to unstage) # # new file: .classpath # new file: .gitignore # new file: .project # new file: .settings/org.eclipse.jdt.core.prefs # new file: build.fxbuild # new file: src/TCX.css # new file: src/TCXAltitude.css # new file: src/TCXHeartrate.css # new file: src/TCXSpeed.css # new file: src/org/hameister/tcxviewer/TCXLoader.java # new file: src/org/hameister/tcxviewer/TCXViewer.java #
Änderungen einchecken
Zum Einchecken der Dateien aus dem Staging-Area wird das Kommando git commit
verwendet. Um einen Kommentar hinzuzufügen wird der Parameter -m
benutzt.
git commit -m "Initialer Checkin."
Die Ausgabe von dem commit
sieht ungefähr so aus:
[master (root-commit) ad80184] Initialer checkin 11 files changed, 653 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 build.fxbuild create mode 100644 src/TCX.css create mode 100644 src/TCXAltitude.css create mode 100644 src/TCXHeartrate.css create mode 100644 src/TCXSpeed.css create mode 100644 src/org/hameister/tcxviewer/TCXLoader.java create mode 100644 src/org/hameister/tcxviewer/TCXViewer.java
Nach einem erneuten Aufruf von git status
wird einem von Git mitgeteilt, dass sich keine Dateien mehr im Arbeitsverzeichnis befinden, die nicht eingecheckt sind.
git status # On branch master nothing to commit, working directory clean
Dateien hinzufügen, ändern und einchecken
Im nächsten Schritt sollen neue und veränderte Dateien eingecheckt werden. Nach den Änderungen wird als erstes wieder git status
aufgerufen.
git status # On branch master # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: .classpath # modified: src/org/hameister/tcxviewer/TCXLoader.java # modified: src/org/hameister/tcxviewer/TCXViewer.java # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # src/org/hameister/tcxviewer/TCXController.java # src/org/hameister/tcxviewer/TCXView.java no changes added to commit (use "git add" and/or "git commit -a")
In der Ausgabe sieht man, dass drei Dateien verändert wurden und zwei hinzugekommen sind, die sich noch im Zustand untracked
befinden.
Um die Dateien auch unter Versionskontroller zu stellen, wird wieder das Kommando git add
verwenden, damit sie in die Staging-Area verschoben werden. Mit den geänderten Dateien verfährt man genauso:
git add .classpath git add src/org/hameister/tcxviewer/TCXLoader.java git add src/org/hameister/tcxviewer/TCXViewer.java git add src/org/hameister/tcxviewer/TCXController.java git add src/org/hameister/tcxviewer/TCXView.java
Anschließend kann durch den Aufruf git status
überprüft werden, ob sich alle veränderten und neuen Dateien im Staging-Area befinden:
git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: .classpath # new file: src/org/hameister/tcxviewer/TCXController.java # modified: src/org/hameister/tcxviewer/TCXLoader.java # renamed: src/org/hameister/tcxviewer/TCXViewer.java -> src/org/hameister/tcxviewer/TCXView.java # modified: src/org/hameister/tcxviewer/TCXViewer.java #
Zum Einchecken wird wieder das Kommando git commit
verwendet:
git commit -m "Change to MVC and added Property bindings"
Die Ausgabe könnte folgendermaßen aussehen:
[master 4992ffb] Change to MVC and added Property bindings 5 files changed, 297 insertions(+), 303 deletions(-) create mode 100644 src/org/hameister/tcxviewer/TCXController.java rename src/org/hameister/tcxviewer/{TCXViewer.java => TCXView.java} (52%) rewrite src/org/hameister/tcxviewer/TCXViewer.java (92%)
Commits auflisten
Um sich einen Überblick über die erfolgten commit
s zu verschaffen, kann das Kommando git log
verwendet werden.
In unserem Beispiel wurde zwei commit
s durchgeführt. Einmal der initiale commit
und anschließend der commit
mit den ersten Änderungen:
git log commit 4992ffb2c4fb5612baebdcd34e06c5e281c1de64 Author: Jörn Hameister <hameister@macbookpro.fritz.box> Date: Sun May 26 20:34:14 2013 +0200 Change to MVC and added Property bindings commit ad80184f75945d7fbdf6e824cb371528be124b26 Author: Jörn Hameister <hameister@macbookpro.fritz.box> Date: Sun May 26 09:22:10 2013 +0200 Initialer checkin
Zwischen Versionen wechseln
Falls man nach einem commit
feststellt, dass die Änderungen falsch sind und man wieder zu der vorhergehenden Version zurückkehren möchte, kann dies ganz einfach mit einem git checkout
erreicht werden. Was allerdings benötigt wird, ist die commit
-Nummer, die man mit git log
bestimmen kann.
In dem Beispiel soll zum Initialen checkin zurück gekehrt werden.
git checkout ad80184f75945d7fbdf6e824cb371528be124b26
Als Ausgabe erhält man dann in etwa folgendes:
Note: checking out 'ad80184f75945d7fbdf6e824cb371528be124b26'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b new_branch_name HEAD is now at ad80184... Initialer checkin
Wie der Text schon sagt, ist man nun abgekoppelt vom HEAD
. Um sich Dinge anzuschauen ist diese Vorgehen in Ordnung. Wenn man allerdings weiterarbeiten möchte und seine Änderungen später in den HEAD zurückfließen lassen möchte, dann sollte man in einem neuen Branch auschecken.
Um wieder zurück zum master
zu kommen, kann folgendes Kommando verwendet werden:
git checkout master
Versionen vergleichen
Um zwei Versionen miteinader zu vergleichen existiert das Kommando diff
. Um beispielsweise die beiden eingecheckten Versionen miteinander zu vergleichen, kann folgendes Kommando ausgeführt werden (Hinweis: Die commit Nummern erhält man mit git log
und sie werden auf deinem Rechner anders aussehen.)
git diff 4992ffb2c4fb5612baebdcd34e06c5e281c1de64 ad80184f75945d7fbdf6e824cb371528be124b26
Ein Ausschnitt der Ausgabe, sieht ungefähr so aus:
... diff --git a/src/org/hameister/tcxviewer/TCXLoader.java b/src/org/hameister/tcxviewer/TCXLoader.java index e3b01f2..8109947 100644 --- a/src/org/hameister/tcxviewer/TCXLoader.java +++ b/src/org/hameister/tcxviewer/TCXLoader.java @@ -5,13 +5,10 @@ import java.io.IOException; import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Collections; import java.util.Date; import java.util.concurrent.TimeUnit; ...
Die Diff-Ansicht läßt sich mit q
beenden.
Das diese Ansicht bei größeren Changesets unübersichtlicht ist, bieten sich grafisches Tools, wie SourceTree an.

Branch anlegen
Um einen Branch anzulegen, wird bei checkout
einfach der Parameter b
mit einem Namen für den Branch mitgegeben.
git checkout -b JH_Test_Branch Switched to a new branch 'JH_Test_Branch'
Nun befindet man sich im Branch JH_Test_Branch
.
Auf diese Weise können in dem Branch Änderungen vorgenommen werden, die auch in den Branch eingecheckt werden. Der master
wird von den Änderungen nicht berührt.
Mergen von Branches
Falls in der Zwischenzeit auch im master
Änderungen durchgeführt werden, kann es notwendig werden den Branch JH_Test_Branch
mit dem master
zu mergen.
Um sich einen Überblick zu verschaffen, wo man sich befindet, kann folgendes Kommando verwendet werden:
git log --graph --oneline --decorate --all
Als Ausgabe erhält man eine Anzeige, in welchem Branch man sich befindet und ob sich der master
verändert hat.

Wie man an der Ausgabe sieht, befinden wir uns im Branch JH_Test_Branch
(das ist der HEAD
). Allerdings hat jemand im master
eine Änderung mit der commit
-Nachricht Local variable for TCXController is not necessary. eingecheckt.
Um sich die Änderungen anzusehen, kann wieder der Befehl git diff
verwendet werden:
macbookpro:TCXViewer hameister$ git diff 2cac78e b525cd6 diff --git a/src/org/hameister/tcxviewer/TCXLoader.java b/src/org/hameister/tcxviewer/TCXLoader.java index e3b01f2..f1e41b2 100644 --- a/src/org/hameister/tcxviewer/TCXLoader.java +++ b/src/org/hameister/tcxviewer/TCXLoader.java @@ -246,7 +246,7 @@ public class TCXLoader { altitudeData = reducePoints(altitudeData); heartRateData = reducePoints(heartRateData); - DecimalFormat altitudeFormatter = new DecimalFormat("#.00 NN"); + DecimalFormat altitudeFormatter = new DecimalFormat("#.00 N.N."); distanceKmProperty.setValue(getDistanceInKilometers()); maxKmPerHourProperty.setValue(getMaximumSpeed()); diff --git a/src/org/hameister/tcxviewer/TCXViewer.java b/src/org/hameister/tcxviewer/TCXViewer.java index 420c314..5b48f7b 100644 --- a/src/org/hameister/tcxviewer/TCXViewer.java +++ b/src/org/hameister/tcxviewer/TCXViewer.java @@ -10,7 +10,7 @@ public class TCXViewer extends Application { public void start(Stage primaryStage) { long startTime = System.currentTimeMillis(); - new TCXController(primaryStage); + TCXController controller = new TCXController(primaryStage); primaryStage.setTitle("TCX Data Viewer");
Da die Änderungen an unterschiedlichen Dateien durchgeführt wurden und keine Abhängikeiten bestehen, kann der Branch einfach in den master
gemerged werden. Dazu wechselt man in den master
und führt die beiden Branches durch folgendes Kommando zusammen:
git checkout master git merge JH_Test_Branch
Branch löschen
Der Branch JH_Test_Branch
wird nach dem Merge nicht mehr benötigt und kann deshalb mit dem Kommando git branch -d
gelöscht werden.
git branch -d JH_Test_Branch Deleted branch JH_Test_Branch (was 9c1747f).
Merge-Konflikte
Merge-Konflikte treten auf, wenn im master
und im Branch
Änderungen an der gleichen Datei durchgeführt wurden.
Wurde beispielsweise ein Feature-Branch FEATURE1_BRANCH
angelegt, um Änderungen an der Datei TCXLoader.java
durchzuführen und gleichzeitig hat jemand anderes Änderungen an der gleichen Datei im master
durchgeführt, dann erscheint folgende Fehlermeldung bei einem Merge-Versuch: (Wir befinden uns im master
)
git merge FEATURE1_BRANCH Auto-merging src/org/hameister/tcxviewer/TCXLoader.java CONFLICT (content): Merge conflict in src/org/hameister/tcxviewer/TCXLoader.java Automatic merge failed; fix conflicts and then commit the result.
Wenn man nun in die Konfliktdatei schaut, ist der Konflikt folgendermaßen hervorgehoben:
<<<<<<< HEAD return "00:00:00"; ======= return "00:00"; >>>>>>> FEATURE1_BRANCH
Diesen Konflikt muss man dann per Hand beheben und die Datei erneut in den master
einchecken.
Wenn man den Branch weiter verwenden möchte, dann sollte man ihn nochmal mit dem master
mergen. Dafür wechselt man in den Branch und ruft git merge master
auf. Konflikte gibt es nicht mehr, so dass der Merge automatisch durchgeführt wird. Nun sind master
und FEATURE1_BRANCH
wird auf dem gleichen stand.
Wenn man den Feature-Branch nach dem Merge nicht weiter verwenden möchte, kann man ihn auch einfach mit git branch -d FEATURE1_BRANCH
löschen.
Tags verwenden
Um eine eingecheckte Version in Git zu markieren, kann ein Tag verwendet werden. Prinzipiell hat man zwar für jeden commit eine eindeutige Id, aber mit einem Tag läßt sich eine Version nochmal "hervorheben".
Um einen Tag anzulegen kann folgendes Kommando verwendet werden:
git tag -a "Version-1.0.2" -m "Hotfix release 1.0.2"
Dabei wird mit dem Parameter -a
die Version angegeben und mit dem Parameter -m
ein Kommentar.
Um sich alle Tags anzeigen zu lassen, kann folgendes Kommando verwendet werden:
git tag
Falls versehentlich ein Tag angelegt wurde und der Tag wieder gelöscht werden soll, kann dies mit folgendem Kommando erreicht werden:
git tag -d "Version-1.0.2"
Remote Repositories benutzen
Um ein remote Repository lokal zu benutzen kann der Befehl git clone
verwendet werden. Möchte man beispielsweise das Twitter-Projekt Bootstrap benutzen, welches bei GitHub abgelegt ist, dann kann folgendes Kommando verwendet werden:
git clone https://github.com/twitter/bootstrap.git
Daraufhin wird das Projekt lokal in ein eigenes Repository abgelegt.
Cloning into 'bootstrap'... remote: Counting objects: 35100, done. remote: Compressing objects: 100% (13392/13392), done. remote: Total 35100 (delta 24316), reused 32078 (delta 21505) Receiving objects: 100% (35100/35100), 27.03 MiB | 3.00 MiB/s, done. Resolving deltas: 100% (24316/24316), done.
Dieses Repository verhält sich genauso, wie ein selbst angelegtes lokales Repository.
Das lokale mit dem remote Repository abgleichen
Wenn man ein clone von einem remote Repository angelegt hat, sollte man regelmäßig überprüfen, ob Änderungen gemacht wurden. Dies kann mit dem Kommando git pull
erreicht werden:
git pull Already up-to-date.
Falls es Änderungen gibt, dann werden diese automatisch in das lokale Repository gemerged.
Änderungen im remote Repository speichern
Wenn man im lokalen Repository Änderungen durchgeführt und diese in remote Repository ablegen möchte, dann wird der Befehl git push
verwendet.
git push
Zu beachten ist dabei, dass man Schreibrechte für das remote Repository benötigt. Hat man diese nicht, dann erhält man eine solche Fehlermeldung:
error: The requested URL returned error: 403 while accessing https://github.com/twitter/bootstrap.git/info/refs?service=git-receive-pack fatal: HTTP request failed
Wie man dennoch Änderungen zurück in das Projekt fließen lassen kann, habe ich in dem Artikel How to use and extend the library JFXtras kurz beschrieben. Unter dem Stichwort Pull Request findet man bei GitHub eine ausführliche Beschreibung. (Using Pull Requests).
Anmerkungen
Dieser Artikel erhebt keinen Anspruch vollständig zu sein und alle Möglichkeiten von Git zu beleuchten. Es werden die wichtigsten Kommandos kurz beschrieben und gezeigt, wie man mit ihnen arbeitet. Ausführlichere Darstellungen findet man hier:
- Git Pro (kostenloses Buch von Apress)
- Pragmatic Guide to Git (Pragmatic Programmers)
- Version Conrol with Git (o'Reilly)