前言
文章是对 Pro Git 第一版的阅读总结,因本人更关注 Git 的实现原理,所以文章只包含了个人认为对理解 Git 内部原理有帮助的第 1、3、4、7、9 章内容的总结,如果你也想深入的了解 Git,推荐阅读 Pro Git 的第一版和第二版:
Git 基础
Git 是一个分布式版本管理系统,与集中式版本管理系统不同,Git 几乎所有的操作都在本地进行,可以在本地进行提交更新等操作而无须联网;在 clone Git 仓库时,实际是将整个仓库镜像克隆下来,镜像包含了所有的历史提交记录,这意味着即使远程仓库丢失或损坏了,也可以轻易的通过本地仓库重建远程仓库。
Git 只关注数据整体的变化,而不是文件内容的具体差异:将变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,Git 计算所有文件的指纹信息,存储文件快照,将文件对应的指纹作为引用指向文件快照。如果文件没有变化,Git 不会再次保存,只是链接到对应的文件快照。
Git 在每次更新提交时,对每个文件进行校验计算,这意味着不会出现文件被修改但 Git 毫无所知的情况,最大程度的保证了文件的完整性。
Git 中文件只有三种状态:
- 已提交(committed)表示文件已安全的保存到本地数据库中
- 已修改(modified)表示修改了某个文件,还没有提交保存
- 已暂存(staged)表示把已修改的文件放在下次提交时要保存的清单中
三种状态对应 Git 的三个工作区域:工作区,暂存区域,以及本地仓库
- 工作区是从本地仓库中取出的某个版本的所有文件和目录,实际是从 Git 目录中的压缩对象数据库中提取的
- 暂存区域是个简单的文件,一般都放在
.git/objects
目录中
Git 简单结构
Git 通过 HEAD
文件保存当前的分支,查看 .git/HEAD
中的内容:
这表明当前分支是 .git/refs/heads/master
,查看对应的文件内容:
这是一个 40 位的指纹信息,引用到具体的文件;Git 以 40 位指纹的前两位作为目录名,剩余的 38 位作为文件名存储文件快照,这些文件都存放在 .git/objects
中:
Git 使用 zlib 的 Deflate
算法压缩内容,需要解压才能看到有意义的文件内容,使用 nodejs 解压上面的 1d/7f00bcdea10b04ceab0d243d181f09a971bcd0
文件:
得到的内容如下:
Git 将他表示为一个 commit 对象,为了直观的感受,以 js 对象表示:
({
/** 对象类型 */
type: "commit",
/** 后续内容包含的字节数 */
contentByteLenght: 178,
/** 指向 root tree 对象 */
tree: "87f0a1ed873d2b2836c5454462d938d874504aec",
/** 指向上一次的提交对象,如果是第一次 commit 则不存在 */
parent?: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/** 作者 */
author: {
name: "yuanyxh",
email: "xxxx@xxx.xxx",
timestamp: 1705569456,
reviseTime: "+0800",
},
/** 提交者 */
committer: {