从代码 diff 里提取字段映射的实操步骤:旧版到新版接口迁移不再靠肉眼比对

拿到一份几百行的接口迁移 diff,最靠谱的做法不是逐行读,而是先用 git diff -U0 把改动压缩成“纯增删行”视图,再按“删除的旧字段名 → 新增的新字段名”配对,最后用脚本或 AI 把配对结果整理成映射表。我上周刚用这套流程处理了一个 47 个字段的支付接口迁移,从拉 diff 到产出可评审的映射文档花了不到 40 分钟。

第一步:用 -U0 把 diff 压成字段级视图

常规 git diff 默认带 3 行上下文,字段一多就淹没在 unchanged 的代码里。-U0 只输出真正变动的行,相当于把 diff 从“段落对比”降维成“字段清单”。

git diff old-branch new-branch -- src/api/payment.ts -U0

输出会变成这样:

-  amount: body.total_amount,
-  currency: body.currency_code,
-  created_at: body.create_time,
+  amount: body.amount,
+  currency: body.currency,
+  createdAt: body.createdAt,

这里能直接看出三组映射:total_amount → amountcurrency_code → currencycreate_time → createdAt。但如果字段顺序被打乱、或者中间插了逻辑改动,纯靠肉眼配对还是会漏。所以下一步要把 diff 结果结构化。

第二步:用脚本提取“删除行”和“新增行”里的字段名

我习惯用 awk 配合 grep 把 diff 输出切成两个文件:一个存所有 - 开头的行,一个存所有 + 开头的行。然后从每行里抠出字段名——通常是被赋值对象(: 左边)或者对象 key(引号内)。

git diff old-branch new-branch -- src/api/payment.ts -U0 \
  | grep '^-[^-]' | sed 's/^-//' > /tmp/old_fields.txt
git diff old-branch new-branch -- src/api/payment.ts -U0 \
  | grep '^+[^+]' | sed 's/^+//' > /tmp/new_fields.txt

这两个文件里的每一行基本就是一个字段的“旧版写法”和“新版写法”。接下来做字段名提取。如果项目里是 TypeScript 对象映射,字段名一般在行首或 : 左侧;如果是 JSON / 模板字符串,可能需要针对引号做正则。

# 提取冒号左侧的字段名(去掉空格和注释)
awk -F: '{print $1}' /tmp/old_fields.txt | sed 's/[[:space:]]//g' | sort -u > /tmp/old_keys.txt
awk -F: '{print $1}' /tmp/new_fields.txt | sed 's/[[:space:]]//g' | sort -u > /tmp/new_keys.txt

这一步产出的是“旧版字段集合”和“新版字段集合”。但它们之间还没有对应关系——字段名可能完全一样(比如 amount 没变),也可能改了命名风格(snake_case → camelCase),还可能拆成了嵌套结构。所以第三步要做配对。

第三步:按三种情况配对字段映射

情况一:同名保留。comm -12 找出两个集合的交集,这些字段不用迁移,直接从待处理清单里划掉。

comm -12 /tmp/old_keys.txt /tmp/new_keys.txt > /tmp/unchanged_keys.txt

情况二:纯命名风格变化。 把 snake_case 转成 camelCase 后如果能在新字段集合里找到,就是直接映射。这一步可以用 sed 做转换再比对:

sed 's/_\([a-z]\)/\U\1/g' /tmp/old_keys.txt | sort > /tmp/old_keys_camel.txt
comm -12 /tmp/old_keys_camel.txt /tmp/new_keys.txt > /tmp/renamed_keys.txt

拿上面的例子来说,total_amount 转成 totalAmount 后在新字段集合里找不到,说明它不是简单改名。但 currency_code → currencyCode 如果新集合里有 currencyCode,就能直接判定为改名映射。实际操作中我会先跑这一层,把能自动配对的全部清掉。

情况三:语义等价但命名不同。 这是最花时间的部分,也是 AI 真正能帮上忙的地方。把剩余的旧字段和新字段分别喂给 LLM,让它根据字段名语义、类型、上下文做匹配。我用的 prompt 大概是这样:

以下是旧版接口字段列表和新版接口字段列表,请根据语义和常见命名习惯做字段映射。
对每个旧字段,给出最可能对应的新字段;如果找不到对应,标注为 DELETED。
对每个新字段,如果没有任何旧字段对应,标注为 ADDED。

旧字段:
total_amount
currency_code
create_time
payer_info
...

新字段:
amount
currency
createdAt
payer
...

GPT-4o 和 Claude 3.5 在这类任务上表现都不错,但要注意两点:一是必须把字段类型也带上,比如 total_amount: string vs amount: number 这种类型变化会影响判断;二是让模型输出 JSON 而不是自然语言,方便后续合并进文档。

第四步:把映射结果落成可评审的表格

最后我一般生成一个 Markdown 表格,包含旧字段、新字段、变化类型(保留 / 改名 / 语义映射 / 删除 / 新增)、以及 diff 里的行号引用。行号用 git diff --unified=0 输出里的 @@ 定位,手动补充。

| 旧字段 | 新字段 | 变化类型 | diff 位置 |
|--------|--------|----------|-----------|
| total_amount | amount | 语义映射 | payment.ts:42 |
| currency_code | currency | 语义映射 | payment.ts:43 |
| create_time | createdAt | 改名 | payment.ts:44 |
| payer_info | payer | 语义映射 | payment.ts:45 |
| risk_level | — | 删除 | payment.ts:46 |
| — | idempotencyKey | 新增 | payment.ts:47 |

这个表格就是迁移指南的核心。后续写文档时,每个映射旁边再补一句行为差异说明(比如 total_amount 从字符串变成数字、单位从分改成元),就足够让下游开发直接动手改了。

常见问题

为什么不用 IDE 的对比视图直接看?

IDE 对比视图适合 review 单文件改动,但字段映射要跨多个文件、多个 commit 汇总时,命令行 + 脚本能把结果沉淀成文本文件,方便喂给 AI 或 diff 工具做二次处理。而且 -U0 的纯增删视图在 IDE 里不好调出来。

字段嵌套在对象深处怎么提取?

jq 处理 JSON 格式的接口定义,或者对 TypeScript 代码先用 typescript 编译器 API 把对象字面量转成 AST,再遍历 key。如果只是临时迁移,手动把嵌套结构拍平后再跑上面的流程也行。

AI 配对的准确率能到多少?

纯字段名配对(不带类型和注释)在 snake_case → camelCase 场景下准确率约 85%~90%,但涉及缩写、业务黑话(比如 mch_idmerchantId)时容易出错。建议把 diff 上下文(前后 2 行)也喂进去,准确率能提到 95% 以上。剩下不确定的映射人工确认,比从零开始快得多。