因爲最近工作上多處都用到了基於 Git 的開發,需要深入理解 Git 的工作原理,以往的 Git 基本知識已經滿足不了需求了,因此寫下這篇 Git 進階的文章,主要是介紹了一些大家平時會碰到但是很少去了解的 Git 知識以及 Git 的一些內部工作原理。我們平時用 Git 的很多操作可能只是記住了一個專業術語,或者一個命令而已,並不知道 Git 爲什麼要這樣做,寫下這篇文章也是爲了讓大家對 Git 應用得更加的得心應手。
讀完本篇文章你會了解到 Git 的以下內容:
✦ 爲什麼使用 Git
✦ Git 基本用法概述
✦ checkout,merge,rebase,reset,reverse 的區別
✦ Git flow
✦ Git hooks
✦ Git 子模塊
✦ Git 內部工作原理
爲什麼使用Git
集中化的版本控制
例如我們常用的 svn,這種版本控制最大的缺點就是中央服務器的單點故障,一旦中央服務器宕機,所有的開發人員都無法正常更新代碼工作了,如果中心數據所在的磁盤損壞又沒有備份,版本數據將永久丟失。
分佈式版本控制系統
客戶端不只是提取最新版本的文件快照,而是將整個代碼倉庫完整的鏡像拉下來,svn 以文件變更列表的方式存儲信息,這類系統將它們保存的信息看作是一組基本文件和每個文件隨時間逐步累積的差異。
Git 基本用法概述
Git 提交與同步流程
Git 撤銷更改
Git 常用命令
checkout、reverse、reset、rebase、merge
1. checkout
這裏我們主要討論 git checkout 檢出分支的操作,當我們執行 git checkout <branchname> 操作時,其實原理上是將 head 指針指向了要檢出的這個分支上。
分叉情況:
當我們在多個分支上同時開發時,不同的分支會有不同的提交記錄,這樣就會出現分叉情況。
此時要協同開發我們需要將各種分支合併到主幹分支上,也就是 git 中的 merge 操作。如圖,上游的分支在之後就與我們的分支有了差異,此時我們執行 merge 命令其實就是將 C , D 提交的內容進行一個融合,然後作爲提交。
2. git merge
3. git rebase <branchname>
merge 操作是將 C , D 合併成一個 commit,如果我們不想這麼做,可以使用變基操作。顧名思義,變基就是將自己的根基改變了(個人理解),rebase 是直接將當前開叉的提交強行給挪到了另一個分支的後面。
4. git reset
reset 操作 HEAD 指針從 C 變到了 D ,也就是說之後的提交線從 B 的後面開始,C 就變成遊離的狀態了。
5. git revert
相對 reset 操作,revert 就相對安全些,因爲它並沒有將 C 丟棄調,而是在 C 的後面複製了一份 B 作爲 B' .
Git flow
Git 工作流主要是爲了讓開發者在協同開發時統一規範,統一流程,減少額外的工作量。
簡單的說就是我們在開發流程時幹特定的事要用到特定的分支,想想如果大家在開發的時候,測試,改 bug,發佈都放在同一個分支做,此時項目的管理者要在衆多的分支中挑選出他想要的提交進行合併是多麼頭疼的一件事情。git flow 就是爲了解決此類問題。
Git flow 會有兩個長期分支:
master:用於記錄官方發佈軌跡
develope:集成分支,用於記錄開發新功能的軌跡
此外還有其他的一些臨時的分支:
Feature(新功能分支) :從 develop 分支中派生出來的分支,當需要添加一個新的功能時可用這個分支,新功能完成後,feature 又合併到 develop 中去。
Release(發佈分支) :用於合併 develop 中的 feature 分支。
Mantaince(熱修復分支):線上 bug 修復分支,直接從 maser 分支中派生出來,完成後合併到 master 和 develop 分支上去。
Release(發佈分支) :用於合併 develop 中的 feature 分支。
Mantaince(熱修復分支) :線上 bug 修復分支,直接從 maser 分支中派生出來,
完成後合併到 master 和 develop 分支上去。
上圖的藍色分支是master,紫色是develop,紅色是hotfix,橙色是feature,綠色是release
Git flow 開發步驟
1. 創建 develop 分支
git branch develop
git push -u origin develop
2. checkout develop 分支
git checkout -b develop origin/develop
3. 基於 develop 創建新功能分支
git checkout -b feature/my-feature develop
4. 合併分支
git pull origin develop
git checkout develop
git merge feature/my-feature
git push
git branchn -d feature/my-feature
5. 線上版本發佈
a.從 develop 分支下創建準備發佈的 realse 分支
git checkout -b release-1.0.0 develop
git push
b.將realse分支合併到 develop
c.打標籤。realse 是一個 develop 合併到 master 的一個緩衝的分支,每當有源碼需要合併到 master 時應該打上標籤
git tag -a 1.0.0-release -m "first release" master
git push --tags
6.線上 bug 修復
在代碼都提交到 master 後項目開始上線,此時如提交修復線上 bug 的代碼?
a.創建 hotfix分支 git checkout -b issue-#001 master
b.修復 bug
c.完成修復合併到 master
d.打標籤
e.合併到 develop
Git hooks (鉤子)
Git hooks 是一些可以在倉庫中的任何時刻自動運行的腳本,這些腳本類似於一些攔截器,可以在執行 git 操作的前後執行一些特定的事件。
當初始化倉庫時,會自動在 .git/hooks 目錄下生成 .example 爲後綴的文件,這些文件就是 hooks,git 爲防止這些hooks默認執行,加了 .example 後綴,要執行這些 hooks只需把 .example 後綴去掉即可。
hooks 分爲本地的 hooks 和服務器端的 hooks。
本地 hooks:
pre-commit
prepare-commit-msg
commit-msg
post-commit
這些都是在 commit 的整個過程中的一些 hooks。
post-checkout
pre-rebase
這兩個 hooks 是用於做一些額外操作或者一些安全檢查。
pre 前綴的 hooks 用於添加多餘操作,post 前綴的 hooks 用於放送通知。
案例演示:
安裝一個 prepare-commit-msg hook。
首先,我們先將 .git/hooks 目錄下的 prepare-commit-msg.example 的後綴去掉。
接着我們使用打開這個文件進行編輯,這是一個 shell 腳本,我們也可以用 python 或 ruby這樣的語言寫 hooks。
保存退出,這個 hooks 是在 commit 操作時添加 msg 之後觸發。
我們來進行一次提交操作。
可以看到在 hooks 中的程序已經執行了。
以上的這些 hooks 都是本地的 hooks,還有一些 hooks 是加在遠程倉庫的,這些就是服務器端的 hooks。
pre-receive:執行 git push 操作時觸發
update:每次 push 之後觸發
post receive:push 成功之後觸發
Git 子模塊
在工作當中我們經常會遇到這樣的情況:我們在當前項目裏想引入例外一個項目,也有可能是一個庫,但是假如我們直接把這個項目加入到中,我們必須要確保每個倉庫中含有這個庫,不然部署很困難,我們每次對這個項目或庫進行自定義操作時還會使上游的合併變得異常的困難。如果我們可以將這個庫克隆到自己的項目中,並且保持這個庫的提交獨立那就完美了。git 子模塊就是爲了解決這個問題。
接下來我們就來演示一下如何在一個被分成多個子項目的項目中進行開發。
首先我們要在我們的主項目 pro-main 中添加子模塊 pro-service
pro-main 的倉庫地址爲:[email protected]:CodeNerverEnd/pro-main.git
pro-service 的倉庫地址爲:[email protected]:CodeNerverEnd/pro-service.git
先將我們的主項目克隆下來,
然後在主項目中添加子模塊,
使用命令 git submodule add [email protected]:CodeNerverEnd/pro-service.git
我們可以在倉庫中看到如下目錄
多了一個 .gitmodules 文件,打開這個文件看到,保存了子模塊的路徑,如果你不想將子模塊放到主項目倉庫路徑下,可以將這裏的路徑改成你想要的路徑。
我們把添加了子模塊的主項目提交到遠程倉庫後,所有的開發人員都可以用這個庫了,下面我們就來看看其他開發人員怎麼用它。
首先其他的開發人員會克隆含子模塊的項目,我們可以用以下的命令:
git clone --recursive [email protected]:CodeNerverEnd/pro-main.git
如果我們克隆的時候沒有加 --recursive,默認是不會將子模塊的內容克隆下來,你只能在項目中看到一個子模塊的文件夾,但裏面的內容是空的。
現在假如開發人員需要更新子模塊,執行以下命令:
git submodule update --remote
假如此時開發人員想要在主項目下編碼的同時又在子模塊下編碼,可以執行以下步驟
1.進入子模塊目錄檢出一個分支
git checkout stable
2.merge子模塊
git submodule update --remote --merge
3.假如其他人在上游做了修改,我們需要併入
git submodule --remote --rebase
4.我們在本地改好了以後想發佈子模塊的改動
git push --recurse-submodules=ondemand
對於 Git 來講,這些遠遠是不足以敘述它的全面。我將在下一個篇幅裏,繼續爲你們介紹 Git 的進階。