准备工作
没有安装python的先安装再跑脚本
使用
解压后,在脚本目录cmd,然后输入以下命令启动脚本
python main.py 本地git仓库地址
或者也可以这样,先调起脚本,进去再输入本地git仓库地址
python main.py
统计完后也可以把结果输出到csv文件上
源码包:下载源码
import subprocess
import sys
from collections import defaultdict
import os
import time
def get_git_author_lines(repo_path):
# 保存原目录
original_dir = os.getcwd()
try:
# 切换到仓库目录
os.chdir(repo_path)
except FileNotFoundError:
print(f"错误:无法找到路径 {repo_path}")
return
# 查看是否是 Git 仓库
result = subprocess.run(
['git', 'rev-parse', '--is-inside-work-tree'], capture_output=True, text=True)
if result.returncode != 0:
print("错误:该路径不是 Git 仓库")
return
# 进度条特效
progress_bar(f"正在统计仓库{repo_path}", 10)
# 配置信息
cmd = [
'git', 'log', # 获取作者和 numstat 信息
'--no-merges', # 排除合并提交
'--pretty=format:%aN', # 输出作者名
'--numstat' # 输出每文件增删行数
]
# 拼接命令并执行
result = subprocess.run(cmd, capture_output=True,
text=True, encoding='utf-8', errors='ignore')
if result.returncode != 0:
print("Git 拼接命令执行失败:", result.stderr)
return
# 分割result数据
lines = result.stdout.splitlines()
# 防止被调用到没有初始化的键,及可以直接使用author_stats[aaa][added]、author_stats[bbb][added]
author_stats = defaultdict(lambda: {'added': 0, 'deleted': 0})
# 循环遍历结果
i = 0
while i < len(lines):
line = lines[i].strip()
if not line:
i += 1
continue
if line.startswith(('commit', 'Merge:', 'Author:', 'Date:')):
# 跳过提交信息头
i += 1
continue
# 作者名
author = line.strip()
i += 1
# numstat 数据(插入、删除、文件)
while i < len(lines) and lines[i].strip() and not lines[i].startswith(('commit', 'Author:', 'Date:', ' ')):
parts = lines[i].split('\t')
if len(parts) == 3:
try:
added = int(parts[0]) if parts[0] != '-' else 0
deleted = int(parts[1]) if parts[1] != '-' else 0
author_stats[author]['added'] += added
author_stats[author]['deleted'] += deleted
except ValueError:
pass # 忽略错误
i += 1
else:
i += 1
if not author_stats:
print("未找到提交记录。")
return
# 进度条特效
progress_bar("正在输出结果", 10)
# 创建抬头
print(f"{'作者':<15} {'新增行数':<10} {'删除行数':<10} {'净增行数':<10}")
# # 按新增行数降序排列
sorted_authors = sorted(author_stats.items(),
key=lambda x: x[1]['added'], reverse=True)
# 输出结果
for author, stats in sorted_authors:
added = stats['added']
deleted = stats['deleted']
net = added - deleted
print(f"{author:<18} {added:<13} {deleted:<13} {net:<13}")
print()
# 导出为 CSV
export = input("\n是否导出为 CSV 文件?(y/n): ").strip().lower()
if export == 'y':
csv_file = os.path.join(repo_path, 'git_stats.csv')
with open(csv_file, 'w', encoding='utf-8') as f:
f.write('提交人,新增行数,删除行数,净增行数\n')
for author, stats in sorted_authors:
f.write(f'{author},{stats["added"]},{stats["deleted"]},{stats["added"] - stats["deleted"]}\n')
print(f"已导出到:{csv_file}")
# 回到原来的目录
os.chdir(original_dir)
# 进度条特效
def progress_bar(name, sum):
print(), print() # 换行
for i in range(5, sum + 1):
bar = '█' * i + '.' * (sum - i)
print(f"\r{name}: {i}/{sum} [{bar}]", end="", flush=True)
time.sleep(0.1)
print() # 换行
if __name__ == '__main__':
# 如果已经配置了仓库地址,就直接拿取这个地址
if len(sys.argv) > 1:
repo_path = sys.argv[1]
else:
# 配置仓库地址,并且移除空格和引号
repo_path = input("请输入 Git 仓库的本地路径: ").strip().strip('"\'')
get_git_author_lines(repo_path)