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.
Mit einer einfachen Anpassung der Git-Konfiguration wird diese Blockliste dann auf alle Projekte angewendet:
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:
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.
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.
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:
Ein einfaches git fco
zeigt danach die Namen der lokalen Branches an und erlaubt es, einen per Pfeiltasten auszuwählen, der dann ausgecheckt wird.
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:
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 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.