首頁>技術>

版本控制系統

版本控制系統主要是針對軟體開發過程中對程式碼變更的管理,保證了程式碼的可回溯、可審查、可管理等功能需求,而最終的目的,是可維護性。在上一篇文章中,我們舉例闡述了沒有版本管理工具會出現什麼問題,因此不會在本文贅述。其實主要問題就是沒有版本管理工具導致的可維護性的下降,從而導致各種不可預測的bug的出現。

版本的主要內容包括三部分:

檢入檢出控制(Check-In / Check-Out Control)分支與合併(Branch / Merge)歷史記錄(History)檢入檢出控制

關於檢入檢出,我們可以理解為程式碼與 版本控制系統資料庫(VCS Database) 的同步與互動過程(如下圖)。

檢出(Check-out) 相當於使用者將 版本控制系統資料庫(VCS Database) 的程式碼同步了一份到本地,如果與原生代碼發生了衝突,會比對原生代碼並做相應處理(後面我們會討論合併)。

檢入(Check-in) 相當於是檢出的逆操作,也就是將原生代碼同步到遠端版本管理系統,其實是對程式碼資料庫的一種更新,相當於一次升級,因此,VCS 需要對將要更新的程式碼進行版本審查,並會拒絕將不合法的程式碼升級(例如非最新升級)更新到資料庫中。

檢入檢出的設定保證了程式碼資料庫的原子操作,不會因為兩個人同時提交程式碼而造成衝突。更改程式碼通常在本地進行,每一次更改完畢後會將最新的程式碼提交到程式碼資料庫,同時更新程式碼倉庫,保證遠端程式碼是最新的。

分支與合併

程式碼的 分支(Branch) 概念是指同一“祖先“的程式碼在不同的方向上各自進行更迭,互不干擾。不同分支的程式碼經過不斷的迭代,雖然來自同一個“祖先”節點,但很可能演變成完全不同的功能和結構。

一個分支相當於是對某個節點的拷貝,同時也可以自己發展成新的版本。版本控制系統中有分支的概念是為了方便開發多個 功能(Feature) 時防止互相干擾,是一種解決程式碼衝突的方式。

例如,某開發團隊需要開發 功能 A 和 功能 B ,而這兩個功能都需要在 檔案 M 上進行修改。如果同時開發 功能 A 和 功能 B,則會很難協調,因為開發人員需要同時操作檔案 M。這就跟要求兩個猴子吃同一根香蕉一樣困難,那麼我們為什麼不分成兩根香蕉、一隻猴吃一根呢?

因此,我們建立了兩個分支 A 和 B,兩個開發者分別開發,互不影響,互不干擾,這樣操作不會造成程式碼衝突,開發起來非常和諧。

雖然我們解決了同時修改一個檔案的問題,我們還需要將兩個開發好的功能整合起來,這就產生了 合併(Merge) 的概念。

如上面那張圖,當 A 功能 和 B 功能 都開發好了之後,分別為版本 A2 和版本 B2,需要將兩者整合在一起,產生新的版本 M2,這個整合的過程就是合併。

當然,合併的過程會產生 衝突(Conflict),也就是兩個分支同時修改了同一份程式碼。一般的版本控制系統(例如 Git 或 SVN)會嘗試自動將程式碼進行合併,例如非常明顯的增刪操作。然而,也有不能自動合併的情況,通常要求開發人員手動合併程式碼,這叫 解決衝突(Resolve Conflict)。

開發過程中,我們的分支管理會有很多種策略,一般開發團隊會根據專案的需要和團隊的情況選擇合適的分支管理策略,這個後面會講。

歷史記錄

這個其實很好理解,程式碼的所有變動,變動了什麼(What)、何處變更的(Where)、何時變的(When)、由誰提交的變更(Who)、為什麼變更(Why,提交註釋),這些都會體現在 歷史記錄(History) 中。

