Git 倉庫
1.1Git 基本概念
在Git中,我們將需要進行版本控制的文件目錄叫做一個倉庫(repository),每個倉庫可以簡單理解成一個目錄,這個目錄里面的所有文件都通過Git來實現版本管理,Git都能跟蹤並記錄在該目錄中發生的所有更新。
現在我們已經知道什么是repository(縮寫repo)了,假如我們現在建立一個倉庫(repo),那么在建立倉庫的這個目錄中有一個“.git”的文件夾。這個文件夾非常重要,所有的版本信息,更新記錄,以及Git進行倉庫管理的相關信息
全部保存在這個文件夾里面。所以,不要修改/刪除其中的文件,以免造成數據的丟失。
進一步的講解請參考下面一張圖,大概展示出了我們需要了解的基本知識。
根據上面的圖片,下面給出了每個部分的簡要說明:
- Directory:使用Git管理的一個目錄,也就是一個倉庫,包含我們的工作空間和Git的管理空間。
- WorkSpace:需要通過Git進行版本控制的目錄和文件,這些目錄和文件組成了工作空間,除了.git之外的都屬於工作區。
- .git:存放Git管理信息的目錄,初始化倉庫的時候自動創建。
- Index/Stage:暫存區,或者叫待提交更新區,在提交進入repo之前,我們可以把所有的更新放在暫存區。
- Local Repo:本地倉庫,一個存放在本地的版本庫;HEAD會只是當前的開發分支(branch)。
- Stash:是一個工作狀態保存棧,用於保存/恢復WorkSpace中的臨時狀態。
有了上面概念的了解,下面簡單介紹倉庫的文件結構。
該目錄下有可能還有其他文件,但這是一個全新的 git init 生成的庫,所以默認情況下這些就是你能看到的結構。新版本的 Git 不再使用 branches 目錄,description 文件僅供 GitWeb 程序使用,所以不用關心這些內容。config 文件包含了項目特有的配置選項,info 目錄保存了一份不希望在 .gitignore 文件中管理的忽略模式 (ignored patterns) 的全局可執行文件。hooks 目錄保存了客戶端或服務端鈎子腳本。
另外還有四個重要的文件或目錄:HEAD 及 index 文件,objects 及 refs 目錄。這些是 Git 的核心部分。
- objects 目錄存儲所有數據內容
- refs 目錄存儲指向數據 (分支) 的提交對象的指針,里面即有stash棧指針以及tag等
- HEAD 文件指向當前分支
- index 文件保存了暫存區域信息
1.2 簡單的代碼提交流程
這里不對每一條命令做詳盡的解釋,這些命令或類似命令貫穿我們順利的一個完整提交,關於其他情況以及這些命令的詳細解釋留待后面敘述。
(1)git status //查看工作區代碼相對於暫存區的差別,
(2)git add . // 將當前目錄下修改的所有代碼從工作區添加到暫存區 . 代表當前目錄
(3)git commit -m “commit-message” //將暫存區的代碼提交到本地版本庫
(4)git push origin master // 將本地版本庫推送到遠程服務器,origin是遠程主機,master表示是遠程服務器上的master分支,分支名是可以修改的。
1.3 GIT的基本操作:
版本管理的挑戰
雖然有這么優秀的版本管理工具,但是我們面對版本管理的時候,依然有非常大得挑戰,我們都知道大家工作在同一個倉庫上,那么彼此的代碼協作必然帶來很多問題和挑戰,如下:
- 如何開始一個Feature的開發,而不影響別的Feature?
- 由於很容易創建新分支,分支多了如何管理,時間久了,如何知道每個分支是干什么的?
- 哪些分支已經合並回了主干?
- 如何進行Release的管理?開始一個Release的時候如何凍結Feature, 如何在Prepare Release的時候,開發人員可以繼續開發新的功能?
- 線上代碼出Bug了,如何快速修復?而且修復的代碼要包含到開發人員的分支以及下一個Release?
大部分開發人員現在使用Git就只是用三個甚至兩個分支,一個是Master, 一個是Develop, 還有一個是基於Develop打得各種分支。這個在小項目規模的時候還勉強可以支撐,因為很多人做項目就只有一個Release, 但是人員一多,而且項目周期一長就會出現各種問題。
Git Flow
就像代碼需要代碼規范一樣,代碼管理同樣需要一個清晰的流程和規范
Vincent Driessen 同學為了解決這個問題提出了 A Successful Git Branching Model
下面是Git Flow的流程圖
上面的圖你理解不了? 沒關系,這不是你的錯,我覺得這張圖本身有點問題,這張圖應該左轉90度,大家應該就很用以理解了。
Git Flow常用的分支
- Production 分支
也就是我們經常使用的Master分支,這個分支最近發布到生產環境的代碼,最近發布的Release, 這個分支只能從其他分支合並,不能在這個分支直接修改
- Develop 分支
這個分支是我們是我們的主開發分支,包含所有要發布到下一個Release的代碼,這個主要合並與其他分支,比如Feature分支
- Feature 分支
這個分支主要是用來開發一個新的功能,一旦開發完成,我們合並回Develop分支進入下一個Release
- Release分支
當你需要一個發布一個新Release的時候,我們基於Develop分支創建一個Release分支,完成Release后,我們合並到Master和Develop分支
- Hotfix分支
當我們在Production發現新的Bug時候,我們需要創建一個Hotfix, 完成Hotfix后,我們合並回Master和Develop分支,所以Hotfix的改動會進入下一個Release
Git Flow如何工作
初始分支
所有在Master分支上的Commit應該Tag
Feature 分支
分支名 feature/*
Feature分支做完后,必須合並回Develop分支, 合並完分支后一般會刪點這個Feature分支,但是我們也可以保留
Release分支
分支名 release/*
Release分支基於Develop分支創建,打完Release分之后,我們可以在這個Release分支上測試,修改Bug等。同時,其它開發人員可以基於開發新的Feature (記住:一旦打了Release分支之后不要從Develop分支上合並新的改動到Release分支)
發布Release分支時,合並Release到Master和Develop, 同時在Master分支上打個Tag記住Release版本號,然后可以刪除Release分支了。
維護分支 Hotfix
分支名 hotfix/*
hotfix分支基於Master分支創建,開發完后需要合並回Master和Develop分支,同時在Master上打一個tag
Git Flow代碼示例
a. 創建develop分支
git branch develop
git push -u origin develop
b. 開始新Feature開發
git checkout -b some-feature develop
# Optionally, push branch to origin: git push -u origin some-feature # 做一些改動 git status git add some-file git commit
c. 完成Feature
git pull origin develop
git checkout develop
git merge --no-ff some-feature git push origin develop git branch -d some-feature # If you pushed branch to origin: git push origin --delete some-feature
d. 開始Relase
git checkout -b release-0.1.0 develop # Optional: Bump version number, commit # Prepare release, commit
e. 完成Release
git checkout master
git merge --no-ff release-0.1.0 git push git checkout develop git merge --no-ff release-0.1.0 git push git branch -d release-0.1.0 # If you pushed branch to origin: git push origin --delete release-0.1.0 git tag -a v0.1.0 master git push --tags
f. 開始Hotfix
git checkout -b hotfix-0.1.1 master
g. 完成Hotfix
git checkout master git merge --no-ff hotfix-0.1.1 git push git checkout develop git merge --no-ff hotfix-0.1.1 git push git branch -d hotfix-0.1.1 git tag -a v0.1.1 master git push --tags
Git flow工具
實際上,當你理解了上面的流程后,你完全不用使用工具,但是實際上我們大部分人很多命令就是記不住呀,流程就是記不住呀,腫么辦呢?
總有聰明的人創造好的工具給大家用, 那就是Git flow script.
安裝
- OS X
brew install git-flow
- Linux
apt-get install git-flow
- Windows
wget -q -O - --no-check-certificate https://github.com/nvie/gitflow/raw/develop/contrib/gitflow-installer.sh | bash
使用
-
初始化: git flow init
-
開始新Feature: git flow feature start MYFEATURE
-
Publish一個Feature(也就是push到遠程): git flow feature publish MYFEATURE
-
獲取Publish的Feature: git flow feature pull origin MYFEATURE
-
完成一個Feature: git flow feature finish MYFEATURE
-
開始一個Release: git flow release start RELEASE [BASE]
- Publish一個Release: git flow release publish RELEASE
-
發布Release: git flow release finish RELEASE
別忘了git push --tags -
開始一個Hotfix: git flow hotfix start VERSION [BASENAME]
-
發布一個Hotfix: git flow hotfix finish VERSION
Git Flow GUI
上面講了這么多,我知道還有人記不住,那么又有人做出了GUI 工具,你只需要點擊下一步就行,工具幫你干這些事!!!
SourceTree
當你用Git-flow初始化后,基本上你只需要點擊git flow菜單選擇start feature, release或者hotfix, 做完后再次選擇git flow菜單,點擊Done Action. 我勒個去,我實在想不到還有比這更簡單的了。
目前SourceTree支持Mac, Windows, Linux.
這么好的工具請問多少錢呢? 免費!!!!
Git flow for visual studio
廣大VS的福音
GitFlow for Visual Studio
GIT基礎知識:
1.文件系統:我們可以把硬盤理解成一本漢語詞典,詞典前面的目錄索引就是文件系 統,能幫助我們快速找到文件內容的具體位置,通常我們也只會通過索引去找文件,windows中的我的電腦就是一個索引系統,索引里面沒有的文件我們就 “找不到”。我們知道從操作系統刪除文件,其實只是刪除了索引,具體文件內容還是存在硬盤上的,雖然我們通過索引找不到,但是我們可以通過內容去查找(利 用一些恢復工具)。
2.git倉庫:就是用來存放備份文件的地方,但是備份文件存入倉庫的時候會壓縮, 這些壓縮的備份文件存放在.git/objects目錄中,直接打開是亂碼,而且為了節省空間,倉庫不會存放重復的文件,只有新增和修改過的文件才會存入 git倉庫,刪除的時候並不會從倉庫移除文件,不然我們怎么恢復呢。
3.HEAD:git的版本日志或版本號,通過git log 我們可以看到很多的編號,我們通過修改head指針來切換版本,每個版本關聯一份快照,每個關照關聯一系列文件,就是“HEAD->快照->倉庫文件”這樣一個關系鏈,我們可以很輕松的通過移動HEAD指針來改變我們的工作區文件。
4.暫存區/緩沖區:暫存區並不存放文件內容,暫存區僅僅是一份處於編輯狀態的快照(索引文件),這份快照沒有編號。commit就是把暫存區保存到版本庫,並給版本日志新增一個編號(HEAD/版本號)指向這個快照副本。
5.git快照:我們知道git是通過快照來管理版本的,快照就是git的文件系統,就是我們說的漢語詞典的索引,每次commit就是創建一份快照,並給快照起一個編號,這個編號就是HEAD。
6.工作區:工作區就是除開.git目錄的其他東西。通過操作系統的文件索引來管理的內容。就是我們正常使用電腦的時候所看到,能編輯的內容。
7.分支:分支其實就是上面說到的版本日志,一個分支就是一個版本分組,每個分支記錄該分支上的所有HEAD,“分支->HEAD->快照->倉庫文件”
同學們可以通過下面的圖片來理解以上幾個概念,下圖中的每個方塊都是存放在硬盤上的文件,git就是建立了這樣一個關系庫來管理版本的(途中的緩沖區就是暫存區)。
大家不要被上圖的復雜線條縮困擾,你只需要弄清HEAD就行了,我們移動HEAD指針其實就是通過HEAD編號找到快照,再通過快照找到這個HEAD的所有文件。
git命令的理解:
1、status
1.1、對比暫存區跟工作區,對比結果主要存在3種情況:
1.1.1、【刪】暫存區記錄的文件在工作區沒有,add的時候會從暫存區移除對應的文件索引,但並不影響git倉庫的內容。
1.1.2、【增】工作區已有的文件在暫存區沒有記錄的,add的時候會把對應的文件拷貝到倉庫中,並在暫存區建立一條索引指向倉庫中對應的文件。
1.1.3、【改】對工作區的文件內容進行算法得出校驗值與暫存區記錄的校驗值不同,add的時候會把對應的文件拷貝到倉庫中,並更新暫存區該條索引的信息。
1.2、對比暫存區與當前HEAD所指向的快照,對比結果也是增、刪、改3種情況
2、add
add會執行2個任務,第一是把【增】【改】的文件拷貝到倉庫,第二個是維護暫存區索引,保證暫存區索引跟操作系統的文件索引內容一致,快照索引指向的是倉庫中的文件,操作系統索引指向的是工作區的文件。
3、commit
commit做的事情就簡單些了,先對比暫存區與工作區,當暫存區與工作區內容相同的時候,直接保存暫存區為一份新的快照、並給這個快照生成1個編號,並把當前分支HEAD改成這個編號。
4、reset
reset分2情況:
4.1、reset文件:reset b86563 b.txt ,將b86563這份快照中b.txt索引復制到暫存區的b.txt的索引。僅僅是對暫存區的索引進行修改,不影響文件內容,僅僅是修改了文件的關聯。
4.2、reset HEAD:reset b86563
4.2.1、參數–soft:僅僅修改HEAD/版本號。
4.2.2、參數–mixed:默認參數,修改當前HEAD/版本號,然后用指定的快照覆蓋暫存區,工作區不變。
4.2.3、參數–hard:修改當前分支HEAD,用參數HEAD關聯 的快照覆蓋暫存區,並把工作區恢復到快照創建時的工作區狀態,實際就是對比這份“歷史快照”與工作區,快照中沒有的文件,從工作區刪除,校驗碼不同以及工 作區沒有的文件,通過快照找到關聯的文件(倉庫中的),並復制到工作區。
5、checkout
reset分2情況:
4.1、checkout HEAD:用HEAD關聯的快照覆蓋暫存區,並把工作區恢復到快照創建時的工作區狀態,checkout 快照與reset –hard的區別就是:checkout是可恢復,reset是不可恢復(后期會刪除倉庫中的文件,checkout不會)
4.2、checkout分支:checkout dev ,切換到dev分支,並修改當前版本號為dev上最后一個版本號。如果dev分支不存在,創建一個名為dev的分支,版本號不變。
6、revert
revert就是創建一個新快照,並把分支HEAD修改為新創建快照的編號,用該快照覆蓋暫存區,並把工作區恢復到快照創建時的工作區狀態。checkout和reset會“丟棄”一些版本日志,cover不會。
總結:
1.暫存區、快照=git的文件系統=索引;倉庫、工作區=我們真正需要的文件內容。
2.安全性:revert>checkout>reset,revert不會影響過去,checkout會丟棄掉一些版本號,reset會丟棄版本號和倉庫中的某些備份文件。
7、遠程倉庫
工作區的文件是可以編輯的,git倉庫的文件是不能編輯的,git上傳到遠程倉庫或從遠程倉庫下載的時候,並不是下載或上傳全部文件。
7.1、上傳的時候,遠程倉庫的最新快照編號肯定是包含在本地的快照日志中的, 如果不存在,則證明遠程倉庫在上次下載后有改動,這時候要求先pull。反之,git會把本地新增的文件上傳到遠程倉庫,並把新增的快照上傳到遠程快照。 通過圖1,我們可以看出git是怎么通過HEAD輕松的找到新增的快照和文件的。
7.2、下載的時候與上傳同理…