版本库间的交互是通过git push和/或git pull命令实现的,这是Git最主要的交互模式,但并不是全部。使用补丁文件是另外一种交互方式,适用于参与者众多的大型项目进行分布式开发。

创建补丁

Git提供了将提交批量转换为补丁文件的命令:git format-patch。该命令后面的参数是一个版本范围列表,会将包含在此列表中的提交一一转换为补丁文件,每个补丁文件包含一个序号并从提交说明中提取字符串作为文件名。

下面演示一下在user1工作区中,如何将master分支的最近3个提交转换为补丁文件。

  • 进入user1工作区,切换到master分支。
$ cd /path/to/user1/workspace/hello-world/
$ git checkout master
$ git pull
  • 执行下面的命令将最近三个提交转换为补丁文件。
$ git format-patch -s HEAD~3..HEAD
0001-Fix-typo-help-to-help.patch
0002-Add-I18N-support.patch
0003-Translate-for-Chinese.patch

在上面的git format-patch命令中使用了-s参数,会在导出的补丁文件中添加当前用户的签名。这个签名并非GnuPG式的数字签名,不过是将作者姓名添加到提交说明中而已,和在本书第2篇开头介绍的git commit -s命令的效果相同。虽然签名很不起眼,但是对于以补丁方式提交数据却非常重要,因为以补丁方式提交可能因为合并冲突或其他原因使得最终提交的作者显示为管理员(提交者)的ID,在提交说明中加入原始作者的署名信息大概是作者唯一露脸的机会。如果在提交时忘了使用-s参数添加签名,可以在用git format-path命令创建补丁文件的时候补救。

看一下补丁文件的文件头,在下面代码中的第7行可以看到新增的签名。

1 From d81896e60673771ef1873b27a33f52df75f70515 Mon Sep 17 00:00:00 2001
2 From: user1 <user1@sun.ossxp.com>
3 Date: Mon, 3 Jan 2011 23:48:56 +0800
4 Subject: [PATCH 1/3] Fix typo: -help to --help.
5
6
7 Signed-off-by: user1 <user1@sun.ossxp.com>
8 ---
9 src/main.c | 2 +-
10 1 files changed, 1 insertions(+), 1 deletions(-)

补丁文件有一个类似邮件一样的文件头(第1-4行),提交日志的第一行作为邮件标题(Subject),其余提交说明作为邮件内容(如果有的话),文件补丁用三个横线和提交说明分开。

实际上这些补丁文件可以直接拿来作为邮件发送给项目的负责人。Git提供了一个辅助邮件发送的命令git send-email。下面用该命令将这三个补丁文件以邮件形式发送出去。

 git send-email *.patch
0001-Fix-typo-help-to-help.patch
0002-Add-I18N-support.patch
0003-Translate-for-Chinese.patch
The following files are 8bit, but do not declare a Content-Transfer-Encoding.
0002-Add-I18N-support.patch
0003-Translate-for-Chinese.patch
Which 8bit encoding should I declare [UTF-8]?
Who should the emails appear to be from? [user1 <user1@sun.ossxp.com>] Emails will be sent from: user1 <user1@sun.ossxp.com>
Who should the emails be sent to? jiangxin
Message-ID to be used as In-Reply-To for the first email?
...
Send this email? ([y]es|[n]o|[q]uit|[a]ll): a
...

命令git send-email提供交互式字符界面,输入正确的收件人地址,邮件就批量地发送出去了。

应用补丁

在前面通过git send-email命令发送邮件给jiangxin用户。现在使用 Linux 上的mail命令检查一下邮件。

$ mail
Mail version 8.1.2 01/15/2001. Type ? for help.
"/var/mail/jiangxin": 3 messages 3 unread
>N 1 user1@sun.ossxp.c Thu Jan 13 18:02 38/1120 [PATCH 1/3] Fix typo: -help to --help.
N 2 user1@sun.ossxp.c Thu Jan 13 18:02 227/6207 =?UTF-8?q?=5BPATCH=202/3=5D=20Add=20I18N=20support=2E?=
N 3 user1@sun.ossxp.c Thu Jan 13 18:02 95/2893 =?UTF-8?q?=5BPATCH=203/3=5D=20Translate=20for=20Chinese=2E?=
&

如果邮件不止这三封,需要将三个包含补丁的邮件挑选出来保存到另外的文件中。 在 mail 命令的提示符(&)下输入命令。

