老曹眼中的Git
為什么使用Git
Git 是 Linus Torvalds 為了幫助管理 Linux 內(nèi)核開發(fā)而開發(fā)的一個(gè)開放源碼的版本控制軟件。大神就是大神,在開發(fā)了Linux之后,Git 是又一抗鼎之作。這是唯一的理由么?
Git在軟件開發(fā)中位置——配置管理SCM
Software configuration management (SCM, or just plain CM) is an organizational framework — that is, a discipline — for managing the evolution of computer systems throughout all stages of systems development.
軟件配置管理:通過(guò)執(zhí)行版本控制、變更控制的規(guī)程,以及使用合適的配置管理軟件,來(lái)保證所有配置資源的完整性和可跟蹤性。配置管理是對(duì)工作成果的一種有效保護(hù)。沒(méi)有軟件配置管理,***的麻煩是工作成果無(wú)法回溯。
配置管理的內(nèi)容和目標(biāo)
配置管理的內(nèi)容:
一類是屬于產(chǎn)品的組成部分,例如需求文檔、設(shè)計(jì)文檔、源代碼、測(cè)試用例等等;
另一類是在管理過(guò)程中產(chǎn)生的文檔,例如各種計(jì)劃、報(bào)告等
軟件配置管理是在貫穿整個(gè)軟件生命周期中建立和維護(hù)項(xiàng)目產(chǎn)品的完整性。它的基本目標(biāo)包括:
1. 軟件配置管理的各項(xiàng)工作是有計(jì)劃進(jìn)行的。
2. 被選擇的項(xiàng)目產(chǎn)品得到識(shí)別,控制并且可以被相關(guān)人員獲取。
3. 已識(shí)別出的項(xiàng)目產(chǎn)品的更改得到控制。
4. 使相關(guān)組別和個(gè)人及時(shí)了解軟件基準(zhǔn)的狀態(tài)和內(nèi)容。
配置管理的主要任務(wù)
軟件配置管理的主要任務(wù)也就歸結(jié)為以下幾條:
(1)制定項(xiàng)目的配置計(jì)劃;
(2)對(duì)配置項(xiàng)進(jìn)行標(biāo)識(shí);
(3)對(duì)配置項(xiàng)進(jìn)行版本控制;
(4)對(duì)配置項(xiàng)進(jìn)行變更控制;
(5)定期進(jìn)行配置審計(jì);
(6)向相關(guān)人員報(bào)告配置的狀態(tài)。
版本控制
版本控制是軟件配置管理的核心功能。所有位于配置資源庫(kù)中的元素都應(yīng)自動(dòng)予以版本的標(biāo)識(shí),并保證版本命名的唯一性。版本在生成過(guò)程中,自動(dòng)依照設(shè)定的使用模型自動(dòng)分支、演進(jìn)。
版本控制(Revision control)確保由不同人所編輯的同一檔案都得到更新。
版本控制中的基本概念
1)簽入,提交,檢出
2)沖突,解決,合并
3)分支,版本
4)鎖定,hook
常見的版本控制工具
作為一個(gè)老碼農(nóng),枚舉一下曾經(jīng)使用過(guò)的版本控制工具。
1. VSS: visual source safe, 微軟的東東,97年寫VC程序時(shí)使用,人多的時(shí)候性能較差,不知道現(xiàn)在的升級(jí)版怎樣了
2. clearcase: 99年開發(fā)Unix 上分布式式應(yīng)用時(shí)使用,功能強(qiáng)大,不只限于版本控制,有錢的大團(tuán)隊(duì)才去用
3. CVS: 02年在互聯(lián)網(wǎng)熱潮的時(shí)候使用,開源產(chǎn)品,當(dāng)時(shí)“Copy-Modify-Merge”開發(fā)模型眼前一亮。
4. SVN:曾經(jīng)的摯愛(ài),在曾工作的合資公司使用,權(quán)限管理和分支合并等方面做的很出色,并在多個(gè)公司推廣使用。還記得TortoiseSVN么?那只可愛(ài)的小烏龜。
5. perforce:是一款具有輕便快速的SCM工具、真正的客戶端/服務(wù)器系統(tǒng)等特點(diǎn)的商業(yè)軟件。高通內(nèi)部使用的版本管理工具。確實(shí)不錯(cuò)。
6. git:現(xiàn)在的***……
比較一下cvs,svn,和git:
Git 簡(jiǎn)要
GIT 是一款免費(fèi)的、開源的、分布式的版本控制系統(tǒng)。每一個(gè)GIT克隆都是一個(gè)完整的文件庫(kù),含有全部歷史記錄和修訂追蹤能力。其***特色就是“分支”及“合并”操作快速、簡(jiǎn)便。支持離線工作,GIT是整個(gè)項(xiàng)目范圍的原子提交,而且GIT中的每個(gè)工作樹都包含一個(gè)具有完整項(xiàng)目歷史的倉(cāng)庫(kù)。
核心特點(diǎn):
- Git 底層自行維護(hù)的存儲(chǔ)文件系統(tǒng):存儲(chǔ)的是文件快照,即整個(gè)文件內(nèi)容,并保存指向快照的索引
- 去中心化的分布式控制
優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
- 適合分布式開發(fā),強(qiáng)調(diào)個(gè)體。
- 公共服務(wù)器壓力和數(shù)據(jù)量都不會(huì)太大, 速度快、靈活。
- 任意兩個(gè)開發(fā)者之間可以很容易的解決沖突。
- 離線工作。
缺點(diǎn):
- 學(xué)習(xí)周期相對(duì)而言比較長(zhǎng)。
- 不符合常規(guī)思維。
- 代碼保密性差,一旦開發(fā)者把整個(gè)庫(kù)克隆下來(lái)就可以完全公開所有代碼和版本信息。
Git 原理
Git的目錄結(jié)構(gòu)
不論通過(guò)git init 還是克隆下來(lái)的git 倉(cāng)庫(kù),都有如下的目錄結(jié)構(gòu):
主要目錄結(jié)構(gòu)描述見下表:
所有的分支指針保存在 .git/refs/heads 目錄下,HEAD 在 .git/HEAD 目錄下,標(biāo)簽在 .git/refs/tags 目錄下。
快照
例如: 一個(gè)工程中有兩個(gè)文件A和B, 有3個(gè)版本:
V1.0 A和B,V1.***1和B,V2.0 A1和B1
在Git 的實(shí)際存儲(chǔ)中實(shí)際存了3個(gè)快照 4個(gè)文件。
Git對(duì)文件進(jìn)行 SHA-1 計(jì)算作為文件的唯一ID,并校驗(yàn)了文件完整性。
SHA-1 算法將文件中的內(nèi)容通過(guò)計(jì)算生成一個(gè) 40 位的 Hash 值。SHA-1 算法的特點(diǎn):
由文件內(nèi)容計(jì)算出的 Hash 值;Hash 值相同,文件內(nèi)容相同。
使用 SHA-1 的前兩位創(chuàng)建了文件夾,剩下的 38 位為文件名。
這些 Obj 文件,其實(shí)分為四種類型,分別是 Blob、Tree、Commit、Tag。
Blob
用來(lái)存放項(xiàng)目文件的內(nèi)容,但是不包括文件的路徑、名字、格式等其它描述信息。項(xiàng)目的任意文件的任意版本都是以 Blob 的形式存放的。
Tree
Tree 用來(lái)表示目錄。我們知道項(xiàng)目就是一個(gè)目錄,目錄中有文件、有子目錄。因此 Tree 中有 Blob、子 Tree,且都是使用 SHA-1值引用的。這是與目錄對(duì)應(yīng)的。從頂層的 Tree 縱覽整個(gè)樹狀的結(jié)構(gòu),葉子結(jié)點(diǎn)就是 Blob,表示文件的內(nèi)容,非葉子結(jié)點(diǎn)表示項(xiàng)目的目錄,頂層的 Tree 對(duì)象就代表了當(dāng)前項(xiàng)目的快照。
Commit
表示一次提交,有 Parent 字段,用來(lái)引用父提交。指向了一個(gè)頂層 Tree,表示了項(xiàng)目的快照,還有一些其它的信息,比如上一個(gè)提交 Committer、Author、Message 等信息。
存儲(chǔ)區(qū)
Git中有4個(gè)類型的存儲(chǔ)區(qū):遠(yuǎn)程倉(cāng)庫(kù),工作區(qū),本地倉(cāng)庫(kù)和緩存區(qū)。
暫存區(qū)的好處:
為了能夠?qū)崿F(xiàn)部分提交
- 為了不在工作區(qū)創(chuàng)建狀態(tài)文件、會(huì)污染工作區(qū)。
- 暫存區(qū)記錄文件的修改時(shí)間等信息,提高文件比較的效率。
- 暫存區(qū)是用來(lái)構(gòu)建項(xiàng)目快照的區(qū)域。暫存區(qū)是一個(gè)文件,路徑為: .Git/index
它是一個(gè)二進(jìn)制文件,第四列是文件名,第三列是文件的沖突狀態(tài),第二列指的是文件的 Blob。
Commit 命令,將暫存區(qū)的內(nèi)容***保存到本地倉(cāng)庫(kù)。提交時(shí) Git 會(huì)使用暫存區(qū)的這些信息生成 Tree 對(duì)象,也就是項(xiàng)目快照,***保存到數(shù)據(jù)庫(kù)中。
文件的狀態(tài)可以分為兩類。一類是暫存區(qū)與本地倉(cāng)庫(kù)比較得出的狀態(tài),另一類是工作區(qū)與暫存區(qū)比較得出的狀態(tài)。為什么要分成兩類的愿意也很簡(jiǎn)單,因?yàn)?**類狀態(tài)在提交時(shí),會(huì)直接寫入本地倉(cāng)庫(kù)。而第二種則不會(huì)。一個(gè)文件可以同時(shí)擁有兩種狀態(tài)。
分支
分支的目的是讓我們可以并行的進(jìn)行開發(fā)。 .Git/HEAD 文件,它保存了當(dāng)前的分支。
分支指向了一次提交,也是 Git 中的分支為什么這么輕量的原因。
因?yàn)榉种Ь褪侵赶蛄艘粋€(gè) Commit 的指針,當(dāng)提交新的 Commit,這個(gè)分支的指向只需要跟著更新就可以了,而創(chuàng)建分支僅僅是創(chuàng)建一個(gè)指針。
Git 必備技能
常見命令速查
git add 和 git commit
Add 操作是將修改保存到暫存區(qū),Commit 是將暫存區(qū)的內(nèi)容***保存到本地倉(cāng)庫(kù)。
每當(dāng)將修改的文件加入到暫存區(qū),Git 都會(huì)根據(jù)文件的內(nèi)容計(jì)算出 SHA-1,并將內(nèi)容轉(zhuǎn)換成 Blob,寫入數(shù)據(jù)庫(kù)。然后使用 SHA-1 值更新該列表中的文件項(xiàng)。
在暫存區(qū)的文件列表中,每一個(gè)文件名,都會(huì)對(duì)應(yīng)一個(gè) SHA-1 值,用于指向文件的實(shí)際內(nèi)容。***提交的那一刻,Git 會(huì)將這個(gè)列表信息轉(zhuǎn)換為項(xiàng)目的快照,也就是 Tree 對(duì)象。寫入數(shù)據(jù)庫(kù),并再構(gòu)建一個(gè) Commit 對(duì)象,寫入數(shù)據(jù)庫(kù)。然后更新分支指向。
分支合并: merge 和rebase
沖突的狀態(tài)
- DELETED_BY_THEM;
- DELETED_BY_US;
- BOTH_ADDED;
- BOTH_MODIFIED
遇到不可自動(dòng)合并沖突時(shí),Git 會(huì)將這些狀態(tài)寫入到暫存區(qū)。
merge
在解決完沖突后,我們可以將修改的內(nèi)容提交為一個(gè)新的提交。這就是 Merge。
Merge 之后仍可以做出新的提交。
rebase
Rebase 會(huì)把從 Merge Base 以來(lái)的所有提交,以補(bǔ)丁的形式一個(gè)一個(gè)重新達(dá)到目標(biāo)分支上。這使得目標(biāo)分支合并該分支的時(shí)候會(huì)直接 Fast Forward,即不會(huì)產(chǎn)生任何沖突。
Rebase 主要在 .Git/Rebase-Merge 下生成了兩個(gè)文件,分別為 Git-Rebase-todo 和 Done 文件,Git-Rebase-todo 存放了 Rebase 將要操作的 Commit。而 Done 存放正在操作或已經(jīng)操作完畢的 Commit。
Rebase 的一個(gè)缺點(diǎn),那就是修改了分支的歷史提交。如果已經(jīng)將分支推送到了遠(yuǎn)程倉(cāng)庫(kù),會(huì)導(dǎo)致無(wú)法將修改后的分支推送上去,必須使用 -f 參數(shù)(Force)強(qiáng)行推送。
所以使用 Rebase ***不要在公共分支上進(jìn)行操作。
checkout
經(jīng)常用來(lái)切換分支、或者切換到某一次提交。
Checkout 找到目標(biāo)提交(Commit),目標(biāo)提交中的快照也就是 Tree 對(duì)象就是我們要檢出的項(xiàng)目版本。
Checkout 首先根據(jù)Tree生成暫存區(qū)的內(nèi)容,再根據(jù) Tree 與其包含的 Blob 轉(zhuǎn)換成我們的項(xiàng)目文件。然后修改 HEAD 的指向,表示切換分支。
Checkout 并沒(méi)有修改提交的歷史記錄。只是將對(duì)應(yīng)版本的項(xiàng)目?jī)?nèi)容提取出來(lái)。
revert
revert 實(shí)現(xiàn)了反向提交,就是舊版本添加了的內(nèi)容,要在新版本中刪除;舊版本中刪除了的內(nèi)容,要在新版本中添加。這在分支已經(jīng)推送到遠(yuǎn)程倉(cāng)庫(kù)的情境下非常有用。
Revert 也不會(huì)修改歷史提交記錄,實(shí)際的操作相當(dāng)于是檢出目標(biāo)提交的項(xiàng)目快照到工作區(qū)與暫存區(qū),然后用一個(gè)新的提交完成版本的“回退”。
reset
在當(dāng)前分支進(jìn)行版本的“回退”,Reset 是會(huì)修改歷史提交記錄的。
Reset 常用的選項(xiàng)有三個(gè),分別是 —Soft, —Mixed, —Hard。他們的作用域依次增大。
Soft 會(huì)僅僅修改分支指向。而不修改工作區(qū)與暫存區(qū)的內(nèi)容,
Mixed 比 Soft 的作用域多了一個(gè) 暫存區(qū)。實(shí)際上 Mixed 選項(xiàng)與 Soft 只差了一個(gè) Add 操作。
Hard 會(huì)比 Mixed作用域又多了一個(gè)工作區(qū)。
注意:在丟失后可以使用 Git Reset –Hard ORIG_HEAD 立即恢復(fù),或者使用 reflog 命令查看之前分支的引用
stash
有時(shí),在一個(gè)分支上做了一些工作,修改了很多代碼,而這時(shí)需要切換到另一個(gè)分支干點(diǎn)別的事。但又不想將只做了一半的工作提交。
Stash 將工作區(qū)與暫存區(qū)中的內(nèi)容做一個(gè)提交,保存起來(lái),然后使用Reset Hard 選項(xiàng)恢復(fù)工作區(qū)與暫存區(qū)內(nèi)容。我們可以隨時(shí)使用 Stash Apply 將修改應(yīng)用回來(lái)。
Stash 實(shí)現(xiàn)思路將我們的修改提交到本地倉(cāng)庫(kù),使用特殊的分支指針(.Git/refs/Stash)引用該提交,然后在恢復(fù)的時(shí)候,將該提交恢復(fù)即可。
Git 典型實(shí)踐
一個(gè)典型的git 并行開發(fā)的流程模型如下:
主要分支
把origin/master作為主要分支,源碼的HEAD總是表示production-ready(可隨時(shí)部署)狀態(tài)。
origin/develop上的代碼是為下一次的代碼發(fā)布準(zhǔn)備的。每日構(gòu)建也是基于此分支。
當(dāng)develop分支達(dá)到了一個(gè)穩(wěn)定狀態(tài)并準(zhǔn)備發(fā)布時(shí),所有的改變都要合并到master分支,并標(biāo)上版本號(hào)。
輔助分支
Feature branches
繼承與合并都與develop 分支相關(guān),用來(lái)開發(fā)新特性的(短期,遠(yuǎn)期都可以)。
當(dāng)要?jiǎng)?chuàng)建一個(gè)新特性時(shí),從develop分支上再創(chuàng)建一個(gè) Feature branch。
- $ git checkout -b myfeature develop
合并feature 到develop
- $ git checkout develop
- Switched to branch 'develop'
- $ git merge --no-ff myfeature
- Updating ea1b82a..05e9557 (Summary of changes)
- $ git branch -d myfeature
- Deleted branch myfeature (was 05e9557).
- $ git push origin develop
Release branches
繼承分支: develop
合并分支:develop 和 master
命名規(guī)范:release-*
創(chuàng)建一個(gè)release 分支
Release branch是通過(guò)develop分支而創(chuàng)建.
- $ git checkout -b release-1.2 develop Switched to a new branch "release-1.2"
- $ ./bump-version.sh 1.2
- Files modified successfully, version bumped to 1.2. $ git commit -a -m "Bumped version number to 1.2"
- [release-1.2 74d9424] Bumped version number to 1.21 files changed, 1 insertions(+), 1 deletions(-)
當(dāng)release branch已經(jīng)準(zhǔn)備就緒,需要做幾件事。
release分支被合并到master分支上(每一個(gè)提交到master上的commit都是一個(gè)新版 本,切記)。
master上的commit都要添加tag,方便將來(lái)查看和回滾。
release上所做的修改必須合并到develop分支上,保證bug已被修補(bǔ)。
前兩個(gè)步驟:
- $ git checkout master Switched to branch 'master'
- $ git merge --no-ff release-1.2
- Merge made by recursive. (Summary of changes) $ git tag -a 1.2
為了把release上的改變保存到develop,需要合并到develop。
- $ git checkout develop Switched to branch 'develop'
- $ git merge --no-ff release-1.2
- Merge made by recursive. (Summary of changes)
這個(gè)步驟可能會(huì)導(dǎo)致沖突,如果這樣的話,解決沖突,然后再提交。
***,可以刪除release 分支。
- $ git branch -d release-1.2
- Deleted branch release-1.2 (was ff452fe).
繼承分支: master
合并分支:develop 和 master
命名規(guī)范:hotfix-*
運(yùn)行過(guò)程中發(fā)現(xiàn)了bug,就必須快速解決,這時(shí)就可以創(chuàng)建一個(gè)Hotfix branch,解決完后合并到master分支上。好處是開發(fā)人員可以繼續(xù)工作,有專人來(lái)負(fù)責(zé)搞定這個(gè)bug。
創(chuàng)建hotfix分支
- $ git checkout -b hotfix-1.2.1 master Switched to a new branch "hotfix-1.2.1"
- $ ./bump-version.sh 1.2.1
- Files modified successfully, version bumped to 1.2.1. $ git commit -a -m "Bumped version number to 1.2.1"
- [hotfix-1.2.1 41e61bb] Bumped version number to 1.2.11 files changed, 1 insertions(+), 1 deletions(-)
fix bug, 解決問(wèn)題
需要一次或幾次commit
- $ git commit -m "Fixed severe production problem"[hotfix-1.2.1 abbe5d6] Fixed severe production problem 5 files changed, 32 insertions(+), 17 deletions(-)
完成Hotfix branch
當(dāng)結(jié)束時(shí),bugfix要被合并到master,同時(shí)也要合并到develop,保證下個(gè)版本發(fā)布時(shí)該bug已被修復(fù)。這跟release branch完成時(shí)一樣。
首先更新master和tag release
- $ git checkout master Switched to branch 'master'
- $ git merge --no-ff hotfix-1.2.1
- Merge made by recursive. (Summary of changes) $ git tag -a 1.2.1
接下來(lái)與develop合并:
- $ git checkout develop Switched to branch 'develop'
- $ git merge --no-ff hotfix-1.2.1
- Merge made by recursive. (Summary of changes)
有一個(gè)例外,就是當(dāng)一個(gè)release branch存在時(shí),bugfix要被合并到release而不是develop,因?yàn)閞elease最終會(huì)被合并到develop。
***移除branch
- $ git branch -d hotfix-1.2.1
- Deleted branch hotfix-1.2.1 (was abbe5d6).
總結(jié)
了解Git 在軟件工程及敏捷開發(fā)中的地位,明白git與其他版本控制工具之間的區(qū)別,掌握Git 工作的基本原理和必備操作,復(fù)雜問(wèn)題可以查找git的相關(guān)命令,應(yīng)用git開發(fā)的流程模型,讓Git 成為我們的真正利器。
【本文來(lái)自51CTO專欄作者老曹的原創(chuàng)文章,作者微信公眾號(hào):喔家ArchiSelf,id:wrieless-com】