Skip to content

Instantly share code, notes, and snippets.

@kevinastone
Created February 8, 2015 17:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinastone/273f6f48dd9cca4218f9 to your computer and use it in GitHub Desktop.
Save kevinastone/273f6f48dd9cca4218f9 to your computer and use it in GitHub Desktop.
Git Blame Results for Linter Violations
#!/usr/bin/env python
import argparse
from collections import defaultdict
import subprocess
def lint(linter, dirs):
files = defaultdict(lambda: defaultdict(list))
for dir in dirs:
try:
subprocess.check_output([linter, dir])
except subprocess.CalledProcessError as ex:
for line in ex.output.split('\n'):
if not line.strip():
continue
loc, desc = line.split(' ', 1)
filename, line_number = loc.rsplit(':', 1)
files[filename][int(line_number)].append(desc)
return files
def blame(files):
blames = defaultdict(list)
for filename, line_mapping in files.items():
commit_details = defaultdict(dict)
output = subprocess.check_output(['git', 'blame', '-e', '-w', '--incremental', filename])
lines = output.strip().split('\n')
line_blame = {}
while lines:
line = lines.pop(0)
sha, source_line_number, result_line_number, num_lines = line.split(' ')
# iterate until we have the filename line
while lines:
line = lines.pop(0)
key, value = line.split(' ', 1)
if key == 'filename':
break
commit_details[sha][key] = value
line_blame[xrange(int(result_line_number), int(result_line_number) + int(num_lines))] = commit_details[sha]
for line, descriptions in line_mapping.items():
for line_range, details in line_blame.items():
if line not in line_range:
continue
for desc in descriptions:
blames[(details['author'], details.get('author-email', None))].append(
'{filename}:{line}\t{desc}'.format(filename=filename, line=line, desc=desc)
)
return blames
if __name__ == '__main__':
parser = argparse.ArgumentParser('linterblame.py')
parser.add_argument('--linter', help='lint command to run')
parser.add_argument('dir', nargs='+', help='where to run linterblame')
args = parser.parse_args()
files = lint(args.linter, args.dir)
blames = blame(files)
for author, violations in blames.items():
name, email = author
print
print name
if email:
print email
for violation in violations:
print violation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment