1. 程式人生 > >【Git專案管理】分散式 Git - 向一個專案貢獻

【Git專案管理】分散式 Git - 向一個專案貢獻

分散式 Git - 向一個專案貢獻

向一個專案貢獻

描述如何向一個專案貢獻的主要困難在於完成貢獻有很多不同的方式。 因為 Git 非常靈活,人們可以通過不同的方式來一起工作,所以描述應該如何貢獻並不是非常準確 - 每一個專案都有一點兒不同。 影響因素包括活躍貢獻者的數量、選擇的工作流程、提交許可權與可能包含的外部貢獻方法。

第一個影響因素是活躍貢獻者的數量 - 積極地向這個專案貢獻程式碼的使用者數量以及他們的貢獻頻率。 在許多情況下,你可能會有兩三個開發者一天提交幾次,對於不活躍的專案可能更少。 對於大一些的公司或專案,開發者的數量可能會是上千,每天都有成百上千次提交。 這很重要,因為隨著開發者越來越多,在確保你的程式碼能幹淨地應用或輕鬆地合併時會遇到更多問題。 提交的改動可能表現為過時的,也可能在你正在做改動或者等待改動被批准應用時被合併入的工作嚴重損壞。 如何保證程式碼始終是最新的,並且提交始終是有效的?

下一個影響因素是專案使用的工作流程。 它是中心化的嗎,即每一個開發者都對主線程式碼有相同的寫入許可權? 專案是否有一個檢查所有補丁的維護者或整合者? 是否所有的補丁是同行評審後批准的? 你是否參與了那個過程? 是否存在副官系統,你必須先將你的工作提交到上面?

下一個問題是提交許可權。 是否有專案的寫許可權會使向專案貢獻所需的流程有極大的不同。 如果沒有寫許可權,專案會選擇何種方式接受貢獻的工作? 是否甚至有一個如何貢獻的規範? 你一次貢獻多少工作? 你多久貢獻一次?

所有這些問題都會影響實際如何向一個專案貢獻,以及對你來說哪些工作流程更適合或者可用。 我們將會由淺入深,通過一系列用例來講述其中的每一個方面;從這些例子應該能夠建立實際中你需要的特定工作流程。

提交準則

在我們開始檢視特定的用例前,這裡有一個關於提交資訊的快速說明。 有一個好的建立提交的準則並且堅持使用會讓與 Git 工作和與其他人協作更容易。 Git 專案提供了一個文件,其中列舉了關於建立提交到提交補丁的若干好的提示 - 可以在 Git 原始碼中的 Documentation/SubmittingPatches 檔案中閱讀它。

首先,你不會想要把空白錯誤(根據 git help diff 的描述,結合下面給出的圖片,空白錯誤是指行尾的空格、Tab 製表符,和行首空格後跟 Tab 製表符的行為)提交上去。 Git 提供了一個簡單的方式來檢查這點 - 在提交前,執行 git diff --check

,它將會找到可能的空白錯誤並將它們為你列出來。

`git diff --check` 的輸出。 Figure 57.  git diff --check 的輸出

如果在提交前執行那個命令,可以知道提交中是否包含可能會使其他開發者惱怒的空白問題。

接下來,嘗試讓每一個提交成為一個邏輯上的獨立變更集。 如果可以,嘗試讓改動可以理解 - 不要在整個週末編碼解決五個問題,然後在週一時將它們提交為一個巨大的提交。 即使在週末期間你無法提交,在週一時使用暫存區域將你的工作最少拆分為每個問題一個提交,並且為每一個提交附帶一個有用的資訊。 如果其中一些改動修改了同一個檔案,嘗試使用 git add --patch 來部分暫存檔案(在 互動式暫存 中有詳細介紹)。 不管你做一個或五個提交,只要所有的改動是在同一時刻新增的,專案分支末端的快照就是獨立的,使同事開發者必須審查你的改動時儘量讓事情容易些。 當你之後需要時這個方法也會使拉出或還原一個變更集更容易些。 重寫歷史 描述了重寫歷史與互動式暫存檔案的若干有用的 Git 技巧 - 在將工作傳送給其他人前使用這些工具來幫助生成一個乾淨又易懂的歷史。

