2015-03-09 21:57:30 +00:00
|
|
|
#!/usr/bin/python
|
2015-03-10 16:17:33 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2015-03-09 23:00:56 +00:00
|
|
|
#
|
|
|
|
# committer_parser.py
|
|
|
|
#
|
|
|
|
# Simple script to parse the output of 'git log' and generate some statistics.
|
|
|
|
# May leverage GitHub API in the future
|
|
|
|
#
|
2015-03-10 16:17:33 +00:00
|
|
|
'''
|
|
|
|
To use this commit parser script pipe git log into the stdin:
|
|
|
|
|
|
|
|
git log | committer_parser.py -c -
|
|
|
|
'''
|
2017-04-05 22:18:00 +00:00
|
|
|
# pylint: disable=resource-leakage
|
2015-03-10 16:17:33 +00:00
|
|
|
|
|
|
|
# Import python libs
|
2015-03-09 23:16:47 +00:00
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import print_function
|
2015-03-09 21:57:30 +00:00
|
|
|
import sys
|
|
|
|
import getopt
|
|
|
|
import re
|
|
|
|
import email.utils
|
|
|
|
import datetime
|
|
|
|
|
2015-03-10 16:17:33 +00:00
|
|
|
|
2015-03-09 21:57:30 +00:00
|
|
|
class Usage(Exception):
|
2015-03-10 16:21:16 +00:00
|
|
|
def __init__(self, msg): # pylint: disable=W0231
|
2015-03-10 16:17:33 +00:00
|
|
|
self.msg = 'committer_parser.py [-c | --contributor-detail] - |' \
|
2015-03-09 23:16:47 +00:00
|
|
|
' <logfilename>\n'
|
|
|
|
self.msg += ' : Parse commit log from git and print number of ' \
|
|
|
|
'commits and unique committers\n'
|
|
|
|
self.msg += ' : by month. Accepts a filename or reads from stdin.\n'
|
|
|
|
self.msg += ' : -c | --contributor-detail generates output by ' \
|
|
|
|
'contributor, by month, in a tab-separated table\n'
|
|
|
|
if msg:
|
|
|
|
self.msg += '\n'
|
|
|
|
self.msg += msg
|
|
|
|
|
2015-03-09 21:57:30 +00:00
|
|
|
|
|
|
|
def parse_date(datestr):
|
|
|
|
d = email.utils.parsedate(datestr)
|
2015-03-10 16:17:33 +00:00
|
|
|
return datetime.datetime(d[0], d[1], d[2], d[3], d[4], d[5], d[6])
|
2015-03-09 21:57:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
def parse_gitlog(filename=None):
|
2015-03-10 16:17:33 +00:00
|
|
|
'''
|
|
|
|
Parse out the gitlog cli data
|
|
|
|
'''
|
2015-03-09 21:57:30 +00:00
|
|
|
results = {}
|
|
|
|
commits = {}
|
2015-03-09 23:00:56 +00:00
|
|
|
commits_by_contributor = {}
|
2015-03-09 21:57:30 +00:00
|
|
|
|
|
|
|
if not filename or filename == '-':
|
|
|
|
fh = sys.stdin
|
|
|
|
else:
|
|
|
|
fh = open(filename, 'r+')
|
|
|
|
|
2017-11-08 18:36:23 +00:00
|
|
|
try:
|
|
|
|
commitcount = 0
|
|
|
|
for line in fh.readlines():
|
|
|
|
line = line.rstrip()
|
|
|
|
if line.startswith('commit '):
|
|
|
|
new_commit = True
|
|
|
|
commitcount += 1
|
|
|
|
continue
|
|
|
|
|
|
|
|
if line.startswith('Author:'):
|
|
|
|
author = re.match(r'Author:\s+(.*)\s+<(.*)>', line)
|
|
|
|
if author:
|
|
|
|
email = author.group(2)
|
|
|
|
continue
|
|
|
|
|
|
|
|
if line.startswith('Date:'):
|
|
|
|
|
|
|
|
isodate = re.match(r'Date:\s+(.*)', line)
|
|
|
|
d = parse_date(isodate.group(1))
|
|
|
|
continue
|
|
|
|
|
|
|
|
if len(line) < 2 and new_commit:
|
|
|
|
new_commit = False
|
|
|
|
key = '{0}-{1}'.format(d.year, str(d.month).zfill(2))
|
|
|
|
|
|
|
|
if key not in results:
|
|
|
|
results[key] = []
|
|
|
|
|
|
|
|
if key not in commits:
|
|
|
|
commits[key] = 0
|
|
|
|
|
|
|
|
if email not in commits_by_contributor:
|
|
|
|
commits_by_contributor[email] = {}
|
|
|
|
|
|
|
|
if key not in commits_by_contributor[email]:
|
|
|
|
commits_by_contributor[email][key] = 1
|
|
|
|
else:
|
|
|
|
commits_by_contributor[email][key] += 1
|
|
|
|
|
|
|
|
if email not in results[key]:
|
|
|
|
results[key].append(email)
|
|
|
|
|
|
|
|
commits[key] += commitcount
|
|
|
|
commitcount = 0
|
|
|
|
finally:
|
|
|
|
fh.close()
|
2015-03-09 23:00:56 +00:00
|
|
|
return (results, commits, commits_by_contributor)
|
|
|
|
|
|
|
|
|
2015-03-10 16:17:33 +00:00
|
|
|
def counts_by_contributor(commits_by_contributor, results):
|
2015-03-09 23:00:56 +00:00
|
|
|
output = ''
|
2015-03-09 23:16:47 +00:00
|
|
|
dates = sorted(results.keys())
|
2015-03-09 23:00:56 +00:00
|
|
|
for d in dates:
|
|
|
|
output += '\t{0}'.format(d)
|
|
|
|
|
|
|
|
output += '\n'
|
2015-03-09 21:57:30 +00:00
|
|
|
|
2015-03-09 23:16:47 +00:00
|
|
|
for email in sorted(commits_by_contributor.keys()):
|
2015-03-09 23:00:56 +00:00
|
|
|
output += '\'{0}'.format(email)
|
|
|
|
for d in dates:
|
|
|
|
if d in commits_by_contributor[email]:
|
|
|
|
output += '\t{0}'.format(commits_by_contributor[email][d])
|
|
|
|
else:
|
|
|
|
output += '\t'
|
|
|
|
output += '\n'
|
|
|
|
return output
|
2015-03-09 21:57:30 +00:00
|
|
|
|
|
|
|
|
2015-03-10 16:17:33 +00:00
|
|
|
def count_results(results, commits):
|
2015-03-09 21:57:30 +00:00
|
|
|
result_str = ''
|
|
|
|
print('Date\tContributors\tCommits')
|
2015-03-09 23:16:47 +00:00
|
|
|
for k in sorted(results.keys()):
|
2015-03-09 21:57:30 +00:00
|
|
|
result_str += '{0}\t{1}\t{2}'.format(k, len(results[k]), commits[k])
|
|
|
|
result_str += '\n'
|
|
|
|
return result_str
|
|
|
|
|
|
|
|
|
|
|
|
def main(argv=None):
|
|
|
|
if argv is None:
|
|
|
|
argv = sys.argv
|
|
|
|
try:
|
|
|
|
try:
|
2015-03-10 16:17:33 +00:00
|
|
|
opts, args = getopt.getopt(argv[1:], 'hc', ['help', 'contributor-detail'])
|
2015-03-09 23:16:47 +00:00
|
|
|
if len(args) < 1:
|
|
|
|
raise Usage('committer_parser.py needs a filename or \'-\' to read from stdin')
|
|
|
|
except getopt.error as msg:
|
2015-03-10 21:37:49 +00:00
|
|
|
raise Usage(msg)
|
2015-03-09 23:16:47 +00:00
|
|
|
except Usage as err:
|
|
|
|
print(err.msg, file=sys.stderr)
|
2015-03-09 21:57:30 +00:00
|
|
|
return 2
|
|
|
|
|
|
|
|
if len(opts) > 0:
|
|
|
|
if '-h' in opts[0] or '--help' in opts[0]:
|
|
|
|
return 0
|
|
|
|
|
2015-03-09 23:00:56 +00:00
|
|
|
data, counts, commits_by_contributor = parse_gitlog(filename=args[0])
|
2015-03-09 21:57:30 +00:00
|
|
|
|
2015-03-09 23:00:56 +00:00
|
|
|
if len(opts) > 0:
|
|
|
|
if '-c' or '--contributor-detail':
|
2015-03-09 23:16:47 +00:00
|
|
|
print(counts_by_contributor(commits_by_contributor, data))
|
2015-03-09 23:00:56 +00:00
|
|
|
else:
|
2015-03-09 23:16:47 +00:00
|
|
|
print(count_results(data, counts))
|
2015-03-09 21:57:30 +00:00
|
|
|
|
2018-12-21 00:19:01 +00:00
|
|
|
|
2015-03-09 21:57:30 +00:00
|
|
|
if __name__ == "__main__":
|
2015-03-10 16:17:33 +00:00
|
|
|
sys.exit(main())
|