diff和patch

对于开源源码修改过程中的必经阶段:对源码打补丁,总是不够精通,搜索了补丁的原理的详细过程,如下:


在移植或版本升级过程中,手动比对(用比对工具)转换是很费力的事情,特别是发生变化的文件非常多的情况下,“制作补丁、打补丁”可以简化这个过程。主要用到diff和patch。在这里不会把man在线文档上所有的选项都介绍一下,那样也没有必要。在99%的时间里,我们只会用到几个选项。

1、diff

--------------------

NAME

      diff - find differences between two files

SYNOPSIS

      diff [options] from-file to-file

      from_file to_file can be a directory.

--------------------

简单的说,diff的功能就是用来比较两个文件的不同,然后记录下来,也就是所谓的diff补丁。
语法格式:diff 【选项】 源文件(夹) 目的文件(夹),就是要给源文件(夹)打个补丁,使之变成目的文件(夹),术语也就是“升级”。
下面介绍三个最为常用选项:

-r 是一个递归选项,设置了这个选项,diff会将两个不同版本源代码目录中的所有对应文件全部都进行一次比较,包括子目录文件。

-N 选项确保补丁文件将正确地处理已经创建或删除文件的情况。

-u 选项以统一格式创建补丁文件,这种格式比缺省格式更紧凑些。

一般 -uN 是一直使用的参数,而 -r 如果是含子目录就使用,不含则不使用。

2、patch

------------------

NAME

       patch - apply a diff file to an original

SYNOPSIS

       patch [options] [originalfile[patchfile]]

       but usually just

       patch -pnum <patchfile

       带下划线的代表需要根据实际情况替换。比如 -pnum 实际使用时一般为 -p0, -p1。

------------------

简单的说,patch就是利用diff制作的补丁来实现源文件(夹)和目的文件(夹)的转换。这样说就意味着你可以由源文件(夹)――>目的文件(夹),也可以目的文件(夹)――>源文件(夹)。下面介绍几个最常用选项:

-pnum 是指查找patch文件中指定的文件时,忽略前num个目录,一个"/"为一层,详细内容下面解释。

-R 选项说明在补丁文件中的“新”文件和“旧”文件现在要调换过来了(实际上就是给新版本打补丁,让它变成老版本)

-E 选项说明如果发现了空文件,那么就删除它

3、patch文件的结构

(1)补丁头

补丁头是分别由---/+++开头的两行,用来表示要打补丁的文件。---开头表示旧文件,+++开头表示新文件。

一个补丁文件中可能包含以---/+++开头的很多节,每一节用来打一个补丁。所以在一个补丁文件中可以包含好多个补丁。

--- linux-2.6.25/arch/alpha/boot/misc.c 2010-05-06 01:56:42.565397700 -0700
+++ linux-2.6.29/arch/alpha/boot/misc.c 2010-05-06 00:51:06.000000000 -0700

(2)块

块是补丁中要修改的地方。它通常由一部分不用修改的东西开始和结束。他们只是用来表示要修改的位置。他们通常以@@开始,结束于另一个块的开始或者一个新的补丁头。

块会缩进一列,这一列是用来表示这一行是要增加还是要删除的。

+号表示这一行是要加上的。

-号表示这一行是要删除的。

没有加号也没有减号表示这里只是引用的而不需要修改。

4、-pnum

我们在生成补丁时,多是对目录进行操作,比如下面我对linux内核25和29两个版本arch目录下的文件做一个diff操作,生成的差异保存在arch.patch中:

diff -uNr linux-2.6.25/arch linux-2.6.29/arch > arch.patch

可以看到arch.patch开始位置的补丁头如下:

--- linux-2.6.25_android/arch/alpha/boot/misc.c 2010-05-06 01:56:42.565397700 -0700(黄色部分为打patch时命令会查找的文件名)
+++ linux-2.6.29_android/arch/alpha/boot/misc.c 2010-05-06 00:51:06.000000000 -0700

patch -p0 patch -p1 从从当前目录中查找arch/alpha/boot/misc.c,然后进行patch操作。为了能找到文件,当前目录应转到arch所属的目录下。
patch -p2 以此类推,patch的目录不限,可以指定patch的目录。

这个功能使用的情景是:打补丁时的目录结构/目录名跟现在要打补丁的目录结构/目录名不一样。
比如上面的例子,这个patch很可能是别人打的,打补丁时目录为 linux-2.6.25/arch,而本地的目录可能是linux_kernel_2625,
那打补丁时就可以进入linux_kernel_2625,用命令patch -p1


5、常用命令

(1)单个文件比较

diff –uN from-file to-file > to-file.patch //生成补丁 【因为单个文件,所以不需要-r选项。选项顺序没有关系,即可以是-uN,也可以是-Nu】

patch –p0 < to-file.patch //打补丁

patch –R –p0 < to-file.patch //去除补丁

(2)目录比较

diff –uNr from-dir to-dir > to-dir.patch //生成补丁

cd from-dir

patch –p1 < to-dir.patch //打补丁

patch –R –p1 < to-dir.patch //去除补丁

6、为内核打补丁

(1)首先是解压,因为发布的补丁文件都是使用gzip压缩的。

$gunzip ../setup-dir/ patch-2.4.21-rmk1.gz

(2)然后进入你的内核源代码目录

$cd linux-2.4.21

(3)打补丁

$patch –p1 < ../../setup-dir/patch-2.4.21-rmk1

打完补丁后,需要检查一下有没有拒绝执行的文件,即检查.rej文件的存在。使用命令:

$find -name *.rej

打补丁时候的常见错误
-------------------


当用patch命令来打一个补丁的时候,它试图以不同的方法来验证这个文件的完整性。

检查这个文件是一个有效的patch文件并且检查这些被改变周围的代码是不是和提供的
上下文相匹配。这些仅仅是patch所作的两个最基本的完整性检查。

如果patch遇到了一些看起来不正确的事情,那么它有两种选择。它可以拒绝应用这些改变并且
异常中断或者它试图找到一个方法来使patch命令仅仅做一些比较小的改变。


一个patch试图修正错误的例子就是:如果所有的上下文都匹配,被改变的行匹配,但是这些行的
行号不匹配。这是可能发生的,例如,如果patch在一个文件的中间做了一些改变,但是出于一些
原因在文件的开头处一些行被添加了进来或者被删除了。在这种情况下,一切看起来都很好,它只
是简单地上下移动一点,这时候patch通常会修正这些行号并且打上这个补丁。

任何时候,只要patch在打补丁的时候需要改动文件的一些内容,它就会告诉你说:
这个补丁打得有点儿混乱。你应该对这些改变保持一些警惕,因为即使补丁很可能被正确
地打上了,但是情况并不总是这样,有些情况下结果会是错误的。

当patch命令遇到一个变化而不能进行使用一种模糊的方法进行弥补的时候,它就会彻底地
放弃这个动作,并且留下来一个以.rej为扩展名的文件。你可以阅读这个文件来查看到底是
什么改变不能进行下去,从而在你愿意的情况下来手动修补它。


如果你的内核源代码上没有应用任何第三方的补丁,只是一些来自kernel.org的补丁,并且你打这些补丁
的顺序是正确的,而且你自己没有对这些源文件进行改动过,那么你应该就不会看到一个补丁
对这个文件的模糊的改变或者是一些拒绝消息。如果你确实看到了这些消息的话,那么将有非常高的
危险性,这说明或者是你的本地的源代码书或者是补丁文件在某些方面被玷污了。在这种情况下,你很可能
是应该重新下载这个补丁文件,如果事情仍然还是保持原样的话,那么我建议你去尝试从kernel.org上
下载一个完整的新的源代码树。

让我们来看一下补丁可能产生的更多信息。


如果patch命令停下来并且显示一个“File to patch”的提示符,那么这个时候patch命令找不到
要打补丁的文件。很可能的情况是你忘记指定-p1参数或者你处于一个错误的目录中了。更加不
常见的一种情况是,你会发现一些补丁需要使用-p0参数而不是-p1参数来打补丁(阅读这个补丁文件
应该能揭示出这些信息--如果是这样的话,这是一种创建补丁文件的人所犯的错误,但是不致命)

如果你得到信息“Hunk #2 succeeded at 1887 with fuzz 2 (offset 7 lines)”,或者是一个类似
的消息,那就意味着patch命令必须调整改变的位置(在这个例子中,它需要在它想打补丁的地方移动7行来
适应这个补丁)。结果得到的文件可能正确也可能不正确,这决定于这个文件与所期望的文件不相同的原因。
这常常发生于你所要打的patch产生于一个另外一个内核版本,这个内核版本和你要打的patch所基于的内核
版本不同。

如果你得到一个类似于“Hunk #3 FAILED at 2387”之类的消息,那么这就意味着不能正确地打上这个
补丁,并且patch程序也不能模糊地通过。这将产生一个导致patch失败的.rej文件并且产生一个.orig
文件把一些不能改变的原始内容显示给你。

如果你得到的信息是:“Reversed (or previously applied) patch detected!  Assume -R? [n]”,
那么patch检测到了这个补丁文件中包含的改变已经应用在了目标文件上。如果你确实已经在此之前打了
这个补丁并且重新打补丁遇到了错误,那么你可以简单地选择[n]o并且终止这次补丁动作。如果你之前
打了补丁并且想得到打补丁以前的版本,但是忘记了指定-R参数,那么你在这里可以回答[y]es来使用patch
为你恢复它。这也可能发生在补丁文件的创建者在创建补丁文件的时候倒置了源文件和目标目录的位置,
在这种情况下从patch中revert实际上是打上了这个补丁。

一个类似于“patch: **** unexpected end of file in patch”或者“patch
unexpectedly ends in middle of line”的消息意味着patch命令对你加入到它之中的
文件觉得没有意义。或者是你的下载被打断了,你试图打上一个没有压缩的patch文件,而事前
并没有解压缩它,或者是你使用的补丁文件在传输的某个地方被一个邮件客户端或者一个邮件
传输代理给损坏了。例如,通过把一个长行分成两行。通常情况下这些警告可以通过把这两个
被分开的行合并起来来解决。

就像我上面提到的那样,如果你打的是从kernel.org得来的补丁到一个正确的版本,
并且你没有修改过源代码树,这些错误将从来不会发生。因此如果你从kernel.org来的
补丁上得来了这些错误,那么你应该很可能认为或者是你的补丁文件或者是源代码树
损坏掉了。我建议你重新开始下载一个完整的内核树以及你想要打的补丁2.6.x内核
---------

这是Linus发布的基础稳定版本.发布的最高版本是最新的。


如果发现了冲突或者严重的瑕疵,那么在这个基础上,一个-stable的修正补丁就会被发布
出来(参见下面)。一旦一个新的2.6.x的基础内核发布出来,就可以得到一个测试版本的补丁
,这个补丁基于先前的2.6.x版本内核和这个新的内核。


为了应用一个从2.6.11到2.6.12的补丁,你最好按照下面来做(注意这些补丁不能应用于2.6.x.y的内核,
而是应用在2.6.x的基础内核---如果你需要从2.6.x.y到2.6.x+1,那么你首先需要卸载掉2.6.x.y的补丁)

下面是一些例子:


#从2.6.11到2.6.12
$ cd ~/linux-2.6.11            # 切换到内核源代码目录
$ patch -p1 < ../patch-2.6.12        # 应用2.6.12补丁
$ cd ..
$ mv linux-2.6.11 linux-2.6.12        # 重命名源代码目录

# moving from 2.6.11.1 to 2.6.12
$ cd ~/linux-2.6.11.1            # 切换到内核源代码目录
$ patch -p1 -R < ../patch-2.6.11.1    # 恢复出来2.6.11.1
                    # 源代码目录现在是2.6.11
$ patch -p1 < ../patch-2.6.12        # 应用新的2.6.12补丁
$ cd ..
$ mv linux-2.6.11.1 linux-2.6.12    # 重命名源代码目录


2.6.x.y内核
-----------

带有四位数字版本号的内核是-stable的内核。他们包含了对一个给定的2.6.x内核的一些安全
问题以及发现的重要的退化的修复。


对于那些想要最近的稳定内核并且对于测试开发中的试验性的版本没有兴趣的
用户来说,我们推荐这个分支。

如果没有可用的2.6.x.y内核,那么最高数字的2.6.x内核是目前的稳定内核。


注意:维护稳定内核的团队通常会做一些增量的补丁,就像是基于最近的主流版本发布
的补丁一样。但是在下面我仅仅说明了非增量的情况。那些增量式的版本可以在下面的ftp
处找到: ftp://ftp.kernel.org/pub/linux/kernel/v2.6/incr/.


这些补丁不是增量式的,意味着例如对于2.6.12.3补丁不能应用于2.6.12.2的内核源代码
上去,但是可以应用在2.6.12内核代码上。
因此,为了为了把2.6.12.3的补丁应用到你使用的2.6.12.2的内核源代码上,你不得不卸载掉
2.5.12.2补丁(因此你可以得到一个基础的2.6.12的内核源代码),并且应用新的2.6.12.3补丁。


下面是一个小例子:

$ cd ~/linux-2.6.12.2            # 切换到内核源代码目录
$ patch -p1 -R < ../patch-2.6.12.2    # 回归2.6.12.2补丁
$ patch -p1 < ../patch-2.6.12.3        # 应用新的2.6.12.3补丁
$ cd ..
$ mv linux-2.6.12.2 linux-2.6.12.3    # 重新命名内核源代码目录


-rc内核
-------

这些是候选的发布内核。当Linus认为目前的git(内核的源代码管理工具)内核树处于一个
健全的稳定状态足以用来测试的时候,而发布的开发内核。


这些内核是不稳定的,如果你试着运行他们应该会想到可能会不时地有问题出现。
但是这是主开发分支上的最稳定的内核,并且最终会变成下一个稳定的内核。因此
让尽可能多的人来测试它就显得格外重要。


对于那些想帮忙测试开发中的内核但是又不想跑那些试验性的东西的人来说,这将是
一个非常好的分支。(这样的人应该参照下面的关于-git和-mm内核的部分)

-rc补丁是非增量式的,他们应用于2.6.x内核上,就像上面描述的2.6.x.y内核一样。在-rcN
后缀之前的内核版本号代表了这个-rc的内核最终会变成的内核版本。

因此,2.6.13-rc5意思是这是2.6.13内核的第五个候选的发布版本,并且这个补丁应该打在
2.6.12的内核源代码上。


下面是3个关于怎样打这些补丁的例子:

# 首先是一个从2.6.12到2.6.13-rc3的例子

$ cd ~/linux-2.6.12            # 切换到2.6.12的源代码目录
$ patch -p1 < ../patch-2.6.13-rc3    # 打上2.6.13-rc3的补丁
$ cd ..
$ mv linux-2.6.12 linux-2.6.13-rc3    # 重新命名源代码目录

# 现在从2.6.13-rc3迁移到2.6.13-rc5

$ cd ~/linux-2.6.13-rc3            # 切换到2.6.12的源代码目录
$ patch -p1 -R < ../patch-2.6.13-rc3    # 卸载掉2.6.13-rc3补丁
$ patch -p1 < ../patch-2.6.13-rc5    # 应用新的2.6.13-rc5补丁
$ cd ..
$ mv linux-2.6.13-rc3 linux-2.6.13-rc5    # 重新命名源代码目录

# 最后让我们试着从2.6.12.3到2.6.13-rc5

$ cd ~/linux-2.6.12.3            # 切换到内核源代码目录
$ patch -p1 -R < ../patch-2.6.12.3    # 回返2.6.12.3补丁
$ patch -p1 < ../patch-2.6.13-rc5    # 应用新的2.6.13-rc5补丁
$ cd ..
$ mv linux-2.6.12.3 linux-2.6.13-rc5    # 重新命名源代码目录


-git内核
--------

这些是每天Linus的内核树的快照(在一个git仓库中管理着,因此得名)。

这些补丁通常每天都发布而且代表了的Linus的内核树的当前状态,由于它们是自动产生的
甚至没有任何一个光标的骚动来看它们是不是健全的,所以它们比-rc内核更具有试验性。


-git补丁不是增量的,它们或者是应用在2.6.x内核上或者是应用在一个基础的
2.6.x-rc内核上---这一点你可以从他们的名字上看出来。一个名字是2.6.12-git1的
补丁应用在2.6.12内核源代码上,一个名字为2.6.13-rc3-git2的补丁应用在2.6.13-rc3
的内核源代码上。


这里是一些怎样打这些补丁的例子:

# 从2.6.12迁移到2.6.12-git1

$ cd ~/linux-2.6.12            # 切换到内核源代码目录
$ patch -p1 < ../patch-2.6.12-git1    # 应用2.6.12-git1补丁
$ cd ..
$ mv linux-2.6.12 linux-2.6.12-git1    # 重新命名内核源代码目录

# 从2.6.12-git1迁移到2.6.13-rc2-git3

$ cd ~/linux-2.6.12-git1        # 切换到内核源代码目录
$ patch -p1 -R < ../patch-2.6.12-git1    # 回返2.6.12-git1补丁
                    # 我们现在有了一个2.6.12内核
$ patch -p1 < ../patch-2.6.13-rc2    # 打上2.6.13-rc2补丁
                    # 内核现在是2.6.13-rc2
$ patch -p1 < ../patch-2.6.13-rc2-git3    # 打上2.6.13-rc2-git3补丁
                    # 内核现在是2.6.13-rc2-git3
$ cd ..
$ mv linux-2.6.12-git1 linux-2.6.13-rc2-git3    # 重新命名内核源代码目录


-mm内核
-------

这是Andrew Morton发布的实验性的内核

-mm树作为一个新特性和实验性的补丁的实验场。一旦一个补丁在-mm中经过一段时间被证明
有价值,为了使它能包含在主流内核中,Andrew就会把它推给Linus。


尽管鼓励的方法是通过-mm树把补丁推给Linus,这个步骤并不是总被实行。子系统的维护者
(或者个人)有些时候直接把补丁推给Linus,尽管(或者之后)它们已经被它并到了-mm中并得
到了测试(或者有些时候并没有事前在-mm中得到测试)。


通常情况下你应该尽力使你的补丁通过-mm中最大程度测试后再到达主流内核中。


这个分支是一个持续的变化并且包含了一些实验性的特征,很多正在debug的补丁并不适合于
主流的内核等等。这个分支是这个文档中描述的最具有试验性的分支。


这些内核内核不适合于应用在要求稳定的系统上面,并且在运行中比其他任何的分支都可能承担
更大的风险(确信你有最新的备份---跟踪了任何试验性的内核但是甚至更多,于是达到-mm内核)。


这些内核除了包含所有的试验性的补丁以外,它们还包含了在主流-git内核发布的时候任何
可用的改变。


对-mm内核测试会得到极大的赏识,因为这个分支的总的目的就是为了在改变被加到更加稳定的
主流的Linus内核树之前,消除退化、死机、数据失败bug、build失败(以及任何通常意义上的bug)。

但是-mm的测试者应该清醒地认识到这个源代码树中的失败会比其他任何树中的都要普遍。


-mm内核并不会以一个固定的时间发布,但是通常一些-mm内核会在每一个-rc内核(通常1到3个)
发布的中间。-mm内核或者是应用于一个基础的2.6.x内核(当还没有-rc内核发布的时候)或者应用于
一个Linus -rc的内核。


这里有一个打-mm补丁的例子

# 从2.6.12到2.6.12-mm1

$ cd ~/linux-2.6.12            # 切换到2.6.12的源文件目录
$ patch -p1 < ../2.6.12-mm1        # 打一个2.6.12-mm1的补丁
$ cd ..
$ mv linux-2.6.12 linux-2.6.12-mm1    # 重新正确命名这个源文件


# 从2.6.12-mm1到2.6.13-rc3-mm3

$ cd ~/linux-2.6.12-mm1
$ patch -p1 -R < ../2.6.12-mm1        # 卸载掉2.6.12-mm1补丁
                    # 现在我们得到了一个2.6.12的源文件
$ patch -p1 < ../patch-2.6.13-rc3    # 打一个2.6.13-rc3的补丁
                    # 我们现在得到一个2.6.13-rc3的源文件
$ patch -p1 < ../2.6.13-rc3-mm3        # 打一个2.6.13-rc3-mm3的补丁
$ cd ..
$ mv linux-2.6.12-mm1 linux-2.6.13-rc3-mm3    # 重新命名源文件目录


上面总结了不同内核树的一些解释。我希望你已经明白了怎样打不同的补丁并且对你测
试内核有所帮助。

patch 命令

用途

  对文件应用更改。

语法

  patch [ -b [ -B Prefix ] ] [ -f ] [ -l ] [ -N ] [ -R ] [ -s ] [ -v ] [ -c | -e | -n ] [ -d Directory ] [ -D Define ] [ -F Number ] [ -i PatchFile ] [ -o OutFile ] [ -p Number ] [ -r RejectFile ] [ -x Number ] [ File ]

描述

  patch 命令读取如何更改文件的源文件指示信息,然后应用这些更改。源文件包含由 diff 命令产生的差别列表(或者 diff 列表)。差异列表是比较两个文件和构建关于如何纠正差别的指示信息的结果。缺省情况下,patch 命令使用从标准输入读入的源文件,但是使用 -i 标志和 PatchFile 变量可以覆盖此设置。
  差异列表有三种格式:正常、上下文或者是 ed 编辑器风格。patch 命令确定差异列表格式,除非被 -c、-e 或 -n 标志否决。
  缺省情况下,文件的打过补丁的版本替换原始版本。指定 -b 标志时,每个补丁文件的原文件保存在同名的文件中,只是在文件名后附加了后缀 .orig。使用 -o 标志也可以指定输出的目的地。
  -p 标志使得无需手工编辑补丁文件就可以定制补丁文件到本地用户目录结构中。通过指定从路径全称除去多少部分可以做到这一点。例如,如果补丁文件包含路径名称 /curds/whey/src/blurfl/blurfl.c,那么:
  * -p 0 导致使用完整路径名。
  * -p 1 除去前导斜杠,留下 curds/whey/src/blurfl/blurfl.c。
  * -p 4 除去前导斜杠和前三个目录,留下 blurfl/blurfl.c。
  不指定 -p 标志使得 patch 命令使用基本名称。在上面的示例中,此文件为 blurfl.c。
  补丁文件格式
  补丁文件必须包含单行或多行头信息,后跟单个或多个补丁。每个补丁必须包含一行或多行文件名标识,其格式由 diff -c 命令和单个或多个 diff 命令输出集产生,通常称为 hunks。
  patch 命令跳过补丁文件中的任何前导文本,然后应用实际的差异列表,并且跳过任何后续文本。因而,可以将包含差异列表的文件或消息当成补丁文件使用,此时 patch 命令仍然有效。在这种情况下,如果整个差异列表使用一致的数量缩进,patch 命令也会调整其间距。
  要更改原始文件中的行范围,每一补丁中的块(hunk)必须为单独的差异列表。补丁内连续块(hunk)的行号必须以升序方式出现。


文件名确定

  如果没有指定 File 参数,要获得供编辑的文件名,patch 命令会执行下面的步骤:
  1. 在上下文差异列表的头部分,文件名由以 ***(三个星号)或者 ---(三个破折号)开头的行确定。以 *** 开头的行表示获取补丁的文件,然而以 --- 开头的行表示应该应用补丁的文件名。选择存在文件的最短名称。
  2. 如果在前导文本中有 Index: 行,patch 命令尝试使用来自于那一行中的文件名。
  3. 上下文差异文件头优先于 Index: 行。
  4. 如果从前导文本中不能确定文件名,patch 命令提示输入需要打补丁的文件名。
  5. 如果不能找到原始文件,但是有适合的 SCCS 或 RCS 文件可用,patch 命令尝试获取或检出文件。
  6. 如果前导文本包含 Prereq: 行,patch 命令从先决条件行中获取第一个词(通常是版本号)并且检查输入文件看是否能找到那个词。如果找不到,patch 命令在继续运行前会提示确认。

补丁应用程序

  如果补丁文件包含不止一个补丁,patch 命令尝试应用每个差异列表,就好像它来自于单独的补丁文件。在这种情况下,为每个差异列表确定需要打补丁的文件名,并且审查每个差异列表前的头文本以获得如文件名和修订版级别的信息。
  如果指定 -c、 -e 或 -n 标志,patch 命令将每块(hunk)内的信息分别解释成上下文差别、ed 编辑器差别或者正常差别。否则,patch 命令确定基于块(hunk)内信息格式的差别类型。
  patch 命令通过获取块(hunk)的首行序号和添加或减去由于应用前一块(hunk)而产生的任何行偏移来搜索位置以应用每一块(hunk)。如果在这一行位置不可能有精确匹配,patch 命令前后搜索以获取与块(hunk)的内容精确匹配的行集合。
  如果找不到这些位置,且如果 patch 命令正在应用上下文差异列表,patch 命令能进行非精确搜索。fuzz factor 指定了非精确匹配的行数目。如果模糊因子设置成 1 或者更大,patch 命令执行第二次扫描,这一次忽略上下文的第一行和最后一行。如果没有匹配结果,且最大模糊因子设置成 2 或者更大, patch 命令执行第三次扫描,这一次会忽略上下文的前两行和最后两行。(缺省模糊因子最大值为 2)。如果找不到匹配的位置,patch 命令在拒绝文件中放置块(hunk)。创建拒绝文件时,其名称和输出文件一样,只是在文件名有后缀 .rej。使用 -r 标志可以覆盖此命名约定。
  以上下文差异列表格式写拒绝块(hunk),而不管补丁文件的格式如何。如果输入是正常差别或 ed 编辑器样式差别,拒绝文件可能包含上下文格式零行差别。拒绝文件中块(hunk)的行编号可能与补丁文件中的行编号不同。这是因为拒绝文件的行编号反映了新文件而不是老文件中故障块(hunk)的大约位置。
  当完成每块(hunk)后,patch 命令会告诉您该块(hunk)是成功还是失败。也可以获知为每块(hunk)假定的新行编号。如果这与差异列表中指定的行编号不同,就会报告偏移量。patch 命令也说明是否使用模糊因子来进行匹配。
  注:单一的大偏移可能表示块(hunk)安装位置不正确。模糊因子的使用可能表示布局不正确。

为其它用户准备补丁

原则

  准备将补丁装载给其它用户的程序员应该考虑下面的附加原则:
  * 如果想两次应用同一补丁,patch 命令假定第二个应用程序应该是逆向补丁,并且提示确认此逆向。因此,避免发送出这些逆向补丁,因为它使用户疑惑他们是否已经应用了此补丁。
  * 建议保留使用最新补丁级别更新的 patchlevel.h 文件。补丁级别可以用作所发送的补丁文件中的第一个差异列表。如果补丁中包含 Prereq: 行,用户不能应用顺序混乱的补丁,同时不收到警告。
  * 在上下文差异列表的头中或者使用 Index: 行以确保正确指定了文件名。如果正在子目录中打某些补丁,请确保通知补丁用户在需要时指定 -p 标志。
  * 可以通过发送差异列表创建文件,此列表比较一个空文件和想要创建的文件。然而,这个方法只有在想要创建的文件的确不存在于目标目录时才有效。
  * 虽然可以将许多差分列表放置到一个文件中,但是将相关补丁分组到单独的文件中会更好。
  * patch 命令不能说明 ed 脚本中的行编号是否正确,只能在当它找到更改或删除命令时才能检测正常差异列表中不正确的行编号。使用模糊因子为 3 的上下文差异列表可能有同样的行编号问题。除非添加了一个适当的交互式接口,在这种情形下才使用上下文差异列表来检测更改的正确性。编译无误通常表示补丁工作正常,但是它并不表示没有错误。
  * 只有当补丁应用到与生成补丁的完全同一版本的文件中,才保证 patch 命令的结果。
  * 如果代码重复,例如:
  #ifdef
  ... NEWCODE
  #else
  ... OLDCODE
  # endif
  patch 命令不能为两个版本都打补丁。如果 patch 命令成功,它可能补丁了错误版本但是返回了一个成功的退出状态。