最後一件要牢記的事是提交資訊。 有一個建立優質提交資訊的習慣會使 Git 的使用與協作容易的多。 一般情況下,資訊應當以少於 50 個字元(25個漢字)的單行開始且簡要地描述變更,接著是一個空白行,再接著是一個更詳細的解釋。 Git 專案要求一個更詳細的解釋,包括做改動的動機和它的實現與之前行為的對比 - 這是一個值得遵循的好規則。 在這些資訊中使用現在時態祈使語氣也是一個好想法。 換句話說,使用命令。 使用 “Add tests for.” 而不是 “I added tests for” 或 “Adding tests for,”。 這裡是一份最初由 Tim Pope 寫的模板:

修改的摘要(50 個字元或更少)

如果必要的話,加入更詳細的解釋文字。在
大概 72 個字元的時候換行。在某些情形下,
第一行被當作一封電子郵件的標題,剩下的
文字作為正文。分隔摘要與正文的空行是
必須的(除非你完全省略正文);如果你將
兩者混在一起,那麼類似變基等工具無法
正常工作。

空行接著更進一步的段落。

  - 句號也是可以的。

  - 專案符號可以使用典型的連字元或星號
    前面一個空格,之間用空行隔開,
    但是可以依據不同的慣例有所不同。

如果你所有的提交資訊看起來都像這樣,對你與跟你工作在一起的其他開發者來說事情會變得非常容易。 Git 專案有一個良好格式化的提交資訊 - 嘗試在那兒執行 git log --no-merges 來看看漂亮的格式化的專案提交歷史像什麼樣。

在接下來的例子中,以及貫穿本書大部分,出於簡潔性的原因本書不會有像這樣漂亮格式的資訊;相反,我們使用 -m 選項的 git commit。 照我們說的做,而不是照我們做的做。

私有小型團隊

你可能會遇到的最簡單的配置是有一兩個其他開發者的私有專案。 “私有” 在這個上下文中,意味著閉源 - 不可以從外面的世界中訪問到。 你和其他的開發者都有倉庫的推送許可權。

在這個環境下,可以採用一個類似使用 Subversion 或其他集中式的系統時會使用的工作流程。 依然可以得到像離線提交、非常容易地新建分支與合併分支等高階功能,但是工作流程可以是很簡單的;主要的區別是合併發生在客戶端這邊而不是在提交時發生在伺服器那邊。 讓我們看看當兩個開發者在一個共享倉庫中一起工作時會是什麼樣子。 第一個開發者,John,克隆了倉庫,做了改動,然後本地提交。 (為了縮短這些例子長度,協議資訊已被替換為 ...。)

# John's Machine
$ git clone [email protected]:simplegit.git
Initialized empty Git repository in /home/john/simplegit/.git/
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'removed invalid default value'
[master 738ee87] removed invalid default value
 1 files changed, 1 insertions(+), 1 deletions(-)

第二個開發者,Jessica,做了同樣的事情 - 克隆倉庫並提交了一個改動:

# Jessica's Machine
$ git clone [email protected]:simplegit.git
Initialized empty Git repository in /home/jessica/simplegit/.git/
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
 1 files changed, 1 insertions(+), 0 deletions(-)

現在,Jessica 把她的工作推送到伺服器上:

# Jessica's Machine
$ git push origin master
...
To [email protected]:simplegit.git
   1edee6b..fbff5bc  master -> master

John 也嘗試推送他的改動:

# John's Machine
$ git push origin master
To [email protected]:simplegit.git
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '[email protected]:simplegit.git'

不允許 John 推送是因為在同一時間 Jessica 已經推送了。 如果之前習慣於用 Subversion 那麼理解這點特別重要,因為你會注意到兩個開發者並沒有編輯同一個檔案。 儘管 Subversion 會對編輯的不同檔案在伺服器上自動進行一次合併,但 Git 要求你在本地合併提交。 John 必須抓取 Jessica 的改動併合並它們,才能被允許推送。

