Quantcast
Channel: Sam的技术Blog
Viewing all articles
Browse latest Browse all 158

diff和patch的使用

$
0
0
作者: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指定)。

块会缩进一列,而这一列是用来表示这一行是要增加还是要删除的。

块的第一列

+号表示这一行是要加上的。

-号表示这一行是要删除的。

没有加号也没有减号表示这里只是引用的而不需要修改。



 

Viewing all articles
Browse latest Browse all 158

Trending Articles