Versionskontrolle mit GIT
Was ist und was kann GIT?¶
Git ist ein kostenloses, verteiltes Versionskontrollsystem für Softwareprojekte. Das Programm ermöglicht es mehreren Entwicklern, unabhängig von ihrem Aufenthaltsort gleichzeitig an einem Projekt zu arbeiten.
Die Versionskontrolle macht es einfach, Änderungen am Projekt vorzunehmen, diese Änderungen zu protokollieren und nachzuvollziehen sowie zu einem späteren Zeitpunkt auf ältere Versionen des Projekts zuzugreifen. Git ist plattformunabhängig und lässt sich somit in nahezu jeder Umgebung nutzen.
In folgender Abbildung ist dargestellt, wie drei Entwickler an einem gemeinsamen Softwareprojekt arbeiten könnten. Der Programmcode wird zentral in einem Remote Repository gespeichert. Dieses wird mit lokalen Repositories auf den Rechnern der einzelnen Entwickler synchronisiert. Jedes lokale Repository ist wiederum mit einem Ordner im Dateisystem verbunden, in dem die eigentlichen Projektdateien liegen:

In diesem Abschnitt wollen wir genauer verstehen, wie dieser Workflow funktioniert. Dazu installieren wir zunächst Git auf unserem Computer:
conda install -c conda-forge gitUnter Linux kann Git auch über den Paketmanager installiert werden, da Git nicht nur für Python-Projekte nützlich ist. Unter Ubuntu verwendet man beispielsweise:
sudo apt-get install gitBei anderen Linux-Distributionen muss entsprechend der jeweilige Paketmanager verwendet werden. Windows-Nutzer können Git von der offiziellen Webseite herunterladen: https://
Erste Schritte¶
Synchronisation mit dem lokalen Repository¶
Wir wollen uns hier mit den grundlegenden Schritten bei der Arbeit mit Git vertraut machen. Die wichtigsten Befehle, die wir in diesem Kapitel kennenlernen, sind:
| Befehl | Bedeutung |
|---|---|
git init | Lokales Repository initialisieren |
git add [files] | Dateien zur Versionskontrolle hinzufügen bzw. für den nächsten Commit vormerken |
git commit -m "[message]" | Vorgemerkte Änderungen in das lokale Repository übernehmen |
git log | Historie der Commits anzeigen |
git status | Statusbericht anzeigen |
Wir legen zunächst einen neuen Ordner für unser Programmierprojekt an und erzeugen uns ein lokales GIT-Repository mit
mkdir my_project
cd my_project
git initLeeres Git-Repository in /home/user/Documents/my_project/.git/ initialisiertDieser Befehl erzeugt einen versteckten Ordner namens .git. In diesem Ordner speichert Git alle Informationen über die Versionsgeschichte des Projekts. In der Regel müssen wir diesen Ordner jedoch nie direkt öffnen.
Wir können nun beginnen, unseren Programmcode zu schreiben. Mit einem Editor unserer Wahl erstellen wir die Datei calender.py und füllen sie mit folgendem Inhalt:
class appointment:
pass
class calender:
passWir fügen diese zur Versionskontrolle hinzu und speichern sie im lokalen Repository:
git add calender.py
git commit -m "Created file for empty calender and appointment class"[master (Root-Commit) ce7d6d2] Created empty calender and appointment class
1 file changed, 6 insertions(+)
create mode 100644 calender.pyWir können unser Programm nun erweitern, beispielsweise die appointment-Klasse:
class appointment:
def __init__(self, date, title):
self.date = date
self.title = title
def __str__(self):
return self.date + ": " + self.titleMit dem folgenden Befehl können wir überprüfen ob wir noch synchron mit unserem lokalen Repository sind:
git statusAuf Branch master
Änderungen, die nicht zum Commit vorgemerkt sind:
(benutzen Sie "git add <Datei>...", um die Änderungen zum Commit vorzumerken)
(benutzen Sie "git restore <Datei>...", um die Änderungen im Arbeitsverzeichnis zu verwerfen)
geändert: calender.py
keine Änderungen zum Commit vorgemerkt (benutzen Sie "git add" und/oder "git commit -a")Wir wollen nun die Änderungen an der Datei calender.py in das lokale Repository hochladen:
git add calender.py
git commit -m "Implemented constructor and string method for appointment class"Im nächsten Arbeitsschritt erweitern wir unsere calender-Klasse
class calender:
def __init__(self, owner):
self.owner = owner
self.appointments = []
def add_appointment(self, appointment):
self.appointments.append(appointment)Anschließend speichern wir Änderungen wieder im lokalen Repository. Wir könnten so vorgehen wie vorhin,
git add calender.py
git commit -m "Implemented constructor and add_appointment method for calender class"Alternativ - und so wird in der Praxis häufig gearbeitet - können wir den Commit mit nur einer Zeile erstellen:
git commit -am "Implemented constructor and add_appointment method for calender class"Die Zeile mit git add wurde hierbei weggelassen, dafür wurde beim git add der Parameter -a ergänzt. Der Parameter -a sorgt dafür, dass alle bereits von Git bekannten (also bereits einmal mit git add hinzugefügten) Dateien automatisch für den Commit vorgemerkt werden. Dadurch kann man sich in vielen Fällen den zusätzlichen git add-Befehl sparen.
Nun haben wir bereits 3 Commits erstellt. Wir erhalten eine Historie mit
git logcommit a95560371b9984f57fff4dcbd028bb757a0918cc (HEAD -> master)
Author: Max Winkler <max.winkler@mathematik.tu-chemnitz.de>
Date: Thu Mar 17 16:04:37 2022 +0100
Implemented constructor and add_appointment method for calender class
commit ed29a89b272ab66e95cb7d014c90fadccb9cacc1
Author: Max Winkler <max.winkler@mathematik.tu-chemnitz.de>
Date: Thu Mar 17 16:00:40 2022 +0100
Implemented constructor and string method for appointment class
commit ce7d6d246e3b0042b70f2b0104ef45139f9de381
Author: Max Winkler <max.winkler@mathematik.tu-chemnitz.de>
Date: Thu Mar 17 15:50:38 2022 +0100
Created empty calender classWir finden hier unsere Commit-Nachrichten wieder und sehen außerdem, wann die einzelnen Commits erstellt wurden. Außerdem wird jedem Commit eine eindeutige Kennung zugewiesen (der kryptische Code hinter dem Wort “commit”).
Mit einem Remote-Repository verbinden¶
Wir wollen nun unser lokales Repository mit einem Remote-Repository verbinden. Dies ist insbesondere dann sinnvoll, wenn das Remote-Repository im Internet oder Intranet für andere Entwickler erreichbar ist.
Es gibt mehrere kostenfreie Anbieter für Git-Repositories:
TU Chemnitz - Das Gitlab der TU Chemnitz
Github - Öffentliches Repository
Bitbucket - Öffentliches Repository
Wir können uns bei einem dieser Anbieter registrieren und dort ein neues Repository anlegen.
Die wichtigsten Befehle, die wir zur Synchronisation mit dem Remote-Repository benötigen, sind:
| Befehl | Bedeutung |
|---|---|
git pull | Änderungen aus dem Remote-Repository herunterladen |
git push | Änderungen im lokalen Repository auf das Remote-Repository laden |
git remote [...] | Verbindung zum Remote-Repository konfigurieren |
git clone <url> | Klone das Remote-Repository in ein lokales |
SSH-Schlüssel erstellen:
Bevor wir mit einem Remote-Repository arbeiten können, müssen wir einen sogenannten SSH-Schlüssel erstellen. Dieser dient zur Authentifizierung beim Zugriff auf das Remote-Repository.
Zunächst überprüfen wir, ob bereits ein öffentlicher SSH-Schlüssel existiert. Dazu geben wir in der Konsole ein:
cat ~/.ssh/id_rsa.pubFalls die Datei id_rsa.pub im Verzeichnis $HOME/.ssh nicht existiert, müssen wir zunächst ein neues Schlüsselpaar erzeugen:
ssh-keygen -t rsa -b 4096Nachdem wir den Anweisungen des Programms gefolgt sind, sollten zwei neue Dateien existieren:
~/.ssh/id_rsa → privater Schlüssel (geheim!)
~/.ssh/id_rsa.pub → öffentlicher Schlüssel (darf geteilt werden)Der private Schlüssel darf niemals weitergegeben werden. Der öffentliche Schlüssel hingegen wird benötigt, um uns bei GitLab zu authentifizieren.
Den öffentlichen Schlüssel können wir mit
cat ~/.ssh/id_rsa.pubanzeigen und anschließend in die Zwischenablage kopieren.
Auf der GitLab-Webseite klicken wir oben rechts auf unseren Avatar, wählen Preferences und navigieren in der linken Seitenleiste zum Menüpunkt SSH Keys. Mit der Schaltfläche Add new key erscheint ein Formular, in das wir unseren öffentlichen Schlüssel einfügen können. Zusätzlich vergeben wir einen Titel (z. B. Max Laptop) und können optional ein Ablaufdatum festlegen.
Dieser Vorgang muss für jedes Gerät durchgeführt werden, von dem aus wir auf das Remote-Repository zugreifen möchten.
Mit dem Remote-Repository verbinden:
Beim ersten Versuch unseren Code zu “pushen” wird uns eine Warnung angezeigt:
git pushfatal: Kein Ziel für "push" konfiguriert.
Entweder spezifizieren Sie die URL von der Befehlszeile oder konfigurieren ein Remote-Repository unter Benutzung von
git remote add <Name> <URL>
und führen "push" dann unter Benutzung dieses Namens aus
git push <Name>Dies ist nicht verwunderlich, da wir Git noch nicht mitgeteilt haben, wo sich unser Remote-Repository befindet. Wie wir dies tun können, steht bereits im Fehlertext.
Die URL unseres Remote-Repositories finden wir beispielsweise im GitLab-Webinterface, wenn wir den Button Clone anklicken:

