Check for git arrows asynchronously (#272)

Previously `command git rev-list --left-right --count HEAD...@'{u}'`
was run in a subshell, and upon `git fetch` completion, run once again.
This is now done as an async job (`prompt_pure_async_git_arrows`) and
is also called within `prompt_pure_async_git_fetch` to return the arrow
status immediately after `git fetch` has completed, this saves us from
waiting until the git fetch is done to only then execute a new async
job to check the arrow status.

Changes in git arrow / git dirty status are also handled better and
`prompt_pure_preprompt_render` is not called unless they actually
changed.
This commit is contained in:
Mathias Fredriksson
2017-01-19 17:30:19 +02:00
committed by GitHub
parent 3dd645970e
commit e06fd4af63

102
pure.zsh
View File

@@ -61,29 +61,6 @@ prompt_pure_clear_screen() {
prompt_pure_preprompt_render precmd prompt_pure_preprompt_render precmd
} }
prompt_pure_check_git_arrows() {
# reset git arrows
prompt_pure_git_arrows=
# check if there is an upstream configured for this branch
command git rev-parse --abbrev-ref @'{u}' &>/dev/null || return
local arrow_status
# check git left and right arrow_status
arrow_status="$(command git rev-list --left-right --count HEAD...@'{u}' 2>/dev/null)"
# exit if the command failed
(( !$? )) || return
# left and right are tab-separated, split on tab and store as array
arrow_status=(${(ps:\t:)arrow_status})
local arrows left=${arrow_status[1]} right=${arrow_status[2]}
(( ${right:-0} > 0 )) && arrows+="${PURE_GIT_DOWN_ARROW:-}"
(( ${left:-0} > 0 )) && arrows+="${PURE_GIT_UP_ARROW:-}"
[[ -n $arrows ]] && prompt_pure_git_arrows=" ${arrows}"
}
prompt_pure_set_title() { prompt_pure_set_title() {
# emacs terminal does not support settings the title # emacs terminal does not support settings the title
(( ${+EMACS} )) && return (( ${+EMACS} )) && return
@@ -216,9 +193,6 @@ prompt_pure_precmd() {
# with the initial preprompt rendering # with the initial preprompt rendering
prompt_pure_cmd_timestamp= prompt_pure_cmd_timestamp=
# check for git arrows
prompt_pure_check_git_arrows
# shows the full path in the title # shows the full path in the title
prompt_pure_set_title 'expand-prompt' '%~' prompt_pure_set_title 'expand-prompt' '%~'
@@ -237,33 +211,46 @@ prompt_pure_precmd() {
# fastest possible way to check if repo is dirty # fastest possible way to check if repo is dirty
prompt_pure_async_git_dirty() { prompt_pure_async_git_dirty() {
local untracked_dirty=$1; shift setopt localoptions noshwordsplit
local untracked_dirty=$1 dir=$2
# use cd -q to avoid side effects of changing directory, e.g. chpwd hooks # use cd -q to avoid side effects of changing directory, e.g. chpwd hooks
builtin cd -q "$*" builtin cd -q $dir
if [[ "$untracked_dirty" == "0" ]]; then if [[ $untracked_dirty = 0 ]]; then
command git diff --no-ext-diff --quiet --exit-code command git diff --no-ext-diff --quiet --exit-code
else else
test -z "$(command git status --porcelain --ignore-submodules -unormal)" test -z "$(command git status --porcelain --ignore-submodules -unormal)"
fi fi
(( $? )) && echo "*" return $?
} }
prompt_pure_async_git_fetch() { prompt_pure_async_git_fetch() {
setopt localoptions noshwordsplit
# use cd -q to avoid side effects of changing directory, e.g. chpwd hooks # use cd -q to avoid side effects of changing directory, e.g. chpwd hooks
builtin cd -q "$*" builtin cd -q $1
# set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+) # set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+)
export GIT_TERMINAL_PROMPT=0 export GIT_TERMINAL_PROMPT=0
# set ssh BachMode to disable all interactive ssh password prompting # set ssh BachMode to disable all interactive ssh password prompting
export GIT_SSH_COMMAND=${GIT_SSH_COMMAND:-"ssh -o BatchMode=yes"} export GIT_SSH_COMMAND=${GIT_SSH_COMMAND:-"ssh -o BatchMode=yes"}
command git -c gc.auto=0 fetch command git -c gc.auto=0 fetch &>/dev/null || return 1
# check arrow status after a successful git fetch
prompt_pure_async_git_arrows $1
}
prompt_pure_async_git_arrows() {
setopt localoptions noshwordsplit
builtin cd -q $1
command git rev-list --left-right --count HEAD...@'{u}'
} }
prompt_pure_async_tasks() { prompt_pure_async_tasks() {
setopt localoptions noshwordsplit
# initialize async worker # initialize async worker
((!${prompt_pure_async_init:-0})) && { ((!${prompt_pure_async_init:-0})) && {
async_start_worker "prompt_pure" -u -n async_start_worker "prompt_pure" -u -n
@@ -282,6 +269,7 @@ prompt_pure_async_tasks() {
# reset git preprompt variables, switching working tree # reset git preprompt variables, switching working tree
unset prompt_pure_git_dirty unset prompt_pure_git_dirty
unset prompt_pure_git_last_dirty_check_timestamp unset prompt_pure_git_last_dirty_check_timestamp
prompt_pure_git_arrows=
# set the new working tree and prefix with "x" to prevent the creation of a named path by AUTO_NAME_DIRS # set the new working tree and prefix with "x" to prevent the creation of a named path by AUTO_NAME_DIRS
prompt_pure_current_working_tree="x${working_tree}" prompt_pure_current_working_tree="x${working_tree}"
@@ -290,10 +278,12 @@ prompt_pure_async_tasks() {
# only perform tasks inside git working tree # only perform tasks inside git working tree
[[ -n $working_tree ]] || return [[ -n $working_tree ]] || return
async_job "prompt_pure" prompt_pure_async_git_arrows $working_tree
# do not preform git fetch if it is disabled or working_tree == HOME # do not preform git fetch if it is disabled or working_tree == HOME
if (( ${PURE_GIT_PULL:-1} )) && [[ $working_tree != $HOME ]]; then if (( ${PURE_GIT_PULL:-1} )) && [[ $working_tree != $HOME ]]; then
# tell worker to do a git fetch # tell worker to do a git fetch
async_job "prompt_pure" prompt_pure_async_git_fetch "${working_tree}" async_job "prompt_pure" prompt_pure_async_git_fetch $working_tree
fi fi
# if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout # if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout
@@ -301,28 +291,52 @@ prompt_pure_async_tasks() {
if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then
unset prompt_pure_git_last_dirty_check_timestamp unset prompt_pure_git_last_dirty_check_timestamp
# check check if there is anything to pull # check check if there is anything to pull
async_job "prompt_pure" prompt_pure_async_git_dirty "${PURE_GIT_UNTRACKED_DIRTY:-1}" "${working_tree}" async_job "prompt_pure" prompt_pure_async_git_dirty ${PURE_GIT_UNTRACKED_DIRTY:-1} $working_tree
fi fi
} }
prompt_pure_async_callback() { prompt_pure_check_git_arrows() {
local job=$1 setopt localoptions noshwordsplit
local output=$3 local arrows left=${1:-0} right=${2:-0}
local exec_time=$4
case "${job}" in (( right > 0 )) && arrows+=${PURE_GIT_DOWN_ARROW:-}
(( left > 0 )) && arrows+=${PURE_GIT_UP_ARROW:-}
[[ -n $arrows ]] || return
typeset -g REPLY=" $arrows"
}
prompt_pure_async_callback() {
setopt localoptions noshwordsplit
local job=$1 code=$2 output=$3 exec_time=$4
case $job in
prompt_pure_async_git_dirty) prompt_pure_async_git_dirty)
prompt_pure_git_dirty=$output local prev_dirty=$prompt_pure_git_dirty
prompt_pure_preprompt_render if (( code == 0 )); then
prompt_pure_git_dirty=
else
prompt_pure_git_dirty="*"
fi
[[ $prev_dirty != $prompt_pure_git_dirty ]] && prompt_pure_preprompt_render
# When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color. # When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color.
# To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this # To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this
# variable. Thus, only upon next rendering of the preprompt will the result appear in a different color. # variable. Thus, only upon next rendering of the preprompt will the result appear in a different color.
(( $exec_time > 2 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS (( $exec_time > 2 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS
;; ;;
prompt_pure_async_git_fetch) prompt_pure_async_git_fetch|prompt_pure_async_git_arrows)
prompt_pure_check_git_arrows # prompt_pure_async_git_fetch executes prompt_pure_async_git_arrows
prompt_pure_preprompt_render # after a successful fetch.
if (( code == 0 )); then
local REPLY
prompt_pure_check_git_arrows ${(ps:\t:)output}
if [[ $prompt_pure_git_arrows != $REPLY ]]; then
prompt_pure_git_arrows=$REPLY
prompt_pure_preprompt_render
fi
fi
;; ;;
esac esac
} }