prezto/plugins/git/functions/git-info
Colin Hebert fc516bcffd Git-plugin: Don't truncate commit number
A branch name shouldn't be a commit number, and a commit number should always be in the long format.
Themes can reduce the size of the commit id, and they should be able to choose between the branch name or the commit id
2012-03-15 00:19:49 +00:00

393 lines
9.9 KiB
Text

#
# Displays Git repository information.
#
# Authors:
# Sorin Ionescu <sorin.ionescu@gmail.com>
#
# Gets the path to the Git directory.
function _git-dir() {
local git_root="$(git-root)"
local git_dir_or_file="${git_root}/.git"
local git_dir
if [[ ! -d "$git_root" ]]; then
return 1
fi
if [[ -f "$git_dir_or_file" ]]; then
git_dir="${${${$(<"$git_dir_or_file")}[(fr)gitdir:*]}#gitdir: }"
else
git_dir="$git_dir_or_file"
fi
if [[ -d "$git_dir" ]]; then
print "$git_dir"
return 0
fi
return 1
}
# Gets the Git special action (am, merge, rebase, etc.).
# Borrowed from vcs_info and edited.
function _git-action() {
local action=''
local action_dir
local git_dir="$(_git-dir)"
for action_dir in \
"${git_dir}/rebase-apply" \
"${git_dir}/rebase" \
"${git_dir}/../.dotest"; do
if [[ -d "$action_dir" ]] ; then
if [[ -f "${action_dir}/rebasing" ]] ; then
action='rebase'
elif [[ -f "${action_dir}/applying" ]] ; then
action='am'
else
action='am/rebase'
fi
print "$action"
return 0
fi
done
for action_dir in \
"${git_dir}/rebase-merge/interactive" \
"${git_dir}/.dotest-merge/interactive"; do
if [[ -f "$action_dir" ]]; then
print 'rebase-i'
return 0
fi
done
for action_dir in \
"${git_dir}/rebase-merge" \
"${git_dir}/.dotest-merge"; do
if [[ -d "$action_dir" ]]; then
print 'rebase-m'
return 0
fi
done
if [[ -f "${git_dir}/MERGE_HEAD" ]]; then
print 'merge'
return 0
fi
if [[ -f "${git_dir}/CHERRY_PICK_HEAD" ]]; then
print 'cherry-pick'
return 0
fi
if [[ -f "${git_dir}/BISECT_LOG" ]]; then
print 'bisect'
return 0
fi
return 1
}
# Turns off git-info for the current repository.
function _git-info-abort() {
if ! is-true "$_git_info_executing"; then
return 1
fi
cat >&2 <<EOF
Gathering status for certain repositories is time intensive.
By pressing CTRL + C, you have turned off prompt Git status
for this repository.
To revert, execute:
git-info on
EOF
unset _git_info_executing
git config --bool prompt.showinfo false
git-info
return 0
}
add-zsh-trap INT _git-info-abort
# Gets the Git status information.
function git-info() {
# Extended globbing is needed to parse repository status.
setopt LOCAL_OPTIONS
setopt EXTENDED_GLOB
local action
local action_format
local action_formatted
local added=0
local added_format
local added_formatted
local ahead
local ahead_format
local ahead_formatted
local ahead_or_behind
local behind
local behind_format
local behind_formatted
local branch
local branch_info
local branch_format
local branch_formatted
local branch_is_set
local clean
local clean_formatted
local commit
local commit_format
local deleted=0
local deleted_format
local deleted_formatted
local dirty
local dirty_formatted
local line_number=0
local modified=0
local modified_format
local modified_formatted
local remote
local remote_format
local remote_formatted
local renamed=0
local renamed_format
local renamed_formatted
local commit_formatted
local stashed=0
local stashed_format
local stashed_formatted
local unmerged=0
local unmerged_format
local unmerged_formatted
local untracked=0
local untracked_format
local untracked_formatted
local prompt
local rprompt
local git_info_var
local -A git_info_vars
local status_cmd
local ignore_submodule
local ignore_submodule_when
# Clean up previous Git info.
unset git_prompt_info
unset git_rprompt_info
# Return if not inside a Git repository work tree.
if ! is-true "$(git rev-parse --is-inside-work-tree 2>/dev/null)"; then
return 1
fi
if (( $# > 0 )); then
if [[ "$1" == [Oo][Nn] ]]; then
git config --bool prompt.showinfo true
elif [[ "$1" == [Oo][Ff][Ff] ]]; then
git config --bool prompt.showinfo false
else
print "usage: $0 [ on | off ]" >&2
fi
return 0
fi
# Return if git-info is disabled.
if ! is-true "${$(git config --bool prompt.showinfo):-true}"; then
return 1
fi
# Used to abort and turn git-info off on SIGINT.
_git_info_executing=true
# Use short status for easy parsing.
status_cmd='git status --short --branch'
# Ignore submodule status.
zstyle -b \
':omz:plugin:git:prompt:ignore' submodule 'ignore_submodule'
zstyle -s \
':omz:plugin:git:prompt:ignore:submodule' when 'ignore_submodule_when'
if is-true "$ignore_submodule"; then
status_cmd+=" --ignore-submodules=${ignore_submodule_when:-all}"
fi
# Get commit.
commit="$(git rev-parse HEAD 2>/dev/null)"
# Format commit (short).
zstyle -s ':omz:plugin:git:prompt' commit 'commit_format'
zformat -f commit_formatted "$commit_format" "c:$commit"
# Stashed
if [[ -f "$(_git-dir)/refs/stash" ]]; then
stashed="$(git stash list 2>/dev/null | wc -l)"
zstyle -s ':omz:plugin:git:prompt' stashed 'stashed_format'
zformat -f stashed_formatted "$stashed_format" "S:$stashed"
fi
# Assume that the working copy is clean.
zstyle -s ':omz:plugin:git:prompt' clean 'clean_formatted'
while IFS=$'\n' read line; do
(( line_number++ ))
if (( line_number == 1 )) && [[ "$line" == *'(no branch)'* ]]; then
# Get action.
action="$(_git-action)"
if [[ -n "$action" ]]; then
zstyle -s ':omz:plugin:git:prompt' action 'action_format'
zformat -f action_formatted "$action_format" "s:$action"
fi
elif (( line_number == 1 )) \
&& [[ "$line" == (#b)'## Initial commit on '(?##) ]];
then
branch="$match[1]"
elif (( line_number == 1 )); then
# Split the line into an array for parsing.
branch_info=(${(s: :)line})
# Match: master...origin/master
if [[ "$branch_info[2]" == (#b)(?##)...(?##/?##) ]]; then
branch="$match[1]"
remote="$match[2]"
# Match: [ahead or [behind
if [[ "$branch_info[3]" == (#b)\[(ahead|behind) ]]; then
ahead_or_behind="$match[1]"
if [[ "$ahead_or_behind" == 'behind' ]]; then
# Extract digits: 10]
behind="${branch_info[4]%\]}"
else
# Extract digits: 10] or 10,
ahead="${branch_info[4]%[,\]]}"
# Extract digits: 10]
behind="${branch_info[6]%\]}"
fi
fi
# Match: master
elif [[ "$branch_info[2]" == (#b)(?##) ]]; then
branch="$match[1]"
fi
else
# Format dirty.
if [[ -z "$dirty" ]]; then
zstyle -s ':omz:plugin:git:prompt' dirty 'dirty_formatted'
if [[ -z "$dirty_formatted" ]]; then
unset clean_formatted
fi
fi
# Count: added/deleted/modified/renamed/unmerged/untracked
[[ "$line" == (((A|M|D|T) )|(AD|AM|AT|MM))\ * ]] && (( added++ ))
[[ "$line" == ( D|AD)\ * ]] && (( deleted++ ))
[[ "$line" == (( (M|T))|(AM|AT|MM))\ * ]] && (( modified++ ))
[[ "$line" == R\ \ * ]] && (( renamed++ ))
[[ "$line" == UU\ * ]] && (( unmerged++ ))
[[ "$line" == \?\?\ * ]] && (( untracked++ ))
fi
done < <("${(z)status_cmd}" 2>/dev/null)
# Format branch.
zstyle -s ':omz:plugin:git:prompt' branch 'branch_format'
zformat -f branch_formatted "$branch_format" "b:$branch"
branch_is_set=$#branch
# Format remote.
if [[ "$branch" != "$commit" ]]; then
if [[ -z "$remote" ]]; then
remote="${$( \
git rev-parse \
--verify ${branch}@{upstream} \
--symbolic-full-name 2>/dev/null)#refs/remotes/}"
fi
zstyle -s ':omz:plugin:git:prompt' remote 'remote_format'
zformat -f remote_formatted "$remote_format" "R:$remote"
fi
# Format ahead.
if [[ -n "$ahead" ]]; then
zstyle -s ':omz:plugin:git:prompt' ahead 'ahead_format'
zformat -f ahead_formatted "$ahead_format" "A:$ahead"
fi
# Format behind.
if [[ -n "$behind" ]]; then
zstyle -s ':omz:plugin:git:prompt' behind 'behind_format'
zformat -f behind_formatted "$behind_format" "B:$behind"
fi
# Format added.
if (( $added > 0 )); then
zstyle -s ':omz:plugin:git:prompt' added 'added_format'
zformat -f added_formatted "$added_format" "a:$added_format"
fi
# Format deleted.
if (( $deleted > 0 )); then
zstyle -s ':omz:plugin:git:prompt' deleted 'deleted_format'
zformat -f deleted_formatted "$deleted_format" "d:$deleted_format"
fi
# Format modified.
if (( $modified > 0 )); then
zstyle -s ':omz:plugin:git:prompt' modified 'modified_format'
zformat -f modified_formatted "$modified_format" "m:$modified"
fi
# Format renamed.
if (( $renamed > 0 )); then
zstyle -s ':omz:plugin:git:prompt' renamed 'renamed_format'
zformat -f renamed_formatted "$renamed_format" "r:$renamed"
fi
# Format unmerged.
if (( $unmerged > 0 )); then
zstyle -s ':omz:plugin:git:prompt' unmerged 'unmerged_format'
zformat -f unmerged_formatted "$unmerged_format" "U:$unmerged"
fi
# Format untracked.
if (( $untracked > 0 )); then
zstyle -s ':omz:plugin:git:prompt' untracked 'untracked_format'
zformat -f untracked_formatted "$untracked_format" "u:$untracked"
fi
# Format prompts.
zstyle -s ':omz:plugin:git:prompt' prompt 'prompt_format'
zstyle -s ':omz:plugin:git:prompt' rprompt 'rprompt_format'
git_info_vars=(
git_prompt_info "$prompt_format"
git_rprompt_info "$rprompt_format"
)
for git_info_var in ${(k)git_info_vars}; do
zformat -f "$git_info_var" "$git_info_vars[$git_info_var]" \
"s:$action_formatted" \
"a:$added_formatted" \
"A:$ahead_formatted" \
"B:$behind_formatted" \
"b:$branch_formatted" \
"C:$clean_formatted" \
"c:$commit_formatted" \
"d:$deleted_formatted" \
"D:$dirty_formatted" \
"h:$branch_is_set" \
"m:$modified_formatted" \
"R:$remote_formatted" \
"r:$renamed_formatted" \
"S:$stashed_formatted" \
"U:$unmerged_formatted" \
"u:$untracked_formatted"
done
unset _git_info_executing
return 0
}
git-info "$@"