如果遇到這個問題:
patch.exe -p1 < xx.patch
Assertion failed: hunk, file ../patch-2.5.9-src/patch.c line 354
可以用這個命令來patch
patch -i xx.patch --verbose --binary
情景一:你正嘗試從代碼編譯一個軟體包,發現有人已經對代碼進行了小小的修改以便在你的系統上編譯。他們通過補丁的方式發布自己的成果,但是你卻不知道該如何使用它。答案是你使用一個叫做patch(很貼切)的命令列工具將補丁應用到原始代碼上。
情景二:你下載了一個開源軟體包的代碼,花了一個小時左右稍作修改,成功的讓它在你的系統上編譯通過。你想把自己的成果分享給其他程式員,或者給軟體包的作者。現在你就需要建立自己的補丁,你需要的工具是diff。
這是一份diff和patch的快速指南,通過講解它們最常見的用法來幫你解決上面問題。它告訴你的東西足夠讓你立刻開始使用。之後,你可以在閑暇的時候用manpage來學習diff和patch的前前後後。我總是樂意聽到你們的問題和意見。用聯絡表單跟我取得聯絡。
用patch命令應用補丁
要對單個檔案應用補丁,進入檔案所在的目錄並調用patch命令:
patch < foo.patch
這些命令假定補丁是以統一格式分發的,這種格式指明了補丁要應用到的檔案。如果不是,你可以在命令列裡指定檔案:
patch foo.txt < bar.patch
應用補丁到整個目錄(這種情況也許更常見)也是類似的,但是你必須注意設定p層級。就是說,在補丁檔案裡,需要打補丁的檔案在你電腦上的路徑名跟在建立補丁的電腦上可能不同。p層級告訴patch命令忽略掉路徑名的幾個部分以正確的識別檔案。通常p層級為1就夠了,所以你使用:
patch -p1 < bar.patch
運行該命令之前,你應該進入原始碼目錄的頂層目錄。如果補丁層級1不能正確識別任何需要打補丁的檔案,檢查補丁檔案裡的檔案名稱。如果你看到這樣一個檔案名稱:
/users/stephen/package/src/net/http.c
而你當前正工作在一個包含net/http.c的目錄,使用:
patch -p5 < bar.patch
總的來說,對於從路徑最開始刪除的每個路徑分隔字元(斜線字元)加一,直到剩下的部分存在於你的工作目錄中。最後得到的就是p層級。
要刪除補丁,用-R參數,例如:
patch -p5 -R bar.patch
使用diff建立補丁
不論是對於單個檔案還是整個源碼目錄,使用diff都很簡單。為單個檔案建立補丁,用下面形式:
diff -u original.c new.c > original.patch
為整個源碼樹建立補丁,複製一份源碼樹:
cp -R original new
在目錄new/裡進行必要的修改,然後用下面的命令建立補丁:
diff -rupN original/ new/ > original.patch
這就是diff和patch入門需要的所有知識。要獲得更多的資訊用:
man diff
man patch
-----------------------------------------------------------------------------------------------------------------
在Unix系統下,維護源碼版本可以使用很多方法,其中最常用的當然是大名鼎鼎的CVS,但實際上,簡單的版本維護工作並沒有必要使用複雜的CVS等專門的版本維護工具,Unix標配中的diff和patch工具就完全可以完成代碼的簡單備份和升級工作。
diff以"行"為單位比較兩個文字檔(也可以是目錄比較),並將不同之處以某種格式輸出到標準輸出上;patch可以讀入這種輸出,並按照一定指令使源檔案(目錄)按照目標檔案(目錄)更新。Linux核心源碼就是按照這種方式保持更新的,我們在www.kernel.org上可以下載到最新核心的patch檔案的bzip2包。本文以gnudiffutils
2.7和patch 2.5為例介紹diff和patch工具的使用。
1.diff
diff既可以用來比較兩個檔案,也可以用來比較兩個目錄中每個檔案。使用-r(--recursive)參數時還可以在目錄中嵌套比較。比較目錄時除比較同名檔案外,對不同名的檔案當成新檔案處理。對於比較C程式檔案,diff還提供了專門的參數(-p,--show-c-function)來標識不同之處所在的函數名。
diff的輸出格式有三種:列舉方式、命令模式和上下文模式,其中命令模式有分為兩種:ed命令格式和RCS(Revision Control System,版本控制系統)命令格式,上下文模式也按格式分為老版和新版兩種。看下面的例子就能基本清楚各個格式的區別:
命令格式記錄的是從test1更新到test2所需要執行的命令,而上下文模式通常可讀性更好一些,它所記錄的主要是二者的差異,通常還記錄所需修改部分的上下幾行(可配置)內容以供比較。見下面的例子:
新版格式較之老版要緊湊一些,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的標準格式為
patch [options] [originalfile] [patchfile] |
如果patchfile為空白則從標準輸入讀取patchfile內容;如果originalfile也為空白,則從patchfile(肯定來自標準輸入)中讀取需要打補丁的檔案名稱。因此,如果需要修改的是目錄,一般都必須在patchfile中記錄目錄下的各個檔案名稱。絕大多數情況下,patch都用以下這種簡單的方式使用:
patch命令可以忽略檔案中的冗餘資訊,從中取出diff的格式以及所需要patch的檔案名稱,檔案名稱按照diff參數中的"源檔案"、"目標檔案"以及冗餘資訊中的"Index:"行中所指定的檔案的順序來決定。也就是說,對於如下diff結果檔案(Linux核心源碼2.4.16升級包,部分):
diff -Nur linux-2.4.15/Makefile linux/Makefile--- linux-2.4.15/Makefile Thu Nov 22 17:22:58 2001+++ linux/Makefile Sat Nov 24 16:21:53 2001@@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4-SUBLEVEL = 15-EXTRAVERSION =-greased-turkey+SUBLEVEL = 16+EXTRAVERSION = KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)…… |
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格式的檔案如何處理)。
回頁首
3.配合使用diff和patch升級源碼
在此僅舉一個簡單的例子來說明如何用diff/patch工具維護源碼升級。
假設program-1.0目錄中為老版,現開發完成的新版位於program-2.0目錄中,將兩個目錄置於同一父目錄下,然後在該父目錄上執行:
diff -Nur program-1.0 program-2.0 >program-2.0.patch |
將產生一個program-2.0.patch的補丁檔案,發布該補丁檔案(當然可以先壓縮成bzip2格式)。
假設拿到的是program-2.0.patch.bz2檔案,則在program-1.0目錄同級執行:
bzcat program-2.0.patch.bz2 | patch -p0 |
如此即完成了從1.0到2.0的升級。
如果希望恢複到原版本,可以使用-R(--reverse)參數,但僅對上下文格式的diff檔案有效。還有一個備份參數也可以使用,但簡單應用中,整個目錄備份可能更方便一些。