您的位置:首页 > 其它

分布式版本控制系统Git------git工作区和主要对象

2016-04-12 16:00 357 查看

前言

接着上个博客来说,在我纠结在暂存区是否有类似于HEAD的指针,带着疑问稍微往深处看了一下,发现这个想法是错误的。
找到了一张描述工作区内部结构的图片,大概可以解决我的疑问。



一句话说明白这个图:暂存区和版本库是引用同一个objects目录的,而暂存区和版本库其实只是存储了文件结构的目录树,并没有存储实际内容,实际内容都存储在objects目录里面。

工作区

工作区就是初始化git目录之后,存储在硬盘上的文件。我们平常工作都是编辑这里面的内容,也就是要把这些修改加入到版本库之中。

暂存区

暂存区是在工作区和版本库中间起缓存作用的一个区域,这个区域也是git的特色之一。它通过git
add命令添加进暂存区。存储了一些即将被commit的文件。

版本库

版本库是使用了git commit命令之后添加进的真正的“仓库”。里面存储了每次commit的记录,每次commit一次会让HEAD指针指向新的目录树,而旧的目录就存在版本库中,可以使用命令来提出之前的目录树,这样就可以“回到过去”了。

objects目录

存放的是实际上的文件资源,每次当使用了git add命令之后,就已经把文件存到了objects目录里面。
objects目录中的object对象都有一个通过哈希算法计算出来的40位16进制的id,前两位是目录名,后38位是文件名。因为哈希算法可以只比较哈希值,就能知道这两个对象是不是一样的,这样可以提高效率。

HEAD指针

head保存了当前的分支,打开HEAD指针文件,它里面写着:
refs/heads/master ,即指向master分支(或者其他分支)。

涉及到的命令

git add命令是将工作区的目录树覆盖到暂存区,同时工作区的文件将会写入到objects对象库中,该对象的id被记录在暂存区的目录树中。这个时候暂存区目录树发生改变。
git commit命令是将暂存区的目录树写到版本库之中,HEAD指针指向最新的目录树,也就是和暂存区一样的目录树。这个时候版本库的目录树发生改变。

git reset HEAD命令是将版本库中的目录树覆盖掉暂存区的目录树。这个时候暂存区的目录树发生改变,变成和版本库一样。

git checkout .或git checkout -- 命令是将版本库中的目录树覆盖到工作区的目录树,这个命令很危险,会用暂存区部分或者全部文件替代工作区的指定文件,会清除工作区未添加进暂存区的改动。

git
checkout HEAD .或git checkout HEAD 命令会用HEAD指向的当前版本库的目录树的全部或部分替换暂存区和工作区中的目录树。这个命令也是极度危险的。和上个命令是一样的,并且这里还会导致暂存区内容被覆盖。

git diff file命令是比较工作区和暂存区的文件是否不同,其实也就是改变工作区中的文件内容和objects目录中比较查看是否改变。

git status命令是扫描工作区的变动。git使用的算法让它更高效,大概分为下面三个步骤:
1.当执行git status命令的时候,将暂存区记录的文件的文件长度,时间截进行比较。
2.如果都一样说明没有改动,break。如果文件长度或者时间截改变了,说明文件内容可能改变。
3.打开文件,与暂存区的文件内容进行比较。如果文件内容没有变化,就把新的时间截覆盖之前的时间截。如果
文件改变了,就替换文件。
因为git数据比较是先把git中的长度和时间截进行比较,如果不同在比较具体内容,这样就提高了git的运行效率。

有一个疑惑产生

疑惑

当看到这里的时候,我新建一个空白的目录。初始化它,然后新建一个test.txt文件,执行git add和git commit命令之后,发现在objects目录中居然是多出了三个文件夹,这引发了我的思考,这三个文件夹分别是什么?





测试(测试写的很乱,大家可以跳过看总结就是

1.新建一个空的目录,初始化为git,进入 .git/objects里面发现只有info和pack文件夹。





2. 返回根目录,新建一个test.txt文件,使用git add test.txt命令。再次进入 .git/objects发现多出了一个名为的80文件夹。





3. 使用git commit test -m "add Hello world"命令,再次进入 .git/objects目录下,发现多出了两个文件夹,分别是名为
5f 和名为 40 的文件夹





4.用git log命令打开观察,commit id是 5f31986……





我决定要查看每个object里面的内容。
5.使用 git cat-file -p 5f3198674502b1935565a10777a60e96d2ec9741命令,解压缩commit文件,发现这个5f3198开头的对象是一个tree类型的数据。

有一个哈希值 401ce7dbd55d28ea49c1c2f1c1439eb7d2b92427,这个哈希值对应的文件也能在objects目录下找到,恰好它在40目录下,证实了git对象前两位是表示文件夹名字。





6.然后再用git cat-file -p 401ce7……命令查看这个object里面的内容。
输出表示有一个blob类型的数据 哈希值为802992c4220de19a90767f3000a79a31b98d0df7,这个数据也是objects目录下80文件夹内的文件。





7.再次使用git cat-file blob 802992c4220de19a90767f3000a79a31b98d0df7命令

输出Hello world,就是test.txt文件中的内容。





结论

由上面的对象,我发现了原来这三个对象分别为:Blob对象,Tree对象,Commit对象。当一个文件使用git add命令之后,会在objects目录下生成一个保存了文件原本信息的blob对象。当使用git
commit命令之后,会生成一个Tree对象和一个commit对象。

总结

经过上面实验,可以得出在使用git add命令的时候,git将文件转化为blob格式的数据存入objects目录之中,更新暂存区的目录树。
当使用git commit命令时,版本库的HEAD指针会指向暂存区,生成包含了blob对象的Tree对象和包含了Tree对象的Commit对象,此时是互相引用的状态。




Blob对象

git中用来存储文件数据的对象,通常是一个文件。
可以用git show <blob>命令来查看blob对象。

Tree对象

这个Tree对象可以存储多个Blob对象或者Tree对象,要把blob对象封装成一个tree对象的item数据块。
item数据块的格式是blob对象的哈希值 + 数据文件名字 + 文件模式。
如: 100644(文件模式) blob 802992c4220de19a90767f3000a79a31b98d0df7(哈希值) test.txt(文件的名字)。
可以用git ls-tree <tree>来查看详细信息。

Commit对象

Commit对象包括四个部分:
1.一个Tree对象,代表在某一时间的内容。
2.提交的说明信息。
3.提交者的信息。
4.父Tree的哈希值(因为之前是第一次提交,所以没有父提交)

Commit对象是这三个对象中最上层面向使用者的对象,其实它是一个对于Tree对象的扩展,用来描述用户姓名,时间,备注等信息的对象。正是因为Commit对象里面包括了时间,不同时间生成的Commit对象就算数据一样还是会产生不同的哈希值。
可以用git show <commit>命令来查看。

Tag对象

Tag对象其实可以看成上面三个对象的一种特殊的表达方式,一个标签对象包括一个对象名(就是SHA1签名),
对象类型, 标签名, 标签创建人的名字, 还有一条可能包含有签名(signature)的消息。因为要记住对象名其实是很麻烦的,所以Tag对象可以来标记上面类型的对象。
可以用 git cat-file 命令来查看这些信息:

PS 两个可以查看所有对象命令:
git cat-file -t 可以查看对象类型
git cat-file -p 可以查看对象内容
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: