Git-補丁文件交互

版本庫間的交互是經過git push和/或git pull命令實現的,這是Git最主要的交互模式,但並非所有。使用補丁文件是另一種交互方式,適用於參與者衆多的大型項目進行分佈式開發。git

建立補丁

Git提供了將提交批量轉換爲補丁文件的命令:git format-patch。該命令後面的參數是一個版本範圍列表,會將包含在此列表中的提交一一轉換爲補丁文件,每一個補丁文件包含一個序號並從提交說明中提取字符串做爲文件名。app

下面演示一下在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行能夠看到新增的簽名。ui

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),其他提交說明做爲郵件內容(若是有的話),文件補丁用三個橫線和提交說明分開。this

實際上這些補丁文件能夠直接拿來做爲郵件發送給項目的負責人。Git提供了一個輔助郵件發送的命令git send-email。下面用該命令將這三個補丁文件以郵件形式發送出去。spa

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 命令的提示符(&)下輸入命令。3d

& 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
相關文章
相關標籤/搜索