日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Git内部原理

發布時間:2025/3/8 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Git内部原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Git有什么特點?

?

  • fast,scalable,distributed revision control system(快速,可擴展的分布式版本控制系統)

?

    • 幾乎所有操作都是本地執行

    • 每一個clone都是整個生命周期的完整副本

?

  • the stupid content tracker(只是一個內容追蹤器)

?

?

    • Git追蹤的是內容而不是文件

    • 如果兩個文件的內容相同,無論是否在相同的目錄,Git在對象庫里只保存一份blob對象

  • Immutable(不可變性)

?

    • Git版本庫中存儲的數據對象均為不可變的,一旦創建數據對象并放入了數據庫中,它們便不可修改。這也意味著存儲在版本數據庫中的整個歷史也是不可變的。

?

  • Porcelain(高層命令)

?

    • init, add, commit, branch, merge.

?

  • Plumbing(底層命令)

?

    • hash-object, update-index, write-tree.

?

每一個Client端都可以是Server

?

Git Version Database是什么?

?

Git是一個內容尋址文件系統。這意味著,Git的核心部分是一個簡單的鍵值對數據庫(key-value data store)。你可以向該數據庫插入任意類型的內容,它會返回一個鍵值,通過該鍵值可以在任意時刻再次檢索該內容。而這些數據全部是存儲在objects目錄里。key是一個hash,hash前兩個字符用于命名子目錄,余下的38個字符則用作文件名。如果了解tree樹的朋友應該會想明白之所以這樣處理是因為檢索優化策略,提高文件系統效率(如果把太多的文件放入同一個目錄中,一些文件系統會變慢)。而這個hash的內容(即hash對應的Value)有四種對象類型,commit(提交),tree(目錄樹),blob(塊),tag(標簽)。

?

Git基本概念:

?

  • Content addressable filesystem(內容尋址文件系統)

  • Simple key-value data store(鍵值對數據)

  • Key:SHA-1散列(hash,哈希)

?

    • Everything is hash

    • 這是一個由40個十六進制字符(0-9和a-f)組成字符串

?

  • Value:binary files

?

    • Commit:Actual git commits(提交)

    • Tree:Directoy(目錄樹)

    • Blob:file content(文件內容)

?

note:可以理解成Commit = Tree + Blob的snapshot

?

什么是SHA-1:SHA-1(安全散列函數),是一種密碼散列函數,美國國家安全局設計,并由美國國家標準技術研究所發布為聯邦數據處理標準。SHA-1可以生成一個被稱為消息摘要的160位(20字節)散列值,散列值通常的呈現形式為40個十六進制數。用js來理解就是一個純函數,輸入一定輸出也一定,相同的輸入一定有相同的輸出。不相同的輸入一定有不同的輸出(不考慮碰撞 ,比彗星撞擊地球的概率還低)。

?

Git到底是如何工作呢?

?

我們知道最簡單的git flow主要有三步:

