自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

這才是真正的Git——Git內(nèi)部原理

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

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

[[317591]]

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

前言

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

后來(lái)我開(kāi)始沉下心,回歸一開(kāi)始接觸編程的時(shí)候,那時(shí)候?qū)W習(xí)一個(gè)知識(shí)都會(huì)深入一點(diǎn)去思考背后的原理。但這并不是說(shuō)掌握并會(huì)使用高級(jí)Api不重要,他們也非常重要,并且是日常工作中大部分時(shí)間都在使用的,快速掌握它們意味著高效學(xué)習(xí),可以快速的應(yīng)用在開(kāi)發(fā)生產(chǎn)上。

只是有時(shí)候知道一些底層的東西,可以更好的幫你理清思路,知道你真正在操作什么,不會(huì)迷失在Git大量的指令和參數(shù)上面。

Git是怎么儲(chǔ)存信息的

這里會(huì)用一個(gè)簡(jiǎn)單的例子讓大家直觀感受一下git是怎么儲(chǔ)存信息的。

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

  1. $ git init 
  2. $ echo '111' > a.txt 
  3. $ echo '222' > b.txt 
  4. $ git add *.txt 

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

  1. $ tree .git/objects 
  2. .git/objects 
  3. ├── 58 
  4. │ └── c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 
  5. ├── c2 
  6. │ └── 00906efd24ec5e783bee7f23b5d7c941b0c12c 
  7. ├── info 
  8. └── pack 

好奇的我們來(lái)看一下里面存的是什么東西

  1. $ cat .git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 
  2. xKOR0a044K% 

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

  1. $ git cat-file -t 58c9 
  2. blob 
  3. $ git cat-file -p 58c9 
  4. 111 

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

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

也就是說(shuō),我們此時(shí)的Git倉(cāng)庫(kù)是這樣子的:

 

這才是真正的Git——Git內(nèi)部原理

 

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

  1. $ git commit -am '[+] init' 
  2. $ tree .git/objects 
  3. .git/objects 
  4. ├── 0c 
  5. │ └── 96bfc59d0f02317d002ebbf8318f46c7e47ab2 
  6. ├── 4c 
  7. │ └── aaa1a9ae0b274fba9e3675f9ef071616e5b209 
  8. ... 

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

  1. $ git cat-file -t 4caaa1 
  2. tree 
  3. $ git cat-file -p 4caaa1 
  4. 100644 blob 58c9bdf9d017fcd178dc8c0...  a.txt 
  5. 100644 blob c200906efd24ec5e783bee7...  b.txt 

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

此時(shí)的Git倉(cāng)庫(kù)是這樣的:

 

這才是真正的Git——Git內(nèi)部原理
  1. $ git cat-file -t 0c96bf 
  2. commit 
  3. $ git cat-file -p 0c96bf 
  4. tree 4caaa1a9ae0b274fba9e3675f9ef071616e5b209 
  5. author lzane 李澤帆 1573302343 +0800 
  6. committer lzane 李澤帆 1573302343 +0800 
  7. [+] init 

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

此時(shí)我們?nèi)タ碐it倉(cāng)庫(kù)是這樣的:

 

這才是真正的Git——Git內(nèi)部原理

 

到這里我們就知道Git是怎么儲(chǔ)存一個(gè)提交的信息的了,那有同學(xué)就會(huì)問(wèn),我們平常接觸的分支信息儲(chǔ)存在哪里呢?

  1. $ cat .git/HEAD 
  2. ref: refs/heads/master 
  3.  
  4. $ cat .git/refs/heads/master 
  5. 0c96bfc59d0f02317d002ebbf8318f46c7e47ab2 

在Git倉(cāng)庫(kù)里面,HEAD、分支、普通的Tag可以簡(jiǎn)單的理解成是一個(gè)指針,指向?qū)?yīng)commit的SHA1值。

 

這才是真正的Git——Git內(nèi)部原理

 

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

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

Git的三個(gè)分區(qū)

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

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

 

這才是真正的Git——Git內(nèi)部原理

 

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

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

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

Git倉(cāng)庫(kù)( git repository ):由Git object記錄著每一次提交的快照,以及鏈?zhǔn)浇Y(jié)構(gòu)記錄的提交變更歷史。

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

 

這才是真正的Git——Git內(nèi)部原理

 

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

 

這才是真正的Git——Git內(nèi)部原理

 

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

 

這才是真正的Git——Git內(nèi)部原理

 

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

  • Git首先根據(jù)當(dāng)前的索引生產(chǎn)一個(gè)tree object,充當(dāng)新提交的一個(gè)快照。
  • 創(chuàng)建一個(gè)新的commit object,將這次commit的信息儲(chǔ)存起來(lái),并且parent指向上一個(gè)commit,組成一條鏈記錄變更歷史。
  • 將master分支的指針移到新的commit結(jié)點(diǎn)。

至此我們知道了Git的三個(gè)分區(qū)分別是什么以及他們的作用,以及歷史鏈?zhǔn)窃趺幢唤⑵饋?lái)的。**基本上Git的大部分指令就是在操作這三個(gè)分區(qū)以及這條鏈。 可以嘗試的思考一下git的各種命令,試一下你能不能夠在上圖將它們 “可視化”**出來(lái),這個(gè)很重要,建議嘗試一下。

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

一些有趣的問(wèn)題

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

問(wèn)題1:為什么要把文件的權(quán)限和文件名儲(chǔ)存在tree object里面而不是blob object呢?

想象一下修改一個(gè)文件的命名。

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

問(wèn)題2:每次commit,Git儲(chǔ)存的是全新的文件快照還是儲(chǔ)存文件的變更部分?

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

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

當(dāng)然,在涉及網(wǎng)絡(luò)傳輸或者Git倉(cāng)庫(kù)真的體積很大的時(shí)候,Git會(huì)有垃圾回收機(jī)制gc,不僅會(huì)清除無(wú)用的object,還會(huì)把已有的相似object打包壓縮。

問(wèn)題3:Git怎么保證歷史記錄不可篡改?

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

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

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2020-05-28 10:45:31

Git分支合并

2020-08-25 23:06:33

開(kāi)發(fā)技能代碼

2024-08-07 10:24:04

2018-07-27 10:39:13

對(duì)象存儲(chǔ)Git

2012-05-17 11:04:18

匈牙利命名法

2016-12-16 19:06:02

擴(kuò)展數(shù)據(jù)庫(kù)架構(gòu)

2015-02-11 09:35:09

iPhone6

2015-04-03 10:11:57

Windows 10免費(fèi)

2015-08-17 13:19:55

大數(shù)據(jù)

2021-01-19 05:44:53

危機(jī)面試技術(shù)

2025-04-02 02:12:00

用戶分析業(yè)務(wù)數(shù)據(jù)

2022-11-14 11:55:39

數(shù)據(jù)分析項(xiàng)目

2022-11-29 11:31:19

商品分析商品銷(xiāo)售庫(kù)存

2023-06-16 11:54:59

數(shù)據(jù)分析項(xiàng)目

2024-05-10 12:01:00

商品分析數(shù)據(jù)分析斷貨

2025-03-05 00:01:00

用戶分層平均數(shù)消費(fèi)

2022-03-10 15:55:44

元宇宙VRVR辦公

2021-12-15 07:24:56

SocketTCPUDP

2017-10-16 15:33:35

微信APP小程序

2021-02-05 15:01:41

GitLinux命令
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)