18 Git
Einführung in Git mit Gesamtabbildung
Die in diesem Kapitel präsentierten Inhalte basieren auf Grundlage der Quellen (ToDo: qeullen einfügen!!) und wurden teilweise angepasst, erweitert sowie ergänzt und werden im weiteren Verlauf nicht noch einmal referenziert:
18.1 Einleitung
In der modernen Softwareentwicklung entstehen kontinuierlich Änderungen am Quellcode. Um diese Änderungen nachvollziehbar zu dokumentieren, frühere Entwicklungsstände wiederherzustellen und die Zusammenarbeit mehrerer Entwickler zu ermöglichen, werden Versionskontrollsysteme eingesetzt.
Das heute am weitesten verbreitete Versionskontrollsystem ist Git. Es bildet die Grundlage zahlreicher Entwicklungsprozesse und wird sowohl in kleinen Projekten als auch in grossen Softwareentwicklungen eingesetzt.
18.1.1 Versionskontrolle
Die Versionskontrolle (engl. Version Control) bezeichnet ein Verfahren zur systematischen Verwaltung und Nachverfolgung von Änderungen an Dateien, insbesondere an Quellcode. Sie ermöglicht es, sämtliche Änderungen über den gesamten Lebenszyklus eines Projekts zu dokumentieren und bei Bedarf auf frühere Zustände zurückzugreifen. Hierzu werden Softwarewerkzeuge, sogenannte Versionskontrollsysteme (VCS), eingesetzt.
Versionskontrollsysteme werden häufig als
- VCS (Version Control System)
- RCS (Revision Control System)
bezeichnet. Der Begriff SCM (Source Code Management) wird häufig im Zusammenhang mit Versionskontrolle verwendet, umfasst jedoch zusätzlich organisatorische Aspekte der Verwaltung von Quellcode.
Heute haben sich insbesondere verteilte Versionskontrollsysteme (Distributed Version Control Systems, DVCS) etabliert. Hierbei existiert lokal auf dem Entwicklungsrechner und auf einem zentralen Server jeweils eine vollständige Kopie des gesamten Projektverlaufs. Änderungen können lokal vorgenommen und später mit dem zentralen Server synchronisiert werden.
In modernen Softwareprojekten stellt die Versionskontrolle ein zentrales Werkzeug dar, da häufig mehrere Entwickler gleichzeitig an derselben Codebasis arbeiten. Durch die lückenlose Dokumentation sämtlicher Änderungen können Fehler einfacher analysiert, Entwicklungsstände reproduziert und parallele Arbeiten effizient koordiniert werden.
18.1.1.1 Motivation
Ohne Versionskontrolle entstehen in Softwareprojekten zahlreiche Herausforderungen:
- Änderungen sind nicht nachvollziehbar
- Frühere Versionen gehen verloren
- Gleichzeitige Änderungen mehrerer Entwickler können zu Konflikten führen
- Fehlerursachen lassen sich nur schwer identifizieren
- Unterschiedliche Entwicklungsstände müssen manuell verwaltet werden
Typische Symptome sind auskommentierter Code, welcher lediglich als Sicherung dient, oder Dateinamen wie “Programm.c”, welche im weiteren Verlauf zu “Programm_final.c”, “Programm_final_v2.c” oder “Programm_final_final.c” umbenannt werden. Solche Praktiken führen zu unübersichtlichen Strukturen, erschweren die Zusammenarbeit und bergen die Gefahr von Datenverlust.
18.1.1.2 Funktionsprinzip
Ein Versionskontrollsystem speichert jede Änderung an einer Datei. Zu jeder Änderung werden zusätzlich Metadaten wie
- Autor
- Zeitpunkt
- Änderungsbeschreibung
- betroffene Dateien
gespeichert. Dadurch entsteht ein vollständiger Änderungsverlauf, welcher jederzeit eingesehen werden kann. Fehlerhafte Änderungen können identifiziert und frühere Versionen wiederhergestellt werden.
18.1.1.3 Vorteile der Versionskontrolle
18.1.1.3.1 Änderungsverlauf
Jede Datei besitzt einen vollständigen Verlauf sämtlicher vorgenommenen Änderungen. Dadurch können
- frühere Versionen wiederhergestellt,
- Fehlerquellen analysiert,
- Änderungen nachvollzogen und
- Verantwortlichkeiten identifiziert
werden. Der Verlauf dokumentiert zudem, wann und warum eine Änderung durchgeführt wurde.
18.1.1.3.2 Zusammenarbeit
Versionskontrollsysteme ermöglichen die Arbeit auf getrennten Entwicklungszweigen.
Ein Branch stellt eine unabhängige Entwicklungsumgebung dar, in welcher neue Funktionen, Fehlerbehebungen oder Experimente umgesetzt werden können, ohne den stabilen Hauptentwicklungszweig zu beeinflussen. Nach erfolgreicher Entwicklung können die Änderungen wieder zusammengeführt (Merge) werden.
18.1.1.3.3 Nachvollziehbarkeit
Jede Änderung kann eindeutig einer Person und einem Zweck zugeordnet werden. Änderungsbeschreibungen (Commit messages) dokumentieren die Motivation hinter einer Anpassung.
Dies erleichtert:
- Fehlersuche
- Wartung bestehender Systeme
- Zusammenarbeit in Teams
- Einarbeitung neuer Entwickler
- Arbeiten an Legacy-Systemen
18.1.2 Git
Git ist ein Versionskontrollsystem zur Verwaltung und Nachverfolgung von Änderungen an Dateien und Quellcode.
Es wurde im Jahr 2005 von Linus Torvalds, dem Entwickler des Linux-Kernels, entwickelt und zählt heute zu den am weitesten verbreiteten Werkzeugen der Softwareentwicklung.
Git setzt die in Section 18.1.1 beschriebenen Konzepte der Versionskontrolle um. Es ermöglicht die strukturierte Dokumentation von Änderungen, die Verwaltung unterschiedlicher Entwicklungsstände sowie die Zusammenarbeit mehrerer Entwickler an einem gemeinsamen Projekt.
18.1.2.1 Verteilte Architektur
Git gehört zur Gruppe der verteilten Versionskontrollsysteme (Distributed Version Control Systems, DVCS).
Jeder Entwickler besitzt eine vollständige lokale Kopie des Projekts inklusive des gesamten Änderungsverlaufs. Dadurch können viele Arbeitsschritte lokal durchgeführt werden, ohne auf einen zentralen Server angewiesen zu sein.
Die lokalen Kopien können bei Bedarf mit einem gemeinsamen Server synchronisiert werden, sodass Änderungen zwischen mehreren Entwicklern ausgetauscht werden können (siehe Figure 18.1). Dieser Server kann entweder selbst gehostet oder als Dienst eines Git-Hosting-Anbieters (z.B. GitHub, GitLab oder Bitbucket) genutzt werden. In beiden Fällen wird der Server als Remote-Repository bezeichnet, da er als zentraler Speicherort für die gemeinsame Codebasis dient.
18.1.2.2 Eigenschaften von Git
Git hat sich in den vergangenen Jahren als De-facto-Standard der Versionskontrolle etabliert und wird heute in zahlreichen Softwareprojekten eingesetzt.
Zu den wichtigsten Eigenschaften und Vorteilen von Git zählen:
- Hohe Arbeitsgeschwindigkeit durch die lokale Verarbeitung vieler Operationen
- Verteilte Architektur mit vollständigen lokalen Projektkopien
- Vollständige Nachverfolgbarkeit sämtlicher Änderungen
- Hohe Zuverlässigkeit durch integrierte Integritätsprüfungen
- Unterstützung paralleler Entwicklungsarbeiten
- Flexible Einsetzbarkeit für kleine und grosse Projekte
- Plattformunabhängige Nutzung
- Grosse Benutzerbasis und umfangreiche Dokumentation
- Breite Unterstützung durch Entwicklungswerkzeuge und Hosting-Plattformen
Diese Eigenschaften machen Git zu einem leistungsfähigen Werkzeug für die Verwaltung von Quellcode und die Zusammenarbeit in Softwareprojekten.
18.2 Terminologie
Git verwendet eine Vielzahl von Begriffen zur Beschreibung von Projekten, Entwicklungsständen und Arbeitsabläufen. Das Verständnis dieser Begriffe bildet die Grundlage für die spätere Arbeit mit Git.
Die folgenden Abschnitte erläutern die wichtigsten Konzepte und Bezeichnungen, welche im weiteren Verlauf dieses Skripts verwendet werden.
18.2.1 Speicherbereiche (Repository, Working Directory, Working Tree, Staging Area)
Im Folgenden werden die drei zentralen Speicherbereiche von Git vorgestellt, welche die Grundlage für die Verwaltung von Dateien und Änderungen bilden. Die Bereiche werden in Figure 18.2 dargestellt und im Anschluss erläutert.
18.2.1.1 Repository
Ein Repository (kurz Repo) ist ein virtueller Speicher und von Git verwaltetes Projekt. Es enthält sämtliche Dateien eines Projekts sowie den vollständigen Änderungsverlauf aller Versionen. Dadurch können frühere Zustände jederzeit nachvollzogen oder wiederhergestellt werden.
Grundsätzlich wird zwischen zwei Arten von Repositories unterschieden:
- Lokales Repository auf dem eigenen Rechner
- Remote-Repository auf einem zentralen Server (selbst gehostet oder als Dienst eines Git-Hosting-Anbieters z.B. GitHub, GitLab oder Bitbucket)
Ein Entwickler besitzt eine vollständige lokale Kopie des Repositories inklusive des gesamten Projektverlaufs. Ein Remote-Repository ist eine entfernte Kopie des Repositories auf einem Server. Es dient als zentrale Austauschplattform für mehrere Entwickler und ermöglicht die Synchronisation von Änderungen zwischen verschiedenen lokalen Repositories. Ein Remote-Repository ist somit kein spezieller Repository-Typ. Es handelt sich um ein gewöhnliches Repository, welches sich auf einem anderen System befindet und über das Netzwerk erreichbar ist.
18.2.1.2 Working Directory
Das Working Directory (Arbeitsverzeichnis) umfasst die aktuell sichtbaren Projektdateien auf dem lokalen Rechner. Hier werden Dateien erstellt, bearbeitet, umbenannt oder gelöscht. Änderungen im Working Directory sind zunächst lediglich lokale Dateisystemänderungen und noch nicht Bestandteil der Versionshistorie. Das Working Directory stellt somit den eigentlichen Arbeitsbereich eines Entwicklers dar.
18.2.1.3 Working Tree
Der Begriff Working Tree bezeichnet die aktuell ausgecheckten Dateien eines bestimmten Commits. In der Praxis werden die Begriffe Working Tree und Working Directory häufig synonym verwendet.
18.2.1.4 Staging Area
Die Staging Area ist ein Zwischenspeicher zwischen dem Working Directory und der Versionshistorie. Sie dient dazu festzulegen, welche Änderungen im nächsten Commit gespeichert werden sollen. Dadurch können mehrere Änderungen vorbereitet, überprüft und gezielt zu einem zusammenhängenden Entwicklungsschritt zusammengefasst werden.
Die Staging Area wird häufig auch als Index bezeichnet.
18.2.2 Versionshistorie (Commit, Commit-ID, Historie)
18.2.2.1 Commit
Ein Commit stellt einen gespeicherten Zustand eines Projekts dar.
Jeder Commit enthält:
- die vorgenommenen Änderungen
- einen Zeitstempel
- den Autor (Benutzername und E-Mail-Adresse)
- eine Commit-Nachricht
Commits bilden gemeinsam die Versionshistorie eines Projekts. Ein Commit kann als Momentaufnahme des Projekts zu einem bestimmten Zeitpunkt betrachtet werden.
18.2.2.2 Commit-ID
Jeder Commit besitzt eine eindeutige Identifikationsnummer. Diese wird durch eine kryptographische Hashfunktion erzeugt und identifiziert einen Commit eindeutig innerhalb eines Repositories.
Eine typische Commit-ID besteht aus einer langen Zeichenfolge:
3e887ab8b1d5d5f8a5c0f2d63c5ef85d2a0a6c9e
Dabei werden häufig nur die ersten Zeichen der Commit-ID verwendet, sofern diese innerhalb des Repositories eindeutig sind.
18.2.2.3 Historie
Die Historie umfasst sämtliche Commits eines Repositories in ihrer zeitlichen Reihenfolge. Sie dokumentiert die Entwicklung eines Projekts vom ersten bis zum aktuellen Commit. Die Historie bildet die Grundlage für Nachvollziehbarkeit, Fehlersuche und Wiederherstellung früherer Entwicklungsstände.
18.2.3 Referenzen (Branch, HEAD, Tag)
18.2.3.1 Branch
Ein Branch stellt einen unabhängigen Entwicklungszweig innerhalb eines Repositories dar. Branches ermöglichen es, neue Funktionen, Fehlerbehebungen oder Experimente umzusetzen, ohne den Hauptentwicklungszweig direkt zu beeinflussen. Mehrere Branches können parallel existieren und später wieder zusammengeführt werden.
Der Main Branch bildet üblicherweise die Hauptentwicklungslinie eines Projekts. Er repräsentiert häufig den stabilen Entwicklungsstand und dient als Ausgangspunkt für weitere Entwicklungsarbeiten. Häufig wurde die Bezeichnung master verwendet. Heute wird in den meisten Projekten die Bezeichnung main eingesetzt.
18.2.3.2 HEAD
HEAD bezeichnet die aktuell ausgecheckte Referenz innerhalb eines Repositories. In den meisten Fällen verweist HEAD auf den aktuell aktiven Branch. Dieser Branch verweist wiederum auf den zuletzt erstellten Commit.
HEAD kennzeichnet somit den aktuellen Entwicklungspunkt, auf dem gearbeitet wird und kann somit als Zeiger auf die aktuelle Arbeitsposition verstanden werden. Durch die Verwendung von HEAD können verschiedene Commits oder Branches ausgecheckt und bearbeitet werden, ohne dass die Historie verloren geht.
18.2.3.3 Tag
Ein Tag kennzeichnet einen bestimmten Commit dauerhaft. Tags werden häufig verwendet, um wichtige Entwicklungsstände zu markieren, beispielsweise:
- Version 1.0
- Version 2.1
- Beta-Versionen
- Release-Kandidaten
Ein Tag verändert die Historie nicht, sondern stellt lediglich eine zusätzliche Referenz auf einen bestehenden Commit dar.
18.2.4 Zusammenführung von Änderungen (Merge, Merge-Konflikt)
18.2.4.1 Merge
Ein Merge beschreibt das Zusammenführen von Änderungen aus unterschiedlichen Branches. Dabei werden die Entwicklungsstände mehrerer Branches in einem gemeinsamen Entwicklungsstand vereint.
Merges werden typischerweise verwendet, um abgeschlossene Entwicklungsarbeiten wieder in den Hauptentwicklungszweig zu integrieren.
18.2.4.2 Merge-Konflikt
Ein Merge-Konflikt entsteht, wenn dieselbe Information in verschiedenen Branches unterschiedlich verändert wurde und Git nicht automatisch entscheiden kann, welche Änderung übernommen werden soll.
In diesem Fall muss der Entwickler den Konflikt manuell auflösen.
18.2.5 Verteilte Zusammenarbeit (Remote, Origin, Fork, Clone)
18.2.5.1 Remote
Ein Remote ist eine gespeicherte Verbindung zu einem entfernten Repository. Ein Repository kann mehrere Remotes besitzen, beispielsweise für verschiedene Server oder Projektkopien. Dabei kann jedes Remote auf ein anderes Repository verweisen, beispielsweise:
origin→ zentrales Repository des Projektsupstream→ ursprüngliches Repository eines Forks
Jedes Remote erhält einen Namen zur Identifikation. Dabei kann ein Remote grundsätzlich ebenso ein Repository auf demselben Server oder sogar auf demselben Rechner sein, sofern es über das Netzwerk erreichbar ist.
18.2.5.2 Origin
Origin ist die Standardbezeichnung des ersten konfigurierten Remote-Repositories. In vielen Projekten verweist origin auf das zentrale Repository des Projekts. Da es sich lediglich um einen Namen handelt, könnte dieser grundsätzlich beliebig gewählt werden.
18.2.5.3 Fork
Ein Fork ist eine unabhängige Kopie eines bestehenden Repositories. Forks werden häufig in Open-Source-Projekten verwendet, um eigene Änderungen vorzunehmen, ohne direkt auf das ursprüngliche Repository schreiben zu müssen. Die Änderungen können später optional wieder in das Ursprungsprojekt eingebracht werden.
18.2.5.4 Clone
Ein Clone ist eine vollständige lokale Kopie eines bestehenden Repositories. Dabei werden sowohl die aktuellen Projektdateien als auch die gesamte Versionshistorie übernommen.
Jeder Entwickler arbeitet in Git typischerweise mit einem lokalen Clone eines Repositories.
18.2.6 Veröffentlichung (Release)
18.2.6.1 Release
Ein Release bezeichnet eine veröffentlichte Version einer Software. In Git wird ein Release häufig durch einen Tag repräsentiert und zusätzlich mit Dokumentationen, Installationsdateien oder Änderungsprotokollen ergänzt.
Während ein Tag lediglich einen Commit markiert, beschreibt ein Release die tatsächliche Bereitstellung einer Version für Anwender.
18.3 Installation und Konfiguration
Im Folgenden wird die Installation, Einrichtung sowie Bedienung von Git als Kommandozeilenwerkzeug beschrieben. Es existieren jedoch zahlreiche grafische Benutzeroberflächen (GUI-Clients) für Git, welche die Bedienung vereinfachen und teilweise zusätzliche Funktionen bieten. Diese werden hier jedoch nicht behandelt. Eine Übersicht über verfügbare Git GUI-Clients findet sich auf der offiziellen Git-Website: Git GUI-clients.
18.3.1 Installation
Git kann kostenlos für Windows, Linux und macOS heruntergeladen werden. Die aktuelle Version für die jeweilige Plattform ist auf der offiziellen Git-Webseite verfügbar: Git-Install.
Für die meisten Anwender sind die Standardoptionen des Installationsprogramms ausreichend.
Nach der Installation kann die erfolgreiche Einrichtung über folgenden Kommandozeilenbefehl überprüft werden:
git --versionDie Ausgabe sollte die installierte Git-Version anzeigen, z.B.:
git version 2.42.0
Wird eine Versionsnummer angezeigt, wurde Git erfolgreich installiert.
18.3.2 Erstkonfiguration
Bevor Git verwendet werden kann, sollten Benutzername und E-Mail-Adresse konfiguriert werden. Diese Informationen werden zukünftigen Änderungen zugeordnet und dienen der Nachverfolgbarkeit innerhalb eines Projekts.
Die Konfiguration erfolgt mit folgenden Befehlen:
git config --global user.name "Vorname Nachname"
git config --global user.email "[email protected]"Die aktuelle Konfiguration kann anschliessend überprüft werden:
git config --listIn der Ausgabe sollten unteranderem die gesetzten Werte für user.name und user.email angezeigt werden, z.B.:
user.name=Vorname Nachname[email protected]
Damit ist Git einsatzbereit und kann verwendet werden.
18.3.3 Git-SSH
Git unterstützt die Authentifizierung über SSH (Secure Shell). Dabei wird anstelle eines Benutzernamens und Passworts ein Schlüsselpaar zur Identifikation verwendet.
Die Verwendung von SSH bietet insbesondere bei der Arbeit mit Remote-Repositories (auf einem zentralen Server) Vorteile, da eine Authentifizierung erfolgt und Zugangsdaten nicht bei jeder Verbindung erneut eingegeben werden müssen.
18.3.3.1 Was ist ein SSH-Schlüssel?
Ein SSH-Schlüsselpaar besteht aus:
- einem privaten Schlüssel (Private Key), welcher auf dem eigenen Rechner verbleibt
- einem öffentlichen Schlüssel (Public Key), welcher beim Git-Hosting-Dienst hinterlegt wird (z.B. GitHub, GitLab oder Bitbucket)
Ein SSH-Schlüssel dient der Authentifizierung innerhalb des SSH-Protokolls, welches eine verschlüsselte und authentifizierte Kommunikation über unsichere Netzwerke ermöglicht.
Mithilfe dieses Schlüsselpaares kann die Identität eines Benutzers nachgewiesen werden. Hierzu hinterlegt der Benutzer seinen öffentlichen Schlüssel beim Git-Hosting-Dienst. Bei einer späteren Verbindung weist der Benutzer mithilfe seines privaten Schlüssels nach, dass er im Besitz des zugehörigen Schlüsselpaares ist. Der Dienst kann diesen Nachweis mit dem hinterlegten öffentlichen Schlüssel überprüfen und dadurch die Identität des Benutzers bestätigen. Nach erfolgreicher Authentifizierung wird die weitere Kommunikation über die SSH-Verbindung verschlüsselt übertragen. Dadurch können Daten sicher zwischen dem lokalen Rechner und dem Remote-Repository ausgetauscht werden.
18.3.3.2 SSH-Schlüssel erstellen
Ein neues SSH-Schlüsselpaar kann beispielsweise mit dem folgenden Befehl erzeugt werden:
ssh-keygen -t ed25519 -C "[email protected]"Der Algorithmus Ed25519 stellt heute den empfohlenen Standard zur Erzeugung von SSH-Schlüsseln dar. Ältere Systeme verwenden teilweise noch RSA-Schlüssel (id_rsa), welche weiterhin unterstützt werden.
Weitere Informationen zu unterstützten Schlüsseltypen finden sich beispielsweise in den offiziellen Dokumentationen der Git-Hosting-Dienste:
Anschliessend werden einige Rückfragen zum Speicherort und optional zu einer Passphrase gestellt. Die Standardwerte können in den meisten Fällen übernommen werden.
Nach erfolgreicher Erstellung befinden sich die Schlüssel standardmässig im Verzeichnis:
- Linux/macOS:
~/.ssh - Windows:
%USERPROFILE%\.ssh
Typischerweise werden dabei die folgenden Dateien erzeugt:
id_ed25519id_ed25519.pub
Die Datei id_ed25519 enthält den privaten Schlüssel und darf niemals weitergegeben werden. Die Datei id_ed25519.pub enthält den öffentlichen Schlüssel, dessen Inhalt beim Git-Hosting-Dienst hinterlegt werden kann.
18.3.3.3 Hinterlegen des öffentlichen Schlüssels
Für die Hinterlegung des öffentlichen Schlüssels beim Git-Hosting-Dienst ist ein entsprechendes Benutzerkonto bei diesem erforderlich. Nach der Anmeldung kann der öffentliche Schlüssel in den Einstellungen des Kontos hinterlegt werden.
Unter den folgenden Links finden sich detaillierte Anleitungen zur Hinterlegung des öffentlichen Schlüssels bei den gängigsten Git-Hosting-Diensten:
18.3.3.4 Verbindung testen
Nach dem Hinterlegen des öffentlichen Schlüssels kann die Verbindung überprüft werden:
ssh -T [email protected]Bei erfolgreicher Authentifizierung erscheint eine entsprechende Bestätigungsmeldung des Git-Hosting-Dienstes, z.B.:
GitHub:
Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access.
GitLab:
Welcome to GitLab, @USERNAME!
18.4 Arbeiten mit Git
18.4.1 Grundlegender Workflow
Der grundlegende Git-Workflow beschreibt den Weg einer Änderung von ihrer Erstellung bis zu ihrer Integration in die Versionshistorie. Dabei durchläuft eine Änderung verschiedene Stationen innerhalb eines Repositorys. Figure 18.3 veranschaulicht diesen Ablauf anhand eines typischen Entwicklungsbeispiels mit einem Hauptbranch (main) und einem separaten Entwicklungsbranch (feature1). In der Praxis werden Änderungen meist in separaten Entwicklungsbranches umgesetzt. Die zusätzlichen Commits auf dem Branch main dienen in diesem Beispiel ausschliesslich dazu, eine parallele Entwicklung und den daraus resultierenden Merge-Commit zu veranschaulichen. Es ist zu beachten, dass es sich hierbei um eine rein lokale Entwicklung handelt, bei der die Änderungen noch nicht mit einem Remote-Repository synchronisiert werden.
Die nachfolgende Abbildung ist interaktiv und visualisiert die einzelnen Schritte des Git-Workflows. Durch das Bewegen des Mauszeigers über die Commit-Knoten werden zusätzliche Informationen und Erläuterungen zu den jeweiligen Arbeitsschritten eingeblendet. Die verwendeten Commit-Nachrichten dienen ausschliesslich der Veranschaulichung und stellen beispielhafte Bezeichnungen dar.
Nach der Initialisierung des Repositorys entsteht mit dem ersten Commit der Ausgangspunkt der Versionshistorie. Der Branch main verweist auf diesen Commit, während HEAD die aktuelle Arbeitsposition kennzeichnet. In der Regel zeigt HEAD auf den aktuell aktiven Branch.
Im weiteren Verlauf wird ausgehend von einem gemeinsamen Basis-Commit ein separater Branch feature1 erstellt. Auf diesem Branch können Änderungen unabhängig vom Hauptbranch umgesetzt werden. Gleichzeitig zeigt die Abbildung, dass sich auch der Branch main weiterentwickeln kann. In realen Projekten erfolgt die Entwicklung neuer Funktionen und Änderungen üblicherweise ausschliesslich in solchen Entwicklungsbranches. Die zusätzlichen Commits auf main dienen in diesem Beispiel lediglich dazu, eine parallele Entwicklung und den daraus resultierenden Merge-Commit darzustellen. Dadurch entstehen zwei parallele Entwicklungslinien.
Die Commits auf Branch feature1 sind zunächst nicht Bestandteil des Branches main. Erst durch den Merge werden die Änderungen des Feature-Branches in den Hauptbranch integriert. Da sowohl der Branch main als auch der Branch feature1 seit dem Abzweigen eigene Commits besitzen, entsteht in diesem Beispiel ein Merge-Commit. Dieser verbindet beide Entwicklungslinien und besitzt daher zwei Eltern-Commits.
Der Ablauf verdeutlicht, dass Git nicht einzelne Dateien isoliert betrachtet, sondern Entwicklungsstände über Commits verwaltet. Branches dienen dabei als bewegliche Zeiger auf bestimmte Commits und ermöglichen es, parallele Entwicklungsarbeiten strukturiert voneinander zu trennen.
18.4.2 Zusammenarbeit
git init und git clone <url>:
Die Befehle git init und git clone <url> dienen beide dazu, ein lokales Git-Repository bereitzustellen, verfolgen jedoch unterschiedliche Ziele.
Mit
git initwird in einem bestehenden Verzeichnis ein neues, leeres Git-Repository erstellt. Der Befehl wird typischerweise verwendet, um ein Projekt erstmals unter Versionskontrolle zu stellen. Das Repository befindet sich zunächst nur lokal auf dem eigenen Rechner und enthält keine Dateien oder Historie.Mit
git clone <url>wird hingegen eine vollständige lokale Kopie eines bereits existierenden Repositorys erzeugt. Dabei werden sowohl die Versionshistorie als auch die aktuellen Projektdateien übernommen. Intern erstelltgit clone <url>zunächst ein neues Repository und lädt anschließend die Daten des entfernten Repositorys herunter.
Pull requests, Code-Review, etc. eher Bestandteil von Git-Hosting Dienst => Infobocx
18.4.3 Dev Workflow mit Release+Tag
Releasenr., Buildnr., etc.
18.4.4 User
18.5 Cheatsheet
Dieses Kapitel dient als kompakte Referenz für die wichtigsten Git-Befehle im Entwicklungsalltag [1]. Die Befehle sind nach typischen Arbeitsschritten gruppiert und können als Nachschlagewerk verwendet werden.
18.5.1 Repository erstellen und klonen (init, clone)
| Aufgabe | Befehl |
|---|---|
| Neues Repository initialisieren | git init |
| Bestehendes Repository klonen | git clone <url> |
18.5.2 Änderungen für einen Commit vorbereiten (add, mv, rm, reset, status)
| Aufgabe | Befehl |
|---|---|
| Datei zum Staging-Bereich hinzufügen | git add <datei> |
| Alle Änderungen hinzufügen | git add . |
| Änderungen interaktiv auswählen | git add -p |
| Datei verschieben oder umbenennen | git mv <alt> <neu> |
| Datei löschen | git rm <datei> |
| Datei aus Git entfernen, lokal behalten | git rm --cached <datei> |
| Datei aus dem Staging-Bereich entfernen | git reset <datei> |
| Gesamten Staging-Bereich zurücksetzen | git reset |
| Aktuellen Repository-Status anzeigen | git status |
18.5.3 Commits erstellen (commit)
| Aufgabe | Befehl |
|---|---|
| Commit mit Editor erstellen | git commit |
| Commit mit Nachricht erstellen | git commit -m "Nachricht" |
| Alle verfolgten Änderungen committen | git commit -am "Nachricht" |
18.5.4 Branches verwalten (branch, switch, checkout)
| Aufgabe | Befehl |
|---|---|
| Branch wechseln | git switch <branch> |
| Branch wechseln (ältere Syntax) | git checkout <branch> |
| Branch erstellen und wechseln | git switch -c <branch> |
| Branch erstellen und wechseln (ältere Syntax) | git checkout -b <branch> |
| Vorhandene Branches anzeigen | git branch |
| Branches nach letzter Aktivität sortieren | git branch --sort=-committerdate |
| Branch löschen | git branch -d <branch> |
| Branch erzwingen löschen | git branch -D <branch> |
18.5.5 Unterschiede anzeigen (diff)
| Aufgabe | Befehl |
|---|---|
| Alle Änderungen gegenüber dem letzten Commit anzeigen | git diff HEAD |
| Nur gestagte Änderungen anzeigen | git diff --staged |
| Nur nicht gestagte Änderungen anzeigen | git diff |
18.5.6 Unterschiede zwischen Commits analysieren (diff, show)
| Aufgabe | Befehl |
|---|---|
| Änderungen eines Commits anzeigen | git show <commit> |
| Zwei Commits vergleichen | git diff <commit1> <commit2> |
| Änderungen einer Datei seit einem Commit anzeigen | git diff <commit> <datei> |
| Zusammenfassung eines Diffs anzeigen | git diff <commit> --stat |
| Zusammenfassung eines Commits anzeigen | git show <commit> --stat |
18.5.7 Commit-Referenzen
Folgende Schreibweisen können anstelle von <commit> verwendet werden:
| Referenztyp | Beispiel |
|---|---|
| Branch | main |
| Tag | v0.1 |
| Commit-ID | 3e887ab |
| Remote-Branch | origin/main |
| Aktueller Commit | HEAD |
| Drei Commits zurück | HEAD~3 oder HEAD^^^ |
18.5.8 Änderungen verwerfen (restore, checkout, reset, clean, stash)
| Aufgabe | Befehl |
|---|---|
| Nicht gestagte Änderungen einer Datei verwerfen | git restore <datei> |
| Alternative Schreibweise | git checkout <datei> |
| Gestagte und nicht gestagte Änderungen einer Datei verwerfen | git restore --staged --worktree <datei> |
| Alternative Schreibweise | git checkout HEAD <datei> |
| Alle lokalen Änderungen verwerfen | git reset --hard |
| Unversionierte Dateien löschen | git clean |
| Änderungen temporär speichern | git stash |
18.5.9 Historie bearbeiten (reset, rebase, reflog, commit)
| Aufgabe | Befehl |
|---|---|
| Letzten Commit rückgängig machen | git reset HEAD^ |
| Letzte fünf Commits zusammenfassen | git rebase -i HEAD~6 |
| Fehlgeschlagenen Rebase analysieren | git reflog <branch> |
| Auf früheren Commit zurücksetzen | git reset --hard <commit> |
| Letzten Commit bearbeiten | git commit --amend |
18.5.10 Historie analysieren (log, blame)
| Aufgabe | Befehl |
|---|---|
| Historie eines Branches anzeigen | git log main |
| Historie als Graph anzeigen | git log --graph main |
| Kompakte Historie anzeigen | git log --oneline |
| Historie einer Datei anzeigen | git log <datei> |
| Historie einer umbenannten Datei verfolgen | git log --follow <datei> |
| Commits mit bestimmtem Text finden | git log -G "Suchbegriff" |
| Letzte Änderung jeder Zeile anzeigen | git blame <datei> |
18.5.11 Branches zusammenführen (merge, rebase, cherry-pick)
Im Folgenden werden verschiedene Möglichkeiten dargestellt, um Änderungen von einem Branch in einen anderen zu integrieren. Je nach Anwendungsfall und gewünschtem Ergebnis können unterschiedliche Strategien gewählt werden. Als Beispiel wird die Integration von Änderungen aus einem Feature-Branch (hier: banana) in den Hauptbranch (main) betrachtet [1].
18.5.11.1 Rebase
Überträgt die Commits des aktuellen Branches auf die Spitze eines anderen Branches und erzeugt eine lineare Historie.
git switch banana
git rebase mainVorher:
Nachher:
18.5.11.2 Merge
Führt zwei Entwicklungszweige über einen Merge-Commit zusammen.
git switch main
git merge bananaVorher:
Nachher:
18.5.11.3 Squash-Merge
Fasst alle Änderungen eines Branches in einem einzigen Commit zusammen.
git switch main
git merge --squash banana
git commitVorher:
Nachher:
18.5.11.4 Fast-Forward-Merge
Verschiebt den Branch-Zeiger, sofern keine parallelen Änderungen existieren.
git switch main
git merge bananaVorher:
Nachher:
18.5.11.5 Einzelnen Commit übernehmen
Überträgt einen bestimmten Commit auf den aktuellen Branch.
git cherry-pick <commit>Vorher:
Nachher:
18.5.12 Alte Dateiversionen wiederherstellen (checkout, restore)
| Aufgabe | Befehl |
|---|---|
| Datei aus einem früheren Commit wiederherstellen | git checkout <commit> <datei> |
| Alternative Schreibweise | git restore <datei> --source <commit> |
18.5.13 Remote-Repositories verwalten (remote)
| Aufgabe | Befehl |
|---|---|
| Remote-Repository hinzufügen | git remote add <name> <url> |
18.5.14 Änderungen veröffentlichen (push)
| Aufgabe | Befehl |
|---|---|
Branch main auf Remote origin übertragen |
git push origin main |
| Aktuellen Branch auf seinen remote Branch übertragen | git push |
| Neuen Branch veröffentlichen | git push -u origin <branch> |
| Force-Push durchführen | git push --force-with-lease |
| Tags übertragen | git push --tags |
18.5.15 Änderungen abrufen (fetch, pull)
| Aufgabe | Befehl |
|---|---|
| Änderungen herunterladen | git fetch origin main |
| Änderungen herunterladen und rebasen | git pull --rebase |
| Änderungen herunterladen und mergen | git pull origin main |
| Änderungen herunterladen (Standardverhalten) | git pull |
18.5.16 Git konfigurieren (config)
| Aufgabe | Befehl |
|---|---|
| Benutzername setzen | git config user.name "Ihr Name" |
| Option global setzen | git config --global <option> |
| Alias definieren | git config alias.st status |
| Verfügbare Optionen anzeigen | man git-config |
18.5.17 Wichtige Git-Dateien
| Datei | Beschreibung |
|---|---|
.git/config |
Lokale Repository-Konfiguration |
~/.gitconfig |
Globale Benutzerkonfiguration |
.gitignore |
Liste der zu ignorierenden Dateien und Verzeichnisse |
18.6 Best Practices
Ein professionelles Quellcodemanagement bildet die Grundlage für eine nachvollziehbare, reproduzierbare und kollaborative Softwareentwicklung. Die folgenden Best Practices haben sich in der Praxis bewährt und unterstützen sowohl Einzelentwickler als auch Entwicklungsteams bei der effizienten Verwaltung von Quellcode.
Regelmässige Commits: Commits sollten regelmässig erstellt werden, um abgeschlossene Arbeitsschritte in der Versionshistorie festzuhalten. Jeder Commit stellt einen definierten Zustand des Projekts dar, auf den bei Bedarf zurückgegriffen werden kann. Kleine, häufige Commits erleichtern die Fehlersuche, erhöhen die Nachvollziehbarkeit von Änderungen und ermöglichen eine sichere Rückkehr zu früheren Entwicklungsständen.
Aussagekräftige Commit-Nachrichten: Jeder Commit sollte mit einer präzisen und verständlichen Commit-Nachricht dokumentiert werden. Eine gute Commit-Nachricht beschreibt nicht nur die vorgenommenen Änderungen, sondern erläutert auch deren Motivation und Zielsetzung. Dadurch wird die Entwicklungshistorie langfristig nachvollziehbar und dient anderen Projektbeteiligten als wertvolle Informationsquelle.
Mit aktuellem Stand der Codebasis arbeiten: In kollaborativen Projekten wird die Codebasis kontinuierlich von mehreren Entwicklern erweitert und angepasst. Vor Beginn neuer Arbeiten sollte daher sichergestellt werden, dass die lokale Arbeitskopie auf dem aktuellen Stand des zentralen Repositories ist. Regelmässiges Aktualisieren der lokalen Codebasis reduziert die Wahrscheinlichkeit von Merge-Konflikten und erleichtert die Integration eigener Änderungen.
Überprüfen von Änderungen vor dem Commit: Vor dem Erstellen eines Commits sollten alle Änderungen sorgfältig überprüft werden. Hierfür kann der Staging-Bereich genutzt werden, um gezielt festzulegen, welche Modifikationen Bestandteil des nächsten Commits werden. Eine bewusste Auswahl und Kontrolle der Änderungen trägt zu einer klaren und konsistenten Versionshistorie bei.
Konsequente Nutzung von Branches: Branches ermöglichen die Entwicklung neuer Funktionen, Fehlerkorrekturen oder Experimente unabhängig von der Hauptentwicklungslinie. Durch die Trennung unterschiedlicher Entwicklungsaufgaben können mehrere Personen gleichzeitig an einem Projekt arbeiten, ohne sich gegenseitig zu beeinflussen. Nach erfolgreicher Fertigstellung werden die Änderungen kontrolliert in die Hauptentwicklung integriert.
Definition eines gemeinsamen Workflows: Versionsverwaltungssysteme bieten eine hohe Flexibilität hinsichtlich der Zusammenarbeit. Um eine konsistente und effiziente Entwicklung zu gewährleisten, sollten sich Teams auf einen gemeinsamen Workflow einigen. Dieser definiert beispielsweise Regeln für das Erstellen von Branches, den Umgang mit Pull Requests sowie den Integrationsprozess von Änderungen. Einheitliche Vorgehensweisen fördern die Zusammenarbeit und reduzieren Kommunikationsaufwand sowie Fehlerquellen.