Digital Adventures

ArchLinux, Ruby, Rails, OpenSource

German Git Introduction

While cleaning up my desktop I stumbled across a file called git.md. It turned out to be a short introduction to Git that I wrote for a few fellow students at the beginning of a joint project. Over the years I’ve had several requests for the document in question and it was always lost somewhere in the depths of my home directory or in the gitlab wiki of some university project. I’ve decided to publish the introduction here on my blog mainly for archiving purposes. That’s why I won’t bother translating it into english.

Anfangen

SSH key hinterlegen

Zu allererst muss man in gitlab einen ssh schlüssel hinterlegen. Damit authentifiziert man sich beim server und stellt sicher dass nicht einfach jeder den code ändern/einsehen darf.

Ein neuer schlüssel kann mit ssh-keygen erzeugt werden. Der öffentliche Schlüssel wird jetzt in gitlab hinterlegt.

From scratch

Will man ein komplett neues repository erstellen gibt man in einem leeren Verzeichnis git init ein. Dies erzeugt die für git notwendige Verzeichnisstruktur.

Um das lokale repository mit upstream zu synchronisieren muss man eine remote repository angeben. Das geschieht mit git remote add origin upstream_url. Die url findet man in gitlab im Projekt oben rechts. Hier sollte man ssh auswählen.

From existing repository

Wenn ein (nicht leeres) repository schon existiert kann man dies mit git clone upstream_url “clonen”. Die url findet man in gitlab im Projekt oben rechts. Hier sollte man ssh auswählen. Wenn man ein repository klont muss man kein remote repository angeben, da die an clone übergebene url als upstream eingestellt wird.

Einstellungen

Mit git config lassen sich verschiedenste Einstellungen setzen. Hier die wichtigsten:

  • git config user.name Name des Autors
  • git config user.email E-Mail-Addresse des Autors
  • git config core.autocrlf true CR am Ende von Zeilen auf Windows rechnern löschen
  • git config core.whitespace Verschiedene whitespace-Vorkommnisse als Fehler behandeln:
    • blank-at-eof Leere Zeilen am Ende einer Datei
    • blank-at-eol Whitespace am Ende einer Zeile
    • cr-at-eol CR am ende einer Zeile
    • indent-with-non-tab Zeilen mit spaces anstelle von tabs eingerückt
    • space-before-tab Spaces vor tabs in erster einrückung einer Zeile
    • tab-in-indent Tabs als einrückung Um mehrere Optionen zu aktivieren mit komma trennen. Vorschlag für uns: git config core.whitespace blank-at-eof,blank-at-eol,cr-at-eol,tab-in-indent

Status anzeigen

Den status des repositories kann man sich mit git status anzeigen lassen. Dort wird aufgelistet welche dateien

  • geändert (modified)
  • gelöscht (deleted)
  • hinzugefügt (untracked)

worden sind und noch nicht committed wurden.

Stagen

Um Änderungen für einen anstehenden commit vorzumerken müssen diese erst mit git add ordner-oder-dateiname “gestaged” werden. Wird ein Ordner gestaged, geschieht das rekursiv! Wichtig: auch gelöschte dateien müssen gestaged werden. Das geschieht mit git rm dateiname.

Will man nur teile der Änderungen einer datei zu einem commit hinzufügen kann man mit git add -i (für interactive) einen prompt öffnen. Dort gibt man p für patch ein und wählt die datei(en) aus die betroffen sind. Danach fragt git bei jeder änderung nach, ob diese gestaged werden soll.

Sind alle gewünschten änderungen gestaged, kann committed werden.

Ignorieren

Oft fallen Dateien in einem Projekt an, die man nie im upstream haben möchte wie zum Beispiel binärdateien die durch kompilieren erzeugt werden können oder im Falle von LaTeX pdf dateien. Dafür gibt es die datei .gitignore. Diese liegt im Wurzelverzeichnis jedes Projekts bzw. muss dort angelegt werden.

Dateien mit binärformat sollten generell vermieden werden, da git nicht besonders gut damit umgehen kann und mergen immer heißt “seins” oder “meins” und zwar ganz oder gar nicht. Außerdem bläht sich das repository durch Binärdateien auf.

Eine .gitignore-Datei kann folgendermaßen aussehen:

1
2
3
4
5
6
# Vim swap dateien ignorieren
*.swo
*.swp

