Git是现在最流行的分布式版本控制工具,本系列文章将带大家了解Git的内部工作原理。如果你还没有使用过Git,可以参考Git新手教程(1)
key-value数据库
使用git时相信大家经常会看到一串十六进制数字,其实这一串数字是某个对象的SHA1值。关于密码学中的哈希函数可以参考认识区块链(1)——哈希算法 | 雅乐网
git提供了底层的命令 git hash-object ,可以用来操作git中的对象
1 2 |
$ echo "yalewoo.com" | git hash-object --stdin 95a7073153bacf84c44b1c79b4b6e6a863e25dca |
上面命令中的–stdin表示从输入流读数据,作用是查看”yalewoo.com”的SHA1值。注意git自身会对对象的内容进行压缩存储,因此这里的SHA1是在压缩后的消息上计算的。
如果带有-w参数,表示将这个object写入git的数据库
1 2 3 4 5 |
$ git init Initialized empty Git repository in D:/code/learngit/.git/ $ echo "yalewoo.com" | git hash-object --stdin -w 95a7073153bacf84c44b1c79b4b6e6a863e25dca |
我们可以在.git/objects/目录下看到刚刚加入的object
使用 git cat-file命令可以查看object,-p表示打印内容,-t可以查看类型
1 2 3 4 |
$ git cat-file 95a7073153bacf84c44b1c79b4b6e6a863e25dca -t blob $ git cat-file 95a7073153bacf84c44b1c79b4b6e6a863e25dca -p yalewoo.com |
可以看到类型是blob,这个blob的内容就是刚刚加入时实际的内容”yalewoo.com”。
Commit
我们先创建一个blog.txt文件,用来存放优秀博客列表
1 2 3 |
$ echo "yalewoo.com" > blogs.txt $ git hash-object blogs.txt 95a7073153bacf84c44b1c79b4b6e6a863e25dca |
再创建一个目录 details 存放每个博客的详细信息,里面有两个文件,readme和yalewoo.txt。注意这里yalewoo.com.txt里面的内容和blogs.txt的内容是一样的。
1 2 3 |
$ mkdir details $ echo "details readme" > details/readme.txt $ echo "yalewoo.com" > details/yalewoo.com.txt |
然后我们来提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ git add * $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: blogs.txt new file: details/readme.txt new file: details/yalewoo.com.txt $ git commit -m "first commit" [master (root-commit) 606b6c3] first commit 3 files changed, 3 insertions(+) create mode 100644 blogs.txt create mode 100644 details/readme.txt create mode 100644 details/yalewoo.com.txt |
可以看到本次提交的commit的SHA1是606b6c3…
1 2 3 4 5 6 7 8 9 |
$ git cat-file -t 606b6c3 commit $ git cat-file -p 606b6c3 tree d096dd512f1c699f99310e4fb2d05f34b76f2468 author yalewoo <chongruyuntian@163.com> 1568515063 +0800 committer yalewoo <chongruyuntian@163.com> 1568515063 +0800 first commit |
打印本次commit的内容,可以发现,git里的一个commit只是记录了一些文本。其中tree指向了另一个git对象,另外还包含了提交作者,提交者和提交注释。
1 2 3 4 5 6 |
$ git cat-file -t d096dd512f1c699f99310e4fb2d05f34b76f2468 tree $ git cat-file -p d096dd512f1c699f99310e4fb2d05f34b76f2468 100644 blob 95a7073153bacf84c44b1c79b4b6e6a863e25dca blogs.txt 040000 tree 2c6c24f3530f2d4be7f8a1290b930a8f3e11b7dc details |
tree对象类似文件系统中的目录,可以包含其他tree和blob。这个tree里包含了blogs.txt这个blob对象和details目录。
你应该还记得,”yalewoo.com”的SHA1就是这里的95a7073153bacf84c44b1c79b4b6e6a863e25dca, 这个对象记录的就是”yalewoo.com”这个字符串。而文件名blogs.txt是记录在tree对象里面的。
类似的,我们可以看一下details对应的tree对象,
1 2 3 |
$ git cat-file -p 2c6c24f3530f2d4be7f8a1290b930a8f3e11b7dc 100644 blob 3fd224609fd7dd3e97b5e8ec134ba84b7052efeb readme.txt 100644 blob 95a7073153bacf84c44b1c79b4b6e6a863e25dca yalewoo.com.txt |
yalewoo.com.txt对应的blob仍然是那个95a7073,可以看出文件内容相同的文件在git里只会存放一份!
你可以在.git/objects/目录里看到所有的object,然后使用git cat-file查看他们。
现在所有的object是这样组织的
第二次commit
我们改一下blogs.txt文件,在最下面加入一行: wordpress.com
1 2 3 |
$ cat blogs.txt yalewoo.com wordpress.com |
然后再次commit
1 2 3 4 5 |
$ git add blogs.txt $ git commit -m "change blogs.txt" [master c428c2c] change blogs.txt 1 file changed, 1 insertion(+) |
可以看到一个新的commit诞生了。我们可以使用上面的方法查看这次commit。
1 2 3 4 5 6 7 |
$ git cat-file c428c2c -p tree 8252d83070251e9c6c74ea13990ef8c787658e6e parent 606b6c3d7e55733783be97b7fc2b929eaa3b790c author yalewoo <chongruyuntian@163.com> 1568516737 +0800 committer yalewoo <chongruyuntian@163.com> 1568516737 +0800 change blogs.txt |
这次commit和第一次不同的是,他有一个parent,指向的正是第一次提交的commit.
details目录下的内容没有改动,所以对应的tree结点仍然是原来的。
Git object
git中的object一共有4种类型,除了上面的commit, tree和blob,还有annotated tag,大家可以自行了解。
谢谢观看~下一次我们讲git中的分支。