$ git fetch origin
...
From [email protected]:simplegit
 + 049d078...fbff5bc master     -> origin/master

在這個時候,John 的本地倉庫看起來像這樣:

John 的分叉歷史。 Figure 58. John 的分叉歷史

John 有一個引用指向 Jessica 推送上去的改動,但是他必須將它們合併入自己的工作中之後才能被允許推送。

$ git merge origin/master
Merge made by recursive.
 TODO |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

合併進行地很順利 - John 的提交歷史現在看起來像這樣:

合併了 `origin/master` 之後 John 的倉庫。 Figure 59. 合併了  origin/master 之後 John 的倉庫

現在,John 可以測試程式碼,確保它依然正常工作,然後他可以把合併的新工作推送到伺服器上:

$ git push origin master
...
To [email protected]:simplegit.git
   fbff5bc..72bbc59  master -> master

最終,John 的提交歷史看起來像這樣:

推送到 `origin` 伺服器後 John 的歷史。 Figure 60. 推送到  origin 伺服器後 John 的歷史

在此期間,Jessica 在一個特性分支上工作。 她建立了一個稱作 issue54 的特性分支並且在那個分支上做了三次提交。 她還沒有抓取 John 的改動,所以她的提交歷史看起來像這樣:

Jessica 的特性分支。 Figure 61. Jessica 的特性分支

Jessica 想要與 John 同步,所以她進行了抓取操作:

# Jessica's Machine
$ git fetch origin
...
From [email protected]:simplegit
   fbff5bc..72bbc59  master     -> origin/master

那會同時拉取 John 推送的工作。 Jessica 的歷史現在看起來像這樣:

抓取 John 的改動後 Jessica 的歷史。 Figure 62. 抓取 John 的改動後 Jessica 的歷史

Jessica 認為她的特性分支已經準備好了,但是她想要知道必須合併什麼進入她的工作才能推送。 她執行 git log 來找出:

$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <[email protected]>
Date:   Fri May 29 16:01:27 2009 -0700

   removed invalid default value

issue54..origin/master 語法是一個日誌過濾器,要求 Git 只顯示所有在後面分支(在本例中是 origin/master)但不在前面分支(在本例中是 issue54)的提交的列表。 我們將會在 提交區間 中詳細介紹這個語法。

目前,我們可以從輸出中看到有一個 John 生成的但是 Jessica 還沒有合併入的提交。 如果她合併 origin/master,也就是說將會修改她的本地工作的那個單個提交。

現在,Jessica 可以合併她的特性工作到她的 master 分支,合併 John 的工作(origin/master)進入她的 master 分支,然後再次推送回伺服器。 首先,為了整合所有這些工作她切換回她的 master 分支。

$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.

她既可以先合併 origin/master 也可以先合併 issue54 - 它們都是上游,所以順序並沒有關係。 不論她選擇的順序是什麼最終的結果快照是完全一樣的;只是歷史會有一點輕微的區別。 她選擇先合併入 issue54

$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
 README           |    1 +
 lib/simplegit.rb |    6 +++++-
 2 files changed, 6 insertions(+), 1 deletions(-)

沒有發生問題;如你所見它是一次簡單的快進。 現在 Jessica 合併入 John 的工作(origin/master):

$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

每一個檔案都乾淨地合併了,Jessica 的歷史看起來像這樣:

合併了 John 的改動後 Jessica 的歷史。 Figure 63. 合併了 John 的改動後 Jessica 的歷史

現在 origin/master 是可以從 Jessica 的 master 分支到達的,所以她應該可以成功地推送(假設同一時間 John 並沒有再次推送):

$ git push origin master
...
To [email protected]:simplegit.git
   72bbc59..8059c15  master -> master

每一個開發者都提交了幾次併成功地合併了其他人的工作。

推送所有的改動回伺服器後 Jessica 的歷史。 Figure 64. 推送所有的改動回伺服器後 Jessica 的歷史

這是一個最簡單的工作流程。 你通常在一個特性分支工作一會兒,當它準備好整合時合併回你的 master 分支。 當想要共享工作時,將其合併回你自己的 master 分支,如果有改動的話然後抓取併合並 origin/master,最終推送到伺服器上的 master 分支。 通常順序像這樣:

一個簡單的多人 Git 工作流程的通常事件順序。 Figure 65. 一個簡單的多人 Git 工作流程的通常事件順序

私有管理團隊

在接下來的情形中,你會看到大型私有團隊中貢獻者的角色。 在你將學習到的這種工作環境中,小組基於特性進行協作,這些團隊的貢獻將會由其他人整合。

讓我們假設 John 與 Jessica 在一個特性上工作,同時 Jessica 與 Josie 在第二個特性上工作。 在本例中,公司使用了一種整合-管理者工作流程,獨立小組的工作只能被特定的工程師整合,主倉庫的 master 分支只能被那些工程師更新。 在這種情況下,所有的工作都是在基於團隊的分支上完成的並且稍後會被整合者拉到一起。

因為 Jessica 在兩個特性上工作,並且平行地與兩個不同的開發者協作,讓我們跟隨她的工作流程。 假設她已經克隆了倉庫,首先決定在 featureA 上工作。 她為那個特性建立了一個新分支然後在那做了一些工作:

# Jessica's Machine
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'add limit to log function'
[featureA 3300904] add limit to log function
 1 files changed, 1 insertions(+), 1 deletions(-)

在這個時候,她需要將工作共享給 John,所以她推送了 featureA 分支的提交到伺服器上。 Jessica 沒有 master 分支的推送許可權 - 只有整合者有 - 所以為了與 John 協作必須推送另一個分支。

$ git push -u origin featureA
...
To [email protected]:simplegit.git
 * [new branch]      featureA -> featureA

Jessica 向 John 發郵件告訴他已經推送了一些工作到 featureA 分支現在可以看一看。 當她等待 John 的反饋時,Jessica 決定與 Josie 開始在 featureB 上工作。 為了開始工作,她基於伺服器的 master 分支開始了一個新分支。

# Jessica's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'

現在,Jessica 在 featureB 分支上建立了幾次提交:

$ vim lib/simplegit.rb
$ git commit -am 'made the ls-tree function recursive'
[featureB e5b0fdc] made the ls-tree function recursive
 1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'add ls-files'
[featureB 8512791] add ls-files
 1 files changed, 5 insertions(+), 0 deletions(-)

Jessica 的倉庫看起來像這樣:

Jessica 的初始提交歷史。 Figure 66. Jessica 的初始提交歷史

她準備好推送工作了,但是一封來自 Josie 的郵件告知一些初始工作已經被推送到伺服器上的 featureBee上了。 Jessica 在能推送到伺服器前首先需要將那些改動與她自己的合併。 然後她可以通過 git fetch 抓取 Josie 的改動:

$ git fetch origin
...
From [email protected]:simplegit
 * [new branch]      featureBee -> origin/featureBee

Jessica 現在可以通過 git merge 將其合併到她做的工作中:

$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by recursive.
 lib/simplegit.rb |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

有點兒問題 - 她需要將在 featureB 分支上合併的工作推送到伺服器上的 featureBee 分支。 她可以通過指定本地分支加上冒號(:)加上遠端分支給 git push 命令來這樣做:

$ git push -u origin featureB:featureBee
...
To [email protected]:simplegit.git
   fba9af8..cd685d1  featureB -> featureBee

這稱作一個 引用規格。 檢視 引用規格 瞭解關於 Git 引用規格與通過它們可以做的不同的事情的詳細討論。 也要注意 -u 標記;這是 --set-upstream 的簡寫,該標記會為之後輕鬆地推送與拉取配置分支。

緊接著,John 發郵件給 Jessica 說他已經推送了一些改動到 featureA 分支並要求她去驗證它們。 她執行一個 git fetch 來拉取下那些改動:

$ git fetch origin
...
From [email protected]:simplegit
   3300904..aad881d  featureA   -> origin/featureA

然後,通過 git log 她可以看到哪些發生了改變:

$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <[email protected]>
Date:   Fri May 29 19:57:33 2009 -0700

    changed log output to 30 from 25

最終,她合併 John 的工作到她自己的 featureA 分支:

$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
 lib/simplegit.rb |   10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)

Jessica 想要輕微調整一些東西,所以她再次提交然後將其推送回伺服器:

$ git commit -am 'small tweak'
[featureA 774b3ed] small tweak
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To [email protected]:simplegit.git
   3300904..774b3ed  featureA -> featureA

Jessica 的提交歷史現在看起來像這樣:

在一個特性分支提交後 Jessica 的歷史。 Figure 67. 在一個特性分支提交後 Jessica 的歷史

Jessica、Josie 與 John 通知整合者在伺服器上的 featureA 與 featureBee 分支準備好整合到主線中了。 在整合者合併這些分支到主線後,一次抓取會拿下來一個新的合併提交,使歷史看起來像這樣:

合併了 Jessica 的兩個特性分支後她的歷史。 Figure 68. 合併了 Jessica 的兩個特性分支後她的歷史

許多團隊切換到 Git 是因為這一允許多個團隊並行工作、並在之後合併不同工作的能力。 團隊中更小一些的子小組可以通過遠端分支協作而不必影響或妨礙整個團隊的能力是 Git 的一個巨大優勢。 在這兒看到的工作流程順序類似這樣:

這種管理團隊工作流程的基本順序。 Figure 69. 這種管理團隊工作流程的基本順序

派生的公開專案

向公開專案做貢獻有一點兒不同。 因為沒有許可權直接更新專案的分支,你必須用其他辦法將工作給維護者。 第一個例子描述在支援簡單派生的 Git 託管上使用派生來做貢獻。 許多託管站點支援這個功能(包括 GitHub、BitBucket、Google Code、repo.or.cz 等等),許多專案維護者期望這種風格的貢獻。 下一節會討論偏好通過郵件接受貢獻補丁的專案。

首先,你可能想要克隆主倉庫,為計劃貢獻的補丁或補丁序列建立一個特性分支,然後在那兒做工作。 順序看起來基本像這樣:

$ git clone (url)
$ cd project
$ git checkout -b featureA
# (work)
$ git commit
# (work)
$ git commit
Note

你可能會想要使用 rebase -i 來將工作壓縮成一個單獨的提交,或者重排提交中的工作使補丁更容易被維護者稽核 - 檢視 重寫歷史 瞭解關於互動式變基的更多資訊。

當你的分支工作完成後準備將其貢獻回維護者,去原始專案中然後點選 “Fork” 按鈕,建立一份自己的可寫的專案派生倉庫。 然後需要新增這個新倉庫 URL 為第二個遠端倉庫,在本例中稱作 myfork

$ git remote add myfork (url)

然後需要推送工作到上面。 相對於合併到主分支再推送上去,推送你正在工作的特性分支到倉庫上更簡單。 原因是工作如果不被接受或者是被揀選的,就不必回退你的 master 分支。 如果維護者合併、變基或揀選你的工作,不管怎樣你最終會通過拉取他們的倉庫找回來你的工作。

$ git push -u myfork featureA

當工作已經被推送到你的派生後,你需要通知維護者。 這通常被稱作一個拉取請求(pull request),你既可以通過網站生成它 - GitHub 有它自己的 Pull Request 機制,我們將會在 GitHub 介紹 - 也可以執行 git request-pull 命令然後手動地將輸出傳送電子郵件給專案的維護者。

request-pull 命令接受特性分支拉入的基礎分支,以及它們拉入的 Git 倉庫 URL,輸出請求拉入的所有修改的總結。 例如,Jessica 想要傳送給 John 一個拉取請求,她已經在剛剛推送的分支上做了兩次提交。她可以執行這個:

$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
  John Smith (1):
        added a new function

are available in the git repository at:

  git://githost/simplegit.git featureA

Jessica Smith (2):
      add limit to log function
      change log output to 30 from 25

 lib/simplegit.rb |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)

這個輸出可以被髮送給維護者 - 它告訴他們工作是從哪個分支開始、歸納的提交與從哪裡拉入這些工作。

在一個你不是維護者的專案上,通常有一個總是跟蹤 origin/master 的 master 分支會很方便,在特性分支上做工作是因為如果它們被拒絕時你可以輕鬆地丟棄。 如果同一時間主倉庫移動了然後你的提交不再能幹淨地應用,那麼使工作主題獨立於特性分支也會使你變基(rebase)工作時更容易。 例如,你想要提供第二個特性工作到專案,不要繼續在剛剛推送的特性分支上工作 - 從主倉庫的 master 分支重新開始:

$ git checkout -b featureB origin/master
# (work)
$ git commit
$ git push myfork featureB
# (email maintainer)
$ git fetch origin

現在,每一個特性都儲存在一個貯藏庫中 - 類似於補丁佇列 - 可以重寫、變基與修改而不會讓特性互相干涉或互相依賴,像這樣:

`featureB` 的初始提交歷史。 Figure 70.  featureB 的初始提交歷史

假設專案維護者已經拉取了一串其他補丁,然後嘗試拉取你的第一個分支,但是沒有乾淨地合併。 在這種情況下,可以嘗試變基那個分支到 origin/master 的頂部,為維護者解決衝突,然後重新提交你的改動:

$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA

這樣會重寫你的歷史,現在看起來像是 featureA 工作之後的提交歷史

`featureA` 工作之後的提交歷史。 Figure 71.  featureA 工作之後的提交歷史

因為你將分支變基了,所以必須為推送命令指定 -f 選項,這樣才能將伺服器上有一個不是它的後代的提交的 featureA 分支替換掉。 一個替代的選項是推送這個新工作到伺服器上的一個不同分支(可能稱作 featureAv2)。

讓我們看一個更有可能的情況:維護者看到了你的第二個分支上的工作並且很喜歡其中的概念,但是想要你修改一下實現的細節。 你也可以利用這次機會將工作基於專案現在的 master 分支。 你從現在的 origin/master 分支開始一個新分支,在那兒壓縮 featureB 的改動,解決任何衝突,改變實現,然後推送它為一個新分支。

 
$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
# (change implementation)
$ git commit
$ git push myfork featureBv2

--squash 選項接受被合併的分支上的所有工作,並將其壓縮至一個變更集,使倉庫變成一個真正的合併發生的狀態,而不會真的生成一個合併提交。 這意味著你的未來的提交將會只有一個父提交,並允許你引入另一個分支的所有改動,然後在記錄一個新提交前做更多的改動。 同樣 --no-commit 選項在預設合併過程中可以用來延遲生成合並提交。

現在你可以給維護者傳送一條訊息,表示你已經做了要求的修改然後他們可以在你的 featureBv2 分支上找到那些改動。

`featureBv2` 工作之後的提交歷史。 Figure 72.  featureBv2 工作之後的提交歷史

通過郵件的公開專案

許多專案建立了接受補丁的流程 - 需要檢查每一個專案的特定規則,因為它們之間有區別。 因為有幾個歷史悠久的、大型的專案會通過一個開發者的郵件列表接受補丁,現在我們將會通過一個例子來演示。

工作流程與之前的用例是類似的 - 你為工作的每一個補丁序列建立特性分支。 區別是如何提交它們到專案中。 生成每一個提交序列的電子郵件版本然後郵寄它們到開發者郵件列表,而不是派生專案然後推送到你自己的可寫版本。

$ git checkout -b topicA
# (work)
$ git commit
# (work)
$ git commit

現在有兩個提交要傳送到郵件列表。 使用 git format-patch 來生成可以郵寄到列表的 mbox 格式的檔案 - 它將每一個提交轉換為一封電子郵件,提交資訊的第一行作為主題,剩餘資訊與提交引入的補丁作為正文。 它有一個好處是是使用 format-patch 生成的一封電子郵件應用的提交正確地保留了所有的提交資訊。

$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch

format-patch 命令打印出它建立的補丁檔名字。 -M 開關告訴 Git 查詢重新命名。 檔案最後看起來像這樣:

$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <[email protected]>
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

---
 lib/simplegit.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log #{treeish}")
+    command("git log -n 20 #{treeish}")
   end

   def ls_tree(treeish = 'master')
--
2.1.0

也可以編輯這些補丁檔案為郵件列表新增更多不想要在提交資訊中顯示出來的資訊。 如果在 --- 行與補丁開頭(diff --git 行)之間新增文字,那麼開發者就可以閱讀它;但是應用補丁時會排除它。

為了將其郵寄到郵件列表,你既可以將檔案貼上進電子郵件客戶端,也可以通過命令列程式傳送它。 貼上文字經常會發生格式化問題,特別是那些不會合適地保留換行符與其他空白的 “更聰明的” 客戶端。 幸運的是,Git 提供了一個工具幫助你通過 IMAP 傳送正確格式化的補丁,這可能對你更容易些。 我們將會演示如何通過 Gmail 傳送一個補丁,它正好是我們所知最好的郵件代理;可以在之前提到的 Git 原始碼中的 Documentation/SubmittingPatches 檔案的最下面瞭解一系列郵件程式的詳細指令。

首先,需要在 ~/.gitconfig 檔案中設定 imap 區塊。 可以通過一系列的 git config 命令來分別設定每一個值,或者手動新增它們,不管怎樣最後配置檔案應該看起來像這樣:

[imap]
  folder = "[Gmail]/Drafts"
  host = imaps://imap.gmail.com
  user = [email protected]
  pass = p4ssw0rd
  port = 993
  sslverify = false

如果 IMAP 伺服器不使用 SSL,最後兩行可能沒有必要,host 的值會是 imap:// 而不是 imaps://。 當那些設定完成後,可以使用 git imap-send 將補丁序列放在特定 IMAP 伺服器的 Drafts 資料夾中:

$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done

在這個時候,你應該能夠到 Drafts 資料夾中,修改收件人欄位為想要傳送補丁的郵件列表,可能需要抄送給維護者或負責那個部分的人,然後傳送。

你也可以通過一個 SMTP 伺服器傳送補丁。 同之前一樣,你可以通過一系列的 git config 命令來分別設定選項,或者你可以手動地將它們新增到你的 ~/.gitconfig 檔案的 sendmail 區塊:

[sendemail]
  smtpencryption = tls
  smtpserver = smtp.gmail.com
  smtpuser = [email protected]
  smtpserverport = 587

當這完成後,你可以使用 git send-email 傳送你的補丁:

$ git send-email *.patch
0001-added-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <[email protected]>]
Emails will be sent from: Jessica Smith <[email protected]>
Who should the emails be sent to? [email protected]
Message-ID to be used as In-Reply-To for the first email? y

然後,對於正在傳送的每一個補丁,Git 會吐出這樣的一串日誌資訊:

(mbox) Adding cc: Jessica Smith <[email protected]> from
  \line 'From: Jessica Smith <[email protected]ple.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i [email protected]
From: Jessica Smith <[email protected]>
To: [email protected]
Subject: [PATCH 1/2] added limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <[email protected]>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>

Result: OK

總結

這個部分介紹了處理可能會遇到的幾個迥然不同型別的 Git 專案的一些常見的工作流程,介紹了幫助管理這個過程的一些新工具。 接下來,你會了解到如何在貢獻的另一面工作:維護一個 Git 專案。 你將會學習如何成為一個仁慈的獨裁者或整合管理者。

 

---------------------------------------------------------技術分享,歡迎轉載! 但必須備註本文連結--------------------------------------------------------------