# 維護項目
除了如何有效地參與一個項目的貢獻之外,你可能也需要了解如何維護項目。這包含接受并應用別人使用 `format-patch` 生成并通過電子郵件發送過來的補丁,或對項目添加的遠程版本庫分支中的更改進行整合。但無論是管理版本庫,還是幫忙驗證、審核收到的補丁,都需要同其他貢獻者約定某種長期可持續的工作方式。
## 在特性分支中工作
如果你想向項目中整合一些新東西,最好將這些嘗試局限在特性分支——一種通常用來嘗試新東西的臨時分支中。這樣便于單獨調整補丁,如果遇到無法正常工作的情況,可以先不用管,等到有時間的時候再來處理。如果你基于你所嘗試進行工作的特性為分支創建一個簡單的名字,比如 `ruby_client` 或者具有類似描述性的其他名字,這樣即使你必須暫時拋棄它,以后回來時也不會忘記。項目的維護者一般還會為這些分支附帶命名空間,比如 `sc/ruby_client`(其中 `sc` 是貢獻該項工作的人名稱的簡寫)。你應該記得,可以使用如下方式基于 master 分支建立特性分支:
~~~
$ git branch sc/ruby_client master
~~~
或者如果你同時想立刻切換到新分支上的話,可以使用 `checkout -b` 選項:
~~~
$ git checkout -b sc/ruby_client master
~~~
現在你已經準備好將別人貢獻的工作加入到這個特性分支,并考慮是否將其合并到長期分支中去了。
## 應用來自郵件的補丁
如果你通過電子郵件收到了一個需要整合進入項目的補丁,你需要將其應用到特性分支中進行評估。有兩種應用該種補丁的方法:使用 `git apply`,或者使用 `git am`。
### 使用 `apply` 命令應用補丁
如果你收到了一個使用 `git diff` 或 Unix `diff` 命令(不推薦使用這種方式,具體見下一節)創建的補丁,可以使用 `git apply` 命令來應用。假設你將補丁保存在了 `/tmp/patch-ruby-client.patch` 中,可以這樣應用補丁:
~~~
$ git apply /tmp/patch-ruby-client.patch
~~~
這會修改工作目錄中的文件。它與運行 `patch -p1` 命令來應用補丁幾乎是等效的,但是這種方式更加嚴格,相對于 patch 來說,它能夠接受的模糊匹配更少。它也能夠處理 `git diff` 格式文件所描述的文件添加、刪除和重命名操作,而 `patch` 則不會。最后,`git apply` 命令采用了一種“全部應用,否則就全部撤銷(apply all or abort all)”的模型,即補丁只有全部內容都被應用和完全不被應用兩個狀態,而 `patch` 可能會導致補丁文件被部分應用,最后使你的工作目錄保持在一個比較奇怪的狀態。總體來看,`git apply` 命令要比 `patch` 謹慎得多。并且,它不會為你創建提交——在運行之后,你需要手動暫存并提交補丁所引入的更改。
在實際應用補丁前,你還可以使用 git apply 來檢查補丁是否可以順利應用——即對補丁運行 `git apply --check` 命令:
~~~
$ git apply --check 0001-seeing-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
~~~
如果沒有產生輸出,則該補丁可以順利應用。如果檢查失敗了,該命令還會以一個非零的狀態退出,所以需要時你也可以在腳本中使用它。
### 使用 `am` 命令應用補丁
如果補丁的貢獻者也是一個 Git 用戶,并且其能熟練使用 `format-patch` 命令來生成補丁,這樣的話你的工作會變得更加輕松,因為這種補丁中包含了作者信息和提交信息供你參考。如果可能的話,請鼓勵貢獻者使用 `format-patch` 而不是 `diff` 來為你生成補丁。而只有對老式的補丁,你才必須使用 `git apply` 命令。
要應用一個由 `format-patch` 命令生成的補丁,你應該使用 `git am` 命令。從技術的角度看,`git am` 是為了讀取 mbox 文件而構建的,mbox 是一種用來在單個文本文件中存儲一個或多個電子郵件消息的簡單純文本格式。其大致格式如下所示:
~~~
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function
Limit log functionality to the first 20
~~~
這其實就是你前面看到的 format-patch 命令輸出的開始幾行。而同時它也是有效的 mbox 電子郵件格式。如果有人使用 git send-email 命令將補丁以電子郵件的形式發送給你,你便可以將它下載為 mbox 格式的文件,之后將 git am 命令指向該文件,它會應用其中包含的所有補丁。如果你所使用的郵件客戶端能夠同時將多封郵件保存為 mbox 格式的文件,你甚至能夠將一系列補丁打包為單個 mbox 文件,并利用 `git am` 命令將它們一次性全部應用。
然而,如果貢獻者將 `format-patch` 生成的補丁文件上傳到類似 Request Ticket 的任務處理系統,你可以先將其保存到本地,之后通過 `git am` 來應用補丁:
~~~
$ git am 0001-limit-log-function.patch
Applying: add limit to log function
~~~
你會看到補丁被順利地應用,并且為你自動創建了一個新的提交。其中的作者信息來自于電子郵件頭部的 `From` 和 `Date` 字段,提交消息則取自 `Subject` 和郵件正文中補丁之前的內容。比如,應用上面那個 mbox 示例后生成的提交是這樣的:
~~~
$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author: Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit: Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700
add limit to log function
Limit log functionality to the first 20
~~~
其中 `Commit` 信息表示的是應用補丁的人和應用補丁的時間。`Author` 信息則表示補丁的原作者和原本的創建時間。
但是,有時候無法順利地應用補丁。這也許是因為你的主分支和創建補丁的分支相差較多,也有可能是因為這個補丁依賴于其他你尚未應用的補丁。這種情況下,`git am` 進程將會報錯并且詢問你要做什么:
~~~
$ git am 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch 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 am --resolved` 繼續應用下一個補丁:
~~~
$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: seeing if this helps the gem
~~~
如果你希望 Git 能夠嘗試以更加智能的方式解決沖突,你可以對其傳遞 `-3` 選項來使 Git 嘗試進行三方合并。該選項默認并沒有打開,因為如果用于創建補丁的提交并不在你的版本庫內的話,這樣做是沒有用處的。而如果你確實有那個提交的話——比如補丁是基于某個公共提交的——那么通常 `-3` 選項對于應用有沖突的補丁是更加明智的選擇。
~~~
$ git am -3 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.
~~~
比如上面這種情況,我在之前已經應用過同樣的補丁。如果沒有 `-3` 選項的話,這看起來就像是存在一個沖突。
如果你正在利用一個 mbox 文件應用多個補丁,也可以在交互模式下運行 `am` 命令,這樣在每個補丁之前,它會停住詢問你是否要應用該補丁:
~~~
$ git am -3 -i mbox
Commit Body is:
--------------------------
seeing if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
~~~
這在你保存的補丁較多時很好用,因為你可以在應用之前查看忘掉內容的補丁,并且跳過已經應用過的補丁。
當與你的特性相關的所有補丁都被應用并提交到分支中之后,你就可以選擇是否以及如何將其整合到更長期的分支中去了。
## 檢出遠程分支
如果你的貢獻者建立了自己的版本庫,并且向其中推送了若干修改,之后將版本庫的 URL 和包含更改的遠程分支發送給你,那么你可以將其添加為一個遠程分支,并且在本地進行合并。
比如 Jessica 向你發送了一封電子郵件,內容是在她的版本庫中的 `ruby-client` 分支中有一個很不錯的新功能,為了測試該功能,你可以將其添加為一個遠程分支,并在本地檢出:
~~~
$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client
~~~
如果她再次發郵件說另一個分支中包含另一個優秀功能,因為之前已經設置好遠程分支了,你就可以直接進行抓取及檢出操作。
這對于與他人長期合作工作來說很有用。而對于提交補丁頻率較小的貢獻者,相對于每個人維護自己的服務器,不斷增刪遠程分支的做法,使用電子郵件來接收可能會比較省時。況且你也不會想要加入數百個只提供一兩個補丁的遠程分支。然而,腳本和托管服務在一定程度上可以簡化這些工作——這很大程度上依賴于你和你的貢獻者開發的方式。
這種方式的另一種優點是你可以同時得到提交歷史。雖然代碼合并中可能會出現問題,但是你能獲知他人的工作是基于你的歷史中的具體哪一個位置;所以Git 會默認進行三方合并,不需要提供 `-3` 選項,你也不需要擔心補丁是基于某個你無法訪問的提交生成的。
對于非持續性的合作,如果你依然想要以這種方式拉取數據的話,你可以對遠程版本庫的 URL 調用 `git pull` 命令。這會執行一個一次性的抓取,而不會將該 URL 存為遠程引用:
~~~
$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
* branch HEAD -> FETCH_HEAD
Merge made by recursive.
~~~
## 確定引入了哪些東西
你已經有了一個包含其他人貢獻的特性分支。現在你可以決定如何處理它們了。本節回顧了若干命令,以便于你檢查若將其合并入主分支所引入的更改。
一般來說,你應該對該分支中所有 master 分支尚未包含的提交進行檢查。通過在分支名稱前加入 `--not` 選項,你可以排除 master 分支中的提交。這和我們之前使用的 `master..contrib` 格式是一樣的。假設貢獻者向你發送了兩個補丁,為此你創建了一個名叫 `contrib` 的分支并在其上應用補丁,你可以運行:
~~~
$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Oct 24 09:53:59 2008 -0700
seeing if this helps the gem
commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Oct 22 19:38:36 2008 -0700
updated the gemspec to hopefully work better
~~~
如果要查看每次提交所引入的具體修改,你應該記得可以給 `git log` 命令傳遞 `-p` 選項,這樣它會在每次提交后面附加對應的差異(diff)。
而要查看將該特性分支與另一個分支合并的完整 diff,你可能需要使用一個有些奇怪的技巧來得到正確的結果。你可能會想到這種方式:
~~~
$ git diff master
~~~
這個命令會輸出一個 diff,但它可能并不是我們想要的。如果在你創建特性分支之后,`master` 分支向前移動了,你獲得的結果就會顯得有些不對。這是因為 Git 會直接將該特性分支與 `master` 分支的最新提交快照進行比較。比如說你在 `master` 分支中向某個文件添加了一行內容,那么直接比對最新快照的結果看上去就像是你在特性分支中將這一行刪除了。
如果 `master` 分支是你的特性分支的直接祖先,其實是沒有任何問題的;但是一旦兩個分支的歷史產生了分叉,上述比對產生的 diff 看上去就像是將特性分支中所有的新東西加入,并且將 `master` 分支所獨有的東西刪除。
而你真正想要檢查的東西,實際上僅僅是特性分支所添加的更改——也就是該分支與 master 分支合并所要引入的工作。要達到此目的,你需要讓 Git 對特性分支上最新的提交與該分支與 master 分支的首個公共祖先進行比較。
從技術的角度講,你可以以手工的方式找出公共祖先,并對其顯式運行 diff 命令:
~~~
$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db
~~~
然而,這種做法比較麻煩,所以 Git 提供了一種比較便捷的方式:三點語法。對于 `diff` 命令來說,你可以通過把 `...` 置于另一個分支名后來對該分支的最新提交與兩個分支的共同祖先進行比較:
~~~
$ git diff master...contrib
~~~
該命令僅會顯示自當前特性分支與 master 分支的共同祖先起,該分支中的工作。這個語法很有用,應該牢記。
## 將貢獻的工作整合進來
當特性分支中所有的工作都已經準備好整合進入更靠近主線的分支時,接下來的問題就是如何進行整合了。此外,還有一個問題是,你想使用怎樣的總體工作流來維護你的項目?你的選擇有很多,我們會介紹其中的一部分。
### 合并工作流
一種非常簡單的工作流會直接將工作合并進入 `master` 分支。在這種情況下,`master` 分支包含的代碼是基本穩定的。當你完成某個特性分支的工作,或審核通過了其他人所貢獻的工作時,你會將其合并進入 master 分支,之后將特性分支刪除,如此反復。如果我們的版本庫包含類似 [Figure?5-20](#) 的兩個名稱分別為 `ruby_client` 和 `php_client` 的分支,并且我們先合并 `ruby_client` 分支,之后合并 `php_client` 分支,那么提交歷史最后會變成 [Figure?5-21](#) 的樣子。

Figure 5-20. 包含若干特性分支的提交歷史。

Figure 5-21. 合并特性分支之后。
這也許是最簡單的工作流了,但是當項目更大,或更穩定,你對自己所引入的工作更加在意時,它可能會帶來問題。
如果你的項目非常重要,你可能會使用兩階段合并循環。在這種情況下,你會維護兩個長期分支,分別是 `master` 和 `develop`,`master` 分支只會在一個非常穩定的版本發布時才會更新,而所有的新代碼會首先整合進入 `develop` 分支。你定期將這兩個分支推送到公共版本庫中。每次需要合并新的特性分支時([Figure?5-22](#)),你都應該合并進入 `develop` 分支([Figure?5-23](#));當打標簽發布的時候,你會將 `master` 分支快進到已經穩定的 `develop` 分支([Figure?5-24](#))。

Figure 5-22. 合并特性分支前。

Figure 5-23. 合并特性分支后。

Figure 5-24. 一次發布之后。
這樣當人們克隆你項目的版本庫后,既可以檢出 master 分支以構建最新的穩定版本并保持更新,也可以檢出包含更多新東西的 develop 分支。你也可以擴展這個概念,維護一個將所有工作合并到一起的整合分支。當該分支的代碼穩定并通過測試之后,將其合并進入 develop 分支;經過一段時間,確認其穩定之后,將其以快進的形式并入 master 分支。
### 大項目合并工作流
Git 項目包含四個長期分支:`master`、`next`,用于新工作的 `pu`(proposed updates)和用于維護性向后移植工作(maintenance backports)的 `maint` 分支。貢獻者的新工作會以類似之前所介紹的方式收入特性分支中(見 [Figure?5-25](#))。之后對特性分支進行測試評估,檢查其是否已經能夠合并,或者仍需要更多工作。安全的特性分支會被合并入 `next` 分支,之后該分支會被推送使得所有人都可以嘗試整合到一起的特性。

Figure 5-25. 管理復雜的一系列接收貢獻的平行特性分支。
如果特性分支需要更多工作,它則會被并入 `pu` 分支。當它們完全穩定之后,會被再次并入 `master` 分支。這意味著 `master` 分支始終在進行快進,`next` 分支偶爾會被變基,而 `pu` 分支的變基比較頻繁:

Figure 5-26. 將貢獻的特性分支并入長期整合分支。
當特性分支最終被并入 `master` 分支后,便會被從版本庫中刪除掉。Git 項目還有一個從上一次發布中派生出來的 `maint` 分支來提供向后移植過來的補丁以供發布維護更新。因此,當你克隆 Git 的版本庫之后,就會有四個可分別評估該項目開發的不同階段的可檢出的分支,檢出哪個分支,取決于你需要多新的版本,或者你想要如何進行貢獻;對于維護者來說,這套結構化的工作流能幫助它們審查新的貢獻。
### 變基與揀選工作流
為了保持線性的提交歷史,有些維護者更喜歡在 master 分支上對貢獻過來的工作進行變基和揀選,而不是直接將其合并。當你完成了某個特性分支中的工作,并且決定要將其整合的時候,你可以在該分支中運行變基命令,在當前 master 分支(或者是 `develop` 等分支)的基礎上重新構造修改。如果結果理想的話,你可以快進 `master` 分支,最后得到一個線性的項目提交歷史。
另一種將引入的工作轉移到其他分支的方法是揀選。Git 中的揀選類似于對特定的某次提交的變基。它會提取該提交的補丁,之后嘗試將其重新應用到當前分支上。這種方式在你只想引入特性分支中的某個提交,或者特性分支中只有一個提交,而你不想運行變基時很有用。舉個例子,假設你的項目提交歷史類似:

Figure 5-27. 揀選之前的示例歷史。
如果你希望將提交 `e43a6` 拉取到 master 分支,你可以運行:
~~~
$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
3 files changed, 17 insertions(+), 3 deletions(-)
~~~
這樣會拉取和 `e43a6` 相同的更改,但是因為應用的日期不同,你會得到一個新的提交 SHA-1 值。現在你的歷史會變成這樣:

Figure 5-28. 揀選特性分支中的一個提交后的歷史。
現在你可以刪除這個特性分支,并丟棄不想拉入的提交。
### Rerere
如果你在進行大量的合并或變基,或維護一個長期的特性分支,Git 提供的一個叫做“rerere”的功能會有一些幫助。
Rerere 是“重用已記錄的沖突解決方案(reuse recorded resolution)”的意思——它是一種簡化沖突解決的方法。當啟用 rerere 時,Git 將會維護一些成功合并之前和之后的鏡像,當 Git 發現之前已經修復過類似的沖突時,便會使用之前的修復方案,而不需要你的干預。
這個功能包含兩個部分:一個配置選項和一個命令。其中的配置選項是 `rerere.enabled`,把它放在全局配置中就可以了:
~~~
$ git config --global rerere.enabled true
~~~
現在每當你進行一次需要解決沖突的合并時,解決方案都會被記錄在緩存中,以備之后使用。
如果你需要和 rerere 的緩存交互,你可以使用 `git rerere` 命令。當單獨調用它時,Git 會檢查解決方案數據庫,嘗試尋找一個和當前任一沖突相關的匹配項并解決沖突(盡管當 `rerere.enabled` 被設置為 `true` 時會自動進行)。它也有若干子命令,可用來查看記錄項,刪除特定解決方案和清除緩存全部內容等。我們將在 [“Rerere”](#) 中詳細探討。
## 為發布打標簽
當你決定進行一次發布時,你可能想要留下一個標簽,這樣在之后的任何一個提交點都可以重新創建該發布。你在 [Chapter?2](#) 中已經了解了創建新標簽的過程。作為一個維護者,如果你決定要為標簽簽名的話,打標簽的過程應該是這樣子的:
~~~
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09
~~~
如果你為標簽簽名了,你可能會遇到分發用來簽名的 PGP 公鑰的問題。Git 項目的維護者已經解決了這一問題,其方法是在版本庫中以 blob 對象的形式包含他們的公鑰,并添加一個直接指向該內容的標簽。要完成這一任務,首先你可以通過運行 `gpg --list-keys` 找出你所想要的 key:
~~~
$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub 1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid Scott Chacon <schacon@gmail.com>
sub 2048g/45D02282 2009-02-09 [expires: 2010-02-09]
~~~
之后你可以通過導出 key 并通過管道傳遞給 `git hash-object` 來直接將 key 導入到 Git 的數據庫中,`git hash-object` 命令會向 Git 中寫入一個包含其內容的新 blob 對象,并向你返回該 blob 對象的 SHA-1 值:
~~~
$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92
~~~
既然 Git 中已經包含你的 key 的內容了,你就可以通過指定由 `hash-object` 命令給出的新 SHA-1 值來創建一個直接指向它的標簽:
~~~
$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92
~~~
如果你運行 `git push --tags` 命令,那么 `maintainer-pgp-pub` 標簽將會被共享給所有人。需要校驗標簽的人可以通過從數據庫中直接拉取 blob 對象并導入到 GPG 中來導入 PGP key:
~~~
$ git show maintainer-pgp-pub | gpg --import
~~~
人們可以使用這個 key 來校驗所有由你簽名的標簽。另外,如果你在標簽信息中包含了一些操作說明,用戶可以通過運行 `git show <tag>` 來獲取更多關于標簽校驗的說明。
## 生成一個構建號
Git 中不存在隨每次提交遞增的“v123”之類的數字序列,如果你想要為提交附上一個可讀的名稱,可以對其運行 `git describe` 命令。Git 將會給出一個字符串,它由最近的標簽名、自該標簽之后的提交數目和你所描述的提交的部分 SHA-1 值構成:
~~~
$ git describe master
v1.6.2-rc1-20-g8c5b85c
~~~
這樣你在導出一個快照或構建時,可以給出一個便于人們理解的命名。實際上,如果你的 Git 是從 Git 自己的版本庫克隆下來并構建的,那么 `git --version` 命令給出的結果是與此類似的。如果你所描述的提交自身就有一個標簽,那么它將只會輸出標簽名,沒有后面兩項信息。
注意 `git describe` 命令只適用于有注解的標簽(即使用 `-a` 或 `-s` 選項創建的標簽),所以如果你在使用 `git describe` 命令的話,為了確保能為標簽生成合適的名稱,打發布標簽時都應該采用加注解的方式。你也可以使用這個字符串來調用 checkout 或 show 命令,但是這依賴于其末尾的簡短 SHA-1 值,因此不一定一直有效。比如,最近 Linux 內核為了保證 SHA-1 值對象的唯一性,將其位數由 8 位擴展到了 10 位,導致以前的 `git describe` 輸出全部失效。
## 準備一次發布
現在你可以發布一個構建了。其中一件事情就是為那些不使用 Git 的可憐包們創建一個最新的快照歸檔。使用 `git archive` 命令完成此工作:
~~~
$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz
~~~
如果有人將這個壓縮包解壓,他就可以得到你的項目文件夾的最新快照。你也可以以類似的方式創建一個 zip 壓縮包,但此時你應該向 `git archive` 命令傳遞 `--format=zip` 選項:
~~~
$ git archive master --prefix='project/' --format=zip > `git describe master`.zip
~~~
現在你有了本次發布的一個 tar 包和一個 zip 包,可以將其上傳到網站或以電子郵件的形式發送給人們。
## 制作提交簡報
現在是時候通知郵件列表里那些好奇你的項目發生了什么的人了。使用 `git shortlog` 命令可以快速生成一份包含從上次發布之后項目新增內容的修改日志(changelog)類文檔。它會對你給定范圍內的所有提交進行總結;比如,你的上一次發布名稱是 v1.0.1,那么下面的命令可以給出上次發布以來所有提交的總結:
~~~
$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (8):
Add support for annotated tags to Grit::Tag
Add packed-refs annotated tag support.
Add Grit::Commit#to_patch
Update version and History.txt
Remove stray `puts`
Make ls_tree ignore nils
Tom Preston-Werner (4):
fix dates in history
dynamic version method
Version bump to 1.0.2
Regenerated gemspec for version 1.0.2
~~~
這份整潔的總結包括了自 v1.0.1 以來的所有提交,并且已經按照作者分好組,你可以通過電子郵件將其直接發送到列表中。
- 前言
- Scott Chacon 序
- Ben Straub 序
- 獻辭
- 貢獻者
- 引言
- 1. 起步
- 1.1 關于版本控制
- 1.2 Git 簡史
- 1.3 Git 基礎
- 1.4 命令行
- 1.5 安裝 Git
- 1.6 初次運行 Git 前的配置
- 1.7 獲取幫助
- 1.8 總結
- 2. Git 基礎
- 2.1 獲取 Git 倉庫
- 2.2 記錄每次更新到倉庫
- 2.3 查看提交歷史
- 2.4 撤消操作
- 2.5 遠程倉庫的使用
- 2.6 打標簽
- 2.7 Git 別名
- 2.8 總結
- 3. Git 分支
- 3.1 分支簡介
- 3.2 分支的新建與合并
- 3.3 分支管理
- 3.4 分支開發工作流
- 3.5 遠程分支
- 3.6 變基
- 3.7 總結
- 4. 服務器上的 Git
- 4.1 協議
- 4.2 在服務器上搭建 Git
- 4.3 生成 SSH 公鑰
- 4.4 配置服務器
- 4.5 Git 守護進程
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管的選擇
- 4.10 總結
- 5. 分布式 Git
- 5.1 分布式工作流程
- 5.2 向一個項目貢獻
- 5.3 維護項目
- 5.4 總結
- 6. GitHub
- 6.1 賬戶的創建和配置
- 6.2 對項目做出貢獻
- 6.3 維護項目
- 6.4 管理組織
- 6.5 腳本 GitHub
- 6.6 總結
- 7. Git 工具
- 7.1 選擇修訂版本
- 7.2 交互式暫存
- 7.3 儲藏與清理
- 7.4 簽署工作
- 7.5 搜索
- 7.6 重寫歷史
- 7.7 重置揭密
- 7.8 高級合并
- 7.9 Rerere
- 7.10 使用 Git 調試
- 7.11 子模塊
- 7.12 打包
- 7.13 替換
- 7.14 憑證存儲
- 7.15 總結
- 8. 自定義 Git
- 8.1 配置 Git
- 8.2 Git 屬性
- 8.3 Git 鉤子
- 8.4 使用強制策略的一個例子
- 8.5 總結
- 9. Git 與其他系統
- 9.1 作為客戶端的 Git
- 9.2 遷移到 Git
- 9.3 總結
- 10. Git 內部原理
- 10.1 底層命令和高層命令
- 10.2 Git 對象
- 10.3 Git 引用
- 10.4 包文件
- 10.5 引用規格
- 10.6 傳輸協議
- 10.7 維護與數據恢復
- 10.8 環境變量
- 10.9 總結
- A. 其它環境中的 Git
- A1.1 圖形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Eclipse 中的 Git
- A1.4 Bash 中的 Git
- A1.5 Zsh 中的 Git
- A1.6 Powershell 中的 Git
- A1.7 總結
- B. 將 Git 嵌入你的應用
- A2.1 命令行 Git 方式
- A2.2 Libgit2
- A2.3 JGit
- C. Git 命令
- A3.1 設置與配置
- A3.2 獲取與創建項目
- A3.3 快照基礎
- A3.4 分支與合并
- A3.5 項目分享與更新
- A3.6 檢查與比較
- A3.7 調試
- A3.8 補丁
- A3.9 郵件
- A3.10 外部系統
- A3.11 管理
- A3.12 底層命令