最近,每当我撰写关于Git的内容时,总会琢磨,我的读者里究竟有多少人听说过我所写的这些东西。今天,我打算讲讲一款超酷的Git工具,叫range - diff
。我猜,知道有这么个命令存在的人,在你们当中应该寥寥无几。
要是你熟悉像《为什么我们中有些人喜欢进行差异对比代码审查》这类文章,那这篇文章就很适合你。接下来,我们将一同探究,如何能更轻松、更有效地开展那种类型的审查。
range - diff
命令是在2018年发布的Git 2.19版本中新增的,所以,读到这篇文章的各位,很可能都能使用这个命令。
找出两个分支版本之间的差异
Git 的 range-diff
命令本质上是对同一分支的两个略有不同的版本进行对比,归纳它们的变更差异。
假设你有一个小分支,其中包含两个提交:第一个提交添加了一个功能(但不小心带上了一些调试打印信息),第二个提交为该功能补充了文档。
你将这个分支作为拉取请求(Pull Request)提交后,收到了反馈,要求你移除那些误加的调试代码行。
处理反馈请求的方式主要有两种。
一种常见做法是直接修复问题,然后创建一个新提交,提交信息可能是“回应约翰的反馈”或“哎呀,失误了”,再将其推送到分支上。这种方式可以确保拉取请求的变更内容保持完整,清晰展示该分支最终引入的修改。
另一种更推荐的方法是直接修改原提交,并强制推送该分支。这样,分支仍然只有两个提交,但整体结构更加清晰合理。
这种方式的不足之处在于,GitHub 及其他代码托管平台无法很好地展示这种修改方式的变更情况。因为这些平台更偏向基于分支的协作方式,而不是基于 补丁系列 的工作流。因此,它们通常更鼓励通过追加提交的方式进行修改,而不是直接调整已有提交并强制推送优化后的提交序列。
不过,无论采用哪种方法,分支最终的统一差异(即真正被合并的变更内容)是相同的。
换句话说,无论是通过变基调整提交,还是通过追加新提交来响应反馈,最终的效果都是一致的。
当然,还有一个现实问题:使用原生 Git 执行这些操作并不算简单(但如果用 GitButler,整个过程会轻松许多 😉)。不过,如果你熟悉 修复提交和自动压缩 或 交互式变基,就能整理出更清晰、更有价值的提交历史。
有什么变化?
那么问题就变成了“有什么变化?”
如果你进行了交互式变基、压缩、修正或以其他方式修改了你的分支,而有人想知道差异对比——即你的分支的第一个版本与后续版本之间的差异,你该如何找到这个差异?
这就是 git range-diff
派上用场的地方。
有几种不同的方法可以运行这个命令,但我认为对于这个用例最简单的方法是 git range-diff <base> <head-a> <head-b>
。这将计算从两个分支头到指定基线的范围,从而给出你想比较的两个补丁范围。
让我们来看一个现实中的简单例子。
一个简单的例子
几天前,Mattias 开始开发一个小功能,让 GitButler 的提交消息可以更方便地插入和渲染 GIF。我基于这个真实案例,额外添加了一些简单的提交,以便更清楚地说明问题。
在我的示例中,最初推送了三个提交供审查:
❯ git log --oneline base..markdown-v1
c7793661e 添加提交消息编辑器的基本 Giphy 插件
ce0c47e2d 用 Markdown 渲染提交消息
bd0c89433 向 README 添加 GIF
- 第一个提交向 README 添加了内容
- 第二个提交实现了 Markdown 渲染器
- 第三个提交为编辑器引入了 Giphy 插件
Caleb 在审查过程中发现了一些小问题——例如,残留的调试信息。
此外,他还建议 README 的改动并不适合这个分支,应当移除。
现在轮到 Mattias 进行调整,以满足审查意见。他做了四项改动:
- 直接修改原始提交,移除了调试信息,而不是新建一个修正提交。
- 调整 第二个 提交的提交信息,使其更清晰。
- 重新排列两个主要提交的顺序。
- 额外添加了一个新提交,在我们的笑话文件中贡献了一则重要的笑话。
最终,他的新补丁系列变成了这样:
❯ git log --oneline base..markdown-v2
2415feb31 添加关于 GIF 的重要笑话
6e775149f 用 Markdown 渲染提交消息,以便我们可以显示 GIF
bd23c94d5 添加提交消息编辑器的基本 Giphy 插件
为了方便讲解,我在每个版本的头部创建了一个分支(
markdown-v1
和markdown-v2
)。但在实际操作中,变基会直接移动分支头部,因此你通常需要记住变基前的原始 SHA(或者在 reflog 中查找它)以便后续操作。
当然,你可以直接比较两个版本,以查看它们的变更内容,但这样只能看到它们的最终差异:
❯ git diff --stat markdown-v1 markdown-v2
README.md | 2 +-
apps/desktop/src/components/v3/GiphyPlugin.svelte | 3 ---
dad-jokes.md | 7 +++++++
3 files changed, 8 insertions(+), 4 deletions(-)
虽然这提供了一定的参考价值,但仍然缺少一些关键上下文。例如,任何新增的提交都会直接显示在变更列表中,但顺序可能被打乱(因文件路径按字母顺序排列)。此外,这种对比方式无法揭示提交消息或提交顺序是否发生了调整。
范围差异
现在,让我们在分支系列的第一个版本和修正后的版本之间运行 git range-diff
,看看它能为我们展示哪些信息。
这个输出可能不是最直观的格式,所以我们来仔细分析一下。
差异的差异
首先需要意识到的是,这种格式的输出方式可能会让人感到困惑——如果你一开始没看明白,那其实是很正常的。它并不是常见的标准化差异格式。
git range-diff
显示的是提交的元数据变更,再加上补丁本身的变更,也就是所谓的差异的差异。
由于它的输出包含 +/-
符号,一开始可能会让人误以为是在处理合并冲突后运行 git diff
时看到的合并差异格式,但实际上并不是。
你可以这样理解:假设你为每个提交生成一个补丁文件,然后对这些补丁文件本身进行比较,得到的结果就是 git range-diff
的输出。
以 -+
开头的行表示,第二个版本的补丁不再包含这些行。如果是 ++
,意味着第二个版本新增了这些行。如果只是 +
,则说明两个版本的补丁都添加了该行。
反之,你可能会看到 +-
,这表示第二个版本移除了第一版本没有移除的行,也就是“新增了一个删除操作”。
补丁的顺序变化
另一个 git range-diff
提供的重要信息是补丁的顺序和内容变更。如果你使用 -s
参数,它会抑制具体的代码差异,仅显示补丁的摘要信息,我们可以先来看看这个版本的输出。
从这里,我们可以观察到几个关键点:
- 第一行 显示,最初的第一个提交(“向 README 添加 GIF”)在第二个版本中被移除了,
<
符号表示它仅存在于第一版中。 - 最后一行 则相反,第二个版本新增了一个关于笑话的提交,在第一版中没有对应的补丁,
>
符号表示它仅存在于第二版中。 - 中间的两个提交 依然存在于新版本中,但都发生了改动,
!
符号表明它们的补丁内容被修改了。此外,它们的顺序也发生了变化——原来的顺序是1, 3, 2
,但在新版中2
和3
互换了位置。
在交互式终端中,Git 还会使用 ANSI 颜色标注,让这些变化更直观,例如红色表示删除,绿色表示新增。
如果某个补丁在两个版本中完全一致,那么 git range-diff
会在中间列显示 =
。在 Git 邮件列表中,就有一个非常好的例子:某个包含 8 个补丁 的系列,在 第 11 个版本 进行比较时,我们可以看到前 6 个补丁保持不变(尽管 SHA 发生了变化),而最后两个补丁自 第 10 版 以来被修改了。
与 v10 的范围差异:
1: a4a5aefa3e = 1: 814c53b402 git-compat-util: 添加 strtoul_ul()
2: c67e79804e = 2: 04f41100c4 cat-file: 添加变量声明
3: 7f0b824714 = 3: 3af67e6648 t1006: 拆分测试工具函数
4: 0d22d6af6e = 4: cb1088e436 fetch-pack: 重构数据包写入
5: 34c34c7464 = 5: 614daac4bb fetch-pack: 移动获取初始化
6: 54dd237c45 = 6: 4bc403fa2c serve: 广告对象信息功能
7: 90a3d987d5 ! 7: adae08d5a8 transport: 添加客户端支持
(差异对比...)
8: 9d932c2cb2 ! 8: 975d39cb6a cat-file: 添加远程对象信息
(差异对比...)
上面是 Eric 最近的远程 cat-file
补丁系列的 v11
版本与 v10
版本的简要对比。从中可以清晰地看到,前 6 个补丁保持一致,而 7
和 8
发生了修改。
通过 git range-diff
,我们可以深入了解不同版本的补丁系列之间的具体变更,不仅能看到代码的最终差异,还能追踪提交顺序、内容调整,以及哪些提交被新增、删除或修改,从而更高效地管理代码历史。
元数据
最后需要注意的是,range-diff
不仅展示代码变更,还会显示提交元数据的变化。
在这个例子中,Mattias 修改了其中一个补丁的提交信息,增加了“so we can show gifs”,让表达更加清晰。
此外,它还能显示作者信息是否被更改,或某些提交是否添加或修改了 Git 注释。
实际上,
range-diff
的工作方式是先运行:git log --no-color -p --reverse --date-order --decorate=no \ --no-prefix --submodule=short --output-indicator-new=> \ --output-indicator-old=< --output-indicator-context=# \ --no-abbrev-commit --pretty=medium --show-notes-by-default
然后解析该命令的输出,获取需要对比的信息,并在处理后的缓冲区上进行差异分析。
以防你感兴趣。😂
我们的原始例子
回到最初的示例,假设我们在第二个版本中从第一个补丁中移除了调试信息,使用 range-diff
可能会得到这样的输出:
现实应用
也许你会好奇,range-diff
在实际开发中是否真的有用?答案是肯定的。(嗯,“某种程度上” 😉)
在 Git 邮件列表中,这个工具被频繁使用。补丁系列通常会提交到列表中,经过反馈后进行修改,并重新提交为 v2
、v3
等版本。此时,range-diff
提供了一种标准化的方式,帮助大家快速理解不同版本之间的变化。
例如,这里是我提交到 Git 邮件列表的自动纠正补丁,它源自我们之前的博客文章。
虽然完整的补丁内容通常会附在邮件的结尾,但 range-diff
允许我们在邮件正文中直接提供版本之间的变化摘要。例如,这个对比清楚地展示了第四个版本和第三个版本之间的差异,方便已经审阅过前一版的开发者快速了解更新内容。
在 Git 邮件列表中,你可以找到许多类似的补丁系列对比示例。
总结
希望这次对 range-diff
的深入解析对你有所帮助!
如果你已经被这个工具和差异对比工作流所吸引,但对交互式变基、修正、压缩等 Git 操作还不太熟悉,别担心,这里有两个不错的学习方式:
- GitButler(毫不羞愧地推荐 😆)—— 通过拖放方式轻松完成大部分 Git 操作。
- 原生 Git 学习—— 如果你想深入掌握 Git 命令,可以试试 Bits and Booze 交互式变基课程,它是一个有趣的进阶方式。😄
Happy coding! 🚀
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件举报,一经查实,本站将立刻删除。
文章由技术书栈整理,本文链接:https://study.disign.me/article/202510/3.interdiff-review-with-git-range-diff.md
发布时间: 2025-03-03