Die git Basics

git ist einer der wohl mächtigsten und auch besten Progamme zur Versionskontrolle, die es gibt. Jedoch ist git, gerade für Beginner, nicht einfach zu verstehen. Es gibt viele Besonderheiten, welche es von anderen, ähnlichen Programmen unterscheidet.

Dies ist eine Einführung in die Basics von git, um überhaupt erstmal zu verstehen, was git eigentlich wirklich ist und macht.

Für eine ausführliche Erklärung von git in all seiner Komplexität empfehle ich diese Website: git-scm.com/

Was macht git eigentlich?

Bei allen Projekten, ob nun im Softwarebereich oder nicht, empfiehlt es sich dringend, eine gute Dokumentation zu führen, um transparent zu arbeiten. Dies ermöglicht es, vergangene Änderungen wieder nachzuvollzuziehen und bei Bedarf auch wiederherzustellen. Für Software wird dies Versionskontrolle genannt.

Versionskontrolle (Software-Configuration-Management, kurz SCM)

DVCS (Distributed version control)
Ein verteiltes Versionskontrollsystem, jeder Client erhält das gesamte Repository inklusive aller Versionen. Zum Bearbeiten muss dieser nicht mit dem Server verbunden sein, Änderungen können auch lokal verfolgt werden.


Repository
Die Verzeichnisstrukur oder Datenbank, bei der alle Daten in jeder Version vorhanden sind.


Checksum
Die Prüfsumme wird aus den Daten vom Sender und Empfänger berechnet und abgegeglichen, womit Bitfehler erkennt werden können.

Und git ist genau das, ein Versionkontrollprogramm. Besonders ist, dass es ein DVCS ist und jeder alle Versionen erhält. Wenn man also einem Projekt neu beitritt hat man nicht nur Zugriff auf die neuste Version, sondern auch auf alle vorangegangenen Änderungen und Versionen. All dies wird in einem Repository gespeichert.

Während viele andere SCMs nur die Diffs (Änderungen) speichert, erstellt git dagegen Snapshots (Abbilder). Diese sind komplett eigenständige Versionen, dabei sind nicht veränderte Daten nicht erneut gespeichert, aber es wird auf sie referenziert, wodurch Speicherplatz nicht unnötig verbraucht wird.

Der wohl größte Vorteil ist dabei, dass die Dateien lokal vorliegen und somit auch ohne Internetanbindung am Projekt gearbeitet und auf vorherige Versionen zugriffen werden kann. Ebenso ermöglicht es das Branching (Verzweigungen, im späteren Kapitel ausführlich erklärt). Hierbei wird die aktuelle Version erst dupliziert, sodass dann parallel an beiden gearbeitet werden kann, ohne das jeweils Andere zu beinflussen. So können verschiedene Features simultan implementiert und entwickelt werden, bevor sie am Ende wieder in den Master-Branch eingefügt werden.

Als Kontrolle wird jede Änderung noch in eine Checksum (Prüfsumme) umgerechnet und verglichen, womit sichergestellt wird, dass alle Änderungen auch wirklich komplett und korrekt vorhanden sind.

Wie macht es das?

Grundlagen

git kennt drei verschiedene Zustände für Dateien: staged, modified und commited. Da das Staging oft übersprungen wird, werden wir diesen Zustand hier  (auch der Einfachheit halber) auslassen.

Eine Datei ist modified, wenn an ihr etwas verändert wurde, sie liegt also nicht mehr in dem Zustand vor, wie sie in der aktuellsten Version gespeichert ist.

Eine Datei ist committed, wenn sie zur Datenbank hinzugefügt wurde. Nachdem also eine Datei bearbeitet wurde (also modified ist), findet ein Commit statt, hierbei erstellt git einen Snapshot, welcher auch im git-Verzeichnis gespeichert wird.

Arbeitsweise

Für das Arbeiten mit git bedeutet dies folgendes:

Es gibt ein git-Verzeichnis, das Repository, in welchem alle relevanten Daten (die Dateien in der Datenbank selbst, aber auch Metadaten) speichert. Dann gibt es noch das normale Arbeitsverzeichnis. In dem Repository werden NIE Daten bearbeitet, es ist quasi das Archiv des gesamten Projektes.

