協同開發指南 - ywchiao/cocoa GitHub Wiki
此頁面是有關協同開發的說明,包括:
基本概念
Git 因為是 DVCS,所以團隊開發架構會分成 remote (GitHub) 和 local 兩部分。Local 代表從 remote 端抓下來存在自己的電腦上的專案目錄,
也就是執行:git clone https://github.com/ywchiao/cocoa.git
。
只要你不將更改上傳到 remote 端,local 端愛怎麼玩都行。但協同開發就是 你 或 你的同事 隨時都會 push (將變更上傳到 remote) 或是 pull (從 remote 將他人的更新同步到你的 local 專案當中),同事間執行這些動作也沒有固定順序,所以一定有發生衝突的可能性,更糟的情況就是,如果有人不小心做了不好的更改並上傳到 remote,其他同事再 pull 就會整個大亂 (例如:歷史紀錄被更動,造成日後無法做 code review)。
因此下面介紹的 工作流程 和 注意事項 就是為了降低 remote 端被搞亂的可能性,藉此讓每個成員能安心地與他人協同開發。
Workflow
Workflow 主要是讓多人協作能順利的進行,減少衝突的發生。目前由於團隊規模很小,因此採用簡單、能持續部署的 GitHub Flow,將來團隊擴增再採用嚴謹的 Git Flow。
如果將來有團隊外的夥伴想要貢獻程式碼,會讓他們以 Fork and Pull Request 模式協作。
GitHub Flow ★
img source 註:最上方那條為 master 分支
這種工作流程的主要分支(branch)是 master,作為 產品發布 用,它是 穩定 且 可部署的。重點是,不能直接在 master 上做修改。以 master 為基底,任何人在做開發時都應該新建額外的暫時性分支 (feature, bugfix, hotfix, etc.)。
當成員要將新的代碼合併回 master,不能在 master 上執行 git merge my-feature-branch
,而是要在 GitHub 發送 Pull Request。當 Project Manager (PM) 透過 pull request 審閱過這次的更改 (commit),確認沒問題了,才由 PM 合併回 master。 所以 pull request 有 code review 和 開發討論版 這兩種功用。由上述可知,成員能在 master 上做的事只有從 remote 端拉回更新 (pull)。
以 基本概念示意圖 為例,兩個成員 (藍色) 分別開發 F1、F2 兩種功能,除了在 local 分別創建 F1、F2 兩個分支,也可以將這些分支 push 到 remote 端,方便與其他成員協作。當成員完成功能開發時,便送出 pull requests。喬老師 (紅色)針對這兩個 pull requests 做 code review,甚至幫忙修正錯誤,最終沒問題了就將更改 merge 回 master。而 F1、F2 分支如果沒特別需求,merge 後便可刪除。
下表列出 PM 和 成員對於 master 分支的權限:
Master | Push | Pull | Merge to |
---|---|---|---|
PM | ✔ | ✔ | ✔ |
Member | ✗ | ✔ | ✗ |
Git Flow
Git Flow 相較於 GitHub Flow 多了一個主要分支,develop。而其它暫時性分支除了上述的兩種以外又多了 release。 一般開發中的變更會從 feature 整合到 develop,等到功能有了一定程度的完善後,就會從 develop 開一個 release 分支, 代表某一版本即將釋出。Release 分支不會再去合併新的 feature,此分支僅僅用來測試及修正,修正的 commits 也可以合併回 develop。 一切測試都通過後,最終在合併到 master,該版本就可以直接部署了。
產品發佈後,也許客戶發現某些問題,需要緊急修改,這時可以從 master 額外開一個 hotfix 分支來修補,問題解決後再合併回 master 與 develop。
所以分支的相依性以兩個主幹 master 和 develop 來看,會是:
- 分出去 (-->):
- master --> develop, hotfix
- develop --> feature, release
- 合併回 (<--):
- master <-- release, hotfix
- develop <-- feature, release (only bugfix), hotfix
Fork and Pull Request
這種協作模式跟上面兩種不一樣。開發者不直接 clone 主專案,而是去 fork 它。後者會直接將源專案拷貝到你的 GitHub 帳戶下,也就是你對拷貝的那份有完整的控制權。反之,如果是 clone 源專案,除非你是 collaborator,不然是沒有 push 權限的。
開發者可以在 fork 的專案中隨意的更改,當你想要貢獻自己的成果給源專案時,就必須向源專案主持人發送 pull request,審閱合格後,你的貢獻會 合併回源專案。這是種相對安全的工作流程。
但這種模式有個小麻煩,fork 的那份如果要與原專案同步更新,需要額外的設定。
Git Flow vs. GitHub Flow
由上面說明可以體會,Git Flow 需要永久的維護兩個主分支,而且又有許多暫時性分支,不利於初步階段的開發,因此才先採用 GitHub Flow。
但是會提到 Git Flow 而且會想在 開發後期 採用,其實有另一層考量:以產品類型來說,GitHub Flow 允許將開發進度直接整合到 master,所以適合 網站 這種常常需要更新的服務。
但考慮到我們是做 mobile app,之後必定會發布到 Google Play 或 AppStore,如果套用 GitHub Flow 就會有個問題,假設目前正好發布某個版本,當一切都穩妥地合併到 master 之後,就部署到 store 上了,而且是 AppStore 哦。大家都知道 Apple 的審查灰常嚴格,常常一拖就是一陣子,但我們的團員仍在開發,如果他們將 commits 合併到 master 分支會造成部署的問題,但不合併,成員彼此之間又沒法協同開發,因此就造成兩難的局面。而這一切的根源正是我們 只有一個主分支 master。而 Git Flow 的 develop 分支正好能作為緩衝區來解決上述問題。
注意事項
危險的指令
請大家特別注意 PUSH 後的 commits 或 branches,因為它們是 共享的 歷史紀錄,也就是說,如果不小心搞亂了這些紀錄 再 PUSH 回去,就會天下大亂。
以上圖來講,從 commit-1be017 之下的都算是共享的歷史紀錄。判斷的方法就是 1be017 右邊有個 (origin/master),意即 remote 的 master,但是上面還有兩個 commits,並且頂端的 commit 右邊有標注 (HEAD -> master)。這代表我們在 local 端加了兩個 commits 還沒 push 出去。
講到這裡,該說說哪些指令 (用在 commit level) 很危險了:
- amend: 修正並替換掉前一次的 commit
- reset: 執行
git reset 7e1775
會讓你的工作目錄回到該 commit 的狀態,但原本 7e1775 之後的四個 commit 都會刪除。 (7e1775 為 origin/master 下方第二個 commit) - rebase: 有兩種用途,第一種是用來 merge 分支的,第二種是整理 commits,我所謂的整理是指新增、刪除、修改、搬移、合併等等操作。
以上指令絕對不要用在共享紀錄。反之,上面三個指令如果用在圖中 origin/master 以上的 local commits 是很有用的。在開發時,常常會很隨意的增加一堆 commits,但有些 commit 可能比較沒意義或是可以跟其它 commit 併在一起,這時可以先用 rebase 去整理再 push 出去。如此,團隊在做 code review 時會比較輕鬆。
某些狀況,一定要回溯 共享紀錄 時,可以用:
- revert
- cherry-pick
- checkout:
git checkout commit-id; git checkout -b new-branch
Commit
提交 commit 以清楚、詳細、最小化為基本原則:
- 重要的修改儘量不要用
git commit -m "short-msg"
,而是用git commit
- 針對一個檔案修改了很多地方時,請用
git add --patch filename
搭配git commit
,這樣可以將 一個檔案內不同的修改分成多個 commits,而不是將 檔案內所有的修改視為一個 commit。
分支命名
分支根據你要做的事來命名,能讓人馬上看懂為原則,例如:
- hotfix/#13425: 緊急要修正的 issue,其 ID 為13425
- arduino/controller: 關於 arduino 的中控模組開發
- android/ui-codeblock: 手機端圖形程式介面的開發
- ...