patch与diff的恩怨

一、概述

diff和patch是一对相辅相成的工具,在数学上来说,diff类似于对两个集合的差运算,patch类似于对两个集合的和运算。diff比较两个文件或文件集合的差异,并记录下来,生成一个diff文件,这也是我们常说的patch文件,即补丁文件。 patch能将diff文件运用于原来的两个集合之一,从而得到另一个集合。

举个例子来说文件A和文件B,经过diff之后生成了补丁文件C,那么这个过程相当于 |A –B| = C ,那么patch的过程就是B + C = A 或A - C = B(A + C = B或者B – C = A)。因此我们只要能得到A, B, C三个文件中的任何两个,就能用diff和patch这对工具生成另外一个文件。这就是diff和patch的妙处。

补丁Patch是天才程序员、Perl的发明者Larry Wall发明的,它应高效地交流程序源代码之需求而生,随着以Linux为代表的开发源代码运行的蓬勃发展,patch这个概念已经成为开放源代码发起者、贡献者和参与者的集体无意识的一部分。patch只包含了对源代码修改的部分,这对于开放源代码社区的协同开发模式具有重要意义,意味的软件新版本的发布和对软件的缺陷或改进可以以更小的文件发布,可以减少网络的传输量,方便软件维护者的管理工作。

patch文件有多种格式,不同平台上所支持的格式不尽相同,但最常见的是context格式和unified格式。context格式被广泛使用,是 patch文件格式事实上的标准。该格式包含了差异部分及其邻近的若干行,邻近就是所谓的上下文,这些行虽然没有变化,但它们出现在patch文件使得还原patch的程序具备更强的容错性。unified格式常见于GNU的patch实现,以patch形式发布的linux内核就使用了该格式。

此外,还有其它比较少用的格式,如Normal格式,并排对比模式(side-by-side),ed script和RCS script模式等。除了并排对比模式方便用户观察文件差异,其它格式大多数是为了兼容旧的patch格式。

二、工具的用法

1、   diff的用法

diff后面可以接两个文件名或两个目录名,例如:

diff [option] oldfile newfile

如果是一个目录名加一个文件名,那么只作用在那么个目录下的同名文件。例如:

diff /usr/xu mine

把目录/usr/xu 中名为mine的文件与当前目录中的mine文件进行比较。

Diff常用的option选项有:

l          -r 比较目录时,进行递归比较,用于产生整个代码树的patch

l          -u 输出统一格式,diff有"传统"和"统一"两种格式,现在一般使用"统一"格式,比较而言,统一格式生成的文件大,但包含了更多的信息,有利于阅读与定位

l          -N 表示如果文件不存在则将其等价为空文件,这个用于产生有文件增加或删除的patch

l          -a 补丁中包含二进制文件缺省时,diff向标准输出打印,所以一般都重定向到文件并以patch为后缀,也就是所谓的补丁文件。关于二进制文件的说明:binary文件可以原始方式存入patch文件。diff可以生成(加-a选项),patch也可以识别。如果觉得这样的patch文件太难看,解决方法之一是用uuencode处理该binary文件。

如果是两个目录的话,作用于该目录下的所有文件,不递归。如果我们希望递归执行,需要使用-r参数。不加任何参数生成的diff文件格式是一种简单的格式,这种格式只标出了不一样的行数和内容。我们需要一种更详细的格式,可以标识出不同之处的上下文环境,这样更有利于提高patch命令的识别能力。这个时候可以用-c开关。可以参考表1 diff的命令行选项和参数。

表1 diff的命令行选项和参数

选项          描述

-a         将所有的文件看作文本,既使文件看起来像是二进制的也不例外,并且进行逐行比较

-b         忽略块中空白数目的改变

-B         忽略插入或删除空行造成的改变

-c         产生"上下文"(context)格式的输出

-C[num]   产生"上下文"(context)格式的输出,显示块前后num行的内容,如果不指定num的值,则显示块前后3行的内容

-H         修改diff处理大文件的方式

-i         忽略大小写

-I         regexp 忽略插入或删除与正则表达式regexp匹配的行

-l         将输出结果通过pr命令处理加上页码

-p         显示出现块的C函数

-q         只报告文件是否不同;不输出差别

-r         比较目录时,进行递归比较,用于产生整个代码树的patch

-s         报告两个文件相同(默认的行为是不报告相同的文件)

-t         输出时tab扩展为空白

-u         产生"统一"(unified)格式的输出

-U[num]   产生"统一"(unified)格式的输出,显示块前后num行的内容,如果不指定num的值,则显示块前后3行的内容

-v         打印diff的版本号

-w         逐行比较时忽略空白

-W cols   如果产生并排格式的输出(参见-y),让输出的每一列有cols个字符宽

-x pattern  当比较目录时,忽略匹配模式pattern的任何文件和子目录

-y         产生并排格式的输出

举例:

void main() { printf("hello the world!\n"); }

void main() { printf("HELLO THE WORLD!\n"); }

使用以下命令生成补丁文件hello.patch:

$diff -u hello.c hello-new.c > hello.patch