?

  • 在工作目錄中修改文件。

  • 暫存文件,將文件的快照放入暫存區域。

  • 提交更新,找到暫存區域的文件,將快照永久性存儲到Git倉庫目錄。

  • ?

    對應高層命令是這樣的:

    $?git?init
    $?git?add?.
    $?git?commit

    在我們看這三個命令到底做了什么之前,先來了解一下幾個概念:

    ?

    ?

    • Working Directory:工作區(工作目錄)

    • Stageing Area (Index):暫存區

    • Repository:倉庫區(本地倉庫)

    ?

    Git init

    ?

    我們先用Git init來初始化一個項目,并查看項目的目錄結構。

    $?git?init?demo1?&&?cd?demo1
    $?tree?.git
    .git
    ├──?HEAD
    ├──?config
    ├──?description
    ├──?hooks
    │???├──?applypatch-msg.sample
    │???├──?commit-msg.sample
    │???├──?fsmonitor-watchman.sample
    │???├──?post-update.sample
    │???├──?pre-applypatch.sample
    │???├──?pre-commit.sample
    │???├──?pre-push.sample
    │???├──?pre-rebase.sample
    │???├──?pre-receive.sample
    │???├──?prepare-commit-msg.sample
    │???└──?update.sample
    ├──?info
    │???└──?exclude
    ├──?objects
    │???├──?info
    │???└──?pack
    └──?refs
    ????├──?heads
    ????└──?tags

    description文件僅供GitWeb程序使用。config文件包含項目特有的配置選項。info目錄包含一個全局性排除文件,用以放置那些不希望被記錄在.gitignore文件中的忽略模式。hooks目錄包含客戶端或服務端的鉤子腳本,這些我們暫時都無需關心。最重要的是:HEAD文件、(尚待創建的)index文件,和objects目錄、refs目錄。這些條目是Git的核心組成部分。objects目錄存儲所有數據內容(hash);refs目錄存儲指向數據(分支)的提交對象的指針(commit hash);HEAD文件指示目前被檢出的分支(refs目錄內的分支名);index 文件保存暫存區信息(git ls-files --stage命令查看當前暫存區信息)。

    ?

    下面我們就用底層命令來實現git init指令(另創建一個demo2目錄)。

    ?

    mkdir -p參數是能直接創建一個不存在的目錄下的子目錄:

    $?mkdir?-p?.git/refs/heads?.git/refs/tags?.git/objects
    $?echo?'ref:?refs/heads/master'?>?.git/HEAD

    ?

    可以看到已經成功初始化了一個Git項目。

    ?

    git add

    ?

    $?echo?'hello?git'?>?index.txt
    $?git?add?index.txt

    執行完這兩句指令后我們再來看.git文件夾發生了什么變化(為了顯示效果,簡化目錄結構,之后tree 都忽略hooks文件夾)

    .git
    ├──?HEAD
    ├──?config
    ├──?description
    ├──?index
    ├──?info
    │???└──?exclude
    ├──?objects
    │???├──?8d
    │???│???└──?0e41234f24b6da002d962a26c2495ea16a425f
    │???├──?info
    │???└──?pack
    └──?refs
    ????├──?heads
    ????└──?tags

    可以看到多了一個index文件,并且objects目錄里面多了一個8d的文件夾,里面有一個0e41開頭的文件、那這個8d0e4這個是什么呢?其實這個就是index.txt文件內容的hash。還記得嘛,剛才寫入文件內容是hello git,我們來手動輸出這個內容的hash。

    $?echo?'hello?git'?|?git?hash-object?--stdin
    $?8d0e41234f24b6da002d962a26c2495ea16a425f

    可以通過cat-file命令從Git那里取回數據。為cat-file指定-p選項可指示該命令自動判斷內容的類型,并為我們顯示格式友好的內容:

    $?git?cat-file?-p?8d0e
    $?hello?git

    為cat-file指定-t選項可以查看文件的類型:

    $?git?cat-file?-t?8d0e
    $?blob

    git add做了兩件事情:

    ?

    • 文件內容做一個hash存成blob object

    • 把index放入到Staging Area

    ?

    當為index.txt創建一個對象的時候,git并不關心index.txt的文件名,git 只關心文件里面的內容。

    ?

    按照這個思路,我們用底層命令來實現一下git add指令。

    $?echo?'hello?git'?|?git?hash-object?-w?--stdin

    $?git?update-index?--add?--cacheinfo?100644?8d0e41234f24b6da002d962a26c2495ea16a425f?index.txt

    -w選項指示hash-object命令存儲數據對象;若不指定此選項,則該命令僅返回對應的鍵值。

    ?

    我們指定的文件模式為100644,表明這是一個普通文件。其他選擇包括:100755,表示一個可執行文件;120000,表示一個符號鏈接。

    ?

    ?

    因為并沒有去創建這個index.txt文件, 所以這邊提示已經刪除了,執行git checkout -- index.txt取出文件。

    ?

    ?

    可以看到已經成功用底層命名實現了git add的功能。

    ?

    到這里,我們自然就會有個疑問了,那文件名怎么辦?

    ?

    Git是通過tree對象來跟蹤文件的路徑名的。當使用git add命令時,git會給添加的文件內容創建一個blob對象,但是這個時候并不會創建tree對象。而只是更新索引,索引在.git/index中,它跟蹤文件的路徑名和相對應blob,每次執行git add 、git rm 、 git mv 的時候,git都會更新索引,我們可以通過命令git ls-files --stage來查看當前的索引信息。

    $?git?ls-files?--s
    $?100644?8d0e41234f24b6da002d962a26c2495ea16a425f?0?index.txt

    ?

    git commit

    ?

    執行git commit -m 'init-1'后,查看tree結構,發現object 多出了兩個文件:

    .git
    ├──?COMMIT_EDITMSG
    ├──?HEAD
    ├──?config
    ├──?description
    ├──?index
    ├──?info
    │???└──?exclude
    ├──?logs
    │???├──?HEAD
    │???└──?refs
    │???????└──?heads
    │???????????└──?master
    ├──?objects
    │???├──?75
    │???│???└──?0d7c0f7f998d3e2ce2d71ec801902f69bf6a39
    │???├──?88
    │???│???└──?bc066ebf3d864e34297f7051a0ded16e49813a
    │???├──?8d
    │???│???└──?0e41234f24b6da002d962a26c2495ea16a425f
    │???├──?info
    │???└──?pack
    └──?refs
    ????├──?heads
    ????│???└──?master
    ????└──?tags
    $?git?log
    $?commit?750d7c0f7f998d3e2ce2d71ec801902f69bf6a39?(HEAD?->?master)

    查看這個commit 的文件類型,可以看到這是一個commit:

    $?git?cat-file?-t?750d
    $?commit

    $?git?cat-file?-p?750d
    $?tree?88bc066ebf3d864e34297f7051a0ded16e49813a

    但是多出來的88bc是什么呢,其實就是當前目錄的tree對象,所以Git是在commit的時候才創建tree對象的(其實是把索引轉化成tree對象)。

    $?git?cat-file?-t?88bc
    $?tree

    $?git?cat-file?-p?88bc
    $?100644?blob?8d0e41234f24b6da002d962a26c2495ea16a425f??index.txt

    這個時候再看HEAD:

    $?cat?.git/HEAD
    $?ref:?refs/heads/master

    繼續查看refs/heads/master:

    $?cat?.git/refs/heads/master
    $?750d7c0f7f998d3e2ce2d71ec801902f69bf6a39

    所以整個指向關系就是:HEAD里面的內容是當前的ref,而當前ref的內容是commit hash,commit對象內容是tree hash,tree對象的內容是文件夾/文件信息,而blob對象存儲著文件的具體內容。這樣當完成一次提交的時候,整個狀態的對應關系也是確定的,所以說commit對象就是當前系統的snapshot。

    ?

    ?

    再來回顧下一次完整的提交流程:

    ?

    總結

    以上是生活随笔為你收集整理的Git内部原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。