fmddlmyy的专栏

伐木丁丁鸟鸣嘤嘤

吕杰ID:fmddlmyy
80509次访问,排名1186好友2人,关注者5
伐木丁丁鸟鸣嘤嘤
fmddlmyy的文章
原创 40 篇
翻译 0 篇
转载 0 篇
评论 94 篇
fmddlmyy的公告

最近评论
慧军:还是设计自己的UI方案比较好,不能认为WM流行就照抄WM的。WM更大的市场是在商业应用,UI上面就不见得好了。
abbot:very good!
htbegin:很不错!
luxiaoyan:我安装了doxygen, graphviz, 生成的的文档无法显示类图,不知道是怎么回事,请各位帮忙解决一下,谢谢阿。
liaoliaopro:这个应该是表达式求值顺序的问题吧?gcc从左开始求值,vc从右开始求值,最后产生这样的结果。
文章分类
收藏
    相册
    个人主页
    fmddlmyy的留言板
    Mime 手机中文输入引擎
    伐木丁丁鸟鸣嘤嘤——我的个人主页
    历法计算程序
    我的程序
    我的随笔集
    存档
    订阅我的博客
    XML聚合  FeedSky

    原创 Linux之旅(1): diff, patch和quilt (上)收藏

    新一篇: Linux之旅(1): diff, patch和quilt (下) | 旧一篇: 谈谈智能手机软件(2):Linux手机软件

    Linux之旅(1): diff, patch和quilt (上)

    diff和patch是在Linux环境为源代码制作和应用补丁的标准工具。diff可以比较文件或目录的差异,并将差异记录到补丁文件。patch可以将补丁文件应用到源代码上。quilt也是一个制作和应用补丁的工具,它适合于管理较多补丁。quilt有自己的特有的工作方式。本文通过简单的例子介绍这三个常用的工具。

    0 示例工程

    我们先准备一个用来做实验的工程,它包含若干子目录和文件。可以用find命令列出文件清单:

    $ find old-prj/ -type f
    old-prj/inc/def1.h
    old-prj/inc/def2.h
    old-prj/src/sys/sys1.c
    old-prj/src/sys/sys1.h
    old-prj/src/app/app1.c
    old-prj/src/app/app2.c
    old-prj/src/app/app2.h
    old-prj/src/app/app1.h
    old-prj/src/drv/drv1.h
    old-prj/src/drv/drv2.c
    old-prj/src/drv/drv1.c
    old-prj/src/drv/drv2.h
    old-prj/build/Makefile

    find命令的"-type f"参数选择普通文件,可以省略掉目录。希望自己操作的读者可以下载这个示例工程

    1 diff和patch

    1.1 比较一个文件

    将old-prj.tar.bz2放到我们的工作目录,然后建立一个子目录,进入后解压示例工程:

    $ mkdir test1; cd test1; tar xvjf ../old-prj.tar.bz2

    用分号分隔多个命令可以节省篇幅。将old-prj复制到new-prj:

    $ cp -a old-prj/ new-prj

    让我们编辑一个文件。src/drv/drv1.h的内容本来是:

    $ cat -n old-prj/src/drv/drv1.h
         1  #ifndef DRV1_H
         2  #define DRV1_H
         3
         4  #include "def1.h"
         5
         6  typedef struct {
         7    int p1;
         8    int p2;
         9    int p3;
        10  } App1;
        11
        12  void do_app1(void);
        13
        14  #endif

    cat命令的"-n"参数可以增加行号。我们用vi将它修改成:

    $ cat -n new-prj/src/drv/drv1.h
         1  #ifndef DRV1_H
         2  #define DRV1_H
         3
         4  #include "def1.h"
         5
         6  typedef struct {
         7    int a;
         8    int b;
         9  } App1;
        10
        11  void do_app1(void);
        12
        13  #endif

    现在可以用diff命令比较文件了:

    $ diff -u old-prj/src/drv/drv1.h new-prj/src/drv/drv1.h
    --- old-prj/src/drv/drv1.h      2008-03-01 12:59:46.000000000 +0800
    +++ new-prj/src/drv/drv1.h      2008-03-01 13:07:14.000000000 +0800
    @@ -4,9 +4,8 @@
     #include "def1.h"
     
     typedef struct {
    -  int p1;
    -  int p2;
    -  int p3;
    +  int a;
    +  int b;
     } App1;
     
     void do_app1(void);

    diff程序按行比较文本文件。比较文件的diff命令格式是:

    $ diff -u 旧文件 新文件

    "-u"参数指定diff命令使用 unified 格式,这是一种最常用的格式,我们来看看它的含义。

    1.2 diff的 unified 格式

    以"---"开头的行是旧文件信息,以"+++"开头的行是新文件信息:

    --- old-prj/src/drv/drv1.h      2008-03-01 12:59:46.000000000 +0800
    +++ new-prj/src/drv/drv1.h      2008-03-01 13:07:14.000000000 +0800

    unified 格式默认在变化部分的前后各显示三行上下文。在上例中,旧文件的7、8、9行被替换成新文件的7、8行。旧文件的变化部分是7-9行,前后多显示3行,因此显示4-12行。新文件的变化部分是7-8行,前后多显示3行,因此显示4-11行。以"@@"包围的行指示补丁的范围:

    @@ -4,9 +4,8 @@

    '-4,9'中,'-'表示旧文件,'4,9'表示从第4行开始,显示9行,即显示4-12行。'+4,8'中,'+'表示新文件,'4,8'表示从第4行开始,显示8行,即显示4-11行。"@@"行之后是上下文和变化的文本,其中'-'开头的行是旧文件特有的,'+'开头的行是新文件特有的,其它行是两个文件都有的,即补丁的上下文。例如:

     #include "def1.h"
     
     typedef struct {
    -  int p1;
    -  int p2;
    -  int p3;
    +  int a;
    +  int b;
     } App1;
     
     void do_app1(void);

    1.3 制作和应用补丁

    所谓制作补丁就是diff的输出重定向到一个文件,这个文件就是补丁文件。例如:

    $ diff -u old-prj/src/drv/drv1.h new-prj/src/drv/drv1.h>../drv1.diff

    我们将old-prj解压到另一个目录,准备应用这个补丁:

    $ cd ..; mkdir test2; cd test2; tar xvjf ../old-prj.tar.bz2; mv old-prj myprj; cd myprj

    在真实场景中,test2目录通常是在用户2的电脑上。用户2可能不使用 old-prj 作为第一级目录的名字。例如:用户1的第一级目录名是 linux-2.6.23.14, 用户2的第一级目录名是linux。所以我们将 old-prj 改为 myprj 以模拟这种情况。

    我们在 myprj 目录使用patch命令应用补丁:

    $ patch -p1 < ../../drv1.diff
    patching file src/drv/drv1.h

    patch命令行中为什么没有出现要打补丁的文件?这是因为patch命令可以使用补丁文件中的文件信息:

    --- old-prj/src/drv/drv1.h      2008-03-01 12:59:46.000000000 +0800

    "-pn"参数(上例中n=1)中的n表示要从补丁文件的文件路径中去掉几层目录,可以理解为去掉几个'/'。例如:p1表示去掉一层目录,"old-prj/src/drv/drv1.h"去掉一层就成为"src/drv/drv1.h"。patch命令在 myprj 目录找到"src/drv/drv1.h"后应用补丁。

    我们通常都在代码树的上一层目录制作补丁,在代码树的根目录应用补丁。因此,最常用的patch命令格式是:

    $ patch -p1 < 补丁文件

    1.4 比较目录

    我们回到test1目录,再对 new_prj 做一些改动。这次我们删除掉src/sys目录及其中的文件。再建立src/usr目录,并在该目录增加两个文件usr1.h和usr1.c。

    $ cd ../../test1; rm -rf new-prj/src/sys; mkdir new-prj/src/usr
    $ echo -e "#ifndef USR1_H\n#define USR1_H\n#include \"def1.h\"\n#endif">new-prj/src/usr/usr1.h
    $ echo -e "#include \"usr1.h\"">new-prj/src/usr/usr1.c

    echo命令的"-e"参数打开对转义符的支持,bash默认是不支持转义符的。

    现在我们比较目录并制作补丁:

    $ diff -Nur old-prj/ new-prj/ > ../prj.diff

    读者可以cat这个补丁文件的内容。根据前面的介绍,读者应该能看懂补丁文件了吧。

    比较目录的常用命令是:

    $ diff -Nur 旧目录 新目录 > 补丁文件

    $ diff -Naur 旧目录 新目录 > 补丁文件

    "-u"参数前面已经介绍过了。"-N"参数将不存在的文件当作空文件。如果没有这个参数,补丁就不会包含孤儿文件(即另一方没有的文件)。"-r"参数表示比较子目录。"-a"参数表示将所有文件当作文本文件。

    我们再准备一个目录来应用补丁:

    $ cd ..; mkdir test3; cd test3; tar xvjf ../old-prj.tar.bz2; mv old-prj myprj; cd myprj

    在源代码树的根目录应用补丁:

    $ patch -p1 < ../../prj.diff
    patching file src/drv/drv1.h
    patching file src/sys/sys1.c
    patching file src/sys/sys1.h
    patching file src/usr/usr1.c
    patching file src/usr/usr1.h

    好了,读者可以用"diff -Nur"比较一下"test1/new_prj"和"test3/myprj",没有输出就表示完全相同。

    $ cd ../..; diff -Nur test1/new-prj test3/myprj

    1.5 很多的补丁...

    一个大项目可能有不同开发者提供很多补丁。这些补丁可能还存在依赖关系,例如补丁B必须打在补丁A上。我们当然可以凭着程序员的“心细如发”去管理好这些补丁,不过有一个叫quilt的工具可以使我们轻松一些。当然,即使有工具的帮助,细心和认真也是必需的。 

    附录

    为了简单起见,前面只介绍了一个"diff -Nur 老目录 新目录"的用法。有时候,新目录里只放了修改过的文件。这时可以不使用-N参数以忽略孤儿文件,即"diff -ur 老目录 新目录"。diff会输出孤儿文件的提示,我们可以删除或保留这些提示,它们对patch没有影响。

    使用diff时可以用--exclude排除文件和目录,例如:

    diff -ur -exclude=.* --exclude=CVS prj_old prj_new

    上例排除了源代码树中以'.'开头的文件和所有CVS目录。其实对于CVS项目,可以直接在源代码树根目录中执行:

    cvs diff -u3 > 补丁文件名

    u3表示输出3行上下文的unified 格式。打补丁时在源代码树根目录中执行:

    patch -p0 < 补丁文件名

    "cvs diff"会自动忽略CVS项目外的文件。通过CVS的tag和补丁文件,我们可以方便地保存工作快照。

    发表于 @ 2008年03月02日 21:38:00|评论(loading...)|编辑

    新一篇: Linux之旅(1): diff, patch和quilt (下) | 旧一篇: 谈谈智能手机软件(2):Linux手机软件

    评论:没有评论。

    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © fmddlmyy