diff可以对整个目录进行比较,生成补丁文件 例如有hello-1.0 和hello-1.1两个目录,其中hello-1.1为hello-1.0的更新命令:

$diff -ruNa hello-1.0 hello-1.1 > hello-1.1.patch

2、   Diff和patch结合使用

Patch用途:根据原文件和补丁文件生成目标文件。举例来说:

patch A C 就能得到B, 这一步叫做对A打上了B的补丁,这个补丁的名字叫做C。经过这一步,你的文件A就变成了文件B。如果你打完补丁之后想恢复到A怎么办呢?

patch -R B C 就可以重新还原到A了。所以不用担心会失去A的问题。

其实patch在具体使用的时候是不用指定原文件的,因为补丁文件中都已经记载了原文件的路径和名称。patch足够聪明可以认出原文件的路径和名称来。但是有时候会有点小问题:

比如一般对两个目录diff的时候可能已经包含了原目录的名字,但是我们打补丁的时候会进入到目录中再使用patch,这个时候就需要你告诉 patch命令怎么处理补丁文件中的路径。可以利用-pn开关,告诉patch命令忽略的路径分隔符的个数。举例如下:

A文件在 DIR_A下,修改后的B文件在DIR_B下,一般DIR_A和DIR_B在同一级目录DIR_P。我们为了对整个目录下的所有文件一次性diff,我们一般会到DIR_A和DIR_B的父目录DIR_P下执行以下命令:

diff -rc DIR_A DIR_B > C

这个时候补丁文件C中会记录了原始文件的路径为 DIR_A/A

现在另一个用户得到了A文件和C文件,其中A文件所在的目录也是DIR_A。一般,他会比较喜欢在DIR_A目录下面进行patch操作,它会执行

patch < C

但是这个时候patch分析C文件中的记录,认为原始文件是./DIR_A/A,但实际上是./A,此时patch会找不到原始文件。为了避免这种情况我们可以使用-p1参数如下

patch -p1 < C

此时,patch会忽略掉第1个”/”之前的内容,认为原始文件是 ./A,这样就正确了。

patch附带有一个很好的帮助,其中罗列了很多选项,但是99%的时间只要两个选项就能满足我们的需要:

patch -p1 < [patchfile]

patch -R < [patchfile] (used to undo a patch)

-p1选项代表patchfile中文件名左边目录的层数,顶层目录在不同的机器上有所不同。要使用这个选项,就要把你的patch放在要被打补丁的目录下,然后在这个目录中运行path -p1 < [patchfile]。

当然,被比较的文件或目录也可以从标准输入获得。如果用”-”表示file1或file2,则表示标准输入。

实例:

cat build.xml | diff -y -W 100 - build-1.10.xml

以每列100个字符的宽度并排对比输出build.xml和build-1.10.xml之间的差异到屏幕(标准输出)

diff -c web.xml web2.xml > web.xml.diff

产生web2.xml相对于web.xml的修改的上下文格式补丁文件到web.xml.diff中去

diff -crN src src_XFIRE > xfire-patch.diff

产生代码树src_XIRE相对于代码树src的上下文补丁文件到xfire-patch.diff中去,在src_XFIRE中新增的文件的内容也会被包含在补丁中。

打补丁可以使用命令行工具patch。它的基本用法是:

patch -pnum < 补丁文件

打补丁时将工作目录切换到需要打补丁源代码顶层目录(注意:运行patch所在的目录应该与用diff生成补丁的时候一致)。

3、     如何然后确定p后面的数字?

该数字表示需要去掉的补丁文件中目录的层数,该数字和补丁创建时候工作目录和代码目录的相对位置有关,一般补丁的作者会在补丁文档中指明。如果没有指定,可以通过观察补丁文件中列出的文件完整路径和代码树中该文件所在相对路径得出。

4、     如果patch失败

如果patch成功,缺省是不建备份文件的(注:FreeBSD下的patch工具缺省是保存备份),如果你需要,可以加上 b 开关。这样把修改前的文件以“原文件名.orig”的名字做备份。如果你喜欢其它后缀名,也可以用“b 后缀”来指定。

  如果patch失败,patch会把成功的patch行给patch上,同时(无条件)生成备份文件和一个.rej文件,并生成原文件的备份“原文件名.orig”,这里“-b”选项可以指定原文件的备份“原文件名.orig”的后缀名。.rej文件里是没有成功提交的patch行,需要手工patch上去。这种情况在原码升级的时候有可能会发生。

5、     对整个软件包做修改

通常,我们需要对整个软件包做修改,并生成一个patch文件,下面是典型的操作过程。这里就要用到前面介绍的几个命令行开关了:

  tar xzvf software.tar.gz  # 展开原始软件包,其目录为software

  cp -a software software-orig # 做个修改前的备份

  cd software

  [修改,测试……]

  cd ..

  diff -ruNa software-orig software > software-my.patch