Sobald man nun an einer Version arbeiten will, findet ein Checkout statt, dabei wird die gewollte Version geklont und im Arbeitsverzeichnis gespeichert. Hier können alle Daten modifiziert, gelöscht und erstellt werden. Am Ende findet dann der Commit statt und eine neue Version wird im Repository gespeichert.

Was muss ich dafür tun?

Installation

Die einfachste Variante, um git auf Linux zu installieren, ist über einen Paketmanager. Hier der Befehl für ein Debian System:  $ sudo apt-get install git

Auf dem Mac bietet sich dieser Installer an: sourceforge.net

Für Windows empfehle ich das msysGit Projekt, da neben der Version für die Kommandozeile auch eine grafische Oberfläche mit installiert wird: msysgit.github.com

Konfiguration

Bevor wir mit der Verwendung von git starten, sollten noch ein paar Dinge eingestellt werden.

Zuerst setzen wir den Namen des Benutzers mit: $ git config --global user.name "Name" (wobei wir   Name in den ” ” mit dem des Benutzers ersetzen)

Das gleiche machen wir mit der Email-Adresse: $ git config --global user.email "Email"

Ohne das   --global wird dieser Wert nur für das aktuelle Projekt verändert (um zum Beispiel an einem einzelnen Projekt mit einem anderen Alias zu arbeiten).

Diese Informationen werden in jedem Commit mit gespeichert, um nachvollziehen zu können, wer welche Änderungen gemacht hat.

Die Hilfe von git zu einem Schlagwort gibt es über:  $ git help <schlagwort>

Wie arbeite ich mit git?

Anlegen eines Repositories

Hierbei muss unterschieden werden zwischen dem Erstellen eines neuen Repositories oder dem Klonen eines schon vorhandenen Repositories.

Wenn wir ein neues Projekt starten oder ein schon vorhandenes Projekt mit git sichern wollen, wechseln wir zuerst in dieses Verzeichnis. (Für Windows: Einfach in dem Verzeichnis ein Rechtsklick auf eine freie Stelle machen und dann auf ‘Git Bash hier’ klicken, somit ist man automatisch im richtigen Verzeichnis. Alternativ geht dies auch mit den bekannten Bash-Befehlen, wie cd)

Dann führen wir den Befehl aus, der das Grundgerüst des Repositories erstellt (ein Ordner mit dem Namen .git wird hierfür automatisch erstellt): $ git init

Um die erste, initiale Version zu erstellen, fügen wir zuerst alle Daten im Verzeichnis hinzu:  $ git add * (hier ist * eine Wildcard, es werden einfach alle Dateien hinzugefügt)

Danach machen wir unseren ersten Commit : $ git commit -m 'first commit' (-m fügt eine Nachricht an, die direkt danach in den ‘ ‘ folgt und möglichst verständlich sein sollte)

Damit haben wir ein Repository und auch gleich die erste Version darin erstellt!

 

SSH Protokoll
SSH ist ein Netzwerkprotokoll um die Verbindung mit entfernten Geräten herzustellen und benutzt diesen Syntax: benutzername@host:/

Wenn wir nun ein schon existierendes Repository klonen wollen, um selbst dran zu arbeiten, tut git eben auch genau das, es erstellt ein komplettes Abbild des gesamten Systemes und nicht nur der neuesten Version.

Um zum Beispiel ein Github Projekt zu clonen,  wird  $ git clone <url> verwendet: $ git clone https://github.com/HackHerz/pusher

Hierbei funktionieren auch andere Protokolle, zum Beispiel  git:// und auch das SSH Protokoll.

Arbeiten mit dem Repository

Um unter git den aktuellen Status aller Dateien zu erhalten, verwenden wir: $ git status

glob patterns
Glob Patterns ermöglichen es, mit einem Set an Ausdrücken viele verschiedene Dateien zu spezifizieren, z.B.: *, ? oder [abc]


Markdown
Markdown ist eine sogenannte Auszeichnungssprache. Mit ihr lässt sich Text sehr einfach formatieren (fett, kursiv, etc.).

Damit eine neue Datei mit ins Repository aufgenommen wird, muss dies explizit mit dem  $ git add <Datei> Befehl hinzugefügt werden. Dies funktioniert genau so mit ganzen Verzeichnissen. ABER ACHTUNG! Auch schon versionierte Daten müssen, nachdem sie verändert wurden, wieder mit diesem Befehl zum neuen Commit hinzugefügt werden. Wird dies nicht getan, bleibt die Datei im Repository auf ihrer alten Version. Hier ist zu beachten, dass eine Datei so committed wird, wie sie in dem Moment des  $ git add Befehles ist. Sollte die Datei nach dem Befehl weiter modifizert werden, werden diese Änderungen nicht mit aufgenommen.

