Versionsverwaltung (VCS – Version Control System)


Üblicherweise wird eine Software bei der Entwicklung und Wartung versioniert. Hierbei wird ein VCS eingesetzt, dass folgende Möglichkeiten bietet:


  • Archivierung aller Projektstände, um bei versehentlichen Änderungen diese zur Wiederherstellung rückgängig machen zu können.

  • Koordinierung des gemeinsamen Zugriffs im Entwicklungsteam auf den Quellcode. Dabei werden Änderungen namentlich protokolliert.

  • Gleichzeitiges Arbeiten an mehreren Entwicklungszweigen (engl. Branches) eines Projekts, z.B. Produktion und Developement.


Git


Logo Git


Abbildung 1: Logo


ist ein freies, weitverbreitetes VCS, dessen Entwicklung von Linus Thorwalds 2005 initiiert wurde. https://git-scm.com/

Beispielhaft wird hier mittels der Konsole (bash) die Arbeitsweise im Einzelnen vorgestellt, um die Abläufe transparent zu machen:


Git Bash starten


Um die Arbeitsweise von und mit Git besser zu verstehen, arbeiten wir nicht mit einer GUI sondern mit der Bash. Hier werden die Befehle manuell eingegeben was ein tieferes Verständnis erfordert.



Git konfigurieren


Nach der lokalen Installation von Git werden die wichtigsten Einstellungen einmalig für alle Projekte vorgenommen:


$ git config --global user.name "Vorname Nachname"
$ git config --global user.email user@mydomain.com



Lokales Repository anlegen


Es wird zunächst ein leeres Repository (engl. Speicher) mit Namen „Test“ angelegt:


git init Test


Ein Verzeichnis Test wird angelegt. In ihm befindet sich ein Unterverzeichnis .git

In ihm werden alle Systeminformationen dieses Repositories gespeichert. (Es ist ein verstecktes Systemverzeichnis und kann nur durch Aktivieren der entsprechenden Ordneroption sichtbar gemacht werden.)



Datei in Repository aufnehmen


Testweise wird eine Datei „first.txt“ mit etwas Text angelegt.


git status


informiert darüber, dass die Datei von Git noch nicht verfolgt (untracked file) wird. Dafür muss sie zunächst zum Index (sog. stage) hinzugefügt werden:


git add first.txt


(Alternativ können auch Platzhalter wie z.B. git add *.txt verwendet werden. Ein einzelner Punkt weist GIT an, alle Dateien hinzuzufügen: git add .)


git status


besagt nun, dass neue Datei hinzugefügt und zum Repository hinzugefügt werden kann.


Jetzt kann die Datei an das System übergeben werden:


git commit -m 'erste Projektversion'

Dateien zum Repository hinzufügen

Abbildung 2: Dateien dem Repository hinzufügen

git status


zeigt jetzt an, dass das nichts weiter zum Repository hinzugefügt werden muss.3


1. Commit

Abbildung 3: 1. Commit

Standardmäßig erhält der Versionszweig den Namen „main“. (Die frühere Bezeichnung „master“ wurde aus Gründen der political correctness abgelegt.)
HEAD (Großschreibung beachten!) weist auf die aktuelle Version. Git verwendet keine fortlaufenden Nummern zur internen Verwaltung, sondern einen Hash-Wert, der Änderungen zum vorhergehenden Commit (der Einfachheit halber nur die ersten 4 Zeichen dargestellt).



Änderungen in Repository einpflegen


Wir legen nun einen Unterorder „subfolder“ mit einer darin befindlichen Datei „second.txt“ mit folgendem Inhalt an:


erste Zeile
zweite Zeile
dritte Zeile


Auch den Text in first.txt verändern wir.


git status


weist uns an, die modifizierte Datei „first.txt“ und die noch nicht verfolgten Dateien in „subfolder“ hinzuzufügen, damit ein Commit diese einschließt:


git add .


fügt alle neuen und geänderten Dateien hinzu.

(git add *.txt würde die Dateien im Unterverzeichnis „subfolder“ nicht miteinbeziehen)


git commit -m 'zweite Projektversion'


pflegt die Änderungen im Repository ein.

Repository nach dem 2. Commit

Abbildung 4: Repository nach dem 2. Commit


Der 2. Commit verweist auf den ersten.



Vorherige Version wiederherstellen


Mit einem Checkout lassen sich einzelne Dateien oder ganze komplette Programmversionen wiederherstellen.


Die Historie wird angezeigt, um die Hash-Werte anzuzeigen:


git log --graph --full-history --all
* commit b5c9d0e8abe60a5589cb24e52d87e183ac2265de
| Author: Vorname Nachname <user@mydomain.com>
| Date: Thu May 26 11:43:17 2016 +0200
|
| zweite Projektversion
|
* fc3929931d94c232605155280cfb2bc6a53befac
 Author: Vorname Nachname <user@mydomain.com>
 Date: Thu May 26 10:22:47 2016 +0200

 erste Projektversion

Ein Checkout lässt sich dann z.B. wie folgt machen.


git checkout fc3929931d94c232605155280cfb2bc6a53befac


HEAD nach 1. checkout


Abbildung 5: HEAD nach 1. checkout


Das Arbeitsverzeichnis ist nun im Zustand direkt nach dem 1. Commit.


Zurückgeschaltet zum main branch wird mit


git checkout main




Programmversion verzweigen


Beispielhaft wird aus der produktiven main-Version zum Entfernen eines Bugs in die bugfix-Version verzweigt:

Parameter -b steht für branch (engl. Zweig)

git checkout -b bugfix


Versionsverzweigung nach checkout


Abbildung 6: Versionsverzweigung nach checkout


Der Code in „second.txt“ wird nun verändert:


erste Zeile
zweite Zeile geändert (bugfix)
dritte Zeile


Die modifizierte Datei wird hinzugefügt und in das Repository eingepflegt:


git commit -am 'Bug gefixed'


(Der Parameter -a (add) bewirkt, dass alle Dateien erst hinzugefügt und dann im Repository gespeichert werden.)


Nach dem 3. Commit im branch bugfix


Abbildung 7: nach dem 3. Commit im branch bugfix


Aber oft hat ein anderer Entwickler in der Zwischenzeit am main branch in der gleichen Datei weitergearbeitet. Er hat den Code in „second.txt“ wie folgt geändert:


erste Zeile
zweite Zeile geändert (main)
dritte Zeile


Wir simulieren das, in dem wir nach main schalten, die Änderung einpflegen und speichern:


git checkout main


Änderung einpflegen


git commit -am 'dritte Programmversion'


zwischenzeitliche Änderung im master branch

Abbildung 8: Zwischenzeitliche Änderung im main branch



Programmzweige wieder zusammenführen


Der Bug ist beseitigt dieser Zweig muss jetzt allerdings in den produktiven main-Zweig integriert werden. Dafür muss erst zum main branch geschaltet werden, falls noch nicht geschehen:


git checkout main


Danach folgt die Integration:


git merge bugfix


Weil die gleiche Zeile in unterschiedlichen branches geändert wurde, gibt es dabei einen Konflikt der manuell behoben werden muss. Der Inhalt von „second.txt“ ist:


erste Zeile 
<<<<<<< HEAD
zweite Zeile geändert (main)
=======
zweite Zeile geändert (bugfix)
>>>>>>> bugfix
dritte Zeile


Der Abschnitt zwischen <<<<<<< HEAD und ======= sind die Änderungen des main branch. Es wurde mittels checkout vor dem Merging ja in diesen Zweig umgeschaltet. Der Teil darunter bis zu >>>>>>> bugfix bezeichnet die Änderungen des bugfix branch.

Um den Konflikt zu lösen, entscheidet man sich für einen der beiden Teile oder ersetzt den ganzen Abschnitt komplett.


Danach wird die Datei eingecheckt:

git commit -am 'vierte Programmversion (Bug gefixed)'


Nach dem Merging

Abbildung 9: Nach dem Merging



Branch löschen


git log --graph --full-history --all


zeigt den Baum noch mal an.


Es ist möglich, den Aufruf langer Befehle durch die eigene Definition eines Alias zu vereinfachen.
Empfohlene Definition des Alias „tree“
git config --global alias.tree 'log --graph --all –-pretty=oneline --abbrev-commit --decorate'
Aufruf:
git tree

--graph erstellt mittels ASCII-Art eine grafische Darstellung der Branches.
--all sorgt dafür, dass auch andere Branches angezeigt werden (z.B. in Abb. 8, würde ohne --all der 3. Commit nicht angezeigt werden).
--pretty=oneline stellt jeden Commit in nur einer Zeile dar; dies erhöht die Übersicht erheblich, besonders wenn man mehr als ein paar Commits hat.
--abbrev-commit kürzt die Hashwerte der Commits von 40 auf 7 Zeichen, da man den kompletten Hashwert eigentlich nie braucht.
--decorate fügt einige Marker ein, die einem anzeigen, welcher Branch auf welchen Commit zeigt.


Der Branch bugfix kann nun gelöscht werden:


git branch -d bugfix


