diff 以"行"为单位比较两个文本文件(也可以是目录比较),并将不同之处以某种格式输出到标准输出上;patch可以读入这种输出,并按照一定指令使源文件(目录)按照目标文件(目录)更新。Linux内核源码就是按照这种方式保持更新的,我们在www.kernel.org上可以下载到最新内核的 patch文件的bzip2包。本文以gnudiffutils 2.7和patch 2.5为例介绍diff和patch工具的使用。
diff 既可以用来比较两个文件,也可以用来比较两个目录中每个文件。使用-r(--recursive)参数时还可以在目录中嵌套比较。比较目录时除比较同名文件外,对不同名的文件当成新文件处理。对于比较C程序文件,diff还提供了专门的参数(-p,--show-c-function)来标识不同之处所在的函数名。
diff的输出格式有三种:列举方式、命令模式和上下文模式,其中命令模式有分为两种:ed命令格式和RCS(Revision Control System,版本控制系统)命令格式,上下文模式也按格式分为老版和新版两种。看下面的例子就能基本清楚各个格式的区别:
![](https://i-blog.csdnimg.cn/blog_migrate/38ffb577fb55928d05cf700b884ea964.png)
命令格式记录的是从test1更新到test2所需要执行的命令,而上下文模式通常可读性更好一些,它所记录的主要是二者的差异,通常还记录所需修改部分的上下几行(可配置)内容以供比较。见下面的例子:
![](https://i-blog.csdnimg.cn/blog_migrate/d90f5dc95555fa5816e27e9424e7ffd2.png)
新版格式较之老版要紧凑一些,Linux内核源码的升级就是按照新版上下文格式用diff组织的,比如patch-2.4.16中所用的具体命令为:
diff -Nur linux-2.4.15 linux
参数N表示如果某个文件仅在一个目录中出现,则假定其在另一个目录中为空文件;u表示unified格式,r表示在目录中嵌套使用,linux-2.4.15显然是老核的目录名,而linux则为新核的目录名。
2 patch尽管并没有指定patch和diff的关系,但通常patch都使用diff的结果来完成打补丁的工作,这和patch本身支持多种diff输出文件格式有很大关系。patch通过读入patch命令文件(可以从标准输入),对目标文件进行修改。通常先用diff命令比较新老版本,patch命令文件则采用 diff的输出文件,从而保持原版本与新版本一致。
patch的标准格式为
|
如 果patchfile为空则从标准输入读取patchfile内容;如果originalfile也为空,则从patchfile(肯定来自标准输入)中读取需要打补丁的文件名。因此,如果需要修改的是目录,一般都必须在patchfile中记录目录下的各个文件名。绝大多数情况下,patch都用以下这种简单的方式使用:
|
patch 命令可以忽略文件中的冗余信息,从中取出diff的格式以及所需要patch的文件名,文件名按照diff参数中的"源文件"、"目标文件"以及冗余信息中的"Index:" 行中所指定的文件的顺序来决定。也就是说,对于如下diff结果文件(Linux内核源码2.4.16升级包,部分):
|
patch 首先尝试当前目录(或者-d参数指定的目录)下的linux-2.4.15/Makefile文件是否存在,如果不存在则试图对linux/Makefile文件操作,仅当两者都不存在时(或者设置了POSIXLY_CORRECT环境变量)才会读取Index:的内容(此文件中没有标识)。
前 面提到的-p参数决定了是否使用读出的源文件名的前缀目录信息,不提供-p参数,则忽略所有目录信息,-p0(或者-p 0)表示使用全部的路径信息,-p1将忽略第一个"/"以前的目录,依此类推。如/usr/src/linux-2.4.15/Makefile这样的文件名,在提供-p3参数时将使用linux-2.4.15/Makefile作为所要patch的文件。
对于刚才举的 Linux内核源码2.4.16升级包的例子,假定源码目录位于/usr/src/linux中,则在当前目录为/usr/src时使用"patch -p0 <patch-2.4.16"可以工作,在当前目录为/usr/src/linux时,"patch -p1<patch-2.4.16"也可以正常工作。
patch可以直接操作上下文格式以及混合ed格式的diff输出文件,而将ed格式文件通过管道提交给ed程序操作(暂时不知RCS格式的文件如何处理)。
在此仅举一个简单的例子来说明如何用diff/patch工具维护源码升级。
假设program-1.0目录中为老版,现开发完成的新版位于program-2.0目录中,将两个目录置于同一父目录下,然后在该父目录上执行:
|
将生成一个program-2.0.patch的补丁文件,发布该补丁文件(当然可以先压缩成bzip2格式)。
假设拿到的是program-2.0.patch.bz2文件,则在program-1.0目录同级执行:
|
如此即完成了从1.0到2.0的升级。
如果希望恢复到原版本,可以使用-R(--reverse)参数,但仅对上下文格式的diff文件有效。还有一个备份参数也可以使用,但简单应用中,整个目录备份可能更方便一些。
================================================================
linux学习杂记_编程风格_diff_patch_svn
两大风格:
GNU编程风格->http://www.gnu.org/prep/standards/ 要点: 函数开头的左花括号在最左边,其他的左括号避免放到最左边; 函数名的起始字符也要在最左边; 每个程序开头都要有一段注释说明其功能; 函数的注释:功能,参数类型,含义,返回值; while,if尽量带上括号; 避免在if中赋值; 结构的声明和typedef,结构变量定义尽可能不放一起; 全局变量要注释; 全局变量和函数,避免采用简单的名字,小写字母加下划线构成; 局部变量命名要简短; kernel编程风格-> 要点: 缩进格式是tab,缺省是8字符; 函数长度不应超过2屏(24*2);函数局部变量不应超过10个; 将开始的大括号放在一行的最后,函数定义除外; 结束的大括号通常单独位于一行,下列情况除外: do { }while(); if () { }else if () { }else{ } indent工具: indent -gnu style.c -o style_gnu.c //变成gnu风格 indent -kr -i8 style.c -o style_kernel.c //变成内核风格 diff: 输出格式:nomal格式, -c上下输出格式, -y并排输出格式, -u统一输出格式 diff file1 file2 的输出: <表示第一个文件file1 >表示第二个文件file2 ? file1要如何变成file2呢: ndm 表示file1的n行删除,后面的部分加到file2的m行后面; ncm 表示file1的n行改成file2的m行那样; nam 表示file1的n行后面的部分添加到file2的m行后面; 上下输出格式说明: - file1需要删除的 ! file1需要更改的 + file1需要添加的 统一输出格式说明: - file1中需要删除的 + file1中需要添加的 @@ -n,m +p,q @@ 该比较块中,file1从n行开始,共m行,file2从p行开始,共q行; 一些选项: -q only report whether differ -b ignore space change,but can detect the difference of no space to have space -w ignore all spaces -B ignore blank lines -r enter subdir to diff 常用的命令格式: diff -urN dir1 dir2 > dir.patch 产生patch的diff应采用统一格式u选项; diff3 [option] file_chg1 file_org file_chg2 > newfile 将前后2个文件与当中的原始文件比较,输出结果. -m选项可以合并文件,对于同一处的不同修改则报告冲突,显示为:<<<<||||====>>>> patch [options] < patchfile 选项: -p num 剥离前num个目录进行patch -d dir 先进入dir目录,再patch -R 逆向还原 如: diff -u hello.c lohhe.c > hello.patch patch -p0 < hello.patch patch -R -p0 < hello.patch diff -urN doc1 doc2 > doc.patch rm doc2 -rf patch -p0 < doc.patch or patch -d doc1 -p1 < doc.patch subversion 产生背景->lock-modify-unlock机制的弊病: 无法并行开发;如果修改同一文件的不同部分,不需要加锁;如果2人同时修改2个依赖的文件,则不可同步了. svn采用copy-modify-merge机制 安装:sudo aptitude install subversion 创建respository: svnadmin create ~/respos 启动网络服务器: svnserve -d 以daemon的方式启动服务,默认端口3690,不需要root权限 修改配置文件: vi ~/respos/conf/svnserve.conf [gerneral] password-db = passwd 表示用户帐号文件是~/respos/conf/passwd vi ~/respos/conf/passwd [users] harry = harrysecret 注意都要顶格写,行首不空; marry = marrysecret 导入项目文件: svn import project_dir ...enter nano, input some description word, ^o save, ^x quit. password for 'PC_user': username: 'SVN_user' password for 'SVN_user': 建立工作站:checkout svn checkout svn://127.0.0.1/home/PC_user/respos/project_dir local_project_dir svn commit 提示服务器上的内容有被他人commit过,commit不成功 svn update 从服务器下载最新内容,并与自己的修改合并,若有冲突,会提示,需先解决 svn resolved file 告诉svn冲突已经解决,然后可以commit了 svn commit 发出更新服务器上的内容 svn status 显示本地工作目录的文件状态 svn add file 将file文件,目录,符号连接,加入respository svn delete file svn copy file1 file2 svn move file2 file3 svn commit 将修改同步到服务器 svn revert file1 撤销本地修改,恢复到update时候的状态 svn log 显示所有nano信息 svn diff --revision 3:5 比较版本3和5 svn update --revision 3 整个目录退回到版本3 svn help 可显示更多命令信息 |
From:
blog.csdn.net/digbug/archive/2007/01/16/1484851.aspx
blog.chinaunix.net/u1/53151/showart_418323.htmldiff和patch的使用
还可以参考这一篇:http://www.hzlitai.com.cn/bbs/dispbbs.asp?boardid=28&id=1215 |