This commit is contained in:
Seth Milliken 2017-05-29 10:50:23 +00:00 committed by GitHub
commit 3df2698364

623
plugins/vcs/vcs.plugin.zsh Normal file
View file

@ -0,0 +1,623 @@
# =====[ NAME ]=================================================================
#
# VCS Plugin
# =====[ DESCRIPTION ]==========================================================
#
# Uniform prompt status for work trees originating from multiple vcs systems.
#
# =====[ FUNCTIONS ]============================================================
#
# DISPLAY
# $(vcs_status_prompt) : top-level status indicator
#
# $(vcs_dirt_status) : indicator of and count for each type of dirty file
# : simple form can indicate just clean or dirty
# $(vcs_dirt_age) : time since last significant action on the work tree
# : helpful for determining staleness of a tree
# $(vcs_rev_short) : abbreviated form of current revision
# $(vcs_rev_long) : more verbose current revision information
#
# ------------------------------------------
# FEATURE SUPPORT BY VCS
# ------------------------------------------
#
# feature | git | hg | svn | bzr |
# ----------------------------------------------
# vcs_status_prompt | O | O | O | X |
# vcs_dirt_status | O | O | O | X |
# vcs_dirt_age | O | O | O | X |
# vcs_rev_short | O | O | O | X |
# vcs_rev_long | O | O | O | X |
#
# O = implemented
# X = unimplemented
# DETECTION
# $(vcs_is_git) : true if cwd is in a git work tree
# : (but returns false if in an ignored subdirectory)
# $(vcs_is_hg) : true if cwd is in a mercurial work tree
# $(vcs_is_svn) : true if cwd is in a subversion work tree
#
# CUSTOMIZATION
# see default implementations below for examples
# implement your own function with these names to override default behavior
#
# _vcs_prompt_git
# _vcs_prompt_hg
# _vcs_prompt_svn
#
# _vcs_rev_short_git
# _vcs_rev_short_hg
# _vcs_rev_short_svn
#
# _vcs_rev_long_git
# _vcs_rev_long_hg
# _vcs_rev_long_svn
# =====[ CONFIGURABLE SETTINGS (and default values) ]===========================
# Associative array for plugin settings
typeset -gA VCS_PLUGIN
# $VCS_PLUGIN[separator] : [string] separates elements in the vcs prompt
VCS_PLUGIN[separator]=" | "
# $VCS_PLUGIN[prompt_prefix] : [string] precedes contents of the vcs prompt
VCS_PLUGIN[prompt_prefix]="("
# $$VCS_PLUGIN[prompt_suffix] : [string] proceeds contents of the vcs prompt
VCS_PLUGIN[prompt_suffix]=")"
# $VCS_PLUGIN[dirt_status_verbosity] : [string] show symbols each type of dirty file
# : or a single dirty or clean symbol
VCS_PLUGIN[dirt_status_verbosity]="full"
#VCS_PLUGIN[dirt_status_verbosity]="simple"
# $VCS_PLUGIN[is_clean_symbol] : [string] show when the work tree has no dirty files
VCS_PLUGIN[is_clean_symbol]="%{$fg[green]%}"
VCS_PLUGIN[is_clean_symbol]+="✔"
VCS_PLUGIN[is_clean_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[is_dirty_symbol] : [string] show when the work tree has dirty files
VCS_PLUGIN[is_dirty_symbol]="%{$fg[red]%}"
VCS_PLUGIN[is_dirty_symbol]+="✖"
VCS_PLUGIN[is_dirty_symbol]+="%{$reset_color%}"
#VCS_PLUGIN[untracked_is_dirty] : [flag] "true" to consider untracked files as dirty
VCS_PLUGIN[untracked_is_dirty]=
#VCS_PLUGIN[untracked_is_dirty]=true
# $VCS_PLUGIN[untracked_symbol] : [string] show when there is at least one untracked file
VCS_PLUGIN[untracked_symbol]="%{$fg_bold[white]%}"
VCS_PLUGIN[untracked_symbol]+="✭"
VCS_PLUGIN[untracked_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[added_symbol] : [string] show when there is at least one added file
VCS_PLUGIN[added_symbol]="%{$fg[green]%}"
VCS_PLUGIN[added_symbol]+="✚"
VCS_PLUGIN[added_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[modified_symbol] : [string] show when there is at least one modified file
VCS_PLUGIN[modified_symbol]="%{$fg_bold[green]%}"
VCS_PLUGIN[modified_symbol]+="✹"
VCS_PLUGIN[modified_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[renamed_symbol] : [string] show when there is at least one renamed file
VCS_PLUGIN[renamed_symbol]="%{$fg[yellow]%}"
VCS_PLUGIN[renamed_symbol]+="➜"
VCS_PLUGIN[renamed_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[deleted_symbol] : [string] show when there is at least one deleted file
VCS_PLUGIN[deleted_symbol]="%{$fg[red]%}"
VCS_PLUGIN[deleted_symbol]+="✖"
VCS_PLUGIN[deleted_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[unmerged_symbol] : [string] show when there is at least one unmerged or conflicted file
VCS_PLUGIN[unmerged_symbol]="%{$fg[blue]%}"
VCS_PLUGIN[unmerged_symbol]+="═"
VCS_PLUGIN[unmerged_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[copied_symbol] : [string] show when there is at least one copied file
VCS_PLUGIN[copied_symbol]="%{fg_bold[blue]%}"
VCS_PLUGIN[copied_symbol]+="ⓒ"
VCS_PLUGIN[copied_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[include_dirty_counts] : [flag] "true" to include counts preceding status symbols
VCS_PLUGIN[include_dirty_counts]=true
#VCS_PLUGIN[include_dirty_counts]=
# $VCS_PLUGIN[git_vcs_symbol] : [string] show when the cwd is in a git work tree
VCS_PLUGIN[git_vcs_symbol]="%F{154}"
VCS_PLUGIN[git_vcs_symbol]+="±"
VCS_PLUGIN[git_vcs_symbol]+="%f"
# $VCS_PLUGIN[hg_vcs_symbol] : [string] show when the cwd is in an hg work tree
VCS_PLUGIN[hg_vcs_symbol]="%{$fg_bold[red]%}"
VCS_PLUGIN[hg_vcs_symbol]+="☿"
VCS_PLUGIN[hg_vcs_symbol]+="%{$reset_color%} "
#
# $VCS_PLUGIN[svn_vcs_symbol] : [string] show when the cwd is in an svn work tree
VCS_PLUGIN[svn_vcs_symbol]="Ⓢ "
#VCS_PLUGIN[svn_vcs_symbol]="⚡ "
# $VCS_PLUGIN[no_vcs_symbol] : [string] show when cwd is not associated with a vcs
VCS_PLUGIN[no_vcs_symbol]="◯ "
# $VCS_PLUGIN[branch_prefix]
VCS_PLUGIN[branch_prefix]="%{$fg[white]%}"
# $VCS_PLUGIN[branch_suffix]
VCS_PLUGIN[branch_suffix]="%{$reset_color%}"
# $VCS_PLUGIN[rev_prefix]
VCS_PLUGIN[rev_prefix]=
# $VCS_PLUGIN[rev_suffix]
VCS_PLUGIN[rev_suffix]=
# $VCS_PLUGIN[ahead_by_prefix]
VCS_PLUGIN[ahead_by_prefix]=
# $VCS_PLUGIN[ahead_by_symbol] : [string] shown when there are local commits not yet pushed upstream
VCS_PLUGIN[ahead_by_symbol]="%{$fg_bold[blue]%}"
VCS_PLUGIN[ahead_by_symbol]+="⬆"
VCS_PLUGIN[ahead_by_symbol]+="%{$reset_color%}"
# $VCS_PLUGIN[ahead_by_suffix]
VCS_PLUGIN[ahead_by_suffix]=
# $VCS_PLUGIN[behind_by_prefix]
VCS_PLUGIN[behind_by_prefix]=
# $VCS_PLUGIN[behind_by_symbol] : [string] shown when there are commits not yet merged in from upstream
VCS_PLUGIN[behind_by_symbol]="%{$fg_bold[red]%}"
VCS_PLUGIN[behind_by_symbol]+="⬇"
VCS_PLUGIN[behind_by_symbol]+="%{$reset_color%}"
#VCS_PLUGIN[behind_by_suffix
VCS_PLUGIN[behind_by_suffix]=
# $VCS_PLUGIN[ahead_behind_prefix] : for combined ahead and behind indicators
VCS_PLUGIN[ahead_behind_prefix]=
# $VCS_PLUGIN[ahead_behind_separator] : [string] appears between ahead and behind status indicators if both are present
VCS_PLUGIN[ahead_behind_separator]=" "
# $VCS_PLUGIN[ahead_behind_suffix]
VCS_PLUGIN[ahead_behind_suffix]=$VCS_PLUGIN[separator]
# $VCS_PLUGIN[long_age_threshold] : [number] time in minutes a work tree can be stale before its age is considered long
VCS_PLUGIN[long_age_threshold]="360"
# $VCS_PLUGIN[medium_age_threshold] : [number] time in minutes a work tree can be stale before its age is considered medium
# : anything less stale will be considered short
# : a directory is not considered stale (is neutral) if it is not dirty
VCS_PLUGIN[medium_age_threshold]="120"
# $VCS_PLUGIN[time_since_commit_neutral] : [color] used for neutral indicator
VCS_PLUGIN[time_since_commit_neutral]="%{$fg[white]%}"
# $VCS_PLUGIN[time_since_commit_short] : [color] used for short age
VCS_PLUGIN[time_since_commit_short]="%{$fg[green]%}"
# $VCS_PLUGIN[time_short_commit_medium] : [color] used for medium age
VCS_PLUGIN[time_short_commit_medium]="%{$fg[yellow]%}"
# $VCS_PLUGIN[time_since_commit_long] : [color] used for long age
VCS_PLUGIN[time_since_commit_long]="%{$fg[red]%}"
# $VCS_PLUGIN[time_verbose] : [flag] "true" to see a more verbose age indicator
VCS_PLUGIN[time_verbose]=
#VCS_PLUGIN[time_verbose]=true
# VCS_DETECTION_ORDER : [hash] order in which cwd will be inspected for vcs type
VCS_DETECTION_ORDER=(svn git hg)
# =====[ IMPLEMENTATION ]=======================================================
# =====[ detection ]============================================================
function vcs_is_git() {
# this invocation handles ignored subdirectories of git work trees
[[ -n $(git ls-files --exclude-standard 2> /dev/null) ]]
}
function vcs_is_hg() {
hg root >/dev/null 2>/dev/null && true
}
function vcs_is_svn() {
[[ -d .svn ]] && true
}
# =====[ display ]==============================================================
function vcs_status_prompt() {
for vcs in $VCS_DETECTION_ORDER; do
vcs_is_${vcs} && _vcs_prompt ${vcs} && return
done
echo "$VCS_PLUGIN[no_vcs_symbol]"
}
function _vcs_prompt() {
local vcs=$1
local result=''
result+="$VCS_PLUGIN[prompt_prefix]"
result+=$(_vcs_prompt_${vcs})
result+="$VCS_PLUGIN[prompt_suffix]"
echo $result
}
# Default status prompts; override by defining your own functions of the same
# name in your theme but still use $(vcs_status_prompt) in your PROMPT.
function _vcs_prompt_git() {
local result=''
result+="$(vcs_dirt_age)"
result+="$VCS_PLUGIN[separator]"
result+="$(vcs_ahead_behind)"
result+="$(vcs_dirt_status) "
result+="$VCS_PLUGIN[git_vcs_symbol]"
result+="$(vcs_branch)"
echo $result
}
function _vcs_prompt_svn() {
local result=''
result+="$(vcs_dirt_age)"
result+="$VCS_PLUGIN[separator]"
result+="$(vcs_dirt_status) "
result+="$VCS_PLUGIN[svn_vcs_symbol]"
result+="${$(vcs_branch)#trunk}" # show nothing if "trunk"
result+="$(vcs_rev_long)"
echo $result
}
function _vcs_prompt_hg() {
local result=''
result+="$(vcs_dirt_age)"
result+="$VCS_PLUGIN[separator]"
result+="$(vcs_dirt_status) "
result+="$VCS_PLUGIN[hg_vcs_symbol]"
result+="$(vcs_branch)"
result+="$(vcs_rev_long)"
echo $result
}
# =====[ branch information ]===================================================
function vcs_branch() {
local length=$1
local value=''
for vcs in $VCS_DETECTION_ORDER; do
vcs_is_${vcs} && value+=$(_vcs_branch_${vcs}) && break
done
[[ -z $value ]] && return
local result=''
result+="$VCS_PLUGIN[branch_prefix]"
result+=$value
result+="$VCS_PLUGIN[branch_suffix]"
echo $result
}
function _vcs_branch_git() {
echo ${$(git symbolic-ref HEAD 2> /dev/null)#refs/heads/} || return
}
function _vcs_branch_hg() {
echo $(hg branch) || return
}
function _vcs_branch_svn() {
# Strip out '/trunk/'
echo $(svn info | awk '/Root/{root=$3 "\/(trunk/)?"};/URL/{url=$2};END{gsub(root,"",url);print url}')
}
# =====[ current revision ]=====================================================
function vcs_rev_short() { _vcs_rev "short"; }
function vcs_rev_long() { _vcs_rev "long"; }
function _vcs_rev() {
local length=$1
local value=''
for vcs in $VCS_DETECTION_ORDER; do
vcs_is_${vcs} && value+=$(_vcs_rev_${length}_${vcs}) && break
done
[[ -z $value ]] && return
local result=''
result+="$VCS_PLUGIN[rev_prefix]"
result+=$value
result+="$VCS_PLUGIN[rev_suffix]"
echo $result
}
function _vcs_rev_short_git() { echo $(git rev-parse --short HEAD 2> /dev/null); }
function _vcs_rev_short_hg() { echo $(hg log -l 1 | awk '/changeset/{split($2,revs,":")};END{print revs[1]}'); }
function _vcs_rev_short_svn() { echo $(svn info | awk '/Revision/{print $2}'); }
function _vcs_rev_long_git() { echo $(git rev-parse HEAD 2> /dev/null); }
function _vcs_rev_long_hg() { echo $(hg log -l 1 | awk '/changeset/{print $2}'); }
function _vcs_rev_long_svn() { echo "r"$(_vcs_rev_short_svn); }
function vcs_ahead_behind() {
local ahead_by="$(vcs_ahead_by)"
local behind_by="$(vcs_behind_by)"
[[ -z "${ahead_by}${behind_by}" ]] && return
local result=''
result+="$VCS_PLUGIN[ahead_behind_prefix]"
result+=$ahead_by
[[ -n $ahead_by && -n $behind_by ]] && result+="$VCS_PLUGIN[ahead_behind_separator]"
result+=$behind_by
result+="$VCS_PLUGIN[ahead_behind_suffix]"
echo $result
}
function vcs_ahead_by() {
local value=''
for vcs in $VCS_DETECTION_ORDER; do
vcs_is_${vcs} && value+="$(_vcs_ahead_by_${vcs})" && break
done
[[ -z $value ]] && return
local result=''
result+="$VCS_PLUGIN[ahead_by_prefix]"
result+=$value
result+="$VCS_PLUGIN[ahead_by_symbol]"
result+="$VCS_PLUGIN[ahead_by_suffix]"
echo $result
}
function _vcs_ahead_by_git() { git status --porcelain --branch --short | awk -F'[' '/ahead/{sub(/ahead /,"",$2);sub(/[],].*/,"",$2); print $2}'; }
function _vcs_ahead_by_svn() { return } # FIXME: unimplemented
function _vcs_ahead_by_hg() { return } # FIXME: unimplemented
function vcs_behind_by() {
local value=''
for vcs in $VCS_DETECTION_ORDER; do
vcs_is_${vcs} && value+="$(_vcs_behind_by_${vcs})" && break
done
[[ -z $value ]] && return
local result=''
result+="$VCS_PLUGIN[behind_by_prefix]"
result+=$value
result+="$VCS_PLUGIN[behind_by_symbol]"
result+="$VCS_PLUGIN[behind_by_suffix]"
echo $result
}
function _vcs_behind_by_git() { git status --porcelain --branch --short | awk -F'[' '/behind/{sub(/.*behind /,"",$2);sub(/]/,"",$2)};END{print $2}'; }
function _vcs_behind_by_svn() { return } # FIXME: unimplemented
function _vcs_behind_by_hg() { return } # FIXME: unimplemented
# =====[ dirt status ]==========================================================
# "simple": Show a clean or dirty status indicator.
# "full": Show the count of files for each type of non-clean status type next to an
# indicator of that type. e.g `6✭ 1✚ 4✹` for 6 untracked, 1 new, and 4 modified
# files.
function vcs_dirt_status() {
for vcs in $VCS_DETECTION_ORDER; do
vcs_is_${vcs} && _vcs_dirt_status_${VCS_PLUGIN[dirt_status_verbosity]} ${vcs} && return
done
}
function _vcs_dirt_status_simple() {
local vcs=$1
if _vcs_is_clean_${vcs}; then
echo "$VCS_PLUGIN[is_clean_symbol]"
else
echo "$VCS_PLUGIN[is_dirty_symbol]"
fi
}
function _vcs_is_clean_git() {
local untracked=''
[[ -z $VCS_PLUGIN[untracked_is_dirty] ]] && untracked+="--untracked=no"
[[ -z $(git status --short --porcelain $untracked 2> /dev/null) ]] && true
}
function _vcs_is_clean_svn() {
local untracked=''
[[ -z $VCS_PLUGIN[untracked_is_dirty] ]] && untracked+="-q"
[[ -z $(svn status $untracked 2> /dev/null) ]] && true
}
function _vcs_is_clean_hg() {
local untracked=''
[[ -z $VCS_PLUGIN[untracked_is_dirty] ]] && untracked+="-q"
[[ -z $(hg status $untracked 2> /dev/null) ]] && true
}
function _vcs_dirt_status_full() {
local vcs=$1
echo $(_vcs_dirt_status_${vcs})
}
function _vcs_dirt_status_git() {
git status --porcelain 2> /dev/null | awk "
/^\?\? / { untracked++ ; total++ };
/^[AM] / { added++ ; total++ };
/^[A ][MT] / { modified++ ; total++ };
/^R / { renamed++ ; total++ };
/^[A ]D / { deleted++ ; total++ };
/^UU / { unmerged++ ; total++ };
/^C / { copied++ ; total++ };
END {
if (total+0 == 0) { printf(\"%s\", \"$VCS_PLUGIN[is_clean_symbol]\"); exit; }
count[\"untracked\"] = untracked+0 ; symbol[\"untracked\"] = \"$VCS_PLUGIN[untracked_symbol]\";
count[\"added\"] = added+0 ; symbol[\"added\"] = \"$VCS_PLUGIN[added_symbol]\" ;
count[\"modified\"] = modified+0 ; symbol[\"modified\"] = \"$VCS_PLUGIN[modified_symbol]\" ;
count[\"renamed\"] = renamed+0 ; symbol[\"renamed\"] = \"$VCS_PLUGIN[renamed_symbol]\" ;
count[\"deleted\"] = deleted+0 ; symbol[\"deleted\"] = \"$VCS_PLUGIN[deleted_symbol]\" ;
count[\"unmerged\"] = unmerged+0 ; symbol[\"unmerged\"] = \"$VCS_PLUGIN[unmerged_symbol]\" ;
count[\"copied\"] = copied+0 ; symbol[\"copied\"] = \"$VCS_PLUGIN[copied_symbol]\" ;
for (i in count) {
if (count[i]+0 != 0) {
if (\"${VCS_PLUGIN[include_dirty_counts]}\" == \"\") {
printf(\"%s\", symbol[i]);
} else {
printf(\"%s%s \", count[i], symbol[i]);
}
}
}
}
" | sed -e 's/ $//' &
}
function _vcs_dirt_status_hg() {
hg status 2> /dev/null | awk "
/^\? / { untracked++ ; total++ };
/^A / { added++ ; total++ };
/^M / { modified++ ; total++ };
/^! / { renamed++ ; total++ };
/^R / { deleted++ ; total++ };
/^_ / { unmerged++ ; total++ };
/^_ / { copied++ ; total++ };
END {
if (total+0 == 0) { printf(\"%s\", \"$VCS_PLUGIN[is_clean_symbol]\"); exit; }
count[\"untracked\"] = untracked+0 ; symbol[\"untracked\"] = \"$VCS_PLUGIN[untracked_symbol]\";
count[\"added\"] = added+0 ; symbol[\"added\"] = \"$VCS_PLUGIN[added_symbol]\" ;
count[\"modified\"] = modified+0 ; symbol[\"modified\"] = \"$VCS_PLUGIN[modified_symbol]\" ;
count[\"renamed\"] = renamed+0 ; symbol[\"renamed\"] = \"$VCS_PLUGIN[renamed_symbol]\" ;
count[\"deleted\"] = deleted+0 ; symbol[\"deleted\"] = \"$VCS_PLUGIN[deleted_symbol]\" ;
count[\"unmerged\"] = unmerged+0 ; symbol[\"unmerged\"] = \"$VCS_PLUGIN[unmerged_symbol]\" ;
count[\"copied\"] = copied+0 ; symbol[\"copied\"] = \"$VCS_PLUGIN[copied_symbol]\" ;
for (i in count) {
if (count[i]+0 != 0) {
if (\"$VCS_PLUGIN[include_dirty_counts]\" == \"\") {
printf(\"%s\", symbol[i]);
} else {
printf(\"%s%s \", count[i], symbol[i]);
}
}
}
}
" | sed -e 's/ $//' &
}
function _vcs_dirt_status_svn() {
svn status 2> /dev/null | awk "
/^\? / { untracked++ ; total++ };
/^A / { added++ ; total++ };
/^M / { modified++ ; total++ };
/^! / { renamed++ ; total++ };
/^D / { deleted++ ; total++ };
/^C / { unmerged++ ; total++ };
/^R / { copied++ ; total++ };
END {
if (total+0 == 0) { printf(\"%s\", \"$VCS_PLUGIN[is_clean_symbol]\"); exit; }
count[\"untracked\"] = untracked+0 ; symbol[\"untracked\"] = \"$VCS_PLUGIN[untracked_symbol]\";
count[\"added\"] = added+0 ; symbol[\"added\"] = \"$VCS_PLUGIN[added_symbol]\" ;
count[\"modified\"] = modified+0 ; symbol[\"modified\"] = \"$VCS_PLUGIN[modified_symbol]\" ;
count[\"renamed\"] = renamed+0 ; symbol[\"renamed\"] = \"$VCS_PLUGIN[renamed_symbol]\" ;
count[\"deleted\"] = deleted+0 ; symbol[\"deleted\"] = \"$VCS_PLUGIN[deleted_symbol]\" ;
count[\"unmerged\"] = unmerged+0 ; symbol[\"unmerged\"] = \"$VCS_PLUGIN[unmerged_symbol]\" ;
count[\"copied\"] = copied+0 ; symbol[\"copied\"] = \"$VCS_PLUGIN[copied_symbol]\" ;
for (i in count) {
if (count[i]+0 != 0) {
if (\"${VCS_PLUGIN[include_dirty_counts]}\" == \"\") {
printf(\"%s\", symbol[i]);
} else {
printf(\"%s%s \", count[i], symbol[i]);
}
}
}
}
" | sed -e 's/ $//' &
}
# =====[ dirt age ]=============================================================
# Determine the time since last commit. If branch is clean,
# use a neutral color, otherwise colors will vary according to time.
function vcs_dirt_age() {
for vcs in $VCS_DETECTION_ORDER; do
vcs_is_${vcs} && _vcs_dirt_age ${vcs} && return
done
}
function _vcs_dirt_age() {
local vcs=$1
echo $(_vcs_dirt_age_${vcs})
}
function _vcs_dirt_age_hg() {
local changed_date="$(hg log -l 1 --template '{date|hgdate}' | awk '{print $1 + $2}')"
_vcs_is_clean_hg && local is_dirty=false
echo $(_vcs_dirt_age_calculate ${changed_date} ${is_dirty:-true})
}
function _vcs_dirt_age_svn() {
local changed_date="$(svn info | awk -F'e: ' '/Changed Date:/{gsub(" \\(.*", "", $2); print $2}')"
_vcs_is_clean_svn && local is_dirty=false
echo $(_vcs_dirt_age_calculate $(date -j -f "%Y-%m-%d %T %z" "${changed_date}" "+%s") ${is_dirty:-true})
}
function _vcs_dirt_age_git() {
if git rev-parse --git-dir > /dev/null 2>&1; then
# Only proceed if there is actually a commit.
if [[ $(git log 2>&1 > /dev/null | grep -c "^fatal: bad default revision") == 0 ]]; then
# Get the last commit.
local last_commit=$(git log --pretty=format:'%at' -1 2> /dev/null)
_vcs_is_clean_git && local is_dirty=false
echo $(_vcs_dirt_age_calculate $last_commit ${is_dirty:-true})
fi
fi
}
function _vcs_dirt_age_calculate() {
local last_commit=$1
local is_dirty=$2
local result=''
local now=$(date +%s)
local seconds_since_last_commit=$((now-last_commit))
# totals
local minutes=$((seconds_since_last_commit / 60))
local hours=$((seconds_since_last_commit/3600))
local years=$((seconds_since_last_commit / (86400 * 365) ))
# sub-hours and sub-minutes
local days=$((seconds_since_last_commit / 86400))
local sub_hours=$((hours % 24))
local sub_minutes=$((minutes % 60))
if $is_dirty; then
if [ "$minutes" -gt "$VCS_PLUGIN[long_age_threshold]" ]; then
color="$VCS_PLUGIN[time_since_commit_long]"
elif [ "$minutes" -gt "$VCS_PLUGIN[long_age_threshold]" ]; then
color="$VCS_PLUGIN[time_short_commit_medium]"
else
color="$VCS_PLUGIN[time_since_commit_short]"
fi
else
color="$VCS_PLUGIN[time_since_commit_neutral]"
fi
if [ "$days" -gt 365 ]; then
result=${years}y
else
if [ "$hours" -gt 24 ]; then
if [[ -n $VCS_PLUGIN[time_verbose] ]]; then
result="${days}d${sub_hours}h${sub_minutes}m"
else
result="${days}d"
fi
elif [ "$minutes" -gt 60 ]; then
if [[ -n $VCS_PLUGIN[time_verbose] ]]; then
result="${hours}h${sub_minutes}m"
else
result="${hours}h"
fi
else
result="${minutes}m"
fi
fi
echo "${color}${result}%{$reset_color%}"
}