用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/microwind/ai-skills --skill git命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | Git分析器 |
| description | 当分析Git仓库、检查提交历史、理解项目演进或查找问题时,分析Git历史和提交。 |
| license | MIT |
Git历史是项目的时间线。正确读取它以了解代码如何演进。误读历史会在调试时浪费时间。
核心原则: Git告诉你什么改变了以及为什么改变。
始终:
触发短语:
问题:
提交信息不清晰或不规范
错误示例:
fix bug
update
temp commit
wip
解决方案:
使用规范的提交格式:
feat: 添加用户登录功能
fix: 修复登录验证错误
docs: 更新API文档
refactor: 重构用户服务模块
问题:
分支命名不规范,合并策略混乱
错误示例:
- feature1
- bug-fix-2
- test-branch
- master分支直接开发
解决方案:
- main/master: 主分支
- develop: 开发分支
- feature/xxx: 功能分支
- hotfix/xxx: 热修复分支
- release/xxx: 发布分支
问题:
频繁的合并冲突
原因:
- 功能分支过长时间不合并
- 多人修改同一文件
- 缺乏代码审查
解决方案:
1. 定期合并主分支
2. 小步提交,频繁集成
3. 建立代码审查流程
4. 使用功能开关
import subprocess
import json
import re
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime
from collections import defaultdict, Counter
import os
@dataclass
class GitCommit:
"""Git提交信息"""
hash: str
author: str
email: str
date: datetime
message: str
files_changed: List[str]
insertions: int
deletions: int
branches: List[str]
@dataclass
class GitFile:
"""Git文件信息"""
path: str
size: int
last_modified: datetime
last_author: str
change_count: int
total_insertions: int
total_deletions: int
@dataclass
class GitAuthor:
"""Git作者信息"""
name: str
email: str
commits: int
insertions: int
deletions:
files_touched:
first_commit: datetime
last_commit: datetime
active_days:
:
total_commits:
total_authors:
total_files:
date_range: [datetime, datetime]
top_authors: [GitAuthor]
hot_files: [GitFile]
commit_frequency: [, ]
:
():
.repo_path = repo_path
.git_cmd = [, , repo_path]
() -> :
:
result = subprocess.run(
.git_cmd + args,
capture_output=,
text=,
check=
)
result.stdout.strip()
subprocess.CalledProcessError e:
Exception()
() -> [GitCommit]:
args = [
,
,
,
,
,
]
since:
args.append()
output = ._run_git_command(args)
._parse_commits(output)
() -> [GitCommit]:
commits = []
lines = output.split()
i =
i < (lines):
lines[i]:
i +=
header = lines[i].split()
(header) >= :
commit_hash = header[]
author = header[]
email = header[]
date = datetime.fromisoformat(header[].replace(, ))
message = .join(header[:])
i +=
insertions =
deletions =
files_changed = []
i < (lines) lines[i]:
line = lines[i]
line:
parts = line.split()
(parts) == :
:
ins = (parts[]) parts[] !=
dels = (parts[]) parts[] !=
file_path = parts[]
insertions += ins
deletions += dels
files_changed.append(file_path)
ValueError:
i +=
branches = ._get_commit_branches(commit_hash)
commits.append(GitCommit(
=commit_hash,
author=author,
email=email,
date=date,
message=message,
files_changed=files_changed,
insertions=insertions,
deletions=deletions,
branches=branches
))
:
i +=
commits
() -> []:
:
output = ._run_git_command([
, , commit_hash
])
branches = []
line output.split():
branch = line.strip().replace(, )
branch branch != :
branches.append(branch)
branches
:
[]
() -> [GitCommit]:
output = ._run_git_command([
,
,
,
,
file_path
])
._parse_commits(output)
() -> [GitAuthor]:
output = ._run_git_command([
,
,
,
])
author_stats = defaultdict(: {
: ,
: ,
: ,
: (),
: [],
: ,
:
})
lines = output.split()
i =
i < (lines):
lines[i]:
i +=
lines[i]:
parts = lines[i].split()
(parts) >= :
author = parts[]
email = parts[]
date = datetime.fromisoformat(parts[].replace(, ))
stats = author_stats[author]
stats[] +=
stats[].append(date)
stats[] date < stats[]:
stats[] = date
stats[] date > stats[]:
stats[] = date
i +=
i < (lines) lines[i] lines[i]:
file_parts = lines[i].split()
(file_parts) >= :
file_path = file_parts[]
stats[].add(file_path)
:
ins = (file_parts[]) file_parts[] !=
dels = (file_parts[]) file_parts[] !=
stats[] += ins
stats[] += dels
ValueError:
i +=
:
i +=
authors = []
author_name, stats author_stats.items():
active_days = ((d.date() d stats[]))
authors.append(GitAuthor(
name=author_name,
email=,
commits=stats[],
insertions=stats[],
deletions=stats[],
files_touched=(stats[]),
first_commit=stats[],
last_commit=stats[],
active_days=active_days
))
(authors, key= x: x.commits, reverse=)
() -> [GitFile]:
output = ._run_git_command([
,
,
])
file_stats = defaultdict(: {
: ,
: ,
: ,
: ,
:
})
lines = output.split()
current_commit =
line lines:
line.strip():
line.startswith():
current_commit = line
:
file_path = line.strip()
file_path file_path != :
stats = file_stats[file_path]
stats[] +=
hot_files = []
file_path, stats file_stats.items():
:
full_path = os.path.join(.repo_path, file_path)
os.path.exists(full_path):
size = os.path.getsize(full_path)
:
size =
last_commit_info = ._get_last_file_commit(file_path)
hot_files.append(GitFile(
path=file_path,
size=size,
last_modified=last_commit_info.get(, datetime.now()),
last_author=last_commit_info.get(, ),
change_count=stats[],
total_insertions=stats[],
total_deletions=stats[]
))
:
(hot_files, key= x: x.change_count, reverse=)[:limit]
() -> [, ]:
:
output = ._run_git_command([
,
,
,
,
file_path
])
output:
parts = output.split()
{
: parts[] (parts) > ,
: datetime.fromisoformat(parts[].replace(, )) (parts) > datetime.now()
}
:
{: , : datetime.now()}
() -> GitAnalysis:
total_commits = (._run_git_command([, , ]))
authors = .get_authors()
total_authors = (authors)
output = ._run_git_command([])
total_files = (output.split()) output
first_date_str = ._run_git_command([, , , ]).split()[]
last_date_str = ._run_git_command([, , , ])
first_date = datetime.fromisoformat(first_date_str.replace(, ))
last_date = datetime.fromisoformat(last_date_str.replace(, ))
hot_files = .get_hot_files()
commit_frequency = ._get_commit_frequency()
GitAnalysis(
total_commits=total_commits,
total_authors=total_authors,
total_files=total_files,
date_range=(first_date, last_date),
top_authors=authors[:],
hot_files=hot_files,
commit_frequency=commit_frequency
)
() -> [, ]:
output = ._run_git_command([
,
,
])
dates = output.split()
(Counter(dates))
() -> [GitCommit]:
commits = .get_commits(limit=)
suspicious_commits = []
commit commits:
(keyword commit.message.lower() keyword [, , , ]):
suspicious_commits.append(commit)
suspicious_commits
() -> :
report = []
report.append()
report.append()
report.append()
report.append()
report.append()
project_age = (analysis.date_range[] - analysis.date_range[]).days
report.append()
project_age > :
avg_commits_per_day = analysis.total_commits / project_age
report.append()
report.append()
report.append()
i, author (analysis.top_authors[:], ):
report.append()
report.append()
report.append()
report.append()
report.append()
report.append()
i, file (analysis.hot_files[:], ):
report.append()
report.append()
report.append()
report.append()
report.append()
report.append()
recent_dates = (analysis.commit_frequency.items(), reverse=)[:]
date, count recent_dates:
report.append()
.join(report)
():
analyzer = GitAnalyzer()
analysis = analyzer.analyze_repository()
report = analyzer.generate_analysis_report(analysis)
(report)
bug_commits = analyzer.find_bug_introduction()
()
__name__ == :
main()
import re
from typing import List, Dict, Any
class GitCodeReviewer:
"""Git代码审查工具"""
def __init__(self, repo_path: str = "."):
self.analyzer = GitAnalyzer(repo_path)
self.review_rules = self._initialize_review_rules()
def review_commit(self, commit_hash: str) -> Dict[str, Any]:
"""审查单个提交"""
commits = self.analyzer.get_commits(limit=1)
if not commits or commits[0].hash != commit_hash:
# 获取特定提交
output = self.analyzer._run_git_command([
"show",
"--pretty=format:%H|%an|%ae|%ad|%s",
"--date=iso",
"--numstat",
commit_hash
])
commits = self.analyzer._parse_commits(output)
if not commits:
return {"error": "提交不存在"}
commit = commits[0]
# 审查提交信息
message_issues = ._review_commit_message(commit.message)
code_issues = ._review_code_changes(commit)
file_issues = ._review_file_changes(commit)
{
: commit,
: {
: message_issues,
: code_issues,
: file_issues
},
: ._calculate_review_score(message_issues, code_issues, file_issues)
}
() -> []:
issues = []
(message) > :
issues.append()
re.(, message):
issues.append()
(message) < :
issues.append()
sensitive_patterns = [
,
,
,
,
]
pattern sensitive_patterns:
re.search(pattern, message, re.IGNORECASE):
issues.append()
issues
() -> []:
issues = []
total_changes = commit.insertions + commit.deletions
total_changes > :
issues.append()
commit.deletions > commit.insertions * :
issues.append()
(commit.files_changed) > :
issues.append()
issues
() -> []:
issues = []
sensitive_files = [
,
,
,
,
]
file_path commit.files_changed:
sensitive sensitive_files:
sensitive file_path:
issues.append()
test_files = [f f commit.files_changed f.lower()]
test_files (commit.files_changed) > :
issues.append()
issues
() -> :
total_issues = (message_issues) + (code_issues) + (file_issues)
score = (, - total_issues * )
score
() -> [, ]:
output = .analyzer._run_git_command([
,
,
])
commit_hashes = output.split() output []
branch_reviews = []
total_score =
commit_hash commit_hashes:
commit_hash.strip():
review = .review_commit(commit_hash.strip())
branch_reviews.append(review)
total_score += review.get(, )
avg_score = total_score / (branch_reviews) branch_reviews
{
: branch_name,
: branch_reviews,
: (branch_reviews),
: avg_score,
: ._get_branch_recommendation(avg_score)
}
() -> :
score >= :
score >= :
:
() -> [, ]:
{
: ,
: ,
: ,
: ,
: [
, , , ,
]
}
():
reviewer = GitCodeReviewer()
latest_commit = reviewer.analyzer._run_git_command([, ])
review_result = reviewer.review_commit(latest_commit)
()
()
()
()
review_result[][]:
()
issue review_result[][]:
()
review_result[][]:
()
issue review_result[][]:
()
review_result[][]:
()
issue review_result[][]:
()
__name__ == :
main()