#!/usr/bin/env python3 import argparse import subprocess from typing import Any parser = argparse.ArgumentParser() parser.add_argument( "rev", metavar="revision", type=str, help="the revision", default="HEAD", nargs="?" ) args = parser.parse_args() authors: dict[str, Any] = dict() max_lenght_author = 0 max_lenght_email = 0 def get_files(rev): """Returns a list of files for the repository, at the given path, for the given revision.""" tree = subprocess.run( ["git", "ls-tree", "--name-only", "-r", rev], capture_output=True, check=True, encoding="utf-8", ) return tree.stdout.splitlines() def line_info(filename, rev): """Generates a set of commit blocks using `git blame` for a file. Each block corresponds to the information about a single line of code.""" blame = subprocess.run( ["git", "blame", "-w", "--line-porcelain", rev, "--", filename], capture_output=True, encoding="utf-8", check=True, ) block = [] for line in blame.stdout.splitlines(): block.append(line) if line.startswith("\t"): yield block block = [] files = get_files(args.rev) for filename in files: try: for block in line_info(filename.rstrip(), args.rev): author = "" author_email = "" commit = "" skip = False for i, val in enumerate(block): if i == 0: commit = val.split()[0] continue if val.startswith("author "): author = " ".join(val.split()[1:]) continue if val.startswith("author-mail"): author_email = " ".join(val.split()[1:]) continue if val.startswith("\t") and val == "\t": skip = True if skip: continue if authors.get(author, None) is None: authors[author] = { "email": author_email, "commits": set(), "files": set(), "lines": 0, } authors[author]["commits"].add(commit) authors[author]["files"].add(filename) authors[author]["lines"] += 1 if len(author) > max_lenght_author: max_lenght_author = len(author) if len(author_email) > max_lenght_email: max_lenght_email = len(author_email) except Exception: continue for author, stats in authors.items(): email = stats["email"] lines = stats["lines"] commits = len(stats["commits"]) files = len(stats["files"]) print( f"{author:{max_lenght_author}} {email:{max_lenght_email}} {lines:6} {commits:6} {files:6}" )