mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 15:36:21 -04:00
234 lines
4.3 KiB
Bash
Executable file
234 lines
4.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
tmp=$(git_extra_mktemp)
|
|
above=0
|
|
# if the output won't be printed to tty, disable the color
|
|
test -t 1 && to_tty=true
|
|
color=
|
|
|
|
#
|
|
# print usage message
|
|
#
|
|
usage() {
|
|
echo 1>&2 "usage: git effort [--above <value>] [<path>...] [-- [<log options>...]]"
|
|
}
|
|
|
|
#
|
|
# get dates for the given <commit>
|
|
#
|
|
dates() {
|
|
eval "git log $args_to_git_log --pretty='format: %ad' --date=short -- \"$1\""
|
|
}
|
|
|
|
# tput, being quiet about unknown capabilities
|
|
tputq() {
|
|
tput "$@" 2>/dev/null
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# hide cursor
|
|
#
|
|
|
|
hide_cursor() {
|
|
tputq civis
|
|
}
|
|
|
|
#
|
|
# show cursor, and remove temporary file
|
|
#
|
|
|
|
show_cursor_and_cleanup() {
|
|
tputq cnorm
|
|
tputq sgr0
|
|
rm "$tmp" > /dev/null 2>&1
|
|
exit 0
|
|
}
|
|
|
|
#
|
|
# get active days for the given <commit>
|
|
#
|
|
|
|
active_days() {
|
|
echo "$1" | sort -r | uniq | wc -l
|
|
}
|
|
|
|
#
|
|
# set 'color' based on the given <num>
|
|
#
|
|
|
|
color_for() {
|
|
if [ "$to_tty" = true ]; then
|
|
if [ $1 -gt 200 ]; then color="$(tputq setaf 1)$(tputq bold)"
|
|
elif [ $1 -gt 150 ]; then color="$(tputq setaf 1)" # red
|
|
elif [ $1 -gt 125 ]; then color="$(tputq setaf 2)$(tputq bold)"
|
|
elif [ $1 -gt 100 ]; then color="$(tputq setaf 2)" # green
|
|
elif [ $1 -gt 75 ]; then color="$(tputq setaf 5)$(tputq bold)"
|
|
elif [ $1 -gt 50 ]; then color="$(tputq setaf 5)" # purplish
|
|
elif [ $1 -gt 25 ]; then color="$(tputq setaf 3)$(tputq bold)"
|
|
elif [ $1 -gt 10 ]; then color="$(tputq setaf 3)" # yellow
|
|
else color="$(tputq sgr0)" # default color
|
|
fi
|
|
else
|
|
color=""
|
|
fi
|
|
}
|
|
|
|
#
|
|
# compute the effort of the given <path ...>
|
|
#
|
|
|
|
effort() {
|
|
path=$1
|
|
local commit_dates
|
|
local color reset_color commits len dot f_dot i msg active
|
|
reset_color=""
|
|
test "$to_tty" = true && reset_color="$(tputq sgr0)"
|
|
commit_dates=$(dates "$path")
|
|
[ $? -gt 0 ] && exit 255
|
|
|
|
# Ensure it's not just an empty line
|
|
if [ -z "$(head -c 1 <<<$(echo $commit_dates))" ]
|
|
then
|
|
exit 0
|
|
fi
|
|
|
|
commits=$(wc -l <<<"$(echo "$commit_dates")")
|
|
color='90'
|
|
|
|
# ignore <= --above
|
|
test $commits -le $above && exit 0
|
|
|
|
# commits
|
|
color_for $(( commits - above ))
|
|
len=${#path}
|
|
dot="."
|
|
f_dot="$path"
|
|
i=0 ; while test $i -lt $(( columns - len )) ; do
|
|
f_dot=$f_dot$dot
|
|
i=$((i+1))
|
|
done
|
|
|
|
msg=$(printf " ${color}%s %-10d" "$f_dot" $commits)
|
|
|
|
# active days
|
|
active=$(active_days "$commit_dates")
|
|
color_for $(( active - above ))
|
|
msg="$msg $(printf "${color} %d${reset_color}\n" $active)"
|
|
echo "$msg"
|
|
}
|
|
|
|
#
|
|
# print heading
|
|
#
|
|
|
|
heading() {
|
|
echo
|
|
printf " %-${columns}s %-10s %s\n" 'path' 'commits' 'active days'
|
|
echo
|
|
}
|
|
|
|
#
|
|
# output sorted results
|
|
#
|
|
|
|
sort_effort() {
|
|
clear
|
|
echo " "
|
|
heading
|
|
< "$tmp" sort -rn -k 2
|
|
}
|
|
|
|
#
|
|
# get processor count, default 8
|
|
#
|
|
procs() {
|
|
if ! (nproc --all || getconf NPROCESSORS_ONLN || getconf _NPROCESSORS_ONLN || grep -c processor /proc/cpuinfo) 2>/dev/null; then
|
|
echo 8
|
|
fi
|
|
}
|
|
|
|
declare -a paths=()
|
|
while [ "${#}" -ge 1 ] ; do
|
|
|
|
case "$1" in
|
|
--above)
|
|
shift
|
|
above=$1
|
|
;;
|
|
--)
|
|
shift
|
|
args_to_git_log=$(printf " %q" "${@:1}")
|
|
break
|
|
;;
|
|
--*)
|
|
usage
|
|
echo 1>&2 "error: unknown argument $1"
|
|
echo 1>&2 "error: if that argument was meant for git-log,"
|
|
echo 1>&2 "error: please put it after two dashes ( -- )."
|
|
exit 1
|
|
;;
|
|
*)
|
|
paths+=( "$1" )
|
|
;;
|
|
esac
|
|
|
|
shift
|
|
done
|
|
|
|
# Exit if above-value is not an int
|
|
if [ -z "${above##*[!0-9]*}" ] ; then
|
|
echo "error: argument to --above was not an integer" 1>&2
|
|
exit 1
|
|
fi
|
|
|
|
# remove empty quotes that appear when there are no arguments
|
|
args_to_git_log="${args_to_git_log#\ \'\'}"
|
|
export args_to_git_log
|
|
|
|
# [path ...]
|
|
|
|
if test "${#paths}" -eq 0; then
|
|
save_ifs=$IFS
|
|
IFS=$(echo -en "\n\b")
|
|
paths=($(git ls-files))
|
|
IFS=$save_ifs
|
|
unset save_ifs
|
|
fi
|
|
|
|
# set column width to match longest filename
|
|
max=0
|
|
for path in "${paths[@]}"; do
|
|
cur=${#path}
|
|
if [[ $max -lt $cur ]]; then
|
|
max=$cur
|
|
fi
|
|
done
|
|
columns=$(( max + 5 ))
|
|
export columns
|
|
|
|
# hide cursor
|
|
|
|
hide_cursor
|
|
trap show_cursor_and_cleanup INT
|
|
|
|
heading
|
|
|
|
# send paths to effort
|
|
nPaths=${#paths[@]}
|
|
nProcs=$(procs)
|
|
nJobs="\j"
|
|
for ((i=0; i<nPaths; ++i))
|
|
do
|
|
while (( ${nJobs@P} >= nProcs )); do
|
|
wait -n
|
|
done
|
|
effort "${paths[i]}" &
|
|
done|tee "$tmp"
|
|
|
|
# if more than one path, sort and print
|
|
test "$(wc -l "$tmp" | awk '{print $1}')" -gt 1 && sort_effort
|
|
echo
|
|
|
|
show_cursor_and_cleanup
|