Spaces:
Build error
Build error
| import os | |
| import subprocess | |
| from collections import Counter | |
| CONFIG_FILE_EXTENSIONS = (".json", ".yml", ".yaml", ".ini", ".conf", ".toml") | |
| def is_text_file(filepath): | |
| # Check for binary file by scanning for null bytes. | |
| try: | |
| with open(filepath, "rb") as f: | |
| chunk = f.read(4096) | |
| if b"\0" in chunk: | |
| return False | |
| return True | |
| except Exception: | |
| return False | |
| def should_skip_file(path): | |
| base = os.path.basename(path) | |
| # Skip dotfiles and dotdirs | |
| if base.startswith("."): | |
| return True | |
| # Skip config files by extension | |
| if base.lower().endswith(CONFIG_FILE_EXTENSIONS): | |
| return True | |
| return False | |
| def get_tracked_files(): | |
| try: | |
| output = subprocess.check_output(["git", "ls-files"], text=True) | |
| files = output.strip().split("\n") | |
| files = [f for f in files if f and os.path.isfile(f)] | |
| return files | |
| except subprocess.CalledProcessError: | |
| print("Error: Are you in a git repository?") | |
| return [] | |
| def main(): | |
| files = get_tracked_files() | |
| email_counter = Counter() | |
| total_lines = 0 | |
| for file in files: | |
| if should_skip_file(file): | |
| continue | |
| if not is_text_file(file): | |
| continue | |
| try: | |
| blame = subprocess.check_output( | |
| ["git", "blame", "-e", file], text=True, errors="replace" | |
| ) | |
| for line in blame.splitlines(): | |
| # The email always inside <> | |
| if "<" in line and ">" in line: | |
| try: | |
| email = line.split("<")[1].split(">")[0].strip() | |
| except Exception: | |
| continue | |
| email_counter[email] += 1 | |
| total_lines += 1 | |
| except subprocess.CalledProcessError: | |
| continue | |
| for email, lines in email_counter.most_common(): | |
| percent = (lines / total_lines * 100) if total_lines else 0 | |
| print(f"{email}: {lines}/{total_lines} {percent:.2f}%") | |
| if __name__ == "__main__": | |
| main() | |