Git-Branch im Bash-Prompt anzeigen

Meine kleinen Coding-Projekte setze ich meistens, direkt auf der Kommandozeile, mit dem Editor vim um. Ich arbeite eher selten in dem grafischen Editor VSCodium und noch weniger in einer Entwicklungsumgebung (IDE) wie Eclipse, Netbeans oder IntelliJ. Gerade die grafischen Tools integrieren die Versionsverwaltung git sehr gut und man hat als Entwickler jederzeit den Einblick, wie es um die Versionierung steht. Auf der Kommandozeile sehe ich das nur, wenn ich die jeweiligen Befehle wie z. B. git branch oder git log eingebe.

Vor ein paar Wochen ist es dann zum wiederholten Mal passiert: Ich hatte im falschen Branch gearbeitet (genervter Blick).

Mein Gedanke: Das passiert mir nicht nochmal … ich will den jeweiligen Branch im Shell-Prompt sehen, sofern ich in einem git-Repo arbeite. Wie ich das umgesetzt hab, dokumentiere ich im folgenden Beitrag.

Vorab-Gedanken

Mir ist bekannt dass es für meinen Bedarf schon komplett fertige Lösungen gibt. Mit Projekten wie https://ohmyposh.dev (Bash) oder https://ohmyz.sh/ (zsh) bekommt man schöne Prompts vor’s Auge gezaubert, die den jeweiligen Status im Branch anzeigen.

Diese Lösungen kommen für mich jedoch nicht in Frage, weil es entweder zu bunt oder zu überladen ist. Ich will einfach nur meinen Editor auf der Konsole bedienen und mit wenigen Zeilen Code einen kleinen Hinweis erhalten, auf welchem git Branch ich mich gerade befinde. Den Prompt auf meinen Clients hatte ich sowieso schon (in Anlehnung an den Bash-Prompt von Kali-Linux) angepasst. Daher dachte ich mir, diese kleine Optimierung kann nicht so schwer sein. Das Ergebnis soll dann wie folgt aussehen:

Mein Wunsch-Prompt
Mein Wunsch-Prompt

Grundlage: Die Variable PS1

Die Konfiguration des Standard-Prompts (primary prompt) der bash erfolgt über die Variable PS1 die man entweder systemweit in der /etc/profile hinterlegt oder User-spezifisch im Verzeichnis ~/.bashrc. Da auf einem Multiuser-System, jeder User seinen eigenen Prompt generieren dürfen soll, wähle ich die Option mit der ~/.bashrc.

Bei der Erstellung des Prompts können “special characters” verwendet werden (eben auch Variablen), die je nach Hostname, Username oder aktuellem Verzeichnis den Prompt generieren. Ein Debian-System (und viele andere Distros) hat initial folgenden Prompt:

Standard-Bash-Prompt auf einem Debian-System
Standard-Bash-Prompt auf einem Debian-System

Mein Test-Benutzer yoda auf der Maschine debvm befindet sich derzeit im Homeverzeichnis (~). Möchte man die Darstellung nun anpassen, hinterlegt man eine passende Zeichenkette in der Variable PS1. Für Testzwecke kann das direkt auf der Kommandozeile eingegeben werden:

Erster eigener Prompt
Erster eigener Prompt

Möchte ich den Standard-Prompt nachbilden, können die eben genannten “special characters” (Begriff aus der Man-Page) genutzt werden. Eine erneute Zuweisung zur Variable PS1 sieht wie folgt aus:

PS1="\u@\h:\w\$ "

Geben wir das auf der Konsole ein, sieht das Ergebnis dem Standardprompt schon recht ähnlich:

Standard-Prompt (etwas farblos)
Standard-Prompt (etwas farblos)

Bedeutung der Variablen:

ZeichenBedeutung
\uUsername
@Normales Zeichen, das im Prompt dargestellt wird
\hHostname
:Normales Zeichen, das im Prompt dargestellt wird
\wAktuelles Arbeitsverzeichnis
\$Indikator für einen eingeschränkten User, der als Zeichen im Prompt dargestellt wird

Farbiger Prompt

