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()