# Alle dateien im bin Verzeichnis ignorieren
bin/*

Wenn eine datei länger in untracked files herumlungert, ist es meistens ein Fall für git rm oder .gitignore.

Committen

Mit git commit können änderungen committed werden. Ein commit fasst Änderungen unter einer Beschreibung zusammen sodass jedem projektmitglied klar wird, warum die änderung getätigt wurde.

Was sollte committed werden?

Ein commit sollte immer soviel wie nötig und so wenig wie möglich Änderungen enthalten. Der commit sollte auch nichts kaputt machen. Wenn das unvermeidlich ist -> feature branch.

Commit messages

Eine commit message kann aus zwei teilen bestehen:

  • Header: Hier wird in ~70 zeichen zusammengefasst was durch den commit verändert wurde.
  • Body: Hier wird, wenn nötig, genauer auf die Änderungen eingegangen.

Getrennt werden die Teile mit einer leeren Zeile.

Der Header kann hier als Richtlinie genuzt werden: Kommt man nicht unter 70 Zeichen für den Header, hat man womöglich zu viel in einen einzelnen commit gepackt.

Gitlab unterstützt gitlab flavored markdown auch in commit messages.

Pushen

Ist man mit seinen Änderungen durch und hat sie in commits verfasst, kann gepusht mit werden mit git push. Wird zum ersten mal gepusht, sollte man mit der option -u den upstream setzen. Bsp.: git push -u origin master.

Anders als bei svn und anderen vcs arbeitet kan in git immer erst lokal. D.h. commits fließen erst in den upstream wenn gepusht wird. Vorher sind sie nur im lokalen repository vorhanden.

Als Richtlinie: git status sollte “Working directory clean” ausgeben. So kann man sicher gehen, dass nichts vergessen wurde, denn wenn einmal etwas gepusht wurde kann es nur sehr schwierig wieder rückgängig gemacht werden.

Pullen

Wie der name vermuten lässt ist pullen das gegenteil von pushen. Mit git pull können alle neuen änderungen seit dem letzten pull vom upstream geholt werden. Ein pull macht zweierlei dinge: Es holt den code von dem remote repository und versucht ihn mit dem lokalen stand zu mergen.

Das klappt meistens sehr gut, außer wenn zwei commits beide eine Änderung der selben zeile in der selben datei aufweisen oder eine datei in einem commit gelöscht und im anderen verändert wurde. In diesem Fall tritt ein merge conflict auf.

Konflikte beheben

Konflikte können - und werden - immer mal auftreten. Dann sollte man sich in der Regel mit dem verfasser des anderen commits zusammen setzen und den Merge zu zweit durchführen. Ein merge conflict verändert die dateien die betroffen sind z.B. folgendermaßen:

1
2
3
4
5
6
the number of planets are
<<<<<<< HEAD
nine
=======
eight
>>>>>>> master

Git schreibt also einen sogenannten “conflict block” in die datei. Gekennzeichnet wird dieser durch die >>>>>>>. Die änderungen werden mit ======= voneinander getrennt. Um den konflikt zu beheben muss eine der beiden änderungen im conflict block verworfen oder eine komplett neue Änderung eingetragen werden. Im Beispiel oben wären folgende Lösungen möglich:

1
2
the number of planets are
nine
1
2
the number of planets are
eight
1
2
the number of planets are
nine or eight, depending on who you ask

Wichtig ist, dass in der datei später keine conflict blöcke mehr vorhanden sind.

Ist der konflikt behoben, können die betroffenen dateien mit git add gestaged und mit git commit committed werden.

Rebasen

Hat man einen commit gemacht diesen aber noch nicht gepusht und möchte etwas dazu hinzufügen bzw. entfernen, den commit komplett verwerfen oder mit einem anderen zusammenführen kann git rebase -i genuzt werden. Es öffnet sich dann ein texteditor in dem man die commits bearbeiten kann.

Blamen

Will man wissen, wer welchen teil einer Datei das letzte mal geändert hat, kann man dies mit git blame dateiname anzeigen lassen.

Branchen

Branches sollten genutzt werden, wenn man plant viele Änderungen zu machen, die isoliert von der hauptentwicklung durchgeführt werden können/sollten. Beispiel: neues feature einbauen.

Ein neuer branch kann mit git branch branchname erstellt werden. Will man nun in diesem branch arbeiten kann mit git checkout branchname in diesen gewechselt werden. Ein branch lässt sich mit git branch -d branchname wieder löschen.

Nicht vergessen: alle änderungen sind lokal bis gepusht wird. Beim push von einem neuen branch muss der upstream wieder gesetzt werden mit git push -u origin branchname.

Will man die Änderungen aus einem Branch wieder in den ursprünglichen Branch (z.B. master) zurückführen, wechselt man mit git branch branchname in den Ziel-branch und führt git merge feature-branch-name aus.

History anzeigen

Mit git log lassen sich die commits aller Änderungen anzeigen lassen.

Debuggen

Git kann sehr schön zum debuggen genutzt werden. Tritt ein bug auf kann man mit git bisect sehr schnell (in log(n)) schritten herausfinden in welchem commit der fehler eingeflossen ist. Dafür startet man die suche mit git bisect start und markiert den aktuellen status als fehlerhaft mit git bisect bad.

Als nächstes geht man zurück auf einen Stand, wo man weiß dass der Fehler noch nicht aufgetreten ist. Dazu kann man sich die history mit git log anzeigen lassen, einen fehlerfreien commit finden und dessen commit hash kopieren und dann mit git checkout commithash auf den stand zurück gehen. Wenn der code zu diesem zeitpunkt funktioniert kann git bisect good aufgerufen werden.

Danach wechselt git automatisch in auf einen stand zwischen den beiden commits. Man testet den code wieder und ruft wieder git bisect good|bad auf usw. bis der commit, der den Fehler produziert hat gefunden wurde.

Zum Schluss wird der bisect mit git bisect reset beendet.

Hooks

In .git/hooks können skripte abgelegt werden die vor/nach verschiedenen Arbeitsschritten von Git ausgeführt werden sollen. Beispielsweise könnte in .git/hooks/pre-commit ein code style validator ausgeführt und im Zweifelsfall der commit abgebrochen werden.

Cheat sheet

  • git clone: remote repository klonen
  • git init: neues (leeres) repository anlegen
  • git status: status anzeigen
  • git add: datei(en) stagen
  • git commit: änderungen die gestaged wurden zu einem commit zusammenfassen
  • git push: commit(s) in upstream pushen
  • git pull: lokales repository mit upstream abgleichen
  • git rebase: lokale commits ändern/ordnen/löschen.
  • git blame: Authoren von Änderungen in einer datei anzeigen lassen.
  • git log: Alle commits ausgeben
  • git bisect: Binärsuche um bugs zu finden

Zu allen Befehlen lässt sich mit git help befehl eine manpage öffnen, die den Befehl im Detail erklärt.

Literatur:

Git Pro - Sehr zu empfehlen. Deckt alles von einfachen bis sehr fortgeschrittenen themen ab.

Comments