Git 是一套內(nèi)容尋址文件系統(tǒng)。很不錯(cuò)。不過這是什么意思呢? 這種說法的意思是,Git
          從核心上來看不過是簡(jiǎn)單地存儲(chǔ)鍵值對(duì)(key-value)。它允許插入任意類型的內(nèi)容,并會(huì)返回一個(gè)鍵值,通過該鍵值可以在任何時(shí)候再取出該內(nèi)容。

          我們都知道當(dāng)我們初始化一個(gè)倉(cāng)庫(kù)的時(shí)候,也就是執(zhí)行以下命令后,文件夾內(nèi)會(huì)生成一個(gè).git文件夾,
          git init
          內(nèi)部會(huì)包含,以下文件夾。



          * hooks
          //鉤子文件夾,內(nèi)部文件實(shí)際上就是一些特定時(shí)間觸發(fā)的shell腳本,我們可以簡(jiǎn)單的做一個(gè)部署系統(tǒng),每次提交特定tag的時(shí)候,則部署最新的代碼到服務(wù)器。
          * objects //真正的內(nèi)容存放的文件夾,下面重點(diǎn)講下這里。
          * refs
          //refs目錄存放了各個(gè)分支(包括各個(gè)遠(yuǎn)端和本地的HEAD)所指向的commit對(duì)象的指針(引用),也就是對(duì)應(yīng)的sha-1值;同時(shí)還包括stash的最新sha-1值
          * config //git配置信息,包括用戶名,email,remote repository的地址,本地branch和remote
          branch的follow關(guān)系
          * HEAD //存放的是一個(gè)具體的路徑,也就是refs文件夾下的某個(gè)具體分支。意義:指向當(dāng)前的工作分支。項(xiàng)目中的HEAD 是指向當(dāng)前 commit
          的引用,它具有唯一性,每個(gè)倉(cāng)庫(kù)中只有一個(gè) HEAD。在每次提交時(shí)它都會(huì)自動(dòng)向前移動(dòng)到最新 的 commit
          * index //存放的索引文件,可使用 git ls-files --stage 查看。應(yīng)該zlib加密后的,PHP可使用gzdeflate()函數(shù)
          這是objects文件夾,可以看到都是些數(shù)字和字符,實(shí)際上就是十六進(jìn)制數(shù)。


          下圖是進(jìn)入00文件夾后所有文件。



          認(rèn)識(shí)下GIT對(duì)象: blob對(duì)象, tree對(duì)象, commit對(duì)象

          1.創(chuàng)建blob對(duì)象

          ??下面我們直接上底層命令, 運(yùn)行此命令后,會(huì)在 .git/objects 文件夾下生成一個(gè) 兩個(gè)字符 的文件夾,文件夾內(nèi)部文件即類似上圖中文件一樣。
          echo 'test' | git hash-object -w --stdin git hash-object -w test.txt
          分解命令:
          hash-object: 計(jì)算文本內(nèi)容的sha-1(哈希值) -w :
          加上此參數(shù)后,會(huì)把內(nèi)容寫入/objects文件夾,不加則僅僅是計(jì)算(不可使用此法單純做計(jì)算用,因?yàn)镚IT計(jì)算的HASH,其基礎(chǔ)內(nèi)容與原內(nèi)容有所區(qū)別)
          --stdin : 此參數(shù)接收來自于標(biāo)準(zhǔn)輸入的內(nèi)容,即前面的 echo 'test'; 不加此參數(shù),則直接寫入某個(gè)文本
          所以實(shí)際上我們看到的,objects 文件夾下的內(nèi)容,文件名實(shí)際上是 hash
          值。文件夾是40個(gè)字符的前兩個(gè)(擁有相同前2位的hash值會(huì)被分配到同一個(gè)文件夾中),
          具體文件名則是后面38個(gè)字符。使用hash值的原因就在于,位數(shù)夠多,并且hash值唯一,一點(diǎn)小變化,都會(huì)生成新的hash值,和md5算法是一樣的道理。


          注意:此hash值就像是GIT的指針,能唯一對(duì)應(yīng)某一個(gè)具體的內(nèi)容或提交,hash值作為尋址作用,不作為內(nèi)容存儲(chǔ)用,具體的文件內(nèi)容存儲(chǔ)方式是GIT更底層的存儲(chǔ)方式?jīng)Q定。(sha-1和md5一樣,均是不可逆的)

          通過Linux find 命令查看所有已存儲(chǔ)的hash文件:
          find .git/objects -type f
          通過 cat-file 命令可以將數(shù)據(jù)內(nèi)容取回。該命令是查看 Git 對(duì)象的瑞士軍刀。傳入 -p 參數(shù)可以讓該命令輸出數(shù)據(jù)內(nèi)容的類型:
          git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4 test content
          通過 hash-object 命令,會(huì)把每一個(gè)文件的內(nèi)容都給記錄下來, 以此生成一個(gè)blob對(duì)象??赏ㄟ^以下命令查看對(duì)象的類型
          git cat-file -t d670460b4b4aece5915caf5c68d12f560a9fe3e4 blob
          在實(shí)際項(xiàng)目過程中,不會(huì)這么簡(jiǎn)單,因?yàn)槲覀兠看翁峤欢际且粋€(gè)多文件的提交。很少的時(shí)候是單文件的,那此時(shí)Git就不是單單存儲(chǔ)一個(gè) blob對(duì)象了,而是
          tree對(duì)象,
          tree對(duì)象,見名知意,就是一個(gè)樹對(duì)象,類似于操作系統(tǒng)目錄,tree的分支,可能還是tree,也可能是blob,這就看實(shí)際的場(chǎng)景了。

          對(duì)象存儲(chǔ)方法:
          GIT使用 zlib 庫(kù) 的 deflate方法對(duì)數(shù)據(jù)內(nèi)容進(jìn)行壓縮,但內(nèi)容為 "blob 字符串長(zhǎng)度+空字節(jié)+字符串本身"; 如:
          blob 3\0aaa
          2.創(chuàng)建tree對(duì)象


          ??上面說的創(chuàng)建blob對(duì)象,僅僅只是對(duì)某一個(gè)文件進(jìn)行的計(jì)算與存儲(chǔ),而我們實(shí)際項(xiàng)目中,可能每一次操作都是好幾個(gè),甚至十幾個(gè)文件一起,那如何才能把他們組織到一起,這就是
          tree 對(duì)象的作用了。
          要?jiǎng)?chuàng)建tree對(duì)象,需要使用 update-index,write-tree 命令:
          git update-index --add a.txt //此命令即可將a.txt加入到暫存區(qū), git write-tree
          //此命令即寫入tree對(duì)象。 or git update-index --add --cacheinfo 100164 sha-1 a.txt git
          write-tree
          ??--cacheinfo 會(huì)從已存在的數(shù)據(jù)庫(kù)(Object)中取得對(duì)應(yīng)的內(nèi)容給添加到索引中。

          ??實(shí)際生產(chǎn)中,一般情況下,會(huì)把末尾文件夾中的所有修改文件創(chuàng)建,blob對(duì)象,再對(duì)該文件夾(也就是所有的blob對(duì)象整體)進(jìn)行write-tree的操作,得到一個(gè)tree對(duì)象,反復(fù)進(jìn)行此操作,最后得到多個(gè)tree對(duì)象和多個(gè)blob對(duì)象。
          ??如上所說,若需要對(duì)某個(gè)存在三級(jí)文件夾的二級(jí)文件夾進(jìn)行write-tree操作,
          在把三級(jí)文件夾下的所有修改文件生成blob后,進(jìn)行整體tree對(duì)象化,之后再與二級(jí)文件夾同級(jí)的文件夾和文件進(jìn)行相同操作。此時(shí)就需要用到: read-tree
          命令。如:
          git read-tree --prefix=test_add_tree c08670e3f77cae748fbda5c0b83613d5f5995655
          //該操作會(huì)把tree對(duì)象b822ff7272492f12b211d3b9c0f90163f48383bb
          加入暫存區(qū)中,并取名test,之后再進(jìn)行write-tree就把tree對(duì)象b822ff7272492f12b211d3b9c0f90163f48383bb
          給加入了 //從實(shí)際生產(chǎn)來看,GIT會(huì)把此prefix默認(rèn)為文件夾的名字 git cat-file -p
          dc054e0c59565791c70a1f6d6ad7d6676baf0349 100644 blob
          765dc741c088b3baef0314a457f74c877a43405b a.txt 100644 blob
          7609a432a0ba538cfe3d7bbdb107096c2f010577 b.txt 100644 blob
          b114c2d776f5dd25dc75a2c7a81f99262d618bc3 c.txt 040000 tree
          c08670e3f77cae748fbda5c0b83613d5f5995655 test_add_tree
          3.創(chuàng)建commit對(duì)象

          ??平時(shí)我們都是用** git commit -m "xxxx"** 提交了信息, 在這之前,會(huì)暫存相關(guān)文件的改動(dòng),
          在提交后,會(huì)生成對(duì)應(yīng)的tree對(duì)象,返回tree所對(duì)應(yīng)的 sha-1值, 再進(jìn)行一次 commit-tree
          操作,最后會(huì)把剛保存的tree對(duì)象所對(duì)應(yīng)的sha-1值 賦值給 commit-tree, 即生成了一個(gè)commit 對(duì)象。用法:
          echo '提交信息' | git commit-tree b822ff7272492f12b211d3b9c0f90163f48383bb
          (對(duì)應(yīng)的tree對(duì)象返回的 sha-1值) f7bc39001ff6cb183022234c94aa61ddedee44e0
          通過 git cat-file -p f7bc39001ff6cb183022234c94aa61ddedee44e0 得到:
          tree b822ff7272492f12b211d3b9c0f90163f48383bb //該commit對(duì)象指向的tree對(duì)象 author
          max.hua <****@****.cn> 1563847402 +0800 //config中指定的user.name信息 committer
          max.hua <****@****.cn> 1563847402 +0800 //config中指定的user.email信息 first commit
          我們還可以給某一個(gè)commit對(duì)象指定它的父commit對(duì)象:
          echo 'second commit' | git commit-tree
          b822ff7272492f12b211d3b9c0f90163f48383bb -p
          f7bc39001ff6cb183022234c94aa61ddedee44e0 (父級(jí)commit對(duì)象sha-1值)
          42e08b70c341b7e60944de6dffc342b77f94f6e4
          通過 git cat-file -p 42e08b70c341b7e60944de6dffc342b77f94f6e4得到:
          tree b822ff7272492f12b211d3b9c0f90163f48383bb parent
          f7bc39001ff6cb183022234c94aa61ddedee44e0 //指向的父級(jí)commit對(duì)象 author max.hua
          <****@****.cn> 1563848153 +0800 committer max.hua <****@****.cn> 1563848153
          +0800 second commit
          想要查看我們使用管道命令生成的log記錄: git log --stat 42e08b70c341b7e60944de6dffc342b77f94f6e4
          ,得到:
          git log --stat 42e08b70c341b7e60944de6dffc342b77f94f6e4 commit
          42e08b70c341b7e60944de6dffc342b77f94f6e4 Author: max.hua <****@****.cn> Date:
          Tue Jul 23 10:15:53 2019 +0800 second commit commit
          f7bc39001ff6cb183022234c94aa61ddedee44e0 Author: max.hua <****@****.cn> Date:
          Tue Jul 23 10:03:22 2019 +0800 first commit a.php | 6 ++++++ b.txt | 1 + c.txt
          | 1 + 3 files changed, 8 insertions(+)
          ??從上面的用法可以得到, git commit-tree 生成的 commit對(duì)象,只會(huì)包含 tree對(duì)象,參數(shù)選項(xiàng)中沒有可以指定blob對(duì)象的參數(shù)。
          如下:在測(cè)試時(shí),強(qiáng)制使用blob對(duì)象的 sha-1值,會(huì)出現(xiàn)報(bào)錯(cuò)現(xiàn)象。
          echo '第一次提交' | git commit-tree e56e15bb7ddb6bd0b6d924b18fcee53d8713d7ea fatal:
          e56e15bb7ddb6bd0b6d924b18fcee53d8713d7ea is not a valid 'tree' object
          4.應(yīng)用

          以上基本上就可概括平時(shí)使用git add 和 git commit 命令時(shí)GIT的工作。

          * 保存已修改文件成blob格式對(duì)象: git hash-object -w 各個(gè)文件

          * 更新索引: git update-index --add 各個(gè)文件名 或者 git update-index --add --cacheinfo
          mode sha-1 文件名 或者 git read-tree --prefix=test sha-1(某個(gè)tree的sha-1)
          ,作用在于把某個(gè)tree讀入索引中

          * 創(chuàng)建樹對(duì)象: git write-tree

          * 最后創(chuàng)建commit對(duì)象: git commit-tree sha-1 -m "提交信息" 或者 echo "提交信息" | git
          commit-tree sha-1 -p 父級(jí)sha-1
          4.1 git add

          ??平時(shí)我們?cè)谑褂玫臅r(shí)候,使用 git add c.txt 后,把 c.txt 放入了暫存區(qū),
          而實(shí)際上此時(shí)已經(jīng)生成了blob對(duì)象,并保存了相應(yīng)的sha-1值命名的文件,同時(shí)添加到了索引文件中;之后當(dāng)我們修改了之前添加到暫存區(qū)的文件并使用git
          status 查看狀態(tài)的時(shí)候,GIT會(huì)再對(duì)文件進(jìn)行一次 hash運(yùn)算,如果發(fā)現(xiàn)和已存在與索引中的內(nèi)容產(chǎn)生了變化(sha-1值不同),則又會(huì)呈現(xiàn)出一個(gè)
          Modify 狀態(tài)。

          ??通過以下命令可查看到 .git/index 文件中的內(nèi)容,其中存放了每一個(gè)被追蹤的文件,對(duì)應(yīng)的blob對(duì)象最新的sha-1值,
          通過這里即可很直接的判斷出哪個(gè)文件是否被修改,哪些沒有被追蹤了。
          git ls-files --stage 100644 45c2647671db4e9d426c2085eba814fea16f6b9a 0 b.txt
          100644 177308c04fc55b0d9985a7dfb545f6cebb7ea432 0 c.txt
          4.2 git diff

          ??同上, 使用 git diff后, 會(huì)把文件的差異給列出來,而對(duì)比對(duì)象即是 索引中的內(nèi)容,并不是HEAD指向的內(nèi)容。 當(dāng)對(duì)某文件執(zhí)行了 git add
          后,之后再進(jìn)行修改,再使用git diff 查看區(qū)別, 你會(huì)發(fā)現(xiàn)已經(jīng)存在區(qū)別了。也就是說,git diff
          實(shí)際上是把當(dāng)前文件與索引中的文件進(jìn)行比較(通過sha-1值比較),當(dāng)有不同的情況,則列出對(duì)應(yīng)的改變。

          4.3 git status

          ??使用 git status 后,GIT會(huì)對(duì)所有文件進(jìn)行sha-1值計(jì)算,若計(jì)算到與前面講到的
          索引中得對(duì)應(yīng)文件的sha-1值不同了,則代表有所改動(dòng),則標(biāo)記為 Modify,若發(fā)現(xiàn)索引中不存在對(duì)應(yīng)文件的sha-1值, 則標(biāo)記為 Untracked
          files。

          4.4 git branch 分支名

          ??該命令會(huì)生成一個(gè)新分支,也就是在
          .git/refs/heads里面生成一個(gè)新的文件,文件名為分支名,如果有前綴feature之類的。則feature是文件夾名,其內(nèi)是文件名。文件內(nèi)容為當(dāng)前的
          commit 對(duì)象對(duì)應(yīng)的sha-1值。所以實(shí)際上分支,也是一個(gè) commit
          對(duì)象的引用。只是在GIT中專門有文件記錄了分支名和指向。我們甚至可以通過創(chuàng)建文件的方式,直接創(chuàng)建branch。
          cd .git/refs/head/ echo 'e56e15bb7ddb6bd0b6d924b18fcee53d8713d7ea' > test_aaa
          4.5 git checkout 某分支

          ??當(dāng)使用 git checkout 的時(shí)候, GIT內(nèi)部實(shí)際上就是把當(dāng)前的HEAD指針給指向了另一個(gè)分支,而實(shí)際上也就是把 .git/HEAD
          文件內(nèi)容修改為切換的分支,而 .git/HEAD 內(nèi)容指向的就是 .git/refs/heads中的分支,此文件內(nèi)容又是一個(gè) commit 對(duì)象的
          sha-1值,所以也就間接指向了某個(gè)具體的 commit對(duì)象了, 從這個(gè)commit對(duì)象可得到它的父級(jí)對(duì)象,依次類推,即可得到完整的代碼。
          git update-ref HEAD <newvalue>
          ??有時(shí)候,我們?cè)谑褂肞HPStorm的時(shí)候,會(huì)用到"Annotate",
          就是查看本文件的GIT提交記錄,還會(huì)查看某個(gè)提交下以前的版本的文件,看具體是修改了啥。"Amnotate previous revision",實(shí)際上就是做了
          git checkout sha-1 文件名 //該命令就會(huì)把某文件給恢復(fù)到某個(gè)提交的時(shí)候,不加文件名的話,就是恢復(fù)整個(gè)項(xiàng)目到某個(gè)提交的時(shí)候
          4.6 git commit -m "提交信息"

          ??見如上信息。

          4.7 git log

          ??使用該命令后,去 .git/logs 下尋找當(dāng)前分支對(duì)應(yīng)的文件名,文件中的內(nèi)容即為每一次提交的信息。

          4.8 git push

          ??使用git push 是把當(dāng)前的分支上傳到遠(yuǎn)程倉(cāng)庫(kù),并把這個(gè) branch 的路徑上的所有 commits 也一并上傳。
          我認(rèn)為實(shí)際就是修改了.git中的文件,因?yàn)檫@些文件里實(shí)際上就已經(jīng)包含了壓縮后的代碼,等你切換分支的時(shí)候,GIT會(huì)根據(jù)這些內(nèi)容把代碼給檢索出來。

          4.9 git tag [version name]

          ??使用 git tag 實(shí)際上和 git branch 類似,branch 是指向某一個(gè)commit的指針,但是branch會(huì)隨著每次提交而移動(dòng),
          但是tag不會(huì), 當(dāng)打了tag后, 那這個(gè) tag 對(duì)應(yīng)的commit對(duì)象指針就固定了,不會(huì)移動(dòng)了。它和 git branch 一樣,都不會(huì)產(chǎn)生blob或
          tree 對(duì)象, git tag 只會(huì)在 .git/refs/tag 下生成一個(gè) tag名的文件,內(nèi)容為指向當(dāng)前commit的sha-1

          4.10 git stash

          ??使用git stash 實(shí)際上是創(chuàng)建了一個(gè)新的commit對(duì)象,為什么這么說呢?在 .git/refs 目錄下,當(dāng)?shù)谝淮蝧tash后,會(huì)生成一個(gè)
          stash文件, 內(nèi)容即為一個(gè)sha-1值, 通過 git cat-file -p查看到具體內(nèi)容為一個(gè) commit對(duì)象的內(nèi)容, 還能看到其有兩個(gè) 父級(jí)
          commit, 一個(gè)是前一個(gè) git commit 的sha-1值,
          一個(gè)是執(zhí)行stash后,新生成的commit。最后把這兩個(gè)commit對(duì)象作為父親,再生成一個(gè)commit對(duì)象存放于stash文件中。


          疑問:

          *
          git
          commit-tree的時(shí)候,是怎么指定父級(jí)commit對(duì)象的,以什么為參考,才能指定對(duì)應(yīng)的父級(jí)commit對(duì)象。我從平時(shí)工單的log記錄來看,有些commit對(duì)象有兩個(gè)父級(jí)commit對(duì)象(可能是合并操作的時(shí)候自動(dòng)生成的commit對(duì)象),但不一定就是前一個(gè)commit。(git
          stash 有兩個(gè)父級(jí)對(duì)象)

          * 我們都知道git stash 存的是一個(gè)棧的結(jié)構(gòu),但是 .git/refs/stash 文件里
          只有一個(gè)sha-1值,只對(duì)應(yīng)一個(gè)commit對(duì)象,我查看了commit對(duì)象的具體內(nèi)容,他的兩個(gè)父親均不是我前一個(gè)創(chuàng)建的stash對(duì)應(yīng)的commit對(duì)象,不知道這個(gè)棧的結(jié)構(gòu)怎么來的。
          *
          看某項(xiàng)目的樹結(jié)構(gòu),會(huì)發(fā)現(xiàn),每一個(gè)commit對(duì)象內(nèi)部對(duì)應(yīng)的 tree,都是一整個(gè)項(xiàng)目,而不是某一個(gè)文件或者某幾個(gè)文件夾,這就解決了我的疑惑, 每次只需
          git update-index --add 后,我想新創(chuàng)建一個(gè) tree對(duì)象, tree對(duì)象內(nèi)部一直會(huì)存在之前加入index中的blob對(duì)象。
          那么GIT實(shí)際上就是每一個(gè)commit,都應(yīng)該能從tree和 p-tree上追溯到整個(gè)項(xiàng)目文件。

          *
          在手動(dòng)創(chuàng)建分支的過程中,發(fā)現(xiàn)在執(zhí)行 git init后,看起來你是在master分支, 但是實(shí)際執(zhí)行 git
          branch,看不到任何輸出,這說明在這個(gè)時(shí)候?qū)嶋H上master分支是沒有創(chuàng)建的,必須要有第一次提交后,master分支才會(huì)創(chuàng)建,因?yàn)橹挥羞@樣 ,
          .git/refs/head/master 文件中才有可寫的 commit 對(duì)象的 sha-1值。


          需要注意:對(duì)commit對(duì)象的跟蹤,commit對(duì)象能跟蹤到具體哪一次修改,改了哪些具體文件,通過對(duì)commit的切換,就能找到某個(gè)時(shí)間點(diǎn)的文件記錄了。GIT每次提交文件,實(shí)際上都是提交的整個(gè)文件,而不僅僅是修改的部分。所以當(dāng)我們執(zhí)行一些回退操作的時(shí)候能回到某個(gè)時(shí)間點(diǎn)的文件,即直接指定某個(gè)commit對(duì)象,查到commit對(duì)象中包含的各類tree對(duì)象和blob對(duì)象,把這些對(duì)象中壓縮內(nèi)容給取出來覆蓋當(dāng)前的同級(jí),同名文件即可;同時(shí)新增的,給刪除了。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                免费在线人成视频 | 黄色动漫免费视频 | 加勒比精品 | 男同桌脱我内裤往里灌水小说 | 伊人9999 |