概述
Git 是非常流行的分布式版本控制系统,众所周知是由 Linux 创始人 Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。Git 不同于 SVN 等中心化的版本控制系统,Git是基于快照的存储,而SVN是基于文件差异的存储。
原理
Git本质上是一个内容寻址文件系统加VCS接口, Git所管理的每一个文件都是通过校验和进行索引的。
存储过程
比如这里有一个文本文件README.md需要被存储, 那么 Git 会根据其文件内容+文件类型计算出一个校验和(SHA-1 Hash),比如 7de504fb32f2227f3616463b55fd02501ce2dd32
。然后将其压缩并存储在目录: .git/objects/7d/e504fb32f2227f3616463b55fd02501ce2dd32
这里可以看出 Git 使用校验和的前2位作为子目录名称进行存储的。
每一个.git/object目录下的文件都是Git所管理的对象,这里的对象包含的内容可以是文件本身,也可以是目录结构,也可以是其他Git的概念,比如commit等。这里列举出一些常见的文件类型:
- blob 二进制文件
- tree 文件树(目录)
- commit 提交记录
- tag 标签
Git 命令提供了一些查看这些对象内容的功能,
比如 git cat-file -t 7de504 会返回 blob 说明这个对象是一个二进制文件,然后通过 git cat-file -p 7de504 命令就会返回这个二进制文件的内容。
我们最常见的每个Git提交都对应了一个校验和的Hash值,这个值就对应了一个对象。我们来看一下commit这种文件类型到底是什么:
首先找一个Git仓库,通过 git log 获取到最新的一条commit记录的Hash值,比如80c786,
那么看一下这个对象的文件类型:
$ git cat-file -t 80c786
commit
然后看一下其文件内容:
$ git cat-file -p 80c786
tree f0da4cc12b5bb15f955413f3e76f78f098c9fbff
parent e7204bec7cbcc583fee7ad158f287dd91765abf0
author leslie <leslie@example.com> 1585377566 +0800
committer leslie <leslie@example.com> 1585377566 +0800
feat: Add README.md
这时就很可以很清楚地看到每个commit在底层所存储的内容了,包括作者、提交信息、文件目录树等。Git就是通过这些信息记录每一次提交的,在运行 git commit 之后,Git 会根据新的文件目录结构和文件内容生成一份快照(文件树对象),然后记录下快照的索引加上commit信息生成这个commit对象。
总结
Git是非常强大的版本管理系统,在理解了其底层工作原理之后能更好的帮助我们去理解记忆不同的Git命令,所有复杂的Git操作都是在这个基础之上进行发展的。
最后
这里笔者没有对 tree 文件类型的内容做展开,希望读者自己在代码仓库里去探索。
在未熟练使用Git命令行操作之前建议少使用任何图形界面的Git管理工具。
引用
- https://git-scm.com/book/en/v2/Git-Internals-Git-Objects