& s 1-3 user1-mail-archive
"user1-mail-archive" [New file]
& q

上面的操作在本地创建了一个由开发者user1的补丁邮件组成的归档文件user1-mail-archive,这个文件是mbox格式的,可以用mail命令打开。

$ mail -f user1-mail-archive
Mail version 8.1.2 01/15/2001. Type ? for help.
"user1-mail-archive": 3 messages
> 1 user1@sun.ossxp.c Thu Jan 13 18:02 38/1121 [PATCH 1/3] Fix typo: -help to --help.
2 user1@sun.ossxp.c Thu Jan 13 18:02 227/6208 =?UTF-8?q?=5BPATCH=202/3=5D=20Add=20I18N=20support=2E?=
3 user1@sun.ossxp.c Thu Jan 13 18:02 95/2894 =?UTF-8?q?=5BPATCH=203/3=5D=20Translate=20for=20Chinese=2E?=
& q

保存在mbox中的邮件可以批量的应用在版本库中,使用git am命令。am是apply email的缩写。下面就演示一下如何应用补丁。

  • 基于HEAD~3版本创建一个本地分支,以便在该分支下应用补丁。
$ git checkout -b user1 HEAD~3
Switched to a new branch 'user1'
  • 将mbox文件user1-mail-archive中的补丁全部应用在当前分支上。
$ git am user1-mail-archive
Applying: Fix typo: -help to --help.
Applying: Add I18N support.
Applying: Translate for Chinese.
  • 补丁成功应用上了,看看提交日志。
$ git log -3 --pretty=fuller
commit 2d9276af9df1a2fdb71d1e7c9ac6dff88b2920a1
Author: Jiang Xin <jiangxin@ossxp.com>
AuthorDate: Thu Jan 13 18:02:03 2011 +0800
Commit: user1 <user1@sun.ossxp.com>
CommitDate: Thu Jan 13 18:21:16 2011 +0800 Translate for Chinese. Signed-off-by: Jiang Xin <jiangxin@ossxp.com>
Signed-off-by: user1 <user1@sun.ossxp.com> commit 41227f492ad37cdd99444a5f5cc0c27288f2bca4
Author: Jiang Xin <jiangxin@ossxp.com>
AuthorDate: Thu Jan 13 18:02:02 2011 +0800
Commit: user1 <user1@sun.ossxp.com>
CommitDate: Thu Jan 13 18:21:15 2011 +0800 Add I18N support. Signed-off-by: Jiang Xin <jiangxin@ossxp.com>
Signed-off-by: user1 <user1@sun.ossxp.com> commit 4a3380fb7ae90039633dec84acc2aab85398efad
Author: user1 <user1@sun.ossxp.com>
AuthorDate: Thu Jan 13 18:02:01 2011 +0800
Commit: user1 <user1@sun.ossxp.com>
CommitDate: Thu Jan 13 18:21:15 2011 +0800 Fix typo: -help to --help. Signed-off-by: user1 <user1@sun.ossxp.com>

从提交信息上可以看出:

  • 提交的时间信息使用了邮件发送的时间。

  • 作者(Author)的信息被保留,和补丁文件中的一致。

  • 提交者(Commit)全都设置为user1,因为提交是在user1的工作区完成的。

  • 提交说明中的签名信息被保留。实际上git am命令也可以提供-s参数,在提交说明中附加执行命令用户的签名。

对于不习惯在控制台用mail命令接收邮件的用户,可以通过邮件附件,U盘或其他方式获取git format-patch生成的补丁文件,将补丁文件保存在本地,通过管道符调用git am命令应用补丁。

$ ls *.patch
0001-Fix-typo-help-to-help.patch 0002-Add-I18N-support.patch 0003-Translate-for-Chinese.patch
$ cat *.patch | git am
Applying: Fix typo: -help to --help.
Applying: Add I18N support.
Applying: Translate for Chinese.

Git还提供一个命令git apply,可以应用一般格式的补丁文件,但是不能执行提交,也不能保持补丁中的作者信息。

StGit和Quilt

