1.引言
在目前的工作中,每每上传代码,需要将项目上的所有文件一一比对(可能是强迫症),所以在想是不是有一种方法进行改善呢,现在的项目都从svn迁移到了git库中,我就想到了git 自带的patch功能,是不是可以减轻这种上传代码的强迫症呢?
先介绍一下Patch是什么。如果一个软件有了新版本,我们可以完整地下载新版本的代码进行编译安装。然而,像Linux Kernel这样的大型项目,代码即使压缩,也超过70MB,每次全新下载是有相当大的代价的。然而,每次更新变动的代码可能不超过1MB,因此,我们只 要能够有两个版本代码的diff的数据,应该就可以以极低的代价更新程序了。因此,Larry Wall开发了一个工具:patch。它可以根据一个diff文件进行版本更新。
不过在git中,我们没有必要直接使用diff和patch来做补丁,这样做既危险又麻烦。git提供了两种简单的patch方案。一是用git diff生成的标准patch,二是git format-patch生成的Git专用Patch。
2.生成patch的两种方法
2.1 git diff生成
我们可以先用git diff 查看我们的修改
我们可以看到这是非常典型的Patch式diff,因此我们可以直接把这个输出变为一个patch:
输入这条指令,会在当前目录下产生一个PATCH文件,具体文件内容如下:
和之前执行git diff 时的输出是一样的。
当然我们也可以生成多个文件的patch:
我们也可以进行分支上的diff,并生成相应的patch:
注意:当执行完git add指令后,就没办法diff来生成patch文件
2.2 git format-patch生成
我们先git log看看
而用formate-patch有如下几种方法:
$ git format-patch HEAD^ #生成最近的1次commit的patch
$ git format-patch HEAD^^ #生成最近的2次commit的patch
$ git format-patch HEAD^^^ #生成最近的3次commit的patch
$ git format-patch HEAD^^^^ #生成最近的4次commit的patch
$ git format-patch<r1>..<r2>
#生成两个commit间的修改的patch(包含两个commit. <r1>和<r2>都是具体的commit号)
差分内容会放到一个.patch文件中
$ git format-patch-1<r1>
#生成单个commit的patch
$ git format-patch <r1>
#生成某commit以来的修改patch(不包含该commit)
$ git format-patch --root <r1>
#生成从根到r1提交的所有patch
2.3 两种patch的比较:
- 兼容性:很明显,git diff生成的Patch兼容性强。如果你在修改的代码的官方版本库不是Git管理的版本库,那么你必须使用git diff生成的patch才能让你的代码被项目的维护人接受。
- 除错功能:对于git diff生成的patch,你可以用git apply --check 查看补丁是否能够干净顺利地应用到当前分支中;如果git format-patch 生成的补丁不能打到当前分支,git am会给出提示,并协助你完成打补丁工作,你也可以使用git am -3进行三方合并,详细的做法可以参考git手册或者《Progit》。从这一点上看,两者除错功能都很强。
- 版本库信息:由于git format-patch生成的补丁中含有这个补丁开发者的名字,因此在应用补丁时,这个名字会被记录进版本库,显然,这样做是恰当的。因此,目前使用Git的开源社区往往建议大家使用format-patch生成补丁。
3.打补丁
3.1 一些指令
有两种指令,git apply和 git am
git apply与git am的区别是,git apply并不会将commit message等打上去,打完patch后需要重新git add和git commit,而git am会直接将patch的所有信息打上去,而且不用重新git add和git commit,author也是patch的author而不是打patch的人
有以下常用指令,先介绍一下:
$ git apply --stat 0001-limit-log-function.patch
# 查看patch的情况
$ git apply --check 0001-limit-log-function.patch
# 检查patch是否能够打上,如果没有任何输出,则说明无冲突,可以打上
$ git am 0001-limit-log-function.patch # 将名字为0001-limit-log-function.patch的patch打上
$ git am --signoff0001-limit-log-function.patch
# 添加-s或者--signoff,还可以把自己的名字添加为signed off by信息,作用是注明打patch的人是谁,因为有时打patch的人并不是patch的作者
$ git am ~/patch-set/*.patch
# 将路径~/patch-set/*.patch 按照先后顺序打上
$ git am--abort
# 当git am失败时,用以将已经在am过程中打上的patch废弃掉(比如有三个patch,打到第三个patch时有冲突,那么这条命令会把打上的前两个patch丢弃掉,返回没有打patch的状态)
ps:但是已经打上去的patch,却没办法直接abort掉
$ git am--resolved
#当git am失败,解决完冲突后,这条命令会接着打patch
3.2 解决冲突
如果打Patch的过程中发生了冲突(conflicts),怎么办?
解决patch冲突的过程是:
如果不想打这一系列patch了,直接:git am --abort。
如果还想打, 我这里提供一个解决方案:
(这是测试时添加的冲突)
(1) 根据git am失败的信息,找到发生冲突的具体patch文件,然后使用命令git apply --reject <patch_name>,强行打这个patch,发生冲突的部分会保存为.rej文件(例如发生冲突的文件是a.txt,那么运行完这个命令后,发生conflict的部分会保存为a.txt.rej),未发生冲突的部分会成功打上patch
(2) 根据.rej文件,通过编辑发生冲突的code文件的方式解决冲突。
(3) 将该patch涉及到的所有文件(不仅仅是发生冲突的文件)通过命令git add <file_name>添加到工作区中
(4) 告诉git冲突已经解决,继续打patch: git am --resolved (git am --resolved 和 git am --continue是一样的)
分析:该方案通过编辑冲突code文件的方式解决冲突。
(其实还有另一种方案,是修改patch文件。参考的网络博客,但自己没有实验成功,方案如下:
(1) 根据git am失败的信息,找到发生冲突的具体patch文件,然后用命令git apply --reject <patch_name>,强行打这个patch,发生冲突的部分会保存为.rej文件(例如发生冲突的文件是a.txt,那么运行完这个命令后,发生conflict的部分会保存为a.txt.rej),未发生冲突的部分会成功打上patch
(2) 根据.rej文件,通过编辑该patch文件的方式解决冲突。
(3) 废弃上一条am命令已经打了的patch:git am --abort
(4) 重新打patch:git am ~/patch-set/*.patchpatch
)
个人认为编辑冲突code文件的方式,更加直观便捷,比较容易修改
我们也经常会遇到下面这种情况:
在使用git apply patch之后出现
这也算是代码规范的一个校验吧,即代码中不允许以空格结尾。
看看git apply(1)手册上怎么说:
简单来说git apply应用补丁时会检测空白错误,默认情况下,尾部空白,包含空白的空行,初始tab缩进之后紧跟的空白字符会被认为是错误。
处理这个错误的行为由命令行参数--whitespace或者core.whitespace配置来控制,共有5种可能的动作。
nowarn 关闭错误提示
warn 输出部分错误提示,但完整的应用补丁,不会处理错误,这是默认动作。
fix 输出部分错误,修正错误后应用补丁
error 输出部分错误,拒绝应用补丁。
error-all 输出全部的错误,拒绝应用补丁。
一般,我是添加fix来应用补丁的:
当然有两种空格fix是不能处理的。
1、patch命令无法处理含有空白的文件名因为diff文件中的文件名含有空格,patch命令无法应用这样的diff补丁。应该避免使用带有特殊字符的文件名。
2、patch也无法引用二进制diff补丁,使用普通diff格式生成含有二进制文件的补丁时,patch会应用成功,但生成的二进制文件是空的。
5.参考文档
https://www.cnblogs.com/y041039/articles/2411600.html
https://blog.csdn.net/liuhaomatou/article/details/54410361
http://linux.die.net/man/1/git-format-patch
https://www.cnblogs.com/ArsenalfanInECNU/p/8931377.html