`
saybody
  • 浏览: 902495 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

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

阅读更多

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-nold-prj/src/drv/drv1.h
1#ifndefDRV1_H
2#defineDRV1_H
3
4#include"def1.h"
5
6typedefstruct{
7intp1;
8intp2;
9intp3;
10}App1;
11
12voiddo_app1(void);
13
14#endif

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

$cat-nnew-prj/src/drv/drv1.h
1#ifndefDRV1_H
2#defineDRV1_H
3
4#include"def1.h"
5
6typedefstruct{
7inta;
8intb;
9}App1;
10
11voiddo_app1(void);
12
13#endif

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

$diff-uold-prj/src/drv/drv1.hnew-prj/src/drv/drv1.h
---old-prj/src/drv/drv1.h2008-03-0112:59:46.000000000+0800
+++new-prj/src/drv/drv1.h2008-03-0113:07:14.000000000+0800
@@-4,9+4,8@@
#include"def1.h"

typedefstruct{
-intp1;
-intp2;
-intp3;
+inta;
+intb;
}App1;

voiddo_app1(void);

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

$diff-u旧文件新文件

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

1.2 diff的 unified 格式

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

---old-prj/src/drv/drv1.h2008-03-0112:59:46.000000000+0800
+++new-prj/src/drv/drv1.h2008-03-0113: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"

typedefstruct{
-intp1;
-intp2;
-intp3;
+inta;
+intb;
}App1;

voiddo_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.h2008-03-0112: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和补丁文件,我们可以方便地保存工作快照。

分享到:
评论

相关推荐

    quilt patch管理

    这些修改通常通过补丁(patch)的形式应用于原始源码之上,以此来适应不同的环境或功能需求。然而,随着补丁数量的增长,管理这些补丁变得日益复杂。为了解决这一问题,`quilt`工具应运而生。本文将详细介绍`quilt`...

    Quilt使用入门介绍.pdf

    lxy@lxy-PC:~/quilt_exercise$ quilt new int-1.diff Patch patches/int-1.diff is now on top 2. 查看当前栈顶补丁文件 lxy@lxy-PC:~/quilt_exercise$ quilt top patches/int-1.diff 3. 将指定文件与当前栈顶...

    openwrt 制作patch

    该文件中包含diff和patch的相关选项。 - 配置Quilt的命令示例如下: ``` cat&gt;~/.quiltrc QUILT_DIFF_ARGS="--no-timestamps --no-index --color=auto" QUILT_REFRESH_ARGS="--no-timestamps --no-index" QUILT...

    quilt-0.46

    用户可以解压该文件,遵循标准的编译和安装流程,例如`./configure`, `make`, `sudo make install`,来在自己的Linux系统上安装这个版本的Quilt。 总的来说,Quilt是Linux环境中进行系统维护和软件开发的强大工具,...

    quilt-0.48.tar.gz

    `quilt`是一款非常实用的补丁管理工具,它主要用于处理和应用Unix/Linux系统的文本文件补丁。在标题提到的`quilt-0.48.tar.gz`文件中,我们看到的是`quilt`工具的0.48版本,这个版本是为Ubuntu操作系统量身定制的。 ...

    Python-Quilt一个数据包管理器像管理代码那样管理数据

    Python-Quilt 是一个专为此目的设计的数据包管理器,它借鉴了软件工程中的代码管理理念,使得数据可以像代码一样被版本控制、共享和复用。通过Python接口,用户能够轻松地组织、检索和分发大数据集,极大地提高了...

    Python库 | quilt3-3.3.0-py3-none-any.whl

    Quilt3库的版本3.3.0是为Python 3设计的,"py3-none-any"表明这个whl文件是针对Python 3的任何平台编译的,意味着无论是在Windows、Linux还是macOS上,只要使用Python 3,都可以安装并运行。.whl文件是Python的一种...

    Quilt使用入门介绍.docx

    Quilt 实际上是一个shell脚本,它可以在源码目录树中的任何位置运行。它会查找名为`.pc`或`patches`的目录来确定补丁的位置。你可以通过设置环境变量`QUILT_PATCHES`来改变Quilt的默认工作路径。例如,如果你想让...

    bbb-preempt-rt-kernel-patch::gear: BeagleBone Black PREEMPT RT 补丁使用 OSADL 补丁和 linux 3.12

    您只需要运行sudo ./bbb-patch.sh start /dev/sdb /dev/sdb 使用所需的设备更改 /dev/sdb工作流程bbb-patch 脚本将: 下载 linux 内核 3.12 和 debian 镜像 (requeriments.sh) 在 Linux 源 (osadl-patch.sh) 上应用...

    Linux Kernel In A Nutshell.pdf

    综上所述,《Linux Kernel In A Nutshell》不仅是一本实用的手册,还是一部深度解析Linux内核的技术指南,它覆盖了从内核构建到定制的各个方面,是所有希望深入了解Linux内核的开发者和用户的宝贵资源。

    linux 2.4.18补丁

    综上所述,通过这些步骤,用户不仅能够成功地在 LN2410SBC 开发板上运行多个不同版本的 Linux 内核,而且还能够了解整个编译和配置过程中的关键技术点。这对于学习嵌入式系统的开发和维护非常有帮助。

    quilt:Hyperledger Quilt-Interledger协议的实现

    为问题加上标签和前缀,以便于识别与之相关的项目。ilp核心 ilp-core模块是任何Interledger Java项目的基础库,提供支持ILPv4协议(在定义)的服务接口,数据包定义和数据模型。 这些原语构成Interledger协议套件的...

    Linux深入浅出系统移植

    理解如何应用和管理补丁,使用 quilt 或 git-patch 等工具是必要的技能。 4. **编译环境配置**:编译环境的设置对于成功移植至关重要。这包括配置内核选项,比如裁剪不必要的模块,以适应目标板的资源限制;设置...

    Quilt:一个数据包管理器,像管理代码那样管理数据-python

    AWS Quilt 的版本化数据门户 open.quiltdata.com 是在 Quilt 上运行的 PB 级开放数据门户 quiltdata.com 包括案例研究、用例、视频以及有关如何运行私有 Quilt 实例的说明用于机器学习中快速实验的版本化数据和模型...

    Quilt-开源

    1. 将Quilt库添加到你的项目依赖中。 2. 配置你的构建脚本(如Ant的build.xml或Maven的pom.xml),以便在执行测试时调用Quilt。 3. 运行测试,Quilt会收集覆盖率数据。 4. 分析生成的覆盖率报告,了解测试的全面性和...

    PyPI 官网下载 | quilt3-3.5.0-py3-none-any.whl

    1. **版本控制**:可以为每个数据集创建版本,确保在开发过程中可以追踪和回溯数据变化。 2. **可重复性**:数据包的定义和内容被记录,使得实验结果可复制,提高了科学研究的可信度。 3. **共享与协作**:通过Quilt...

    PyPI 官网下载 | quilt-2.8.4-py3-none-any.whl

    标题 "PyPI 官网下载 | quilt-2.8.4-py3-none-any.whl" 暗示我们正在讨论的是一个 Python 的第三方库,Quilt,它可以从 Python 的包索引(PyPI)上获取。PyPI 是 Python 开发者发布自己软件包的主要平台,供全球用户...

    PyPI 官网下载 | quilt-lang-0.4.6.tar.gz

    **PyPI 官网下载 | quilt-lang-0.4.6.tar.gz** PyPI(Python Package Index)是Python开发者发布和获取Python软件包的主要平台。`quilt-lang-0.4.6.tar.gz`是一个从PyPI官网下载的压缩文件,它包含了一个名为`quilt...

Global site tag (gtag.js) - Google Analytics