AI 生成: 一个命令行工具,输入 Git 仓库路径后统计每个作者的提交次数并生成排名。
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# git-author-stats
|
||||
|
||||
一个命令行工具,用于统计 Git 仓库中每个作者的提交次数并生成排名。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- 输入 Git 仓库路径,统计所有作者的提交次数
|
||||
- 按提交次数降序排列,生成排名
|
||||
- 支持显示提交占比
|
||||
- 支持查看指定分支的统计
|
||||
- 无需安装额外依赖(仅使用 Python 标准库)
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Python 3.6+
|
||||
- 系统中已安装 Git,并且 `git` 命令可在终端中直接调用
|
||||
|
||||
## 安装
|
||||
|
||||
无需安装,直接下载 `git_author_stats.py` 文件即可使用。
|
||||
|
||||
## 使用方法
|
||||
|
||||
```bash
|
||||
git_author_stats.py <仓库路径> [选项]
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `repo_path` | Git 仓库的路径(必填) |
|
||||
| `--branch` / `-b` | 指定分支,默认为当前分支 |
|
||||
| `--top` / `-t` | 只显示前 N 名作者 |
|
||||
| `--percent` / `-p` | 显示每位作者的提交占比 |
|
||||
|
||||
### 示例
|
||||
|
||||
```bash
|
||||
# 统计当前仓库
|
||||
git_author_stats.py .
|
||||
|
||||
# 统计指定路径的仓库
|
||||
git_author_stats.py /path/to/repo
|
||||
|
||||
# 统计指定分支并显示前 5 名
|
||||
git_author_stats.py /path/to/repo --branch main --top 5
|
||||
|
||||
# 显示提交占比
|
||||
git_author_stats.py /path/to/repo --percent
|
||||
```
|
||||
|
||||
### 输出示例
|
||||
|
||||
```
|
||||
仓库: /path/to/repo
|
||||
分支: main
|
||||
总提交数: 128
|
||||
|
||||
排名 作者 提交次数 占比
|
||||
------------------------------------------------
|
||||
1 Alice 68 53.13%
|
||||
2 Bob 42 32.81%
|
||||
3 Charlie 18 14.06%
|
||||
```
|
||||
|
||||
## 实现原理
|
||||
|
||||
工具通过调用 `git log` 命令获取提交记录,并解析每条提交的作者信息,
|
||||
然后使用字典统计每位作者的提交次数,最后按次数排序输出。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
git-author-stats/
|
||||
├── README.md
|
||||
└── git_author_stats.py
|
||||
```
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
git-author-stats
|
||||
|
||||
统计 Git 仓库中每个作者的提交次数并生成排名。
|
||||
|
||||
用法:
|
||||
git_author_stats.py <仓库路径> [--branch 分支] [--top N] [--percent]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
|
||||
def run_git_log(repo_path, branch=None):
|
||||
"""调用 git log 获取作者列表。
|
||||
|
||||
使用 --format=%aN 只输出作者名字,每行一个。
|
||||
返回作者名字的列表。
|
||||
"""
|
||||
cmd = ["git", "-C", repo_path, "log", "--format=%aN"]
|
||||
if branch:
|
||||
cmd.append(branch)
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
errors="replace",
|
||||
)
|
||||
except FileNotFoundError:
|
||||
print("错误: 未找到 git 命令,请先安装 Git。", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("错误: 执行 git log 失败。", file=sys.stderr)
|
||||
if e.stderr:
|
||||
print(e.stderr.strip(), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
authors = [line.strip() for line in result.stdout.splitlines() if line.strip()]
|
||||
return authors
|
||||
|
||||
|
||||
def count_authors(authors):
|
||||
"""统计每位作者的提交次数,返回按次数降序排列的列表。"""
|
||||
counter = Counter(authors)
|
||||
# 按提交次数降序,次数相同按作者名字升序,保证结果稳定
|
||||
return sorted(counter.items(), key=lambda item: (-item[1], item[0]))
|
||||
|
||||
|
||||
def format_table(rows, total, show_percent):
|
||||
"""格式化输出表格。"""
|
||||
# 计算列宽
|
||||
name_width = max([len(name) for name, _ in rows] + [len("作者")]) + 2
|
||||
rank_width = max(len(str(len(rows))), len("排名")) + 2
|
||||
count_width = max([len(str(c)) for _, c in rows] + [len("提交次数")]) + 2
|
||||
|
||||
header = (
|
||||
f"{'排名':<{rank_width}}"
|
||||
f"{'作者':<{name_width}}"
|
||||
f"{'提交次数':<{count_width}}"
|
||||
)
|
||||
if show_percent:
|
||||
header += "占比"
|
||||
|
||||
lines = [header, "-" * (rank_width + name_width + count_width + (10 if show_percent else 0))]
|
||||
|
||||
for idx, (name, count) in enumerate(rows, start=1):
|
||||
line = (
|
||||
f"{idx:<{rank_width}}"
|
||||
f"{name:<{name_width}}"
|
||||
f"{count:<{count_width}}"
|
||||
)
|
||||
if show_percent:
|
||||
percent = (count / total * 100) if total else 0.0
|
||||
line += f"{percent:.2f}%"
|
||||
lines.append(line)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def parse_args(argv=None):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="git-author-stats",
|
||||
description="统计 Git 仓库中每个作者的提交次数并生成排名。",
|
||||
)
|
||||
parser.add_argument("repo_path", help="Git 仓库路径")
|
||||
parser.add_argument(
|
||||
"-b", "--branch",
|
||||
default=None,
|
||||
help="指定分支(默认当前分支)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t", "--top",
|
||||
type=int,
|
||||
default=None,
|
||||
help="只显示前 N 名作者",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--percent",
|
||||
action="store_true",
|
||||
help="显示每位作者的提交占比",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
args = parse_args(argv)
|
||||
|
||||
repo_path = os.path.abspath(args.repo_path)
|
||||
if not os.path.isdir(repo_path):
|
||||
print(f"错误: 路径不存在或不是目录: {args.repo_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 检查是否是 Git 仓库
|
||||
if not os.path.isdir(os.path.join(repo_path, ".git")):
|
||||
# 也可能是在 worktree 中,.git 是文件
|
||||
git_marker = os.path.join(repo_path, ".git")
|
||||
if not os.path.exists(git_marker):
|
||||
print(f"错误: 该路径不是 Git 仓库: {args.repo_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
authors = run_git_log(repo_path, args.branch)
|
||||
if not authors:
|
||||
print("该仓库没有任何提交记录。")
|
||||
return
|
||||
|
||||
ranking = count_authors(authors)
|
||||
total = len(authors)
|
||||
|
||||
if args.top is not None and args.top > 0:
|
||||
ranking = ranking[: args.top]
|
||||
|
||||
branch_display = args.branch if args.branch else "(当前分支)"
|
||||
print(f"仓库: {repo_path}")
|
||||
print(f"分支: {branch_display}")
|
||||
print(f"总提交数: {total}")
|
||||
print()
|
||||
print(format_table(ranking, total, args.percent))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user