如何阅读 diff 输出:理解补丁文件与变更
diff 输出是版本控制系统的通用语言。每位开发者都会经常遇到 diff 输出——无论是在代码审查时、解决合并冲突时,还是在应用来自开源贡献的补丁时。然而,许多开发者从未学会高效地阅读 diff 输出。
本指南讲解如何阅读最常见格式的 diff 输出、补丁文件如何工作,以及如何自信地解读变更。
什么是 diff 输出?
diff 输出是两文件或两组文件之间差异的结构化表示。diff 工具由 Douglas McIlroy 于 1970 年代在贝尔实验室(Bell Labs)创建,至今仍是 Git、Mercurial 以及大多数其他版本控制系统的基础。
当您运行 git diff 或任何 diff 命令时,输出会精确显示哪些内容发生了变化、在哪里变化以及如何变化——采用一种人类和机器都能读取的格式。
理解 unified diff 格式
unified diff 格式是当今使用最广泛的输出格式。它将变更分组为多个 hunk,每个 hunk 前面都有帮助您在文件中定位变更的上下文行。
Hunk 头剖析
每个 hunk 以如下所示的头部开始:
@@ -start,count +start,count @@
让我们来分解一下:
| 部分 | 含义 |
|------|---------|
| @@ | 标记 hunk 开始的分隔符 |
| -3,7 | 在原始文件中,此 hunk 从第 3 行开始,跨越 7 行 |
| +3,8 | 在新文件中,此 hunk 从第 3 行开始,跨越 8 行 |
| @@ | 结束分隔符 |
紧跟头部之后的上下文行(通常是一个函数名或附近的注释)有助于您识别该变更属于文件的哪个部分。
Hunk 中的行
hunk 内的每一行都以单个字符作为前缀:
context line (unchanged)
-added line (present in new file, absent in old)
+added line (present in new file, absent in old)
- 上下文行以空格开头。这些行在两个版本中完全相同,提供参考点。
- 删除行以减号(
-)开头。这些行存在于旧文件中,但已被删除。 - 新增行以加号(
+)开头。这些是变更文件中的新内容。
实际示例
下面是一个显示函数被修改的 diff:
@@ -5,7 +5,9 @@
def greet(name):
- print("Hello, " + name)
- print("Welcome!")
+ message = f"Hello, {name}!"
+ print(message)
+ print("We are glad to see you.")
+
if name == "admin":
print("You have admin privileges.")
阅读此 diff:
- 旧版本打印 "Hello, " 加上姓名,然后打印 "Welcome!"。
- 新版本构建了一个格式化消息字符串并打印它,增加了一行额外的问候语,并保持 admin 检查不变。
- 删除了两行(
-行),新增了三行(+行),空的+行保留了一个空行以增强可读性。
补丁文件:可应用的 diff
补丁文件就是一个保存到文件中的 diff——通常带有 .patch 或 .diff 扩展名。补丁文件作为一种可移植的方式来共享变更,而无需共享整个代码库。
创建补丁
使用 Git,从您最近的提交创建补丁:
git format-patch HEAD~1
或者从未提交的变更创建补丁:
git diff > my-changes.patch
应用补丁
将补丁应用到目标仓库:
git apply my-changes.patch
或者使用经典的 patch 命令:
patch -p1 < my-changes.patch
-p1 标志会去除文件路径中的第一个目录组件,从而解决补丁创建者与应用者之间目录结构的差异。
常见的 diff 输出格式
除了 unified diff 之外,您可能还会遇到以下格式:
普通 diff(Normal Diff)
原始 diff 命令的默认输出。变更以指令形式呈现,使用 a(追加,append)、c(更改,change)和 d(删除,delete)等命令来更改、添加或删除行。
3c3
< Hello world
---
> Hello beautiful world
上下文 diff(Context Diff)
一种较旧的格式,在每个变更周围提供三行上下文。它使用感叹号来表示变更的行。
并排 diff(Side-by-Side Diff)
这不是一种单文件格式——而是并排显示两列。这在图形化 diff 工具和在线 diff 检查器中很常见。
在代码审查中阅读 diff 输出
审查拉取请求(pull request)时,请关注以下要素:
- Hunk 头:它们告诉您哪些文件和函数受到影响。
- 新增行与删除行的比例:关键文件中的大量变更值得更密切的关注。
- 上下文行:它们向您展示周围的逻辑,帮助您评估变更是否正确。
- 仅空白字符的变更:许多 diff 工具会以不同的方式高亮显示这些变更,因为它们可能会使审查变得杂乱。
解读 diff 的最佳实践
- 从上到下、逐文件阅读 diff——这与变更被做出的顺序一致。
- 留意修改代码附近未变化的上下文行;它们揭示了新旧代码之间的结构关系。
- 使用支持 diff 视图语法高亮的工具——颜色让模式更容易被发现。
- 审查大型 diff 时,从测试文件开始,以理解作者意图变更的行为。
理解 diff 输出是协作开发的一项基础技能。一旦您能熟练阅读 diff,您就能以更大的信心驾驭代码审查、合并冲突和补丁文件。