<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## Git 對象 Git 是一個內容尋址文件系統。 看起來很酷, 但這是什么意思呢? 這意味著,Git 的核心部分是一個簡單的鍵值對數據庫(key-value data store)。 你可以向該數據庫插入任意類型的內容,它會返回一個鍵值,通過該鍵值可以在任意時刻再次檢索(retrieve)該內容。 可以通過底層命令?`hash-object`?來演示上述效果——該命令可將任意數據保存于?`.git`?目錄,并返回相應的鍵值。 首先,我們需要初始化一個新的 Git 版本庫,并確認?`objects`?目錄為空: ~~~ $ git init test Initialized empty Git repository in /tmp/test/.git/ $ cd test $ find .git/objects .git/objects .git/objects/info .git/objects/pack $ find .git/objects -type f ~~~ 可以看到 Git 對?`objects`?目錄進行了初始化,并創建了?`pack`?和?`info`?子目錄,但均為空。 接著,往 Git 數據庫存入一些文本: ~~~ $ echo 'test content' | git hash-object -w --stdin d670460b4b4aece5915caf5c68d12f560a9fe3e4 ~~~ `-w`?選項指示?`hash-object`?命令存儲數據對象;若不指定此選項,則該命令僅返回對應的鍵值。`--stdin`?選項則指示該命令從標準輸入讀取內容;若不指定此選項,則須在命令尾部給出待存儲文件的路徑。 該命令輸出一個長度為 40 個字符的校驗和。 這是一個 SHA-1 哈希值——一個將待存儲的數據外加一個頭部信息(header)一起做 SHA-1 校驗運算而得的校驗和。后文會簡要討論該頭部信息。 現在我們可以查看 Git 是如何存儲數據的: ~~~ $ find .git/objects -type f .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 ~~~ 可以在?`objects`?目錄下看到一個文件。 這就是開始時 Git 存儲內容的方式——一個文件對應一條內容,以該內容加上特定頭部信息一起的 SHA-1 校驗和為文件命名。 校驗和的前兩個字符用于命名子目錄,余下的 38 個字符則用作文件名。 可以通過?`cat-file`?命令從 Git 那里取回數據。 這個命令簡直就是一把剖析 Git 對象的瑞士軍刀。 為?`cat-file`?指定?`-p`?選項可指示該命令自動判斷內容的類型,并為我們顯示格式友好的內容: ~~~ $ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4 test content ~~~ 至此,你已經掌握了如何向 Git 中存入內容,以及如何將它們取出。 我們同樣可以將這些操作應用于文件中的內容。 例如,可以對一個文件進行簡單的版本控制。 首先,創建一個新文件并將其內容存入數據庫: ~~~ $ echo 'version 1' > test.txt $ git hash-object -w test.txt 83baae61804e65cc73a7201a7252750c76066a30 ~~~ 接著,向文件里寫入新內容,并再次將其存入數據庫: ~~~ $ echo 'version 2' > test.txt $ git hash-object -w test.txt 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a ~~~ 數據庫記錄下了該文件的兩個不同版本,當然之前我們存入的第一條內容也還在: ~~~ $ find .git/objects -type f .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a .git/objects/83/baae61804e65cc73a7201a7252750c76066a30 .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 ~~~ 現在可以把文件內容恢復到第一個版本: ~~~ $ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt $ cat test.txt version 1 ~~~ 或者第二個版本: ~~~ $ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt $ cat test.txt version 2 ~~~ 然而,記住文件的每一個版本所對應的 SHA-1 值并不現實;另一個問題是,在這個(簡單的版本控制)系統中,文件名并沒有被保存——我們僅保存了文件的內容。 上述類型的對象我們稱之為數據對象(blob object)。 利用?`cat-file -t`?命令,可以讓 Git 告訴我們其內部存儲的任何對象類型,只要給定該對象的 SHA-1 值: ~~~ $ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a blob ~~~ ### [樹對象](http://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1#樹對象) 接下來要探討的對象類型是樹對象(tree object),它能解決文件名保存的問題,也允許我們將多個文件組織到一起。 Git 以一種類似于 UNIX 文件系統的方式存儲內容,但作了些許簡化。 所有內容均以樹對象和數據對象的形式存儲,其中樹對象對應了 UNIX 中的目錄項,數據對象則大致上對應了 inodes 或文件內容。 一個樹對象包含了一條或多條樹對象記錄(tree entry),每條記錄含有一個指向數據對象或者子樹對象的 SHA-1 指針,以及相應的模式、類型、文件名信息。 例如,某項目當前對應的最新樹對象可能是這樣的: ~~~ $ git cat-file -p master^{tree} 100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README 100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile 040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib ~~~ `master^{tree}`?語法表示?`master`?分支上最新的提交所指向的樹對象。 請注意,`lib`?子目錄(所對應的那條樹對象記錄)并不是一個數據對象,而是一個指針,其指向的是另一個樹對象: ~~~ $ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb ~~~ 從概念上講,Git 內部存儲的數據有點像這樣: ![](https://box.kancloud.cn/2015-10-12_561bcbb51efa8.png) Figure 10-1.?簡化版的 Git 數據模型。 你可以輕松創建自己的樹對象。 通常,Git 根據某一時刻暫存區(即 index 區域,下同)所表示的狀態創建并記錄一個對應的樹對象,如此重復便可依次記錄(某個時間段內)一系列的樹對象。 因此,為創建一個樹對象,首先需要通過暫存一些文件來創建一個暫存區。 可以通過底層命令`update-index`?為一個單獨文件——我們的 test.txt 文件的首個版本——創建一個暫存區。 利用該命令,可以把 test.txt 文件的首個版本人為地加入一個新的暫存區。 必須為上述命令指定?`--add`?選項,因為此前該文件并不在暫存區中(我們甚至都還沒來得及創建一個暫存區呢);同樣必需的還有?`--cacheinfo`?選項,因為將要添加的文件位于 Git 數據庫中,而不是位于當前目錄下。 同時,需要指定文件模式、SHA-1 與文件名: ~~~ $ git update-index --add --cacheinfo 100644 \ 83baae61804e65cc73a7201a7252750c76066a30 test.txt ~~~ 本例中,我們指定的文件模式為?`100644`,表明這是一個普通文件。 其他選擇包括:`100755`,表示一個可執行文件;`120000`,表示一個符號鏈接。 這里的文件模式參考了常見的 UNIX 文件模式,但遠沒那么靈活——上述三種模式即是 Git 文件(即數據對象)的所有合法模式(當然,還有其他一些模式,但用于目錄項和子模塊)。 現在,可以通過?`write-tree`?命令將暫存區內容寫入一個樹對象。 此處無需指定?`-w`?選項——如果某個樹對象此前并不存在的話,當調用?`write-tree`?命令時,它會根據當前暫存區狀態自動創建一個新的樹對象: ~~~ $ git write-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 $ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579 100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt ~~~ 不妨驗證一下它確實是一個樹對象: ~~~ $ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579 tree ~~~ 接著我們來創建一個新的樹對象,它包括 test.txt 文件的第二個版本,以及一個新的文件: ~~~ $ echo 'new file' > new.txt $ git update-index test.txt $ git update-index --add new.txt ~~~ 暫存區現在包含了 test.txt 文件的新版本,和一個新文件:new.txt。 記錄下這個目錄樹(將當前暫存區的狀態記錄為一個樹對象),然后觀察它的結構: ~~~ $ git write-tree 0155eb4229851634a0f03eb265b69f5a2d56f341 $ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt ~~~ 我們注意到,新的樹對象包含兩條文件記錄,同時 test.txt 的 SHA-1 值(`1f7a7a`)是先前值的“第二版”。 只是為了好玩:你可以將第一個樹對象加入第二個樹對象,使其成為新的樹對象的一個子目錄。 通過調用?`read-tree`?命令,可以把樹對象讀入暫存區。 本例中,可以通過對?`read-tree`?指定?`--prefix`?選項,將一個已有的樹對象作為子樹讀入暫存區: ~~~ $ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579 $ git write-tree 3c4e9cd789d88d8d89c1073707c3585e41b0e614 $ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614 040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt ~~~ 如果基于這個新的樹對象創建一個工作目錄,你會發現工作目錄的根目錄包含兩個文件以及一個名為`bak`?的子目錄,該子目錄包含 test.txt 文件的第一個版本。 可以認為 Git 內部存儲著的用于表示上述結構的數據是這樣的: ![](https://box.kancloud.cn/2015-10-12_561bcbb53536e.png) Figure 10-2.?當前 Git 的數據內容結構。 ### [提交對象](http://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1#提交對象) 現在有三個樹對象,分別代表了我們想要跟蹤的不同項目快照。然而問題依舊:若想重用這些快照,你必須記住所有三個 SHA-1 哈希值。 并且,你也完全不知道是誰保存了這些快照,在什么時刻保存的,以及為什么保存這些快照。 而以上這些,正是提交對象(commit object)能為你保存的基本信息。 可以通過調用?`commit-tree`?命令創建一個提交對象,為此需要指定一個樹對象的 SHA-1 值,以及該提交的父提交對象(如果有的話)。 我們從之前創建的第一個樹對象開始: ~~~ $ echo 'first commit' | git commit-tree d8329f fdf4fc3344e67ab068f836878b6c4951e3b15f3d ~~~ 現在可以通過?`cat-file`?命令查看這個新提交對象: ~~~ $ git cat-file -p fdf4fc3 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 author Scott Chacon <schacon@gmail.com> 1243040974 -0700 committer Scott Chacon <schacon@gmail.com> 1243040974 -0700 first commit ~~~ 提交對象的格式很簡單:它先指定一個頂層樹對象,代表當前項目快照;然后是作者/提交者信息(依據你的?`user.name`?和?`user.email`?配置來設定,外加一個時間戳);留空一行,最后是提交注釋。 接著,我們將創建另兩個提交對象,它們分別引用各自的上一個提交(作為其父提交對象): ~~~ $ echo 'second commit' | git commit-tree 0155eb -p fdf4fc3 cac0cab538b970a37ea1e769cbbde608743bc96d $ echo 'third commit' | git commit-tree 3c4e9c -p cac0cab 1a410efbd13591db07496601ebc7a059dd55cfe9 ~~~ 這三個提交對象分別指向之前創建的三個樹對象快照中的一個。 現在,如果對最后一個提交的 SHA-1 值運行?`git log`?命令,會出乎意料的發現,你已有一個貨真價實的、可由?`git log`?查看的 Git 提交歷史了: ~~~ $ git log --stat 1a410e commit 1a410efbd13591db07496601ebc7a059dd55cfe9 Author: Scott Chacon <schacon@gmail.com> Date: Fri May 22 18:15:24 2009 -0700 third commit bak/test.txt | 1 + 1 file changed, 1 insertion(+) commit cac0cab538b970a37ea1e769cbbde608743bc96d Author: Scott Chacon <schacon@gmail.com> Date: Fri May 22 18:14:29 2009 -0700 second commit new.txt | 1 + test.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d Author: Scott Chacon <schacon@gmail.com> Date: Fri May 22 18:09:34 2009 -0700 first commit test.txt | 1 + 1 file changed, 1 insertion(+) ~~~ 太神奇了: 就在剛才,你沒有借助任何上層命令,僅憑幾個底層操作便完成了一個 Git 提交歷史的創建。 這就是每次我們運行?`git add`?和?`git commit`?命令時, Git 所做的實質工作——將被改寫的文件保存為數據對象,更新暫存區,記錄樹對象,最后創建一個指明了頂層樹對象和父提交的提交對象。 這三種主要的 Git 對象——數據對象、樹對象、提交對象——最初均以單獨文件的形式保存在`.git/objects`?目錄下。 下面列出了目前示例目錄內的所有對象,輔以各自所保存內容的注釋: ~~~ $ find .git/objects -type f .git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2 .git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3 .git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2 .git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3 .git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1 .git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2 .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content' .git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1 .git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt .git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1 ~~~ 如果跟蹤所有的內部指針,將得到一個類似下面的對象關系圖: ![](https://box.kancloud.cn/2015-10-12_561bcbb54d492.png) Figure 10-3.?你的 Git 目錄下的所有對象。 ### [對象存儲](http://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1#對象存儲) 前文曾提及,在存儲內容時,會有個頭部信息一并被保存。 讓我們略花些時間來看看 Git 是如何存儲其對象的。 通過在 Ruby 腳本語言中交互式地演示,你將看到一個數據對象——本例中是字符串“what is up, doc?”——是如何被存儲的。 可以通過?`irb`?命令啟動 Ruby 的交互模式: ~~~ $ irb >> content = "what is up, doc?" => "what is up, doc?" ~~~ Git 以對象類型作為開頭來構造一個頭部信息,本例中是一個“blob”字符串。 接著 Git 會添加一個空格,隨后是數據內容的長度,最后是一個空字節(null byte): ~~~ >> header = "blob #{content.length}\0" => "blob 16\u0000" ~~~ Git 會將上述頭部信息和原始數據拼接起來,并計算出這條新內容的 SHA-1 校驗和。 在 Ruby 中可以這樣計算 SHA-1 值——先通過?`require`?命令導入 SHA-1 digest 庫,然后對目標字符串調用`Digest::SHA1.hexdigest()`: ~~~ >> store = header + content => "blob 16\u0000what is up, doc?" >> require 'digest/sha1' => true >> sha1 = Digest::SHA1.hexdigest(store) => "bd9dbf5aae1a3862dd1526723246b20206e5fc37" ~~~ Git 會通過 zlib 壓縮這條新內容。在 Ruby 中可以借助 zlib 庫做到這一點。 先導入相應的庫,然后對目標內容調用?`Zlib::Deflate.deflate()`: ~~~ >> require 'zlib' => true >> zlib_content = Zlib::Deflate.deflate(store) => "x\x9CK\xCA\xC9OR04c(\xCFH,Q\xC8,V(-\xD0QH\xC9O\xB6\a\x00_\x1C\a\x9D" ~~~ 最后,需要將這條經由 zlib 壓縮的內容寫入磁盤上的某個對象。 要先確定待寫入對象的路徑(SHA-1 值的前兩個字符作為子目錄名稱,后 38 個字符則作為子目錄內文件的名稱)。 如果該子目錄不存在,可以通過 Ruby 中的?`FileUtils.mkdir_p()`?函數來創建它。 接著,通過?`File.open()`打開這個文件。最后,對上一步中得到的文件句柄調用?`write()`?函數,以向目標文件寫入之前那條 zlib 壓縮過的內容: ~~~ >> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38] => ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37" >> require 'fileutils' => true >> FileUtils.mkdir_p(File.dirname(path)) => ".git/objects/bd" >> File.open(path, 'w') { |f| f.write zlib_content } => 32 ~~~ 就是這樣——你已創建了一個有效的 Git 數據對象。 所有的 Git 對象均以這種方式存儲,區別僅在于類型標識——另兩種對象類型的頭部信息以字符串“commit”或“tree”開頭,而不是“blob”。 另外,雖然數據對象的內容幾乎可以是任何東西,但提交對象和樹對象的內容卻有各自固定的格式。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看