标志

  -b 在应用差别前保存每个修改后文件的副本。复制的原始文件归档时与原文件同名且添加了后缀 .orig。如果使用那个名称的文件已经存在,它就被覆盖。如果对同一文件应用多个补丁,原始文件只生成一个副本(在第一次补丁时)。如果也指定 -o OutFile 标志,就不会创建 .orig 文件。但是如果指定文件已经存在,就创建 OutFile.orig。
  -B Prefix 指定备份文件名称的前缀。此标志只有在和 -b 标志连接使用时才有效。
  -c 将补丁文件解释成上下文差异列表(diff -c 或 diff -C 命令的输出)。此标志不能和 -e 或 -n 标志一起使用。
  -d Directory 在处理前,更改当前目录到指定目录。
  -D Define 使用下面的 C 预处理器构造标记更改:
  #ifdef Define
  ... (NEWCODE)
  #else
  ... (OLDCODE)
  #endif
  Define 变量用作差分符号。此标志只有当正常或上下文格式差异列表用作补丁文件时才有效。
  -e 将补丁文件解释成 ed 编辑器脚本。此标志不能和 -c 或 -n 标志一起使用。
  -f 禁止查询用户。要禁止注释,使用 -s 标志。
  -F Number 设置最大模糊因子。此标志只应用于上下文差异列表,它使 patch 命令在确定块(hunk)的安装位置时忽略指定行编号。如果没有指定 -F 标志,缺省模糊因子为 2。此因子不可以设置成大于上下文差异列表中内容的行的数目(通常为 3)。
  注:较大的模糊因子会增加错误补丁的可能性。
  -i PatchFile 从指定文件,而不是从标准输入中读取补丁信息。
  -l (L 的小写)使差异列表脚本中的任何空格字符序列匹配输入文件中的任何空格字符序列。精确匹配其它字符。
  -n 将脚本解释成正常差异列表。此标志不能和 -c 或 -e 标志一起使用。
  -N 忽略差别已经向文件应用了的补丁。缺省情况下,会拒绝已经应用的补丁。
  -o OutFile 复制要打补丁的文件,然后应用更改,接着将修改版本写到指定的输出文件。单个文件的多个补丁应用于以前补丁所创建的文件的中间版本。因此,多补丁会生成输出文件的多个连接版本。
  -p Number 设置路径名的剥离数目,它控制如何处理在补丁文件中找到的路径名称。如果将文件保留在与指定路径不同的目录中,此标志就有用。剥离数目指定了从路径名前去除多少个斜杠。也去除所有中间的目录名。例如,假定补丁文件指定 /u/leon/src/blurf1/blurf1.c:
  * -p 0 留下未修改的完整路径名。
  * -p 1 除去前导斜杠,留下 u/leon/src/blurf1/blurf1.c。
  * -p 4 卸下四个斜杠和三个目录,留下 blurf1/blurf1.c。
  如果没有指定 -p 标志,只使用基本名称(最后的路径名称组件)。此标志只有在没有指定 File 参数时才起作用。
  -r RejectFile 覆盖缺省拒绝文件名。通过附加后缀 .rej 到原始文件名中,就形成了缺省拒绝文件名。
  -R 逆向补丁脚本理解。例如,如果从新版本到旧版本创建差异列表,使用 -R 标志使 patch 命令在应用前逆向脚本的每个部分。以交换格式保存拒绝差别。 -R 标志不能和 ed 脚本一起使用,因为其中只有很少的信息可以重新构造逆向操作。如果没有指定 -R 标志,patch 命令尝试以逆向理解和正常理解应用每个部分,直到成功应用补丁文件的每一部分。如果尝试成功,提示用户确定是否应该设置 -R 标志。
  注:如果此方法和第一个命令是附加命令(就是说,逆序是删除)的正常差异列表一起使用,它就不能检测逆向补丁。因为空上下文无论在何处都匹配,所以附加总是成功的。幸运的是,大多数补丁是添加或更改行而不是删除行。因此大多数逆序的正常差异列表以一个删除开始,它导致故障,并且引起启发(heuristic)。
  -s 静默地进行补丁,直到发生错误。
  -v 打印修订版头和补丁级别。如果 -v 标志和其它标志一起使用,就忽略其它标志。
  -x Number 设置内部调试标志。此标志只适用于 patch 命令开发者。
  退出状态
  返回下面的出口值:
  0 成功完成。
  1 产生错误。

示例

  1. 要将 difflisting 文件中的差异列表应用到 prog.c 文件,请输入:
  patch -i difflisting prog.c
  2. 要保存 prog.c 文件的原始版本,请输入:
  patch -b -i difflisting prog.c
  它将更改应用到 prog.c 并且在 prog.c.orig 文件中保存 prog.c 的原始内容。
  3. 要打补丁到 prog.c 文件而不改变原始版本,请输入:
  patch -i difflisting -o prog.new prog.c
  它将 prog.c 当成源文件使用,但是更改后的版本写到名为 prog.new 的文件中。

文件

  /usr/bin/patch 包含 patch 命令。

参考:diff  -  http://baike.baidu.com/view/1374858.htm#1
     diff & patch 制作及打补丁   http://apps.hi.baidu.com/share/detail/31021126
     在linux下打补丁  http://www.360doc.com/content/10/1107/17/3360461_67400736.shtml

你可能感兴趣的:(linux开发)