Lokales .gitignore

Wenn wir in einem Team arbeiten, in dem nicht alle die gleichen Werkzeuge verwenden, wächst die gemeinsame .gitignore-Datei oft an: Schnell umfasst sie Verzeichnisse und Dateien, um Einstellungen unterschiedlicher IDEs auszuschliessen.

Wir können dieses Problem vermeiden, indem wir individuelle Konfigurationseinstellungen gar nicht erst in das projektspezifischen .gitignore aufnehmen. Stattdessen kann jede:r ein persönliches .gitignore lokal erstellen, welches dann für alle Git-Projekte gültig ist.

Dazu legt man sich (typischerweise im eigenen Home-Verzeichnis) ein .gitignore an und trägt seine Standardpfade und Dateien dort ein.

.DS_Store
.vscode

Mit einer einfachen Anpassung der Git-Konfiguration wird diese Blockliste dann auf alle Projekte angewendet:

# Unix

git config --global core.excludesfile ~/.gitignore

#Windows

git config --global core.excludesfile %USERPROFILE%\.gitignore

Separate Konfigurationen mit includeif

Wir können noch weiter gehen und ganze Teile der Konfiguration für spezifische Projekte überschreiben. Private und geschäftliche Identitäten in Projekten trennen? Unterschiedliche Branch-Strategien? Verschiedene Signierschlüssel? Alles kein Problem.

Das Mittel der Wahl sind pfadabhängige Konfigurationen. Dazu trägt man in seiner .gitconfig Folgendes ein:

[includeIf "gitdir:/path/to/project/"]
    path = /path/to/project/.gitconfig

In dieser Konfiguration lassen sich dann beliebige Parameter der Konfiguration setzen und so für ein spezifisches Projekt (oder eine Gruppe davon) überschreiben.

Neben dem Pfad lassen sich auch Branchnamen oder Remotes für das konditionale Einbinden nutzen.

Automatische Konfliktlösung mittels rerere

Arbeitet man in einem Projekt, das via Rebase für eine lineare Historie sorgt, kommt man häufiger in die Verlegenheit, bei einem Rebase auch Merges durchführen und ggf. Konflikte beheben zu müssen.

Damit man nicht immer die gleichen Merges wieder abarbeiten muss, bietet sich rerere (“reuse recorded resolution”) an. Damit merkt sich Git die Merges, die man erfolgreich durchgeführt hat und kann diese erneut anwenden, falls notwendig – sehr praktisch, wenn man seinen eigenen Branch häufiger einmal rebasen muss.

git config --global rerere.enabled 1
git config --global rerere.autoupdate 1

Bessere Diffs

Hat man einen der unausweichlichen Merge-Konflikte, so kann das Einstellen des Darstellungsstils helfen, besser zu verstehen, welche Änderungen passiert sind.

Der Standard-Stil merge zeigt einfach die lokalen und die remote-Version an. Der Stil diff3 fügt zwischen diesen noch die ursprüngliche Fassung der Datei an – man kann also sehen, von welchem Stand aus lokal und Remote Änderungen vorgenommen haben. Mit zdiff3 wird versucht, dabei noch möglichst weitgehend gleiche Zeilen auszublenden und noch stärker auf die eigentlichen Änderungen zu fokussieren.

git config --global merge.conflictStyle diff3|zdiff3

Visueller Branch Checkout

Ist man in diversen Branches gleichzeitig unterwegs, wird es manchmal schwierig, sich alle Namen zu merken, um zum richtigen Branch zu springen (ganz zu schweigen von der Tipperei). Wenn man trotzdem das Terminal für solche Operationen mag, kann man fzf (Fuzzy Find) prima mit den Git Tools mischen, um einen visuellen Checkout zu bekommen:

git config --global alias.fco !git branch | fzf +m | awk '{print $1}' | xargs git checkout

Ein einfaches git fco zeigt danach die Namen der lokalen Branches an und erlaubt es, einen per Pfeiltasten auszuwählen, der dann ausgecheckt wird.

Screenshot der 'git fco' auf der Konsole vorführt
Screenshot der ‘git fco’ auf der Konsole vorführt

Signieren mit SSH-Schlüsseln

Mit Git kann man schon lange Commits signieren und somit verifizierbar machen, dass spezifische Änderungen auch von einer bestimmten Person kommen. Da dies lange Zeit mit dem Verwalten von GPG-Schlüsseln einherging, hat es sich im Firmenkontext nie stark durchgesetzt.

Seit Version 2.34.0 unterstützt Git aber auch die Verwendung von SSH-Schlüsseln für Signaturen. Da diese weiter verbreitet sind, bieten sie eine ideale Grundlage dafür, auch das Signieren von Commits zu etablieren.

Die Konfiguration dafür verteilt sich auf ein paar Einstellungen:

# Das Format auf ssh-keys definieren

git config --global gpg.format ssh

# Keyfile angeben (hier per Pfad, wahlweise auch den eigentlichen Wert)

git config --global user.signingKey 'path/to/yourkey.pub'

# Signiere Commits standardmässig

git config --global commit.gpgsign true

# Signiere Tags standardmässig

git config --global tag.gpgsign true

Die weiter oben vorgestellten projektspezifischen Konfigurationen lassen sich hier hervorragend einsetzen.

Sauberes Bereinigen von Fehlern über commit –fixup

Wenn wir einen Pull Request erstellt haben gibt es oft Feedback in dem noch dies oder jenes bereinigt werden soll. Das führt zu einer Reihe an weiteren Commits deren Commitmessages der Form “Fix xyz” oder “Review comments” folgen und die nicht wirklich viel inhaltliches beitragen.

git rebase hilft, dass wir diese nicht einzeln in der Historie sehen müssen, weil sich über ein interactive Rebase einzelne Commits zu bereits bestehenden hinzufügen lassen (indem wir fixup als die Behandlungsweise auswählen).

Will man jetzt nicht einfach den letzten Commit in der Historie mit allen Anpassungen belasten, kann man die Bereinigungs-Commits durch eine Umsortierung zu den eigentlichen Commits zuordnen. Hier bietet git aber inzwischen auch Unterstützung.

Erstellt man einen Commit per git commit --fixup <SHA> (wobei der Identifier eines bestehenden Commits ist), so ordnet Git den neuen Commit dem bestehenden zu, so dass diese für ein git rebase --interactive bereits in der richtigen Reihenfolge sind.

git rebase kennt zusätzlich den Parameter --autosquash, der alle Commits die per commit --fixup erzeugt worden sind automatisch in den vorhergehenden Commit einfügen, so dass man nur noch git rebase -i --autosquash <branch> ausführen muss, um alle seine Korrekturen automatisch in den eigentlichen Commits unterzubringen. Jordan Elver hat eine etwas ausführlichere Beschreibung dieses Workflows (auf Englisch).

Noch mehr Git

Julia Evans bloggt schon eine ganze Weile über Git (auf Englisch), und auch wenn man glücklicherweise nicht dauernd in die Tiefen gehen muss, sind einige Dinge doch spannend zu wissen. Sie hat auch beliebte Konfigurationsoptionen per Umfrage gesammelt und stellt diese vor – ein weiterer Ort, um zu sehen, wie sich Git noch mehr an den eigenen Stil anpassen lässt.

Mein herzlicher Dank geht an Dominik Guhr für den Anstoss, das hier einfach mal aufzuschreiben und an FND für inhaltliches Feedback.