Skip to content

Instantly share code, notes, and snippets.

@iamtew
Last active June 5, 2021 16:03
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iamtew/7006481 to your computer and use it in GitHub Desktop.
Save iamtew/7006481 to your computer and use it in GitHub Desktop.
#! /usr/bin/env bash
# Skip all of this if we don't have a TTY
tty -s || return 0
# Colors + \[...\] wrapping to ensure there's no odd wrapping on command line
C_OFF="\[$(tput sgr0)\]"
C_BOLD="\[$(tput bold)\]"
C_BLACK="\[$(tput setaf 0)\]"
C_RED="\[$(tput setaf 1)\]"
C_GREEN="\[$(tput setaf 2)\]"
C_YELLOW="\[$(tput setaf 3)\]"
C_BLUE="\[$(tput setaf 4)\]"
C_MAGENTA="\[$(tput setaf 5)\]"
C_CYAN="\[$(tput setaf 6)\]"
C_WHITE="\[$(tput setaf 7)\]"
C_MAIN="$C_CYAN"
C_ROOT="$C_WHITE"
C_FAIL="$C_RED"
p_prefix="["
p_suffix="]"
# p_prefix="┇"
# p_suffix="┆"
# p_prefix="「"
# p_suffix="」"
# Various variables for easier referencing
TIME24H="\t"
TIME12H="\T"
TIME12A="\@"
PATHSHORT="\w"
PATHFULL="\W"
HOSTNAMESHORT="\h"
HOSTNAMEFULL="\H"
NEWLINE="\n"
JOBS="\j"
# Command stack, use this to store last executed command
cmd_stack=()
trap 'cmd_stack=("${cmd_stack[@]}" "$BASH_COMMAND")' DEBUG
# Git information
__git_prompt() {
local git_prompt
local status
local branch
local ref
local dirty
local tracking
status=$(git status 2> /dev/null)
# Check dirty status
[[ -z $(git status --porcelain 2> /dev/null) ]] \
&& dirty="${c_green}✓${c_off}" \
|| dirty="${c_red}✗${c_off}"
# Get our branch or ref, depending if we're in detached HEAD or not.
ref=$(git symbolic-ref HEAD 2>/dev/null) \
&& { ref=${ref#refs/heads/} ; branch="$ref" ; } \
|| ref=$(git reflog HEAD | awk 'NR==1 && /checkout:/ { print $NF }')
# Check if we're ahead or behind upstream
if [[ -n "$branch" ]] ; then
local upstream_ref
local ahead
local behind
upstream_ref=$(git for-each-ref --format='%(refname:short) %(upstream:short)' refs/heads | awk -v branch="$branch" '"/^$branch/" { print $NF }')
if [[ -n "$upstream_ref" ]] ; then
ahead=$(git rev-list --count --left-right "$upstream_ref"...HEAD | cut -f1)
behind=$(git rev-list --count --left-right "$upstream_ref"...HEAD | cut -f2)
[[ $ahead -gt 0 ]] && tracking+="${C_CYAN}<${ahead}${C_OFF}:"
[[ $behind -gt 0 ]] && tracking+="${C_MAGENTA}<${behind}${C_OFF}:"
fi
fi
# Put together our prompt string
git_prompt+="["
git_prompt+="${c_green}${ref}${c_off}:"
git_prompt+="${tracking}"
git_prompt+="${dirty}"
git_prompt+="]"
echo "$git_prompt"
}
prompt.git.dirty() {
local status
status=$(git status --porcelain 2> /dev/null) || return 1
[[ -z $status ]] \
&& echo "${C_GREEN}✓${C_OFF}" \
|| echo "${C_RED}✗${C_OFF}"
}
prompt.git.updown() {
local branch
local tracking
local upstream_ref
local updown
local git_ahead
local git_behind
branch="$(prompt.git.branch)"
upstream_ref=$(git for-each-ref --format='%(refname:short)|%(upstream:short)' refs/heads \
| grep "^$branch|" \
| cut -d'|' -f2)
if [[ "$upstream_ref" ]]; then
updown=( $(git rev-list --count --left-right "$upstream_ref"...HEAD) )
[[ ${updown[0]} -gt 0 ]] && tracking+="${C_CYAN}-${updown[0]}${C_OFF}:" # Behind
[[ ${updown[1]} -gt 0 ]] && tracking+="${C_MAGENTA}+${updown[1]}${C_OFF}:" # Ahead
# git_ahead=$(git rev-list --count --left-right "$upstream_ref"...HEAD | cut -f1)
# git_behind=$(git rev-list --count --left-right "$upstream_ref"...HEAD | cut -f2)
fi
echo "$tracking"
}
prompt.git.branch() {
local branch
branch="$(git symbolic-ref HEAD 2> /dev/null)"
branch="${branch#refs/heads/}"
# If we're in detached HEAD the above doesn't work, so use reflog instead
[[ -z $branch ]] && branch="$(git reflog HEAD | grep 'checkout:' | head -n1 | grep -oE '[^ ]+$')"
echo "$branch"
}
# Main prompt function
prompt() {
local cmd_exit="$?" # Exit code of previous command
local tformat="%T"
# If last command was this function, reset cmd_exit
[[ "${cmd_stack[@]}" == "prompt" ]] && cmd_exit=0
# Reset stack
cmd_stack=()
# # Update the terminal title
if [[ "$TERM" =~ xterm* ]] ; then
echo -en "\033]0;${USER}@$(hostname -s):${PWD}\007"
fi
# Begin building up our prompt
# Change the color if we're root :^D
[[ $UID -eq 0 ]] && C_MAIN="$C_ROOT"
# Show previous command exit code if non-zero
PS1="${C_MAIN}${p_prefix}"
if [[ $cmd_exit -eq 0 ]] ; then
PS1+="$(date +$tformat)"
else
# Add some padding to look nice
case "${#cmd_exit}" in
1) PS1+="${C_FAIL}EXIT ${cmd_exit} " ;;
2) PS1+="${C_FAIL}EXIT ${cmd_exit} " ;;
3) PS1+="${C_FAIL}EXIT ${cmd_exit}" ;;
esac
# PS1+="${C_RED}EXIT ${cmd_exit}"
fi
PS1+="${C_YELLOW}:\w${C_MAIN}${p_suffix}${C_OFF}"
# Add Git information
if [[ -d .git ]] || [[ $(git status 2> /dev/null) ]]; then
PS1+="\n${C_GREEN}${p_prefix}"
PS1+="$(prompt.git.branch):$(prompt.git.updown)$(prompt.git.dirty)"
PS1+="${C_GREEN}${p_suffix}${C_OFF}"
fi
[[ -n $PL ]] \
&& PS1+="$(tput setab 4)$(tput setaf 7)$(tput bold) $PL $(tput sgr0)"
# $ or # at the end of the prompt
PS1+='\$ '
# Export variables to the environment
export PS1
# Append last command to our history file
history -a
}
export PROMPT_DIRTRIM=2
export PROMPT_COMMAND="prompt"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment