本文由雲+社區發表javascript
做者:騰訊工蜂用戶:王二衛java
從不同的視角瞭解git,以便更好的使用gitgit
git 是一個內容尋址的文件系統,其核心部分是一個簡單的鍵值對數據庫(key-value data store),能夠向該數據庫插入任意類型的內容,它會返回一個40位長的哈希鍵值。並在此基礎上提供了一個版本控制系統的用戶界面。github
git 版本庫其實只是一個簡單的數據庫,其中包含全部用來維護與管理項目的修訂版本和歷史信息。其不一樣於subversion,git版本庫不只提供版本庫中全部文件的完整副本,還提供版本庫自己的副本。在git版本庫中,git維護兩個主要數據結構:對象庫(object store),索引(index)。web
從總體來看,一個項目的git倉庫,就如一張帶節點的漁網(該漁網是一張有向網),隨着項目的不斷推動,該漁網也將不斷的向四周擴散。數據庫
漁網上的節點就像一個個的提交,從某一個正常的節點都能漫遊至項目最開始的起點。而分支就如該網上不一樣節點上的一個特殊標記,分支的演變就是該標記不斷的移至其餘節點。 分支的合併,根據合併方式的不一樣,使得這一張網的交叉緊密度愈來愈高。緩存
對象庫是git版本庫實現的心臟,包含四種類型:服務器
塊(blob,binary lare object),文件的每個版本表示爲一個塊。一個blob被視爲一個存儲任意數據,且內部結構被程序忽略的變量或文件的黑盒。一個blob保存一個文件的數據,但不包含任何關於這個文件的元數據(Metadata,描述數據的數據)。網絡
目錄樹(tree), 一個目錄樹對象表明一層目錄信息。它記錄blob標識符、路徑名和在一個目錄裏全部文件的一的元數據。它也能夠遞歸引用其餘目錄樹或子樹對象,從而創建一個包含文件和子目錄的完整層次結構。數據結構
提交(commit),一個提交對象保存版本庫中每一次變化的元數據,每個提交對象指向一個目錄樹對象,這個樹對象在一張完整的快照中補貨提交時版本庫的狀態。
標籤(tag) ,一個標籤對象分配一個可讀的名字給一個特定的對象,一般是一個提交對象。
爲了有效的利用磁盤空間和網絡帶寬名,git把對象壓縮並存儲在打包文件(pack file)裏,這些文件也在對象庫裏。
索引是一個臨時的、動態的二進制文件,不包含任何文件內容,它僅僅追蹤你想要提交的那些內容。使得開發的推動與提交的變動之間可以分離開來。
引用(ref)是一個保存SHA-1值的文件,該文件的名字指針來替代原始的SHA-1值,通常指向提交對象。本地分支名稱、遠程跟蹤分支名稱和標籤名都是引用。
.git/refs .git/refs/heads .git/refs/tags
$ echo 「1a410efbd13591db07496601ebc7a059dd55cfe9」 > .git/refs/heads/master
如今能夠經過新建的引用來代替SHA-1的值: $ git log —pretty=oneline master 1a410efbd13591db07496601ebc7a059dd55cfe9 third commit cac0cab538b970a37ea1e769cbbde608743bc96d second commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
不提倡直接編輯引用文件,能夠經過update-ref
更新某個引用 $ git update-ref refs/heads/master 1a410efbd13591db07496601ebc7a059dd55cfe9
好比新建一個分支(git分支的本質:一個指向某一系列提交之首的指針或引用) $git update-ref refs/heads/feature-zhangsan cac0ca
符號引用(symbolic reference),間接指向git對象,其實際也是一個引用,不像普通引用那樣包含一個SHA-1值,它是一個指向其餘引用的指針。 git自動維護幾個用於特定目的的特殊符號引用,這些引用能夠在使用提交的任何地方使用。
若執行 $ git checkout test,git會這樣更新HEAD文件 ref:refs/heads/test
若是你添加了一個遠程版本庫並對其執行過推送操做,Git 會記錄下最近一次推送操做時每個分支所對應的值,並保存在 refs/remotes 目錄下。 如:$cat .git/refs/remotes/origin/master ca82a6dff817ec66f44342007202690a93763949 發現添加的遠程origin遠程庫的master分支鎖對應的SHA-1值,就是最近一次與服務器通訊時master分支所對應的SHA-1值。 遠程引用和分支(位於 refs/heads 目錄下的引用)之間最主要的區別在於,遠程引用是隻讀的。 雖然能夠git checkout
到某個遠程引用,可是 Git 並不會將 HEAD 引用指向該遠程引用。 所以,你永遠不能經過commit
命令來更新遠程引用。 Git 將這些遠程引用做爲記錄遠程服務器上各分支最後已知位置狀態的書籤來管理。
準備工做:建立一個沒有任何文件的git初始庫 $ git init test Initialized empty Git repository in /data/work/test/test/.git/
$ echo ‘test conten’ | git hash-object -w —stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
-w 執行寫入數據庫操做,若不指定該選項,只會返回hash,不會寫入數據庫。 --stdin 標準輸入輸出讀取 默認存入是blob類型,經過-t 參數指定
$ find .git/objects/ -type f .git/objects//d6/870460b4b4aece5915caf5c68d12f560a9fe3e4
頭部信息-對象類型(blob或tree或commit)+一個空格+數據內容長度+一個空字節 git 會經過zlib將文件內容和頭部信息拼接一塊兒的內容進行壓縮寫入磁盤某個對象,並用計算出的sha-1值的前兩個字符串做爲目錄名稱,後38個字符串做爲子目錄內文件的名稱。
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
$ echo ‘version 1’ > test.txt
$ git hash-object -w ./test.txt 83baae61804e65cc73a7201a7252750c76066a30
$ echo ‘version 2’ > test.txt
$ git hash-object -w ./test.txt 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
此時數據庫已經存儲了test.txt兩個不一樣的版本,以下:
$ find .git/objects/ -type f .git/objects//1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a .git/objects//83/baae61804e65cc73a7201a7252750c76066a30
能夠經過cat-file -p
查看內容,以上都是數據(blob)對象。可使用 cat-file -t
查看。
樹對像(tree object) 解決文件名和目錄保存問題。一個樹對象包含了一條或多條樹對象記錄,每條記錄包含一個指向數據對象或子樹對象的sha-1指針,以及相應的模式/類型/文件信息。
以下所示:
$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README 100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile 040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb
master^{tree}指向master分支最新提交所指的樹對象。 數據對象幾種類型
$ git update-index —add —cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt
建立第一個樹
$ git write-tree 將暫存區內容生成一個樹對象,並輸出樹對象SHA-1 d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ echo ‘new file’ > new.txt
$echo ‘test file2’ > test.txt
$git update-index —cacheinfo 100644 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
$ git update-index test.txt
$ git update-index —add new.txt
建立第二個樹
$ git write-tree 0155eb4229851634a0f03eb265b69f5a2d56f341
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341 100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt 100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
此時發現,第一個樹丟了,並無跟第一個樹有關係,經過 read-tree進行連接 $ 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
經過commit對象將這些樹對象串起來。 建立第一個提交
$ echo ‘first commit’ | git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 fdf4fc3344e67ab068f836878b6c4951e3b15f3d
建立第二個提交
$ echo ‘second commit’ | git commit-tree 0155eb -p fdf4fc3 cac0cab538b970a37ea1e769cbbde608743bc96d
建立第三個提交
$ echo ‘third commit’ | git commit-tree 3c4e9c -p cac0cab 1a410efbd13591db07496601ebc7a059dd55cfe9
版本庫目錄變化` **$ 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 `提交版本圖
沒有執行read-tree $ git log --stat 92387 commit 923879712b02f980a2edbe1cee315d883ee72503 Author: erweiwang <erweiwang@tencent.com> Date: Tue Jul 17 15:55:53 2018 +0800 second commit new.txt | 1 + test.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) commit e624badd39a25484a08ae74231be65ea50a0fe32 Author: erweiwang <erweiwang@tencent.com> Date: Tue Jul 17 15:54:20 2018 +0800 first commit test.txt | 1 + 1 file changed, 1 insertion(+)
Git 最初向磁盤中存儲對象時所使用的格式被稱爲「鬆散(loose)」對象格式。 可是,Git 會時不時地將多個這些對象打包成一個稱爲「包文件(packfile)」的二進制文件,以節省空間和提升效率。 當版本庫中有太多的鬆散對象,或者你手動執行 git gc 命令,或者你向遠程服務器執行推送時,Git都會這樣作。
git 打包對象時,會查找命名及大小相近的文件,並只保存文件不一樣版本之間的差別內容和文件最新版本的完整內容。
引用規格的格式由一個可選的 + 號和緊隨其後的 : 組成,其中 是一個模式(pattern),表明遠程版本庫中的引用; 是那些遠程引用在本地所對應的位置。 + 號告訴 Git 即便在不能快進的狀況下也要(強制)更新引用。
[remote "origin"] url = https://github.com/schacon/simplegit-progit fetch = +refs/heads/*:refs/remotes/origin/*
若是想讓git每次只拉取遠程master分支,而不是全部分支,能夠將引用規格那一行修改成: fetch = +refs/heads/master:refs/remotes/origin/master
執行git clone後,
在遍歷過程當中,如果未能直接找到(非鬆散對象)某些對象,會去替代版本庫或某個包文件獲取。
爲了上傳數據至遠端,Git 使用 send-pack 和 receive-pack 進程。 運行在客戶端上的 send-pack 進程鏈接到遠端運行的 receive-pack 進程。
git gc —auto //整理鬆散對象並放置包文件,將多個包文件合併爲一個大的包文件,移除與任何提交不相關的陳舊對象
$ git branch recover-branch ab1afef
當引用日誌所在目錄.git/logs/ 被不當心清空時
$ git fsck —full Checking object directories: 100% (256/256), done. Checking objects: 100% (18/18), done. dangling blob d670460b4b4aece5915caf5c68d12f560a9fe3e4 dangling commit ab1afef80fac8e34258ff41fc1b867c702daa24b dangling tree aea790b9a58f6cf6f2804eeac9f0abbe9631e4c9 dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293
該操做使用須謹慎,會致使提交歷史不被重寫。應用場景,必須對已上庫的某些文件(因文件太大或保密信息)進行完全移除可使用。
此文已由騰訊雲+社區在各渠道發佈
獲取更多新鮮技術乾貨,能夠關注咱們騰訊雲技術社區-雲加社區官方號及知乎機構號