如果贡献者也用 Git,且擅于制作 format-patch
补丁,那你的合并工作将会非常轻松。因为这些补丁中除了文件内容差异外,还包含了作者信息和提交消息。所以请鼓励贡献者用 format-patch
生成补丁。对于传统的 diff
命令生成的补丁,则只能用 git apply
处理。
对于 format-patch
制作的新式补丁,应当使用 git am
命令。从技术上来说,git am
能够读取 mbox 格式的文件。这是种简单的纯文本文件,可以包含多封电邮,格式上用 From 加空格以及随便什么辅助信息所组成的行作为分隔行,以区分每封邮件,就像这样:
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001From: Jessica Smith <jessica@example.com>Date: Sun, 6 Apr 2008 10:17:23 -0700Subject: [PATCH 1/2] add limit to log functionLimit log functionality to the first 20
这是 format-patch
命令输出的开头几行,也是一个有效的 mbox 文件格式。如果有人用 git send-email
给你发了一个补丁,你可以将此邮件下载到本地,然后运行 git am
命令来应用这个补丁。如果你的邮件客户端能将多封电邮导出为 mbox 格式的文件,就可以用 git am
一次性应用所有导出的补丁。
如果贡献者将 format-patch
生成的补丁文件上传到类似 Request Ticket 一样的任务处理系统,那么可以先下载到本地,继而使用 git am
应用该补丁:
$ git am 0001-limit-log-function.patchApplying: add limit to log function
你会看到它被干净地应用到本地分支,并自动创建了新的提交对象。作者信息取自邮件头 From
和 Date
,提交消息则取自 Subject
以及正文中补丁之前的内容。来看具体实例,采纳之前展示的那个 mbox 电邮补丁后,最新的提交对象为:
$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0Author: Jessica Smith <jessica@example.com>AuthorDate: Sun Apr 6 10:17:23 2008 -0700Commit: Scott Chacon <schacon@gmail.com>CommitDate: Thu Apr 9 09:19:06 2009 -0700
add limit to log functionLimit log functionality to the first 20
Commit
部分显示的是采纳补丁的人,以及采纳的时间。而 Author
部分则显示的是原作者,以及创建补丁的时间。
有时,我们也会遇到打不上补丁的情况。这多半是因为主干分支和补丁的基础分支相差太远,但也可能是因为某些依赖补丁还未应用。这种情况下,git am
会报错并询问该怎么做:
$ git am 0001-seeing-if-this-helps-the-gem.patchApplying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not applyPatch failed at 0001.When you have resolved this problem run "git am --resolved".If you would prefer to skip this patch, instead run "git am --skip".To restore the original branch and stop patching run "git am --abort".
Git 会在有冲突的文件里加入冲突解决标记,这同合并或衍合操作一样。解决的办法也一样,先编辑文件消除冲突,然后暂存文件,最后运行 git am --resolved
提交修正结果:
$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolvedApplying: seeing if this helps the gem
如果想让 Git 更智能地处理冲突,可以用 -3
选项进行三方合并。如果当前分支未包含该补丁的基础代码或其祖先,那么三方合并就会失败,所以该选项默认为关闭状态。一般来说,如果该补丁是基于某个公开的提交制作而成的话,你总是可以通过同步来获取这个共同祖先,所以用三方合并选项可以解决很多麻烦:
$ git am -3 0001-seeing-if-this-helps-the-gem.patchApplying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not applyUsing index info to reconstruct a base tree...Falling back to patching base and 3-way merge...No changes -- Patch already applied.
像上面的例子,对于打过的补丁我又再打一遍,自然会产生冲突,但因为加上了 -3
选项,所以它很聪明地告诉我,无需更新,原有的补丁已经应用。
对于一次应用多个补丁时所用的 mbox 格式文件,可以用 am
命令的交互模式选项 -i
,这样就会在打每个补丁前停住,询问该如何操作:
$ git am -3 -i mboxCommit Body is:--------------------------
seeing if this helps the gem--------------------------Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
在多个补丁要打的情况下,这是个非常好的办法,一方面可以预览下补丁内容,同时也可以有选择性的接纳或跳过某些补丁。
打完所有补丁后,如果测试下来新特性可以正常工作,那就可以安心地将当前特性分支合并到长期分支中去了。