如果程式碼出了問題、或者需要參考歷史功能等,開發者可以根據歷史記錄來回溯程式碼,找到 Bug 發生的原因,以及理解歷史程式碼的設計等等。這有助於幫助後續的開(jie)發(pan)者(xia)更加輕鬆地掌握需要管理的程式碼。

分支管理策略

我們在前面著重介紹了一下分支與合併,這裡我們會介紹一下其衍生出來的分支管理策略,這對日常開發來說非常重要,因為不同的分支策略對專案開發來說有著深遠的影響。

分支管理策略可以看作是一種開發模式:團隊成員之間如何通過分支與合併操作來協同工作,將不同的功能整合在一起,開發測試環境如何隔離,生產環境上線後如何做相應更改等等。

下面我們將介紹幾種常見的分支管理策略。

主幹開發(Trunk Based Development)

主幹開發(Trunk Based Development) 簡稱為 TBD,是一種常見的分支管理策略,也是 Google 和 Facebook 等大型網際網路巨頭經常採用的策略之一。

主幹開發要求所有程式碼都提交到 主幹(Master) 分支上,從而避免了開發者們看到的程式碼是過時程式碼的情況。只有當需要 釋出(Release) 的時候,主幹才建立一個當前節點的分支,作為釋出用。

對於主幹開發來說,開發者要求每天開發前都同步最新程式碼,如果有與新提交程式碼的衝突,需要在本地自己解決後重新提交到主幹上。這樣的好處在於,由於開發者的原生代碼基本是跟主幹同步的,因此合併時並不會有重大的變更,合併程式碼的時候相對比較容易,不會花很多時間。

而這樣做的缺點也很明顯,如果很多人在專案上進行開發,會導致源源不斷的更新程式碼提交到主幹,這會導致釋出的時候存在眾多提交而導致出現了 bug 難以追溯,進而難以修復,容易出現“一顆耗子屎壞了一鍋粥”的情況。

我們接下來要介紹的 Git Flow 分支管理策略就是來解決這個缺點的。

Git Flow

Git Flow 是一種 特性分支(Feature Branch) 策略。特性分支策略的概念跟主幹開發策略的概念是相對的,意思是不同的功能拉出一個分支單獨開發,開發好後再合併到主幹,保證功能之間互不影響和干擾。

Git Flow 是特性分支策略的一種,是 Vincent Driessen 在 《A Successful Git Branching Model》(nvie.com/posts/a-suc…) 中提出的一種分支模型(如下圖)。

簡單來說,Git Flow 要求有 Master(主幹)、Develop(開發)、Release(釋出)和 Hotfix(熱修復)幾個基礎分支。

每次需要開發新功能時,在 Develop 分支下拉出一個 Feature 分支(特性分支)來進行單獨開發,開發好後合併到 Develop 分支。當 Develop 分支開發到一定程度的時候,再將其合併到 Release 分支。

Release 分支是在生產環境的 Master 分支前的起 UAT 測試緩衝作用的預備分支。當 Release 分支準備好之後,就將其合併入 Master 主幹分支,這樣就相當於在生產環境上釋出了新版本了。

當線上版本出了 Bug 需要修復的時候,我們會在直接在 Master 分支上拉出一個 Hotfix 分支,在這個分支上直接做修復,然後合併回主幹。同時 Hotfix 上的幾次修復還會合併到 Develop 上的某個節點,以儲存這次修復,這樣 Develop 與 Master 就基本保持了一致。

這是經典意義的 Git Flow,但在實際操作的時候我們並不一定會原封不動地完全照搬這個模式。例如,很多時候我們其實用不到 Release 分支,只存在 Master 和 Develop,這對於中小型專案來說比較靈活。有時候 Release 也被稱為 Test 測試分支。另外還有些變種,例如主幹開發與 Git Flow 的部分結合,在 Develop 分支上做主幹開發,每次需要部署到生產環境的時候就將其合併入 Release 或 Master 分支,以釋出正式版本。

Git or SVN?

Git 和 SVN(Subversion) 是兩個比較受歡迎的版本控制工具。兩者之間最大的區別是:SVN 是集中式的,而 Git 是分散式的(如下圖)。

