commit cbe6428c979068a68a47bc76aad1a248b938372d Author: allan Date: Wed Feb 4 11:06:15 2026 +0100 latest commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3eaaa81 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Allan Christensen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6901e46 --- /dev/null +++ b/README.md @@ -0,0 +1,228 @@ +# Bash Git Prompt for Ubuntu 24.04 Server +[![OS](https://img.shields.io/badge/ubuntu-24.04-E95420)](#) +[![Shell](https://img.shields.io/badge/shell-bash-121011)](#) +[![Feature](https://img.shields.io/badge/feature-git_prompt-0078D7)](#) +[![License](https://img.shields.io/badge/License-MIT-green)](./LICENSE) + +Install Bash Git Prompt on Ubuntu 24.04 server. + +> **Note** +> Safe for production use. This tool only modifies your shell prompt and does not change system services. + +--- + +## Why this installer exists + +Most Bash Git prompts out there are bloated. You have to install oh-my-zsh, oh-my-bash, or some other framework that drags in far more than you need. + +This project is the opposite: a fast, lightweight Git status prompt based purely on Bash and Git's own built-in commands. +No frameworks, no plugins, no bloat. + +--- + +## What this installer does + +- Shows current Git branch and live repo status +- Supports **five clean themes** (including Powerline and two-line layouts) switchable in real time +- Displays staged, changed, untracked, stashed, and remote state +- Indicates clean/dirty status immediately +- Lets you customise PS1 inside/outside Git repos centrally +- Works seamlessly over SSH +- Includes an **optional** Nerd Font installer + +--- + +## What this installer does NOT do +It will not stop you from running the script without reading the documentation like there is no tomorrow. +Skip the README, and whatever happens next is your headache, not a bug report. + +--- + +### Prerequisites + +Requires **JetBrainsMono Nerd Font Mono** for proper symbol display like shown below. + +

+Extended Symbols +

+ +--- + +### Prompt Structure + +The default prompt layout is: + +```plaintext + +``` + +Example output with the default theme: + +

+Default Theme +

+ +And here the default theme with all the bells and whistles: + +

+Default Theme All Icons +

+ +--- + +### Installation + +Clone the repository: + +``` +git clone https://git.x-files.dk/bash/bash-git-prompt.git ~/.bash-git-prompt +``` + +Install fonts: + +``` +cd ~/.bash-git-prompt +./install-fonts.sh +``` + +--- + +## Important note about fonts (SSH users take notice) + +The Git prompt uses Nerd Font symbols, but: + +**You only need Nerd Fonts installed on your local machine.** +Your terminal emulator on your laptop/workstation renders the symbols, not the server. + +If you SSH into the server, the prompt will display correctly as long as your local terminal already has the Nerd Font installed. + +If you are installing this on a remote server, you can safely skip the font installer. + +--- + +## Make it happen + +Add the following to your .bashrc file: + +``` +# Git Prompt Start +if [ -f "$HOME/.bash-git-prompt/bash-git-prompt" ]; then + export GIT_PROMPT_THEME=1 + source "$HOME/.bash-git-prompt/bash-git-prompt" +fi +# Git Prompt Stop +``` + +Reload your environment: + +``` +source ~/.bashrc +``` + +--- + +### Switching Themes + +Change themes dynamically with: + +``` +gpchange +``` + +Example: +``` +gpchange 3 +``` + +This updates the theme instantly for your current session. + +--- + +### Changing the prompt layout + +Prompt layout is handled centrally in the script inside the `update_git_prompt()` function. + +Each theme controls its own structure: + +- Theme 1, 4, and 5 use a single-line prompt +- Theme 2 uses a Powerline-style segmented prompt +- Theme 3 uses a two-line prompt + +If you want to customise the prompt layout, edit the relevant theme block inside `update_git_prompt()`. + +This keeps icons, colours, and layout clearly separated and avoids breaking other themes. + +--- + +### Available Themes + +Preview of all five themes (clean and dirty as well as ahead and behind shown together for comparison): + +Branch -> Staged -> Changed -> Untracked -> Stashed -> Ahead -> Behind -> No-Remote -> Conflict -> Dirty -> Clean + +

+All Themes +