Die farbliche Gestaltung ist darüber hinaus etwas komplizierter. Farben werden mit einer Art Tag (ähnich HTML) eingeleitet. Der korrekte Begriff lautet “Escape-Sequenz”. \[\033 oder \[\e leitet die Zeichenformatierung ein, gefolgt von einer Zeichenkette [<ZAHL>m\]¸ die beispielsweise den Text oder den Hintergrund farbig darstellt.

Das kann ziemlich “frickelig” sein. Soll der komplette Prompt in grün erscheinen, kann das wie folgt umgesetzt werden:

Standard-Prompt in Farbe
Standard-Prompt in Farbe

Man kann erkennen, dass schon ein einfacher Prompt sehr schnell recht unübersichtlich werden kann. Letztendlich instruiert \[\e[32m\], dass alle nachfolgenden Zeichen in grüner Farbe dargestellt werden. Am Ende der Zeile sorgt die Sequenz \[\e[m\] dafür, dass an dieser Stelle der Farbverlauf endet. Alles danach wird wieder wie gewohnt dargestellt.

Die Zeichenfolge 32m steht dabei für grün. Weitere Farben sind möglich. Ich benötige später gelb (33m), grau (37m) und rot (31m). Nicht weil diese Farben besonders schön sind, sondern weil mir die farbig hinterlegten Bestandteile gut ins Auge fallen.

Eine etwas ausführlichere Erklärung zu den Farbcodes hab ich hier gefunden. Eine entsprechende Man-Page man console_codes gibt es ebenfalls. Wer hier zusätzliche Unterstützung möchte, sucht sich im Netz einen Bash-Prompt-Generator. Die können häufig ebenfalls farbige Prompts generieren.

Mit den genannten Möglichkeiten, hatte meine Variable PS1 bisher folgenden Aufbau:

PS1="\n┌── \[\e[33m\]\A \[\e[m\]- \[\e[37m\]\u@\h\[\e[m\]: \[\e[33m\][ \w ]\[\e[m\]\n└─\$ "

Der Prompt stellt sich wie folgt dar:

Mein bisheriger Prompt
Mein bisheriger Prompt

Der Aufbau hat sich aus folgenden Gründen bewährt:

  • Uhrzeit vorne: Manchmal führe ich ein Skript aus, das längere Zeit läuft. Da schau ich nicht zu und drehe Däumchen. Wenn das Skript fertig ist, sehe ich anhand des neuen Prompts, wie lange das Ding gelaufen ist.
  • Angabe von Usernamen und Host (wie im Standard-Prompt)
  • Arbeitsverzeichnis (wie im Standard-Prompt)
  • Kommando-Eingabe in der zweiten Zeile: Der gesamte Pfad des aktuellen Arbeitsverzeichnisses kann sehr lang sein. Dann brechen die Befehle im Standardprompt hässlich um. Durch die Eingabe meines Kommandos in der zweiten Zeile umgehe ich das. Wie oben beschrieben: Das ist jetzt keine geniale Eigenleistung. Ich hatte das in der Distro Kali-Linux aufgeschnappt und empfand diese Darstellung praktisch.

Branch im Prompt darstellen

Um meinem eigentlichen Ziel näher zu kommen, brauche ich eine Funktion in der Datei ~/.bashrc. Diese soll prüfen ob ich in einem Branch bin:

function check_branch {
    if git status > /dev/null 2>&1; then
        branch="<<< $(printf '\uE0A0') $(git branch --show current) >>>"
    else
        branch=""
    fi
}

Es wird in der Bedingungsprüfung der Befehl git status ausgeführt, jedoch sämtliche Ausgaben nach /dev/null geschoben. Das System registriert dabei jedoch, ob der Return-Wert des letzten Befehls 0 (=wahr) ist oder nicht. Ist das der Fall wird eine Zeichenkette gebaut die ein kleines Symbol \uE0A0 (dieser abzweigende Pfeil) und den aktuellen Branch zurück gibt. Andernfalls ist die Zeichenkette leer.

Bleibt die Frage, wann die Funktion aufgerufen wird? Im Prinzip möchte ich bei jedem Bestätigen mit der Eingabetaste checken, ob ich derzeit in einem git-Repo arbeite. Hierfür gibt es eine Variable PROMPT_COMMAND. Deren Inhalt wird ausgeführt, bevor ein neuer Prompt in der Kommandozeile angezeigt wird. Daher erfolgt der Funktionsaufruf per Zuweisung an diese Variable:

PROMPT_COMMAND="check_branch"

Zuletzt muss noch der Variable PS1 die Prompt-Struktur in leicht abgewandelter Form (mit zusätzlicher Variable $branch) zugewiesen werden:

PS1="\n┌── \[\e[33m\]\A \[\e[m\]- \[\e[37m\]\u@\h\[\e[m\]: \[\e[33m\][ \w ]\[\e[m\] \[\e[31m\]\$branch \[\e[m\]\n└─\$ "

Die drei Bausteine liegen hintereinander in der erwähnten ~/.bashrc. Jedes Mal wenn ich nach einer Eingabe die Enter-Taste drücke, wird die Variable PROMPT_COMMAND ausgelesen und die Funktion check_branch aufgerufen, die mir eine Zeichenkette baut (entsprechender Branch oder leere Zeichenkette). Das Ergebnis sieht dann wie folgt aus:

Mein neuer Prompt mit Hinweis auf aktuellen git Branch
Mein neuer Prompt mit Hinweis auf aktuellen git Branch

Ich bin mit dem Ergebnis erstmal zufrieden. Klar … es können noch weitere nützliche Infos hinterlegt werden. Aber das reicht mir für’s Erste. Die Variable PROMPT_COMMAND benötige ich noch für ein weiteres Szenario. Das beschreibe ich in einem folgenden Beitrag hier.

Fazit

Ich hatte die Möglichkeit mit einem 32MB großen Fertig-Gericht (https://ohmyposh.dev/) oder mit 8 Zeilen Code mein Ziel zu erreichen. Der Zeitaufwand hier, ist eher in den Blog-Beitrag als in die reine Umsetzung geflossen. Hätte ich mich für ohmyposh entschieden, hätte ich wahrscheinlich meine Zeit mit der Auswahl von Themes und deren Anpassung verbraten (siehe: ohmyposh themes).

Quellen