Skip to content

Instantly share code, notes, and snippets.

@Cosmo
Created September 3, 2025 20:53
Show Gist options
  • Save Cosmo/ce00bb601fe8003067ea4784b10f88ae to your computer and use it in GitHub Desktop.
Save Cosmo/ce00bb601fe8003067ea4784b10f88ae to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -euo pipefail
# git-logger is free software and is licensed under the GNU GPLv2+.
# (C) 2018-2019 Nuno Ferreira and contributors
# Contributors: James Brown,
# qZeta
# boweeb1011
# Mathias Weber
# macOS-compatible tweaks by Devran Cosmo Uenal and ChatGPT (2025)
# ------- setup / cleanup -------
tmp_file="$(mktemp -t gitlogger1)"
tmp_file_2="$(mktemp -t gitlogger2)"
on_exit() {
status=$?
[[ -f "$tmp_file" ]] && rm -f "$tmp_file"
[[ -f "$tmp_file_2" ]] && rm -f "$tmp_file_2"
exit $status
}
trap on_exit EXIT SIGINT SIGTERM
# Force predictable weekday abbreviations (Mon..Sun)
export LC_ALL=C
# ------- constants -------
# Fixed order avoids GNU/BSD date differences
days=' Mon Tue Wed Thu Fri Sat Sun '
hours='01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 00'
colors=(19 21 23 25 27 30 36 39 41 49 51 154 178 172 166 202 196)
max_hourly_commit=0
total_commits=0
total_daily_commits=0
# ------- collect data -------
if [ -d ".git" ]; then
# %a = weekday abbrev, %H = hour (00..23)
git log --pretty=format:"%ad" --date=local --date=format:'%a %H' > "$tmp_file"
elif [ -d ".hg" ]; then
# Mercurial on macOS: parse ISO dates via Python for portability
# Outputs "Mon 13" style lines in C-locale
hg log --template "{date|isodate}\n" | python3 - "$tmp_file" <<'PY'
import sys, datetime, locale
locale.setlocale(locale.LC_ALL, 'C')
for line in sys.stdin:
line=line.strip()
if not line: continue
# Accept e.g. 2021-03-22 14:55 +0100 or 2021-03-22T14:55:00+01:00
for fmt in ("%Y-%m-%d %H:%M %z","%Y-%m-%d %H:%M:%S %z","%Y-%m-%dT%H:%M %z","%Y-%m-%dT%H:%M:%S%z","%Y-%m-%dT%H:%M:%S%z"):
try:
dt=datetime.datetime.strptime(line, fmt)
break
except ValueError:
dt=None
if dt is None:
continue
print(dt.strftime("%a %H"))
PY
else
echo "Could not find a git or mercurial repository" >&2
exit 1
fi
# ------- functions -------
find_max() {
local day hour number_hourly_commits
for day in $days; do
grep -F "$day" "$tmp_file" > "$tmp_file_2" || true
for hour in $hours; do
number_hourly_commits=$(grep -c -F " $hour" "$tmp_file_2" || true)
if [ "$number_hourly_commits" -gt "$max_hourly_commit" ]; then
max_hourly_commit="$number_hourly_commits"
fi
done
done
}
no_color=$(tput sgr0 2>/dev/null || echo "")
display_256() {
local day hour number_hourly_commits idx color
printf "\n%s\n" " ****** Punch card for $(basename "$PWD") ****** "
printf "\n%s\n%s" " ------------------------------------time of day (hours)-----------------------------------------" " "
for hour in $hours; do printf "%s" "| $hour"; done
printf "| Total"
for day in $days; do
printf "\n%s" " $day -"
grep -F "$day" "$tmp_file" > "$tmp_file_2" || true
for hour in $hours; do
number_hourly_commits=$(grep -c -F " $hour" "$tmp_file_2" || true)
# Guard against divide-by-zero and clamp index 0..15
if [ "$max_hourly_commit" -gt 0 ]; then
idx=$(( 16*number_hourly_commits / max_hourly_commit ))
else
idx=0
fi
[ "$idx" -gt 15 ] && idx=15
color=$(tput setab "${colors[$idx]}" 2>/dev/null || echo "")
if [ "$number_hourly_commits" -eq 0 ]; then
printf "%s" " $number_hourly_commits "
elif [ "$number_hourly_commits" -lt 1000 ]; then
printf "%s%4s%s" "${color}$(tput setaf 0 2>/dev/null)" "$number_hourly_commits" "${no_color}"
else
printf "%s%4sk%s" "${color}$(tput setaf 0 2>/dev/null)" "$((number_hourly_commits/1000))" "${no_color}"
fi
(( total_commits=total_commits+number_hourly_commits ))
(( total_daily_commits=total_daily_commits+number_hourly_commits ))
done
printf "%s" "| $total_daily_commits "
total_daily_commits=0
done
printf "\n\n%s\n\n" "total_commits = $total_commits"
}
display_8() {
local day hour number_hourly_commits
for day in $days; do
printf "\n%s" " $day -"
grep -F "$day" "$tmp_file" > "$tmp_file_2" || true
for hour in $hours; do
number_hourly_commits=$(grep -c -F " $hour" "$tmp_file_2" || true)
if [ "$number_hourly_commits" -eq 0 ]; then
printf "%s" " $number_hourly_commits "
elif [ "$number_hourly_commits" -lt 10 ]; then
printf "%4s" "$(tput setab 2 2>/dev/null) $(tput setaf 0 2>/dev/null) $number_hourly_commits $(tput sgr0 2>/dev/null)"
elif [ "$number_hourly_commits" -lt 100 ]; then
printf "%4s" "$(tput setab 3 2>/dev/null) $(tput setaf 0 2>/dev/null) $number_hourly_commits $(tput sgr0 2>/dev/null)"
else
printf "%4s" "$(tput setab 1 2>/dev/null) $(tput setaf 0 2>/dev/null) $number_hourly_commits $(tput sgr0 2>/dev/null)"
fi
(( total_commits=total_commits+number_hourly_commits ))
(( total_daily_commits=total_daily_commits+number_hourly_commits ))
done
printf "%s" "| $total_daily_commits "
total_daily_commits=0
done
printf "\n\n%s\n\n" "Total number of commits is $total_commits"
}
# ------- render -------
colors_supported=$(tput colors 2>/dev/null || echo 0)
if [ "${colors_supported:-0}" -ge 256 ]; then
find_max
display_256
else
display_8
printf "\n%s\n\n" "####### 8 color display detected. For best results try 256 color display #######"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment