在工作場合實施Git的時候,有很多種工作流程可供選擇,此時反而會讓你手足無措。本文羅列了企業團隊最常用的一些git工作流程,包括Centralized Workflow、Feature Branch Workflow、Gitflow Workflow、Forking Workflow。愿以此文拋磚引玉。
在你開始閱讀之前,請記住:這些流程應被視作為指導方針,而非“鐵律”。我們只是想告訴你可能的做法。因此,如果有必要的話,你可以組合使用不同的流程。
(本文主要介紹Gitflow Workflow……)

Vincent Driessen曾經寫過一篇博文,題為“A successful Git branching model”(一個成功的Git分支模型)。Gitflow工作流程就是從這篇文章里來的。
Gitflow工作流程圍繞項目發布定義了嚴格的分支模型。盡管它比Feature Branch Workflow更復雜一些,但它也為管理更大規模的項目提供了堅實的框架。
與Feature Branch Workflow比起來,Gitflow流程并沒有增加任何新的概念或命令。其特色在于,它為不同的分支分配了非常明確的角色,并且定義了使用場景和用法。除了用于功能開發的分支,它還使用獨立的分支進行發布前的準備、記錄以及后期維護。當然,你還是能充分利用Feature Branch Workflow的好處:拉拽請求(Pull Request)、隔離的試驗以及更高效率的合作。
### 它是怎么工作的?
Gitflow流程仍然使用一個中央代碼倉庫,它是所有開發者的信息交流中心。跟其他的工作流程一樣,開發者在本地完成開發,然后再將分支代碼推送到中央倉庫。唯一不同的是項目中分支的結構。
### 用于記錄歷史的分支
Gitflow使用兩個分支來記錄項目開發的歷史,而不是使用單一的master分支。在Gitflow流程中,master只是用于保存官方的發布歷史,而develop分支才是用于集成各種功能開發的分支。使用版本號為master上的所有提交打標簽(tag)也很方便。

事實上,Gitflow流程就是圍繞這兩個特點鮮明的分支展開的。
### 用于功能開發的分支
每一個新功能的開發都應該各自使用獨立的分支。為了備份或便于團隊之間的合作,這種分支也可以被推送到中央倉庫。但是,在創建新的功能開發分支時,父分支應該選擇develop(而不是master)。當功能開發完成時,改動的代碼應該被合并(merge)到develop分支。功能開發永遠不應該直接牽扯到master。

注意:組合使用功能開發分支和develop分支的這種設計,其實完全就是Feature Branch Workflow的理念。然而,Gitflow流程并不止于此。且看下文分解。
### 用于發布的分支

一旦develop分支積聚了足夠多的新功能(或者預定的發布日期臨近了),你可以基于develop分支建立一個用于產品發布的分支。這個分支的創建意味著一個發布周期的開始,也意味著本次發布不會再增加新的功能——在這個分支上只能修復bug,做一些文檔工作或者跟發布相關的任務。在一切準備就緒的時候,這個分支會被合并入master,并且用版本號打上標簽。另外,發布分支上的改動還應該合并入develop分支——在發布周期內,develop分支仍然在被使用(一些開發者會把其他功能集成到develop分支)。
使用專門的一個分支來為發布做準備的好處是,在一個團隊忙于當前的發布的同時,另一個團隊可以繼續為接下來的一次發布開發新功能。這也有助于清晰表明開發的狀態,比如說,團隊在匯報狀態時可以輕松使用這樣的措辭,“這星期我們要為發布4.0版本做準備。”從代碼倉庫的結構上也能直接反映出來。常用的一些措辭還有:基于develop新建分支,合并入master;命名規則為:release-或release/
### 用于維護的分支

發布后的維護工作或者緊急問題的快速修復也需要使用一個獨立的分支。這是唯一一種可以直接基于master創建的分支。一旦問題被修復了,所做的改動應該被合并入master和develop分支(或者用于當前發布的分支)。在這之后,master上還要使用更新的版本號打好標簽。
這種為解決緊急問題專設的綠色通道,讓團隊不必打亂當前的工作流程,也不必等待下一次的產品發布周期。你可以把用于維護的分支看成是依附于master的一種特別的發布分支。
## 舉例說明
下面的例子將演示Gitflow流程如何被用來管理一次產品發布。假設你已經創建好了一個中央倉庫。
### 1. 創建develop分支

第一步是給默認的master配備一個develop分支。一種簡單的做法是:讓一個開發者在本地建立一個空的develop分支,然后把它推送到服務器。
~~~
git branch develop
git push -u origin develop
~~~
develop分支將包含項目的所有歷史,而master會是一個縮減版本。現在,其他開發者應該克隆(clone)中央倉庫,并且為develop創建一個追蹤分支。
~~~
git clone ssh://user@host/path/to/repo.git
git checkout -b develop origin/develop
~~~
到現在,所有人都把包含有完整歷史的分支(develop)在本地配置好了。
### 2. 小馬和小明開始開發新功能

我們的故事從小馬和小明要分別開發新功能開始。他們倆各自建立了自己的分支。注意,他們在創建分支時,父分支不能選擇master,而要選擇develop。
`git checkout -b some-feature develop`
他們倆都在自己的功能開發分支上開展工作。通常就是這種Git三部曲:edit,stage,commit:
~~~
git status
git add <some-file>
git commit
~~~
### 3. 小馬把她的功能開發好了

在提交過幾次代碼之后,小馬覺得她的功能做完了。如果她所在的團隊使用“拉拽請求”,此刻便是一個合適的時機——她可以提出一個將她所完成的功能合并入develop分支的請求。要不然,她可以自行將她的代碼合并入本地的develop分支,然后再推送到中央倉庫,像這樣:
~~~
git pull origin develop
git checkout develop
git merge some-feature
git push
git branch -d some-feature
~~~
第一條命令確保了本地的develop分支擁有最新的代碼——這一步必須在將功能代碼合并之前做!注意,新開發的功能代碼永遠不能直接合并入master。必要時,還需要解決在代碼合并過程中的沖突。
### 4. 小馬開始準備一次發布

盡管小明還在忙著開發他的功能,小馬卻可以開始準備這個項目的第一次正式發布了。類似于功能開發,她使用了一個新的分支來做產品發布的準備工作。在這一步,發布的版本號也最初確定下來。
`git checkout -b release-0.1 develop`
這個分支專門用于發布前的準備,包括一些清理工作、全面的測試、文檔的更新以及任何其他的準備工作。它與用于功能開發的分支相似,不同之處在于它是專為產品發布服務的。
一旦小馬創建了這個分支并把它推向中央倉庫,這次產品發布包含的功能也就固定下來了。任何還處于開發狀態的功能只能等待下一個發布周期。
### 5. 小馬完成了發布

一切準備就緒之后,小馬就要把發布分支合并入master和develop分支,然后再將發布分支刪除。注意,往develop分支的合并是很重要的,因為開發人員可能在發布分支上修復了一些關鍵的問題,而這些修復對于正在開發中的新功能是有益的。再次提醒一下,如果小馬所在的團隊強調代碼評審(Code Review),此時非常適合提出這樣的請求。
~~~
git checkout master
git merge release-0.1
git push
git checkout develop
git merge release-0.1
git push
git branch -d release-0.1
~~~
發布分支扮演的角色是功能開發(develop)與官方發布(master)之間的一個緩沖。無論什么時候你把一些東西合并入master,你都應該隨即打上合適的標簽。
~~~
git tag -a 0.1 -m"Initial public release" master
git push --tags
~~~
Git支持鉤子(hook)的功能,也就是說,在代碼倉庫里某些特定的事件發生的時候,可以執行一些預定義的腳本。因此,一種可行的做法是:在服務器端配置一個鉤子,當你把master推送到中央倉庫或者推送標簽時,Git服務器能為產品發布進行一次自動的構建。
### 6. 用戶發現了一個bug

當一次發布完成之后,小馬便回去與小明一起開發其他功能了。突然,某個用戶提出抱怨說當前發布的產品里有一個bug。為了解決這個問題,小馬(或者小明)基于master創建了一個用于維護的分支。她在這個分支上修復了那個bug,然后把改動的代碼直接合并入master。
~~~
git checkout -b issue-#001 master
\# Fix the bug
git checkout master
git merge issue-#001
git push
~~~
跟用于發布的分支一樣,在維護分支上的改動也需要合并入develop分支,這一點是很重要的!因此,小馬務必不能忘了這一步。隨后,她就可以將維護分支刪除。
~~~
git checkout develop
git merge issue-#001
git push
git branch -d issue-#001
~~~