博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
git深入理解(一):暂存区(Stage),索引(index)
阅读量:3986 次
发布时间:2019-05-24

本文共 5840 字,大约阅读时间需要 19 分钟。

所谓的暂存区Stage只是一个简单的索引文件而已。指的是是 .git/index文件,理解了索引更加有助于理解git的内部原理。

索引文件里面包含的是文件的目录树,像一个虚拟的工作区,在这个虚拟工作区的目录树中,记录了文件名、文件的最后修改时间、文件长度、文件类型以及最重要的SHA-1值,文件的内容并没有存储在其中。而这个SHA-1值就是某个文件的当前版本号。而文件的具体内容是在.git/objects目录下,你会发现这个目录占用空间比较大,因为它存储着当前项目文件各个版本的内容。

进入objects目录,会看到一些文件夹,先不管infopack,其他目录名称都是两个英文字母的。每一个文件的每一个版本号都是SHA-1值(40位),取前2位作为目录名,取后38位作为文件名,所以你会看到这个目录没有具体意义,只是做一个归纳而已,目录下面的文件之间也没什么关系。

在这个目录下,git将其下文件分为四种类型,其中有两种是 tree 类型和 blob 类型,blob类型文件存储的就是我们运行命令增加的文件加上一个特定的文件头,而 tree 类型文件就相当于我们系统下的目录了,里面存储的是多条记录,每一条记录含有一个指向 blob 或子 tree 对象的 SHA-1 指针,并附有该对象的权限模式 (mode)、类型和文件名信息,它和blob类型对象不一样,存储的并非文件的内容。

那么,这个SHA-1是在什么时候生成的呢?这就要明白git add命令做了什么工作。

git add 命令是由两条底层命令实现的:

git hash-object 
git update-index

运行第一条命令,git将会根据新生成的文件产生一个长度为40的SHA-1哈希字符串,并在.git/objects目录下生成一个以该SHA-1的前两个字符命名的子目录,然后在该子目录下,存储刚刚生成的一个新文件,新文件名称是SHA-1的剩下的38个字符。

第二条命令将会更新.git/index索引,使它指向 objects 目录下新生成的文件。

新建的文件在index中没有记录,如果也不在.gitignore中,那么git status就能检测到;如果修改了文件,那么文件的最后修改时间和index中的不一样,那么git status就能检测到。。。

git add不仅更新索引还生成快照文件,减少git add次数可以减小.git占用的空间。

有时候不想跟踪某些文件,可以将其从index删除掉 git rm --cached filename;当然如果它从未加入到index,那么只需要加入到.gitignore即可。

新建一个空的文件夹来测试

