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
393 lines
9.9 KiB
Text
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 "$@"
|
|
|