+ +> **Note** +> Theme 1, 2 and 3 require **JetBrainsMono Nerd Font Mono** for proper symbol rendering. +> Theme 4 is purely **Unicode** -- no special fonts are needed for proper symbol rendering. +> Theme 5 is **terminal safe** so no special fonts needed for proper symbol rendering. + +--- + +### Terminal Font Setup + +Set your terminal to use: +``` +JetBrainsMono Nerd Font Mono +``` + +

+Terminal Font Settings +

+ +--- + +### Troubleshooting + +**Symbols not showing correctly:** +Make sure your terminal uses *JetBrainsMono Nerd Font Mono*. +Restart your terminal after installing fonts. +Log out and back in after installing fonts. + +**Prompt not loading:** +Verify these lines exist and are correct in `~/.bashrc`: +``` +# Git Prompt Start +if [ -f "$HOME/.bash-git-prompt/bash-git-prompt" ]; then + export GIT_PROMPT_THEME=1 + source "$HOME/.bash-git-prompt/bash-git-prompt" +fi +# Git Prompt Stop +``` + +--- + +### FAQ + +**Q:** Does this replace my default prompt entirely? +**A:** No. The Git-aware segment only appears when your current directory is inside a Git repository. + +**Q:** Can I create my own theme? +**A:** Absolutely. Each theme is defined in the script under `set_git_prompt_theme_icons()`. +You can modify or extend it, then set your new theme using `export GIT_PROMPT_THEME=`. + +**Q:** Does it slow down Git-heavy directories? +**A:** Negligibly. The script uses optimized Git calls and caches status checks for performance. + +--- + +### More Information + +More guides and documentation can be found on [wiki.x-files.dk](https://wiki.x-files.dk) + +--- + +### License +Licensed under the [MIT License](./LICENSE). diff --git a/bash-git-prompt b/bash-git-prompt new file mode 100644 index 0000000..ba98a37 --- /dev/null +++ b/bash-git-prompt @@ -0,0 +1,367 @@ +# Author : Allan Christensen +# First Created : 24-06-2025 (DD-MM-YYYY) +# Description : Bash Git prompt for Linux +# License : MIT License + +# +# Set default theme to 1 if it's not already set +# +GIT_PROMPT_THEME="${GIT_PROMPT_THEME:-1}" + +# +# ANSI-safe base colors (cursor safe) +# +C_USER='\[\e[0;32m\]' +C_PATH='\[\e[38;5;178m\]' +C_RESET='\[\e[0m\]' + +# +# Default git reset (theme-aware override below) +# +C_GIT_RESET="$C_RESET" + +# +# Powerline glyph only 1 specific glyph is needed. +# +PL_SEP="" + +# Icons define *symbols only* — no colors or layout here + +# +# Theme icon definitions +# +set_git_prompt_theme_icons() { + case "$GIT_PROMPT_THEME" in + 1) + # Theme 1: Default icons (requires Nerd Font) + BRANCH_ICON=" " + STAGED_ICON="" + CONFLICT_ICON="≠" + CHANGED_ICON="±" + UNTRACKED_ICON="…" + STASHED_ICON="" + AHEAD_ICON="" + BEHIND_ICON="" + NO_REMOTE_ICON="󰊠" + CLEAN_ICON="✔" + DIRTY_ICON="✘" + ;; + 2) + # Theme 2: Powerline icons (requires Nerd Font) + BRANCH_ICON=" " + STAGED_ICON="" + CONFLICT_ICON="≠" + CHANGED_ICON="±" + UNTRACKED_ICON="…" + STASHED_ICON="" + AHEAD_ICON="" + BEHIND_ICON="" + NO_REMOTE_ICON="󰊠" + CLEAN_ICON="✔" + DIRTY_ICON="✘" + ;; + 3) + # Theme 3: Two-liner icons (requires Nerd Font) + BRANCH_ICON=" " + STAGED_ICON="" + CONFLICT_ICON="≠" + CHANGED_ICON="±" + UNTRACKED_ICON="…" + STASHED_ICON="" + AHEAD_ICON="" + BEHIND_ICON="" + NO_REMOTE_ICON="󰊠" + CLEAN_ICON="✔" + DIRTY_ICON="✘" + ;; + 4) + # Theme 4: ANSI / Unicode-safe icons no special font needed + BRANCH_ICON="⎇ " + STAGED_ICON="o" + CONFLICT_ICON="!" + CHANGED_ICON="±" + UNTRACKED_ICON="…" + STASHED_ICON="☰" + AHEAD_ICON="⇡" + BEHIND_ICON="⇣" + NO_REMOTE_ICON="-" + CLEAN_ICON="✓" + DIRTY_ICON="✗" + ;; + 5) + # Theme 5: Terminal-safe (ASCII) icons no special font needed + BRANCH_ICON=":: " + STAGED_ICON="+" + CONFLICT_ICON="x" + CHANGED_ICON="*" + UNTRACKED_ICON="?" + STASHED_ICON="s" + AHEAD_ICON="a" + BEHIND_ICON="b" + NO_REMOTE_ICON="no" + CLEAN_ICON="ok" + DIRTY_ICON="!" + ;; + esac +} + +# Colors define *foreground/background* — layout happens later + +# +# Theme-aware git colors +# +set_git_prompt_theme_colors() { + case "$GIT_PROMPT_THEME" in + 1) + # Theme 1: Default colors + GC_BRANCH='\[\e[38;5;117m\]' + GC_STAGED='\[\e[38;5;196m\]' + GC_CONFLICT='\[\e[38;5;196m\]' + GC_CHANGED='\[\e[38;5;69m\]' + GC_UNTRACKED='\[\e[38;5;41m\]' + GC_STASHED='\[\e[38;5;226m\]' + GC_AHEAD='\[\e[0;37m\]' + GC_BEHIND='\[\e[0;37m\]' + GC_NOREMOTE='\[\e[38;5;250m\]' + GC_CLEAN='\[\e[0;32m\]' + GC_DIRTY='\[\e[38;5;196m\]' + C_GIT_RESET="$C_RESET" + ;; + 2) + # Theme 2: Powerline colors + PL_GIT_BG='\[\e[44m\]' + PL_GIT_FG='\[\e[97m\]' + PL_GIT_SEP='\[\e[30;44m\]' + PL_PATH_BG='\[\e[107m\]' + PL_PATH_FG='\[\e[30m\]' + PL_PATH_SEP='\[\e[34;107m\]' + PL_END_SEP='\[\e[97;49m\]' + GC_BRANCH='\[\e[97m\]' + GC_STAGED='\[\e[97m\]' + GC_CONFLICT='\[\e[97m\]' + GC_CHANGED='\[\e[97m\]' + GC_UNTRACKED='\[\e[97m\]' + GC_STASHED='\[\e[97m\]' + GC_AHEAD='\[\e[97m\]' + GC_BEHIND='\[\e[97m\]' + GC_NOREMOTE='\[\e[97m\]' + GC_CLEAN='\[\e[97m\]' + GC_DIRTY='\[\e[97m\]' + C_GIT_RESET="${PL_GIT_BG}${PL_GIT_FG}" + ;; + 3) + # Theme 3: Two-liner colors + GC_BRANCH='\[\e[38;5;117m\]' + GC_STAGED='\[\e[38;5;196m\]' + GC_CONFLICT='\[\e[38;5;196m\]' + GC_CHANGED='\[\e[38;5;69m\]' + GC_UNTRACKED='\[\e[38;5;41m\]' + GC_STASHED='\[\e[38;5;226m\]' + GC_AHEAD='\[\e[0;37m\]' + GC_BEHIND='\[\e[0;37m\]' + GC_NOREMOTE='\[\e[38;5;250m\]' + GC_CLEAN='\[\e[0;32m\]' + GC_DIRTY='\[\e[38;5;196m\]' + C_GIT_RESET="$C_RESET" + ;; + 4) + # Theme 4: ANSI / Unicode-safe colors + GC_BRANCH='\[\e[0;37m\]' + GC_STAGED='\[\e[0;31m\]' + GC_CONFLICT='\[\e[0;31m\]' + GC_CHANGED='\[\e[0;33m\]' + GC_UNTRACKED='\[\e[0;32m\]' + GC_STASHED='\[\e[0;36m\]' + GC_AHEAD='\[\e[0;37m\]' + GC_BEHIND='\[\e[0;37m\]' + GC_NOREMOTE='\[\e[0;37m\]' + GC_CLEAN='\[\e[0;32m\]' + GC_DIRTY='\[\e[0;31m\]' + C_GIT_RESET="$C_RESET" + ;; + 5) + # Theme 5: Terminal-safe (ASCII) colors + GC_BRANCH='\[\e[0;37m\]' + GC_STAGED='\[\e[0;31m\]' + GC_CONFLICT='\[\e[0;31m\]' + GC_CHANGED='\[\e[0;33m\]' + GC_UNTRACKED='\[\e[0;32m\]' + GC_STASHED='\[\e[0;36m\]' + GC_AHEAD='\[\e[0;37m\]' + GC_BEHIND='\[\e[0;37m\]' + GC_NOREMOTE='\[\e[0;37m\]' + GC_CLEAN='\[\e[0;32m\]' + GC_DIRTY='\[\e[0;31m\]' + C_GIT_RESET="$C_RESET" + ;; + esac +} + +set_git_prompt_theme_icons +set_git_prompt_theme_colors + +# +# Cache (per shell tree) +# +GIT_CACHE_FILE="/dev/shm/git_prompt_${USER}_${PPID}.cache" + +# +# State collection +# +git_collect_state() { + + GIT_STATE_HEAD=$(git rev-parse HEAD 2>/dev/null) || return 1 + GIT_STATE_STATUS=$(git status --porcelain=v1 --branch 2>/dev/null) || return 1 + GIT_STATE_STASH_LIST=$(git stash list 2>/dev/null || true) + + local untracked_raw + untracked_raw=$(git ls-files --others --exclude-standard 2>/dev/null || true) + + GIT_STATE_UNTRACKED="" + while IFS= read -r f; do + for d in node_modules vendor dist build cache uploads public/uploads tmp .terraform .cache; do + [[ "$f" == "$d/"* ]] && continue 2 + done + GIT_STATE_UNTRACKED+="$f"$'\n' + done <<< "$untracked_raw" + + GIT_STATE_FINGERPRINT=$(printf '%s%s%s%s%s' \ + "$GIT_STATE_HEAD" \ + "$GIT_STATE_STATUS" \ + "$GIT_STATE_STASH_LIST" \ + "$GIT_STATE_UNTRACKED" \ + "$GIT_PROMPT_THEME" | cksum | awk '{print $1}') +} + +# +# Render logic +# +actual_git_prompt_info_logic() { + + local branch + branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "(detached)") + + local staged conflicts changed untracked stashed + staged=$(grep -cE '^[AMDR] ' <<<"$GIT_STATE_STATUS") + conflicts=$(grep -cE '^UU ' <<<"$GIT_STATE_STATUS") + changed=$(grep -cE '^.[MD] ' <<<"$GIT_STATE_STATUS") + untracked=$(grep -c . <<<"$GIT_STATE_UNTRACKED") + stashed=$(grep -c . <<<"$GIT_STATE_STASH_LIST") + + local first_line=${GIT_STATE_STATUS%%$'\n'*} + local ahead=0 behind=0 has_remote=0 + [[ "$first_line" == *"..."* ]] && has_remote=1 + [[ "$first_line" =~ ahead\ ([0-9]+) ]] && ahead=${BASH_REMATCH[1]} + [[ "$first_line" =~ behind\ ([0-9]+) ]] && behind=${BASH_REMATCH[1]} + + local p="" + p+="${GC_BRANCH}${BRANCH_ICON}${branch}${C_GIT_RESET}" + + ((staged > 0)) && p+=" ${GC_STAGED}${STAGED_ICON}${staged}${C_GIT_RESET}" + ((conflicts > 0)) && p+=" ${GC_CONFLICT}${CONFLICT_ICON}${conflicts}${C_GIT_RESET}" + ((changed > 0)) && p+=" ${GC_CHANGED}${CHANGED_ICON}${changed}${C_GIT_RESET}" + ((untracked > 0)) && p+=" ${GC_UNTRACKED}${UNTRACKED_ICON}${untracked}${C_GIT_RESET}" + ((stashed > 0)) && p+=" ${GC_STASHED}${STASHED_ICON}${stashed}${C_GIT_RESET}" + ((ahead > 0)) && p+=" ${GC_AHEAD}${AHEAD_ICON}${ahead}${C_GIT_RESET}" + ((behind > 0)) && p+=" ${GC_BEHIND}${BEHIND_ICON}${behind}${C_GIT_RESET}" + ((has_remote == 0)) && p+=" ${GC_NOREMOTE}${NO_REMOTE_ICON}${C_GIT_RESET}" + + if (( staged + conflicts + changed + untracked == 0 )); then + p+=" ${GC_CLEAN}${CLEAN_ICON}${C_GIT_RESET}" + else + p+=" ${GC_DIRTY}${DIRTY_ICON}${C_GIT_RESET}" + fi + + echo "$p" +} + +# +# Prompt logic (cache-safe) +# +git_prompt_info() { + git_collect_state || return + + if [[ -f "$GIT_CACHE_FILE" ]]; then + local cached_fp + read -r cached_fp < "$GIT_CACHE_FILE" + if [[ "$cached_fp" == "$GIT_STATE_FINGERPRINT" ]]; then + sed '1d' "$GIT_CACHE_FILE" + return + fi + fi + + local output + output=$(actual_git_prompt_info_logic) + + { + echo "$GIT_STATE_FINGERPRINT" + echo "$output" + } > "$GIT_CACHE_FILE" + + echo "$output" +} + +# +# Update prompt (readability-ordered) +# +update_git_prompt() { + local PROMPT_CHAR='$' + [[ $EUID -eq 0 ]] && PROMPT_CHAR='#' + + local GIT_PS1 + GIT_PS1=$(git_prompt_info) + + # + # Theme 2: Powerline + # + if [[ "$GIT_PROMPT_THEME" -eq 2 ]]; then + if [[ -n "$GIT_PS1" ]]; then + PS1="${PL_GIT_SEP}${PL_SEP}${PL_GIT_BG}${PL_GIT_FG} ${GIT_PS1} ${PL_PATH_SEP}${PL_SEP}${PL_PATH_BG}${PL_PATH_FG} \w ${PL_END_SEP}${PL_SEP}${C_RESET} ${PROMPT_CHAR} " + else + PS1="${PL_PATH_SEP}${PL_SEP}${PL_PATH_BG}${PL_PATH_FG} \w ${PL_END_SEP}${PL_SEP}${C_RESET} ${PROMPT_CHAR} " + fi + + # + # Theme 3: Two-liner + # + elif [[ "$GIT_PROMPT_THEME" -eq 3 ]]; then + if [[ -n "$GIT_PS1" ]]; then + PS1="┌─ ${C_USER}\u@\h${C_RESET} [${GIT_PS1}] ${C_PATH}\w${C_RESET}\n└─ ${PROMPT_CHAR} " + else + PS1="┌─ ${C_USER}\u@\h${C_RESET} ${C_PATH}\w${C_RESET}\n└─ ${PROMPT_CHAR} " + fi + + # + # Themes 1, 4, 5: Default single-line + # + else + if [[ -n "$GIT_PS1" ]]; then + PS1="${C_USER}\u@\h${C_RESET} [${GIT_PS1}] ${C_PATH}\w${C_RESET} ${PROMPT_CHAR} " + else + PS1="${C_USER}\u@\h${C_RESET} ${C_PATH}\w${C_RESET} ${PROMPT_CHAR} " + fi + fi +} + +PROMPT_COMMAND=update_git_prompt + +# +# Theme switcher +# +gpchange() { + local theme="$1" + + if [[ "$theme" =~ ^[1-5]$ ]]; then + export GIT_PROMPT_THEME="$theme" + set_git_prompt_theme_icons + set_git_prompt_theme_colors + update_git_prompt + echo "Switched to Git prompt theme $theme" + else + echo "Usage: gpchange <1-5>" + fi +} + +# +# End of script +# diff --git a/fonts/JetBrainsMonoNerdFontMono-Regular.ttf b/fonts/JetBrainsMonoNerdFontMono-Regular.ttf new file mode 100644 index 0000000..f693474 Binary files /dev/null and b/fonts/JetBrainsMonoNerdFontMono-Regular.ttf differ diff --git a/install-fonts.sh b/install-fonts.sh new file mode 100755 index 0000000..5c02fe5 --- /dev/null +++ b/install-fonts.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +################################################################################## +# First Created: 03012025 Author: Allan Desc: Installs fonts for bash-git-prompt # +################################################################################## + + mkdir -p $HOME/.local/share/fonts + cp $HOME/.bash-git-prompt/fonts/*.ttf $HOME/.local/share/fonts + fc-cache -f diff --git a/last-tested b/last-tested new file mode 100644 index 0000000..f37b9a1 --- /dev/null +++ b/last-tested @@ -0,0 +1,6 @@ +------------------------------------ +Last tested: 04-02-2026 (DD-MM-YYYY) +Environment: Ubuntu Server 24.04 LTS +Environment: Ubuntu Desktop 24.04 LTS +Environment: Fedora Desktop 43 +------------------------------------