[toc]
引用
抄自 https://www.bilibili.com/video/BV11z4y1X79p?spm_id_from=333.337.search-card.all.click 該鏈接
并且結(jié)合 PRO_GIT 一書總結(jié)
git 不關(guān)注差異副硅,只關(guān)注當(dāng)前快照
git和svn的一大區(qū)別是弄诲,svn關(guān)注的是修改微猖,git關(guān)注當(dāng)前現(xiàn)狀盟猖。
比方說胆绊,一個空房間里有兩個箱子。每次這兩個箱子移動嗽交,svn都會記錄他們的移動路線篮幢,但是git不在乎,它只會在你commit的時候穷缤,拿著相機進屋拍個照敌蜂,箱子在哪就是哪里,愛咋咋地津肛。
Git 不按照以上方式對待或保存數(shù)據(jù)章喉。反之,Git 更像是把數(shù)據(jù)看作是對小型文件系統(tǒng)的一系列快照。 在 Git
中秸脱,每當(dāng)你提交更新或保存項目狀態(tài)時落包,它基本上就會對當(dāng)時的全部文件創(chuàng)建一個快照并保存這個快照的索引。為了效率摊唇,如果文件沒有修改咐蝇,Git 不再重新存儲該文件,而是只保留一個鏈接指向之前存儲的文件巷查。 Git 對待數(shù)據(jù)更像是一個 快照流有序。
git commit tree blob 結(jié)構(gòu)圖
現(xiàn)在,Git 倉庫中有五個對象:三個 blob 對象(保存著文件快照)吮便、一個 樹 對象 (記錄著目錄結(jié)構(gòu)和 blob 對象索引)以及一個 提交 對象(包含著指向前述樹對象的指針和所有提交信息)笔呀。
做些修改后再次提交,那么這次產(chǎn)生的提交對象會包含一個指向上次提交對象(父對象)的指針髓需。
./git 中的對象 commit tree blob
blob自行百度许师,總之這里你理解成個存儲數(shù)據(jù)的格式就行了
仿照視頻里面的步驟,我們也用個窗口僚匆,來觀察當(dāng)你執(zhí)行 add commit操作的時候微渠, ./git 里面發(fā)生了什么。同樣我們也刪除 hooks
git init后
.git
├── branches
├── config
├── description
├── HEAD
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
8 directories, 4 files
變換一:新建test1.c咧擂, 之后add --生成blob
vi test1.c
寫一行 The Master Chief
逞盆,然后 wq
這個時候沒有任何變化
git add test1.c
我們發(fā)現(xiàn)多了下面的object
.git
├── branches
├── config
├── description
├── HEAD
├── index
├── info
│ └── exclude
├── objects
│ ├── c2
│ │ └── 673408f719c6224ae85b27c9d4245ad96e55d6
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
9 directories, 6 files
git cat-file -t c267
發(fā)現(xiàn)是 blob 類型
git cat-file -p c267
會顯示 The Master Chief
變化二:第一次 commit --生成tree和commit
git commit -m "first commit"
該操作會生成兩個新的 objects 作為 tree 和 commit
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
├── objects
│ ├── 12
│ │ └── 6dbd7bfa165928164e2807e0e6a42b4d85ac37
│ ├── 55
│ │ └── d9df99408ea612721fbe8ba7e18be1b7a3c594
│ ├── c2
│ │ └── 673408f719c6224ae85b27c9d4245ad96e55d6
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
14 directories, 12 files
這里我們分別的 git cat-file -t
git cat-file -p
看下
git cat-file -t 126d
顯示 commit
git cat-file -p 126d
顯示四行,分別是 tree/author/committer 和 和最后的commit信息
git cat-file -t 55d9
顯示 tree
git cat-file -p 55d9
顯示 100644 blob c2673408f719c6224ae85b27c9d4245ad96e55d6 test1.c
這里面的blob 剛好就是我們之前 add 的blob 對應(yīng)的文件
完全相同的文件松申,將會使用同一個blob
創(chuàng)建一個test2.c云芦,內(nèi)容完全和 test1.c 一致,觀察
vi test2.c
寫一行 The Master Chief
贸桶,然后 wq
這個時候沒有任何變化
git add test2.c
結(jié)果.git文件夾里面的內(nèi)容毫無變化
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
├── objects
│ ├── 12
│ │ └── 6dbd7bfa165928164e2807e0e6a42b4d85ac37
│ ├── 55
│ │ └── d9df99408ea612721fbe8ba7e18be1b7a3c594
│ ├── c2
│ │ └── 673408f719c6224ae85b27c9d4245ad96e55d6
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
14 directories, 12 files
第二次commit
git commit -m "second commit"
該操作會再生成兩個新的 objects 作為 tree 和 commit
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
├── objects
│ ├── 12
│ │ └── 6dbd7bfa165928164e2807e0e6a42b4d85ac37
│ ├── 55
│ │ └── d9df99408ea612721fbe8ba7e18be1b7a3c594
│ ├── 8f
│ │ └── ddf9dedce34df33de41c544e7bf6213494e9e4
│ ├── b9
│ │ └── be6a158220da6aa0cd39e64402d7909a217773
│ ├── c2
│ │ └── 673408f719c6224ae85b27c9d4245ad96e55d6
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
16 directories, 14 files
這里我們分別的 git cat-file -t
git cat-file -p
看下
git cat-file -t 8fdd
顯示 commit
git cat-file -p 8fdd
顯示五行舅逸,分別是 tree/parent/author/committer 和 和 second commit
git cat-file -t b9be
顯示 tree
git cat-file -p b9be
顯示
100644 blob c2673408f719c6224ae85b27c9d4245ad96e55d6 test1.c
100644 blob c2673408f719c6224ae85b27c9d4245ad96e55d6 test2.c
可以看到tree里面引用了 一模一樣的blob
修改同一個文件,將生成額外的blob
修改 test1.c皇筛,然后add
現(xiàn)在我們修改 vi test1.c
琉历,另起一行加入一句 Spartan 117
,然后 wq
git add test1.c
多了一條object ``38a1```
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
├── objects
│ ├── 12
│ │ └── 6dbd7bfa165928164e2807e0e6a42b4d85ac37
│ ├── 38
│ │ └── a13225d42cba196f7f183ce0b17e0ec31b7cd9
│ ├── 55
│ │ └── d9df99408ea612721fbe8ba7e18be1b7a3c594
│ ├── 8f
│ │ └── ddf9dedce34df33de41c544e7bf6213494e9e4
│ ├── b9
│ │ └── be6a158220da6aa0cd39e64402d7909a217773
│ ├── c2
│ │ └── 673408f719c6224ae85b27c9d4245ad96e55d6
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
17 directories, 15 files
檢查一下這個 38a1
git cat-file -t 38a1
顯示 blob
git cat-file -p 38a1
顯示
The Master Chief
Spartan 117
第三次 commit水醋,生成新的 tree 和 commit
git commit -m "third commit"
該操作會再生成兩個新的 objects 作為 tree 和 commit
4d65
5385
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
├── objects
│ ├── 12
│ │ └── 6dbd7bfa165928164e2807e0e6a42b4d85ac37
│ ├── 38
│ │ └── a13225d42cba196f7f183ce0b17e0ec31b7cd9
│ ├── 4d
│ │ └── 65a099692964508d4cab2dc6f4764e2e8bfeae
│ ├── 53
│ │ └── 854de4bae0ad5aaeb7f841da470fbec0008a78
│ ├── 55
│ │ └── d9df99408ea612721fbe8ba7e18be1b7a3c594
│ ├── 8f
│ │ └── ddf9dedce34df33de41c544e7bf6213494e9e4
│ ├── b9
│ │ └── be6a158220da6aa0cd39e64402d7909a217773
│ ├── c2
│ │ └── 673408f719c6224ae85b27c9d4245ad96e55d6
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
19 directories, 17 files
git cat-file -t 4d65
顯示 commit
git cat-file -p 4d65
顯示五行旗笔,分別是 tree/parent/author/committer 和 third commit
git cat-file -t 5385
顯示 tree
git cat-file -p 5385
顯示
100644 blob 38a13225d42cba196f7f183ce0b17e0ec31b7cd9 test1.c
100644 blob c2673408f719c6224ae85b27c9d4245ad96e55d6 test2.c
當(dāng)前的tree引用了 test1.c的新blob 38a1,但是原先的 test1.c blob并沒有消失拄踪,依然保存在 ./git
這也就是為什么 git reset 的時候你能很快的回到當(dāng)前commit點蝇恶,因為直接通過指針,找到了對應(yīng)的tree和blob
test1.c 又改回到第一次commit的狀態(tài)惶桐,新commit一次艘包,tree會怎么變
tree會回到第一次的狀態(tài)的猛,雖然他們上面的commit點哈希值不一樣,tree本身的哈希值也不一樣想虎,但是tree的內(nèi)容會一樣
reset --hard 對 .git 內(nèi) objects 的影響
事實上是沒有影響卦尊,所有已經(jīng)存在的 objects 都會被保留。所以reset --hard 并不是不能恢復(fù)的舌厨,但是如果你刪除掉本地的庫岂却,又沒有遠程庫保存,那么就徹底不能恢復(fù)了裙椭,因為你的 objects 都被干掉了
剛才有三次commit躏哩,我reset --hard 到第一次,再cherry之前的commit點哈希揉燃,發(fā)現(xiàn)是可以cherry-pick的扫尺。
所以,如果不小心 reset --hard炊汤,遠端也沒有保存正驻,
千萬不要隨便刪除本地庫的文件夾,沒有這個.git你就徹底恢復(fù)不了了
git分支原理 && 為什么 git 分支切換速度快
svn如果你創(chuàng)建一個分支抢腐,它會將自己的所有的代碼全部復(fù)制一份姑曙,然后建立一個新分支。
git的分支不同迈倍,它只是一個指針伤靠,指向了你的某個commit點,相當(dāng)于你創(chuàng)建一個新分支的時候啼染,只不過是創(chuàng)建了一個指針而已宴合。
之前提過,或者結(jié)合下圖迹鹅,我們打印commit點的時候卦洽,會生成5行內(nèi)容
分別是 tree/parent/author/committer
其中parent就是指向父commit點的指針。
首次提交產(chǎn)生的提交對象沒有父對象徒欣,普通提交操
作產(chǎn)生的提交對象有一個父對象逐样, 而由多個分支合并產(chǎn)生的提交對象有多個父對象蜗字。
git在管理代碼時打肝,如果出現(xiàn)reset rebase pull merge
等等改變當(dāng)前commit點的操作時,會根據(jù)最終的commit點指針挪捕,找到它下面連著的tree粗梭,再找到tree對應(yīng)的blob文件,瞬間恢復(fù)到該commit點應(yīng)該有的狀態(tài)级零。