git 补丁相关操作

2017-10-08 develop git

在 Linux 中可以通过 diff patch 命令生成补丁以及打补丁,而且 git 实际上也提供了很简单的命令直接生成 Patch 文件,然后,可以再结合 patch 命令使用即可。

这里简单介绍其使用方法。

简介

该命令通常用于文本文件的区别查看。

----- diff常用选项
    -r :  递归;
    -N :  确保能正确处理已创建或删除的文件;
    -u :  以统一格式创建,默认输出上下文前后 3 行,-u5 显示 5 行;
    -E :  忽略 tab 的改变;
    -b :  忽略 white space;
    -w :  忽略所有空白,当使用不同的对齐方式时非常方便,注意空白行仍然认为有差别;
    -B :  忽略空白行。

----- patch常用选项
    -R :  新旧版本互换;
    -E :  直接删除空文件。

假设文件 A 和文件 B ,其中 A 为原文件, B 为修改后的文件,经过 diff 之后生成了补丁文件 C ,那么着个过程相当于 A - B = C ,那么 patch 的过程就是 B + C = AA - C = B

也就是说,如果简单使用 diff A B > C,那么只能用 patch A C,使用 patch B C 将出现错误,如果想恢复则应该使用 patch -RE A C

格式

其中简单示例如下:

$ diff -uN A B
--- A   2013-02-17 11:20:08.926661164 +0800         # 原文件
+++ B   2013-02-17 11:20:38.666854034 +0800         # 修改后的文件
@@ -1 +1,2 @@                  # 原文件的第一行,以及改后的1~2行
 Hello World                   # 两者之差的内容即为修改的内容
 +foo bar                      # 其中减号('-')表示删除,加号('+')表示添加

常用命令

----- 单个文件
# diff -uN from-file to-file > to-file.patch     # 产生补丁
# patch -p0 < to-file.patch                      # 打补丁,针对目录下所有文件
# patch -p0 from-file to-file.patch              # 同上,但是指定文件
# patch -RE -p0 < to-file.patch                  # 恢复原文件

----- 多个文件
# diff -uNr from-docu to-docu > to-docu.patch
# patch -p1 < to-docu.patch
# patch -R -p1 < to-docu.patch

patch 使用时不用指定文件,在补丁文件中已经记载了原文件的路径和名称。

另外需要通过 -pn 来处理补丁中的路径问题,如 dir/dirA/Adir/dirB/B ,在 dir 目录下执行 diff -rc dirA dirB > C,此时 C 中记录的路径为 dirA/A ;此时如果在 dirA/ 目录下,那么应该使用 patch -p1 ,即忽略 dirA/

补丁失败的文件会以 .rej 结尾,下面命令可以找出所有 rej 文件, find . -name '*.rej'

dir# diff -uNr from-dir to-dir > dir.patch
dir# patch -p0 < dir.patch                       # from-dir 将会被删除
dir/from-dir# patch -p1 < ../dir.patch           # 正确的处理方法

git

在 git 中有两种补丁方式可以使用,最简单的就是使用上述的方式,先提取出来标准的 patch 文件。我们可以通过 git log 获取提交时的 Commit ID ,然后再通过如下命令获取提交的差异。

----- 当前HEAD到2e4bb3提交的差异
$ git diff 2e4bb3 > /tmp/foobar.patch

----- 然后开始打补丁
$ cd /your/project/path
$ patch -R -p1 < /tmp/foobar.patch

最佳实践

实际上 git 已经提供了一些常用的命令支持补丁。

----- 获取某个提交作为Patch,通过-N表示前N次提交(含当前CommitID)
$ git format-patch -1 707c816
----- 或者指定两次之间的提交
$ git format-patch -1 707c816..49e1c90

另外,如果 format-patch 不指定 CommitID 那么就是当前提交之前的 N 的提交。默认会生成对应的 patch 文件,也可以通过 -o 参数指定输出目录。

----- 先检查patch文件
$ git apply --stat xxx.patch
----- 检查是否应用成功
$ git apply --check xxx.patch
----- 然后可以打补丁,如果有冲突是不会打补丁的
$ git am --signoff < xxx.patch

其中 am 全称为 apply-mailbox 命令,而这里 --signoff 用于标识是谁打的补丁,如果发生冲突之后可以通过如下命令执行。

----- 先应用不冲突的部分,冲突内容会保存为 xxx.rej 文件,其中 xxx 为对应文件名
$ git apply --reject xxx.patch
----- 解决冲突后删除后缀为.rej的文件,然后添加到暂存区
$ git add . -A
----- 然后标记为已经解决,这里会将相应的提交信息保存
$ git am --resolved

上述操作后会直接提交。