Git内部原理
Git官网:https://git-scm.com/
Git:How it works?
版本控制系统VCS
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
版本控制系统:是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统

存储系统
项目里的文件/目录怎么存?存在哪里?怎么去检索?
以Git对象(文件内容对象、目录对象、提交对象)方式存储;本地存放在.git
目录;通过Hash Code
去检索
根本上来讲:Git 是一个内容寻址 (content-addressable)文件系统。可以理解成:Git一个Key-Value
数据库
- Key:Hash Code,就是查看git日志里面的一串码(hash算法基于
内容
计算出来) - Value:Git对象(文件对象blob,目录对象tree,提交对象commit)
Git项目目录

仓库.git
目录介绍
bash
$ tree .git # 输出:有省略部分内容
.git
├── HEAD # 记录工作区的代码版本
├── config # 项目配置
├── objects # Git对象存储目录(核心目录)
└── refs # 引用:存放分支、远程分析、Tags
├── heads # 本地分支存放的目录
│ └── master
├── remotes
│ └── origin
└── tags
推理

底层命令和高层命令
Git最开始工具集(多个命令工具),后面整合改成了git的子命令
- 高层命令:简单的理解成,工作常用的这些命令:
init
、clone
、add
、commit
、push
、pull
、checkout
...... - 底层命令:可以去操作Git的KV数据库,完成底层工作的命令:
cat-file
、hash-object
、write-tree
、commit-tree
关系
高层命令会去调用条底层命令;底层命令可以窥探 Git 内部的工作机制,通过底层命令帮助理解Git是如何运作的
Git 对象
核心Git对象类型:
- 文件内容对象
blob
:文件内容 - 目录对象
tree
:可包含文件或目录 - 提交对象
commit
:包含目录和其它信息(git log
内容)
底层命令cat-file
用于查看Git对象:
bash
$ git cat-file -t ${hash_code} # 查看Git对象的类型 type
$ git cat-file -p ${hash_code} # 查看Git对象的内容 pretty-print
$ git cat-file -s ${hash_code} # 查看Git对象的大小 size
实践
通过使用底层命令完成高层命令的工作流(init
、add
、commit
、log
),来理解Git内容原理
项目准备git init
bash
$ mkdir git_demo && cd git_demo # 创建项目目录
$ mkdir .git # 创建.git目录
$ mkdir .git/objects # 创建Git数据库目录
$ mkdir -p .git/refs/heads # 创建本地分支目录
$ touch .git/HEAD # 创建Git工作区文件
$ echo "ref: refs/heads/master" > .git/HEAD # 指定当前工作区是指向master分支
Git文件内容对象Blob:
文件内容,在项目代码里面指代码内容;不包含文件名,文件名保存在Tree对象中
bash
$ git hash-object ${file_name} # 计算文件内容的Hash Code
$ git hash-object -w ${file_name} # 计算文件内容的Hash Code,并写入Git对象存储目录
$ echo "hello git" | git hash-object --stdin # 从标准输入 计算内容的Hash Code
$ git cat-file -t ${hash_code} # 查看Git对象的类型
$ git cat-file -p ${hash_code} # 查看Git对象的内容
$ git cat-file -s ${hash_code} # 查看Git对象的大小
Git树对象Tree:
目录:包含Blob对象,以及Tree对象
bash
$ git update-index --add ${file_name} # 将文件添加到暂存区
$ git write-tree # 从暂存区创建树对象(root跟目录树对象)
read-tree
:可以把树对象读入暂存区
Git提交对象Commit:
基本就是用git log
可以看到的内容
bash
$ git commit-tree ${tree_hash} -m ${commit_message} # 创建提交对象;第一次提交
$ git commit-tree ${tree_hash} -m ${commit_message} -p ${parent_commit_hash} # 创建提交对象;第二次提交
$ git log --stat ${commit_hash}
$ echo "${commit_hash}" > .git/heads/master # 指定master分支指向的提交对象
Git存储结构图

对象存储
Git 是如何存储其对象(blob对象、tree对象、commit对象)
bash
# store = header + content
blob 16\u0000what is up, doc?
- 存储格式:
header + content
,比如:"blob 16\u0000what is up, doc?"header
格式:比如文件blob #{content.length}\0
- 对内容计算SHA1
- 用zlib压缩内容
- 用前面计算出来的SHA1,创建目录和文件
- 把内容写到文件中去
理解分支、Tag是如何实现的?
就是.git
目录下的一个文件,文件名就是分支名,文件内容就是该分支的最后一个Commit
对象;比如:dev
分支文件.git/heads/dev
Ref:
- Git Internals:https://git-scm.com/book/en/v2/Git-Internals-Git-Objects