<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          .git 目錄里到底包含了什么?

          共 10406字,需瀏覽 21分鐘

           ·

          2023-11-12 02:38

          我猜想,大多數(shù)閱讀這篇博客的人們幾乎每天都在使用 git。但你們有沒有真正看過 git 生成的 .git 文件夾中的內(nèi)容呢?現(xiàn)在,讓我們一同探索,并理解里面發(fā)生了什么。

          這篇文章是我最近一場演講的文字版。很遺憾,我無法分享那次講座的錄音鏈接 :(

          簡單地說,git 只是一堆通過文件名相互鏈接的文本文件。

          1. 開始初始化

          大家都知道,在開始使用 git 時,我們首先要做的就是執(zhí)行 git init。這個指令會顯示出一個我們都很熟悉的提示,特別是對于那些經(jīng)常啟動但又很快放棄的項目。

          Initialized empty Git repository in /home/meain/dev/src/git-talk/.git/

          接下來,我們來探索一下 .git 倉庫里都有些什么。

          $ tree .git

          .git
          ├── config
          ├── HEAD
          ├── hooks
          │ └── prepare-commit-msg.msample
          ├── objects
          │ ├── info
          │ └── pack
          └── refs
          ├── heads
          └── tags

          如上所示,創(chuàng)建了多個文件和文件夾。它們分別扮演什么角色呢?下面我們來逐一了解。

          • config 是一個 txt 文件,里面記錄了當前倉庫的 git 設置,如作者信息、文件模式等。
          • HEAD 表示倉庫的當前 head。根據(jù)你設置的默認分支,它可能是 refs/heads/masterrefs/heads/main 或其他你設定的名字。實際上,它指向 refs/heads 這個文件夾,并關聯(lián)了一個名為 master 的文件,但該文件目前還不存在。只有在你完成首次提交后,master 文件才會生成。
          • hooks 是一個特殊的目錄,其中包含了可以在 git 執(zhí)行任何操作前后運行的腳本。如果你對此感興趣,我在這里寫了一篇更詳細的文章,介紹了 git 鉤子的工作方式。(https://blog.meain.io/2019/making-sure-you-wont-commit-conflict-markers/)
          • objects 存放的是 git 的對象,比如關于倉庫中的文件、提交等的數(shù)據(jù)。我們稍后會對此進行深入探討。
          • refs 正如我們之前提到的,是用來存放引用的目錄。例如,refs/heads 里存放的是分支的引用,而 refs/tags 則存放的是標簽的引用。我們將進一步深入了解這些文件的內(nèi)容。

          1.1 加入一個新文件的操作

          了解了 .git 中的初始文件集后,我們來進行第一個操作,將內(nèi)容添加到 .git 目錄?,F(xiàn)在我們將創(chuàng)建并加入一個文件(此刻還未提交)。

          echo 'meain.io' > file
          git add file

          執(zhí)行后,變動如下:

          --- init       2023-07-02 15:14:00.584674816 +0530
          +++ add 2023-07-02 15:13:53.869525054 +0530
          @@ -3,7 +3,10 @@
          ├── HEAD
          ├── hooks
          │ └── prepare-commit-msg.msample
          +├── index
          ├── objects
          +│ ├── 4c
          +│ │ └── 5b58f323d7b459664b5d3fb9587048bb0296de
          │ ├── info
          │ └── pack
          └── refs

          此操作主要引發(fā)了兩個變化。首先,文件 index 被修改。index 是記錄當前暫存信息的地方,這表明名為 file 的文件已經(jīng)被加入到索引中。

          更為關鍵的是,新建了一個 objects/4c 文件夾,并在其中添加了 5b58f323d7b459664b5d3fb9587048bb0296de 文件。

          1.2 這個文件里都保存了什么內(nèi)容?

          為了深入理解 git 的存儲機制,我們先來看看這個文件具體包含了什么信息。

          $ file .git/objects/4c/5b58f323d7b459664b5d3fb9587048bb0296de
          .git/objects/4c/5b58f323d7b459664b5d3fb9587048bb0296de: zlib compressed data

          那么,這個用 zlib 壓縮的數(shù)據(jù)中具體包含了什么呢?

          $ zlib-flate -uncompress <.git/objects/4c/5b58f323d7b459664b5d3fb9587048bb0296de
          blob 9\0meain.io

          從結(jié)果可以看出,這個文件記錄了我們之前通過 git add 命令添加的 file 文件的相關信息,包括文件的類型、大小和內(nèi)容。具體地說,文件類型為 blob,大小為 9,內(nèi)容則是 meain.io。

          1.3 那個文件名是如何得來的?

          這確實是個有趣的問題。這個文件名其實是基于內(nèi)容的 sha1 哈希值生成的。通過對 zlib 壓縮的數(shù)據(jù)進行 sha1sum 處理,我們就可以得到這樣的文件名。

          $ zlib-flate -uncompress <.git/objects/4c/5b58f323d7b459664b5d3fb9587048bb0296de|sha1sum
          4c5b58f323d7b459664b5d3fb9587048bb0296de

          git 在存儲內(nèi)容時,會使用內(nèi)容的 sha1 哈希值,取其前兩個字符作為文件夾名(如 4c),余下的部分作為文件名。這種方式是為了確保在 objects 文件夾中不會有過多的文件,從而使文件系統(tǒng)保持高效。

          1.4 了解 git cat-file

          實際上,由于這是 git 中的一個更為重要的部分,git 提供了一個基礎命令 git cat-file,讓我們可以更直觀地查看它。通過 -t 參數(shù),你可以查詢對象的類型;使用 -s 參數(shù),你可以得知對象的大?。欢?-p 參數(shù)則能讓你直觀地查看對象的具體內(nèi)容。

          $ git cat-file -t 4c5b58f323d7b459664b5d3fb9587048bb0296de
          blob

          $ git cat-file -s 4c5b58f323d7b459664b5d3fb9587048bb0296de
          9

          $ git cat-file -p 4c5b58f323d7b459664b5d3fb9587048bb0296de
          meain.io

          2. 開始提交

          現(xiàn)在我們已經(jīng)了解當增加一個文件時,git 會有哪些變化,接下來,我們將通過進行"提交"操作來進行下一步探索。

          $ git commit -m 'Initial commit'
          [master (root-commit) 4c201df] Initial commit
          1 file changed, 1 insertion(+)
          create mode 100644 file

          以下是相關的變動:

          --- init        2023-07-02 15:14:00.584674816 +0530
          +++ commit 2023-07-02 15:33:28.536144046 +0530
          @@ -1,11 +1,25 @@
          .git
          +├── COMMIT_EDITMSG
          ├── config
          ├── HEAD
          ├── hooks
          │ └── prepare-commit-msg.msample
          ├── index
          +├── logs
          +│ ├── HEAD
          +│ └── refs
          +│ └── heads
          +│ └── master
          ├── objects
          +│ ├── 3c
          +│ │ └── 201df6a1c4d4c87177e30e93be1df8bfe2fe19
          │ ├── 4c
          │ │ └── 5b58f323d7b459664b5d3fb9587048bb0296de
          +│ ├── 62
          +│ │ └── 901ec0eca9faceb8fe0a9870b9b6cde75a9545
          │ ├── info
          │ └── pack
          └── refs
          ├── heads
          + │ └── master
          └── tags

          變化還真多。首先有一個新文件 COMMIT_EDITMSG,顧名思義,它保存了最新的提交信息。

          若直接運行 git commit 未帶 -m 參數(shù),git 會啟動一個編輯器并加載 COMMIT_EDITMSG 文件,方便用戶編輯提交信息。編輯完成后,git 就采用該文件內(nèi)容作為提交信息。

          此外,新增了一個 logs 目錄,git 通過它來記錄所有的提交變動。在此,你可以查看所有引用(refs)及 HEAD 的提交記錄。

          object 文件夾也發(fā)生了些變化,但首先,我希望你關注一下 refs/heads 目錄,里面現(xiàn)有一個 master 文件。毫無疑問,這就是 master 分支的引用。來,我們進一步了解其中的內(nèi)容。

          $ cat refs/heads/master
          3c201df6a1c4d4c87177e30e93be1df8bfe2fe19

          顯然,它是指向了一個新的對象。我們有方法查看這類對象,接著來試試。

          $ git cat-file -t 3c201df6a1c4d4c87177e30e93be1df8bfe2fe19
          commit

          $ git cat-file -p 3c201df6a1c4d4c87177e30e93be1df8bfe2fe19
          tree 62902ec0eca9faceb8fe0a9870b9b6cde75a9545
          author Abin Simon <[email protected]> 1688292123 +0530
          committer Abin Simon <[email protected]> 1688292123 +0530

          Initial commit

          你同樣可以使用 git cat-file -t refs/heads/master 命令來查看。

          看起來,這是我們未曾遇見的新對象類型:commit。從 commit 的內(nèi)容中,我們得知它包含了一個哈希值為 62902ec0eca9faceb8fe0a9870b9b6cde75a9545tree 對象,這與我們在提交時新加的對象相似。commit 還顯示了這次提交的作者和提交者信息,這里都是我。最后,它還展示了這次提交的信息。

          接下來,讓我們看一下 tree 對象中包含的內(nèi)容。

          $ git cat-file -t 62902ec0eca9faceb8fe0a9870b9b6cde75a9545
          tree

          $ git cat-file -p 62901ec0eca9faceb8fe0a9870b9b6cde75a9545
          100644 blob 4c5b58f323d7b459664b5d3fb9587048bb0296de file

          tree 對象中會通過其他 treeblob 對象的形式呈現(xiàn)工作目錄的狀態(tài)。在這個示例中,因為我們僅有一個名為 file 的文件,所以你只能見到一個對象。細看的話,你會發(fā)現(xiàn)這個文件指向了我們在執(zhí)行 git add file 時加入的那個初始對象。

          下面展示了一個更為成熟的倉庫中的 tree 示意。在 commit 對象關聯(lián)的 tree 對象中,嵌套有更多的 tree 對象,用以標識不同的文件夾。

          $ git cat-file -p 2e5e84c3ee1f7e4cb3f709ff5ca0ddfc259a8d04
          100644 blob 3cf56579491f151d82b384c211cf1971c300fbf8 .dockerignore
          100644 blob 02c348c202dd41f90e66cfeb36ebbd928677cff6 .gitattributes
          040000 tree ab2ba080c4c3e4f2bc643ae29d5040f85aca2551 .github
          100644 blob bdda0724b18c16e69b800e5e887ed2a8a210c936 .gitignore
          100644 blob 3a592bc0200af2fd5e3e9d2790038845f3a5cf9b CHANGELOG.md
          100644 blob 71a7a8c5aacbcaccf56740ce16a6c5544783d095 CODE_OF_CONDUCT.md
          100644 blob f433b1a53f5b830a205fd2df78e2b34974656c7b LICENSE
          100644 blob 413072d502db332006536e1af3fad0dce570e727 README.md
          100644 blob 1dd7ed99019efd6d872d5f6764115a86b5121ae9 SECURITY.md
          040000 tree 918756f1a4e5d648ae273801359c440c951555f9 build
          040000 tree 219a6e58af53f2e53b14b710a2dd8cbe9fea15f5 design
          040000 tree 5810c119dd4d9a1c033c38c12fae781aeffeafc1 docker
          040000 tree f09c5708676cdca6562f10e1f36c9cfd7ee45e07 src
          040000 tree e6e1595f412599d0627a9e634007fcb2e32b62e5 website

          3. 進行修改

          讓我們對文件進行修改,并觀察這樣做的結(jié)果。

          $ echo 'blog.meain.io' > file
          $ git commit -am 'Use blog link'
          [master 68ed5aa] Use blog link
          1 file changed, 1 insertion(+), 1 deletion(-)

          更改內(nèi)容如下:

          --- commit      2023-07-02 15:33:28.536144046 +0530
          +++ update 2023-07-02 15:47:20.841154907 +0530
          @@ -17,6 +17,12 @@
          │ │ └── 5b58f323d7b459664b5d3fb9587048bb0296de
          │ ├── 62
          │ │ └── 901ec0eca9faceb8fe0a9870b9b6cde75a9545
          +│ ├── 67
          +│ │ └── ed5aa2372445cf2249d85573ade1c0cbb312b1
          +│ ├── 8a
          +│ │ └── b377e2f9acd9eaca12e750a7d3cb345065049e
          +│ ├── e5
          +│ │ └── ec63cd761e6ab9d11e7dc2c4c2752d682b36e2
          │ ├── info
          │ └── pack
          └── refs

          總的來說,我們新增了三個對象。一個是含有文件新內(nèi)容的 blob 對象,還有一個是 tree 對象,以及一個 commit 對象。

          我們再次從 HEADrefs/heads/master 開始追蹤這些對象。

          $ git cat-file -p refs/heads/master
          tree 9ab377e2f9acd9eaca12e750a7d3cb345065049e
          parent 3c201df6a1c4d4c87177e30e93be1df8bfe2fe19
          author Abin Simon <[email protected]> 1688292975 +0530
          committer Abin Simon <[email protected]> 1688292975 +0530

          Use blog link

          $ git cat-file -p 9ab377e2f9acd9eaca12e750a7d3cb345065049e
          100644 blob e5ec63cd761e6ab9d11e7dc2c4c2752d682b36e2 file

          $ git cat-file -p e6ec63cd761e6ab9d11e7dc2c4c2752d682b36e2
          blog.meain.io

          仔細觀察的話,你會注意到 commit 對象現(xiàn)在有了一個額外的鍵名為 parent,它鏈接到上一個提交,因為當前提交是基于上一個提交創(chuàng)建的。

          4. 創(chuàng)建新分支

          現(xiàn)在我們需要創(chuàng)建一個新的分支。我們將使用 git branch fix-url 來完成這個操作。

          --- update      2023-07-02 15:47:20.841154907 +0530
          +++ branch 2023-07-02 15:55:25.165204941 +0530
          @@ -27,5 +28,6 @@
          │ └── pack
          └── refs
          ├── heads
          + │ ├── fix-url
          │ └── master
          └── tags
          ...

          此操作會在 `refs/heads` 目錄下加入一個新的文件。該文件的名稱就是我們新建的分支名,而內(nèi)容則是最新的提交標識 id。

          ```batch
          $ cat .git/refs/heads/fix-url
          68ed5aa2372445cf2249d85573ade1c0cbb312b1

          這基本上就是創(chuàng)建分支的全部內(nèi)容。在 git 中,分支是相當輕便的。另外,標簽的創(chuàng)建也是類似的操作,但它們是被創(chuàng)建在 refs/tags 目錄下。

          logs 目錄下也新增了一個文件,該文件用于記錄與 master 分支類似的提交歷史信息。

          5. 分支切換

          git 中進行分支切換實際上是讓 git 獲取某個提交的 tree 對象,并更新工作區(qū)中的文件,使其與其中記錄的狀態(tài)相匹配。在此例中,由于我們是從 master 分支切換到 fix-url 分支,而這兩個分支都指向同一個 commit 和它的 tree 對象,因此 git 在工作區(qū)的文件上并沒有任何更改。

          git checkout fix-url

          在進行分支切換時,.git 目錄中唯一發(fā)生的變化是 .git/HEAD 文件的內(nèi)容,現(xiàn)在它指向 fix-url 分支。

          $ cat .git/HEAD
          ref: refs/heads/fix-url

          既然我們在這里,我將進行一個提交操作。這將有助于我稍后展示合并的效果。

          $ echo 'https://blog.meain.io'>file
          $ git commit -am 'Fix url'

          6. 合并操作

          有三種主要的合并方法。

          1. 最簡單且直觀的是快進式合并。這種方式中,你只是更新一個分支的提交,使其指向另一個分支的提交。具體操作就是把 refs/heads/fix-url 中的哈希值復制到 refs/heads/master。
          2. 第二種是變基(rebase)合并。在這種方式中,我們首先將更改依次應用到主分支當前的提交上,然后進行類似于快進式的合并。
          3. 第三種是通過創(chuàng)建一個獨立的合并來合并兩個分支。這種方法與前兩者略有不同,因為它的提交對象會有兩個 parent 條目。我們稍后會詳細探討這種方法。

          首先,我們來看看合并前的 graph。

          git log --graph --oneline --all
          * 42c6318 (fix-url) Fix url
          * 67ed5aa (HEAD -> master) Use blog link
          * 3c201df Initial commit

          接下來進行合并:

          $ git merge fix-url # updates refs/heads/master to the hash in refs/heads/fix-url

          我們再來看看合并后的 graph。

          $ git log --graph --oneline --all
          * 42c6318 (HEAD -> master) (fix-url) Fix url
          * 67ed5aa Use blog link
          * 3c201df Initial commit

          7. 執(zhí)行推送

          在我們對本地 git 倉庫進行了一系列操縱之后,現(xiàn)在我們來看看進行推送時會發(fā)生什么事情。遠程 git 倉庫會接收哪些數(shù)據(jù)?

          為了演示這個過程,我首先創(chuàng)建了一個新的 git 倉庫作為這個倉庫的遠程連接。

          $ mkdir git-talk-2
          $ cd git-talk-2 && git init --bare

          $ cd ../git-talk && git remote add origin ../git-talk-2

          另外,添加新的遠程倉庫實際上是修改了配置文件,你可以在 .git/config 中查看這個變更。具體做了哪些修改,我鼓勵你自己去探索。

          接下來,執(zhí)行推送操作。

          $ git push origin master

          我們再來檢查一下本地倉庫發(fā)生了哪些改變。

          --- branch	2023-07-02 15:55:25.165204941 +0530
          +++ remote 2023-07-02 17:41:05.170923141 +0530
          @@ -22,12 +29,18 @@
          │ ├── e5
          │ │ └── ec63cd761e6ab9d11e7dc2c4c2752d682b36e2
          │ ├── info
          │ └── pack
          ├── ORIG_HEAD
          └── refs
          ├── heads
          │ ├── fix-url
          │ └── master
          + ├── remotes
          + │ └── origin
          + │ └── master
          └── tags

          你會發(fā)現(xiàn)新增了一個新的 refs/remotes 目錄,這是用來存儲不同遠程倉庫相關信息的。

          但是,實際上傳送到遠程 git 倉庫的數(shù)據(jù)是什么呢?那就是 objects 文件夾內(nèi)的所有數(shù)據(jù),以及你明確推送的 refs 下的分支和標簽。僅憑這些,遠程的 git 就能完整地構(gòu)建出你的所有 git 歷史記錄。

          參考資料:

          1. https://git-scm.com/book/en/v3/Git-Internals-Git-Objects
          2. https://matthew-brett.github.io/curious-git/reading_git_objects.html
          3. https://blog.meain.io/2020/bunch-of-git-stuff/

          瀏覽 1506
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色片一级视频 | 日韩成人精品免费播放 | 国内精品视频在线观看 | 人人色人人操人人 | www射www |