作者:Sam (甄峰) sam_code@hotmail.com
0. 基础知识:
diff和patch是一对对应工具。从某种意义来说,diff就像是差值运算,patch就像是和运算。
diff命令比较两个文件或者连个文件夹的差异,并记录到到一个文件中,它就是patch文件,即补丁文件。
patch命令可以将补丁文件和之前的两个元素之一组合,形成另一个集合。
1. diff使用实例:
1.1:diff 两个文件:
diff a_file b_file > patch_file
这里把a_file叫做原始文件,b_file当作修改后的文件。patch_file则是A的补丁文件。
有两个文件,a.cpp, b.cpp
他们内容相近,只是a.cpp的21,22行, b.cpp的21-24行内容不同。
a.cpp:
#include
#include
#include
#include
#include
#include
#include
#include
#include
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
static void l2cap_tx(struct l2cap_chan *chan, struct
l2cap_ctrl *control,
struct sk_buff_head
*skbs, u8 event);
b.cpp:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn
*conn,
u8
code, u8 ident, u16 dlen, void *data);
static void l2cap_send_cmd(struct l2cap_conn *conn, u8
ident, u8 code, u16 len,
void *data);
$diff a.cpp b.cpp > test.patch
test.patch内容为:
11a12
> #include
21,22c22,25
< static void l2cap_tx(struct l2cap_chan *chan, struct
l2cap_ctrl *control,
<
struct sk_buff_head *skbs, u8 event);
---
> static struct sk_buff *l2cap_build_cmd(struct l2cap_conn
*conn,
>
u8 code, u8 ident, u16 dlen, void *data);
> static void l2cap_send_cmd(struct l2cap_conn *conn, u8
ident, u8 code, u16 len,
>
void *data);
解读:
11a12
> #include
原始文件a.cpp11行后添加一行。内容如上。
21,22c22,25
< static void l2cap_tx(struct
l2cap_chan *chan, struct l2cap_ctrl *control,
<
struct
sk_buff_head *skbs, u8 event);
---
> static struct sk_buff
*l2cap_build_cmd(struct l2cap_conn *conn,
>
u8 code, u8
ident, u16 dlen, void *data);
> static void
l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16
len,
>
void
*data);
原始文件a.cpp21-22行修改为后面的22-25行内容。
这个格式虽然简单,但看起来有乱,有时候想加入上下文,这时,可以加入-c
. 则会加入上下文。缺省是加入三行。也可以自己指定,如-c5. 表示加入5行上下文。
$diff -c2 a.cpp b.cpp >
test.patch
test.patch内容为:
*** a.cpp
2016-01-19 17:36:58.371134156 +0800
--- b.cpp
2016-01-19 17:37:01.516312351 +0800
***************
*** 10,13 ****
--- 10,14 ----
#include
#include
+ #include
bool disable_ertm;
***************
*** 19,22 ****
static DEFINE_RWLOCK(chan_list_lock);
! static void l2cap_tx(struct l2cap_chan *chan, struct
l2cap_ctrl *control,
!
struct sk_buff_head *skbs, u8 event);
--- 20,25 ----
static DEFINE_RWLOCK(chan_list_lock);
! static struct sk_buff *l2cap_build_cmd(struct l2cap_conn
*conn,
!
u8 code, u8 ident, u16 dlen, void *data);
! static void l2cap_send_cmd(struct l2cap_conn *conn, u8
ident, u8 code, u16 len,
!
void *data);
和之前类似,不需要讲解。
+表示增加line.
- 表示删除line.
! 表示要交换的某line.
思考:
diff a b >c 与 diff b a > c。
那两个c会相同的么?
当然不相同。他们更应该是一个相反的数据。
例如:
diff b.cpp a.cpp > test1.patch -c2
那test1.patch内容如下:
*** b.cpp
2016-01-19 17:37:01.516312351 +0800
--- a.cpp
2016-01-19 17:36:58.371134156 +0800
***************
*** 10,14 ****
#include
#include
- #include
bool disable_ertm;
--- 10,13 ----
***************
*** 20,25 ****
static DEFINE_RWLOCK(chan_list_lock);
! static struct sk_buff *l2cap_build_cmd(struct l2cap_conn
*conn,
!
u8 code, u8 ident, u16 dlen, void *data);
! static void l2cap_send_cmd(struct l2cap_conn *conn, u8
ident, u8 code, u16 len,
!
void *data);
--- 19,22 ----
static DEFINE_RWLOCK(chan_list_lock);
! static void l2cap_tx(struct l2cap_chan *chan, struct
l2cap_ctrl *control,
!
struct sk_buff_head *skbs, u8 event);
与test.patch刚好是相对的。
1.2: diff 两个目录:
比较两个目录时,要注意一个参数
-r:
recursively compare any subdirectories found. 递归的比较子目录。
有一个目录Dir_top, 其中包含两个自目录:Dir_A, Dir_B.
#diff -rc Dir_A Dir_B > dir.patch
dir.patch文件内纪录了是哪些目录,那些文件比较:
diff -rc Dir_A/SerialPort_Utils.h
Dir_B/SerialPort_Utils.h
*** Dir_A/SerialPort_Utils.h
2016-01-20 12:23:57.634223761 +0800
--- Dir_B/SerialPort_Utils.h
2016-01-20 12:32:48.352736912 +0800
2. patch使用实例:
2.1:patch 文件和补丁:
patch命令用于根据原文件和补丁文件生成新的目标文件。
所以,如果diff a b > c
那么patch a c 则得到b.
以上面a.cpp, b.cpp, test.patch为例。
$patch < test.patch
patching file a.cpp
此时,a.cpp的内容根据test.patch修改为b.cpp了。
patch是如何知道原始文件是谁呢?因为在patch文件中已记录了。
此时,a.cpp的内容被修改为目标文件b.cpp ,
但如果我们想找回原始a.cpp内容呢?
patch a.cpp < test.patch -R
此时a.cpp内容又变回原始内容了。
还是用a.cpp, b.cpp, test.patch为例。
patch a.cpp < test.patch
//把a.cpp内容根据补丁,变化到b.cpp去。(原始变目标)
patch -R b.cpp < test.patch
//把b.cpp的内容根据补丁,变化到a.cpp去。(目标变原始)
2.2:两个目录补丁操作:
与1.2的目录结构类似:
有一个目录Dir_top,
其中包含两个自目录:Dir_A, Dir_B.
Dir_top下还有Dir_A与Dir_B diff出来的补丁。dir.patch.
在Dir_top目录下:
patch -p0 <
dir.patch
则给Dir_A打上补丁。
如果打上补丁又后悔了,想恢复原状。同样用 -R
patch -p0 < dir.patch
-R
2.3: 关于patch时的目录层级和-pNum 的关系。
如上面1.2, 2.2的情况:
有一个目录Dir_top, 其中包含两个自目录:Dir_A,
Dir_B.
Dir_top下还有Dir_A与Dir_B
diff出来的补丁。dir.patch.
这个dir.patch是在Dir_top目录下打得。所以patch文件中包含了文件的目录信息,Dir_A, Dir_B
*** Dir_A/hi_audio.cpp
2016-01-20 12:23:57.617222781 +0800
--- Dir_B/hi_audio.cpp
2016-01-20 12:28:32.630397684 +0800
那么此时,如果我们把这个dir.patch文件放在Dir_top根目录下。则文件内的目录结构和patch文件所在位置是符合的。patch
-p0 <
dir.patch
但很多情况下,我们会把patch文件copy
到原文件目录内,Dir_A中去。此时,文件内的目录结构就和patch文件所在位置无法对应了。因为它找不到Dir_A/hi_audio.cpp 文件。
所以,可以采用:patch
-p1 <
dir.patch
-p1: patch会忽略掉第1个”/”之前的内容,认为原始文件是hi_audio.cpp。
这样就可以找到原始文件了。
3.
patch文件格式:
补丁头:
成对出现的文件,上面的是原始文件,后面的是目标文件。
例:
---- a.cpp
2016-01-20 00:00:02.433388026
+0800
+++ b.cpp
2016-01-20 00:02:01.253181756
+0800
或者:
*** a.cpp
2016-01-20 00:00:02.433388026
+0800
--- b.cpp
2016-01-20 00:02:01.253181756
+0800
又或者:
diff -rc Dir_A/hi_audio.cpp
Dir_B/hi_audio.cpp
*** Dir_A/hi_audio.cpp
2016-01-20 12:23:57.617222781 +0800
--- Dir_B/hi_audio.cpp
2016-01-20 12:28:32.630397684 +0800
每个补丁文件中,可以包含多个补丁头。
补丁块:
块是补丁中要修改的地方。它通常由一部分不用修改的东西开始和结束(上下文部分,由diff参数
-c,-u指定)。
块会缩进一列,而这一列是用来表示这一行是要增加还是要删除的。
块的第一列
+号表示这一行是要加上的。
-号表示这一行是要删除的。
没有加号也没有减号表示这里只是引用的而不需要修改。