所有用來表示項目歷史信息的文件,是通過一個 40 個字符的(40-digit)“對象名”來索引的,對象名看起來像這樣:
6ff87c4664981e4397625791c8ea3bbb5f2279a3
你會在 Git 里到處看到這種“40 個字符”字符串。每一個“對象名”都是對“對象”內(nèi)容做 SHA1 哈希計算得來的,(SHA1 是一種密碼學(xué)的哈希算法)。這樣就意味著兩個不同內(nèi)容的對象不可能有相同的“對象名”。
這樣做會有幾個好處:
每個對象(object) 包括三個部分:類型,大小和內(nèi)容。大小就是指內(nèi)容的大小,內(nèi)容取決于對象的類型,有四種類型的對象:"blob"、"tree"、 "commit" 和"tag"。
幾乎所有的 Git 功能都是使用這四個簡單的對象類型來完成的。它就像是在你本機(jī)的文件系統(tǒng)之上構(gòu)建一個小的文件系統(tǒng)。
Git 與你熟悉的大部分版本控制系統(tǒng)的差別是很大的。也許你熟悉 Subversion、CVS、Perforce、Mercurial 等等,他們使用 “增量文件系統(tǒng)” (Delta Storage systems), 就是說它們存儲每次提交(commit)之間的差異。Git 正好與之相反,它會把你的每次提交的文件的全部內(nèi)容(snapshot)都會記錄下來。這會是在使用 Git 時的一個很重要的理念。
一個 blob 通常用來存儲文件的內(nèi)容。
http://wiki.jikexueyuan.com/project/git-community-book/images/object-blob.png" alt="" />
你可以使用 git show 命令來查看一個 blob 對象里的內(nèi)容。假設(shè)我們現(xiàn)在有一個 Blob 對象的 SHA1 哈希值,我們可以通過下面的的命令來查看內(nèi)容:
$ git show 6ff87c4664
Note that the only valid version of the GPL as far as this project
is concerned is _this_ particular version of the license (ie v2, not
v2.2 or v3.x or whatever), unless explicitly otherwise stated.
...
一個"blob對象"就是一塊二進(jìn)制數(shù)據(jù),它沒有指向任何東西或有任何其它屬性,甚至連文件名都沒有.
因?yàn)?blob 對象內(nèi)容全部都是數(shù)據(jù),如兩個文件在一個目錄樹(或是一個版本倉庫)中有同樣的數(shù)據(jù)內(nèi)容,那么它們將會共享同一個 blob 對象。Blob 對象和其所對應(yīng)的文件所在路徑、文件名是否改被更改都完全沒有關(guān)系。
一個 tree 對象有一串(bunch)指向 blob 對象或是其它 tree 對象的指針,它一般用來表示內(nèi)容之間的目錄層次關(guān)系。
http://wiki.jikexueyuan.com/project/git-community-book/images/object-tree.png" alt="" />
git show 命令還可以用來查看 tree 對象,但是 git ls-tree 能讓你看到更多的細(xì)節(jié)。如果我們有一個 tree 對象的 SHA1 哈希值,我們可以像下面一樣來查看它:
$ git ls-tree fb3a8bdd0ce
100644 blob 63c918c667fa005ff12ad89437f2fdc80926e21c .gitignore
100644 blob 5529b198e8d14decbe4ad99db3f7fb632de0439d .mailmap
100644 blob 6ff87c4664981e4397625791c8ea3bbb5f2279a3 COPYING
040000 tree 2fb783e477100ce076f6bf57e4a6f026013dc745 Documentation
100755 blob 3c0032cec592a765692234f1cba47dfdcc3a9200 GIT-VERSION-GEN
100644 blob 289b046a443c0647624607d471289b2c7dcd470b INSTALL
100644 blob 4eb463797adc693dc168b926b6932ff53f17d0b1 Makefile
100644 blob 548142c327a6790ff8821d67c2ee1eff7a656b52 README
...
就如同你所見,一個 tree 對象包括一串(list)條目,每一個條目包括:mode、對象類型、SHA1 值 和名字(這串條目是按名字排序的)。它用來表示一個目錄樹的內(nèi)容。
一個 tree 對象可以指向(reference):一個包含文件內(nèi)容的blob對象,也可以是其它包含某個子目錄內(nèi)容的其它tree 對象. Tree 對象、blob 對象和其它所有的對象一樣,都用其內(nèi)容的 SHA1 哈希值來命名的;只有當(dāng)兩個tree對象的內(nèi)容完全相同(包括其所指向所有子對象)時,它的名字才會一樣,反之亦然。這樣就能讓 Git 僅僅通過比較兩個相關(guān)的 tree 對象的名字是否相同,來快速的判斷其內(nèi)容是否不同。
(注意:在 submodules 里,trees 對象也可以指向 commits 對象. 請參見 Submodules 章節(jié))
注意:所有的文件的 mode 位都是 644 或 755,這意味著 Git 只關(guān)心文件的可執(zhí)行位。
"commit 對象"指向一個"tree 對象", 并且?guī)в邢嚓P(guān)的描述信息。
http://wiki.jikexueyuan.com/project/git-community-book/images/object-commit.png" alt="" />
你可以用 --pretty=raw 參數(shù)來配合 git show 或 git log 去查看某個提交(commit):
$ git show -s --pretty=raw 2be7fcb476
commit 2be7fcb4764f2dbcee52635b91fedb1b3dcf7ab4
tree fb3a8bdd0ceddd019615af4d57a53f43d8cee2bf
parent 257a84d9d02e90447b149af58b271c19405edb6a
author Dave Watson <dwatson@mimvista.com> 1187576872 -0400
committer Junio C Hamano <gitster@pobox.com> 1187591163 -0700
Fix misspelling of 'suppress' in docs
Signed-off-by: Junio C Hamano <gitster@pobox.com>
你可以看到, 一個提交(commit)由以下的部分組成:
注意:一個提交(commit)本身并沒有包括任何信息來說明其做了哪些修改;所有的修改(changes)都是通過與父提交(parents)的內(nèi)容比較而得出的。值得一提的是,盡管 Git 可以檢測到文件內(nèi)容不變而路徑改變的情況, 但是它不會去顯式(explicitly)的記錄文件的更名操作?!?你可以看一下 git diff 的 -M 參數(shù)的用法)
一般用 git commit 來創(chuàng)建一個提交(commit),這個提交(commit)的父對象一般是當(dāng)前分支(current HEAD),同時把存儲在當(dāng)前索引(index)的內(nèi)容全部提交。
現(xiàn)在我們已經(jīng)了解了3種主要對象類型(blob,tree 和 commit),好現(xiàn)在就讓我們大概了解一下它們怎么組合到一起的。
如果我們一個小項目, 有如下的目錄結(jié)構(gòu):
$>tree
.
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb
2 directories, 3 files
如果我們把它提交(commit)到一個 Git 倉庫中,在 Git 中它們也許看起來就如下圖:
http://wiki.jikexueyuan.com/project/git-community-book/images/objects-example.png" alt="" />
你可以看到:每個目錄都創(chuàng)建了 tree 對象 (包括根目錄),每個文件都創(chuàng)建了一個對應(yīng)的 blob 對象。最后有一個 commit 對象來指向根 tree 對象(root of trees),這樣我們就可以追蹤項目每一項提交內(nèi)容。
http://wiki.jikexueyuan.com/project/git-community-book/images/object-tag.png" alt="" />
一個標(biāo)簽對象包括一個對象名(譯者注:就是 SHA1 簽名),對象類型,標(biāo)簽名,標(biāo)簽創(chuàng)建人的名字("tagger"),還有一條可能包含有簽名(signature)的消息。你可以用 git cat-file 命令來查看這些信息:
$ git cat-file tag v1.5.0
object 437b1b20df4b356c9342dac8d38849f24ef44f27
type commit
tag v1.5.0
tagger Junio C Hamano <junkio@cox.net> 1171411200 +0000
GIT 1.5.0
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQBF0lGqwMbZpPMRm5oRAuRiAJ9ohBLd7s2kqjkKlq1qqC57SbnmzQCdG4ui
nLE/L9aUXdWeTFPron96DLA=
=2E+0
-----END PGP SIGNATURE-----
點(diǎn)擊 git tag,可以了解如何創(chuàng)建和驗(yàn)證標(biāo)簽對象。(注意:git tag 同樣也可以用來創(chuàng)建 "輕量級的標(biāo)簽"(lightweight tags),但它們并不是標(biāo)簽對象,而只一些以 "refs/tags/" 開頭的引用罷了)。