Blog

10 Stufen von Git-Aliasen: Fortgeschritten und darüber hinaus

JUN 17, 2024

Im ersten Teil meines zweiteiligen Blogbeitrags habe ich euch von den absoluten Grundlagen bis zu Git-Aliassen geführt. Dabei habe ich gezeigt, wie sie lange und komplexe Befehle ersetzen können, und einige Konzepte vorgestellt, die den meisten Nutzern nicht bekannt sind – etwa Aliasse, die Parameter direkt an Git übergeben, sowie erweiterte Log-Formatierung mit benutzerdefinierten Pretty-Formaten.

Jan Krag

Jan has been with us since 2014 as a Continuous Improvement Agent. He is an accredited trainer for Docker and Github and is also a certified life coach. Jan has a truly diverse range of interests. He breeds oriental cats, builds and flies his own kites, collects Rubik’s cubes, and enjoys snowboarding. Wow!

Bevor ihr weiterlest, empfehle ich euch dringend, zuerst Teil eins zu lesen.

In Teil zwei stelle ich noch fortgeschrittenere Konzepte vor und wage mich an wirklich „abgefahrene“ Einsatzmöglichkeiten von Git-Aliasen – einschließlich einiger geradezu verrückter Beispiele. Zum Schluss zeige ich einige Alternativen für dieselben Anforderungen.

Also, los geht's.

Level 6: ! Nicht-Git-Befehle

Mehr fürs Geld.

Git-Aliase erweitern unser Repertoire an Möglichkeiten in unserem Git-Kontext. In diesem und den meisten folgenden Abschnitten geht es darum, was wir tun können, wenn wir die Grenzen der Git-Subcommands überschreiten.

Die „Bang“-Funktion erweitert die Möglichkeiten von Git-Aliasen grundlegend: Sie erlaubt uns, die Shell aufzurufen und jeden beliebigen Shell-Befehl auf unserem System auszuführen. Dazu stellen wir der Alias-Erweiterung einfach ein Ausrufezeichen voran – in der Unix-/Shell-Welt auch „Bang“ genannt.

Beginnen wir mit einem sehr einfachen Beispiel, das das Konzept klar veranschaulicht und tatsächlich nützlich ist.

Auf einigen Plattformen wird Git mit zwei nützlichen GUI-Tools vorinstalliert: „Git gui“ zum Stagen und Committen sowie „Gitk“ zum Anzeigen der Historie. Aber warum ist git gui ein Git-Subcommand und gitk nicht? Ziemlich verwirrend, aber mit einem Alias leicht zu beheben:

Und wenn wir schon dabei sind: Ich hatte ein ähnliches Problem. Wenn mein Gehirn tief im „Git“-Modus steckt, tippen meine Finger oft „Git“, bevor ich mich für einen Befehl entschieden habe. Dann entscheide ich plötzlich, dass ich die Dateien im Ordner auflisten muss, und tippe schnell ll <enter> (mein täglicher Shell-Alias für ls -al). Und plötzlich bekomme ich:

Der „Tippfehler“ git ll funktioniert jetzt zwar, führt aber zu einer sehr wichtigen Einschränkung der Bang-Funktion. Wie gezeigt, kann diese sowohl ein Segen als auch ein Fluch sein.

Mein Alias git ll scheint also zu funktionieren, zeigt mir aber immer den Inhalt des Root-Ordners des Repositorys an – selbst wenn ich mich in einem Unterordner befinde.

Der Shell-Befehl pwd gibt das aktuelle Arbeitsverzeichnis aus. Dieser Alias bietet eine schnelle Möglichkeit, den Pfad zu meinem Repository anzuzeigen.

Zugegeben, dieses spezielle Problem ließe sich besser mit einem normalen Git-Alias lösen, ohne eine neue Shell-Sitzung zu erzeugen. Welcher davon schneller läuft, ist allerdings noch nicht abschließend geklärt.

Für diesen Nebeneffekt habe ich aber mindestens einen sehr guten Anwendungsfall. Wenn ihr aus anderen Gründen per cd tief in einen Unterordner gewechselt seid, kann es sehr lästig werden, die Git-Statusausgabe zu lesen. Alle anderen Pfade werden nämlich relativ zu eurem aktuellen Ordner ausgegeben, sodass Änderungen etwa so angezeigt werden:

Nutzen wir also den Nebeneffekt des „Root-Ordners“ und fügen Alternativen zum regulären st-Alias hinzu:

Diese zeigen die Statusausgabe immer relativ zum Root-Verzeichnis des Repositorys an.

Git-1

(-s steht lediglich für die weniger ausführliche Ausgabe im „short“-Format.)

Beachtet, dass sich ein ähnlicher Effekt mit der Konfigurationsvariable status.relativePaths und der im ersten Teil dieses Blogbeitrags in „Level 5“ beschriebenen -c-Funktion erzielen ließe.

Zum Abschluss dieses Abschnitts wollen wir etwas Spaß haben. Wenn ich unbewusst Git vor ll tippe, kann es auch passieren, dass ich Git tippe, bevor ich mich beispielsweise für Git status entscheide. Das führt zu dem bedauerlichen:

$ git git statusgit: 'git' is not a git command.Also kam mein Gehirn auf die Idee, Folgendes zu erstellen:

Ja, damit funktioniert git git status . Und dank der rekursiven Natur von Aliasen funktioniert jetzt sogar git git git git git status … zumindest auf dem Papier.

In der Praxis ist das eher ein guter Witz als eine gute Idee, denn es hat zwei schwerwiegende Folgen. Das erste Problem ist, wie oben zu sehen, dass der Befehl nun im Root-Ordner des Repositorys ausgeführt wird, was für Verwirrung sorgen kann. Das zweite Problem: Es verhindert die recht wichtige Möglichkeit, mit git help git to get die Hilfeseite für den eigentlichen Git-Befehl aufzurufen. Stattdessen würde nun die weitaus weniger hilfreiche Ausgabe erscheinen:

Level 7: Git-Aliase wiederverwenden

Auf dem aufbauen, was vorher kam … Das verwenden, was vorher kam

In älteren Git-Versionen vor 2.20 durften Git-Aliasse nicht auf andere Git-Aliasse verweisen. Das führte oft zu vielen ähnlichen Aliasen in der Konfigurationsdatei, wie wir bei den benutzerdefinierten Git-Log-Befehlen gesehen haben.

Als einzige Umgehungslösung stand die Bang-Funktion zur Verfügung, denn damit lässt sich Folgendes problemlos umsetzen:

Damit wird kein vorhandener Alias rekursiv aufgerufen, sondern eine neue Shell-Session gestartet, die zufällig Git mit einem Alias verwendet.

Zum Glück sind seit Git 2.20 (2018) rekursive Aliasse erlaubt, und seit 2.30 (Q1 2021) versteht und erweitert sogar bash-completion (Tab-Vervollständigung) diese.

Für eine deutlich aufgeräumtere Git-Konfiguration kann ich jetzt also Folgendes verwenden:

Level 8: Aktionen per Pipeline verketten

Unix-Befehle für mehr Kontrolle (oder Wahnsinn) verketten

Ich habe bereits erwähnt, dass die Bang-Funktion ein Gamechanger war, aber wir haben damit nur an der Oberfläche gekratzt. Der nächste Schritt folgt, wenn ihr erkennt, dass der Aufruf einer Shell es ermöglicht, mehrere Befehle miteinander zu verketten.

Die einfachste Anwendung dafür sind Aliasse, die mit der Shell und dem Operator && mehrere separate Aktionen nacheinander ausführen.

Hinweis: Der obige Alias ist für eine bessere Lesbarkeit umgebrochen. In der Konfiguration muss er in einer einzigen Zeile stehen. Mehr dazu später.

Wir können sogar vorhandene Umgebungsvariablen verwenden oder neue direkt innerhalb eines Alias definieren.

In case of fire

Ich habe ein Exemplar für unser eigenes Büro ausgedruckt, was eine lange und humorvolle Diskussion auf Slack darüber auslöste, warum das nicht funktionieren würde und wie es verbessert werden müsste.

Das Endergebnis war dieses Alias-Juwel, das die Verwendung von Variablen anschaulich demonstrierte:

Die eigentliche Stärke zeigt sich aber, wenn wir noch einen Schritt weitergehen und Unix-Pipes nutzen, um die Ausgabe eines Befehls an den nächsten weiterzugeben.

Zuerst definiere ich einen schnellen remoteurl-Alias, um die URL meines Git-Remotes leichter abzurufen. Was aber, wenn ich das Repository per SSH geklont habe und eine URL im Format „git@“ erhalte, obwohl ich damit die Website des Repositories finden möchte?

Senden wir die Ausgabe des ersten Befehls an sed, das Suchen und Ersetzen ausführt und git@ durch https:// ersetzt.

Zum Abschluss fügen wir einen Alias hinzu, der diese Ausgabe mit dem Mac-Befehl pbcopy direkt in meine Zwischenablage kopiert. (Unter Windows könnten wir dafür clip verwenden.)

Ebenso können wir andere Shell-Funktionen nutzen, etwa um Ausgaben in Dateien umzuleiten. Manchmal möchte ich in meinen Repositories eine Datei .mailmap erstellen. Damit könnt ihr zum Beispiel die alte E-Mail-Adresse einiger Beitragender ihrer neuen zuordnen oder sicherstellen, dass unterschiedliche Schreibweisen eines Benutzers zusammengeführt werden.

Für einen guten Ausgangspunkt für eine Mailmap brauche ich eine Liste der Autoren mit ihren Namen und E-Mail-Adressen im Standardformat „Jan Krag <jan.krag@example.com>“, ähnlich der Ausgabe von git shortlog -sne, aber ohne die erste Zahlenspalte.

Dafür habe ich mir die folgenden Aliasse ausgedacht:

[alias]mm   = "!git log  --format='%aN ' | sort -u"mmm  = "!git mm >> .mailmap"mmme = "!git mmm && code .mailmap"

Der Log-Befehl gibt für jeden Commit einfach Autor und E-Mail-Adresse aus und verwendet dann die Option „unique“ des Shell-Befehls sort, um Duplikate zu entfernen und die Ausgabe zu sortieren. Der erste Alias gibt sie in der Konsole aus, während der zweite die Ausgabe umleitet und eine vorhandene .mailmap-Datei erstellt oder ergänzt.

Der dritte ist die praktische „Ich habe es eilig“-Variante, die die neue .mailmap-Datei sofort in meinem VSCode-Editor öffnet. Hier kombinieren wir also Pipes, Umleitungen und die &&-Funktion in einem Alias.

Level 9: Funktionen

Bash-Funktionen sind einfach unschlagbar!

Lasst uns noch einen Schritt weitergehen und schauen, was wir tun können. Jetzt, da wir diesen Shell-Kontext haben, steht uns eine weitere Funktion zur Verfügung: Wir können Funktionen definieren. Warum sollte ich das wollen, fragt ihr euch vielleicht? Komplexe Aliase werden dadurch zwar etwas übersichtlicher, aber der wichtigste Vorteil ist, dass wir Kommandozeilenargumente auslesen und gezielter verwenden können.

Bei normalen Aliasen könnt ihr beliebige Optionen und Argumente nach dem Alias anhängen. Sie werden dann nach der Erweiterung hinzugefügt. Um auf meinen Git-slog-Alias aus dem vorherigen Beitrag zurückzukommen: Ihr könnt problemlos git slog -3 oder git slog --all. verwenden. Was aber, wenn ich einen Alias erstellen möchte, bei dem ich ein Argument an mehreren Stellen brauche?

Ein sehr einfaches Beispiel dafür ist mein Alias, um einen Branch sowohl lokal als auch auf dem Remote zu löschen:

Beachtet die Syntax. In meinem Shell-Kontext definiere ich zunächst eine Funktion f(), die die Anweisungen von der öffnenden \{ bis zur abschließenden \}; ausführt.

Anschließend rufe ich diese Funktion einfach über ihren Namen f auf. Das Praktische daran: Wir können jetzt Argumente an die Funktion übergeben. Sie stehen im Funktionsbereich als „Positionsparameter“ zur Verfügung, nummeriert ab eins. ${1} verweist also einfach auf das erste Argument, das der Benutzer beim Aufruf der Funktion übergibt. Wie ihr im Beispiel seht, können wir einen Parameter beliebig oft verwenden.

Schauen wir uns ein weiteres einfaches Beispiel an:

[alias] # Easy add a GitHub repo as new remote ghremote= "!f(){ git remote add $1 https://github.com/$2.git; }; f"

In diesem Beispiel verwende ich eine Funktion nicht, um ein Argument wiederzuverwenden, sondern weil ich mehrere Argumente entgegennehmen und an ganz bestimmten Stellen innerhalb des Alias einsetzen möchte.

Vielleicht ist euch aufgefallen, dass dieses Beispiel die Positionsparameter ohne geschweifte Klammern verwendet. Damit soll nur verdeutlicht werden, dass die Klammern nach bash-Standard nur für Positionsparameter über neun erforderlich sind. Die Wahl bleibt also euch überlassen.

Probieren wir diesen Alias ghremote aus:

Level 10: Übertreiben

Einführung in die mehrzeilige Formatierung.

Wir haben bereits einige Beispiele für Aliase gesehen, die in eurer Konfiguration sehr lange Zeilen ergeben und dadurch fast „unlesbar“ werden. Es gibt jedoch eine Möglichkeit, echte mehrzeilige Aliase zu erstellen. Fügt einfach am Ende einen Backslash hinzu, der den Zeilenumbruch maskiert.

Das beantwortet allerdings nicht wirklich die Frage: „Sollte ich das tun?“

Irgendwann erreichen wir die Grenzen dessen, was bei Aliasen noch sinnvoll ist. Dann solltet ihr einige der alternativen Optionen in Betracht ziehen, die ich in „Level 11“ vorstelle. Aber schließlich geht es in diesem Blogbeitrag um Aliase. Also verschieben wir die Grenzen ein wenig, und ihr könnt euch selbst ein Urteil bilden.

Beginnen wir diesen Abschnitt mit einem recht komplexen, aber manchmal nützlichen Beispiel, das ich nicht im Detail erklären werde.

Die Kombination mit dem symbolic-ref-Trick aus dem Alias swm, um den Standard-Branch zu finden, überlasse ich euch als Übung, liebe Leser.

Es gibt nicht viel mehr zu erklären. Schauen wir uns daher noch ein paar Beispiele an, die euch dazu inspirieren können, eigene Aliase zu schreiben.

„Ich möchte einfach die Website für dieses Repository öffnen.“

Aber was tun, wenn das Git-Remote SSH verwendet?

Sehen wir uns das in der Anwendung an:

git-1

Welche Dateien bekommen die meiste Aufmerksamkeit?

Ich habe diesen Alias ursprünglich in einem .gitconfig-Blogbeitrag von Michael Wales gefunden und leicht an meine Vorlieben angepasst.

[alias]churn = !git -p log --all -M -C --name-only\      --format='format:' $@ \        | sort \        | grep -v '^$' \        | uniq -c \        | sort -r \        | awk 'BEGIN {print count,file} {print $1 , $2}'

Und in der Anwendung im Repository git-katas:

$ git churn42 README.md24 basic-commits/README.md21 basic-branching/README.md19 ignore/README.md19 basic-staging/README.md17 submodules/README.md17 3-way-merge/README.md16 configure-git/README.md15 Overview.md14 ff-merge/README.md

Warum muss ich wissen, wie ich fortfahre?

Ich gebe häufig Git-Schulungen, und jedes Mal, wenn wir über das Lösen von Merge-/Rebase-Konflikten sprechen, habe ich das zweifelhafte Vergnügen, Folgendes vorzustellen:

git merge --continuegit rebase --continuegit cherry-pick --continuegit revert --continue

Irgendwann fragte ich mich: „Warum um alles in der Welt muss ich als Nutzer angeben, ,was‘ ich fortsetze, wenn Git offensichtlich weiß, dass es sich in einem Merge-, Rebase- oder anderen Zustand befindet? Warum gibt es nicht einfach einen continue-Befehl, der ,das Richtige tut‘?“

Soweit ich mich erinnere, habe ich das getan, was wir vor ChatGPT gemacht haben: im Netz gesucht. Dabei fand ich ein Bash-Skript, das genau das erledigt. Wie im nächsten Kapitel erwähnt, ist das vermutlich auch die vernünftige Lösung. Als Fan habe ich jedoch getan, was getan werden musste, und es als reinen Alias umgesetzt. Daher kann ich euch dieses Monstrum präsentieren, das den Geist des maßlosen Übertreibens wahrlich verkörpert:

Git-Aliase teilen

Zum Abschluss dieses Abschnitts teile ich mit euch meinen „heiligen Gral“ unter den Aliasen, den ich vor etwa zehn Jahren mit harter Arbeit und Entschlossenheit entwickelt habe.

Das Problem, das ich lösen wollte, war das Teilen von Aliasen mit Kollegen, zum Beispiel über Slack. Wenn ich einem weniger erfahrenen Git-Nutzer schnell einen Alias bereitstellen möchte, ist es unnötig kompliziert, jedes Mal erklären zu müssen, wie man die globale Konfigurationsdatei findet und bearbeitet, wo der Alias-Code in die Datei gehört und so weiter.

Viel sauberer wäre es, einfach den passenden Befehl git config –global alias.foo "do this Git thing" zu senden, den sie direkt ausführen können. Einfache Aliase tippe ich einfach aus dem Gedächtnis ein. Bei auch nur leicht komplexen Aliasen mit Anführungszeichen und vielleicht Variablen ist es jedoch nicht trivial, alles korrekt zu setzen. Deshalb kam mir die Idee, einen Alias namens „exportalias“ zu schreiben. Ich ahnte nicht, wie schwer es sein würde, diesen korrekt umzusetzen. Eines der Erfolgskriterien war dabei, dass er sich selbst exportieren können sollte. Ich habe ihn nicht mit all den wirklich verrückten mehrzeiligen Aliasen getestet, aber für diese würde ich ohnehin immer den .gitconfig-Ausschnitt teilen.

Diesen Alias habe ich absichtlich ohne Zeilenumbrüche gelassen, damit er sicher in einem exportierbaren Format vorliegt.

Probieren wir ihn mit einem nicht ganz trivialen Alias von weiter oben in diesem Beitrag aus – einem, der nicht nur Sonderzeichen, sondern auch maskierte Anführungszeichen enthält.

Level 11: Bonusrunde

Gibt es eine Grenze für den Wahnsinn?

In diesem abschließenden Bonusabschnitt stelle ich euch einige alternative Ansätze vor, um die Git-Funktionalität zu erweitern – Optionen, die sich möglicherweise als vernünftiger erweisen als mehrzeilige Aliase über eine ganze Seite.

Benutzerdefinierte ausführbare Dateien

Zunächst einmal die Erkenntnis, dass Git klug aufgebaut ist: Jede ausführbare Datei in eurem Pfad namens git-something wird zu einem Befehl git something.

Das bedeutet, dass einige unserer längeren Aliase stattdessen als einfaches Bash-Skript umgesetzt werden könnten. Warum ist das relevant, fragt ihr euch vielleicht? Unter anderem, weil ihr viele Formatierungsprobleme innerhalb der Grenzen der .gitconfig-Datei vermeidet – etwa das Maskieren von Zeilenumbrüchen und unnötig komplizierte Anführungszeichen.

Ein weiterer Vorteil ist, dass wir besser „echte“ Skripte mit ordentlicher Argumentverarbeitung, Fehlerbehandlung, Hilfetexten zur Nutzung und mehr schreiben können.

Tatsächlich verlangt Git nicht einmal, dass ihr Bash verwendet. Ihr könnt eure neuen Git-Befehle in jeder Sprache schreiben, solltet aber vielleicht eine mit einer guten Git-Bibliothek wählen, etwa Python, Java oder Go.

Git-Erweiterungspakete

Wenn wir benutzerdefinierte Git-Befehle als Binärdateien in unserem Pfad schreiben können, können wir auch ganze Sammlungen davon erstellen und verteilen. Es gibt sogar Open-Source-Projekte, die solche „Git-Erweiterungen“ bereitstellen – entweder für bestimmte Zwecke oder als allgemeine Sammlungen „nützlicher“ Befehle.