一个复杂功能的开发一定是由多个提交来完成的,对于在以接收和应用补丁文件为开发模式的项目中,复杂的功能需要通过多个补丁文件来完成。补丁文件因为要经过审核才能被接受,因此针对一个功能的多个补丁文件一定要保证各个都是精品:补丁1用来完成一个功能点,补丁2用来完成第二个功能点,等等。一定不能出现这样的情况:补丁3用于修正补丁1的错误,补丁10改正了补丁7中的文字错误,等等。这样就带来补丁管理的难题。

实际上基于特性分支的开发又何尝不是如此?在将特性分支归并到开发主线前,要接受团队的评审,特性分支的开发者一定想将特性分支上的提交进行重整,把一些提交合并或者拆分。使用变基命令可以实现提交的重整,但是操作起来会比较困难,有什么好办法呢?

StGit

Stacked Git(http://www.procode.org/stgit/)简称StGit就是解决上述两个难题的答案。实际上StGit在设计上参考了一个著名的补丁管理工具Quilt,并且可以输出Quilt兼容的补丁列表。

StGit是一个Python项目,安装起来还是很方便的。在Debian/Ubuntu下,可以直接通过包管理器安装:

$ sudo aptitude install stgit stgit-contrib

下面还是用hello-world版本库,进行StGit的实践。

  • 首先检出hello-world版本库。
$ cd /path/to/my/workspace/
$ git clone file:///path/to/repos/hello-world.git stgit-demo
$ cd stgit-demo
  • 在当前工作区初始化StGit。
$ stg init
  • 现在补丁列表为空。
$ stg series
  • 将最新的三个提交转换为StGit补丁。
$ stg uncommit -n 3
Uncommitting 3 patches ...
Now at patch "translate-for-chinese"
done
  • 现在补丁列表中有三个文件了。

第一列是补丁的状态符号。加号(+)代表该补丁已经应用在版本库中,大于号(>)用于标识当前的补丁。

$ stg ser
+ fix-typo-help-to-help
+ add-i18n-support
> translate-for-chinese
  • 现在查看master分支的日志,发现和之前没有两样。
$ git log -3 --oneline
c4acab2 Translate for Chinese.
683448a Add I18N support.
d81896e Fix typo: -help to --help.
  • 执行StGit补丁出栈的命令,会将补丁撤出应用。使用-a参数会将所有补丁撤出应用。
$ stg pop
Popped translate-for-chinese
Now at patch "add-i18n-support"
$ stg pop -a
Popped add-i18n-support -- fix-typo-help-to-help
No patch applied
  • 再来看版本库的日志,会发现最新的三个提交都不见了。
$ git log -3 --oneline
10765a7 Bugfix: allow spaces in username.
0881ca3 Refactor: use getopt_long for arguments parsing.
ebcf6d6 blank commit for GnuPG-signed tag test.
  • 查看补丁列表的状态,会看到每个补丁前都用减号(-)标识。
$ stg ser
- fix-typo-help-to-help
- add-i18n-support
- translate-for-chinese
  • 执行补丁入栈,即应用补丁,使用命令stg push或者stg goto命令,注意stg push命令和git push命令风马牛不相及。
$ stg push
Pushing patch "fix-typo-help-to-help" ... done (unmodified)
Now at patch "fix-typo-help-to-help"
$ stg goto add-i18n-support
Pushing patch "add-i18n-support" ... done (unmodified)
Now at patch "add-i18n-support"
  • 现在处于应用add-i18n-support补丁的状态。这个补丁有些问题,本地化语言模板有错误,我们来修改一下。
$ cd src/
$ rm locale/helloworld.pot
$ make po
xgettext -s -k_ -o locale/helloworld.pot main.c
msgmerge locale/zh_CN/LC_MESSAGES/helloworld.po locale/helloworld.pot -o locale/temp.po
. 完成。
mv locale/temp.po locale/zh_CN/LC_MESSAGES/helloworld.po
  • 现在查看工作区,发现工作区有改动。
$ git status -s
M locale/helloworld.pot
M locale/zh_CN/LC_MESSAGES/helloworld.po
  • 不要将改动添加暂存区,也不要提交,而是执行stg refresh命令,更新补丁。
$ stg refresh
Now at patch "add-i18n-support"
  • 这时再查看工作区,发现本地修改不见了。
$ git status -s
  • 执行stg show会看到当前的补丁add-i18n-support已经更新。
$ stg show
...
  • 将最后一个补丁应用到版本库,遇到冲突。这是因为最后一个补丁是对中文本地化文件的翻译,因为翻译前的模板文件被更改了所以造成了冲突。
$ stg push
Pushing patch "translate-for-chinese" ... done (conflict)
Error: 1 merge conflict(s)
CONFLICT (content): Merge conflict in
src/locale/zh_CN/LC_MESSAGES/helloworld.po
Now at patch "translate-for-chinese"
  • 这个冲突文件很好解决,直接编辑冲突文件helloworld.po即可。编辑好之后,注意一下第50行和第62行是否像下面写的一样。
50 "    hello -h, --help\n"
51 " 显示本帮助页。\n"
...
61 msgid "Hi,"
62 msgstr "您好,"
  • 执行git add命令完成冲突解决。
$ git add locale/zh_CN/LC_MESSAGES/helloworld.po
  • 不要提交,而是使用stg refresh命令更新补丁,同时更新提交。
$ stg refresh
Now at patch "translate-for-chinese"
$ git status -s
  • 看看修改后的程序,是不是都能显示中文了。
$ ./hello
世界你好。
(version: v1.0-5-g733c6ea)
$ ./hello Jiang Xin
您好, Jiang Xin.
(version: v1.0-5-g733c6ea)
$ ./hello -h
...
  • 导出补丁,使用命令stg export。导出的是Quilt格式的补丁集。
$ cd /path/to/my/workspace/stgit-demo/
$ stg export -d patches
Checking for changes in the working directory ... done
  • 看看导出补丁的目标目录。
$ ls patches/
add-i18n-support fix-typo-help-to-help series translate-for-chinese
  • 其中文件series是补丁文件的列表,列在前面的补丁先被应用。
$ cat patches/series
# This series applies on GIT commit d81896e60673771ef1873b27a33f52df75f70515
fix-typo-help-to-help
add-i18n-support
translate-for-chinese

通过上面的演示可以看出StGit可以非常方便的对提交进行整理,整理提交时无需使用复杂的变基命令,而是采用:提交StGit化,修改文件,执行stg refresh的工作流程即可更新补丁和提交。StGit还可以将补丁导出为补丁文件,虽然导出的补丁文件没有像git format-patch那样加上代表顺序的数字前缀,但是用文件series标注了补丁文件的先后顺序。实际上可以在执行stg export时添加-n参数为补丁文件添加数字前缀。

StGit还有一些功能,如合并补丁/提交,插入新补丁/提交等,

Quilt

Quilt是一款补丁列表管理软件,用Shell语言开发,安装也很简单,在Debian/Ubuntu上直接用下面的命令即可安装:

$ sudo aptitude install quilt

Quilt约定俗成将补丁集放在项目根目录下的子目录patches中,否则需要通过环境变量QUILT_PATCHES对路径进行设置。为了减少麻烦,在上面用stg export导出补丁的时候就导出到了patches目录下。

简单说一下Quilt的使用,会发现真的和StGit很像,实际上是先有的Quilt,后有的StGit。

  • 重置到三个提交前的版本,否则应用补丁的时候会失败。还不要忘了删除src/locale目录。
$ git reset --hard HEAD~3
$ rm -rf src/locale/
  • 显示补丁列表
$ quilt series
01-fix-typo-help-to-help
02-add-i18n-support
03-translate-for-chinese
  • 应用一个补丁。
$ quilt push
Applying patch 01-fix-typo-help-to-help
patching file src/main.c Now at patch 01-fix-typo-help-to-help
  • 下一个补丁是什么?
$ quilt next
02-add-i18n-support
  • 应用全部补丁。
$ quilt push -a
Applying patch 02-add-i18n-support
patching file src/Makefile
patching file src/locale/helloworld.pot
patching file src/locale/zh_CN/LC_MESSAGES/helloworld.po
patching file src/main.c Applying patch 03-translate-for-chinese
patching file src/locale/zh_CN/LC_MESSAGES/helloworld.po Now at patch 03-translate-for-chinese

Git-补丁文件交互的更多相关文章

  1. 关于Git补丁文件交互

    之前各个章节的版本库的交互都是通过 git push和git pull命令来实现的.这个是Git最主要的交互模式,但并不是全部. 使用补丁文件是另外一种交互方式,适用于参与者众多的大型项目进行的分布式 ...

  2. Git补丁

    引子: 上班有问题没有解决,在家里搞定了,于是把改动打成一个补丁,明天应用到公司的工作电脑上.(以下内容转自别处) 1.创建补丁,比如把最新的两次提交纪录转化为补丁文件,可以用如下命令: git fo ...

  3. git的介绍、git的功能特性、git工作流程、git 过滤文件、git多分支管理、远程仓库、把路飞项目传到远程仓库(非空的)、ssh链接远程仓库,协同开发

    Git(读音为/gɪt/)是一个开源的分布式版本控制系统,可以有效.高速地处理从很小到非常大的项目版本管理. [1] 也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码 ...

  4. git 添加文件

    git 添加文件三步骤 git add filename git commit -m 'remarks' git push origin master

  5. Git忽略文件方法【转】

    转自:http://www.cnblogs.com/shangdawei/archive/2012/09/08/2676669.html http://cwind.iteye.com/blog/166 ...

  6. git忽略文件【转】

    转自: http://cwind.iteye.com/blog/1666646 有很多文件不必使用git管理.例如Eclipse或其他IDE生成的项目文件,编译生成的各种目标或临时文件等.使用git ...

  7. git忽略文件并删除git仓库中的文件

    问题描述 不慎在创建.gitignore  文件之前的时候将文件push到了 git仓库,即使之后在.gitignore文件中写入新的过滤规则,这些规则也不会起作用的,git依然会对所有git仓库中的 ...

  8. eclipse设置git忽略文件

    使用eclipse开发的程序员们经常会接触版本控制软件,这里只要说下eclipse使用egit的情况下设置忽略文件. 特此说明在这里使用window->team->ignored对于git ...

  9. git忽略文件不起作用时

    开始我是直接进到仓库建立了.gitignore文件,再从仓库进入到项目add时总是会添加不需要添加的文件, 后来明白应该是在哪里提交在哪里创建.gitignore文件 git忽略文件操作步骤如下: 1 ...

随机推荐

  1. sass入门(一)

    一].sass入门安装sass安装koala // sass中可以自定义变量 $fontStack: Microsoft Yahei; $primaryColor: #333; body { font ...

  2. Android基础Activity篇——Intent向下一个活动传递数据

    1.向下一个活动传递数据 String data ="bilibilbilbilbili"; Intent intent1=new Intent(this,secondActivi ...

  3. Azure进阶攻略 | 该如何唤醒你?因内核超时而沉睡的Linux虚拟机!

    周五下午,当你收拾好东西准备下班,奔赴 Happy Hour 时,突然接到开发团队的电话: 对方:伙计救命啊,我在搭建开发环境,但 Azure 上的 Linux 虚拟机无法启动! 你(心里想着:你要加 ...

  4. 理解python yield

    python源代码中经常会有使用yield,带有yield的函数是generator(生成器),它返回是一个迭代值,下面我们分析yield是什么原理,有什么好处? 首先,我们写一个简单的斐波那契数列前 ...

  5. python3线程介绍01(如何启动和调用线程)

    #!/usr/bin/env python# -*- coding:utf-8 -*- import osimport time,randomimport threading # 1-进程说明# 进程 ...

  6. php程序员发展建议

    2017PHP程序员的进阶之路 又是一年毕业季,可能会有好多毕业生即将进入开发这个圈子,踏上码农这个不归路.根据这些年在开发圈子总结的LNMP程序猿发展轨迹,结合个人经验体会,总结出很多程序员对未来的 ...

  7. php:定义时间跳转到指定页面

    我们想要定义延迟时间,再跳转到指定页面,只要用header()即可,语法: header("Refresh:延迟时间;url=要跳转的页面"); 例子: 注意注意:我们在heade ...

  8. April 13 2017 Week 15 Thursday

    Happiness takes no account of time. 幸福不觉光阴过. Do you know the theory of relativity? If you know about ...

  9. React怎么创建.babelrc文件

    在windows环境下做react开发其实是一件非常让人头疼的事,强烈建议使用Mac或者是Linux系统,否则真的是自己挖坑自己跳了. 不过,这里还是给大家说说如何在windows环境下新建一个.ba ...

  10. Android(java)学习笔记141:Android下的逐帧动画(Drawable Animation)

    1. 帧动画: 帧动画顾名思义,一帧一帧播放的动画就是帧动画. 帧动画和我们小时候看的动画片的原理是一样的,在相同区域快速切换图片给人们呈现一种视觉的假象感觉像是在播放动画,其实不过是N张图片在一帧一 ...