SVN 要求程式碼倉庫只有一箇中心倉庫,所有開發者在做提交程式碼的操作前,必須保證自己原生代碼與中心倉庫的程式碼完全同步;而 Git 相對來說就靈活得多,Git 不要求所有開發者的原生代碼完全一致,在提交時只要求被 push 的倉庫分支與本地的一致就可以。

另外,SVN 的分支合併複雜且不健壯,因為 SVN 無法區分合並是人工操作還是自動合併的,因此將不會建立一個合併記錄節點;而 Git 則相反,會在合併後建立一個合併記錄節點,這增加了可回溯性。而且,用 Git 建立分支的成本很低,只需要 git branch <branch_name> 就可以了。

由於分支合併的複雜性,SVN 通常不適合用作特性分支策略,而適合主幹開發模式(Google App Engine 就是 SVN 管理的)。

而 Git 則既適合主幹開發模式,又天生支援特性分支策略。所以,我們一般都採用更靈活的 Git 作為版本控制工具,Git 也是現在更為主流的選擇。

開源工具 GitLabGitLab 簡介

GitLab 是一個開源的版本控制系統,使用 Git 作為程式碼管理工具,並在此基礎上搭建了 Web 服務,它有著精美的 Web UI 介面,方便使用者操作使用。

GitLab 支援 Git 程式碼倉庫、許可權管理、合併請求、Issues、Wiki、CI/CD 等非常多的強大功能。

GitLab 非常類似 GitHub,支援程式碼倉庫、程式碼合併、程式碼稽核等基礎功能,區別在於:GitHub 是一款雲端產品,而且專案大多為開源專案(私有倉庫有限制);而 GitLab 是開源產品,可以非常輕鬆的部署在任意一臺伺服器上。

我們用 GitLab 作為我們 DevOps 工作流的原因主要在其強大的視覺化介面和許可權管理,相當於加強版的 Git 倉庫。因為人是視覺動物,視覺化之後可以更加高效的處理各種複雜資訊,GitLab 可以幫助我們做到視覺化操作。

而對於企業開發來說,通常會有管理多個專案的需求,不同的專案也會有不同的開發者參與進來,因此有效地管理這些許可權會是個需要注意的問題,而 GitLab 本身就支援相關的許可權管理。

安裝 GitLab

安裝 GitLab 非常簡單,我們推薦的方式是用容器化工具 Docker 來進行安裝。如果您對 Docker 不熟悉,可以去網上查詢一下相關資料(我相信會非常多),或者關注本系列即將介紹的 DevOps 容器篇,在這篇文章中,我們將著重介紹 Docker。

Docker Hub 上的 GitLab 首頁:hub.docker.com/r/gitlab/gi…Docker 安裝 GitLab 官方教程:doc.gitlab.com/omnibus/doc…

在安裝之前,確保您已經在本機或者伺服器上安裝了 Docker,能夠執行基本的 Docker 操作,例如docker ps。

sudo docker run --detach \\ # --detach 表示是後臺執行 --hostname gitlab.example.com \\ # GitLab 中引用的 hostname,需要設定為伺服器域名 --publish 443:443 --publish 80:80 --publish 22:22 \\ # 對映埠 --name gitlab \\ # 容器名稱 --restart always \\ # 容器掛了之後會自動重啟 --volume /srv/gitlab/config:/etc/gitlab \\ # 配置持久化 --volume /srv/gitlab/logs:/var/log/gitlab \\ # 日誌持久化 --volume /srv/gitlab/data:/var/opt/gitlab \\ # 資料持久化 gitlab/gitlab-ce:latest # 映象名稱複製程式碼

這是官方文件中的 Docker 啟動命令,只需要在命令列中輸入以上命令,就可以啟動 GitLab 了。

稍微等待幾十秒,在瀏覽器中輸入 http://localhost 就可以看到 GitLab 的登陸頁面了。

使用 GitLab