Nun zu einer noch recht wichtigen Datei für das Repository, die  .gitignore Datei. Bei dieser werden die Standard glob patterns verwendet. Dateien, die darauf passen, werden automatisch von git ignoriert. Dies macht besonders bei temporären Dateien Sinn. Schon vorgeschriebene .gitignore können hier gefunden werden: github.com

Eine letzte Datei, die in jedem Repository vorliegen sollte, ist eine simple  README Datei, in der alle relevanten Infos zum Projekt zu finden sind. Diese wird meist in Markdown geschrieben.

Arbeiten mit externen Repositories

Meistens wird bei git mit anderen Leuten zusammengearbeitet, das heißt, dass es externe Repositories gibt, also Versionen vom Projekt, die nicht lokal auf deinem PC liegen. Mit  $ git remote -v werden einem alle externen Repositories (bzw. dessen Kurznamen) inklusive Link angezeigt. Um ein neues externes Repository hinzuzufügen, verwenden wir diesen Befehl:  $ git remote add <kurzname> <url>

Nehmen wir nun an, dass wir ein Projekt geklont haben und wir unser Repository auf den neusten Stand bringen wollen. Das originale Repository hat als Kurznamen dann den Namen origin. Um jetzt alle Neuigkeiten zu erhalten, schreiben wir  $ git fetch origin (wobei  origin hier auch ein beliebiger anderer Kurzname sein kann)

Wollen wir, dass git auch gleich alle Änderungen bei uns vornimmt und merged, verwenden wir statt fetch nun einfach pull.

Nachdem wir ein paar Dinge geändert haben, wollen wir nun unsere Version (nach dem Commit, unser Repository ist also auf dem aktuellen Stand) in das  origin Repository hochladen. Hierfür verwenden wir: $ git push origin master (hier ist  origin wieder der Kurzname und   master der Branch, auf den wir pushen wollen)

Und was sind jetzt Branches?

Beim Branching erstellen wir einen Zweig von der normalen Entwicklungslinie, dem   master Branch und arbeiten an diesem neuen Zweig unabhängig weiter. Dieser muss dann am Ende natürlich auch wieder mit dem   master Branch verbunden, oder merged, werden. Dies ist eine, wenn nicht sogar die, größte Unterscheidung zu anderen SCMs, denn git löst dies besonders unkompliziert und gut.

Erklärung

Um das Prinzip besser zu verstehen, müssen wir uns wieder klar werden, wie git  Änderungen speichert, als Snapshots, also komplette Versionen. Bei einem Commit wird ein Objekt erstellt, welches auf den jeweiligen Snapshot der Daten “zeigt”. Somit hat ein Commit (abgesehen vom Ersten) immer so genannte Parent-Commits, dies sind die vorherigen Commits.

Bei einem neuen Branch wird nun ein neuer Zeiger erstellt, welcher am Anfang noch auf den gleichen Commit zeigt, wie der originale Branch. Wenn jetzt Änderungen durchgeführt werden, bewegen sich diese Zeiger unabhängig voneinander, da aber git eben alle Versionen immer gespeichert hat, kann sehr schnell und einfach wieder zu einem anderen Branch gewechselt werden, wodurch die Dateien wieder auf den Zustand gesetzt werden, wie sie dort eben im letzten Commit waren.

Dies eignet sich besonders gut, um neue Features zu implementieren. Man erstellt einfach einen Branch für dieses neue Feature, kann dort probieren und implementieren, während am   master Branch nichts geändert wird. Ist man mit dem Feature Branch zufrieden, so merged man diesen und den   master Branch. Sollte die Featureimplementierung aber scheitern, so kann dieser Branch einfach verworfen werden, ohne dass am   master Branch je etwas passiert ist.

Verwendung

Um einen neuen Branch aus dem Aktuellen (im Regelfall dem   master Branch)  zu erstellen: $ git branch <branch> (hier ist <branch> einfach der Name, den wir dem neuen Branch geben)

Danach befindet man sich aber noch immer im alten Branch, man wechselt mit  $ git checkout <branch_name>

