本篇文章帶大家了解git,介紹一下git 的基礎知識、以及基于 git 的多種工作流,希望對大家有所幫助!
通過本文你可以了解到:
- git 的起源
- git 的基礎知識
- gitflow 流程的基本方式
- 基于 git 的多種工作流
在講 Git Flow 之前,我們先講講別的東西
-
什么是版本?
版是指印刷時的版,本就是印刷出來的書本;版本是一種稱謂,用于描述同一事物的相互之間有差異的各種形式、狀態或內容。
換言之,任何事物只要有差異化都會涉及到版本這個概念,但是,我們這里說的版本,包括后面聊到的東西,都應該是一些有意義的版本,舉個例子,小明 1 月 1 日 至 1 月 31 日 每天都在改一份策劃書,2 月 1 號小明的甲方說還是上一個版本好,此時對于小明來說,上一個版本是什么?也許是最近一次小明發給甲方的一個方案,也許是上一個甲方說還可以的方案,但小明可能已經不記得具體是幾號改完給甲方的方案了。 -
常見的版本控制有哪些?
copy 文件以命名區分的方式、本編輯器的撤回/前進功能、使用專業工具如 svn、git 等等都屬于版本控制的范疇,不同的版本控制有不同的用途,比如文本編輯器的撤回,可以輕松撤銷本次修改,比如 copy 文件,可以讓新舊文件同時存在,方便對比,但這些方式太過簡單了,而且中間過程都是一些臨時性的東西,不足以作為一個修改歷史參考或者完整版本來看待,為此,還需要一些專業工具,如 集中式版本管理系統 SVN、CVS,分布式版本管理系統 BitKeeper、Git 等。 -
Git 開發背景
同生活中的許多偉大事物一樣,Git 誕生于一個極富紛爭大舉創新的年代。 linux 內核開源項目有著為數眾多的參與者。 絕大多數的 Linux 內核維護工作都花在了提交補丁和保存歸檔的繁瑣事務上(1991-2002年間)。 到 2002 年,整個項目組開始啟用一個專有的分布式版本控制系統 BitKeeper 來管理和維護代碼。到了 2005 年,開發 BitKeeper 的商業公司同 Linux 內核開源社區的合作關系結束,他們收回了 Linux 內核社區免費使用 BitKeeper 的權力。 這就迫使 Linux 開源社區(特別是 Linux 的締造者 Linus Torvalds)基于使用 BitKeeper 時的經驗教訓,開發出自己的版本系統。 他們對新的系統制訂了若干目標:速度簡單的設計對非線性開發模式的強力支持(允許成千上萬個并行開發的分支)完全分布式有能力高效管理類似 Linux 內核一樣的超大規模項目(速度和數據量) 自誕生于 2005 年以來,Git 日臻成熟完善,在高度易用的同時,仍然保留著初期設定的目標。 它的速度飛快,極其適合管理大項目,有著令人難以置信的非線性分支管理系統(參見 Git 分支)。
-
1991 年 Linux 開發了 linux 系統這個開源項目,采用郵件發送源文件附帶patch的方式進行寫作開發,由 Linux 本人進行手工合并;
-
2002 年 BitKeeper 與 Linux 社區達成協議,允許 Linux 社區免費試用 BitKeeper,由于免費試用,協議內容更多地是保護 BitKeeper 自身。
-
2005 年 BitKeeper 不滿 Linux 社區破壞協議內容(說白了就是反編譯 BitKeeper,試圖做破解版或其他),終止合作;
-
同 2005 年,Linux 花費了 2 周時間,開發了 Git 第一版,一個月內使用 Git 來管理 Linux 代碼;
Git 基礎知識
工作區(Workspace)、暫存區(Index)、版本庫(Repository)
#?創建并進入?testGitFlow?目錄 #?此時?testGitFlow?就是我們的工作區(Workspace),也就是工作目錄 $?mkdir?testGitFlow?&&?cd?testGitFlow #?初始化?git?倉庫 #?此時目錄中增加了?.git?目錄,.git?目錄就是?git?倉庫,不屬于工作區 $?git?init #?新增兩個文件 $?echo?111?>?a.txt $?echo?222?>?b.txt #?添加兩個文件到暫存區/索引(Index) $?git?add?. #?把索引中的兩個文件添加到版本庫(Repository) $?git?commit?-m?'init'
以上涉及的幾個概念:
Workspace: 簡單理解就是我們的項目目錄
Index: 簡單理解就是存儲即將提交的內容的區域
Repository: 版本倉庫
Commit、Tree、Blob 對象
#?通過?git?log?查看版本 $?git?log > commit?2b304a56998989dbcfd77f370f4b43fcad9e5872?(HEAD?->?master) Author:?huihuipan?<huihuipan163> Date:???Mon?Feb?27?17:56:53?2023?+0800 ????init #?通過?git?cat-file?查看?commit?信息 #?查看?commit?類型 $?git?cat-file?-t?2b304a >?commit #?查看?commit?內容 $?git?cat-file?-p?10d717 > tree?4caaa1a9ae0b274fba9e3675f9ef071616e5b209 author?huihuipan?<huihuipan163>?1677491813?+0800 committer?huihuipan?<huihuipan163>?1677491813?+0800 init #?可以發現有?tree,?author,?committer?等信息 #?繼續查看?tree?內容 $?git?cat-file?-t?4caaa1 >?tree $?git?cat-file?-p?4caaa1 > 100644?blob?58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c a.txt 100644?blob?c200906efd24ec5e783bee7f23b5d7c941b0c12c b.txt #?可以發現有?blob?信息 #?繼續查看?blob?內容 $?git?cat-file?-t?58c9bd? >?blob $?git?cat-file?-p?58c9bd? >?111 #?可以看到里面存儲的是?a.txt?的內容</huihuipan163></huihuipan163></huihuipan163>
以上涉及的幾個概念:
commit: commit 記錄提交的版本
tree: tree 記錄不同版本下的目錄結構和文件名
blob: blob 記錄文件內容
此時我們的 git 項目結構如下
修改文件及提交 commit 的時發生了什么?
- 首先,a.txt 內容 從 111 修改為 333,此時 git 倉庫沒有變化,只是工作區和索引的內容對不上了;
- 執行 git add 命令
- git 倉庫根據新的 a.txt 內容(333)創建出一個新的 blob 結點,記錄 a.txt 內容
- 索引從舊 blob 的指向新的 blob
- 執行 git commit 命令
- 根據索引的狀態,生成 tree 對象
- 根據新生成的 tree 對象和 上一個 commit 對象,生成新的 commit 對象
- 把分支指針從舊的 commit 對象移動到新的 commit 對象
HEAD、Branch、Tag
Branch: 是指向 Commit 的指針,每一次提交新的commit,當前的 Branch 都會指向最新的 commit;
HEAD: 指向 Branch 的指針,當checkout 到非 branch 時,會提示處于分離頭指針狀態,可以做一些試驗性的動作;
Tag: 指向 Commit 的指針,用作標簽,通常用作記錄固定版本,也可以理解為是指定 commit 的別名;
以上我們可以得知,git 的版本管理粒度去到了文件級別,blob 之間的對比即可得到 diff,這里也引申出了一個開發上的一個思考,當我們的程序設計的基礎是一個比較小粒度的時候,后續開發和擴展就會更加靈活,事實上git 對commit 的操作也是非常靈活,靈活到稍不注意就有可能釀成事故。
Checkout、Merge、Rebase、Fetch、Pull
checkout 檢出: 把 HEAD 檢出到指定 branch 或 commit,或者檢出指定版本指定文件的內容,由于在 git 里面checkout 承載了太多的功能,所有切換分支有專屬命令 switch。
merge 合并:
rebase 變基:
rebase 會修改版本歷史,即使 rebase 前與 rebase 后的內容一致,但版本不再是同一個版本
fetch: 從另一個存儲庫下載對象和引用,如遠程庫
pull: git pull = fetch + merge
基于 Git 的幾種工作流
Git Flow
簡介
出自 Vincent Driessen 在 2010年寫的一篇文章
《A successful Git branching model》
主要分支
有兩個分支會貫穿整個版本的生命周期,也就是長期分支:
- master 分支:用于發布
- develop 分支:用于開發 master 分支和 develop 分支的關系如上,虛線部分指著兩個分支并不是直接發生關聯,而是通過 release/hotfix 分支發生關聯
支撐分支
- feature branches: 用于需求開發
開發需求時從 develop 分支拉出 feature 分支,feature 分支開發完畢后(開發自測無問題)則合并回 develop 分支,合并后刪除分支,后續出現 bug 則在 develop 分支修改。
- release branches: 用于發布
當 develop 分支處于一個相對穩定的狀態時即可從 develop 分支拉出 release 分支準備發布,release 分支不進行功能開發,僅進行 bug 修復,直至無問題時合并到 master 分支進行發布,同時合并回 develop 分支后刪除 release 分支。
- hotfix branches: 用于修復生產問題
hotfix 分支用于修復生產環境上急需修復的 bug, 當生產環境出現 bug 時,從 master 分支拉出 hotfix 分支,修復后合并回 master 分支進行發布,同時合并到 develop 分支后刪除。
最后回顧一下完整的 gitflow
補充
在 2020年 Vincent Driessen 補充了一條《A successful Git branching model》,大概說 Git Flow這種模式在持續交付的軟件下顯得復雜,可以考慮使用 github Flow 而不是將 Git Flow 硬塞到項目中。
繼 Git Flow 之后 Adam Ruka 針對 Git Flow 的技術細節做了優化,提出了 One Flow
Github Flow
相對于 Git Flow,Github Flow 只有一條主干分支,通過 github 平臺加持增加 PR 流程: 進行某功能開發時,從 master 分支拉出 feature 分支,完成功能后提交 pr, 讓相關人員進行 review, review 期間仍可以對 feature 進行提交,直至確認無問題后通過 pr, 可以把 feature 分支合并到 master 分支進行發布
gitlab Flow
GitLab Flow 使用 master 分支作為開發分支,基于 master 分支另起發布分支 production
GitLab Flow 增加以下分支定義:
環境分支:當你需要在不同環境發布不同的版本時使用
發布分支:當項目需要發布不同的版本時使用,聲明了一個發布分支后,這個分支只會合并嚴重的漏洞修復更新。
持續發布
gitlab-flow 推薦使用 master 分支進行開發,基于 master 分支另建 production 分支進行發布,另外提出了 環境分支的概念,根據不同環境,逐層合并,最后匯總到 production 發布分支后進行發布
版本發布
如果你的項目需要發布不同的版本, gitlab-flow 版本發布模式可能更適合,在持續發布模式下,不同的版本會有不同的發布分支進行發布。
Aone Flow
Aone-flow 是以 master 分支為基礎,除 master 分支外其他都是臨時分支。基于 master 分支拉出環境分支,環境分支之間不進行任何關聯,獨立發展,環境分支也不允許直接修改,而是通過合并不同的 feature 分支進行組合。 feature 分支直至合并到 發布分支后才會刪除。有點是操作粒度更高更可控,缺點是環境分支的內容即使是一樣的,但版本歷史卻有可能不一致。
怎樣選擇版本控制
上面介紹了好幾種 flow, 從 gitflow 開始,gitflow 讓自由度超高的 git 得到了指導性的使用方式;
而 github-flow 又針對了 gitflow 的復雜性提出了極簡版的 flow;
gitlab-flow 又針對 gitflow 和 github-flow 過于復雜或過于簡單的方式,提出了自己折中的方案,同時還給出了兩種交付方式(持續交付、版本交付)的方案;
最后也介紹了 AoneFlow,一種操作粒度更自由的方案。
其實沒有一種萬能方案,不同的團隊/項目有著其特殊的情況,針對不同情況,flow 也在變化,合適的就是最好的。
最后
To conclude, always remember that panaceas don’t exist. Consider your own context. Don’t be hating. Decide for yourself.
引用 Vincent Driessen 的一句話:“最后,永遠記住萬能藥不存在。考慮你自己的背景。不要討厭。自己決定”
更多編程相關知識,請訪問:《A successful Git branching model》!!