tj.git-extras/bin/git-effort
Sam Thursfield 6a000ec99b effort, summary: Correctly estimate the number of active days
The output of `git log` is not exactly in chronological order. The
default sort option (--date-order) is described in the man page as
follows:

    Show no parents before all of its children are shown, but otherwise
    show commits in the commit timestamp order.

The `uniq` program will only remove duplicate lines if they follow each
other. In order for this to work correctly, we need to ensure that the
list of dates is exactly in chronological order. There doesn't seem to
be a `git log` option to achieve this, but we can use the `sort`
program. I used reverse sort (`sort -r`) since the output will be roughly
reverse-sorted already.

It's also worth noting that the default --date-order option sorts by
commit date, but git-summary uses *author* date (%ai). Passing
--author-date-order doesn't fix this issue, though, so I didn't change
that.

At the time of writing, this change reduces the number of 'active days'
for git-extras.git from 367 to 331.
2015-09-14 13:32:10 +01:00

204 lines
3.5 KiB
Bash
Executable file

#!/usr/bin/env bash
tmp=$(git_extra_mktemp)
above=0
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\""
}
#
# hide cursor
#
hide_cursor() {
printf '\033[?25l'
}
#
# show cursor, and remove temporary file
#
show_cursor_and_cleanup() {
printf '\033[?25h'
printf '\033[m\n'
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() {
test $1 -gt 10 && color='33'
test $1 -gt 25 && color='33;1'
test $1 -gt 50 && color='93'
test $1 -gt 75 && color='93;1'
test $1 -gt 100 && color='31'
test $1 -gt 125 && color='31;1'
test $1 -gt 150 && color='91'
test $1 -gt 200 && color='91;1'
}
#
# compute the effort of the given <path ...>
#
effort() {
path=$1
local commit_dates
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 $((45-$len)) ; do
f_dot=$f_dot$dot
i=$(($i+1))
done
msg=$(printf " \033[${color}m%s %-10d" "$f_dot" $commits)
# active days
active=`active_days "$commit_dates"`
color_for $(( $active - $above ))
msg="$msg $(printf "\033[${color}m %d\033[0m\n" $active)"
echo "$msg"
}
#
# print heading
#
heading() {
echo
printf " %-45s %-10s %s\n" 'path' 'commits' 'active days'
echo
}
#
# output sorted results
#
sort_effort() {
clear
echo
heading
< $tmp sort -rn -k 2
}
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
# hide cursor
hide_cursor
trap show_cursor_and_cleanup INT
# export functions so subshells can call them
export -f effort
export -f color_for
export -f active_days
export -f dates
export above
export log_args
bash_params=
# If bash exits sucessfully with --import-functions,
# then we need to pass it (FreeBSD probably)
bash --import-functions -c ":" 1>/dev/null 2>&1
if [ $? -eq 0 ] ; then
bash_params="--import-functions"
fi
heading
# send paths to effort
printf "%s\0" "${paths[@]}" | xargs -0 -n 1 -P 4 -I % bash $bash_params -c "effort \"%\"" | 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