Der Einfachheit halber erstellen und wechseln wir mit diesem Befehl einfach gleich in diesen und überspringen den separaten  checkout Befehl: $ git checkout -b <branch_name>

Ein Branch wird hiermit gelöscht: $ git branch -d <branch_name>

Wenn mit einem externen Repository gearbeitet wird, wollen wir diesen Branch natürlich auch anderen zur Verfügung stellen und pushen (neben  origin sind hier natürlich auch andere Repos möglich): $git push origin <branch>

Und um einen Branch wieder in den Hauptbranch einzufügen, wechseln wir erst in den Branch, in den gemerged werden soll. Dann verwenden wir: $ git merge <branch> Danach kann der alte Branch gelöscht werden. Wurde am Hauptbranch in der Zeit nicht gearbeitet, findet nur ein Fast Forward statt, denn dann stehen keine Daten in Konflikt zueinanander. Dies kann aber durchaus der Fall sein, wenn an beiden Branches gearbeitet wird. Mit $ git status kann eingesehen werden, welche Dateien Probleme machen. Diese Konflikte können manuell gelöst und mit  $ git add nachträglich hinzugefügt werden, es kann aber auch mit dem $ git mergetool (hiervon gibt es, neben dem Standardprogramm von git, diverse) gelöst werden.

Beispiel eines Workflows mit git

  1. Initialisieren eines neuen Projektes mit git init
  2. Daten zur Versionskontrolle hinzufügen mit git add
  3. Diese Änderungen committen mit git commit
  4. [Wiederholung von 1.-3. für ein paar Mal]
  5. Anbindung eines Remote Repositories mit git remote add origin <server>
  6. Hochladen seiner Version mit git push origin master
  7. Kaffeepause
  8. Die neueste Version, die in der Zwischenzeit von anderen Benutzern erstellt wurde, holen mit git pull
  9. Zur neuen Featureimplementierung einen Branch erstellen mit git checkout -b feature_x
  10. [An diesem wie in Schritten 1.-3. arbeiten]
  11. Den Branch mit dem Masterbranch zusammenfügen mit git checkout master, dann git merge feature_x und letztlich git branch -d feature_x
  12. [Ad infinitum]

Fazit

git ist ein unglaublich mächtiges Programm, jedoch sind viele Prinzipe nicht direkt von Beginn an wirklich verständlich. Die Lernkurve ist sehr steil, doch um all die tollen und komplexen Features verwenden zu können, muss erst verstanden werden, was git überhaupt macht.

In dieser Einführung wurden viele, teils auch wichtige, Punkte für eben dieses Verständnis ausgelassen. Die Stage, die Instanzen des Repositories (Arbeitskopie, Index und HEAD) oder das Stashen sind Themen, die früher oder später bekannt sein sollten. Um mit git professionell zu arbeiten ist ein richtiges Einlesen nicht vermeidbar, doch sollte hiermit ein grundlegendes Verständnis geschaffen sein, welches die Anfänge ein wenig erleichtern sollten.

Als kleiner Spickzettel bietet sich die Tabelle unten oder auch diese Website an: rogerdudler.github.io

Für weiterführende Erklärungen empfehle ich den Link zu Beginn dieses Artikels.

Die wichtigsten Befehle

Befehl Funktion
config Bearbeiten von Name und Email, für globale Einstellungen mit –global aufrufen
init Initialisieren eines Repositories
add Hinzufügen einer Datei oder eines Verzeichnisses
commit Um einen Commit aller Daten in der Stage auszuführen, meist mit -m für eine Commit-Message
clone Klonen eines Remote Repositories
status Den Status aller Dateien im git-Verzeichnis einsehen (z.B: untracked, modified, staged)
diff Zeigt die Änderungen zum letzten Commit
log Historie aller Dateien anzeigen (viele Optionen, wie -p, –word-diff oder -stat)
remote Zum Arbeiten mit externen Repositories verwendet, Hinzufügen neuer Repos mit add
push Hochladen seiner Commits in das Remote Repository
pull Aktualisieren seines lokalen Repositories aus dem Remote Repository
tag Zum Hervorheben von wichtigen Dateien und Änderungen
branch Zum Erstellen von Branches
merge Zum Zusammenfügen von Branches
rm löschen einer Datei aus dem Repository
mv Verschieben einer Datei