sapling/contrib/scm-prompt.sh
Andrew Regner 537d945c15 Do not probe the home directory because autofs
Summary:
I got tired of my prompt taking a long time to return to me sometimes after a command ended, and started to investigate, and found this:

```
aregner  4069767  0.0  0.0 115052  5076 pts/17   Ss+  12:32   0:00  \_ -bash
aregner   298922  0.0  0.0 115052  3540 pts/17   S+   14:40   0:00  |   \_ -bash
aregner   298923  0.0  0.0 115052  3668 pts/17   S+   14:40   0:00  |       \_ -bash
aregner   273995  0.0  0.0 115052  4824 pts/9    Ss+  14:32   0:00  \_ -bash
aregner   274061  0.0  0.0 115052  3820 pts/9    S+   14:32   0:00      \_ -bash
[devbig308 (77bac96|remote/fbcode/warm):~/2nd-fbcode] strace -p 298923
strace: Process 298923 attached
stat("/home/.hg",
```

Sometimes it will hang ong this for 5+ minutes.  I'm assuming this has something to do with autofs and NFS home dirs or something or another, but it doesn't matter because when it gets to this point it should just stop looking for a source control directory.

Reviewed By: ryanmce

Differential Revision: D6790747

fbshipit-source-id: d32c74476046aa6b62bc26725eea6b60a4eaa9f7
2018-04-13 21:50:59 -07:00

246 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") || \
( [[ -f "$hg/../.eden/client/SNAPSHOT" ]] && \
command hexdump -s 8 -vn 20 -e '1/1 "%02x"' \
"$hg/../.eden/client/SNAPSHOT") || \
builtin echo "empty")"
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="$(command cat $hg/sharedpath)/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-7)"
fi
local remote="$hg/remotenames"
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-7)" ;;
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"
}