0x7Fancy

diff和patch基本使用

Time: 2022.10.26
Tags: 开发

0x00 前言

Linux 系统下默认提供的 diff 和 patch 命令,能够帮助我们清晰的对比两个文件之间的差异,并进行补丁操作;常见的代码版本控制工具(git/svn)的基本思想就是 diff 和 patch。

当面向没有代码版本控制的项目开发时,我们可以使用 diff 和 patch 这一组命令进行代码管理:使用 diff 命令制作补丁文件,使用 patch 命令对代码打补丁或还原代码;除此之外,补丁文件可以较为方便的传递给他人,能够以这种方式参与开源项目或分享代码补丁。Linux 早期开发也采用这种方式。

本文简单介绍 diff 和 patch 命令的基本使用。

0x01 diff

diff 命令的基本操作:

diff <变动前的文件> <变动后的文件>

我们准备两个测试文件,file1 如下:

a b
b
c
d
e
f
g
h

file2 如下:

a b c
b
d
e
z
g
h

使用 diff 结果如下:

$ diff file1 file2 
1c1
< a b
---
> a b c
3d2
< c
6c5
< f
---
> z

由于历史原因,diff有三种格式:

1.正常格式
diff 默认输出即为正常格式,所上所示;正常格式中以「修改块」为单位:

1c1
< a b
---
> a b c

第一行用于说明变动位置,第一个数字 1 表示 file1 的第 1 行有变化,第二个字符 c 表示变化的模式为内容改变(change),其他模式还有增加(addition)和删除(deletion),最后一个数组 1 表示变动后为 file2 的第 1 行;

第二行分为两部分,< 字符表示要从 file1 中删除该行(也就是第 1 行),后面的 a b 表示删除的内容;

第三行 --- 时分割符,用于分割 file1 和 file2;

第四行和第二行类似,> 字符表示要在 file2 中增加改行,内容为 a b c

如果有多个修改块则在文件中顺序拼接。

2.上下文格式
diff 使用 --context 参数使用上下文格式输出:

$ diff -c file1 file2
*** file1	2022-10-26 12:00:00.000000000 +0800
--- file2	2022-10-26 12:01:00.000000000 +0800
***************
*** 1,8 ****
! a b
  b
- c
  d
  e
! f
  g
  h
--- 1,7 ----
! a b c
  b
  d
  e
! z
  g
  h

上下文格式可分为四个部分;

第一部分显示两个文件的基本情况,*** 表示变动前的文件,--- 表示变动后的文件:

*** file1	2022-10-26 12:00:00.000000000 +0800
--- file2	2022-10-26 12:01:00.000000000 +0800

第二部分是15个星号,将文件的基本情况与变动内容分割开:

***************

第三部分显示变动前的文件内容:

*** 1,8 ****
! a b
  b
- c
  d
  e
! f
  g
  h

上下文模式不仅显示发生变化的那一行,还显示前后几行的信息(默认显示 7 行),其中 *** 1,8 **** 表示展示内容从第 1 行开始连续 8 行;

随后展示的是文件内容的变动,第一列为变化的模式:

随后为该行的内容;

第四部分显示变动后的文件内容,其格式和第三部分一致:

--- 1,7 ----
! a b c
  b
  d
  e
! z
  g
  h

如果有多个修改块也在文件中顺序拼接,如果有多个文件的变动也可以写在一个文件中。

3.合并格式
上下文格式很好的解决了正常格式内容过于简单的问题,但是当两个文件相似度很高时,上下文格式就会出现大量的重复内容;合并格式则在上下文格式上做了优化。

diff 使用 --unified 参数使用上下文格式输出:

$ diff -u file1 file2
--- file1	2022-10-26 12:00:00.000000000 +0800
+++ file2	2022-10-26 12:01:00.000000000 +0800
@@ -1,8 +1,7 @@
-a b
+a b c
 b
-c
 d
 e
-f
+z
 g
 h

合并格式可分为三个部分;

第一部分显示两个文件的基本情况,--- 表示变动前的文件,+++ 表示变动后的文件:

--- file1	2022-10-26 12:00:00.000000000 +0800
+++ file2	2022-10-26 12:01:00.000000000 +0800

第二部分表示变动的位置,用 @@ 作为开始和结束标志:

@@ -1,8 +1,7 @@

-1,8 表示上下文是 file1 文件第 1 行开始的连续 8 行内容,+1,7 表示上下文是 file2 文件的第 1 行开始的连续 7 行内容;

第三部分表示具体的内容变化:

-a b
+a b c
 b
-c
 d
 e
-f
+z
 g
 h

其中第一列表示变化的模式:

随后为该行的内容

同样如果有多个修改块可在文件中顺序拼接,如果有多个文件的变动也可以写在一个文件中。

PS:合并模式是目前使用得最多的格式。

0x02 patch

使用 diff 命令将结果重定向到文件,就生成了补丁文件,patch 命令可以根据其内容进行打补丁(对于上文中不同的 diff 格式,patch 都能够正常工作,这里我们以合并格式为例):

$ diff -u file1 file2 > a.diff
$ cat a.diff
--- file1	2022-10-26 12:00:00.000000000 +0800
+++ file2	2022-10-26 12:01:00.000000000 +0800
@@ -1,8 +1,7 @@
-a b
+a b c
 b
-c
 d
 e
-f
+z
 g
 h

使用 patch 命令对 file1 进行打补丁操作如下:

$ patch file1 a.diff
patching file file1
$ cat file1
a b c
b
d
e
z
g
h

随后我们再使用 patch 命令取消 file1 的补丁内容:

$ patch -R file1 a.diff 
patching file file1
$ cat file1 
a b
b
c
d
e
f
g
h

在某些时候,代码版本控制出现错误或者用户误操作,可能导致 patch 打补丁失败,如果发生失败则会在当前目录下生成 [filename.orig][filename].rejorig 为文件的原始内容,rej 文件则发生错误的补丁内容,可根据 rej 文件分析错误原因。

除此之外,diff 和 patch 还可以对整个目录进行补丁操作。

0x03 References

https://www.ruanyifeng.com/blog/2012/08/how_to_read_diff.html
https://www.cnblogs.com/053179hu/p/14665795.html
http://linux-wiki.cn/wiki/zh-hans/%E8%A1%A5%E4%B8%81(patch)%E7%9A%84%E5%88%B6%E4%BD%9C%E4%B8%8E%E5%BA%94%E7%94%A8