I have scripts to display commit statistics and merge statistics of my repos, and they work. I wrote them for my personal usage, and because I was interested in finding trends in my git repos.
This script reports statistics about commits (number, average length in words, etc.). Relevant commits can be selected using git-rev-list
options.
Features and times
count
: report number of commits (not a performance issue)len
: report length in words of commit message and commit hash (~20s for 1561 commits)len min
,len max
, andlen avg
: report minimum, maximum, or average commit message length in words and commit hash (~10-15s for the same)
Benchmarks run with bash's
time
on my dotfiles repo
A previous implementation using for-loops had similar performance.
Obviously, the algorithms are O(n). They are still too slow for every-day usage.
#! /usr/bin/env bash
set -euo pipefail
USAGE='[-h] (count | len [min|max|avg]) [rev-list options]
Display commit statistics
Filter commits based on [rev-list options]'
SUBDIRECTORY_OK=true
# source git-sh-setup for some helpers
set +u
source "$(git --exec-path)"/git-sh-setup
set -u
SIZER=(
wc
# count words
-w
)
size() {
local commit="$1"
git log "$commit" -1 --format=%B | "${SIZER[@]}" | tr -d ' '
}
commits_list() {
command=(
git
rev-list
# start somewhere
--all
)
if (($# > 0)) ; then
command+=("$@")
fi
"${command[@]}" 2>/dev/null
}
commit_count() {
commits_list "$@" | wc -l | tr -d ' '
}
commit_len() {
commits_list "$@" |
while read c ; do
size "$c" | tr -d '[:space:]'
printf ' %s\n' "$c"
done
}
commit_len_min() {
commit_len "$@" |
sort -n |
head -n 1
}
commit_len_max() {
commit_len "$@" |
sort -rn |
head -n 1
}
commit_len_avg() {
local num=0
{
printf '%s\n' '5k'
while read c ; do
((++num))
size "$c"
((num >= 2)) && printf '%s\n' '+'
done < <(commits_list "$@")
printf '%s\n' "$num" '/p'
} | dc
}
main() {
(($# >= 1)) || usage
case "$1" in
count) commit_count "${@:2}" ;;
len)
if (($# >= 2)); then
case "$2" in
max|min|avg) commit_len_"$2" "${@:3}" ;;
*) commit_len "${@:2}" ;;
esac
else
commit_len "${@:2}"
fi
;;
*) usage ;;
esac
}
main "$@"
Shell scripts being hard to profile, I've been unable to identify the bottleneck (though commit_len
seems like a good place to start).
I run shellcheck
regularly.
PS4='\t'
or similar. That can identify commands that take over a second. \$\endgroup\$size
ing about 61 commits/s, so at ~1600 commits this takes 26s! \$\endgroup\$