Skip to content

Instantly share code, notes, and snippets.

@fritschy
Created September 12, 2017 15:06
Show Gist options
  • Select an option

  • Save fritschy/82befa8a499d7d00b6cca88eae5f3faf to your computer and use it in GitHub Desktop.

Select an option

Save fritschy/82befa8a499d7d00b6cca88eae5f3faf to your computer and use it in GitHub Desktop.
Create a useless AF spreadsheet from git history
#!/usr/bin/env python3
import xlsxwriter, os, re, sys
from os.path import join as pjoin, isdir
from datetime import timezone, datetime, timedelta
from pygit2 import Repository as GR, Tree, Blob, Diff, GIT_SORT_REVERSE, GIT_SORT_TIME
from functools import reduce
CONTEXT_LINES = 100
OUTFILE = 'winman-patches.xlsx'
REPLACE_CHARS = set([i for i in '<>;:,.()[]{}$%§"\'?`/´\\=*+~^°#|!& '])
MUTLIPLE_DASHES = re.compile(r'--+')
def get_patch_name(x, m):
n = ['-' if i in REPLACE_CHARS else i for i in m] # normalize
n = re.sub(MUTLIPLE_DASHES, '-', ''.join(n)) # replace consecutive - with single -
return '%04d-%s.patch' % (x, n.lower())
def get_repo_path():
for rn in ('.', '..'): # executing this from build-dirs
if isdir(pjoin(rn, '.git')):
return pjoin(rn, '.git')
print('Could not find ".git"')
sys.exit(1)
def main():
with xlsxwriter.Workbook(OUTFILE) as wb:
ws = wb.add_worksheet()
header_format, std_format = wb.add_format({'bold': True}), wb.add_format({})
r = GR(get_repo_path())
n = 0 # tracking row for append(), first row
def append(*args, **kwargs):
nonlocal n
for i, a in enumerate(args):
ws.write(n, i, a, kwargs.get('fmt', std_format))
n += 1
append('Index', 'Date', 'Author', 'Short Description', 'Size', 'Insertions', 'Deletions', 'Files Changed', 'Patch File', fmt=header_format)
for i, c in enumerate(r.walk(r.head.target, GIT_SORT_TIME | GIT_SORT_REVERSE)):
firstline = c.message.split('\n', 1)[0]
st = {'insertions': 0, 'deletions': 0, 'files_changed': 0}
if len(c.parents) > 0:
s = r.diff(c.parents[0], c).stats
st['insertions'] = s.insertions
st['deletions'] = s.deletions
st['files_changed'] = s.files_changed
else:
def s(l):
return reduce(lambda x, y: (x[0] + y[0], x[1] + y[1]), (b(r[x.id]) for x in l), (0, 0))
def b(bl):
if isinstance(bl, Blob):
d = bl.diff(old_as_path='/dev/null')
return (sum(d.line_stats), 1) # there are only insertions
elif isinstance(bl, Tree):
return s(bl)
return (0, 0)
ins, fs = s(c.tree)
st['insertions'] = ins
st['files_changed'] = fs
p = get_patch_name(i, firstline)
os.system('git show -U%d %s > %s' % (CONTEXT_LINES, c.oid, p)) # so, sue me!
ps = os.stat(p).st_size
committer = '%s <%s>' % (c.author.name, c.author.email)
tzi = timezone(timedelta(minutes=c.author.offset))
t = datetime.fromtimestamp(float(c.author.time), tzi)
append(str(i+1), t.strftime('%Y-%m-%dT%H:%M:%S%z'), committer, firstline, ps, st['insertions'], st['deletions'], st['files_changed'], p)
__name__ == '__main__' and main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment