sapling/contrib/scm-prompt.sh
Jun Wu bbd4b91102 remotenames: move 'remotenames' state to storevfs
Summary:
In the future the changelog would need to access 'remotenames' to figure out
public heads. Move the state file to svfs so it can be read by the changelog
object, which only has access to svfs.

Reviewed By: sfilipco

Differential Revision: D17199834

fbshipit-source-id: 9000e0d8e8bb8d398d6c77b5b395da904fef6418
2019-09-13 20:19:09 -07:00

250 lines
7.8 KiB
Bash

# 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!
#
# setopt PROMPT_SUBST
# source /path/to/scm-prompt
# export PS1='$(_scm_prompt)$USER@%m:%~%% '
#
# To use from bash:
#
# source /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)
#
# Options: you may want to set these environment variables
# * HOME_IS_NOT_A_REPO : Stop walking up the filesystem looking for a git or
# hg repo at (but not including) /home instead of /. This helps when /home
# is a remote or autofs mount point.
# * WANT_OLD_SCM_PROMPT : Use '%s' as the formatting for the prompt instead
# of ' (%s)'
#
# Notes to developers:
#
# * Aliases can screw up the default commands. To prevent this issue, use
# the 'builtin' prefix for built-in shell commands (eg, 'cd' and 'echo')
# and use the 'command' prefix for external commands that you do not want
# to invoke aliases for (eg, 'grep', 'cut').
#
# =========================================================================
#
_find_most_relevant() {
# We don't want to output all remote bookmarks because there can be many
# of them. This function finds the most relevant remote bookmark using this
# algorithm:
# 1. If 'master' or '@' bookmark is available then output it
# 2. Sort remote bookmarks and output the first in reverse sorted order (
# it's a heuristic that tries to find the newest bookmark. It will work well
# with bookmarks like 'release20160926' and 'release20161010').
relevantbook="$(command grep -m1 -E -o "^[^/]+/(master|@)$" <<< "$1")"
if [[ -n $relevantbook ]]; then
builtin echo $relevantbook
return 0
fi
builtin echo "$(command sort -r <<< "$1" | command head -n 1)"
}
_hg_prompt() {
local hg br extra
hg="$1"
if [[ -f "$hg/bisect.state" ]]; then
extra="|BISECT"
elif [[ -f "$hg/histedit-state" ]]; then
extra="|HISTEDIT"
elif [[ -f "$hg/graftstate" ]]; then
extra="|GRAFT"
elif [[ -f "$hg/unshelverebasestate" ]]; then
extra="|UNSHELVE"
elif [[ -f "$hg/rebasestate" ]]; then
extra="|REBASE"
elif [[ -d "$hg/merge" ]]; then
extra="|MERGE"
elif [[ -L "$hg/store/lock" ]]; then
extra="|STORE-LOCKED"
elif [[ -L "$hg/wlock" ]]; then
extra="|WDIR-LOCKED"
fi
local dirstate="$( \
( [[ -f "$hg/dirstate" ]] && \
command hexdump -vn 20 -e '1/1 "%02x"' "$hg/dirstate") || \
builtin echo "empty")"
local remote="$hg/store/remotenames"
local shared_hg="$hg"
if [[ -f "$hg/sharedpath" ]]; then
shared_hg="$(command cat $hg/sharedpath)"
remote="$shared_hg/store/remotenames"
fi
local active="$hg/bookmarks.current"
if [[ -f "$active" ]]; then
br="$(command cat "$active")"
# check to see if active bookmark needs update (eg, moved after pull)
local marks="$hg/bookmarks"
if [[ -f "$hg/sharedpath" && -f "$hg/shared" ]] &&
command grep -q '^bookmarks$' "$hg/shared"; then
marks="$shared_hg/bookmarks"
fi
if [[ -z "$extra" ]] && [[ -f "$marks" ]]; then
local markstate="$(command grep " $br$" "$marks" | \
command cut -f 1 -d ' ')"
if [[ $markstate != "$dirstate" ]]; then
extra="|UPDATE_NEEDED"
fi
fi
else
br="$(builtin echo "$dirstate" | command cut -c 1-8)"
fi
if [[ -f "$remote" ]]; then
local allremotemarks="$(command grep "^$dirstate bookmarks" "$remote" | \
command cut -f 3 -d ' ')"
if [[ -n "$allremotemarks" ]]; then
local remotemark="$(_find_most_relevant "$allremotemarks")"
if [[ -n "$remotemark" ]]; then
br="$br|$remotemark"
if [[ "$remotemark" != "$allremotemarks" ]]; then
# if there is more than one, let the user know with an elipsis
br="${br}..."
fi
fi
fi
fi
local branch
if [[ -f "$hg/branch" ]]; then
branch="$(command cat "$hg/branch")"
if [[ "$branch" != "default" ]]; then
br="$br|$branch"
fi
fi
br="$br$extra"
builtin printf "%s" "$br"
}
_git_prompt() {
local git br
git="$1"
if [[ -f "$git/HEAD" ]]; then
read br < "$git/HEAD"
case $br in
ref:\ refs/heads/*) br=${br#ref: refs/heads/} ;;
*) br="$(builtin echo "$br" | command cut -c 1-8)" ;;
esac
if [[ -f "$git/rebase-merge/interactive" ]]; then
b="$(command cat "$git/rebase-merge/head-name")"
b="${b#refs/heads/}"
br="$br|REBASE-i|$b"
elif [[ -d "$git/rebase-merge" ]]; then
b="$(command cat "$git/rebase-merge/head-name")"
b="${b#refs/heads/}"
br="$br|REBASE-m|$b"
else
if [[ -d "$git/rebase-apply" ]]; then
if [[ -f "$git/rebase-apply/rebasing" ]]; then
b="$(command cat "$git/rebase-apply/head-name")"
b="${b#refs/heads/}"
br="$br|REBASE|$b"
elif [[ -f "$git/rebase-apply/applying" ]]; then
br="$br|AM"
else
br="$br|AM/REBASE"
fi
elif [[ -f "$git/CHERRY_PICK_HEAD" ]]; then
br="$br|CHERRY-PICKING"
elif [[ -f "$git/REVERT_HEAD" ]]; then
br="$br|REVERTING"
elif [[ -f "$git/MERGE_HEAD" ]]; then
br="$br|MERGE"
elif [[ -f "$git/BISECT_LOG" ]]; then
br="$br|BISECT"
fi
fi
fi
builtin printf "%s" "$br"
}
_scm_prompt() {
local dir fmt br
# Default to 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="${1:- (%s)}"
# find out if we're in a git or hg repo by looking for the control dir
dir="$PWD"
while : ; do
[[ -n "$HOME_IS_NOT_A_REPO" ]] && [[ "$dir" = "/home" ]] && break
if [[ -d "$dir/.git" ]]; then
br="$(_git_prompt "$dir/.git")"
break
elif [[ -d "$dir/.hg" ]]; then
br="$(_hg_prompt "$dir/.hg")"
break
fi
[[ "$dir" = "/" ]] && break
# portable "realpath" equivalent
dir="$(builtin cd -P "$dir/.." && builtin echo "$PWD")"
done
if [[ -n "$br" ]]; then
builtin printf "$fmt" "$br"
fi
}
#
# Backwards-compatibility layer for odl scm-prompt script
#
# Older versions of this file at Facebook used a longer function name.
# These versions also included support for an environmental directive
# called $WANT_OLD_SCM_PROMPT. Support this to remain compatible.
#
_dotfiles_scm_info() {
local fmt
fmt=$1
if [[ -z "$fmt" ]]; then
if [[ -n "$WANT_OLD_SCM_PROMPT" ]]; then
fmt="%s"
else
fmt=' (%s)'
fi
fi
_scm_prompt "$fmt"
}