#
之所以稱為正規(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開始,并依次遞增,除非強制指定修訂版號
|