gitlink-cli/skills/gitlink-code-review/references/code-review-analyze.md

8.8 KiB
Raw Blame History

代码变更分析

本文档详细说明如何使用 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 的元数据信息。

命令:

gitlink-cli pr +view --id <pr_id> --format json

示例:

# 获取 PR #123 的基本信息
gitlink-cli pr +view --id 123 --format 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 中所有变更的文件列表,了解代码变更的范围。

命令:

gitlink-cli pr +files --id <pr_id> --format json

示例:

# 获取 PR #123 的变更文件列表
gitlink-cli pr +files --id 123 --format 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 代码分析。

命令:

gitlink-cli pr +diff --id <pr_id> --format json

示例:

# 获取 PR #123 的 diff 内容
gitlink-cli pr +diff --id 123 --format 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

过滤规则:

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 分析。

示例:

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 组织分析数据

最终数据结构:

{
  "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. 按文件类型分组

将变更文件按语言和类型分组,便于针对性分析:

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. 优先审查核心文件

优先审查核心业务逻辑文件:

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限制分析范围

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 会正确处理重命名:

  • statusrenamed
  • patch 包含重命名前后的完整路径
  • 分析时使用新文件名

Q: 如何检测文件语言?

A: 使用文件扩展名检测:

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';
};

📚 相关文档


最后更新: 2026-06-12