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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

git object 很大_这才是真正的Git——Git内部原理

發(fā)布時間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 git object 很大_这才是真正的Git——Git内部原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文以一個具體例子結合動圖介紹了Git的內(nèi)部原理,包括Git是什么儲存我們的代碼和變更歷史的、更改一個文件時,Git內(nèi)部是怎么變化的、Git這樣實現(xiàn)的好處等等。

TL;DR

本文以一個具體例子結合動圖介紹了Git的內(nèi)部原理,包括Git是什么儲存我們的代碼和變更歷史的、更改一個文件時,Git內(nèi)部是怎么變化的、Git這樣實現(xiàn)的有什么好處等等。

通過例子解釋清楚上面這張動圖,讓大家了解Git的內(nèi)部原理。如果你已經(jīng)能夠看懂這張圖了,下面的內(nèi)容可能對你來說會比較基礎。

本文是2019/11/24在深圳騰訊大廈2樓多功能廳舉辦的FCC前端分享會(freeCodeConf 2019 深圳站)上分享的文字版。

視頻:https://www.bilibili.com/video/av77252063

PPT:https://www.lzane.com/slide/git-under-the-hood

前言

近幾年技術發(fā)展十分迅猛,讓部分同學養(yǎng)成了一種學習知識停留在表面,只會調(diào)用一些指令的習慣。我們時常有一種“我會用這個技術、這個框架”的錯覺,等到真正遇到問題,才發(fā)現(xiàn)事情沒有那么簡單。

而Git也是一個大部分人都知道如何去使用它,知道有哪些命令,卻只有少部分人知道具體原理的東西。了解一些底層的東西,可以更好的幫你理清思路,知道你真正在操作什么,不會迷失在Git大量的指令和參數(shù)上面。

Git是怎么儲存信息的

這里會用一個簡單的例子讓大家直觀感受一下git是怎么儲存信息的。

首先我們先創(chuàng)建兩個文件

$?git?init
$?echo?'111'?>?a.txt
$?echo?'222'?>?b.txt
$?git?add?*.txt

Git會將整個數(shù)據(jù)庫儲存在.git/目錄下,如果你此時去查看.git/objects目錄,你會發(fā)現(xiàn)倉庫里面多了兩個object。

$?tree?.git/objects
.git/objects
├──?58
│???└──?c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
├──?c2
│???└──?00906efd24ec5e783bee7f23b5d7c941b0c12c
├──?info
└──?pack

好奇的我們來看一下里面存的是什么東西

$?cat?.git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
xKOR0a044K%

怎么是一串亂碼?這是因為Git將信息壓縮成二進制文件。但是不用擔心,因為Git也提供了一個能夠幫助你探索它的api git cat-file [-t] [-p],?-t可以查看object的類型,-p可以查看object儲存的具體內(nèi)容。

$?git?cat-file?-t?58c9
blob
$?git?cat-file?-p?58c9
111

可以發(fā)現(xiàn)這個object是一個blob類型的節(jié)點,他的內(nèi)容是111,也就是說這個object儲存著a.txt文件的內(nèi)容。

這里我們遇到第一種Git object,blob類型,它只儲存的是一個文件的內(nèi)容,不包括文件名等其他信息。然后將這些信息經(jīng)過SHA1哈希算法得到對應的哈希值
58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c,作為這個object在Git倉庫中的唯一身份證。

也就是說,我們此時的Git倉庫是這樣子的:

我們繼續(xù)探索,我們創(chuàng)建一個commit。

$?git?commit?-am?'[+]?init'
$?tree?.git/objects
.git/objects
├──?0c
│???└──?96bfc59d0f02317d002ebbf8318f46c7e47ab2
├──?4c
│???└──?aaa1a9ae0b274fba9e3675f9ef071616e5b209
...

我們會發(fā)現(xiàn)當我們commit完成之后,Git倉庫里面多出來兩個object。同樣使用cat-file命令,我們看看它們分別是什么類型以及具體的內(nèi)容是什么。

$?git?cat-file?-t?4caaa1
tree
$?git?cat-file?-p?4caaa1
100644?blob?58c9bdf9d017fcd178dc8c0...?????a.txt
100644?blob?c200906efd24ec5e783bee7...????b.txt

這里我們遇到了第二種Git object類型——tree,它將當前的目錄結構打了一個快照。從它儲存的內(nèi)容來看可以發(fā)現(xiàn)它儲存了一個目錄結構(類似于文件夾),以及每一個文件(或者子文件夾)的權限、類型、對應的身份證(SHA1值)、以及文件名。

此時的Git倉庫是這樣的:

$?git?cat-file?-t?0c96bf
commit
$?git?cat-file?-p?0c96bf
tree?4caaa1a9ae0b274fba9e3675f9ef071616e5b209
author?lzane?李澤帆??1573302343?+0800
committer?lzane?李澤帆??1573302343?+0800
[+]?init

接著我們發(fā)現(xiàn)了第三種Git object類型——commit,它儲存的是一個提交的信息,包括對應目錄結構的快照tree的哈希值,上一個提交的哈希值(這里由于是第一個提交,所以沒有父節(jié)點。在一個merge提交中還會出現(xiàn)多個父節(jié)點),提交的作者以及提交的具體時間,最后是該提交的信息。

此時我們?nèi)タ碐it倉庫是這樣的:

到這里我們就知道Git是怎么儲存一個提交的信息的了,那有同學就會問,我們平常接觸的分支信息儲存在哪里呢?

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

$?cat?.git/refs/heads/master
0c96bfc59d0f02317d002ebbf8318f46c7e47ab2

