补丁 (patch) 的制作与应用
补丁(patch)的制作与应用
如果修改了开源代码,为了方便分享(如提交 Bug)或自己留存使用,通常需要制作一个补丁(Patch)。在从源码安装软件时,也可能需要应用他人制作的补丁。本文介绍如何制作和应用补丁。
提示:本文目前的多数内容是对《Linux 下 patch 的制作和应用》的修正、整理与重新排版。
命令简介
制作和应用补丁主要使用 diff
和 patch
两个命令。
diff
diff
可以比较两个对象,并记录它们的区别。制作补丁时的一般用法和常见选项为:
1 | diff [选项] 源文件(夹) 目的文件(夹) |
常用选项:
-r
:递归。设置后,diff
会将两个不同版本源代码目录中的所有对应文件进行比较,包括子目录文件。-N
:确保补丁文件能正确处理已创建或删除的文件。-u
:输出每个修改前后的 3 行,也可以用-u5
等指定输出更多上下文。-E
、-b
、-w
、-B
、--strip-trailing-cr
:忽略各种空白,具体可参见文档,按需选用。
patch
patch
的作用是将 diff
记录的结果(即补丁)应用到相应文件(夹)上。最常见的用法为:
1 | patch -pNUM < patchfile |
常用选项:
-pNUM
:忽略几层文件夹,随后详解。-R
:取消已打过的补丁。
为解释 -p
参数,需查看如下补丁文件片段:
1 | --- old/modules/pcitable Mon Sep 27 11:03:56 1999 |
如果使用参数 -p0
,表示从当前目录找一个名为 old
的文件夹,再在其下寻找 modules/pcitable
文件来执行 patch
操作。而使用参数 -p1
,表示忽略第一层目录(即不管 old
),从当前目录寻找 modules
文件夹,再在其下找 pcitable
。
应用
利用以上命令,处理单个文件补丁的方法:
1 | # 产生补丁 |
对整个文件夹打补丁的情况:
1 | # 产生补丁 |
另外,使用版本控制工具时,可以直接用 svn diff
或 git diff
生成补丁文件。
值得一提的是,由于应用补丁时的目标代码和生成补丁时的代码未必相同,打补丁操作可能失败。补丁失败的文件会以 .rej
结尾,以下命令可以找出所有 .rej
文件:
1 | find . -name '*.rej' |
补丁文件构成
补丁文件里存储了哪些信息呢?看看这个例子:
1 | --- test0 2006-08-18 09:12:01.000000000 +0800 |
补丁头
补丁头是分别由 ---
和 +++
开头的两行,用来表示要打补丁的文件。---
开头表示旧文件,+++
开头表示新文件。一个补丁文件中可能包含以 ---
和 +++
开头的多个节,每一节用于打一个补丁。
块
块是补丁中要修改的地方,通常由一部分无需修改的内容开始和结束,用来表示要修改的位置。它们通常以 @@
开始,结束于另一个块的开始或新的补丁头。-
后的数字代表该块在旧文件的行号,修改前该块总行数;+
后的数字代表该块在新文件的行号,修改后该块总行数。
块的缩进
块会缩进一列,用于表示该行是要增加还是要删除的:
+
号表示该行是要增加的。-
号表示该行是要删除的。- 没有
+
号或-
号表示该行无需修改。
实例分析
单文件补丁
设当前目录有文件 test0
:
1 | 111111 |
和文件 test1
:
1 | 222222 |
使用 diff
创建补丁 test1.patch
:
1 | diff -uN test0 test1 > test1.patch |
因为是单个文件,故不需要 -r
选项。此命令得到如下补丁:
1 | --- test0 2006-08-18 09:12:01.000000000 +0800 |
要应用补丁,只需:
1 | patch -p0 < test1.patch |
此时 test0
就和 test1
一样了。
如果要取消补丁做出的更改,恢复旧版本:
1 | patch -R -p0 < test1.patch |
文件夹补丁
设有如下环境:
1 | prj0/ |
prj0/prj0name内容为如下三行:
1 | -------- |
prj1/prj1name内容为如下三行:
1 | -------- |
用 diff -uNr 创建补丁,
1 | diff -uNr prj0 prj1 > prj1.patch |
得到的patch文件为:
1 | diff -uNr prj0/prj0name prj1/prj0name |
如果要应用此补丁,则:
1 | $ ls |
此时可用ls看到打补丁后的结果:
1 | $ ls |
类似的,如果要回滚补丁操作:
1 | $ patch -R -p1 < ../prj1.patch |