Git range-diff 对照两轮提交:变基以后,哪些补丁真的改了
一个分支完成评审后,作者把它变基到新主线,又修改了一条提交。提交编号全部变了,普通的两端 diff 还会混入新主线的文件变化。评审者真正想知道的是:上一轮的每个补丁,在这一轮变成什么,哪些只是换了基础,哪些修改了内容。
git range-diff 接收两组提交范围,为旧、新补丁建立对应关系,再显示这些补丁之间的差异。它适合复查重新排列、变基或修订过的提交序列,不是把两个目录当前内容作一次简单比较。
保留旧范围,再建立新范围
下例在 Git 2.52.0 和 Bash 5.2.37 验证,仅操作自动清理的临时仓库。旧主题分支包含两个提交:增加 timeout 和增加 retry。主线另增 upstream.txt;主题分支变基后,把第二个提交中的 retry 从一改成二。
AI概念示意图:用抽象图形说明本文主题,不代表真实界面或运行结果。
#!/usr/bin/env bash set -euo pipefail export LC_ALL=C GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null lab=$(mktemp -d) trap 'rm -rf -- "$lab"' EXIT git -c init.templateDir= init -q -b main "$lab/repo" cd "$lab/repo" git config user.name Demo git config user.email demo@example.invalid git config commit.gpgSign false printf 'base\n' > readme.txt git add . git commit -qm base base=$(git rev-parse HEAD) git switch -qc topic printf 'timeout=10\n' > timeout.txt git add . git commit -qm 'Add timeout' old_first=$(git rev-parse HEAD) printf 'enabled=yes\nretry=1\nbackoff=linear\njitter=no\n' > retry.txt git add . git commit -qm 'Add retry' git branch review-old git switch -q main printf 'upstream\n' > upstream.txt git add . git commit -qm 'Advance main' git switch -q topic git rebase main printf 'enabled=yes\nretry=2\nbackoff=linear\njitter=no\n' > retry.txt git add retry.txt git commit --amend -qm 'Add retry' [[ $old_first != $(git rev-parse HEAD~1) ]] [[ $(git show review-old:retry.txt) == $'enabled=yes\nretry=1\nbackoff=linear\njitter=no' ]] [[ $(git show topic:retry.txt) == $'enabled=yes\nretry=2\nbackoff=linear\njitter=no' ]] [[ $(git rev-list --count main..topic) == 2 ]] [[ -f upstream.txt ]] if git cat-file -e review-old:upstream.txt 2>/dev/null; then exit 1; fi git --no-pager range-diff --no-color --creation-factor=90 "$base..review-old" main..topic printf 'verified: two topic commits; retry 1 -> 2\n'
范围的左端同样重要
第一个范围从原始 base 到 review-old,包含旧版的两个提交;第二个从新 main 到 topic,包含新版的两个提交。两边明确写出各自基础,可以避免把主线新增的提交也当成主题补丁。旧分支是实验刻意保留的参照点,不依赖易被后续操作改变的最近一次位置。
运行后会看到第一对提交标为等号,第二对标为感叹号。第一对补丁仍然相同,但因为父提交变了,对象编号也跟着改变;第二对除了编号变化,补丁内容也不同,下面的嵌套差异展示 retry 从一改到二。脚本还直接断言新旧文件内容,以免只靠颜色猜结果。
把对应标记当作评审导航
等号表示匹配的提交没有检测到相关变化,感叹号表示匹配后发现差异。只有旧侧的一项以小于号表示,只有新侧的一项以大于号表示。阅读时先找未对应项和有变化项,再查看具体提交,通常比重新审完所有已看过的补丁更省力。
对应关系是根据补丁等信息计算出来的。本例补丁很短,显式把 creation-factor 设为九十,提高把修改视作匹配提交的倾向;默认值六十可能把第二项显示为删除与新增。这不是永久保存的身份映射。一次提交被拆成很多份,或者几个提交合为一份时,匹配结果需要人工理解;非常大的重写也可能让你看到删除加新增,而不是理想中的一一对应。
它不能替代最终状态验证
脚本故意确认新版含有 upstream.txt、旧版没有。这个差别来自主线前进,不是第二轮主题提交新写的工作;因此没有出现在两组主题补丁的核心对照里。这正是 range-diff 有用的地方,也提醒你它不是完整最终文件树审计。
通过补丁评审之后,仍要检查新版主题分支与当前主线的最终差异,并运行项目测试。补丁看似没改,在新的基础上仍可能与其他代码发生交互;相同的几行增加内容,不保证运行时依赖、配置或调用顺序完全相同。
官方文档明确把这份输出定位为供人阅读的格式,不承诺跨版本文本稳定,也不应把它交给 git apply。示例没有用正则解析提交摘要来做自动化通过判定;可靠断言直接读取提交内容、提交数量和对象关系,range-diff 则留下给人解释差异。
实际评审前,先记住上一轮的头提交和对应基础,再让作者重写自己的分支。保存这些参照能让复查可重复;对已经由多人共同依赖的历史进行重写,则需要另行协调,不能因为存在一个好用的比较命令就忽略协作影响。