在Git倉庫里面,HEAD、分支、普通的Tag可以簡單的理解成是一個指針,指向?qū)猚ommit的SHA1值。

其實還有第四種Git object,類型是tag,在添加含附注的tag(git tag -a)的時候會新建,這里不詳細介紹,有興趣的朋友按照上文中的方法可以深入探究。

至此我們知道了Git是什么儲存一個文件的內(nèi)容、目錄結構、commit信息和分支的。其本質(zhì)上是一個key-value的數(shù)據(jù)庫加上默克爾樹形成的有向無環(huán)圖(DAG)。這里可以蹭一下區(qū)塊鏈的熱度,區(qū)塊鏈的數(shù)據(jù)結構也使用了默克爾樹。

Git的三個分區(qū)

接下來我們來看一下Git的三個分區(qū)(工作目錄、Index 索引區(qū)域、Git倉庫),以及Git變更記錄是怎么形成的。了解這三個分區(qū)和Git鏈的內(nèi)部原理之后可以對Git的眾多指令有一個“可視化”的理解,不會再經(jīng)常搞混。

接著上面的例子,目前的倉庫狀態(tài)如下:

這里有三個區(qū)域,他們所儲存的信息分別是:

  • 工作目錄 ( working directory ):操作系統(tǒng)上的文件,所有代碼開發(fā)編輯都在這上面完成。

  • 索引( index or staging area ):可以理解為一個暫存區(qū)域,這里面的代碼會在下一次commit被提交到Git倉庫。

  • Git倉庫( git repository ):由Git object記錄著每一次提交的快照,以及鏈式結構記錄的提交變更歷史。

我們來看一下更新一個文件的內(nèi)容這個過程會發(fā)生什么事。

運行echo "333" > a.txt將a.txt的內(nèi)容從111修改成333,此時如上圖可以看到,此時索引區(qū)域和git倉庫沒有任何變化。

運行git add a.txt將a.txt加入到索引區(qū)域,此時如上圖所示,git在倉庫里面新建了一個blob object,儲存了新的文件內(nèi)容。并且更新了索引將a.txt指向了新建的blob object。

運行git commit -m 'update'提交這次修改。如上圖所示

  • Git首先根據(jù)當前的索引生產(chǎn)一個tree object,充當新提交的一個快照。

  • 創(chuàng)建一個新的commit object,將這次commit的信息儲存起來,并且parent指向上一個commit,組成一條鏈記錄變更歷史。

  • 將master分支的指針移到新的commit結點。

  • 至此我們知道了Git的三個分區(qū)分別是什么以及他們的作用,以及歷史鏈是怎么被建立起來的。基本上Git的大部分指令就是在操作這三個分區(qū)以及這條鏈。可以嘗試的思考一下git的各種命令,試一下你能不能夠在上圖將它們“可視化”出來,這個很重要,建議嘗試一下。

    如果不能很好的將日常使用的指令“可視化”出來,推薦閱讀?圖解Git

    一些有趣的問題

    有興趣的同學可以繼續(xù)閱讀,這部分不是文章的主要內(nèi)容

    問題1:為什么要把文件的權限和文件名儲存在tree object里面而不是blob object呢?

    想象一下修改一個文件的命名。

    如果將文件名保存在blob里面,那么Git只能多復制一份原始內(nèi)容形成一個新的blob object。而Git的實現(xiàn)方法只需要創(chuàng)建一個新的tree object將對應的文件名更改成新的即可,原本的blob object可以復用,節(jié)約了空間。

    問題2:每次commit,Git儲存的是全新的文件快照還是儲存文件的變更部分?

    由上面的例子我們可以看到,Git儲存的是全新的文件快照,而不是文件的變更記錄。也就是說,就算你只是在文件中添加一行,Git也會新建一個全新的blob object。那這樣子是不是很浪費空間呢?

    這其實是Git在空間和時間上的一個取舍,思考一下你要checkout一個commit,或?qū)Ρ葍蓚€commit之間的差異。如果Git儲存的是問卷的變更部分,那么為了拿到一個commit的內(nèi)容,Git都只能從第一個commit開始,然后一直計算變更,直到目標commit,這會花費很長時間。而相反,Git采用的儲存全新文件快照的方法能使這個操作變得很快,直接從快照里面拿取內(nèi)容就行了。

    當然,在涉及網(wǎng)絡傳輸或者Git倉庫真的體積很大的時候,Git會有垃圾回收機制gc,不僅會清除無用的object,還會把已有的相似object打包壓縮。

    問題3:Git怎么保證歷史記錄不可篡改?

    通過SHA1哈希算法和哈系樹來保證。假設你偷偷修改了歷史變更記錄上一個文件的內(nèi)容,那么這個問卷的blob object的SHA1哈希值就變了,與之相關的tree object的SHA1也需要改變,commit的SHA1也要變,這個commit之后的所有commit SHA1值也要跟著改變。又由于Git是分布式系統(tǒng),即所有人都有一份完整歷史的Git倉庫,所以所有人都能很輕松的發(fā)現(xiàn)存在問題。


    希望大家讀完有所收獲,下一篇文章會寫一些我日常工作中覺得比較實用的Git技巧、經(jīng)常被問到的問題、以及發(fā)生一些事故時的處理方法。

    參考

    • Scott Chacon, Ben Straub - Pro Git-Apress (2014)?免費,有興趣繼續(xù)深入的同學推薦閱讀這本書

    • Jon Loeliger, Matthew McCullough - Version Control with Git, 2nd Edition - O'Reilly Media (2012)?作為上面那本書的補充

    總結

    以上是生活随笔為你收集整理的git object 很大_这才是真正的Git——Git内部原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。