Skip to content

Instantly share code, notes, and snippets.

@toonetown
Created February 20, 2021 18:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save toonetown/f7e905f6c730043e84a0b918332fc24d to your computer and use it in GitHub Desktop.
Save toonetown/f7e905f6c730043e84a0b918332fc24d to your computer and use it in GitHub Desktop.
# Copyright (C) 2015 Facebook, Inc
# Maintained by Ryan McElroy <rm@fb.com>
#
# Inspiration and derivation from git-completion.bash by Shawn O. Pearce.
#
# Distributed under the GNU General Public License, version 2.0.
#
# ========================================================================
#
# Quickly determines the and emits some useful information about the state
# of your current mercurial or git repository. Useful for PS1 prompts.
#
# Design goals:
# * Useful for both git and mercurial
# * Portable to both zsh and bash
# * Portable to both Mac (BSD-based utils) and Linux (GNU-based utils)
# * As fast as possible given the above constraints (few command invocations)
# * Avoids invoking git or mercurial, which may be slow on large repositories
#
# To use from zsh:
#
# NOTE! the single quotes are important; if you use double quotes
# then the prompt won't change when you chdir or checkout different
# branches!
#
# . /path/to/scm-prompt
# setopt PROMPT_SUBST
# export PS1='$(_scm_prompt)$USER@%m:%~%% '
#
# To use from bash:
#
# . /path/to/scm-prompt
# export PS1="\$(_scm_prompt)\u@\h:\W\$ "
#
# NOTE! You *EITHER* need to single-quote the whole thing *OR* back-slash
# the $(...) (as above), but not both. Which one you use depends on if
# you need the rest of your PS1 to interpolate variables.
#
# You may additionally pass a format-string to the scm_info command. This
# allows you to control the format of the prompt string without interfering
# with the prompt outside of a mercurial or git repository. For example:
#
# $(_scm_prompt "%s")
#
# The default format string is " (%s)" (note the space)
#
# =========================================================================
#
_scm_prompt()
{
# find out if we're in a git or hg repo by looking for the control dir
local d git hg fmt
fmt=$1
if [[ -z "$fmt" ]]; then
if [ -n "$WANT_OLD_SCM_PROMPT" ]; then
fmt="%s"
else
# Be compatable with __git_ps1. In particular:
# - provide a space for the user so that they don't have to have
# random extra spaces in their prompt when not in a repo
# - provide parens so it's differentiated from other crap in their prompt
fmt=" (%s)"
fi
fi
d=$PWD
while : ; do
if test -d "$d/.git" ; then
git=$d
break
elif test -d "$d/.hg" ; then
hg=$d
break
fi
test "$d" = / && break
# portable "realpath" equivalent
d=$(cd -P "$d/.." && echo "$PWD")
done
local br
if test -n "$hg" ; then
local extra
if [ -f "$hg/.hg/bisect.state" ]; then
extra="|BISECT"
elif [ -f "$hg/.hg/histedit-state" ]; then
extra="|HISTEDIT"
elif [ -f "$hg/.hg/graftstate" ]; then
extra="|GRAFT"
elif [ -f "$hg/.hg/unshelverebasestate" ]; then
extra="|UNSHELVE"
elif [ -f "$hg/.hg/rebasestate" ]; then
extra="|REBASE"
elif [ -d "$hg/.hg/merge" ]; then
extra="|MERGE"
fi
local dirstate=$(test -f "$hg/.hg/dirstate" && \
hexdump -vn 20 -e '1/1 "%02x"' "$hg/.hg/dirstate" || \
echo "empty")
local current="$hg/.hg/bookmarks.current"
if [[ -f "$current" ]]; then
br=$(cat "$current")
# check to see if active bookmark needs update (eg, moved after pull)
local marks="$hg/.hg/bookmarks"
if [[ -f "$hg/.hg/sharedpath" && -f "$hg/.hg/shared" ]] &&
grep -q '^bookmarks$' "$hg/.hg/shared"; then
marks="$(cat $hg/.hg/sharedpath)/bookmarks"
fi
if [[ -z "$extra" ]] && [[ -f "$marks" ]]; then
local markstate=$(grep --color=never " $br$" "$marks" | cut -f 1 -d ' ')
if [[ $markstate != "$dirstate" ]]; then
extra="|UPDATE_NEEDED"
fi
fi
else
br=$(echo "$dirstate" | cut -c 1-7)
fi
local remote="$hg/.hg/remotenames"
if [[ -f "$remote" ]]; then
local marks=$(grep --color=never "^$dirstate bookmarks" "$remote" | \
cut -f 3 -d ' ' | tr '\n' '|' | sed -e 's/|$//')
if [[ -n "$marks" ]]; then
br="$br|$marks"
fi
fi
local branch
if [[ -f "$hg/.hg/branch" ]]; then
branch=$(cat "$hg/.hg/branch")
if [[ $branch != "default" ]]; then
br="$br|$branch"
fi
fi
br="$br$extra"
elif test -n "$git" ; then
if test -f "$git/.git/HEAD" ; then
read br < "$git/.git/HEAD"
case $br in
ref:\ refs/heads/*) br=${br#ref: refs/heads/} ;;
*) br=$(echo "$br" | cut -c 1-7) ;;
esac
if [ -f "$git/.git/rebase-merge/interactive" ]; then
b="$(cat "$git/.git/rebase-merge/head-name")"
b=${b#refs/heads/}
br="$br|REBASE-i|$b"
elif [ -d "$git/.git/rebase-merge" ]; then
b="$(cat "$git/.git/rebase-merge/head-name")"
b=${b#refs/heads/}
br="$br|REBASE-m|$b"
else
if [ -d "$git/.git/rebase-apply" ]; then
if [ -f "$git/.git/rebase-apply/rebasing" ]; then
b="$(cat "$git/.git/rebase-apply/head-name")"
b=${b#refs/heads/}
br="$br|REBASE|$b"
elif [ -f "$git/.git/rebase-apply/applying" ]; then
br="$br|AM"
else
br="$br|AM/REBASE"
fi
elif [ -f "$git/.git/CHERRY_PICK_HEAD" ]; then
br="$br|CHERRY-PICKING"
elif [ -f "$git/.git/REVERT_HEAD" ]; then
br="$br|REVERTING"
elif [ -f "$git/.git/MERGE_HEAD" ]; then
br="$br|MERGE"
elif [ -f "$git/.git/BISECT_LOG" ]; then
br="$br|BISECT"
fi
fi
fi
fi
if [ -n "$br" ]; then
printf "$fmt" "$br"
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment