本文是 IntelliGit 智能提交链路优化复盘系列的下篇聚焦能力落地AST 语义全链路接入、Hunk 级精准暂存、智能分组校正、提交信息生成质量提升。建议先阅读上篇了解底层引擎设计。一、前言底层能力落地实现体验闭环上篇介绍了astChangeAnalyzer.ts中 AST 语义分析、Hunk 细粒度识别、三级置信度计算等底层基建。技术基建的核心价值最终要在业务场景中得到体现。本次迭代在搭建完底层分析引擎后完成了智能分组、提交信息生成、精准暂存、视图展示全链路接入解决了传统智能提交体验差、准确率低、实用性弱的痛点。整体改造围绕三大模块展开底层 AST 分析引擎astChangeAnalyzer.ts ↓ 核心服务层smartCommitService.ts ├── analyzeSmartCommitChanges() ← AST 上下文全量接入分组分析 ├── generateSmartCommitMessage() ← AST 上下文驱动提交信息生成 └── stageGroupAndGenerateMessage() ← Hunk 级精准暂存 按组生成 ↓ LLM Provider 层smartCommitProvider.ts ├── analyzeChanges() ← AI 分组 本地 AST 兜底 └── generateMessage() ← AI 生成 格式化校验 ↓ 视图层CommitPanel / DiffView └── 可视化展示 精准操作二、核心服务升级AST 语义全链路接入2.1 构建完整 AST 上下文旧版analyzeSmartCommitChanges()仅传递 Diff 文本和文件列表给 AIAI 只能凭文本猜测变更意图。新版在调用 Provider 之前先构建完整的 AST 语义上下文export async function analyzeSmartCommitChanges(): PromiseAgentResultSmartCommitAnalysisResult { const [workdirDiff, stagedDiff] await Promise.all([ invokeGit(diff.workdirRaw, {}), invokeGit(diff.stagedRaw, {}) ]) const diff workdirDiff.diff || stagedDiff.diff const files getChangedFiles() // 1. 构建文件内容映射HEAD 版本 vs 当前版本 const astContentMap await buildAstContentMap(diff) // 2. 运行 AST 分析引擎得到结构化语义洞察 const astInsights analyzeAstChanges(files, diff, astContentMap) // 3. 渲染为 AI 可直接消费的文本上下文 const astContext renderAstContext(files, diff, astContentMap) if (!diff.trim()) { return { success: false, error: 当前没有可分析的代码变更 } } const result await smartCommitProvider.analyzeChanges({ diff, files, astContext }) // 4. 用 AST 结果反向校正 AI 分组数据 if (result.success result.data) { return { ...result, data: enrichGroupsWithAst(result.data, astInsights) } } return result }renderAstContext()输出的内容示例- [typescript/modified] smartCommitService.ts函数体/结构变更、参数变更涉及 buildGroupContext、enrichGroupsWithAst2 个 Hunk置信度 high 变更类型函数体/结构变更, 参数变更 关注符号buildGroupContext, enrichGroupsWithAst AST 符号function:buildGroupContext28-35, function:enrichGroupsWithAst82-97 AST Diff新增参数 hunks: string[]修改函数体逻辑 Hunk 摘要 -28,6 28,8 2, -0置信度 high所属 function:buildGroupContextAI 拿到这份上下文后不再需要猜变更意图而是基于精准的符号级变更描述来做分组判断。2.2buildAstContentMap()获取文件双版本内容AST 分析需要文件的旧版本HEAD和新版本内容做对比。buildAstContentMap()通过解析 Diff 文件路径并发拉取各文件在 HEAD 的内容async function buildAstContentMap(diff: string): PromiseAstFileContentMap { const entries parseDiffFilePaths(diff) const pairs await Promise.all( entries.map(async (entry) { const oldPath entry.oldPath || entry.filePath const [oldContent, newContent] await Promise.all([ entry.status added ? Promise.resolve() : readGitText(oldPath, HEAD), Promise.resolve() // 新版本内容由 AST 引擎从 Diff 中重建 ]) return [entry.filePath, { oldContent, newContent }] as const }) ) return Object.fromEntries(pairs) }对于新增文件status added旧版本内容为空AST diff 会将所有符号归类为added-symbol。三、智能分组双向增强AI 本地 AST 校正3.1 传统纯 AI 分组的问题纯 AI 分组的核心缺陷是随机性强、结果不可控同样的 Diff 两次调用可能产生不同分组且分组置信度全靠 AI 自述没有客观依据。3.2enrichGroupsWithAst()AST 回填校正新增的enrichGroupsWithAst()将底层 AST 分析结果回填至 AI 分组数据function enrichGroupsWithAst( result: SmartCommitAnalysisResult, insights: AstChangeInsight[] ): SmartCommitAnalysisResult { const insightMap new Map(insights.map(insight [insight.filePath, insight])) const fallback buildAnalysisSummary(insights) const groups result.groups.map(group { // 找出该分组关联文件的 AST 洞察数据 const groupInsights group.files .map(file insightMap.get(file)) .filter((insight): insight is AstChangeInsight Boolean(insight)) return { ...group, // AI 给了置信度就用 AI 的否则用 AST 聚合计算结果 confidence: group.confidence || mergeConfidence( groupInsights.map(insight insight.confidence) ) } }) return { ...result, groups, // 分析摘要、置信度、变更类型AI 有则用 AI否则 AST 兜底 analysisSummary: result.analysisSummary || fallback.analysisSummary, confidence: result.confidence || fallback.confidence, changeKinds: result.changeKinds?.length ? result.changeKinds : fallback.changeKinds } }分析摘要生成逻辑buildAnalysisSummary()function buildAnalysisSummary(insights: AstChangeInsight[]): PickSmartCommitAnalysisResult, analysisSummary | confidence | changeKinds { const changeKinds [...new Set(insights.flatMap(i i.changeKinds))].slice(0, 6) const confidence mergeConfidence(insights.map(i i.confidence)) const files insights.length return { analysisSummary: files 0 ? 识别到 ${files} 个文件的 ${changeKinds.slice(0, 3).join(、) || 代码} 变更 : 已完成智能分组分析, confidence, changeKinds } }改造后智能分组从「纯 AI 黑盒输出」升级为「AI 智能判断 本地 AST 精准校正兜底」的双向增强模式。四、完善分组上下文高质量提交信息的基础4.1buildGroupContext()封装完整意图数据包生成提交信息时旧版只向 AI 传递 Diff 文本。新版通过buildGroupContext()为每个分组封装完整的语义数据包function buildGroupContext(group: CommitIntentGroup): string { const scope group.scope ? \n作用域${group.scope} : const confidence group.confidence ? \n置信度${group.confidence} : const hunkText group.hunks?.length ? \n关联 Hunk\n${group.hunks.map(h - ${h}).join(\n)} : return 提交意图${group.type}${scope}${confidence}\n分组摘要${group.summary}\n分组文件\n${ group.files.map(f - ${f}).join(\n) }${hunkText} }输出示例提交意图feat 作用域smart-commit 置信度high 分组摘要新增 AST 上下文接入与 Hunk 精准暂存能力 分组文件 - src/services/smartCommitService.ts - src/utils/astChangeAnalyzer.ts 关联 Hunk - src/services/smartCommitService.ts -82,12 82,28 AI 基于这份完整意图包生成提交信息产出内容更贴合 Conventional Commits 规范精准匹配实际改动场景。4.2 Provider 层的提交信息格式化与校验smartCommitProvider.ts中的格式化函数对 AI 输出做严格清洗避免不合规格式进入提交记录function sanitizeType(type: string | undefined): string { const normalized type?.trim().toLowerCase() return normalized COMMIT_TYPES.has(normalized) ? normalized : chore } function sanitizeScope(scope: string | undefined): string | undefined { const normalized scope?.trim().toLowerCase().replace(/[^a-z0-9-]/g, -) if (!normalized || !SCOPE_PATTERN.test(normalized)) return undefined return normalized } function sanitizeSubject(subject: string | undefined): string { const normalized limitText(subject || 更新代码变更, MAX_COMMIT_SUBJECT_LENGTH) return normalized.replace(/[。.!]$/g, ) || 更新代码变更 }校验规则一览字段规则type必须在 COMMIT_TYPES 白名单内否则降级为chorescope只允许小写字母、数字、连字符不合规则丢弃subject截断至 72 字符移除末尾标点body去除首尾空白为空则省略五、Hunk 级精准暂存最具工程价值的核心突破5.1 传统文件级暂存的痛点日常开发中一个文件往往包含多次独立改动——新增功能、修复 bug、代码重构混在一起。传统git add file只能整体暂存必须手动用git add -p拆分操作繁琐且极易出错。5.2stageGroupAndGenerateMessage()按意图分组暂存新版核心函数stageGroupAndGenerateMessage()实现了按意图分组精准暂存export async function stageGroupAndGenerateMessage( group: CommitIntentGroup ): PromiseAgentResultSmartCommitGroupWorkflowResult { return withOperation(commit.generateMessage, async () { const files normalizeFiles(group.files) // Step 1获取该分组文件的 workdir diff const workdirGroupDiff (await Promise.all( files.map(async f (await invokeGit(diff.workdirRaw, { path: f })).diff) )).filter(Boolean).join(\n) // Step 2优先尝试 Hunk 级 patch 暂存 const appliedPatch workdirGroupDiff.trim() ? await applyGroupHunks(group, workdirGroupDiff) : null // Step 3Hunk 匹配失败时回退到文件级暂存兜底 if (!appliedPatch) { for (const f of files) { await invokeGit(staging.add, { path: f }) } } await useGitStatusStore.getState().refreshStatus() // Step 4基于实际暂存的 diff 生成提交信息 const stagedGroupDiff (await Promise.all( files.map(async f (await invokeGit(diff.stagedRaw, { path: f })).diff) )).filter(Boolean).join(\n) const groupDiff appliedPatch || stagedGroupDiff const astContentMap await buildAstContentMap(groupDiff) const result await smartCommitProvider.generateMessage({ diff: groupDiff, stagedFileCount: files.length, groupContext: buildGroupContext(group), astContext: renderAstContext(files, groupDiff, astContentMap) }) // ... }) }5.3 Hunk 匹配与 Patch 应用applyGroupHunks()从完整 Diff 中精准提取分组对应的 Hunk patch只暂存目标变更片段function matchGroupHunks(group: CommitIntentGroup, diff: string): RawHunk[] { const files new Set(normalizeFiles(group.files)) const wantedHunks new Set(group.hunks || []) return parseRawDiffHunks(diff).filter(hunk { if (!files.has(hunk.filePath)) return false if (wantedHunks.size 0) return true // 优先精确匹配 filePathhunkHeader 格式的 Hunk ID return wantedHunks.has(${hunk.filePath}${hunk.header}) || wantedHunks.has(hunk.header) }) } async function applyGroupHunks(group: CommitIntentGroup, diff: string): Promisestring | null { const hunks matchGroupHunks(group, diff) if (hunks.length 0) return null const patch hunks.map(h h.patch).join(\n) await invokeGit(staging.applyPatch, { patch }) return patch }Hunk ID 格式定义在CommitIntentGroup.hunks字段中格式为${filePath}${hunkHeader}例如src/services/smartCommitService.ts -82,12 82,28 AI 在分组时可以为每组分配关联的 Hunk ID 列表实现AI 决策 精准执行的完整闭环。5.4 容错兜底策略场景处理方式Hunk 匹配成功应用精准 patch只暂存目标变更Hunk 匹配失败patch 为空回退到git add file文件级暂存暂存后 diff 仍为空返回错误不继续生成信息三层容错保证功能 100% 可用不会因 Hunk 匹配失败阻塞正常工作流。六、Provider 层的降级闭环设计smartCommitProvider.ts中的LlmSmartCommitProvider实现了AI 优先、本地兜底的完整闭环async analyzeChanges(input: SmartCommitAnalyzeInput): PromiseAgentResultSmartCommitAnalysisResult { const config getCurrentLlmConfig() // 未配置 API Key → 直接走本地 fallback if (!hasUsableLlmConfig(config)) { const fallback fallbackCommitGroups(input.files) as AgentResultSmartCommitAnalysisResult return { ...fallback, error: toFallbackReason(config) } } // 调用 AI const result await runAgent(config, { ... }, parseStructured) if (result.success result.data) { const sanitized sanitizeGroups(result.data, input.files) if (sanitized) return { ...result, data: sanitized } } // AI 失败 / 数据校验不通过 → 本地 fallback const fallback fallbackCommitGroups(input.files) as AgentResultSmartCommitAnalysisResult return { ...fallback, error: result.error || toFallbackReason(config), rawOutput: result.rawOutput } }降级层次AI 服务已配置 调用成功 数据校验通过 → 返回 AI 分组结果经 AST 校正 AI 服务已配置 调用失败 / 数据异常 → 本地 fallback 分组 记录错误原因 AI 服务未配置 → 本地 fallback 分组 提示用户配置无论哪种情况用户都能得到一个可用的分组结果功能不会因 AI 服务问题而中断。七、分组数据安全校验sanitizeGroups()AI 输出的分组数据在进入业务逻辑前经过严格的安全过滤function sanitizeGroups( result: SmartCommitAnalysisResult, inputFiles: string[] ): SmartCommitAnalysisResult | null { const allowedFiles new Set(inputFiles) const usedFiles new Setstring() const groups result.groups .map(group ({ type: sanitizeType(group.type), scope: sanitizeScope(group.scope), summary: limitText(group.summary, MAX_GROUP_SUMMARY_LENGTH), confidence: group.confidence, // 过滤掉不在变更文件列表中的幻觉文件并去重 files: group.files .map(f f.trim()) .filter(f allowedFiles.has(f) !usedFiles.has(f)) })) .filter(group { group.files.forEach(f usedFiles.add(f)) return group.summary group.files.length 0 }) .slice(0, 5) // 最多 5 个分组避免过碎 return groups.length 0 ? { groups, ... } : null }这里有一个重要细节过滤 AI 幻觉文件。AI 有时会在分组中编造不存在于实际变更列表的文件路径allowedFiles过滤器可完全杜绝这类问题。八、完整链路数据流本次全链路优化的完整数据流用户触发智能提交分析 ↓ invokeGit(diff.workdirRaw) / invokeGit(diff.stagedRaw) ↓ ↓ buildAstContentMap(diff) getChangedFiles() ↓ analyzeAstChanges() → AstChangeInsight[] renderAstContext() → AI 可用文本上下文 ↓ smartCommitProvider.analyzeChanges({ diff, files, astContext }) ↓ runAgent()AI 分组 ↓ sanitizeGroups()安全校验 ↓ enrichGroupsWithAst()AST 置信度回填 ↓ SmartCommitAnalysisResult分组 摘要 置信度 变更类型 ↓ 用户选择分组 → stageGroupAndGenerateMessage() ↓ applyGroupHunks()Hunk 精准暂存 ← 失败 → git add file回退 ↓ smartCommitProvider.generateMessage({ diff, groupContext, astContext }) ↓ formatCommitMessage()格式化校验 ↓ 最终提交信息填入输入框九、优化价值复盘核心能力对比能力优化前优化后AI 分析上下文仅 Diff 文本Diff AST 符号 变更类型 Hunk 摘要智能分组依据纯 AI 黑盒AI 分组 AST 置信度校正分组兜底机制无AI 失败时本地 fallback 兜底暂存粒度文件级Hunk 级失败回退文件级提交信息上下文仅 Diff完整意图包意图 作用域 Hunk AST输出格式校验无type / scope / subject 严格校验整体价值本次全链路优化的本质是将智能提交从依赖文本匹配的粗粒度工具升级为基于 AST Hunk 语义分析的智能化开发辅助能力。系统不再只知道文件改了更能读懂代码结构改了什么、为何改动、可信度如何。从底层分析、智能分组、精准暂存到提交信息生成形成了完整的语义化智能链路让 AI 辅助开发真正落地到高频 Git 操作场景。系列导读上篇[从文本到语义AST Hunk 重构 IntelliGit 智能提交底层分析能力]下篇本文全链路落地——AST 赋能智能分组、Hunk 精准暂存、提交信息生成
IntelliGit第九期:全链路落地:AST + Hunk 赋能 IntelliGit 智能提交极致体验
发布时间:2026/6/10 2:14:51
本文是 IntelliGit 智能提交链路优化复盘系列的下篇聚焦能力落地AST 语义全链路接入、Hunk 级精准暂存、智能分组校正、提交信息生成质量提升。建议先阅读上篇了解底层引擎设计。一、前言底层能力落地实现体验闭环上篇介绍了astChangeAnalyzer.ts中 AST 语义分析、Hunk 细粒度识别、三级置信度计算等底层基建。技术基建的核心价值最终要在业务场景中得到体现。本次迭代在搭建完底层分析引擎后完成了智能分组、提交信息生成、精准暂存、视图展示全链路接入解决了传统智能提交体验差、准确率低、实用性弱的痛点。整体改造围绕三大模块展开底层 AST 分析引擎astChangeAnalyzer.ts ↓ 核心服务层smartCommitService.ts ├── analyzeSmartCommitChanges() ← AST 上下文全量接入分组分析 ├── generateSmartCommitMessage() ← AST 上下文驱动提交信息生成 └── stageGroupAndGenerateMessage() ← Hunk 级精准暂存 按组生成 ↓ LLM Provider 层smartCommitProvider.ts ├── analyzeChanges() ← AI 分组 本地 AST 兜底 └── generateMessage() ← AI 生成 格式化校验 ↓ 视图层CommitPanel / DiffView └── 可视化展示 精准操作二、核心服务升级AST 语义全链路接入2.1 构建完整 AST 上下文旧版analyzeSmartCommitChanges()仅传递 Diff 文本和文件列表给 AIAI 只能凭文本猜测变更意图。新版在调用 Provider 之前先构建完整的 AST 语义上下文export async function analyzeSmartCommitChanges(): PromiseAgentResultSmartCommitAnalysisResult { const [workdirDiff, stagedDiff] await Promise.all([ invokeGit(diff.workdirRaw, {}), invokeGit(diff.stagedRaw, {}) ]) const diff workdirDiff.diff || stagedDiff.diff const files getChangedFiles() // 1. 构建文件内容映射HEAD 版本 vs 当前版本 const astContentMap await buildAstContentMap(diff) // 2. 运行 AST 分析引擎得到结构化语义洞察 const astInsights analyzeAstChanges(files, diff, astContentMap) // 3. 渲染为 AI 可直接消费的文本上下文 const astContext renderAstContext(files, diff, astContentMap) if (!diff.trim()) { return { success: false, error: 当前没有可分析的代码变更 } } const result await smartCommitProvider.analyzeChanges({ diff, files, astContext }) // 4. 用 AST 结果反向校正 AI 分组数据 if (result.success result.data) { return { ...result, data: enrichGroupsWithAst(result.data, astInsights) } } return result }renderAstContext()输出的内容示例- [typescript/modified] smartCommitService.ts函数体/结构变更、参数变更涉及 buildGroupContext、enrichGroupsWithAst2 个 Hunk置信度 high 变更类型函数体/结构变更, 参数变更 关注符号buildGroupContext, enrichGroupsWithAst AST 符号function:buildGroupContext28-35, function:enrichGroupsWithAst82-97 AST Diff新增参数 hunks: string[]修改函数体逻辑 Hunk 摘要 -28,6 28,8 2, -0置信度 high所属 function:buildGroupContextAI 拿到这份上下文后不再需要猜变更意图而是基于精准的符号级变更描述来做分组判断。2.2buildAstContentMap()获取文件双版本内容AST 分析需要文件的旧版本HEAD和新版本内容做对比。buildAstContentMap()通过解析 Diff 文件路径并发拉取各文件在 HEAD 的内容async function buildAstContentMap(diff: string): PromiseAstFileContentMap { const entries parseDiffFilePaths(diff) const pairs await Promise.all( entries.map(async (entry) { const oldPath entry.oldPath || entry.filePath const [oldContent, newContent] await Promise.all([ entry.status added ? Promise.resolve() : readGitText(oldPath, HEAD), Promise.resolve() // 新版本内容由 AST 引擎从 Diff 中重建 ]) return [entry.filePath, { oldContent, newContent }] as const }) ) return Object.fromEntries(pairs) }对于新增文件status added旧版本内容为空AST diff 会将所有符号归类为added-symbol。三、智能分组双向增强AI 本地 AST 校正3.1 传统纯 AI 分组的问题纯 AI 分组的核心缺陷是随机性强、结果不可控同样的 Diff 两次调用可能产生不同分组且分组置信度全靠 AI 自述没有客观依据。3.2enrichGroupsWithAst()AST 回填校正新增的enrichGroupsWithAst()将底层 AST 分析结果回填至 AI 分组数据function enrichGroupsWithAst( result: SmartCommitAnalysisResult, insights: AstChangeInsight[] ): SmartCommitAnalysisResult { const insightMap new Map(insights.map(insight [insight.filePath, insight])) const fallback buildAnalysisSummary(insights) const groups result.groups.map(group { // 找出该分组关联文件的 AST 洞察数据 const groupInsights group.files .map(file insightMap.get(file)) .filter((insight): insight is AstChangeInsight Boolean(insight)) return { ...group, // AI 给了置信度就用 AI 的否则用 AST 聚合计算结果 confidence: group.confidence || mergeConfidence( groupInsights.map(insight insight.confidence) ) } }) return { ...result, groups, // 分析摘要、置信度、变更类型AI 有则用 AI否则 AST 兜底 analysisSummary: result.analysisSummary || fallback.analysisSummary, confidence: result.confidence || fallback.confidence, changeKinds: result.changeKinds?.length ? result.changeKinds : fallback.changeKinds } }分析摘要生成逻辑buildAnalysisSummary()function buildAnalysisSummary(insights: AstChangeInsight[]): PickSmartCommitAnalysisResult, analysisSummary | confidence | changeKinds { const changeKinds [...new Set(insights.flatMap(i i.changeKinds))].slice(0, 6) const confidence mergeConfidence(insights.map(i i.confidence)) const files insights.length return { analysisSummary: files 0 ? 识别到 ${files} 个文件的 ${changeKinds.slice(0, 3).join(、) || 代码} 变更 : 已完成智能分组分析, confidence, changeKinds } }改造后智能分组从「纯 AI 黑盒输出」升级为「AI 智能判断 本地 AST 精准校正兜底」的双向增强模式。四、完善分组上下文高质量提交信息的基础4.1buildGroupContext()封装完整意图数据包生成提交信息时旧版只向 AI 传递 Diff 文本。新版通过buildGroupContext()为每个分组封装完整的语义数据包function buildGroupContext(group: CommitIntentGroup): string { const scope group.scope ? \n作用域${group.scope} : const confidence group.confidence ? \n置信度${group.confidence} : const hunkText group.hunks?.length ? \n关联 Hunk\n${group.hunks.map(h - ${h}).join(\n)} : return 提交意图${group.type}${scope}${confidence}\n分组摘要${group.summary}\n分组文件\n${ group.files.map(f - ${f}).join(\n) }${hunkText} }输出示例提交意图feat 作用域smart-commit 置信度high 分组摘要新增 AST 上下文接入与 Hunk 精准暂存能力 分组文件 - src/services/smartCommitService.ts - src/utils/astChangeAnalyzer.ts 关联 Hunk - src/services/smartCommitService.ts -82,12 82,28 AI 基于这份完整意图包生成提交信息产出内容更贴合 Conventional Commits 规范精准匹配实际改动场景。4.2 Provider 层的提交信息格式化与校验smartCommitProvider.ts中的格式化函数对 AI 输出做严格清洗避免不合规格式进入提交记录function sanitizeType(type: string | undefined): string { const normalized type?.trim().toLowerCase() return normalized COMMIT_TYPES.has(normalized) ? normalized : chore } function sanitizeScope(scope: string | undefined): string | undefined { const normalized scope?.trim().toLowerCase().replace(/[^a-z0-9-]/g, -) if (!normalized || !SCOPE_PATTERN.test(normalized)) return undefined return normalized } function sanitizeSubject(subject: string | undefined): string { const normalized limitText(subject || 更新代码变更, MAX_COMMIT_SUBJECT_LENGTH) return normalized.replace(/[。.!]$/g, ) || 更新代码变更 }校验规则一览字段规则type必须在 COMMIT_TYPES 白名单内否则降级为chorescope只允许小写字母、数字、连字符不合规则丢弃subject截断至 72 字符移除末尾标点body去除首尾空白为空则省略五、Hunk 级精准暂存最具工程价值的核心突破5.1 传统文件级暂存的痛点日常开发中一个文件往往包含多次独立改动——新增功能、修复 bug、代码重构混在一起。传统git add file只能整体暂存必须手动用git add -p拆分操作繁琐且极易出错。5.2stageGroupAndGenerateMessage()按意图分组暂存新版核心函数stageGroupAndGenerateMessage()实现了按意图分组精准暂存export async function stageGroupAndGenerateMessage( group: CommitIntentGroup ): PromiseAgentResultSmartCommitGroupWorkflowResult { return withOperation(commit.generateMessage, async () { const files normalizeFiles(group.files) // Step 1获取该分组文件的 workdir diff const workdirGroupDiff (await Promise.all( files.map(async f (await invokeGit(diff.workdirRaw, { path: f })).diff) )).filter(Boolean).join(\n) // Step 2优先尝试 Hunk 级 patch 暂存 const appliedPatch workdirGroupDiff.trim() ? await applyGroupHunks(group, workdirGroupDiff) : null // Step 3Hunk 匹配失败时回退到文件级暂存兜底 if (!appliedPatch) { for (const f of files) { await invokeGit(staging.add, { path: f }) } } await useGitStatusStore.getState().refreshStatus() // Step 4基于实际暂存的 diff 生成提交信息 const stagedGroupDiff (await Promise.all( files.map(async f (await invokeGit(diff.stagedRaw, { path: f })).diff) )).filter(Boolean).join(\n) const groupDiff appliedPatch || stagedGroupDiff const astContentMap await buildAstContentMap(groupDiff) const result await smartCommitProvider.generateMessage({ diff: groupDiff, stagedFileCount: files.length, groupContext: buildGroupContext(group), astContext: renderAstContext(files, groupDiff, astContentMap) }) // ... }) }5.3 Hunk 匹配与 Patch 应用applyGroupHunks()从完整 Diff 中精准提取分组对应的 Hunk patch只暂存目标变更片段function matchGroupHunks(group: CommitIntentGroup, diff: string): RawHunk[] { const files new Set(normalizeFiles(group.files)) const wantedHunks new Set(group.hunks || []) return parseRawDiffHunks(diff).filter(hunk { if (!files.has(hunk.filePath)) return false if (wantedHunks.size 0) return true // 优先精确匹配 filePathhunkHeader 格式的 Hunk ID return wantedHunks.has(${hunk.filePath}${hunk.header}) || wantedHunks.has(hunk.header) }) } async function applyGroupHunks(group: CommitIntentGroup, diff: string): Promisestring | null { const hunks matchGroupHunks(group, diff) if (hunks.length 0) return null const patch hunks.map(h h.patch).join(\n) await invokeGit(staging.applyPatch, { patch }) return patch }Hunk ID 格式定义在CommitIntentGroup.hunks字段中格式为${filePath}${hunkHeader}例如src/services/smartCommitService.ts -82,12 82,28 AI 在分组时可以为每组分配关联的 Hunk ID 列表实现AI 决策 精准执行的完整闭环。5.4 容错兜底策略场景处理方式Hunk 匹配成功应用精准 patch只暂存目标变更Hunk 匹配失败patch 为空回退到git add file文件级暂存暂存后 diff 仍为空返回错误不继续生成信息三层容错保证功能 100% 可用不会因 Hunk 匹配失败阻塞正常工作流。六、Provider 层的降级闭环设计smartCommitProvider.ts中的LlmSmartCommitProvider实现了AI 优先、本地兜底的完整闭环async analyzeChanges(input: SmartCommitAnalyzeInput): PromiseAgentResultSmartCommitAnalysisResult { const config getCurrentLlmConfig() // 未配置 API Key → 直接走本地 fallback if (!hasUsableLlmConfig(config)) { const fallback fallbackCommitGroups(input.files) as AgentResultSmartCommitAnalysisResult return { ...fallback, error: toFallbackReason(config) } } // 调用 AI const result await runAgent(config, { ... }, parseStructured) if (result.success result.data) { const sanitized sanitizeGroups(result.data, input.files) if (sanitized) return { ...result, data: sanitized } } // AI 失败 / 数据校验不通过 → 本地 fallback const fallback fallbackCommitGroups(input.files) as AgentResultSmartCommitAnalysisResult return { ...fallback, error: result.error || toFallbackReason(config), rawOutput: result.rawOutput } }降级层次AI 服务已配置 调用成功 数据校验通过 → 返回 AI 分组结果经 AST 校正 AI 服务已配置 调用失败 / 数据异常 → 本地 fallback 分组 记录错误原因 AI 服务未配置 → 本地 fallback 分组 提示用户配置无论哪种情况用户都能得到一个可用的分组结果功能不会因 AI 服务问题而中断。七、分组数据安全校验sanitizeGroups()AI 输出的分组数据在进入业务逻辑前经过严格的安全过滤function sanitizeGroups( result: SmartCommitAnalysisResult, inputFiles: string[] ): SmartCommitAnalysisResult | null { const allowedFiles new Set(inputFiles) const usedFiles new Setstring() const groups result.groups .map(group ({ type: sanitizeType(group.type), scope: sanitizeScope(group.scope), summary: limitText(group.summary, MAX_GROUP_SUMMARY_LENGTH), confidence: group.confidence, // 过滤掉不在变更文件列表中的幻觉文件并去重 files: group.files .map(f f.trim()) .filter(f allowedFiles.has(f) !usedFiles.has(f)) })) .filter(group { group.files.forEach(f usedFiles.add(f)) return group.summary group.files.length 0 }) .slice(0, 5) // 最多 5 个分组避免过碎 return groups.length 0 ? { groups, ... } : null }这里有一个重要细节过滤 AI 幻觉文件。AI 有时会在分组中编造不存在于实际变更列表的文件路径allowedFiles过滤器可完全杜绝这类问题。八、完整链路数据流本次全链路优化的完整数据流用户触发智能提交分析 ↓ invokeGit(diff.workdirRaw) / invokeGit(diff.stagedRaw) ↓ ↓ buildAstContentMap(diff) getChangedFiles() ↓ analyzeAstChanges() → AstChangeInsight[] renderAstContext() → AI 可用文本上下文 ↓ smartCommitProvider.analyzeChanges({ diff, files, astContext }) ↓ runAgent()AI 分组 ↓ sanitizeGroups()安全校验 ↓ enrichGroupsWithAst()AST 置信度回填 ↓ SmartCommitAnalysisResult分组 摘要 置信度 变更类型 ↓ 用户选择分组 → stageGroupAndGenerateMessage() ↓ applyGroupHunks()Hunk 精准暂存 ← 失败 → git add file回退 ↓ smartCommitProvider.generateMessage({ diff, groupContext, astContext }) ↓ formatCommitMessage()格式化校验 ↓ 最终提交信息填入输入框九、优化价值复盘核心能力对比能力优化前优化后AI 分析上下文仅 Diff 文本Diff AST 符号 变更类型 Hunk 摘要智能分组依据纯 AI 黑盒AI 分组 AST 置信度校正分组兜底机制无AI 失败时本地 fallback 兜底暂存粒度文件级Hunk 级失败回退文件级提交信息上下文仅 Diff完整意图包意图 作用域 Hunk AST输出格式校验无type / scope / subject 严格校验整体价值本次全链路优化的本质是将智能提交从依赖文本匹配的粗粒度工具升级为基于 AST Hunk 语义分析的智能化开发辅助能力。系统不再只知道文件改了更能读懂代码结构改了什么、为何改动、可信度如何。从底层分析、智能分组、精准暂存到提交信息生成形成了完整的语义化智能链路让 AI 辅助开发真正落地到高频 Git 操作场景。系列导读上篇[从文本到语义AST Hunk 重构 IntelliGit 智能提交底层分析能力]下篇本文全链路落地——AST 赋能智能分组、Hunk 精准暂存、提交信息生成