簡(jiǎn)單地說(shuō),Git 究竟是怎樣的一個(gè)系統(tǒng)呢?請(qǐng)注意接下來(lái)的內(nèi)容非常重要,若你理解了 Git 的思想和基本工作原理,用起來(lái)就會(huì)知其所以然,游刃有余。
在學(xué)習(xí) Git 時(shí),請(qǐng)盡量理清你對(duì)其它版本管理系統(tǒng)已有的認(rèn)識(shí),如 CVS、Subversion 或 Perforce, 這樣能幫助你使用工具時(shí)避免發(fā)生混淆。
盡管 Git 用起來(lái)與其它的版本控制系統(tǒng)非常相似, 但它在對(duì)信息的存儲(chǔ)和認(rèn)知方式上卻有很大差異,理解這些差異將有助于避免使用中的困惑。
Git 初始化代碼倉(cāng)庫(kù)
執(zhí)行完成了 git init 命令,究竟做了什么呢?
執(zhí)行完成如下命令之后,我們可以得到下圖所示的內(nèi)容,右側(cè)的就是 Git 為我們創(chuàng)建的代碼倉(cāng)庫(kù),其中包含了用于版本管理所需要的內(nèi)容。
# 左邊執(zhí)行
$ mkdir git-demo
$ cd git-demo && git init
$ rm -rf .git/hooks/*.sample
# 右邊執(zhí)行
$ watch -n 1 -d find .
我們這里可以一起看下生成的 .git 目錄的結(jié)構(gòu)如何:
? tree .git
.git
├── HEAD
├── config
├── description
├── hooks
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
.git/config - 當(dāng)前代碼倉(cāng)庫(kù)本地的配置文件
- 本地配置文件(.git/config)和全局配置文件(~/.gitconfig)
- 通過(guò)執(zhí)行如下命令,可以將用戶(hù)配置記錄到本地代碼倉(cāng)庫(kù)的配置文件中去
- git config user.name "demo"
- git config user.email "[email protected]"
? cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
name = demo
email = [email protected]
.git/objects - 當(dāng)前代碼倉(cāng)庫(kù)代碼的存儲(chǔ)位置
- blob 類(lèi)型
- commit 類(lèi)型
- tree 類(lèi)型
# 均無(wú)內(nèi)容
? ll .git/objects
total 0
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 info
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 pack
? ll .git/objects/info
? ll .git/objects/pack
.git/info - 當(dāng)前倉(cāng)庫(kù)的排除等信息
? cat ./.git/info/exclude
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
.git/hooks - 當(dāng)前代碼倉(cāng)庫(kù)默認(rèn)鉤子腳本
./.git/hooks/commit-msg.sample
./.git/hooks/pre-rebase.sample
./.git/hooks/pre-commit.sample
./.git/hooks/applypatch-msg.sample
./.git/hooks/fsmonitor-watchman.sample
./.git/hooks/pre-receive.sample
./.git/hooks/prepare-commit-msg.sample
./.git/hooks/post-update.sample
./.git/hooks/pre-merge-commit.sample
./.git/hooks/pre-applypatch.sample
./.git/hooks/pre-push.sample
./.git/hooks/update.sample
.git/HEAD - 當(dāng)前代碼倉(cāng)庫(kù)的分支指針
? cat .git/HEAD
ref: refs/heads/master
.git/refs - 當(dāng)前代碼倉(cāng)庫(kù)的頭指針
# 均無(wú)內(nèi)容
? ll .git/refs
total 0
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 heads
drwxr-xr-x 2 escape staff 64B Nov 23 20:39 tags
? ll .git/refs/heads
? ll .git/refs/tags
.git/description - 當(dāng)前代碼倉(cāng)庫(kù)的描述信息
? cat .git/description
Unnamed repository; edit this file 'description' to name the repository.
add 之后發(fā)生了什么
執(zhí)行完成了 git add 命令,究竟做了什么呢?
執(zhí)行完成如下命令之后,我們可以得到下圖所示的內(nèi)容,我們發(fā)現(xiàn)右側(cè)新增了一個(gè)文件,但是 Git 目錄里面的內(nèi)容絲毫沒(méi)有變化。這是因?yàn)椋覀儸F(xiàn)在執(zhí)行的修改默認(rèn)是放在工作區(qū)的,而工作區(qū)里面的修改不歸 Git 目錄去管理。
而當(dāng)我們執(zhí)行 git status 命令的時(shí)候,Git 又可以識(shí)別出來(lái)現(xiàn)在工作區(qū)新增了一個(gè)文件,這里怎么做到的呢?—— 詳見(jiàn)[理解 blob 對(duì)象和 SHA1]部分
而當(dāng)我們執(zhí)行 git add 命令讓 Git 幫助我們管理文件的時(shí)候,發(fā)現(xiàn)右側(cè)新增了一個(gè)目錄和兩個(gè)文件,分別是 8d 目錄、index 和 0e41.. 文件。
# 左邊執(zhí)行
$ echo "hello git" > helle.txt
$ git status
$ git add hello.txt
# 右邊執(zhí)行
$ watch -n 1 -d find .
我們這里重點(diǎn)看下,生成的 8d 這個(gè)目錄以及下面的文件。而其名稱(chēng)的由來(lái)是因?yàn)?Git 對(duì)其進(jìn)行了一個(gè)叫做 SHA1 的 Hash 算法,用于將文件內(nèi)容或者字符串變成這么一串加密的字符。
# 查看 objects 的文件類(lèi)型
$ git cat-file -t 8d0e41
blob
# 查看 objects 的文件內(nèi)容
$ git cat-file -p 8d0e41
hello git
# 查看 objects 的文件大小
$ git cat-file -s 8d0e41
10
# 拼裝起來(lái)
blob 10\\0hello git
現(xiàn)在我們就知道了,執(zhí)行 git add 命令將文件從工作區(qū)添加到暫存區(qū)里面,Git 會(huì)把幫助我們生成一些 Git 的對(duì)象,它存儲(chǔ)的是文件的內(nèi)容和文件類(lèi)型并不存儲(chǔ)文件名稱(chēng)。
微信搜索公眾號(hào):架構(gòu)師指南,回復(fù):架構(gòu)師 領(lǐng)取資料 。
為了驗(yàn)證我們上述的說(shuō)法,我們可以添加同樣的內(nèi)容到另一個(gè)文件,然后進(jìn)行提交,來(lái)觀察 .git 目錄的變化。我們發(fā)現(xiàn),右側(cè)的 objects 目錄并沒(méi)有新增目錄和文件。這就可以證明,blob 類(lèi)型的 object 只存儲(chǔ)的是文件的內(nèi)容,如果兩個(gè)文件的內(nèi)容一致的話,則只需要存儲(chǔ)一個(gè) object 即可。
話說(shuō)這里 object 為什么沒(méi)有存儲(chǔ)文件名稱(chēng)呢?這里因?yàn)?SHA1 的 Hash 算法計(jì)算哈希的時(shí)候,本身就不包括文件名稱(chēng),所以取什么名稱(chēng)都是無(wú)所謂的。那問(wèn)題來(lái)了,就是文件名的信息都存儲(chǔ)到哪里去了呢?—— 詳見(jiàn)[理解 blob 對(duì)象和 SHA1]部分
# 左邊執(zhí)行
$ echo "hello git" > tmp.txt
$ git add tmp.txt
# 右邊執(zhí)行
$ watch -n 1 -d find .
理解 blob 對(duì)象和 SHA1
了解 Git 的 blob 對(duì)象和 SHA1 之前的關(guān)系和對(duì)應(yīng)計(jì)算!
Hash 算法是把任意長(zhǎng)度的輸入通過(guò)散列算法變化成固定長(zhǎng)度的輸出,根據(jù)算法的不同,生成的長(zhǎng)度也有所不同。
Hash 算法:
- MD5 - 128bit - 不安全 - 文件校驗(yàn)
- SHA1 - 160bit(40位) - 不安全 - Git 存儲(chǔ)
- SHA256 - 256bit- 安全 - Docker 鏡像
- SHA512 - 512bit - 安全
但是,當(dāng)我們使用工具對(duì)上述文件內(nèi)容進(jìn)行 SHA1 計(jì)算的時(shí)候,會(huì)發(fā)現(xiàn)并沒(méi)有我們?cè)?.git 目錄里面看到的那樣,這是為什么呢?
? echo "hello git" | shasum
d6a96ae3b442218a91512b9e1c57b9578b487a0b -
這里因?yàn)?Git 工具的計(jì)算方式,是使用類(lèi)型 長(zhǎng)度 \\0 內(nèi)容的方式進(jìn)行計(jì)算的。這里,我們算了下文件內(nèi)容只有九位,但是這里是十位,這里因?yàn)閮?nèi)容里面有換行符的存在導(dǎo)致的。現(xiàn)在我們就可以使用 git cat-file 命令來(lái)拼裝 Git 工具存儲(chǔ)的完整內(nèi)容了。
? ls -lh hello.txt
-rw-r--r-- 1 escape staff 10B Nov 23 21:12 hello.txt
? echo "blob 10\\0hello git" | shasum
8d0e41234f24b6da002d962a26c2495ea16a425f -
# 拼裝起來(lái)
blob 10\\0hello git
當(dāng)我們使用 cat 命令來(lái)查看 object 對(duì)象里面的內(nèi)容的時(shí)候,發(fā)現(xiàn)看著像是一串亂碼。其實(shí)這是 Git 工具將文件的原始內(nèi)容進(jìn)行一個(gè)壓縮,然后再存儲(chǔ)到 object 對(duì)象里面。奇怪的是,我們發(fā)現(xiàn)壓縮之后的內(nèi)容反而比原始內(nèi)容還大!
這是因?yàn)槠溥M(jìn)行了壓縮,存儲(chǔ)了一些壓縮相關(guān)的信息。上例所示的比原始文件大,是因?yàn)槲覀儎?chuàng)建的內(nèi)容實(shí)在是太小了。當(dāng)我們常見(jiàn)一個(gè)比較大的文件時(shí),就會(huì)看到壓縮之后的文件大小遠(yuǎn)小于原始文件的。
? cat .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
xKOR04`HWH,6A%
? ls -lh .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
-r--r--r-- 1 escape staff 26B Nov 23 21:36 .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
? file .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
.git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f: VAX COFF executable not stripped - version 16694
其實(shí),我們這里也是可以通過(guò) Python 代碼來(lái)獲取二進(jìn)制 object 對(duì)象的內(nèi)容的。
import zlib
contents = open('0e41234f24b6da002d962a26c2495ea16a425f', 'rb').read()
zlib.decompress(contents)
聊聊工作區(qū)和暫存區(qū)
聊聊工作區(qū)和暫存區(qū),以及文件如何在工作區(qū)和緩存區(qū)之間同步的問(wèn)題。
之前的章節(jié)我們也聊到了,當(dāng)我們執(zhí)行 git status 命令的時(shí)候,Git 工具怎么知道我們有一個(gè)文件沒(méi)有追蹤,以及文件名的信息都存儲(chǔ)到哪里去了?
這一切的答案,都要從工作區(qū)和索引區(qū)講起。Git 根據(jù)其存儲(chǔ)的狀態(tài)不同,將對(duì)應(yīng)狀態(tài)的“空間”分為工作區(qū)、暫存區(qū)(也可稱(chēng)為索引區(qū))和版本區(qū)三類(lèi)。具體示例,可以參考下圖。
而更加深層次的理解,就要從執(zhí)行 git add 命令后生成相關(guān)的 object 對(duì)象,但是其存儲(chǔ)的是文件的類(lèi)容、大小和內(nèi)容,并不包含文件名稱(chēng)的信息。而文件名稱(chēng)相關(guān)的信息就包含在生成的 index 文件(索引文件)里面。
當(dāng)我們直接查看 index 文件里面的內(nèi)容,發(fā)現(xiàn)使我們無(wú)法理解的亂碼,但是通過(guò)基本的輸出,我們可以看到其文件名稱(chēng)。要想查看 index 文件的內(nèi)容,可以通過(guò) Git 提供的相關(guān)命令進(jìn)行查看。
# 左邊執(zhí)行
$ echo "file1" > file1.txt
$ git add file1.txt
$ cat .git/index
$ git ls-files # 列出當(dāng)前暫存區(qū)的文件列表信息
$ git ls-files -s # 列出當(dāng)前暫存區(qū)文件的詳細(xì)信息
# 右邊執(zhí)行
$ watch -n 1 -d tree .git
當(dāng)添加文件的時(shí)候,文件或目錄會(huì)從工作區(qū)流向暫存區(qū),加之一些其他操作,會(huì)導(dǎo)致工作區(qū)和暫存區(qū)是會(huì)有一定差別的。這就會(huì)導(dǎo)致,當(dāng)我們執(zhí)行 git status 的結(jié)果就是兩者的差別。
經(jīng)過(guò)如下操作,會(huì)使工作區(qū)和暫存區(qū)和的內(nèi)容不一致了,通過(guò)命令我們也是可以查看區(qū)別的。當(dāng)我們使用 add 命令將新文件添加到暫存區(qū)的時(shí)候,會(huì)發(fā)現(xiàn)這下就一致了。
# 左邊執(zhí)行
$ git status
$ echo "file2" > file2.txt
$ git ls-files -s
$ git status
$ git add file2.txt
$ git ls-files -s
$ git status
# 右邊執(zhí)行
$ watch -n 1 -d tree .git
如果我們這里去修改一個(gè)文件的話,很顯然這個(gè)時(shí)候我們的工作區(qū)和暫存區(qū)又不一致了。當(dāng)我們使用命令去查看文件狀態(tài)的時(shí)候,發(fā)現(xiàn)一個(gè)文件被修改了,而 Git 是怎么知道的呢?咳咳,就是通過(guò)查找 index 文件的內(nèi)容,找到對(duì)應(yīng)文件名稱(chēng)以及其內(nèi)部引用的 object 對(duì)象,與工作區(qū)的文件內(nèi)容進(jìn)行對(duì)比而來(lái)的。
# 左邊執(zhí)行
$ git ls-files -s
$ echo "file.txt" > file1.txt
$ git status
# 右邊執(zhí)行
$ watch -n 1 -d tree .git
而這個(gè)時(shí)候,我們?cè)偈褂?git add 命令將其修改內(nèi)容保存至?xí)捍鎱^(qū)的話,會(huì)發(fā)現(xiàn)對(duì)應(yīng)文件的 object 的 blob 對(duì)象的引用值發(fā)生改變了。這時(shí)可以發(fā)現(xiàn),objects 目錄下面有三個(gè)對(duì)象了,其中 file1.txt 占了兩個(gè),但是文件卻只有兩個(gè)。通過(guò)命令查看對(duì)應(yīng) blob 對(duì)象的內(nèi)容,發(fā)現(xiàn)各有不同。
# 左邊執(zhí)行
$ git ls-files -s
$ git add file1.txt
$ git ls-files -s
# 右邊執(zhí)行
$ watch -n 1 -d tree .git
理解 commit 提交原理
執(zhí)行完成了 git commit 命令,究竟做了什么呢?
Git 倉(cāng)庫(kù)中的提交記錄保存的是你的目錄下所有文件的快照,就像是把整個(gè)目錄復(fù)制,然后再粘貼一樣,但比復(fù)制粘貼優(yōu)雅許多!Git 希望提交記錄盡可能地輕量,因此在你每次進(jìn)行提交時(shí),它并不會(huì)盲目地復(fù)制整個(gè)目錄。條件允許的情況下,它會(huì)將當(dāng)前版本與倉(cāng)庫(kù)中的上一個(gè)版本進(jìn)行對(duì)比,并把所有的差異打包到一起作為一個(gè)提交記錄。Git 還保存了提交的歷史記錄。這也是為什么大多數(shù)提交記錄的上面都有父節(jié)點(diǎn)的原因。
當(dāng)我們使用 add 命令將工作區(qū)提交到暫存區(qū),而暫存區(qū)其實(shí)保存的是當(dāng)前文件的一個(gè)狀態(tài),其中包括有哪些目錄和文件,以及其對(duì)應(yīng)的大小和內(nèi)容等信息。但是我們最終是需要將其提交到代碼倉(cāng)庫(kù)(本地)的,而其命令就是 git commit 了。
而當(dāng)我們執(zhí)行 git commit 命令的時(shí)候,究竟都發(fā)生了什么呢?可以看到當(dāng)提交之后,.git 目錄中生成了兩個(gè)信息的 object 對(duì)象,其中 logs 和 refs 目錄都有新的文件生成。通過(guò)如下操作,我們可以查看到其提交的類(lèi)型和對(duì)應(yīng)內(nèi)容。
# 左邊執(zhí)行
$ git commit -m "1st commit"
$ git cat-file -t 6e4a700 # 查看 commit 對(duì)象的類(lèi)型
$ git cat-file -p 6e4a700 # 查看 commit 對(duì)象的內(nèi)容
$ git cat-file -t 64d6ef5 # 查看 tree 對(duì)象的類(lèi)型
$ git cat-file -p 64d6ef5 # 查看 tree 對(duì)象的內(nèi)容
# 右邊執(zhí)行
$ watch -n 1 -d tree .git
這樣我們就理解了,當(dāng)我們執(zhí)行 git commit 命令之后,會(huì)生成一個(gè) commit 對(duì)象和一個(gè) tree 對(duì)象。commit 對(duì)象內(nèi)容里面包含了一個(gè) tree 對(duì)象和相關(guān)提交信息,而 tree 對(duì)象里面則包含了這次我們提交版本里面的文件狀態(tài)(文件名稱(chēng)和 blob 對(duì)象),這樣我們就知道了這次提交的變動(dòng)了。
我們這次提交之后,處理 objects 目錄發(fā)生變動(dòng)之外,還有一些其他的變化。比如 logs 和 refs 的目錄有所變化。我們查看 refs 目錄里面的內(nèi)容,發(fā)現(xiàn)其指向了 6e4a70 這個(gè) commit 對(duì)象,即當(dāng)前 master 分支上面最新的提交就是這個(gè) 6e4a70 了。
而這個(gè) 6e4a70 這個(gè) commit 對(duì)象,有一個(gè) HEAD 的指向,就是 .git 目錄下的 HEAD 文件。其實(shí)質(zhì)就是一個(gè)指針,其永遠(yuǎn)指向我們當(dāng)前工作的分支,即這里我們工作在 master 分支上。當(dāng)我們切換分支的時(shí)候,這個(gè)文件的指向也會(huì)隨機(jī)改變的。
# 左邊執(zhí)行
$ cat .git/refs/heads/master
$ cat .git/HEAD
# 右邊執(zhí)行
$ watch -n 1 -d tree .git
-
CVS
+關(guān)注
關(guān)注
0文章
14瀏覽量
11104 -
Git
+關(guān)注
關(guān)注
0文章
203瀏覽量
16064 -
版本管理
+關(guān)注
關(guān)注
0文章
7瀏覽量
230
發(fā)布評(píng)論請(qǐng)先 登錄
串聯(lián)諧振逆變器的基本原理
電機(jī)轉(zhuǎn)動(dòng)的基本原理是什么?
線性電源的基本原理是什么
SPWM的基本原理
無(wú)線充電的基本原理是什么
探究Git基本原理(下)

6.4.2.1 基本原理∈《碳化硅技術(shù)基本原理——生長(zhǎng)、表征、器件和應(yīng)用》

評(píng)論