鍍金池/ 教程/ Java/ Git 對象模型
儲藏
Git 樹名
子模塊
使用 Git Grep 進(jìn)行搜索
建立一個私有倉庫
追蹤分支
Git 的撤消操作 - 重置,簽出和撤消
Git 和 Email
定制 Git
查看 Git 對象
Git 是如何存儲對象的
Git 標(biāo)簽
交互式 rebase
獲得一個 Git 倉庫
高級分支與合并
Git 對象模型
Git 日志
建立一個公共倉庫
分布式的工作流程
交互式添加
維護(hù) Git
安裝與初始化
安裝 Git
更底層的 Git
歡迎使用 Git
查找問題的利器 - Git Blame
找回丟失的對象
比較提交
創(chuàng)建新的空分支
查找問題的利器 - Git Bisect
Git 引用
忽略某些文件
傳輸協(xié)議
打包文件
Git 索引
rebase
正常的工作流程
分支合并
Git 目錄與工作目錄

Git 對象模型

SHA

所有用來表示項目歷史信息的文件,是通過一個 40 個字符的(40-digit)“對象名”來索引的,對象名看起來像這樣:

6ff87c4664981e4397625791c8ea3bbb5f2279a3

你會在 Git 里到處看到這種“40 個字符”字符串。每一個“對象名”都是對“對象”內(nèi)容做 SHA1 哈希計算得來的,(SHA1 是一種密碼學(xué)的哈希算法)。這樣就意味著兩個不同內(nèi)容的對象不可能有相同的“對象名”。

這樣做會有幾個好處:

  • Git 只要比較對象名,就可以很快的判斷兩個對象是否相同。
  • 因?yàn)樵诿總€倉庫(repository)的“對象名”的計算方法都完全一樣,如果同樣的內(nèi)容存在兩個不同的倉庫中,就會存在相同的“對象名”下。
  • Git 還可以通過檢查對象內(nèi)容的 SHA1 的哈希值和“對象名”是否相同,來判斷對象內(nèi)容是否正確。

對象

每個對象(object) 包括三個部分:類型,大小和內(nèi)容。大小就是指內(nèi)容的大小,內(nèi)容取決于對象的類型,有四種類型的對象:"blob"、"tree"、 "commit" 和"tag"。

  • “blob”用來存儲文件數(shù)據(jù),通常是一個文件。
  • “tree”有點(diǎn)像一個目錄,它管理一些“tree”或是 “blob”(就像文件和子目錄)
  • 一個“commit”只指向一個"tree",它用來標(biāo)記項目某一個特定時間點(diǎn)的狀態(tài)。它包括一些關(guān)于時間點(diǎn)的元數(shù)據(jù),如時間戳、最近一次提交的作者、指向上次提交(commits)的指針等等。
  • 一個“tag”是來標(biāo)記某一個提交(commit) 的方法。

幾乎所有的 Git 功能都是使用這四個簡單的對象類型來完成的。它就像是在你本機(jī)的文件系統(tǒng)之上構(gòu)建一個小的文件系統(tǒng)。

與 SVN 的區(qū)別

Git 與你熟悉的大部分版本控制系統(tǒng)的差別是很大的。也許你熟悉 Subversion、CVS、Perforce、Mercurial 等等,他們使用 “增量文件系統(tǒng)” (Delta Storage systems), 就是說它們存儲每次提交(commit)之間的差異。Git 正好與之相反,它會把你的每次提交的文件的全部內(nèi)容(snapshot)都會記錄下來。這會是在使用 Git 時的一個很重要的理念。

Blob 對象

一個 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 對象

一個 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 對象

"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)由以下的部分組成:

  • 一個 tree 對象:tree 對象的 SHA1 簽名,代表著目錄在某一時間點(diǎn)的內(nèi)容。
  • 父對象 (parent(s)):提交(commit)的SHA1簽名代表著當(dāng)前提交前一步的項目歷史. 上面的那個例子就只有一個父對象; 合并的提交(merge commits)可能會有不只一個父對象。如果一個提交沒有父對象,那么我們就叫它“根提交"(root commit),它就代表著項目最初的一個版本(revision)。每個項目必須有至少有一個“根提交"(root commit). 一個項目可能有多個"根提交“,雖然這并不常見(這不是好的作法)。
  • 作者:做了此次修改的人的名字, 還有修改日期。
  • 提交者(committer):實(shí)際創(chuàng)建提交(commit)的人的名字,同時也帶有提交日期。TA 可能會和作者不是同一個人;例如作者寫一個補(bǔ)丁(patch)并把它用郵件發(fā)給提交者,由他來創(chuàng)建提交(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)容。

標(biāo)簽對象

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/" 開頭的引用罷了)。