#!/usr/bin/env python3 import subprocess import datetime import os import sys from collections import defaultdict def get_current_branch(): try: result = subprocess.run( ["git", "branch", "--show-current"], capture_output=True, text=True, check=True ) return result.stdout.strip() except subprocess.CalledProcessError: return None # 非Git仓库或命令失败 def get_git_root(path="."): """获取当前目录所在的Git仓库根目录""" try: result = subprocess.run( ["git", "rev-parse", "--show-toplevel"], cwd=path, capture_output=True, text=True, check=True ) return result.stdout.strip() except subprocess.CalledProcessError: print("错误:当前目录不是Git仓库的一部分", file=sys.stderr) sys.exit(1) def get_today_commits(repo_path): """获取今日所有提交的哈希值""" today = datetime.date.today().strftime("%Y-%m-%d") try: result = subprocess.run( ["git", "log", f"--since={today} 00:00", "--until={today} 23:59", "--format=%H"], cwd=repo_path, capture_output=True, text=True, check=True ) return result.stdout.strip().split() except subprocess.CalledProcessError as e: print(f"获取提交历史失败: {e.stderr}", file=sys.stderr) return [] def count_changes_in_commit(repo_path, commit_hash): """统计单个提交的代码变动行数""" try: result = subprocess.run( ["git", "diff", "--numstat", f"{commit_hash}^!", commit_hash], cwd=repo_path, capture_output=True, text=True, check=True ) insertions = 0 deletions = 0 totals=0 for line in result.stdout.strip().split('\n'): parts = line.split('\t') if len(parts) == 3: ins, dels, total = parts insertions += int(ins) if ins != '-' else 0 deletions += int(dels) if dels != '-' else 0 totals += 1 return insertions, deletions,totals except subprocess.CalledProcessError: return 0, 0 def get_file_extension(filename): """获取文件扩展名""" ext = os.path.splitext(filename)[1].lower() return ext if ext else "unknown" def count_changes_by_filetype(repo_path, commit_hash): """按文件类型统计代码变动行数""" try: result = subprocess.run( ["git", "diff", "--numstat", f"{commit_hash}^!", commit_hash], cwd=repo_path, capture_output=True, text=True, check=True ) filetype_stats = defaultdict(lambda: {"insertions": 0, "deletions": 0}) for line in result.stdout.strip().split('\n'): parts = line.split('\t') if len(parts) == 3: ins, dels, filename = parts ext = get_file_extension(filename) filetype_stats[ext]["insertions"] += int(ins) if ins != '-' else 0 filetype_stats[ext]["deletions"] += int(dels) if dels != '-' else 0 return filetype_stats except subprocess.CalledProcessError: return defaultdict(lambda: {"insertions": 0, "deletions": 0}) def main(): # 获取Git仓库根目录 repo_path = get_git_root() repo_name = os.path.basename(repo_path) branch_name = get_current_branch() # 获取今日提交 commits = get_today_commits(repo_path) if not commits: print(f"今日({datetime.date.today()})在仓库 '{repo_name}' 中没有提交") return # 统计总变动 total_insertions = 0 total_deletions = 0 total_files = 0 filetype_stats = defaultdict(lambda: {"insertions": 0, "deletions": 0}) for commit in commits: # 统计总变动 ins, dels, totals= count_changes_in_commit(repo_path, commit) total_insertions += ins total_deletions += dels total_files += totals # 按文件类型统计 commit_filetype_stats = count_changes_by_filetype(repo_path, commit) for ext, stats in commit_filetype_stats.items(): filetype_stats[ext]["insertions"] += stats["insertions"] filetype_stats[ext]["deletions"] += stats["deletions"] # 打印结果 print(f"Git仓库代码变动统计-{repo_name}-{branch_name}") print(f"日期: {datetime.date.today()}") print(f"提交数: {len(commits)}") print(f"总行数: {total_insertions}") print(f"总文件数: {total_files}") # print(f"总删除行数: {total_deletions}") # print(f"净变动行数: {total_insertions - total_deletions}") print("\n按文件类型统计:") print("{:<10} {:>12} {:>12} {:>12}".format("类型", "插入", "删除", "净变动")) print("-" * 48) # 按插入行数排序并打印 for ext, stats in sorted(filetype_stats.items(), key=lambda x: x[1]["insertions"], reverse=True): net = stats["insertions"] - stats["deletions"] print("{:<10} {:>12} {:>12} {:>12}".format(ext or "无扩展名", stats["insertions"], stats["deletions"], net)) if __name__ == "__main__": main()