In dieser Blogreihe werden wir uns mit dem Thema Git beschäftigen und damit, warum das Arbeiten mit Git oder mit einem ähnlichen Tool für Entwickler fast schon unumgänglich ist.

Die folgenden Informationen werden auch, jedoch um einiges ausführlicher, in unseren – offiziell bei der SAP im Schulungskatalog gelisteten – Kursen HOUI5, HOFIO und WDECPS behandelt.

Inhalt

Auf folgende Themen werden wir in unserer Blogereihe eingehen:

  • Einführung in die Welt von Git
  • Begrifflichkeiten und die wichtigsten Funktionen
  • SAPUI5-Entwicklung mit Hilfe von Git
    • Exkurs: LDAP-Anbindung und Git-Server-Anbindung an die SAP Cloud Platform

Zielsetzung

Unser Ziel ist es, dass man nach unserer Blogreihe die unten stehende Grafik interpretieren und mit Git die SAPUI5-Entwicklung optimieren kann.

Part 2 – Inhaltsverzeichnis

Begrifflichkeiten:

  1. Repository
  2. Branch
  3. Commit
  4. Staging Area

Funktionen:

  1. Push
  2. Clone
  3. Fetch
  4. Merge
  5. Pull
  6. Best practice
  7. Rebase
  8. Reset (Hard Reset & Mixed Reset)

Begrifflichkeiten

Anbei erklären wir die wichtigsten Begrifflichkeiten und gängigsten Funktionen im Git-Umfeld.

Repository

Ein Repository kann mit einem einfachen Verzeichnis verglichen werden, in welchem weitere Verzeichnisse und Dateien abgelegt werden können. Ein Repository beinhaltet normalerweise ein Projekt bzw. ein Programm.

Es gibt eine Unterscheidung zwischen einem Remote (am Server oder in der Cloud) und einem Local (in der IDE) Repository.

Branch

Ein Repository beinhaltet mindestens einen Entwicklungsstrang, kann aber auch in mehrere Entwicklungsstränge unterteilt werden. Diese Stränge nennt man Branches.

Branches benutzt man entweder um Systemlandschaften (z.B. DEV, QAS und PRD) oder Feature-Entwicklungen (z.B. eine neue Funktionalität für Kunden) abzubilden. Die Unterteilung in den Landschaften ist üblicherweise am Remote Repository vertreten.

Je nachdem wo die Branches liegen, redet man von Remote und Local Branches.

Wenn man ein Remote Repository erstellt, so wird automatisch auch der erste Branch mit dem Namen „origin/master“ angelegt. Bei einem Local Repository heißt der erste Branch nur „master“.

Commit

Einen Commit kann man mit einem Snapshot, sprich mit einer Momentaufnahme, vergleichen. Diese Momentaufnahme beinhaltet die gesamten Änderungen seit dem letzten Commit. Diese Commits werden mit bestimmten Daten (Description, ID, Author, Timestamp, …) versehen und abgespeichert.

Ein Commit ist somit die kleinste „Einheit“ im Git-Umfeld.

Staging Area

In der Staging Area befinden sich alle Files, die seit dem letzten Commit editiert wurden. Das heißt, die Staging Area ist ein Ort für das Zwischenspeichern der Änderungen. Zusätzlich kann man auswählen, welche Änderungen aus der Staging Area man beim nächsten Commit mitnehmen möchte.

So könnte man X Änderungen aus der Staging Area auf Y Commits aufteilen und so einen sauberen Entwicklungsstrang abbilden.

Funktionen

Anbei gehen wir auf die gängigsten Funktionen ein, mit der man Repositories und Branches manipulieren kann. Wir werden uns nur die wichtigsten anschauen (Push, Fetch, Merge, Pull, Clone, Rebase), aber es gibt noch um einiges mehr (Cherry-Pick, Stashing, …).

Push

Wenn man in der IDE entwickelt und irgendwann einen Stand hat, den man mit anderen Entwicklern teilen möchte, dann muss man den letzten Commit mit einem Push in das Remote Repository bringen.