$ git init此时 .git 下没有index文件;.git/objects 下只有info和pack。创建两个文件 t1.txt; t2.txt,写入一点内容。$ git statusOn branch masterNo commits yetUntracked files:  (use "git add 
..." to include in what will be committed) t1.txt t2.txtnothing added to commit but untracked files present (use "git add" to track)$ git add .On branch masterNo commits yetChanges to be committed: (use "git rm --cached
..." to unstage) new file: t1.txt new file: t2.txt 此时可以看到 .git/index 文件,并且 objects 目录下多出了两个文件夹,每个文件夹下有一个文件。再次修改t1.txt和t2.txt。$ git statusOn branch masterNo commits yetChanges to be committed: (use "git rm --cached
..." to unstage) new file: t1.txt new file: t2.txtChanges not staged for commit: (use "git add
..." to update what will be committed) (use "git checkout --
..." to discard changes in working directory) modified: t1.txt modified: t2.txt $ git add .再次查看 objects 目录,发现又多出了两个目录。到目前为止我们一次都没有提交。$ git commit -m 'first commit'[master (root-commit) 01d308a] first commit 2 files changed, 2 insertions(+) create mode 100644 t1.txt create mode 100644 t2.txt 再次查看 objects 目录,发现在 01 下多出了 d308a7ef190b881969ea9b9112424819ab346a ;在 ee 目录下多出了 67353000fd373ff1229f5bf55cea66e88f6c8c ;其中 01d308a7ef190b881969ea9b9112424819ab346a 是一个commit对象的 SHA-1 值,可以通过 cat-file 命令查看对象中存储的内容。$ git cat-file -p 01d308atree ee67353000fd373ff1229f5bf55cea66e88f6c8cauthor phprao
1607322243 +0800committer phprao
1607322243 +0800first commit-p 为 pretty print 即美化输出。可以看到 commit对象指向的是tree对象,没有父级parent即代表第一次提交;查看tree对象的存储内容。$ git cat-file -p ee67353100644 blob ee9808dcf8b9fc9326ce4d96ff74e478c3809447 t1.txt100644 blob d6d2f13a2ed121d421a912680d9174bca9e5d44b t2.txt可以发现tree对象存储的是指向两个我们创建的文件SHA-1值和其他相关信息,而不是数据本身内容,100644 为 blob 文件的权限模式。我们可以查看blob文件的内容$ git cat-file -p ee9808dcaaaaaa$ git cat-file -p d6d2f13abbbbbb至此我们看到了 objects 下的三种文件类型,他们的文件名都是 SHA-1:blob对象:文件,存储了具体的文件内容。tree对象:目录,存储的是目录下的文件和目录的SHA-1值和其他相关信息。commit对象:存储着此次commit的信息。上面最终找到的两个 blob 文件是第二次 add 时生成的,而第一次 add 的文件成了多余的无用的文件。再次修改t1.txt, t2.txt并add, commit$ git commit -m '2'[master f2b85bf] 2 2 files changed, 2 insertions(+), 2 deletions(-)$ git cat-file -p f2b85bftree a8ae6ab5f2d715619b8712496e19a74dc61edf3cparent 01d308a7ef190b881969ea9b9112424819ab346aauthor phprao
1607325004 +0800committer phprao
1607325004 +08002看到 parent 字段,指向的是上一次的commit对象,于是我们知道commit对象之间是通过parent找到上一次的提交。那么如何取理解这个tree对象呢?创建 /a/b/c/t3.txt 文件,并编辑。$ git add .此时 objects 目录下只多了一个文件。显然就是 t3.txt 对应的blob文件。$ git commit -m 'add t3'[master 8262ea4] add t3 1 file changed, 1 insertion(+) create mode 100644 a/b/c/t3.txt $ git cat-file -p 8262ea4tree 996948379dab78c6413e00a361634de0ece813b2parent f2b85bf7f7516a6a6a0768e44266d09414b03a2eauthor phprao
1607325752 +0800committer phprao
1607325752 +0800add t3$ git cat-file -p 996948379dab78c6413e00a361634de0ece813b2040000 tree fc60d8c480bd0880ad7162d35810a5d6eaf5d582 a100644 blob c17f6345cada893de31a8a883b056998a9387e00 t1.txt100644 blob 66901dbe63bac3ab349ad28400c916cd45f1de01 t2.txt$ git cat-file -p fc60d8c480bd0880ad7162d35810a5d6eaf5d582040000 tree 5a1d544e8f7f0bdce703b6587a2e4542767e8d98 b$ git cat-file -p 5a1d544e8f7f0bdce703b6587a2e4542767e8d98040000 tree 6c3336da7d1c07cfbe706ecd29bdfb13d5b4f665 c$ git cat-file -p 6c3336da7d1c07cfbe706ecd29bdfb13d5b4f665100644 blob 2383bd587474492050db93523ca1bb4faf7773a0 t3.txt看到了这里,你会发现 tree 就是目录的意思,blob就是文件的意思。当前目录下有多个目录就有多个tree字段。在根目录下新建 /q/w/t4.txt 并编辑提交。$ git cat-file -p 86e331b1db9d9075131c80dd6e725a2905080abc040000 tree fc60d8c480bd0880ad7162d35810a5d6eaf5d582 a040000 tree f22d05f2ba32112c150b79f40041c4d89bfe5e0c q100644 blob c17f6345cada893de31a8a883b056998a9387e00 t1.txt100644 blob 66901dbe63bac3ab349ad28400c916cd45f1de01 t2.txt并且根目录时第一个tree。我们可以列出index上有那些文件$ git ls-filesa/b/c/t3.txtq/w/t4.txtt1.txtt2.txt

至此我们大致可以理解为什么commit之后会多出多个文件,一个是commit对象,一个是当前commit时项目的根目录tree对象,如果有新建目录,还会产生tree对象(而在 git add 的时候则只会产生blob对象),这些tree和blob展示了当前version下的文件目录结构全量信息。

如果某个文件发生了变化,那么从这个文件出发直到最外层的根目录在下次commit的时候都会生成新的tree对象,一个tree和blob可能会被多个commit所共用。

关于 tree 对象的生成条件:

比如你删除了一个文件,那么它所处的当前目录以及上面的所有目录都会改变,所以会生成多个 tree 对象原先是 treeA -> treeB -> treeC -> treeD你把 treeD 目录下删了一个文件,就变成了treeA2 -> treeB2 -> treeC2 -> treeD2

git正是拷贝index的这些目录树和文件的SHA-1等信息到commit对象而生成git的一个提交,即commit对象,commit对象保存的提交完整的记录了当前所有已跟踪文件的快照。所以你所提交的项目来自于index。

简而言之,文件索引即暂存区,建立了文件和.git/objects目录下的对象实体之间的映射关系。

借用别人的图片

img
通过前面的介绍,我们对 工作区 --> 暂存区的过程已经很清楚了,要知道index只有一个,我们在切换分支,版本,tag等操作的时候,随着HEAD的变化,会对应到不同的commit对象,此时会有一个逆向的过程,那就是从commit对象来恢复index,因为commit对象保存着全量的tree对象,所以这个过程是很明确的,于是index被改写,最后再从index将目录结构和文件检出到工作区,于是我们才能看到指定的commit时的代码。

暂存区的好处很多,比如:

1、分批提交
2、方便进行快照回退

转载地址:http://mnaui.baihongyu.com/

你可能感兴趣的文章
No.46 - LeetCode877 -动态规划 -首尾取数字最大 - 博弈
查看>>
No.49-LeetCode188 - 动态规划-K次股票最大收益-很难
查看>>
No.47 - LeetCode392
查看>>
No.48-LeetCode42 - 一维蓄水
查看>>
No.50 - leetCode407- 二维蓄水-边界水面上升-很难
查看>>
No.51 - LeetCode931-路径动态规划-简单
查看>>
No.52 - LeetCode1130 - 区间dp - 数组最小乘积和
查看>>
No.53 - leetCode1143 - 最长公共子序列
查看>>
No.54-leetcode647-统计回文子串数量
查看>>
No.55 - leetcode983 - 买票-简单dp
查看>>
No.56 - leetcode413 - 等差子串个数
查看>>
网络:TCP三次握手四次挥手
查看>>
网络:TCP可靠性传输
查看>>
网络:syn flood攻击
查看>>
Thinking in java(六)
查看>>
Thinking in java(七)
查看>>
StringBuffer、StringBuilder与为什么说StringBuilder是不安全的
查看>>
《java 编程思想》 读书笔记 (一)
查看>>
eclipse注释乱码的一个小问题
查看>>
《java 编程思想》 读书笔记 (二)
查看>>