核心答案:Diff(差异对比)找出两个文本的最小改动集:− 开头的是旧文件被删除的行,+ 开头的是新增的行,无符号是上下文。背后核心算法是 LCS(最长公共子序列)——保留不变的部分,剩下的就是差异。
Diff 是什么
Diff 输出两个文件的「最小编辑脚本」:把文件 A 变成文件 B,需要删哪些行、加哪些行。
应用:Git 版本对比、代码审查(PR)、配置变更审计、论文修订对比、法律合同改订对照。
+/- 号怎么读
```diff
@@ -1,3 +1,4 @@
共同的上文行
-旧文件中这行被删除了
+新文件中这行是新增的
+这一行也是新增的
共同的下文行
```
- @@ -1,3 +1,4 @@:hunk 头——旧文件从第 1 行起 3 行,新文件从第 1 行起 4 行
- 空格开头:上下文(两边都有,用于定位)
- 逐字符 diff 中:~~删除线~~ 与 高亮 标记词级差异
算法原理
LCS(最长公共子序列)思路:
- 找出两文件相同行的最长序列(保序但不必连续)
- 不在 LCS 中的旧文件行 = 删除
- 不在 LCS 中的新文件行 = 新增
动态规划:dp[i][j] = A 前 i 行与 B 前 j 行的 LCS 长度
- A[i] == B[j]:dp[i][j] = dp[i−1][j−1] + 1
- 不等:dp[i][j] = max(dp[i−1][j], dp[i][j−1])
Myers 算法:Git 实际使用,把问题转为「最短编辑距离」图搜索,时间复杂度 O((N+M)D),D 为差异行数——差异越小越快。
Git diff 实战
```bash
git diff # 工作区 vs 暂存区
git diff --cached # 暂存区 vs 上次提交
git diff main..dev # 两分支差异
git diff HEAD~3 # 当前 vs 3 个提交前
git log -p file.txt # 单文件逐版本 diff
```
读 PR 的技巧:先看 hunk 头(@@ 行)了解改动位置,再看 +/- 行;大改动配合 --stat 看文件级概览。
合并冲突基础
冲突产生:同一文件的同一区域,两个分支都改了——Git 无法自动取舍:
```
<<<<<<< HEAD
当前分支的内容
=======
合并进来的内容
>>>>>>> feature
```
解决步骤:
- 打开冲突文件,理解两边改动意图
- 手动编辑保留正确版本(或两者合并)
- 删除 <<<<<<<、=======、>>>>>>> 标记
- git add 标记解决,完成合并
预防:小步提交、频繁同步主干、同一文件的热点区域模块化拆分。
常见误区
- 「diff 显示的是新文件内容」:diff 显示的是变化指令,不是任何一边的完整文件
- 移动代码块 = 删除+新增:diff 无法识别「移动」,一块代码换位置显示为大段 − 和大段 +,属正常现象
- 空行/缩进改动无意义:空白变化同样产生 diff(代码审查噪音),可用 git diff -w 忽略空白
- 冲突标记直接提交:<<<<<<< 标记忘了删就提交,直接把语法错误带进仓库——合并后先编译/测试再提交
常见 diff 场景对照表
| 场景 | 命令/方式 | 输出特点 |
|---|---|---|
| 工作区 vs 暂存区 | git diff | 未暂存的改动 |
| 暂存区 vs 上次提交 | git diff --cached | 已暂存待提交 |
| 两分支对比 | git diff main..dev | 分支间全部差异 |
| 两文件对比 | diff a.txt b.txt | 行级差异 |
| 目录递归对比 | diff -r dir1 dir2 | 含新增/缺失文件 |
| 只看变更统计 | git diff --stat | 每文件增删行数 |
unified diff 格式速读表
| 标记 | 含义 |
|---|---|
| @@ -a,b +c,d @@ | 旧文件第 a 行起 b 行 → 新文件第 c 行起 d 行 |
| 行首 − | 旧文件有、新文件删掉的行 |
| 行首 + | 新文件新增的行 |
| 行首空格 | 上下文(两边都有) |
| --- / +++ | 旧/新文件名与版本 |
实例:@@ -10,7 +10,6 @@ 表示从第 10 行起,旧文件 7 行内容在新文件变成 6 行——即净删 1 行。代码审查时先看 @@ 头定位,再看 +/- 行确认改动意图。