2019 iT 邦幫忙鐵人賽 🔗文章補完計劃,從零開始建立自動化發佈的流水線 版控篇
當軟體持續發展,難免出現客制化需求,或是要求針對特定版本進行功能異動,尤其是團隊協作的情況下,有沒有那種方式可以提供有效的統整現有的程式碼,讓協作開發人員,都可以取得最新的開發版本。也可以快速的調出過往的程式異動版本,以便追查問題或調整。
版本控制系統( Version Control System, VCS)
在版本控制系統中,最重要的,莫過於「倉庫(Repository)」,在 Repository 中,會記錄所有的版本資訊、分支(Branch)、合併(Merge) 等等資料。而版控所有的指令,都是建立於系統對 Repository 的操作行為。
此外,版本控制系統,也一定存在同步、追溯與檔案備份,這三種特性。
- 同步 - 確保每個使用者,最終取回的內容都是相同的。
- 追溯 - 可以知道所有版本間的變動項目與原因,也能回復到歷史版本中的任一版本。
- 檔案備份 - 因為將所有的變動都同步到 Respository,間接的達到備份的功用。
目前主流的版本控制系統的架構,大致上,都可以歸屬於集中式與分散式這兩種類型。在下面的圖中,Subversion 就是集中式版本控制系統,Git 則是分散式版本控制系統。

集中式版本控制系統(Centralized VCS)
集中式的版本控制系統,就是基於……同個團隊之間合作的方式,是共用同一個 Repository。系統必須確保每位使用者都需與 Respository 保持一致。為要維持使用者之間保持同步的狀態,分為 鎖定模式 與 合併模式 兩種作法。
-
鎖定模式
當使用者想要修改某檔案、簽出該檔案後,該檔案便會進入鎖定狀態,其他使用成員便無法加以修改,直到簽出者將該檔簽回為止。
對於維持同步來說,這當然是一個十分保險的作法,因為永遠不會有兩個或以上的使用者同時修改同一個檔案。
只是,這種方法造成了使用者對於檔案修改的互斥效應,使得使用效率受到影響。
-
合併模式
允許多位使用者同時針對同一檔案進行修改,當他們分別將檔案提交回集中的檔案庫時,若發生衝突的情況,便會自動進行合併,而若自動合併失敗,再要求人工進行衝突的調解。
對於 Subversion SVN 版本控管有興趣的話,可以延伸閱讀 DEMO 大的 Subversion SVN 版本控管 🔗,針對 SVN 寫了一系列的教學文章。
集中式版本控制不足處
-
無法離線工作
因為所有使用者共用同一個 Respository ,因此 Respository 幾乎存放在網路可連結的主機。使用者想要對 Respository 進行任何動作,都必須在能夠連網的環境下進行。
-
動一鬆而牽全身
如果某一個使用者,在檔案修改的過程中,就提交到 Respository ,那麼,便有可能影響到 Respository 內的檔案,處於不穩定或異常的狀態。
若強制使用者必需修成完成後,才能提交檔案至 Respository。但是,在修改的過程中,使用者無法得到版本控制系統的支援,有效控制各階段的修改內容。
分散式版本控制系統 (Distributed VCS)
分散式的版本控制系統允許 Respository 存在一份或多份。每個使用者,都可以在自己電腦中,建立 Respository。
對於分散式版本控制系統而言,讓每個使用者都可以修改各自的 Respository,享受版本控制系統的支援,而不受其他因素的限制。
同時,運用 Push、Pull 的動作,使用者之間可分享自己的變更內容,達到同步的結果。因此,為了更有效的同步版本內容,分散式版本控制系統就更重視 Branch、Merge 的支援與功能。

(圖片出處: www.git-tower.com 🔗)
在分散式版本控制系統中,分成本地端與遠端 Repository。
在本地端,為了確實的管控所有的檔案變更與異常,並讓使用者充分的運用到版本控制系統的支援。分成工作區(Working Copy/Working Directory)、暫存區(Staging area)以及本地Repository。
-
工作區
這個部份,是我們進行檔案操作的位置。不管是 切換 (checkout)、複制 (clone)、重置 (reset)、拉 (pull),複原 (discard) 等動作結果,都會直接影響這邊。
-
暫存區
當工作複本內容檔案修改後,會將要上傳變更的內容快照一份放在這邊。在 提交 (commit) 時,會將這區域內的資料,上傳到到 Repository。
-
本地 Repository
保存了所有變更過的檔案,以及各版本的歷史紀錄。
在遠端,也會存在一個遠端 Repository,以儲存管理所有使用者,所上傳的最終變更的版本資訊。同時,也是它提供其他使用者同步資訊的共同來源。利用 推(push)、拉 (pull)、複制 (clone)、拿取(fetch) 等動作,來達到同步旳結果。
版本控制的幾點建議
-
適時的 提交(Commit) 變動
-
每次的 Commit ,盡可能的確實變動內容的單純性與獨主性。例如,這次 Commit 的 Fix Bug 的變動,那在變動的內容,應盡可能避免與 Fix Bug 無關的內容。
-
每次的 Commit,都應確保變更後內容,是可以順利 Build 的。
-
-
良好的 Commit 訊息
每次的 Commit ,確認描述此次變動的原因與修改內容。利用有效的訊息,提高軟體的維護性。
Git 簡說
Git 本地端的使用
# Init
git init
# 提交 commit
git commit
# 切換 checkout
git checkout master
# 重置 reset
git reset C3
Git 分支
當我們使用 Git 時,一定會有一條主線 master,但是在實務上,有時會遇到幾種狀況。(PS: 由於 2020 年的黑人平權運動,已逐漸調整名稱為 main)
- 功能己經寫了一半,但客戶突然告知要放棄該功能。
- 針對現有的軟體,業務跟你說,某客戶要求增修某個功能,但這功能只是個案。
- ……
像這個時候,為了跟原本軟體版本有所差異,只好分成不同支線來控制版本,以方便管理。但最後,別忘了評估那些功能可以合併回主線,盡可能避免無限開分支的情況。
別忘了,當要維護的分支越多,對開發人員的負擔就越大。
# 分支 branch
git branch develop
# 合併 merge
git merge master
Git 與遠端同步
因為本地端在沒有設定前,它並不知道遠端 Repository 的位置。所以要同步資訊到遠端前,必需先將遠端 Repository 建立起來後,將它跟本地端進行綁定。
# 設定 Remote Repository
git remote add origin http://xxxx.xxx.xx.xx/ironman/git_tech.git
# push 方法一
git push origin master
# push 方法二: 指定遠端的分支名稱
git push origin master:master
# fetch: 確認本地與遠端的異常,此時並不會真正的下載遠端的資料
git fetch
# pull: 確認本地與遠端的異常,並下載遠端資料
git pull
# clone: 將遠端資料複制於本地端。
git clone
Git Flow
在網路上找到 git flow 的介紹,但研究後,還是有部份的疑問。
因此,吉米打電話跟 Eric 請教 git flow 的問題。
gitflow 是 Vincent Driessen 在 2010 年提出來的一種關於 git 工作流程的建議。

(圖片來源: A successful Git branching model 🔗)
Branch 規則
在 git flow 中,有兩支最重要的 branch,分別是 master、develop 。
Master
原本的主線,但在 git flow 中,比較接近交付給客戶的產品版本。因此,會變更到 master 的時機點,只有在完成 release 與 hotfix 後,才會變動。
Develop
develop 是基於 master 所分支出的 開發用主線。當採用 git flow 作為管理方式時,會自行從 master 開一支名為 develop 的分支。
所有對於需求的新增、修改,或是問題的修正,都會在此作業。直到完成開發後,才會用 release ,與 master 進行同步。
Feature
所有功能的增加、修改,會不同的需求目標,開一條前綴為 feature 的分支。直到完成功能的開發後,就會 merage 回 develop。
而每個 feature 只處理各自需求目標。也就是一次( feature)只做一件事 (功能)。
Release
當預定的功能都開發完成時,這時就會開一條前綴為 release 的分支,此時,只能針對預備釋出的功能,進行 現有功能的錯誤修正。
確定所有功能正常無誤,完成 release 後,會先後 merge 到 master 與 develop。
HotFix
當交付的軟體回報有問題時,會開前綴為 hotfix 的分支。完成修正後,會先後 merge 到 master 與 develop。
Git Flow 的爭議點
目前 git flow 主要的爭議點,就筆者看到的,大約可分成兩點。
- develop/master 的分割
- 多人協作時,完成 feature 時的衝突。
develop/master 的分割
這可能是因為 develop 與 master 的功用相近,但在 git flow 的流程下,操作相當變得複雜。尤其在講求 快速交付 的現在,執行 DevOps 的人眼中看來,develop 存在的意義不大。
但在某些軟體公司而言,因為客戶性質的關係,交付軟體的週期較長。master/develop 可以確保產品維護的同時,又可以進行開功能的開發。
或是開發過程的異動太大,develop 的異動,不會直接影響到 master,就也就己交付軟體的內容。
多人協作時,完成 feature 時的衝突
會發生這個問題,多數是由於負責功能項目,在程式內的耦合性太高,導致兩個 feature 以上,去變更到同一份文件。
這部份能從幾個部份下手,例如 開發人員之間的溝通協商、增加 push 時的審核機制,例如 Github 所使用的 pull request 機制。或是優化軟體架構,讓它其中的物件低耦合,高內聚。
Git Flow 使用經驗與建議
- 針對 Feature,要實作的功能範圍應該可能的小,以確保該功能可以在半個到一個工作日內完成。
- 每天工作前,都應該 Fetch 最新版本。下班前,將完成的 feature push 。
- 軟體架構內物件,要盡可能的低耦合高內聚,才能減少功能修改時的交互影響。
延伸閱讀
▶ 版控觀念
- Local, Central and Distributed Version Control Systems 🔗
- 三個使用版本控制系統的建議 🔗
- How to write the perfect pull request 🔗
- 卓越開發者必備的版本控制技巧 🔗
▶ Git
- Git 官網 🔗
- 連猴子都能懂的 Git 入門指南 🔗
- 30 天精通 Git 版本控管 🔗
- Learn Git Branching 🔗
- Subversion SVN 版本控管 🔗
- Git 教學 🔗
- Git 達人教你搞懂 GitHub 基礎觀念 🔗
- 為你自己學 Git 🔗
▶ Git Flow
💬 留下你的想法
有問題、不同看法,或是你踩過類似的坑?歡迎留言討論,我會盡量回覆。