2010-11-24

补丁中的二进制文件

有的时候,需要将对代码的改动以补丁文件的方式进行传递,最终合并入版本库。例如直接在软件部署目录内进行改动,再将改动传送到开发平台。或者是因为在某个开源软件的官方版本库中没有提交权限,需要将自己的改动以补丁文件的方式提供给官方。 关于补丁文件的格式,补丁的生成和应用在我们第一章 TODO 已经进行了介绍,使用的是 Gnu diff 和 patch 命令。很多版本控制系统也可以生成 GNU 兼容的 diff 文件,例如 svn diff 命令。但是 GNU 的 diff 格式有一个不足:不支持二进制文件。即如果有二进制文件发生了变化或者增加了新的二进制文件,在差异文件中无法体现,这样的差异文件应用到代码树中,二进制文件丢失了! Git 突破了传统 DIFF 格式的限制,通过引入新的 DIFF 格式,实现了对二进制文件的支持。

8.3.2   对非 Git 版本库中二进制文件变更的支持

不在 Git 版本库中的文件和目录可以比较生成 Git 格式的补丁文件么,以及可以执行应用补丁(apply patch)的操作么? 是的,Git 的 diff 命令和 apply 命令支持对非 Git 版本库/工作区进行操作。但是当前 Git 最新版本(1.7.3)的 git apply 命令有一个 bug,这个 bug 导致目前的 git apply 命令只能引用 patch level(补丁文件前缀级别) 为 1 的补丁。我已经将改正这个 Bug 的补丁文件提交到 Git 开发列表中,参见: 下面的示例演示一下如何对非 Git 版本库使用 git diffgit patch 命令。首先我们准备两个目录,一个为 hello-1.0 目录,在其中我们创建一个文本文件以及一个二进制文件。
$ mkdir hello-1.0
$ echo hello > hello-1.0/readme.txt
$ dd if=/bin/ls of=hello-1.0/binary.dat count=1 bs=32
记录了1+0 的读入
记录了1+0 的写出
32字节(32 B)已复制,0.0001026 秒,312 kB/秒
另外一个 hello-2.0 目录,其中的文本文件和二进制文件都有所更改。
$ mkdir hello-2.0
$ printf "hello\nworld\n" > hello-2.0/readme.txt
$ dd if=/bin/ls of=hello-2.0/binary.dat count=1 bs=64
记录了1+0 的读入
记录了1+0 的写出
64字节(64 B)已复制,0.0001022 秒,626 kB/秒
然后我们执行 git diff 命令。命令中的 --no-index 参数对于不在版本库中的目录/文件进行比较时可以省略。我们还用了 --no-prefix 参数,这样就可以生成前缀级别(patch level)为 1 的补丁文件。
$ git diff --no-index --binary --no-prefix hello-1.0 hello-2.0 > patch.txt
$ cat patch.txt
diff --git hello-1.0/binary.dat hello-2.0/binary.dat
index dc2e37f81e0fa88308bec48cd5195b6542e61a20..bf948689934caf2d874ff8168cb716fbc2a127c3 100644
GIT binary patch
delta 37
hcmY#zn4qBGzyJX+<}pH93=9qo77QFfQiegA0RUZd1MdI;

delta 4
LcmZ=zn4kav0;B;E

diff --git hello-1.0/readme.txt hello-2.0/readme.txt
index ce01362..94954ab 100644
--- hello-1.0/readme.txt
+++ hello-2.0/readme.txt
@@ -1 +1,2 @@
 hello
+world
进入到 hello-1.0 目录,执行 git apply 应用补丁,即使 hello-1.0 不是一个 Git 库。
$ cd hello-1.0
$ git apply ../patch.txt
我们会惊喜的发现 hello-1.0 应用补丁后,已经变得和 hello-2.0 一样了。
$ git diff --stat . ../hello-2.0
命令 git apply 也支持反向应用补丁。反向应用补丁后,hello-1.0 中文件被还原,和 hello-2.0 比较又可以看到差异了。
$ git apply -R ../patch.txt
$ git diff --stat . ../hello-2.0
 {. => ../hello-2.0}/binary.dat |  Bin 32 -> 64 bytes
 {. => ../hello-2.0}/readme.txt |    1 +
 2 files changed, 1 insertions(+), 0 deletions(-)
blog comments powered by Disqus