forked from Gitlink/gitlink-cli
404 lines
8.8 KiB
Markdown
404 lines
8.8 KiB
Markdown
# 代码变更分析
|
||
|
||
本文档详细说明如何使用 gitlink-cli 分析 PR 的代码变更。
|
||
|
||
## 📋 概述
|
||
|
||
代码变更分析是智能代码审查的第一步,通过获取 PR 的文件列表和 diff 内容,为后续的 AI 分析提供数据基础。
|
||
|
||
## 🎯 分析流程
|
||
|
||
```
|
||
开始
|
||
↓
|
||
1. 获取 PR 基本信息
|
||
├─ 使用 pr +view 获取 PR 详情
|
||
└─ 确认 PR 存在且可访问
|
||
↓
|
||
2. 获取变更文件列表
|
||
├─ 使用 pr +files 获取文件列表
|
||
└─ 识别新增/修改/删除的文件
|
||
↓
|
||
3. 获取 diff 内容
|
||
├─ 使用 pr +diff 获取完整 diff
|
||
└─ 解析代码变更详情
|
||
↓
|
||
4. 数据预处理
|
||
├─ 过滤无关文件(如二进制文件)
|
||
├─ 提取代码片段
|
||
└─ 组织分析数据
|
||
↓
|
||
完成
|
||
```
|
||
|
||
## 🔧 步骤详解
|
||
|
||
### 步骤 1:获取 PR 基本信息
|
||
|
||
**目的**: 确认 PR 存在且可访问,获取 PR 的元数据信息。
|
||
|
||
**命令**:
|
||
```bash
|
||
gitlink-cli pr +view --id <pr_id> --format json
|
||
```
|
||
|
||
**示例**:
|
||
```bash
|
||
# 获取 PR #123 的基本信息
|
||
gitlink-cli pr +view --id 123 --format json
|
||
```
|
||
|
||
**返回结果**:
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"data": {
|
||
"id": 123,
|
||
"project_issues_index": 123,
|
||
"title": "Feature: Add user authentication",
|
||
"body": "This PR adds user authentication...",
|
||
"author": {
|
||
"login": "developer",
|
||
"user_id": 456
|
||
},
|
||
"status": "open",
|
||
"pull_request_status": 0,
|
||
"head": "feature/auth",
|
||
"base": "main",
|
||
"created_at": "2026-06-12T10:00:00Z",
|
||
"updated_at": "2026-06-12T10:30:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**关键信息提取**:
|
||
- `id`: PR 数据库 ID(用于后续 API 调用)
|
||
- `project_issues_index`: PR 编号(网页显示)
|
||
- `title`: PR 标题
|
||
- `author`: 作者信息
|
||
- `status`: PR 状态(open/closed/merged)
|
||
- `head` / `base`: 分支信息
|
||
|
||
### 步骤 2:获取变更文件列表
|
||
|
||
**目的**: 获取 PR 中所有变更的文件列表,了解代码变更的范围。
|
||
|
||
**命令**:
|
||
```bash
|
||
gitlink-cli pr +files --id <pr_id> --format json
|
||
```
|
||
|
||
**示例**:
|
||
```bash
|
||
# 获取 PR #123 的变更文件列表
|
||
gitlink-cli pr +files --id 123 --format json
|
||
```
|
||
|
||
**返回结果**:
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"data": {
|
||
"files": [
|
||
{
|
||
"filename": "src/auth/login.go",
|
||
"status": "modified",
|
||
"additions": 50,
|
||
"deletions": 20,
|
||
"changes": 70,
|
||
"patch": "@@ -1,10 +1,15 @@\n+func login() {"
|
||
},
|
||
{
|
||
"filename": "src/auth/user.go",
|
||
"status": "added",
|
||
"additions": 80,
|
||
"deletions": 0,
|
||
"changes": 80,
|
||
"patch": "+package auth\n+\n+func User() {"
|
||
},
|
||
{
|
||
"filename": "README.md",
|
||
"status": "modified",
|
||
"additions": 5,
|
||
"deletions": 2,
|
||
"changes": 7,
|
||
"patch": "@@ -1,5 +1,7 @@\n+## Usage\n ..."
|
||
}
|
||
],
|
||
"total_files": 3,
|
||
"total_additions": 135,
|
||
"total_deletions": 22,
|
||
"total_changes": 157
|
||
}
|
||
```
|
||
|
||
**文件状态说明**:
|
||
- `added`: 新增文件
|
||
- `modified`: 修改文件
|
||
- `deleted`: 删除文件
|
||
- `renamed`: 重命名文件
|
||
|
||
**统计信息**:
|
||
- `total_files`: 变更文件总数
|
||
- `total_additions`: 新增行数
|
||
- `total_deletions`: 删除行数
|
||
- `total_changes`: 总变更行数
|
||
|
||
### 步骤 3:获取 diff 内容
|
||
|
||
**目的**: 获取 PR 的完整 diff 内容,用于 AI 代码分析。
|
||
|
||
**命令**:
|
||
```bash
|
||
gitlink-cli pr +diff --id <pr_id> --format json
|
||
```
|
||
|
||
**示例**:
|
||
```bash
|
||
# 获取 PR #123 的 diff 内容
|
||
gitlink-cli pr +diff --id 123 --format json
|
||
```
|
||
|
||
**返回结果**:
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"data": {
|
||
"diff": "diff --git a/src/auth/login.go b/src/auth/login.go\nindex 1234567..abcdefg 100644\n--- a/src/auth/login.go\n+++ b/src/auth/login.go\n@@ -1,10 +1,15 @@\n package auth\n\n+func login(username, password string) error {\n+\tdb, _ := sql.Open(\"mysql\", dsn)\n+\tquery := \"SELECT * FROM users WHERE username = '\" + username + \"'\"\n+\t...\n+}\n",
|
||
"files_count": 3,
|
||
"additions": 135,
|
||
"deletions": 22
|
||
}
|
||
}
|
||
```
|
||
|
||
**diff 格式说明**:
|
||
- 标准 unified diff 格式
|
||
- 包含文件头、变更块、代码行
|
||
- `+` 表示新增行
|
||
- `-` 表示删除行
|
||
|
||
### 步骤 4:数据预处理
|
||
|
||
**目的**: 清理和组织数据,为 AI 分析做准备。
|
||
|
||
#### 4.1 过滤无关文件
|
||
|
||
**需要过滤的文件类型**:
|
||
- 二进制文件(图片、字体、压缩包)
|
||
- 配置文件(package.json、tsconfig.json)
|
||
- 文档文件(README.md、CHANGELOG.md)
|
||
- 测试文件(*_test.go、*.spec.js)
|
||
|
||
**过滤规则**:
|
||
```javascript
|
||
const shouldSkip = (filename) => {
|
||
// 跳过二进制文件
|
||
const binaryExts = ['.png', '.jpg', '.gif', '.pdf', '.zip', '.exe'];
|
||
if (binaryExts.some(ext => filename.endsWith(ext))) {
|
||
return true;
|
||
}
|
||
|
||
// 跳过配置文件
|
||
const configFiles = ['package.json', 'tsconfig.json', '.gitignore'];
|
||
if (configFiles.includes(filename)) {
|
||
return true;
|
||
}
|
||
|
||
// 跳过文档文件
|
||
if (filename.match(/^(README|CHANGELOG|CONTRIBUTING)\.md$/i)) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
```
|
||
|
||
#### 4.2 提取代码片段
|
||
|
||
**目的**: 从 diff 中提取变更的代码片段,便于 AI 分析。
|
||
|
||
**示例**:
|
||
```javascript
|
||
const extractCodeSnippets = (diff) => {
|
||
const lines = diff.split('\n');
|
||
const snippets = [];
|
||
let currentSnippet = [];
|
||
let inHunk = false;
|
||
|
||
lines.forEach(line => {
|
||
if (line.startsWith('@@')) {
|
||
// 开始新的代码块
|
||
if (currentSnippet.length > 0) {
|
||
snippets.push(currentSnippet.join('\n'));
|
||
}
|
||
currentSnippet = [line];
|
||
inHunk = true;
|
||
} else if (inHunk && (line.startsWith('+') || line.startsWith('-') || line.startsWith(' '))) {
|
||
// 收集代码行
|
||
currentSnippet.push(line);
|
||
}
|
||
});
|
||
|
||
if (currentSnippet.length > 0) {
|
||
snippets.push(currentSnippet.join('\n'));
|
||
}
|
||
|
||
return snippets;
|
||
};
|
||
```
|
||
|
||
#### 4.3 组织分析数据
|
||
|
||
**最终数据结构**:
|
||
```json
|
||
{
|
||
"pr_info": {
|
||
"id": 123,
|
||
"title": "Feature: Add user authentication",
|
||
"author": "developer",
|
||
"branch": "feature/auth → main"
|
||
},
|
||
"files": [
|
||
{
|
||
"filename": "src/auth/login.go",
|
||
"status": "modified",
|
||
"language": "go",
|
||
"code_snippets": [
|
||
{
|
||
"start_line": 10,
|
||
"end_line": 25,
|
||
"code": "+func login(username, password string) error {"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"statistics": {
|
||
"total_files": 3,
|
||
"code_files": 2,
|
||
"total_additions": 135,
|
||
"total_deletions": 22
|
||
}
|
||
}
|
||
```
|
||
|
||
## 💡 最佳实践
|
||
|
||
### 1. 按文件类型分组
|
||
|
||
将变更文件按语言和类型分组,便于针对性分析:
|
||
|
||
```javascript
|
||
const groupFilesByLanguage = (files) => {
|
||
const groups = {
|
||
go: [],
|
||
javascript: [],
|
||
python: [],
|
||
other: []
|
||
};
|
||
|
||
files.forEach(file => {
|
||
const ext = file.filename.split('.').pop();
|
||
const lang = detectLanguage(ext);
|
||
groups[lang].push(file);
|
||
});
|
||
|
||
return groups;
|
||
};
|
||
```
|
||
|
||
### 2. 优先审查核心文件
|
||
|
||
优先审查核心业务逻辑文件:
|
||
|
||
```javascript
|
||
const prioritizeFiles = (files) => {
|
||
const priority = {
|
||
'high': [], // 核心业务逻辑
|
||
'medium': [], // 工具函数
|
||
'low': [] // 配置、测试
|
||
};
|
||
|
||
files.forEach(file => {
|
||
if (file.filename.includes('core') || file.filename.includes('service')) {
|
||
priority.high.push(file);
|
||
} else if (file.filename.includes('util') || file.filename.includes('helper')) {
|
||
priority.medium.push(file);
|
||
} else {
|
||
priority.low.push(file);
|
||
}
|
||
});
|
||
|
||
return priority;
|
||
};
|
||
```
|
||
|
||
### 3. 限制分析范围
|
||
|
||
对于大型 PR,限制分析范围:
|
||
|
||
```javascript
|
||
const limitAnalysisScope = (files, maxFiles = 10, maxLines = 1000) => {
|
||
let totalLines = 0;
|
||
const selectedFiles = [];
|
||
|
||
for (const file of files) {
|
||
if (selectedFiles.length >= maxFiles) break;
|
||
if (totalLines + file.changes > maxLines) break;
|
||
|
||
selectedFiles.push(file);
|
||
totalLines += file.changes;
|
||
}
|
||
|
||
return selectedFiles;
|
||
};
|
||
```
|
||
|
||
## 🔍 常见问题
|
||
|
||
### Q: 如何处理大型 PR?
|
||
|
||
**A**: 大型 PR(>1000 行)建议:
|
||
1. 按模块分组分析
|
||
2. 优先审查核心文件
|
||
3. 分批生成审查报告
|
||
4. 建议作者拆分为多个小 PR
|
||
|
||
### Q: 如何处理重命名文件?
|
||
|
||
**A**: GitLink 的 PR API 会正确处理重命名:
|
||
- `status` 为 `renamed`
|
||
- `patch` 包含重命名前后的完整路径
|
||
- 分析时使用新文件名
|
||
|
||
### Q: 如何检测文件语言?
|
||
|
||
**A**: 使用文件扩展名检测:
|
||
|
||
```javascript
|
||
const detectLanguage = (filename) => {
|
||
const ext = filename.split('.').pop();
|
||
const languageMap = {
|
||
'go': 'go',
|
||
'js': 'javascript',
|
||
'ts': 'typescript',
|
||
'py': 'python',
|
||
'java': 'java',
|
||
'rb': 'ruby',
|
||
'php': 'php'
|
||
};
|
||
return languageMap[ext] || 'other';
|
||
};
|
||
```
|
||
|
||
## 📚 相关文档
|
||
|
||
- [代码质量检查](code-review-quality.md) - 代码质量分析
|
||
- [安全性检查](code-review-security.md) - 安全性分析
|
||
- [性能检查](code-review-performance.md) - 性能分析
|
||
- [完整工作流](../examples/comprehensive-review-workflow.md) - 完整审查流程
|
||
|
||
---
|
||
|
||
*最后更新: 2026-06-12*
|