#! /usr/bin/zsh
-export __ZSH_GIT_BASEDIR=""
-export __ZSH_GIT_BRANCH=""
-export __ZSH_GIT_SUBDIR=""
-export __ZSH_GIT_STATE=""
-export __ZSH_GIT_VARS_INVALID=1
+__ZSH_GIT_BASEDIR=""
+__ZSH_GIT_SUBDIR=""
+__ZSH_GIT_BRANCH=""
+__ZSH_GIT_ACTION=""
+__ZSH_GIT_STATUS=""
+__ZSH_GIT_VARS_INVALID=1
+__ZSH_GIT_STATUS_INVALID=1
+
+
+git_chpwd() {
+ # On cd, invalidate git status in prompt
+ __ZSH_GIT_VARS_INVALID=1
+}
+
+
+git_preexec() {
+ # On git command, invalidate git status in prompt
+ case "$1" in
+ git*)
+ __ZSH_GIT_VARS_INVALID=1
+ ;;
+ esac
+
+ # *any* command could invalidate the repository status (new file, ...)
+ __ZSH_GIT_STATUS_INVALID=1
+}
+
+
+git_get_status() {
+ # Return only git status
+ local gitstat gitstatus
+
+ gitstat=$(git status 2> /dev/null | grep '\(# Untracked\|# Changes\|# Changed but not updated:\)')
+ gitstatus=""
+
+ if [[ $(echo ${gitstat} | grep -c "^# Changes to be committed:$") > 0 ]]; then
+ gitstatus='+'
+ fi
+
+ if [[ $(echo ${gitstat} | grep -c "^\# Changed but not updated:$") > 0 ]]; then
+ gitstatus="${gitstatus}!"
+ fi
+
+ if [[ $(echo ${gitstat} | grep -c "^# Untracked files:$") > 0 ]]; then
+ gitstatus="${gitstatus}?"
+ fi
+
+ echo $gitstatus
+}
+
git_parse() {
+ # psvar[5] == current action (merge, rebase, ...)
+ # psvar[6] == current branch
+ # psvar[7] == repository base directory
+ # psvar[8] == current subdir into repository
+ # psvar[9] == status (untracked, unstaged, staged)
+
+ local git_dir ref base_dir sub_dir action branch gitstat gitstatus
+
+
+ # If nothing has been invalidated
+ if [[ "${__ZSH_GIT_VARS_INVALID}" == "0" && "${__ZSH_GIT_STATUS_INVALID}" == "0" ]]; then
+
+ # reuse previous values
+ psvar[5]=${__ZSH_GIT_ACTION}
+ psvar[6]=${__ZSH_GIT_BRANCH}
+ psvar[7]=${__ZSH_GIT_BASEDIR}
+ psvar[8]=${__ZSH_GIT_SUBDIR}
+ psvar[9]=${__ZSH_GIT_STATUS}
+
+ return
+ fi
+
+ # If only status has been invalidated
+ if [[ "${__ZSH_GIT_VARS_INVALID}" == "0" && "${__ZSH_GIT_STATUS_INVALID}" == "1" ]]; then
- psvar=()
- local git_dir ref base_dir sub_dir action branch
+ # reuse previous values
+ psvar[5]=${__ZSH_GIT_ACTION}
+ psvar[6]=${__ZSH_GIT_BRANCH}
+ psvar[7]=${__ZSH_GIT_BASEDIR}
+ psvar[8]=${__ZSH_GIT_SUBDIR}
+
+ __ZSH_GIT_STATUS=$(git_get_status)
+ psvar[9]=${__ZSH_GIT_STATUS}
+
+ __ZSH_GIT_STATUS_INVALID=0
+ return
+ fi
+
+
+ # Git prompt variables are invalid. Update them.
git_dir=$(git rev-parse --git-dir 2> /dev/null) || return
sub_dir=${sub_dir%/}
ref=$(git symbolic-ref HEAD 2> /dev/null) || return
- psvar[3]=""
+ action=""
if [ -d "$git_dir/../.dotest" ]; then
if [ -f "$git_dir/../.dotest/rebasing" ]; then
- psvar[3]="-rebase"
+ action="-rebase"
elif [ -f "$git_dir/../.dotest/applying" ]; then
- psvar[3]="-am"
+ action="-am"
else
- psvar[3]="-am-rebase"
+ action="-am-rebase"
fi
branch="$ref"
elif [ -f "$git_dir/.dotest-merge/interactive" ]; then
- psvar[3]="-rebase-i"
+ action="-rebase-i"
branch="$(cat "$git_dir/.dotest-merge/head-name")"
elif [ -d "$git_dir/.dotest-merge" ]; then
- psvar[3]="-rebase-m"
+ action="-rebase-m"
branch="$(cat "$git_dir/.dotest-merge/head-name")"
elif [ -f "$git_dir/MERGE_HEAD" ]; then
- psvar[3]="-merge"
+ action="-merge"
branch="$ref"
else
test -f "$git_dir/BISECT_LOG" && psvar[3]="bisect"
branch="$(cut -c1-7 "$git_dir/HEAD")..."
fi
+ # Status
+ gitstatus=`git_get_status`
+
# Got here, we're in git
- psvar[4]=${branch#refs/heads/}
- psvar[5]=${base_dir}
- psvar[6]=${sub_dir}
+ psvar[5]=${action}
+ psvar[6]=${branch#refs/heads/}
+ psvar[7]=${base_dir}
+ psvar[8]=${sub_dir}
+ psvar[9]=${gitstatus}
+
+ # Save for next time
+ __ZSH_GIT_BASEDIR="${base_dir}"
+ __ZSH_GIT_SUBDIR="${sub_dir}"
+ __ZSH_GIT_BRANCH="${branch#refs/heads/}"
+ __ZSH_GIT_ACTION="${action}"
+ __ZSH_GIT_STATUS="${gitstatus}"
+
+ __ZSH_GIT_VARS_INVALID=0
+ __ZSH_GIT_STATUS_INVALID=0
}
+
+chpwd_functions+='git_chpwd'
+preexec_functions+='git_preexec'
precmd_functions+='git_parse'