git patch的一些个人总结

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文件的方式,更加直观便捷,比较容易修改

 

  1. 遇到的问题

 我们也经常会遇到下面这种情况:

在使用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

猜你喜欢

转载自blog.csdn.net/zhangyufeikk/article/details/86062135