#!/usr/bin/env python
# 
# Prints ownership metrics based on Christian Bird's 2011 paper for a git repo.
# 
# ----
# Written by Jonathan Foote (jmfoote@loyola.edu)
# - Stats from "Don't Touch My Code!" by Bird, et. al. (ACM ESEC/FSE 2011)
# - Modeled loosely on 'git-churn' by Gary Bernhardt:
#     https://github.com/garybernhardt/dotfiles
# ----
#
# Show ownership for whole repo:
#   $ git ownership
#
# Show ownership for specific directories:
#   $ git ownership app lib
#
# Show ownership for a time range:
#   $ git ownership --since='1 month ago'
#
# (The above are all standard arguments to `git log`.)
#
# Hack: Show respective ownership for all dirs in 'src' for a repo
# $ find src -type d -depth 1 -exec git ownership {} \; -print | \
#     grep -A 1 "(all)"

import subprocess, re, sys

#
# Gather data from git-log
#
regex = re.compile("^author_line(.*)$")

try:
    lines = subprocess.check_output(["git", "log", "--all", "-M", "-C", 
        "--name-only", "--format=author_line%aN"] + sys.argv[1:]).splitlines()
except subprocess.CalledProcessError as e:
    sys.stderr.write(e.output)
    sys.exit(e.returncode)

i = 1
edits = {}
while i < len(lines):
    if not lines[i]: # delineates author from filename
        author = re.match(regex, lines[i-1]).groups()[0]
        i += 1
        while i < len(lines) and not re.match(regex, lines[i]):
            edits[lines[i]] = edits.get(lines[i], {'authors':{}, 'total_edits':0, 'majors':[], 'minors':[], 'ownership':0})
            edits[lines[i]]['authors'][author] = edits[lines[i]]['authors'].get(author, 0) + 1
            edits[lines[i]]['total_edits'] += 1
            i+=1
    else:
        i+=1

#
# Calculate per-file ownership stats
#
for filepath, info in sorted(edits.items()):
    #print filepath
    authors = sorted(info['authors'].items(), key=lambda i:i[1], reverse=True) 
    max_ownership = 0
    for author, count in authors:
        ownership = float(count) / info['total_edits'] 
        if ownership >= 0.05:
            info['majors'].append(author)
            #print "\t", author, count, "(MAJOR:%d%%)" % (ownership * 100)
        else:
            info['minors'].append(author)
            #print "\t", author, count, "(MINOR:%d%%)" % (ownership * 100)
        if ownership > max_ownership:
            max_ownership = ownership
    info['ownership'] = max_ownership * 100
    #print "\tMAJOR:%d MINOR:%d" % (len(info['majors']), len(info['minors']))

#
# Print per-file ownership stats
#
print "ownrshp\tmajor\tminor\tfile"
for filepath, info in sorted(edits.items(), key=lambda i:len(i[1]['minors'])):
    print "%d\t%d\t%d\t%s" % (info['ownership'], len(info['majors']), len(info['minors']), filepath)

#
# Print ownership stats across all items if necessary
#
if len(edits.items()) <= 1:
    sys.exit(0) 
total_edits = 0
max_ownership = 0
majors = []
minors = []
commits = {}
for filepath, info in edits.items():
    total_edits += info['total_edits']
    for author, count in info['authors'].items():
        commits[author] = commits.get(author, 0) + count
for author, count in commits.items():
    ownership = float(count) / total_edits 
    if ownership >= 0.05:
        majors.append(author)
        #print author, "(major %d%%)" % (ownership*100)
    else:
        minors.append(author)
    if ownership > max_ownership:
        max_ownership = ownership

print "-" * 8
print "ownrshp\tmajor\tminor\tfile"
print "%d\t%d\t%d\t(all)" % (max_ownership*100, len(majors), len(minors))
