1. Git三大工作區(工作區、暫存區和版本庫)

工作區(WORKING DIRECTORY): 直接編輯文件的地方,肉眼可見直接操作;
暫存區(STAGIN AREA):數據(快照)暫時存放的地方;
版本庫(GIT DIRECTORT(RESPOSITORY)):存放已經提交的數據,push 的時候,就是把這個區的數據 push 到遠程git倉庫了。
git add就是將工作區的修改緩存在暫存區,git commit就是將暫存區的數據快照提交到本地庫
這就是為什么 git commit 之前要先執行 git add 的原因,如果不先執行add,那么直接執行commit時不會把當前的修改內容提交到代碼庫中的。
2. Git 基本概念(實體、引用和索引)
實體:
提交到 git 代碼倉庫中的所有文件,包括每個提交的說明信息,目錄結構等都會轉換成 git 實體
所有實體均存在於.git/objects/目錄中
git中每一個實體以一個40字符長度的十六進制字符串來唯一標識
git中包括4種類型的實體:
1.blob-存儲文件內容
2.tree-存儲目錄結構和文件名
3.commit-存儲提交的作者、日期、說明等
4.tag- 存儲指向特定提交對象的引用
引用:
Git 中,一個分支(branch)、遠程分支(remote branch)或一個標簽(tag)(也稱為輕量標簽)僅是指向一個實體的一個指針,這里的實體通常是一個commit實體。這些引用以文本文件的形式存儲在目錄 .git/refs/ 中符號引用(Symbolic References)Git 有一種特殊的引用,稱為符號引用。它並不直接指向一個實體,而是指向另一個引用。舉例說,.git/HEAD就是一個符號引用。它指向你正在工作的當前分支。
索引:
索引是一個暫存區,以二進制文件的形式存儲為文件 .git/index 中當git add 一個文件,git 將該文件的信息添加到索引中當git commit時,git 僅提交索引文件中列出的文件到 git 本地倉庫
實體、引用和索引之間的關系:

測試說明:
1. 新建一個readme,txt並提交到本地庫
git add readme.txt git commit -m 'first commit'
2. git log查看commit實體,可以看出對於的SHA1值為999976c43b0e684f1bf7af4bed1acd11b3afa636

3. git cat-file目錄查看該commit實體的內容, 可以看出該commit實體包含了提交的作者及郵箱等全局信息,除此之外還包括了一個tree實體, 該實體的SHA1值為bb527569763dcd71a5dcd4b9a4ba692f1ebb56c0

4. 使用git ls-tree查看該tree實體, 發現tree實體中包含了一個blob實體, 該實體的的SHA1值為0cce6dff89d87d991136ad27e18c928eb65f5bb3

5. 使用git cat-file -p XXX查看blob實體內容, 可以發現blob實體存儲的就是readme.txt文件內容

6. 此時我們查看.git/refs/heads/master文件內容,可以發現其剛好為commit實體的SHA1值,也就說明 master 為一個 引用 ,並且指向最后一個 commit 實體

7. 在剛剛介紹中,我們知道了tag實體存儲了指向指定commit實體的引用,下面來創建一個標簽"version1.1", 通過git rev-parse tagname來查看tag實體的SHA1值,再通過git cat-file -p XXXX可以發現該tag內部即為commit實體,且該commit實體為最后一次提交的實體

8. 至此我們生成了四個實體,分別為
999976c43b0e684f1bf7af4bed1acd11b3afa636 ----commit實體
bb527569763dcd71a5dcd4b9a4ba692f1ebb56c0 ---tree實體
0cce6dff89d87d991136ad27e18c928eb65f5bb3 ---blob實體
50e5866a324c6e2afaf6374c39b4b93d394b504a --tag實體
8. 查看各實體、引用、索引的儲存位置
實體(.git/objects/):以SHA1頭兩位作為文件名

引用(.git/refs): heads下存儲歷史commit實體, tags下存儲每一個標簽

索引(.git/index):

通過git ls-files --stage查看.git/index文件內容,可以發現並非存儲着最新commit實體的SHA1值,而是其commit實體關聯的blob實體SHA1值

再次測試.git/index中文件內容, 我們新建一個文件index並提交到本地庫


3. Git SHA1
git 為每一個實體生成一個160位的散列值,通常使用40個字符長度的16進制字符串表示
散列碰撞:
幾乎不會出現散列碰撞情況(相同SHA1,出現沖突)
通常情況下你不需要擔心該散列值會產生碰撞,對於 160 位數,你有 2160 或者大約 1048 種可能的 SHA1 散列值。這個數有多么巨大,你可以簡單感受下,即使你雇一萬億人來每秒產生一萬億個新的唯一 blob 對象,持續一萬億年,你也只有 1043 個 blob 對象,所以你基本不用擔心該散列值會產生碰撞,而且只有當不同的內容產生了相同的 SHA1 散列值才能稱為碰撞。
git 基於內容的 SHA1:
對應相同的內容得到的永遠是相同的 SHA1
之前已經提到過 git 會為每個實體生成一個唯一的 SHA1 值來標識該實體,並且 git 會將生成的實體以二進制文件的形式保存在 .git/objets 目錄下,但是 git 在生成 SHA1 時並不是簡單地基於文件名、文件路徑、創建者及創建時間組合起來,相反 git 是基於內容來生成 SHA1,試想一下,如果我們項目中有一個 a.txt 文件,分別存放在 dir1,dir2 目錄下,這時候 git 並不會為 dir1/a.txt 和 dir2/a.txt 文件創建兩個 blob 實體,因為 a.txt 雖然在兩個目錄下,但是他們的內容是完全一樣的,所以 git 在生成實體時,通過散列算法,git 會發現這兩個文件得出的 SHA1 值是完全一樣的,所以 git 只會保存一個 blob 實體。這樣可以避免當我們再重命名文件,或者移動文件所在目錄時,生成重復的 blob 實體,這也得益於 git 基於內容的散列算法可以很好地發現這兩個文件其實是同一個文件。
測試:

4. git 特殊符號引用介紹
git 自動維護了幾個用於特定目的的特殊符號引用。這些引用可以在使用提交的任何地方使用。
- HEAD 始終指向當前分支的最終提交。當切換分支時 HEAD 會更新為指向新分支的最新提交。
- ORIG_HEAD 某些操作,例如 merage / reset 會把 merge 之前的 HEAD 保存到 ORIG_HEAD 中,以便在 merge 之后可以使用 ORIG_HEAD 來回滾到合並之前的狀態(在分支合並的時候,產生了沖突,如果已經修改了沖突,並產生了新的提交,但是沖突解決的有問題,想要還原之前的狀態重新合並,這時可以使用
git reset --hard ORIG_HEAD來還原到合並之前的狀態)。 - FETCH_HEAD 當使用命令
git fetch抓取遠程倉庫更新時,FETCH_HEAD 保存着最近抓取的分支的 HEAD。 - MERGE_HEAD 當一個合並正在進行時,其他分支的頭暫時記錄在 MERGE_HEAD 中,換言之, MERGE_HEAD 是正在合並進 HEAD 的提交。
