作為一名工程師, Git
在日常開發中是不可或缺的工具。
這裡詳細介紹幾種比較常用的基於 Git 的工作流模型, 以便於團隊協作的規範化和效率提升。
中心化工作流
使用過SVN的應該都知道, SVN使用的是集中式管理流程, 如果你剛從SVN 切換到 Git , 你可以嘗試使用中心化工作流的方式。這樣,你幾乎不需要變更之前的工作方式, 就可以完成平滑的過渡了。 而且在使用過程中還可以看到 Git 優於 SVN 的地方:
第一,每個成員都可以在本地擁有一份完整的專案程式碼倉庫,而不只是一個工作區的副本,任何人都可以在本地執行 add
和 commit
,而不需要考慮遠端倉庫是否有變更,直到需要的時候再去提交即可。
第二,Git 的工作區、暫存區、引用更新等設計,可以給開發者更多自由來切換當前工作,且不會造成程式碼丟失。
工作細節
中心化工作流的方式是:在遠端(遠端可以是伺服器端,也可以是本地的任意目錄)新建一個倉庫,預設是 master 分支,作為唯一的中心倉庫。 所有人都 clone
這個倉庫作為本地倉庫,並在本地倉庫進行開發。本地的提交是和遠端倉庫無關的,等需要的時候再 push
進主倉庫的 master 分支即可。
在這種方式下, 遠端是唯一確定的中心倉庫, 所有人都要以這個倉庫為準。 所以,在提交之前要先 fetch
最新提交,在這些提交之上作出自己的更改(一般我們使用 rebase
來完成)。
如果本地的修改和遠端倉庫中的變更發生了衝突,那麼 Git 會暫停 rebase
,並讓你來解決這些衝突。我們可以很簡單的使用 git status
和 git add
等命令完成衝突的合併。 另外, 如果我們解決不了衝突, 我們也可以使用 git rebase --abort
很容易的退出 rebase
的過程。
這樣每天的工作方式就變成了,從中心倉庫拉取最新程式碼, 然後開始一天的工作, 開發完成後,拉取中心倉庫的更新, 合併程式碼後, 再提交至中心倉庫, 結束一天的工作。 這樣的好處就是不需要變更原先(使用SVN)的工作方式。當然弊端也很明顯,你並不知道中心倉庫的程式碼是否是穩定的,或者說並不能確定當你的程式碼和中心倉庫程式碼合併後,是否是穩定的,帶來的問題就是開發進度和回滾不那麼方便控制。
示例
我們有兩位程式設計師, A 和 B, 兩人同時在對一個專案做開發, 並且使用 Git 的中心化工作流方式。
1.建立遠端中心倉庫
這裡我們有兩種方式:
藉助於已經搭建好的平臺 GitHub/GitLab 之類的,點選
create repo
即可。在遠端(這裡只是為了區別本地倉庫,事實上,使用任何一個其他人可以連通的機器都可以,包括自己本地其他目錄) 建立一個 裸倉庫 ,建立裸倉庫和我們平時建立本地倉庫的區別,可以參考我另一篇文章 Git 本地倉庫和裸倉庫 。
這裡以第二種方式為例:
# --bare 引數必須有
git init --bare /the/repo/path.git複製程式碼
2.所有人都 clone 中心倉庫到本地作為本地倉庫
git clone /the/repo/path.git複製程式碼
注意倉庫地址必須是正確的, 且有許可權訪問才能 clone
成功。
3.程式設計師 A 在他的本地倉庫進行功能開發並進行釋出
一般情況下,我們通過 git status
看看當前狀態,並通過 git add
和 git commit
等命令完成本地倉庫的提交。 當然這個提交影響的也只是本地倉庫而已,並沒有對中心倉庫產生任何影響,所以我們既不需要關心別人有什麼提交,也不用擔心我們當前的提交是否對別人造成了影響。當 A 認為自己所開發的功能已經完成, 那他將執行 git push origin master
這樣的操作,將自己本地倉庫所有不存在於中心倉庫的提交都 push
到遠端的中心倉庫上。
4.程式設計師 B 在他本地倉庫進行功能開發
B 在 clone
中心倉庫後所做的操作和 A 一樣,在本地倉庫進行專案開發,並在本地倉庫進行提交,他不需要知道中心倉庫發生了什麼樣的變化。
5.程式設計師 B 將自己開發的功能並進行釋出
B 在確認自己開發的功能已經完成後,想要將自己的程式碼通過 git push origin master
這樣的操作釋出至中心倉庫,但是卻被中心倉庫提示他的修改已經和中心倉庫有了分叉, 需要他先執行 git pull
之類的操作, 將中心倉庫上 A 的提交與 B 本地的提交進行合併才允許他併入中心倉庫。所以,他執行了 git pull --rebase origin master
來將中心倉庫的修改併入他的本地倉庫。使用 --rebase
引數的意義在於 fetch
執行完成後,將把 B 的所有提交移至 master
頂端。
當然這裡不使用 --rebase
引數也會成功,只不過是會生成一個合併提交,有些情況下使用 --ff
引數也可以避免產生合併提交。在這裡使用 --rebase
只是一個建議操作。
如果 A 和 B 修改的檔案沒有關聯,一般情況下會直接完成合並,如果發生衝突,Git 將會暫停 rebase
的過程,並列出當前衝突的檔案,你可以簡單的使用 git status
和 git add
等命令進行合併,合併後使用 git rebase --continue
繼續 rebase 的過程。或者使用 git rebase --abort
退出 rebase 過程。
在 B 合併完成後,可以執行 git push origin master
將自己開發的功能釋出至中心倉庫。
至此,基礎的中心化工作流方式就介紹完了,但是這裡也很容易看出來其中的問題,除了前面說到過的以外,還有就是效率低下,如果很多人都在持續進行提交,那很影響新功能的提交(多人持續性進行提交)。 一個比較容易提升效率的方式就是切換到特性分支工作流的方式。
特性分支工作流
基於特性的分支工作流,可以為每個特性做隔離,避免對中心倉庫主幹程式碼造成影響。
工作細節
顧名思義, 就是根據每個特性都會開一個新的分支,每個分支都應該包含著描述性的名稱,無論是一個人開發,還是多人協同,該特性的全部開發工作都在這個分支上進行。待該特性開發完成後, 併入主分支,然後刪除分支,程式碼上線。
這種情況下, 最大的優勢在於, 所有的特性開發都可以並行處理。 不必要像中心化工作流方式, 每個人的變動都可能引起其他的人的程式碼合併, 並且所有功能都雜糅在一起, 從測試和回滾都會變得很繁瑣。 另外的一個好處就是特性分支可以推送到中心倉庫,這樣也便於單獨測試。
這裡需要注意的是,特性分支往主分支合併的時機,應該是該特性開發完成,並測試通過,避免對主幹程式碼造成汙染。
在進行分支隔離後,我們發現,我們當前只處理了開發模式,但並沒有涵蓋一個很完備的產品生命週期, 開發、釋出、維護等過程,所以,我們有了 Gitflow 工作流。
Gitflow 工作流
基於Gitflow 的工作流方式, 這種工作流方式, 主要是管理著新功能開發,釋出及維護等模式,根據不同型別的工作對分支進行定義, 分為 特性分支 ,修復分支,release 分支,開發分支 和 主分支 。
主分支:中心倉庫建立後的預設 master 分支(當然使用其他分支也可以,但要保證該分支是受保護的)。主分支隨時保持程式碼是穩定的,並且有明確的版本標籤,後續程式碼回滾等操作都將從主分支進行。
開發分支:中心倉庫建立後,從 master 分支切出來,此時與 master 分支保持一致。後續演進中,開發分支隨時保持程式碼最新,但卻不一定是線上實際執行的程式碼。
git checkout -b develop複製程式碼
特性分支:應該從開發分支切出,開發完成後, 再合併進入開發分支, 如果達到了釋出標準, 則從開發分支切出 release 分支, 切出來的這個分支,只做該版本內的程式碼修復, 不再加入新功能, 這時此分支處於鎖定的狀態。
修復分支, 用於對線上主分支程式碼的及時修復, 待修復完成後, 合併進入主分支, 再併入開發分支。 修復分支只能從主分支切出。
發版分支, 一般命名為 release-xxx
這個分支只能從開發分支切出, 最後併入主分支,打上版本號的標籤,它也應該併入開發分支,如果中間有其他修復的話。
fork 工作流
fork 分支流和上面介紹的所有工作流都不太一樣。它的上游有一個唯一倉庫, 所有人都是 fork 這個倉庫, 在自己的遠端和自己的本地各維護一個倉庫,待開發完成後推入自己的遠端倉庫,並結合 GitHub/GitLab等提交 Pull Request,進入了 review 階段,待通過後,將會被合併入上游唯一的倉庫。這種方式比較適合 GitHub 中的大型開源專案, 對於小團隊的內部專案, 這種方式可能未必合適。
而且 fork 工作流, 會佔用更多的資源(畢竟每個人都維護一份遠端倉庫)。 而且每個人都看不到其他人的動態,只有當提交 Pull Request 的時候, 才知道每個人發生了什麼。
總結
我個人比較推薦的是 Gitflow 的開發工作流, 這種方式下,一切都是可控的, 每個分支都有各自獨立的功能,目的性很明確, 同時,在做程式碼回滾之類的操作也是可以直接剔除。 另外, 在這種工作流方式下, 團隊中的每個人都能很輕易的知道其他人在做什麼, 做出了什麼樣的改變, 對於團隊協作, 或許更加合適。
當然所有的工作流並不一定能完全套用, 可以吸取一些規範, 合併入自己的日常工作, 將程式碼倉庫的合作流程標準化和規範化, 這也是一切自動化的基礎。
可以通過公眾號 MoeLove 和我聯絡.
<全文完>