Dabei verwenden wir in der Regel die SSH-URL. Mit dieser können wir unser lokales Repository wie folgt mit dem Remote-Repository verknüpfen:
git remote add Gitlab git@gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git
git push GitlabObjekte aufzählen: 9, fertig.
Zähle Objekte: 100% (9/9), fertig.
Delta-Kompression verwendet bis zu 4 Threads.
Komprimiere Objekte: 100% (6/6), fertig.
Schreibe Objekte: 100% (9/9), 983 Bytes | 327.00 KiB/s, fertig.
Gesamt 9 (Delta 1), Wiederverwendet 0 (Delta 0), Pack wiederverwendet 0
To gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git
* [new branch] master -> masterAuf der GitLab-Webseite unseres Projekts sollte nun auch die Datei calender.py erscheinen.
Beachte dabei, dass die Weboberfläche nur den Inhalt des Remote-Repositories anzeigt. Änderungen, die sich nur in unserem lokalen Repository befinden und noch nicht mit git push übertragen wurden, sind dort nicht sichtbar.
Ist das Remote-Repository erst einmal eingerichtet, können andere Programmierer dieses herunterladen und mit der Arbeit am Projekt beginnen. Dazu verwendet man den Befehl
git clone git@gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.gitDieser Befehl erstellt ein neues lokales Repository und lädt gleichzeitig alle Dateien sowie die gesamte Versionshistorie des Projekts herunter.
Anschließend darf der Programmierer Dateien verändern, die Änderungen in sein lokales Repository committen und mit
git pushin das Remote-Repository übertragen. Ein weiterer Programmierer kann sich diese Änderungen wiederum mit
git pullin sein lokales Repository laden.
Mit dem Befehl
git remote -vkann man sich jederzeit anzeigen lassen, mit welchen Remote-Repositories das lokale Repository verbunden ist. Dabei werden sowohl die Adresse zum Herunterladen (fetch) als auch zum Hochladen (push) der Daten angezeigt.
Im Team arbeiten¶
Merges und Mergekonflikte¶
Wir haben bereits gelernt, wie wir die Daten unseres lokalen Repositories mit dem Remote-Repository synchronisieren können (push und pull). Weitere Programmierer können dieses Repository ebenfalls klonen (clone) und uns bei der Programmierung unterstützen.
Doch was passiert eigentlich, wenn mehrere Benutzer gleichzeitig Änderungen einpflegen?
Wir testen dies aus und lassen unser Repository von zwei Programmierern – Programmierer A und Programmierer B – klonen. Diese arbeiten nun unabhängig voneinander an der calender- bzw. an der appointment-Klasse weiter:
Programmierer A
from datetime import datetime
class appointment:
def __init__(self, date, title):
try:
self.date = datetime.strptime(date, '%d.%m.%y %H:%M:%S')
except:
print("Fehler:", date, "ist kein gültiges Datumsformat.")
self.title = title
def __str__(self):
return str(self.date) + ": " + self.title
def __lt__(self, other):
return self.date <= self.otherProgrammierer B
class calender:
def __init__(self, owner):
self.owner = owner
self.appointments = []
def add_appointment(self, appointment):
self.appointments.append(appointment)
def __str__(self):
res = "Calender of "+self.owner+":\n"
if len(res) == 0:
print("<keine Einträge vorhanden>")
else:
for appointment in self.appointments:
res += str(appointment) + "\n"
return resBeide Programmierer können während ihrer Arbeit beliebig mit git add und git commit ihren Programmcode mit ihren lokalen Repositories synchronisieren. Kritisch wird es allerdings beim git push.
Die Programmiererin, die zuerst ihre Änderungen in das Remote-Repository lädt (git push), kann dies ohne Konflikte tun. Der zweite Programmierer erhält jedoch folgende Fehlermeldung:
git pushTo gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'git@gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.Durch die Änderungen von Programmierer A ist also ein Konflikt mit den lokalen Daten von Programmierer B entstanden. Programmierer B muss zunächst diese Änderungen herunterladen (git pull) und eventuell entstandene Konflikte im Programmcode beheben, bevor er seine Änderungen wieder in das Remote-Repository übertragen kann.
Gibt Programmierer B nun
git pullein, versucht Git automatisch, die Änderungen beider Programmierer zusammenzuführen (Merge). Dieser Merge ist selbst wieder ein Commit. Daher wird man nach einer Commit-Nachricht gefragt. Es öffnet sich ein Texteditor mit folgendem Inhalt:
Merge branch 'master' of gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lectureDiese Nachricht kann man einfach übernehmen und den Editor anschließend speichern und schließen (beispielsweise in Vim mit :wq).
Im Idealfall erscheint anschließend eine Erfolgsmeldung auf der Konsole:
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture
a955603..5872b6e master -> origin/master
Auto-merging calender.py
Merge made by the 'recursive' strategy.
calender.py | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)Dies bestätigt, dass der Merge erfolgreich war. Öffnet man nun die Datei calender.py, sieht man, dass die Änderungen beider Programmierer enthalten sind.
Programmierer B kann nun anschließend mit
git pushdie aktuellste Version wieder in das Remote-Repository übertragen.
Wir überprüfen nun mit git log, versehen mit einigen Zusatzoptionen, was eigentlich passiert ist:
git log --oneline --graph* 224c7c9 (HEAD -> master, origin/master, origin/HEAD)
Merge branch 'master' of gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture
|\
| * 5872b6e Modified appointment class
* | 209c906 Modified calender class
|/
* a955603 Implemented constructor and add_appointment method for calender class
* ed29a89 Implemented constructor and string method for appointment class
* ce7d6d2 Created empty calender classLinks erkennt man eine Baumstruktur. Nach dem dritten Commit verzweigt sich die Historie in zwei Zweige (Branches), jeweils für die Commits der beiden Programmierer, die zu diesem Zeitpunkt nicht mehr synchron waren. Beim git pull hat Programmierer B diese beiden Zweige wieder zusammengeführt.
Das war der einfache Fall. Was passiert jedoch, wenn der automatische Merge fehlschlägt? Beispielsweise dann, wenn beide Programmierer dieselbe Zeile verändern.
Nehmen wir an, beide Programmierer fügen einen Kommentar zur Klasse appointment hinzu:
Programmierer A
# Class that stores date and title of an appointment
class appointment:Programmierer B
# Class represents an appointment of the calender owner
class appointment:Beide committen und pushen ihre Änderungen. Beim zweiten Programmierer schlägt der git push fehl und er muss zunächst mit git pull die Änderungen herunterladen:
git pullremote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture
224c7c9..c7bebfa master -> origin/master
Auto-merging calender.py
CONFLICT (content): Merge conflict in calender.py
Automatic merge failed; fix conflicts and then commit the result.Der Merge ist offensichtlich fehlgeschlagen. Git kann schließlich nicht entscheiden, welche Version korrekt ist. Programmierer B muss daher die Datei calender.py im Editor öffnen und findet dort folgende Markierungen:
<<<<<<< HEAD
# Class represents an appointment of the calender owner
=======
# Class that stores date and title of an appointment
>>>>>>> c7bebfacf5aa664e1f3ed705794856828970b787
class appointment:Diese Markierungen zeigen den Konfliktbereich an. Programmierer B muss nun den Konflikt manuell auflösen, also entscheiden, welche Version erhalten bleiben soll. Anschließend müssen die Konfliktmarkierungen entfernt werden.
Danach kann er die Änderungen mit
git commit -am "Solved merge conflict"
git pushwieder in das lokale und anschließend in das Remote-Repository übertragen.
Programmiererin A erhält diese Änderungen beim nächsten git pull ebenfalls.
Arbeiten mit Branches¶
Um Konflikte zwischen mehreren Programmierern zu reduzieren, lohnt es sich, für neue Features eigene Entwicklungszweige (Branches) anzulegen und gezielt auf diesen zu arbeiten.
Ist ein Feature fertig implementiert, kann es anschließend in den Haupt-Branch
(meist main, früher häufig master) integriert werden.

