#
之所以称为正规(normal)格式输出是因为这种格式只显示有差别的行,不会混入任何相同的行.它称为默认得输出格式的原因是为了遵守POSIX标准.正规格式很少用于发布软件补丁,但以此为基础对理解任何一种diff的输出格式很有用处.一般来说,正规块(normal
hunk)的格式如下: change_command <srcfile line <srcfile
line ... >dstfile line >dstfile
line change_command的格式如下:首先是一个来自srcfile的行号或以逗号隔开的行号范围,然后是一个命令符,接下来是一个来自dstfile的行号或以逗号隔开的行号范围.的命令符可以为: .a--添加 .b--删除 .c--更改
并排(side-by-side)格式虽然对于创建源代码补丁来说没有什么用处,但是用它直接比较源代码文件比较容易,因为它把srcfile和dstfile的内容并排显示在屏幕上. ivan@debian $diff -y -W 80 hello.c howdy.c #include
<stdio.h> #include
<stdio.h> > #include
<stdlib.h> int main(void) int
main(void) { { char msg[] = "Hello,
Linux pr | char msg[] = "Hello, Linux
pr puts(msg); printf("Here
you are, using d | printf("Here you are, using d
return
0; | exit(EXIT_SUCCESS); }
字符">"表示该行在dstfile而不在srcfile里.类型地,字符"<"表示该行在srcfile而不在dstfile里.字符"|"标记出两个文件不相同的行.
以前曾提到过,在发布软件补丁时很少(可能从不)使用正规和并排的块格式.但diff产生的上下文(context)或统一(unified)的块格式是创建补丁所采用的格式.为了产生上下文的差异文件(它们称为context
diff的原因是它们显示出了有差别的行的上下文内容),可使用diff的-c或-C[num]选项. 提示:和大多数GNU程序一样,diff也支持长选项,也就是以两个两字符"--"开头,后面跟着更容易记忆的名字的选项,例如,创建一个上下文diff文件的长选项是"--context=[num]".
上下文输出举例 $diff
-c hello.c howdy.c *** hello.c Web Aug 9 21:02:42 2000 --- howdy.c Web Aug
9 21:04:30 2000 ************* *** 1,12 **** #include
<stdio.h> int main(void) { ! char msg[ ] = "Hello,
Linux programmer!"; puts(msg); ! printf("Here you are,
using diff.\n");
! return 0; } --- 1,13 --- #include
<stdio.h> + #include <stdlib.h>
int
main(void) { ! char msg[] = "Hello, Linux programmer, from
howdy.c!"); puts(msg); ! printf("howdy.c says, `Here you are,
using diff.`\n"); !
exit(EXIT_SUCCESS); } $
上下文块的格式采用以下一般形式: *** srcfile
srcfile_timestamp --- dstfile
dstfile_timestamp **************** *** srcfile_line_range
*** srcfile line --- dstfile line_line_range dstfile
line dstfile line...
. + -----向srcfile添加一行以创建dstfile . -
-----从srcfile删除一行以创建dstfile . !
-----在srcfile改变一行以创建dstfile.srcfile中标记"!"的每一行或一段,在dstfile中相应的每一行或一段也标记"!".
每一块(hunk)都用一长串最多15个星号和下一块(hunk)分隔开来.
统一格式是对上下文格式的修改版本,它不显示重复的上下文而且还用其他办法压缩输出内容.统一格式以下面的开头来标识要比较大文件: --- srcfile
srcfile_timestamp 其后是一个或多个块(hunk),格式如下: @@ srcfile_range
dstfile_range
@@ line_from_either_file line_from_either_file 以@@开头的每一行都标志一个块的开始.在块中,上下文行以空格开头,而有差别的行以"+"或"-"开头,以表示相对于srcfile在此位置上添加或删除一行. 命令diff
-u hello.c howdy.c产生的输出如下: ---hello.c Web Aug 9 21:02:42
2000 +++howdy.c Web Aug 9 21:04:30 2000 @@ -1,12 +1,13
@@ #include <stdio.h> +#include
<stdlib.h>
int main(void) { - char msg [] =
"Hello,Linux programmer!"; + char msg [] = "Hello,Linux programmer,
from howdy.c!"; puts(msg); - printf("Here you are,
using diff.\n"); + printf("howdy.c says, `Here you are, using
diff.`\n"); - return 0; +
exit(EXIT_SUCCESS); } 对这一输出进行翻译,用语言来描述怎么把hello.c转变成howdy.c: .紧挨着#include
<stdio.h>一行之后加入#include <stdlib.h> .紧挨着前半个大括号之后,删除 char
msg[] = "Hello, Linux Programmer!"; 并加入 char msg[] = "Hello, Linux
Programmer, from howdy.c!"; .紧挨着puts(msg);之后,删除 printf("Here you
are, using diff.\n"); 并加入 printf("howdy.c says, 'Here you are,
using diff.'\n"); .紧挨着最后一个空行之后,删除 return
0; 并加入 exit(EXIT_SUCCESS); 虽然统一格式既紧凑又容易阅读,但是统一格式的差异文件却有一个缺点:目前,只有GNU
diff能产生统一格式的差异文件而且只有GNU
patch能理解统一格式.
要判别两个二进制文件的差异,只需把它们的名字作为参数传递给diff: $diff hello
howdy Binary files hello and howdy
differ 如果要显示有差别的行,则使用-a选项.但是需注意这样做会输出重定向到一个文件: $diff -a hello howdy >
diffs Files hello.c and howdy.c
differ 要查看两个文本文件是否不同但又不显示差异之处,可以使用diff的-q选项: $diff -q hello.c
howdy.c Files hello.c and howdy.c differ
假如你想忽略某种差别.实现这个目的的做法是使用-I
regexp选项. $diff -u -I include hello.c howdy.c --- hello.c Web Aug
9 21:02:42 2000 +++ howdy.c Web Aug 9 21:04:30 2000 @@ -2,11 +3,11
@@
int main(void) { - char msg[ ] = "Hello. Linux
programmer!"; + char msg[ ] = "Hello. Linux programmer, from
howdy.c!";
puts(msg); - printf("Here you are,
using diff.\n"); + printf("howdy.c says, `Here you are, using
diff.\n'"; - return
0; + exit(EXIT_SUCCESS); } 上面的例子使用了-I
regexp来忽略"include"的行.
另一组有用的diff命令行选项能改变它对空白的处理方式.-b选项让diff忽略输入文件中空白数量的变化;-B让diff忽略删除或插入空行的改动;-w在逐行比较时忽略空白的变化.-b和-w的区别在哪里?-b忽略的是输入文件之间空白数量上的变化,而-w则忽略在原本没有空白的地方添加的空白.
注:而2路hunk则在"===="后加上1,2或3来指出引起不同的那个文件.
$diff3 sigrot.2 sigrot.1
sigrot.3 产生如下的输出(在这里因为空间的原因对输出做了截断): ==== 1:3c #Version
2.0 2:3c #Version 1.0 3:3c #Version
3.0 ====1 1:9,10c srcdir=$HOME/doc/signatures srcfile=$srcdir/$sigfile 2:8a 3:8a ====1 1:12c old=$(cat $srcdir/num) 2:10c 3:10c old=$(cat
num) ... 第一个hunk是3路hunk,其他的都是2路hunk.从sigrot.1或sigrot.3中生成sigrot.2时,必须把从sigrot.2中来的下面两行添加到sigrot.1或sigrot.3的第8行后: srcdir=$HOME/doc/signatures srcfile=$srcdir/$sigfile 类似地,若要依据sigrot.2来生成sigrot.1,必须把sigrot.1的第10行改为sigrot.2中来的第12行.
可以使用-m或--merge选项来告诉diff3对文件进行合并,然后再手工对结果排序: $diff3
-m sigrot.2 sigrot.1 sigrot.3 > sigrot.merged
程序清单6.7
使用diff3合并选项产生的输出 #!/usr/local/bin/bash #sigrot.sh <<<<<<<
sigrot.2 #Version 2.0 |||||||sigrot.1 #Version
1.0 ======= #Version 3.0 >>>>>>>
sigrot.3 #Rotate signatures #Suitable to be run via
cron ############################
sigfile=signature srcdir=$HOME/doc/signatures srcfile=$srcdir/$srcfile
old=$(cat $srcdir/num) let new=$(expr $old+1)
if [ -f $srcfile.$new ];
then cp $srcfile.$new $HOME/.$sigfile echo $new > $srcdir
/num else cp $srcfile.1 $HOME/.$sigfile echo 1 > $srcdir
/num fi
return
0
"<<<<<<<"标记对应myfile,">>>>>>>"对应yourfile,"|||||||"对应oldfile.在本例中,只需要最新的版本号,为了成功合并3个版本,将删除标记行和1.0及2.0版本指定的行.
当两个人同时修改一个公用文件时,diff3就会发挥作用.它比较两个人做出的两套修改内容,创建第3个文件保存并后的输出结果,并且指出双方修改的冲突之处.diff3的语法是: diff
[options] myfile oldfile
yourfile oldfile是派生出myfile和yourfile的共同源文件. 程序清单6.4
sigrot.1 #!/bin/bash #sigrot.sh #Version 1.0 #Rotate
signatures #Suitable to be run via
cron ############################ sigfile=signature old=$(cat
num) let new=$(expr $old+1)
if [ -f $sigfile.$new ];
then cp $sigfile.$new .$sigfile echo $new >
num else cp $sigfile.1 .$sigfile echo 1 >
num fi
程序清单6.5
sigrot.2 #!/bin/bash #sigrot.sh #Version 2.0 #Rotate
signatures #Suitable to be run via
cron ############################
sigfile=signature srcdir=$HOME/doc/signatures srcfile=$srcdir/$sigfile old=$(cat $srcdir/num) let new=$(expr $old+1)
if [ -f $srcfile.$new ];
then cp $srcfile.$new $HOME/.$sigfile echo $new > $srcdir/num else cp $srcfile.1 $HOME/.$sigfile echo 1
> $srcdir/num fi
程序清单6.6
sigrot.3 #!/bin/bash #sigrot.sh #Version 3.0 #Rotate
signatures #Suitable to be run via
cron ############################
sigfile=signature old=$(cat
num) let new=$(expr $old+1)
if [ -f $sigfile.$new ];
then cp $sigfile.$new .$sigfile echo $new >
num else cp $sigfile.1 .$sigfile echo 1 >
num fi return
0;
diff3在列举hunk的同时给出了生成这些hunk所需的一个或多个命令(仍旧使用ed形式).这些命令如下: .file:la 该hunk出现在第1行后,但在file中不存在这个hunk,所以如果要依据file生成其他文件,必须加入在第1行后这个hunk. .file:rc 该hunk由file的中第r行组成,因此在生成其他文件时必须对该行进行指定的修改.
patch选项 选项 含义 -c 把输入的补丁文件看作是上下文格式的差异文件 -d
dir 把dir设置为解释补丁文件名的当前目录 -e 把输入的补丁文件看作时ed脚本 -F
num|
--fuzz=NUM 把非精确匹配的fuzz因子设置为NUM行 -l 把不同的空字符序列视为相同 -n 把输入的补丁文件看作是正规格式的差异文件 -pnum|
--strip=NUM 剥离文件名中的前NUM个目录成分 -R 假定在生成补丁的命令中交换了老文件和新文件的次序 -s 除非方式错误,否则保持缄默 -t 执行过程中不要求任何输入 -u 把输入的补丁文件看作是统一格式的差异文件 -v 显示patch的版本信息并退出
当patch程序运行时,它会对将要改动的每个源文件做备份,在备份文件名的末尾加上.orig作后缀.如果patch程序不能应用某个块(hunk),它会用补丁文件中存储的文件名加上.rej(拒绝)后缀来保存该块.
$diff -c sigrot.1 sigrot.2 > sigrot.patch 或者使用 $diff -u sigrot.1
sigrot.2 >
sigrot.patch 如果源代码树内包含子目录,则在使用diff时指定-r(recursive)选项以告诉diff在创建补丁文件时遍历所有子目录.
使用补丁的命令行如下: $patch -p0 < sigrot.patch -pnum
选项指定使用补丁前补丁中所包含的文件名中需要剥离的"/"的重数.例如,如果补丁中的文件名是/home/kwall/src/sigrot/sigrot.1,则-p1的结果是home/kwall/src/sigrot/sigrot.1;-p4的结果是/sigrot/sigrot.1;-p则剥去了除最终文件名之外的所有部分,得到sigrot.1.
如果在安装完补丁后发现错误,只要简单地在原命令行中加上-R选项后再安装一次该补丁就能得到原来的文件: $patch
-p0 -R < sigrot.patch
版本控制术语 名称 说明 RCS
File 在RCS目录下的文件,由RCS控制,并通过RCS命令存取.一个RCS文件包含某一特殊文件的所有版本.通常,RCS文件的扩展名是.v Working
file 从RCS源代码库(即RCS目录)中检索到的一个或多个文件,放置在当前工作目录下,并能够被编辑. Lock 以编辑目的取回工作文件时别人就不能同时编辑这个文件.此时,文件由第一个编辑它的人锁定 Revision 源文件的一个特定版本,用数字标识.Revision的编号从1.1开始,并依次递增,除非强制指定修订版号
|