Vorgehensweise beim Verzweigen und Zusammenführen von Programmversionen


Die Vorgehensweise bei der Arbeit mit Git muss im Team abgesprochen werden.

Vincent Driessen schlägt folgende Strukturen vor:


Vorgehensweise mit Git

Eine mögliche Vorgehensweise beim Verzweigen und Zusammenführen nach https://nvie.com/posts/a-successful-git-branching-model/    (31.08.2021)




Externes Repository anbinden


Um im Team an einer Codebasis arbeiten zu können, muss der Code mit einem Remote Repository synchronisiert werden.

https://git-scm.com/book/de/v1/Los-geht%E2%80%99s-Wozu-Versionskontrolle%3F (27.05.2016)


Anbindung an Server

Abbildung 10: Anbindung an Server


Es empfiehlt sich, sich testweise bei Github anzumelden und ein Repository einzurichten.

Das Anlegen von öffentlichen und privaten Repositories bei Github ist in bestimmten Grenzen kostenfrei. Der Account kann später wieder gelöscht werden. https://github.com/


Remote Repository hinzufügen:


git remote add origin https://github.com/ateachment/test.git



origin ist dabei der Kurzname der bei Git standardmäßig für den Server vergeben wird.


Eventuell muss ein Proxy konfiguriert werden:


git config --global http.proxy https://172.22.100.2:443


oder falls der WvS-Proxy so nicht funktioniert, dann halt ausnahmsweise unverschlüsselt sad


git remote rm origin                                          # erst wieder remove origin
git remote add origin http://github.com/ateachment/test.git # kein https
git config --global http.proxy http://172.22.100.2:80 # Port 80


Angebundene remote repositories können angezeigt werden mit:


git remote -v



Änderungen in ein Remote Repository hochladen


Nachdem die Anbindung erfolgt ist, kann nun ein Branch ins Remote Repository hochgeladen werden. Dafür muss man sich bei Github ein Personal Access Token PAT erzeugen (Die Kennwortauthentifizierung wurde Github zu unsicher.). Auf github.com navigiert man über Settings => Developer Settings => Personal access tokens generiert dort ein neues PAT und konfiguriert folgende Berechtigungen
workflow

write:packages

delete:packages


Kopieren Sie das PAT an einen sicheren Ort.


git push origin main


lädt nun die Branch in das Repository hoch. Die Eingabe des Personal Access Tokens muss nun manuell vorgenommen werden.


Es geht auch in einem Schritt direkt von der Kommandozeile mit:


git push https://<GITHUB_ACCESS_TOKEN>@github.com/<GITHUB_USERNAME>/<REPOSITORY_NAME>.git

(Großgeschriebene Bezeichnungen mit persönlichem PAT, Benutzernamen und Repository-Namen ersetzen.)
https://techglimpse.com/git-push-github-token-based-passwordless/    (07.09.2021)



Remote Repository klonen


Andere Entwickler können nun das Repository klonen:


git clone https://github.com/ateachment/test.git




Änderungen aus dem Remote Repository herunterladen und zusammenführen


Änderungen aus dem vormals geklonten Repository werden heruntergeladen mit:


git fetch origin


Diese Änderungen müssen dann mit eventuellen eigenen Änderungen noch manuell zusammen geführt werden.

Um das Herunterladen gleich mit einem Merging zu verbinden, benutzt man:


git pull origin



Dateien und Verzeichnisse von der Synchronisation ausschließen


Natürlich sollte binäre Dateien oder z.B. Konfigurationsdateien der IDE von der Synchronisation ausgeschlossen werden. Dies geschieht mittels einer Datei mit der Bezeichnung „.gitignore“ im Projektverzeichnis.

Das Erstellen dieser .gitignore-Datei kann sehr aufwändig werden. Fertige Dateien sind im WWW erhältlich, z.B. hier für Visual Studio: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore (14.08.2018)


Versionsnummern vergeben


Wichtige Versionsstände, wie z.B. Releases, sollten durch eine Versionsnummer markiert werden, was die Verwaltung der Commits erleichtert.13


git tag v0.0.1


Weitere Möglichkeiten des Tagging: https://git-scm.com/book/de/v1/Git-Grundlagen-Tags (14.08.2018)




Danksagung


Dank gilt den Schülern der Klasse FI4E der Werner-von-Siemens-Schule, insbesondere den Herren Tom Rosenberger und Tobias Stensbeck, deren Vortrag die Vorlage für diesen Text darstellt und den Korrekturvorschlägen vieler anderer Schüler.


Last modified: Wednesday, 29 September 2021, 12:06 PM