现在我们就可以保存software-my.patch做为这次修改的结果,至于原始软件包,可以不必保存。等到下次需要再修改的时候,可以用 patch命令把这个补丁打进原始包,再继续工作。比如是在linux kernel 上做的工作,就不必每次保存几十兆修改后的源码了。

这是好处之一,好处之二是维护方便,由于unified patch格式有一定的模糊匹配能力,能减少原软件包升级带来的维护工作量。

时间: 2024-10-12 14:30:18

patch与diff的恩怨的相关文章

&lt;转载&gt; diff 和 patch 命令

本文转载自:http://blog.chinaunix.net/uid-23390992-id-3312321.html diff与patch命令 diff与patch命令真可谓是天作之合,命令中的黄金搭档.老师讲了之后其实自己不是很懂,因为上课的时候没有好好听,上课走神了.怎么办呢,肯定不能放着不管了,而这两个命令确实很重要,所以只能厚着脸皮课后死啃资料自己学习咯,学了之后才有开头第一句的感慨. diff和patch是一对工具,数学上说,diff是对两个集合的差运算,patch是对两个集合的和

diff与patch的简单用法

diff 比较相似文件的差异(以行为单位),还可以生成用以更新的"补丁文件" 及指定目录下文件名的不同 diff比较两个目录下文件名的不一致 [[email protected] tmp]# ls aaaa my.cnf [[email protected] tmp]# ls bbbb myy.cnf [[email protected] tmp]# ls aaaa  bbbb  my.cnf  myyy.cnf [[email protected] tmp]# diff aaaa b

Linux之旅(1): diff, patch和quilt (下)

Linux之旅(1): diff, patch和quilt (下) 2 quilt 我们自己的项目能够用cvs或svn管理所有代码.但有时我们要使用其它开发人员维护的项目.我们须要改动一些文件,但又不能直接向版本号管理工具提交代码.自己用版本号管理工具重建整个项目是不合适的,由于大多数代码都是别人维护的,比如Linux内核.我们仅仅是想管理好自己的补丁.这时能够使用quilt. 2.1 基本概念 quilt是一个帮助我们管理补丁的程序.quilt的命令格式相似于cvs: quilt 子命令 [參

Linux中使用diff生成补丁与用patch打补丁

1.使用diff生成补丁: diff是Linux下的文件比较命令,参数这里就不说了,直接man一下就行了,不仅可以比较文件,也可以比较两个目录,并且可以将不同之处生成补丁文件,其实就是一种打补丁的命令.使用方法如下: diff -rNu a b > diff.patch 其中,a是旧的目录或文件,b为最近修改后的目录或文件,生成的补丁文件为patch. 2.使用patch打补丁: patch命令的参数详见man手册,使用patch给另外一个a目录打补丁时如下: patch -p0 < diff

用Diff和Patch工具维护源码

在Unix系统下,维护源码版本可以使用很多方法,其中最常用的当然是大名鼎鼎的CVS,但实际上,简单的版本维护工作并没有必要使用复杂的CVS等专门的版本维护工具,Unix标配中的diff和patch工具就完全可以完成代码的简单备份和升级工作. diff以"行"为单位比较两个文本文件(也可以是目录比较),并将不同之处以某种格式输出到标准输出上:patch可以读入这种输出,并按照一定指令使源文件(目录)按照目标文件(目录)更新.Linux内核源码就是按照这种方式保持更新的,我们在www.ke

The Ten Minute Guide to diff and patch

Situation one: you are trying to compile a package from source and you discover that somebody has already done the work for you of modifying it slightly to compile on your system. They have made their work available as a "patch", but you're not

git apply、git am打补丁.diff 和 .patch【转】

本文转载自:https://www.jianshu.com/p/e5d801b936b6 前提: 生成patch: git format-patch -M master 生成指定patch,0163bed3bf59ae74c36cc5138b4c24f1556d8304是commit id,-1是指从当前id开始,向下提交次数,包含此次且计数从1开始. 也就是说,我想要打出0163bed3bf59ae74c36cc5138b4c24f1556d8304当前的patch,则: git format

Linux中的Diff和Patch

转自:https://www.cnblogs.com/cocowool/p/6409643.html 本文主要记录两个命令的学习情况:diff 和 patch.diff 和 patch 是一对工具,使用这对工具可以获取更新文件与历史文件的差异,并将更新应用到历史文件上.在数学上说,diff就是对两个集合的差运算,patch就是对两个集合的和运算. 简单的例子 使用这个例子来说明如何进行文件的对比和打补丁. 这里有两个文件 original.txt 和 updated.txt,如下: #inclu

diff和patch应用于提取差异和打补丁

对开源或第三方代码进行修改,我们需要将原始代码和修改部分分别上传.此时,需要先将修改的部分使用diff提取出来,记录为patch文件.其他人使用时,下载原始代码和patch文件,通过patch命令恢复为和你修改后同样的代码. 比如,将kernel原始代码的目录记为a,修改后的kernel代码目录记为b.提取差异为kernel.patch文件,可使用命令, diff -arNu a b > kernel.patch 注意,使用-a选项是为了将二进制文件也作为文本文件处理进行对比. 在原始代码中,需