Softwarequalitätsmanagement: Unterschied zwischen den Versionen

Aus CCWiki
Zur Navigation springen Zur Suche springen
 
(194 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
Im folgenden sollen einige Maßnahmen erläutert werden, welche der Erhöhung der Softwarequalität dienen.
{{TOC limit|3}}
= Testing =
= Testing =
Softwaretesting beschreibt den Prozess des verifizierens einer Anwendung, oder Teile davon, gegenüber einer gewissen Spezifikation, oder gegen ein erwartetes Verhalten. Durch Testing kann ein hohes Maß an Softwarequalität gewährleistet werden, natürlich nur wenn Tests gut spezifiert und auch ausgeführt werden. Gerade im Bereich des automatisierten Testens kann durch eine '''hohe Testabdeckung''' des Quellcodes, '''Regression''', verhindert werden.
Softwaretesting beschreibt den Prozess des verifizierens einer Anwendung, oder Teile davon, gegenüber einer gewissen Spezifikation, oder gegen ein erwartetes Verhalten. Durch Testing kann ein hohes Maß an Softwarequalität gewährleistet werden, natürlich nur wenn Tests gut spezifiert und auch ausgeführt werden. Gerade im Bereich des automatisierten Testens kann durch eine '''hohe Testabdeckung''' des Quellcodes, '''Regression''', verhindert werden.
  Eine '''hohe Testabdeckung''', beschreibt, wieviel Code durch Testfälle auch wirklich erreicht wird. Werden alle Verzweigungen in Methoden erreicht? Werden überhaubt alle Methoden getestet?
  Eine '''hohe Testabdeckung''' beschreibt, wieviel Code durch Testfälle auch wirklich erreicht wird. Werden alle Verzweigungen in {{AL|Methoden}} erreicht? Werden überhaupt alle {{AL|Methoden}} getestet?


  Unter '''Regression''' wird eine Verschlechterung des Quellcodes verstanden. Im Laufe des Entwicklungsprozesses funktioniert Code, der zuvor funktionert hat, auf einmal nicht mehr.
  Unter '''Regression''' wird eine Verschlechterung des Quellcodes verstanden. Im Laufe des Entwicklungsprozesses funktioniert Code, der zuvor funktionert hat, auf einmal nicht mehr.
== Beispiel mit JUnit ==
Anhand von folgendem Beispiel soll die '''Testabdeckung''' einer '''Java''' {{AL|Klasse}} mit dem Testframework '''JUnit''' veranschaulicht werden:
Anhand von folgendem Beispiel soll die '''Testabdeckung''' einer '''Java''' {{AL|Klasse}} mit dem Testframework '''JUnit''' veranschaulicht werden:
{{JML|code=
{{JML|code=
public class DateUtils {
public class DateUtils {
   public String getDayOfWeekName(int month) {
   public String getDayOfWeekName(int day) {
     if(month == 1) {
     if(day == 1) {
       return "Montag";
       return "Montag";
     } else if(month == 2) {
     } else if(day == 2) {
       return "Dienstag";
       return "Dienstag";
     } else if(month == 3) {
     } else if(day == 3) {
       return "Mittwoch";
       return "Mittwoch";
     } else if(month == 4) {
     } else if(day == 4) {
       return "Donnerstag";
       return "Donnerstag";
     } else if(month == 5) {
     } else if(day == 5) {
       return "Freitag";
       return "Freitag";
     } else if(month == 6) {
     } else if(day == 6) {
       return "Samstag";
       return "Samstag";
     } else if(month == 7) {
     } else if(day == 7) {
       return "Sonntag";
       return "Sonntag";
     }
     }
Zeile 65: Zeile 68:
Es gibt verschiedene Ansätze wie Tests entwickelt und ausgeführt werden können. Im folgenden sollen die '''Box Ansätze''' kurz erläutert werden.
Es gibt verschiedene Ansätze wie Tests entwickelt und ausgeführt werden können. Im folgenden sollen die '''Box Ansätze''' kurz erläutert werden.
==== White Box ====
==== White Box ====
Der zu testende Quellcode ist bekannt, das Test Design kann sich am Code orientieren um alle Verzweigungen zu erreichen. Weiters können auch Teile des Codes, welche nicht nach außen hin Sichtbar sind, getestet werden. {{Link|Unit_Tests|Unit Tests}} fallen Normalerweise in diese Kategorie. Durch '''White Box''' Tests kann eine '''hohe '''Testabdeckung''' des Codes erreicht werden.
Der zu testende Quellcode ist bekannt, das Testdesign kann sich am Code orientieren um alle Verzweigungen zu erreichen. Weiters können auch Teile des Codes, welche nicht nach außen hin Sichtbar sind, getestet werden. {{Link|Unit_Test|Unit Tests}} fallen normalerweise in diese Kategorie. Durch '''White Box''' Tests kann eine '''hohe '''Testabdeckung''' des Codes erreicht werden.
 
==== Black Box ====
==== Black Box ====
Über den Quellcode sind keine Informationen bekannt. Die Tests richten sich lediglich nach den nach außenhin sichtbaren Schnittstellen, die ein Programm bereitstellt. Dieser Ansatz ist für alle {{Link|Teststufen}} geeignet.
Über den Quellcode sind keine Informationen bekannt. Die Tests richten sich lediglich nach den nach außen hin sichtbaren Schnittstellen, die ein Programm bereitstellt. Dieser Ansatz ist für alle {{Link|Teststufen}} geeignet.
==== Grey Box ====
==== Grey Box ====
Informationen über den Quellcode sind vorhanden, beispielsweise über '''Reverse Engineering'''. Zum Testen werden jedoch wie bei den {{Link|Black_Box|Black Box}} Tests, lediglich die nach außenhin sichtbaren Schnittstellen werden verwendet.
Informationen über den Quellcode sind vorhanden, beispielsweise über '''Reverse Engineering'''. Zum Testen werden jedoch wie bei den {{Link|Black_Box|Black Box}} Tests, lediglich die nach außen hin sichtbaren Schnittstellen werden verwendet.
'''Reverse Engineering''' beschreibt den Prozess bei dem kompilierter Code zu lesbarem Quellcode umgewandelt bzw. dekompiliert wird.


== Teststufen ==
== Teststufen ==
Testing kann in verschiedene Stufen unterteilt werden, die im folgenden Angeführt werden. Im folgenden von '''fein''' bis '''grob''':
Testing kann in verschiedene Stufen unterteilt werden die im folgenden Erläuter werden, von '''fein''' bis '''grob''':
==== Unit Test ====
==== Unit Test ====
Beschreibt das Testen auf Modul- oder Komponentenebene. Hierbei sollen abgrenzbare Teile eines Programmes getestet werden. Ein einfaches Beispiel dafür wäre das Testen von {{AL|Methode|Methoden}} einer {{AL|Klasse}}.
Beschreibt das Testen auf '''Modul- oder Komponentenebene'''. Hierbei sollen abgrenzbare Teile eines Programmes getestet werden. Dies wäre Beispielsweise das Testen von {{AL|Methoden}} einer {{AL|Klasse}}. Ein konkretes Beispiel ist {{Link|Beispiel_mit_JUnit|hier}} zu finden.


==== Integration Test ====
==== Integrationstest ====
Beim '''Integrations Test''' wird das zusammenspiel unterschiedlicher Komponenten miteinander getestet.
Beim '''Integrationstest''' wird das Zusammenspiel unterschiedlicher Komponenten miteinander getestet.


==== System Test ====
==== Systemtest ====
Beim '''System Test''' wird das gesamte System gegen alle Anforderungen getestet. Beim Test soll die Produktivumgebung des Kunden simuliert werden.
Beim '''Systemtest''' wird das gesamte System gegen alle Anforderungen getestet. Beim Test soll die Produktivumgebung des Kunden simuliert werden.


==== Abnahme Test ====
==== Abnahmetest ====
Der '''Abnahme Test''' ist das testen des gesamten Systems durch den Kunden.
Der '''Abnahmetest''' ist das Testen des gesamten Systems durch den Kunden.


= Continous Integration =
= CI/CD =
'''Continous Integration''' und '''Continous Delivery''' sind eng miteinander verknüpft. Durch '''Continous Integration''' wird ein höheres maß an Qualität erreicht, '''Continous Delivery''' bietet durch automatisiertes ausliefern der Software hohen Komfort und Zeitersparnis, da dies nicht mehr manuell gemacht werden muss.
== Continous Integration ==
'''Continous Integration''' beschreibt einen kontinuierlichen, automatisierten Prozess der im wesentlichen aus zwei Bestandteilen besteht:
'''Continous Integration''' beschreibt einen kontinuierlichen, automatisierten Prozess der im wesentlichen aus zwei Bestandteilen besteht:
# '''Dem kontinuierlichen Bauen der Software''' - Der Quellcode wird automatisch kompiliert. Dadurch können schwere Fehler, die das erstellen des Programmes verhindern direkt erkannt werden.
# '''Dem kontinuierlichen Bauen der Software''' - Der Quellcode wird automatisch kompiliert bzw. zusammengebaut. Dadurch können schwere Fehler, die das erstellen des Programmes verhindern direkt erkannt werden.
# '''Dem kontinuierlichen Testen der Software''' - Automatisierte Tests werden automatisch ausgeführt. Somit können effektiv '''Regressionen''' verhindert werden.
# '''Dem kontinuierlichen Testen der Software''' - Automatisierte Tests werden automatisch ausgeführt. Somit können effektiv '''Regressionen''' verhindert werden und die Softwarequalität bleibt hoch.
  '''Kontinuierlich''', immer fortwährend, läuft dauernd.  
  Unter '''kontinuierlich''' wird in diesem Kontext immer fortwährend oder andauernd verstanden. Natürlich läuft die '''Continous Integration''' nicht immer, sondern wird durch gewisse Ereignisse getriggert (gestartet).  
Dieser kontinuierliche Prozess wird sehr oft in Kombination mit {{Link|Git}} verwendet. Über {{Link|Git_Hooks|Git Hooks}}, kann der Integrationsprozess gestartet werden, z.B. wenn Änderungen auf einen gewissen {{Link|Branch}} gepusht werden.
Dieser kontinuierliche Prozess wird sehr oft in Kombination mit {{Link|Versionskontrolle_-_Git|Git}} verwendet. Über {{Link|Git_Hooks|Git Hooks}} kann der Integrationsprozess gestartet werden, z.B. wenn Änderungen auf einen gewissen {{Link|Branch}} '''gepusht''' werden. Anwendungen für '''Continous Integration''' verfügen oft über ein integriertes '''Git''' und bieten die Möglichkeit, '''Repositories''' anzulegen und zu verwalten.
<br>
<br>
Schlägt die '''Continous Integration''' nach einer Änderung fehl, so kann der Entwickler, für die Änderung verantwortliche Entwickler, beispielsweise via Email automatisch informiert werden.<br>
Schlägt die '''Continous Integration''' nach einer Änderung fehl, so kann der Entwickler der für diese Änderung verantwortlich ist, beispielsweise via Email automatisch informiert werden.<br>
'''Beispiele für Continous Integration Systeme:'''
==== Anwendungen ====
* Gitlab
* [https://gitlab.com Gitlab]
* Jenkins
* [https://jenkins.io Jenkins]
* Github
* [https://github.com Github]
 
== Continous Delivery ==
'''Continous Delivery''' beschreibt den automatisierten Prozess des '''Deployments'''. In diesem Kontext beschreibt '''Deployment''' das ausliefern der Software. Gleich wie {{Link|Continous_Integration|Continous Integration}} wird '''Continous Delivery''' durch gewisse Ereignisse getriggert, und steht meist in der Pipeline hinter der {{Link|Continous_Integration|Continous Integration}}.<br>
Zuerst wird die Software gebaut, dann ausgeliefert.<br>
Die Auslieferung der Software kann je nach Anwendung und Szenario variieren. Dies könnte Beispielsweise sein:
* Hochladen auf den Playstore/Appstore
* Versenden der Software per Email
* Hochladen der Software auf eine Webseite
* Eine neue Webseite zur Verfügung stellen
* Einen Docker Container mit einem neuen Image aktualisieren
Anwendungen die {{Link|Continous_Integration|Continous Integration}} anbieten, sind meist auch für '''Continous Delivery''' geeignet, deswegen sind die angeführten Vertreter die selben wie {{Link|Anwendungen|hier}}.
 
= Versionskontrolle - Git =
<cite>Git ist eine '''freie Software''' zur '''verteilten Versionsverwaltung''' von Dateien, die durch Linus Torvalds initiert wurde.<ref>https://de.wikipedia.org/wiki/Git</ref></cite>
'''Git''' wurde entwickelt um die '''Versionsverwaltung''' BitKeeper, die zuvor für den Quellcode des Linux Kernels verwendet wurde, zu ersetzen.<br>
Eine '''Versionsverwaltung''' von Dateien soll es ermöglichen die Änderungen, die an Dateien vorgenommen wurden, nachzuvollziehen. Weiters sollen diese unterschiedlichen Versionen abgerufen oder wiederhergestellt werden können. So kann bei Auftreten eines Fehlers, vor allem wenn {{Link|Continous_Integration|Continous Integration}} eingesetzt wird, sehr schnell festgestellt werden, wo der Fehler seinen Weg in den Quellcode gefunden hat.
'''Git''' ist eine '''verteilte Versionsverwaltung''' da das '''Repository''' selbst, nicht nur auf dem Server der dieses bereitstellt, sondern auch auf jedem Client der das '''Repository''' verwendet, vorhanden ist. Ein '''Repository''' kann somit, ohne große Probleme, durch den Client von einem {{Link|Remote}}, zu einem anderen {{Link|Remote}} gewechselt werden.
== Remote ==
Ein '''Git''' '''Repository''' kann mit einem oder mehreren '''Remotes''' verknüpft sein. Ein '''Remote''' ist ein Server der das '''Repository''' bereitstellt. Die Verbindung zum '''Remote''' vom Client aus, findet meist über die Protkolle {{AL|HTTP_Protokoll|HTTP}} oder '''SSH''' statt. Der initiale '''Remote''' wird als '''origin''' (Ursprung) bezeichnet. Weitere '''Remotes''' können mit beliebigen Namen hinzugefügt werden:
{{BML|code=
#Fügt ein weiteres Remote mit dem Namen 'backup' hinzu
git remote add backup https://gitlab.drlue.at/root/webservice.git
}}
Soll bei einer Aktion nun das '''Remote ''backup''''' angesprochen werden, so wird im Befehl anstatt '''origin''' '''''backup''''' verwendet:
{{BML|code=
#Holen der änderungen von 'origin' (vom Ursprung)
git pull origin main
#Holen der änderungen von 'backup'
git pull backup main
}}


= Continous Delivery =
== Arbeitsverzeichnis ==
Die Dateien im '''Git Repository''' entsprechen immer dem Stand des aktuell aktiven {{Link|Commit|Commits}} inklusive der lokalen Änderungen, welche noch zu keinem {{Link|Commit}} gehören. Wird beispielsweise mit dem Befehl {{Link|reset}} das '''Repository''' auf einen anderen {{Link|Commit}} zurückgesetzt, so ändern sich auch die Dateien im Verzeichnis.


= Versionskontrolle =
== Branch ==
Wird ein leeres '''Repository geklont''' so gibt es initial noch keinen '''Branch'''. Mit dem ersten {{Link|Commit}} wird der '''Master''' (jetzt '''Main''') '''Branch''' erzeugt. Wird ein neuer '''Branch''' erstellt, so zweigt die '''Codebasis''' vom aktuellen '''Branch''' ab. Der letzte {{Link|Commit}} des aktuellen '''Branches''' bildet somit die '''Basis''' des neuen '''Branches'''. Folgende Abbildung zeigt wie so eine Verzweigung aussehen kann:
[[Datei:Git-branch.png|mini|400px|ohne|Branches]]
In obiger Abbildung sind drei '''Branches''' ersichtlicht. Der '''Master''', '''Branch 1''' und '''Branch 2'''. Nach dem '''Branches''' abgezweigt wurden, so können sich diese natürlich (bzgl. ihrer {{Link|Commit|Commits}}) unterschiedlich weiterentwickeln. In der Grafik ist ersichtlich, dass alle '''Branches''' zwei unterschiedliche {{Link|Commit|Commits}} aufweisen. Sollen sie wieder zusammengeführt werden, so gibt es zwei Möglichkeiten:
* {{Link|Merge|Mergen}} - Verschmelzen
* {{Link|Rebase|Rebasen}} - Die Basis von der abgezweigt wurde, wird auf eine andere Basis geändert
Wird ein '''Repository''' (mit bestehenden '''Branches''') '''geklont''', so kann es initial in etwa so aussehen:
{{BML|code=
git branch -a
>>* master
>>  remotes/origin/HEAD -> origin/master
>>  remotes/origin/feature_login
>>  remotes/origin/feature_logout
>>  remotes/origin/master
}}
Alle Zeilen beginnend mit '''remotes''' beschreiben einen '''Branch''' der auf dem {{Link|Remote}}, also auf dem Server, vorhanden ist. Die anderen Zeilen sind '''Branches''' welche lokal verfügbar sind, in diesem Fall ist dies nur der '''master''' '''Branch'''. '''Branches''' beginnend mit '''remotes''' spiegeln den Stand des Servers bei der letzten Aktualisierung der '''Repository''' Informationen wieder. Mit {{BSL|git fetch}}, siehe {{Link|fetch}}, können diese {{Link|Remote}} '''Branches''' aktualisiert werden. Soll nun einer dieser '''Branches''' verwendet werden, so geschieht dies mit {{BSL|git checkout}}, siehe {{Link|checkout}}. Mit dem Befehl {{BSL|git checkout feature_login}} wird nun eine lokale Version des {{Link|Remote}} '''Branches''' erstellt:
{{BML|code=
git checkout feature_login
>>Branch 'feature_login' folgt nun Remote-Branch 'feature_login' von 'origin'.
>>Zu neuem Branch 'feature_login' gewechselt
git branch -a
>>* feature_login
>>  master
>>  remotes/origin/HEAD -> origin/master
>>  remotes/origin/feature_login
>>  remotes/origin/feature_logout
>>  remotes/origin/master
}}
Der lokale '''Branch''' ist nun in ''Zeile 5'' ersichtlich.
=== Feature Branch Workflow ===
[[Datei:Slide1.jpg|mini|600px|ohne|Feature Branch Modell <ref>https://dzone.com/articles/feature-branching-using-feature-flags-1</ref>]]
Der '''Feature Branch Workflow''' definiert, dass für jedes neue '''Feature''' ein eigener Branch erstellt wird<ref>https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow</ref>. Dieses '''Branching Modell''' begünstigt das verwenden von {{Link|Qualitätskontrolle Pull/Merge Requests|Pull Requests}}. In der Grafik ist weiters ein '''development''' und ein '''master''' {{Link|Branch}} ersichtlich. Dass zusätzlich ein '''Development {{Link|Branch}}''' verwendet wird, sind Elemente aus anderen Workflows wie z.B.: [https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow Gitflow]<br>
'''development'''<br>Wird für die stetige Weiterentwicklung verwendet.


== Pull/Merge Requests ==
'''master'''<br>Steht ein neuer '''Release''' an, so wird '''development''' in den '''master''' gemerged. Im '''development Branch''' kann nun weiterentwickelt werden. Etwaige Fehlerbehebungen oder Anpassungen fürs '''Release''' passieren im '''master Branch'''. Auch die Pflege und Fehlerbehebungen eines bestehenden '''Releases''' findet im '''master Branch''' statt.
 
== Commit ==
Ein '''Commit''' beschreibt eine abgeschlossene Einheit von Veränderungen. In einem '''Commit''' können neue, geänderte, oder gelöschte Dateien enthalten sein. Ein '''Commit''' enthält einen '''Schnappschuss''' aller geänderten Dateien zum vorhergehenden '''Commit'''. Ein '''Commit''' enthält zusätzlich zu den geänderten Dateien noch weitere Daten:
* '''Autor'''
* '''Datum'''
* '''Eindeutige [[Webservices_und_Client_Server_Konzepte#Caching|Hashsumme]]''' - Über diese kann ein '''Commit''' angesprochen bzw. referenziert werden
Im folgenden Beispiel ist das zurücksetzen der lokalen Dateien auf einen bestimmten '''Commit''' ersichtlich, weiters wird eine Beispielausgabe von {{BSL|git log}} gezeigt:
{{BML|code=
git log
>>...
>>commit 29b645bf9c1a4dd54dcb51026390231729287cc8
>>Author: Luke <luke@luke.at>
>>Date:  Tue Feb 2 08:44:33 2021 +0100
>>
>>    - Fastlane update.
>>...
//Zurücksetzen des lokalen Codes auf obigen commit
git reset --hard 29b645bf9c1a4dd54dcb51026390231729287cc8
}}
 
== Wichtige Befehle ==
Folgend sollen einige wichtige '''Git''' Befehle erläutert werden. Diese beginnen immer mit {{BSL|git}} und sind somit eigentlich nur '''Parameter''' des Programmes {{BSL|git}}.
 
==== -h ====
Mit {{BSL|git -h}} erhält man eine Hilfeseite. Dies gilt auch für jeden '''Parameter''', z.B.: {{BSL| git commit -h}},{{BSL| git branch -h}},{{BSL| git clone -h}},...
 
==== clone ====
Um ein '''Repository''' zu '''clonen''' wird der Befehl {{BSL|git clone}}, gefolgt von der '''URL''' verwendet. Optional kann auch ein Name für den lokalen Ordner des '''Repositories''' angegeben werden, wird kein Name angegeben, so wird der Ordner von der '''URL''' abgeleitet. Folgender Befehl wird das '''Repository''' in den Ordner {{BSL|webservice}} '''clonen''':
{{BML|code=
git clone https://gitlab.drlue.at/root/webservice.git
}}
Folgend soll das '''clonen''' des '''Repositories''' in den Ordner {{BSL|backup}} erfolgen:
{{BML|code=
git clone https://gitlab.drlue.at/root/webservice.git backup
}}
 
==== add ====
Um gelöschte, geänderte oder neue Dateien einem {{Link|Commit}} hinzuzufügen, müssen diese zuerst '''gestaged''', also der '''Staging Area''' hinzugefügt, werden. Dies geschieht mit dem Befehl {{BSL|git add}}. Den {{Link|Commit}} kann man sich wie ein Packet vorstellen, zuerst werden Dinge mit {{BSL|git add}} hineingegeben, ist alles gewünschte drinnen, wird das Packet mit {{BSL|git commit}} verschlossen. {{BSL|git add}} nimmt als Parameter reguläre Ausdrücke (*.txt), mehrere Dateinamen (a.txt b.txt c.txt) oder auch einen '''.''' (alle Änderungen hinzufügen).
 
==== commit ====
Mit dem Befehl {{BSL|git commit}} werden alle Änderungen in der '''Staging Area''' in einen {{Link|Commit}} verpackt und mit einem Text versehen.
 
==== checkout ====
Der Befehl {{BSL|git checkout}} hat mehrere Funktionen:
# Änderungen von Dateien können rückgängig gemacht werden. Dies betrifft nur die Änderungen die seit dem letzten {{Link|Commit}} passiert sind. Änderungen die sich bereits in der '''Staging Area''' befinden werden ignoriert. Gleich wie bei {{Link|add}}, können weitere Parameter, ein regulärer Ausdruck, mehrere Dateinamen oder ein '''.''' sein.
# Neue {{Link|Branch|Branches}} können mittels {{BSL|git checkout -b [Branchname]}} erstellt werden, oder es kann zu einem anderen {{Link|Branch}} mit {{BSL|git checkout [Branchname]}} gewechselt werden.
 
==== fetch ====
{{BSL|git fetch}}, optional mit nachgestelltem {{AL|Remote}} Parameter, holt alle Änderungen vom Server und aktualisiert damit die {{Link|Remote}} {{Link|Branch|Branches}}, also die Kopie der {{Link|Branch|Branches}} auf dem Server. Die lokal verwendeten {{Link|Branch|Branches}} werden jedoch '''nicht''' verändert.
 
==== push ====
Mittels {{BSL|git push}} können lokale {{Link|Commit|Commits}} an einen {{Link|Branch}} auf einem {{Link|Remote}} gesendet werden.<br>
Angenommen es wurden {{Link|Commit|Commits}} im Branch '''feature_login''' gemacht, und diese {{Link|Commit|Commits}} sollen an den {{Link|Remote}} '''origin''' gesendet werden:
{{BML|code=
git push origin feature_login
}}
Die Änderungen sollen weiters an den {{Link|Remote}} '''backup''' gesendet werden:
{{BML|code=
git push backup feature_login
}}
Existiert der {{Link|Branch}} noch nicht auf dem {{Link|Remote}}, so wird dieser erstellt.
==== branch ====
Der Befehl {{BSL|git branch -a}} zeigt alle {{Link|Branch|Branches}} in einem Repository an. Die lokalen und die {{Link|Remote}} {{Link|Branch|Branches}}. {{BSL|git branch}} zeigt '''nur''' die lokalen {{Link|Branch|Branches}} an. Mit diesem Befehl können weiters lokale {{Link|Branch|Branches}} erstellt oder gelöscht werden.
 
==== pull ====
Mit dem Befehl {{BSL|git pull}} werden Änderungen von einem {{Link|Remote}} {{Link|Branch}} in den aktiven lokalen {{Link|Branch}} geholt. Kann der lokale {{Link|Branch}} nicht vorgespult werden, so findet ein {{Link|Merge_2|Merge}} statt. Folgendes Beispiel illustriert das holen vom {{Link|Remote}} '''origin''' '''feature_login''' {{Link|Branch}}:
{{BML|code=
git pull origin feature_login
}}
Im folgenden werden die neuen {{Link|Commit|Commits}} vom {{Link|Remote}} '''backup''' geholt:
{{BML|code=
git pull backup feature_login
}}
 
==== merge ====
Siehe {{Link|Merge_2|Merge}}.
==== rebase ====
Siehe {{Link|Rebase_2|Rebase}}.
==== log ====
Der '''Befehl''' {{BSL|git log}} zeigt die {{Link|Commit}} History an. Also die Geschichte der {{Link|Commit|Commits}} sortiert von neu bis alt.
 
==== status ====
Der Befehl {{BSL|git status}}, zeigt den aktuellen Status des Arbeitsverzeichnisses an:
* Welche Dateien wurden gelöscht oder verändert
* Welche Dateien sind neu hinzugefügt worden, also nicht nicht mit dem '''Git Repository''' verbunden
* Welche Dateien wurden bereits '''getstaged''' und befinden sich somit in der '''Staging Area''', siehe {{Link|add}}
* In welchem {{BSL|Branch}} befindet man sich gerade
 
==== reset ====
{{BSL|git reset}} hat mehrere Funktionen:
# Wird gleich wie bei {{Link|add}} oder {{Link|checkout}} ein regulärer Ausdruck, mehrere Dateinamen, oder '''.''' als Parameter verwendet, so werden Dateien aus der '''Staging Area''' (siehe {{Link|add}}) entfernt.
# Wird ein {{Link|Commit}} '''Hash''' angegeben, so wird der aktuelle lokale {{Link|Branch}} auf diesen {{Link|Commit}} zurückgesetzt.
 
==== diff ====
{{BSL|git diff}} ohne zusätzliche Parameter listet alle Änderungen im {{Link|Arbeitsverzeichnis}} auf. Anstatt {{BSL|git diff}} kann auch {{BSL|git difftool}} verwendet werden, dann wird das aktive '''Difftool''' gestartet. Weiters kann {{BSL|git diff}} mehrere Parameter erhalten:
* Eine spezielle Datei, dann werden die aktuellen Änderungen dieser Datei angezeigt (sofern nicht bereits in '''Staging Area''')
* Zwei {{Link|Commit}} '''Hashes''', es werden die Unterschiede zwischen den {{Link|Commit|Commits}} angezeigt
* Zwei {{Link|Branch|Branches}}, es werden die Unterschiede zwischen den {{Link|Branch|Branches}} angezeigt
* HEAD HEAD~11, Unterschied zwischen dem obersten {{Link|Commit}} (HEAD) und 11 {{Link|Commit|Commits}} (HEAD~11) zurück.
 
== Hooks ==
Mittels '''Git Hooks''' kann auf gewisse '''Git''' Aktionen gehorcht werden.
=== Client-Side Hooks ===
Diese ermöglichen das Horchen auf '''Repository''' Aktionen auf dem Rechner des Clients.
Z.b.:<br>Mit dem '''Pre Commit Hook''', könnte bevor Code '''commited''' wird, ein '''Codeanalyse Tool''' gestartet werden. Nur wenn dieses auch erfolgreich durchgelaufen ist, wird der '''Commit''' erstellt.
 
=== Server-Side Hooks ===
Diese ermöglichen das Horchen auf '''Repository''' Aktionen auf dem Server der das '''Repository''' hostet.
Z.b.:<br>Mit dem '''Post Receive Hook''', also wenn ein neuer '''Push''' auf das '''Repository''' erfolgt ist, kann die {{Link|Continous_Integration|Continous Integration}} und {{Link|Continous_Delivery|Continous Delivery}} gestartet werden.
 
== Branches zusammenführen ==
Wenn {{Link|Branch|Branches}} immer weiter auseinandergehen, so wird es dann auch irgendwann mal Zeit, diese wieder zusammenzuführen. Dafür gibt es zwei Varianten, die im folgenden erklärt werden sollen.
=== Merge ===
{{BSL|git merge}} ist im Gegensatz zu {{BSL|git rebase}} eine '''nicht inversive Methode''' um {{Link|Branch|Branches}} zusammenzuführen. Die {{Link|Commit}} '''History''' wird nicht verändert. {{Link|Branch|Branches}} werden mit einem einzelnen '''Merge Commit''' angeglichen. Bildet ein {{Link|Branch}} die Basis eines Anderen, so kann ein '''Fast Forward''' erfolgen, und es gibt keinen '''Merge Commit'''. Durch das verwenden von '''Merge''' erhält man eine etwas komplexere {{Link|Commit}} '''History''' mit vielen Ausbuchtungen. Der Vorteil ist, dass alle Änderungen, die durch den '''Merge''' gemacht wurden, nachvollzogen werden können. Folgende Grafik illustriert das '''mergen''' von Änderungen des '''Feature''' {{Link|Branch}} in den '''Master''' {{Link|Branch}}:
[[Datei:Merge-without-rebase.svg|400px|mini|ohne|Merge <ref name="git_graphics">https://gcapes.github.io/git-course/10-rebasing/</ref>]]
Folgendes Beispiel soll das das '''Mergen''' vom '''Feature''' {{Link|Branch}} in den '''Master''' {{Link|Branch}} wie in der Abbildung veranschaulichen:
{{BML|code=
#Wir befinden uns aktuell im feature Branch
#Wechsel in den master Branch
git checkout master
#Merge starten
git merge feature
#Treten Konflikte auf, so müssen diese über
git mergetool
#und ein anschließendes
git merge --continue
#behoben werden
#Nun kann der Push auf den Remote master Branch erfolgen
git push origin master -f
}}
=== Rebase ===
{{BSL|git rebase}} eignet sich nur wenn an einem eigenen {{Link|Branch}} gearbeitet wird und die Änderungen von einem anderen {{Link|Branch}} geholt werden sollen, da die {{Link|Commit}} History umgeschrieben wird. Die {{Link|Commit|Commits}} des anderen {{Link|Branch|Branches}} werden unter die eigenen {{Link|Commit|Commits}} geschoben, die Basis des {{Link|Branch|Branches}} wird geändert. Dieser Vorgang verändert weiters jeden {{Link|Commit}} über der alten Basis. Durch '''Rebasing''' erhält man eine sehr geradlinige {{Link|Commit}} '''History''':
[[Datei:Git-rebase.svg|mini|400px|none|Rebase<ref name="git_graphics"/>]]
Soll nach einem '''Rebase''' erneut auf den eigenen {{Link|Branch}} '''gepushed''' werden so ist ein {{BSL|git push -f}}, ein '''force push''' nötig, da der lokale {{Link|Branch}} nicht mehr mit dem {{Link|Branch}} auf dem {{Link|Remote}} kompatibel ist. Folgendes Beispiel soll das das '''Rebasen''' vom '''feature''' {{Link|Branch}} mit dem '''Master''' {{Link|Branch}} wie in der Abbildung veranschaulichen:
{{BML|code=
#Wir befinden uns aktuell im feature Branch
#Wechsel in den master Branch
git checkout master
#Änderungen holen
git pull origin master
#Zurückwechseln in Feature Branch
git checkout feature
#Rebase starten
git rebase master
#Treten Konflikte auf, so müssen diese über
git mergetool
#und ein anschließendes
git rebase --continue
#behoben werden
#Nun kann der Push auf den Remote feature Branch mit force push erfolgen
git push origin feature -f
}}
Hier sei Angemerkt, dass die Konfliktlösung theoretisch für jeden {{Link|Commit}} über der Basis (in der Grafik sind es zwei) durchgeführt werden muss (''Zeile 11-13''), wenn dies nicht automatisch erfolgen kann.
=== Rebase/Merge ===
Eine Kombination aus beidem ist eine sehr gebräuchliche Vorgehensweise. Wird an einem '''Feature''' gearbeitet, so erfolgt zuerst ein '''Rebase''' mit dem {{Link|Branch|Zielbranch}} und dann wird in den {{Link|Branch|Zielbranch}} '''gemerged'''. Dadurch bleibt die '''History''' geradlinig.
Die Information welche {{Link|Commit|Commits}} im '''Feature Branch''' erzeugt wurden geht jedoch verloren, wenn kein '''Merge Commit erzwungen wird'''.
<gallery widths="300" heights="234">
Datei:Forked-history.svg|500px|1) Ausgangssituation<ref name="git_graphics"/>
Datei:Git-rebase.svg|2) '''Feature Branch''' wird mit '''Master Branch''' '''rebased''' (ACHTUNG 3ter Knödel bei Feature Branch fehlt)<ref name="git_graphics"/>
Datei:Rebase-then-merge.svg|3) '''Merge''' von '''Feature Branch''' in den '''Master Branch'''<ref name="git_graphics"/>
</gallery>
 
== Qualitätskontrolle Pull/Merge Requests ==
Für eine leichtere Lesbarkeit wird im Folgenden nur noch von '''Merge Request''' gesprochen. Je nach verwendeter '''Git Platform''' wird ein anderer Begriff dafür verwendet:
{| class="wikitable"
|-
! System !! Bezeichnung
|-
| [https://github.com Github] || Pull Request
|-
| [https://bitbucket.org Bitbucket] || Pull Request
|-
| [https://gitlab.com Gitlab] || Merge Request
|}
In größeren Softwareprojekten erfolgt die Qualitätskontrolle sehr oft über '''Merge Requests'''. Wird ein neues '''Feature''' entwickelt, so erfolgt eine Abzweigung vom aktuellen Entwicklungsbranch '''development''' {{Link|Branch}} (natürlich ist auch das Abzweigen von anderen {{Link|Branch|Branches}} möglich). Der Entwickler programmiert, {{Link|Commit_2|commitet}} und {{Link|Push|pusht}}, solange an dem '''Feature''' gearbeitet wird, nur im eigenen {{Link|Branch}}. Von Zeit zu Zeit kann es nötig sein, dass der eigene {{Link|Branch}} mit den Änderungen aus dem '''development''' {{Link|Branch}} aktualisiert werden muss, dies kann über {{Link|Merge|merge}} oder {{Link|Rebase|rebase}} stattfinden. Ist das '''Feature''' abgeschlossen, so startet der '''Merge Request'''. Dieser wird meist über die Webseite der verwendeten '''Git Platform''' gestartet. Ein anderer Entwickler erhält den '''Merge Request''' und übernimmt die Qualitätskontrolle, den '''Peer Review'''. Dabei wird die Funktionalität, Coding Conventions, usw. geprüft. Gibt es nichts zu beanstanden, so erfolgt, der eigentliche {{Link|Merge_2|Merge}} in den gewünschten {{Link|Branch}}. Ansonsten wird der '''Merge Request''' für weitere Verbesserungen zurückgewiesen und muss dann erneut gestellt werden.
 
== Beispiel ==
Im folgenden Beispiel soll veranschaulicht werden wie zwei {{Link|Branch|Branches}} zusammengeführt werden:
'''Repository'''<br>'''https://gitlab.drlue.at/root/webservice.git'''
 
'''Aufgabe'''<br>'''{{Link|Branch}} feature_login''' soll in '''{{Link|Branch}} development''' gemerged und anschließend soll '''development''' gepusht werden.
{{BML|code=
#Clonen des Repositories
git clone https://gitlab.drlue.at/root/webservice.git
#Wechseln in das Eigentliche Repository
cd webservice
#Initial befinden wir uns im Main Branch und wechseln auf feature_login.
#Dies ist nötig, da nach dem clonen noch keine lokale Version von feature_login vorhanden ist, nur die remote Version
git checkout feature_login
#Wechseln auf development, damit auch dieser Branch lokal verfügbar ist
git checkout development
#Merge durchführen
git merge feature_login
#Treten Konflikte auf, so müssen diese über
git mergetool
#und ein anschließendes
git merge --continue
#behoben werden
#Nun kann der push erfolgen
git push origin development
}}

Aktuelle Version vom 23. Februar 2021, 19:56 Uhr

Im folgenden sollen einige Maßnahmen erläutert werden, welche der Erhöhung der Softwarequalität dienen.

Testing

Softwaretesting beschreibt den Prozess des verifizierens einer Anwendung, oder Teile davon, gegenüber einer gewissen Spezifikation, oder gegen ein erwartetes Verhalten. Durch Testing kann ein hohes Maß an Softwarequalität gewährleistet werden, natürlich nur wenn Tests gut spezifiert und auch ausgeführt werden. Gerade im Bereich des automatisierten Testens kann durch eine hohe Testabdeckung des Quellcodes, Regression, verhindert werden.

Eine hohe Testabdeckung beschreibt, wieviel Code durch Testfälle auch wirklich erreicht wird. Werden alle Verzweigungen in Methoden erreicht? Werden überhaupt alle Methoden getestet?
Unter Regression wird eine Verschlechterung des Quellcodes verstanden. Im Laufe des Entwicklungsprozesses funktioniert Code, der zuvor funktionert hat, auf einmal nicht mehr.

Beispiel mit JUnit

Anhand von folgendem Beispiel soll die Testabdeckung einer Java Klasse mit dem Testframework JUnit veranschaulicht werden:

public class DateUtils {
  public String getDayOfWeekName(int day) {
    if(day == 1) {
      return "Montag";
    } else if(day == 2) {
      return "Dienstag";
    } else if(day == 3) {
      return "Mittwoch";
    } else if(day == 4) {
      return "Donnerstag";
    } else if(day == 5) {
      return "Freitag";
    } else if(day == 6) {
      return "Samstag";
    } else if(day == 7) {
      return "Sonntag";
    }
    return "Unbekannt";
  }
}

Das Programm verfügt über eine Methode, welche 8 verschieden Verzweigungen hat.
Testabdeckung von 50%:

@Test
public void testDayOfWeekName() {
  DateUtils utils = new DateUtils();
  assertEquals("Montag", utils.getDayOfWeekName(1));
  assertEquals("Dienstag", utils.getDayOfWeekName(2));
  assertEquals("Mittwoch", utils.getDayOfWeekName(3));
  assertEquals("Donnerstag", utils.getDayOfWeekName(4));
}

Testabdeckung von 100%:

@Test
public void testDayOfWeekName() {
  DateUtils utils = new DateUtils();
  assertEquals("Montag", utils.getDayOfWeekName(1));
  assertEquals("Dienstag", utils.getDayOfWeekName(2));
  assertEquals("Mittwoch", utils.getDayOfWeekName(3));
  assertEquals("Donnerstag", utils.getDayOfWeekName(4));
  assertEquals("Freitag", utils.getDayOfWeekName(5));
  assertEquals("Samstag", utils.getDayOfWeekName(6));
  assertEquals("Sonntag", utils.getDayOfWeekName(7));
  assertEquals("Unbekannt", utils.getDayOfWeekName(8));
}

Testabdeckung von 0%:

@Test
public void testDayOfWeekName() {
  DateUtils utils = new DateUtils();
}

Testansätze

Es gibt verschiedene Ansätze wie Tests entwickelt und ausgeführt werden können. Im folgenden sollen die Box Ansätze kurz erläutert werden.

White Box

Der zu testende Quellcode ist bekannt, das Testdesign kann sich am Code orientieren um alle Verzweigungen zu erreichen. Weiters können auch Teile des Codes, welche nicht nach außen hin Sichtbar sind, getestet werden. Unit Tests fallen normalerweise in diese Kategorie. Durch White Box Tests kann eine hohe Testabdeckung des Codes erreicht werden.

Black Box

Über den Quellcode sind keine Informationen bekannt. Die Tests richten sich lediglich nach den nach außen hin sichtbaren Schnittstellen, die ein Programm bereitstellt. Dieser Ansatz ist für alle Teststufen geeignet.

Grey Box

Informationen über den Quellcode sind vorhanden, beispielsweise über Reverse Engineering. Zum Testen werden jedoch wie bei den Black Box Tests, lediglich die nach außen hin sichtbaren Schnittstellen werden verwendet.

Reverse Engineering beschreibt den Prozess bei dem kompilierter Code zu lesbarem Quellcode umgewandelt bzw. dekompiliert wird.

Teststufen

Testing kann in verschiedene Stufen unterteilt werden die im folgenden Erläuter werden, von fein bis grob:

Unit Test

Beschreibt das Testen auf Modul- oder Komponentenebene. Hierbei sollen abgrenzbare Teile eines Programmes getestet werden. Dies wäre Beispielsweise das Testen von Methoden einer Klasse. Ein konkretes Beispiel ist hier zu finden.

Integrationstest

Beim Integrationstest wird das Zusammenspiel unterschiedlicher Komponenten miteinander getestet.

Systemtest

Beim Systemtest wird das gesamte System gegen alle Anforderungen getestet. Beim Test soll die Produktivumgebung des Kunden simuliert werden.

Abnahmetest

Der Abnahmetest ist das Testen des gesamten Systems durch den Kunden.

CI/CD

Continous Integration und Continous Delivery sind eng miteinander verknüpft. Durch Continous Integration wird ein höheres maß an Qualität erreicht, Continous Delivery bietet durch automatisiertes ausliefern der Software hohen Komfort und Zeitersparnis, da dies nicht mehr manuell gemacht werden muss.

Continous Integration

Continous Integration beschreibt einen kontinuierlichen, automatisierten Prozess der im wesentlichen aus zwei Bestandteilen besteht:

  1. Dem kontinuierlichen Bauen der Software - Der Quellcode wird automatisch kompiliert bzw. zusammengebaut. Dadurch können schwere Fehler, die das erstellen des Programmes verhindern direkt erkannt werden.
  2. Dem kontinuierlichen Testen der Software - Automatisierte Tests werden automatisch ausgeführt. Somit können effektiv Regressionen verhindert werden und die Softwarequalität bleibt hoch.
Unter kontinuierlich wird in diesem Kontext immer fortwährend oder andauernd verstanden. Natürlich läuft die Continous Integration nicht immer, sondern wird durch gewisse Ereignisse getriggert (gestartet). 

Dieser kontinuierliche Prozess wird sehr oft in Kombination mit Git verwendet. Über Git Hooks kann der Integrationsprozess gestartet werden, z.B. wenn Änderungen auf einen gewissen Branch gepusht werden. Anwendungen für Continous Integration verfügen oft über ein integriertes Git und bieten die Möglichkeit, Repositories anzulegen und zu verwalten.
Schlägt die Continous Integration nach einer Änderung fehl, so kann der Entwickler der für diese Änderung verantwortlich ist, beispielsweise via Email automatisch informiert werden.

Anwendungen

Continous Delivery

Continous Delivery beschreibt den automatisierten Prozess des Deployments. In diesem Kontext beschreibt Deployment das ausliefern der Software. Gleich wie Continous Integration wird Continous Delivery durch gewisse Ereignisse getriggert, und steht meist in der Pipeline hinter der Continous Integration.
Zuerst wird die Software gebaut, dann ausgeliefert.
Die Auslieferung der Software kann je nach Anwendung und Szenario variieren. Dies könnte Beispielsweise sein:

  • Hochladen auf den Playstore/Appstore
  • Versenden der Software per Email
  • Hochladen der Software auf eine Webseite
  • Eine neue Webseite zur Verfügung stellen
  • Einen Docker Container mit einem neuen Image aktualisieren

Anwendungen die Continous Integration anbieten, sind meist auch für Continous Delivery geeignet, deswegen sind die angeführten Vertreter die selben wie hier.

Versionskontrolle - Git

Git ist eine freie Software zur verteilten Versionsverwaltung von Dateien, die durch Linus Torvalds initiert wurde.[1] Git wurde entwickelt um die Versionsverwaltung BitKeeper, die zuvor für den Quellcode des Linux Kernels verwendet wurde, zu ersetzen.

Eine Versionsverwaltung von Dateien soll es ermöglichen die Änderungen, die an Dateien vorgenommen wurden, nachzuvollziehen. Weiters sollen diese unterschiedlichen Versionen abgerufen oder wiederhergestellt werden können. So kann bei Auftreten eines Fehlers, vor allem wenn Continous Integration eingesetzt wird, sehr schnell festgestellt werden, wo der Fehler seinen Weg in den Quellcode gefunden hat.

Git ist eine verteilte Versionsverwaltung da das Repository selbst, nicht nur auf dem Server der dieses bereitstellt, sondern auch auf jedem Client der das Repository verwendet, vorhanden ist. Ein Repository kann somit, ohne große Probleme, durch den Client von einem Remote, zu einem anderen Remote gewechselt werden.

Remote

Ein Git Repository kann mit einem oder mehreren Remotes verknüpft sein. Ein Remote ist ein Server der das Repository bereitstellt. Die Verbindung zum Remote vom Client aus, findet meist über die Protkolle oder SSH statt. Der initiale Remote wird als origin (Ursprung) bezeichnet. Weitere Remotes können mit beliebigen Namen hinzugefügt werden:

#Fügt ein weiteres Remote mit dem Namen 'backup' hinzu
git remote add backup https://gitlab.drlue.at/root/webservice.git

Soll bei einer Aktion nun das Remote backup angesprochen werden, so wird im Befehl anstatt origin backup verwendet:

#Holen der änderungen von 'origin' (vom Ursprung)
git pull origin main
#Holen der änderungen von 'backup'
git pull backup main

Arbeitsverzeichnis

Die Dateien im Git Repository entsprechen immer dem Stand des aktuell aktiven Commits inklusive der lokalen Änderungen, welche noch zu keinem Commit gehören. Wird beispielsweise mit dem Befehl reset das Repository auf einen anderen Commit zurückgesetzt, so ändern sich auch die Dateien im Verzeichnis.

Branch

Wird ein leeres Repository geklont so gibt es initial noch keinen Branch. Mit dem ersten Commit wird der Master (jetzt Main) Branch erzeugt. Wird ein neuer Branch erstellt, so zweigt die Codebasis vom aktuellen Branch ab. Der letzte Commit des aktuellen Branches bildet somit die Basis des neuen Branches. Folgende Abbildung zeigt wie so eine Verzweigung aussehen kann:

Branches

In obiger Abbildung sind drei Branches ersichtlicht. Der Master, Branch 1 und Branch 2. Nach dem Branches abgezweigt wurden, so können sich diese natürlich (bzgl. ihrer Commits) unterschiedlich weiterentwickeln. In der Grafik ist ersichtlich, dass alle Branches zwei unterschiedliche Commits aufweisen. Sollen sie wieder zusammengeführt werden, so gibt es zwei Möglichkeiten:

  • Mergen - Verschmelzen
  • Rebasen - Die Basis von der abgezweigt wurde, wird auf eine andere Basis geändert

Wird ein Repository (mit bestehenden Branches) geklont, so kann es initial in etwa so aussehen:

git branch -a
>>* master
>>  remotes/origin/HEAD -> origin/master
>>  remotes/origin/feature_login
>>  remotes/origin/feature_logout
>>  remotes/origin/master

Alle Zeilen beginnend mit remotes beschreiben einen Branch der auf dem Remote, also auf dem Server, vorhanden ist. Die anderen Zeilen sind Branches welche lokal verfügbar sind, in diesem Fall ist dies nur der master Branch. Branches beginnend mit remotes spiegeln den Stand des Servers bei der letzten Aktualisierung der Repository Informationen wieder. Mit git fetch, siehe fetch, können diese Remote Branches aktualisiert werden. Soll nun einer dieser Branches verwendet werden, so geschieht dies mit git checkout, siehe checkout. Mit dem Befehl git checkout feature_login wird nun eine lokale Version des Remote Branches erstellt:

git checkout feature_login
>>Branch 'feature_login' folgt nun Remote-Branch 'feature_login' von 'origin'.
>>Zu neuem Branch 'feature_login' gewechselt
git branch -a
>>* feature_login
>>  master
>>  remotes/origin/HEAD -> origin/master
>>  remotes/origin/feature_login
>>  remotes/origin/feature_logout
>>  remotes/origin/master

Der lokale Branch ist nun in Zeile 5 ersichtlich.

Feature Branch Workflow

Feature Branch Modell [2]

Der Feature Branch Workflow definiert, dass für jedes neue Feature ein eigener Branch erstellt wird[3]. Dieses Branching Modell begünstigt das verwenden von Pull Requests. In der Grafik ist weiters ein development und ein master Branch ersichtlich. Dass zusätzlich ein Development Branch verwendet wird, sind Elemente aus anderen Workflows wie z.B.: Gitflow

development
Wird für die stetige Weiterentwicklung verwendet.
master
Steht ein neuer Release an, so wird development in den master gemerged. Im development Branch kann nun weiterentwickelt werden. Etwaige Fehlerbehebungen oder Anpassungen fürs Release passieren im master Branch. Auch die Pflege und Fehlerbehebungen eines bestehenden Releases findet im master Branch statt.

Commit

Ein Commit beschreibt eine abgeschlossene Einheit von Veränderungen. In einem Commit können neue, geänderte, oder gelöschte Dateien enthalten sein. Ein Commit enthält einen Schnappschuss aller geänderten Dateien zum vorhergehenden Commit. Ein Commit enthält zusätzlich zu den geänderten Dateien noch weitere Daten:

  • Autor
  • Datum
  • Eindeutige Hashsumme - Über diese kann ein Commit angesprochen bzw. referenziert werden

Im folgenden Beispiel ist das zurücksetzen der lokalen Dateien auf einen bestimmten Commit ersichtlich, weiters wird eine Beispielausgabe von git log gezeigt:

git log
>>...
>>commit 29b645bf9c1a4dd54dcb51026390231729287cc8
>>Author: Luke <luke@luke.at>
>>Date:   Tue Feb 2 08:44:33 2021 +0100
>>
>>    - Fastlane update.
>>...
//Zurücksetzen des lokalen Codes auf obigen commit
git reset --hard 29b645bf9c1a4dd54dcb51026390231729287cc8

Wichtige Befehle

Folgend sollen einige wichtige Git Befehle erläutert werden. Diese beginnen immer mit git und sind somit eigentlich nur Parameter des Programmes git.

-h

Mit git -h erhält man eine Hilfeseite. Dies gilt auch für jeden Parameter, z.B.: git commit -h,git branch -h,git clone -h,...

clone

Um ein Repository zu clonen wird der Befehl git clone, gefolgt von der URL verwendet. Optional kann auch ein Name für den lokalen Ordner des Repositories angegeben werden, wird kein Name angegeben, so wird der Ordner von der URL abgeleitet. Folgender Befehl wird das Repository in den Ordner webservice clonen:

git clone https://gitlab.drlue.at/root/webservice.git

Folgend soll das clonen des Repositories in den Ordner backup erfolgen:

git clone https://gitlab.drlue.at/root/webservice.git backup

add

Um gelöschte, geänderte oder neue Dateien einem Commit hinzuzufügen, müssen diese zuerst gestaged, also der Staging Area hinzugefügt, werden. Dies geschieht mit dem Befehl git add. Den Commit kann man sich wie ein Packet vorstellen, zuerst werden Dinge mit git add hineingegeben, ist alles gewünschte drinnen, wird das Packet mit git commit verschlossen. git add nimmt als Parameter reguläre Ausdrücke (*.txt), mehrere Dateinamen (a.txt b.txt c.txt) oder auch einen . (alle Änderungen hinzufügen).

commit

Mit dem Befehl git commit werden alle Änderungen in der Staging Area in einen Commit verpackt und mit einem Text versehen.

checkout

Der Befehl git checkout hat mehrere Funktionen:

  1. Änderungen von Dateien können rückgängig gemacht werden. Dies betrifft nur die Änderungen die seit dem letzten Commit passiert sind. Änderungen die sich bereits in der Staging Area befinden werden ignoriert. Gleich wie bei add, können weitere Parameter, ein regulärer Ausdruck, mehrere Dateinamen oder ein . sein.
  2. Neue Branches können mittels git checkout -b [Branchname] erstellt werden, oder es kann zu einem anderen Branch mit git checkout [Branchname] gewechselt werden.

fetch

git fetch, optional mit nachgestelltem Parameter, holt alle Änderungen vom Server und aktualisiert damit die Remote Branches, also die Kopie der Branches auf dem Server. Die lokal verwendeten Branches werden jedoch nicht verändert.

push

Mittels git push können lokale Commits an einen Branch auf einem Remote gesendet werden.
Angenommen es wurden Commits im Branch feature_login gemacht, und diese Commits sollen an den Remote origin gesendet werden:

git push origin feature_login

Die Änderungen sollen weiters an den Remote backup gesendet werden:

git push backup feature_login

Existiert der Branch noch nicht auf dem Remote, so wird dieser erstellt.

branch

Der Befehl git branch -a zeigt alle Branches in einem Repository an. Die lokalen und die Remote Branches. git branch zeigt nur die lokalen Branches an. Mit diesem Befehl können weiters lokale Branches erstellt oder gelöscht werden.

pull

Mit dem Befehl git pull werden Änderungen von einem Remote Branch in den aktiven lokalen Branch geholt. Kann der lokale Branch nicht vorgespult werden, so findet ein Merge statt. Folgendes Beispiel illustriert das holen vom Remote origin feature_login Branch:

git pull origin feature_login

Im folgenden werden die neuen Commits vom Remote backup geholt:

git pull backup feature_login

merge

Siehe Merge.

rebase

Siehe Rebase.

log

Der Befehl git log zeigt die Commit History an. Also die Geschichte der Commits sortiert von neu bis alt.

status

Der Befehl git status, zeigt den aktuellen Status des Arbeitsverzeichnisses an:

  • Welche Dateien wurden gelöscht oder verändert
  • Welche Dateien sind neu hinzugefügt worden, also nicht nicht mit dem Git Repository verbunden
  • Welche Dateien wurden bereits getstaged und befinden sich somit in der Staging Area, siehe add
  • In welchem Branch befindet man sich gerade

reset

git reset hat mehrere Funktionen:

  1. Wird gleich wie bei add oder checkout ein regulärer Ausdruck, mehrere Dateinamen, oder . als Parameter verwendet, so werden Dateien aus der Staging Area (siehe add) entfernt.
  2. Wird ein Commit Hash angegeben, so wird der aktuelle lokale Branch auf diesen Commit zurückgesetzt.

diff

git diff ohne zusätzliche Parameter listet alle Änderungen im Arbeitsverzeichnis auf. Anstatt git diff kann auch git difftool verwendet werden, dann wird das aktive Difftool gestartet. Weiters kann git diff mehrere Parameter erhalten:

  • Eine spezielle Datei, dann werden die aktuellen Änderungen dieser Datei angezeigt (sofern nicht bereits in Staging Area)
  • Zwei Commit Hashes, es werden die Unterschiede zwischen den Commits angezeigt
  • Zwei Branches, es werden die Unterschiede zwischen den Branches angezeigt
  • HEAD HEAD~11, Unterschied zwischen dem obersten Commit (HEAD) und 11 Commits (HEAD~11) zurück.

Hooks

Mittels Git Hooks kann auf gewisse Git Aktionen gehorcht werden.

Client-Side Hooks

Diese ermöglichen das Horchen auf Repository Aktionen auf dem Rechner des Clients.

Z.b.:
Mit dem Pre Commit Hook, könnte bevor Code commited wird, ein Codeanalyse Tool gestartet werden. Nur wenn dieses auch erfolgreich durchgelaufen ist, wird der Commit erstellt.

Server-Side Hooks

Diese ermöglichen das Horchen auf Repository Aktionen auf dem Server der das Repository hostet.

Z.b.:
Mit dem Post Receive Hook, also wenn ein neuer Push auf das Repository erfolgt ist, kann die Continous Integration und Continous Delivery gestartet werden.

Branches zusammenführen

Wenn Branches immer weiter auseinandergehen, so wird es dann auch irgendwann mal Zeit, diese wieder zusammenzuführen. Dafür gibt es zwei Varianten, die im folgenden erklärt werden sollen.

Merge

git merge ist im Gegensatz zu git rebase eine nicht inversive Methode um Branches zusammenzuführen. Die Commit History wird nicht verändert. Branches werden mit einem einzelnen Merge Commit angeglichen. Bildet ein Branch die Basis eines Anderen, so kann ein Fast Forward erfolgen, und es gibt keinen Merge Commit. Durch das verwenden von Merge erhält man eine etwas komplexere Commit History mit vielen Ausbuchtungen. Der Vorteil ist, dass alle Änderungen, die durch den Merge gemacht wurden, nachvollzogen werden können. Folgende Grafik illustriert das mergen von Änderungen des Feature Branch in den Master Branch:

Merge [4]

Folgendes Beispiel soll das das Mergen vom Feature Branch in den Master Branch wie in der Abbildung veranschaulichen:

#Wir befinden uns aktuell im feature Branch
#Wechsel in den master Branch
git checkout master
#Merge starten
git merge feature
#Treten Konflikte auf, so müssen diese über
git mergetool
#und ein anschließendes
git merge --continue
#behoben werden
#Nun kann der Push auf den Remote master Branch erfolgen
git push origin master -f

Rebase

git rebase eignet sich nur wenn an einem eigenen Branch gearbeitet wird und die Änderungen von einem anderen Branch geholt werden sollen, da die Commit History umgeschrieben wird. Die Commits des anderen Branches werden unter die eigenen Commits geschoben, die Basis des Branches wird geändert. Dieser Vorgang verändert weiters jeden Commit über der alten Basis. Durch Rebasing erhält man eine sehr geradlinige Commit History:

Rebase[4]

Soll nach einem Rebase erneut auf den eigenen Branch gepushed werden so ist ein git push -f, ein force push nötig, da der lokale Branch nicht mehr mit dem Branch auf dem Remote kompatibel ist. Folgendes Beispiel soll das das Rebasen vom feature Branch mit dem Master Branch wie in der Abbildung veranschaulichen:

#Wir befinden uns aktuell im feature Branch
#Wechsel in den master Branch
git checkout master
#Änderungen holen
git pull origin master
#Zurückwechseln in Feature Branch
git checkout feature
#Rebase starten
git rebase master
#Treten Konflikte auf, so müssen diese über
git mergetool
#und ein anschließendes
git rebase --continue
#behoben werden
#Nun kann der Push auf den Remote feature Branch mit force push erfolgen
git push origin feature -f

Hier sei Angemerkt, dass die Konfliktlösung theoretisch für jeden Commit über der Basis (in der Grafik sind es zwei) durchgeführt werden muss (Zeile 11-13), wenn dies nicht automatisch erfolgen kann.

Rebase/Merge

Eine Kombination aus beidem ist eine sehr gebräuchliche Vorgehensweise. Wird an einem Feature gearbeitet, so erfolgt zuerst ein Rebase mit dem Zielbranch und dann wird in den Zielbranch gemerged. Dadurch bleibt die History geradlinig.

Die Information welche Commits im Feature Branch erzeugt wurden geht jedoch verloren, wenn kein Merge Commit erzwungen wird.

Qualitätskontrolle Pull/Merge Requests

Für eine leichtere Lesbarkeit wird im Folgenden nur noch von Merge Request gesprochen. Je nach verwendeter Git Platform wird ein anderer Begriff dafür verwendet:

System Bezeichnung
Github Pull Request
Bitbucket Pull Request
Gitlab Merge Request

In größeren Softwareprojekten erfolgt die Qualitätskontrolle sehr oft über Merge Requests. Wird ein neues Feature entwickelt, so erfolgt eine Abzweigung vom aktuellen Entwicklungsbranch development Branch (natürlich ist auch das Abzweigen von anderen Branches möglich). Der Entwickler programmiert, commitet und pusht, solange an dem Feature gearbeitet wird, nur im eigenen Branch. Von Zeit zu Zeit kann es nötig sein, dass der eigene Branch mit den Änderungen aus dem development Branch aktualisiert werden muss, dies kann über merge oder rebase stattfinden. Ist das Feature abgeschlossen, so startet der Merge Request. Dieser wird meist über die Webseite der verwendeten Git Platform gestartet. Ein anderer Entwickler erhält den Merge Request und übernimmt die Qualitätskontrolle, den Peer Review. Dabei wird die Funktionalität, Coding Conventions, usw. geprüft. Gibt es nichts zu beanstanden, so erfolgt, der eigentliche Merge in den gewünschten Branch. Ansonsten wird der Merge Request für weitere Verbesserungen zurückgewiesen und muss dann erneut gestellt werden.

Beispiel

Im folgenden Beispiel soll veranschaulicht werden wie zwei Branches zusammengeführt werden:

Repository
https://gitlab.drlue.at/root/webservice.git
Aufgabe
Branch feature_login soll in Branch development gemerged und anschließend soll development gepusht werden.
#Clonen des Repositories
git clone https://gitlab.drlue.at/root/webservice.git
#Wechseln in das Eigentliche Repository
cd webservice
#Initial befinden wir uns im Main Branch und wechseln auf feature_login.
#Dies ist nötig, da nach dem clonen noch keine lokale Version von feature_login vorhanden ist, nur die remote Version
git checkout feature_login
#Wechseln auf development, damit auch dieser Branch lokal verfügbar ist
git checkout development
#Merge durchführen
git merge feature_login
#Treten Konflikte auf, so müssen diese über
git mergetool
#und ein anschließendes
git merge --continue
#behoben werden
#Nun kann der push erfolgen
git push origin development