Da das Remote Repository am Anfang leer ist, kann der erste Entwickler, der z.B. das Rahmenprojekt aufgesetzt hat, seine Entwicklungen ohne Rücksicht pushen:

Später sollte vor jedem Push gecheckt werden, ob sich am Remote Repository etwas geändert hat. Wie das funktioniert, erfahren Sie weiter unten.

Clone

Es kann nur einen ersten bzw. initialen Push geben. Wenn also sich am Remote Repository bereits Daten befinden, dann muss ich das Ganze in meine lokale Umgebung bekommen.

Das funktioniert mit einem Clone:

Fetch

Mit einem Fetch kann ich die Änderungen, die seit meinem letzten Fetch gemacht wurden, in die lokale Umgebung laden. Einfacher ausgedrückt: Wir schauen nach, ob andere Entwickler was Neues gemacht haben.

Merge

Nur nachschauen, ob andere Entwickler etwas gemacht haben, reicht noch nicht ganz aus. Wir müssen die Datenstände zusammenführen und diesen Vorgang nennt man Merge.

Wir haben vorhin, als wir über Branches geredet haben, etwas verschwiegen. Die Namen für Branches sind nichts anderes wie Pointer bzw. Zeiger. Ein Zeiger auf den aktuellen Commit, sprich auf den letzten Snapshot.

Was bei einem Merge technisch passiert ist ganz einfach: Der Zeiger von meinem altem Commit, wird auf den neuen Commit gesetzt, welchen wir uns mit einem Fetch geladen haben. Dabei wird geschaut, ob sich Änderungen überschneiden. Wenn sich Änderungen überschneiden, gibt es Konflikte.

Auf Konflikte wird man immer hingewiesen und der Entwickler, der die Zusammenführung macht, kann entscheiden, welche Code-Passagen die Aktuellen sind. Ein Merge wird in einem eigenen Commit protokolliert.

Pull

Man sieht jetzt schon, dass man Fetch und Merge oft brauchen wird. Das haben sich auch die Entwickler von Git gedacht und deswegen die Funktion Pull ins Leben gerufen.

Pull führt zuerst einen Fetch und dann automatisch einen Merge aus:

Best practice

Einmalig:

  • Clone or Initial Commit and Push
    • wenn ich der erste bin = Push
    • App vom Remote Repository clonen

Wiederkehrend:

  1. Fetch: schauen ob sich was getan hat
  2. Merge: den Zeiger auf die aktuelle Version verschieben und die Datenstände zusammenführen (auf eventuelle Konflikte reagieren)
  3. Push: die aktuelle Version mit anderen Entwicklern teilen
  1. Pull: schauen ob sich was getan hat, den Zeiger auf die aktuelle Version verschieben und die Datenstände zusammenführen (auf eventuelle Konflikte reagieren)
  2. Push: die aktuelle Version mit anderen Entwicklern teilen

Wir kennen jetzt viele Begriffe und kennen auch schon die wichtigsten Funktionalitäten. Jetzt gehen wir nochmal auf die Branches ein und bringen all das Wissen mit, was wir gelernt haben.

Wir haben gesagt, dass auch am Remote Repository Branches erstellt werden können (z.B. P,Q,D), aber auch am Local Repository (z.B. Features).

Gehen wir von folgendem Beispiel aus: Es existiert bereits ein Repository und da wir jetzt auch im Entwicklerteam sind, können wir vom Remote Repository vom Entwicklungsbranch die aktuellen Stände clonen.

Wir machen sicherheitshalber einen Local Feature Branch wo wir einige Entwicklungen und auch Commits machen. Nach einigen Entwicklungstagen sind wir fertig und mergen lokal unsere Features mit dem lokalen Master.

Bevor wir unsere Änderungen mit anderen teilen können, müssen wir checken, ob andere Entwickler auch zwischenzeitlich gepusht haben und führen einen Fetch und danach Merge bzw. einen Pull aus.

Nachdem wir uns mit einigen Konflikten beschäftigen durften, können wir nun endlich unsere Änderungen pushen und mit anderen Entwicklern teilen.

Unser Administrator nimmt nach eigenem Ermessen die Datenstände und macht einen Merge zwischen D und Q und signalisiert somit dem Testteam, dass sie testen können.

Wenn die Tests abgeschlossen sind, dann kann der Administrator zwischen Q und P einen Merge durchführen. Der P-Branch wird dann entweder exportiert oder der Administrator deployed ihn direkt auf das Kundensystem und der Kunde hat nun eine neue Version der Applikation.

Rebase

Wenn wir uns die obenstehende Grafik genauer anschauen, dann werden wir erkennen, dass durch einen Merge immer ein „unnötiger“ Commit entsteht.

Durch Rebase können wir auch dieses – oft von Entwicklern empfundene – Designproblem lösen und einen sauberen Entwicklungsstrang ohne „Merge-Commits“ aufbauen.

Nehmen wir an, wir haben lokal einen Feature Branch gemacht. Dort haben wir fleißig entwickelt und möchten jetzt unsere Änderungen wieder auf den Master Branch bekommen:

Wenn wir jetzt ein Rebase von unserem Feature auf den Master Branch machen, dann schauen unsere Commits wie folgt aus:

Nichtsdestotrotz müssen wir an dieser Stelle noch einen Merge machen, da passiert aber wiederum nichts anderes, als das der Zeiger von dem alten Commit auf den neuesten Commit bewegt wird, jedoch mit dem Unterschied, dass wir an dieser Stelle keinen zusätzlichen Commit für das Mergen erzeugen:

Jetzt schaut unser Master Branch sauber aus, wir haben einen sauberen Entwicklungsstrang und ein Außenstehender würde meinen, dass wir keinen Feature Branch – sprich keinen weiteren Branch – für die Entwicklung verwendet haben.

Rebase mit Konflikten

Wenn es zu einem Konflikt kommt, so wird das nicht, wie bei einem Merge, in einem extra Commit verpackt, sondern die zwei Commits, die von den Änderungen betroffen sind, werden entweder fusioniert oder einer von den beiden Commits gewinnt. Entscheidungsträger ist hier wiederum der Entwickler, der gerade einen Rebase vornimmt.

 

Reset

Viele „Git-Neulinge“ verwechseln Reset mit Rebase. Oft glaubt man, dass Rebase irgendetwas mit dem Zurücksetzen der Änderungen zu tun hat, was aber nicht stimmt.

Mit Reset hat man zwei Möglichkeiten um das Zurücksetzen der Daten bzw. den Änderungen bis zu einem, in der Vergangenheit liegenden, bestimmten Commit zu erreichen:

Hard Reset

Gehen wir davon aus, dass wir einen Feature Branch eröffnet haben und dem Feature, welches ein Change Request vom Kunden war, eine Absage erteilt wird. Wir wissen schon, dass wir an einem weiteren Feature arbeiten werden, welches aber nichts mit unserem bisherigen Feature Entwicklungen zu tun hat.

In diesem Fall werden wir einen Hard Reset machen. Die bisherigen Entwicklungen am Feature Branch werden verworfen und der Feature Branch wird mit dem ausgewählten Branch, in unserem Fall dem Master Branch, gemerged um wieder auf dem gleichen Datenstand zu sein:

Mixed Reset

Bei einem Mixed Reset schaut das ganze fast gleich aus, mit dem Unterschied, dass wir nicht alles verwerfen, sondern einige Änderungen, die sich in der Staging Area befinden, mitgenommen werden können.

Zusammenfassung

Nachdem wir jetzt einiges über Git wissen und auch schon die wichtigsten Begrifflichkeiten und Funktionalitäten kennengelernt haben, können wir uns im dritten Teil unserer Blogreihe anschauen, wie man Git effizient bei der Entwicklung mit der SAP WebIDE einsetzt.