For quickly getting an overview of what happened in the last 52 weeks
in a Git project, I use a script git-story
, so I can run git story
in a Git repo and get a nice overview of what happened in a project
(similar to Github's contribution history, but on a per-repo basis).
Example Output
Here's an example output of the vim
Git repo as of 2019-11-01:
ONov Dec Jan Feb Mar Apr May Jun Jul Aug Sep Oct M █████████████████████████████████████████████████████ T █████████████████████████████████████████████████████ W █████████████████████████████████████████████████████ T █████████████████████████████████████████████████████ F █████████████████████████████████████████████████████ ← today S ████████████████████████████████████████████████████ S ████████████████████████████████████████████████████ 1779 commits in 52 weeks Less █ █ █ █ █ █ █ More
Source
Download the script here: git-story
#!/usr/bin/env python3
# git-story: Github-like "commit history" on the console
# Copyright 2019 Thomas Perl <m@thp.io>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
import subprocess, collections, re, sys
from datetime import date, datetime, timedelta
# https://en.wikipedia.org/wiki/Xterm#/media/File:Xterm_256color_chart.svg
PALETTE, DARK = [22, 28, 34, 40, 46, 47], 232
DAYS = 52*7
DTFMT = '%Y-%m-%d %H:%M:%S %z'
MONTHS = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split()
today = date.today()
first = today - timedelta(DAYS+today.weekday())
commits_per_day = collections.defaultdict(int)
lines = subprocess.check_output(['git', 'log',
'--date=iso8601', '--all']).decode(sys.getdefaultencoding())
for line in (line for line in lines.split('\n') if line.startswith('Date:')):
commits_per_day[datetime.strptime(line[6:].strip(), DTFMT).date()] += 1
commits_per_day = {k: v for k, v in commits_per_day.items() if k >= first}
maxc = max(commits_per_day.values()) if commits_per_day else 1
for i in range(7):
this_weekday = today + timedelta(days=i-today.weekday())
for j in range(-DAYS, 1, 7):
then = this_weekday + timedelta(days=j)
if then > today:
break
if j == -DAYS:
if i == 0:
print('\n ', re.sub(r'([a-l])\1*', lambda m:
(MONTHS[ord(m.group(1))-ord('a')]+' ')[:len(m.group(0))],
''.join((chr(ord('a')+(then+timedelta(days=d)).month-1))
for d in range(-DAYS, 1, 7))))
print('', 'MTWTFSS'[then.weekday()], end=' ')
n = commits_per_day.get(then, 0)
color = PALETTE[int(n/maxc*(len(PALETTE)-1))] if n > 0 else DARK
print(end='\033[38;5;%dm\u2588\033[0m' % (color,))
print(' ← today' if then == today else '')
legend = ' '.join('\033[38;5;%dm\u2588' % (color,) for color in [DARK]+PALETTE)
summary = '{} commits in 52 weeks'.format(sum(commits_per_day.values()))
print('\n %-31s Less %s\033[0m More\n' % (summary, legend))