Wir lernen hier folgende Befehle kennen:
| Befehl | Bedeutung |
|---|---|
git branch ... | Branch erzeugen/löschen/verwalten |
git checkout <branch> | Den Branch wechseln |
git merge <branch> | Branch mit dem aktuellen verschmelzen |
Einen neuen Branch erstellen
Wir stellen uns nun vor, dass Programmiererin A und B getrennt voneinander weiter arbeiten. Während Programmiererin A weiter auf dem master-Branch arbeitet und vielleicht ein Paar Testskripte für die Verwendung unserer calender-Klasse programmiert, arbeitet Programmierer B an neuen Features für unseren Kalender. Um die Arbeit von Programmiererin A nicht zu stören erzeugt Programmierer B einen neuen Branch:
git branch calender_features
git checkout calender_featuresSwitched to branch 'calender_features'Alternativ kann man einen neuen Branch auch direkt erstellen und zu diesem wechseln mit
git checkout -b calender_featuresWir können uns mit
git branch master
* calender_featuresnoch einmal die verfügbaren Branches anzeigen lassen. Der Stern markiert dabei den Branch, auf dem wir uns aktuell befinden.
Wir können nun die Klasse calender erweitern. Beispielsweise fügen wir folgende Methode hinzu:
def remove_old_appointments(self):
today = datetime.today()
upcoming_appointments = []
for appointment in self.appointments:
if appointment.date > today:
upcoming_appointments.append(appointment)
self.appointments = upcoming_appointmentsWir schauen uns noch einmal die Ausgabe von git status an:
git statusOn branch calender_features
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: calender.py
no changes added to commit (use "git add" and/or "git commit -a")Die erste Zeile zeigt uns, dass wir uns tatsächlich auf dem Branch calender_features befinden.
Wir können nun committen und anschließend versuchen zu pushen:
git commit -am "Implemented method to remove old appointments"
git pushfatal: The current branch calender_features has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin calender_features
Der Grund ist, dass der neue Branch bisher nur lokal existiert. Das Remote-Repository kennt diesen Branch noch nicht. Beim ersten Push müssen wir daher angeben, dass ein neuer Remote-Branch erstellt werden soll.
Dies erledigen wir mit:
git push --set-upstream origin calender_featuresCounting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 484 bytes | 161.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote:
remote: To create a merge request for calender_features, visit:
remote: https://gitlab.hrz.tu-chemnitz.de/maxwin--tu-chemnitz.de/python-lecture/-/merge_requests/new?merge_request%5Bsource_branch%5D=calender_features
remote:
To gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git
* [new branch] calender_features -> calender_features
Branch 'calender_features' set up to track remote branch 'calender_features' from 'origin'.Auf der GitLab-Webseite sehen wir in der Rubrik Branches nun ebenfalls diesen neuen Branch.
Mit git log können wir überprüfen, auf welchem Branch wir uns lokal und remote befinden:
git logcommit 2671386641522f6d2ceeffdec51263830f76d86d
(HEAD -> calender_features, origin/calender_features)Der HEAD ist ein Zeiger auf den aktuell ausgecheckten Commit, also die Spitze des Branches, auf dem wir gerade arbeiten. In unserem Fall zeigt HEAD auf den Branch calender_features, welcher mit dem Remote-Branch origin/calender_features verknüpft ist.
Der Name origin bezeichnet dabei einfach unser Remote-Repository. Wir können uns die konfigurierten Remote-Repositories mit folgendem Befehl anzeigen lassen:
git remote -vorigin git@gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git (fetch)
origin git@gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git (push)Was hat Programmiererin A in der Zwischenzeit gemacht? Sie war weiterhin auf dem master-Branch unterwegs und hat ein kleines Testskript test.py geschrieben, dieses committed und anschließend gepusht. Da beide Entwickler auf unterschiedlichen Branches gearbeitet haben, gab es beim Pushen keine Konflikte.
Branches mergen
Programmierer B hat mittlerweile seine Arbeit beendet, sein neues Feature ausgiebig getestet und möchte dieses nun in den master-Branch integrieren.
Dazu wechselt er zunächst auf den master-Branch:
git checkout masterSwitched to branch 'master'
Your branch is up to date with 'origin/master'.Anschließend lädt er mit
git pulldie Änderungen herunter, die andere Programmiererinnen in der Zwischenzeit durchgeführt haben. Dieser Schritt ist wichtig, damit wir nicht mit einer veralteten Version des master-Branches arbeiten.
Nun kann Programmierer B (oder auch Programmiererin A) den Merge durchführen:
git merge calender_featuresEs öffnet sich ein Editor, in dem eine Commit-Nachricht für den Merge eingetragen werden kann. Nach dem Speichern erhalten wir beispielsweise folgende Ausgabe:
Merge made by the 'recursive' strategy.
calender.py | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)Der automatische Merge war hier erfolgreich. Es kann jedoch vorkommen, dass Git einen Konflikt nicht automatisch auflösen kann, beispielsweise wenn auf beiden Branches dieselben Codezeilen verändert wurden. In diesem Fall müssen die Konflikte manuell im Quellcode behoben werden.
Zum Abschluss laden wir den neuen Stand noch in das Remote-Repository:
git pushAnschließend kann ganz normal auf dem master-Branch weitergearbeitet werden. Beispielsweise könnte Programmierer B sein neues Feature in das Testskript test.py einbauen.
Branch schließen
Nach dem erfolgreichen Merge wird der Branch calender_features nicht mehr benötigt. Dabei werden nicht die Commits gelöscht, sondern lediglich der Zeiger auf diesen Branch entfernt. Die Änderungen bleiben weiterhin Teil der Projekthistorie.
Lokal können wir den Branch mit
git branch -d calender_featuresDeleted branch calender_features (was 2671386).löschen.
Der Befehl git branch zeigt uns diesen Branch nun nicht mehr an. Auf dem Remote-Repository existiert er jedoch weiterhin, was wir auch auf der GitLab-Webseite sehen können.
Um den Branch auch dort zu löschen verwenden wir:
git push --delete origin calender_featuresTo gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture.git
- [deleted] calender_featuresZusammenfassung
Wir können uns nun noch einmal die Commit-Historie anschauen:
git log --oneline --graph* 79912a3 (HEAD -> master, origin/master, origin/HEAD) Extended test script test.py
* e501a9e Merge branch 'calender_features'
|\
| * 2671386 Implemented method to remove old appointments
* | 17a8414 Implemented test script
|/
* b53b4d2 Resolved merge conflict
|\
| * c7bebfa Added comment to appointment class
* | 4e73b1c Added comment to appointment class
|/
* 224c7c9 Merge branch 'master' of gitlab.hrz.tu-chemnitz.de:maxwin--tu-chemnitz.de/python-lecture
|\
| * 5872b6e Modified appointment class
* | 209c906 Modified calender class
|/
* a955603 Implemented constructor and add_appointment method for calender class
* ed29a89 Implemented constructor and string method for appointment class
* ce7d6d2 Created empty calender classLinks erkennen wir eine Baumstruktur. Diese zeigt, wie sich verschiedene Entwicklungszweige (Branches) im Laufe der Zeit verzweigt und später wieder zusammengeführt haben.
Auch auf dem Remote-Repository ergibt sich ein ähnliches Bild. Unter Repository → Graph sehen wir folgenden Graphen:

Hier sehen wir sowohl die Branches, die durch gleichzeitiges Arbeiten auf dem master-Branch entstanden sind, als auch unseren manuell erzeugten calender_features-Branch.
Git-Cheatsheet (kompakt)¶
| Befehl | Bedeutung / Tipp |
|---|---|
git init | Lokales Repository erstellen |
git clone <url> | Repository vom Remote herunterladen |
git status | Zeigt Änderungen, Branch und Commit-Status |
git add <datei> | Datei zum nächsten Commit vormerken |
git commit -m "Nachricht" | Änderungen festschreiben |
git commit -am "Nachricht" | Änderungen in bekannten Dateien add + commit in einem Schritt |
git pull | Änderungen vom Remote einholen (immer zuerst!) |
git push | Eigene Änderungen in das Remote-Repository hochladen |
git branch | Alle Branches anzeigen |
git checkout <branch> | Auf einen anderen Branch wechseln |
git checkout -b <branch> | Branch erstellen und direkt wechseln |
git merge <branch> | Anderen Branch in den aktuellen integrieren |
git log --oneline --graph | Historie kompakt als Baum anzeigen |
git remote -v | Zeigt die Remote-Repository URLs an |
git push --set-upstream origin <branch> | Neuen lokalen Branch mit Remote verknüpfen |
git push --delete origin <branch> | Remote-Branch löschen |
Tipp für Teams:
Vor jedem Push immer
git pullausführen, um Konflikte zu vermeiden.Für neue Features eigene Branches nutzen und nach Fertigstellung mergen.
Konflikte rechtzeitig manuell lösen, nicht einfach überschreiben.
SSH-Key einmal erstellen, dann kann jeder Push/Pull ohne Passwort funktionieren.
Was gehört in ein Repository?
Ein Git-Repository sollte in der Regel nur Quellcode und wichtige Projektdateien enthalten. Dateien, die automatisch erzeugt werden oder leicht neu erstellt werden können, gehören normalerweise nicht in ein Repository.
Typischerweise gehören in ein Repository:
Quellcode (z.B.
.py,.c,.cpp)Konfigurationsdateien
Dokumentation (
.md,.tex)Skripte und Build-Dateien
kleine Beispieldaten
Folgende Dateien sollten dagegen nicht versioniert werden:
kompilierte Programme (
.exe,.out,.class)automatisch erzeugte Dateien (
.log,.aux,.toc)erzeugte PDF-Dateien aus LaTeX
Python-Cache-Dateien (
__pycache__/,.pyc)temporäre Dateien oder Editor-Backups
große Datenmengen
Solche Dateien werden normalerweise in einer Datei namens .gitignore aufgeführt. Git ignoriert alle dort eingetragenen Dateien und nimmt sie nicht in Commits auf.
Ein einfaches Beispiel für eine .gitignore-Datei in einem Python-Projekt wäre:
__pycache__/
*.pyc
*.log
*.aux
*.toc
*.out
*.pdfDiese Datei wird im Hauptverzeichnis des Repositories gespeichert und ebenfalls versioniert, damit alle Entwickler die gleichen Regeln verwenden.