patch与diff的恩怨

时间:2022-09-17 13:26:47

一、概述

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格式有一定的模糊匹配能力,能减少原软件包升级带来的维护工作量。

patch与diff的恩怨的更多相关文章

  1. 内核诊断(二)-- patch 和diff

    patch文件结构 生成patch文件 --diff命令 patch 使用 -- patch命令 3.1 打path 3.1撤销patch 使用举例 4.1 基本命令使用 4.2 内核打补丁 1. p ...

  2. GIT打补丁 - patch和diff应用

    一. 准备工作: [root@guangzhou gittest]# git br * master [root@guangzhou gittest]# git chk -b patch-test1 ...

  3. diff生成补丁与patch打补丁

    1.使用diff生成补丁: diff是Linux下的文件比较命令,参数这里就不说了,直接man一下就行了,不仅可以比较文件,也可以比较两个目录,并且可以将不同之处生成补丁文件,其实就是一种打补丁的命令 ...

  4. git diff 生成patch&comma; git apply patch 打补丁方法说明,以及分支管理的简单操作。

    git diff 简易操作说明 先git log 查看commit ID, 记录你想要打的补丁的ID 比如说: git log commit 4ff35d800fa62123a28b7bda2a04e ...

  5. svn&sol;git的diff、patch

    svn/git的diff.patch 前几天,正当我突突的写代码,企业微信嘀嘀一声响”在不,过来帮我看个bug”.本人一向助人为乐,高兴的冲了过去,然后就开始了一段长达1分钟的问题描述.很明显,此同学 ...

  6. diff&comma; cmp&comma; patch

    diff 以行为单位比较两个文件之间的差异,经常用来查看同一个文件的新旧版本的差异,通常用在文本文件的比较,可以使用重定向'>'制作补丁文档,通常以.patch结尾 \(diff [-bBi] ...

  7. Linux之旅&lpar;1&rpar;&colon; diff&comma; patch和quilt (下)

    Linux之旅(1): diff, patch和quilt (下) 2 quilt 我们自己的项目能够用cvs或svn管理所有代码.但有时我们要使用其它开发人员维护的项目.我们须要改动一些文件,但又不 ...

  8. Linux游&lpar;1&rpar;&colon; diff&comma; patch和quilt (下一个)

    Linux游(1): diff, patch和quilt (下一个) 2 quilt 我们自己的项目可以用cvs或svn管理所有代码.但有时我们要使用其它开发人员维护的项目.我们须要改动一些文件.但又 ...

  9. 查找修补文件差异diff、patch

    diff patch -p1 diff -Naur directory1 directory2

随机推荐

  1. Application Request Route实现IIS Server Farms集群负载详解

    序言 随着公司业务的发展,后台业务就变的越来越多,然而服务器的故障又像月经一样,时不时的汹涌而至,让我们防不胜防.那么后台的高可用,以及服务器的处理能力就要做一个横向扩展的方案,以使后台业务持续的稳定 ...

  2. 在javascrit中怎样来刷新页面

    a页面里iframe了个b页面,我想实现在b页面里一个按钮,一按就刷新a页面,也就是父页面,不是只刷新iframe里面的b页面 哦~ 请问b页面里的<input type="butto ...

  3. 深入理解JPEG图像格式Jphide隐写

    0x00 隐写原理 Jphide是基于最低有效位LSB的JPEG格式图像隐写算法,使用JPEG图像作为载体是因为相比其他图像格式更不容易发现隐藏信息,因为JPEG图像在DCT变换域上进行隐藏比空间域隐 ...

  4. ASP&period;NET MVC another entity of the same type already has the same primary key value

    ASP.NET MVC项目 Repository层中,Update.Delete总是失败 another entity of the same type already has the same pr ...

  5. linux把EDT时间修改为CST格式

    初始时间:2012年 09月 14日 星期五 18:15:33 EDT [root@test ~]# mv /etc/localtime /etc/localtime.bak [root@test ~ ...

  6. 【C&plus;&plus;11】30分钟了解C&plus;&plus;11新特性

    作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 什么是C++11 C++11是曾经被叫做C+ ...

  7. mysql 1449 : The user specified as a definer &lpar;&&num;39&semi;root&&num;39&semi;&commat;&&num;39&semi;&percnt;&&num;39&semi;&rpar; does not exist 解决方法

    权限问题,授权 给 root  所有sql 权限 mysql> grant all privileges on *.* to root@"%" identified by & ...

  8. 5个最优秀的Java和C&num;代码转换工具

    http://www.codeceo.com/article/5-java-csharp-convert-tools.html 毋庸置疑,Java是一门最受欢迎而且使用最广泛的编程语言,目前有超过9百 ...

  9. hive 中的Sort By、 Order By、Cluster By、Distribute By 区别

    Order by: order by 会对输入做全局排序,因此只有一个reducer(多个reducer无法保证全局有序)只有一个reducer,会导致当输入规模较大时,需要较长的计算时间.在hive ...

  10. PHP自学2——将用户提交表单存储到外部普通文件中

    在上一节中我们已经实现了将用户的订单信息提交到服务器端,然后服务器端将提交信息返回并显示到页面上.这一节将把上一节用户的订单信息保存到外部的普通文件中(即.txt文本文件中). 本节代码将用户提交的订 ...