Ing Mathematik: git

Aus Wikibooks


Einleitung[Bearbeiten]

Wikipedia hat einen Artikel zum Thema:
Wikipedia hat einen Artikel zum Thema:


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]

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 geschreiben? 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.