Ing Mathematik: git
Einleitung
[Bearbeiten]
Abschließend in diesem Band sei nun die Versionsverwaltung mittels git kurz vorgestellt. Bisher haben wir ausführlich das Programmieren behandelt. Auch das Schreiben mathematisch orientierter Texte mittels LaTeX gehört im weiteren Sinne dazu. All diese Dinge werden Sie nicht in einem Ruck erledigen, sondern inkrementell in Schüben, eventuell sogar als Gruppenarbeit.
Schreiben Sie hier ein Wikibook, so kommen Sie auf Schritt und Tritt mit Versionsverwaltung in Kontakt. Sie können Versionen abspeichern, Sie können Änderungen rückgängig machen, Sie können die letzten Änderungen ansehen etc. Sie können verschiedene Bild-Versionen z.B. auf Commons hochladen usw. Administratoren können Versionen oder ganze Bücher wieder löschen. All das dient der Datensicherung, dem kollaborativen Arbeiten, dem Sauberhalten des Artikelnamensraumes usw. Ähnliches können sie auch manuell mittels git machen.
In diesem Kapitel wird weniger die Teamarbeit in den Vordergrund gestellt, sondern vielmehr, wie Sie als Einzelperson git zur Datensicherung und Versionsverwaltung einsetzen können.
Git gehört zur Gruppe der verteilt arbeitenden Versionsverwaltungssysteme (siehe auch Versionsverwaltung).
Wichtige Begriffe
[Bearbeiten]- Repository, Repo: Lager, Depot Repository
- Version: Definiertes Entwicklungsstadium einer Software Version (Software)
- Patch: Flicken, Reparatur, Nachbesserung Patch (Software)
- Bugfix: Patch
- Commit: Freischaltung von Änderungen Commit
- Branch: Entwicklungszweig einer Software
- Feature: Funktionalität einer Software
- Staging, Index: Bereitstellungsraum, Sammelplatz Staging (Datenbank)
- HEAD: aktuell ausgecheckter Commit
- Fork: Gabelung, Abspaltung Abspaltung (Softwareentwicklung)
- Stash: Eine Art Zwischenspeicher (LIFO Last In – First Out)
- Release: Hauptversion einer Software Entwicklungsstadium (Software)
- Merge: Vereinigen, zusammenführen Merge
Der Datenfluss mit git
[Bearbeiten]Obige drei Bilder zeigen im Prinzip den gleichen Sachverhalt jeweils in etwas anderer Darstellung, nämlich den Datenfluss mit git. Die einzelnen Befehle werden später erklärt.
Installation
[Bearbeiten]Arbeiten Sie mit dem Betriebssystem Linux, dann stehen die Chancen nicht schlecht, dass git bereits vorinstalliert ist.
Unter MS Windows müssen Sie das Programmpaket erst downloaden. Dazu gehen Sie auf die Webite [1] und können von dort den Download starten. Weiter geht es wie bei jedem anderen größeren zu installierenden Programm.
Git ist Open Source und steht unter der GNU General Public License, Version 2. Für die Erstellung dieses Kapitels wurde git in der Version 2.44.0 vom Februar 2024 verwendet.
Mit git starten
[Bearbeiten]Vorbereitungsarbeiten
[Bearbeiten]Unter MS Windows starten Sie die Git Bash. Diese finden Sie z.B. im Startmenü unter dem Begiff Git. Standardmäßig legt das git-Installationsprogramm auch auf dem Dektop ein entsprechendes Icon an. Grafische Benutzeroberflächen für git werden hier nicht behandelt. Sie erhalten eine Ausgabe ähnlich dieser:
Xyz@DESKTOP-O7BKMI9 MINGW64 ~ $
Wechseln Sie in das Verzeichnis c:/tmp:
$ cd c:/tmp Xyz@DESKTOP-O7BKMI9 MINGW64 /c/tmp
Kreieren Sie ein Unterverzeichnis, in dem Sie im Nachfolgenden arbeiten wollen, z.B. c:/tmp/test
:
$ mkdir test
Wechseln Sie in dieses Unterverzeichnis:
$ cd test
Legen Sie ein Repository an:
git init
Es erscheint folgende Ausgabe:
Initialized empty Git repository in C:/tmp/test/.git/ Xyz@DESKTOP-O7BKMI9 MINGW64 /c/tmp/test (master) $
Was hat git in das ominöse .git geschrieben? Dazu listen wir .git mit
ls -l .git
Ausgabe:
total 7 -rw-r--r-- 1 Xyz 197121 23 Apr 1 13:32 HEAD -rw-r--r-- 1 Xyz 197121 130 Apr 1 13:32 config -rw-r--r-- 1 Xyz 197121 73 Apr 1 13:32 description drwxr-xr-x 1 Xyz 197121 0 Apr 1 13:32 hooks/ drwxr-xr-x 1 Xyz 197121 0 Apr 1 13:32 info/ drwxr-xr-x 1 Xyz 197121 0 Apr 1 13:32 objects/ drwxr-xr-x 1 Xyz 197121 0 Apr 1 13:32 refs/
Dieses Verzeichnis sollten Sie nicht löschen oder händisch verändern (mit kleinen Ausnahmen), denn hier liegt das Gedächtnis des git-Repositories.
Das git-Repository sollten Sie noch konfigurieren, z.B. Ihren Namen und E-Mail setzen. Global können Sie das so machen:
$ git config --global user.email "test@test.org" $ git config --global user.name "Hugo Mustermann"
Ansehen können Sie die Konfiguration mit
git config -l
Es werden etliche Daten ausgegeben, auszugsweise
... init.defaultbranch=master user.email=test@test.org user.name=Hugo Mustermann credential.helper=manager ...
Sie können sich auch den Status Ihres Repositories ansehen:
$ git status
Ausgabe:
On branch master No commits yet nothing to commit (create/copy files and use "git add" to track)
Die git-Hilfe-Funktion
[Bearbeiten]Z.B.:
git help status
öffnet ein Browserfenster. Darin wird ein umfangreicher Hilfetext zum git-Befehl status
geliefert.
Die aktuell eingesetzte git-Version können Sie mit
git --version
abrufen. Es wird ein Text ähnlich diesem ausgegeben:
git version 2.44.0.windows.1
Der initiale Commit
[Bearbeiten]Momentan ist das Repository noch leer.
Wir erstellen Unterverzeichnisse in unserem Testverzeichnis c:/tmp/test
mkdir python mkdir latex mkdir pictures
und befüllen diese mit ein paar Dateien.
Im python-Verzeichnis:
Datei test1.py:
# Das ist ein Kommentar print("Hallo Welt!")
Datei test2.py:
import keyword print(keyword.kwlist)
Im latex-Verzeichnis:
Datei test1.tex:
\documentclass[a4paper]{scrartcl} \usepackage[utf8]{inputenc} \usepackage[ngerman]{babel} \usepackage[T1]{fontenc} \begin{document} % Kommentar Hallo Welt! \end{document}
Ins pictures-Verzeichnis kopieren wir zwei Grafikdateien, die wir im Zuge des Python-Kurses erstellt haben:
- PythonIng_cosh4.png
- PythonIng_spirale2.png
Sehen wir uns wieder den Status unseres Projekts an:
git status
Ausgabe:
On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) latex/ pictures/ python/ nothing added to commit but untracked files present (use "git add" to track)
Jetzt ist das Arbeitsverzeichnis mit einigen Daten gefüllt, das Repository aber noch leer. Wir haben also schon ein bisschen gearbeitet und wollen nun das Ergebnis im Repository sichern. Dazu fügen wir die zu sichernden Dateien zum Staging-Bereich (das ist die Vorstufe zum commit
) hinzu. Vorerst wollen wir nur die Textdateien sichern (die im python-, bzw. latex-Verzeichnis liegen)
git add python latex
Das Ergebnis sehen wir uns wieder mittels
git status
an.
On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: latex/test1.tex new file: python/test1.py new file: python/test2.py Untracked files: (use "git add <file>..." to include in what will be committed) pictures/
Nun wollen wir die im Staging-Bereich vorhandenen Dateien zum Repository hinzufügen (Commit):
git commit -m "Füge wichtige Dateien hinzu"
Ausgabe:
[master (root-commit) ff3b68d] Füge wichtige Dateien hinzu 3 files changed, 13 insertions(+) create mode 100644 latex/test1.tex create mode 100644 python/test1.py create mode 100644 python/test2.py
Das Repository ist nun gefüllt, der Staging-Bereich wieder leer. Wir sehen uns den Repository-Status wieder an und erhalten:
On branch master Untracked files: (use "git add <file>..." to include in what will be committed) pictures/ nothing added to commit but untracked files present (use "git add" to track)
Mit
git log
erhält man die Ausgabe:
commit ff3b68da8d3f265235fd6fed1748de18142d7be1 (HEAD -> master) Author: Hugo Mustermann <test@test.org> Date: Mon Apr 1 15:03:32 2024 +0200 Füge wichtige Dateien hinzu
Dateien hinzufügen und ändern
[Bearbeiten]Nun wollen wir weiterarbeiten und eine neue Python-Datei erstellen, z.B. test3.py:
import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) plt.plot(x,y) plt.grid() plt.show()
Diese Datei wird wieder zum Repository hinzugefügt:
git add python/test3.py git commit -m "test3.py hinzugefügt" [master 1be2c1a] test3.py hinzugefügt 1 file changed, 9 insertions(+) create mode 100644 python/test3.py
An dieser test3.py-Datei wollen wir weiterarbeiten und Änderungen vornehmen:
import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) y = np.cosh(x) + 2**x
plt.plot(x,y) plt.grid() plt.show()
Status:
On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: python/test3.py Untracked files: (use "git add <file>..." to include in what will be committed) pictures/ no changes added to commit (use "git add" and/or "git commit -a")
Änderungen mittels
git diff
ansehen:
diff --git a/python/test3.py b/python/test3.py index 8fd4e62..1afe2ae 100644 --- a/python/test3.py +++ b/python/test3.py @@ -2,7 +2,7 @@ import matplotlib.pyplot as plt import numpy as np x = np.arange(-3., 3.1, .1) -y = np.cosh(x) +y = np.cosh(x) + 2**x plt.plot(x,y) plt.grid()
Änderungen committen:
git add python/test3.py git commit -m "cosh(x) -> cosh(x)+2**x"
Ausgabe:
[master 1af596b] cosh(x) -> cosh(x)+2**x 1 file changed, 1 insertion(+), 1 deletion(-)
Mit git show
können wir die Änderungen ansehen.
Dateien verschieben und löschen
[Bearbeiten]Wir wollen die python/test3.py
-Datei nach python/test4.py
verschieben:
git mv python/test3.py python/test4.py
dir python/
zeigt das gewünschte Ergebnis
test1.py test2.py test4.py
Staging:
git add python/test4.py
Status:
git status
On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) renamed: python/test3.py -> python/test4.py Untracked files: (use "git add <file>..." to include in what will be committed) pictures/
Commit:
git commit -m "Datei verschoben"
[master c127546] Datei verschoben 1 file changed, 0 insertions(+), 0 deletions(-) rename python/{test3.py => test4.py} (100%)
Datei python/test4.py wieder löschen:
git rm python/test4.py
Ausgabe:
rm 'python/test4.py'
Status:
On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) deleted: python/test4.py Untracked files: (use "git add <file>..." to include in what will be committed) pictures/
Branches
[Bearbeiten]Wollen Sie ein wirklich komplexes Beispiel für Branches sehen, dann beachten Sie die nachfolgende Grafik, die die ganzen zeitlichen Abspaltungen von Linux-Distributionen darstellt. Dies ist ein Beispiel für Projekte, an denen viele Programmierer gleichzeitig arbeiten. Als Ingenieur werden Sie kaum mit solchen Ungetümen zu tun haben. Aber ein paar Branches können Sie, sofern Ihr Projekt das erfordert, selber auch anlegen.
Ein etwas einfacheres Beispiel:
Einen Branch erzeugen
[Bearbeiten]Ein Branch ist ein Zeiger auf einen Commit. Der master-Branch ist der Hauptbranch.
Welche Branches gibt es zu diesem Zeitpunkt:
git branch
ergibt:
* master
Es gibt nur einen Branch namens master
. Das Sternchen (*) zeigt an, dass dieser Branch aktiv ist.
Einen neuen Branch anlegen:
git branch neuerbranch
git branch
ergibt:
* master neuerbranch
Zwischen Branches switchen
[Bearbeiten]Jetzt wollen wir zum neuerbranch-Branch wechseln und auf diesem Branch weiterarbeiten:
git switch neuerbranch
Ausgabe:
Switched to branch 'neuerbranch' D python/test4.py
Alternativ kann man auch den Befehl checkout
verwenden:
git checkout neuerbranch
Ausgabe ist wie oben.
Status (git status
):
On branch neuerbranch Changes to be committed: (use "git restore --staged <file>..." to unstage) deleted: python/test4.py Untracked files: (use "git add <file>..." to include in what will be committed) pictures/
Nun ändern wir die Datei test1.py
# Das ist ein Kommentar print("Hallo Welt!") print("Eine Änderung")
und fügen diese Änderung zum Repository hinzu:
git add python git commit -m "eine Änderung"
und erhalten als Ausgabe:
[neuerbranch 291e89b] eine Änderung 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 python/test4.py
git log
liefert
commit 291e89b00f5b873b3e52b6599b89ee1633b0c1ad (HEAD -> neuerbranch) Author: Hugo Mustermann <test@test.org> Date: Tue Apr 2 12:18:54 2024 +0200 eine Änderung commit c1275465838ab8227e57c0bebe270023b17283d9 (master) Author: Hugo Mustermann <test@test.org> Date: Mon Apr 1 19:00:28 2024 +0200 Datei verschoben commit 1af596b1551a46f103a68a373c627dfb8e581c83 Author: Hugo Mustermann <test@test.org> Date: Mon Apr 1 15:32:41 2024 +0200 cosh(x) -> cosh(x)+2**x commit f49734c9aa77efab6afb7498d50f2e2bb6b0506a Author: Hugo Mustermann <test@test.org> Date: Mon Apr 1 15:27:32 2024 +0200 Füge wichtige Dateien hinzu
Im Verzeichnis python
sind folgende Dateien vorhanden (ls -la python):
drwxr-xr-x 1 Xyz 197121 0 Apr 2 12:29 ./ drwxr-xr-x 1 Xyz 197121 0 Apr 1 15:25 ../ -rw-r--r-- 1 Xyz 197121 72 Apr 2 12:29 test1.py -rw-r--r-- 1 Xyz 197121 39 Apr 1 14:43 test2.py
Wechseln wir nun zum master-Branch zurück:
git switch master
Ausgabe:
Switched to branch 'master'
Im Verzeichnis python
sind folgende Dateien vorhanden (ls -la python):
drwxr-xr-x 1 Xyz 197121 0 Apr 2 12:32 ./ drwxr-xr-x 1 Xyz 197121 0 Apr 1 15:25 ../ -rw-r--r-- 1 Xyz 197121 47 Apr 2 12:32 test1.py -rw-r--r-- 1 Xyz 197121 39 Apr 1 14:43 test2.py -rw-r--r-- 1 Xyz 197121 149 Apr 2 12:32 test4.py
Und die Datei test1.py
hat wieder den Inhalt
# Das ist ein Kommentar print("Hallo Welt!")
Branches mergen
[Bearbeiten]git merge neuerbranch
Ausgabe:
Updating c127546..291e89b Fast-forward python/test1.py | 1 + python/test4.py | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 python/test4.py
Die Datei test1.py
lautet nun:
# Das ist ein Kommentar print("Hallo Welt!") print("Eine Änderung")
Revert und Reset
[Bearbeiten]Revert ist ähnlich wie bei Wikibooks eine Änderung rückgängig zu machen. Um eine Änderung rückgängig zu machen, aber nicht den Commit, geben Sie z.B. folgenden Befehl ein
git revert HEAD -m "HEAD revertieren"
Man kann jeden Commit spezifizieren, nicht nur HEAD, z.B.
- Den Commit vor HEAD: HEAD^
- Den Commit fünf Schritte zurück: HEAD~5
- Den Commit mit einer bestimmten Hash-Nummer (Kurz-ID oder vollständige ID): git revert e531378
Reset ist ein bisschen etwas anderes Reset. Es gibt verschiedene Reset-Typen:
- soft: Commit wird rückgängig gemacht, die Änderung aber nicht
- mixed: Default
- hard: sowohl Änderung als auch der Commit werden unwiederbringlich entfernt
Bsp.:
git reset --hard HEAD~1
Datei .gitignore
[Bearbeiten]Wenn wir im Verzeichnis latex
den Befehl pdflatex test1.tex
ausführen, so werden zusätzliche Dateien erstellt (test1.pdf, test1.log, test1.aux). Diese Dateien wollen wir nicht im Repository haben. Sie müssen nicht gesichert werden, da sie jederzeit leicht automatisch aus der Textdatei test1.tex
erstellt werden können. Es wäre Speicherplatzverschwendung, solche Dateien zu sichern. Um solche Dateien auszuschließen, gibt es die Datei .gitignore
. Wir speichern folgenden Text in die .gitignore
-Datei ins c:/tmp/test
-Hauptverzeichnis:
*.pdf *.aux *.log
und committen diese Änderung:
git add .gitignore git commit -m ".gitignore"
Damit weiß git, dass Dateien mit den gelisteten Dateiendungen nicht ins Repository gehören.
Vorgefertigte .gitignore
-Dateien für eine Vielzahl von Programmiersprachen finden Sie z.B. auf [2]
Clean
[Bearbeiten]Nun erstellen wir eine temporäre Datei namens temporär.tmp
im latex
-Verzeichnis mit beliebigen Inhalt.
Status:
On branch master Untracked files: (use "git add <file>..." to include in what will be committed) "latex/tempor\303\244r.tmp" pictures/ nothing added to commit but untracked files present (use "git add" to track)
Diese temporäre Datei wollen wir wieder loswerden. Zuerst sehen wir uns an, was ein "dry run"-Lauf bewirken würde:
git clean -n
Ausgabe:
Would remove "latex/tempor\303\244r.tmp"
Jetzt löschen wir diese Datei (f steht für force):
git clean -f
Removing "latex/tempor\303\244r.tmp"
Um auch die per .gitignore
ignorierten pdf-, aux- und log-Dateien loszuwerden, müssen wir
git clean -fX
aufrufen.
Sonstige Konzepte
[Bearbeiten]Remote Repositories, Datensicherung
[Bearbeiten]Nun wollen wir die Datensicherung in ein anderes Verzeichnis besprechen. Das kann ein USB-Stick, eine externe Festplatte, ein externer Server usw. sein. Solche Repositories werden auch "Remote Repositories" genannt.
Wir wollen in ein Verzeichnis c:/tmp/sicherung
sichern. In der Praxis wird man nicht auf die selbe Festplatte sichern, aber hier geht es nur ums Prinzip. Zuerst wechseln wir in das Verzeichnis c:/tmp
. Anschließend klonen wir das Repository mit
git clone c:/tmp/test sicherung
Ausgabe:
Cloning into 'sicherung'... done.
Wenn wir das c:/tmp/sicherung
-Verzeichnis listen, sehen wir, dass nur Dateien/Verzeichnisse aufgenommen wurden, die wir ins Original-Repository aufgenommen haben. Wechseln wir ins c:/tmp/test
-Verzeichnis und konfigurieren wir nun das Remote-Repository:
git remote add origin ../sicherung/
Der Name origin
ist Konvention, aber nicht Pflicht.
Arbeiten wir im Original-Verzeichnis (c:/tmp/test) weiter. Wir öffnen einen neuen Branch namens nb
. Wir ändern die Datei latex/test1.tex
beliebig ab und committen das Ganze wieder. Diese Änderung ist nun nur im Original-Repository vorhanden, nicht im
Sicherungsverzeichnis. Wir wollen diese Änderung ins Sicherungs-Verzeichnis übertragen.
git push --set-upstream origin nb
Ausgabe:
Enumerating objects: 7, done. Counting objects: 100% (7/7), done. Delta compression using up to 4 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 373 bytes | 373.00 KiB/s, done. Total 4 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0) To ../sicherung/ e529d75..feda063 nb -> nb
Switched man im Sicherungsverzeichnis in den nb
-Branch, so sieht man die Änderung.
Um alle Daten vom Sicherungsverzeichnis zu übertragen (bspw. weil das Projektverzeichnis kaputtgegangen ist oder man es irrtümlich gelöscht hat), kann man wieder git clone
(wie oben) verwenden. Hier sieht man wieder, dass Daten, die nicht ins Sicherungs-Repository übertragen wurden, auch nicht
gesichert wurden.
Um geänderte Daten aus dem Sicherungs-Repository herunterzuladen, kann man auch git fetch
verwenden.
Konflikte beim mergen
[Bearbeiten]Merge-Konflikte müssen manuell bereinigt werden. Haben wir z.B. zwei Branches master
und neuerbranch
mit gleichem Stand. Die Python-Datei test1.py
sei folgendermaßen:
# Das ist ein Kommentar print("Hallo Welt!")
Nun ändern wir diese Datei wie folgt ab:
# Das ist ein Kommentar print("Hallo Welt!") print("Servus")
speichern und committen sie.
Jetzt wechseln wir in den neuerbranch
git switch neuerbranch
und ändern dort oben genannte Datei wie folgt:
# Das ist ein Kommentar print("Hallo Welt!") print("Grüetzi")
Speichern und committen wieder.
Nun wollen wir den neuerbranch
in den master
mergen:
git switch master git merge neuerbranch
Ausgabe:
Auto-merging python/test1.py CONFLICT (content): Merge conflict in python/test1.py Automatic merge failed; fix conflicts and then commit the result.
Die Python-Datei test1.py
sieht jetzt folgendermaßen aus:
# Das ist ein Kommentar print("Hallo Welt!") <<<<<<< HEAD print("Servus") ======= print("Grüetzi") >>>>>>> neuerbranch
Nun muss man manuell eingreifen und die gewünschte Änderung ausführen. Wir wollen hier "Grüetzi" behalten und editieren folgendermaßen,
# Das ist ein Kommentar print("Hallo Welt!") print("Grüetzi")
speichern und committen wieder. Wir haben somit den Merge-Konflikt manuell gelöst.
Tagging
[Bearbeiten]Tag bedeutet soviel wie Markierung oder Auszeichnung (siehe auch Tag (Informatik)). Damit können Versionen markiert werden (z.B. v1.0, v2.0).
Einen neuen Tag anlegen:
git tag -a v1.1 -m "Version 1.1"
Tags listen:
git tag
Ausgabe:
v1.1
Ausblick
[Bearbeiten]Hier wurde nicht der gesamte Umfang von git behandelt. Aber es sollte klar geworden sein, wofür git von Nutzen sein kann und wie man mit git arbeitet. Wollen Sie mehr zu git wissen, so gibt es dazu gedruckte Bücher, z.B. Vijayakumaran: Git Schnelleinstieg. mitp, 2022, ISBN 978-3-7475-0526-7. Auch Online sind Quellen verfügbar, z.B. auf der Seite [3] die git-Referenz und das E-Book Chacon, Straub: Pro Git. Apress, 2014 oder auf en:Git ein englischsprachiges git-Wikibook.