[轉發]深刻理解git,從研究git目錄開始

轉發學習的啦。git

彷佛不多有人在讀某個git快速教程的時候會說:「這個關於git的快速教程太酷了!讀完了用起git來超級舒服,而且我一點也不怕本身會破壞什麼東西。」web

 

對git的初學者來講,剛接觸git時就像進入了一個他不會聽/說當地語言的陌生國度。若是你知道本身在哪裏而且知道怎麼走,那就很好。可是若是你迷路了,那麻煩就大了。數據結構

 

市面上已經有不少關於學習git基本指令的文章,所以這篇文章不講這個。這裏咱們將試着從不一樣的角度來理解。工具

 

新手們一般都很懼怕git,的確,git確實是一個很強大的工具,但它對使用者並不友好。大量的新概念,一個指令在不一樣的狀況下作徹底不一樣的事情,各類隱含的反饋……學習

 

我認爲克服這個困難的一個可行方法就是在git的平常commit/puah以外再多作一點工做,若是花點時間來理解一下git是如何產生的,能幫咱們避免不少麻煩。測試

 理解.gitspa

當你創建了一個git repo,使用git init,git便建立了一個奇妙的目錄:.git指針

 

這個文件夾裏包含了git工做時所須要的全部信息。須要明確的是,若是想從你的項目中移除git,但保留項目文件,只須要刪除.git文件夾便可。可是,爲何要這樣作呢?對象

|── HEADblog

|── branches

|── config

|── description

|── hooks

| |── pre-commit.sample

| |── pre-push.sample

| └── ...

|── info

| └── exclude

|── objects

| |── info

| └── pack

└── refs

|── heads

└── tags

 

這是你第一次commit以前.git文件夾的樣子。

 

HEAD:這個咱們稍後再說。

 

config:這個文件夾裏是你的保存設置,這裏將寫入遠程URL,好比你的郵箱、用戶名等。每一次在控制端使用「git config」,它都會在這裏結束。

 

description:被gitweb (Github的原型)用來顯示對repo的描述。

 

hooks:這裏有一個有趣的特性。Git有一套能夠自動運行在任何一個有意義的git階段下的腳本,叫作hooks。hooks能夠運行在commit/rebase/pull等等狀態的以前或以後。腳本的名稱決定了它何時被執行。一個有用的pre-push腳本的例子將會被運行以測試控制器(遠程控制)中的全部樣式規則保持一致。

 

info - exclude:能夠將你不想被git處理的文件放到.gitignore文件夾裏。被排除的文件能夠作到相同的事情,除了它不能被共享。若是你不想將你的自定義IDE關聯到congif文件裏,就可使用這個。雖然大部分狀況下,.gitignore就足夠了。

 commit的內部是什麼?

每次你建立了一個文件並跟蹤它,git都會將其壓縮並存儲到本身的數據結構中。這個壓縮對象有一個獨特的名字、一個哈希碼,存儲在對象目錄下。

 

在研究對象目錄前,咱們要問一個問題,什麼是commit。這裏commit能夠看作是工做目錄的一種快照,但不只僅是快照。

 

實際上當你commit時,git只作兩件事情來創造你工做目錄的快照:

  1. 若是文件沒有修改,git僅僅增長壓縮文件的名字(hash)到快照中。

  2. 若是文件被修改過,git就將其壓縮,再將壓縮後的文件存儲到對象文件夾中。而後再添加這個壓縮文件的文件名(hash)到快照中去。

 

這是一個簡化的描述,實際整個過程會複雜一點。

 

一旦快照文件被創建,它也會被壓縮並用hash文件命名。那麼這些壓縮文件在什麼地方呢?答案:對象文件夾。

|── 4c

| └── f44f1e3fe4fb7f8aa42138c324f63f5ac85828 // hash

|── 86

| └── 550c31847e518e1927f95991c949fc14efc711 // hash

|── e6

| └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 // hash

|── info 

└── pack 

當我建立了一個名爲file_1.txt的空文件並commit以後,對象目錄應該是如上所看到的樣子。請注意若是你的文件的哈希碼是「89faaee…」,git將會吧這個文件儲存在名爲「89」的子目錄下,而且命名這個文件爲「faaee…」。

 

你看到右邊有3個hash字樣。一個對應的是個人file_1.txt文件,另外一個對應的是我commit時所建立的快照文件。那麼第三個呢?這是由於commit自己也是一個對象,它也會被壓縮並被存儲在對象文件夾中。

 

記住,一個commit指令實際上包含4件事:

  • 工做目錄的快照文件的名稱(哈希碼)

  • 註釋

  • 提交者信息

  • Parent commit的哈希碼

 

而後,大家能夠本身看一下若是不壓縮commit文件時會發生什麼:

 

// 查看歷史,你會很容易就找到你的commit哈希碼

// 你也沒必要粘貼所有的哈希碼,只要足夠就能夠了

// 創造了獨特哈希碼的字符串

git cat-file -p 4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828

獲得結果:

tree 86550c31847e518e1927f95991c949fc14efc711

author Pierre De Wulf <test@gmail.com> 1455775173 -0500

committer Pierre De Wulf <test@gmail.com> 1455775173 -0500

commit A

你看,正如咱們所指望的,咱們獲得了快照文件的哈希碼、做者信息、和個人commit信息。

 

這裏有兩件很重要的事情:

  • 正如所指望的,那個名爲「86550…」的快照哈希文件也是一個對象,而且可以在對象文件夾中被找到。

  • 由於這是個人第一個commit,因此沒有Parent。

 

快照文件本質上是什麼呢?

git cat-file -p 86550c31847e518e1927f95991c949fc14efc711

100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file_1.txt

這裏,咱們找到咱們對象存儲器中以前存儲的最後一個對象,咱們快照文件中僅有的對象。這是一個blob,但不是咱們今天要講的。

 

分支、標籤、HEAD,它們都同樣。

 

因此,如今你明白了git中的任何事情均可以用一個正確的hash來獲得。如今,讓咱們來看一看HEAD。那麼,HEAD中又有什麼呢?

cat HEAD

ref: refs/heads/master

這不是一個哈希碼,但它有價值,由於HEAD能夠被認爲是一個指向你正在使用的分支的指針。如今,咱們看一下refs/heads/master內部,咱們會看到以下信息:

 

cat refs/heads/master

4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828

是否是很眼熟?是的,這和咱們第一次commit時的哈希碼是徹底同樣的。這說明 branches和tags無非就是一個指向commit的指針。

 

這意味着你能夠刪除全部你想刪除的branches和tags,而它們所指向的 commit會仍然保留在這裏。僅僅是訪問commit更困難一點。

如今你應該明白,當你用一堆其它的指令來命令「壓縮」你目前的工做目錄並儲存在對象文件夾時,git實際上都作了些什麼。若是你對這些工具足夠熟悉,你就應該徹底知道哪些文件應該包含在commit中,而哪些文件不該該。

 

個人意思是commit並不真的是你工做目錄的快照,而是你想要commit的文件的快照。那麼在你保存你想要commit的文件以前,git將它們保存在哪裏呢?它將這些文件保存在索引文件。這裏咱們就不繼續往下深究了,感興趣的讀者請繼續研究。

相關文章
相關標籤/搜索