diff --git a/.bashrc b/.bashrc index ea9796f..1dd4a0a 100644 --- a/.bashrc +++ b/.bashrc @@ -1,5 +1,110 @@ #!/bin/bash - not strictly necessary, but helps nano with syntax highlighting. +__prompt_git_status() { + # Generate a git branch/status prompt. + # Environment variables: + # GIT_PROMPT_SHOW_TYPE=1 - Show type of repository (Bare, Shallow). + # GIT_PROMPT_SHOW_IGNORED=1 - Show a ! if the directory is ignored. + # GIT_PROMPT_SHOW_UNSTAGED=1 - Show a * if there are unstaged changes (superceeded by above). + # GIT_PROMPT_SHOW_UNTRACKED=1 - Show a ? if there are untracked files in the working directory (superceeded by above). + # GIT_PROMPT_SHOW_STASH=1 - Show a & if there is a stash in this repository (superceeded by above). + # GIT_PROMPT_SHOW_UPSTREAM=1 - Show status of this repository compaired to upstream: + # ?? - No upstream set. + # == - Working tree is equal to upstream. + # <> - Divergent from upstream. + # >> or >x - Working tree is ahead of upstream (x = commits ahead when used with next option). + # << or /dev/null) ) + ERR="$?" + + # Do nothing if there's an error. + (( ERR >= 1 )) || { (( ERR == 128 )) && (( ${#GIT_REPO_INFO[@]} != 3 )); } && return "$RET" + + # Generate the prompt. + if [[ "${GIT_REPO_INFO[2]}" == "true" ]]; then + # If in the git directory, use a special branch marker. + GIT_PROMPT+="!GIT_DIR!" + elif [[ "${GIT_REPO_INFO[3]}" == "true" ]]; then + # If in the working directory, generate the prompt. + # Add status markers. + [[ -n "$GIT_PROMPT_SHOW_TYPE" ]] && { + if [[ "${GIT_REPO_INFO[0]}" == "true" ]]; then + GIT_PROMPT+="B:" + elif [[ "${GIT_REPO_INFO[1]}" == "true" ]]; then + GIT_PROMPT+="S:" + fi + } + + # Add the branch. + GIT_PROMPT+="$(git describe --contains --all HEAD)" + + # Add a marker if directory is ignored, there's untracked files or a stash. + if [[ -n "$GIT_PROMPT_SHOW_IGNORED" ]] && git check-ignore -q "${PWD#"$(git rev-parse --show-toplevel)/"}"; then + GIT_PROMPT+=" !" + elif [[ -n "$GIT_PROMPT_SHOW_UNSTAGED" ]] && git ls-files --modified --exclude-standard --directory --error-unmatch -- ':/*' >/dev/null 2>&1; then + GIT_PROMPT+=" *" + elif [[ -n "$GIT_PROMPT_SHOW_UNTRACKED" ]] && git ls-files --others --exclude-standard --directory --error-unmatch -- ':/*' >/dev/null 2>&1; then + GIT_PROMPT+=" ?" + elif [[ -n "$GIT_PROMPT_SHOW_STASH" ]] && git rev-parse --verify --quiet refs/stash >/dev/null; then + GIT_PROMPT+=" &" + fi + + # Get upstream status. + [[ -n "$GIT_PROMPT_SHOW_UPSTREAM" ]] || [[ -n "$GIT_PROMPT_SHOW_UPSTREAM_EXTENDED" ]] && { + COUNT="$(git rev-list --count --left-right '@{upstream}'...HEAD 2>/dev/null | tr '[:blank:]' ' ')" + case "$COUNT" in + "") + # No upstream. + GIT_PROMPT+=" ??" + ;; + "0 0") + # Equal to upstream. + GIT_PROMPT+=" ==" + ;; + "0 "*) + # Ahead of upstream. + GIT_PROMPT+=" >" + if [[ -n "$GIT_PROMPT_SHOW_UPSTREAM_EXTENDED" ]]; then + # Show the number of the difference. + GIT_PROMPT+="${COUNT#0 }" + else + GIT_PROMPT+=">" + fi + ;; + *" 0") + # Behind upstream. + GIT_PROMPT+=" <" + if [[ -n "$GIT_PROMPT_SHOW_UPSTREAM_EXTENDED" ]]; then + # Show the number of the difference. + GIT_PROMPT+="${COUNT% 0}" + else + GIT_PROMPT+="<" + fi + ;; + *) + # Divergent from upstream. + GIT_PROMPT+=" <>" + ;; + esac + } + fi + + # Output the prompt. + # shellcheck disable=SC2059 + printf -- "$1" "$GIT_PROMPT" + + # Return the original error code. + return "$RET" +} + __find_ssh_agent_sock() { # Find an *active* ssh agent socket. # Returns: 0 = Found an active socket. @@ -248,6 +353,18 @@ else COLOUR=5 # Purple fi +# The commands to execute before the prompt is displayed. +PROMPT_COMMAND="__ssh_agent_prompt_command" + +# Git prompt options. +GIT_PROMPT_SHOW_TYPE=1 +GIT_PROMPT_SHOW_IGNORED=1 +GIT_PROMPT_SHOW_UNSTAGED=1 +GIT_PROMPT_SHOW_UNTRACKED=1 +GIT_PROMPT_SHOW_STASH=1 +GIT_PROMPT_SHOW_UPSTREAM=1 +GIT_PROMPT_SHOW_UPSTREAM_EXTENDED=1 + # Version specific set up. if (( BASH_VERSINFO[0] >= 4 )); then # Add to the shopts. @@ -256,12 +373,12 @@ if (( BASH_VERSINFO[0] >= 4 )); then # Trim the path in the prompt. PROMPT_DIRTRIM=2 # Coloured username + host + directory: - PS1="[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\w\[$(tput sgr0)\] ->" + PS1='[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\w\[$(tput sgr0)\]$(__prompt_git_status "\[\033[0;35;40m\] (%s)\[\033[0;37;40m\]") ->' else # Set the prompts. # Coloured username + host + directory: # shellcheck disable=SC2154 - PS1="[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}')\[$(tput sgr0)\] ->" + PS1="[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}')\[$(tput sgr0)\]$(__prompt_git_status "\[\033[0;35;40m\] (%s)\[\033[0;37;40m\]") ->" fi unset COLOUR @@ -269,9 +386,6 @@ unset COLOUR # shellcheck disable=SC2155 export PS4="+(\[\e[1;33;40m\]\$?\[$(tput sgr0)\]) \[$(tput bold)$(tput setaf 4)\]\${BASH_SOURCE##*/}\[$(tput sgr0)\]\${FUNCNAME[0]:+(\[$(tput bold)$(tput setaf 2)\]\${FUNCNAME[0]}\[$(tput sgr0)\])}:\[$(tput bold)$(tput setaf 1)\]\$LINENO\[$(tput sgr0)\]: " -# The commands to execute before the prompt is displayed. -PROMPT_COMMAND="__ssh_agent_prompt_command" - # Common aliases. hash bc >/dev/null 2>&1 && alias bc='bc -lq' hash diff >/dev/null 2>&1 && alias diff='diff --color=auto -u'