Eine der größten dieser allgemeinen Sammlungen ist https://github.com/tj/git-extras. Nach der Installation erweitert sie euer Repertoire um über 70 neue Befehle, darunter den eigennützigen Befehl git extras, der sie alle auflistet. Die Installation ist mit den meisten gängigen Paketmanagern möglich. Falls das keine Option ist, könnt ihr das Projekt auch manuell klonen, zum Beispiel:$sudo apt-get install git-extras

oder

Ich werde nicht alle 70 Befehle auflisten, aber hier sind einige ausgewählte:

  • git-abort: Aktuellen Git-Vorgang abbrechen.

  • git-alias: Aliasse definieren, suchen und anzeigen.

  • git-magic: Add/Commit/Push-Routinen automatisieren.

  • git-repl: Git-Read-Eval-Print-Loop.

  • git-standup: Abrufen, was du am letzten Arbeitstag getan hast.

Ein weiteres Erweiterungspaket, das einen Blick wert sein könnte, ist Git Pastiche. Es enthält eine deutlich kleinere Auswahl an Befehlen, vielleicht etwas niedrigschwelliger, aber besonders git stats und git activity finde ich gelegentlich sehr nützlich.

In diesem Zusammenhang ist vielleicht auch erwähnenswert, dass etwas, das wir als „Kern“-Erweiterung von Git betrachten, wie Git LFS, auch bekannt als Git Large File Storage, ebenfalls als Erweiterung nach diesem Konzept entwickelt und in Go geschrieben wurde. Der zentrale Git-LFS-Befehl ist lediglich eine ausführbare git-lfs-Datei, die wiederum alle LFS-Unterbefehle an andere Go-Programme weiterleitet.

Hilfe

Abschließend gehe ich kurz auf die Hilfe ein. Wenn du bei deinen eigenen Git-Befehlen wirklich alles geben möchtest, wie es „git extras“ und andere getan haben, kannst du eigene Hilfeseiten bereitstellen.

Wenn du git help mycustom eingibst, prüft Git tatsächlich, ob auf deinem System eine Manpage für mycustom vorhanden ist. Wie Manpages erstellt werden, würde den Rahmen dieses Blogbeitrags sprengen, aber dazu findest du leicht gute Ressourcen.

Bei Git-Aliassen ist dir vielleicht aufgefallen, dass etwas wie git help st einfach Folgendes ausgibt:

Je nach deinen Anforderungen kann das nützlich sein – oder auch nicht.

Für einfache Aliasse wie diesen, also solche, die nicht Bash aufrufen, kannst du auch das Format git st --help verwenden, um die normale Hilfeseite für den Befehl des Alias aufzurufen – in diesem Fall git status.

Danksagungen und Hinweise

Die Beispiele in diesem Beitrag stammen aus meiner persönlichen Sammlung aus mehr als einem Jahrzehnt Git-Nutzung. Einige habe ich angepasst, um in den einzelnen Abschnitten zu zeigen, was ich jeweils demonstrieren möchte.

Was die Herkunft dieser Aliasse angeht: Einige habe ich selbst geschrieben, um ein eigenes Problem zu lösen, andere wurden mir von Kollegen, Teilnehmern meiner Git-Kurse und Freunden weitergegeben. Manche habe ich im Laufe der Jahre einfach online gefunden und entweder unverändert übernommen oder für meinen eigenen Gebrauch angepasst. Generell gibt es in der Community einen Geist des Teilens, daher hoffe ich, dass dies unter „Fair Use“ fällt.

In letzter Zeit habe ich begonnen, bei sehr komplexen Aliassen Kommentare in meiner Konfiguration hinzuzufügen, die angeben, wo ich sie gefunden habe. Aber selbst dann ist es schwer, den Ursprung dieser Codebeispiele nachzuverfolgen, da andere genauso vorgehen wie ich: Sie übernehmen etwas von irgendwoher und passen es bei Bedarf an.

Wenn du in diesem Blogbeitrag einen Alias gefunden hast, dessen ursprünglicher Autor du deiner Meinung nach bist, lass es mich bitte wissen. Dann nenne ich dich entweder als Quelle oder entferne ihn auf Wunsch.

Schau dir auch Teil eins dieses Blogbeitrags an, falls du das noch nicht getan hast!

  • Software development
  • DevOps
  • CI/CD

Subscribe to our newsletter