這裡我們不打算詳細介紹 GitLab 的所有功能,我們只會簡單介紹 DevOps 工作流中的重要部分,主要是 Git 的版本控制部分:克隆倉庫、建立分支、合併分支。剩下的功能交給讀者閱讀官方文件或者自己安裝體驗。

克隆倉庫

在程式碼專案中,複製下面 SSH 或者 HTTP 的地址,例如 ssh://localhost/user/project1 ,在本地命令列中輸入如下內容。

git clone ssh://localhost/user/project1複製程式碼

然後會在當前目錄建立一個該專案的目錄,並將檔案從遠端拷貝下來。這裡會建立一個origin的remote記錄。可以通過如下方式看到。

git remote -v複製程式碼

如果我們通過 merge request 的方式來進行版本控制管理,我們將還會用到 git remote 的操作。

建立分支

我們可以在介面中建立分支,但我們也可以在本地建立。

在本地專案中輸入如下內容建立 develop 分支。

git branch developgit checkout develop# orgit checkout -b develop複製程式碼

然後更改一些程式碼,並通過git commit提交程式碼。接下來,將程式碼推送到遠端伺服器。

git push origin develop複製程式碼

這樣,遠端就建立了一個 develop 分支,並且將本地 develop 分支上的commits更新到伺服器上了。

合併分支

當我們想通過合併的方式將 develop 的更新同步到主幹 master 分支時,我們需要合併操作。

在本地,可以這樣操作。

git checkout mastergit merge developgit push origin master複製程式碼

這樣就完成了主幹的合併。

然而,有些時候特別是比較大型的專案時,我們並不希望在 master 分支上進行修改,我們想強制要求通過合併程式碼的方式來更新主幹分支。因此,我們需要加一個保護操作。我們可以在專案中的 Settings -> Repository -> Protected Branch 中設定保護主幹分支,也就是說,不允許 push 到 master (讀者請自行了解如何設定,這裡不贅述)。

如果做了這樣的保護限制,我們需要通過 merge request 的方式來合併分支。在 GitLab 中的專案首頁,點選左側選單的 Merge Requests,點選 New merge request,選擇 Source branch 為 develop,Target branch 為 master,點選 Compare branches and continue,進入提交合並請求頁面。在這個頁面中,你可以看到一些合併資訊,包括這次合併有哪些 commits,哪些程式碼有更改、更改了什麼,等等。點選 Submit merge request,一個 merge request 就建立好了。然後,如果你是有許可權的角色的話,你可以在這個合併請求頁面中點選 Merge 同意這次請求,master 分支也就合併好了。

GitLab 在合併分支時加入的程式碼審閱功能對於 Code Review 來說非常方便。通常來說有許可權同意合併請求的開發者都是 Reviewer,需要在合併之前審閱提交的程式碼,並根據審閱的結果同意或拒絕合併請求。

總結

本篇文章主要介紹了版本控制系統的主要概念,包括檢入檢出、分支合併、歷史記錄,以及兩種分支開發策略(主幹開發和 Git Flow),還比較了 Git 和 SVN。另外,本文還著重介紹了開源版本控制系統 GitLab,介紹了其概念、安裝和基本使用。這些知識,對於我們後面講 DevOps 工作流是非常重要的基礎。下一篇,我們將介紹持續整合(CI),這與版本控制系統息息相關,因為諸如 Jenkins 或者 GitLab CI/CD 可以用 GitLab 程式碼倉庫作為原始碼來源來自動構建產品。沒有版本控制系統,CI 的優勢會受到侷限。因此,版本控制系統是 DevOps 工作流中非常重要的模組。

在後面的文章中,我們將繼續介紹 DevOp 的其他內容,包括持續整合、容器化、編排、網路、以及如何將這一切串聯起來協同組成企業級的 DevOps 工作流。敬請期待後面的內容。

作者:MarvinZhang連結:https://juejin.im/post/5e61b3b86fb9a07ce152d131。

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 用開源軟體打造企業級 DevOps 工作流(一):概述