|
@@ -0,0 +1,137 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+import subprocess
|
|
|
+import datetime
|
|
|
+import os
|
|
|
+import sys
|
|
|
+from collections import defaultdict
|
|
|
+
|
|
|
+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
|
|
|
+ for line in result.stdout.strip().split('\n'):
|
|
|
+ parts = line.split('\t')
|
|
|
+ if len(parts) == 3:
|
|
|
+ ins, dels, _ = parts
|
|
|
+ insertions += int(ins) if ins != '-' else 0
|
|
|
+ deletions += int(dels) if dels != '-' else 0
|
|
|
+
|
|
|
+ return insertions, deletions
|
|
|
+ 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)
|
|
|
+
|
|
|
+ # 获取今日提交
|
|
|
+ commits = get_today_commits(repo_path)
|
|
|
+ if not commits:
|
|
|
+ print(f"今日({datetime.date.today()})在仓库 '{repo_name}' 中没有提交")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 统计总变动
|
|
|
+ total_insertions = 0
|
|
|
+ total_deletions = 0
|
|
|
+ filetype_stats = defaultdict(lambda: {"insertions": 0, "deletions": 0})
|
|
|
+
|
|
|
+ for commit in commits:
|
|
|
+ # 统计总变动
|
|
|
+ ins, dels = count_changes_in_commit(repo_path, commit)
|
|
|
+ total_insertions += ins
|
|
|
+ total_deletions += dels
|
|
|
+
|
|
|
+ # 按文件类型统计
|
|
|
+ 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}")
|
|
|
+ print(f"日期: {datetime.date.today()}")
|
|
|
+ print(f"提交数: {len(commits)}")
|
|
|
+ print(f"总插入行数: {total_insertions}")
|
|
|
+ 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()
|