Git ist ein verteiltes Versionskontrollsystem (VCS), das es Entwicklern ermöglicht, den Quellcode eines Projekts über die Zeit hinweg nachzuvollziehen, zu verändern und zu verwalten. Ursprünglich von Linus Torvalds entwickelt, um den Entwicklungsprozess des Linux-Kernels zu unterstützen, hat sich Git zu einem Standard-Tool in der Softwareentwicklung entwickelt.
Git unterscheidet sich von zentralisierten VCS wie Subversion (SVN), da es vollständig dezentral arbeitet. Jeder Entwickler hat eine vollständige Kopie des gesamten Repositorys, einschließlich der Historie. Dies macht Git sehr robust gegenüber Netzwerkproblemen und ermöglicht paralleles Arbeiten ohne zentrale Abhängigkeiten.
Im Gegensatz zu vielen älteren VCS, die nur Änderungen (Deltas) speichern, basiert Git auf einem Snapshot-Modell. Jeder Zustand des Repositorys wird als vollständiger Snapshot der Dateien gespeichert. Git optimiert dies jedoch durch einen Mechanismus, der unveränderte Dateien nicht erneut speichert, sondern auf die existierenden Daten verweist.
Wenn ein Entwickler eine Änderung committet, erstellt Git eine Momentaufnahme (Snapshot) des aktuellen Projektzustands. Jede dieser Momentaufnahmen wird durch einen eindeutigen Hash identifiziert, der auf dem Inhalt der Dateien, dem Commit-Metadaten und dem Eltern-Commit basiert. Dies bedeutet, dass zwei identische Dateien immer denselben Hash erhalten, unabhängig von ihrem Speicherort im Repository.
Git verwendet kryptografische SHA-1-Hashes zur Identifizierung von Objekten im Repository. Jeder Commit, jeder Baum und jede Datei wird durch einen eindeutigen Hash repräsentiert. Dieser Hash garantiert die Integrität der Daten, da jede Änderung sofort zu einem neuen Hash führt. Folgende Objekttypen werden in Git gespeichert:
Die Art und Weise, wie Git Objekte hasht und diese Beziehungen verwaltet, ähnelt einem Merkle Tree. Bei einem Merkle Tree handelt es sich um eine baumartige Datenstruktur, bei der jeder Knoten durch den Hash seiner Kinderknoten bestimmt wird. Eine Änderung in einem Teil des Baumes führt dazu, dass sich der Hash des gesamten Baumes ändert. Dies stellt sicher, dass jede Modifikation in Git sofort erkennbar ist und die Datenintegrität jederzeit gewahrt bleibt.
Ein Git-Repository besteht aus einer Verzeichnisbaumstruktur, in der die Dateien des Projekts organisiert sind. Der oberste Tree ist der Wurzelbaum, der in weiteren Trees und Blobs strukturiert ist. Diese hierarchische Struktur erlaubt es Git, Verzeichnisse effizient darzustellen und die Änderungen zwischen Commits präzise zu verfolgen.
Ein wichtiger Aspekt dieser Baumstruktur ist, dass sie nicht nur die Dateien im Projekt darstellt, sondern auch die Verbindungen zwischen verschiedenen Versionen. Jeder Commit verweist auf einen Baum und ermöglicht dadurch die Rekonstruktion des Projekts zu einem bestimmten Zeitpunkt.
Commits sind das zentrale Element in Git. Ein Commit stellt eine abgeschlossene Einheit dar, die den Zustand des Projekts zu einem bestimmten Zeitpunkt beschreibt. Jeder Commit enthält die folgenden Informationen:
Git verwendet diese Commit-Historie, um eine lineare oder verzweigte Zeitachse des Projektfortschritts zu erstellen. Durch die kryptografischen Hashes ist jeder Commit eindeutig und kann nicht nachträglich verändert werden, ohne dass dies in der Historie sichtbar wird.
Ein Branch in Git ist nichts weiter als ein Label, das auf einen spezifischen Commit verweist. Dieser Commit stellt den aktuellen Stand der Arbeit auf diesem Branch dar. Branches sind in Git extrem leichtgewichtig, da sie nur den Hash des letzten Commits speichern.
Ein Branch kann jederzeit verschoben werden, indem der Referenz-Commit geändert wird. Dies unterscheidet Branches in Git von traditionellen VCS, wo Branches oft schwergewichtiger und komplexer zu handhaben sind.
Die HEAD-Datei in Git spielt eine zentrale Rolle.
Sie zeigt immer auf den aktuell ausgecheckten Branch oder Commit. Wenn
du auf einem Branch arbeitest, enthält die Datei einen Pfad zu
refs/heads/branch_name
, der wiederum den Hash des obersten
Commits dieses Branches enthält.
Wenn du direkt auf einen Commit arbeitest, zeigt HEAD
auf den Hash dieses Commits, was als “detached HEAD” bezeichnet
wird.
$ cat .git/HEAD
ref: refs/heads/main
Dieser Eintrag zeigt, dass der aktuelle Branch main
ist
und dass die Datei refs/heads/main
den Hash des obersten
Commits enthält.
Die Staging Area, auch als Index bekannt, ist eine Art Zwischenspeicher, in dem Änderungen vor dem Commit gesammelt werden. Sie ermöglicht es Entwicklern, gezielt einzelne Änderungen zum nächsten Commit hinzuzufügen, anstatt alle Modifikationen im Arbeitsverzeichnis sofort zu committen. Nur Änderungen, die in die Staging Area übernommen wurden, werden in den nächsten Commit aufgenommen.
Die Staging Area erlaubt eine feingranulare Kontrolle über den Commit-Prozess und bietet Entwicklern die Möglichkeit, ihre Arbeitsschritte besser zu strukturieren.
Git unterscheidet zwischen lokalen und Remote-Repositories. Ein lokales Repository befindet sich auf dem Rechner des Entwicklers, während ein Remote-Repository auf einem Server liegt und von mehreren Entwicklern gemeinsam genutzt werden kann. Das zentrale Konzept in Git ist jedoch, dass das lokale Repository vollständig unabhängig vom Remote-Repository ist. Änderungen werden lokal erfasst und können später synchronisiert werden.
Die Kommunikation zwischen lokalem und Remote-Repository erfolgt
typischerweise über Befehle wie git fetch
,
git pull
und git push
. Diese Befehle
ermöglichen es, Änderungen aus dem Remote-Repository abzurufen oder
lokale Änderungen hochzuladen.