@@ -2,199 +2,217 @@
title: diff 和 patch 的入门(及 Windows 下的用法)
layout: post
permalink: /html/diff-and-patch-and-windows.html
-yourls_shorturl:
- - http://miv.im/xjf3
-categories:
- - Coding
-tags:
- - Linux
- - shell
- - Windows
---
自从我开始使用 Git 以及接触 Linux 之后,我发现我越来越离不开 Linux 上那些方便好用的工具。比如 `diff` & `patch`,相对于 Windows 自带的 `fc` 要好用太多太多。
-<div class='et-box et-info'>
- <div class='et-box-content'>
- 除 Windows 部分外,本文目前的多数内容是对《补丁(patch)的制作与应用》(见参考阅读)的修正、整理、简化与重新排版。
- </div>
-</div>
+**除 Windows 部分外,本文目前的多数内容是对[补丁(patch)的制作与应用](http://linux-wiki.cn/wiki/zh-hans/%E8%A1%A5%E4%B8%81%28patch%29%E7%9A%84%E5%88%B6%E4%BD%9C%E4%B8%8E%E5%BA%94%E7%94%A8)的修正、整理、简化与重新排版。**
如果修改了开源代码,为了方便分享(如提交漏洞)或自己留存使用,一般都要制作一个补丁。在从源码安装软件时,也难免要应用些别人做好的补丁,本文介绍如何制作和应用补丁。
-<!--more-->
-
## diff 简介
**diff** 可以比较两个东西,并可同时记录下二者的区别。制作补丁时的一般用法和常见选项为:
-<pre class="lang:sh decode:true">diff 【选项】 源文件(夹) 目的文件(夹)</pre>
-
--r
-: 递归。设置后diff会将两个不同版本源代码目录中的所有对应文件全部都进行一次比较,包括子目录文件。
-
--N
-: 选项确保补丁文件将正确地处理已经创建或删除文件的情况。
-
--u
-: 输出每个修改前后的3行,也可以用-u5等指定输出更多上下文。
-
--E, -b, -w, -B, –strip-trailing-cr
-: 忽略各种空白,可参见文档,按需选用。
+```bash
+diff 【选项】 源文件(夹) 目的文件(夹)
+```
+
+<dl>
+ <dt>-r</dt>
+ <dd>递归。设置后 diff 会将两个不同版本源代码目录中的所有对应文件全部都进行一次比较,包括子目录文件。</dd>
+ <dt>-N</dt>
+ <dd>选项确保补丁文件将正确地处理已经创建或删除文件的情况。</dd>
+ <dt>-u</dt>
+ <dd>输出每个修改前后的 3 行,也可以用 <code>-u5</code> 等指定输出更多上下文。</dd>
+ <dt>-E, -b, -w, -B, --strip-trailing-cr</dt>
+ <dd>忽略各种空白,可参见文档,按需选用。</dd>
+</dl>
## patch 简介
**patch**的作用则是将 diff 记录的结果(即补丁)应用到相应文件(夹)上。最常见的用法为:
-<pre class="lang:sh decode:true">patch -pNUM < patchfile</pre>
-
--p Num
-: 忽略几层文件夹,随后详解。
+```bash
+patch -pNUM < patchfile
+```
--E
-: 选项说明如果发现了空文件,那么就删除它
-
--R
-: 取消打过的补丁。
+<dl>
+ <dt>-p Num</dt>
+ <dd>忽略几层文件夹,随后详解。</dd>
+ <dt>-E</dt>
+ <dd>选项说明如果发现了空文件,那么就删除它。</dd>
+ <dt>-R</dt>
+ <dd>取消打过的补丁。</dd>
+</dl>
为了解释 *-p* 参数,需要看看如下 patch 文件片段:
-<pre class="lang:sh decode:true">--- old/modules/pcitable Mon Sep 27 11:03:56 1999
-+++ new/modules/pcitable Tue Dec 19 20:05:41 2000</pre>
+```diff
+--- old/modules/pcitable Mon Sep 27 11:03:56 1999
++++ new/modules/pcitable Tue Dec 19 20:05:41 2000
+```
-如果使用参数 **-p0**,那就表示从当前目录找一个叫做old的文件夹,再在它下面寻找 *modules/pcitable* 文件来执行patch操作。
-而如果使用参数 **-p1**,那就表示忽略第一层目录(即不管old),从当前目录寻找 *modules* 的文件夹,再在它下面找pcitable。
+如果使用参数 **-p0**,那就表示从当前目录找一个叫做 old 的文件夹,再在它下面寻找 *modules/pcitable* 文件来执行 patch 操作。
+而如果使用参数 **-p1**,那就表示忽略第一层目录(即不管 old ),从当前目录寻找 *modules* 的文件夹,再在它下面找 pcitable。
## 简单应用
利用以上命令,处理单个文件补丁的方法:
-<pre class="lang:sh"># 产生补丁
-diff -uN from-file to-file >to-file.patch
+```bash
+# 产生补丁
+diff -uN original new > patch
# 打补丁
-patch -p0 < to-file.patch
+patch -p0 < patch
# 取消补丁
-patch -RE -p0 < to-file.patch</pre>
+patch -RE -p0 < patch
+```
对整个文件夹打补丁的情况:
-<pre class="lang:sh"># 产生补丁
-diff -uNr from-docu to-docu >to-docu.patch
+```bash
+# 产生补丁
+diff -uNr original new > patch
# 打补丁
-cd to-docu
-patch -p1 < to-docu.patch
+cd original
+patch -p1 < patch
# 取消补丁
-patch -R -p1</pre>
+patch -R -p1 < patch
+```
-另外,使用版本控制工具时,可以直接用*svn diff*或*git diff*生成补丁文件。
+另外,使用版本控制工具时,可以直接用 **svn diff** 或 **git diff** 生成补丁文件。
值得一提的是,由于应用补丁时的目标代码和生成补丁时的代码未必相同,打补丁操作可能失败。补丁失败的文件会以.rej结尾,下面命令可以找出所有rej文件:
-<pre>find . -name '*.rej'</pre>
+```bash
+find . -name '*.rej'
+```
## patch 文件构成
补丁文件里到底存储了哪些信息呢?看看这个例子:
-<pre class="lang:diff">--- test0 2006-08-18 09:12:01.000000000 +0800
+```diff
+--- test0 2006-08-18 09:12:01.000000000 +0800
+++ test1 2006-08-18 09:13:09.000000000 +0800
@@ -1,3 +1,4 @@
+222222
111111
-111111
+222222
- 111111</pre>
-
-补丁头
-: 补丁头是分别由—/+++开头的两行,用来表示要打补丁的文件。—开头表示旧文件,+++开头表示新文件。
-
-一个补丁文件中的多个补丁
-: 一个补丁文件中可能包含以—/+++开头的很多节,每一节用来打一个补丁。所以在一个补丁文件中可以包含好多个补丁。
-
-块
-: 块是补丁中要修改的地方。它通常由一部分不用修改的东西开始和结束。他们只是用来表示要修改的位置。他们通常以@@开始,结束于另一个块的开始或者一个新的补丁头。
-
-块的缩进
-: 块会缩进一列,而这一列是用来表示这一行是要增加还是要删除的。
-
-块的第一列
-: +号表示这一行是要加上的。-号表示这一行是要删除的。没有加号也没有减号表示这里只是引用的而不需要修改。
+ 111111
+```
+
+<dl>
+ <dt>补丁头</dt>
+ <dd>补丁头是分别由 ---/+++ 开头的两行,用来表示要打补丁的文件。--- 开头表示旧文件,+++ 开头表示新文件。</dd>
+ <dt>一个补丁文件中的多个补丁</dt>
+ <dd>一个补丁文件中可能包含以 ---/+++ 开头的很多节,每一节用来打一个补丁。所以在一个补丁文件中可以包含好多个补丁。</dd>
+ <dt>块</dt>
+ <dd>块是补丁中要修改的地方。它通常由一部分不用修改的东西开始和结束。他们只是用来表示要修改的位置。他们通常以 @@ 开始,结束于另一个块的开始或者一个新的补丁头。</dd>
+ <dt>块的缩进</dt>
+ <dd>块会缩进一列,而这一列是用来表示这一行是要增加还是要删除的。</dd>
+ <dt>块的第一列</dt>
+ <dd>+ 号表示这一行是要加上的。- 号表示这一行是要删除的。没有加号也没有减号表示这里只是引用的而不需要修改。</dd>
+</dl>
## 单文件实例分析
设当前目录有文件 **test0**:
-<pre class="lang:sh">111111
+```
+111111
+111111
111111
-111111</pre>
+```
-和文件**test1**:
+和文件 **test1**:
-<pre class="lang:sh">222222
+```
+222222
111111
222222
-111111</pre>
+111111
+```
-使用diff创建补丁**test1.patch**
+使用 diff 创建补丁 **test1.patch**:
-<pre class="lang:sh">diff -uN test0 test1 > test1.patch</pre>
+```bash
+diff -uN test0 test1 > test1.patch
+```
-因为是单个文件,故不需要 -r 选项。此命令得到如下补丁:
+因为是单个文件,故不需要 **-r** 选项。此命令得到如下补丁:
-<pre class="lang:diff">--- test0 2006-08-18 09:12:01.000000000 +0800
+```diff
+--- test0 2006-08-18 09:12:01.000000000 +0800
+++ test1 2006-08-18 09:13:09.000000000 +0800
@@ -1,3 +1,4 @@
+222222
111111
-111111
+222222
- 111111</pre>
+ 111111
+```
要应用补丁,只需:
-<pre class="lang:sh">$ patch -p0 < test1.patch
-patching file test0</pre>
+```bash
+$ patch -p0 < test1.patch
+patching file test0
+```
-此时test0就和test1一样了。
+此时 test0 就和 test1 一样了。
如果要取消补丁做出的更改,恢复旧版本:
-<pre class="lang:sh">$ patch -RE -p0 < test1.patch
-patching file test0</pre>
+```bash
+$ patch -RE -p0 < test1.patch
+patching file test0
+```
## 文件夹实例分析
设有如下环境:
-<pre>--prj0/
+```
+--prj0/
test0
prj0name
--prj1/
test1
- prj1name</pre>
+ prj1name
+```
*prj0/prj0name*内容为如下三行:
-<pre>--------
+```
+--------
prj0/prj0name
---------</pre>
+--------
+```
*prj1/prj1name*内容为如下三行:
-<pre>--------
+```
+--------
prj1/prj1name
---------</pre>
+--------
+```
用 *diff -uNr* 创建补丁,
-<pre class="lang:sh">diff -uNr prj0 prj1 > prj1.patch</pre>
+```bash
+diff -uNr prj0 prj1 > prj1.patch
+```
得到的patch文件为:
-<pre class="lang:diff">diff -uNr prj0/prj0name prj1/prj0name
+```diff
+diff -uNr prj0/prj0name prj1/prj0name
--- prj0/prj0name 2006-08-18 09:25:11.000000000 +0800
+++ prj1/prj0name 1970-01-01 08:00:00.000000000 +0800
@@ -1,3 +0,0 @@
@@ -222,46 +240,63 @@ diff -uNr prj0/test1 prj1/test1
+222222
+111111
+222222
-+111111</pre>
++111111
+```
如果要应用此补丁,则:
-<pre class="lang:sh">$ ls
+```bash
+$ ls
prj0 prj1 prj1.patch
$ cd prj0
-$ patch -p1 < ../prj1.patch
+$ patch -p1 < ../prj1.patch
patching file prj0name
patching file prj1name
patching file test0
-patching file test1</pre>
+patching file test1
+```
-此时可用[ls][1]看到打补丁后的结果:
+此时可用 ls 看到打补丁后的结果:
-<pre class="lang:sh">$ ls
-prj1name test1</pre>
+```bash
+$ ls
+prj1name test1
+```
类似的,如果要回滚补丁操作:
-<pre class="lang:sh">$ patch -R -p1 < ../prj1.patch
+```bash
+$ patch -R -p1 < ../prj1.patch
patching file prj0name
patching file prj1name
patching file test0
patching file test1
$ ls
-prj0name test0</pre>
+prj0name test0
+```
## 在 Windows 下
-您可以安装 mingw,或者安装 Git,或者 RailsInstaller 等之类的,他们都包括了 diff 和 patch。如果您并不想安装这些庞然大物,可以尝试我打包的 diff 和 patch。
-<a href='https://github.com/orzFly/Tour-DiffAndPatch/archive/master.zip' target='_blank' class='icon-button download-icon'><span class='et-icon'><span>下载适合 Windows 的 diff 和 patch</span></span></a>
+您可以安装 mingw,或者安装 Git,或者 RailsInstaller 等之类的,他们都包括了 diff 和 patch。如果您并不想安装这些庞然大物,可以尝试[我打包的 diff 和 patch](https://github.com/orzFly/Tour-DiffAndPatch/archive/master.zip)。
-<pre class="lang:default decode:true" data-url="https://github.com/orzFly/Tour-DiffAndPatch/raw/master/README.md"></pre>
+### 文件清单
-
+<dl>
+ <dt>README.md</dt>
+ <dd>您正在阅读的这个说明文档。</dd>
+ <dt>msys-1.0.dll</dt>
+ <dd>MSYS POSIX Emulation DLL 1.0.12</dd>
+ <dt>diff.exe</dt>
+ <dd>diff - GNU diffutils version 2.7</dd>
+ <dt>patch.exe</dt>
+ <dd>patch 2.5</dd>
+ <dt>patch.exe.manifest</dt>
+ <dd>解决 patch.exe 的 UAC 问题所需的清单文件</dd>
+</dl>
-## 扩展阅读
+### 如何使用
- * [补丁(patch)的制作与应用 – Linux Wiki][2]
+我个人推荐您将除了说明文件之外的四个文件都放置到 Windows\System32 目录下,
+当然放到您 PATH 变量中所包含的目录也可以。
- [1]: /wiki/Ls "Ls"
- [2]: http://linux-wiki.cn/wiki/zh-hans/%E8%A1%A5%E4%B8%81%28patch%29%E7%9A%84%E5%88%B6%E4%BD%9C%E4%B8%8E%E5%BA%94%E7%94%A8
\ No newline at end of file
+在这之后您便可以在命令行提示符中享受 diff 与 patch 给您带来的便利。
\ No newline at end of file