# 6.1.2 Git基本原理
### 6.1.2 Git基本原理
與CVS或者SVN等一般軟件不同,要想掌握Git,必須對其實現原理有深入的理解;否則只能停留在表面的簡單操作上,一旦遇到復雜問題就無從下手。
了解Git的基本原理需要從Git倉庫的數據結構入手。Git把一個項目所有的版本和歷史數據保存在一個Git倉庫(Repository)里。倉庫通常位于項目根目錄下的.git子目錄,主要包含兩種數據:對象存儲(object store)和索引(index)。
#### 對象存儲(object store)
對象存儲(object store)中的對象共有4種類型:
- Blob - 一個blob對象對應著項目的一個文件的內容(不包括文件名、創建/修改時間等信息)。項目的每個文件的每個不同版本都對應著一個blob對象。
- Tree - 一個tree對象對應著一層目錄結構,每個目錄項包括文件名、文件對應的blob對象或者另一個tree對象的引用(可以看出,利用blob和tree對象可以實現一個完整的多層次目錄結構)。
- Commit - 一個commit對象保存著用戶的一次commit數據,包括:作者信息、提交日期和日志,以及一個指向tree對象的引用(注意commit對象并不包括版本之間的diff差異,后詳)。
- Tag - 一個tag對象含有對另一個對象(通常是commit對象)的引用,以及一些附加的注釋信息,主要供人類閱讀。
舉例來說,假設一個項目的目錄結構如下:
```
.
├── hello.txt
└── sub-dir
└── world.txt
```
同時假設這個項目只進行了一次commit(包含以上兩個文件和一個子目錄)并且該commit上有一個tag,那么項目對應的Git對象存貯應如下圖所示:

需要注意的是:我們在前面提到的“對像引用”,如Tree對象代表的目錄中每個目錄項含有的對blob或者另一個tree對象的引用,commit對象含有的對tree對象的引用,以及tag對象對一個commit對象的引用,不是依賴文件系統的路徑來表示的,而是由對象內容得出的SHA1哈希代碼,類似`670a245535fe6316eb2316c1103b1a88bb519334`。因此上圖可修正如下:

在項目的根目錄下執行
```
find .git/objects/
```
可以看到項目的Git倉庫中的所有的對象,類似如下:
```
.git/objects/
.git/objects//0d
.git/objects//0d/8ad63a4626b776800bbed61c2dac660c5037e2
.git/objects//1d
.git/objects//1d/5230840c2573394e0ae86f30e01e03062804fc
.git/objects//38
.git/objects//38/8df287b748962a5a1e28a36172fce3240b53e8
.git/objects//4a
.git/objects//4a/3ec58d02c75da1d303d55a08cd172508256525
.git/objects//5a
.git/objects//5a/e514f1ff6a54b6de97db5418038b4b183d8764
.git/objects//67
.git/objects//67/0a245535fe6316eb2316c1103b1a88bb519334
.git/objects//info
.git/objects//pack
```
它們都是以對象內容的SHA1碼來命名的。你還可以通過以下命令查看其內容:
```
git cat-file -p 0d8ad63
```
以上`0d8ad63`是`0d8ad63a4626b776800bbed61c2dac660c5037e2`的短前綴——在不引起沖突的情況下可以使用短前綴代替完整的SHA1碼。
Git通過對象內容的SHA1哈希碼來存貯、引用對象的方式稱作**通過內容尋址的存貯(Content-Addressable Storage)**。
現在我們進一步:如果修改了`hellt.txt`的內容并提交一個新的commit,對象存貯如何變化?它變成了這樣:

仔細對比一下前后兩張圖,*Git的奧妙就在這里*:
- `hello.txt`的內容改變后,Git新增了一個blob對象,對應著`hello.txt`的新內容,并且有一個新的SHA1代碼。
- 由于`hello.txt`的SHA1代碼改變了,因此包含這個blob對象的tree對象也發生了變化(因為要更新對`hello.txt`的引用)。同樣地,Git也新增了一個tree對象對應著新內容。同時,`sub-dir`的內容沒有變化,因此新的tree對象仍然引用著原來的`sub-dir`對象(而不是拷貝一份)。
- 新的commit對象包含這個新的tree對象的引用,同時指向之前的commit對象——這樣就形成了歷史版本紀錄,并且你隨時可以恢復任何一個歷史版本!
#### 索引(index)
根對象存貯相比,索引就簡單的多:它不過是一個臨時文件,用來存貯你對項目做的一些改變,如增加、刪除、修改文件或者目錄等。一般地,你使用`git add`、`git rm`等命令把你修改過的文件、目錄提交到這個索引中去,然后應用`git commit`命令來完成一次提交。
- 前言
- 1 Web概述
- 1.1 什么是Web
- 1.2 超文本和超鏈接
- 1.3 URL
- 1.4 DNS
- 1.5 HTTP
- 1.5.1 客戶端請求
- 1.5.2 服務器應答
- 1.5.3 進一步了解HTTP
- 1.6 HTTPS
- 2 Web瀏覽器
- 2.1 HTML
- 2.1.1 文檔類型聲明
- 2.1.2 標簽和屬性
- 2.1.3 文檔結構
- 2.1.4 DOM
- 2.1.5 進一步了解HTML
- 2.2 CSS
- 2.2.1 樣式與樣式表
- 2.2.2 樣式表語法
- 2.2.3 級聯樣式表
- 2.2.4 進一步了解CSS
- 2.3 JavaScript
- 2.3.1 script標簽
- 2.3.2 操縱DOM
- 2.3.3 jQuery
- 2.3.4 進一步了解JavaScript
- 2.4 Ajax
- 2.5 移動設備與響應式Web設計
- 3 Web服務器
- 3.1 方法與資源
- 3.2 狀態代碼
- 3.3 靜態內容與動態內容
- 3.4 編程語言與技術
- 3.4.1 CGI
- 3.4.2 PHP
- 3.4.3 Java
- 3.4.4 Python
- 3.4.5 Ruby
- 3.4.6 Node.js
- 3.5 RESTful Web API
- 3.6 服務器架構
- 3.7 Web緩存
- 3.8 服務器推送
- 4 數據庫
- 4.1 關系型數據庫
- 4.2 NoSQL數據庫
- 5 Web服務器的其他組件
- 5.1 Cron
- 5.2 消息隊列
- 5.3 郵件服務器
- 6 開發工具與技術
- 6.1 Git
- 6.1.1 Git基礎操作
- 6.1.2 Git基本原理
- 6.1.3 進一步了解Git
- 6.2 敏捷開發