diff patch制作补丁打补丁
對于開源源碼修改過程中的必經(jīng)階段:對源碼打補丁,總是不夠精通,搜索了補丁的原理的詳細過程,如下:
在移植或版本升級過程中,手動比對(用比對工具)轉換是很費力的事情,特別是發(fā)生變化的文件非常多的情況下,“制作補丁、打補丁”可以簡化這個過程。主要用到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 選項確保補丁文件將正確地處理已經(jīng)創(chuàng)建或刪除文件的情況。
-u 選項以統(tǒng)一格式創(chuàng)建補丁文件,這種格式比缺省格式更緊湊些。
一般 -uN 是一直使用的參數(shù),而 -r 如果是含子目錄就使用,不含則不使用。
2、patch
------------------
NAME
?????? patch - apply a diff file to an original
SYNOPSIS
?????? patch [options] [originalfile[patchfile]]
?????? but usually just
???????patch -pnum?<patchfile
?????? 帶下劃線的代表需要根據(jù)實際情況替換。比如 -pnum?實際使用時一般為 -p0, -p1。
------------------
簡單的說,patch就是利用diff制作的補丁來實現(xiàn)源文件(夾)和目的文件(夾)的轉換。這樣說就意味著你可以由源文件(夾)――>目的文件(夾),也可以目的文件(夾)――>源文件(夾)。下面介紹幾個最常用選項:
-pnum?是指查找patch文件中指定的文件時,忽略前num個目錄,一個"/"為一層,詳細內(nèi)容下面解釋。
-R 選項說明在補丁文件中的“新”文件和“舊”文件現(xiàn)在要調(diào)換過來了(實際上就是給新版本打補丁,讓它變成老版本)
-E 選項說明如果發(fā)現(xiàn)了空文件,那么就刪除它
3、patch文件的結構
(1)補丁頭
補丁頭是分別由---/+++開頭的兩行,用來表示要打補丁的文件。---開頭表示舊文件,+++開頭表示新文件。
一個補丁文件中可能包含以---/+++開頭的很多節(jié),每一節(jié)用來打一個補丁。所以在一個補丁文件中可以包含好多個補丁。
--- 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內(nèi)核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 <arch.patch 代表忽略0層目錄,即從當前目錄中查找linux-2.6.25_android/arch/alpha/boot/misc.c,然后進行patch操作。
patch -p1 <arch.patch 代表忽略一層目錄,即從從當前目錄中查找arch/alpha/boot/misc.c,然后進行patch操作。為了能找到文件,當前目錄應轉到arch所屬的目錄下。
patch -p2 <arch.patch 代表忽略兩層目錄,即從從當前目錄中查找alpha/boot/misc.c,然后進行patch操作。為了能找到文件,當前目錄應轉到alpha所屬的目錄下。
以此類推,patch的目錄不限,可以指定patch的目錄。
這個功能使用的情景是:打補丁時的目錄結構/目錄名跟現(xiàn)在要打補丁的目錄結構/目錄名不一樣。
比如上面的例子,這個patch很可能是別人打的,打補丁時目錄為 linux-2.6.25/arch,而本地的目錄可能是linux_kernel_2625,
那打補丁時就可以進入linux_kernel_2625,用命令patch -p1 <arch.patch 忽略第一層目錄即可打上補丁而無需修改自己的目錄名或結構也不需要修改patch文件。
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、為內(nèi)核打補丁
(1)首先是解壓,因為發(fā)布的補丁文件都是使用gzip壓縮的。
$gunzip ../setup-dir/ patch-2.4.21-rmk1.gz
(2)然后進入你的內(nèi)核源代碼目錄
$cd linux-2.4.21
(3)打補丁
$patch –p1 < ../../setup-dir/patch-2.4.21-rmk1
打完補丁后,需要檢查一下有沒有拒絕執(zhí)行的文件,即檢查.rej文件的存在。使用命令:
$find -name *.rej
打補丁時候的常見錯誤-------------------
當用patch命令來打一個補丁的時候,它試圖以不同的方法來驗證這個文件的完整性。
檢查這個文件是一個有效的patch文件并且檢查這些被改變周圍的代碼是不是和提供的
上下文相匹配。這些僅僅是patch所作的兩個最基本的完整性檢查。
如果patch遇到了一些看起來不正確的事情,那么它有兩種選擇。它可以拒絕應用這些改變并且
異常中斷或者它試圖找到一個方法來使patch命令僅僅做一些比較小的改變。
一個patch試圖修正錯誤的例子就是:如果所有的上下文都匹配,被改變的行匹配,但是這些行的
行號不匹配。這是可能發(fā)生的,例如,如果patch在一個文件的中間做了一些改變,但是出于一些
原因在文件的開頭處一些行被添加了進來或者被刪除了。在這種情況下,一切看起來都很好,它只
是簡單地上下移動一點,這時候patch通常會修正這些行號并且打上這個補丁。
任何時候,只要patch在打補丁的時候需要改動文件的一些內(nèi)容,它就會告訴你說:
這個補丁打得有點兒混亂。你應該對這些改變保持一些警惕,因為即使補丁很可能被正確
地打上了,但是情況并不總是這樣,有些情況下結果會是錯誤的。
當patch命令遇到一個變化而不能進行使用一種模糊的方法進行彌補的時候,它就會徹底地
放棄這個動作,并且留下來一個以.rej為擴展名的文件。你可以閱讀這個文件來查看到底是
什么改變不能進行下去,從而在你愿意的情況下來手動修補它。
如果你的內(nèi)核源代碼上沒有應用任何第三方的補丁,只是一些來自kernel.org的補丁,并且你打這些補丁
的順序是正確的,而且你自己沒有對這些源文件進行改動過,那么你應該就不會看到一個補丁
對這個文件的模糊的改變或者是一些拒絕消息。如果你確實看到了這些消息的話,那么將有非常高的
危險性,這說明或者是你的本地的源代碼書或者是補丁文件在某些方面被玷污了。在這種情況下,你很可能
是應該重新下載這個補丁文件,如果事情仍然還是保持原樣的話,那么我建議你去嘗試從kernel.org上
下載一個完整的新的源代碼樹。
讓我們來看一下補丁可能產(chǎn)生的更多信息。
如果patch命令停下來并且顯示一個“File?to?patch”的提示符,那么這個時候patch命令找不到
要打補丁的文件。很可能的情況是你忘記指定-p1參數(shù)或者你處于一個錯誤的目錄中了。更加不
常見的一種情況是,你會發(fā)現(xiàn)一些補丁需要使用-p0參數(shù)而不是-p1參數(shù)來打補丁(閱讀這個補丁文件
應該能揭示出這些信息--如果是這樣的話,這是一種創(chuàng)建補丁文件的人所犯的錯誤,但是不致命)
如果你得到信息“Hunk?#2?succeeded?at?1887?with?fuzz?2?(offset?7?lines)”,或者是一個類似
的消息,那就意味著patch命令必須調(diào)整改變的位置(在這個例子中,它需要在它想打補丁的地方移動7行來
適應這個補丁)。結果得到的文件可能正確也可能不正確,這決定于這個文件與所期望的文件不相同的原因。
這常常發(fā)生于你所要打的patch產(chǎn)生于一個另外一個內(nèi)核版本,這個內(nèi)核版本和你要打的patch所基于的內(nèi)核
版本不同。
如果你得到一個類似于“Hunk?#3?FAILED?at?2387”之類的消息,那么這就意味著不能正確地打上這個
補丁,并且patch程序也不能模糊地通過。這將產(chǎn)生一個導致patch失敗的.rej文件并且產(chǎn)生一個.orig
文件把一些不能改變的原始內(nèi)容顯示給你。
如果你得到的信息是:“Reversed?(or?previously?applied)?patch?detected!??Assume?-R??[n]”,
那么patch檢測到了這個補丁文件中包含的改變已經(jīng)應用在了目標文件上。如果你確實已經(jīng)在此之前打了
這個補丁并且重新打補丁遇到了錯誤,那么你可以簡單地選擇[n]o并且終止這次補丁動作。如果你之前
打了補丁并且想得到打補丁以前的版本,但是忘記了指定-R參數(shù),那么你在這里可以回答[y]es來使用patch
為你恢復它。這也可能發(fā)生在補丁文件的創(chuàng)建者在創(chuàng)建補丁文件的時候倒置了源文件和目標目錄的位置,
在這種情況下從patch中revert實際上是打上了這個補丁。
一個類似于“patch:?****?unexpected?end?of?file?in?patch”或者“patch
unexpectedly?ends?in?middle?of?line”的消息意味著patch命令對你加入到它之中的
文件覺得沒有意義?;蛘呤悄愕南螺d被打斷了,你試圖打上一個沒有壓縮的patch文件,而事前
并沒有解壓縮它,或者是你使用的補丁文件在傳輸?shù)哪硞€地方被一個郵件客戶端或者一個郵件
傳輸代理給損壞了。例如,通過把一個長行分成兩行。通常情況下這些警告可以通過把這兩個
被分開的行合并起來來解決。
就像我上面提到的那樣,如果你打的是從kernel.org得來的補丁到一個正確的版本,
并且你沒有修改過源代碼樹,這些錯誤將從來不會發(fā)生。因此如果你從kernel.org來的
補丁上得來了這些錯誤,那么你應該很可能認為或者是你的補丁文件或者是源代碼樹
損壞掉了。我建議你重新開始下載一個完整的內(nèi)核樹以及你想要打的補丁2.6.x內(nèi)核
---------
這是Linus發(fā)布的基礎穩(wěn)定版本.發(fā)布的最高版本是最新的。
如果發(fā)現(xiàn)了沖突或者嚴重的瑕疵,那么在這個基礎上,一個-stable的修正補丁就會被發(fā)布
出來(參見下面)。一旦一個新的2.6.x的基礎內(nèi)核發(fā)布出來,就可以得到一個測試版本的補丁
,這個補丁基于先前的2.6.x版本內(nèi)核和這個新的內(nèi)核。
為了應用一個從2.6.11到2.6.12的補丁,你最好按照下面來做(注意這些補丁不能應用于2.6.x.y的內(nèi)核,
而是應用在2.6.x的基礎內(nèi)核---如果你需要從2.6.x.y到2.6.x+1,那么你首先需要卸載掉2.6.x.y的補丁)
下面是一些例子:
#從2.6.11到2.6.12
$?cd?~/linux-2.6.11????????????#?切換到內(nèi)核源代碼目錄
$?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????????????#?切換到內(nèi)核源代碼目錄
$?patch?-p1?-R?<?../patch-2.6.11.1????#?恢復出來2.6.11.1
????????????????????#?源代碼目錄現(xiàn)在是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內(nèi)核
-----------
帶有四位數(shù)字版本號的內(nèi)核是-stable的內(nèi)核。他們包含了對一個給定的2.6.x內(nèi)核的一些安全
問題以及發(fā)現(xiàn)的重要的退化的修復。
對于那些想要最近的穩(wěn)定內(nèi)核并且對于測試開發(fā)中的試驗性的版本沒有興趣的
用戶來說,我們推薦這個分支。
如果沒有可用的2.6.x.y內(nèi)核,那么最高數(shù)字的2.6.x內(nèi)核是目前的穩(wěn)定內(nèi)核。
注意:維護穩(wěn)定內(nèi)核的團隊通常會做一些增量的補丁,就像是基于最近的主流版本發(fā)布
的補丁一樣。但是在下面我僅僅說明了非增量的情況。那些增量式的版本可以在下面的ftp
處找到:?ftp://ftp.kernel.org/pub/linux/kernel/v2.6/incr/.
這些補丁不是增量式的,意味著例如對于2.6.12.3補丁不能應用于2.6.12.2的內(nèi)核源代碼
上去,但是可以應用在2.6.12內(nèi)核代碼上。
因此,為了為了把2.6.12.3的補丁應用到你使用的2.6.12.2的內(nèi)核源代碼上,你不得不卸載掉
2.5.12.2補丁(因此你可以得到一個基礎的2.6.12的內(nèi)核源代碼),并且應用新的2.6.12.3補丁。
下面是一個小例子:
$?cd?~/linux-2.6.12.2????????????#?切換到內(nèi)核源代碼目錄
$?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????#?重新命名內(nèi)核源代碼目錄
-rc內(nèi)核
-------
這些是候選的發(fā)布內(nèi)核。當Linus認為目前的git(內(nèi)核的源代碼管理工具)內(nèi)核樹處于一個
健全的穩(wěn)定狀態(tài)足以用來測試的時候,而發(fā)布的開發(fā)內(nèi)核。
這些內(nèi)核是不穩(wěn)定的,如果你試著運行他們應該會想到可能會不時地有問題出現(xiàn)。
但是這是主開發(fā)分支上的最穩(wěn)定的內(nèi)核,并且最終會變成下一個穩(wěn)定的內(nèi)核。因此
讓盡可能多的人來測試它就顯得格外重要。
對于那些想幫忙測試開發(fā)中的內(nèi)核但是又不想跑那些試驗性的東西的人來說,這將是
一個非常好的分支。(這樣的人應該參照下面的關于-git和-mm內(nèi)核的部分)
-rc補丁是非增量式的,他們應用于2.6.x內(nèi)核上,就像上面描述的2.6.x.y內(nèi)核一樣。在-rcN
后綴之前的內(nèi)核版本號代表了這個-rc的內(nèi)核最終會變成的內(nèi)核版本。
因此,2.6.13-rc5意思是這是2.6.13內(nèi)核的第五個候選的發(fā)布版本,并且這個補丁應該打在
2.6.12的內(nèi)核源代碼上。
下面是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????#?重新命名源代碼目錄
#?現(xiàn)在從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????????????#?切換到內(nèi)核源代碼目錄
$?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內(nèi)核
--------
這些是每天Linus的內(nèi)核樹的快照(在一個git倉庫中管理著,因此得名)。
這些補丁通常每天都發(fā)布而且代表了的Linus的內(nèi)核樹的當前狀態(tài),由于它們是自動產(chǎn)生的
甚至沒有任何一個光標的騷動來看它們是不是健全的,所以它們比-rc內(nèi)核更具有試驗性。
-git補丁不是增量的,它們或者是應用在2.6.x內(nèi)核上或者是應用在一個基礎的
2.6.x-rc內(nèi)核上---這一點你可以從他們的名字上看出來。一個名字是2.6.12-git1的
補丁應用在2.6.12內(nèi)核源代碼上,一個名字為2.6.13-rc3-git2的補丁應用在2.6.13-rc3
的內(nèi)核源代碼上。
這里是一些怎樣打這些補丁的例子:
#?從2.6.12遷移到2.6.12-git1
$?cd?~/linux-2.6.12????????????#?切換到內(nèi)核源代碼目錄
$?patch?-p1?<?../patch-2.6.12-git1????#?應用2.6.12-git1補丁
$?cd?..
$?mv?linux-2.6.12?linux-2.6.12-git1????#?重新命名內(nèi)核源代碼目錄
#?從2.6.12-git1遷移到2.6.13-rc2-git3
$?cd?~/linux-2.6.12-git1????????#?切換到內(nèi)核源代碼目錄
$?patch?-p1?-R?<?../patch-2.6.12-git1????#?回返2.6.12-git1補丁
????????????????????#?我們現(xiàn)在有了一個2.6.12內(nèi)核
$?patch?-p1?<?../patch-2.6.13-rc2????#?打上2.6.13-rc2補丁
????????????????????#?內(nèi)核現(xiàn)在是2.6.13-rc2
$?patch?-p1?<?../patch-2.6.13-rc2-git3????#?打上2.6.13-rc2-git3補丁
????????????????????#?內(nèi)核現(xiàn)在是2.6.13-rc2-git3
$?cd?..
$?mv?linux-2.6.12-git1?linux-2.6.13-rc2-git3????#?重新命名內(nèi)核源代碼目錄
-mm內(nèi)核
-------
這是Andrew?Morton發(fā)布的實驗性的內(nèi)核
-mm樹作為一個新特性和實驗性的補丁的實驗場。一旦一個補丁在-mm中經(jīng)過一段時間被證明
有價值,為了使它能包含在主流內(nèi)核中,Andrew就會把它推給Linus。
盡管鼓勵的方法是通過-mm樹把補丁推給Linus,這個步驟并不是總被實行。子系統(tǒng)的維護者
(或者個人)有些時候直接把補丁推給Linus,盡管(或者之后)它們已經(jīng)被它并到了-mm中并得
到了測試(或者有些時候并沒有事前在-mm中得到測試)。
通常情況下你應該盡力使你的補丁通過-mm中最大程度測試后再到達主流內(nèi)核中。
這個分支是一個持續(xù)的變化并且包含了一些實驗性的特征,很多正在debug的補丁并不適合于
主流的內(nèi)核等等。這個分支是這個文檔中描述的最具有試驗性的分支。
這些內(nèi)核內(nèi)核不適合于應用在要求穩(wěn)定的系統(tǒng)上面,并且在運行中比其他任何的分支都可能承擔
更大的風險(確信你有最新的備份---跟蹤了任何試驗性的內(nèi)核但是甚至更多,于是達到-mm內(nèi)核)。
這些內(nèi)核除了包含所有的試驗性的補丁以外,它們還包含了在主流-git內(nèi)核發(fā)布的時候任何
可用的改變。
對-mm內(nèi)核測試會得到極大的賞識,因為這個分支的總的目的就是為了在改變被加到更加穩(wěn)定的
主流的Linus內(nèi)核樹之前,消除退化、死機、數(shù)據(jù)失敗bug、build失敗(以及任何通常意義上的bug)。
但是-mm的測試者應該清醒地認識到這個源代碼樹中的失敗會比其他任何樹中的都要普遍。
-mm內(nèi)核并不會以一個固定的時間發(fā)布,但是通常一些-mm內(nèi)核會在每一個-rc內(nèi)核(通常1到3個)
發(fā)布的中間。-mm內(nèi)核或者是應用于一個基礎的2.6.x內(nèi)核(當還沒有-rc內(nèi)核發(fā)布的時候)或者應用于
一個Linus?-rc的內(nèi)核。
這里有一個打-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補丁
????????????????????#?現(xiàn)在我們得到了一個2.6.12的源文件
$?patch?-p1?<?../patch-2.6.13-rc3????#?打一個2.6.13-rc3的補丁
????????????????????#?我們現(xiàn)在得到一個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????#?重新命名源文件目錄
上面總結了不同內(nèi)核樹的一些解釋。我希望你已經(jīng)明白了怎樣打不同的補丁并且對你測
試內(nèi)核有所幫助。
致謝列表:
Randy?Dunlap,?Rolf?Eike?Beer,?Linus?Torvalds,?Bodo?Eggert,
Johannes?Stezenbach,?Grant?Coady,?Pavel?Machek,還有一些人我可能忘記了他們的名字,
但是他們對這篇文檔進行了評論或者貢獻。
總結
以上是生活随笔為你收集整理的diff patch制作补丁打补丁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gdb的简单使用和gdb+gdbserv
- 下一篇: 成功移植mplayer到mini2440